# Introduction to cert-manager for Kubernetes cert-manager adds certificates and certificate issuers as resource types in Kubernetes clusters and simplifies obtaining, renewing and using those certificates. ![](https://i.imgur.com/lvqE24E.png) I am gonna be discussing the two examples of cert-manager which would be selfsigned and ACME. **We need a Kubernetes cluster** Lets create a Kubernetes cluster to play with using kind `kind create cluster --name certmanager --image kindest/node:v1.19.1` **Concepts** It's important to understand the various concepts and new Kubernetes resources that cert-manager introduces. Issuers [docs](https://cert-manager.io/docs/concepts/issuer/) Certificate [docs](https://cert-manager.io/docs/concepts/certificate/) CertificateRequests [docs](https://cert-manager.io/docs/concepts/certificaterequest/) Orders and Challenges [docs](https://cert-manager.io/docs/concepts/acme-orders-challenges/) **Installation** You can find the latest release for cert-manager on their GitHub Releases page ``` # Get a container to work in # mount our kubeconfig file and source code docker run -it --rm -v ${HOME}:/root/ -v ${PWD}:/work -w /work --net host alpine sh # install kubectl for mac brew install kubectl # or for Linux apk add --no-cache curl curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl chmod +x ./kubectl mv ./kubectl /usr/local/bin/kubectl ``` ### test cluster access: ``` kubectl get nodes NAME STATUS ROLES AGE VERSION certmanager-control-plane Ready master 3m6s v1.19.1 # get cert-manager cd cert-manager curl -LO https://github.com/jetstack/cert-manager/releases/download/v1.10.0/cert-manager.yaml mv cert-manager.yaml cert-manager-1.10.0.yaml # install cert-manager kubectl apply --validate=false -f cert-manager-1.10.0.yaml ``` ## Cert Manager Resources We can see our components deployed ``` kubectl -n cert-manager get all NAME READY STATUS RESTARTS AGE pod/cert-manager-86548b886-2b8x7 1/1 Running 0 77s pod/cert-manager-cainjector-6d59c8d4f7-hrs2v 1/1 Running 0 77s pod/cert-manager-webhook-578954cdd-tphpj 1/1 Running 0 77s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/cert-manager ClusterIP 10.96.87.136 <none> 9402/TCP 77s service/cert-manager-webhook ClusterIP 10.104.59.25 <none> 443/TCP 77s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/cert-manager 1/1 1 1 77s deployment.apps/cert-manager-cainjector 1/1 1 1 77s deployment.apps/cert-manager-webhook 1/1 1 1 77s NAME DESIRED CURRENT READY AGE replicaset.apps/cert-manager-86548b886 1 1 1 77s replicaset.apps/cert-manager-cainjector-6d59c8d4f7 1 1 1 77s replicaset.apps/cert-manager-webhook-578954cdd 1 1 1 77 ``` ## Test Certificate Issuing Let's create some test certificates ./cert-manager/selfsigned/issuer.yaml ``` apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: test-selfsigned namespace: cert-manager-example spec: selfSigned: {} ``` ./cert-manager/selfsigned/certificate.yaml ``` apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: selfsigned-cert namespace: cert-manager-example spec: dnsNames: - example.com secretName: selfsigned-cert-tls issuerRef: name: test-selfsigned ``` Now lets apply the issuer and certificate in our kubernetes cluster. ``` kubectl create ns cert-manager-example kubectl apply -f ./selfsigned/issuer.yaml kubectl apply -f ./selfsigned/certificate.yaml kubectl describe certificate -n cert-manager-example kubectl get secrets -n cert-manager-example kubectl delete ns cert-manager-example ``` ## Configuration https://cert-manager.io/docs/configuration/ # Example 2: Creating a Basic ACME Issuer ./cert-manager/certificate.yaml ``` apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: example-app namespace: default spec: dnsNames: - marcel.guru secretName: example-app-tls issuerRef: name: letsencrypt-cluster-issuer kind: ClusterIssuer ``` ./cert-manager/ingress.yaml ``` apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: "nginx" name: example-app spec: tls: - hosts: - marcel.guru secretName: example-app-tls rules: - host: marcel.guru http: paths: - path: / pathType: Prefix backend: service: name: example-service port: number: 80 ``` ./cert-manager/cert-issuer-nginx-ingress.yaml ``` apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-cluster-issuer spec: acme: server: https://acme-v02.api.letsencrypt.org/directory email: your-email@email.com privateKeySecretRef: name: letsencrypt-cluster-issuer-key solvers: - http01: ingress: class: nginx ``` ## Ingress Controller Let's deploy an Ingress controller: ``` kubectl create ns ingress-nginx kubectl -n ingress-nginx apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.41.2/deploy/static/provider/cloud/deploy.yaml kubectl -n ingress-nginx get pods kubectl -n ingress-nginx --address 0.0.0.0 port-forward svc/ingress-nginx-controller 80 kubectl -n ingress-nginx --address 0.0.0.0 port-forward svc/ingress-nginx-controller 443 ``` We should be able to access NGINX in the browser and see a 404 Not Found page: http://localhost/ This indicates there are no routes to / and the ingress controller is running ## Setup my DNS In my container, I can get the public IP address of my computer by running a simple command: `curl ifconfig.co` I can log into my DNS provider and point my DNS A record to my IP. Also setup my router to allow 80 and 443 to come to my PC If you are running in the cloud, your Ingress controller and Cloud provider will give you a public IP and you can point your DNS to that accordingly. Create Let's Encrypt Issuer for our cluster We create a ClusterIssuer that allows us to issue certs in any namespace ``` kubectl apply -f cert-issuer-nginx-ingress.yaml # check the issuer kubectl describe clusterissuer letsencrypt-cluster-issuer ``` ## Deploy a pod that uses SSL ./deployments/deployment.yaml ``` apiVersion: apps/v1 kind: Deployment metadata: name: example-deploy labels: app: example-app test: test spec: selector: matchLabels: app: example-app replicas: 2 template: metadata: labels: app: example-app spec: containers: - name: example-app image: aimvector/python:1.0.4 imagePullPolicy: Always ports: - containerPort: 5000 ``` ./services/service.yaml ``` apiVersion: v1 kind: Service metadata: name: example-service labels: app: example-app spec: type: LoadBalancer selector: app: example-app ports: - protocol: TCP name: http port: 80 targetPort: 5000 ``` ``` kubectl apply -f deployments/deployment.yaml kubectl apply -f services/service.yaml kubectl get pods # deploy an ingress route kubectl apply -f ingress.yaml Issue Certificate kubectl apply -f certificate.yaml # check the cert has been issued kubectl describe certificate example-app ``` ``` # TLS created as a secret kubectl get secrets NAME TYPE DATA AGE example-app-tls kubernetes.io/tls 2 84m ```