kubernetes / ingress-nginx

Ingress NGINX Controller for Kubernetes
https://kubernetes.github.io/ingress-nginx/
Apache License 2.0
17.4k stars 8.24k forks source link

Intermediate certificates not in SSL-Chain after ingress-nginx upgrade from 0.24.1 to 0.40.1 #6876

Closed voigt closed 3 years ago

voigt commented 3 years ago

NGINX Ingress controller version: 0.40.1

Kubernetes version (use kubectl version): 1.16.4

Environment:

What happened:

Hi, we just recently updated our ingress controllers from 0.24.1 to 0.40.1. So far so good.

What we now noticed is, that our intermediate certificates are not any longer part of the ssl-chain on an ssl connect:

openssl s_client -connect on a 0.40.1 endpoint ```bash $ openssl s_client -connect service.domain.com:443 -servername service.domain.com CONNECTED(00000005) depth=0 C = IE, ST = City, L = Country, O = Organisation, OU = , CN = service.domain.com verify error:num=20:unable to get local issuer certificate verify return:1 depth=0 C = IE, ST = City, L = Country, O = Organisation, OU = , CN = service.domain.com verify error:num=21:unable to verify the first certificate verify return:1 --- Certificate chain 0 s:C = IE, ST = City, L = Country, O = Organisation, OU = , CN = service.domain.com i:DC = com, DC = , DC = ORGPKI, CN = , O = Organisation --- Server certificate -----BEGIN CERTIFICATE----- -----END CERTIFICATE----- [...] --- closed ```

What you expected to happen:

An output, similar to environments where we are running 0.24.1 (without)

openssl s_client -connect on a 0.24.1 endpoint ``` $ openssl s_client -connect otherservice.domain.com:443 -servername otherservice.domain.com CONNECTED(00000005) depth=2 DC = com, DC = , DC = ORGPKI, CN = -05 verify error:num=19:self signed certificate in certificate chain --- Certificate chain 0 s:C = IE, ST = , L = , O = , OU = , CN = otherservice.domain.com i:DC = com, DC = , DC = ORGPKI, CN = -01, O = 1 s:DC = com, DC = , DC = ORGPKI, CN = -01, O = i:DC = com, DC = , DC = ORGPKI, CN = -05 2 s:DC = com, DC = , DC = ORGPKI, CN = -05 i:DC = com, DC = , DC = ORGPKI, CN = -05 3 s:DC = com, DC = , DC = ORGPKI, CN = -01, O = i:DC = com, DC = , DC = ORGPKI, CN = -05 --- [...] ```

How to reproduce it:

$ openssl s_client -connect service.domain.com:443 -servername service.domain.com

Anything else we need to know:

        enable-ssl-passthrough: "true"
        enable-ssl-chain-completion: "true"
