Generating Let’s Encrypt Wildcard Certs Using Cert Manager on GKE

Goutham Pilla
4 min readJan 18, 2021

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.

  1. 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
  2. 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

Cloud DNS — Google Cloud Console

2. Create public zone

Create a zone — Google Cloud Console

3. Update your nameservers with the values in zone details (optional step).

Name server details

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

Cert Manager concepts

Online dig

To verify if SSL certs are being served — https://www.whynopadlock.com/

To verify DNS propagation — https://www.whatsmydns.net/

--

--