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 specifiespolicyTypes: [Ingress, Egress]
but provides noingress
oregress
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 aningress
rule allowing trafficfrom
any Pod in the same namespace (podSelector: {}
). Define anegress
rule allowing trafficto
any Pod in the same namespace (podSelector: {}
). Omitports
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 labeledapp=backend
on TCP port 8080. Deny all other ingress to the backend Pods.Strategy: Create a policy selecting the
backend
Pods. SetpolicyTypes: [Ingress]
. Define oneingress
rule allowing trafficfrom
Pods matchingapp=frontend
(podSelector
) to the specificport
(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 labeledapp=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. SetpolicyTypes: [Egress]
. Define twoegress
rules (remember, rules within the list are ORed):Rule 1: Allows traffic
to
Pods matchingapp=database
(podSelector
) onport
TCP 5432.Rule 2: Allows traffic
to
any destination (omitto
or usepodSelector: {}
) but only onports
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 labeledrole=ingress-gateway
on TCP port 443.Strategy: Create a policy selecting the
ingress-gateway
Pods.policyTypes: [Ingress]
. Define oneingress
rule allowing trafficfrom
anipBlock
specifying the CIDR, to the specificport
(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?