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"
2017-05-17 16:22:44 +03:00
"github.com/containous/traefik/integration/try"
2018-11-14 12:18:03 +03:00
"github.com/containous/traefik/old/types"
2018-03-06 12:12:04 +03:00
traefiktls "github.com/containous/traefik/tls"
2016-04-02 13:40:21 +03:00
"github.com/go-check/check"
2015-11-21 04:59:49 +03:00
checker "github.com/vdemeester/shakers"
)
// HTTPSSuite
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 ) {
2017-09-13 11:34:04 +03:00
cmd , display := s . traefikCmd ( withConfigFile ( "fixtures/https/https_sni.toml" ) )
defer display ( c )
2015-11-21 04:59:49 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2017-05-17 16:22:44 +03:00
// wait for Traefik
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 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 ) {
2017-09-13 11:34:04 +03:00
cmd , display := s . traefikCmd ( withConfigFile ( "fixtures/https/https_sni.toml" ) )
defer display ( c )
2015-11-21 04:59:49 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2017-05-17 16:22:44 +03:00
// wait for Traefik
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 1 * time . Second , try . BodyContains ( "Host:snitest.org" ) )
2017-05-17 16:22:44 +03:00
c . Assert ( err , checker . IsNil )
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 )
}
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 ) {
cmd , display := s . traefikCmd ( withConfigFile ( "fixtures/https/https_sni_strict.toml" ) )
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
// wait for Traefik
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 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 ) {
cmd , display := s . traefikCmd ( withConfigFile ( "fixtures/https/https_sni_default_cert.toml" ) )
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
// wait for Traefik
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 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" )
c . Assert ( err , checker . IsNil , check . Commentf ( "certificate did not serve correct default certificate" ) )
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 ) {
cmd , display := s . traefikCmd ( withConfigFile ( "fixtures/https/https_sni_default_cert.toml" ) )
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
// wait for Traefik
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 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" )
c . Assert ( err , checker . IsNil , check . Commentf ( "certificate did not serve correct default certificate" ) )
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 ) {
cmd , display := s . traefikCmd ( withConfigFile ( "fixtures/https/https_sni_default_cert.toml" ) )
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
// wait for Traefik
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 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" )
c . Assert ( err , checker . IsNil , check . Commentf ( "certificate did not serve correct default certificate" ) )
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 ) {
cmd , display := s . traefikCmd ( withConfigFile ( "fixtures/https/dynamic_https_sni_default_cert.toml" ) )
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
// wait for Traefik
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 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" )
c . Assert ( err , checker . IsNil , check . Commentf ( "certificate did not serve correct default certificate" ) )
proto := cs . NegotiatedProtocol
c . Assert ( proto , checker . Equals , "h2" )
}
2016-06-18 22:11:34 +03:00
// TestWithClientCertificateAuthentication
2017-11-10 12:30:04 +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 ) {
2017-09-13 11:34:04 +03:00
cmd , display := s . traefikCmd ( withConfigFile ( "fixtures/https/clientca/https_1ca1config.toml" ) )
defer display ( c )
2016-06-18 22:11:34 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2017-05-17 16:22:44 +03:00
// wait for Traefik
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 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
// Use two CA:s and test that clients with client signed by either of them can connect
func ( s * HTTPSSuite ) TestWithClientCertificateAuthenticationMultipeCAs ( c * check . C ) {
2017-09-13 11:34:04 +03:00
cmd , display := s . traefikCmd ( withConfigFile ( "fixtures/https/clientca/https_2ca1config.toml" ) )
defer display ( c )
2016-06-18 22:11:34 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2017-05-17 16:22:44 +03:00
// wait for Traefik
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 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
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 )
2016-06-18 22:11:34 +03:00
c . Assert ( err , checker . NotNil , check . Commentf ( "should not be allowed to connect to server" ) )
// 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 )
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 ( )
// Connect with client signed by ca2
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 )
conn , err = tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
c . Assert ( err , checker . IsNil , check . Commentf ( "failed to connect to server" ) )
2017-08-18 03:18:02 +03:00
2016-06-18 22:11:34 +03:00
conn . Close ( )
// Connect with client signed by ca3 should fail
tlsConfig = & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
Certificates : [ ] tls . Certificate { } ,
}
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 )
2017-08-18 03:18:02 +03:00
_ , err = tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2016-06-18 22:11:34 +03:00
c . Assert ( err , checker . NotNil , check . Commentf ( "should not be allowed to connect to server" ) )
}
// TestWithClientCertificateAuthentication
// Use two CA:s in two different files and test that clients with client signed by either of them can connect
func ( s * HTTPSSuite ) TestWithClientCertificateAuthenticationMultipeCAsMultipleFiles ( c * check . C ) {
2017-09-13 11:34:04 +03:00
cmd , display := s . traefikCmd ( withConfigFile ( "fixtures/https/clientca/https_2ca2config.toml" ) )
defer display ( c )
2016-06-18 22:11:34 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2017-05-17 16:22:44 +03:00
// wait for Traefik
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 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
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 )
2016-06-18 22:11:34 +03:00
c . Assert ( err , checker . NotNil , check . Commentf ( "should not be allowed to connect to server" ) )
// 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 )
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 ( )
// Connect with client signed by ca2
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 )
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 ( )
// Connect with client signed by ca3 should fail
tlsConfig = & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
Certificates : [ ] tls . Certificate { } ,
}
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 )
2017-08-18 03:18:02 +03:00
_ , err = tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2016-06-18 22:11:34 +03:00
c . Assert ( err , checker . NotNil , check . Commentf ( "should not be allowed to connect to server" ) )
}
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 )
defer cmd . Process . Kill ( )
// wait for Traefik
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/services" , 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 )
defer cmd . Process . Kill ( )
// wait for Traefik
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/services" , 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 )
}
2015-11-21 04:59:49 +03:00
func startTestServer ( port string , statusCode int ) ( ts * httptest . Server ) {
handler := http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . WriteHeader ( statusCode )
} )
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 )
defer cmd . Process . Kill ( )
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
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 1 * time . Second , try . BodyContains ( "Host:" + tr1 . TLSClientConfig . ServerName ) )
2017-11-09 14:16:03 +03:00
c . Assert ( err , checker . IsNil )
backend1 := startTestServer ( "9010" , http . StatusNoContent )
backend2 := startTestServer ( "9020" , http . StatusResetContent )
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 )
defer cmd . Process . Kill ( )
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
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 1 * time . Second , try . BodyContains ( "Host:" + tr2 . TLSClientConfig . ServerName ) )
2017-11-09 14:16:03 +03:00
c . Assert ( err , checker . IsNil )
backend1 := startTestServer ( "9010" , http . StatusNoContent )
backend2 := startTestServer ( "9020" , http . StatusResetContent )
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
modifyCertificateConfFileContent ( c , tr1 . TLSClientConfig . ServerName , dynamicConfFileName , "https" )
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 )
defer cmd . Process . Kill ( )
tr2 := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.org" ,
} ,
}
// wait for Traefik
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 1 * time . Second , try . BodyContains ( "Host:" + tr2 . TLSClientConfig . ServerName ) )
2017-12-21 16:16:03 +03:00
c . Assert ( err , checker . IsNil )
backend2 := startTestServer ( "9020" , http . StatusResetContent )
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
2017-12-21 23:24:03 +03:00
modifyCertificateConfFileContent ( c , "" , dynamicConfFileName , "https02" )
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.
2017-12-21 16:16:03 +03:00
func modifyCertificateConfFileContent ( c * check . C , certFileName , confFileName , entryPoint 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 {
tlsConf := types . Configuration {
2018-09-18 09:22:03 +03:00
TLS : [ ] * traefiktls . Configuration { {
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
} ,
2018-09-18 09:22:03 +03:00
EntryPoints : [ ] string { entryPoint } ,
} } ,
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
func ( s * HTTPSSuite ) TestEntrypointHttpsRedirectAndPathModification ( c * check . C ) {
cmd , display := s . traefikCmd ( withConfigFile ( "fixtures/https/https_redirect.toml" ) )
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
// wait for Traefik
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 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 {
req , err := http . NewRequest ( "GET" , sourceURL , nil )
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 )
defer resp . Body . Close ( )
2018-07-31 12:28:03 +03:00
2018-08-14 19:38:04 +03:00
location := resp . Header . Get ( "Location" )
expected := fmt . Sprintf ( "https://%s:8443%s" , host , 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
}
}