2017-07-06 16:28:13 +02:00
package integration
2015-11-21 02:59:49 +01:00
import (
"crypto/tls"
"net"
"net/http"
"net/http/httptest"
2017-06-23 15:15:07 +02:00
"os"
2015-11-21 02:59:49 +01:00
"time"
2017-05-17 15:22:44 +02:00
"github.com/containous/traefik/integration/try"
2016-04-02 12:40:21 +02:00
"github.com/go-check/check"
2015-11-21 02:59:49 +01: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-07-10 14:58:31 +02:00
cmd , _ := s . cmdTraefik ( withConfigFile ( "fixtures/https/https_sni.toml" ) )
2015-11-21 02:59:49 +01:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2017-05-17 15:22:44 +02:00
// wait for Traefik
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers" , 500 * time . Millisecond , try . BodyContains ( "Host:snitest.org" ) )
c . Assert ( err , checker . IsNil )
2015-11-21 02:59:49 +01:00
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
2016-11-09 18:43:59 +01:00
NextProtos : [ ] string { "h2" , "http/1.1" } ,
2015-11-21 02:59:49 +01:00
}
2016-01-13 22:46:44 +01:00
conn , err := tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2015-11-21 02:59:49 +01: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 18:43:59 +01:00
proto := conn . ConnectionState ( ) . NegotiatedProtocol
c . Assert ( proto , checker . Equals , "h2" )
2015-11-21 02:59:49 +01: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-07-10 14:58:31 +02:00
cmd , _ := s . cmdTraefik ( withConfigFile ( "fixtures/https/https_sni.toml" ) )
2015-11-21 02:59:49 +01:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2017-05-17 15:22:44 +02:00
// wait for Traefik
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers" , 500 * time . Millisecond , try . BodyContains ( "Host:snitest.org" ) )
c . Assert ( err , checker . IsNil )
backend1 := startTestServer ( "9010" , http . StatusNoContent )
backend2 := startTestServer ( "9020" , http . StatusResetContent )
2015-11-21 02:59:49 +01:00
defer backend1 . Close ( )
defer backend2 . Close ( )
2017-05-17 15:22:44 +02: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 02:59:49 +01:00
tr1 := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
} ,
}
tr2 := & http . Transport {
TLSClientConfig : & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.org" ,
} ,
}
client := & http . Client { Transport : tr1 }
2017-05-17 15:22:44 +02:00
req , err := http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
c . Assert ( err , checker . IsNil )
2015-11-21 02:59:49 +01:00
req . Host = "snitest.com"
req . Header . Set ( "Host" , "snitest.com" )
req . Header . Set ( "Accept" , "*/*" )
resp , err := client . Do ( req )
c . Assert ( err , checker . IsNil )
// Expected a 204 (from backend1)
2017-05-17 15:22:44 +02:00
c . Assert ( resp . StatusCode , checker . Equals , http . StatusNoContent )
2015-11-21 02:59:49 +01:00
client = & http . Client { Transport : tr2 }
2017-05-17 15:22:44 +02:00
req , err = http . NewRequest ( http . MethodGet , "https://127.0.0.1:4443/" , nil )
c . Assert ( err , checker . IsNil )
2015-11-21 02:59:49 +01:00
req . Host = "snitest.org"
req . Header . Set ( "Host" , "snitest.org" )
req . Header . Set ( "Accept" , "*/*" )
resp , err = client . Do ( req )
c . Assert ( err , checker . IsNil )
// Expected a 205 (from backend2)
2017-05-17 15:22:44 +02:00
c . Assert ( resp . StatusCode , checker . Equals , http . StatusResetContent )
2015-11-21 02:59:49 +01:00
}
2016-06-18 21:11:34 +02:00
// TestWithClientCertificateAuthentication
// The client has to send a certificate signed by a CA trusted by the server
func ( s * HTTPSSuite ) TestWithClientCertificateAuthentication ( c * check . C ) {
2017-07-10 14:58:31 +02:00
cmd , _ := s . cmdTraefik ( withConfigFile ( "fixtures/https/clientca/https_1ca1config.toml" ) )
2016-06-18 21:11:34 +02:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2017-05-17 15:22:44 +02:00
// wait for Traefik
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers" , 500 * time . Millisecond , try . BodyContains ( "Host:snitest.org" ) )
c . Assert ( err , checker . IsNil )
2016-06-18 21:11:34 +02:00
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
Certificates : [ ] tls . Certificate { } ,
}
// Connection without client certificate should fail
2017-08-18 02:18:02 +02:00
_ , err = tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2016-06-18 21:11:34 +02:00
c . Assert ( err , checker . NotNil , check . Commentf ( "should not be allowed to connect to server" ) )
// 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 02:18:02 +02:00
conn , err := tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2016-06-18 21:11:34 +02:00
c . Assert ( err , checker . IsNil , check . Commentf ( "failed to connect to server" ) )
conn . Close ( )
// 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 02:18:02 +02:00
_ , err = tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2016-06-18 21:11:34 +02:00
c . Assert ( err , checker . NotNil , check . Commentf ( "should not be allowed to connect to server" ) )
}
// 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-07-10 14:58:31 +02:00
cmd , _ := s . cmdTraefik ( withConfigFile ( "fixtures/https/clientca/https_2ca1config.toml" ) )
2016-06-18 21:11:34 +02:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2017-05-17 15:22:44 +02:00
// wait for Traefik
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers" , 500 * time . Millisecond , try . BodyContains ( "Host:snitest.org" ) )
c . Assert ( err , checker . IsNil )
2016-06-18 21:11:34 +02:00
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
Certificates : [ ] tls . Certificate { } ,
}
// Connection without client certificate should fail
2017-08-18 02:18:02 +02:00
_ , err = tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2016-06-18 21:11:34 +02: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 02:18:02 +02:00
conn , err := tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2016-06-18 21:11:34 +02: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 02:18:02 +02:00
2016-06-18 21:11:34 +02: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 02:18:02 +02:00
_ , err = tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2016-06-18 21:11:34 +02: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-07-10 14:58:31 +02:00
cmd , _ := s . cmdTraefik ( withConfigFile ( "fixtures/https/clientca/https_2ca2config.toml" ) )
2016-06-18 21:11:34 +02:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2017-05-17 15:22:44 +02:00
// wait for Traefik
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers" , 1000 * time . Millisecond , try . BodyContains ( "Host:snitest.org" ) )
c . Assert ( err , checker . IsNil )
2016-06-18 21:11:34 +02:00
tlsConfig := & tls . Config {
InsecureSkipVerify : true ,
ServerName : "snitest.com" ,
Certificates : [ ] tls . Certificate { } ,
}
// Connection without client certificate should fail
2017-08-18 02:18:02 +02:00
_ , err = tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2016-06-18 21:11:34 +02: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 02:18:02 +02:00
conn , err := tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2016-06-18 21:11:34 +02: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 02:18:02 +02:00
_ , err = tls . Dial ( "tcp" , "127.0.0.1:4443" , tlsConfig )
2016-06-18 21:11:34 +02:00
c . Assert ( err , checker . NotNil , check . Commentf ( "should not be allowed to connect to server" ) )
}
2017-06-23 15:15:07 +02:00
func ( s * HTTPSSuite ) TestWithRootCAsContentForHTTPSOnBackend ( c * check . C ) {
backend := httptest . NewTLSServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . WriteHeader ( 200 )
} ) )
defer backend . Close ( )
file := s . adaptFile ( c , "fixtures/https/rootcas/https.toml" , struct { BackendHost string } { backend . URL } )
defer os . Remove ( file )
2017-07-10 14:58:31 +02:00
cmd , _ := s . cmdTraefik ( withConfigFile ( file ) )
2017-06-23 15:15:07 +02:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
// wait for Traefik
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers" , 1000 * time . Millisecond , try . BodyContains ( backend . URL ) )
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( "http://127.0.0.1:8081/ping" , 1000 * time . Millisecond , try . StatusCodeIs ( http . StatusOK ) )
c . Assert ( err , checker . IsNil )
}
func ( s * HTTPSSuite ) TestWithRootCAsFileForHTTPSOnBackend ( c * check . C ) {
backend := httptest . NewTLSServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . WriteHeader ( 200 )
} ) )
defer backend . Close ( )
file := s . adaptFile ( c , "fixtures/https/rootcas/https_with_file.toml" , struct { BackendHost string } { backend . URL } )
defer os . Remove ( file )
2017-07-10 14:58:31 +02:00
cmd , _ := s . cmdTraefik ( withConfigFile ( file ) )
2017-06-23 15:15:07 +02:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
// wait for Traefik
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers" , 1000 * time . Millisecond , try . BodyContains ( backend . URL ) )
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( "http://127.0.0.1:8081/ping" , 1000 * time . Millisecond , try . StatusCodeIs ( http . StatusOK ) )
c . Assert ( err , checker . IsNil )
}
2015-11-21 02:59:49 +01: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 15:22:44 +02:00
return ts
2015-11-21 02:59:49 +01:00
}