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: []
-
podSelector
targets the pods labelledapp=backend
-
policyTypes: ["Ingress"]
tells the Kubernetes, this policy will apply only to the inbound traffic - An empty
ingress
listingress:[]
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
Here is what we are doing
-
selective:role == 'frontend'
replaces theprodSelector
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 labelledrole=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.
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]
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
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
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
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 resourcescustom-resources.yml
from project calicos official release:
Apply the Manifests
kubectl apply -f tigera-oprator.yaml
kubectl apply -f custom-resources.yaml
- 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
- Add Calico Helm repository:
helm repo add projectcalico https://projectcalico.docs.tigera.io/charts
- Install the chart
helm install calico projectcalico/tigera-operator --version <CHART_VERSION>
- 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
- Verify the installation
- check the operator and Calico pods are up and running in the relevant namespaces
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
- List pods in the relevant namespaces
kubectl get pods -n calico-system
or
kubectl get pods -n tigera-operator
The exact namespace depends on the method used. In many setups you can find calico-node
, calico-cube-controllers
or tigers-operator
pods.
- Detailed Pod Status
kubectl describe pod <pod-name> -n calico-system
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
- Check node configurations
kubectl get nodes -o wide
You can see the Calico-specific network details in the node' internal address or in the annotation that reference the chi.projectcalico.org
- Inspect a test pod
kubectl run testpod --image=busybox --restart=Never -it
kubectl describe pod testpod
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.
- 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
- Check the connectivity between the
test1
andtest2
kubectl exec -n dev test1 -- ping -c 3 test2.prod.svc.cluster.local
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
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
and
kubectl get networkpolicy -n app-namespace
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
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>
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
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...
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
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)
Thank yoou for reading. I hope you like the article