Enforcing Network Policies
If you are using KIND based environment, you would have to recreate the cluster to support network policies as the kind-net
does not support this feature. Use the following instructions to recreate the cluster with calico
and then proceed to create the network policies.
Recreate KIND Cluster with Calico
For Network Policies to work, you need to have a CNI Plugin which supports it. kind-net
which is installed as a defulay CNI with KIND, does not support it. You could recreate the cluster with calico
as a CNI plugin by following the instructions here.
Delete existing cluster created with KIND as,
kind get clusters
kind delete cluster --name kind
assuming kind
is the name of the cluster. Change it with the actual name.
File : k8s-code/helper/kind/kind-three-node-cluster.yaml
disable the default network as
# three node (two workers) cluster config
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
disableDefaultCNI: true
podSubnet: 192.168.31.0/16
launch the cluster again as
cd k8s-code/helper/kind
kind create cluster --config kind-three-node-cluster.yaml
validate
kubectl get nodes
the nodes would be in NotReady stat at this time because of no CNI (Network) Plugin.
Set up calico as
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/tigera-operator.yaml
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/custom-resources.yaml
wait for calico pods to be ready
watch kubectl get pods -l k8s-app=calico-node -A
once the pods for calico are setup, exit from this watch command (use ^c) and validate the node status again as:
kubectl get nodes
kubectl get pods -A
At this time, nodes should be up and running. That
You may proceed to create any deployments, services needed at this time.
Recreating the Application Deployment
kubectl create namespace instavote
kubectl config set-context --current --namespace=instavote
validate you are switched to instavote
namespace as
kubectl config get-contexts
assuming you have access to all the code to create instavote
stack, apply it using command similar to follows
kubectl apply -f vote-svc.yaml -f vote-deploy.yaml \
-f redis-svc.yaml -f redis-deploy.yaml \
-f db-svc.yaml -f db-deploy.yaml \
-f worker-deploy.yaml -f results-svc.yaml \
-f results-deploy.yaml
validate you have 5 deployments and 4 services as
kubectl get deploy,svc
[sample output]
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/db 1/1 1 1 45m
deployment.apps/redis 1/1 1 1 45m
deployment.apps/result 1/1 1 1 45m
deployment.apps/vote 1/1 1 1 45m
deployment.apps/worker 1/1 1 1 45m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/db ClusterIP 10.96.180.62 <none> 5432/TCP 45m
service/redis ClusterIP 10.96.197.185 <none> 6379/TCP 45m
service/result NodePort 10.96.61.34 <none> 80:30100/TCP 45m
service/vote NodePort 10.96.242.67 <none> 80:30000/TCP 45m
Locking down access with a NetworkPolicy
Now, define a restrictive network policy which would,
- Block all incoming connections
- Block all outgoing connections
+-----------------------------------------------------------+
| |
| +----------+ +-----------+ |
x | | results | | db | |
| | | | | |
| +----------+ +-----------+ |
| |
| |
| +----+----+--+ |
| | worker | |
| | | |
| +----+-------+ |
| |
| |
| +----------+ +-----------+ |
| | vote | | redis | |
x | | | | | |
| +----------+ +-----------+ |
| |
+-----------------------------------------------------------+
file: instavote-netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default
namespace: instavote
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
apply
kubectl get netpol
kubectl apply -f instavote-netpol.yaml
kubectl get netpol
kubectl describe netpol/default
Try accessing the vote and results ui. Can you access it ?
Troubleshooting Tip
: If you do not see the above policy being in effect (i.e. if you can still access the applications), go back and check if you have applied the label to the namespace as mentioned in the beginning of this section.
Enabling external traffic to outward facing applications
+-----------------------------------------------------------+
| |
| +----------+ +-----------+ |
=====> | results | | db | |
| | | | | |
| +----------+ +-----------+ |
| |
| |
| +----+----+--+ |
| | worker | |
| | | |
| +----+-------+ |
| |
| |
| +----------+ +-----------+ |
| | vote | | redis | |
=====> | | | | |
| +----------+ +-----------+ |
| |
+-----------------------------------------------------------+
To the same file, add a new network policy object.
file: instavote-netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default
namespace: instavote
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: public-ingress
namespace: instavote
spec:
podSelector:
matchExpressions:
- {key: role, operator: In, values: [vote, result]}
policyTypes:
- Ingress
ingress:
- {}
where,
instavote-ingress is a new network policy which,
- defines policy for pods with vote and results role
- and allows them incoming access from anywhere
apply
kubectl apply -f instavote-netpol.yaml
Exercise
- Try accessing the ui now and check if you are able to.
- Try to vote, see if that works? Why ?
Enabling communication between pods in the same namespace
When you tried to vote, you might have observed that it does not work. Thats because the default network policy we created earlier blocks all outgoing traffic. Which is good for securing the environment, however you still need to provide inter connection between services from the same project. Specifically vote, worker and results apps need outgoing connection to redis and db. Lets allow that with a egress policy.
+-----------------------------------------------------------+
| |
| +------------+ +-----------+ |
=====> | results | ------>| db | |
| | | | | <-------+ |
| +------------+ +-----------+ | |
| | |
| | |
| +----+----+---+ |
| | worker | |
| | | |
| +----+--------+ |
| | |
| | |
| +----------+ +-----------+ | |
| | vote | | redis | <-------+ |
=====> | | ------> | | |
| +----------+ +-----------+ |
| |
+-----------------------------------------------------------+
Edit the same policy file and add the following snippet,
file: instavote-netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default
namespace: instavote
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector: {} # Allows all pods within the same namespace
egress:
- to:
- podSelector: {} # Allows all pods within the same namespace
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: public-ingress
namespace: instavote
spec:
podSelector:
matchExpressions:
- {key: role, operator: In, values: [vote, result]}
policyTypes:
- Ingress
ingress:
- {}
where,
instavote-egress is a new network policy which,
- defines policy for pods with vote, worker and results role
- and allows them outgoing access to any pods in the same namespace, and that includes redis and db
Troubleshooting Exercise
Applying the above policy has no effect on the communication between vote and redis applications. You could validate this by loading the vote app and submit a vote. It should not work. There is a problem in the network policy file above. Analyse the policies, compare them against the kubernetes api reference document, understand how its being applied and see if you could fix this problem. Your task is to ensure that vote and redis apps are communicating with one another.
Solution
The reason why communication between vote
and redis
is broken is because of the name resoulution (DNS Based Service Discovery) is broken. This is because the network policies that you have set up do not allow the services in instavote
namespace to communicate to even the DNS server in the cluster running in kube-system
namespace.
You could allow this by adding one more policiy. You could add it to the same file instavote-netpol.yaml
e.g.
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dns-access
namespace: instavote
spec:
podSelector: {} # Applies to all pods in the namespace
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
now apply and validate
kubectl apply -f instavote-netpol.yaml
Now you should see vote
app connecting with redis
.
Nano Project
The above network policies are a good start. However you could even further restrict access by creating a granular network policy for each application.
Create network policies with following specs,
vote
- allow incoming connections from anywhere, only on port 80
- allow outgoing connections to redis
- block everything else, incoming and outgoing
redis
- allow incoming connections from vote and worker, only on port 6379
- block everything else, incoming and outgoing
worker
- allow outgoing connections to redis and db
- block everything else, incoming and outgoing
db
- allow incoming connections from worker and results, only on port 5342
- block everything else, incoming and outgoing
result
- allow incoming connections from anywhere, only on port 80
- allow outgoing connections to db
- block everything else, incoming and outgoing