Kubernetes Network Policy objects allow for fine-grained network policy enforcement, ensuring that traffic within your Kubernetes cluster can only flow in the direction that you specify. As an example, if we take a scenario where Kubernetes namespaces are used to enforce boundaries between products, or even enforce boundaries between different environments (e.g. development vs production), network policies can be configured to ensure no unauthorized network traffic is allowed beyond its boundary. Think of it as being similar to applying Security Groups in the AWS world.
Network policies are implemented by an add-on; there are several available. Any of these solutions allow you to specify a network policy and then enforce the rules while your services are running.
Calico is an open-source plugin that implements pod networking and Network Policy.
Weave Net is an open-source plugin that implements pod networking and Network Policy.
If you’re using Weave Cloud you can visualize your container networks and services all from a single dashboard and also configure monitoring to alert you about any suspicious network activity.
You must install one of the above Network Policy add-ons before continuing. Then we will configure Kubernetes Network Policies.
We will create a namespace, deploy some test pods into it, and see the before and after effects of configuring a Network Policy, which will be enforced by the add-on you just installed.
All configuration files for this chapter are in the network-policies
directory. Make sure you change to that directory before giving any commands in this chapter.
-
Create a namespace:
$ kubectl create ns ns-1 namespace "ns-1" created
-
Deploy a container into namespace
ns-1
that will expose an http endpoint, and log all requests it receives. First, create a Deployment, ReplicaSet and Pod using the command:$ kubectl run --namespace=ns-1 http-echo --image=solsson/http-echo --env="PORT=80" --port=80 deployment "http-echo" created
-
Label the pod (we will use labels as part of defining network policies):
$ kubectl label po --selector=run=http-echo --namespace=ns-1 app=http-echo pod "http-echo-1790350443-z2v7n" labeled
-
Create a Service to expose the pod:
$ kubectl expose --namespace=ns-1 deployment http-echo --port=80 service "http-echo" exposed
Monitor the logs of the deployed container by querying the name of the pod defined with the label
run=http-echo
, then passing it to thekubectl logs
command:kubectl get po \ --selector=run=http-echo \ --namespace=ns-1 \ -o jsonpath='{.items[*].metadata.name}' | \ xargs kubectl logs -f --namespace=ns-1
This command will sit silently until
http-echo
logs a request.Let’s say this is
shell 1
. -
In another shell, say
shell 2
, deploy a second container:$ kubectl run \ --namespace=ns-1 \ -i --tty \ busybox \ --image=busybox \ --restart=Never \ -- sh If you don't see a command prompt, try pressing enter. / #
-
We will now attempt to call the
http-echo
pod from ourbusybox
pod by performing an HTTP POST . As we have no network policies in place, we should see the following command return successfully with a 200 response, along with a log message being output in thehttp-echo
shell window:/ # wget -S http://http-echo.ns-1.svc.cluster.local/test --post-data '{"message":"hello"}' -O test Connecting to http-echo.ns-1.svc.cluster.local (100.71.77.153:80) HTTP/1.1 200 OK X-Powered-By: Express Content-Type: application/json; charset=utf-8 Content-Length: 533 ETag: W/"215-KyoPN1JoGjQlzW9TxpIay22VPF8" Date: Thu, 26 Oct 2017 00:53:21 GMT Connection: close test 100% |*************************************************************************************************| 533 0:00:00 ETA
HTTP POST request succeeds.
Let’s now create a Network Policy, but we will not configure any rules which by default will deny all traffic within the namespace. Leaving the 2 shells open from the previous steps, run the following in another shell, say shell 3
:
$ kubectl create -f templates/deny-all-by-default-network-policy.yaml --namespace=ns-1 networkpolicy "deny-all-by-default" created
When running the following command again in shell 2, we should see it eventually timeout and fail (note that rather than waiting for it to time out, you can press Ctrl
+ C
to quit after about 10 seconds once satisfied that no response will be returned):
/ # wget -S http://http-echo.ns-1.svc.cluster.local/test --post-data '{"message":"hello"}' -O test
Connecting to http-echo.ns-1.svc.cluster.local (100.64.161.56:80)
wget: can't connect to remote host (100.64.61.223): Connection timed out
We will now delete the NetworkPolicy that we just created, and create a new NetworkPolicy with a rule defined. If you cat templates/allow-network-policy.yaml
you will see the following rule defined:
spec: podSelector: matchLabels: app: http-echo ingress: - from: - podSelector: matchLabels: app: busybox
The rule above is stating that for every pod that has the label app: http-echo
defined, allow access to it from pods that have the label app: busybox
defined.
Run the following in shell 3
to remove the deny all by default rule, and replace with the above allow rule:
$ kubectl delete netpol deny-all-by-default --namespace=ns-1 networkpolicy "deny-all-by-default" deleted $ kubectl create -f templates/allow-network-policy.yaml --namespace=ns-1 networkpolicy "allow" created
If we repeat the following command in shell 2
, the call should still timeout and fail (again, you can press CTRL-C to quit after 10 seconds rather than waiting for the full timeout to occur):
/ # wget -S http://http-echo.ns-1.svc.cluster.local/test --post-data '{"message":"hello"}' -O test
Connecting to http-echo.ns-1.svc.cluster.local (100.64.161.56:80)
wget: can't connect to remote host (100.64.61.223): Connection timed out
Why is this still failing even after creating a rule? It is failing because we configured the rule so that only pods with the label app: busybox
are authorized to call pods with the label app: http-echo
. Let’s go ahead and label our busybox
pod on shell 3
:
/ # kubectl label po --selector=run=busybox --namespace=ns-1 app=busybox pod "busybox" labeled
Repeating the test in shell 2
again should now be successful:
/ # wget -S http://http-echo.ns-1.svc.cluster.local/test --post-data '{"message":"hello"}' -O test
Connecting to http-echo.ns-1.svc.cluster.local (100.64.161.56:80)
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 536
ETag: W/"218-xgvU8WZSN+2SEyOX6Q2R/AhLuRM"
Date: Thu, 26 Oct 2017 02:15:32 GMT
Connection: close
test 100% |*************************************************************************************************| 534 0:00:00 ETA
Remove all the resources and the namespace using the command:
$ kubectl delete ns ns-1 namespace "ns-1" deleted
You are now ready to continue on with the workshop!