Add organizationalUnit to passtlscert middleware
This commit is contained in:
parent
c76d58d532
commit
817ac8f256
18 changed files with 339 additions and 157 deletions
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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` |
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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,
|
||||||
|
OrganizationalUnit: true,
|
||||||
CommonName: true,
|
CommonName: true,
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
DomainComponent: true,
|
DomainComponent: true,
|
||||||
},
|
},
|
||||||
Issuer: &dynamic.TLSCLientCertificateDNInfo{
|
Issuer: &dynamic.TLSCLientCertificateIssuerDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -402,16 +402,16 @@ 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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
OrganizationalUnit: true,
|
||||||
CommonName: true,
|
CommonName: true,
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
DomainComponent: 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,
|
||||||
|
OrganizationalUnit: true,
|
||||||
CommonName: true,
|
CommonName: true,
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
DomainComponent: 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",
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -310,6 +310,10 @@ func TestPassTLSClientCert_PEM(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
tlsClientHeaders, err := New(context.Background(), next, test.config, "foo")
|
tlsClientHeaders, err := New(context.Background(), next, test.config, "foo")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -322,10 +326,6 @@ func TestPassTLSClientCert_PEM(t *testing.T) {
|
||||||
|
|
||||||
tlsClientHeaders.ServeHTTP(res, req)
|
tlsClientHeaders.ServeHTTP(res, req)
|
||||||
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
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,9 +376,10 @@ 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,
|
||||||
|
OrganizationalUnit: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Country: true,
|
Country: 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,
|
||||||
|
OrganizationalUnit: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
SerialNumber: 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,
|
||||||
|
OrganizationalUnit: true,
|
||||||
CommonName: true,
|
CommonName: true,
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
DomainComponent: 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,
|
||||||
|
OrganizationalUnit: true,
|
||||||
CommonName: true,
|
CommonName: true,
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
DomainComponent: true,
|
DomainComponent: true,
|
||||||
},
|
},
|
||||||
Issuer: &dynamic.TLSCLientCertificateDNInfo{
|
Issuer: &dynamic.TLSCLientCertificateIssuerDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
@ -509,6 +533,10 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
tlsClientHeaders, err := New(context.Background(), next, test.config, "foo")
|
tlsClientHeaders, err := New(context.Background(), next, test.config, "foo")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -521,10 +549,6 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
|
||||||
|
|
||||||
tlsClientHeaders.ServeHTTP(res, req)
|
tlsClientHeaders.ServeHTTP(res, req)
|
||||||
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
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])
|
||||||
|
|
|
@ -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,
|
||||||
|
OrganizationalUnit: true,
|
||||||
CommonName: true,
|
CommonName: true,
|
||||||
SerialNumber: true,
|
SerialNumber: true,
|
||||||
DomainComponent: true,
|
DomainComponent: true,
|
||||||
},
|
},
|
||||||
Issuer: &dynamic.TLSCLientCertificateDNInfo{
|
Issuer: &dynamic.TLSCLientCertificateIssuerDNInfo{
|
||||||
Country: true,
|
Country: true,
|
||||||
Province: true,
|
Province: true,
|
||||||
Locality: true,
|
Locality: true,
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
Loading…
Reference in a new issue