🏷️ Use Network security policies to restrict cluster level access#
Key Concepts
By default, all ingress traffic is blocked and egress traffic is allowed when a Network Policy is applied in a namespace.
Use podSelector to target specific pods and policyTypes: [Ingress, Egress] to define which traffic types are restricted.
Define ingress rules using from to allow traffic from specific pods, namespaces, or CIDR blocks.
Use egress rules to restrict outbound traffic to specific destinations.
Kubernetes Networking Basics
Pod Networking & CNI Plugins
Every pod in Kubernetes receives a unique, routable IP address, enabling direct communication across nodes. This is implemented via Container Network Interface (CNI) plugins, which handle IP assignment, interface setup, and routing.
Popular CNI plugins include:
Flannel: Simple overlay network using VXLAN.
Calico: Supports BGP routing and advanced network policies.
Cilium: Leverages eBPF for high-performance networking and deep security.
AWS VPC CNI: Assigns native VPC IPs to pods for low-latency communication.
Services: Stable Endpoints
Services abstract groups of pods using label selectors and provide stable access via:
ClusterIP: Internal cluster communication.
NodePort: Exposes service on a static port across all nodes.
LoadBalancer: Integrates with cloud providers for external access.
ExternalName: Maps to external DNS names.
Services rely on kube-proxy (running on each node) to maintain iptables/IPVS rules for traffic routing.
Ingress: External HTTP(S) Access
Ingress manages external HTTP/HTTPS traffic routing based on host or path rules. It requires an Ingress Controller (e.g., NGINX, Traefik) to function.
Example:
rules:
- host: app.example.com
http:
paths:
- path: /api
backend: api-service
- path: /
backend: frontend-service
It supports TLS termination and consolidates external access through a single entry point.
DNS & Service Discovery
Kubernetes uses CoreDNS to enable service discovery:
Services resolve to
<service>.<namespace>.svc.cluster.local.Pods resolve to
<pod-ip>.<namespace>.pod.cluster.local.
Pods use dnsPolicy (e.g., ClusterFirst, Default) to control DNS resolution behavior.
Network Policies: Security Enforcement
By default, all pods can communicate. Network Policies act as a firewall to restrict traffic using:
podSelectorandnamespaceSelectorIngress/egress rules
IP block restrictions
Enforced by CNI plugins like Calico or Cilium using iptables or eBPF.
Example: Allow only frontend pods to access backend.
ingress:
- from:
- podSelector:
matchLabels: app: frontend
Network Policies for Cluster Security#
Default Deny Strategy#
Best Practice: Start with a default deny-all policy to enforce zero-trust networking.
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-all namespace: default spec: podSelector: {} policyTypes: - Ingress - Egress
This blocks all traffic unless explicitly allowed, aligning with the principle of least privilege.
Ingress and Egress Control#
Ingress Example: Allow traffic only from frontend pods to backend services.
spec: podSelector: matchLabels: app: backend ingress: - from: - podSelector: matchLabels: app: frontend ports: - protocol: TCP port: 8080
Egress Example: Restrict outbound traffic to specific CIDRs (e.g., payment APIs).
egress: - to: - ipBlock: cidr: 35.190.247.0/24 ports: - protocol: TCP port: 443
DNS and Metadata Protection#
Critical Step: Always allow DNS egress (port 53) to
kube-system/kube-dnsafter applying egress policies.egress: - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system podSelector: matchLabels: k8s-app: kube-dns ports: - port: 53 protocol: UDP
Cloud Metadata: Block access to
169.254.169.254/32(AWS/Azure/GCP metadata endpoint) unless required.
Advanced: Cilium for L7 Policies#
Use CiliumNetworkPolicy for HTTP-level controls (e.g., allow only
GET /api).apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy spec: endpointSelector: matchLabels: app: hello-world ingress: - toPorts: - ports: - port: "80" protocol: TCP rules: http: - method: "GET" path: "/api"
Supports pod identity, L7 filtering, and encryption.
Environment-Specific Policies#
Apply different egress rules for dev, staging, and production:
Dev: DNS + cluster-internal only.
Staging: Add sandbox APIs.
Prod: Allow production endpoints (e.g., monitoring, payment gateways).
Filter access to pod#
Create a nginx pod in default ns
k run web --image=nginx --port=80 --expose --labels app=web
Create same defautl and notes tester
k run tester --image=bash --command -- sleep infinity
k run tester -n notes --image=bash --labels app=tester --command -- sleep infinity
Check http connection.
k exec -ti tester -- sh -c "awk '/<title>/' <(wget -qO- http://web)"
<title>Welcome to nginx!</title>
k -n notes exec -ti tester -- sh -c "awk '/<title>/' <(wget -qO- http://web.default)"
<title>Welcome to nginx!</title>
Deny all incoming traffic to web pods.
cat <<EOF | k apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-web
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress: []
EOF
Allow http connection only from tester pods in same ns.
cat <<EOF | k apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-web
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: tester
ports:
- port: 80
EOF
k exec -ti tester -- sh -c "awk '/<title>/' <(wget -qO- http://web)"
<title>Welcome to nginx!</title>
Allow traffic from tester pods in default and notes ns.
cat <<EOF | k apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-web
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: tester
ports:
- port: 80
- from:
- namespaceSelector:
matchLabels:
name: notes
podSelector:
matchLabels:
app: tester
ports:
- port: 80
EOF
Check http connection.
k exec -ti tester -- sh -c "awk '/<title>/' <(wget -qO- http://web)"
<title>Welcome to nginx!</title
k -n notes exec -ti tester -- sh -c "awk '/<title>/' <(wget -qO- http://web.default)"
<title>Welcome to nginx!</title>
k exec -ti -n notes notes-569c5676cb-l8lrg -c notes -- sh -c "awk '/<title>/' <(wget -T 5 -qO- http://web.default)"
wget: download timed out
Tip
Install Krew plugin manager
(
set -x; cd "$(mktemp -d)" &&
OS="$(uname | tr '[:upper:]' '[:lower:]')" &&
ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')" &&
KREW="krew-${OS}_${ARCH}" &&
curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/${KREW}.tar.gz" &&
tar zxvf "${KREW}.tar.gz" &&
./"${KREW}" install krew
)
echo 'export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"' >> .zshrc
tail -1 .zshrc
export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"
Cyclonous : check netpol
k krew search cyclonus
NAME DESCRIPTION INSTALLED
cyclonus NetworkPolicy analysis tool suite no
k cyclonus --mode explain -A
explained policies:
+---------+--------------------+------------------+--------------------------+-------------------------+
| TYPE | TARGET | SOURCE RULES | PEER | PORT/PROTOCOL |
+---------+--------------------+------------------+--------------------------+-------------------------+
| Ingress | namespace: default | default/deny-web | namespace: default | port 80 on protocol TCP |
| | Match labels: | | pods: Match labels: | |
| | app: web | | app: tester | |
+ + + +--------------------------+ +
| | | | namespace: Match labels: | |
| | | | name: notes | |
| | | | pods: Match labels: | |
| | | | app: tester | |
+---------+--------------------+------------------+--------------------------+-------------------------+
| | | | | |
+---------+--------------------+------------------+--------------------------+-------------------------+