Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-SkillsFramework mappings
MITRE ATT&CK
Overview
Kubernetes NetworkPolicies provide pod-level network segmentation by defining ingress and egress rules that control traffic flow between pods, namespaces, and external endpoints. Combined with CNI plugins like Calico or Cilium, network policies enforce zero-trust microsegmentation to prevent lateral movement within the cluster.
When to Use
- When deploying or configuring implementing network policies for kubernetes capabilities in your environment
- When establishing security controls aligned to compliance requirements
- When building or improving security architecture for this domain
- When conducting security assessments that require this implementation
Prerequisites
- Kubernetes cluster with NetworkPolicy-supporting CNI (Calico, Cilium, Antrea)
- kubectl configured with admin access
- Understanding of pod labels and selectors
Workflow
Step 1: Default Deny All Traffic
# default-deny-all.yaml - Apply to every namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {} # Applies to all pods
policyTypes:
- Ingress
- EgressStep 2: Allow DNS Egress (Required for Service Discovery)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53Step 3: Application-Specific Policies
# Allow frontend to reach backend only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-allow-frontend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
---
# Allow backend to reach database only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database-allow-backend
namespace: production
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 5432Step 4: Cross-Namespace Policies
# Allow monitoring namespace to scrape metrics
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-monitoring-scrape
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
purpose: monitoring
ports:
- protocol: TCP
port: 9090 # Prometheus metrics portStep 5: Egress Restrictions
# Restrict egress to specific external services
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-egress
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
- to: # Allow external API
- ipBlock:
cidr: 203.0.113.0/24
ports:
- protocol: TCP
port: 443
- to: # DNS
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53Step 6: Block Cloud Metadata Access
# Prevent SSRF to cloud metadata service
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: block-metadata
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32 # AWS/GCP metadata
- 100.100.100.200/32 # Azure metadataValidation Commands
# Verify policies are applied
kubectl get networkpolicies -n production
# Test connectivity (should be blocked)
kubectl run test-pod --image=busybox --restart=Never -n production -- wget -qO- --timeout=2 http://database-service:5432
# Expected: timeout (blocked by policy)
# Test allowed traffic
kubectl run frontend-test --image=busybox --labels=app=frontend --restart=Never -n production -- wget -qO- --timeout=2 http://backend-service:8080
# Expected: connection succeedsReferences
Source materials
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.4 KB
API Reference: Implementing Network Policies for Kubernetes
Default Deny-All Policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: production
spec:
podSelector: {}
policyTypes: [Ingress, Egress]Allow Specific Ingress
spec:
podSelector:
matchLabels: { app: backend }
ingress:
- from:
- podSelector: { matchLabels: { app: frontend } }
ports:
- port: 8080kubectl Commands
# List all network policies
kubectl get networkpolicy --all-namespaces
# Describe policy
kubectl describe networkpolicy default-deny -n production
# Apply policy
kubectl apply -f netpol.yamlPolicy Types
| Type | Behavior when present |
|---|---|
| Ingress | Restrict inbound traffic |
| Egress | Restrict outbound traffic |
| Both empty | Default deny all |
Common Patterns
| Pattern | Description |
|---|---|
| Default deny | Empty podSelector, no rules |
| Allow DNS | Egress to kube-system:53 |
| Allow same namespace | namespaceSelector match |
| Allow from ingress controller | Label-based ingress |
References
- K8s NetworkPolicy: https://kubernetes.io/docs/concepts/services-networking/network-policies/
- Network Policy Editor: https://editor.networkpolicy.io/
- CNI Comparison: https://kubernetes.io/docs/concepts/cluster-administration/networking/
standards.md0.7 KB
Standards Reference - Kubernetes Network Policies
CIS Kubernetes Benchmark v1.8 - Section 5.3
- 5.3.1: Ensure CNI supports Network Policies
- 5.3.2: Ensure default deny NetworkPolicy for all namespaces
NSA/CISA Kubernetes Hardening Guide
- Implement network segmentation between namespaces
- Apply default-deny network policies
- Restrict pod-to-pod communication to required paths only
- Block access to cloud metadata endpoints
MITRE ATT&CK Mitigations
| Technique | Mitigation via Network Policy |
|---|---|
| T1046 - Network Service Scanning | Limit reachable services |
| T1021 - Remote Services | Block lateral movement |
| T1552 - Credentials from IMDS | Block 169.254.169.254 |
workflows.md0.8 KB
Workflows - Kubernetes Network Policies
Workflow 1: Network Policy Deployment
[Identify communication paths] --> [Create default-deny] --> [Add allow rules per service]
| | |
v v v
Map pod-to-pod traffic Apply to all namespaces Test with connectivity checks
Document required flows Verify DNS still works Monitor for broken connectionsWorkflow 2: Progressive Enforcement
Step 1: Deploy in audit mode (Calico: log-only)
Step 2: Monitor traffic patterns for 1 week
Step 3: Create policies matching observed traffic
Step 4: Apply default-deny in non-production
Step 5: Validate application functionality
Step 6: Roll out to production namespacesScripts 2
agent.py6.2 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Kubernetes Network Policy Agent - audits pod-to-pod communication and network policy coverage."""
import json
import argparse
import logging
import subprocess
from datetime import datetime
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
def kubectl_json(args_list):
"""Execute kubectl command and return JSON output."""
cmd = ["kubectl"] + args_list + ["-o", "json"]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
return json.loads(result.stdout) if result.returncode == 0 else {}
def get_namespaces():
return kubectl_json(["get", "namespaces"])
def get_network_policies(namespace="--all-namespaces"):
if namespace == "--all-namespaces":
return kubectl_json(["get", "networkpolicies", "--all-namespaces"])
return kubectl_json(["get", "networkpolicies", "-n", namespace])
def get_pods(namespace="--all-namespaces"):
if namespace == "--all-namespaces":
return kubectl_json(["get", "pods", "--all-namespaces"])
return kubectl_json(["get", "pods", "-n", namespace])
def analyze_policy_coverage(policies, pods):
"""Determine which pods are covered by network policies."""
policy_selectors = []
for item in policies.get("items", []):
ns = item.get("metadata", {}).get("namespace", "")
spec = item.get("spec", {})
selector = spec.get("podSelector", {}).get("matchLabels", {})
policy_types = spec.get("policyTypes", [])
policy_selectors.append({
"namespace": ns,
"selector": selector,
"ingress": "Ingress" in policy_types or spec.get("ingress") is not None,
"egress": "Egress" in policy_types or spec.get("egress") is not None,
})
covered_pods = set()
uncovered_pods = []
for pod in pods.get("items", []):
pod_ns = pod.get("metadata", {}).get("namespace", "")
pod_name = pod.get("metadata", {}).get("name", "")
pod_labels = pod.get("metadata", {}).get("labels", {})
is_covered = False
for policy in policy_selectors:
if policy["namespace"] != pod_ns:
continue
if not policy["selector"]:
is_covered = True
break
if all(pod_labels.get(k) == v for k, v in policy["selector"].items()):
is_covered = True
break
if is_covered:
covered_pods.add(f"{pod_ns}/{pod_name}")
else:
uncovered_pods.append({"namespace": pod_ns, "pod": pod_name, "labels": pod_labels})
total = len(pods.get("items", []))
return {
"total_pods": total,
"covered_pods": len(covered_pods),
"uncovered_pods": len(uncovered_pods),
"coverage_percent": round(len(covered_pods) / max(total, 1) * 100, 1),
"uncovered_pod_list": uncovered_pods[:20],
}
def detect_overly_permissive_policies(policies):
"""Find network policies that allow all traffic."""
findings = []
for item in policies.get("items", []):
name = item.get("metadata", {}).get("name", "")
ns = item.get("metadata", {}).get("namespace", "")
spec = item.get("spec", {})
if not spec.get("podSelector", {}).get("matchLabels"):
ingress_rules = spec.get("ingress", [])
for rule in ingress_rules:
if not rule.get("from"):
findings.append({"policy": name, "namespace": ns,
"issue": "allows_all_ingress", "severity": "high"})
egress_rules = spec.get("egress", [])
for rule in egress_rules:
if not rule.get("to"):
findings.append({"policy": name, "namespace": ns,
"issue": "allows_all_egress", "severity": "medium"})
return findings
def analyze_namespace_isolation(policies, namespaces):
"""Check which namespaces have default-deny policies."""
ns_with_deny = set()
for item in policies.get("items", []):
spec = item.get("spec", {})
if (not spec.get("podSelector", {}).get("matchLabels") and
not spec.get("ingress") and "Ingress" in spec.get("policyTypes", [])):
ns_with_deny.add(item.get("metadata", {}).get("namespace", ""))
all_ns = [ns.get("metadata", {}).get("name", "") for ns in namespaces.get("items", [])]
system_ns = {"kube-system", "kube-public", "kube-node-lease"}
user_ns = [ns for ns in all_ns if ns not in system_ns]
return {
"total_namespaces": len(user_ns),
"namespaces_with_default_deny": len(ns_with_deny),
"isolation_percent": round(len(ns_with_deny) / max(len(user_ns), 1) * 100, 1),
"unprotected_namespaces": [ns for ns in user_ns if ns not in ns_with_deny],
}
def generate_report(policies, pods, namespaces):
coverage = analyze_policy_coverage(policies, pods)
permissive = detect_overly_permissive_policies(policies)
isolation = analyze_namespace_isolation(policies, namespaces)
report = {
"timestamp": datetime.utcnow().isoformat(),
"total_network_policies": len(policies.get("items", [])),
"pod_coverage": coverage,
"namespace_isolation": isolation,
"overly_permissive_policies": permissive,
"total_findings": len(permissive),
}
return report
def main():
parser = argparse.ArgumentParser(description="Kubernetes Network Policy Audit Agent")
parser.add_argument("--namespace", default="--all-namespaces", help="Namespace to audit")
parser.add_argument("--output", default="k8s_netpol_report.json")
args = parser.parse_args()
policies = get_network_policies(args.namespace)
pods = get_pods(args.namespace)
namespaces = get_namespaces()
report = generate_report(policies, pods, namespaces)
with open(args.output, "w") as f:
json.dump(report, f, indent=2, default=str)
logger.info("K8s NetPol: %.1f%% pod coverage, %.1f%% namespace isolation, %d findings",
report["pod_coverage"]["coverage_percent"],
report["namespace_isolation"]["isolation_percent"],
report["total_findings"])
print(json.dumps(report, indent=2, default=str))
if __name__ == "__main__":
main()
process.py3.9 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Kubernetes Network Policy Auditor
Checks for missing network policies, default-deny enforcement,
and identifies namespaces without proper segmentation.
"""
import subprocess
import json
import sys
from dataclasses import dataclass, field
@dataclass
class NetPolFinding:
namespace: str
severity: str
issue: str
remediation: str
def run_kubectl_json(args: list):
cmd = ["kubectl"] + args + ["-o", "json"]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
if result.returncode != 0:
return None
return json.loads(result.stdout)
except (subprocess.TimeoutExpired, json.JSONDecodeError, FileNotFoundError):
return None
def audit_network_policies():
findings = []
system_ns = {"kube-system", "kube-public", "kube-node-lease"}
namespaces = run_kubectl_json(["get", "namespaces"])
if not namespaces:
print("[!] Cannot list namespaces")
return findings
for ns in namespaces.get("items", []):
ns_name = ns["metadata"]["name"]
if ns_name in system_ns:
continue
netpols = run_kubectl_json(["get", "networkpolicies", "-n", ns_name])
policies = netpols.get("items", []) if netpols else []
if not policies:
findings.append(NetPolFinding(
namespace=ns_name, severity="HIGH",
issue="No NetworkPolicies defined",
remediation=f"Create default-deny ingress/egress policy in namespace '{ns_name}'"
))
continue
# Check for default-deny
has_default_deny_ingress = False
has_default_deny_egress = False
for pol in policies:
spec = pol.get("spec", {})
pod_selector = spec.get("podSelector", {})
policy_types = spec.get("policyTypes", [])
if not pod_selector.get("matchLabels") and not pod_selector.get("matchExpressions"):
if "Ingress" in policy_types and not spec.get("ingress"):
has_default_deny_ingress = True
if "Egress" in policy_types and not spec.get("egress"):
has_default_deny_egress = True
if not has_default_deny_ingress:
findings.append(NetPolFinding(
namespace=ns_name, severity="HIGH",
issue="Missing default-deny ingress policy",
remediation="Create NetworkPolicy with empty podSelector and Ingress policyType with no ingress rules"
))
if not has_default_deny_egress:
findings.append(NetPolFinding(
namespace=ns_name, severity="MEDIUM",
issue="Missing default-deny egress policy",
remediation="Create NetworkPolicy with empty podSelector and Egress policyType with no egress rules"
))
return findings
def main():
print("[*] Kubernetes Network Policy Auditor\n")
findings = audit_network_policies()
print(f"\n{'='*60}")
print(f"NETWORK POLICY AUDIT REPORT")
print(f"{'='*60}")
print(f"Total Findings: {len(findings)}")
for sev in ["CRITICAL", "HIGH", "MEDIUM", "LOW"]:
sev_findings = [f for f in findings if f.severity == sev]
if sev_findings:
print(f"\n{sev}:")
for f in sev_findings:
print(f" [{f.namespace}] {f.issue}")
print(f" Fix: {f.remediation}")
with open("netpol_audit_report.json", "w") as fh:
json.dump({"findings": [{"namespace": f.namespace, "severity": f.severity,
"issue": f.issue, "remediation": f.remediation}
for f in findings]}, fh, indent=2)
print("\n[*] Report saved to netpol_audit_report.json")
if any(f.severity in ("CRITICAL", "HIGH") for f in findings):
sys.exit(1)
if __name__ == "__main__":
main()
Assets 1
template.mdtext/markdown · 0.5 KBKeep exploring