all nginx-ingress-controller arguments used ``` spec: containers: - args: - /nginx-ingress-controller - --default-backend-service=ingress/ingress-nginx-defaultbackend - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller - --election-id=ingress-controller-leader - --ingress-class=portal - --configmap=ingress/ingress-nginx-controller - --validating-webhook=:8443 - --validating-webhook-certificate=/usr/local/certificates/cert - --validating-webhook-key=/usr/local/certificates/key - --enable-ssl-chain-completion=true - --enable-ssl-passthrough=true ```
Sample Ingress ```yaml apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: ingress.kubernetes.io/force-ssl-redirect: "true" ingress.kubernetes.io/rewrite-target: / kubernetes.io/ingress.class: portal nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "false" nginx.ingress.kubernetes.io/auth-tls-secret: default/casecret nginx.ingress.kubernetes.io/auth-tls-verify-client: "on" nginx.ingress.kubernetes.io/auth-tls-verify-depth: "1" nginx.ingress.kubernetes.io/configuration-snippet: | set $allowed 0; if ($ssl_client_i_dn = "CN=,DC=,DC=,DC=") { set $allowed 1; } if ($ssl_client_i_dn = "CN=,DC=,DC=,DC=") { set $allowed 1; } if ($ssl_client_i_dn = "CN=,DC=,DC=,DC=") { set $allowed 1; } if ($ssl_client_s_dn = "C=,O=,OU=,CN=") { set $allowed 1; } if ($allowed != 1) { return 401 $ssl_client_s_dn; } labels: app: service name: service-ingress namespace: default spec: rules: - host: service.domain.com http: paths: - backend: serviceName: service servicePort: 8080 path: / tls: - hosts: - service.domain.com secretName: service.domain.com status: loadBalancer: ingress: - ip: 100.69.155.90 ```
kubectl get secret service.domain.com -oyaml ```yaml root@4de00908bc18:/# kubectl get secret service.domain.com -oyaml apiVersion: v1 data: ca.crt: tls.crt: kind: Secret metadata: annotations: certmanager.k8s.io/alt-names: service.domain.com certmanager.k8s.io/common-name: service.domain.com certmanager.k8s.io/ip-sans: "" certmanager.k8s.io/issuer-kind: ClusterIssuer certmanager.k8s.io/issuer-name: vault-issuer creationTimestamp: "2019-06-21T08:42:00Z" labels: certmanager.k8s.io/certificate-name: service.domain.com name: service.domain.com namespace: default type: kubernetes.io/tls ```

I'm a bit puzzled here...

Any help kindly appreciated!

/triage support

k8s-ci-robot commented 3 years ago

@voigt: The label(s) triage/support cannot be applied, because the repository doesn't have them

