ingress, kubernetes, networking, web

Ingress & TLS in kubernetes using self-signed certificates

Provision a cluster

In this tutorial to setup local cluster I will use kind. Follow this instructions to configure ingress controller (nginx in this example).

Create cluster

$ cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  kubeadmConfigPatches:
  - |
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-labels: "ingress-ready=true"
  extraPortMappings:
  - containerPort: 80
    hostPort: 80
    protocol: TCP
  - containerPort: 443
    hostPort: 443
    protocol: TCP
EOF

Deploy nginx Ingress controller

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml

$ kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=90s

...

pod/ingress-nginx-controller-78f889f8b9-2tbjz condition met



Check if it works

# apply ingress test manifests
$ kubectl apply -f https://kind.sigs.k8s.io/examples/ingress/usage.yaml

# test ingress
$ curl localhost/foo
foo
$ curl localhost/bar
bar

# cleanup
$ kubectl apply -f https://kind.sigs.k8s.io/examples/ingress/usage.yaml


Create basic nginx pod & expose it using Ingress

$ k run nginx --image=nginx
pod/nginx created

$ k expose pod nginx --port=80
service/nginx exposed

$ cat <<EOF | k apply -f-
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: ingress.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx
            port:
              number: 80
EOF

ingress.networking.k8s.io/nginx created

To make ingress.local available in local browser, add to the /etc/hosts line:

127.0.0.1 ingress.local – in my case cluster is running at 127.0.0.1.

You can check it by running k cluster-info --context kind-<CLUSTER_NAME>

$ k cluster-info --context kind-kind
Kubernetes control plane is running at https://127.0.0.1:63119

If succedeed, you should see similar result:

$ curl ingress.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Add TLS encryption with self-signed certificate to enable HTTPs

Until now, pod is exposed using Ingress, but the connection is over HTTP and therefore it is unencrypted. Let’s add some security to the server. First, create certifiates using openssl, then create kubernetes Secret of type ssl. And finally utilize it in Ingress resource.

Create self-signed certificate

$ openssl req \
-x509 -newkey rsa:4096 -sha256 -nodes \
-keyout tls.key -out tls.crt \
-subj "/CN=ingress.local" -days 365

Generating a 4096 bit RSA private key
........++
...................................................................................................++
writing new private key to 'tls.key'
-----

$ ls
tls.crt  tls.key

Create kubernetes secret with those keys

$ k create secret tls ingress-local-tls \
  --cert=tls.crt \
  --key=tls.key

secret/ingress-local-tls created

Make changes to Ingress

$ cat <<EOF | k apply -f-
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  tls:                                # add those 4 lines
  - hosts:                            #
      - ingress.local                 #
    secretName: ingress-local-tls     #
  rules:
  - host: ingress.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx
            port:
              number: 80

ingress.networking.k8s.io/nginx configured

Check if everything has been done correctly:

$ curl https://ingress.local
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

We can see that curl recognised our certificate, hence it is self-signed, it treats it like non-legit. We know it is, so just pass --insecure flag to suppress this warning:

$ curl --insecure https://ingress.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Similar situation will occur when we will try to access web page via browser:

From now, every connection to our ingress.local site will be encrypted! In the next posts I will describe how to automate certificate management & provisioning using cert-manager. Eventually I will use Let's encrypt to create certificates that will be considered by web browsers as legit – then the green padlock will appear.