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"
2015-11-21 04:59:49 +03:00
"time"
2017-11-09 14:16:03 +03:00
"github.com/BurntSushi/toml"
2016-04-02 13:40:21 +03:00
"github.com/go-check/check"
2020-09-16 16:46:04 +03:00
"github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/config/dynamic"
traefiktls "github.com/traefik/traefik/v2/pkg/tls"
2015-11-21 04:59:49 +03:00
checker "github.com/vdemeester/shakers"
)
2020-05-11 13:06:07 +03:00
// HTTPSSuite tests suite.
2015-11-21 04:59:49 +03:00
type HTTPSSuite struct { BaseSuite }
// 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.
func ( s * HTTPSSuite ) TestWithSNIConfigHandshake ( c * check . C ) {
2019-07-15 11:22:03 +03:00
file := s . adaptFile ( c , "fixtures/https/https_sni.toml" , struct { } { } )
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
2017-09-13 11:34:04 +03:00
defer display ( c )
2015-11-21 04:59:49 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2015-11-21 04:59:49 +03:00
2017-05-17 16:22:44 +03:00
// wait for Traefik
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.org`)" ) )
2017-05-17 16:22:44 +03:00
c . Assert ( err , checker . IsNil )
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 )
2015-11-21 04:59:49 +03:00
c . Assert ( err , checker . IsNil , check . Commentf ( "failed to connect to server" ) )
defer conn . Close ( )
err = conn . Handshake ( )
c . Assert ( err , checker . IsNil , check . Commentf ( "TLS handshake error" ) )
cs := conn . ConnectionState ( )
err = cs . PeerCertificates [ 0 ] . VerifyHostname ( "snitest.com" )
c . Assert ( err , checker . IsNil , check . Commentf ( "certificate did not match SNI servername" ) )
2016-11-09 20:43:59 +03:00
proto := conn . ConnectionState ( ) . NegotiatedProtocol
c . Assert ( proto , checker . Equals , "h2" )
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.
func ( s * HTTPSSuite ) TestWithSNIConfigRoute ( c * check . C ) {
2019-07-15 11:22:03 +03:00
file := s . adaptFile ( c , "fixtures/https/https_sni.toml" , struct { } { } )
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
2017-09-13 11:34:04 +03:00
defer display ( c )
2015-11-21 04:59:49 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2015-11-21 04:59:49 +03:00
2017-05-17 16:22:44 +03:00
// wait for Traefik
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`snitest.org`)" ) )
2017-05-17 16:22:44 +03:00
c . Assert ( err , checker . IsNil )
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 ) )
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( backend2 . URL , 1 * time . Second , try . StatusCodeIs ( http . StatusResetContent ) )
c . Assert ( err , checker . IsNil )
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 )
c . Assert ( err , checker . IsNil )
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 ) )
2015-11-21 04:59:49 +03:00
c . Assert ( err , checker . IsNil )
2017-05-17 16:22:44 +03:00
req , err = http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
c . Assert ( err , checker . IsNil )
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 ) )
2015-11-21 04:59:49 +03:00
c . Assert ( err , checker . IsNil )
}
2019-06-17 19:14:08 +03:00
// TestWithTLSOptions verifies that traefik routes the requests with the associated tls options.
func ( s * HTTPSSuite ) TestWithTLSOptions ( c * check . C ) {
2019-07-15 11:22:03 +03:00
file := s . adaptFile ( c , "fixtures/https/https_tls_options.toml" , struct { } { } )
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
2019-06-17 19:14:08 +03:00
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2019-06-17 19:14:08 +03:00
// wait for Traefik
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`snitest.org`)" ) )
c . Assert ( err , checker . IsNil )
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 ) )
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( backend2 . URL , 1 * time . Second , try . StatusCodeIs ( http . StatusResetContent ) )
c . Assert ( err , checker . IsNil )
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 )
c . Assert ( err , checker . IsNil )
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 ) )
c . Assert ( err , checker . IsNil )
// With a valid TLS version
req , err = http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
c . Assert ( err , checker . IsNil )
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 ) )
c . Assert ( err , checker . IsNil )
// With a bad TLS version
req , err = http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
c . Assert ( err , checker . IsNil )
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 )
c . Assert ( err , checker . NotNil )
2022-08-09 18:36:08 +03:00
c . Assert ( err . Error ( ) , checker . Contains , "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" ) )
2019-06-17 19:14:08 +03:00
c . Assert ( err , checker . IsNil )
}
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.
func ( s * HTTPSSuite ) TestWithConflictingTLSOptions ( c * check . C ) {
2019-07-15 11:22:03 +03:00
file := s . adaptFile ( c , "fixtures/https/https_tls_options.toml" , struct { } { } )
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
2019-07-03 20:22:05 +03:00
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2019-07-03 20:22:05 +03:00
// wait for Traefik
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`snitest.net`)" ) )
c . Assert ( err , checker . IsNil )
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 ) )
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( backend2 . URL , 1 * time . Second , try . StatusCodeIs ( http . StatusResetContent ) )
c . Assert ( err , checker . IsNil )
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 )
c . Assert ( err , checker . IsNil )
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 ) )
c . Assert ( err , checker . IsNil )
// With a bad TLS version
req , err = http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
c . Assert ( err , checker . IsNil )
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 )
c . Assert ( err , checker . NotNil )
2022-08-09 18:36:08 +03:00
c . Assert ( err . Error ( ) , checker . Contains , "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 ) ) )
2019-07-03 20:22:05 +03:00
c . Assert ( err , checker . IsNil )
}
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.
func ( s * HTTPSSuite ) TestWithSNIStrictNotMatchedRequest ( c * check . C ) {
2019-07-15 11:22:03 +03:00
file := s . adaptFile ( c , "fixtures/https/https_sni_strict.toml" , struct { } { } )
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
2018-07-06 11:30:03 +03:00
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2018-07-06 11:30:03 +03:00
// wait for Traefik
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.com`)" ) )
2018-07-06 11:30:03 +03:00
c . Assert ( err , checker . IsNil )
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 )
c . Assert ( err , checker . NotNil , check . Commentf ( "failed to connect to server" ) )
}
// 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.
func ( s * HTTPSSuite ) TestWithDefaultCertificate ( c * check . C ) {
2019-07-15 11:22:03 +03:00
file := s . adaptFile ( c , "fixtures/https/https_sni_default_cert.toml" , struct { } { } )
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
2018-07-06 11:30:03 +03:00
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2018-07-06 11:30:03 +03:00
// wait for Traefik
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.com`)" ) )
2018-07-06 11:30:03 +03:00
c . Assert ( err , checker . IsNil )
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 )
c . Assert ( err , checker . IsNil , check . Commentf ( "failed to connect to server" ) )
defer conn . Close ( )
err = conn . Handshake ( )
c . Assert ( err , checker . IsNil , check . Commentf ( "TLS handshake error" ) )
cs := conn . ConnectionState ( )
err = cs . PeerCertificates [ 0 ] . VerifyHostname ( "snitest.com" )
2022-09-13 21:34:08 +03:00
c . Assert ( err , checker . IsNil , check . Commentf ( "server did not serve correct default certificate" ) )
2018-07-06 11:30:03 +03:00
proto := cs . NegotiatedProtocol
c . Assert ( proto , checker . Equals , "h2" )
}
// 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.
func ( s * HTTPSSuite ) TestWithDefaultCertificateNoSNI ( c * check . C ) {
2019-07-15 11:22:03 +03:00
file := s . adaptFile ( c , "fixtures/https/https_sni_default_cert.toml" , struct { } { } )
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
2018-07-06 11:30:03 +03:00
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2018-07-06 11:30:03 +03:00
// wait for Traefik
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.com`)" ) )
2018-07-06 11:30:03 +03:00
c . Assert ( err , checker . IsNil )
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
NextProtos : [ ] string { "h2" , "http/1.1" } ,
}
conn , err := tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
c . Assert ( err , checker . IsNil , check . Commentf ( "failed to connect to server" ) )
defer conn . Close ( )
err = conn . Handshake ( )
c . Assert ( err , checker . IsNil , check . Commentf ( "TLS handshake error" ) )
cs := conn . ConnectionState ( )
err = cs . PeerCertificates [ 0 ] . VerifyHostname ( "snitest.com" )
2022-09-13 21:34:08 +03:00
c . Assert ( err , checker . IsNil , check . Commentf ( "server did not serve correct default certificate" ) )
2018-07-06 11:30:03 +03:00
proto := cs . NegotiatedProtocol
c . Assert ( proto , checker . Equals , "h2" )
}
// 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.
func ( s * HTTPSSuite ) TestWithOverlappingStaticCertificate ( c * check . C ) {
2019-07-15 11:22:03 +03:00
file := s . adaptFile ( c , "fixtures/https/https_sni_default_cert.toml" , struct { } { } )
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
2018-07-06 11:30:03 +03:00
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2018-07-06 11:30:03 +03:00
// wait for Traefik
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.com`)" ) )
2018-07-06 11:30:03 +03:00
c . Assert ( err , checker . IsNil )
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 )
c . Assert ( err , checker . IsNil , check . Commentf ( "failed to connect to server" ) )
defer conn . Close ( )
err = conn . Handshake ( )
c . Assert ( err , checker . IsNil , check . Commentf ( "TLS handshake error" ) )
cs := conn . ConnectionState ( )
err = cs . PeerCertificates [ 0 ] . VerifyHostname ( "www.snitest.com" )
2022-09-13 21:34:08 +03:00
c . Assert ( err , checker . IsNil , check . Commentf ( "server did not serve correct default certificate" ) )
2018-07-06 11:30:03 +03:00
proto := cs . NegotiatedProtocol
c . Assert ( proto , checker . Equals , "h2" )
}
// 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.
func ( s * HTTPSSuite ) TestWithOverlappingDynamicCertificate ( c * check . C ) {
2019-07-15 11:22:03 +03:00
file := s . adaptFile ( c , "fixtures/https/dynamic_https_sni_default_cert.toml" , struct { } { } )
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
2018-07-06 11:30:03 +03:00
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2018-07-06 11:30:03 +03:00
// wait for Traefik
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.com`)" ) )
2018-07-06 11:30:03 +03:00
c . Assert ( err , checker . IsNil )
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 )
c . Assert ( err , checker . IsNil , check . Commentf ( "failed to connect to server" ) )
defer conn . Close ( )
err = conn . Handshake ( )
c . Assert ( err , checker . IsNil , check . Commentf ( "TLS handshake error" ) )
cs := conn . ConnectionState ( )
err = cs . PeerCertificates [ 0 ] . VerifyHostname ( "www.snitest.com" )
2022-09-13 21:34:08 +03:00
c . Assert ( err , checker . IsNil , check . Commentf ( "server did not serve correct default certificate" ) )
2018-07-06 11:30:03 +03:00
proto := cs . NegotiatedProtocol
c . Assert ( proto , checker . Equals , "h2" )
}
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.
2016-06-18 22:11:34 +03:00
func ( s * HTTPSSuite ) TestWithClientCertificateAuthentication ( c * check . C ) {
2019-07-15 11:22:03 +03:00
file := s . adaptFile ( c , "fixtures/https/clientca/https_1ca1config.toml" , struct { } { } )
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
2017-09-13 11:34:04 +03:00
defer display ( c )
2016-06-18 22:11:34 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2016-06-18 22:11:34 +03:00
2017-05-17 16:22:44 +03:00
// wait for Traefik
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.org`)" ) )
2017-05-17 16:22:44 +03:00
c . Assert ( err , checker . IsNil )
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 )
2017-11-10 12:30:04 +03:00
c . Assert ( err , checker . IsNil , check . Commentf ( "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" )
c . Assert ( err , checker . IsNil , check . Commentf ( "unable to load client certificate and key" ) )
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 )
2016-06-18 22:11:34 +03:00
c . Assert ( err , checker . IsNil , check . Commentf ( "failed to connect to server" ) )
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" )
c . Assert ( err , checker . IsNil , check . Commentf ( "unable to load client certificate and key" ) )
tlsConfig . Certificates = append ( tlsConfig . Certificates , cert )
conn , err = tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
c . Assert ( err , checker . IsNil , check . Commentf ( "failed to connect to server" ) )
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" )
c . Assert ( err , checker . IsNil , check . Commentf ( "unable to load client certificate and key" ) )
tlsConfig . Certificates = append ( tlsConfig . Certificates , cert )
2017-08-18 03:18:02 +03:00
_ , err = tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2017-11-10 12:30:04 +03:00
c . Assert ( err , checker . IsNil , check . Commentf ( "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.
2019-03-01 13:48:04 +03:00
func ( s * HTTPSSuite ) TestWithClientCertificateAuthenticationMultipleCAs ( c * check . C ) {
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 ( )
} ( )
file := s . adaptFile ( c , "fixtures/https/clientca/https_2ca1config.toml" , struct {
Server1 string
Server2 string
} {
Server1 : server1 . URL ,
Server2 : server2 . URL ,
} )
2019-07-15 11:22:03 +03:00
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
2017-09-13 11:34:04 +03:00
defer display ( c )
2016-06-18 22:11:34 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2016-06-18 22:11:34 +03:00
2017-05-17 16:22:44 +03:00
// wait for Traefik
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`snitest.org`)" ) )
2017-05-17 16:22:44 +03:00
c . Assert ( err , checker . IsNil )
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 )
c . Assert ( err , checker . IsNil )
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 )
c . Assert ( err , checker . NotNil )
2016-06-18 22:11:34 +03:00
cert , err := tls . LoadX509KeyPair ( "fixtures/https/clientca/client1.crt" , "fixtures/https/clientca/client1.key" )
c . Assert ( err , checker . IsNil , check . Commentf ( "unable to load client certificate and key" ) )
tlsConfig . Certificates = append ( tlsConfig . Certificates , cert )
2019-08-26 16:06:05 +03:00
// Connect with client signed by ca1
_ , err = client . Do ( req )
c . Assert ( err , checker . IsNil )
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" )
c . Assert ( err , checker . IsNil , check . Commentf ( "unable to load client certificate and key" ) )
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 )
c . Assert ( err , checker . IsNil )
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" )
c . Assert ( err , checker . IsNil , check . Commentf ( "unable to load client certificate and key" ) )
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 )
c . Assert ( err , checker . NotNil )
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.
2019-03-01 13:48:04 +03:00
func ( s * HTTPSSuite ) TestWithClientCertificateAuthenticationMultipleCAsMultipleFiles ( c * check . C ) {
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 ( )
} ( )
file := s . adaptFile ( c , "fixtures/https/clientca/https_2ca2config.toml" , struct {
Server1 string
Server2 string
} {
Server1 : server1 . URL ,
Server2 : server2 . URL ,
} )
2019-07-15 11:22:03 +03:00
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
2017-09-13 11:34:04 +03:00
defer display ( c )
2016-06-18 22:11:34 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2016-06-18 22:11:34 +03:00
2017-05-17 16:22:44 +03:00
// wait for Traefik
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`snitest.org`)" ) )
2017-05-17 16:22:44 +03:00
c . Assert ( err , checker . IsNil )
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 )
c . Assert ( err , checker . IsNil )
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 )
c . Assert ( err , checker . NotNil )
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" )
c . Assert ( err , checker . IsNil , check . Commentf ( "unable to load client certificate and key" ) )
tlsConfig . Certificates = append ( tlsConfig . Certificates , cert )
2019-08-26 16:06:05 +03:00
_ , err = client . Do ( req )
c . Assert ( err , checker . IsNil )
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" )
c . Assert ( err , checker . IsNil , check . Commentf ( "unable to load client certificate and key" ) )
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 )
c . Assert ( err , checker . IsNil )
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" )
c . Assert ( err , checker . IsNil , check . Commentf ( "unable to load client certificate and key" ) )
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 )
c . Assert ( err , checker . NotNil )
2016-06-18 22:11:34 +03:00
}
2017-06-23 16:15:07 +03:00
func ( s * HTTPSSuite ) TestWithRootCAsContentForHTTPSOnBackend ( c * check . C ) {
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 ( )
file := s . adaptFile ( c , "fixtures/https/rootcas/https.toml" , struct { BackendHost string } { backend . URL } )
defer os . Remove ( file )
2017-09-13 11:34:04 +03:00
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
defer display ( c )
2017-06-23 16:15:07 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2017-06-23 16:15:07 +03:00
// wait for Traefik
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( backend . URL ) )
2017-06-23 16:15:07 +03:00
c . Assert ( err , checker . IsNil )
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 ) )
2017-06-23 16:15:07 +03:00
c . Assert ( err , checker . IsNil )
}
func ( s * HTTPSSuite ) TestWithRootCAsFileForHTTPSOnBackend ( c * check . C ) {
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 ( )
file := s . adaptFile ( c , "fixtures/https/rootcas/https_with_file.toml" , struct { BackendHost string } { backend . URL } )
defer os . Remove ( file )
2017-09-13 11:34:04 +03:00
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
defer display ( c )
2017-06-23 16:15:07 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2017-06-23 16:15:07 +03:00
// wait for Traefik
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( backend . URL ) )
2017-06-23 16:15:07 +03:00
c . Assert ( err , checker . IsNil )
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 ) )
2017-06-23 16:15:07 +03:00
c . Assert ( err , checker . IsNil )
}
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.
func ( s * HTTPSSuite ) TestWithSNIDynamicConfigRouteWithNoChange ( c * check . C ) {
dynamicConfFileName := s . adaptFile ( c , "fixtures/https/dynamic_https.toml" , struct { } { } )
defer os . Remove ( dynamicConfFileName )
confFileName := s . adaptFile ( c , "fixtures/https/dynamic_https_sni.toml" , struct {
DynamicConfFileName string
} {
DynamicConfFileName : dynamicConfFileName ,
} )
defer os . Remove ( confFileName )
cmd , display := s . traefikCmd ( withConfigFile ( confFileName ) )
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
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
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`" + tr1 . TLSClientConfig . ServerName + "`)" ) )
2017-11-09 14:16:03 +03:00
c . Assert ( err , checker . IsNil )
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 ) )
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( backend2 . URL , 500 * time . Millisecond , try . StatusCodeIs ( http . StatusResetContent ) )
c . Assert ( err , checker . IsNil )
req , err := http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
c . Assert ( err , checker . IsNil )
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 ) )
2017-11-09 14:16:03 +03:00
c . Assert ( err , checker . IsNil )
2018-07-31 11:48:03 +03:00
req , err = http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
c . Assert ( err , checker . IsNil )
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 ) )
2017-11-09 14:16:03 +03:00
c . Assert ( err , checker . IsNil )
}
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.
func ( s * HTTPSSuite ) TestWithSNIDynamicConfigRouteWithChange ( c * check . C ) {
dynamicConfFileName := s . adaptFile ( c , "fixtures/https/dynamic_https.toml" , struct { } { } )
defer os . Remove ( dynamicConfFileName )
confFileName := s . adaptFile ( c , "fixtures/https/dynamic_https_sni.toml" , struct {
DynamicConfFileName string
} {
DynamicConfFileName : dynamicConfFileName ,
} )
defer os . Remove ( confFileName )
cmd , display := s . traefikCmd ( withConfigFile ( confFileName ) )
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
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
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`" + tr2 . TLSClientConfig . ServerName + "`)" ) )
2017-11-09 14:16:03 +03:00
c . Assert ( err , checker . IsNil )
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 ) )
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( backend2 . URL , 500 * time . Millisecond , try . StatusCodeIs ( http . StatusResetContent ) )
c . Assert ( err , checker . IsNil )
2018-07-31 11:48:03 +03:00
// Change certificates configuration file content
2019-07-15 11:22:03 +03:00
modifyCertificateConfFileContent ( c , 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 )
2018-07-31 11:48:03 +03:00
c . Assert ( err , checker . IsNil )
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 ) )
c . Assert ( err , checker . IsNil )
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 )
2017-11-09 14:16:03 +03:00
c . Assert ( err , checker . IsNil )
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 ) )
2017-11-09 14:16:03 +03:00
c . Assert ( err , checker . IsNil )
}
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.
func ( s * HTTPSSuite ) TestWithSNIDynamicConfigRouteWithTlsConfigurationDeletion ( c * check . C ) {
dynamicConfFileName := s . adaptFile ( c , "fixtures/https/dynamic_https.toml" , struct { } { } )
defer os . Remove ( dynamicConfFileName )
confFileName := s . adaptFile ( c , "fixtures/https/dynamic_https_sni.toml" , struct {
DynamicConfFileName string
} {
DynamicConfFileName : dynamicConfFileName ,
} )
defer os . Remove ( confFileName )
cmd , display := s . traefikCmd ( withConfigFile ( confFileName ) )
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2017-12-21 16:16:03 +03:00
tr2 := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.org" ,
} ,
}
// wait for Traefik
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`" + tr2 . TLSClientConfig . ServerName + "`)" ) )
2017-12-21 16:16:03 +03:00
c . Assert ( err , checker . IsNil )
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 ) )
c . Assert ( err , checker . IsNil )
req , err := http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
2018-07-31 11:48:03 +03:00
c . Assert ( err , checker . IsNil )
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 ) )
2017-12-21 16:16:03 +03:00
c . Assert ( err , checker . IsNil )
2018-07-31 11:48:03 +03:00
2017-12-21 16:16:03 +03:00
// Change certificates configuration file content
2019-07-15 11:22:03 +03:00
modifyCertificateConfFileContent ( c , "" , 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 ) )
2017-12-21 16:16:03 +03:00
c . Assert ( err , checker . IsNil )
}
2017-11-09 14:16:03 +03:00
// modifyCertificateConfFileContent replaces the content of a HTTPS configuration file.
2019-07-15 11:22:03 +03:00
func modifyCertificateConfFileContent ( c * check . C , certFileName , confFileName string ) {
2018-09-18 09:22:03 +03:00
file , err := os . OpenFile ( "./" + confFileName , os . O_WRONLY , os . ModeExclusive )
2017-11-09 14:16:03 +03:00
c . Assert ( err , checker . IsNil )
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 )
c . Assert ( err , checker . IsNil )
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 {
CertFile : traefiktls . FileOrContent ( "fixtures/https/" + certFileName + ".cert" ) ,
KeyFile : traefiktls . FileOrContent ( "fixtures/https/" + certFileName + ".key" ) ,
} ,
} ,
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 )
2017-12-21 23:24:03 +03:00
c . Assert ( err , checker . IsNil )
2018-09-18 09:22:03 +03:00
_ , err = file . Write ( confBuffer . Bytes ( ) )
2017-12-21 23:24:03 +03:00
c . Assert ( err , checker . IsNil )
}
2017-11-09 14:16:03 +03:00
}
2018-07-31 12:28:03 +03:00
2019-07-15 11:22:03 +03:00
func ( s * HTTPSSuite ) TestEntryPointHttpsRedirectAndPathModification ( c * check . C ) {
file := s . adaptFile ( c , "fixtures/https/https_redirect.toml" , struct { } { } )
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
2018-07-31 12:28:03 +03:00
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2018-07-31 12:28:03 +03:00
// wait for Traefik
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 5 * time . Second , try . BodyContains ( "Host(`example.com`)" ) )
2018-07-31 12:28:03 +03:00
c . Assert ( err , checker . IsNil )
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 )
2018-08-14 19:38:04 +03:00
c . Assert ( err , checker . IsNil )
req . Host = host
2018-07-31 12:28:03 +03:00
2018-08-14 19:38:04 +03:00
resp , err := client . Do ( req )
c . Assert ( err , checker . IsNil )
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
2018-08-14 19:38:04 +03:00
c . Assert ( location , checker . Equals , expected )
}
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.
func ( s * HTTPSSuite ) TestWithSNIDynamicCaseInsensitive ( c * check . C ) {
2019-07-15 11:22:03 +03:00
file := s . adaptFile ( c , "fixtures/https/https_sni_case_insensitive_dynamic.toml" , struct { } { } )
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
2018-11-26 12:38:03 +03:00
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2018-11-26 12:38:03 +03:00
// wait for Traefik
2019-05-16 11:58:06 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "HostRegexp(`{subdomain:[a-z1-9-]+}.www.snitest.com`)" ) )
2018-11-26 12:38:03 +03:00
c . Assert ( err , checker . IsNil )
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 )
c . Assert ( err , checker . IsNil , check . Commentf ( "failed to connect to server" ) )
defer conn . Close ( )
err = conn . Handshake ( )
c . Assert ( err , checker . IsNil , check . Commentf ( "TLS handshake error" ) )
cs := conn . ConnectionState ( )
err = cs . PeerCertificates [ 0 ] . VerifyHostname ( "*.WWW.SNITEST.COM" )
c . Assert ( err , checker . IsNil , check . Commentf ( "certificate did not match SNI servername" ) )
proto := conn . ConnectionState ( ) . NegotiatedProtocol
c . Assert ( proto , checker . Equals , "h2" )
}
2020-07-17 16:38:04 +03:00
// TestWithDomainFronting verify the domain fronting behavior
func ( s * HTTPSSuite ) TestWithDomainFronting ( c * check . C ) {
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 ( )
file := s . adaptFile ( c , "fixtures/https/https_domain_fronting.toml" , struct { } { } )
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2020-10-09 10:32:03 +03:00
defer s . killCmd ( cmd )
2020-07-17 16:38:04 +03:00
// wait for Traefik
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`site1.www.snitest.com`)" ) )
c . Assert ( err , checker . IsNil )
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 {
test := test
2020-07-20 19:32:03 +03:00
2020-07-17 16:38:04 +03:00
req , err := http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443" , nil )
c . Assert ( err , checker . IsNil )
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 {
c . Assert ( err , checker . NotNil )
} else {
c . Assert ( err , checker . IsNil )
}
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.
func ( s * HTTPSSuite ) TestWithInvalidTLSOption ( c * check . C ) {
backend := startTestServer ( "9010" , http . StatusOK , "server1" )
defer backend . Close ( )
file := s . adaptFile ( c , "fixtures/https/https_invalid_tls_options.toml" , struct { } { } )
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer s . killCmd ( cmd )
// wait for Traefik
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 500 * time . Millisecond , try . BodyContains ( "Host(`snitest.com`)" ) )
c . Assert ( err , checker . IsNil )
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 {
test := test
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
}
if test . serverName != "" {
tlsConfig . ServerName = test . serverName
}
conn , err := tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
c . Assert ( err , checker . NotNil , check . Commentf ( "connected to server successfully" ) )
c . Assert ( conn , checker . IsNil )
}
}