Kubernetes network policies
Kubernetes NetworkPolicy examples covering default deny, frontend-to-backend rules, cross-namespace access to Cassandra, and DNS egress policies.
Here is an example of network policies
First, remove the master taint so pods can be scheduled on the control plane node. Then create two pods (frontend and backend) and expose them as services.
1
2
3
4
5
6
7
k taint node scw-k8s-cks node-role.kubernetes.io/master-
k run frontend --image=nginx
k run backend --image=nginx
k get pods
k expose pod frontend --port 80
k expose pod backend --port 80
k get svc
If a pod has multiple network policies applied, all of them will be merged.
Default deny network policy
This policy blocks all ingress and egress traffic for every pod in the default namespace. After applying it, pods can no longer communicate with each other.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@scw-k8s-cks:~# cat default-deny.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-policy
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
# connection does not work anymore
root@scw-k8s-cks:~# k exec frontend -- curl backend
root@scw-k8s-cks:~# k exec backend -- curl frontend
Setting up following network policies
There are two pods currently running in your cluster.
1
2
3
4
root@scw-k8s-cks:~# k get pods
NAME READY STATUS RESTARTS AGE
backend 1/1 Running 0 4h27m
frontend 1/1 Running 0 4h30m
Frontend part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
root@scw-k8s-cks:~# cat allow-frontend-to-talk-to-backend.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-talk-to-backend
namespace: default
spec:
podSelector:
matchLabels:
run: frontend
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
run: backend
ports:
- protocol: TCP
port: 80
Backend part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
root@scw-k8s-cks:~# cat backend-to-frontend.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-to-frontend
namespace: default
spec:
podSelector:
matchLabels:
run: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
run: frontend
ports:
- protocol: TCP
port: 80
Now check whether we can connect from frontend -> backend
Verify that the frontend pod can reach the backend by curling its IP address directly. The connection should succeed because we created matching egress and ingress policies.
1
2
3
4
5
6
7
8
9
10
11
root@scw-k8s-cks:~# k get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
backend 1/1 Running 0 23h 10.32.0.5 scw-k8s-cks <none> <none>
frontend 1/1 Running 0 23h 10.32.0.4 scw-k8s-cks <none> <none>
root@scw-k8s-cks:~# k exec -it frontend -- curl 10.32.0.5
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</html>
Please notice that a direction from backend -> frontend does not work (as it is supposed to be)
Since we only created an egress policy from frontend to backend and an ingress policy allowing frontend into backend, the reverse direction (backend to frontend) is blocked and will time out.
1
2
3
root@scw-k8s-cks:~# k exec -it backend -- curl 10.32.0.4
...
# times out ...
One more extended use case with connection to cassandra database
Create a dedicated namespace for Cassandra, label it appropriately, expose the pod as a service, and verify the pod IP address for later connectivity tests.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
root@scw-k8s-cks:~# k create namespace namespace-cassandra
namespace/namespace-cassandra created
# create a label on pod <- this is not needed but it's good to see how to add label and remove label from a POD
root@scw-k8s-cks:~# k label pod --namespace namespace-cassandra cassandra ns=cassandra
pod/cassandra labeled
root@scw-k8s-cks:~# k label pod --namespace namespace-cassandra cassandra ns-
# create a label on a namespace
root@scw-k8s-cks:~# k label namespace namespace-cassandra ns=cassandra
namespace/namespace-cassandra labeled
# check labels
root@scw-k8s-cks:~# k get ns namespace-cassandra --show-labels
NAME STATUS AGE LABELS
namespace-cassandra Active 12m kubernetes.io/metadata.name=namespace-cassandra,ns=cassandra
root@scw-k8s-cks:~# k expose pod --namespace namespace-cassandra cassandra --port 80
service/cassandra exposed
# Check cassandra IPv4 address
root@scw-k8s-cks:~# k get pods -o wide -n namespace-cassandra
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
cassandra 1/1 Running 0 30m 10.32.0.6 scw-k8s-cks <none> <none>
Create one more policy
This egress policy allows the backend pod in the default namespace to send traffic to any pod in the namespace labeled ns: cassandra on TCP port 80.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
root@scw-k8s-cks:~# cat backend-to-cassandra.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-to-cassandra
namespace: default
spec:
podSelector:
matchLabels:
run: backend
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
ns: cassandra
ports:
- protocol: TCP
port: 80
k create -f backend-to-cassandra.yaml
Check connection from backend to Cassandra
Test that the backend pod can now reach the Cassandra pod in the other namespace over HTTP.
1
2
3
4
5
6
7
8
root@scw-k8s-cks:~# k exec backend -- curl http://10.32.0.6
<!DOCTYPE html>
<html>
<head>
...
</body>
</html>
root@scw-k8s-cks:~#
Make it more complex - create default deny in namespace-cassandra to block all traffic
Apply a default deny policy in the cassandra namespace to block all ingress and egress. This will break the previously working connection from backend to cassandra until a new ingress rule is added.
1
2
3
4
5
6
7
8
9
10
11
12
13
root@scw-k8s-cks:~# cat default-deny-cassandra.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: cassandra-deny-policy
namespace: namespace-cassandra
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
k create -f default-deny-cassandra.yaml
Now, the connection between backend and cassandra does not work anymore.
1
2
3
4
5
root@scw-k8s-cks:~# k exec backend -- curl http://10.32.0.6
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:00:01 --:--:-- 0^C
Label the default namespace and create an ingress policy in the cassandra namespace to allow traffic from the default namespace. This restores connectivity from backend to cassandra.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Create a new label on default namespace
root@scw-k8s-cks:~# k label namespaces default ns=default
namespace/default labeled
# Create a new network policy for Ingress at pod cassandra
root@scw-k8s-cks:~# cat from-backend-to-cassandra.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-to-cassandra
namespace: namespace-cassandra
spec:
podSelector:
matchLabels:
run: cassandra
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
ns: default
k create -f from-backend-to-cassandra.yaml
Check the connection now from backend to cassandra
Confirm that the backend pod can once again reach the cassandra pod now that the ingress rule is in place.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
root@scw-k8s-cks:~# k exec backend -- curl http://10.32.0.6
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 615 100 615 0 0 200k 0 --:--:-- --:--:-- --:--:-- 200k
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Allow DNS 53 from default namespace
This policy denies all egress and ingress by default but explicitly allows DNS traffic (TCP and UDP port 53) so that pod DNS resolution continues to work.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cat <<EOF > allow-dns-np.yaml
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny
namespace: default
spec:
podSelector: {}
policyTypes:
- Egress
- Ingress
egress:
- ports:
- port: 53
protocol: TCP
- port: 53
protocol: UDP
EOF
There are existing Pods in Namespace app.
We need a new default-deny NetworkPolicy named deny-out for all outgoing traffic of these Pods. It should still allow DNS traffic on port 53 TCP and UDP.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat np.yaml
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-out
namespace: app
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 53


