Table of Contents
Kubernetes Pods can freely communicate with each other by default. This poses a security risk when your cluster’s used for multiple applications or teams. Errant behavior or malicious access in one Pod could direct traffic to the other Pods in your cluster.
This article will teach you how to avoid this scenario by setting up network policies. These rules let you control Pod-to-Pod traffic flows at the IP address level (OSI layer 3 or 4). You can precisely define the ingress and egress sources permitted for each Pod.
Creating a Network Policy
Network policies are created by adding NetworkPolicy
objects to your cluster. Each policy defines the Pods it applies to and one or more ingress and egress rules. Here’s a basic policy manifest:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: network-policy namespace: app spec: podSelector: matchLabels: component: database policyTypes: - Ingress - Egress ingress: - from: - podSelector: matchLabels: component: api egress: - to: - podSelector: matchLabels: component: api
This network policy applies to any Pod with a component: database
label in the app
namespace. It states that ingress (incoming) and egress (outgoing) traffic is only allowed from and to Pods with a component: api
label. Any requests originating from other Pods, such as component: web-frontend
, will be blocked.
Network policies can be applied like any other object by using Kubectl. They’ll take effect immediately after they’re created. You can add the networking policy before you start the Pods it selects.
$ kubectl apply -f policy.yaml networkingpolicy.networking.k8s.io/network-policy created
How Network Policies Work
Network policies are implemented by your cluster’s active networking plugin. Your policies won’t have any effect if your plugin doesn’t support the feature. Most popular options such as Calico and Cilium ship with network policy support enabled.
When a network policy applies to a Pod, the plugin will inspect its traffic to check it’s compliant with the policy’s requirements. Any connections that don’t meet the criteria will be disallowed. The Pod that tried to initiate the connection will find the remote host is unreachable, either because it was trying to access a resource blocked by an egress rule, or because a remote Pod denied the incoming connection using an ingress rule.
A successful connection between two Pods can only be established when the network policies on both of them permit it. The connection could be forbidden by an egress rule of the initiating Pod, or an ingress rule on the target.
Network policies are always additive in nature. When multiple policies select the same Pod, the list of permitted ingress and egress sources will be the combination of all the policies.
Example Network Policies
Network policies support many different options for customizing the Pods they target and the types of connection that are allowed. The following examples showcase several common use cases.
Apply a policy to every Pod in the namespace, only allowing Ingress traffic from a specific IP address block
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: network-policy namespace: app spec: podSelector: {} policyTypes: - Ingress ingress: - from: - ipBlock: cidr: 172.17.0.0/16
The empty podSelector
block means all the namespace’s Pods are targeted by the policy. The ipBlock
rule restricts ingress traffic to Pods with an IP address in the specified range. Egress traffic is not blocked.
Allow Ingress traffic from an IP address block, but exclude some specific IPs
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: network-policy namespace: app spec: podSelector: {} policyTypes: - Ingress ingress: - from: - ipBlock: cidr: 172.17.0.0/16 except: - 172.17.0.1/24 - 172.17.0.2/24 - 172.17.0.3/24
ipBlock
rules support an except
field to exclude traffic originating from, or being directed to, specific IPs.
Allow Ingress traffic from all Pods in the namespace, but only from a specific port
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: network-policy namespace: app spec: podSelector: {} policyTypes: - Ingress ingress: - from: - podSelector: {} ports: - protocol: TCP port: 443
The ports
field is available on ingress and egress rules. It defines the ports that traffic can be received from and sent to. You can optionally specify a range of ports, such as 3000 – 3500, by setting the endPort
field (3500) in addition to port
(3000).
Allow traffic from Pods with a specific label that exist in a different namespace
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: network-policy namespace: database spec: podSelector: {} policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: application: demo-app podSelector: matchLabels: component: database
The policy states that any Pod labelled component: database
can reach all the Pods in the database
namespace, if its own namespace is labelled demo-app
.
You can allow traffic from all the Pods in an external namespace by creating a rule that only includes a namespaceSelector
field.
Explicitly allow all traffic
Sometimes you might want to explicitly allow all traffic of a particular type within a namespace. Include the type in your policy but supply an empty Pod selector and no rules:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: network-policy namespace: app spec: podSelector: {} policyTypes: - Ingress - Egress ingress: - {} egress: - {}
All the Pods in the namespace can freely communicate, as if there was no policy. Creating the policy anyway lets you indicate your intentions to other cluster users. They might question the presence of a namespace with unrestricted networking in a cluster which has otherwise been secured.
When to Use Network Policies
Network policies should be created for each of the namespaces and Pods in your cluster. This better isolates your Pods and puts you in control of traffic flow.
Try to make your policies as granular as possible. Widening access too much, such as allowing access between all Pods in a namespace, leaves you exposed to risks if one of your containers is compromised. Consider using precise selectors to identify individual ingress and egress remotes for sensitive Pods such as authentication services, databases, and payment handlers.
Kubernetes doesn’t enable any network policies by default which can allow oversights to occur, even if you intend all Pods to be protected by a policy. You can mitigate against this risk by adding a catch-all policy to your namespaces. This policy selects every Pod in the namespace and applies a rule that forbids all network communication:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all namespace: app spec: podSelector: {} policyTypes: - Ingress - Egress
Network policies are always scoped to namespaces so you’ll need to create a separate catch-all for each one.
Summary
Kubernetes allows all the Pods in your cluster to communicate with each other. This is too permissive for real-world applications running in multi-purpose clusters. Network policies address this problem by providing a firewall-like system for managing the ingress sources and egress targets that each Pod accepts.
It’s good practice to configure a network policy on all of your Pods. This will secure your cluster so only legitimate traffic flows are permitted. Network policies are only one part of Kubernetes security, however: other protection mechanisms such as RBAC and Pod security contexts are also essential tools for hardening your environment.