container security

Implementing Kubernetes Network Policy with Calico

Implement Kubernetes network segmentation using Calico NetworkPolicy and GlobalNetworkPolicy for zero-trust pod-to-pod communication.

calicocnikubernetesnetwork-policynetwork-segmentationzero-trust
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

Calico is an open-source CNI plugin that provides fine-grained network policy enforcement for Kubernetes clusters. It implements the full Kubernetes NetworkPolicy API and extends it with Calico-specific GlobalNetworkPolicy, supporting policy ordering, deny rules, and service-account-based selectors.

When to Use

  • When deploying or configuring implementing kubernetes network policy with calico 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 (v1.24+)
  • Calico CNI installed (v3.26+)
  • kubectl and calicoctl CLI tools
  • Cluster admin RBAC permissions

Installing Calico

Operator-based Installation (Recommended)

# Install the Tigera operator
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/tigera-operator.yaml
 
# Install Calico custom resources
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/custom-resources.yaml
 
# Verify installation
kubectl get pods -n calico-system
watch kubectl get pods -n calico-system
 
# Install calicoctl
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calicoctl.yaml

Verify Calico is Running

# Check Calico pods
kubectl get pods -n calico-system
 
# Check Calico node status
kubectl exec -n calico-system calicoctl -- calicoctl node status
 
# Check IP pools
kubectl exec -n calico-system calicoctl -- calicoctl get ippool -o wide

Kubernetes NetworkPolicy

Default Deny All Traffic

# deny-all-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
 
---
# deny-all-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress

Allow Specific Pod-to-Pod Communication

# allow-frontend-to-backend.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080

Allow DNS Egress

# allow-dns-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector: {}
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

Namespace Isolation

# allow-same-namespace.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-same-namespace
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector: {}

Calico-Specific Policies

GlobalNetworkPolicy (Cluster-Wide)

# global-deny-external.yaml
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: deny-external-ingress
spec:
  order: 100
  selector: "projectcalico.org/namespace != 'ingress-nginx'"
  types:
    - Ingress
  ingress:
    - action: Deny
      source:
        nets:
          - 0.0.0.0/0
      destination: {}

Calico NetworkPolicy with Deny Rules

# calico-deny-policy.yaml
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: deny-database-from-frontend
  namespace: production
spec:
  order: 10
  selector: app == 'database'
  types:
    - Ingress
  ingress:
    - action: Deny
      source:
        selector: app == 'frontend'
    - action: Allow
      source:
        selector: app == 'backend'
      destination:
        ports:
          - 5432

Service Account Based Policy

# sa-based-policy.yaml
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: allow-by-service-account
  namespace: production
spec:
  selector: app == 'api'
  ingress:
    - action: Allow
      source:
        serviceAccounts:
          names:
            - frontend-sa
            - monitoring-sa
  egress:
    - action: Allow
      destination:
        serviceAccounts:
          names:
            - database-sa

Host Endpoint Protection

# host-endpoint-policy.yaml
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: restrict-host-ssh
spec:
  order: 10
  selector: "has(kubernetes.io/hostname)"
  applyOnForward: false
  types:
    - Ingress
  ingress:
    - action: Allow
      protocol: TCP
      source:
        nets:
          - 10.0.0.0/8
      destination:
        ports:
          - 22
    - action: Deny
      protocol: TCP
      destination:
        ports:
          - 22

Calico Policy Tiers

# security-tier.yaml
apiVersion: projectcalico.org/v3
kind: Tier
metadata:
  name: security
spec:
  order: 100
 
---
# platform-tier.yaml
apiVersion: projectcalico.org/v3
kind: Tier
metadata:
  name: platform
spec:
  order: 200

Monitoring and Troubleshooting

# List all network policies
kubectl get networkpolicy --all-namespaces
 
# List Calico-specific policies
kubectl exec -n calico-system calicoctl -- calicoctl get networkpolicy --all-namespaces -o wide
kubectl exec -n calico-system calicoctl -- calicoctl get globalnetworkpolicy -o wide
 
# Check policy evaluation for a specific endpoint
kubectl exec -n calico-system calicoctl -- calicoctl get workloadendpoint -n production -o yaml
 
# View Calico logs
kubectl logs -n calico-system -l k8s-app=calico-node --tail=100
 
# Test connectivity
kubectl exec -n production frontend-pod -- wget -qO- --timeout=2 http://backend-svc:8080/health

