2018-11-14 09:18:03 +00:00
package passtlsclientcert
2018-08-29 09:36:03 +00:00
import (
2018-11-14 09:18:03 +00:00
"context"
2018-08-29 09:36:03 +00:00
"crypto/tls"
"crypto/x509"
"encoding/pem"
"net"
"net/http"
"net/http/httptest"
"net/url"
"regexp"
"strings"
"testing"
2018-11-14 09:18:03 +00:00
"github.com/containous/traefik/config"
2018-08-29 09:36:03 +00:00
"github.com/containous/traefik/testhelpers"
"github.com/stretchr/testify/require"
)
const (
rootCrt = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIDhjCCAm6gAwIBAgIJAIKZlW9a3VrYMA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNV
BAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQHDAhUb3Vsb3VzZTEh
MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTE4MDcxNzIwMzQz
OFoXDTE4MDgxNjIwMzQzOFowWDELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUt
U3RhdGUxETAPBgNVBAcMCFRvdWxvdXNlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn
aXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1P8GJ
H9LkIxIIqK9MyUpushnjmjwccpSMB3OecISKYLy62QDIcAw6NzGcSe8hMwciMJr +
CdCjJlohybnaRI9hrJ3GPnI ++ UT / MMthf2IIcjmJxmD4k9L1fgs1V6zSTlo0 + o0x
0 gkAGlWvRkgA + 3 nt555ee84XQZuneKKeRRIlSA1ygycewFobZ / pGYijIEko + gYkV
sF3LnRGxNl673w + EQsvI7 + z29T1nzjmM / xE7WlvnsrVd1 / N61jAohLota0YTufwd
ioJZNryzuPejHBCiQRGMbJ7uEEZLiSCN6QiZEfqhS3AulykjgFXQQHn4zoVljSBR
UyLV0prIn5Scbks / AgMBAAGjUzBRMB0GA1UdDgQWBBTroRRnSgtkV + 8 dumtcftb /
lwIkATAfBgNVHSMEGDAWgBTroRRnSgtkV + 8 dumtcftb / lwIkATAPBgNVHRMBAf8E
BTADAQH / MA0GCSqGSIb3DQEBCwUAA4IBAQAJ67U5cLa0ZFa / 7 zQQT4ldkY6YOEgR
0 LNoTu51hc + ozaXSvF8YIBzkEpEnbGS3x4xodrwEBZjK2LFhNu / 33 gkCAuhmedgk
KwZrQM6lqRFGHGVOlkVz + QrJ2EsKYaO4SCUIwVjijXRLA7A30G5C / CIh66PsMgBY
6 QHXVPEWm / v1d1Q / DfFfFzSOa1n1rIUw03qVJsxqSwfwYcegOF8YvS / eH4HUr2gF
cEujh6CCnylf35ExHa45atr3 + xxbOVdNjobISkYADtbhAAn4KjLS4v8W6445vxxj
G5EIZLjOHyWg1sGaHaaAPkVpZQg8EKm21c4hrEEMfel60AMSSzad / a / V
-- -- - END CERTIFICATE -- -- - `
minimalCert = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIDGTCCAgECCQCqLd75YLi2kDANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJG
UjETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIVG91bG91c2UxITAfBgNV
BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xODA3MTgwODI4MTZaFw0x
ODA4MTcwODI4MTZaMEUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRl
MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQC / + frDMMTLQyXG34F68BPhQq0kzK4LIq9Y0 / gl
FjySZNn1C0QDWA1ubVCAcA6yY204I9cxcQDPNrhC7JlS5QA8Y5rhIBrqQlzZizAi
Rj3NTrRjtGUtOScnHuJaWjLy03DWD + aMwb7q718xt5SEABmmUvLwQK + EjW2MeDwj
y8 / UEIpvrRDmdhGaqv7IFpIDkcIF7FowJ / hwDvx3PMc + z / JWK0ovzpvgbx69AVbw
ZxCimeha65rOqVi + lEetD26le + WnOdYsdJ2IkmpPNTXGdfb15xuAc + gFXfMCh7Iw
3 Ynl6dZtZM / Ok2kiA7 / OsmVnRKkWrtBfGYkI9HcNGb3zrk6nAgMBAAEwDQYJKoZI
hvcNAQELBQADggEBAC / R + Yvhh1VUhcbK49olWsk / JKqfS3VIDQYZg1Eo + JCPbwgS
I1BSYVfMcGzuJTX6ua3m / AHzGF3Tap4GhF4tX12jeIx4R4utnjj7 / YKkTvuEM2f4
xT56YqI7zalGScIB0iMeyNz1QcimRl + M / 49 au8ow9hNX8C2tcA2cwd / 9 OIj / 6 T8q
SBRHc6ojvbqZSJCO0jziGDT1L3D + EDgTjED4nd77v / NRdP + egb0q3P0s4dnQ / 5 AV
aQlQADUn61j3ScbGJ4NSeZFFvsl38jeRi / MEzp0bGgNBcPj6JHi7qbbauZcZfQ05
jECvgAY7Nfd9mZ1KtyNaW31is + kag7NsvjxU / kM =
-- -- - END CERTIFICATE -- -- - `
completeCert = ` Certificate :
Data :
Version : 3 ( 0x2 )
Serial Number : 3 ( 0x3 )
Signature Algorithm : sha1WithRSAEncryption
Issuer : C = FR , ST = Some - State , L = Toulouse , O = Internet Widgits Pty Ltd
Validity
Not Before : Jul 18 0 8 : 00 : 16 2018 GMT
Not After : Jul 18 0 8 : 00 : 16 2019 GMT
2018-11-14 09:18:03 +00:00
subject : C = FR , ST = SomeState , L = Toulouse , O = Cheese , CN = * . cheese . org
subject Public Key Info :
2018-08-29 09:36:03 +00:00
Public Key Algorithm : rsaEncryption
Public - Key : ( 2048 bit )
Modulus :
00 : a6 : 1 f : 96 : 7 c : c1 : cc : b8 : 1 c : b5 : 91 : 5 d : b8 : bf : 70 :
bc : f7 : b8 : 04 : 4 f : 2 a : 42 : de : ea : c5 : c3 : 19 : 0 b : 03 : 04 :
ec : ef : a1 : 24 : 25 : de : ad : 05 : e7 : 26 : ea : 89 : 6 c : 59 : 60 :
10 : 18 : 0 c : 73 : f1 : bf : d3 : cc : 7 b : ed : 6 b : 9 c : ea : 1 d : 88 :
e2 : ee : 14 : 81 : d7 : 07 : ee : 87 : 95 : 3 d : 36 : df : 9 c : 38 : b7 :
7 b : 1 e : 2 b : 51 : 9 c : 4 a : 1 f : d0 : cc : 5 b : af : 5 d : 6 c : 5 c : 35 :
49 : 32 : e4 : 01 : 5 b : f9 : 8 c : 71 : cf : 62 : 48 : 5 a : ea : b7 : 31 :
58 : e2 : c6 : d0 : 5 b : 1 c : 50 : b5 : 5 c : 6 d : 5 a : 6 f : da : 41 : 5 e :
d5 : 4 c : 6 e : 1 a : 21 : f3 : 40 : f9 : 9 e : 52 : 76 : 50 : 25 : 3 e : 03 :
9 b : 87 : 19 : 48 : 5 b : 47 : 87 : d3 : 67 : c6 : 25 : 69 : 77 : 29 : 8 e :
56 : 97 : 45 : d9 : 6 f : 64 : a8 : 4 e : ad : 35 : 75 : 2 e : fc : 6 a : 2 e :
47 : 87 : 76 : fc : 4 e : 3 e : 44 : e9 : 16 : b2 : c7 : f0 : 23 : 98 : 13 :
a2 : df : 15 : 23 : cb : 0 c : 3 d : fd : 48 : 5 e : c7 : 2 c : 86 : 70 : 63 :
8 b : c6 : c8 : 89 : 17 : 52 : d5 : a7 : 8 e : cb : 4 e : 11 : 9 d : 69 : 8 e :
8 e : 59 : cc : 7 e : a3 : bd : a1 : 11 : 88 : d7 : cf : 7 b : 8 c : 19 : 46 :
9 c : 1 b : 7 a : c9 : 39 : 81 : 4 c : 58 : 0 8 : 1 f : c7 : ce : b0 : 0 e : 79 :
64 : d3 : 11 : 72 : 65 : e6 : dd : bd : 00 : 7 f : 22 : 30 : 46 : 9 b : 66 :
9 c : b9
Exponent : 65537 ( 0x10001 )
X509v3 extensions :
X509v3 Basic Constraints :
CA : FALSE
2018-11-14 09:18:03 +00:00
X509v3 subject Alternative Name :
2018-08-29 09:36:03 +00:00
DNS : * . cheese . org , DNS : * . cheese . net , DNS : cheese . in , IP Address : 10.0 .1 .0 , IP Address : 10.0 .1 .2 , email : test @ cheese . org , email : test @ cheese . net
2018-11-14 09:18:03 +00:00
X509v3 subject Key Identifier :
2018-08-29 09:36:03 +00:00
AB : 6 B : 89 : 25 : 11 : FC : 5 E : 7 B : D4 : B0 : F7 : D4 : B6 : D9 : EB : D0 : 30 : 93 : E5 : 58
Signature Algorithm : sha1WithRSAEncryption
ad : 87 : 84 : a0 : 88 : a3 : 4 c : d9 : 0 a : c0 : 14 : e4 : 2 d : 9 a : 1 d : bb : 57 : b7 :
12 : ef : 3 a : fb : 8 b : b2 : ce : 32 : b8 : 04 : e6 : 59 : c8 : 4 f : 14 : 6 a : b5 : 12 :
46 : e9 : c9 : 0 a : 11 : 64 : ea : a1 : 86 : 20 : 96 : 0 e : a7 : 40 : e3 : aa : e5 : 98 :
91 : 36 : 89 : 77 : b6 : b9 : 73 : 7 e : 1 a : 58 : 19 : ae : d1 : 14 : 83 : 1 e : c1 : 5 f :
a5 : a0 : 32 : bb : 52 : 68 : b4 : 8 d : a3 : 1 d : b3 : 0 8 : d7 : 45 : 6 e : 3 b : 87 : 64 :
7 e : ef : 46 : e6 : 6 f : d5 : 79 : d7 : 1 d : 57 : 68 : 67 : d8 : 18 : 39 : 61 : 5 b : 8 b :
1 a : 7 f : 88 : da : 0 a : 51 : 9 b : 3 d : 6 c : 5 d : b1 : cf : b7 : e9 : 1 e : 06 : 65 : 8 e :
96 : d3 : 61 : 96 : f8 : a2 : 61 : f9 : 40 : 5 e : fa : bc : 76 : b9 : 64 : 0 e : 6 f : 90 :
37 : de : ac : 6 d : 7 f : 36 : 84 : 35 : 19 : 88 : 8 c : 26 : af : 3 e : c3 : 6 a : 1 a : 03 :
ed : d7 : 90 : 89 : ed : 18 : 4 c : 9 e : 94 : 1 f : d8 : ae : 6 c : 61 : 36 : 17 : 72 : f9 :
bb : de : 0 a : 56 : 9 a : 79 : b4 : 7 d : 4 a : 9 d : cb : 4 a : 7 d : 71 : 9 f : 38 : e7 : 8 d :
f0 : 87 : 24 : 21 : 0 a : 24 : 1 f : 82 : 9 a : 6 b : 67 : ce : 7 d : af : cb : 91 : 6 b : 8 a :
de : e6 : d8 : 6 f : a1 : 37 : b9 : 2 d : d0 : cb : e8 : 4 e : f4 : 43 : af : ad : 90 : 13 :
7 d : 61 : 7 a : ce : 86 : 48 : fc : 00 : 8 c : 37 : fb : e0 : 31 : 6 b : e2 : 18 : ad : fd :
1 e : df : 0 8 : db
-- -- - BEGIN CERTIFICATE -- -- -
MIIDvTCCAqWgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJGUjET
MBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIVG91bG91c2UxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xODA3MTgwODAwMTZaFw0xOTA3
MTgwODAwMTZaMFwxCzAJBgNVBAYTAkZSMRIwEAYDVQQIDAlTb21lU3RhdGUxETAP
BgNVBAcMCFRvdWxvdXNlMQ8wDQYDVQQKDAZDaGVlc2UxFTATBgNVBAMMDCouY2hl
ZXNlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKYflnzBzLgc
tZFduL9wvPe4BE8qQt7qxcMZCwME7O + hJCXerQXnJuqJbFlgEBgMc / G / 0 8 x77Wuc
6 h2I4u4UgdcH7oeVPTbfnDi3ex4rUZxKH9DMW69dbFw1STLkAVv5jHHPYkha6rcx
WOLG0FscULVcbVpv2kFe1UxuGiHzQPmeUnZQJT4Dm4cZSFtHh9NnxiVpdymOVpdF
2 W9kqE6tNXUu / GouR4d2 / E4 + ROkWssfwI5gTot8VI8sMPf1IXscshnBji8bIiRdS
1 aeOy04RnWmOjlnMfqO9oRGI1897jBlGnBt6yTmBTFgIH8fOsA55ZNMRcmXm3b0A
fyIwRptmnLkCAwEAAaOBjTCBijAJBgNVHRMEAjAAMF4GA1UdEQRXMFWCDCouY2hl
ZXNlLm9yZ4IMKi5jaGVlc2UubmV0ggljaGVlc2UuaW6HBAoAAQCHBAoAAQKBD3Rl
c3RAY2hlZXNlLm9yZ4EPdGVzdEBjaGVlc2UubmV0MB0GA1UdDgQWBBSra4klEfxe
e9Sw99S22evQMJPlWDANBgkqhkiG9w0BAQUFAAOCAQEArYeEoIijTNkKwBTkLZod
u1e3Eu86 + 4 uyzjK4BOZZyE8UarUSRunJChFk6qGGIJYOp0DjquWYkTaJd7a5c34a
WBmu0RSDHsFfpaAyu1JotI2jHbMI10VuO4dkfu9G5m / VedcdV2hn2Bg5YVuLGn + I
2 gpRmz1sXbHPt + keBmWOltNhlviiYflAXvq8drlkDm + QN96sbX82hDUZiIwmrz7D
ahoD7deQie0YTJ6UH9iubGE2F3L5u94KVpp5tH1KnctKfXGfOOeN8IckIQokH4Ka
a2fOfa / LkWuK3ubYb6E3uS3Qy + hO9EOvrZATfWF6zoZI / ACMN / vgMWviGK39Ht8I
2 w ==
-- -- - END CERTIFICATE -- -- -
`
)
func getCleanCertContents ( certContents [ ] string ) string {
var re = regexp . MustCompile ( "-----BEGIN CERTIFICATE-----(?s)(.*)" )
var cleanedCertContent [ ] string
for _ , certContent := range certContents {
2018-11-14 09:18:03 +00:00
cert := re . FindString ( certContent )
2018-08-29 09:36:03 +00:00
cleanedCertContent = append ( cleanedCertContent , sanitize ( [ ] byte ( cert ) ) )
}
return strings . Join ( cleanedCertContent , "," )
}
func getCertificate ( certContent string ) * x509 . Certificate {
roots := x509 . NewCertPool ( )
ok := roots . AppendCertsFromPEM ( [ ] byte ( rootCrt ) )
if ! ok {
panic ( "failed to parse root certificate" )
}
block , _ := pem . Decode ( [ ] byte ( certContent ) )
if block == nil {
panic ( "failed to parse certificate PEM" )
}
cert , err := x509 . ParseCertificate ( block . Bytes )
if err != nil {
panic ( "failed to parse certificate: " + err . Error ( ) )
}
return cert
}
func buildTLSWith ( certContents [ ] string ) * tls . ConnectionState {
var peerCertificates [ ] * x509 . Certificate
for _ , certContent := range certContents {
peerCertificates = append ( peerCertificates , getCertificate ( certContent ) )
}
return & tls . ConnectionState { PeerCertificates : peerCertificates }
}
2018-11-14 09:18:03 +00:00
var next = http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
_ , err := w . Write ( [ ] byte ( "bar" ) )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
}
2018-08-29 09:36:03 +00:00
} )
func getExpectedSanitized ( s string ) string {
return url . QueryEscape ( strings . Replace ( s , "\n" , "" , - 1 ) )
}
func TestSanitize ( t * testing . T ) {
testCases := [ ] struct {
desc string
toSanitize [ ] byte
expected string
} {
{
desc : "Empty" ,
} ,
{
desc : "With a minimal cert" ,
toSanitize : [ ] byte ( minimalCert ) ,
expected : getExpectedSanitized ( ` MIIDGTCCAgECCQCqLd75YLi2kDANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJG
UjETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIVG91bG91c2UxITAfBgNV
BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xODA3MTgwODI4MTZaFw0x
ODA4MTcwODI4MTZaMEUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRl
MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQC / + frDMMTLQyXG34F68BPhQq0kzK4LIq9Y0 / gl
FjySZNn1C0QDWA1ubVCAcA6yY204I9cxcQDPNrhC7JlS5QA8Y5rhIBrqQlzZizAi
Rj3NTrRjtGUtOScnHuJaWjLy03DWD + aMwb7q718xt5SEABmmUvLwQK + EjW2MeDwj
y8 / UEIpvrRDmdhGaqv7IFpIDkcIF7FowJ / hwDvx3PMc + z / JWK0ovzpvgbx69AVbw
ZxCimeha65rOqVi + lEetD26le + WnOdYsdJ2IkmpPNTXGdfb15xuAc + gFXfMCh7Iw
3 Ynl6dZtZM / Ok2kiA7 / OsmVnRKkWrtBfGYkI9HcNGb3zrk6nAgMBAAEwDQYJKoZI
hvcNAQELBQADggEBAC / R + Yvhh1VUhcbK49olWsk / JKqfS3VIDQYZg1Eo + JCPbwgS
I1BSYVfMcGzuJTX6ua3m / AHzGF3Tap4GhF4tX12jeIx4R4utnjj7 / YKkTvuEM2f4
xT56YqI7zalGScIB0iMeyNz1QcimRl + M / 49 au8ow9hNX8C2tcA2cwd / 9 OIj / 6 T8q
SBRHc6ojvbqZSJCO0jziGDT1L3D + EDgTjED4nd77v / NRdP + egb0q3P0s4dnQ / 5 AV
aQlQADUn61j3ScbGJ4NSeZFFvsl38jeRi / MEzp0bGgNBcPj6JHi7qbbauZcZfQ05
jECvgAY7Nfd9mZ1KtyNaW31is + kag7NsvjxU / kM = ` ) ,
} ,
}
for _ , test := range testCases {
test := test
t . Run ( test . desc , func ( t * testing . T ) {
t . Parallel ( )
require . Equal ( t , test . expected , sanitize ( test . toSanitize ) , "The sanitized certificates should be equal" )
} )
}
}
2018-11-14 09:18:03 +00:00
func TestTLSClientHeadersWithPEM ( t * testing . T ) {
2018-08-29 09:36:03 +00:00
testCases := [ ] struct {
2018-11-14 09:18:03 +00:00
desc string
certContents [ ] string // set the request TLS attribute if defined
config config . PassTLSClientCert
expectedHeader string
2018-08-29 09:36:03 +00:00
} {
{
desc : "No TLS, no option" ,
} ,
{
desc : "TLS, no option" ,
certContents : [ ] string { minimalCert } ,
} ,
{
2018-11-14 09:18:03 +00:00
desc : "No TLS, with pem option true" ,
config : config . PassTLSClientCert { PEM : true } ,
2018-08-29 09:36:03 +00:00
} ,
{
2018-11-14 09:18:03 +00:00
desc : "TLS with simple certificate, with pem option true" ,
certContents : [ ] string { minimalCert } ,
config : config . PassTLSClientCert { PEM : true } ,
expectedHeader : getCleanCertContents ( [ ] string { minimalCert } ) ,
2018-08-29 09:36:03 +00:00
} ,
{
2018-11-14 09:18:03 +00:00
desc : "TLS with complete certificate, with pem option true" ,
certContents : [ ] string { completeCert } ,
config : config . PassTLSClientCert { PEM : true } ,
expectedHeader : getCleanCertContents ( [ ] string { completeCert } ) ,
2018-08-29 09:36:03 +00:00
} ,
{
2018-11-14 09:18:03 +00:00
desc : "TLS with two certificate, with pem option true" ,
certContents : [ ] string { minimalCert , completeCert } ,
config : config . PassTLSClientCert { PEM : true } ,
expectedHeader : getCleanCertContents ( [ ] string { minimalCert , completeCert } ) ,
2018-08-29 09:36:03 +00:00
} ,
}
for _ , test := range testCases {
2018-11-14 09:18:03 +00:00
tlsClientHeaders , err := New ( context . Background ( ) , next , test . config , "foo" )
require . NoError ( t , err )
2018-08-29 09:36:03 +00:00
res := httptest . NewRecorder ( )
req := testhelpers . MustNewRequest ( http . MethodGet , "http://example.com/foo" , nil )
if test . certContents != nil && len ( test . certContents ) > 0 {
req . TLS = buildTLSWith ( test . certContents )
}
2018-11-14 09:18:03 +00:00
tlsClientHeaders . ServeHTTP ( res , req )
2018-08-29 09:36:03 +00:00
test := test
t . Run ( test . desc , func ( t * testing . T ) {
t . Parallel ( )
require . Equal ( t , http . StatusOK , res . Code , "Http Status should be OK" )
require . Equal ( t , "bar" , res . Body . String ( ) , "Should be the expected body" )
if test . expectedHeader != "" {
require . Equal ( t , getCleanCertContents ( test . certContents ) , req . Header . Get ( xForwardedTLSClientCert ) , "The request header should contain the cleaned certificate" )
} else {
require . Empty ( t , req . Header . Get ( xForwardedTLSClientCert ) )
}
require . Empty ( t , res . Header ( ) . Get ( xForwardedTLSClientCert ) , "The response header should be always empty" )
} )
}
}
func TestGetSans ( t * testing . T ) {
urlFoo , err := url . Parse ( "my.foo.com" )
require . NoError ( t , err )
urlBar , err := url . Parse ( "my.bar.com" )
require . NoError ( t , err )
testCases := [ ] struct {
desc string
cert * x509 . Certificate // set the request TLS attribute if defined
expected [ ] string
} {
{
desc : "With nil" ,
} ,
{
desc : "Certificate without Sans" ,
cert : & x509 . Certificate { } ,
} ,
{
desc : "Certificate with all Sans" ,
cert : & x509 . Certificate {
DNSNames : [ ] string { "foo" , "bar" } ,
EmailAddresses : [ ] string { "test@test.com" , "test2@test.com" } ,
IPAddresses : [ ] net . IP { net . IPv4 ( 10 , 0 , 0 , 1 ) , net . IPv4 ( 10 , 0 , 0 , 2 ) } ,
URIs : [ ] * url . URL { urlFoo , urlBar } ,
} ,
expected : [ ] string { "foo" , "bar" , "test@test.com" , "test2@test.com" , "10.0.0.1" , "10.0.0.2" , urlFoo . String ( ) , urlBar . String ( ) } ,
} ,
}
for _ , test := range testCases {
sans := getSANs ( test . cert )
2018-11-14 09:18:03 +00:00
test := test
2018-08-29 09:36:03 +00:00
t . Run ( test . desc , func ( t * testing . T ) {
t . Parallel ( )
if len ( test . expected ) > 0 {
for i , expected := range test . expected {
require . Equal ( t , expected , sans [ i ] )
}
} else {
require . Empty ( t , sans )
}
} )
}
}
2018-11-14 09:18:03 +00:00
func TestTLSClientHeadersWithCertInfos ( t * testing . T ) {
2018-08-29 09:36:03 +00:00
minimalCertAllInfos := ` Subject="C=FR,ST=Some-State,O=Internet Widgits Pty Ltd",NB=1531902496,NA=1534494496,SAN= `
completeCertAllInfos := ` Subject="C=FR,ST=SomeState,L=Toulouse,O=Cheese,CN=*.cheese.org",NB=1531900816,NA=1563436816,SAN=*.cheese.org,*.cheese.net,cheese.in,test@cheese.org,test@cheese.net,10.0.1.0,10.0.1.2 `
testCases := [ ] struct {
2018-11-14 09:18:03 +00:00
desc string
certContents [ ] string // set the request TLS attribute if defined
config config . PassTLSClientCert
expectedHeader string
2018-08-29 09:36:03 +00:00
} {
{
desc : "No TLS, no option" ,
} ,
{
desc : "TLS, no option" ,
certContents : [ ] string { minimalCert } ,
} ,
{
desc : "No TLS, with pem option true" ,
2018-11-14 09:18:03 +00:00
config : config . PassTLSClientCert {
Infos : & config . TLSClientCertificateInfos {
Subject : & config . TLSCLientCertificateSubjectInfos {
2018-08-29 09:36:03 +00:00
CommonName : true ,
Organization : true ,
Locality : true ,
Province : true ,
Country : true ,
SerialNumber : true ,
} ,
} ,
} ,
} ,
{
desc : "No TLS, with pem option true with no flag" ,
2018-11-14 09:18:03 +00:00
config : config . PassTLSClientCert {
2018-08-29 09:36:03 +00:00
PEM : false ,
2018-11-14 09:18:03 +00:00
Infos : & config . TLSClientCertificateInfos {
Subject : & config . TLSCLientCertificateSubjectInfos { } ,
2018-08-29 09:36:03 +00:00
} ,
} ,
} ,
{
desc : "TLS with simple certificate, with all infos" ,
certContents : [ ] string { minimalCert } ,
2018-11-14 09:18:03 +00:00
config : config . PassTLSClientCert {
Infos : & config . TLSClientCertificateInfos {
2018-08-29 09:36:03 +00:00
NotAfter : true ,
NotBefore : true ,
2018-11-14 09:18:03 +00:00
Subject : & config . TLSCLientCertificateSubjectInfos {
2018-08-29 09:36:03 +00:00
CommonName : true ,
Organization : true ,
Locality : true ,
Province : true ,
Country : true ,
SerialNumber : true ,
} ,
Sans : true ,
} ,
} ,
expectedHeader : url . QueryEscape ( minimalCertAllInfos ) ,
} ,
{
desc : "TLS with simple certificate, with some infos" ,
certContents : [ ] string { minimalCert } ,
2018-11-14 09:18:03 +00:00
config : config . PassTLSClientCert {
Infos : & config . TLSClientCertificateInfos {
2018-08-29 09:36:03 +00:00
NotAfter : true ,
2018-11-14 09:18:03 +00:00
Subject : & config . TLSCLientCertificateSubjectInfos {
2018-08-29 09:36:03 +00:00
Organization : true ,
} ,
Sans : true ,
} ,
} ,
expectedHeader : url . QueryEscape ( ` Subject="O=Internet Widgits Pty Ltd",NA=1534494496,SAN= ` ) ,
} ,
{
desc : "TLS with complete certificate, with all infos" ,
certContents : [ ] string { completeCert } ,
2018-11-14 09:18:03 +00:00
config : config . PassTLSClientCert {
Infos : & config . TLSClientCertificateInfos {
2018-08-29 09:36:03 +00:00
NotAfter : true ,
NotBefore : true ,
2018-11-14 09:18:03 +00:00
Subject : & config . TLSCLientCertificateSubjectInfos {
2018-08-29 09:36:03 +00:00
CommonName : true ,
Organization : true ,
Locality : true ,
Province : true ,
Country : true ,
SerialNumber : true ,
} ,
Sans : true ,
} ,
} ,
expectedHeader : url . QueryEscape ( completeCertAllInfos ) ,
} ,
{
desc : "TLS with 2 certificates, with all infos" ,
certContents : [ ] string { minimalCert , completeCert } ,
2018-11-14 09:18:03 +00:00
config : config . PassTLSClientCert {
Infos : & config . TLSClientCertificateInfos {
2018-08-29 09:36:03 +00:00
NotAfter : true ,
NotBefore : true ,
2018-11-14 09:18:03 +00:00
Subject : & config . TLSCLientCertificateSubjectInfos {
2018-08-29 09:36:03 +00:00
CommonName : true ,
Organization : true ,
Locality : true ,
Province : true ,
Country : true ,
SerialNumber : true ,
} ,
Sans : true ,
} ,
} ,
expectedHeader : url . QueryEscape ( strings . Join ( [ ] string { minimalCertAllInfos , completeCertAllInfos } , ";" ) ) ,
} ,
}
for _ , test := range testCases {
2018-11-14 09:18:03 +00:00
tlsClientHeaders , err := New ( context . Background ( ) , next , test . config , "foo" )
require . NoError ( t , err )
2018-08-29 09:36:03 +00:00
res := httptest . NewRecorder ( )
req := testhelpers . MustNewRequest ( http . MethodGet , "http://example.com/foo" , nil )
if test . certContents != nil && len ( test . certContents ) > 0 {
req . TLS = buildTLSWith ( test . certContents )
}
2018-11-14 09:18:03 +00:00
tlsClientHeaders . ServeHTTP ( res , req )
2018-08-29 09:36:03 +00:00
test := test
t . Run ( test . desc , func ( t * testing . T ) {
t . Parallel ( )
require . Equal ( t , http . StatusOK , res . Code , "Http Status should be OK" )
require . Equal ( t , "bar" , res . Body . String ( ) , "Should be the expected body" )
if test . expectedHeader != "" {
require . Equal ( t , test . expectedHeader , req . Header . Get ( xForwardedTLSClientCertInfos ) , "The request header should contain the cleaned certificate" )
} else {
require . Empty ( t , req . Header . Get ( xForwardedTLSClientCertInfos ) )
}
require . Empty ( t , res . Header ( ) . Get ( xForwardedTLSClientCertInfos ) , "The response header should be always empty" )
} )
}
}