Table of Contents
Concept and Scope of NetworkPolicies
Network policies in OpenShift define how pods are allowed to communicate with each other and with other network endpoints. They are Kubernetes NetworkPolicy objects, enforced by the cluster’s CNI plugin, and apply at the pod level (L3/L4 – IP and port).
Key points:
- Policies are namespaced: they only affect pods and traffic within a given namespace (plus ingress/egress across namespaces as selected).
- Policies are deny-by-default per selected pod: once any NetworkPolicy selects a pod for a given direction (ingress or egress), that pod’s traffic in that direction is allowed only as explicitly specified.
- Policies are additive: multiple policies combine to grant more allowed traffic. If any policy permits a particular connection, it’s allowed (subject to other controls like firewalls and Security Context Constraints).
Basic Structure of a NetworkPolicy
A NetworkPolicy resource has a standard shape:
podSelector: which pods this policy applies to.policyTypes:Ingress,Egress, or both.ingress: rules describing who can talk to the selected pods and on which ports.egress: rules describing where the selected pods can talk to and on which ports.
Minimal example – allow only HTTP traffic from pods labeled role=frontend to pods labeled role=backend in the same namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: my-app
spec:
podSelector:
matchLabels:
role: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 8080Interpretation:
- The policy applies to all pods with
role=backend. - For ingress traffic, only:
- Traffic from pods with
role=frontendin the same namespace, - To TCP port 8080,
is allowed. - Any other inbound traffic to
role=backendpods is denied (once this policy is in effect).
Selecting Pods and Traffic Sources
Target pods: `podSelector`
podSelector defines which pods are protected by the policy:
podSelector: {}(empty) → select all pods in the namespace.- Label-based selection → only pods matching those labels.
Example – apply to all pods in payments namespace:
spec:
podSelector: {}
policyTypes: [Ingress]
ingress:
- {} # some rulesExample – apply only to application pods:
spec:
podSelector:
matchLabels:
app.kubernetes.io/part-of: my-serviceSources/destinations: `from` and `to`
Within ingress and egress rules, you define where traffic can come from or go to using:
podSelector: pods (within the same namespace if nonamespaceSelectoris used).namespaceSelector: namespaces with particular labels.ipBlock: IP CIDRs, usually for external or non-pod traffic.
Pod-based selection
Allow traffic from any pod with label team=analytics in the same namespace:
ingress:
- from:
- podSelector:
matchLabels:
team: analyticsCross-namespace selection
Allow traffic from pods in namespaces labeled env=staging:
ingress:
- from:
- namespaceSelector:
matchLabels:
env: stagingCombine pod and namespace selectors to restrict to certain pods in specific namespaces:
ingress:
- from:
- namespaceSelector:
matchLabels:
env: staging
podSelector:
matchLabels:
role: api-clientIP-based selection (`ipBlock`)
ipBlock is useful to allow or deny traffic to/from external networks.
Allow traffic from a corporate subnet except a management range:
ingress:
- from:
- ipBlock:
cidr: 10.0.0.0/16
except:
- 10.0.10.0/24Notes:
ipBlockapplies to IP addresses, not pod labels.- It is commonly used to define access from on-prem networks, VPNs, or specific external ranges.
Controlling Ports and Protocols
Network policies operate at TCP/UDP/SCTP layer with optional ports:
ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 443
If ports is omitted:
- All ports are allowed for the specified source/destination.
- Protocol defaults to TCP if
portis a name; generally you should setprotocol.
You can also use named ports defined in the pod’s container spec:
ports:
- port: http # name defined in containerPortsIngress vs Egress Policies
Ingress policies
Control traffic into selected pods.
Example – only allow:
- Internal calls from namespace
frontend, - From pods with
role=web, - On port 443.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-ingress
namespace: payments
spec:
podSelector:
matchLabels:
app: payments-api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
team: frontend
podSelector:
matchLabels:
role: web
ports:
- protocol: TCP
port: 443
Once this policy is applied, all other inbound traffic to app=payments-api pods is blocked.
Egress policies
Control outbound traffic from selected pods.
Example – limit outgoing connections from app=payments-api to:
- The database namespace on port 5432.
- A specific external IP range for an API provider.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: payments-egress
namespace: payments
spec:
podSelector:
matchLabels:
app: payments-api
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
name: databases
podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
- to:
- ipBlock:
cidr: 203.0.113.0/24
ports:
- protocol: TCP
port: 443When at least one egress policy selects these pods, all other outgoing traffic is denied, including DNS, unless explicitly allowed.
Typical Policy Patterns in OpenShift
Default deny for a namespace
A common security posture is to deny all traffic by default and explicitly allow necessary flows.
Deny all ingress to pods in a namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: my-namespace
spec:
podSelector: {}
policyTypes:
- Ingress
ingress: []Deny all egress:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-egress
namespace: my-namespace
spec:
podSelector: {}
policyTypes:
- Egress
egress: []Use these as a baseline, then add more specific allow policies.
Allow same-namespace traffic
After a default deny, you might allow all pods in the same namespace to talk to each other:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace
namespace: my-namespace
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {}This still blocks cross-namespace and external traffic, while permitting intra-namespace communication.
Layered policies for a microservice
For a backend microservice:
- Default deny ingress for the namespace.
- Specific policy to allow ingress from only selected frontends.
- Specific egress policy to limit dependencies.
Example set:
# 1. Default deny
kind: NetworkPolicy
metadata:
name: default-deny
namespace: shop
spec:
podSelector: {}
policyTypes: [Ingress]
ingress: []
# 2. Allow frontend to backend
kind: NetworkPolicy
metadata:
name: allow-web-to-orders
namespace: shop
spec:
podSelector:
matchLabels:
app: orders
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 8080
# 3. Egress from backend only to db
kind: NetworkPolicy
metadata:
name: orders-egress
namespace: shop
spec:
podSelector:
matchLabels:
app: orders
policyTypes: [Egress]
egress:
- to:
- namespaceSelector:
matchLabels:
name: databases
podSelector:
matchLabels:
app: orders-db
ports:
- protocol: TCP
port: 5432OpenShift-Specific Considerations
Interaction with OpenShift SDN and OVN-K
OpenShift supports different network plugins (e.g. OpenShift SDN, OVN-Kubernetes). Both implement Kubernetes NetworkPolicy, but capabilities and performance characteristics can differ.
From a user perspective:
- Use standard
NetworkPolicymanifests. - Rely on label-based selection and namespaces; avoid relying on implementation-specific behavior.
- Be aware that some advanced features (e.g. additional CRDs or extended policy constructs) might be specific to one plugin and not portable.
Namespaces and tenant isolation
OpenShift commonly uses namespaces as an isolation boundary between teams or applications. Network policies:
- Strengthen tenant isolation by blocking cross-namespace pod-to-pod communication unless explicitly allowed.
- Work together with authorization (who can create/modify policies) to enforce isolation policies.
A typical multi-tenant pattern:
- Label each project/namespace with
tenant=<name>. - Apply default-deny ingress/egress per tenant.
- Explicitly create policies only where cross-tenant communication is required.
Interaction with Routes and external access
Network policies only control pod-level traffic. They do not directly manage:
- Which Routes expose services externally.
- External load balancer configuration.
However, they can affect traffic that originates from:
- Routers (ingress controllers) running as pods.
- Node-local processes or external IPs, depending on CNI behavior.
Practical implication:
- If you lock down ingress policies to only allow certain namespaces/pods, ensure that:
- Router or ingress pods are allowed by your
fromrules. - Any other in-cluster component that must access the service is included.
Example – allow traffic from the default router namespace to your app:
ingress:
- from:
- namespaceSelector:
matchLabels:
network.openshift.io/policy-group: ingress(Exact labels may vary; in practice you should check how router namespaces are labeled in your cluster.)
DNS and Egress Gotchas
When applying egress policies:
- Pods often need to reach:
- Cluster DNS (CoreDNS or equivalent).
- Cluster services (e.g. for service discovery, metrics).
- External APIs.
If you create a strict egress policy, you must explicitly allow DNS:
Example – allow DNS and specific external HTTPS:
spec:
podSelector:
matchLabels:
app: my-service
policyTypes: [Egress]
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: openshift-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
- to:
- ipBlock:
cidr: 198.51.100.0/24
ports:
- protocol: TCP
port: 443Labels and namespaces for DNS may differ; inspect your cluster to identify the correct selectors.
Designing Network Policies Incrementally
Because policies can easily block traffic, an incremental approach is safer:
- Start with observation
- Understand current communication patterns.
- Document which pods/services talk to which, and on what ports.
- Introduce soft boundaries
- Begin with less restrictive policies that allow known traffic patterns without yet adding global default denies.
- Use labels to structure your application:
tier,role,team, etc. - Add default deny per namespace
- After validating specific allow policies, introduce default deny for ingress (and optionally egress).
- Monitor for any broken flows and adjust rules.
- Refine and group
- Group common rules into reusable patterns (e.g. “allow monitoring namespace to scrape metrics”).
- Standardize labels across teams to simplify selectors.
Testing and Troubleshooting Network Policies
When a pod cannot reach another:
- Check whether any
NetworkPolicyexists in the source or destination namespace. - For ingress issues:
- Inspect policies with
policyTypes: Ingressthat select the destination pod. - Confirm that the source is allowed in
fromand correct port is open. - For egress issues:
- Inspect policies with
policyTypes: Egressthat select the source pod. - Confirm that the destination (pod, namespace, or IP) and port are allowed.
- Temporarily relax policies:
- As a diagnostic step, you can add a policy that broadens access (e.g. allow from all pods) to verify if policies are the root cause, then tighten again.
Example quick diagnostic policy (namespace-local ingress allow):
kind: NetworkPolicy
metadata:
name: debug-allow-all-ingress
namespace: my-namespace
spec:
podSelector: {}
policyTypes: [Ingress]
ingress:
- from:
- podSelector: {}Remove diagnostic policies once you identify the issue to restore your intended security posture.
Best Practices for Network Policies in OpenShift
- Label consistently: define a standard label taxonomy for apps, tiers, teams, and environments to simplify policy rules.
- Default deny then allow: prefer explicit allowlists to implicit open access.
- Separate concerns:
- One policy for default deny.
- Separate policies for application flows, monitoring, logging, and external access.
- Version control policies: treat
NetworkPolicymanifests like application code (Git, review, testing). - Document intent: use
metadata.annotationsand descriptive names to clarify what each policy is supposed to do. - Coordinate with other security layers: NetworkPolicies complement, not replace:
- Role-Based Access Control.
- Security Context Constraints.
- Image security and compliance settings.
- Periodically review: remove obsolete rules, check for overly permissive patterns, and ensure new services are covered.
These practices help you use network policies as an effective, maintainable tool for in-cluster traffic control and multi-tenant isolation in OpenShift.