Set up nginx ingress with letsencrypt certificate on VMs or bare metal kubernetes cluster
Tags: kubernetes, ingress, certmanager, letsencrypt
Published: 2018-11-03 06:58:41

---
title: Set up nginx ingress with letsencrypt certificate on VMs or bare metal kubernetes cluster
published: true
description: Set up nginx ingress and cert-manager to automatically apply letsencrypt certificate
tags: #kubernetes,#ingress,#cert-manager,#letsencrypt
cover_image: https://thepracticaldev.s3.amazonaws.com/i/2ez40cwe8ex2fg3w7jgf.jpg
---
[helm]: https://thepracticaldev.s3.amazonaws.com/i/4ukf0lo1qritsisdeyd5.png "Helm version"
[ingress1]: https://thepracticaldev.s3.amazonaws.com/i/aexnlwvy1jddsc96a2f8.png "Install success"
[route53]: https://thepracticaldev.s3.amazonaws.com/i/mftgd7u72g64qgxhqfa2.png "Route53 dns"
[forwardport]: https://thepracticaldev.s3.amazonaws.com/i/brxpkrtzukdmtzp3762i.png "Router config"
[testapp1]: https://thepracticaldev.s3.amazonaws.com/i/j8fxoxk79wsikhu00q0u.png "Test app"
[testapp2]: https://thepracticaldev.s3.amazonaws.com/i/010n1txkx1i39g95i5ka.png "Test app with domain"
[secret]: https://thepracticaldev.s3.amazonaws.com/i/lc7xmjk1ud212nr29pnd.png "TLS secret"
[https]: https://thepracticaldev.s3.amazonaws.com/i/gb8f1ewrocwg91s3ro2f.png "https"

This post is a guide to setup ingress with cert-manager on kubernetes cluster (VMs based) so that the application deploy on the cluster can apply for https certificate automatically.

When we use kubernetes cluster using cloud providers, they already provide ingress mechanism based on their load balancer technology. If we want this capability on VMs or bare metal based cluster, we need to set it up ourselves.

