2017-07-06 17:28:13 +03:00
package integration
2015-11-21 04:59:49 +03:00
import (
2017-11-09 14:16:03 +03:00
"bytes"
2015-11-21 04:59:49 +03:00
"crypto/tls"
2018-08-14 19:38:04 +03:00
"fmt"
2015-11-21 04:59:49 +03:00
"net"
"net/http"
"net/http/httptest"
2017-06-23 16:15:07 +03:00
"os"
2024-01-09 19:00:07 +03:00
"testing"
2015-11-21 04:59:49 +03:00
"time"
2017-11-09 14:16:03 +03:00
"github.com/BurntSushi/toml"
2024-01-09 19:00:07 +03:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
2023-02-03 17:24:05 +03:00
"github.com/traefik/traefik/v3/integration/try"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
traefiktls "github.com/traefik/traefik/v3/pkg/tls"
2024-01-11 19:06:06 +03:00
"github.com/traefik/traefik/v3/pkg/types"
2015-11-21 04:59:49 +03:00
)
2020-05-11 13:06:07 +03:00
// HTTPSSuite tests suite.
2015-11-21 04:59:49 +03:00
type HTTPSSuite struct { BaseSuite }
2024-01-09 19:00:07 +03:00
func TestHTTPSSuite ( t * testing . T ) {
suite . Run ( t , & HTTPSSuite { } )
}
2015-11-21 04:59:49 +03:00
// TestWithSNIConfigHandshake involves a client sending a SNI hostname of
// "snitest.com", which happens to match the CN of 'snitest.com.crt'. The test
// verifies that traefik presents the correct certificate.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithSNIConfigHandshake ( ) {
file := s . adaptFile ( "fixtures/https/https_sni.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
2015-11-21 04:59:49 +03:00
2017-05-17 16:22:44 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.org`)" ) )
require . NoError ( s . T ( ) , err )
2015-11-21 04:59:49 +03:00
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
2016-11-09 20:43:59 +03:00
NextProtos : [ ] string { "h2" , "http/1.1" } ,
2015-11-21 04:59:49 +03:00
}
2016-01-14 00:46:44 +03:00
conn , err := tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "failed to connect to server" )
2015-11-21 04:59:49 +03:00
defer conn . Close ( )
err = conn . Handshake ( )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "TLS handshake error" )
2015-11-21 04:59:49 +03:00
cs := conn . ConnectionState ( )
err = cs . PeerCertificates [ 0 ] . VerifyHostname ( "snitest.com" )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "certificate did not match SNI servername" )
2016-11-09 20:43:59 +03:00
proto := conn . ConnectionState ( ) . NegotiatedProtocol
2024-01-09 19:00:07 +03:00
assert . Equal ( s . T ( ) , "h2" , proto )
2015-11-21 04:59:49 +03:00
}
// TestWithSNIConfigRoute involves a client sending HTTPS requests with
// SNI hostnames of "snitest.org" and "snitest.com". The test verifies
// that traefik routes the requests to the expected backends.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithSNIConfigRoute ( ) {
file := s . adaptFile ( "fixtures/https/https_sni.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
2015-11-21 04:59:49 +03:00
2017-05-17 16:22:44 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`snitest.org`)" ) )
require . NoError ( s . T ( ) , err )
2017-05-17 16:22:44 +03:00
2020-07-17 16:38:04 +03:00
backend1 := startTestServer ( "9010" , http . StatusNoContent , "" )
backend2 := startTestServer ( "9020" , http . StatusResetContent , "" )
2015-11-21 04:59:49 +03:00
defer backend1 . Close ( )
defer backend2 . Close ( )
2017-05-17 16:22:44 +03:00
err = try . GetRequest ( backend1 . URL , 1 * time . Second , try . StatusCodeIs ( http . StatusNoContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-05-17 16:22:44 +03:00
err = try . GetRequest ( backend2 . URL , 1 * time . Second , try . StatusCodeIs ( http . StatusResetContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2015-11-21 04:59:49 +03:00
tr1 := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
} ,
}
tr2 := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.org" ,
} ,
}
2017-05-17 16:22:44 +03:00
req , err := http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2018-07-31 11:48:03 +03:00
req . Host = tr1 . TLSClientConfig . ServerName
req . Header . Set ( "Host" , tr1 . TLSClientConfig . ServerName )
2015-11-21 04:59:49 +03:00
req . Header . Set ( "Accept" , "*/*" )
2018-07-31 11:48:03 +03:00
err = try . RequestWithTransport ( req , 30 * time . Second , tr1 , try . HasCn ( tr1 . TLSClientConfig . ServerName ) , try . StatusCodeIs ( http . StatusNoContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2015-11-21 04:59:49 +03:00
2017-05-17 16:22:44 +03:00
req , err = http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2018-07-31 11:48:03 +03:00
req . Host = tr2 . TLSClientConfig . ServerName
req . Header . Set ( "Host" , tr2 . TLSClientConfig . ServerName )
2015-11-21 04:59:49 +03:00
req . Header . Set ( "Accept" , "*/*" )
2018-07-31 11:48:03 +03:00
err = try . RequestWithTransport ( req , 30 * time . Second , tr2 , try . HasCn ( tr2 . TLSClientConfig . ServerName ) , try . StatusCodeIs ( http . StatusResetContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2015-11-21 04:59:49 +03:00
}
2019-06-17 19:14:08 +03:00
// TestWithTLSOptions verifies that traefik routes the requests with the associated tls options.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithTLSOptions ( ) {
file := s . adaptFile ( "fixtures/https/https_tls_options.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
2019-06-17 19:14:08 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`snitest.org`)" ) )
require . NoError ( s . T ( ) , err )
2019-06-17 19:14:08 +03:00
2020-07-17 16:38:04 +03:00
backend1 := startTestServer ( "9010" , http . StatusNoContent , "" )
backend2 := startTestServer ( "9020" , http . StatusResetContent , "" )
2019-06-17 19:14:08 +03:00
defer backend1 . Close ( )
defer backend2 . Close ( )
err = try . GetRequest ( backend1 . URL , 1 * time . Second , try . StatusCodeIs ( http . StatusNoContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-06-17 19:14:08 +03:00
err = try . GetRequest ( backend2 . URL , 1 * time . Second , try . StatusCodeIs ( http . StatusResetContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-06-17 19:14:08 +03:00
tr1 := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
2022-08-09 18:36:08 +03:00
MaxVersion : tls . VersionTLS12 ,
2019-06-17 19:14:08 +03:00
ServerName : "snitest.com" ,
} ,
}
tr2 := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
MaxVersion : tls . VersionTLS12 ,
ServerName : "snitest.org" ,
} ,
}
tr3 := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
MaxVersion : tls . VersionTLS11 ,
ServerName : "snitest.org" ,
} ,
}
// With valid TLS options and request
req , err := http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-06-17 19:14:08 +03:00
req . Host = tr1 . TLSClientConfig . ServerName
req . Header . Set ( "Host" , tr1 . TLSClientConfig . ServerName )
req . Header . Set ( "Accept" , "*/*" )
err = try . RequestWithTransport ( req , 30 * time . Second , tr1 , try . HasCn ( tr1 . TLSClientConfig . ServerName ) , try . StatusCodeIs ( http . StatusNoContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-06-17 19:14:08 +03:00
// With a valid TLS version
req , err = http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-06-17 19:14:08 +03:00
req . Host = tr2 . TLSClientConfig . ServerName
req . Header . Set ( "Host" , tr2 . TLSClientConfig . ServerName )
req . Header . Set ( "Accept" , "*/*" )
err = try . RequestWithTransport ( req , 3 * time . Second , tr2 , try . HasCn ( tr2 . TLSClientConfig . ServerName ) , try . StatusCodeIs ( http . StatusResetContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-06-17 19:14:08 +03:00
// With a bad TLS version
req , err = http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-06-17 19:14:08 +03:00
req . Host = tr3 . TLSClientConfig . ServerName
req . Header . Set ( "Host" , tr3 . TLSClientConfig . ServerName )
req . Header . Set ( "Accept" , "*/*" )
client := http . Client {
Transport : tr3 ,
}
_ , err = client . Do ( req )
2024-01-09 19:00:07 +03:00
assert . Error ( s . T ( ) , err )
assert . Contains ( s . T ( ) , err . Error ( ) , "tls: no supported versions satisfy MinVersion and MaxVersion" )
2019-06-17 19:14:08 +03:00
// with unknown tls option
2019-06-21 18:18:05 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "unknown TLS options: unknown@file" ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-06-17 19:14:08 +03:00
}
2019-07-03 20:22:05 +03:00
// TestWithConflictingTLSOptions checks that routers with same SNI but different TLS options get fallbacked to the default TLS options.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithConflictingTLSOptions ( ) {
file := s . adaptFile ( "fixtures/https/https_tls_options.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
2019-07-03 20:22:05 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`snitest.net`)" ) )
require . NoError ( s . T ( ) , err )
2019-07-03 20:22:05 +03:00
2020-07-17 16:38:04 +03:00
backend1 := startTestServer ( "9010" , http . StatusNoContent , "" )
backend2 := startTestServer ( "9020" , http . StatusResetContent , "" )
2019-07-03 20:22:05 +03:00
defer backend1 . Close ( )
defer backend2 . Close ( )
err = try . GetRequest ( backend1 . URL , 1 * time . Second , try . StatusCodeIs ( http . StatusNoContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-07-03 20:22:05 +03:00
err = try . GetRequest ( backend2 . URL , 1 * time . Second , try . StatusCodeIs ( http . StatusResetContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-07-03 20:22:05 +03:00
tr4 := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
MaxVersion : tls . VersionTLS11 ,
ServerName : "snitest.net" ,
} ,
}
trDefault := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
MaxVersion : tls . VersionTLS12 ,
ServerName : "snitest.net" ,
} ,
}
// With valid TLS options and request
req , err := http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-07-03 20:22:05 +03:00
req . Host = trDefault . TLSClientConfig . ServerName
req . Header . Set ( "Host" , trDefault . TLSClientConfig . ServerName )
req . Header . Set ( "Accept" , "*/*" )
err = try . RequestWithTransport ( req , 30 * time . Second , trDefault , try . StatusCodeIs ( http . StatusNoContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-07-03 20:22:05 +03:00
// With a bad TLS version
req , err = http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-07-03 20:22:05 +03:00
req . Host = tr4 . TLSClientConfig . ServerName
req . Header . Set ( "Host" , tr4 . TLSClientConfig . ServerName )
req . Header . Set ( "Accept" , "*/*" )
client := http . Client {
Transport : tr4 ,
}
_ , err = client . Do ( req )
2024-01-09 19:00:07 +03:00
assert . ErrorContains ( s . T ( ) , err , "tls: no supported versions satisfy MinVersion and MaxVersion" )
2019-07-03 20:22:05 +03:00
// with unknown tls option
2019-07-19 12:52:04 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( fmt . Sprintf ( "found different TLS options for routers on the same host %v, so using the default TLS options instead" , tr4 . TLSClientConfig . ServerName ) ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-07-03 20:22:05 +03:00
}
2018-07-06 11:30:03 +03:00
// TestWithSNIStrictNotMatchedRequest involves a client sending a SNI hostname of
// "snitest.org", which does not match the CN of 'snitest.com.crt'. The test
// verifies that traefik closes the connection.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithSNIStrictNotMatchedRequest ( ) {
file := s . adaptFile ( "fixtures/https/https_sni_strict.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
2018-07-06 11:30:03 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.com`)" ) )
require . NoError ( s . T ( ) , err )
2018-07-06 11:30:03 +03:00
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.org" ,
NextProtos : [ ] string { "h2" , "http/1.1" } ,
}
// Connection with no matching certificate should fail
_ , err = tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2024-01-09 19:00:07 +03:00
assert . Error ( s . T ( ) , err , "failed to connect to server" )
2018-07-06 11:30:03 +03:00
}
// TestWithDefaultCertificate involves a client sending a SNI hostname of
// "snitest.org", which does not match the CN of 'snitest.com.crt'. The test
// verifies that traefik returns the default certificate.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithDefaultCertificate ( ) {
file := s . adaptFile ( "fixtures/https/https_sni_default_cert.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
2018-07-06 11:30:03 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.com`)" ) )
require . NoError ( s . T ( ) , err )
2018-07-06 11:30:03 +03:00
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.org" ,
NextProtos : [ ] string { "h2" , "http/1.1" } ,
}
conn , err := tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "failed to connect to server" )
2018-07-06 11:30:03 +03:00
defer conn . Close ( )
err = conn . Handshake ( )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "TLS handshake error" )
2018-07-06 11:30:03 +03:00
cs := conn . ConnectionState ( )
err = cs . PeerCertificates [ 0 ] . VerifyHostname ( "snitest.com" )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "server did not serve correct default certificate" )
2018-07-06 11:30:03 +03:00
proto := cs . NegotiatedProtocol
2024-01-09 19:00:07 +03:00
assert . Equal ( s . T ( ) , "h2" , proto )
2018-07-06 11:30:03 +03:00
}
// TestWithDefaultCertificateNoSNI involves a client sending a request with no ServerName
// which does not match the CN of 'snitest.com.crt'. The test
// verifies that traefik returns the default certificate.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithDefaultCertificateNoSNI ( ) {
file := s . adaptFile ( "fixtures/https/https_sni_default_cert.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
2018-07-06 11:30:03 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.com`)" ) )
require . NoError ( s . T ( ) , err )
2018-07-06 11:30:03 +03:00
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
NextProtos : [ ] string { "h2" , "http/1.1" } ,
}
conn , err := tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "failed to connect to server" )
2018-07-06 11:30:03 +03:00
defer conn . Close ( )
err = conn . Handshake ( )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "TLS handshake error" )
2018-07-06 11:30:03 +03:00
cs := conn . ConnectionState ( )
err = cs . PeerCertificates [ 0 ] . VerifyHostname ( "snitest.com" )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "server did not serve correct default certificate" )
2018-07-06 11:30:03 +03:00
proto := cs . NegotiatedProtocol
2024-01-09 19:00:07 +03:00
assert . Equal ( s . T ( ) , "h2" , proto )
2018-07-06 11:30:03 +03:00
}
// TestWithOverlappingCertificate involves a client sending a SNI hostname of
// "www.snitest.com", which matches the CN of two static certificates:
// 'wildcard.snitest.com.crt', and `www.snitest.com.crt`. The test
// verifies that traefik returns the non-wildcard certificate.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithOverlappingStaticCertificate ( ) {
file := s . adaptFile ( "fixtures/https/https_sni_default_cert.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
2018-07-06 11:30:03 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.com`)" ) )
require . NoError ( s . T ( ) , err )
2018-07-06 11:30:03 +03:00
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
ServerName : "www.snitest.com" ,
NextProtos : [ ] string { "h2" , "http/1.1" } ,
}
conn , err := tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "failed to connect to server" )
2018-07-06 11:30:03 +03:00
defer conn . Close ( )
err = conn . Handshake ( )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "TLS handshake error" )
2018-07-06 11:30:03 +03:00
cs := conn . ConnectionState ( )
err = cs . PeerCertificates [ 0 ] . VerifyHostname ( "www.snitest.com" )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "server did not serve correct default certificate" )
2018-07-06 11:30:03 +03:00
proto := cs . NegotiatedProtocol
2024-01-09 19:00:07 +03:00
assert . Equal ( s . T ( ) , "h2" , proto )
2018-07-06 11:30:03 +03:00
}
// TestWithOverlappingCertificate involves a client sending a SNI hostname of
// "www.snitest.com", which matches the CN of two dynamic certificates:
// 'wildcard.snitest.com.crt', and `www.snitest.com.crt`. The test
// verifies that traefik returns the non-wildcard certificate.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithOverlappingDynamicCertificate ( ) {
file := s . adaptFile ( "fixtures/https/dynamic_https_sni_default_cert.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
2018-07-06 11:30:03 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.com`)" ) )
require . NoError ( s . T ( ) , err )
2018-07-06 11:30:03 +03:00
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
ServerName : "www.snitest.com" ,
NextProtos : [ ] string { "h2" , "http/1.1" } ,
}
conn , err := tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "failed to connect to server" )
2018-07-06 11:30:03 +03:00
defer conn . Close ( )
err = conn . Handshake ( )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "TLS handshake error" )
2018-07-06 11:30:03 +03:00
cs := conn . ConnectionState ( )
err = cs . PeerCertificates [ 0 ] . VerifyHostname ( "www.snitest.com" )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "server did not serve correct default certificate" )
2018-07-06 11:30:03 +03:00
proto := cs . NegotiatedProtocol
2024-01-09 19:00:07 +03:00
assert . Equal ( s . T ( ) , "h2" , proto )
2018-07-06 11:30:03 +03:00
}
2016-06-18 22:11:34 +03:00
// TestWithClientCertificateAuthentication
2020-05-11 13:06:07 +03:00
// The client can send a certificate signed by a CA trusted by the server but it's optional.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithClientCertificateAuthentication ( ) {
file := s . adaptFile ( "fixtures/https/clientca/https_1ca1config.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
2016-06-18 22:11:34 +03:00
2017-05-17 16:22:44 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.org`)" ) )
require . NoError ( s . T ( ) , err )
2016-06-18 22:11:34 +03:00
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
Certificates : [ ] tls . Certificate { } ,
}
// Connection without client certificate should fail
2017-08-18 03:18:02 +03:00
_ , err = tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "should be allowed to connect to server" )
2016-06-18 22:11:34 +03:00
// Connect with client certificate signed by ca1
cert , err := tls . LoadX509KeyPair ( "fixtures/https/clientca/client1.crt" , "fixtures/https/clientca/client1.key" )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "unable to load client certificate and key" )
2016-06-18 22:11:34 +03:00
tlsConfig . Certificates = append ( tlsConfig . Certificates , cert )
2017-08-18 03:18:02 +03:00
conn , err := tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "failed to connect to server" )
2016-06-18 22:11:34 +03:00
conn . Close ( )
2017-11-10 12:30:04 +03:00
// Connect with client certificate not signed by ca1
cert , err = tls . LoadX509KeyPair ( "fixtures/https/snitest.org.cert" , "fixtures/https/snitest.org.key" )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "unable to load client certificate and key" )
2017-11-10 12:30:04 +03:00
tlsConfig . Certificates = append ( tlsConfig . Certificates , cert )
conn , err = tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "failed to connect to server" )
2017-11-10 12:30:04 +03:00
conn . Close ( )
2016-06-18 22:11:34 +03:00
// Connect with client signed by ca2 should fail
tlsConfig = & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
Certificates : [ ] tls . Certificate { } ,
}
cert , err = tls . LoadX509KeyPair ( "fixtures/https/clientca/client2.crt" , "fixtures/https/clientca/client2.key" )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "unable to load client certificate and key" )
2016-06-18 22:11:34 +03:00
tlsConfig . Certificates = append ( tlsConfig . Certificates , cert )
2017-08-18 03:18:02 +03:00
_ , err = tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "should be allowed to connect to server" )
2016-06-18 22:11:34 +03:00
}
// TestWithClientCertificateAuthentication
2020-05-11 13:06:07 +03:00
// Use two CA:s and test that clients with client signed by either of them can connect.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithClientCertificateAuthenticationMultipleCAs ( ) {
2019-09-10 18:52:04 +03:00
server1 := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , _ * http . Request ) { _ , _ = rw . Write ( [ ] byte ( "server1" ) ) } ) )
server2 := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , _ * http . Request ) { _ , _ = rw . Write ( [ ] byte ( "server2" ) ) } ) )
2019-08-26 16:06:05 +03:00
defer func ( ) {
server1 . Close ( )
server2 . Close ( )
} ( )
2024-01-09 19:00:07 +03:00
file := s . adaptFile ( "fixtures/https/clientca/https_2ca1config.toml" , struct {
2019-08-26 16:06:05 +03:00
Server1 string
Server2 string
} {
Server1 : server1 . URL ,
Server2 : server2 . URL ,
} )
2024-01-09 19:00:07 +03:00
s . traefikCmd ( withConfigFile ( file ) )
2016-06-18 22:11:34 +03:00
2017-05-17 16:22:44 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`snitest.org`)" ) )
require . NoError ( s . T ( ) , err )
2016-06-18 22:11:34 +03:00
2019-08-26 16:06:05 +03:00
req , err := http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443" , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-08-26 16:06:05 +03:00
req . Host = "snitest.com"
2016-06-18 22:11:34 +03:00
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
Certificates : [ ] tls . Certificate { } ,
}
2019-08-26 16:06:05 +03:00
client := http . Client {
Transport : & http . Transport { TLSClientConfig : tlsConfig } ,
Timeout : 1 * time . Second ,
}
2016-06-18 22:11:34 +03:00
// Connection without client certificate should fail
2019-08-26 16:06:05 +03:00
_ , err = client . Do ( req )
2024-01-09 19:00:07 +03:00
assert . Error ( s . T ( ) , err )
2016-06-18 22:11:34 +03:00
cert , err := tls . LoadX509KeyPair ( "fixtures/https/clientca/client1.crt" , "fixtures/https/clientca/client1.key" )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "unable to load client certificate and key" )
2016-06-18 22:11:34 +03:00
tlsConfig . Certificates = append ( tlsConfig . Certificates , cert )
2019-08-26 16:06:05 +03:00
// Connect with client signed by ca1
_ , err = client . Do ( req )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2016-06-18 22:11:34 +03:00
// Connect with client signed by ca2
tlsConfig = & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
Certificates : [ ] tls . Certificate { } ,
}
2019-08-26 16:06:05 +03:00
2016-06-18 22:11:34 +03:00
cert , err = tls . LoadX509KeyPair ( "fixtures/https/clientca/client2.crt" , "fixtures/https/clientca/client2.key" )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "unable to load client certificate and key" )
2016-06-18 22:11:34 +03:00
tlsConfig . Certificates = append ( tlsConfig . Certificates , cert )
2019-08-26 16:06:05 +03:00
client = http . Client {
Transport : & http . Transport { TLSClientConfig : tlsConfig } ,
Timeout : 1 * time . Second ,
}
2017-08-18 03:18:02 +03:00
2019-08-26 16:06:05 +03:00
// Connect with client signed by ca1
_ , err = client . Do ( req )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2016-06-18 22:11:34 +03:00
// Connect with client signed by ca3 should fail
tlsConfig = & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
Certificates : [ ] tls . Certificate { } ,
}
2019-08-26 16:06:05 +03:00
2016-06-18 22:11:34 +03:00
cert , err = tls . LoadX509KeyPair ( "fixtures/https/clientca/client3.crt" , "fixtures/https/clientca/client3.key" )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "unable to load client certificate and key" )
2016-06-18 22:11:34 +03:00
tlsConfig . Certificates = append ( tlsConfig . Certificates , cert )
2019-08-26 16:06:05 +03:00
client = http . Client {
Transport : & http . Transport { TLSClientConfig : tlsConfig } ,
Timeout : 1 * time . Second ,
}
// Connect with client signed by ca1
_ , err = client . Do ( req )
2024-01-09 19:00:07 +03:00
assert . Error ( s . T ( ) , err )
2016-06-18 22:11:34 +03:00
}
// TestWithClientCertificateAuthentication
2020-05-11 13:06:07 +03:00
// Use two CA:s in two different files and test that clients with client signed by either of them can connect.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithClientCertificateAuthenticationMultipleCAsMultipleFiles ( ) {
2019-09-10 18:52:04 +03:00
server1 := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , _ * http . Request ) { _ , _ = rw . Write ( [ ] byte ( "server1" ) ) } ) )
server2 := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , _ * http . Request ) { _ , _ = rw . Write ( [ ] byte ( "server2" ) ) } ) )
2019-08-26 16:06:05 +03:00
defer func ( ) {
server1 . Close ( )
server2 . Close ( )
} ( )
2024-01-09 19:00:07 +03:00
file := s . adaptFile ( "fixtures/https/clientca/https_2ca2config.toml" , struct {
2019-08-26 16:06:05 +03:00
Server1 string
Server2 string
} {
Server1 : server1 . URL ,
Server2 : server2 . URL ,
} )
2024-01-09 19:00:07 +03:00
s . traefikCmd ( withConfigFile ( file ) )
2016-06-18 22:11:34 +03:00
2017-05-17 16:22:44 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`snitest.org`)" ) )
require . NoError ( s . T ( ) , err )
2016-06-18 22:11:34 +03:00
2019-08-26 16:06:05 +03:00
req , err := http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443" , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-08-26 16:06:05 +03:00
req . Host = "snitest.com"
2016-06-18 22:11:34 +03:00
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
Certificates : [ ] tls . Certificate { } ,
}
2019-08-26 16:06:05 +03:00
client := http . Client {
Transport : & http . Transport { TLSClientConfig : tlsConfig } ,
Timeout : 1 * time . Second ,
}
2016-06-18 22:11:34 +03:00
// Connection without client certificate should fail
2019-08-26 16:06:05 +03:00
_ , err = client . Do ( req )
2024-01-09 19:00:07 +03:00
assert . Error ( s . T ( ) , err )
2016-06-18 22:11:34 +03:00
// Connect with client signed by ca1
cert , err := tls . LoadX509KeyPair ( "fixtures/https/clientca/client1.crt" , "fixtures/https/clientca/client1.key" )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "unable to load client certificate and key" )
2016-06-18 22:11:34 +03:00
tlsConfig . Certificates = append ( tlsConfig . Certificates , cert )
2019-08-26 16:06:05 +03:00
_ , err = client . Do ( req )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2016-06-18 22:11:34 +03:00
// Connect with client signed by ca2
tlsConfig = & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
Certificates : [ ] tls . Certificate { } ,
}
2019-08-26 16:06:05 +03:00
2016-06-18 22:11:34 +03:00
cert , err = tls . LoadX509KeyPair ( "fixtures/https/clientca/client2.crt" , "fixtures/https/clientca/client2.key" )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "unable to load client certificate and key" )
2016-06-18 22:11:34 +03:00
tlsConfig . Certificates = append ( tlsConfig . Certificates , cert )
2019-08-26 16:06:05 +03:00
client = http . Client {
Transport : & http . Transport { TLSClientConfig : tlsConfig } ,
Timeout : 1 * time . Second ,
}
_ , err = client . Do ( req )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2016-06-18 22:11:34 +03:00
// Connect with client signed by ca3 should fail
tlsConfig = & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
Certificates : [ ] tls . Certificate { } ,
}
2019-08-26 16:06:05 +03:00
2016-06-18 22:11:34 +03:00
cert , err = tls . LoadX509KeyPair ( "fixtures/https/clientca/client3.crt" , "fixtures/https/clientca/client3.key" )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "unable to load client certificate and key" )
2016-06-18 22:11:34 +03:00
tlsConfig . Certificates = append ( tlsConfig . Certificates , cert )
2019-08-26 16:06:05 +03:00
client = http . Client {
Transport : & http . Transport { TLSClientConfig : tlsConfig } ,
Timeout : 1 * time . Second ,
}
_ , err = client . Do ( req )
2024-01-09 19:00:07 +03:00
assert . Error ( s . T ( ) , err )
2016-06-18 22:11:34 +03:00
}
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithRootCAsContentForHTTPSOnBackend ( ) {
2017-06-23 16:15:07 +03:00
backend := httptest . NewTLSServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2017-11-20 11:40:03 +03:00
w . WriteHeader ( http . StatusOK )
2017-06-23 16:15:07 +03:00
} ) )
defer backend . Close ( )
2024-01-09 19:00:07 +03:00
file := s . adaptFile ( "fixtures/https/rootcas/https.toml" , struct { BackendHost string } { backend . URL } )
s . traefikCmd ( withConfigFile ( file ) )
2017-06-23 16:15:07 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( backend . URL ) )
require . NoError ( s . T ( ) , err )
2017-06-23 16:15:07 +03:00
2017-11-09 14:16:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8081/ping" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-06-23 16:15:07 +03:00
}
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithRootCAsFileForHTTPSOnBackend ( ) {
2017-06-23 16:15:07 +03:00
backend := httptest . NewTLSServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2017-11-20 11:40:03 +03:00
w . WriteHeader ( http . StatusOK )
2017-06-23 16:15:07 +03:00
} ) )
defer backend . Close ( )
2024-01-09 19:00:07 +03:00
file := s . adaptFile ( "fixtures/https/rootcas/https_with_file.toml" , struct { BackendHost string } { backend . URL } )
s . traefikCmd ( withConfigFile ( file ) )
2017-06-23 16:15:07 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( backend . URL ) )
require . NoError ( s . T ( ) , err )
2017-06-23 16:15:07 +03:00
2017-11-09 14:16:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8081/ping" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-06-23 16:15:07 +03:00
}
2020-07-17 16:38:04 +03:00
func startTestServer ( port string , statusCode int , textContent string ) ( ts * httptest . Server ) {
2015-11-21 04:59:49 +03:00
handler := http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . WriteHeader ( statusCode )
2020-07-17 16:38:04 +03:00
if textContent != "" {
_ , _ = w . Write ( [ ] byte ( textContent ) )
}
2015-11-21 04:59:49 +03:00
} )
listener , err := net . Listen ( "tcp" , "127.0.0.1:" + port )
if err != nil {
panic ( err )
}
ts = & httptest . Server {
Listener : listener ,
Config : & http . Server { Handler : handler } ,
}
ts . Start ( )
2017-05-17 16:22:44 +03:00
return ts
2015-11-21 04:59:49 +03:00
}
2017-11-09 14:16:03 +03:00
2017-12-21 16:16:03 +03:00
// TestWithSNIDynamicConfigRouteWithNoChange involves a client sending HTTPS requests with
2017-11-09 14:16:03 +03:00
// SNI hostnames of "snitest.org" and "snitest.com". The test verifies
// that traefik routes the requests to the expected backends thanks to given certificate if possible
// otherwise thanks to the default one.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithSNIDynamicConfigRouteWithNoChange ( ) {
dynamicConfFileName := s . adaptFile ( "fixtures/https/dynamic_https.toml" , struct { } { } )
confFileName := s . adaptFile ( "fixtures/https/dynamic_https_sni.toml" , struct {
2017-11-09 14:16:03 +03:00
DynamicConfFileName string
} {
DynamicConfFileName : dynamicConfFileName ,
} )
2024-01-09 19:00:07 +03:00
s . traefikCmd ( withConfigFile ( confFileName ) )
2017-11-09 14:16:03 +03:00
tr1 := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.org" ,
} ,
}
tr2 := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
} ,
}
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`" + tr1 . TLSClientConfig . ServerName + "`)" ) )
require . NoError ( s . T ( ) , err )
2017-11-09 14:16:03 +03:00
2020-07-17 16:38:04 +03:00
backend1 := startTestServer ( "9010" , http . StatusNoContent , "" )
backend2 := startTestServer ( "9020" , http . StatusResetContent , "" )
2017-11-09 14:16:03 +03:00
defer backend1 . Close ( )
defer backend2 . Close ( )
err = try . GetRequest ( backend1 . URL , 500 * time . Millisecond , try . StatusCodeIs ( http . StatusNoContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-11-09 14:16:03 +03:00
err = try . GetRequest ( backend2 . URL , 500 * time . Millisecond , try . StatusCodeIs ( http . StatusResetContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-11-09 14:16:03 +03:00
req , err := http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-11-09 14:16:03 +03:00
req . Host = tr1 . TLSClientConfig . ServerName
req . Header . Set ( "Host" , tr1 . TLSClientConfig . ServerName )
req . Header . Set ( "Accept" , "*/*" )
2018-07-31 11:48:03 +03:00
// snitest.org certificate must be used yet && Expected a 204 (from backend1)
err = try . RequestWithTransport ( req , 30 * time . Second , tr1 , try . HasCn ( tr1 . TLSClientConfig . ServerName ) , try . StatusCodeIs ( http . StatusResetContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-11-09 14:16:03 +03:00
2018-07-31 11:48:03 +03:00
req , err = http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-11-09 14:16:03 +03:00
req . Host = tr2 . TLSClientConfig . ServerName
req . Header . Set ( "Host" , tr2 . TLSClientConfig . ServerName )
2018-07-31 11:48:03 +03:00
req . Header . Set ( "Accept" , "*/*" )
// snitest.com certificate does not exist, default certificate has to be used && Expected a 205 (from backend2)
err = try . RequestWithTransport ( req , 30 * time . Second , tr2 , try . HasCn ( "TRAEFIK DEFAULT CERT" ) , try . StatusCodeIs ( http . StatusNoContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-11-09 14:16:03 +03:00
}
2017-12-21 16:16:03 +03:00
// TestWithSNIDynamicConfigRouteWithChange involves a client sending HTTPS requests with
2017-11-09 14:16:03 +03:00
// SNI hostnames of "snitest.org" and "snitest.com". The test verifies
// that traefik updates its configuration when the HTTPS configuration is modified and
// it routes the requests to the expected backends thanks to given certificate if possible
// otherwise thanks to the default one.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithSNIDynamicConfigRouteWithChange ( ) {
dynamicConfFileName := s . adaptFile ( "fixtures/https/dynamic_https.toml" , struct { } { } )
confFileName := s . adaptFile ( "fixtures/https/dynamic_https_sni.toml" , struct {
2017-11-09 14:16:03 +03:00
DynamicConfFileName string
} {
DynamicConfFileName : dynamicConfFileName ,
} )
2024-01-09 19:00:07 +03:00
s . traefikCmd ( withConfigFile ( confFileName ) )
2017-11-09 14:16:03 +03:00
tr1 := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
} ,
}
tr2 := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.org" ,
} ,
}
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`" + tr2 . TLSClientConfig . ServerName + "`)" ) )
require . NoError ( s . T ( ) , err )
2017-11-09 14:16:03 +03:00
2020-07-17 16:38:04 +03:00
backend1 := startTestServer ( "9010" , http . StatusNoContent , "" )
backend2 := startTestServer ( "9020" , http . StatusResetContent , "" )
2017-11-09 14:16:03 +03:00
defer backend1 . Close ( )
defer backend2 . Close ( )
err = try . GetRequest ( backend1 . URL , 500 * time . Millisecond , try . StatusCodeIs ( http . StatusNoContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-11-09 14:16:03 +03:00
err = try . GetRequest ( backend2 . URL , 500 * time . Millisecond , try . StatusCodeIs ( http . StatusResetContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-11-09 14:16:03 +03:00
2018-07-31 11:48:03 +03:00
// Change certificates configuration file content
2024-01-09 19:00:07 +03:00
s . modifyCertificateConfFileContent ( tr1 . TLSClientConfig . ServerName , dynamicConfFileName )
2018-07-31 11:48:03 +03:00
2017-11-09 14:16:03 +03:00
req , err := http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-11-09 14:16:03 +03:00
req . Host = tr1 . TLSClientConfig . ServerName
req . Header . Set ( "Host" , tr1 . TLSClientConfig . ServerName )
req . Header . Set ( "Accept" , "*/*" )
2018-07-31 11:48:03 +03:00
err = try . RequestWithTransport ( req , 30 * time . Second , tr1 , try . HasCn ( tr1 . TLSClientConfig . ServerName ) , try . StatusCodeIs ( http . StatusNotFound ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-11-09 14:16:03 +03:00
2018-07-31 11:48:03 +03:00
req , err = http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-11-09 14:16:03 +03:00
req . Host = tr2 . TLSClientConfig . ServerName
req . Header . Set ( "Host" , tr2 . TLSClientConfig . ServerName )
2018-07-31 11:48:03 +03:00
req . Header . Set ( "Accept" , "*/*" )
2017-11-09 14:16:03 +03:00
2018-07-31 11:48:03 +03:00
err = try . RequestWithTransport ( req , 30 * time . Second , tr2 , try . HasCn ( "TRAEFIK DEFAULT CERT" ) , try . StatusCodeIs ( http . StatusNotFound ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-11-09 14:16:03 +03:00
}
2018-01-24 13:57:06 +03:00
// TestWithSNIDynamicConfigRouteWithTlsConfigurationDeletion involves a client sending HTTPS requests with
2017-12-21 16:16:03 +03:00
// SNI hostnames of "snitest.org" and "snitest.com". The test verifies
// that traefik updates its configuration when the HTTPS configuration is modified, even if it totally deleted, and
// it routes the requests to the expected backends thanks to given certificate if possible
// otherwise thanks to the default one.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithSNIDynamicConfigRouteWithTlsConfigurationDeletion ( ) {
dynamicConfFileName := s . adaptFile ( "fixtures/https/dynamic_https.toml" , struct { } { } )
confFileName := s . adaptFile ( "fixtures/https/dynamic_https_sni.toml" , struct {
2017-12-21 16:16:03 +03:00
DynamicConfFileName string
} {
DynamicConfFileName : dynamicConfFileName ,
} )
2024-01-09 19:00:07 +03:00
s . traefikCmd ( withConfigFile ( confFileName ) )
2017-12-21 16:16:03 +03:00
tr2 := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.org" ,
} ,
}
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`" + tr2 . TLSClientConfig . ServerName + "`)" ) )
require . NoError ( s . T ( ) , err )
2017-12-21 16:16:03 +03:00
2020-07-17 16:38:04 +03:00
backend2 := startTestServer ( "9020" , http . StatusResetContent , "" )
2017-12-21 16:16:03 +03:00
defer backend2 . Close ( )
err = try . GetRequest ( backend2 . URL , 500 * time . Millisecond , try . StatusCodeIs ( http . StatusResetContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-12-21 16:16:03 +03:00
req , err := http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-12-21 16:16:03 +03:00
req . Host = tr2 . TLSClientConfig . ServerName
req . Header . Set ( "Host" , tr2 . TLSClientConfig . ServerName )
req . Header . Set ( "Accept" , "*/*" )
2018-07-31 11:48:03 +03:00
err = try . RequestWithTransport ( req , 30 * time . Second , tr2 , try . HasCn ( tr2 . TLSClientConfig . ServerName ) , try . StatusCodeIs ( http . StatusResetContent ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2018-07-31 11:48:03 +03:00
2017-12-21 16:16:03 +03:00
// Change certificates configuration file content
2024-01-09 19:00:07 +03:00
s . modifyCertificateConfFileContent ( "" , dynamicConfFileName )
2017-12-21 16:16:03 +03:00
2018-07-31 11:48:03 +03:00
err = try . RequestWithTransport ( req , 30 * time . Second , tr2 , try . HasCn ( "TRAEFIK DEFAULT CERT" ) , try . StatusCodeIs ( http . StatusNotFound ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-12-21 16:16:03 +03:00
}
2017-11-09 14:16:03 +03:00
// modifyCertificateConfFileContent replaces the content of a HTTPS configuration file.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) modifyCertificateConfFileContent ( certFileName , confFileName string ) {
2018-09-18 09:22:03 +03:00
file , err := os . OpenFile ( "./" + confFileName , os . O_WRONLY , os . ModeExclusive )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-11-09 14:16:03 +03:00
defer func ( ) {
2018-09-18 09:22:03 +03:00
file . Close ( )
2017-11-09 14:16:03 +03:00
} ( )
2018-09-18 09:22:03 +03:00
err = file . Truncate ( 0 )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2018-09-18 09:22:03 +03:00
2017-12-21 23:24:03 +03:00
// If certificate file is not provided, just truncate the configuration file
if len ( certFileName ) > 0 {
2019-07-10 10:26:04 +03:00
tlsConf := dynamic . Configuration {
TLS : & dynamic . TLSConfiguration {
2020-07-07 15:42:03 +03:00
Certificates : [ ] * traefiktls . CertAndStores {
{
Certificate : traefiktls . Certificate {
2024-01-11 19:06:06 +03:00
CertFile : types . FileOrContent ( "fixtures/https/" + certFileName + ".cert" ) ,
KeyFile : types . FileOrContent ( "fixtures/https/" + certFileName + ".key" ) ,
2020-07-07 15:42:03 +03:00
} ,
} ,
2017-12-21 23:24:03 +03:00
} ,
2019-06-28 00:58:03 +03:00
} ,
2017-12-21 23:24:03 +03:00
}
2018-09-18 09:22:03 +03:00
2017-12-21 23:24:03 +03:00
var confBuffer bytes . Buffer
2018-09-18 09:22:03 +03:00
err := toml . NewEncoder ( & confBuffer ) . Encode ( tlsConf )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-12-21 23:24:03 +03:00
2018-09-18 09:22:03 +03:00
_ , err = file . Write ( confBuffer . Bytes ( ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2017-12-21 23:24:03 +03:00
}
2017-11-09 14:16:03 +03:00
}
2018-07-31 12:28:03 +03:00
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestEntryPointHttpsRedirectAndPathModification ( ) {
file := s . adaptFile ( "fixtures/https/https_redirect.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
2018-07-31 12:28:03 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 5 * time . Second , try . BodyContains ( "Host(`example.com`)" ) )
require . NoError ( s . T ( ) , err )
2018-07-31 12:28:03 +03:00
client := & http . Client {
CheckRedirect : func ( req * http . Request , via [ ] * http . Request ) error {
return http . ErrUseLastResponse
} ,
}
testCases := [ ] struct {
2018-08-14 19:38:04 +03:00
desc string
hosts [ ] string
path string
2018-07-31 12:28:03 +03:00
} {
{
2018-08-14 19:38:04 +03:00
desc : "Stripped URL redirect" ,
hosts : [ ] string { "example.com" , "foo.com" , "bar.com" } ,
path : "/api" ,
2018-07-31 12:28:03 +03:00
} ,
{
2018-08-14 19:38:04 +03:00
desc : "Stripped URL with trailing slash redirect" ,
hosts : [ ] string { "example.com" , "example2.com" , "foo.com" , "foo2.com" , "bar.com" , "bar2.com" } ,
path : "/api/" ,
2018-07-31 12:28:03 +03:00
} ,
{
2018-08-14 19:38:04 +03:00
desc : "Stripped URL with double trailing slash redirect" ,
hosts : [ ] string { "example.com" , "example2.com" , "foo.com" , "foo2.com" , "bar.com" , "bar2.com" } ,
path : "/api//" ,
2018-07-31 12:28:03 +03:00
} ,
{
2018-08-14 19:38:04 +03:00
desc : "Stripped URL with path redirect" ,
hosts : [ ] string { "example.com" , "example2.com" , "foo.com" , "foo2.com" , "bar.com" , "bar2.com" } ,
path : "/api/bacon" ,
2018-07-31 12:28:03 +03:00
} ,
{
2018-08-14 19:38:04 +03:00
desc : "Stripped URL with path and trailing slash redirect" ,
hosts : [ ] string { "example.com" , "example2.com" , "foo.com" , "foo2.com" , "bar.com" , "bar2.com" } ,
path : "/api/bacon/" ,
2018-07-31 12:28:03 +03:00
} ,
{
2018-08-14 19:38:04 +03:00
desc : "Stripped URL with path and double trailing slash redirect" ,
hosts : [ ] string { "example.com" , "example2.com" , "foo.com" , "foo2.com" , "bar.com" , "bar2.com" } ,
path : "/api/bacon//" ,
2018-07-31 12:28:03 +03:00
} ,
{
2018-08-14 19:38:04 +03:00
desc : "Root Path with redirect" ,
hosts : [ ] string { "test.com" , "test2.com" , "pow.com" , "pow2.com" } ,
path : "/" ,
2018-07-31 12:28:03 +03:00
} ,
{
2018-08-14 19:38:04 +03:00
desc : "Root Path with double trailing slash redirect" ,
hosts : [ ] string { "test.com" , "test2.com" , "pow.com" , "pow2.com" } ,
path : "//" ,
2018-07-31 12:28:03 +03:00
} ,
{
2018-08-14 19:38:04 +03:00
desc : "Path modify with redirect" ,
hosts : [ ] string { "test.com" , "test2.com" , "pow.com" , "pow2.com" } ,
path : "/wtf" ,
2018-07-31 12:28:03 +03:00
} ,
{
2018-08-14 19:38:04 +03:00
desc : "Path modify with trailing slash redirect" ,
hosts : [ ] string { "test.com" , "test2.com" , "pow.com" , "pow2.com" } ,
path : "/wtf/" ,
2018-07-31 12:28:03 +03:00
} ,
{
2018-08-14 19:38:04 +03:00
desc : "Path modify with matching path segment redirect" ,
hosts : [ ] string { "test.com" , "test2.com" , "pow.com" , "pow2.com" } ,
path : "/wtf/foo" ,
2018-07-31 12:28:03 +03:00
} ,
}
for _ , test := range testCases {
2018-08-14 19:38:04 +03:00
sourceURL := fmt . Sprintf ( "http://127.0.0.1:8888%s" , test . path )
for _ , host := range test . hosts {
2021-11-25 13:10:06 +03:00
req , err := http . NewRequest ( http . MethodGet , sourceURL , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2018-08-14 19:38:04 +03:00
req . Host = host
2018-07-31 12:28:03 +03:00
2018-08-14 19:38:04 +03:00
resp , err := client . Do ( req )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2021-11-25 13:10:06 +03:00
resp . Body . Close ( )
2018-07-31 12:28:03 +03:00
2018-08-14 19:38:04 +03:00
location := resp . Header . Get ( "Location" )
2022-05-17 16:48:08 +03:00
expected := "https://" + net . JoinHostPort ( host , "8443" ) + test . path
2018-07-31 12:28:03 +03:00
2024-01-09 19:00:07 +03:00
assert . Equal ( s . T ( ) , expected , location )
2018-08-14 19:38:04 +03:00
}
2018-07-31 12:28:03 +03:00
}
}
2018-11-26 12:38:03 +03:00
// TestWithSNIDynamicCaseInsensitive involves a client sending a SNI hostname of
// "bar.www.snitest.com", which matches the DNS SAN of '*.WWW.SNITEST.COM'. The test
// verifies that traefik presents the correct certificate.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithSNIDynamicCaseInsensitive ( ) {
file := s . adaptFile ( "fixtures/https/https_sni_case_insensitive_dynamic.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
2018-11-26 12:38:03 +03:00
// wait for Traefik
2024-01-10 12:47:44 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "HostRegexp(`[a-z1-9-]+\\\\.www\\\\.snitest\\\\.com`)" ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2018-11-26 12:38:03 +03:00
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
ServerName : "bar.www.snitest.com" ,
NextProtos : [ ] string { "h2" , "http/1.1" } ,
}
conn , err := tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "failed to connect to server" )
2018-11-26 12:38:03 +03:00
defer conn . Close ( )
err = conn . Handshake ( )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "TLS handshake error" )
2018-11-26 12:38:03 +03:00
cs := conn . ConnectionState ( )
err = cs . PeerCertificates [ 0 ] . VerifyHostname ( "*.WWW.SNITEST.COM" )
2024-01-09 19:00:07 +03:00
assert . NoError ( s . T ( ) , err , "certificate did not match SNI servername" )
2018-11-26 12:38:03 +03:00
proto := conn . ConnectionState ( ) . NegotiatedProtocol
2024-01-09 19:00:07 +03:00
assert . Equal ( s . T ( ) , "h2" , proto )
2018-11-26 12:38:03 +03:00
}
2020-07-17 16:38:04 +03:00
// TestWithDomainFronting verify the domain fronting behavior
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithDomainFronting ( ) {
2020-07-17 16:38:04 +03:00
backend := startTestServer ( "9010" , http . StatusOK , "server1" )
defer backend . Close ( )
backend2 := startTestServer ( "9020" , http . StatusOK , "server2" )
defer backend2 . Close ( )
backend3 := startTestServer ( "9030" , http . StatusOK , "server3" )
defer backend3 . Close ( )
2024-01-09 19:00:07 +03:00
file := s . adaptFile ( "fixtures/https/https_domain_fronting.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
2020-07-17 16:38:04 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`site1.www.snitest.com`)" ) )
require . NoError ( s . T ( ) , err )
2020-07-17 16:38:04 +03:00
testCases := [ ] struct {
desc string
hostHeader string
serverName string
2023-07-18 19:50:05 +03:00
expectedError bool
2020-07-17 16:38:04 +03:00
expectedContent string
expectedStatusCode int
} {
{
desc : "SimpleCase" ,
hostHeader : "site1.www.snitest.com" ,
serverName : "site1.www.snitest.com" ,
expectedContent : "server1" ,
expectedStatusCode : http . StatusOK ,
} ,
2020-07-20 19:32:03 +03:00
{
desc : "Simple case with port in the Host Header" ,
hostHeader : "site3.www.snitest.com:4443" ,
serverName : "site3.www.snitest.com" ,
expectedContent : "server3" ,
expectedStatusCode : http . StatusOK ,
} ,
{
desc : "Spaces after the host header" ,
hostHeader : "site3.www.snitest.com " ,
serverName : "site3.www.snitest.com" ,
2023-07-18 19:50:05 +03:00
expectedError : true ,
2020-07-20 19:32:03 +03:00
expectedContent : "server3" ,
expectedStatusCode : http . StatusOK ,
} ,
{
desc : "Spaces after the servername" ,
hostHeader : "site3.www.snitest.com" ,
serverName : "site3.www.snitest.com " ,
expectedContent : "server3" ,
expectedStatusCode : http . StatusOK ,
} ,
{
desc : "Spaces after the servername and host header" ,
hostHeader : "site3.www.snitest.com " ,
serverName : "site3.www.snitest.com " ,
2023-07-18 19:50:05 +03:00
expectedError : true ,
2020-07-20 19:32:03 +03:00
expectedContent : "server3" ,
expectedStatusCode : http . StatusOK ,
} ,
2020-07-17 16:38:04 +03:00
{
desc : "Domain Fronting with same tlsOptions should follow header" ,
hostHeader : "site1.www.snitest.com" ,
serverName : "site2.www.snitest.com" ,
expectedContent : "server1" ,
expectedStatusCode : http . StatusOK ,
} ,
{
desc : "Domain Fronting with same tlsOptions should follow header (2)" ,
hostHeader : "site2.www.snitest.com" ,
serverName : "site1.www.snitest.com" ,
expectedContent : "server2" ,
expectedStatusCode : http . StatusOK ,
} ,
{
desc : "Domain Fronting with different tlsOptions should produce a 421" ,
hostHeader : "site2.www.snitest.com" ,
serverName : "site3.www.snitest.com" ,
expectedContent : "" ,
expectedStatusCode : http . StatusMisdirectedRequest ,
} ,
{
desc : "Domain Fronting with different tlsOptions should produce a 421 (2)" ,
hostHeader : "site3.www.snitest.com" ,
serverName : "site1.www.snitest.com" ,
expectedContent : "" ,
expectedStatusCode : http . StatusMisdirectedRequest ,
} ,
{
desc : "Case insensitive" ,
hostHeader : "sIte1.www.snitest.com" ,
serverName : "sitE1.www.snitest.com" ,
expectedContent : "server1" ,
expectedStatusCode : http . StatusOK ,
} ,
}
for _ , test := range testCases {
req , err := http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443" , nil )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2020-07-17 16:38:04 +03:00
req . Host = test . hostHeader
2020-07-20 19:32:03 +03:00
2020-07-17 16:38:04 +03:00
err = try . RequestWithTransport ( req , 500 * time . Millisecond , & http . Transport { TLSClientConfig : & tls . Config { InsecureSkipVerify : true , ServerName : test . serverName } } , try . StatusCodeIs ( test . expectedStatusCode ) , try . BodyContains ( test . expectedContent ) )
2023-07-18 19:50:05 +03:00
if test . expectedError {
2024-01-09 19:00:07 +03:00
assert . Error ( s . T ( ) , err )
2023-07-18 19:50:05 +03:00
} else {
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2023-07-18 19:50:05 +03:00
}
2020-07-17 16:38:04 +03:00
}
}
2022-12-06 20:28:05 +03:00
// TestWithInvalidTLSOption verifies the behavior when using an invalid tlsOption configuration.
2024-01-09 19:00:07 +03:00
func ( s * HTTPSSuite ) TestWithInvalidTLSOption ( ) {
2022-12-06 20:28:05 +03:00
backend := startTestServer ( "9010" , http . StatusOK , "server1" )
defer backend . Close ( )
2024-01-09 19:00:07 +03:00
file := s . adaptFile ( "fixtures/https/https_invalid_tls_options.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
2022-12-06 20:28:05 +03:00
// wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.com`)" ) )
require . NoError ( s . T ( ) , err )
2022-12-06 20:28:05 +03:00
testCases := [ ] struct {
desc string
serverName string
} {
{
desc : "With invalid TLS Options specified" ,
serverName : "snitest.com" ,
} ,
{
desc : "With invalid Default TLS Options" ,
serverName : "snitest.org" ,
} ,
{
desc : "With TLS Options without servername (fallback to default)" ,
} ,
}
for _ , test := range testCases {
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
}
if test . serverName != "" {
tlsConfig . ServerName = test . serverName
}
conn , err := tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2024-01-09 19:00:07 +03:00
assert . Error ( s . T ( ) , err , "connected to server successfully" )
assert . Nil ( s . T ( ) , conn )
2022-12-06 20:28:05 +03:00
}
}