Best Practices

  1. Start with default deny - Apply deny-all policies to every namespace, then allow specific traffic
  2. Use labels consistently - Define a labeling standard for app, tier, environment
  3. Order policies - Use Calico policy ordering (order field) to control evaluation precedence
  4. Allow DNS first - Always create DNS egress rules before applying egress deny policies
  5. Use GlobalNetworkPolicy for cluster-wide security baselines
  6. Test policies in staging - Validate network connectivity after applying policies
  7. Monitor denied traffic - Enable Calico flow logs for visibility into blocked connections
  8. Use tiers - Organize policies into security, platform, and application tiers
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 Kubernetes Network Policy with Calico

Kubernetes NetworkPolicy

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]

Calico GlobalNetworkPolicy

apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: deny-external
spec:
  order: 100
  selector: app == "backend"
  types: [Ingress]
  ingress:
    - action: Deny
      source:
        nets: ["0.0.0.0/0"]

calicoctl CLI

# Apply policy
calicoctl apply -f policy.yaml
# Get policies
calicoctl get globalnetworkpolicy -o yaml
# Get host endpoints
calicoctl get hostendpoint

Policy Types

Type Scope Ordering
NetworkPolicy Namespace Additive (OR)
GlobalNetworkPolicy Cluster-wide Ordered by order field

Common Policy Patterns

Pattern Description
Default deny Empty podSelector, no rules
Allow DNS Egress to kube-system UDP/TCP 53
Allow ingress from namespace namespaceSelector match
Allow to external CIDR ipBlock in egress

References

standards.md2.6 KB

Standards and References - Kubernetes Network Policy with Calico

Industry Standards

NIST SP 800-190: Application Container Security Guide

  • Section 4.4: Container Networking - Isolate container network traffic using network policies
  • Section 5.3: Network Security - Implement micro-segmentation between containers
  • Recommends default-deny policies with explicit allowlisting

CIS Kubernetes Benchmark v1.8

  • 5.3.1: Ensure that the CNI in use supports Network Policies
  • 5.3.2: Ensure that all Namespaces have Network Policies defined
  • 5.3.3: Ensure that the default namespace does not contain any pods

NIST SP 800-53 Rev 5

  • SC-7: Boundary Protection - Implement network segmentation controls
  • AC-4: Information Flow Enforcement - Control network traffic between pods
  • SC-7(5): Deny by Default / Allow by Exception

NSA/CISA Kubernetes Hardening Guide v1.2

  • Section 3: Network Separation and Hardening
    • Use network policies to isolate workloads
    • Implement default deny ingress and egress policies
    • Limit pod-to-pod communication to minimum required

Calico Documentation References

Resource URL
Calico NetworkPolicy https://docs.tigera.io/calico/latest/network-policy/get-started/calico-policy/calico-network-policy
Kubernetes Policy Tutorial https://docs.tigera.io/calico/latest/network-policy/get-started/kubernetes-policy/kubernetes-policy-basic
GlobalNetworkPolicy https://docs.tigera.io/calico/latest/reference/resources/globalnetworkpolicy
Policy Tiers https://docs.tigera.io/calico-enterprise/latest/network-policy/policy-tiers/tiered-policy
Calico eBPF Dataplane https://docs.tigera.io/calico/latest/operations/ebpf/enabling-ebpf

Zero Trust Network Model

Principles Applied

  1. Never trust, always verify - Default deny all traffic between pods
  2. Least privilege access - Only allow specific required communication paths
  3. Micro-segmentation - Isolate workloads at pod-to-pod granularity
  4. Identity-based policies - Use service accounts and labels for policy selection
  5. Continuous monitoring - Log and alert on denied traffic patterns

Compliance Mappings

PCI DSS v4.0

  • Requirement 1.2.1: Restrict inbound and outbound traffic to that which is necessary
  • Requirement 1.3.1: Inbound traffic is restricted to that which is necessary
  • Requirement 1.3.2: Outbound traffic is restricted to that which is necessary

SOC 2 Type II

  • CC6.1: Logical access security - Network isolation between components
  • CC6.6: Network boundaries - Restrict access at network boundaries

HIPAA

  • 164.312(e)(1): Transmission Security - Protect data in transit between services
workflows.md4.5 KB

Workflow - Implementing Kubernetes Network Policy with Calico

Phase 1: Discovery and Planning

Map Application Communication Flows

