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
|