DEV Community

Cover image for Calico Network Policy
alakkadshaw
alakkadshaw

Posted on • Originally published at edgelb.net

Calico Network Policy

In this article we are going to learn about Calico Network policy. But first let us focus on the basics and learn what is a Kubernetes network policy.

Kubernetes Network Policy Basics

What is a Kubernetes network policy?

A Kubernetes network policy is a resource that defines: How pods in one namespace can communicate with pods in another namespace and sometimes with external traffic.

Network policy objects depend on label selectors to target specific pods and determine whether the traffic is allowed or denied for inbound or outbound connections that is ingress or egress.

Default Behaviour in Kubernetes

  • By default in Kubernetes, all the pods can communicate with each other without any restrictions.

  • A Network Policy introduces rules that restrict or allow traffic from one pod to another based on criteria such as labels, namespaces and IPblocks.

Basic Example of a Kubernetes Network Policy

Here we present a simple example of a Kubernetes-native NetworkPolicy that allows all ingress traffic to the pod app-backed in the default namespace

Here we are using the Kubernetes networking.k8s.io instead of Calico API.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Ingress
  ingress: []
Enter fullscreen mode Exit fullscreen mode
  • podSelector targets the pods labelled app=backend
  • policyTypes: ["Ingress"] tells the Kubernetes, this policy will apply only to the inbound traffic
  • An empty ingress list ingress:[] means: no inbound traffic is allowed to the matched pods.
  • Note: If a pod is selected by this network policy then no traffic can enter the pod from anywhere unless there is another Network Policy that allows the traffic in

2.Core Calico Terminology

Calico extends the standard Kubernetes NetworkPolicy in important ways. It introduces its own API s and concepts

Endpoints

  • Workload Endpoint (WEPs): These are network interfaces for pods. When you attach a pod to calico network, calico creates a workload endpoint behind the scenes.
  • Host Endpoints (HEPs): represents the hosts or node's own interface. When Calico applies the policies, it applies them not just to the pods but also to the cabernets nodes as well. This is quite helpful when you want to restrict traffic that enters or leaves the cluster nodes directly

Namespaced vs Global Policies

The Calico has two primary Policy Objects

  • Network Policy: (This is also called as the Calico NetworkPolicy)
  • This is similar to Kubernetes NetworkPolicy, but extends the functionality
  • Applies to the pods that is the workload endpoints within a specific namespace
  • GlobalNetworkPolicy:
  • Has Cluster-wide scope. It is not bound to a particular namespace
  • Can apply to namespaces and even host endpoints. This makes the GlobalNetworkPolicy a great tool for implementing cluster-level security rules such as default denies, auditing, or compliance policies.

Let us consider an example of Calico-specific namespaced NetworkPolicy.

In the below code notice the difference between the standard Kubernetes policy -specifically apiVerison: project calico.org/v3 and additional fields like selector

apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: allow-frontend
  namespace: default
spec:
  selector: role == 'frontend'
  types:
    - Ingress
  ingress:
    - action: Allow
      protocol: TCP
      source:
        selector: role == 'backend'
      destination:
        ports:
          - 8080

Enter fullscreen mode Exit fullscreen mode

Here is what we are doing

  • selective:role == 'frontend' replaces the prodSelector of the Kubernetes. Calico label selectors can apply to multiple resource types and offer more advanced operators
  • action:Allow is explicitly stated
  • source.selector:role == 'backend' means that the traffic is allowed only form pods that are labelled role=backend
  • destination.ports: [8080] restricts the allowed ports to TCP port 8080

Selectors

Selectors are at the centre of Calico policies. The selectors define which endpoints the rules apply to

Calico has a rich label-based matching syntax

  • has(label) - matches resources that has the given label
  • label == 'value' - exact match for the label's value
  • label in {"v1","v2"} - matches if the label value is within a set
  • Negotiations operators are also available, ! and other operators such as &&, == , != and others.

Ingress and Egress Overview

3.Ingress and Egress Overview

