Working with K8S Network Policy
When to use network policy?
👉 Control traffic flow at the IP and port level (OSI layer 3 or 4).
The quick way to get started with network policy is to use minikube with Calico plugin. See my previous post to configure minikube.
minikube start \
--network-plugin=cni \
--cni=calico
Network policies need a network plugin which supports Network Policy. Without a properly configured network plugin, the network policies applied to the cluster have no effects (not affect any traffics).
Verify calico plugin running
kubectl -n kube-system get po -l=k8s-app=calico-node
kubectl -n kube-system get po -l=k8s-app=calico-kube-controllers
The output should look like this
NAME READY STATUS RESTARTS AGE
calico-node-lk7sz 1/1 Running 0 3m7s
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-85578c44bf-lgzf5 1/1 Running 0 3m23s
Network Policy 101
Network Policy can control:
- Pods that are allowed to communicate with other pods
- Namespaces that are allowed (all pods in that namespace are allowed)
- IP range are allowed
Network Policy uses selector and CIDR range to evaluate policy rules.
- For pods and namespaces: selector is used for identifying the resources which the network policy will apply to.
- For IP range restrictions, CIDR range will be used.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress: {}
egress: {}
A sample network policy
Network Policy key notes
- Namespace scope: Network Policies are scoped to the namespace, which means it will affect the traffic of the pods in the namespace at which the policy is applied.
- Allow All: By default the pod allows all ingress and egress. It means it has no restrictions for both inbound and outbound traffic.
- No deny rules: Network policy only allows to define specific traffic to be allowed (the rest is denied).
- Empty selector: An empty selector means everything. For example, if
podSelector: {}
means all pods in the namespace in which the policy applied to. - 'OR' rule: If multiple policies are applied to a single pod, all the policies are OR’ed.
Network Policy resource definition explained
podSelector
Determines pods which the policy will be applied to. An empty podSelector: {}
means the policy will be applied to all pod in the namespace the network policy created (remember network policy is namespace scoped).
To select specific pods in the namespace use .podSelector.matchLabels
...
podSelector:
matchLabels:
# selects pods in the namespace with label "app=wordpress"
app: wordpress
...
policyTypes
Indicates the traffic flow direction. It can be Ingress
,Egress
, or both. If no policyTypes
are defined in a NetworkPolicy then by default Ingress
will always be set and Egress
will be set if the NetworkPolicy has any egress rules.
Rules: ingress
, egress
Each network policy has a rules section named ingress
and egress
based on the policy type mentioned in policyTypes
.
ingress
: contains ingress-allowed rules. It controls which pods/namespaces/CIDR are allowed (.ingress.from
) and which ports are allowed (.ingress.ports
)egress
: contains egress-allowed rules. It controls to which pods/namespaces/CIDR traffic are allowed at which ports.
Network Policy In Use
Suppose we have a redis db in namespace shared-db
, we would like to share redis db with some micro-services in namespace service
.
service | labels |
---|---|
ui | app=demo role=ui |
api | app=demo role=api |
Let's get started
kubectl create namespace shared-db
kubectl create namespace service
Run redis db in shared-db
namespace
kubectl -n shared-db run db \
--image=redis:4 \
--labels="app=demo,role=db" \
--expose --port=6379
Verify redis db running
kubectl -n shared-db get po,svc
The output should look like this
NAME READY STATUS RESTARTS AGE
pod/db 1/1 Running 0 25s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/db ClusterIP 10.106.74.138 <none> 6379/TCP 25s
Now let's define a network policy
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: redis-allow-services
namespace: shared-db
spec:
# select pods in the namespace with match labels
podSelector:
matchLabels:
app: demo
role: db
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: service
podSelector:
matchLabels:
app: demo
role: ui
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: service
podSelector:
matchLabels:
app: demo
role: api
Create network policy in namespace shared-db
.
kubectl apply -f redis-allow-services.yaml
Try it out
Pod with matched labels can connect to redis db in shared-db
namespace
kubectl -n service run test-$RANDOM \
--labels="app=demo,role=ui" \
--rm -it \
--image=alpine -- sh
The above command let us access to container shell where we can use netcat (nc
) command to verify connection to redis db
/ # nc -v -w2 db.shared-db 6379
db.shared-db (10.106.74.138:6379) open
Pods with labels not matched the network policy cannot connect!
kubectl -n service run test-$RANDOM \
--labels="app=other,role=ui" \
--rm -it \
--image=alpine -- sh
/ # nc -v -w2 db.shared-db 6379
nc: db.shared-db (10.106.74.138:6379): Operation timed out
Pods with matched labels but in wrong namespace cannot connect!
kubectl -n default run test-$RANDOM \
--labels="app=demo,role=ui" \
--rm -it \
--image=alpine -- sh
/ # nc -v -w 2 db.shared-db 6379
nc: db.shared-db (10.106.74.138:6379): Operation timed out