# Identify all namespaces
kubectl get namespaces
 
# List all services per namespace
kubectl get svc --all-namespaces -o wide
 
# Identify pod labels
kubectl get pods --all-namespaces --show-labels
 
# Check existing network policies
kubectl get networkpolicy --all-namespaces

Document Required Traffic Flows

Create a traffic matrix documenting:

  • Source pod/namespace -> Destination pod/namespace
  • Protocol and port
  • Business justification

Phase 2: Install and Verify Calico

# Install Tigera operator
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/tigera-operator.yaml
 
# Wait for operator
kubectl wait --for=condition=Available deployment/tigera-operator -n tigera-operator --timeout=120s
 
# Install Calico custom resources
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/custom-resources.yaml
 
# Verify all Calico pods are running
kubectl get pods -n calico-system -w
 
# Install calicoctl as a pod
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calicoctl.yaml
 
# Verify node status
kubectl exec -n calico-system calicoctl -- calicoctl node status

Phase 3: Apply Default Deny Policies

Step 1 - Create DNS Allow Policy First

kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to: []
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
EOF

Step 2 - Apply Default Deny Ingress

kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
EOF

Step 3 - Apply Default Deny Egress

kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress
EOF

Step 4 - Apply Allow Rules per Traffic Flow

# Allow frontend to backend
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080
EOF

Phase 4: Validate Policies

Connectivity Testing

# Test allowed path (should succeed)
kubectl exec -n production deploy/frontend -- wget -qO- --timeout=5 http://backend-svc:8080/health
 
# Test blocked path (should timeout/fail)
kubectl exec -n production deploy/frontend -- wget -qO- --timeout=5 http://database-svc:5432
 
# Test cross-namespace (should fail if denied)
kubectl exec -n staging deploy/test -- wget -qO- --timeout=5 http://backend-svc.production:8080/health

Monitor Denied Connections

# Check Calico logs for denied connections
kubectl logs -n calico-system -l k8s-app=calico-node --tail=50 | grep -i deny
 
# Enable flow logs (Calico Enterprise)
kubectl exec -n calico-system calicoctl -- calicoctl get felixconfiguration default -o yaml

Phase 5: Advanced Calico Policies

Apply Global Security Baseline

kubectl exec -n calico-system calicoctl -- calicoctl apply -f - <<EOF
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: security-baseline
spec:
  order: 100
  types:
    - Ingress
    - Egress
  egress:
    - action: Allow
      protocol: UDP
      destination:
        ports:
          - 53
    - action: Allow
      protocol: TCP
      destination:
        ports:
          - 53
  ingress:
    - action: Allow
      source:
        selector: "projectcalico.org/namespace in {'kube-system', 'monitoring'}"
EOF

Phase 6: Ongoing Operations

Regular Policy Audits

  1. Review traffic flow matrix monthly
  2. Validate policies match documented flows
  3. Remove stale policies for decommissioned services
  4. Update policies when new services are deployed

Incident Response

  1. If suspicious traffic detected, apply emergency deny policy
  2. Analyze Calico flow logs for investigation
  3. Identify compromised pod via workload endpoint
  4. Isolate pod by applying targeted deny policy

Scripts 2

