Implementing Common Security Patterns

Understanding the NetworkPolicy structure and the rule logic allows us to implement various security postures. Let's look at some common patterns and how you would construct the relevant parts of the networkingv1.NetworkPolicySpec in Go. Remember, these policies need a CNI plugin that enforces them (like Calico, Cilium, etc.) to be effective.

Pattern 1: Default Deny All (Namespace Isolation)

  • Goal: Block all ingress and all egress traffic for every Pod in a specific namespace. This creates a "zero-trust" starting point for the namespace.

  • Strategy: Create a NetworkPolicy that selects all Pods in the namespace (podSelector: {}) and specifies policyTypes: [Ingress, Egress] but provides no ingress or egress rules.

  • YAML Equivalent:

    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: default-deny-all
      namespace: my-secure-namespace
    spec:
      podSelector: {} # Selects all pods
      policyTypes:
      - Ingress
      - Egress
      # No ingress rules defined -> Deny all ingress
      # No egress rules defined -> Deny all egress
  • Go spec Construction:

    import (
        networkingv1 "k8s.io/api/networking/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    )
    
    policySpec := networkingv1.NetworkPolicySpec{
        // Select all pods in the namespace
        PodSelector: metav1.LabelSelector{}, // Empty LabelSelector selects everything
        // Apply policy to both ingress and egress directions
        PolicyTypes: []networkingv1.PolicyType{
            networkingv1.PolicyTypeIngress,
            networkingv1.PolicyTypeEgress,
        },
        // Ingress rules list is nil/empty - blocks all ingress
        Ingress: []networkingv1.NetworkPolicyIngressRule{}, // Or simply omit
        // Egress rules list is nil/empty - blocks all egress
        Egress: []networkingv1.NetworkPolicyEgressRule{}, // Or simply omit
    }
    
    // Create NetworkPolicy object with this spec...
    // clientset.NetworkingV1().NetworkPolicies(namespace).Create(...)

Pattern 2: Allow All Within Namespace (After Default Deny)

  • Goal: After implementing a default deny, explicitly allow all Pods within the same namespace to communicate freely with each other on any port, while still blocking traffic to/from other namespaces or external IPs.

  • Strategy: Create a policy selecting all Pods (podSelector: {}). Define an ingress rule allowing traffic from any Pod in the same namespace (podSelector: {}). Define an egress rule allowing traffic to any Pod in the same namespace (podSelector: {}). Omit ports to allow all ports.

  • YAML Equivalent:

    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-intra-namespace
      namespace: my-secure-namespace
    spec:
      podSelector: {} # Apply to all pods
      policyTypes:
      - Ingress
      - Egress
      ingress:
      - from:
        - podSelector: {} # Allow from any pod in this namespace
      egress:
      - to:
        - podSelector: {} # Allow to any pod in this namespace
  • Go spec Construction:

    policySpec := networkingv1.NetworkPolicySpec{
        PodSelector: metav1.LabelSelector{},
        PolicyTypes: []networkingv1.PolicyType{
            networkingv1.PolicyTypeIngress,
            networkingv1.PolicyTypeEgress,
        },
        Ingress: []networkingv1.NetworkPolicyIngressRule{
            { // Rule allowing ingress from same namespace
                From: []networkingv1.NetworkPolicyPeer{
                    {
                        // Empty PodSelector within From/To defaults to all pods
                        // in the policy's namespace.
                        PodSelector: &metav1.LabelSelector{},
                    },
                },
                // No Ports specified = allow all ports
            },
        },
        Egress: []networkingv1.NetworkPolicyEgressRule{
            { // Rule allowing egress to same namespace
                To: []networkingv1.NetworkPolicyPeer{
                    {
                        PodSelector: &metav1.LabelSelector{},
                    },
                },
                // No Ports specified = allow all ports
            },
        },
    }

Pattern 3: Allow Specific Ingress (e.g., Frontend to Backend)

  • Goal: Allow ingress traffic only from Pods labeled app=frontend to Pods labeled app=backend on TCP port 8080. Deny all other ingress to the backend Pods.

  • Strategy: Create a policy selecting the backend Pods. Set policyTypes: [Ingress]. Define one ingress rule allowing traffic from Pods matching app=frontend (podSelector) to the specific port (TCP 8080).

  • YAML Equivalent:

    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: backend-allow-frontend
      namespace: my-app
    spec:
      podSelector: # Apply to backend pods
        matchLabels:
          app: backend
      policyTypes: [Ingress]
      ingress:
      - from:
        - podSelector: # Allow ONLY from frontend pods
            matchLabels:
              app: frontend
        ports: # ONLY to this port/protocol
        - protocol: TCP
          port: 8080
  • Go spec Construction:

    tcp := v1.ProtocolTCP // Use v1 from k8s.io/api/core/v1
    port8080 := intstr.FromInt(8080) // Use intstr from k8s.io/apimachinery/pkg/util/intstr
    
    policySpec := networkingv1.NetworkPolicySpec{
        PodSelector: metav1.LabelSelector{
            MatchLabels: map[string]string{"app": "backend"},
        },
        PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
        Ingress: []networkingv1.NetworkPolicyIngressRule{
            {
                From: []networkingv1.NetworkPolicyPeer{
                    {
                        PodSelector: &metav1.LabelSelector{
                            MatchLabels: map[string]string{"app": "frontend"},
                        },
                        // NamespaceSelector omitted = defaults to policy's namespace
                    },
                },
                Ports: []networkingv1.NetworkPolicyPort{
                    {
                        Protocol: &tcp, // Pointer to protocol type
                        Port:     &port8080, // Pointer to IntOrString port
                    },
                },
            },
        },
    }

Pattern 4: Allow Specific Egress (e.g., Backend to Database + DNS)

  • Goal: Allow Pods labeled app=backend to initiate connections only to Pods labeled app=database on TCP port 5432, and also allow them to make DNS queries (to any Pod on UDP/TCP port 53). Deny all other egress.

  • Strategy: Create a policy selecting backend Pods. Set policyTypes: [Egress]. Define two egress rules (remember, rules within the list are ORed):

    • Rule 1: Allows traffic to Pods matching app=database (podSelector) on port TCP 5432.

    • Rule 2: Allows traffic to any destination (omit to or use podSelector: {}) but only on ports UDP 53 and TCP 53.

  • YAML Equivalent:

    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: backend-allow-db-dns
      namespace: my-app
    spec:
      podSelector:
        matchLabels:
          app: backend
      policyTypes: [Egress]
      egress:
      - to: # Rule 1: Allow to database
        - podSelector:
            matchLabels:
              app: database
        ports:
        - protocol: TCP
          port: 5432
      - ports: # Rule 2: Allow DNS lookup (to any destination)
        - protocol: UDP
          port: 53
        - protocol: TCP # Some DNS uses TCP
          port: 53
  • Go spec Construction:

    tcp := v1.ProtocolTCP
    udp := v1.ProtocolUDP
    port5432 := intstr.FromInt(5432)
    port53 := intstr.FromInt(53)
    
    policySpec := networkingv1.NetworkPolicySpec{
        PodSelector: metav1.LabelSelector{
            MatchLabels: map[string]string{"app": "backend"},
        },
        PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeEgress},
        Egress: []networkingv1.NetworkPolicyEgressRule{
            // Rule 1: DB Access
            {
                To: []networkingv1.NetworkPolicyPeer{
                    {
                        PodSelector: &metav1.LabelSelector{
                            MatchLabels: map[string]string{"app": "database"},
                        },
                    },
                },
                Ports: []networkingv1.NetworkPolicyPort{
                    {Protocol: &tcp, Port: &port5432},
                },
            },
            // Rule 2: DNS Access
            {
                // 'To' is omitted = allow to any destination IP/Pod
                Ports: []networkingv1.NetworkPolicyPort{
                    {Protocol: &udp, Port: &port53},
                    {Protocol: &tcp, Port: &port53},
                },
            },
        },
    }

    Note: Allowing DNS is crucial. If you implement default egress deny, you must explicitly allow DNS traffic, otherwise, Pods won't be able to resolve Service names or external hostnames.

