Getting started with AKS Network Policies
An important part of Container Security mechanism ‘Network Policy’ plays a very vital role in securing your cluster . It can help you to have a granular level of control on the traffic Ingressing or Egressing a Pod/Container. This reduces the attack surface by keeping unwanted traffic at bay .
Let’s highlight the things we will discuss before we begin
What is network policies in AKS ?
Why network policies ?
How to create a basic network policy ?
Ingress/Egress selectors
Combining selectors
Setting allowed port ranges
Kubernetes Default Network Policy examples
Best practices for Kubernetes Network Policies
What is network policy ?
Network Policies are a mechanism for controlling network traffic flow in Kubernetes clusters. They allow you to define which of your Pods are allowed to exchange network traffic. You should use them in your clusters to prevent apps from reaching each other over the network, which will help limit the damage if one of your apps is compromised.
Each Network Policy you create targets a group of Pods and sets the Ingress (incoming) and Egress (outgoing) network endpoints those Pods can communicate with.
There are three different ways to identify target endpoints:
- Specific Pods (Pods matching a label are allowed)
- Specific Namespaces (all Pods in the namespace are allowed)
- IP address blocks (endpoints with an IP address in the block are allowed
Let’s consider the following example on an AKS cluster;
In the diagram above you can see we have a test-service which requires to accept TCP connections only from client-green pod and not from client-red. In such a situation a network policy can help you control this traffic flow as desired .
A network policy (a .yaml file) for the test-service would look something like this .
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-service-allowed-traffic
namespace: default
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: test-service
policyTypes:
- Ingress
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: client-green
We will discuss about the each and every line of the policy later. But as of now just keep in mind running this yaml on the cluster will create network routing rules which will block ingress traffic to test-service from client red whereas it will be allowed from client-green.
Okay !! we understood a basic example of what a Network policy can do on a cluster .. So now let’s get our concept cleared on WHY WE NEED A NETWORK POLCIY?
From the below pictorial representation of a Container attack surfaces , Insecure networking can be eliminated by configuring stringent Network policies
Let’s go through few attack scenarios in which a Network Policy can save the day
- Internet exposed Microservice hosted on AKS
- Microservice BE is exposed to Internet via External Nginx Ingress.
- As per the design , traffic should flow from APIM -> Ingress -> Microservice BE
- However, due to no network restrictions an attacker on Open Internet was able to establish TCP connection to the Microservice BE on open port 80/443.
- If the Microservice BE is having any known vulnerability then it might lead to Initial access
- Adding a network policy for the nginx ingress Pod to accept traffic only from the APIM public IP will cut down the attack surface.
2. Compromised Microservice running on AKS
- An attacker has gained access to the App_X pod
- This is a privileged Pod or the attacker has escalated the privilege by exploiting any existing loophole in the POD config.
- Using this pod , attacker gets details about other pods/Host VM
- Now the attacker can laterally move from one pod to another (as there is no restriction for pod-pod communication by default) & even to the Host VM ( if it has poor access restriction) thus increasing the attack surface.
- We can simply add a Network Policy for App_X pod to control the egress traffic . Practicing this for every pod will definitely help in reducing the attack surface
So it is evident that Network policies helps you to stop/frustrate the attacker at different stages of an attack.
Creating a basic network policy in AKS
Now we have an idea about What a network policy is & Why we need it at the first place . Let’s understand how to write these policies
Consider Fig 1
STEP 1 : Make sure CNI networking plugin is installed on your AKS cluster
STEP 2 : Determine the legit ingress and egress traffic for the pod
- In our case it is client-green to test-service = Allow , client-red to test-service= Block
- So we will create a policy for test-service pod to accept traffic accordingly
STEP 3 : Assign labels to your pods
- To attach a Pod to a network policy, labels and selectors are used. First, a label is applied to the pod, for instance:
labels:
app.kubernetes.io/name: test-service
labels:
app.kubernetes.io/name: client-green
STEP 4 : Start writing the yaml — test.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-service-allowed-traffic --- name of the policy
namespace: default --- namespace in which network policy is to be applied
spec:
podSelector: --- type of selector
matchLabels:
app.kubernetes.io/name: test-service -- label of the pod for which the policy is created
policyTypes:
- Ingress -- Rules for ingress traffic
- from:
- podSelector: --- type of selector
matchLabels:
app.kubernetes.io/name: client-green --- Incoming traffc only being accepted from client-green . Rest all will be denied.
STEP 5 : Apply the yaml
kubectl apply -f test.yaml
Hold on !!! We are not done here. This is just a basic .yaml example of a network policy. There are many more features a Network Policy offers
Ingress/Egress selectors
Your Network Policy Ingress and Egress rules can use a few different selector types to identify the Pods that are allowed to communicate with the policy’s target
- podSelector — selects pod that match a defined set of labels
podSelector:
matchLabels:
app: test
- namespaceSelector — is similar to podSelector but it selects an entire namespace using labels. All the Pods in the namespace will be included.
namespaceSelector:
matchLabels:
app: test
You can match a specific namespace by name by referencing the kubernetes.io/metadata.name label that Kubernetes automatically assigns:
namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: test-namespace
- ipBlock — ipBlock selectors are used to allow traffic to or from specific IP address CIDR ranges. This is intended to be used to filter traffic from IP addresses that are outside the cluster. It’s not suitable for controlling Pod-to-Pod traffic because Pod IP addresses are ephemeral — they will change when a Pod is replaced.
ipBlock:
cidr: 10.0.0.0/24
Combining selectors
Combining selectors you can use multiple selectors to create complex conditions in your policies. The following policy selects all the Pods that are either labelled test-api or belong to a namespace labelled app: test
ingress:
- from:
- namespaceSelector:
matchLabels:
app: test
- podSelector:
matchLabels:
app: test-api
Above example represents a logical “or”. You can also create “and” conditions by combining selectors together as shown below:
ingress:
- from:
- namespaceSelector:
matchLabels:
app: test
podSelector:
matchLabels:
app: test-api
This policy only targets Pods that are both labelled app: test-api and in a namespace labelled app: test.
Setting allowed port ranges
The examples above permit allowed Pods to communicate using the entire available port range. However, adding a ports field to your Ingress and Egress rules lets you restrict this to just the ports your app actually requires:
ingress:
- from:
- podSelector:
matchLabels:
app: demo
ports:
- protocol: TCP
port: 32000
endPort: 32100
Now communication is only allowed on TCP ports in the range 32000 to 32100. You can omit the endPort field if you only use a single port.
Kubernetes Default Network Policy examples
- Deny all traffic to a Pod
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: network-policy
spec:
podSelector:
matchLabels:
app: demo
policyTypes:
- Ingress
- Egress
- Deny all traffic to all Pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: network-policy
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
- Deny all ingress traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: network-policy
spec:
podSelector: {}
policyTypes:
- Ingress
- Deny all egress traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: network-policy
spec:
podSelector: {}
policyTypes:
- Egress
- Allow all traffic to a Pod
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: network-policy
spec:
podSelector:
matchLabels:
app: demo
policyTypes:
- Ingress
- Egress
ingress:
- {}
egress:
- {}
Best practices for Kubernetes Network Policies
Make sure to stick to the following 5 rules for a well-structured and effective Network policies
1. Ensure all Pods are covered by a Network Policy
All Pods in a Kubernetes cluster should be subject to Network Policies that limit their network interactions to the minimal set of Ingress/Egress targets they require. Not setting Network Policies allows all Pods to communicate, which is a potential security risk.
2. Use precise Ingress/Egress target selectors
Keep your Pod selectors, namespace selectors, and ipBlock ranges as precise as possible to prevent them from accidentally selecting new Pods in the future. For example, a namespace selector is inappropriate if you’re likely to deploy additional Pods to the namespace, and those Pods shouldn’t automatically communicate with your Network Policy’s target.
3. Set a default deny policy, then add your allow policies
You can ensure your cluster has complete Network Policy coverage by creating a default “deny all” policy (as shown in the example above), then adding specific “allow” policies that authorize required traffic flows. This method means new Pods are protected from accidental network exposure, even if you forget to create a specific Network Policy for them.
4. Regularly review your policies and keep them updated
Your Network Policy requirements are likely to change as your cluster evolves with new Pods and namespaces. You should regularly review your policies and make any alterations required so they remain appropriate for your environment.
5. Test your policies to check they’re working as intended
One of the difficulties associated with Network Policies is the lack of clear visibility into whether they’re working. It’s worthwhile to test new policies to be sure they’re configured correctly. As in the examples above, you can create a new Pod with labels that match your Network Policy’s selectors, then use commands like curl and ping to test the connectivity available within the container.
Okay so this sums up our discussion. In next article we will implement end-to-end Network Policy for a microservice application hosted on AKS.