Securing Kubernetes: Ingress and SSL Configuration with Nginx and Cert-Manager in AKS
Continuing from where we left off, the next step is to set up an Ingress controller and secure access to our application endpoints with HTTPS using a custom domain. If you haven’t seen part 1, you can find it here.
Introduction to Ingress Controllers
An Ingress controller is a Kubernetes resource responsible for managing external access to services within a cluster, typically HTTP and HTTPS traffic. It acts as a reverse proxy, routing requests from external sources to the appropriate services based on defined rules.
Why Use an Ingress Controller?
Ingress controllers streamline the process of exposing services to external users. Instead of managing individual load balancers or exposing each service separately (as in the previous post), an Ingress controller provides a centralized solution for routing traffic and managing SSL termination, among other functionalities.
By leveraging an Ingress controller, we can simplify our deployment architecture, enhance security through TLS termination, and enable access to our applications via a custom domain.
Installing Nginx Ingress Controller
To set up our Ingress controller, we’ll be using Nginx. Here’s how you can install it:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx `
--version 4.7.1 `
--namespace ingress-basic `
--create-namespace `
--set controller.replicaCount=2 `
--set controller.nodeSelector."kubernetes\.io/os"=linux `
--set controller.admissionWebhooks.patch.nodeSelector."kubernetes\.io/os"=linux `
--set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz `
--set controller.service.externalTrafficPolicy=Local `
--set defaultBackend.nodeSelector."kubernetes\.io/os"=linux
Creating Ingress Resources and Updating Services
Below are the Ingress resources for our API and UI services. These resources define how external traffic will be routed to our services:
# Ingress for API Service
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webapp-demo-api
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$1
cert-manager.io/cluster-issuer: letsencrypt
spec:
ingressClassName: nginx
tls:
- hosts:
- webapp-demo-api-test.gago.works
secretName: webapp-demo-api-tls-secret
rules:
- host: webapp-demo-api-test.gago.works
http:
paths:
- backend:
service:
name: webapp-demo-api
port:
number: 80
path: /(.*)
pathType: ImplementationSpecific
# Service for API
apiVersion: v1
kind: Service
metadata:
name: webapp-demo-api
spec:
selector:
app: webapp-demo-api
ports:
- protocol: TCP
port: 80
name: http
type: ClusterIP
# Ingress for UI Service
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webapp-demo-ui
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
nginx.ingress.kubernetes.io/use-regex: "true"
cert-manager.io/cluster-issuer: letsencrypt
spec:
ingressClassName: nginx
tls:
- hosts:
- webapp-demo-ui-test.gago.works
secretName: webapp-demo-ui-tls-secret
rules:
- host: webapp-demo-ui-test.gago.works
http:
paths:
- path: /(.*)
backend:
service:
name: webapp-demo-ui
port:
number: 80
pathType: ImplementationSpecific
# Service for UI
apiVersion: v1
kind: Service
metadata:
name: webapp-demo-ui
spec:
selector:
app: webapp-demo-ui
ports:
- protocol: TCP
port: 80
targetPort: 8080
name: http
type: ClusterIP
After configuring the Ingress resources, you’ll notice a shift from LoadBalancer to ClusterIP for service types. This change optimizes resource utilization and efficiency. Managing multiple LoadBalancer services incur additional costs and overhead, particularly in cloud environments. By leveraging ClusterIP, we streamline internal communication between services, reducing costs and simplifying management. Instead, the Ingress controller will handle external traffic routing to each service based on the host name.
Installing Cert-Manager
To manage our SSL certificates, we’ll use Cert-Manager. Here’s how to install it:
kubectl label namespace ingress-basic cert-manager.io/disable-validation=true
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager --namespace ingress-basic --version=v1.8.0 --set installCRDs=true --set nodeSelector."kubernetes\.io/os"=linux
Run kubectl get pods -n ingress-basic again to verify Cert-Manager is up and running:
To set up Cert-Manager, we begin by defining a ClusterIssuer. This resource specifies the details for certificate issuance, including the certificate authority server, contact email, and private key reference. While Issuers are scoped to a specific namespace, and can only issue certificates within that namespace, ClusterIssuers, on the other hand, are applicable cluster-wide and can issue certificates across all namespaces.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: <your_email>
privateKeySecretRef:
name: letsencrypt
solvers:
- http01:
ingress:
class: nginx
podTemplate:
spec:
nodeSelector:
"kubernetes.io/os": linux
In this configuration:
- server: Specifies the endpoint of the Let’s Encrypt ACME server.
- email: Provides the contact email for important notifications related to certificate management.
- privateKeySecretRef: References the Kubernetes secret where the private key for certificate generation is stored.
- solvers: Defines the challenge solvers for proving domain ownership to Let’s Encrypt. Here, we use the HTTP01 challenge solver, which verifies domain ownership by serving a token over HTTP.
The HTTP01 challenge solver is one of the methods Cert-Manager uses to validate domain ownership. When requesting a certificate, Cert-Manager creates a temporary HTTP endpoint on our cluster, responding to challenges from Let’s Encrypt. This mechanism proves that we control the domain for which we’re requesting the certificate.
Run kubectl get certificates –all-namespaces to verify certificates and their status:
You can get additional information on certificate issuance by running kubectl describe certificate <name> -n <namespace>. If your certificate is failing to change into a Ready state, it’s worth investigating other Cert-Manager resources associated with this process:
With the ClusterIssuer configured, Cert-Manager automates the certificate issuance process, retrieving certificates from Let’s Encrypt and managing their lifecycle within our Kubernetes environment. This ensures secure communication for our services over HTTPS.
DNS configuration
In this step, I had to access the DNS configuration in my domain provider and add two A records with matching names based on the Ingress files. The value should point to the External IP address provided by our Load Balancer:
Once the DNS records have propagated, we should be able to access our application at the specified URLs:
Conclusion
In this effort, we’ve configured Ingress for API and UI services in AKS, bolstering security and simplifying external access using HTTPS and a custom domain. By installing the Nginx Ingress controller, we efficiently manage traffic routing within our Kubernetes cluster. Transitioning from LoadBalancer to ClusterIP for service types optimizes resource utilization and simplifies management.
Moreover, by installing Cert-Manager, we automate SSL certificate management, ensuring secure communication over HTTPS. With DNS configuration, we enable access to our application via custom domains. Stay tuned for the third and final part, where we’ll configure FluxCD to automate Kubernetes deployments, further enhancing our deployment workflow.
References
- Ingress | Kubernetes
- Ingress Controllers | Kubernetes
- cert-manager
- HTTP01 — cert-manager documentation
- Troubleshooting Problems with ACME / Let’s Encrypt Certificates — cert-manager Documentation
- Create an unmanaged ingress controller | Microsoft Learn
- Use TLS with an ingress controller on Azure Kubernetes Service (AKS) | Microsoft Learn