ClusterIssuer with Cloudflare based DNS solver - kubernetes

I'm trying to install the cert-manager ClusterIssuer on a AKS, and because the cluster is behind Azure Application Gateway I've gone down the route of using a DNS solver rather the HTTP. However, the challenge fails with an error calling the Cloudflare API. I've redacted emails and domains through the code snippets, the output of kubectl describe challenge rabt-cert-tls-g4mcl-1991965707-2468967546 is:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Started 72s cert-manager Challenge scheduled for processing
Warning PresentError 3s (x5 over 71s) cert-manager Error presenting challenge: Cloudflare API Error for GET "/zones?name=<domain>"
Error: 6003: Invalid request headers<- 6103: Invalid format for X-Auth-Key header
I have followed the guide at https://blog.darkedges.com/2020/05/04/cert-manager-kubernetes-cloudflare-dns-update/ and the issues at https://github.com/jetstack/cert-manager/issues/3021 and https://github.com/jetstack/cert-manager/issues/2384 but can't see any differences beyond the apiVersion of the issuer. I've checked this against the official documentation and there are no changes from what appears in these guides.
The relationship between ingress and cluster issuer seems fine; if I delete and recreate the ingress a new certificate, order and challenge are created. I've verified the secret is populated and I can print it to console, so it shouldn't be sending a blank string in the header. The token is valid, I can use the example CURL request from CloudFlare to check its validity.
Is there somewhere I can see logs and find out exactly what is being sent?
ClusterIssuer
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-api-token-secret
namespace: cert-manager
type: Opaque
stringData:
api-token: ${CLOUDFLARE_API_TOKEN}
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: rabt-letsencrypt
spec:
acme:
# You must replace this email address with your own.
# Let's Encrypt will use this to contact you about expiring
# certificates, and issues related to your account.
email: <email>
# ACME server URL for Let’s Encrypt’s staging environment.
# The staging environment will not issue trusted certificates but is
# used to ensure that the verification process is working properly
# before moving to production
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
# Secret resource used to store the account's private key.
name: rabt-letsencrypt-key
# Enable the HTTP-01 challenge provider
# you prove ownership of a domain by ensuring that a particular
# file is present at the domain
solvers:
- dns01:
cloudflare:
email: <email>
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-key
Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rabt-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: azure/application-gateway
appgw.ingress.kubernetes.io/backend-protocol: https
appgw.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: rabt-letsencrypt
cert-manager.io/acme-challenge-type: dns01
appgw.ingress.kubernetes.io/backend-path-prefix: "/"
spec:
tls:
- hosts:
- "*.rabt.<domain>"
secretName: rabt-cert-tls
rules:
- host: "mq.rabt.<domain>"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: rabt-mq
port:
number: 15672
- host: es.rabt.<domain>
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: rabt-db-es-http
port:
number: 9200
- host: "kibana.rabt.<domain>"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: rabt-kb-http
port:
number: 5601

As Harsh Manvar guessed, it was an issue with the secret. I wasn't running the kubectl apply command through envsubst so it was encoding the literal string "${CLOUDFLARE_API_TOKEN}"

Related

How to create another issuer for another subdomain in Kubernetes using Nginx-ingress controller?

I am trying to create another Issuer can for another subdomain. I am following this example: Digital Ocean Kubernetes tutorial and in this example the author gives an example for the http://echo.starter-kit.online/ subdomain which I was able to get working using my own subdomain.
I am trying to get this working for the quote.starter-kit.online example by creating a new Issuer like following:
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: quote-letsencrypt-nginx
namespace: backend
spec:
# ACME issuer configuration
# `email` - the email address to be associated with the ACME account (make sure it's a valid one)
# `server` - the URL used to access the ACME server’s directory endpoint
# `privateKeySecretRef` - Kubernetes Secret to store the automatically generated ACME account private key
acme:
email: my#mydomain.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: quote-letsencrypt-nginx-private-key
solvers:
# Use the HTTP-01 challenge provider
- http01:
ingress:
class: nginx
And the following Ingress rule for the quote subdomain:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-quote
namespace: backend
annotations:
cert-manager.io/issuer: letsencrypt-nginx
spec:
tls:
- hosts:
- quote.mydomain.com
secretName: quote-letsencrypt
rules:
- host: quote.mydomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: quote
port:
number: 8080
ingressClassName: nginx
when I do the following:
>kubectl get certificates -n backend
NAME READY SECRET AGE
letsencrypt-nginx True letsencrypt-nginx 5d2h
quote-letsencrypt-nginx False quote-letsencrypt-nginx 2s
I can see the certs. However, when I do the following I see the https is not working:
curl -Li quote.mydomain.com
HTTP/1.1 308 Permanent Redirect
Date: Sun, 02 Jan 2022 23:49:40 GMT
Content-Type: text/html
Content-Length: 164
Connection: keep-alive
Location: https://quote.mydomain.com
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
Try:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-quote
namespace: backend
annotations:
cert-manager.io/issuer: quote-letsencrypt-nginx # <-- changed
spec:
tls:
- hosts:
- quote.mydomain.com
secretName: quote-letsencrypt-tls
rules:
...

cert-manager: no configured challenge solvers can be used for this challenge

I followed this instruction to set up a cert manager on my EKS cluster https://cert-manager.io/docs/tutorials/acme/ingress/.
here is my ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/issuer: "letsencrypt-staging"
spec:
tls:
- hosts:
- '*.test.com'
secretName: test-tls
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: test-service
port:
number: 80
Here is the issuer. I just copied the config from the instruction
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: info#test.com
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- http01:
ingress:
class: nginx
After deployment, I found the certificate ready state is false
kubectl get certificate
NAME READY SECRET AGE
test-tls False test-tls 2m45s
Then I followed this to troubleshoot https://cert-manager.io/docs/faq/troubleshooting/
I ran kubectl describe certificaterequest <request name>, found error Waiting on certificate issuance from order test-tls-xxx: "pending"
then ran kubectl describe order test-tls-xxx, found error
Warning Solver 20m cert-manager Failed to determine a valid solver configuration for the set of domains on the Order: no configured challenge solvers can be used for this challenge.
Any idea why it couldn't determine a valid solver? how do I test if solver is working?
It's not working due you are using the staging URL in cluster issuer to verify the image.
Please try with the Production URL.
here a simple and proper example of Clusterissuer and ingress YAML (do note you were trying with staging API https://acme-staging-v02.api.letsencrypt.org/directory if possible use the production server address so it works properly with all browsers)
Example:
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: cluster-issuer-name
namespace: development
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: harsh#example.com
privateKeySecretRef:
name: secret-name
solvers:
- http01:
ingress:
class: nginx-class-name
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx-class-name
cert-manager.io/cluster-issuer: cluster-issuer-name
nginx.ingress.kubernetes.io/rewrite-target: /
name: example-ingress
spec:
rules:
- host: sub.example.com
http:
paths:
- path: /api
backend:
serviceName: service-name
servicePort: 80
tls:
- hosts:
- sub.example.com
secretName: secret-name
Note : When you are trying again please try deleting the old objects like ingress, Clusterissuer first.
Issuer vs ClusterIssuer
An Issuer is a namespaced resource, and it is not possible to issue
certificates from an Issuer in a different namespace. This means you
will need to create an Issuer in each namespace you wish to obtain
Certificates in.
If you want to create a single Issuer that can be consumed in multiple
namespaces, you should consider creating a ClusterIssuer resource.
This is almost identical to the Issuer resource, however is
non-namespaced so it can be used to issue Certificates across all
namespaces.
Ref : https://cert-manager.io/docs/concepts/issuer/
Wildcard cert
You can use as per requirement, if you are using issuer you can update the ingress annotation line like
cert-manager.io/issuer: issuer-name
If you are trying to get the wildcard * certificate you won't be able to get it using HTTP auth method
solvers:
- http01:
ingress:
class: nginx-class-name
instead of this you have to use the DNS-auth method for wildcard cert.
solvers:
- dns01:
cloudDNS:
project: my-project
serviceAccountSecretRef:
name: prod-clouddns-svc-acct-secret
key: service-account.json
Read more at : https://cert-manager.io/docs/configuration/acme/dns01/
Ref article to get the wildcard cert : https://medium.com/#harsh.manvar111/wild-card-certificate-using-cert-manager-in-kubernetes-3406b042d5a2

How to make ingress use my TLS Certificate in Microk8s

I have the following Ingress configuration:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: http-ingress
spec:
rules:
- host: example-adress.com
http:
paths:
- path: /apple
pathType: Prefix
backend:
service:
name: apple-service
port:
number: 80
- path: /banana
pathType: Prefix
backend:
service:
name: banana-service
port:
number: 80
tls:
- hosts:
- example-adress.com
secretName: testsecret-tls
And i also created the Secret:
apiVersion: v1
kind: Secret
metadata:
name: testsecret-tls
namespace: default
data:
tls.crt: path to .crt
tls.key: Zpath to .key
type: kubernetes.io/tls
But when i connect to one of my services and check the certificate it says that it uses a cert created by Kubernetes Ingress Controller Fake certificate.
When i run microk8s kubectl describe ingress i get the following output:
Name: http-ingress
Namespace: default
Address: 127.0.0.1
Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
TLS:
testsecret-tls terminates example-adress.com
Rules:
Host Path Backends
---- ---- --------
example-adress.com
/apple apple-service:80 (10.1.55.17:5678)
/banana banana-service:80 (10.1.55.10:5678)
Annotations: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal CREATE 28m nginx-ingress-controller Ingress default/http-ingress
Normal UPDATE 20m (x2 over 28m) nginx-ingress-controller Ingress default/http-ingress
What do i need to change to make my Ingress use my Cert instead of generating a new one everytime?
Posting this out of comment as it works.
Based on your tls secret yaml, you tried to add certificate and private key using paths, which is not supported currently (reference)
Fragment from reference:
When using this type of Secret, the tls.key and the tls.crt key must be provided in the data (or stringData) field of the Secret configuration, although the API server doesn't actually validate the values for each key.
Therefore there are two suggestions how to move forward:
Add base64 encrypted values for key and certificate to tls secret
Allow kubernetes do it for you with the following command:
kubectl create secret tls testsecret-tls --cert=tls.cert --key=tls.key

kubernetes certmanager letsencrypt giving error "Your connection is not private"

I set up a cluster issuer, certificate, and ingress under my Kubernetes environment and everything is working fine as per status, but when I am connecting to the host as per my ingress, it's giving me "Your connection is not private".
CluserterIssuer output lastlines;-
...
Conditions:
Last Transition Time: 2020-02-16T10:21:24Z
Message: The ACME account was registered with the ACME server
Reason: ACMEAccountRegistered
Status: True
Type: Ready
Events: <none>
Certificate output last lines :-
Status:
Conditions:
Last Transition Time: 2020-02-16T10:24:06Z
Message: Certificate is up to date and has not expired
Reason: Ready
Status: True
Type: Ready
Not After: 2020-05-14T09:24:05Z
Events: <none>
Ingress file:-
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: b1-ingress # change me
namespace: b1
annotations:
kubernetes.io/ingress.class: "nginx"
kubernetes.io/tls-acme: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- '*.testing.example.com'
secretName: acme-crt
rules:
- host: flower.testing.example.com
http:
paths:
- backend:
serviceName: flower-service
servicePort: 5555
- host: hasura.testing.example.com
http:
paths:
- backend:
serviceName: hasura-service
servicePort: 80
Based on cert menager documentation
certmanager.k8s.io/issuer - The Issuer must be in the same namespace as the Ingress resource.
As #me25 confirmed in comments
yes everything worked when I copied secret in to namespace: b1 – me25
The answer here was missing secret certificate in proper namespace.
The solution was to copy secret certificate to namespace: b1,same as the ingress.
Do you know any better way other than a copy secrets
This stackoverflow post provide few tricks about copying secret from one namespace to other.
Additional links:
https://itnext.io/automated-tls-with-cert-manager-and-letsencrypt-for-kubernetes-7daaa5e0cae4
https://cert-manager.io/docs/tutorials/acme/ingress/
nginx server is using self-signed certificates and chrome does not trust that. usually when u click on "Advanced" tab, you should have an option that says "trust this website" or something like that and nginx server config does not show that. the solution is just type this on the keyword :
"thisisunsafe"
and error message will gone

How to deploy a letsencryp with cert-manager and HAProxy-ingress

Digging into www, didn't found an answer:
I want to know how I can use cert-manager with haproxy-ingress and lets encrypt.
Any documentation / guidelines?
Deploy Certmanager with:
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.4.0/cert-manager.yaml
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.4.0/cert-manager.crds.yaml
Deploy a ClusterIssuer (cluster issuers are namespace agnostic)
cat > prod-issuer.yaml <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: email#example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: haproxy
EOF
Apply the cluster issuer with kubectl apply -f prod-issuer.yaml
Create an Ingress Resource (namespace gnostic, this example is using Nginx backend service)
cat > nginx-ingress.yaml <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-router
namespace: production
annotations:
kubernetes.io/ingress.class: "haproxy"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- "example.com"
secretName: nginx-tls-secret
rules:
- host: example.com
http:
paths:
- pathType: ImplementationSpecific
path: "/"
backend:
service:
name: nginx
port:
number: 80
- host: www.example.com
http:
paths:
- pathType: ImplementationSpecific
path: "/"
backend:
service:
name: nginx
port:
number: 80
EOF
Apply the ingress recourse with kubectl apply -f nginx-ingress.yaml
The important piece of information here, is that the Haproxy controller does NOT need the annotation acme.cert-manager.io/http01-edit-in-place: "true"
that nginx-ingress controller does. It works as expected without any extra annotations.
When you apply the Ingress Resourse to the cluster, the certificate will be issued in 1-2 minutes tops.
Use kubectl describe certificate nginx-tls-secret -n production to check the status of the certificate, and look at the event to get the certificate.
For more debugging info incase something went wrong, refer here https://cert-manager.io/docs/faq/acme/
you can try installing cert manager provided by jetstack, can be found here and then you need to follow the steps mentioned in this stackoverflow post and this will get things sorted for you.
An internal acme-challenge will be done by cert manager and once you patch the secret name, mentioned in the certificate to the TLS of ingress then certificate status will get ready state, Note that the secret will get created automatically, you need not create it