Ingress provides balancing, SSL termination and name-based routing to Kubernetes services. For more details visit official page
Let's Encrypt is a Security Research Group & TLS Certificate Authority. They provide X.509 certificates for Transport Layer Security encryption free of cost. But the validity of the certificate is 90 day. So for every 90 days, you need to renew the license.
cert-manager is Kubernetes certificate management controller. It can help with issuing certificates from a variety of sources like Let’s Encrypt, HashiCorp Vault, Venafi, a simple signing keypair or self-signed. This controller also checks the if certificates are up to date, if not it renews the certificates. The Certificate can be issued in two ways:
- DNS Validation (Going to describe)
- HTTP Validation
Here we will install Kubernetes Ingress Controller (NGINX), cert-manager and deploy an example application. For the example application, we will generate a certificate from Let's Encrypt and add it to ingress resource for the example application.
- Valid domain (For production use)
- Kubernetes Cluster or Minikube
- Static IP for Kubernetes Host
- CloudFlare account configured with Domain (For DNS Validation Procedure. Check this for configuring domain with CloudFlare)
- Deploy Nginx-Ingress Controller.
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
- Create NodePort of Ingress Controller Service
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/service-nodeport.yaml
- Create Namespace for cert-manager
kubectl create namespace cert-manager
- Disable resource validation on the namespace that cert-manager runs in
kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true
- Deploy cert-manager and CustomResourceDefinitions on Kubernetes
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.10.0/cert-manager.yaml
- Add wildcard Entry in CloudFlare for your all Sub-Domain which will be pointing to Kubernetes Node IP where we have created Ingress Controller Service NodePort in step 2.
- Get your CloudFlare Global API Key from the CloudFlare Dashboard
- Goto CloudFlare Dashboard
- Select Domain
- Goto Account Setting
- Click Get your API key link
- Click View next to Global API Key
- Enter your details and pass the I am not a robot challenge and click View
- Set ENV
API_KEY
with the API Key by encoding to base64. Run Following command to create a secret:API_KEY=$(echo -n 3f7cf4643a2cfeddcadf16ea1ee92f40b1fa7 | base64 -) cat <<EOF | kubectl apply -f - --- apiVersion: v1 kind: Secret metadata: name: cloudflare-api-key namespace: cert-manager type: Opaque data: api-key.txt: ${API_KEY} EOF
- Set ENV
EMAIL_ADDRESS
with your Email Address and Run Following command to create a ClusterIssuer:EMAIL_ADDRESS="[email protected]" cat <<EOF | kubectl apply -f - --- apiVersion: certmanager.k8s.io/v1alpha1 kind: ClusterIssuer metadata: name: letsencrypt spec: acme: server: https://acme-v02.api.letsencrypt.org/directory email: ${EMAIL_ADDRESS} privateKeySecretRef: name: letsencrypt dns01: providers: - name: cf-dns cloudflare: email: ${EMAIL_ADDRESS} apiKeySecretRef: name: cloudflare-api-key key: api-key.txt EOF
- Create a nginx application
kubectl run nginx --image nginx -n default
- Create Service for the nginx application
kubectl expose nginx --port 80 -n default
- Set ENV
DOMAIN_NAME
with your Subdomain Address you want to create for the application and Run Following command to create a Certificate. This Certificate will automatically create a secret with the same name of subdomain ('.' replaced by '-'). Also don't forget to change thenamespace
metadata if you are using different namespace for your application:DOMAIN_NAME="nginx.example.com" cat <<EOF | kubectl apply -f - --- apiVersion: certmanager.k8s.io/v1alpha1 kind: Certificate metadata: name: $(echo $DOMAIN_NAME | tr . -) namespace: default spec: secretName: $(echo $DOMAIN_NAME | tr . -) issuerRef: name: letsencrypt kind: ClusterIssuer commonName: '${DOMAIN_NAME}' dnsNames: - ${DOMAIN_NAME} acme: config: - dns01: provider: cf-dns domains: - ${DOMAIN_NAME} EOF
- Check if Certificate Issued successfully. It may take few minutes.
kubectl describe certificate $(echo $DOMAIN_NAME | tr . -) -n default
- Check if the certificate has created the
secret
successfully. You will get a secret with the same name of subdomain ('.' replaced by '-').kubectl get secret -n default
- Create
ingress
resource for your application and use thesecret
generated by thecertificate
. Also, don't forget to change thenamespace
metadata if you are using a different namespace for your application.cat <<EOF | kubectl apply -f - --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: nginx # Name of Ingress Resource namespace: default # Namespace of your application annotations: kubernetes.io/ingress.class: "nginx" # nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" # For https containernonly # certmanager.k8s.io/cluster-issuer: "letsencrypt" # nginx.ingress.kubernetes.io/ssl-redirect: "true" # nginx.ingress.kubernetes.io/force-ssl-redirect: "true" spec: tls: - hosts: - ${DOMAIN_NAME} secretName: $(echo $DOMAIN_NAME | tr . -) rules: - host: ${DOMAIN_NAME} http: paths: - path: / backend: serviceName: nginx # Service Name of your Application servicePort: 80 # Service Port of your Application. If HTTPS uncomment line - 7 EOF
- To delete certificate and secret generated by certificate run:
Don't delete and create same certificate for multiple times. Somtime it does not allow to create it for multiple time
kubectl delete certificate $(echo $DOMAIN_NAME | tr . -) -n default kubectl delete secret $(echo $DOMAIN_NAME | tr . -) -n default