Defining the Ingress rules that is Inbound rules

  • Ingress rules govern traffic that is coming into the pods or your endpoints.
  • For example, if you want traffic to come only from certain pods or from specific namespaces or from designated IP addresses, you can define ingress rules that match the criteria.
  • Here an an example of the ingress rule structure inside a policy
ingress:
  - action: Allow
    source:
      selector: label == 'some-value'
    destination:
      ports: [80, 443]
Enter fullscreen mode Exit fullscreen mode

Defining Outbound rules that is Egress rules

  • Egress rules govern the traffic that goes out of the pods or your endpoints
  • This is useful for restricting external traffic. Example you can only allow traffic to a known CIDR range or certain IP blocks
egress:
  - action: Deny
    destination:
      nets:
        - 0.0.0.0/0
Enter fullscreen mode Exit fullscreen mode

The above snippet effectively denies all outbound traffic if the snippet is included in a policy spec

You can refine it to allow certain IP or ports.

Default Deny vs Default Allow

  • By default, in Kubernetes with no policies, everything is effectively allow all
  • The best practice for production is to start with a default deny then add explicit allows for the traffic that you need.
  • You can do this with a Calico policy that denies all ingress/egress and then adding additional policies for specific traffic that you want to allow.
  • Calico makes it straightforward to enforce the cluster wide default denies, then to apply the exception as suitable for each namespace or endpoint

Order of Enforcement

4.Order of Enforcement

there is an order field in Calico

Unlike native Kubernetes NetworkPolicy, where you do not have an explicit ordering mechanism.

Calico has this feature where you define the order or priority for each policy. A lower order or priority means that policy is enforced first.

Example
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: high-priority-policy
  namespace: my-namespace
spec:
  order: 100
  selector: app == "critical"
  ingress:
    - action: Deny

apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: lower-priority-allow
  namespace: my-namespace
spec:
  # A lower order number means this policy is evaluated first
  order: 50
  selector: app == "critical"
  types:
    - Ingress
  ingress:
    - action: Allow

Enter fullscreen mode Exit fullscreen mode

If there are multiple policies that match the same traffic. Calico will apply them starting with the policy that has the lowest ordering number

The first terminal action encountered example "allow" or "deny" determines what happens with the packet.

In the above example the action allow will be implemented as the policy with the lower number of 50 has the allow action.

Comparison with the Native Kubernetes Ordering

  • In Native Kubernetes the NetworkPolicy objects have no inherent ordering. Multiple policies can apply simultaneously and traffic is allowed only if it is allowed by all the different policies.
  • Calico approach is more flexible. Calico first looks at the lowest order and once if the traffic is allowed or denied by the terminal rule then Calico does not continue evaluating subsequent network policies.

Installation and Calico Cluster setup.

Installing Calico

To install Calico, you can use Helm or kubectl apply, depending on your preference and cluster environment.

Below is an overview of both the methods

Installation using kubectl apply

Download the Calico manifests

  • Generally, you have to get a file such as tiger-operator.yaml and another containing your custom resources custom-resources.yml from project calicos official release:

Apply the Manifests

kubectl apply -f tigera-oprator.yaml
kubectl apply -f custom-resources.yaml
Enter fullscreen mode Exit fullscreen mode
  • The first command here installs teh Tigera Operator which manages the Calico components
  • The second command configures the Calico with defaults or your custom parameters for example IP pools or CIDR ranges

Wait for the Pods to start

  • Calico components will typically run in either the calico–system or tiger-operator namespace.

Installing using Helm

  1. Add Calico Helm repository:
helm repo add projectcalico https://projectcalico.docs.tigera.io/charts
Enter fullscreen mode Exit fullscreen mode
  1. Install the chart
helm install calico projectcalico/tigera-operator --version <CHART_VERSION>
Enter fullscreen mode Exit fullscreen mode
  • This command deploys the Tiger operator and additional resources needed to run Calico
  • If you have values to override the defaults, you can specify them -f custom-values.yaml
  1. Verify the installation
  • check the operator and Calico pods are up and running in the relevant namespaces

