Add serial number certificate to forward headers

This commit is contained in:
David 2019-12-12 00:32:03 +01:00 committed by Traefiker Bot
parent 3f1484480e
commit 5f50d2e230
8 changed files with 52 additions and 29 deletions

View file

@ -70,6 +70,7 @@ http:
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.notafter=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.notafter=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.notbefore=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.notbefore=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.sans=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.sans=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.serialnumber=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.commonname=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.commonname=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.country=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.country=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true"
@ -482,7 +483,7 @@ SAN="*.cheese.org,*.cheese.net,*.cheese.com,test@cheese.org,test@cheese.net,10.0
!!! info "multiple values" !!! info "multiple values"
All the SANs data are separated by a `,`. All the SANs data are separated by a `,`.
#### `info.subject` #### `info.subject`
The `info.subject` select the specific client certificate subject details you want to add to the `X-Forwarded-Tls-Client-Cert-Info` header. The `info.subject` select the specific client certificate subject details you want to add to the `X-Forwarded-Tls-Client-Cert-Info` header.

View file

@ -196,6 +196,7 @@
notAfter = true notAfter = true
notBefore = true notBefore = true
sans = true sans = true
serialNumber = true
[http.middlewares.Middleware12.passTLSClientCert.info.subject] [http.middlewares.Middleware12.passTLSClientCert.info.subject]
country = true country = true
province = true province = true

View file

@ -239,6 +239,7 @@ http:
commonName: true commonName: true
serialNumber: true serialNumber: true
domainComponent: true domainComponent: true
serialNumber: true
Middleware13: Middleware13:
rateLimit: rateLimit:
average: 42 average: 42

View file

@ -92,6 +92,7 @@
| `traefik/http/middlewares/Middleware12/passTLSClientCert/info/notAfter` | `true` | | `traefik/http/middlewares/Middleware12/passTLSClientCert/info/notAfter` | `true` |
| `traefik/http/middlewares/Middleware12/passTLSClientCert/info/notBefore` | `true` | | `traefik/http/middlewares/Middleware12/passTLSClientCert/info/notBefore` | `true` |
| `traefik/http/middlewares/Middleware12/passTLSClientCert/info/sans` | `true` | | `traefik/http/middlewares/Middleware12/passTLSClientCert/info/sans` | `true` |
| `traefik/http/middlewares/Middleware12/passTLSClientCert/info/serialNumber` | `true` |
| `traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/commonName` | `true` | | `traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/commonName` | `true` |
| `traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/country` | `true` | | `traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/country` | `true` |
| `traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/domainComponent` | `true` | | `traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/domainComponent` | `true` |

View file

