Install NGINX Ingress Controller on Kubernetes + Let's Encrypt Cert Issuer
Tags: kubernetes, letsencrypt, nginx
Published: 2020-11-14 20:43:39

This article is a perspective rewrite of the given sources that allowed me to get from point A to point B from my perspective.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/moq05num3o8cx61mxghm.png)

Requirements:
- A Kubernetes Cluster
- Helm (https://helm.sh/docs/using_helm/)

I recommend DigitalOcean for a very affordable Kubernetes Cluster.

Click here to sign-up for Digital Ocean: https://m.do.co/c/79bf9fee3de8.

Amazon has its own Managed k8s service as well: https://aws.amazon.com/eks/.

Google (the creators of Kubernetes) has its own, very convenient and highly managed, k8s service. https://cloud.google.com/kubernetes-engine/.

---

## Apply the latest version of the NGINX Ingress Controller
Check https://github.com/kubernetes/ingress-nginx/releases for the newest version number.
```bash
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.24.1/deploy/mandatory.yaml
```
Output:
```
namespace/ingress-nginx created
configmap/nginx-configuration created
configmap/tcp-services created
configmap/udp-services created
serviceaccount/nginx-ingress-serviceaccount created
clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole created
role.rbac.authorization.k8s.io/nginx-ingress-role created
rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding created
deployment.extensions/nginx-ingress-controller created
```
Install the Cloud Generic Load Balancer for the Ingress
```bash
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.24.1/deploy/provider/cloud-generic.yaml -n ingress-nginx
```
Output:
```
service/ingress-nginx created
// If you want all nodes to pass the health check:
$ kubectl patch svc ingress-nginx -n ingress-nginx -p '{"spec":{"externalTrafficPolicy":"Cluster"}}'
```
You can check on the service with:
```bash
$ kubectl get svc --namespace=ingress-nginx
```
## Installing cert-manager + lets-encrypt
Apply the CRDs (Custom Resource Definitions)
```bash
$ kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.8/deploy/manifests/00-crds.yaml
```
Output:
```
customresourcedefinition.apiextensions.k8s.io/certificates.certmanager.k8s.io created
customresourcedefinition.apiextensions.k8s.io/challenges.certmanager.k8s.io created
customresourcedefinition.apiextensions.k8s.io/clusterissuers.certmanager.k8s.io created
customresourcedefinition.apiextensions.k8s.io/issuers.certmanager.k8s.io created
customresourcedefinition.apiextensions.k8s.io/orders.certmanager.k8s.io created
```
Set a label on `kube-system` namespace to enable advanced resource validation with a webhook.
```bash
$ kubectl label namespace kube-system certmanager.k8s.io/disable-validation="true"
```
Output:
```
namespace/kube-system labeled
```
---
If you do not have Helm installed on your client and k8s cluster. Before the next step, I had to install the Helm client (https://helm.sh/docs/using_helm/) and run the following command:
```bash
$ helm init
```
Output:
```
$HELM_HOME has been configured at /Users/<User>/.helm.
Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.
Please note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.
To prevent this, run `helm init` with the --tiller-tls-verify flag.
For more information on securing your installation see: https://docs.helm.sh/using_helm/#securing-your-helm-installation
```
Then I had to give tiller a `ServiceAccount` and link everything up via roles.
```bash
$ kubectl create serviceaccount --namespace kube-system tiller
```
Output:
```
serviceaccount/tiller created
$ kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
```
Output:
```
clusterrolebinding.rbac.authorization.k8s.io/tiller-cluster-rule created
$ kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'
```
Output:
```
deployment.extensions/tiller-deploy patched
```
---
Add `jetstack` to your Helm repositories.
```bash
$ helm repo add jetstack https://charts.jetstack.io
```
Output:
```
"jetstack" has been added to your repositories
```
Install the cert-manager helm chart.
```bash
$ helm install --name cert-manager --namespace kube-system jetstack/cert-manager --version v0.8.0
```
Output:
```
NAME:   cert-manager
LAST DEPLOYED: Tue Jun 25 08:39:05 2019
NAMESPACE: kube-system
STATUS: DEPLOYED
RESOURCES:
==> v1/ClusterRole
NAME                                    AGE
cert-manager-edit                       3s
cert-manager-view                       3s
cert-manager-webhook:webhook-requester  3s
==> v1/Pod(related)
NAME                                     READY  STATUS             RESTARTS  AGE
cert-manager-5d669ffbd8-glqgv            0/1    ContainerCreating  0         3s
cert-manager-cainjector-79b7fc64f-bwn7t  0/1    ContainerCreating  0         3s
cert-manager-webhook-6484955794-p2jgn    0/1    ContainerCreating  0         3s
==> v1/Service
NAME                  TYPE       CLUSTER-IP      EXTERNAL-IP  PORT(S)  AGE
cert-manager-webhook  ClusterIP  ${CLUSTER_IP}  <none>       443/TCP  3s
==> v1/ServiceAccount
NAME                     SECRETS  AGE
cert-manager             1        3s
cert-manager-cainjector  1        3s
cert-manager-webhook     1        3s
==> v1alpha1/Certificate
NAME                              AGE
cert-manager-webhook-ca           3s
cert-manager-webhook-webhook-tls  2s
==> v1alpha1/Issuer
NAME                           AGE
cert-manager-webhook-ca        2s
cert-manager-webhook-selfsign  2s
==> v1beta1/APIService
NAME                                  AGE
v1beta1.admission.certmanager.k8s.io  3s
==> v1beta1/ClusterRole
NAME                     AGE
cert-manager             3s
cert-manager-cainjector  3s
==> v1beta1/ClusterRoleBinding
NAME                                 AGE
cert-manager                         3s
cert-manager-cainjector              3s
cert-manager-webhook:auth-delegator  3s
==> v1beta1/Deployment
NAME                     READY  UP-TO-DATE  AVAILABLE  AGE
cert-manager             0/1    1           0          3s
cert-manager-cainjector  0/1    1           0          3s
cert-manager-webhook     0/1    1           0          3s
==> v1beta1/RoleBinding
NAME                                                AGE
cert-manager-webhook:webhook-authentication-reader  3s
==> v1beta1/ValidatingWebhookConfiguration
NAME                  AGE
cert-manager-webhook  2s
NOTES:
cert-manager has been deployed successfully!
In order to begin issuing certificates, you will need to set up a ClusterIssuer
or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).
More information on the different types of issuers and how to configure them
can be found in our documentation:
https://docs.cert-manager.io/en/latest/reference/issuers.html
For information on how to configure cert-manager to automatically provision
Certificates for Ingress resources, take a look at the `ingress-shim`
documentation:
https://docs.cert-manager.io/en/latest/reference/ingress-shim.html
```
Create a file called `issuer.yaml`:
```yaml
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: letsencrypt
spec:
  acme:
    # The ACME server URL
    server: https://acme-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: ${YOUR_EMAIL_ADDRESS}
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt
    # Enable the HTTP-01 challenge provider
    http01: {}
```
Replace `${YOUR_EMAIL_ADDRESS}` with your e-mail address.

Create the issuer with the following command:
```bash
$ kubectl create -f issuer.yaml
```
Output:
```
clusterissuer.certmanager.k8s.io/letsencrypt created
```
See this example `Ingress` resource using the issuer:
```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: echo-ingress
  annotations:  
    kubernetes.io/ingress.class: nginx
    certmanager.k8s.io/cluster-issuer: letsencrypt
spec:
  tls:
  - hosts:
    - echo1.example.com
    - echo2.example.com
    secretName: letsencrypt
  rules:
  - host: echo1.example.com
    http:
      paths:
      - backend:
          serviceName: echo1
          servicePort: 80
  - host: echo2.example.com
    http:
      paths:
      - backend:
          serviceName: echo2
          servicePort: 80
```
Create as many `Ingress` resources as you would like. The best practice, in my opinion, is having 1 per domain, just for organizational reasons.

If you have any questions or comments please feel free to comment below!

Thanks!

## Sources:
- https://www.digitalocean.com/community/tutorials/how-to-set-up-an-nginx-ingress-with-cert-manager-on-digitalocean-kubernetes
- https://github.com/fnproject/fn-helm/issues/21
- https://github.com/heptio/aws-quickstart/issues/173
- https://helm.sh/docs/using_helm/