Securing Ingress
Overview
An Ingress resource manages external access to services within a Kubernetes cluster, typically HTTP and HTTPS traffic. From a security perspective, the Ingress is the front door to your cluster -- it must be properly secured with TLS, security headers, rate limiting, and access controls.
CKS Exam Relevance
You may be asked to configure TLS termination on an Ingress resource, create TLS secrets, or secure an Ingress with specific annotations. Know how to create a self-signed certificate, store it as a Kubernetes secret, and reference it from an Ingress resource.
Secure Ingress Architecture
TLS Termination at Ingress
TLS termination means the Ingress controller decrypts HTTPS traffic and forwards plain HTTP to backend services. This is the most common pattern.
Step 1: Generate TLS Certificates
For the exam, you typically create self-signed certificates:
# Generate a self-signed certificate and key
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout tls.key \
-out tls.crt \
-subj "/CN=myapp.example.com/O=MyOrg"
# For multiple domains (SANs), use a config file
cat > openssl.cnf <<EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
CN = myapp.example.com
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = myapp.example.com
DNS.2 = api.example.com
DNS.3 = *.example.com
EOF
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout tls.key \
-out tls.crt \
-config openssl.cnf \
-extensions v3_reqStep 2: Create a Kubernetes TLS Secret
# Create the TLS secret
kubectl create secret tls myapp-tls \
--cert=tls.crt \
--key=tls.key \
-n productionOr declaratively:
apiVersion: v1
kind: Secret
metadata:
name: myapp-tls
namespace: production
type: kubernetes.io/tls
data:
tls.crt: <base64-encoded-certificate>
tls.key: <base64-encoded-private-key>Quick Base64 Encoding
# Encode certificate and key
cat tls.crt | base64 -w 0
cat tls.key | base64 -w 0Step 3: Configure the Ingress Resource
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true" # Force HTTPS
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls # Reference the TLS secret
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80Multiple TLS Hosts
spec:
tls:
- hosts:
- app1.example.com
secretName: app1-tls
- hosts:
- app2.example.com
secretName: app2-tls
rules:
- host: app1.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app1-service
port:
number: 80
- host: app2.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app2-service
port:
number: 80Security Headers via Annotations
Ingress controllers (especially nginx) support adding security headers through annotations.
Force HTTPS Redirect
metadata:
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"HTTP Strict Transport Security (HSTS)
metadata:
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "Strict-Transport-Security: max-age=31536000; includeSubDomains; preload";Content Security Headers
metadata:
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Frame-Options: DENY";
more_set_headers "X-Content-Type-Options: nosniff";
more_set_headers "X-XSS-Protection: 1; mode=block";
more_set_headers "Referrer-Policy: strict-origin-when-cross-origin";
more_set_headers "Content-Security-Policy: default-src 'self'";Complete Secure Ingress Example
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: secure-ingress
namespace: production
annotations:
# Force HTTPS
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
# Security headers
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "Strict-Transport-Security: max-age=31536000; includeSubDomains";
more_set_headers "X-Frame-Options: DENY";
more_set_headers "X-Content-Type-Options: nosniff";
more_set_headers "X-XSS-Protection: 1; mode=block";
# Rate limiting
nginx.ingress.kubernetes.io/limit-rps: "10"
nginx.ingress.kubernetes.io/limit-connections: "5"
# Client body size limit
nginx.ingress.kubernetes.io/proxy-body-size: "1m"
# Connection and read timeouts
nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
nginx.ingress.kubernetes.io/proxy-read-timeout: "30"
spec:
ingressClassName: nginx
tls:
- hosts:
- secure.example.com
secretName: secure-tls
rules:
- host: secure.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: secure-app
port:
number: 80Rate Limiting
Rate limiting protects against brute-force attacks, DDoS, and abuse.
nginx Ingress Rate Limiting
metadata:
annotations:
# Limit requests per second per IP
nginx.ingress.kubernetes.io/limit-rps: "10"
# Limit concurrent connections per IP
nginx.ingress.kubernetes.io/limit-connections: "5"
# Custom response when rate limited
nginx.ingress.kubernetes.io/limit-rate-after: "1m"
nginx.ingress.kubernetes.io/limit-rate: "100"TLS Passthrough
For applications that handle their own TLS (end-to-end encryption):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: passthrough-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- secure-backend.example.com
rules:
- host: secure-backend.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: secure-backend
port:
number: 443Web Application Firewall (WAF) Concepts
While not deeply tested in the CKS exam, understanding WAF integration with Ingress is valuable.
ModSecurity with nginx Ingress
metadata:
annotations:
# Enable ModSecurity WAF
nginx.ingress.kubernetes.io/enable-modsecurity: "true"
nginx.ingress.kubernetes.io/enable-owasp-modsecurity-crs: "true"
# Custom ModSecurity rules
nginx.ingress.kubernetes.io/modsecurity-snippet: |
SecRuleEngine On
SecRule ARGS "@contains <script>" "id:1,deny,status:403"Ingress Controller Security
Securing the Ingress Controller Itself
Ingress Controller Permissions
The Ingress controller runs as a pod and needs elevated permissions. Ensure:
- It runs in a dedicated namespace (e.g.,
ingress-nginx) - Its service account has minimal RBAC permissions
- It uses a NetworkPolicy to restrict traffic
- It runs as a non-root user where possible
NetworkPolicy for Ingress Controller
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: ingress-controller-policy
namespace: ingress-nginx
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
policyTypes:
- Ingress
- Egress
ingress:
# Allow external traffic on HTTP/HTTPS
- ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 443
egress:
# Allow traffic to backend services
- to:
- namespaceSelector: {} # All namespaces (backends can be anywhere)
ports:
- protocol: TCP
# Allow DNS
- ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53Default Backend Security
Configure a default backend for unmatched requests:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: default-backend
namespace: production
spec:
ingressClassName: nginx
defaultBackend:
service:
name: default-backend-service
port:
number: 80This ensures that requests to unknown hosts or paths receive a controlled response rather than an error page that might leak information.
Verifying Ingress TLS
# Check if the ingress has TLS configured
kubectl describe ingress myapp-ingress -n production
# Verify the TLS secret exists
kubectl get secret myapp-tls -n production
# Inspect the certificate in the secret
kubectl get secret myapp-tls -n production -o jsonpath='{.data.tls\.crt}' | \
base64 -d | openssl x509 -noout -text
# Test TLS from outside (if accessible)
curl -v --resolve myapp.example.com:443:<INGRESS_IP> \
https://myapp.example.com/
# Check certificate details
openssl s_client -connect <INGRESS_IP>:443 \
-servername myapp.example.com </dev/null 2>/dev/null | \
openssl x509 -noout -subject -datesQuick Reference
# Complete TLS Ingress setup in one flow
# 1. Generate certificate
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout tls.key -out tls.crt -subj "/CN=app.example.com"
# 2. Create TLS secret
kubectl create secret tls app-tls --cert=tls.crt --key=tls.key -n production
# 3. Create Ingress with TLS
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-service
port:
number: 80
EOF
# 4. Verify
kubectl describe ingress app-ingress -n productionExam Speed
The three steps you need to remember:
openssl req-- generate cert/keykubectl create secret tls-- create TLS secret- Add
tls:section to Ingress spec -- reference the secret