Generating Let’s Encrypt Wildcard Certs Using Cert Manager on GKE
Generating SSL certs using cert-manager and ingress-nginx ingress controller is straightforward. Cert manager uses either HTTP01 or DNS01 solvers to verify domain ownership before generating an SSL certificate. For wildcard SSL certificates you can only use DNS01 solver. Before jumping into the actual creation of wildcard SSL certificates, let's look at a few scenarios where wildcard SSL certs make sense.
- Assume you want to provide a custom sub-domain for each organization/user in the application. eg: https://org1.app.example.com https://user.app.example.com
- Convenience. Instead of maintaining certs for each sub-domain you can create one cert and use it for all sub-domains.
In this article, we will create a Let’s Encrypt wildcard certificate for a domain in GKE using cert-manager. For certificate issuing, we will use Let's Encrypt.
In the following example, we will create an Nginx deployment and expose the service using the ingress controller.
Nginx deployment and service
Apply the following Nginx deployment using kubectl apply -f nginx-deployment.yaml
.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
Apply the following Nginx service using kubectl apply -f nginx-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
run: nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
app: nginx
Install Ingress controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.43.0/deploy/static/provider/cloud/deploy.yaml
Install cert-manager
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.1.0/cert-manager.yaml
Configure Google Cloud DNS
Before setting the DNS01 solver you should first configure your cloud DNS.
1. Enable Google Cloud DNS API — https://console.cloud.google.com/apis/library/dns.googleapis.com?q=clouddns
After enabling the API, go to Google Cloud DNS console — Networking -> Network Services -> Cloud DNS
2. Create public zone
3. Update your nameservers with the values in zone details (optional step).
This is how you do it in GoDaddy and Google Domains. You can find it fairly easy for other domain registrars.
Setting up DNS01 solver
Use DNS01 solver instead of HTTP01 solver. It varies depending on your cloud provider. The following steps are for GCP.
gcloud auth login
export PROJECT_ID=<project_id># service account
gcloud iam service-accounts create dns01-solver --display-name "dns01-solver"# iam
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member serviceAccount:dns01-solver@$PROJECT_ID.iam.gserviceaccount.com \
--role roles/dns.admin# download service account key
gcloud iam service-accounts keys create key.json \
--iam-account dns01-solver@$PROJECT_ID.iam.gserviceaccount.com
Service account JSON key as Kubernetes secret
kubectl create secret generic clouddns-dns01-solver-svc-acct \
--from-file=key.json
Cert Manager — ClusterIssuer resource
Apply the following ClusterIssuer resource using kubectl apply -f clusterissuer.yaml
.
Replace <email>
and project_id
with the actual values.
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt
namespace: default
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: <email>
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt
# Enable the HTTP-01 challenge provider
solvers:
- dns01:
clouddns:
project: <project_id>
serviceAccountSecretRef:
name: clouddns-dns01-solver-svc-acct
key: key.json
Cert Manager — Certificate resource
Apply the following Certificate resource using kubectl apply -f certificate.yaml
.
Replace <domain>
with your actual domain.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: acme-crt
spec:
secretName: acme-crt-secret-wildcard
dnsNames:
- '*.<domain>'
acme:
config:
- dns01:
provider: dns
domains:
- '*.<domain>'
issuerRef:
name: letsencrypt
kind: ClusterIssuer
Before using the certificate make sure your certificate has been created successfully.
kubectl get certificates
Use the certificate with ingress resource
Apply the following ingress resource using kubectl apply -f ingress.yaml
. Replace <domain>
with the actual domain.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: "nginx"
name: test-ingress
spec:
tls:
- hosts:
- test.<domain>
secretName: acme-crt-secret-wildcard
rules:
- host: test1.<domain>
http:
paths:
- backend:
serviceName: nginx
servicePort: 80
path: /
Gotchas
- Updating nameservers is optional. After you create a certificate resource in Kubernetes, cert-manager creates a TXT record in Cloud DNS. If you wish not to update your nameservers, you can add the TXT record created in Cloud DNS to your domain registrar. This is how you add a TXT record in GoDaddy.
- Certificates are namespaced resources. If you are creating ingress resources in other namespaces make sure you replicate the secret in other namespaces.
References and Tools
Setting up DNS01 resolver on Google Cloud DNS
To verify if SSL certs are being served — https://www.whynopadlock.com/
To verify DNS propagation — https://www.whatsmydns.net/