In response to [this](https://github.com/kubernetes/ingress-nginx/issues/6876): > >**NGINX Ingress controller version**: `0.40.1` > >**Kubernetes version** (use `kubectl version`): `1.16.4` > >**Environment**: > >- **Cloud provider or hardware configuration**: AWS EC2 c5n.large >- **OS** (e.g. from /etc/os-release): Ubuntu 18.04.5 LTS >- **Kernel** (e.g. `uname -a`): 5.4.0-1037-aws >- **Install tools**: - >- **Others**: cert-manager v0.9.1 > >**What happened**: > >Hi, >we just recently updated our ingress controllers from `0.24.1` to `0.40.1`. So far so good. > >What we now noticed is, that our intermediate certificates are not any longer part of the ssl-chain on an ssl connect: > >
openssl s_client -connect on a 0.40.1 endpoint > >```bash >$ openssl s_client -connect service.domain.com:443 -servername service.domain.com >CONNECTED(00000005) >depth=0 C = IE, ST = City, L = Country, O = Organisation, OU = , CN = service.domain.com >verify error:num=20:unable to get local issuer certificate >verify return:1 >depth=0 C = IE, ST = City, L = Country, O = Organisation, OU = , CN = service.domain.com >verify error:num=21:unable to verify the first certificate >verify return:1 >--- >Certificate chain > 0 s:C = IE, ST = City, L = Country, O = Organisation, OU = , CN = service.domain.com > i:DC = com, DC = , DC = ORGPKI, CN = , O = Organisation >--- >Server certificate >-----BEGIN CERTIFICATE----- > >-----END CERTIFICATE----- >[...] >--- >closed >``` >

What you expected to happen:

An output, similar to environments where we are running 0.24.1 (without)

openssl s_client -connect on a 0.24.1 endpoint ``` $ openssl s_client -connect otherservice.domain.com:443 -servername otherservice.domain.com CONNECTED(00000005) depth=2 DC = com, DC = , DC = ORGPKI, CN = -05 verify error:num=19:self signed certificate in certificate chain --- Certificate chain 0 s:C = IE, ST = , L = , O = , OU = , CN = otherservice.domain.com i:DC = com, DC = , DC = ORGPKI, CN = -01, O = 1 s:DC = com, DC = , DC = ORGPKI, CN = -01, O = i:DC = com, DC = , DC = ORGPKI, CN = -05 2 s:DC = com, DC = , DC = ORGPKI, CN = -05 i:DC = com, DC = , DC = ORGPKI, CN = -05 3 s:DC = com, DC = , DC = ORGPKI, CN = -01, O = i:DC = com, DC = , DC = ORGPKI, CN = -05 --- [...] ```

How to reproduce it:

$ openssl s_client -connect service.domain.com:443 -servername service.domain.com

Anything else we need to know:

  • We have multiple environments running and only those with the updated version IC 0.40.1 show this behavior.

  • from the Helm-Chart we enabled the following extraArgs. Please note, that with 0.24.1 we also enabled enable-dynamic-certificates: "false", which does not seem to exist anymore. I'm explicitly pointing that out, as I found issue #4245.

       enable-ssl-passthrough: "true"
       enable-ssl-chain-completion: "true"
  • Arguments with which the IC is started:
all nginx-ingress-controller arguments used ``` spec: containers: - args: - /nginx-ingress-controller - --default-backend-service=ingress/ingress-nginx-defaultbackend - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller - --election-id=ingress-controller-leader - --ingress-class=portal - --configmap=ingress/ingress-nginx-controller - --validating-webhook=:8443 - --validating-webhook-certificate=/usr/local/certificates/cert - --validating-webhook-key=/usr/local/certificates/key - --enable-ssl-chain-completion=true - --enable-ssl-passthrough=true ```
  • A sample ingress (redacted) - we make use of nginx.ingress.kubernetes.io/configuration-snippet, which hasn't changed after we updated to 0.40.1:
Sample Ingress ```yaml apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: ingress.kubernetes.io/force-ssl-redirect: "true" ingress.kubernetes.io/rewrite-target: / kubernetes.io/ingress.class: portal nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "false" nginx.ingress.kubernetes.io/auth-tls-secret: default/casecret nginx.ingress.kubernetes.io/auth-tls-verify-client: "on" nginx.ingress.kubernetes.io/auth-tls-verify-depth: "1" nginx.ingress.kubernetes.io/configuration-snippet: | set $allowed 0; if ($ssl_client_i_dn = "CN=,DC=,DC=,DC=") { set $allowed 1; } if ($ssl_client_i_dn = "CN=,DC=,DC=,DC=") { set $allowed 1; } if ($ssl_client_i_dn = "CN=,DC=,DC=,DC=") { set $allowed 1; } if ($ssl_client_s_dn = "C=,O=,OU=,CN=") { set $allowed 1; } if ($allowed != 1) { return 401 $ssl_client_s_dn; } labels: app: service name: service-ingress namespace: default spec: rules: - host: service.domain.com http: paths: - backend: serviceName: service servicePort: 8080 path: / tls: - hosts: - service.domain.com secretName: service.domain.com status: loadBalancer: ingress: - ip: 100.69.155.90 ```
  • We use cert-manager v0.9.1 (I know, pretty old - the upgrade is WIP) to get TLS certificates that are then inserted into our ingress resources. So for the sake of completeness a sample Kubernetes secret as it is used with 0.24.1 and 0.40.1:
kubectl get secret service.domain.com -oyaml ```yaml root@4de00908bc18:/# kubectl get secret service.domain.com -oyaml apiVersion: v1 data: ca.crt: tls.crt: kind: Secret metadata: annotations: certmanager.k8s.io/alt-names: service.domain.com certmanager.k8s.io/common-name: service.domain.com certmanager.k8s.io/ip-sans: "" certmanager.k8s.io/issuer-kind: ClusterIssuer certmanager.k8s.io/issuer-name: vault-issuer creationTimestamp: "2019-06-21T08:42:00Z" labels: certmanager.k8s.io/certificate-name: service.domain.com name: service.domain.com namespace: default type: kubernetes.io/tls ```
  • Note that TLS secrets, as well as Ingress resources, did not change after the upgrade

I'm a bit puzzled here...

  • Why does --enable-dynamic-certificates=false not exist anymore in 0.40.1, and can it be related? (#4245)
  • Is it possible, that the requirements for the certificate format changed with a later version of the IC? Does cert-manager create secrets that the newer IC versions can't deal with?
  • And most importantly: how do I get back the full ssl-chain? 😅
  • I already tried my luck in the slack channel, so far without any response, that's why I try it here...

Any help kindly appreciated!

/triage support

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

voigt commented 3 years ago

In addition to that, I checked the contents of the ingress controller container, which shows, that the default-service.domain.com-full-chain.pem doesn't seem to exist in 0.40.1:

Contents in a 0.24.1 container:

www-data@ip-11-220-168-28:/etc/nginx$ ls -lah /etc/ingress-controller/ssl/
total 356K
drwxr-xr-x 1 www-data www-data 4.0K Feb 11 12:22 .
drwxr-xr-x 1 root     root     4.0K Apr 13  2019 ..
-rw-r--r-- 1 www-data www-data  61K Feb 14 16:40 ca-default-casecret.pem
-rw-r--r-- 1 www-data www-data 2.2K Feb 14 00:40 default-service.domain.com-full-chain.pem
-rw-r--r-- 1 www-data www-data  12K Feb 14 00:39 default-service.domain.com.pem

Flags set for 0.24.1:

  containers:
  - args:
    - /nginx-ingress-controller
    - --default-backend-service=ingress/ingress-nginx-default-backend
    - --election-id=ingress-controller-leader
    - --ingress-class=portal
    - --configmap=ingress/ingress-nginx-controller
    - --enable-dynamic-certificates=false
    - --enable-ssl-chain-completion=true
    - --enable-ssl-passthrough=true

Contents in a 0.40.1 container:

bash-5.0$ ls -lah /etc/ingress-controller/ssl/
total 224K
drwxr-xr-x    1 www-data www-data    4.0K Feb 12 13:01 .
drwxr-xr-x    1 www-data www-data    4.0K Oct  2 17:57 ..
-rw-r--r--    1 www-data www-data   60.9K Feb 12 13:02 ca-default-casecret.pem
-rwx------    1 www-data www-data    7.5K Feb 12 13:02 default-service.domain.com.pem

Flags set for 0.40.1:

  containers:
  - args:
    - /nginx-ingress-controller
    - --default-backend-service=ingress/ingress-nginx-defaultbackend
    - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
    - --election-id=ingress-controller-leader
    - --ingress-class=portal
    - --configmap=ingress/ingress-nginx-controller
    - --validating-webhook=:8443
    - --validating-webhook-certificate=/usr/local/certificates/cert
    - --validating-webhook-key=/usr/local/certificates/key
    - --enable-ssl-chain-completion=true
    - --enable-ssl-passthrough=true

It might be, that I'm on the wrong track, but I have the feeling that --enable-dynamic-certificates=false is the key point here, as the flag has been removed and dynamic certificates are enabled by default... It would be great if anyone who has more knowledge on that matter could confirm...

fejta-bot commented 3 years ago

Issues go stale after 90d of inactivity. Mark the issue as fresh with /remove-lifecycle stale. Stale issues rot after an additional 30d of inactivity and eventually close.

If this issue is safe to close now please do so with /close.

Send feedback to sig-contributor-experience at kubernetes/community. /lifecycle stale

artemkrim commented 3 years ago

I have this promplem

muffl0n commented 3 years ago

The flag was removed in https://github.com/kubernetes/ingress-nginx/commit/9c6873a55d1db5dd8a6d7ebb7cd24a84fe977146

fejta-bot commented 3 years ago

Stale issues rot after 30d of inactivity. Mark the issue as fresh with /remove-lifecycle rotten. Rotten issues close after an additional 30d of inactivity.

If this issue is safe to close now please do so with /close.

Send feedback to sig-contributor-experience at kubernetes/community. /lifecycle rotten

k8s-triage-robot commented 3 years ago

The Kubernetes project currently lacks enough active contributors to adequately respond to all issues and PRs.

This bot triages issues and PRs according to the following rules:

You can:

Please send feedback to sig-contributor-experience at kubernetes/community.

/close

k8s-ci-robot commented 3 years ago

@k8s-triage-robot: Closing this issue.

In response to [this](https://github.com/kubernetes/ingress-nginx/issues/6876#issuecomment-899152872): >The Kubernetes project currently lacks enough active contributors to adequately respond to all issues and PRs. > >This bot triages issues and PRs according to the following rules: >- After 90d of inactivity, `lifecycle/stale` is applied >- After 30d of inactivity since `lifecycle/stale` was applied, `lifecycle/rotten` is applied >- After 30d of inactivity since `lifecycle/rotten` was applied, the issue is closed > >You can: >- Reopen this issue or PR with `/reopen` >- Mark this issue or PR as fresh with `/remove-lifecycle rotten` >- Offer to help out with [Issue Triage][1] > >Please send feedback to sig-contributor-experience at [kubernetes/community](https://github.com/kubernetes/community). > >/close > >[1]: https://www.kubernetes.dev/docs/guide/issue-triage/ Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md). If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
Timoses commented 3 years ago

Did anybody find out how to get nginx ingress controller to send the ca.crt from a Ingress TLS secret to the client (i.e. send full chain)?

tschechniker commented 3 years ago

Same problem here. Any solutions?

gmarkey commented 2 years ago

I am also not seeing nginx provide the intermediate certificate from my tls.crt. Installed from Helm chart v4.0.12.

muffl0n commented 2 years ago

Try this:

This works for me while using the certificate as a default

Name:         ingress-nginx-controller-7598bb9dbd-7fxkq
Namespace:    ingress-nginx
...
Containers:
  controller:
  ...
    Args:
      /nginx-ingress-controller
      --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
      --election-id=ingress-controller-leader
      --controller-class=k8s.io/ingress-nginx
      --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
      --default-ssl-certificate=ingress-nginx/cert

->

 * Certificates Information:
       Hostname sent for SNI:             test.foo.bar
       Number of certificates detected:   1

     Certificate #0 ( _RSAPublicKey )
       SHA1 Fingerprint:                  123
       Common Name:                       *.foo.bar
       Issuer:                            Sectigo RSA Domain Validation Secure Server CA
       Serial Number:                     123
       Not Before:                        2021-05-31
       Not After:                         2022-05-31
       Public Key Algorithm:              _RSAPublicKey
       Signature Algorithm:               sha256
       Key Size:                          4096
       Exponent:                          65537
       DNS Subject Alternative Names:     ['*.foo.bar']

     Certificate #0 - Trust
       Hostname Validation:               OK - Certificate matches server hostname
       Android CA Store (9.0.0_r9):       OK - Certificate is trusted
       Apple CA Store (iOS 14, iPadOS 14, macOS 11, watchOS 7, and tvOS 14):OK - Certificate is trusted
       Java CA Store (jdk-13.0.2):        OK - Certificate is trusted
       Mozilla CA Store (2021-01-24):     OK - Certificate is trusted
       Windows CA Store (2021-02-08):     OK - Certificate is trusted
       Symantec 2018 Deprecation:         OK - Not a Symantec-issued certificate
       Received Chain:                    *.foo.bar --> Sectigo RSA Domain Validation Secure Server CA --> USERTrust RSA Certification Authority
       Verified Chain:                    *.foo.bar --> Sectigo RSA Domain Validation Secure Server CA --> USERTrust RSA Certification Authority
       Received Chain Contains Anchor:    OK - Anchor certificate not sent
       Received Chain Order:              OK - Order is valid
       Verified Chain contains SHA1:      OK - No SHA1-signed certificate in the verified certificate chain

     Certificate #0 - Extensions
       OCSP Must-Staple:                  NOT SUPPORTED - Extension not found
       Certificate Transparency:          OK - 3 SCTs included
jtermine commented 2 months ago

Did anybody find out how to get nginx ingress controller to send the ca.crt from a Ingress TLS secret to the client (i.e. send full chain)?

Did you?