Pattern 5: Allow Ingress from Outside Cluster (IP Block)

  • Goal: Allow ingress traffic from a specific external IP range (e.g., 198.51.100.0/24, maybe a corporate network or monitoring service) to Pods labeled role=ingress-gateway on TCP port 443.

  • Strategy: Create a policy selecting the ingress-gateway Pods. policyTypes: [Ingress]. Define one ingress rule allowing traffic from an ipBlock specifying the CIDR, to the specific port (TCP 443).

  • YAML Equivalent:

    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-external-monitoring
      namespace: infra
    spec:
      podSelector:
        matchLabels:
          role: ingress-gateway
      policyTypes: [Ingress]
      ingress:
      - from:
        - ipBlock:
            cidr: 198.51.100.0/24
            # except: [ "198.51.100.10/32" ] # Optional exception
        ports:
        - protocol: TCP
          port: 443
  • Go spec Construction:

    tcp := v1.ProtocolTCP
    port443 := intstr.FromInt(443)
    
    policySpec := networkingv1.NetworkPolicySpec{
        PodSelector: metav1.LabelSelector{
            MatchLabels: map[string]string{"role": "ingress-gateway"},
        },
        PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
        Ingress: []networkingv1.NetworkPolicyIngressRule{
            {
                From: []networkingv1.NetworkPolicyPeer{
                    {
                        IPBlock: &networkingv1.IPBlock{
                            CIDR: "198.51.100.0/24",
                            // Except: []string{"198.51.100.10/32"}, // Optional
                        },
                    },
                },
                Ports: []networkingv1.NetworkPolicyPort{
                    {Protocol: &tcp, Port: &port443},
                },
            },
        },
    }

These patterns provide a starting point. By combining these techniques – default deny, specific allows based on pod/namespace selectors or IP blocks, and careful port/protocol specification – you can construct sophisticated Network Policies using client-go to enforce least-privilege network access for your Kubernetes applications.

Last updated

Was this helpful?