It is part of series ["Setup your own kubernetes cluster on VMs"](https://dev.to/pongsatt/setup-your-own-kubernetes-cluster-on-vms-5ln).

##Prerequisite:##
- A running kubernetes cluster
- A domain name with you have administration access

**Note:**
> For those who follow along, you need to run everything on master node

#Install helm#

First of all, we need helm, a cluster package manager, that will help us install ingress and cert-manager.

###1. Create service account and its permission###

Run command.
```
echo 'apiVersion: v1
kind: ServiceAccount
metadata:
  name: tiller
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: tiller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: tiller
    namespace: kube-system' | kubectl apply -f -
```

###2. Install helm###

Run below command to install helm from official bash script.

*Note: You will need to enter root password.*

```
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get | bash
```

###3. Initialize helm###

We need to initialize helm to install server application on the cluster.

```
helm init --service-account tiller
```

###4. Check installation###

Run `helm version`.

If things are ok, you should see.

![][helm]

#Install Ingress#

We will install nginx ingress using host network on all worker nodes (DaemonSet).

See this document to understand more about nginx ingress [Bare-metal considerations](https://kubernetes.github.io/ingress-nginx/deploy/baremetal)

###1. Install Ingress pod###

Install using helm command below.

```
helm install --name nginx-ingress --namespace nginx-ingress stable/nginx-ingress --set controller.hostNetwork=true --set controller.kind=DaemonSet
```

Open one of your worker node IP address in the browser. If ingress installation successful, you will see.

![][ingress1]


###2. Configure domain name###

Configure your dns to point your test domain name to one of your worker node.

In my example, I point my route53 test dns record to my public IP.

![][route53]

I also need to configure my router to forword port 80 and 443 to one of my worker node. Let's say 192.168.1.110.

![][forwardport]


###3. Deploy test application###

Run this command to deploy hello world application to the cluster.

```
kubectl run hello-world --image=gcr.io/google-samples/node-hello:1.0  --port=8080
kubectl expose deployment hello-world --type=NodePort --name=example-service
```

Try access this application using node port `http://<master or worker ip>:<node port>`. You should see.

![][testapp1]


###4. Deploy test ingress###

Run command below to install ingress for test application.

**Note:**
> Please replace **"testingress.yourdomain.com"** with your real domain.

```
echo 'apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.class: "nginx"
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
  tls:
  - hosts:
    - testingress.yourdomain.com
    secretName: testingress-tls
  rules:
  - host: testingress.yourdomain.com
    http:
      paths:
      - path: /
        backend:
          # The name of your service
          serviceName: example-service
          servicePort: 8080' | kubectl apply -f -
```

If everything is ok, open url `http://testingress.yourdomain.com` and you should see.

![][testapp2]


#Install cert-manager#

We will install cert-manager to the cluster then create cluster issuer for letsencrypt. The cluster issuer will automatically detect our ingress with tls configure then try to acquire certificate and store keys in secret name "testingress-tls".

###1. Install cert-manager###

Run this command using helm.

```
helm install --name cert-manager --namespace kube-system stable/cert-manager
```

###2. Create cluster issuer (use http01 protocol)###

Run this command to create cluster issuer.

**Note:**
> Please replace "yourreal@email.com" to your real email

```
echo 'apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
  namespace: default
spec:
  acme:
    email: yourreal@email.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod
    http01: {}' | kubectl apply -f -
```

Run below to update cert-manager to use cluster issuer created above.

```
helm upgrade cert-manager stable/cert-manager --namespace kube-system --set ingressShim.defaultIssuerName=letsencrypt-prod --set ingressShim.defaultIssuerKind=ClusterIssuer
```

Wait for sometimes maybe an hour. Then run `kubectl get secret`. If you see secret name "testingress-tls", this mean it works. You should be able to access `https://testingress.yourdomain.com` successfully.

###3. Create cluster issuer (use dns01 protocol)###

In case that step 2 with http02 protocol does not work, we will need a more advance protocol "dns01".

"dns01" protocol support many cloud providers, this example will be for route53.

####3.1 Import IAM policy####

Import this to your IAM user that you have access key.

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "route53:GetChange",
            "Resource": "arn:aws:route53:::change/*"
        },
        {
            "Effect": "Allow",
            "Action": "route53:ChangeResourceRecordSets",
            "Resource": "arn:aws:route53:::hostedzone/*"
        },
        {
            "Effect": "Allow",
            "Action": "route53:ListHostedZonesByName",
            "Resource": "*"
        }
    ]
}
```

####3.2 Create AWS secret####

Create kubernetes secret using below.

**Note:**
> Replace <IAM User Secret Key> with your real secret key

```
kubectl create secret -n kube-system generic aws-secret --from-literal=secret-key=<IAM User Secret Key>
```

####3.3 Create Cluster Issuer (DNS01)####

**Note:**
> Replace yourreal@email.com with your real email
> Replace <Route53 hosted zone ID> and <Route53 region> and <IAM User Access key> with your real data

```
echo 'apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
  namespace: default
spec:
  acme:
    email: yourreal@email.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod
    dns01:
      providers:
      - name: aws-route53
        route53:
          hostedZoneID: <Route53 hosted zone ID>
          region: <Route53 region>
          accessKeyID: <IAM User Access key>
          secretAccessKeySecretRef:
            name: aws-secret
            key: secret-key' | kubectl apply -f -
```

####3.4 Update test ingress to use DNS01####

**Note:**
> Replace yourdomain.com to your real domain

```
echo 'apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    certmanager.k8s.io/acme-challenge-type: "dns01"
    certmanager.k8s.io/acme-dns01-provider: "aws-route53"
spec:
  tls:
  - hosts:
    - testingress.yourdomain.com
    secretName: testingress-tls
  rules:
  - host: testingress.yourdomain.com
    http:
      paths:
      - path: /
        backend:
          # The name of your service
          serviceName: example-service
          servicePort: 8080' | kubectl apply -f -
```

Wait until `kubectl get secret` return secret name "testingress-tls", this mean it works. You should be able to access `https://testingress.yourdomain.com` successfully.

![][secret]

![][https]

#Summary#

At this point, we have a kubernete cluster that we can deploy application that support https. [Next](https://dev.to/pongsatt/deploy-static-front-end-application-to-kubernetes-cluster-using-jenkins-cd-and-minio-with-automatic-https-cert-44gk), we will set up a static front-end application in the kubernetes cluster.