Computer scienceSystem administration and DevOpsKubernetesNetworking and traffic management

Ingress and egress network policies

16 minutes read

By default, every Pod can communicate with all other Pods within a Kubernetes cluster. However, in certain scenarios, you need to restrict access to specific Pods. For instance, consider a backend database Pod holding sensitive information; you want to ensure that only specific backend Pods can connect to it.

This topic will explore the usage of NetworkPolicy to establish isolated Pods or control access to designated groups of Pods.

NetworkPolicy

Networking policy allowing backend traffic but not frontend.

Imagine you have deployed an application with three distinct components: frontend, backend, and a database. In this setup, you don't want the frontend Pods to communicate directly with the database Pod. Allowing such access could potentially create security vulnerabilities, leading to possible data breaches or loss. Fortunately, you can prevent such communication easily with network policies.

NetworkPolicies provide a way to define and control traffic flow between Pods within a cluster. A network policy resource acts like a firewall for your internal cluster network, controlling both inbound and outbound traffic at the Pod level. You specify rules that define which Pods can communicate with each other, namespaces, or IP blocks.

Network policies work in two ways: traffic coming into Pods (ingress) and traffic going out (egress). By default, both ingress and egress traffic are allowed. A Pod can receive traffic from or send traffic to any target. Let's see how you can restrict that with the NetworkPolicy resource.

NetworkPolicy resource

NetworkPolicy deals with traffic at the Pod level. It is a namespace-scoped resource and applies only to Pods within that namespace. Here is an example:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-netpol
  namespace: prod
spec:
  podSelector:
    matchLabels:
      tier: backend	
    
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              tier: frontend
      ports:
        - protocol: TCP
          port: 3000
                    
  egress:
    - to: 
      - podSelector:
          matchLabels:
              tier: database
      ports:
        - protocol: TCP
          port: 5432

Similar to other Kubernetes resources, the NetworkPolicy resource includes the standard fields, such as apiVersion, kind, metadata, and spec. In the example above, the NetworkPolicy will operate in the prod namespace. The .spec.podSelector field selects the grouping of Pods to which a policy applies. In our example, we're targeting Pods with the label "tier=backend". Without a podSelector, the policy selects all Pods in the namespace.

You also include a policyTypes list specifying either Ingress, Egress, or both. The ingress field defines rules for incoming traffic, while egress defines the outbound traffic from these Pods. Each Ingress or Egress rule allows traffic matching both the from/to and ports sections. In our example above, we're allowing inbound traffic from our frontend Pods on port 3000 using a podSelector. We're also allowing outbound traffic to our database Pods by specifying another podSelector for our database Pods.

To verify, use kubectl describe after creating the object to see how the policy is interpreted.

A closer look at selectors

In the above manifest, we only used the podSelector selector to select Pods in the same namespace. However, we can use other selectors:

  • namespaceSelector

  • namespaceSelector and podSelector

  • ipBlock.

The namespaceSelector lets you specify namespaces whose Pods can send or receive traffic:

...
egress:
  - to:
    - namespaceSelector:
        matchLabels:
          tier: monitoring
    ports:
      - protocol: TCP
        port: 9100     

In this example, the Pods selected in spec.podSelector can send traffic to namespaces with the label tier=monitoring. Such a namespace probably has Pods running Prometheus for monitoring.

When you combine namespaceSelector and podSelector in a single to/from entry, you can target Pods more precisely. You need to use the correct syntax depending on whether the policy needs to be a logical AND or OR:

...
ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          subscription: premium
      podSelector:
        matchLabels:
          tier: frontend

For this to apply, both conditions must be true because they are in the same array. The policy applies to a source namespace with the label subscription=premium and a source Pod with the label tier=frontend.

podSelector and namespaceSelector when ANDed together

...
ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          subscription: premium
    -  podSelector:
        matchLabels:
          tier: frontend

In this case, the policy will allow traffic from any namespace with the label subscription=premium or any Pod with the label tier=frontend.

podSelector and namespaceSelector as separate selectors.

Finally, you can use ipBlock as your selector. In this case, IP CIDR ranges specify traffic sources or destinations. This is very useful for services that live outside the cluster, such as a managed database in a cloud environment. In such cases, you target the VPC or subnet CIDR where the service runs:

...
egress:
  - to:
      - ipBlock:
          cidr: 10.0.0.0/16
          except:
            - 10.0.1.0/16    
            - 10.0.5.0/16 

In the above example, we're allowing traffic to the whole private network 10.0.0.0/8 using the cidr field. However, we're restricting traffic to 10.1.0.0/24 and 10.5.0.0/16 subnets, which may house sensitive workloads. For ipBlock, there are some key rules:

  • The cidr must be a valid CIDR (e.g., 192.168.1.0/24, not 192.168.1.0).

  • Each except entry must fall within the parent cidr range.

  • ipBlock cannot be combined with podSelector/namespaceSelector in the same to/from element — it must be its own list item.

As an alternative to matchLabels in your selectors, you can also use matchExpressions for a more expressive syntax:

...
ingress:
  - from:
    - podSelector:
        matchExpressions:
          - key: tier
            operator: In
            values: [ "monitoring", "evaluation" ]

Using matchExpressions to select multiple values.

Default policies

As noted earlier, the default is to allow all ingress and egress traffic if no policies exist in a namespace. You can override this behavior by creating policies. Let's begin with a policy to deny all incoming traffic:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
	  - Ingress

This policy selects all Pods but does not have any rules to allow traffic to them. That effectively denies all traffic. This policy acts as a safety net for Pods not selected by other policies to explicitly allow ingress traffic. Here's a slightly adjusted manifest to allow all ingress traffic:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-allow-ingress
spec:
  podSelector: {}
  policyTypes:
	  - Ingress
	ingress:
		- {}

Be careful when using this policy because no other policy can restrict incoming traffic. This can pose security risks.

You create egress isolation policies similarly. You create a policy that selects all Pods but has no rule to allow them to send traffic:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
	  - Egress

Pods not targeted by other network policies will not be allowed to send traffic. This can serve as a safety net in various scenarios. To allow some traffic, create an egress policy that permits it. To allow all traffic, here's the policy you'd need:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-allow-egress
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

Again, with this policy applied in a namespace, no other policy can prevent outbound traffic. Therefore, use it with caution.

To deny all ingress and all egress traffic, simply use:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all
spec:
  podSelector: {}
  policyTypes:
	  - Ingress
	  - Egress

You can then create more targeted policies to allow specific types of traffic, thus securing your applications.

Conclusion

We've explored how to use the NetworkPolicy resource for traffic control in a Kubernetes cluster. We saw how you can use various selectors: podSelector, namespaceSelector, and ipBlock to precisely define ingress and egress communication paths. By mastering these configurations, alongside default deny safety nets, you can effectively strengthen the security posture of your cluster.

5 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo