In this post we will review the steps to create a signed SSL certificate for our site, running in Google Kubernetes Engine (aka GKE) and using Ingress to handle the incoming traffic.
The SSL certificate is created by the Let's Encrypt service, which will automatically, and free of charge supplies the signing service, and renewal service, based on the ownership of the domain in the DNS. This works only if traffic the the related domain that we are signing is sent to the GKE ingress, hence assuring that the SSL certificate requester is indeed authentic.
In the following example, we wee present signing of 2 FQDNs:
www.my-domain.com, api.my-domain.com
After connecting to the GKE cluster, use helm to install cert manager, which will manage the SSL certificate creation and renewals.
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.2.0/cert-manager.crds.yaml
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.2.0
kubectl get pods --namespace cert-manager
The following steps include 2 parts. First we use a test/staging let's encrypt service to ensure that the integration with let's encrypt indeed works well. Only then we move to the production let's encrypt service. This is since the production let's encrypt service has some limits on the requests amounts, and the certificate singing is slower.
Using Staging Let's Encrypt
First we create a test domain certificate.
cat <<EOF > test-resources.yaml
apiVersion: v1
kind: Namespace
metadata:
name: cert-manager-test
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: test-selfsigned
namespace: cert-manager-test
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: selfsigned-cert
namespace: cert-manager-test
spec:
dnsNames:
- example.com
secretName: selfsigned-cert-tls
issuerRef:
name: test-selfsigned
EOF
kubectl apply -f test-resources.yaml
And check that it is working without errors using the command:
kubectl describe certificate -n cert-manager-test
And once we see all is working fine, remove the test domain certificate:
kubectl delete -f test-resources.yaml
Now, let move to the actual staging issuer:
cat <<EOF > clusterissuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: john.doe@my-email.com
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- http01:
ingress:
class: ingress-gce
EOF
kubectl apply -f clusterissuer.yaml
and use the following command to check that the issuer is ready:
kubectl describe clusterissuer letsencrypt-staging
And next update the ingress to use the certificate manager, by adding the annotations, and update the tls section.
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging
acme.cert-manager.io/http01-edit-in-place: "true"
tls:
- secretName: ingress-secret-letsencrypt
hosts:
- www.my-domain.com
- api.my-domain.com
Use the following commands to following the certificate sign process:
kubectl get certificate
kubectl describe certificate ingress-secret-letsencrypt
kubectl describe secret ingress-secret-letsencrypt
Once the process is done, wait for the ingress related load balancer to be updated. It would take about 15 minutes. Then connecting to the domain, we still get an invalid certificate error, since it was signed by the staging service, but when viewing the certificate details, we can see that it was signed by the staging service, and this indicates that we can move to the next step, to use the production service.
Move to Production Let's Encrypt
As we've done before, we create a issuer, but this time for the production service.
cat <<EOF > clusterissuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: john.doe@my-email.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: ingress-gce
EOF
kubectl apply -f clusterissuer.yaml
kubectl delete secret ingress-secret-letsencrypt
And track the progress using the commands:
kubectl describe clusterissuer letsencrypt-prod
kubectl get order
Next, we should wait again for the ingress load balancer to update, for another ~15 minutes, and then check that the certificate is indeed valid.
Final Note
In case of need, see also how to start with a self signed certificate GKE ingress in
the following post.