Kubernetes Network Policies for Zero-Trust Architecture: Enterprise Implementation Guide
Zero-trust network architecture represents a fundamental shift in cloud-native security strategy, requiring explicit authorization for every network connection rather than implicit trust based on network location. Kubernetes Network Policies provide the foundational building blocks for implementing zero-trust networking at the pod level. This comprehensive guide explores enterprise-grade network policy implementations using native Kubernetes Network Policies and advanced CNI-specific features from Calico and Cilium.
Kubernetes Network Policies for Zero-Trust Architecture
Understanding Zero-Trust Networking
Traditional perimeter-based security models assume trust within network boundaries. Zero-trust architecture eliminates this assumption by requiring explicit verification for every network interaction.
Zero-Trust Principles in Kubernetes
Explicit Verification: Every connection requires authentication
- Default deny all traffic
- Explicit allow rules for legitimate communication
- Identity-based policy enforcement
- Continuous verification and monitoring
Least Privilege Access: Minimize blast radius
- Granular network segmentation
- Namespace isolation
- Pod-to-pod communication control
- Service-level access restrictions
Assume Breach: Design for compromise scenarios
- Lateral movement prevention
- Network microsegmentation
- Traffic encryption (mTLS)
- Comprehensive logging and monitoring
Native Kubernetes Network Policies
Basic Network Policy Structure
# Default Deny All Ingress Traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
---
# Default Deny All Egress Traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
---
# Allow Specific Ingress Traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
tier: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
tier: web
ports:
- protocol: TCP
port: 8080
---
# Allow Specific Egress Traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-database-egress
namespace: production
spec:
podSelector:
matchLabels:
app: backend
tier: api
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: postgresql
tier: database
ports:
- protocol: TCP
port: 5432
# Allow DNS
- to:
- namespaceSelector:
matchLabels:
name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
Multi-Tier Application Policy
# Three-Tier Application Network Policies
---
# Frontend Pod Policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: frontend
app: webapp
policyTypes:
- Ingress
- Egress
ingress:
# Allow ingress from Ingress Controller
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
podSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 443
egress:
# Allow egress to backend API
- to:
- podSelector:
matchLabels:
tier: backend
app: api-server
ports:
- protocol: TCP
port: 8080
# Allow DNS
- to:
- namespaceSelector:
matchLabels:
name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# Allow HTTPS to external services
- to:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 443
---
# Backend API Policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: backend
app: api-server
policyTypes:
- Ingress
- Egress
ingress:
# Allow from frontend
- from:
- podSelector:
matchLabels:
tier: frontend
app: webapp
ports:
- protocol: TCP
port: 8080
# Allow from monitoring
- from:
- namespaceSelector:
matchLabels:
name: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 9090
egress:
# Allow to database
- to:
- podSelector:
matchLabels:
tier: database
app: postgresql
ports:
- protocol: TCP
port: 5432
# Allow to Redis cache
- to:
- podSelector:
matchLabels:
tier: cache
app: redis
ports:
- protocol: TCP
port: 6379
# Allow DNS
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53
# Allow to external APIs
- to:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 443
---
# Database Policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: database
app: postgresql
policyTypes:
- Ingress
- Egress
ingress:
# Allow only from backend
- from:
- podSelector:
matchLabels:
tier: backend
app: api-server
ports:
- protocol: TCP
port: 5432
# Allow from backup jobs
- from:
- podSelector:
matchLabels:
app: database-backup
ports:
- protocol: TCP
port: 5432
egress:
# Allow DNS only
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53
# Database replication (if multi-instance)
- to:
- podSelector:
matchLabels:
tier: database
app: postgresql
ports:
- protocol: TCP
port: 5432
Cross-Namespace Policies
# Allow Cross-Namespace Communication
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-staging
namespace: production
spec:
podSelector:
matchLabels:
app: shared-service
policyTypes:
- Ingress
ingress:
# Allow from production namespace
- from:
- namespaceSelector:
matchLabels:
environment: production
podSelector:
matchLabels:
access: shared-service
ports:
- protocol: TCP
port: 8080
# Allow from staging namespace (limited)
- from:
- namespaceSelector:
matchLabels:
environment: staging
podSelector:
matchLabels:
access: shared-service
approved: "true"
ports:
- protocol: TCP
port: 8080
---
# Service Mesh Integration
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-service-mesh
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress:
# Allow from Istio/Linkerd sidecar
- from:
- podSelector: {}
ports:
- protocol: TCP
port: 15090 # Envoy metrics
egress:
# Allow to Istio control plane
- to:
- namespaceSelector:
matchLabels:
name: istio-system
ports:
- protocol: TCP
port: 15012 # Istiod
Calico Network Policies
Calico extends Kubernetes Network Policies with advanced features including global policies, service account matching, and application layer policies.
Global Network Policies
# Calico GlobalNetworkPolicy - Applies Cluster-Wide
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: global-deny-all
spec:
order: 1000
types:
- Ingress
- Egress
# Empty selector applies to all pods
selector: ""
---
# Allow Kubernetes System Traffic
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: allow-kubernetes-system
spec:
order: 100
types:
- Ingress
- Egress
selector: k8s-app != ""
ingress:
- action: Allow
protocol: TCP
source:
selector: k8s-app != ""
egress:
- action: Allow
protocol: TCP
destination:
selector: k8s-app != ""
---
# Allow DNS Globally
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: allow-dns-global
spec:
order: 200
types:
- Egress
selector: all()
egress:
- action: Allow
protocol: UDP
destination:
selector: k8s-app == "kube-dns"
ports:
- 53
---
# Monitoring and Observability
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: allow-monitoring
spec:
order: 300
types:
- Ingress
selector: has(monitoring)
ingress:
- action: Allow
protocol: TCP
source:
namespaceSelector: projectcalico.org/name == "monitoring"
destination:
ports:
- 9090 # Prometheus metrics
- 8080 # Application metrics
Service Account-Based Policies
# Calico Policy with Service Account Matching
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
name: backend-sa-policy
namespace: production
spec:
order: 500
selector: app == "backend"
types:
- Ingress
- Egress
ingress:
- action: Allow
protocol: TCP
source:
serviceAccounts:
names:
- frontend-sa
selector: environment == "production"
destination:
ports:
- 8080
egress:
- action: Allow
protocol: TCP
destination:
serviceAccounts:
names:
- database-sa
ports:
- 5432
- action: Allow
protocol: UDP
destination:
selector: k8s-app == "kube-dns"
ports:
- 53
---
# Layer 7 Policy (Application Layer)
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
name: api-l7-policy
namespace: production
spec:
selector: app == "api-gateway"
types:
- Ingress
ingress:
- action: Allow
protocol: TCP
source:
selector: tier == "frontend"
destination:
ports:
- 443
http:
methods:
- GET
- POST
paths:
- exact: /api/v1/users
- prefix: /api/v1/products
Host Endpoint Policies
# Calico Host Endpoint Policy
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: host-endpoint-policy
spec:
order: 10
selector: host-endpoint == "true"
types:
- Ingress
- Egress
ingress:
# Allow SSH from bastion only
- action: Allow
protocol: TCP
source:
nets:
- 10.0.1.0/24 # Bastion subnet
destination:
ports:
- 22
# Allow Kubernetes API
- action: Allow
protocol: TCP
destination:
ports:
- 6443
# Allow kubelet
- action: Allow
protocol: TCP
destination:
ports:
- 10250
egress:
# Allow all outbound (can be restricted)
- action: Allow
---
# Host Endpoint Definition
apiVersion: projectcalico.org/v3
kind: HostEndpoint
metadata:
name: node01-eth0
labels:
host-endpoint: "true"
environment: production
spec:
interfaceName: eth0
node: node01
expectedIPs:
- 10.0.2.10
Cilium Network Policies
Cilium provides eBPF-based networking with advanced L3-L7 policies, identity-aware security, and API-aware filtering.
Cilium Network Policy
# Cilium Native Network Policy
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-gateway-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: api-gateway
tier: frontend
ingress:
- fromEndpoints:
- matchLabels:
io.kubernetes.pod.namespace: ingress-nginx
app.kubernetes.io/name: ingress-nginx
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: GET
path: "/api/v1/.*"
- method: POST
path: "/api/v1/users"
headers:
- "Content-Type: application/json"
egress:
- toEndpoints:
- matchLabels:
app: backend-service
tier: backend
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: GET
path: "/internal/.*"
- toEndpoints:
- matchLabels:
io.kubernetes.pod.namespace: kube-system
k8s-app: kube-dns
toPorts:
- ports:
- port: "53"
protocol: UDP
rules:
dns:
- matchPattern: "*.company.com"
- toFQDNs:
- matchName: "api.external-service.com"
toPorts:
- ports:
- port: "443"
protocol: TCP
---
# Cluster-Wide Cilium Policy
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: global-default-deny
spec:
endpointSelector: {}
ingress:
- {}
ingressDeny:
- fromEndpoints:
- {}
egressDeny:
- toEndpoints:
- {}
---
# DNS-Based Policy
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-external-apis
namespace: production
spec:
endpointSelector:
matchLabels:
app: backend
egress:
- toFQDNs:
- matchName: "api.github.com"
- matchName: "api.stripe.com"
- matchPattern: "*.amazonaws.com"
toPorts:
- ports:
- port: "443"
protocol: TCP
- toEndpoints:
- matchLabels:
io.kubernetes.pod.namespace: kube-system
k8s-app: kube-dns
toPorts:
- ports:
- port: "53"
protocol: UDP
Identity-Aware Policies
# Cilium Identity-Based Policy
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: identity-based-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: sensitive-service
ingress:
# Allow from specific security identity
- fromEndpoints:
- matchLabels:
security-identity: "trusted"
compliance: "pci-dss"
toPorts:
- ports:
- port: "8443"
protocol: TCP
rules:
http:
- method: GET
path: "/secure/.*"
headers:
- "Authorization: Bearer .*"
# Deny from specific identities
ingressDeny:
- fromEndpoints:
- matchLabels:
security-identity: "untrusted"
---
# Service Account Based Identity
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: service-account-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: database
ingress:
- fromEndpoints:
- matchLabels:
io.cilium.k8s.policy.serviceaccount: backend-sa
io.kubernetes.pod.namespace: production
toPorts:
- ports:
- port: "5432"
protocol: TCP
Kafka Protocol Policies
# Cilium Kafka-Aware Policy
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: kafka-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: kafka
ingress:
- fromEndpoints:
- matchLabels:
app: producer
toPorts:
- ports:
- port: "9092"
protocol: TCP
rules:
kafka:
- role: produce
topic: events.orders
- fromEndpoints:
- matchLabels:
app: consumer
toPorts:
- ports:
- port: "9092"
protocol: TCP
rules:
kafka:
- role: consume
topic: events.orders
clientID: consumer-group-1
Policy Management and Automation
Policy Generator Script
#!/bin/bash
# Network Policy Generator for Zero-Trust
set -euo pipefail
NAMESPACE="${1:-default}"
OUTPUT_DIR="${2:-./network-policies}"
mkdir -p "${OUTPUT_DIR}"
# Generate default deny policies
generate_default_deny() {
local ns="$1"
cat > "${OUTPUT_DIR}/${ns}-default-deny.yaml" <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: ${ns}
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
EOF
echo "Generated default deny policy for namespace: ${ns}"
}
# Generate policies from running pods
generate_from_pods() {
local ns="$1"
kubectl get pods -n "${ns}" -o json | \
jq -r '.items[] |
.metadata.name as $pod |
.metadata.labels as $labels |
(.spec.containers[].ports[]? // {}) as $port |
{
pod: $pod,
labels: $labels,
port: $port.containerPort,
protocol: $port.protocol
}' | \
while read -r pod_data; do
# Generate policy based on pod configuration
echo "Analyzing pod: $(echo "$pod_data" | jq -r '.pod')"
done
}
# Validate policies
validate_policies() {
local policy_dir="$1"
echo "Validating network policies..."
for policy in "${policy_dir}"/*.yaml; do
if kubectl apply --dry-run=server -f "${policy}"; then
echo "✓ Valid: $(basename "${policy}")"
else
echo "✗ Invalid: $(basename "${policy}")"
fi
done
}
# Main execution
main() {
echo "Generating network policies for namespace: ${NAMESPACE}"
generate_default_deny "${NAMESPACE}"
generate_from_pods "${NAMESPACE}"
validate_policies "${OUTPUT_DIR}"
echo "Policies generated in: ${OUTPUT_DIR}"
}
main "$@"
Policy Testing Framework
#!/bin/bash
# Network Policy Testing Script
set -euo pipefail
TEST_NAMESPACE="netpol-test"
RESULTS_DIR="./test-results"
mkdir -p "${RESULTS_DIR}"
# Setup test environment
setup_test_environment() {
echo "Setting up test environment..."
kubectl create namespace "${TEST_NAMESPACE}" --dry-run=client -o yaml | kubectl apply -f -
# Deploy test pods
kubectl run test-client -n "${TEST_NAMESPACE}" --image=nicolaka/netshoot -- sleep 3600
kubectl run test-server -n "${TEST_NAMESPACE}" --image=nginx --labels="app=test-server"
kubectl wait --for=condition=ready pod -l app=test-server -n "${TEST_NAMESPACE}" --timeout=60s
}
# Test connectivity
test_connectivity() {
local source_pod="$1"
local target_pod="$2"
local target_port="$3"
local should_succeed="$4"
echo "Testing: ${source_pod} -> ${target_pod}:${target_port}"
local target_ip=$(kubectl get pod "${target_pod}" -n "${TEST_NAMESPACE}" -o jsonpath='{.status.podIP}')
if kubectl exec -n "${TEST_NAMESPACE}" "${source_pod}" -- timeout 5 nc -zv "${target_ip}" "${target_port}" &>/dev/null; then
if [[ "${should_succeed}" == "true" ]]; then
echo "✓ PASS: Connection succeeded as expected"
return 0
else
echo "✗ FAIL: Connection succeeded but should have been blocked"
return 1
fi
else
if [[ "${should_succeed}" == "false" ]]; then
echo "✓ PASS: Connection blocked as expected"
return 0
else
echo "✗ FAIL: Connection blocked but should have succeeded"
return 1
fi
fi
}
# Run test suite
run_tests() {
echo "Running network policy tests..."
# Test 1: Default deny
kubectl apply -n "${TEST_NAMESPACE}" -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
EOF
test_connectivity "test-client" "test-server" "80" "false"
# Test 2: Allow specific traffic
kubectl apply -n "${TEST_NAMESPACE}" -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-test-client
spec:
podSelector:
matchLabels:
app: test-server
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
run: test-client
ports:
- protocol: TCP
port: 80
EOF
sleep 5 # Allow policy to propagate
test_connectivity "test-client" "test-server" "80" "true"
}
# Cleanup
cleanup() {
echo "Cleaning up test environment..."
kubectl delete namespace "${TEST_NAMESPACE}" --ignore-not-found=true
}
# Main execution
main() {
trap cleanup EXIT
setup_test_environment
run_tests
echo "Test execution completed. Results saved to: ${RESULTS_DIR}"
}
main "$@"
Monitoring and Observability
Prometheus Metrics
# ServiceMonitor for Network Policy Metrics
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: calico-felix-metrics
namespace: calico-system
spec:
selector:
matchLabels:
k8s-app: calico-node
endpoints:
- port: metrics-port
interval: 30s
---
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: network-policy-alerts
namespace: monitoring
spec:
groups:
- name: network-policies
interval: 30s
rules:
- alert: NetworkPolicyDroppedPackets
expr: |
rate(calico_dropped_packets_total[5m]) > 100
for: 5m
labels:
severity: warning
annotations:
summary: "High rate of dropped packets"
description: "Network policy dropping {{ $value }} packets/sec"
- alert: NetworkPolicyNotApplied
expr: |
calico_policy_errors_total > 0
for: 5m
labels:
severity: critical
annotations:
summary: "Network policy errors detected"
description: "{{ $value }} policy application errors"
Grafana Dashboard
{
"dashboard": {
"title": "Network Policy Monitoring",
"panels": [
{
"title": "Allowed vs Denied Connections",
"targets": [
{
"expr": "rate(calico_allowed_packets_total[5m])",
"legendFormat": "Allowed"
},
{
"expr": "rate(calico_dropped_packets_total[5m])",
"legendFormat": "Denied"
}
]
},
{
"title": "Policy Evaluation Time",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(calico_policy_evaluation_duration_seconds_bucket[5m]))",
"legendFormat": "p95"
}
]
}
]
}
}
Conclusion
Implementing zero-trust networking through Kubernetes Network Policies requires careful planning, comprehensive policy coverage, and continuous monitoring. By starting with default deny policies, implementing least privilege access, and leveraging advanced CNI features from Calico or Cilium, organizations can significantly reduce their attack surface while maintaining operational flexibility. Regular policy audits, automated testing, and integration with observability platforms ensure that network policies remain effective as applications evolve.