Kubernetes Ingress can't find SSL secret created by CertManager - ssl

I have been trying to genereate an SSL certificate from Let's Encrypt to my domain but cert-manager creates the secret and adds a random suffix, causing the NGINX ingress controller to not be able to find it:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/issuer: scaleway
kubernetes.io/tls-acme: "true"
spec:
rules:
- host: <DOMAIN>
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-svc
port:
number: 80
tls:
- hosts:
- <DOMAIN>
secretName: tls-cert
Here is the error in the NGINX logs :
Error obtaining X.509 certificate: no object matching key "default/tls-cert" in local store
Error getting SSL certificate "default/storek8s.igesa.it": local SSL certificate default/storek8s.igesa.it was not found. Using default certificate
The secret is always created like this :
NAME TYPE DATA AGE
...
tls-secret-fjrm5 Opaque 1 60m
How do I disable adding the random suffix for the secret generated by Ingress ? Is there any workaround to solve this ?

At the moment I have too little information to give an exact answer. Are you for example using this Helm chart, what values does your values.yaml contain, and how did you setup your issuer?
Also, I know that tls secrets created by cert-manager should be of type kubernetes.io/tls and not Opaque as is the case in your situation. So there is definitly something going wrong there.

Related

How to use wildcard certificates in Kubernetes with nginx ingress and cert-manager?

I am trying to deploy a React website together with an Express API on GKE. There should be multiple subdomains for both the website and the API, i.e.
domain.com, a.domain.com, b.domain.com, ... -> React app
api.domain.com, a.api.domain.com, b.api.domain.com -> Express API
The reasoning behind this is that the application is using cookie-based authentication, so the API and app need to be on the same subdomain (e.g. a.domain.com and api.a.domain.com).
Cert-manager and nginx ingress were deployed in the Kubernetes cluster with commands:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.8.2/cert-manager.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.5.1/deploy/static/provider/cloud/deploy.yaml
Everything works fine when I explicitly put all subdomains in the ingress and give each a separate TLS entry in the ingress. The certificates are successfully issued. But when using wildcards the certificates never get ready and when I try to open api.domain.com or domain.com it returns a "page not found" error.
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
cert-manager.io/issuer: letsencrypt-production
spec:
rules:
- host: "*.domain.com"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: react
port:
number: 80
- host: wordpress.domain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: wordpress
port:
number: 80
- host: "*.api.domain.com"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api
port:
number: 9000
tls:
- hosts:
- "*.domain.com"
- domain.com
secretName: certificate-wildcard-domain
- hosts:
- "wordpress.domain.com"
secretName: certificate-wordpress-domain
- hosts:
- "*.api.domain.com"
- api.domain.com
secretName: certificate-api-domain
This is what is showing in the logs of the cert-manager pod:
cert-manager/orders "msg"="Failed to determine the list of Challenge resources needed for the Order" "error"="no configured challenge solvers can be used for this challenge" "resource_kind"="Order" "resource_name"="certificate-api-domain-9wvd9-2527200272" "resource_namespace"="default" "resource_version"="v1"
I do not understand the problem, why certificates are not issued correctly for wildcard entries and the website is not reachable, while e.g. for wordpress.domain.com the certificates are issued successfully and the website can be reached.
Issuer/ClusterIssuer solvers(Try Upgrade from v0.10 to v0.11)
Update the apiVersion on all your backed up resources from certmanager.k8s.io/v1alpha1 to cert-manager.io/v1alpha2
Support for the deprecated spec.http01 or spec.dns01 fields in Issuer and ClusterIssuer have been removed. Any Issuer or ClusterIssuer objects must be converted to use the equivalent spec.solvers[].http01 or spec.solvers[].dns01 syntax. You can read more about the Issuer resource in the configuration documentation.
Any issuers that haven't been converted will result in the cert-manager pod being unable to find any solvers at the expected location. This will result in errors like the following: no configured challenge solvers can be used for this challenge.
Let’s Encrypt issues wildcard certificates via ACMEv2 using the DNS-01 challenge. See ACME v2 Production Environment & Wildcard certificates for more technical information. Cert-manager.io: Docs: Configuration: ACME: DNS-01 contains details on the different options available on the Issuer resource's DNS01 challenge solver configuration.
Refer to No configured Challenge Solvers for ACME Prod only #2494 for more information, which may help to resolve your issue.

Adding SSL certificate to ingress controller vs adding it to ingress resource

