Post

Kubernetes Ingress

How to configure Kubernetes Ingress resources with NGINX, including TLS termination with self-signed certificates.

Kubernetes Ingress
Services in Kubernetes
  • ClusterIP (points to a pod via labels selectors)
  • NodePort (in addition a port is exported at each node)
  • Loadbalancer (in addition creates LB at cloud provider)
Deploy Nginx ingress controller

Deploy the NGINX Ingress Controller to your cluster. This creates the necessary Deployment, Service, and RBAC resources in the ingress-nginx namespace.

1
2
# Install NGINX Ingress
kubectl apply -f https://raw.githubusercontent.com/killer-sh/cks-course-environment/master/course-content/cluster-setup/secure-ingress/nginx-ingress-controller.yaml

Create two test pods and expose them as ClusterIP services so the Ingress can route traffic to them.

1
2
3
4
5
# Create two different pods in your cluster
k run nginx1 --image=nginx:alpine
k run httpd1 --image=httpd
k expose pod nginx1 --port 80 --name service1 --type ClusterIP
k expose pod httpd1 --port 80 --name service2 --type ClusterIP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
cat <<'EOF' > ingress.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: secure-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: /service1
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80
      - path: /service2
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 80
EOF

####### Test connection

Verify the services are created and test the Ingress routing by curling the NodePort endpoints.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
k get svc
hugo % k get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP   2d22h
service1     ClusterIP   10.108.141.216   <none>        80/TCP    4m5s
service2     ClusterIP   10.105.255.4     <none>        80/TCP    3m49s

hugo % k get svc -n ingress-nginx
NAME                                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             NodePort    10.100.237.178   <none>        80:31500/TCP,443:31209/TCP   13m
ingress-nginx-controller-admission   ClusterIP   10.99.183.238    <none>        443/TCP                      13m


curl http://127.0.0.1:31500/service1
curl http://127.0.0.1:31500/service2
Secure Nginx Ingress with Self Signed Certificate

Generate a self-signed TLS certificate and store it as a Kubernetes secret. Then create an Ingress resource that uses TLS termination with the secret.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# generate cert & key
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes

k create secret tls secure-ingress --cert cert.pem --key key.pem
secret/secure-ingress created



cat <<'EOF' > secure-ingress.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: secure-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  tls:
  - hosts:
      - secure-ingress.com
    secretName: secure-ingress
  rules:
  - host: secure-ingress.com
    http:
      paths:
      - path: /service1
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80
      - path: /service2
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 80
EOF

# Test connection
curl -k  https://127.0.0.1:31209/service1 -H "Host: secure-ingress.com" #     < --- this will not work !!!
curl -kv  https://secure-ingress.com:31209/service1 --resolve secure-ingress.com:31209:127.0.0.1

...
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=SK; ST=SK; L=KO; O=Devops; OU=Security; CN=secure-ingress.com
*  start date: Feb 21 12:23:48 2022 GMT
*  expire date: Feb 21 12:23:48 2023 GMT
*  issuer: C=SK; ST=SK; L=KO; O=Devops; OU=Security; CN=secure-ingress.com
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
...

For a complete example, see the CKS course secure-ingress repository. Also refer to the official Kubernetes Ingress documentation.

Ingress object can’t be created by kubectl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
cat ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: app-space
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /watch
        pathType: Prefix
        backend:
          service:
            name: video-service
            port:
              number: 8080
      - path: /wear
        pathType: Prefix
        backend:
          service:
            name: wear-service
            port:
              number: 8080

Expose already existing deployment with proper namespace

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
kubectl expose deployment ingress-controller --port=80 --target-port=80 --name ingress --type=NodePort --namespace ingress-space --dry-run=client -o yaml | sed -E 's/^(\s*targ.*)/\1\n    nodePort: 30080/' | sed -E 's/^(\s*metadata.*)/\1\n  namespace: ingress-space/'
apiVersion: v1
kind: Service
metadata:
  namespace: ingress-space
  creationTimestamp: null
  name: ingress
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30080
  selector:
    name: nginx-ingress
  type: NodePort
status:
  loadBalancer: {}

Ingress cannot refer to a service in a different namespace

  • As such, it is important to create an Ingress in the same namespace as the service resides.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
cat ing.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
  name: ingress-wear-watch
  namespace: app-space
spec:
  rules:
  - http:
      paths:
      - backend:
          serviceName: wear-service
          servicePort: 8080
        path: /wear
        pathType: ImplementationSpecific

      - backend:
          serviceName: video-service
          servicePort: 8080
        path: /watch
        pathType: ImplementationSpecific

      - backend:
          serviceName: video-service
          servicePort: 8080
        path: /stream
        pathType: ImplementationSpecific

Examples

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  namespace: critical-space
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /pay
        backend:
          serviceName: pay-service
          servicePort: 8282
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
  name: rewrite
  namespace: default
spec:
  rules:
  - host: rewrite.bar.com
    http:
      paths:
      - backend:
          serviceName: http-svc
          servicePort: 80
        path: /something(/|$)(.*)
Generate SSL certificate and create a kubernetes secret
1
2
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
kubectl  create  secret tls secure-ingress --cert cert.pem --key key.pem

Create a Kubernetes Ingress specification with TLS enabled.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
cat ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  tls:
  - hosts:
      - secure-ingress.com
    secretName: secure-ingress
  rules:
  - host: secure-ingress.com
    http:
      paths:
      - path: /service1
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80
      - path: /service2
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 80

Adjust /etc/hosts to resolve the Ingress hostname locally.

1
echo -e "35.198.101.56 secure-ingress.com" >> /etc/hosts

Check the applications via curl.

1
curl https://secure-ingress.com:30769/service2 -kv
This post is licensed under CC BY 4.0 by the author.