Network Policies
Let's start with the basics of networking...

Responses are allowed for whoever sent the request. We need to control what it can receive and where it can go.
In this scenario:
The frontend application must allow incoming traffic on port 443 HTTPS that it listens to from the Internet and must allow communication with the backend on port 5000.
The backend application should only accept requests on port 5000, respond to them, and can make requests to the database on port 3306.
The database only accepts requests on port 3306 and responds to them, not allowing outbound connections anywhere.
For this we need to create rules for the ingress and egress of these applications.
Ingress Rules are the inbound rules and Egress Rules are the outbound rules.
What rules are needed for everything to work?
-
Frontend
- Allow port 443 on ingress
- Allow backend:5000 on egress
-
Backend
- Allow port 5000 on ingress
- Allow database:3306 on egress
-
Database
- Allow port 3306 on ingress
One of the prerequisites for the workflow in Kubernetes is that any pod can communicate with the others without any additional configuration like routes.

In this networking solution, all pods are on a virtual network that extends across the nodes in the Kubernetes cluster and all of them, BY DEFAULT, can reach each other through IPs, names, and services configured for this purpose.
Kubernetes is configured by default with an (All Allow) rule that allows traffic from any pod to any other pod.
Returning to the proposed scenario, the frontend, backend, and database can all communicate with each other without any impediment within the cluster. The rules are going beyond what we want; for this we need to restrict these pods.
Network policies are applied to pods, not on pods. It is a rule completely independent of the pod.
For example, we need the database to only allow traffic coming from the backend pods on port 3306.
To bind a network rule to a pod, we use the same technique that binds replicasets to pods and services to pods, using labels and selectors.


apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
# Will be applied to all pods that have the label app: database
matchLabels:
app: database
policyTypes:
- Ingress
# Ingress rules accept traffic from pods that have the label app: backend
ingress:
- from:
- podSelector:
matchLabels:
app: backend
# On port 3306 using TCP protocol
ports:
- protocol: TCP
port: 3306
There is only an effect if there is ingress and/or egress in the policy types because the rule must be applied either on ingress or egress.
The CNI used for the network solution in the cluster must support network policies, otherwise it will have no effect.
Calico, Cilium, Weave Net, Kube-router natively support network policies.
Flannel does not support them.
Now let's think about the following. We have different pods that can have the same label, but in different namespaces.

This way we can also restrict at the namespace level.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
# Will be applied to all pods that have the label app: database
matchLabels:
app: database
policyTypes:
- Ingress
# Ingress rules accept traffic from pods that have the label app: backend and are in the prod namespace.
ingress:
- from:
- podSelector:
matchLabels:
app: backend
namespaceSelector:
matchLabels:
name: prod
# On port 3306 using TCP protocol
ports:
- protocol: TCP
port: 3306
If we only use the namespaceSelector, all pods within the namespace could have access to the database.
What if a backup server needed to access this database directly? The previous rule wouldn't work.
However, if we analyze the yaml more closely, we'll see that from is a list of rules that are allowed. If any of the rules are satisfied, then it can pass. In the example above, we only have one rule (be from the prod namespace and have the backend label). Let's add another rule then.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
# Will be applied to all pods that have the label app: database
matchLabels:
app: database
policyTypes:
- Ingress
# Ingress rules accept traffic from pods that have the label app: backend and are in the prod namespace.
ingress:
- from:
########### Rule 1 ##########
- podSelector:
matchLabels:
app: backend
namespaceSelector:
matchLabels:
name: prod
########### Rule 2 ##########
- ipBlock:
cidr: 192.168.5.10/32 # Backup server IP
# On port 3306 using TCP protocol
ports:
- protocol: TCP
port: 3306
This shows that we have two ingress rules and any one that is satisfied is sufficient to allow the connection.
Rule 1 - Be in the prod namespace and the pod needs to have the label app: backend Rule 2 - The connection would need to come from 192.168.5.10
If you had done this, just made the mistake of putting a - in front of namespaceSelector.
ingress:
- from:
- podSelector:
matchLabels:
app: backend
- namespaceSelector:
matchLabels:
name: prod
- ipBlock:
cidr: 192.168.5.10/32 # Backup server IP
There would be 3 different rules:
Rule 1 - Any pod that has the label app: backend regardless of the namespace could be allowed.
Rule 2 - Any pod in the prod namespace would be allowed
Rule 3 - The request coming from IP 192.168.5.10 would be allowed
Just one mistake here and everything can change!
But let's imagine that the database connects to the backup on port 80 and not the other way around, therefore.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
- Egress # Egress rule also added
ingress:
- from:
- podSelector:
matchLabels:
app: backend
namespaceSelector:
matchLabels:
name: prod
ports:
- protocol: TCP
port: 3306
egress:
- to:
- ipBlock:
cidr: 192.168.5.10/32
ports:
- protocol: TCP
port: 80
Both the from of ingress and the to of egress are also lists. So we could still have more rules within the same policy.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
- Egress # Egress rule also added
ingress:
- from:
- podSelector:
matchLabels:
app: backend
namespaceSelector:
matchLabels:
name: prod
ports:
- protocol: TCP
port: 3306
- from:
- podSelector:
matchLabels:
app: backend
namespaceSelector:
matchLabels:
name: staging
ports:
- protocol: TCP
port: 3306
egress:
- to:
- ipBlock:
cidr: 192.168.5.10/32
ports:
- protocol: TCP
port: 80
- to:
- ipBlock:
cidr: 192.168.5.10/32
ports:
- protocol: TCP
port: 443
I know this could be simpler, but it was just to illustrate.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
- Egress # Egress rule also added
ingress:
- from:
- podSelector:
matchLabels:
app: backend
matchExpressions:
- key: name
operator: In
values:
- prod
- staging
ports:
- protocol: TCP
port: 3306
egress:
- to:
- ipBlock:
cidr: 192.168.5.10/32
ports:
- protocol: TCP
port: 80
ports:
- protocol: TCP
port: 443