Calico
Calico network policy extends Kubernetes network policy by adding features such as namespace selectors and traffic logging.
The examples below demonstrate the usage of Calico network policies.
How to allow / deny traffic from other namespace ?
Let's verify the network connectivity between podinfo
and podtato-head
pods by testing traffic flow in both directions. First, let's check if podinfo
can reach podtato-head
:
kubectl exec -it deployments/podinfo -c netshoot -- /bin/zsh
curl podtato-head-entry.podtato:9000
The output shows:
<html>
<head>
<title>Hello Podtato!</title>
<link rel="stylesheet" href="./assets/css/styles.css"/>
<link rel="stylesheet" href="./assets/css/custom.css"/>
</head>
<body style="background-color: #849abd;color: #faebd7;">
<main class="container">
<div class="text-center">
<h1>Hello from <i>pod</i>tato head!</h1>
<div style="width:700px; height:800px; margin:auto; position:relative;">
<img src="./assets/images/body/body.svg" style="position:absolute;margin-top:80px;margin-left:200px;">
<img src="./parts/hat/hat.svg" style="position:absolute;margin-left:200px;margin-top:0px;">
<img src="./parts/left-arm/left-arm.svg" style="position:absolute;top:100px;left:-50px;">
<img src="./parts/right-arm/right-arm.svg" style="position:absolute;top:100px;left:450px;">
<img src="./parts/left-leg/left-leg.svg" style="position:absolute;top:480px;left: -0px;" >
<img src="./parts/right-leg/right-leg.svg" style="position:absolute;top:480px;left: 400px;">
</div>
<h2> Version v0.1.0 </h2>
</div>
</main>
</body>
</html>
Then let's verify the traffic flow in the opposite direction, from podtato-head
to podinfo
:
kubectl -n podtato exec -it deployments/podtato-head-entry -c netshoot -- /bin/zsh
curl podinfo.default:9898
The output shows:
{
"hostname": "podinfo-7f9d98d56d-c8zhf",
"version": "6.7.1",
"revision": "6b7aab8a10d6ee8b895b0a5048f4ab0966ed29ff",
"color": "#34577c",
"logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif",
"message": "greetings from podinfo v6.7.1",
"goos": "linux",
"goarch": "arm64",
"runtime": "go1.23.2",
"num_goroutine": "8",
"num_cpu": "8"
}#
Now it's time to create a Calico NetworkPolicy that allows ingress traffic from the podinfo
pod in the default namespace while blocking all egress traffic from podtato-head
:
cat <<EOF | kubectl apply -f -
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
name: allow-from-default-ns
namespace: podtato
spec:
selector: component == 'podtato-head-entry'
types:
- Ingress
- Egress
ingress:
- action: Allow
protocol: TCP
source:
selector: app == 'podinfo'
namespaceSelector: kubernetes.io/metadata.name == 'default'
destination:
ports:
- 9000
egress: []
EOF
Now let's verify the created NetworkPolicy
:
kubectl get networkpolicies.projectcalico.org -A
Possible output can be as below:
NAMESPACE NAME CREATED AT
podtato default.allow-from-default-ns 2025-03-15T21:50:07Z
Let's verify that traffic is allowed from podinfo
to podtato-head
by sending a request from the podinfo
pod:
kubectl exec -it deployments/podinfo -c netshoot -- /bin/zsh
curl podtato-head-entry.podtato:9000
The output shows:
<html>
<head>
<title>Hello Podtato!</title>
<link rel="stylesheet" href="./assets/css/styles.css"/>
<link rel="stylesheet" href="./assets/css/custom.css"/>
</head>
<body style="background-color: #849abd;color: #faebd7;">
<main class="container">
<div class="text-center">
<h1>Hello from <i>pod</i>tato head!</h1>
<div style="width:700px; height:800px; margin:auto; position:relative;">
<img src="./assets/images/body/body.svg" style="position:absolute;margin-top:80px;margin-left:200px;">
<img src="./parts/hat/hat.svg" style="position:absolute;margin-left:200px;margin-top:0px;">
<img src="./parts/left-arm/left-arm.svg" style="position:absolute;top:100px;left:-50px;">
<img src="./parts/right-arm/right-arm.svg" style="position:absolute;top:100px;left:450px;">
<img src="./parts/left-leg/left-leg.svg" style="position:absolute;top:480px;left: -0px;" >
<img src="./parts/right-leg/right-leg.svg" style="position:absolute;top:480px;left: 400px;">
</div>
<h2> Version v0.1.0 </h2>
</div>
</main>
</body>
</html>#
Next let's verify that traffic is blocked from podtato-head
to podinfo
by attempting to send a request from the podtato-head
pod:
kubectl -n podtato exec -it deployments/podtato-head-entry -c netshoot -- /bin/zsh
curl podinfo.default:9898 --connect-timeout 10
The output shows that the connection timed out, indicating that the network policy is successfully blocking traffic from the podtato-head
pod to podinfo
:
curl: (28) Resolving timed out after 10003 milliseconds
How to allow / deny traffic from specific IP range ?
Let's get the IP addresses of our pods to use in the network policy. First, let's get the podtato-head pod IP:
kubectl get pods -n podtato -o json | jq -r '.items[] | select(.metadata.name | startswith("podtato-head-entry")) | .status.podIP'
192.168.41.196
kubectl get pods -n default -o json | jq -r '.items[] | select(.metadata.name | startswith("podinfo")) | .status.podIP'
192.168.41.194
192.168.209.137
As a next step let's create a Calico NetworkPolicy that allows traffic from specific podinfo
pod IPs to the podtato-head
pod:
kubectl -n podtato delete networkpolicies.projectcalico.org default.allow-from-default-ns
cat <<EOF | kubectl apply -f -
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
name: allow-from-default-pod-ip
namespace: podtato
spec:
selector: component == 'podtato-head-entry'
types:
- Ingress
- Egress
ingress:
- action: Allow
protocol: TCP
source:
nets:
- 192.168.41.194/32
- 192.168.209.137/32
egress: []
EOF
After that verify the network policies that are currently active in our cluster:
kubectl get networkpolicies.projectcalico.org -A
Output:
NAMESPACE NAME CREATED AT
podtato default.allow-from-default-pod-ip 2025-03-15T22:04:17Z
Verify that traffic is allowed to flow from the podinfo
pods to the podtato-head
service by executing a curl command from one of the podinfo
pods:
kubectl exec -it deployments/podinfo -c netshoot -- /bin/zsh
curl podtato-head-entry.podtato:9000
The output shows:
<html>
<head>
<title>Hello Podtato!</title>
<link rel="stylesheet" href="./assets/css/styles.css"/>
<link rel="stylesheet" href="./assets/css/custom.css"/>
</head>
<body style="background-color: #849abd;color: #faebd7;">
<main class="container">
<div class="text-center">
<h1>Hello from <i>pod</i>tato head!</h1>
<div style="width:700px; height:800px; margin:auto; position:relative;">
<img src="./assets/images/body/body.svg" style="position:absolute;margin-top:80px;margin-left:200px;">
<img src="./parts/hat/hat.svg" style="position:absolute;margin-left:200px;margin-top:0px;">
<img src="./parts/left-arm/left-arm.svg" style="position:absolute;top:100px;left:-50px;">
<img src="./parts/right-arm/right-arm.svg" style="position:absolute;top:100px;left:450px;">
<img src="./parts/left-leg/left-leg.svg" style="position:absolute;top:480px;left: -0px;" >
<img src="./parts/right-leg/right-leg.svg" style="position:absolute;top:480px;left: 400px;">
</div>
<h2> Version v0.1.0 </h2>
</div>
</main>
</body>
</html>#
Now let's verify traffic flow in the opposite direction - from podtato-head
to podinfo
:
kubectl -n podtato exec -it deployments/podtato-head-entry -c netshoot -- /bin/zsh
curl podinfo.default:9898 --connect-timeout 10
The output shows that the connection timed out:
curl: (28) Resolving timed out after 10002 milliseconds
How to generate logs for specific traffic ?
Let's define a network policy that logs and denies traffic from podinfo to podtato-head:
kubectl -n podtato delete networkpolicies.projectcalico.org default.allow-from-default-pod-ip
cat <<EOF | kubectl apply -f -
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
name: log-and-deny-ingress
namespace: podtato
spec:
selector: component == 'podtato-head-entry'
types:
- Ingress
- Egress
ingress:
- action: Log
protocol: TCP
source:
selector: app == 'podinfo'
egress: []
EOF
Generate traffic from podinfo
to podtato-head
to trigger the logging policy
kubectl exec -it deployments/podinfo -c netshoot -- /bin/zsh
curl podtato-head-entry.podtato:9000
Check the logs from the Calico node pod to see the logged traffic:
kubectl logs -n calico-system calico-node-kh2jg | grep log-and-deny-ingress
The output shows the following:
Defaulted container "calico-node" out of: calico-node, flexvol-driver (init), install-cni (init)
2025-03-15 22:10:52.248 [INFO][89] felix/label_inheritance_index.go 185: Updating selector id=Policy(tier=default, name=podtato/default.log-and-deny-ingress) selector=(component == "podtato-head-entry" && projectcalico.org/namespace == "podtato")
2025-03-15 22:10:52.250 [INFO][89] felix/int_dataplane.go 2041: Received *proto.ActivePolicyUpdate update from calculation graph msg=id:<tier:"default" name:"podtato/default.log-and-deny-ingress" > policy:<namespace:"podtato" inbound_rules:<action:"log" protocol:<name:"tcp" > src_ip_set_ids:"s:pj5ATU1IVi7BRY37dKv1j9dhGgIqdfFkIpMDIQ" original_src_selector:"app == 'podinfo'" rule_id:"Vpf0HAInm19ya0fI" > original_selector:"(component == \"podtato-head-entry\" && projectcalico.org/namespace == \"podtato\")" >
2025-03-15 22:10:52.251 [INFO][89] felix/int_dataplane.go 2041: Received *proto.WorkloadEndpointUpdate update from calculation graph msg=id:<orchestrator_id:"k8s" workload_id:"podtato/podtato-head-entry-68f945f584-hdkfj" endpoint_id:"eth0" > endpoint:<state:"active" name:"cali19d64cc69db" profile_ids:"kns.podtato" profile_ids:"ksa.podtato.default" ipv4_nets:"192.168.41.196/32" tiers:<name:"default" ingress_policies:"podtato/default.log-and-deny-ingress" egress_policies:"podtato/default.log-and-deny-ingress" default_action:"Deny" > >
Clean up by deleting the network policy:
kubectl -n podtato delete networkpolicies.projectcalico.org default.log-and-deny-ingress