Single Node Kubernetes Cluster on Raspberry Pi - Part Two
In the first part we configured a Kubernetes cluster, deployed a Spring Boot application, configured a firewall and exposed the cluster to the world. However, there was something else I would like to cover. Kubernetes generates a self signed SSL/TSL certificate for HTTPS requests and this is not very nice, specially because you will see an ugly warning on your browser saying that the certificate is not trusted 😢.
On this part we will cover how to issue a trusted SSL/TSL certificate with cert-manager and Let’s Encrypt! 🚀
Quick intro
We are going to use cert-manager for issuing trusted SSL/TLS certificates for our Kubernetes cluster. cert-manager is able to generate certificates from some of the most popular certificate authorities like Let’s Encrypt, Vault or Venafi.
cert-manager will also make sure that our certificates are valid and up to date.
cert-manager installation
There are multiple ways of installing cert-manager in our Kubernetes cluster and for this guide we will use Helm.
- First of all make sure you have
Helm
cli installed. On mac you can install it via Homebrew:brew install helm
-
Install Jetstack and update repository cache:
helm repo add jetstack https://charts.jetstack.io helm repo update
-
A few new Kubernetes CRD (CustomResourceDefinition) resources are required:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.8.2/cert-manager.crds.yaml
this is installing version
v1.8.0
but a newer version might be available. -
Install cert-manager in the Kubernetes cluster:
helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.8.0
Issue an ACME certificate using HTTP validation
Now that cert-manager is running in our Kubernetes cluster we can start issues our certificates.
We are going to create to issuers, one for stating and one for production, so we can test the one for staging before issuing production certification, since there is a rate limiting in the number of certificates that Let’s Encrypt issues per day.
-
Create
ClusterIssuer
resource definition.staging-issuer.yaml
apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-staging spec: acme: server: https://acme-staging-v02.api.letsencrypt.org/directory email: <your_email> # it should be a valid one privateKeySecretRef: name: letsencrypt-staging solvers: - selector: {} http01: ingress: class: public # the built-in ingress class is called public in MicroK8s, not nginx
production-issuer.yaml
apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-prod spec: acme: server: https://acme-v02.api.letsencrypt.org/directory email: <your_email> # it should be a valid one privateKeySecretRef: name: letsencrypt-prod solvers: - selector: {} http01: ingress: class: public # the built-in ingress class is called public in MicroK8s, not nginx
IMPORTANT: The selected ingress class is called
public
instead ofnginx
. This is specific for a MicroK8s cluster.
-
Apply the issuers:
kubectl apply -f staging-issuer.yaml kubectl apply -f prod-issuer.yaml
-
Update the
ingress.yaml
:apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: spring-boot-demo-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / cert-manager.io/cluster-issuer: "letsencrypt-staging" spec: tls: - hosts: - <your_dns> secretName: tls-secret rules: - host: <your_dns> # this is required to make the certificate work http: paths: - path: / pathType: Prefix backend: service: name: spring-boot-demo-service port: number: 8080
After a few seconds or minutes when running
kubectl get certificate
you should see:NAME READY SECRET AGE tls-secret True tls-secret <some_time>
For the ACME challenge validation to succeed you might need to open the port 80.
If everything went well you can now use the production certificate by updating the ingress. Otherwise I recommend to use this troubleshooting page from cert-manager.
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: spring-boot-demo-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / cert-manager.io/cluster-issuer: "letsencrypt-prod" spec: tls: - hosts: - <your_dns> secretName: tls-secret rules: - host: <your_dns> # this is required to make the certificate work http: paths: - path: / pathType: Prefix backend: service: name: spring-boot-demo-service port: number: 8080
Now browsers shouldn’t show any warnings about self signed certificates! 🙌