In this post, we will continue to explore Calico network policies in Azure Kubernetes Service. We will configure a simple policy to allow ingress traffic from a central ingress controller.
Prerequisites
As always, we will need a few prerequisites.
- An Azure account and valid subscription.
- An AKS cluster configured with
- Azure AD integration for cluster access
- Azure CNI network plugin
- Calico
- A workstation with the following tools installed
- az cli
- kubectl
- calicoctl
- helm version 3
Deploy nginx ingress controller
By default, no ingress controller is deployed inside your AKS cluster. We will thus deploy the one from nginx using the official helm chart as described in AKS official documentation page.
# Create a namespace for your ingress resources
kubectl create namespace ingress-basic
# Add a label to ingress-basic namespace that will act as selector for Calico policy
kubectl label namespace ingress-basic env=ingress-basic
# Add the ingress-nginx repository
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
# Use Helm to deploy an NGINX ingress controller
helm install nginx-ingress ingress-nginx/ingress-nginx \
--namespace ingress-basic \
--set controller.replicaCount=1 \
--set controller.nodeSelector."beta\.kubernetes\.io/os"=linux \
--set defaultBackend.nodeSelector."beta\.kubernetes\.io/os"=linux \
--set controller.admissionWebhooks.patch.nodeSelector."beta\.kubernetes\.io/os"=linux
# Check the helm release
helm list -n ingress-basic
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
nginx-ingress ingress-basic 1 2021-01-19 09:32:58.7207368 +0100 CET deployed ingress-nginx-3.20.1 0.43.0
Caution : deploying an ingress controller with the helm chart does provision an additional external ip address on your cluster’s Azure Load Balancer which incurs additional costs.
Check the status of the ingress service that is exposed externally through an Azure load balancer.
kubectl --namespace ingress-basic get services -o wide -w nginx-ingress-ingress-nginx-controller
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
nginx-ingress-ingress-nginx-controller LoadBalancer 192.168.62.68 <external ip> 80:32537/TCP,443:31205/TCP 4m21s app.kubernetes.io/component=controller,app.kubernetes.io/instance=nginx-ingress,app.kubernetes.io/name=ingress-nginx
Deploy the hello-world sample application
We will use a sample hello-world application that we will deploy in the development
namespace already configured with basic Calico policies. See my article on Testing Calico on AKS with Azure CNI for further info on how to setup this namespace and the Calico policies.
Create a deployment file with the following content:
apiVersion: apps/v1
kind: Deployment
metadata:
name: aks-helloworld
spec:
replicas: 1
selector:
matchLabels:
app: aks-helloworld
template:
metadata:
labels:
app: aks-helloworld
spec:
containers:
- name: aks-helloworld
image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
ports:
- containerPort: 80
env:
- name: TITLE
value: "Welcome to Azure Kubernetes Service (AKS)"
---
apiVersion: v1
kind: Service
metadata:
name: aks-helloworld
spec:
type: ClusterIP
ports:
- port: 80
selector:
app: aks-helloworld
Apply this deployment to the development
namespace.
# Deploy the application and the internal service
kubectl apply -f aks-helloword-deployment.yaml -n development
Configure the ingress route
Create a file aks-helloworld-ingress.yaml
with the following content :
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: hello-world-ingress
namespace: development
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- http:
paths:
- backend:
serviceName: aks-helloworld
servicePort: 80
path: /hello-world(/|$)(.*)
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: hello-world-ingress-static
namespace: development
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/rewrite-target: /static/$2
spec:
rules:
- http:
paths:
- backend:
serviceName: aks-helloworld
servicePort: 80
path: /static(/|$)(.*)
Apply the ingress route definition in the development
namespace where the aks-helloworld
resources have been deployed
kubectl apply -f hello-world-ingress.yaml -n development
Remark : nginx-ingress controller deployed with helm chart watches ingress rules in all namespaces.
Open a web browser and test the access to the application by calling the URL http://<ingress-external-ip>/hellow-world
.
You will receive a 504
timeout and this is expected because we did not yet define a network policy to allow ingress traffic coming from ingress-basic
namespace to our pods running in development
namespace.
Create a Calico network policy to allow the ingress traffic.
# Apply the network policy to accept ingress
calicoctl apply -f 06-allow-ingress-from-nginx.yaml
# Check the applied network policies in the development namespace
calicoctl get networkpolicies -n development
NAMESPACE NAME
development default-deny
development development-ns-allow
development dns-allow
development nginx-ingress-allow
development sqlserver-allow
Now run the test once again …
Ta daaaaa! Now it works as expected.
Conclusion
Calico makes it really easy to define network policies to control the traffic coming from ingress controller to your pods. It requires some discipline and makes your Kubernetes cluster a safer place :)
Try to not overengineer your network policies otherwise it can become difficult to manage. As always, a good balance between security and ease of management must be found and it all depends on your requirements.