Ingress controller deployment.yml
spec:
containers:
- args:
- /nginx-ingress-controller
- --default-backend-service=stratus/nginx-ingress-default-backend
- --election-id=ingress-controller-leader
- --ingress-class=nginx
- --configmap=ingress-controller-leader-nginx
- --enable-ssl-passthrough
Ingress resource.yml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: 'REPOSITORY_NAME'
namespace: service
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-passthrough: "false"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
tls:
- hosts:
- "xyz-development.com"
- secretName: ingress-secret-tls
rules:
- host: "xyz-development.com"
http:
paths:
- path: /service/
backend:
serviceName: 'REPOSITORY_NAME'
servicePort: 8080
the secret consists of a signed certificate with the CN as xyz-development.com
endpoint : xyz-development.com/service/swagger-ui.html
If I try to access the endpoint with the above config, I end up with "Your connection is not private" error.
But if I modify the ingress controller deployment.yml to
spec:
containers:
- args:
- /nginx-ingress-controller
- --default-backend-service=stratus/nginx-ingress-default-backend
- --election-id=ingress-controller-leader
- --ingress-class=nginx
- --configmap=stratus/ingress-controller-leader-nginx
- --enable-ssl-passthrough
- --default-ssl-certificate=service/ingress-secret-tls
Then the site is secure with my valid certificate.
Is this expected behaviour?
Even if the default ssl certificate flag is removed in the controller, shouldn't the secret mentioned in the ingress resource.yml be used?
Any other pointers or better practice would be appreciated
There's a minor typo in your manifest that's causing the first option to fail; it should not be a new element in the array of .spec.tls entries:
- secretName: ingress-secret-tls # wrong
secretName: ingress-secret-tls # correct
When you getting connection is not private, are you hitting the URL https://xyz-development.com/ or using an IP address instead? If you are using an IP address, NGINX will not load that TLS certificate, it might load other default certificate (kubernetes fake certificate if you are running on k8s)
And when you add that configuration into ingress controller directly, that will become your default TLS certificate, so whatever domain name that you are using, it will give you that certificate.

How to use digicert with nginx-ingress to enable https

I'm trying to use the certificates obtained through digicert to enable https on my nginx-ingress. We've obtained a wildcard certificate and I have the following files.
domain_name_2019-2021.csr
domain_name_2019-2021.key
domain_name_2019-2021.pem
DigiCertCA2_2019-2021.pem
star_domain_name_2019_2021.pem
TrustedRoot.pem
I've created the tls secrets by running the following commands
kubectl create secret tls tls-secret --key ${KEY_FILE} --cert ${CERT_FILE}
And used these secrets in my ingress configuration like so
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-service
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
tls:
- hosts:
- {{ .Values.host }}
secretName: tls-secret
rules:
- host: {{ .Values.host }}
http:
paths:
- path: /
backend:
serviceName: service_name
servicePort: 443
However when browse to subdomain.domain_name.com I get an invalid certificate with an error of This certificate has not been verified by a third party. And the certificate its using says Kubernetes Ingress Controller Fake Certificate
you can follow this, to install Jetstack cert-manager, once you make this installed, please follow this stackoverflow post.
It will solve your query.
The current certificates created by you are not necessary for this, here the certificate will be automatically created by jetstack once it would be able to get the acme challenge verified, for that verification sake you need to map the DNS or hostname to the Load balancer IP of nginx.
This should solve your purpose to get http to https conversion

Cert-Manager Certificate Renewal process - How it is performed?

