Going Beyond `NetworkPolicy` with Calico
The Kubernetes NetworkPolicy API allows users to express ingress policy to Kubernetes pods based on labels and ports. Calico implements this API, but also supports a number of policy features which are not currently expressble through the NetworkPolicy API such as CIDR and egress policy.
This guide walks through using the Calico APIs directly in conjunction with Kubernetes NetworkPolicy in order to define more complex network policies.
Requirements
- This guide assumes you have a working Kubernetes cluster with Calico for policy. (See: installation for help)
- This guide assumes that your pods have connectivity to the public internet.
- This guide assumes you are familiar with Kubernetes NetworkPolicy
- This guide assumes you are using etcdv2 (or v3) as the Calico backend datastore.
- You must have configured kubectl access to the cluster.
- You must have installed and configured the calicoctl tool
Setup
Create the Namespace
We’ll use a new namespace for this guide. Run the following command to create it.
kubectl create ns advanced-policy-demo
And then enable isolation on the Namespace.
kubectl annotate ns advanced-policy-demo "net.beta.kubernetes.io/network-policy={\"ingress\":{\"isolation\":\"DefaultDeny\"}}"
Run an nginx Service
We’ll run an nginx Service in the Namespace.
kubectl run --namespace=advanced-policy-demo nginx --replicas=2 --image=nginx
kubectl expose --namespace=advanced-policy-demo deployment nginx --port=80
Check using calicoctl
Now that we’ve created a Namespace and a set of pods, we should see those objects show up in the
Calico API using calicoctl
.
We can see that the Namespace has a corresponding Profile.
$ calicoctl get profile -o wide
NAME TAGS
k8s_ns.advanced-policy-demo k8s_ns.advanced-policy-demo
k8s_ns.default k8s_ns.default
k8s_ns.kube-system k8s_ns.kube-system
Because we’ve enabled isolation on the Namespace, the profile denies all ingress traffic and allows all egress traffic.
$ calicoctl get profile k8s_ns.advanced-policy-demo -o yaml
- apiVersion: v1
kind: profile
metadata:
name: k8s_ns.advanced-policy-demo
tags:
- k8s_ns.advanced-policy-demo
spec:
egress:
- action: allow
destination: {}
source: {}
ingress:
- action: deny
destination: {}
source: {}
We can see that this is the case by running another pod in the Namespace and attempting to access the nginx Service.
$ kubectl run --namespace=advanced-policy-demo access --rm -ti --image busybox /bin/sh
Waiting for pod advanced-policy-demo/access-472357175-y0m47 to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.
/ # wget -q --timeout=5 nginx -O -
wget: download timed out
/ #
We can also see that the two nginx pods are represented as WorkloadEndpoints in the Calico API.
calicoctl get workloadendpoint
NODE ORCHESTRATOR WORKLOAD NAME
k8s-node-01 k8s advanced-policy-demo.nginx-701339712-x1uqe eth0
k8s-node-02 k8s advanced-policy-demo.nginx-701339712-xeeay eth0
k8s-node-01 k8s kube-system.kube-dns-v19-mjd8x eth0
Taking a closer look, we can see that they reference the correct profile for the Namespace,
and that the correct label information has been filled in. Notice that the endpoint also
includes a special label calico/k8s_ns
, which is automatically populated with the
pod’s Kubernetes Namespace.
$ calicoctl get wep --workload advanced-policy-demo.nginx-701339712-x1uqe -o yaml
- apiVersion: v1
kind: workloadEndpoint
metadata:
labels:
calico/k8s_ns: advanced-policy-demo
pod-template-hash: "701339712"
run: nginx
name: eth0
node: k8s-node-01
orchestrator: k8s
workload: advanced-policy-demo.nginx-701339712-x1uqe
spec:
interfaceName: cali347609b8bd7
ipNetworks:
- 192.168.44.65/32
mac: 56:b5:54:be:b2:a2
profiles:
- k8s_ns.advanced-policy-demo
Define Kubernetes policy
We’ll define some network policy through the Kubernetes API. Run the following to create a NetworkPolicy which allows traffic to nginx pods from any pods in the advanced-policy-demo Namespace.
kubectl create -f - <<EOF
kind: NetworkPolicy
apiVersion: extensions/v1beta1
metadata:
name: access-nginx
namespace: advanced-policy-demo
spec:
podSelector:
matchLabels:
run: nginx
ingress:
- from:
- podSelector:
matchLabels: {}
EOF
It now shows up as a Policy object in the Calico API.
$ calicoctl get policy -o wide
NAME ORDER SELECTOR
advanced-policy-demo.access-nginx 1000 calico/k8s_ns == 'advanced-policy-demo' && run == 'nginx'
k8s-policy-no-match 2000 has(calico/k8s_ns)
NOTE
The
k8s-policy-no-match
policy is used to send all unmatched traffic to the corresponding per-namespace Profile. This policy is created automatically by the calico/kube-policy-controller.
After creating the policy, we can now access the nginx Service. We also see that the pod can access google.com on the public internet. This is because we have not defined any egress policy.
$ kubectl run --namespace=advanced-policy-demo access --rm -ti --image busybox /bin/sh
Waiting for pod advanced-policy-demo/access-472357175-y0m47 to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.
/ # wget -q --timeout=5 nginx -O -
...
/ # ping google.com
PING google.com (216.58.219.206): 56 data bytes
64 bytes from 216.58.219.206: seq=0 ttl=61 time=14.365 ms
Prevent outgoing connections from pods
Kubernetes NetworkPolicy does not provide a way to prevent outgoing connections from pods. However,
Calico does. In this section we’ll create a Policy using calicoctl
which prevents all outgoing
connections from Kubernetes pods in the advanced-policy-demo Namespace.
To do this, we’ll need to create a Policy which selects all pods in the Namespace, and denies traffic that doesn’t match another Pod in the Namespace.
calicoctl apply -f - <<EOF
apiVersion: v1
kind: policy
metadata:
name: advanced-policy-demo.deny-egress
spec:
selector: calico/k8s_ns == 'advanced-policy-demo'
order: 500
egress:
- action: deny
destination:
notSelector: calico/k8s_ns == 'advanced-policy-demo'
EOF
Notice that we’ve specified an order of 500. This means that this policy will be applied before any of the Kubernetes policies.
We also need to create a policy which allows traffic to access kube-dns. Let’s create a policy which applies to the entire cluster, and allows it to access kube-dns pods in the kube-system Namespace. We’ll specify an order of 400 so that it takes precendent over other policies.
calicoctl apply -f - <<EOF
apiVersion: v1
kind: policy
metadata:
name: advanced-policy-demo.allow-dns
spec:
selector: has(calico/k8s_ns)
order: 400
egress:
- action: allow
protocol: udp
destination:
selector: calico/k8s_ns == 'kube-system' && k8s-app == 'kube-dns'
ports: [53]
EOF
We should see now that traffic still works for pods within the Namespace, but we can no longer access the public internet.
$ kubectl run --namespace=advanced-policy-demo access --rm -ti --image busybox /bin/sh
Waiting for pod advanced-policy-demo/access-472357175-y0m47 to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.
/ # wget -q --timeout=5 nginx -O -
...
/ # ping google.com
PING google.com (216.58.219.206): 56 data bytes
Teardown
You can clean up after this guide by deleteing the advanced policy demo Namespace.
kubectl delete ns advanced-policy-demo
You will also need to delete the Calico policies that were created.
calicoctl delete policy advanced-policy-demo.deny-egress
calicoctl delete policy advanced-policy-demo.allow-dns