In my last blog post, I covered how to do egress traffic blocking with Cilium bring-your-own CNI on Azure Kubernetes Service as Azure CNI powered by Cilium does not officially support Cilium cluster-wide network policies and Cilium CIDR groups. -> https://www.danielstechblog.io/egress-traffic-blocking-with-cilium-cluster-wide-network-policies-on-azure-kubernetes-service/ In addition to the Cilium option on Azure Kubernetes Service, there has been and […]
In my last blog post, I covered how to do egress traffic blocking with Cilium bring-your-own CNI on Azure Kubernetes Service as Azure CNI powered by Cilium does not officially support Cilium cluster-wide network policies and Cilium CIDR groups.
In addition to the Cilium option on Azure Kubernetes Service, there has been and still is the option to deploy an Azure Kubernetes Service cluster with Azure CNI and Calico for the network policy part.
So, in today’s blog post, we talk about how to block egress traffic with Calico global network policies on Azure Kubernetes Service. For this, we need an Azure Kubernetes Service cluster with Azure CNI and Calico for the network policy part or alternatively with Calico installed via the bring-your-own CNI approach.
Egress traffic blocking is done to prevent network traffic to malicious network entities, to country-level network CIDR ranges, called geo-blocking, etc.
Using Calico global network policies with Calico global network sets is an easy way to achieve this without setting up additional infrastructure or services. As security, including network security, is a multi-layer approach today’s presented solution is only one building block but a powerful one.
Configuration
The entire configuration in this example is kept simple and can become a bit more complex when it should be covering the geo-blocking approach. Geo-blocking requires constant updates to the Calico global network set and includes the usage of third-party services like MaxMind.
In my example, I am using the three IP addresses of my blog and added a /32 at the end to define them in CIDR annotation.
The corresponding Calico global network set template is shown below.
apiVersion: crd.projectcalico.org/v1 kind: GlobalNetworkSet metadata: name: egress-traffic-blocking labels: policy: egress-traffic-blocking spec: nets: - 217.160.0.92/32 - 217.160.0.111/32 - 217.160.223.1/32
Without being referenced by a Calico global network policy a Calico global network set is just another resource in Kubernetes and does not affect egress traffic at all.
apiVersion: crd.projectcalico.org/v1 kind: GlobalNetworkPolicy metadata: name: egress-traffic-allow labels: policy: egress-traffic-allow spec: order: 1000 types: - Egress egress: - action: Allow --- apiVersion: crd.projectcalico.org/v1 kind: GlobalNetworkPolicy metadata: name: egress-traffic-blocking labels: policy: egress-traffic-blocking spec: order: 0 types: - Egress egress: - action: Log destination: selector: policy == 'egress-traffic-blocking' - action: Deny destination: selector: policy == 'egress-traffic-blocking'
As seen above, we require two Calico global network policies.
The first one with order 1000 ensures that all egress traffic not blocked by the egress-traffic-blocking policy is allowed. Otherwise, egress traffic would be blocked by default after applying the first global network policy targeting egress traffic.
The second Calico global network policy references the Calico global network set via a defined label to block egress traffic targeting the specified CIDR ranges. Besides blocking egress traffic, we like to generate logs for it. Hence, we use the action Log in the policy definition. It is important that the Log action comes before the Deny action as actions are processed in the order they have been defined in the template.
After rolling out all three templates to the Azure Kubernetes Service cluster, we test the egress traffic blocking in an application pod using the curl command and tail on the Kubernetes node’s syslog to monitor if the egress traffic is blocked as intended.
root@bash:/# curl https://www.danielstechblog.de ----------------------------------------------------------------------------------------------------------------------- sh5.1# tail -f /var/log/messages | grep 'calico-packet' 2025-01-19T16:13:46.406155+00:00 aks-default-30331449-vmss000000 kernel: [ 6236.5514311 calico-packet: IN=azv7c40892f581 OUT=ethO MAC=aa:aa:aa:aa:aa:aa:da:a0:27:a7:b9:41:08:00 SRC=10.244.0.179 DST=217.160.0.92 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=27413 DF PROTO=TCP SPT=53940 DPT=443 WINDOW=64240 RES=0x00 SYN URGP=0 2025-01-19T16:13:46.406172+00:00 aks-default-30331449-vmss000000 kernel: calico-packet: IN=azv7c40892f581 OUT=eth0 MAC=aa:aa:aa:aa:aa:aa:da:a0:27:a7:b9:41:08:00 SRC=10.244.0.179 DST=217.160.0.92 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=27413 DF PROTO=TCP SPT=53940 DPT=443 WINDOW=64240 RES=0x00 SYN URGP=0
As seen above the egress traffic is blocked successfully by the defined Calico global network policy.
Calico only produces log output for network policies, when we use the Log action. The log output ends up in the Syslog and not in the Calico pod stdout log. On an Azure Linux Kubernetes node, the Syslog can be found in /var/log/messages. So, gathering those logs requires extra configuration, when you do not gather the Syslog already with your logging solution.
Summary
Doing egress traffic blocking on Azure Kubernetes Service using Calico is straightforward with its global network sets and global network policies.
The examples can be found on my GitHub repository.
-> https://github.com/neumanndaniel/kubernetes/tree/master/calico/egress-traffic-blocking