agent.py7.7 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for auditing and generating Calico Kubernetes network policies."""

import json
import argparse
import subprocess
from datetime import datetime


def kubectl_get(resource, namespace=None, label_selector=None):
    """Get Kubernetes resources via kubectl."""
    cmd = ["kubectl", "get", resource, "-o", "json"]
    if namespace:
        cmd.extend(["-n", namespace])
    else:
        cmd.append("--all-namespaces")
    if label_selector:
        cmd.extend(["-l", label_selector])
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
    if result.returncode != 0:
        return {"error": result.stderr.strip()}
    return json.loads(result.stdout) if result.stdout.strip() else {}


def list_network_policies():
    """List all NetworkPolicy and Calico GlobalNetworkPolicy resources."""
    k8s_np = kubectl_get("networkpolicy")
    calico_gnp = kubectl_get("globalnetworkpolicy")
    return {
        "k8s_network_policies": k8s_np.get("items", []) if isinstance(k8s_np, dict) else [],
        "calico_global_policies": calico_gnp.get("items", []) if isinstance(calico_gnp, dict) else [],
    }


def audit_network_policies(policies_path):
    """Audit network policies for security gaps."""
    with open(policies_path) as f:
        data = json.load(f)
    policies = data if isinstance(data, list) else data.get("items", data.get("policies", []))
    findings = []
    namespaces_covered = set()

    for policy in policies:
        metadata = policy.get("metadata", {})
        spec = policy.get("spec", {})
        ns = metadata.get("namespace", "default")
        namespaces_covered.add(ns)

        policy_types = spec.get("policyTypes", [])
        if "Ingress" not in policy_types and "Egress" not in policy_types:
            findings.append({
                "policy": metadata.get("name", ""),
                "namespace": ns,
                "issue": "No policyTypes defined (defaults to ingress-only)",
                "severity": "MEDIUM",
            })

        if "Egress" not in policy_types:
            findings.append({
                "policy": metadata.get("name", ""),
                "namespace": ns,
                "issue": "No egress policy - all outbound traffic allowed",
                "severity": "HIGH",
            })

        ingress_rules = spec.get("ingress", [])
        for rule in ingress_rules:
            if not rule.get("from"):
                findings.append({
                    "policy": metadata.get("name", ""),
                    "issue": "Ingress rule allows from all sources",
                    "severity": "HIGH",
                })

        egress_rules = spec.get("egress", [])
        for rule in egress_rules:
            if not rule.get("to"):
                findings.append({
                    "policy": metadata.get("name", ""),
                    "issue": "Egress rule allows to all destinations",
                    "severity": "MEDIUM",
                })

    return {"findings": findings, "namespaces_covered": list(namespaces_covered),
            "total_policies": len(policies)}


def generate_default_deny(namespace):
    """Generate a default deny-all NetworkPolicy for a namespace."""
    return {
        "apiVersion": "networking.k8s.io/v1",
        "kind": "NetworkPolicy",
        "metadata": {"name": "default-deny-all", "namespace": namespace},
        "spec": {
            "podSelector": {},
            "policyTypes": ["Ingress", "Egress"],
        },
    }


def generate_allow_dns_egress(namespace):
    """Generate a policy allowing DNS egress to kube-dns."""
    return {
        "apiVersion": "networking.k8s.io/v1",
        "kind": "NetworkPolicy",
        "metadata": {"name": "allow-dns-egress", "namespace": namespace},
        "spec": {
            "podSelector": {},
            "policyTypes": ["Egress"],
            "egress": [{
                "to": [{"namespaceSelector": {"matchLabels": {
                    "kubernetes.io/metadata.name": "kube-system"}}}],
                "ports": [{"protocol": "UDP", "port": 53},
                           {"protocol": "TCP", "port": 53}],
            }],
        },
    }


def generate_app_policy(namespace, app_label, allowed_ingress_labels=None,
                         allowed_egress_ports=None):
    """Generate a Calico-style network policy for an application."""
    policy = {
        "apiVersion": "networking.k8s.io/v1",
        "kind": "NetworkPolicy",
        "metadata": {"name": f"allow-{app_label}", "namespace": namespace},
        "spec": {
            "podSelector": {"matchLabels": {"app": app_label}},
            "policyTypes": ["Ingress", "Egress"],
            "ingress": [],
            "egress": [],
        },
    }
    if allowed_ingress_labels:
        for label in allowed_ingress_labels:
            policy["spec"]["ingress"].append({
                "from": [{"podSelector": {"matchLabels": {"app": label}}}],
            })
    if allowed_egress_ports:
        for port_info in allowed_egress_ports:
            policy["spec"]["egress"].append({
                "ports": [{"protocol": port_info.get("protocol", "TCP"),
                           "port": port_info["port"]}],
            })
    return policy


def check_unprotected_namespaces():
    """Find namespaces without any network policies."""
    namespaces = kubectl_get("namespaces")
    policies = kubectl_get("networkpolicy")
    if isinstance(namespaces, dict) and "error" not in namespaces:
        all_ns = {item["metadata"]["name"] for item in namespaces.get("items", [])}
    else:
        return {"error": "Cannot list namespaces"}

    protected_ns = set()
    if isinstance(policies, dict) and "error" not in policies:
        for item in policies.get("items", []):
            protected_ns.add(item.get("metadata", {}).get("namespace", "default"))

    system_ns = {"kube-system", "kube-public", "kube-node-lease"}
    unprotected = all_ns - protected_ns - system_ns
    return {
        "total_namespaces": len(all_ns),
        "protected": len(protected_ns),
        "unprotected": list(unprotected),
        "severity": "HIGH" if unprotected else "INFO",
    }


def main():
    parser = argparse.ArgumentParser(description="Calico Network Policy Agent")
    parser.add_argument("--audit", help="Network policies JSON to audit")
    parser.add_argument("--namespace", help="Namespace for policy generation")
    parser.add_argument("--app", help="App label for policy generation")
    parser.add_argument("--action", choices=["audit", "generate", "check", "full"],
                        default="full")
    parser.add_argument("--output", default="calico_netpol_report.json")
    args = parser.parse_args()

    report = {"generated_at": datetime.utcnow().isoformat(), "results": {}}

    if args.action in ("audit", "full") and args.audit:
        result = audit_network_policies(args.audit)
        report["results"]["audit"] = result
        print(f"[+] Audit: {len(result['findings'])} findings across {result['total_policies']} policies")

    if args.action in ("generate", "full") and args.namespace:
        policies = [
            generate_default_deny(args.namespace),
            generate_allow_dns_egress(args.namespace),
        ]
        if args.app:
            policies.append(generate_app_policy(args.namespace, args.app))
        report["results"]["generated"] = policies
        print(f"[+] Generated {len(policies)} policies for {args.namespace}")

    if args.action in ("check", "full"):
        result = check_unprotected_namespaces()
        report["results"]["unprotected"] = result
        print(f"[+] Unprotected namespaces: {result.get('unprotected', [])}")

    with open(args.output, "w") as f:
        json.dump(report, f, indent=2, default=str)
    print(f"[+] Report saved to {args.output}")


if __name__ == "__main__":
    main()
process.py7.7 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Calico Network Policy Manager - Generate, validate, and audit Kubernetes
network policies using Calico for zero-trust pod communication.
"""

