2019-03-14 11:30:04 +03:00
package tls
import (
2019-09-13 20:28:04 +03:00
"context"
2019-03-14 11:30:04 +03:00
"crypto/tls"
2019-07-12 18:50:04 +03:00
"crypto/x509"
"encoding/pem"
2019-03-14 11:30:04 +03:00
"testing"
2019-06-21 18:18:05 +03:00
"github.com/stretchr/testify/assert"
2019-07-12 18:50:04 +03:00
"github.com/stretchr/testify/require"
2024-01-11 19:06:06 +03:00
"github.com/traefik/traefik/v3/pkg/types"
2019-03-14 11:30:04 +03:00
)
// LocalhostCert is a PEM-encoded TLS cert with SAN IPs
// "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT.
// generated from src/crypto/tls:
// go run generate_cert.go --rsa-bits 1024 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
var (
2024-01-11 19:06:06 +03:00
localhostCert = types . FileOrContent ( ` -- -- - BEGIN CERTIFICATE -- -- -
2022-02-14 16:08:07 +03:00
MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS
2019-03-14 11:30:04 +03:00
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
2022-02-14 16:08:07 +03:00
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3 / ebDUU4rvwCUg / CNaJ2PT5xLD4N1Vcb8r
bFSW2HXKq + MPfVdwIKR / 1 DczEoAGf / JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U
aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO + 9 ad1W5BqJaRI6P
YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk
POGKBV / q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu
h7AXF5 + 4 nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB / wQE
AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH / MB0GA1Ud
DgQWBBStsdjh3 / JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv
bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI
5 NhpF3nwwy / 4 yB4i / CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx / o5HyNwsv
cxv3HdkLW59i / 0 SlJSrNnWdfZ19oTcS + 6 PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2
+ tK / tKHRP1Y / Ra0RiDpOAmqn0gCOFGz8 + lqDIor / T7MTpibL3IxqWfPrvfVRHL3B
grw / ZQTTIVjjh4JBSW3WyWgNo / ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK
5 d + NTDREkSnUbie4GeutujmX3Dsx88UiV6UY / 4 lHJa6I5leHUNOHahRbpbWeOfs /
WkBKOclmOV2xlTVuPw ==
2019-03-14 11:30:04 +03:00
-- -- - END CERTIFICATE -- -- - ` )
// LocalhostKey is the private key for localhostCert.
2024-01-11 19:06:06 +03:00
localhostKey = types . FileOrContent ( ` -- -- - BEGIN RSA PRIVATE KEY -- -- -
2022-02-14 16:08:07 +03:00
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDoZtrm0dXV0Aqi
4 Bpc7f95sNRTiu / AJSD8I1onY9PnEsPg3VVxvytsVJbYdcqr4w99V3AgpH / UNzMS
gAZ / 8 lZBNbsSDOVesJ3euVqMRfYPvd9pYl6QPRRpSDPm + 2 tNdn3QFAvta9EgJ3sW
URnoU85w + W6aLI2bNSq3AaE771p3VbkGolpEjo9h + i42TBHo1rhPNKPkGupR8 / QX
AOLMpInRdeaHyDwb2a3DE5I3dG7VAVzrVfJ6W6Q84YoFX + rpEE2SVM17SAjy6xQy
VjKgLvK2mk0xbtfa + h0B6VK7bmODHZqeP18NVm6HsBcXn7iclLgAC3SfWU1jucZK
x1lqzw9tAgMBAAECggEABWzxS1Y2wckblnXY57Z + sl6YdmLV + gxj2r8Qib7g4ZIk
lIlWR1OJNfw7kU4eryib4fc6nOh6O4AWZyYqAK6tqNQSS / eVG0LQTLTTEldHyVJL
dvBe + MsUQOj4nTndZW + QvFzbcm2D8lY5n2nBSxU5ypVoKZ1EqQzytFcLZpTN7d89
EPj0qDyrV4NZlWAwL1AygCwnlwhMQjXEalVF1ylXwU3QzyZ / 6 MgvF6d3SSUlh + sq
XefuyigXw484cQQgbzopv6niMOmGP3of + yV4JQqUSb3IDmmT68XjGd2Dkxl4iPki
6 ZwXf3CCi + c + i / zVEcufgZ3SLf8D99kUGE7v7fZ6AQKBgQD1ZX3RAla9hIhxCf + O
3 D + I1j2LMrdjAh0ZKKqwMR4JnHX3mjQI6LwqIctPWTU8wYFECSh9klEclSdCa64s
uI / GNpcqPXejd0cAAdqHEEeG5sHMDt0oFSurL4lyud0GtZvwlzLuwEweuDtvT9cJ
Wfvl86uyO36IW8JdvUprYDctrQKBgQDycZ697qutBieZlGkHpnYWUAeImVA878sJ
w44NuXHvMxBPz + lbJGAg8Cn8fcxNAPqHIraK + kx3po8cZGQywKHUWsxi23ozHoxo
+ bGqeQb9U661TnfdDspIXia + xilZt3mm5BPzOUuRqlh4Y9SOBpSWRmEhyw76w4ZP
OPxjWYAgwQKBgA / FehSYxeJgRjSdo + MWnK66tjHgDJE8bYpUZsP0JC4R9DL5oiaA
brd2fI6Y + SbyeNBallObt8LSgzdtnEAbjIH8uDJqyOmknNePRvAvR6mP4xyuR + Bv
m + Lgp0DMWTw5J9CKpydZDItc49T / mJ5tPhdFVd + am0NAQnmr1MCZ6nHxAoGABS3Y
LkaC9FdFUUqSU8 + Chkd / YbOkuyiENdkvl6t2e52jo5DVc1T7mLiIrRQi4SI8N9bN
/ 3 oJWCT + uaSLX2ouCtNFunblzWHBrhxnZzTeqVq4SLc8aESAnbslKL4i8 / + vYZlN
s8xtiNcSvL + lMsOBORSXzpj / 4 Ot8WwTkn1qyGgECgYBKNTypzAHeLE6yVadFp3nQ
Ckq9yzvP / ib05rvgbvrne00YeOxqJ9gtTrzgh7koqJyX1L4NwdkEza4ilDWpucn0
xiUZS4SoaJq6ZvcBYS62Yr1t8n09iG47YL8ibgtmH3L + svaotvpVxVK + d7BLevA /
ZboOWVe3icTy64BT3OQhmg ==
2019-03-14 11:30:04 +03:00
-- -- - END RSA PRIVATE KEY -- -- - ` )
)
func TestTLSInStore ( t * testing . T ) {
2019-06-28 00:58:03 +03:00
dynamicConfigs := [ ] * CertAndStores { {
Certificate : Certificate {
CertFile : localhostCert ,
KeyFile : localhostKey ,
} ,
} }
2019-03-14 11:30:04 +03:00
tlsManager := NewManager ( )
2019-09-13 20:28:04 +03:00
tlsManager . UpdateConfigs ( context . Background ( ) , nil , nil , dynamicConfigs )
2019-03-14 11:30:04 +03:00
certs := tlsManager . GetStore ( "default" ) . DynamicCerts . Get ( ) . ( map [ string ] * tls . Certificate )
if len ( certs ) == 0 {
t . Fatal ( "got error: default store must have TLS certificates." )
}
}
2019-06-21 17:32:04 +03:00
func TestTLSInvalidStore ( t * testing . T ) {
2019-06-28 00:58:03 +03:00
dynamicConfigs := [ ] * CertAndStores { {
Certificate : Certificate {
CertFile : localhostCert ,
KeyFile : localhostKey ,
} ,
} }
2019-06-21 17:32:04 +03:00
tlsManager := NewManager ( )
2019-09-13 20:28:04 +03:00
tlsManager . UpdateConfigs ( context . Background ( ) ,
map [ string ] Store {
"default" : {
DefaultCertificate : & Certificate {
CertFile : "/wrong" ,
KeyFile : "/wrong" ,
} ,
2019-06-21 17:32:04 +03:00
} ,
2019-09-13 20:28:04 +03:00
} , nil , dynamicConfigs )
2019-06-21 17:32:04 +03:00
certs := tlsManager . GetStore ( "default" ) . DynamicCerts . Get ( ) . ( map [ string ] * tls . Certificate )
if len ( certs ) == 0 {
t . Fatal ( "got error: default store must have TLS certificates." )
}
}
2019-06-21 18:18:05 +03:00
func TestManager_Get ( t * testing . T ) {
2019-06-28 00:58:03 +03:00
dynamicConfigs := [ ] * CertAndStores { {
Certificate : Certificate {
CertFile : localhostCert ,
KeyFile : localhostKey ,
} ,
} }
tlsConfigs := map [ string ] Options {
2022-12-06 20:28:05 +03:00
"foo" : { MinVersion : "VersionTLS12" } ,
"bar" : { MinVersion : "VersionTLS11" } ,
"invalid" : { CurvePreferences : [ ] string { "42" } } ,
2019-06-21 18:18:05 +03:00
}
testCases := [ ] struct {
desc string
tlsOptionsName string
expectedMinVersion uint16
expectedError bool
} {
{
desc : "Get a tls config from a valid name" ,
tlsOptionsName : "foo" ,
expectedMinVersion : uint16 ( tls . VersionTLS12 ) ,
} ,
{
desc : "Get another tls config from a valid name" ,
tlsOptionsName : "bar" ,
expectedMinVersion : uint16 ( tls . VersionTLS11 ) ,
} ,
{
2022-12-06 20:28:05 +03:00
desc : "Get a tls config from an invalid name" ,
2019-06-21 18:18:05 +03:00
tlsOptionsName : "unknown" ,
expectedError : true ,
} ,
{
2022-12-06 20:28:05 +03:00
desc : "Get a tls config from unexisting 'default' name" ,
2019-06-21 18:18:05 +03:00
tlsOptionsName : "default" ,
expectedError : true ,
} ,
2022-12-06 20:28:05 +03:00
{
desc : "Get an invalid tls config" ,
tlsOptionsName : "invalid" ,
expectedError : true ,
} ,
2019-06-21 18:18:05 +03:00
}
tlsManager := NewManager ( )
2019-09-13 20:28:04 +03:00
tlsManager . UpdateConfigs ( context . Background ( ) , nil , tlsConfigs , dynamicConfigs )
2019-06-21 18:18:05 +03:00
for _ , test := range testCases {
t . Run ( test . desc , func ( t * testing . T ) {
t . Parallel ( )
config , err := tlsManager . Get ( "default" , test . tlsOptionsName )
if test . expectedError {
2022-12-06 20:28:05 +03:00
require . Nil ( t , config )
require . Error ( t , err )
2019-06-21 18:18:05 +03:00
return
}
2022-05-19 18:12:08 +03:00
require . NoError ( t , err )
2023-11-17 03:50:06 +03:00
assert . Equal ( t , test . expectedMinVersion , config . MinVersion )
2022-05-19 18:12:08 +03:00
} )
}
}
2019-07-12 18:50:04 +03:00
func TestClientAuth ( t * testing . T ) {
tlsConfigs := map [ string ] Options {
2019-07-19 12:52:04 +03:00
"eca" : {
ClientAuth : ClientAuth { } ,
} ,
"ecat" : {
ClientAuth : ClientAuth { ClientAuthType : "" } ,
} ,
"ncc" : {
ClientAuth : ClientAuth { ClientAuthType : "NoClientCert" } ,
} ,
"rcc" : {
ClientAuth : ClientAuth { ClientAuthType : "RequestClientCert" } ,
} ,
"racc" : {
ClientAuth : ClientAuth { ClientAuthType : "RequireAnyClientCert" } ,
} ,
2019-07-12 18:50:04 +03:00
"vccig" : {
ClientAuth : ClientAuth {
2024-01-11 19:06:06 +03:00
CAFiles : [ ] types . FileOrContent { localhostCert } ,
2019-07-12 18:50:04 +03:00
ClientAuthType : "VerifyClientCertIfGiven" ,
} ,
} ,
"vccigwca" : {
ClientAuth : ClientAuth { ClientAuthType : "VerifyClientCertIfGiven" } ,
} ,
2019-07-19 12:52:04 +03:00
"ravcc" : {
ClientAuth : ClientAuth { ClientAuthType : "RequireAndVerifyClientCert" } ,
} ,
2019-07-12 18:50:04 +03:00
"ravccwca" : {
ClientAuth : ClientAuth {
2024-01-11 19:06:06 +03:00
CAFiles : [ ] types . FileOrContent { localhostCert } ,
2019-07-12 18:50:04 +03:00
ClientAuthType : "RequireAndVerifyClientCert" ,
} ,
} ,
"ravccwbca" : {
ClientAuth : ClientAuth {
2024-01-11 19:06:06 +03:00
CAFiles : [ ] types . FileOrContent { "Bad content" } ,
2019-07-12 18:50:04 +03:00
ClientAuthType : "RequireAndVerifyClientCert" ,
} ,
} ,
2019-07-19 12:52:04 +03:00
"ucat" : {
ClientAuth : ClientAuth { ClientAuthType : "Unknown" } ,
} ,
2019-07-12 18:50:04 +03:00
}
block , _ := pem . Decode ( [ ] byte ( localhostCert ) )
cert , err := x509 . ParseCertificate ( block . Bytes )
require . NoError ( t , err )
testCases := [ ] struct {
desc string
tlsOptionsName string
expectedClientAuth tls . ClientAuthType
expectedRawSubject [ ] byte
2019-07-19 12:52:04 +03:00
expectedError bool
2019-07-12 18:50:04 +03:00
} {
{
desc : "Empty ClientAuth option should get a tls.NoClientCert (default value)" ,
tlsOptionsName : "eca" ,
expectedClientAuth : tls . NoClientCert ,
} ,
{
desc : "Empty ClientAuthType option should get a tls.NoClientCert (default value)" ,
tlsOptionsName : "ecat" ,
expectedClientAuth : tls . NoClientCert ,
} ,
{
desc : "NoClientCert option should get a tls.NoClientCert as ClientAuthType" ,
tlsOptionsName : "ncc" ,
expectedClientAuth : tls . NoClientCert ,
} ,
{
desc : "RequestClientCert option should get a tls.RequestClientCert as ClientAuthType" ,
tlsOptionsName : "rcc" ,
expectedClientAuth : tls . RequestClientCert ,
} ,
{
desc : "RequireAnyClientCert option should get a tls.RequireAnyClientCert as ClientAuthType" ,
tlsOptionsName : "racc" ,
expectedClientAuth : tls . RequireAnyClientCert ,
} ,
{
desc : "VerifyClientCertIfGiven option should get a tls.VerifyClientCertIfGiven as ClientAuthType" ,
tlsOptionsName : "vccig" ,
expectedClientAuth : tls . VerifyClientCertIfGiven ,
} ,
{
2019-07-19 12:52:04 +03:00
desc : "VerifyClientCertIfGiven option without CAFiles yields a default ClientAuthType (NoClientCert)" ,
2019-07-12 18:50:04 +03:00
tlsOptionsName : "vccigwca" ,
expectedClientAuth : tls . NoClientCert ,
2019-07-19 12:52:04 +03:00
expectedError : true ,
2019-07-12 18:50:04 +03:00
} ,
{
2019-07-19 12:52:04 +03:00
desc : "RequireAndVerifyClientCert option without CAFiles yields a default ClientAuthType (NoClientCert)" ,
2019-07-12 18:50:04 +03:00
tlsOptionsName : "ravcc" ,
expectedClientAuth : tls . NoClientCert ,
2019-07-19 12:52:04 +03:00
expectedError : true ,
2019-07-12 18:50:04 +03:00
} ,
{
desc : "RequireAndVerifyClientCert option should get a tls.RequireAndVerifyClientCert as ClientAuthType with CA files" ,
tlsOptionsName : "ravccwca" ,
expectedClientAuth : tls . RequireAndVerifyClientCert ,
expectedRawSubject : cert . RawSubject ,
} ,
{
desc : "Unknown option yields a default ClientAuthType (NoClientCert)" ,
tlsOptionsName : "ucat" ,
expectedClientAuth : tls . NoClientCert ,
2019-07-19 12:52:04 +03:00
expectedError : true ,
2019-07-12 18:50:04 +03:00
} ,
{
desc : "Bad CA certificate content yields a default ClientAuthType (NoClientCert)" ,
tlsOptionsName : "ravccwbca" ,
expectedClientAuth : tls . NoClientCert ,
2019-07-19 12:52:04 +03:00
expectedError : true ,
2019-07-12 18:50:04 +03:00
} ,
}
tlsManager := NewManager ( )
2019-09-13 20:28:04 +03:00
tlsManager . UpdateConfigs ( context . Background ( ) , nil , tlsConfigs , nil )
2019-07-12 18:50:04 +03:00
for _ , test := range testCases {
t . Run ( test . desc , func ( t * testing . T ) {
t . Parallel ( )
config , err := tlsManager . Get ( "default" , test . tlsOptionsName )
2019-07-19 12:52:04 +03:00
if test . expectedError {
assert . Error ( t , err )
return
}
2019-07-12 18:50:04 +03:00
assert . NoError ( t , err )
if test . expectedRawSubject != nil {
subjects := config . ClientCAs . Subjects ( )
assert . Len ( t , subjects , 1 )
2023-11-17 03:50:06 +03:00
assert . Equal ( t , test . expectedRawSubject , subjects [ 0 ] )
2019-07-12 18:50:04 +03:00
}
2023-11-17 03:50:06 +03:00
assert . Equal ( t , test . expectedClientAuth , config . ClientAuth )
2019-07-12 18:50:04 +03:00
} )
}
}
2022-09-08 11:56:08 +03:00
func TestManager_Get_DefaultValues ( t * testing . T ) {
tlsManager := NewManager ( )
// Ensures we won't break things for Traefik users when updating Go
config , _ := tlsManager . Get ( "default" , "default" )
2023-11-17 03:50:06 +03:00
assert . Equal ( t , uint16 ( tls . VersionTLS12 ) , config . MinVersion )
assert . Equal ( t , [ ] string { "h2" , "http/1.1" , "acme-tls/1" } , config . NextProtos )
assert . Equal ( t , [ ] uint16 {
2022-09-08 11:56:08 +03:00
tls . TLS_AES_128_GCM_SHA256 ,
tls . TLS_AES_256_GCM_SHA384 ,
tls . TLS_CHACHA20_POLY1305_SHA256 ,
tls . TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA ,
tls . TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ,
tls . TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA ,
tls . TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ,
tls . TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ,
tls . TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ,
tls . TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ,
tls . TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ,
tls . TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 ,
tls . TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 ,
2023-11-17 03:50:06 +03:00
} , config . CipherSuites )
2022-09-08 11:56:08 +03:00
}