Exercise

In this exercise, you will install an Nginx-based Ingress Controller and configure it to serve the country.dev domain over HTTPS.

Prerequisites

Ensure you have installed the nginx Ingress Controller as detailed in the previous exercise.

Test Application

Copy the following specification into the country.yaml file. This specification defines a Pod running a simple Python application that returns a random country (on a GET /random request), and a ClusterIP type service to expose this Pod.

country.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: country
  name: country
spec:
  containers:
  - image: lucj/country:1.0
    name: country
    ports:
    - containerPort: 5000
---
apiVersion: v1
kind: Service
metadata:
  name: country
spec:
  ports:
  - port: 80
    targetPort: 5000
  selector:
    app: country

Create the Pod and corresponding Service:

kubectl apply -f country.yaml

Certificate and Private Key

Use the following command to create a private key and a self-signed certificate for the domain name country.dev

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout cert.key -out cert.crt -subj "/CN=country.dev"
⚠️

:warning: if you’re using Git-Bash, use the following command to generate the certificate and private key (double // in the -subj definition)

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout cert.key -out cert.crt -subj "//CN=country.dev"

Then create a tls type secret containing the certificate and private key created previously.

kubectl create secret tls country-certs --cert=./cert.crt --key=./cert.key

Ingress Controller Configuration

You will now create the Ingress resource that will configure the Ingress Controller to act as a TLS termination for the country.dev domain.

Copy the following specification into the ingress.yaml file:

ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: country
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - country.dev
    secretName: country-certs
  rules:
  - host: "country.dev"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: country
            port:
              number: 80

then create the resource:

kubectl apply -f ingress.yaml

The tls section of this specification indicates that the certificates needed for the domain name country.dev are found in the country-certs secret created previously.

Testing

There are 2 scenarios:

  • 1st case: if the Ingress Controller is exposed with a LoadBalancer type service, as in the case below:
$ kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP       PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.106.237.119   194.182.170.229   80:30810/TCP,443:31703/TCP   98s
ingress-nginx-controller-admission   ClusterIP      10.96.179.17     <none>            443/TCP                      98s

Modify your /etc/hosts file (C:\Windows\System32\drivers\etc\hosts on Windows) to add the mapping between the domain name and your Load Balancer’s IP address. In the previous example, you would need to add the following record to this file:

194.182.170.229 country.dev

Alternatively, you can use the following command to verify that you can access the application using the domain name country.dev directly, making sure to replace LOAD_BALANCER_IP with your Load Balancer’s IP (194.182.170.229 in the above example).

Note: the –resolve option handles the name resolution of country.dev

curl -k --resolve country.dev:443:LOAD_BALANCER_IP https://country.dev/random

You should get a result similar to the following:

{"alpha_2":"LA","alpha_3":"LAO","name":"Lao People's Democratic Republic","numeric":"418"}
  • 2nd case: if the Ingress Controller is exposed with a NodePort type service, as in the case below:
$ kubectl get svc -n ingress-nginx
NAME                                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller-admission   ClusterIP   10.43.139.141   <none>        443/TCP                      5m14s
ingress-nginx-controller             NodePort    10.43.106.85    <none>        80:30846/TCP,443:32615/TCP   5m14s

Use the following command after replacing:

  • NODE-PORT with the port exposing port 443 externally (32615 in this example)
  • NODE_IP with the IP address of one of your cluster nodes (which you can get with the command kubectl get no -o wide)
curl -k --resolve country.dev:NODE-PORT:NODE_IP https://country.dev:NODE_PORT/random

You should get a result similar to the following:

{"alpha_2":"SA","alpha_3":"SAU","name":"Saudi Arabia","numeric":"682"}

The application is now exposed over TLS. In a real world scenario we usually rely on an external component, like Cert-Manager to handle the certificate issuance and renewal for us.