import json
import subprocess
import sys
import argparse
import yaml
from typing import Optional


def run_kubectl(args: list, namespace: Optional[str] = None) -> str:
    """Execute kubectl command and return output."""
    cmd = ["kubectl"]
    if namespace:
        cmd.extend(["-n", namespace])
    cmd.extend(args)
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        print(f"kubectl error: {result.stderr}", file=sys.stderr)
    return result.stdout


def get_namespaces() -> list:
    """Get all non-system namespaces."""
    output = run_kubectl(["get", "namespaces", "-o", "json"])
    ns_data = json.loads(output)
    system_ns = {"kube-system", "kube-public", "kube-node-lease", "calico-system", "tigera-operator"}
    return [
        ns["metadata"]["name"]
        for ns in ns_data["items"]
        if ns["metadata"]["name"] not in system_ns
    ]


def get_network_policies(namespace: str) -> list:
    """Get all network policies in a namespace."""
    output = run_kubectl(["get", "networkpolicy", "-o", "json"], namespace=namespace)
    if not output.strip():
        return []
    data = json.loads(output)
    return data.get("items", [])


def check_default_deny(namespace: str) -> dict:
    """Check if default deny policies exist for a namespace."""
    policies = get_network_policies(namespace)
    has_ingress_deny = False
    has_egress_deny = False

    for pol in policies:
        spec = pol.get("spec", {})
        selector = spec.get("podSelector", {})
        policy_types = spec.get("policyTypes", [])

        if selector == {} or selector.get("matchLabels") is None:
            if "Ingress" in policy_types and not spec.get("ingress"):
                has_ingress_deny = True
            if "Egress" in policy_types and not spec.get("egress"):
                has_egress_deny = True

    return {
        "namespace": namespace,
        "default_deny_ingress": has_ingress_deny,
        "default_deny_egress": has_egress_deny,
        "policy_count": len(policies),
    }


def audit_all_namespaces() -> list:
    """Audit all namespaces for network policy coverage."""
    namespaces = get_namespaces()
    results = []
    for ns in namespaces:
        result = check_default_deny(ns)
        results.append(result)
    return results