I am using cert-manager-v0.10.0 installed from its helm chart
I am using kong like ingress controller to manage the ingress operations.
So I have created a ClusterIssuer resource in order it could be contacted from an Ingress resource via kong-ingress controller.
The ClusterIssuer is this:
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: username#mydomain.org
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-prod
# Enable the HTTP-01 challenge provider
solvers:
- http01:
ingress:
class: kong
The ingress resource that I am using is this.
You can see here, that I am pointing it to the ClusterIssuer created previously and
also I am pointing it to kong as an ingress controller, according to the kubernetes.io/ingress.class: "kong" annotation included there:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
# add an annotation indicating the issuer to use.
certmanager.k8s.io/cluster-issuer: letsencrypt-prod # letsencrypt-staging
kubernetes.io/ingress.class: "kong"
plugins.konghq.com: swaggerapi-customer-production-basic-auth, swaggerapi-customer-production-acl
name: production-customer-ingress-app
# namespace: default
spec:
rules:
- host: appprod.mydomain.org
http:
paths:
- backend:
serviceName: customer-production-app
servicePort: 80
path: /comcustomerpr
tls: # < placing a host in the TLS config will indicate a cert should be created
- hosts:
- appprod.mydomain.org
secretName: letsencrypt-prod # < cert-manager will store the created certificate in this secret.
So, when I create the Ingress resource above, the secretName referenced above in my ingress is created and also a certificate resource with the same name ... that is letsencrypt-prod.
It will be the certificate resource which receive the LetsEncrypt validation successful process ...
I got TLS encryption and everything is OK here.
But now, I want to know how will be the renewal process. Because I am pretty sure at the moment this renewal certificate process it does not to happen automatically ...
I was reading something here https://docs.cert-manager.io/en/latest/reference/certificates.html?highlight=renewal#certificate-duration-and-renewal-window and this documentation says that is necessary attach to the certificate resource created (kind:Certificate) the spec.duration and spec.renewBefore attributes of this way
spec:
secretName: example-tls
duration: 24h
renewBefore: 12h
If my certificate issued by LetsEncrypt has a 90 days as a default duration, how can I specify these spec.duration and spec.renewBefore attributes?
I want to get into in this concern, because my main question is I am not creating the certificate, it is created when the Ingress resource (above referenced) is executed.
How can I address the renewal process here with this approach that I am doing?
UPDATE
Do I need to create a specific kind:Certificate resource, referencing the secret that I got from LetsEncrypt?
I mean, something like this?
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: letsencrypt-prod
spec:
secretName: letsencrypt-prod
dnsNames:
- mydomain.com
acme:
config:
- http01:
ingressClass: kong
domains:
- mydomain.com
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
I have the doubt here, because currently I am not getting the certificate renewal action
since you have used the letsencrypt-prod issuer, and haven't done anything special/non-standard, the certificate renewal process will be completely automatic for you.
By default the letsencrypt certificates are valid fro 90-days, and renewed automatically every 30-days. If you don't have some strict requirements to use purchased certificates, or use some other specific Certificate Authority, this is a great option to use.
If you still have doubts then you can do the following to see for yourself. First decode the current certificates secret data and inspect the certificate contents with the openssl command. You'll be able to see the certificate expiry date, and make a note of that. Now if you subtract 59-days from that expiry date that should give you roughly the date that cert-manager will attempt to renew the certificate on. I add an extra day just to be safe we aren't too early. Then on that date repeat this process again; decoding the certificate secret, inspecting the certificate with the openssl command, and checking the certificate expiry date. You'll notice the expiry date for the certificate is different than before, hence it's was automatically renewed as we expected.
Hope this helps.

Ingress TLS routes with cert-manager not applied

I have a K8s cluster (v1.12.8-gke.10) in GKE and have a nginx ingress with hosts rules. I am trying to enable TLS using cert-manager for ingress routes. I am using a selfsign cluster issuer. But, when I access the site over HTTPS, I am still getting the default K8s certificate. (The certificate is only valid for the following names: kubernetes, kubernetes.default, kubernetes.default.svc, kubernetes.default.svc.cluster.local)
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
namespace: test
name: test
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/ingress.allow-http: "false"
nginx.ingress.kubernetes.io/rewrite-target: /
certmanager.k8s.io/cluster-issuer: selfsign
spec:
tls:
- secretName: test
hosts:
- test.example.com
rules:
- host: test.example.com
http:
paths:
- path: /
backend:
serviceName: test
servicePort: 80
I have checked the following and is working fine:
A cluster issuer named "selfsign"
A valid self-signed certificate backed by a secret "test"
A healthy and running nginx ingress deployment
A healthy and running ingress service of type load-balancer
I think it's issue of clusterissuer
Just have a look at my cluster issuer and check
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: prod
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: it-support#something.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: prod
# Enable the HTTP-01 challenge provider
http01: {}
Check for the right url to get production-grade certificates:
server: https://acme-v02.api.letsencrypt.org/directory
If your server url is something like this :
server: https://acme-staging-v02.api.letsencrypt.org/directory
which means you are applying for the staging certificate which may occur the error.
I've followed the tutorial from Digital Ocean and was able to enable TLS using cert-manager for ingress routes using Helm, Tiller, Letsencrypt and Nginx Ingress controller in GKE.
Instead of host test-example.com, I used my own domain name and spun up dummy backend services (echo1 and echo2) for testing purposes.
After followed the tutorial and to verify that HTTPS is working correctly, try to curl the host:
$ curl test.example.com
you should see a 308 http response (Permanent Redirect). This indicates that HTTP requests are being redirected to use HTTPS.
On the other hand, try running curl on:
$ curl https://test.example.com
Should show you the site response.
You can run the previous commands with the verbose -v flag to check into the certificate handshake and to verify the certificate information.