Verify Calico functionality

Verify Calico functionality

After the installation , it is important to confirm that Calico has successfully started and is running as the primary Container Network Interface CNI

Check Running Pods

  1. List pods in the relevant namespaces
kubectl get pods -n calico-system
Enter fullscreen mode Exit fullscreen mode

or

kubectl get pods -n tigera-operator
Enter fullscreen mode Exit fullscreen mode

The exact namespace depends on the method used. In many setups you can find calico-node, calico-cube-controllers or tigers-operator pods.

  1. Detailed Pod Status
kubectl describe pod <pod-name> -n calico-system
Enter fullscreen mode Exit fullscreen mode

If any pod is in CrashLoopBackOff or Pending , inspect the Events section for hints about what might have be misconfigured.

Confirming Calico as the CNI

  1. Check node configurations
kubectl get nodes -o wide
Enter fullscreen mode Exit fullscreen mode

You can see the Calico-specific network details in the node' internal address or in the annotation that reference the chi.projectcalico.org

  1. Inspect a test pod
kubectl run testpod --image=busybox --restart=Never -it
kubectl describe pod testpod
Enter fullscreen mode Exit fullscreen mode

Look for references of Calico in the description of the test pod in the network annotations for example k8s.v1.cni.projectcalico.org/...

To confirm that your pods are now being networked via Calico look for chi.projectcalico.org references

Default behaviour of Calico

Default, all calico deployments start with the allow all mode. This means that

  • No network policy rules are actively restricting any kind of traffic unless you explicitly create the rules.
  • Unless you use a Kubernetes distribution that enforces default restrictions. All the Pods in your Cluster are able to communicate with each other across namespaces.

Testing Baseline connectivity

Before you start crafting and applying any Calico NetworkPolicy objects, it is good to verify that your cluster's default networking behaves as expected.

  1. Create multiple test pods in different namespaces
kubectl create namespace dev
kubectl create namespace prod

kubectl run test1 --image=busybox --restart=Never -n dev -- sleep 3600
kubectl run test2 --image=busybox --restart=Never -n prod -- sleep 3600
Enter fullscreen mode Exit fullscreen mode
  1. Check the connectivity between the test1 and test2
kubectl exec -n dev test1 -- ping -c 3 test2.prod.svc.cluster.local
Enter fullscreen mode Exit fullscreen mode

you can confirm that pods are bale to reach each other across namespaces when you see successful pins responses.

Why a Baseline Matters

Having a baseline for pod to pod communication means that if you apply a network policy and the connectivity breaks. You know that the policy is the cause rather than any other cluster issue

This helps you troubleshoot easily


Creating a Basic Network Policy with Example

Namespaces Deny Policy

apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: deny-inbound
  namespace: app-namespace
spec:
  selector: app == "my-service"
  types:
    - Ingress
  ingress:
    - action: Deny

Enter fullscreen mode Exit fullscreen mode

Field by Field Explanation

  • apiVersion: projectcalico.org/v3 / kind: NetworkPolicy
  • This shows that the policy is using Calico's custom resource definition for its network policies
  • Calico network policies are fully compatible with kubernetes networking policies, it just offers some extended features
  • metadata
  • name: deny-inbound : This deny-inbound is the name of the policy
  • namespace:app-namespace : This means that the this policy will only apply to app-namespace namespace
  • spec:
  • selector: app == "my-service"
  • Calico uses a label based selector to target pods
  • In the app-namespace any pod that is labelled app=my-service will have this policy
  • This is like the Kubernetes podSelector but has more flexible Calico selector syntax
  • types: ["Ingress"]
  • Specifies that the policy affects incoming traffic
  • "Egress" controls the traffic going outside the pod. You can also have both Ingress and Egress on the same array as well. If you want to specify a policy for both
  • ingress:
  • Here we define one rule with - action: Deny
  • Since there are no Allow rules after it, all the traffic that wants to enter pods matching app=my-service will be dropped.

Testing the Policy

Let us now test the policy

kubectl apply -f deny-inbound.yaml
Enter fullscreen mode Exit fullscreen mode

