NetworkPolicy Best Practices

In a perfect world, your Pod will only allow the network traffic that you explicitly permit — “zero-trust”, or perhaps more accurately “least-privilege” principle. By default, however, Kubernetes imposes no restrictions on network traffic.

However, as soon as you implement your first NetworkPolicy that selects a Pod, Kubernetes will start restricting all traffic to/from that Pod that matches the type of that NetworkPolicy (Ingress, Egress). If you create a “default deny” NetworkPolicy, that selects all Pods and specifies both Ingress and Egress types, Kubernetes will start behaving in a least-privilege manner (at least for your Namespace).

Here is an example of a default deny NetworkPolicy:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: my-ns
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress

With this policy in place, any/all network traffic will require explicit NetworkPolicies to be defined. This includes traffic that you might not initially anticipate, such as DNS lookups.

If you have other standard requirements (for example, access to DNS) that you want every pod in every Namespace to have, then create these NetworkPolicies in addition to your default deny at Namespace creation.

This one is probably pretty self-explanatory, but a network-policy named “my-network-policy” probably doesn’t help anyone to understand the purpose of the network policy. Names like “default-deny-all” or “allow-allpods-to-dns” give a lot more clarity to what the NetworkPolicy is intended to do.

Combining disparate NetworkPolicies into one may make naming the policy a bit of a challenge, but also may result in allowing more access to/from your Pods than desired, violating the least-privilege principle. Consider this NetworkPolicy:

# This is bad because it grants access to more than it should
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: egress-network-policy
namespace: my-ns
spec:
policyTypes:
- Egress
podSelector: {}
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
- ipBlock:
cidr: 93.184.216.34/32
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
- port: 80
protocol: TCP

It allows access from all Pods to the kube-dns pods in the kube-system Namespace on port 53, as well as to “example.com” on port 80. Unfortunately, it also allows access to DNS services on port 53 at “example.com” as well as (in-theory) access to anything running on port 80 in the kube-dns Pods as well. It would be better to have two discrete NetworkPolicies:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-allpods-to-dns
namespace: my-ns
spec:
policyTypes:
- Egress
podSelector: {}
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP

and (if we assume that only certain Pods need access to “example.com” on port 80):

apiVersion: networking.k8s.io/v1 
kind: NetworkPolicy
metadata:
name: allow-cronjob-to-examplecom
namespace: my-ns
spec:
podSelector:
matchLabels:
app.kubernetes.io/component: cronjob
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 93.184.216.34/32
ports:
- protocol: TCP
port: 80

Usually, your Pods will not all have the same connectivity requirements. Specifying /spec/podSelector: {} is usually not a good idea, except for connections that are pretty ubiquitous (for example, DNS).

Similarly, when you don’t specify a to: block in an egress policy or a from: block in an ingress policy, you are effectively saying that ALL connections from/to ALL sources should be allowed on the ports specified. For example, the following NetworkPolicy will allow outbound connections on port 80 to everywhere (which, while perhaps better than nothing, is certainly much more than is required to allow access to “example.com”):

# This is bad, because it allows connections to anything listening
# on port 80
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-cronjob-to-examplecom
namespace: my-ns
spec:
podSelector:
matchLabels:
app.kubernetes.io/component: cronjob
policyTypes:
- Egress
egress:
- ports:
- protocol: TCP
port: 80

Ensuring that your NetworkPolicies are specifying which Pods and/or ipBlocks they will permit traffic to/from (as demonstrated in previous examples) will go a long way to ensuring that your NetworkPolicies are actually providing the security you expect.

--

--

--

Distinguished Architect at Sun Life Financial. Focused on containers & Kubernetes. Views & opinions expressed here are my own, not necessarily those of Sun Life

Love podcasts or audiobooks? Learn on the go with our new app.

Gotta catch ’em all

How to Manage SAP Implementation Successfully: Chris Salis

Auto Backup MySql database to Google Drive using Crontab in Ubuntu 16.04

Time for InnerSource?

Right Sibling Tree | Rust

How We Handle Thousands of Requests per Second

The power of a small WIP

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Paul Dally

Paul Dally

Distinguished Architect at Sun Life Financial. Focused on containers & Kubernetes. Views & opinions expressed here are my own, not necessarily those of Sun Life

More from Medium

Kubernetes Taints & Tolerations: Tutorial With Examples

1:1 with kubernetes

Multi-Tenancy in Kubernetes using Loft’s Vcluster

Keycloak Clustering on Kubernetes