@ -377,11 +377,12 @@ type StripPrefixRegex struct {
// TLSClientCertificateInfo holds the client TLS certificate info configuration. // TLSClientCertificateInfo holds the client TLS certificate info configuration.
type TLSClientCertificateInfo struct { type TLSClientCertificateInfo struct {
NotAfter bool `json:"notAfter,omitempty" toml:"notAfter,omitempty" yaml:"notAfter,omitempty"` NotAfter bool `json:"notAfter,omitempty" toml:"notAfter,omitempty" yaml:"notAfter,omitempty"`
NotBefore bool `json:"notBefore,omitempty" toml:"notBefore,omitempty" yaml:"notBefore,omitempty"` NotBefore bool `json:"notBefore,omitempty" toml:"notBefore,omitempty" yaml:"notBefore,omitempty"`
Sans bool `json:"sans,omitempty" toml:"sans,omitempty" yaml:"sans,omitempty"` Sans bool `json:"sans,omitempty" toml:"sans,omitempty" yaml:"sans,omitempty"`
Subject *TLSCLientCertificateDNInfo `json:"subject,omitempty" toml:"subject,omitempty" yaml:"subject,omitempty"` Subject *TLSCLientCertificateDNInfo `json:"subject,omitempty" toml:"subject,omitempty" yaml:"subject,omitempty"`
Issuer *TLSCLientCertificateDNInfo `json:"issuer,omitempty" toml:"issuer,omitempty" yaml:"issuer,omitempty"` Issuer *TLSCLientCertificateDNInfo `json:"issuer,omitempty" toml:"issuer,omitempty" yaml:"issuer,omitempty"`
SerialNumber bool `json:"serialNumber,omitempty" toml:"serialNumber,omitempty" yaml:"serialNumber,omitempty"`
} }
// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen=true

View file

@ -84,6 +84,7 @@ func TestDecodeConfiguration(t *testing.T) {
"traefik.http.middlewares.Middleware11.passtlsclientcert.info.notafter": "true", "traefik.http.middlewares.Middleware11.passtlsclientcert.info.notafter": "true",
"traefik.http.middlewares.Middleware11.passtlsclientcert.info.notbefore": "true", "traefik.http.middlewares.Middleware11.passtlsclientcert.info.notbefore": "true",
"traefik.http.middlewares.Middleware11.passtlsclientcert.info.sans": "true", "traefik.http.middlewares.Middleware11.passtlsclientcert.info.sans": "true",
"traefik.http.middlewares.Middleware11.passTLSClientCert.info.serialNumber": "true",
"traefik.http.middlewares.Middleware11.passtlsclientcert.info.subject.commonname": "true", "traefik.http.middlewares.Middleware11.passtlsclientcert.info.subject.commonname": "true",
"traefik.http.middlewares.Middleware11.passtlsclientcert.info.subject.country": "true", "traefik.http.middlewares.Middleware11.passtlsclientcert.info.subject.country": "true",
"traefik.http.middlewares.Middleware11.passtlsclientcert.info.subject.domaincomponent": "true", "traefik.http.middlewares.Middleware11.passtlsclientcert.info.subject.domaincomponent": "true",
@ -294,8 +295,9 @@ func TestDecodeConfiguration(t *testing.T) {
PassTLSClientCert: &dynamic.PassTLSClientCert{ PassTLSClientCert: &dynamic.PassTLSClientCert{
PEM: true, PEM: true,
Info: &dynamic.TLSClientCertificateInfo{ Info: &dynamic.TLSClientCertificateInfo{
NotAfter: true, NotAfter: true,
NotBefore: true, NotBefore: true,
SerialNumber: true,
Subject: &dynamic.TLSCLientCertificateDNInfo{ Subject: &dynamic.TLSCLientCertificateDNInfo{
Country: true, Country: true,
Province: true, Province: true,
@ -699,8 +701,9 @@ func TestEncodeConfiguration(t *testing.T) {
PassTLSClientCert: &dynamic.PassTLSClientCert{ PassTLSClientCert: &dynamic.PassTLSClientCert{
PEM: true, PEM: true,
Info: &dynamic.TLSClientCertificateInfo{ Info: &dynamic.TLSClientCertificateInfo{
NotAfter: true, NotAfter: true,
NotBefore: true, NotBefore: true,
SerialNumber: true,
Subject: &dynamic.TLSCLientCertificateDNInfo{ Subject: &dynamic.TLSCLientCertificateDNInfo{
Country: true, Country: true,
Province: true, Province: true,
@ -1061,6 +1064,7 @@ func TestEncodeConfiguration(t *testing.T) {
"traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.NotAfter": "true", "traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.NotAfter": "true",
"traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.NotBefore": "true", "traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.NotBefore": "true",
"traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.Sans": "true", "traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.Sans": "true",
"traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.SerialNumber": "true",
"traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.Subject.Country": "true", "traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.Subject.Country": "true",
"traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.Subject.Province": "true", "traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.Subject.Province": "true",
"traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.Subject.Locality": "true", "traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.Subject.Locality": "true",

View file

@ -64,11 +64,12 @@ func newDistinguishedNameOptions(info *dynamic.TLSCLientCertificateDNInfo) *Dist
// tlsClientCertificateInfo is a struct for specifying the configuration for the passTLSClientCert middleware. // tlsClientCertificateInfo is a struct for specifying the configuration for the passTLSClientCert middleware.
type tlsClientCertificateInfo struct { type tlsClientCertificateInfo struct {
notAfter bool notAfter bool
notBefore bool notBefore bool
sans bool sans bool
subject *DistinguishedNameOptions subject *DistinguishedNameOptions
issuer *DistinguishedNameOptions issuer *DistinguishedNameOptions
serialNumber bool
} }
func newTLSClientCertificateInfo(info *dynamic.TLSClientCertificateInfo) *tlsClientCertificateInfo { func newTLSClientCertificateInfo(info *dynamic.TLSClientCertificateInfo) *tlsClientCertificateInfo {
@ -77,11 +78,12 @@ func newTLSClientCertificateInfo(info *dynamic.TLSClientCertificateInfo) *tlsCli
} }
return &tlsClientCertificateInfo{ return &tlsClientCertificateInfo{
issuer: newDistinguishedNameOptions(info.Issuer), issuer: newDistinguishedNameOptions(info.Issuer),
notAfter: info.NotAfter, notAfter: info.NotAfter,
notBefore: info.NotBefore, notBefore: info.NotBefore,
subject: newDistinguishedNameOptions(info.Subject), subject: newDistinguishedNameOptions(info.Subject),
sans: info.Sans, serialNumber: info.SerialNumber,
sans: info.Sans,
} }
} }
@ -155,6 +157,13 @@ func (p *passTLSClientCert) getCertInfo(ctx context.Context, certs []*x509.Certi
values = append(values, fmt.Sprintf(`Issuer="%s"`, strings.TrimSuffix(issuer, subFieldSeparator))) values = append(values, fmt.Sprintf(`Issuer="%s"`, strings.TrimSuffix(issuer, subFieldSeparator)))
} }
if p.info.serialNumber && peerCert.SerialNumber != nil {
sn := peerCert.SerialNumber.String()
if sn != "" {
values = append(values, fmt.Sprintf(`SerialNumber="%s"`, strings.TrimSuffix(sn, subFieldSeparator)))
}
}
if p.info.notBefore { if p.info.notBefore {
values = append(values, fmt.Sprintf(`NB="%d"`, uint64(peerCert.NotBefore.Unix()))) values = append(values, fmt.Sprintf(`NB="%d"`, uint64(peerCert.NotBefore.Unix())))
} }

View file

@ -345,6 +345,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
minimalCheeseCertAllInfo := strings.Join([]string{ minimalCheeseCertAllInfo := strings.Join([]string{
`Subject="C=FR,ST=Some-State,O=Cheese"`, `Subject="C=FR,ST=Some-State,O=Cheese"`,
`Issuer="DC=org,DC=cheese,C=FR,C=US,ST=Signing State,ST=Signing State 2,L=TOULOUSE,L=LYON,O=Cheese,O=Cheese 2,CN=Simple Signing CA 2"`, `Issuer="DC=org,DC=cheese,C=FR,C=US,ST=Signing State,ST=Signing State 2,L=TOULOUSE,L=LYON,O=Cheese,O=Cheese 2,CN=Simple Signing CA 2"`,
`SerialNumber="481535886039632329873080491016862977516759989652"`,
`NB="1544094636"`, `NB="1544094636"`,
`NA="1632568236"`, `NA="1632568236"`,
}, fieldSeparator) }, fieldSeparator)
@ -352,6 +353,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
completeCertAllInfo := strings.Join([]string{ completeCertAllInfo := strings.Join([]string{
`Subject="DC=org,DC=cheese,C=FR,C=US,ST=Cheese org state,ST=Cheese com state,L=TOULOUSE,L=LYON,O=Cheese,O=Cheese 2,CN=*.cheese.com"`, `Subject="DC=org,DC=cheese,C=FR,C=US,ST=Cheese org state,ST=Cheese com state,L=TOULOUSE,L=LYON,O=Cheese,O=Cheese 2,CN=*.cheese.com"`,
`Issuer="DC=org,DC=cheese,C=FR,C=US,ST=Signing State,ST=Signing State 2,L=TOULOUSE,L=LYON,O=Cheese,O=Cheese 2,CN=Simple Signing CA 2"`, `Issuer="DC=org,DC=cheese,C=FR,C=US,ST=Signing State,ST=Signing State 2,L=TOULOUSE,L=LYON,O=Cheese,O=Cheese 2,CN=Simple Signing CA 2"`,
`SerialNumber="1"`,
`NB="1544094616"`, `NB="1544094616"`,
`NA="1607166616"`, `NA="1607166616"`,
`SAN="*.cheese.org,*.cheese.net,*.cheese.com,test@cheese.org,test@cheese.net,10.0.1.0,10.0.1.2"`, `SAN="*.cheese.org,*.cheese.net,*.cheese.com,test@cheese.org,test@cheese.net,10.0.1.0,10.0.1.2"`,
@ -399,9 +401,10 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
certContents: []string{minimalCheeseCrt}, certContents: []string{minimalCheeseCrt},
config: dynamic.PassTLSClientCert{ config: dynamic.PassTLSClientCert{
Info: &dynamic.TLSClientCertificateInfo{ Info: &dynamic.TLSClientCertificateInfo{
NotAfter: true, NotAfter: true,
NotBefore: true, NotBefore: true,
Sans: true, Sans: true,
SerialNumber: true,
Subject: &dynamic.TLSCLientCertificateDNInfo{ Subject: &dynamic.TLSCLientCertificateDNInfo{
CommonName: true, CommonName: true,
Country: true, Country: true,
@ -446,9 +449,10 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
certContents: []string{completeCheeseCrt}, certContents: []string{completeCheeseCrt},
config: dynamic.PassTLSClientCert{ config: dynamic.PassTLSClientCert{
Info: &dynamic.TLSClientCertificateInfo{ Info: &dynamic.TLSClientCertificateInfo{
NotAfter: true, NotAfter: true,
NotBefore: true, NotBefore: true,
Sans: true, Sans: true,
SerialNumber: true,
Subject: &dynamic.TLSCLientCertificateDNInfo{ Subject: &dynamic.TLSCLientCertificateDNInfo{
Country: true, Country: true,
Province: true, Province: true,
@ -476,9 +480,10 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
certContents: []string{minimalCheeseCrt, completeCheeseCrt}, certContents: []string{minimalCheeseCrt, completeCheeseCrt},
config: dynamic.PassTLSClientCert{ config: dynamic.PassTLSClientCert{
Info: &dynamic.TLSClientCertificateInfo{ Info: &dynamic.TLSClientCertificateInfo{
NotAfter: true, NotAfter: true,
NotBefore: true, NotBefore: true,
Sans: true, Sans: true,
SerialNumber: true,
Subject: &dynamic.TLSCLientCertificateDNInfo{ Subject: &dynamic.TLSCLientCertificateDNInfo{
Country: true, Country: true,
Province: true, Province: true,