def generate_default_deny(namespace: str) -> str:
    """Generate default deny ingress and egress policies for a namespace."""
    policies = [
        {
            "apiVersion": "networking.k8s.io/v1",
            "kind": "NetworkPolicy",
            "metadata": {
                "name": "default-deny-ingress",
                "namespace": namespace,
            },
            "spec": {
                "podSelector": {},
                "policyTypes": ["Ingress"],
            },
        },
        {
            "apiVersion": "networking.k8s.io/v1",
            "kind": "NetworkPolicy",
            "metadata": {
                "name": "default-deny-egress",
                "namespace": namespace,
            },
            "spec": {
                "podSelector": {},
                "policyTypes": ["Egress"],
            },
        },
        {
            "apiVersion": "networking.k8s.io/v1",
            "kind": "NetworkPolicy",
            "metadata": {
                "name": "allow-dns-egress",
                "namespace": namespace,
            },
            "spec": {
                "podSelector": {},
                "policyTypes": ["Egress"],
                "egress": [
                    {
                        "ports": [
                            {"protocol": "UDP", "port": 53},
                            {"protocol": "TCP", "port": 53},
                        ]
                    }
                ],
            },
        },
    ]

    return yaml.dump_all(policies, default_flow_style=False)


def generate_allow_policy(
    namespace: str,
    name: str,
    target_labels: dict,
    source_labels: dict,
    port: int,
    protocol: str = "TCP",
) -> str:
    """Generate an allow ingress policy."""
    policy = {
        "apiVersion": "networking.k8s.io/v1",
        "kind": "NetworkPolicy",
        "metadata": {
            "name": name,
            "namespace": namespace,
        },
        "spec": {
            "podSelector": {"matchLabels": target_labels},
            "policyTypes": ["Ingress"],
            "ingress": [
                {
                    "from": [{"podSelector": {"matchLabels": source_labels}}],
                    "ports": [{"protocol": protocol, "port": port}],
                }
            ],
        },
    }
    return yaml.dump(policy, default_flow_style=False)


def print_audit_report(results: list):
    """Print formatted audit report."""
    print("\n=== Kubernetes Network Policy Audit Report ===\n")
    print(f"{'Namespace':<30} {'Deny Ingress':<15} {'Deny Egress':<15} {'Policies':<10} {'Status'}")
    print("-" * 85)

    compliant = 0
    total = len(results)

    for r in results:
        ingress = "YES" if r["default_deny_ingress"] else "NO"
        egress = "YES" if r["default_deny_egress"] else "NO"
        status = "COMPLIANT" if r["default_deny_ingress"] and r["default_deny_egress"] else "NON-COMPLIANT"
        if status == "COMPLIANT":
            compliant += 1
        print(f"{r['namespace']:<30} {ingress:<15} {egress:<15} {r['policy_count']:<10} {status}")

    print(f"\n{compliant}/{total} namespaces compliant with default-deny policy")


def main():
    parser = argparse.ArgumentParser(description="Calico Network Policy Manager")
    subparsers = parser.add_subparsers(dest="command")

    # Audit command
    subparsers.add_parser("audit", help="Audit all namespaces for network policy coverage")

    # Generate deny command
    gen_deny = subparsers.add_parser("generate-deny", help="Generate default deny policies")
    gen_deny.add_argument("--namespace", "-n", required=True, help="Target namespace")
    gen_deny.add_argument("--apply", action="store_true", help="Apply policies directly")

    # Generate allow command
    gen_allow = subparsers.add_parser("generate-allow", help="Generate allow policy")
    gen_allow.add_argument("--namespace", "-n", required=True)
    gen_allow.add_argument("--name", required=True, help="Policy name")
    gen_allow.add_argument("--target-app", required=True, help="Target pod app label")
    gen_allow.add_argument("--source-app", required=True, help="Source pod app label")
    gen_allow.add_argument("--port", type=int, required=True, help="Allowed port")
    gen_allow.add_argument("--protocol", default="TCP", choices=["TCP", "UDP"])

    args = parser.parse_args()

    if args.command == "audit":
        results = audit_all_namespaces()
        print_audit_report(results)

    elif args.command == "generate-deny":
        policy_yaml = generate_default_deny(args.namespace)
        if args.apply:
            proc = subprocess.run(
                ["kubectl", "apply", "-f", "-"],
                input=policy_yaml,
                capture_output=True,
                text=True,
            )
            print(proc.stdout)
            if proc.stderr:
                print(proc.stderr, file=sys.stderr)
        else:
            print(policy_yaml)

    elif args.command == "generate-allow":
        policy_yaml = generate_allow_policy(
            namespace=args.namespace,
            name=args.name,
            target_labels={"app": args.target_app},
            source_labels={"app": args.source_app},
            port=args.port,
            protocol=args.protocol,
        )
        print(policy_yaml)

    else:
        parser.print_help()


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 1.9 KB
Keep exploring