and

kubectl get networkpolicy -n app-namespace
Enter fullscreen mode Exit fullscreen mode

you can see the deny-inbound listed with other active network policies

Using kubectl exec

creating a test pod in the namespace

kubectl run test-client --image=busybox --restart=Never -it
Enter fullscreen mode Exit fullscreen mode

Attempting a connection to the pod labelled app=my-service

# For example, if the "my-service" pod is named my-service-pod:
kubectl exec -it test-client -- wget -qO- http://<my-service-pod-ip> 
Enter fullscreen mode Exit fullscreen mode

and because of the policy, you can see either the connection fail or hand thus showing that the ingress is denied.

Consistent labeling

Making sure that your labels match exactly app: my-service in the pod spec and that.


Practical Tips and Next Steps

  • Layer Additional Policies: In real world scenarios, you might need to add multiple network policies-some allow specific ports or certain traffic form pods or specific IP addresses
  • Monitor Pod Logs: If the pods have logs or metrics about incoming and outgoing requests, you can verify that the policies that you have set are working properly

Advanced Policy Features

1.Global Network Policies

A GlobalNetworkPolicy is a Calico resource that applies to all namespaces and host endpoints within a Kubernetes cluster. As compared to NetworkPolicy that is namespaced.

Here are some of the use-cases

  • Enforcing a cluster-wide default deny
  • Applying audit/logging rules across namespaces for compliance reasons

Example: A Global policy for Auditing

apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: audit-global-policy
spec:
  selector: all()
  types:
    - Ingress
  ingress:
    - action: Log
Enter fullscreen mode Exit fullscreen mode

Key Points

  • selector: all()
  • The selector all() means that it will target all the resources in the cluster including all pods, host endpoints etc
  • types ["Ingress"]
  • Ingress means it focuses in incoming traffic
  • action:log
  • This just logs all the traffic

Advanced Label Matching

Calico Allows you to combine multiple selector types like selector, namespaceselector and serviceAccountSelector

This helps you to control multi tenant clusters or large scale environments

Example: namespaceSelector + Standard Selector

here is a sample code illustrating how you might allow traffic only for pods labelled role=frontend

apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: allow-prod-frontend
  namespace: my-namespace
spec:
  selector: role == 'frontend'
  namespaceSelector: environment == 'production'
  types:
    - Ingress
  ingress:
    - action: Allow
      # Additional rule details...

Enter fullscreen mode Exit fullscreen mode

Ingress vs Ingress Nuances

Ingress vs Engress in Calico

  • Ingress rules defined what traffic is allowed in the pods/endpoints
  • Engress rules define what traffic goes out of the pods/endpoints

Because modern applications often rely on the external services, egress policies are essential to prevent pods from making unauthorized connections

Example Scenario app=web Pods Restricted Egress

Here we are considering an example, we want to allow pods labelled app=web to only egress to an external IP range hosting an API 10.0.0.23/24 while blocking all other external egress.

apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: restrict-web-egress
  namespace: prod-apps
spec:
  selector: app == "web"
  types:
    - Egress
  egress:
    # Deny everything first
    - action: Deny
    # Then allow traffic to the known IP block
    - action: Allow
      destination:
        nets:
          - 10.0.10.0/24
Enter fullscreen mode Exit fullscreen mode

Applying Actions: Allow, Deny, Pass,Log

Calico supports for main terminal actions in policy. these are

  • Allow: Permits traffic to flow and halts further evaluation of subsequent rules.
  • Deny: Drops the traffic immediately and halts further evaluation of subsequent rules
  • Log: Logs the traffic but continues to evaluate additional rules if present. If this is used as a part of a rule then traffic matching that rule is logged
  • Pass: This rule is dedicated to calico, when encountered, it just skips ant calico policies and evaluates the profile attached to workload. The Pass rules defers the final decision making to profile level rules.

Top comments (1)

Collapse
 
alakkadshaw profile image
alakkadshaw

Thank yoou for reading. I hope you like the article