Add organizationalUnit to passtlscert middleware

This commit is contained in:
Eric 2021-07-28 16:42:09 +01:00 committed by GitHub
parent c76d58d532
commit 817ac8f256
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 339 additions and 157 deletions

View file

@ -76,6 +76,7 @@ http:
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organizationalunit=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname=true"
@ -104,6 +105,7 @@ http:
province: true province: true
locality: true locality: true
organization: true organization: true
organizationalUnit: true
commonName: true commonName: true
serialNumber: true serialNumber: true
domainComponent: true domainComponent: true
@ -127,6 +129,7 @@ http:
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organizationalunit=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname=true"
@ -148,6 +151,7 @@ http:
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent": "true", "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent": "true",
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality": "true", "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality": "true",
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization": "true", "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization": "true",
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organizationalunit": "true",
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province": "true", "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province": "true",
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber": "true", "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber": "true",
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname": "true", "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname": "true",
@ -171,6 +175,7 @@ http:
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organizationalunit=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber=true"
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname=true" - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname=true"
@ -197,6 +202,7 @@ http:
province: true province: true
locality: true locality: true
organization: true organization: true
organizationalUnit: true
commonName: true commonName: true
serialNumber: true serialNumber: true
domainComponent: true domainComponent: true
@ -223,6 +229,7 @@ http:
province = true province = true
locality = true locality = true
organization = true organization = true
organizationalUnit = true
commonName = true commonName = true
serialNumber = true serialNumber = true
domainComponent = true domainComponent = true
@ -247,7 +254,7 @@ PassTLSClientCert can add two headers to the request:
!!! info !!! info
* The headers are filled with escaped string so it can be safely placed inside a URL query. * Each header value is a string that has been escaped in order to be a valid URL query.
* These options only work accordingly to the [MutualTLS configuration](../../https/tls.md#client-authentication-mtls). * These options only work accordingly to the [MutualTLS configuration](../../https/tls.md#client-authentication-mtls).
That is to say, only the certificates that match the `clientAuth.clientAuthType` policy are passed. That is to say, only the certificates that match the `clientAuth.clientAuthType` policy are passed.
@ -412,15 +419,18 @@ In the example, it is the part between `-----BEGIN CERTIFICATE-----` and `-----E
!!! warning "`X-Forwarded-Tls-Client-Cert` value could exceed the web server header size limit" !!! warning "`X-Forwarded-Tls-Client-Cert` value could exceed the web server header size limit"
The header size limit of web servers is commonly between 4kb and 8kb. The header size limit of web servers is commonly between 4kb and 8kb.
You could change the server configuration to allow bigger header or use the `info` option with the needed field(s). If that turns out to be a problem, and if reconfiguring the server to allow larger headers is not an option,
one can alleviate the problem by selecting only the interesting parts of the cert,
through the use of the `info` options described below. (And by setting `pem` to false).
### `info` ### `info`
The `info` option selects the specific client certificate details you want to add to the `X-Forwarded-Tls-Client-Cert-Info` header. The `info` option selects the specific client certificate details you want to add to the `X-Forwarded-Tls-Client-Cert-Info` header.
The value of the header is an escaped concatenation of all the selected certificate details. The value of the header is an escaped concatenation of all the selected certificate details.
But in the following, unless specified otherwise, all the header values examples are shown unescaped, for readability.
The following example shows an unescaped result that uses all the available fields: The following example shows such a concatenation, when all the available fields are selected:
```text ```text
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=*.example.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";NB="1544094616";NA="1607166616";SAN="*.example.org,*.example.net,*.example.com,test@example.org,test@example.net,10.0.1.0,10.0.1.2" 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=*.example.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";NB="1544094616";NA="1607166616";SAN="*.example.org,*.example.net,*.example.com,test@example.org,test@example.net,10.0.1.0,10.0.1.2"
@ -441,7 +451,7 @@ The data is taken from the following certificate part:
Not After : Dec 5 11:10:16 2020 GMT Not After : Dec 5 11:10:16 2020 GMT
``` ```
The escaped `notAfter` info part is formatted as below: And it is formatted as follows in the header:
```text ```text
NA="1607166616" NA="1607166616"
@ -458,7 +468,7 @@ Validity
Not Before: Dec 6 11:10:16 2018 GMT Not Before: Dec 6 11:10:16 2018 GMT
``` ```
The escaped `notBefore` info part is formatted as below: And it is formatted as follows in the header:
```text ```text
NB="1544094616" NB="1544094616"
@ -475,7 +485,7 @@ The data is taken from the following certificate part:
DNS:*.example.org, DNS:*.example.net, DNS:*.example.com, IP Address:10.0.1.0, IP Address:10.0.1.2, email:test@example.org, email:test@example.net DNS:*.example.org, DNS:*.example.net, DNS:*.example.com, IP Address:10.0.1.0, IP Address:10.0.1.2, email:test@example.org, email:test@example.net
``` ```
The escape SANs info part is formatted as below: And it is formatted as follows in the header:
```text ```text
SAN="*.example.org,*.example.net,*.example.com,test@example.org,test@example.net,10.0.1.0,10.0.1.2" SAN="*.example.org,*.example.net,*.example.com,test@example.org,test@example.net,10.0.1.0,10.0.1.2"
@ -501,7 +511,7 @@ Set the `info.subject.country` option to `true` to add the `country` information
The data is taken from the subject part with the `C` key. The data is taken from the subject part with the `C` key.
The escape country info in the subject part is formatted as below: And it is formatted as follows in the header:
```text ```text
C=FR,C=US C=FR,C=US
@ -513,7 +523,7 @@ Set the `info.subject.province` option to `true` to add the `province` informati
The data is taken from the subject part with the `ST` key. The data is taken from the subject part with the `ST` key.
The escape province info in the subject part is formatted as below: And it is formatted as follows in the header:
```text ```text
ST=Cheese org state,ST=Cheese com state ST=Cheese org state,ST=Cheese com state
@ -525,7 +535,7 @@ Set the `info.subject.locality` option to `true` to add the `locality` informati
The data is taken from the subject part with the `L` key. The data is taken from the subject part with the `L` key.
The escape locality info in the subject part is formatted as below: And it is formatted as follows in the header:
```text ```text
L=TOULOUSE,L=LYON L=TOULOUSE,L=LYON
@ -537,19 +547,31 @@ Set the `info.subject.organization` option to `true` to add the `organization` i
The data is taken from the subject part with the `O` key. The data is taken from the subject part with the `O` key.
The escape organization info in the subject part is formatted as below: And it is formatted as follows in the header:
```text ```text
O=Cheese,O=Cheese 2 O=Cheese,O=Cheese 2
``` ```
##### `info.subject.organizationalUnit`
Set the `info.subject.organizationalUnit` option to `true` to add the `organizationalUnit` information into the subject.
The data is taken from the subject part with the `OU` key.
And it is formatted as follows in the header:
```text
OU=Cheese Section,OU=Cheese Section 2
```
##### `info.subject.commonName` ##### `info.subject.commonName`
Set the `info.subject.commonName` option to `true` to add the `commonName` information into the subject. Set the `info.subject.commonName` option to `true` to add the `commonName` information into the subject.
The data is taken from the subject part with the `CN` key. The data is taken from the subject part with the `CN` key.
The escape common name info in the subject part is formatted as below: And it is formatted as follows in the header:
```text ```text
CN=*.example.com CN=*.example.com
@ -561,7 +583,7 @@ Set the `info.subject.serialNumber` option to `true` to add the `serialNumber` i
The data is taken from the subject part with the `SN` key. The data is taken from the subject part with the `SN` key.
The escape serial number info in the subject part is formatted as below: And it is formatted as follows in the header:
```text ```text
SN=1234567890 SN=1234567890
@ -573,7 +595,7 @@ Set the `info.subject.domainComponent` option to `true` to add the `domainCompon
The data is taken from the subject part with the `DC` key. The data is taken from the subject part with the `DC` key.
The escape domain component info in the subject part is formatted as below: And it is formatted as follows in the header:
```text ```text
DC=org,DC=cheese DC=org,DC=cheese
@ -595,7 +617,7 @@ Set the `info.issuer.country` option to `true` to add the `country` information
The data is taken from the issuer part with the `C` key. The data is taken from the issuer part with the `C` key.
The escape country info in the issuer part is formatted as below: And it is formatted as follows in the header:
```text ```text
C=FR,C=US C=FR,C=US
@ -607,7 +629,7 @@ Set the `info.issuer.province` option to `true` to add the `province` informatio
The data is taken from the issuer part with the `ST` key. The data is taken from the issuer part with the `ST` key.
The escape province info in the issuer part is formatted as below: And it is formatted as follows in the header:
```text ```text
ST=Signing State,ST=Signing State 2 ST=Signing State,ST=Signing State 2
@ -619,7 +641,7 @@ Set the `info.issuer.locality` option to `true` to add the `locality` informatio
The data is taken from the issuer part with the `L` key. The data is taken from the issuer part with the `L` key.
The escape locality info in the issuer part is formatted as below: And it is formatted as follows in the header:
```text ```text
L=TOULOUSE,L=LYON L=TOULOUSE,L=LYON
@ -631,7 +653,7 @@ Set the `info.issuer.organization` option to `true` to add the `organization` in
The data is taken from the issuer part with the `O` key. The data is taken from the issuer part with the `O` key.
The escape organization info in the issuer part is formatted as below: And it is formatted as follows in the header:
```text ```text
O=Cheese,O=Cheese 2 O=Cheese,O=Cheese 2
@ -643,7 +665,7 @@ Set the `info.issuer.commonName` option to `true` to add the `commonName` inform
The data is taken from the issuer part with the `CN` key. The data is taken from the issuer part with the `CN` key.
The escape common name info in the issuer part is formatted as below: And it is formatted as follows in the header:
```text ```text
CN=Simple Signing CA 2 CN=Simple Signing CA 2
@ -655,7 +677,7 @@ Set the `info.issuer.serialNumber` option to `true` to add the `serialNumber` in
The data is taken from the issuer part with the `SN` key. The data is taken from the issuer part with the `SN` key.
The escape serial number info in the issuer part is formatted as below: And it is formatted as follows in the header:
```text ```text
SN=1234567890 SN=1234567890
@ -667,7 +689,7 @@ Set the `info.issuer.domainComponent` option to `true` to add the `domainCompone
The data is taken from the issuer part with the `DC` key. The data is taken from the issuer part with the `DC` key.
The escape domain component info in the issuer part is formatted as below: And it is formatted as follows in the header:
```text ```text
DC=org,DC=cheese DC=org,DC=cheese

View file

@ -90,6 +90,7 @@
- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.domaincomponent=true" - "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.domaincomponent=true"
- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.locality=true" - "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.locality=true"
- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.organization=true" - "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.organization=true"
- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.organizationalunit=true"
- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.province=true" - "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.province=true"
- "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.serialnumber=true" - "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.serialnumber=true"
- "traefik.http.middlewares.middleware13.passtlsclientcert.pem=true" - "traefik.http.middlewares.middleware13.passtlsclientcert.pem=true"

View file

@ -217,6 +217,7 @@
province = true province = true
locality = true locality = true
organization = true organization = true
organizationalUnit = true
commonName = true commonName = true
serialNumber = true serialNumber = true
domainComponent = true domainComponent = true

View file

@ -250,6 +250,7 @@ http:
province: true province: true
locality: true locality: true
organization: true organization: true
organizationalUnit: true
commonName: true commonName: true
serialNumber: true serialNumber: true
domainComponent: true domainComponent: true

View file

@ -106,6 +106,7 @@
| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/domainComponent` | `true` | | `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/domainComponent` | `true` |
| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/locality` | `true` | | `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/locality` | `true` |
| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/organization` | `true` | | `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/organization` | `true` |
| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/organizationalUnit` | `true` |
| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/province` | `true` | | `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/province` | `true` |
| `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/serialNumber` | `true` | | `traefik/http/middlewares/Middleware13/passTLSClientCert/info/subject/serialNumber` | `true` |
| `traefik/http/middlewares/Middleware13/passTLSClientCert/pem` | `true` | | `traefik/http/middlewares/Middleware13/passTLSClientCert/pem` | `true` |

View file

@ -90,6 +90,7 @@
"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.domaincomponent": "true", "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.domaincomponent": "true",
"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.locality": "true", "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.locality": "true",
"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.organization": "true", "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.organization": "true",
"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.organizationalunit": "true",
"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.province": "true", "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.province": "true",
"traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.serialnumber": "true", "traefik.http.middlewares.middleware13.passtlsclientcert.info.subject.serialnumber": "true",
"traefik.http.middlewares.middleware13.passtlsclientcert.pem": "true", "traefik.http.middlewares.middleware13.passtlsclientcert.pem": "true",

View file

@ -398,8 +398,9 @@ spec:
info configuration. info configuration.
properties: properties:
issuer: issuer:
description: TLSCLientCertificateDNInfo holds the client TLS description: TLSCLientCertificateIssuerDNInfo holds the client
certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739 TLS certificate distinguished name info configuration. cf
https://tools.ietf.org/html/rfc3739
properties: properties:
commonName: commonName:
type: boolean type: boolean
@ -425,8 +426,9 @@ spec:
serialNumber: serialNumber:
type: boolean type: boolean
subject: subject:
description: TLSCLientCertificateDNInfo holds the client TLS description: TLSCLientCertificateSubjectDNInfo holds the client
certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739 TLS certificate distinguished name info configuration. cf
https://tools.ietf.org/html/rfc3739
properties: properties:
commonName: commonName:
type: boolean type: boolean
@ -438,6 +440,8 @@ spec:
type: boolean type: boolean
organization: organization:
type: boolean type: boolean
organizationalUnit:
type: boolean
province: province:
type: boolean type: boolean
serialNumber: serialNumber:

View file

@ -840,8 +840,9 @@ spec:
info configuration. info configuration.
properties: properties:
issuer: issuer:
description: TLSCLientCertificateDNInfo holds the client TLS description: TLSCLientCertificateIssuerDNInfo holds the client
certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739 TLS certificate distinguished name info configuration. cf
https://tools.ietf.org/html/rfc3739
properties: properties:
commonName: commonName:
type: boolean type: boolean
@ -867,8 +868,9 @@ spec:
serialNumber: serialNumber:
type: boolean type: boolean
subject: subject:
description: TLSCLientCertificateDNInfo holds the client TLS description: TLSCLientCertificateSubjectDNInfo holds the client
certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739 TLS certificate distinguished name info configuration. cf
https://tools.ietf.org/html/rfc3739
properties: properties:
commonName: commonName:
type: boolean type: boolean
@ -880,6 +882,8 @@ spec:
type: boolean type: boolean
organization: organization:
type: boolean type: boolean
organizationalUnit:
type: boolean
province: province:
type: boolean type: boolean
serialNumber: serialNumber:

View file

@ -315,16 +315,17 @@ func TestDo_dynamicConfiguration(t *testing.T) {
NotAfter: true, NotAfter: true,
NotBefore: true, NotBefore: true,
Sans: true, Sans: true,
Subject: &dynamic.TLSCLientCertificateDNInfo{ Subject: &dynamic.TLSCLientCertificateSubjectDNInfo{
Country: true, Country: true,
Province: true, Province: true,
Locality: true, Locality: true,
Organization: true, Organization: true,
CommonName: true, OrganizationalUnit: true,
SerialNumber: true, CommonName: true,
DomainComponent: true, SerialNumber: true,
DomainComponent: true,
}, },
Issuer: &dynamic.TLSCLientCertificateDNInfo{ Issuer: &dynamic.TLSCLientCertificateIssuerDNInfo{
Country: true, Country: true,
Province: true, Province: true,
Locality: true, Locality: true,

View file

@ -288,6 +288,7 @@
"province": true, "province": true,
"locality": true, "locality": true,
"organization": true, "organization": true,
"organizationalUnit": true,
"commonName": true, "commonName": true,
"serialNumber": true, "serialNumber": true,
"domainComponent": true "domainComponent": true

View file

@ -330,6 +330,7 @@
province = true province = true
locality = true locality = true
organization = true organization = true
organizationalUnit = true
commonName = true commonName = true
serialNumber = true serialNumber = true
domainComponent = true domainComponent = true

View file

@ -399,19 +399,19 @@ 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" export:"true"` NotAfter bool `json:"notAfter,omitempty" toml:"notAfter,omitempty" yaml:"notAfter,omitempty" export:"true"`
NotBefore bool `json:"notBefore,omitempty" toml:"notBefore,omitempty" yaml:"notBefore,omitempty" export:"true"` NotBefore bool `json:"notBefore,omitempty" toml:"notBefore,omitempty" yaml:"notBefore,omitempty" export:"true"`
Sans bool `json:"sans,omitempty" toml:"sans,omitempty" yaml:"sans,omitempty" export:"true"` Sans bool `json:"sans,omitempty" toml:"sans,omitempty" yaml:"sans,omitempty" export:"true"`
Subject *TLSCLientCertificateDNInfo `json:"subject,omitempty" toml:"subject,omitempty" yaml:"subject,omitempty" export:"true"` Subject *TLSCLientCertificateSubjectDNInfo `json:"subject,omitempty" toml:"subject,omitempty" yaml:"subject,omitempty" export:"true"`
Issuer *TLSCLientCertificateDNInfo `json:"issuer,omitempty" toml:"issuer,omitempty" yaml:"issuer,omitempty" export:"true"` Issuer *TLSCLientCertificateIssuerDNInfo `json:"issuer,omitempty" toml:"issuer,omitempty" yaml:"issuer,omitempty" export:"true"`
SerialNumber bool `json:"serialNumber,omitempty" toml:"serialNumber,omitempty" yaml:"serialNumber,omitempty" export:"true"` SerialNumber bool `json:"serialNumber,omitempty" toml:"serialNumber,omitempty" yaml:"serialNumber,omitempty" export:"true"`
} }
// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen=true
// TLSCLientCertificateDNInfo holds the client TLS certificate distinguished name info configuration. // TLSCLientCertificateIssuerDNInfo holds the client TLS certificate distinguished name info configuration.
// cf https://tools.ietf.org/html/rfc3739 // cf https://tools.ietf.org/html/rfc3739
type TLSCLientCertificateDNInfo struct { type TLSCLientCertificateIssuerDNInfo struct {
Country bool `json:"country,omitempty" toml:"country,omitempty" yaml:"country,omitempty" export:"true"` Country bool `json:"country,omitempty" toml:"country,omitempty" yaml:"country,omitempty" export:"true"`
Province bool `json:"province,omitempty" toml:"province,omitempty" yaml:"province,omitempty" export:"true"` Province bool `json:"province,omitempty" toml:"province,omitempty" yaml:"province,omitempty" export:"true"`
Locality bool `json:"locality,omitempty" toml:"locality,omitempty" yaml:"locality,omitempty" export:"true"` Locality bool `json:"locality,omitempty" toml:"locality,omitempty" yaml:"locality,omitempty" export:"true"`
@ -423,6 +423,21 @@ type TLSCLientCertificateDNInfo struct {
// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen=true
// TLSCLientCertificateSubjectDNInfo holds the client TLS certificate distinguished name info configuration.
// cf https://tools.ietf.org/html/rfc3739
type TLSCLientCertificateSubjectDNInfo struct {
Country bool `json:"country,omitempty" toml:"country,omitempty" yaml:"country,omitempty" export:"true"`
Province bool `json:"province,omitempty" toml:"province,omitempty" yaml:"province,omitempty" export:"true"`
Locality bool `json:"locality,omitempty" toml:"locality,omitempty" yaml:"locality,omitempty" export:"true"`
Organization bool `json:"organization,omitempty" toml:"organization,omitempty" yaml:"organization,omitempty" export:"true"`
OrganizationalUnit bool `json:"organizationalUnit,omitempty" toml:"organizationalUnit,omitempty" yaml:"organizationalUnit,omitempty" export:"true"`
CommonName bool `json:"commonName,omitempty" toml:"commonName,omitempty" yaml:"commonName,omitempty" export:"true"`
SerialNumber bool `json:"serialNumber,omitempty" toml:"serialNumber,omitempty" yaml:"serialNumber,omitempty" export:"true"`
DomainComponent bool `json:"domainComponent,omitempty" toml:"domainComponent,omitempty" yaml:"domainComponent,omitempty" export:"true"`
}
// +k8s:deepcopy-gen=true
// Users holds a list of users. // Users holds a list of users.
type Users []string type Users []string

View file

@ -1535,17 +1535,33 @@ func (in *TCPWeightedRoundRobin) DeepCopy() *TCPWeightedRoundRobin {
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TLSCLientCertificateDNInfo) DeepCopyInto(out *TLSCLientCertificateDNInfo) { func (in *TLSCLientCertificateIssuerDNInfo) DeepCopyInto(out *TLSCLientCertificateIssuerDNInfo) {
*out = *in *out = *in
return return
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSCLientCertificateDNInfo. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSCLientCertificateIssuerDNInfo.
func (in *TLSCLientCertificateDNInfo) DeepCopy() *TLSCLientCertificateDNInfo { func (in *TLSCLientCertificateIssuerDNInfo) DeepCopy() *TLSCLientCertificateIssuerDNInfo {
if in == nil { if in == nil {
return nil return nil
} }
out := new(TLSCLientCertificateDNInfo) out := new(TLSCLientCertificateIssuerDNInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TLSCLientCertificateSubjectDNInfo) DeepCopyInto(out *TLSCLientCertificateSubjectDNInfo) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSCLientCertificateSubjectDNInfo.
func (in *TLSCLientCertificateSubjectDNInfo) DeepCopy() *TLSCLientCertificateSubjectDNInfo {
if in == nil {
return nil
}
out := new(TLSCLientCertificateSubjectDNInfo)
in.DeepCopyInto(out) in.DeepCopyInto(out)
return out return out
} }
@ -1555,12 +1571,12 @@ func (in *TLSClientCertificateInfo) DeepCopyInto(out *TLSClientCertificateInfo)
*out = *in *out = *in
if in.Subject != nil { if in.Subject != nil {
in, out := &in.Subject, &out.Subject in, out := &in.Subject, &out.Subject
*out = new(TLSCLientCertificateDNInfo) *out = new(TLSCLientCertificateSubjectDNInfo)
**out = **in **out = **in
} }
if in.Issuer != nil { if in.Issuer != nil {
in, out := &in.Issuer, &out.Issuer in, out := &in.Issuer, &out.Issuer
*out = new(TLSCLientCertificateDNInfo) *out = new(TLSCLientCertificateIssuerDNInfo)
**out = **in **out = **in
} }
return return

View file

@ -95,6 +95,7 @@ func TestDecodeConfiguration(t *testing.T) {
"traefik.http.middlewares.Middleware11.passtlsclientcert.info.subject.domaincomponent": "true", "traefik.http.middlewares.Middleware11.passtlsclientcert.info.subject.domaincomponent": "true",
"traefik.http.middlewares.Middleware11.passtlsclientcert.info.subject.locality": "true", "traefik.http.middlewares.Middleware11.passtlsclientcert.info.subject.locality": "true",
"traefik.http.middlewares.Middleware11.passtlsclientcert.info.subject.organization": "true", "traefik.http.middlewares.Middleware11.passtlsclientcert.info.subject.organization": "true",
"traefik.http.middlewares.Middleware11.passtlsclientcert.info.subject.organizationalunit": "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.serialnumber": "true", "traefik.http.middlewares.Middleware11.passtlsclientcert.info.subject.serialnumber": "true",
"traefik.http.middlewares.Middleware11.passtlsclientcert.info.issuer.commonname": "true", "traefik.http.middlewares.Middleware11.passtlsclientcert.info.issuer.commonname": "true",
@ -366,16 +367,17 @@ func TestDecodeConfiguration(t *testing.T) {
NotAfter: true, NotAfter: true,
NotBefore: true, NotBefore: true,
SerialNumber: true, SerialNumber: true,
Subject: &dynamic.TLSCLientCertificateDNInfo{ Subject: &dynamic.TLSCLientCertificateSubjectDNInfo{
Country: true, Country: true,
Province: true, Province: true,
Locality: true, Locality: true,
Organization: true, Organization: true,
CommonName: true, OrganizationalUnit: true,
SerialNumber: true, CommonName: true,
DomainComponent: true, SerialNumber: true,
DomainComponent: true,
}, },
Issuer: &dynamic.TLSCLientCertificateDNInfo{ Issuer: &dynamic.TLSCLientCertificateIssuerDNInfo{
Country: true, Country: true,
Province: true, Province: true,
Locality: true, Locality: true,
@ -844,16 +846,17 @@ func TestEncodeConfiguration(t *testing.T) {
NotAfter: true, NotAfter: true,
NotBefore: true, NotBefore: true,
SerialNumber: true, SerialNumber: true,
Subject: &dynamic.TLSCLientCertificateDNInfo{ Subject: &dynamic.TLSCLientCertificateSubjectDNInfo{
Country: true, Country: true,
Province: true, Province: true,
Locality: true, Locality: true,
Organization: true, Organization: true,
CommonName: true, OrganizationalUnit: true,
SerialNumber: true, CommonName: true,
DomainComponent: true, SerialNumber: true,
DomainComponent: true,
}, },
Issuer: &dynamic.TLSCLientCertificateDNInfo{ Issuer: &dynamic.TLSCLientCertificateIssuerDNInfo{
Country: true, Country: true,
Province: true, Province: true,
Locality: true, Locality: true,
@ -1234,6 +1237,7 @@ func TestEncodeConfiguration(t *testing.T) {
"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",
"traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.Subject.Organization": "true", "traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.Subject.Organization": "true",
"traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.Subject.OrganizationalUnit": "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.SerialNumber": "true", "traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.Subject.SerialNumber": "true",
"traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.Subject.DomainComponent": "true", "traefik.HTTP.Middlewares.Middleware11.PassTLSClientCert.Info.Subject.DomainComponent": "true",

View file

@ -35,8 +35,10 @@ var attributeTypeNames = map[string]string{
"0.9.2342.19200300.100.1.25": "DC", // Domain component OID - RFC 2247 "0.9.2342.19200300.100.1.25": "DC", // Domain component OID - RFC 2247
} }
// DistinguishedNameOptions is a struct for specifying the configuration for the distinguished name info. // IssuerDistinguishedNameOptions is a struct for specifying the configuration
type DistinguishedNameOptions struct { // for the distinguished name info of the issuer. This information is defined in
// RFC3739, section 3.1.1.
type IssuerDistinguishedNameOptions struct {
CommonName bool CommonName bool
CountryName bool CountryName bool
DomainComponent bool DomainComponent bool
@ -46,12 +48,12 @@ type DistinguishedNameOptions struct {
StateOrProvinceName bool StateOrProvinceName bool
} }
func newDistinguishedNameOptions(info *dynamic.TLSCLientCertificateDNInfo) *DistinguishedNameOptions { func newIssuerDistinguishedNameOptions(info *dynamic.TLSCLientCertificateIssuerDNInfo) *IssuerDistinguishedNameOptions {
if info == nil { if info == nil {
return nil return nil
} }
return &DistinguishedNameOptions{ return &IssuerDistinguishedNameOptions{
CommonName: info.CommonName, CommonName: info.CommonName,
CountryName: info.Country, CountryName: info.Country,
DomainComponent: info.DomainComponent, DomainComponent: info.DomainComponent,
@ -62,13 +64,44 @@ func newDistinguishedNameOptions(info *dynamic.TLSCLientCertificateDNInfo) *Dist
} }
} }
// SubjectDistinguishedNameOptions is a struct for specifying the configuration
// for the distinguished name info of the subject. This information is defined
// in RFC3739, section 3.1.2.
type SubjectDistinguishedNameOptions struct {
CommonName bool
CountryName bool
DomainComponent bool
LocalityName bool
OrganizationName bool
OrganizationalUnitName bool
SerialNumber bool
StateOrProvinceName bool
}
func newSubjectDistinguishedNameOptions(info *dynamic.TLSCLientCertificateSubjectDNInfo) *SubjectDistinguishedNameOptions {
if info == nil {
return nil
}
return &SubjectDistinguishedNameOptions{
CommonName: info.CommonName,
CountryName: info.Country,
DomainComponent: info.DomainComponent,
LocalityName: info.Locality,
OrganizationName: info.Organization,
OrganizationalUnitName: info.OrganizationalUnit,
SerialNumber: info.SerialNumber,
StateOrProvinceName: info.Province,
}
}
// 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 *SubjectDistinguishedNameOptions
issuer *DistinguishedNameOptions issuer *IssuerDistinguishedNameOptions
serialNumber bool serialNumber bool
} }
@ -78,10 +111,10 @@ func newTLSClientCertificateInfo(info *dynamic.TLSClientCertificateInfo) *tlsCli
} }
return &tlsClientCertificateInfo{ return &tlsClientCertificateInfo{
issuer: newDistinguishedNameOptions(info.Issuer), issuer: newIssuerDistinguishedNameOptions(info.Issuer),
notAfter: info.NotAfter, notAfter: info.NotAfter,
notBefore: info.NotBefore, notBefore: info.NotBefore,
subject: newDistinguishedNameOptions(info.Subject), subject: newSubjectDistinguishedNameOptions(info.Subject),
serialNumber: info.SerialNumber, serialNumber: info.SerialNumber,
sans: info.Sans, sans: info.Sans,
} }
@ -147,12 +180,12 @@ func (p *passTLSClientCert) getCertInfo(ctx context.Context, certs []*x509.Certi
var values []string var values []string
if p.info != nil { if p.info != nil {
subject := getDNInfo(ctx, p.info.subject, &peerCert.Subject) subject := getSubjectDNInfo(ctx, p.info.subject, &peerCert.Subject)
if subject != "" { if subject != "" {
values = append(values, fmt.Sprintf(`Subject="%s"`, strings.TrimSuffix(subject, subFieldSeparator))) values = append(values, fmt.Sprintf(`Subject="%s"`, strings.TrimSuffix(subject, subFieldSeparator)))
} }
issuer := getDNInfo(ctx, p.info.issuer, &peerCert.Issuer) issuer := getIssuerDNInfo(ctx, p.info.issuer, &peerCert.Issuer)
if issuer != "" { if issuer != "" {
values = append(values, fmt.Sprintf(`Issuer="%s"`, strings.TrimSuffix(issuer, subFieldSeparator))) values = append(values, fmt.Sprintf(`Issuer="%s"`, strings.TrimSuffix(issuer, subFieldSeparator)))
} }
@ -187,7 +220,7 @@ func (p *passTLSClientCert) getCertInfo(ctx context.Context, certs []*x509.Certi
return strings.Join(headerValues, certSeparator) return strings.Join(headerValues, certSeparator)
} }
func getDNInfo(ctx context.Context, options *DistinguishedNameOptions, cs *pkix.Name) string { func getIssuerDNInfo(ctx context.Context, options *IssuerDistinguishedNameOptions, cs *pkix.Name) string {
if options == nil { if options == nil {
return "" return ""
} }
@ -229,6 +262,52 @@ func getDNInfo(ctx context.Context, options *DistinguishedNameOptions, cs *pkix.
return content.String() return content.String()
} }
func getSubjectDNInfo(ctx context.Context, options *SubjectDistinguishedNameOptions, cs *pkix.Name) string {
if options == nil {
return ""
}
content := &strings.Builder{}
// Manage non standard attributes
for _, name := range cs.Names {
// Domain Component - RFC 2247
if options.DomainComponent && attributeTypeNames[name.Type.String()] == "DC" {
content.WriteString(fmt.Sprintf("DC=%s%s", name.Value, subFieldSeparator))
}
}
if options.CountryName {
writeParts(ctx, content, cs.Country, "C")
}
if options.StateOrProvinceName {
writeParts(ctx, content, cs.Province, "ST")
}
if options.LocalityName {
writeParts(ctx, content, cs.Locality, "L")
}
if options.OrganizationName {
writeParts(ctx, content, cs.Organization, "O")
}
if options.OrganizationalUnitName {
writeParts(ctx, content, cs.OrganizationalUnit, "OU")
}
if options.SerialNumber {
writePart(ctx, content, cs.SerialNumber, "SN")
}
if options.CommonName {
writePart(ctx, content, cs.CommonName, "CN")
}
return content.String()
}
func writeParts(ctx context.Context, content io.StringWriter, entries []string, prefix string) { func writeParts(ctx context.Context, content io.StringWriter, entries []string, prefix string) {
for _, entry := range entries { for _, entry := range entries {
writePart(ctx, content, entry, prefix) writePart(ctx, content, entry, prefix)

View file

@ -310,22 +310,22 @@ func TestPassTLSClientCert_PEM(t *testing.T) {
} }
for _, test := range testCases { for _, test := range testCases {
tlsClientHeaders, err := New(context.Background(), next, test.config, "foo")
require.NoError(t, err)
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)
}
tlsClientHeaders.ServeHTTP(res, req)
test := test test := test
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
t.Parallel() t.Parallel()
tlsClientHeaders, err := New(context.Background(), next, test.config, "foo")
require.NoError(t, err)
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)
}
tlsClientHeaders.ServeHTTP(res, req)
assert.Equal(t, http.StatusOK, res.Code, "Http Status should be OK") assert.Equal(t, http.StatusOK, res.Code, "Http Status should be OK")
assert.Equal(t, "bar", res.Body.String(), "Should be the expected body") assert.Equal(t, "bar", res.Body.String(), "Should be the expected body")
@ -351,7 +351,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
}, fieldSeparator) }, fieldSeparator)
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,OU=Simple Signing Section,OU=Simple Signing Section 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"`, `SerialNumber="1"`,
`NB="1544094616"`, `NB="1544094616"`,
@ -376,13 +376,14 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
desc: "No TLS, with subject info", desc: "No TLS, with subject info",
config: dynamic.PassTLSClientCert{ config: dynamic.PassTLSClientCert{
Info: &dynamic.TLSClientCertificateInfo{ Info: &dynamic.TLSClientCertificateInfo{
Subject: &dynamic.TLSCLientCertificateDNInfo{ Subject: &dynamic.TLSCLientCertificateSubjectDNInfo{
CommonName: true, CommonName: true,
Organization: true, Organization: true,
Locality: true, OrganizationalUnit: true,
Province: true, Locality: true,
Country: true, Province: true,
SerialNumber: true, Country: true,
SerialNumber: true,
}, },
}, },
}, },
@ -392,7 +393,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
config: dynamic.PassTLSClientCert{ config: dynamic.PassTLSClientCert{
PEM: false, PEM: false,
Info: &dynamic.TLSClientCertificateInfo{ Info: &dynamic.TLSClientCertificateInfo{
Subject: &dynamic.TLSCLientCertificateDNInfo{}, Subject: &dynamic.TLSCLientCertificateSubjectDNInfo{},
}, },
}, },
}, },
@ -405,16 +406,17 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
NotBefore: true, NotBefore: true,
Sans: true, Sans: true,
SerialNumber: true, SerialNumber: true,
Subject: &dynamic.TLSCLientCertificateDNInfo{ Subject: &dynamic.TLSCLientCertificateSubjectDNInfo{
CommonName: true, CommonName: true,
Country: true, Country: true,
DomainComponent: true, DomainComponent: true,
Locality: true, Locality: true,
Organization: true, Organization: true,
Province: true, OrganizationalUnit: true,
SerialNumber: true, Province: true,
SerialNumber: true,
}, },
Issuer: &dynamic.TLSCLientCertificateDNInfo{ Issuer: &dynamic.TLSCLientCertificateIssuerDNInfo{
CommonName: true, CommonName: true,
Country: true, Country: true,
DomainComponent: true, DomainComponent: true,
@ -434,10 +436,30 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
Info: &dynamic.TLSClientCertificateInfo{ Info: &dynamic.TLSClientCertificateInfo{
NotAfter: true, NotAfter: true,
Sans: true, Sans: true,
Subject: &dynamic.TLSCLientCertificateDNInfo{ Subject: &dynamic.TLSCLientCertificateSubjectDNInfo{
Organization: true, Organization: true,
}, },
Issuer: &dynamic.TLSCLientCertificateDNInfo{ Issuer: &dynamic.TLSCLientCertificateIssuerDNInfo{
Country: true,
},
},
},
expectedHeader: `Subject="O=Cheese";Issuer="C=FR,C=US";NA="1632568236"`,
},
{
desc: "TLS with simple certificate, requesting non-existent info",
certContents: []string{minimalCheeseCrt},
config: dynamic.PassTLSClientCert{
Info: &dynamic.TLSClientCertificateInfo{
NotAfter: true,
Sans: true,
Subject: &dynamic.TLSCLientCertificateSubjectDNInfo{
Organization: true,
// OrganizationalUnit is not set on this example certificate,
// so even though it's requested, it will be absent.
OrganizationalUnit: true,
},
Issuer: &dynamic.TLSCLientCertificateIssuerDNInfo{
Country: true, Country: true,
}, },
}, },
@ -453,16 +475,17 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
NotBefore: true, NotBefore: true,
Sans: true, Sans: true,
SerialNumber: true, SerialNumber: true,
Subject: &dynamic.TLSCLientCertificateDNInfo{ Subject: &dynamic.TLSCLientCertificateSubjectDNInfo{
Country: true, Country: true,
Province: true, Province: true,
Locality: true, Locality: true,
Organization: true, Organization: true,
CommonName: true, OrganizationalUnit: true,
SerialNumber: true, CommonName: true,
DomainComponent: true, SerialNumber: true,
DomainComponent: true,
}, },
Issuer: &dynamic.TLSCLientCertificateDNInfo{ Issuer: &dynamic.TLSCLientCertificateIssuerDNInfo{
Country: true, Country: true,
Province: true, Province: true,
Locality: true, Locality: true,
@ -484,16 +507,17 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
NotBefore: true, NotBefore: true,
Sans: true, Sans: true,
SerialNumber: true, SerialNumber: true,
Subject: &dynamic.TLSCLientCertificateDNInfo{ Subject: &dynamic.TLSCLientCertificateSubjectDNInfo{
Country: true, Country: true,
Province: true, Province: true,
Locality: true, Locality: true,
Organization: true, Organization: true,
CommonName: true, OrganizationalUnit: true,
SerialNumber: true, CommonName: true,
DomainComponent: true, SerialNumber: true,
DomainComponent: true,
}, },
Issuer: &dynamic.TLSCLientCertificateDNInfo{ Issuer: &dynamic.TLSCLientCertificateIssuerDNInfo{
Country: true, Country: true,
Province: true, Province: true,
Locality: true, Locality: true,
@ -509,22 +533,22 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
} }
for _, test := range testCases { for _, test := range testCases {
tlsClientHeaders, err := New(context.Background(), next, test.config, "foo")
require.NoError(t, err)
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)
}
tlsClientHeaders.ServeHTTP(res, req)
test := test test := test
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
t.Parallel() t.Parallel()
tlsClientHeaders, err := New(context.Background(), next, test.config, "foo")
require.NoError(t, err)
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)
}
tlsClientHeaders.ServeHTTP(res, req)
assert.Equal(t, http.StatusOK, res.Code, "Http Status should be OK") assert.Equal(t, http.StatusOK, res.Code, "Http Status should be OK")
assert.Equal(t, "bar", res.Body.String(), "Should be the expected body") assert.Equal(t, "bar", res.Body.String(), "Should be the expected body")
@ -621,12 +645,12 @@ func Test_getSANs(t *testing.T) {
} }
for _, test := range testCases { for _, test := range testCases {
sans := getSANs(test.cert)
test := test test := test
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
t.Parallel() t.Parallel()
sans := getSANs(test.cert)
if len(test.expected) > 0 { if len(test.expected) > 0 {
for i, expected := range test.expected { for i, expected := range test.expected {
assert.Equal(t, expected, sans[i]) assert.Equal(t, expected, sans[i])

View file

@ -156,6 +156,7 @@ func Test_buildConfiguration(t *testing.T) {
"traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/province": "true", "traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/province": "true",
"traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/locality": "true", "traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/locality": "true",
"traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/organization": "true", "traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/organization": "true",
"traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/organizationalunit": "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/serialNumber": "true", "traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/serialNumber": "true",
"traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/domainComponent": "true", "traefik/http/middlewares/Middleware12/passTLSClientCert/info/subject/domainComponent": "true",
@ -478,16 +479,17 @@ func Test_buildConfiguration(t *testing.T) {
NotAfter: true, NotAfter: true,
NotBefore: true, NotBefore: true,
Sans: true, Sans: true,
Subject: &dynamic.TLSCLientCertificateDNInfo{ Subject: &dynamic.TLSCLientCertificateSubjectDNInfo{
Country: true, Country: true,
Province: true, Province: true,
Locality: true, Locality: true,
Organization: true, Organization: true,
CommonName: true, OrganizationalUnit: true,
SerialNumber: true, CommonName: true,
DomainComponent: true, SerialNumber: true,
DomainComponent: true,
}, },
Issuer: &dynamic.TLSCLientCertificateDNInfo{ Issuer: &dynamic.TLSCLientCertificateIssuerDNInfo{
Country: true, Country: true,
Province: true, Province: true,
Locality: true, Locality: true,

View file

@ -866,17 +866,21 @@
<q-card-section v-if="middleware.passTLSClientCert && middleware.passTLSClientCert.info && middleware.passTLSClientCert.info.subject"> <q-card-section v-if="middleware.passTLSClientCert && middleware.passTLSClientCert.info && middleware.passTLSClientCert.info.subject">
<div class="row items-start no-wrap"> <div class="row items-start no-wrap">
<div class="col"> <div class="col">
<div class="text-subtitle2">Common Name</div> <div class="text-subtitle2">Organizational Unit</div>
<boolean-state :value="!!exData(middleware).info.subject.commonName"/> <boolean-state :value="!!exData(middleware).info.subject.organizationalUnit"/>
</div> </div>
<div class="col"> <div class="col">
<div class="text-subtitle2">Serial Number</div> <div class="text-subtitle2">Common Name</div>
<boolean-state :value="!!exData(middleware).info.subject.serialNumber"/> <boolean-state :value="!!exData(middleware).info.subject.commonName"/>
</div> </div>
</div> </div>
</q-card-section> </q-card-section>
<q-card-section v-if="middleware.passTLSClientCert && middleware.passTLSClientCert.info && middleware.passTLSClientCert.info.subject"> <q-card-section v-if="middleware.passTLSClientCert && middleware.passTLSClientCert.info && middleware.passTLSClientCert.info.subject">
<div class="row items-start no-wrap"> <div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">Serial Number</div>
<boolean-state :value="!!exData(middleware).info.subject.serialNumber"/>
</div>
<div class="col"> <div class="col">
<div class="text-subtitle2">Domain Component</div> <div class="text-subtitle2">Domain Component</div>
<boolean-state :value="!!exData(middleware).info.subject.domainComponent"/> <boolean-state :value="!!exData(middleware).info.subject.domainComponent"/>