14.3 Restricting Ingress from Specific Pods and Namespaces
Right, so you’ve got a cluster up and running, pods are chatting away happily, and now you need to introduce some law and order. You don’t want every pod in the dev namespace being able to poke at your super-secret payment-processor pod, do you? Of course not. This is where you stop being a benevolent creator and start being a traffic cop with a badge and a serious attitude.
Network Policies are your Kubernetes-native tool for this job. They’re not some add-on; they’re first-class citizens that define how pods are allowed to communicate with each other and other network endpoints. Think of them as firewall rules for your cluster, but way more pod-aware. The crucial thing to remember is that by default, if no Network Policies are present, all traffic is allowed. It’s a “default allow” world. The moment you slap a Network Policy on a pod, you shift it into a “default deny” mode for that direction (ingress/egress), and you must explicitly allow what you want. This trips up everyone at least once.
Isolating a Pod from a Specific Namespace
Let’s say you have a prod namespace that should be a digital fortress, and you want to block all incoming traffic from the notoriously noisy testing namespace. Here’s how you lay down the law.
First, you need a policy that selects your precious prod pods. We’ll use a podSelector. Then, you’ll write an ingress rule that says, “Only allow traffic if it’s NOT from the testing namespace.” You do this with a namespaceSelector.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: block-testing-namespace
namespace: prod # This is critical! The policy is enforced in this namespace.
spec:
podSelector:
matchLabels: {} # This selects ALL pods in the 'prod' namespace.
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: NotIn
values:
- testing
- namespaceSelector: {} # This allows traffic from all other namespaces
podSelector: {} # and all pods within them.
Wait, why two entries in the from array? This is one of those “questionable choices” in the API design. The rules inside a single from (or to) block are evaluated with OR logic. Each block is a potential source. So this policy reads as: “Allow ingress from sources that are EITHER (a) in a namespace whose name is not ’testing’ OR (b) from any pod in any namespace.” That second rule is redundant because the first one already allows all non-testing namespaces. A more precise way to write this is to use a single rule with a negative match inside the namespaceSelector.
A better, more explicit example for only allowing the prod namespace itself would be:
...
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: prod
podSelector: {} # Allow all pods within the 'prod' namespace
Targeting a Specific Rogue Pod
Maybe the problem isn’t an entire namespace, but one specific pod, like chaos-monkey-v2. You need to block it, but only from talking to your database. This is where combining namespaceSelector and podSelector in a single from block gets interesting. The designers decided that within a single from block, the selectors are additive (AND logic).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: block-chaos-monkey-from-db
namespace: prod
spec:
podSelector:
matchLabels:
app: database # This policy only protects our database pod
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: prod
podSelector:
matchLabels:
app: not-chaos-monkey # Label your good pods with this
This rule says: “Allow traffic from pods that are BOTH in the prod namespace AND have the label app=not-chaos-monkey.” The chaos-monkey-v2 pod, lacking that label, is effectively banned. The key insight here is that the podSelector inside a from block is scoped to the namespaces already selected by the namespaceSelector. It does not select pods in all namespaces unless you explicitly set the namespaceSelector to {} (select all namespaces).
Common Pitfalls and How to Avoid Them
The Default Deny Trap: You create a policy that only allows traffic from namespace
A. You’ve forgotten thatkube-system(CoreDNS, metrics scrapers, etc.) is also a namespace. Suddenly, your pod can’t resolve DNS. Always remember to explicitly allow crucial cluster infrastructure. You’ll often need a rule allowing DNS (UDP/TCP port 53 tokube-systempods) and maybe your monitoring stack.The Missing
namespaceField: This one will burn you. Thenamespacefield in the NetworkPolicy metadata is what defines where the rule is applied. If you forget it and apply the policy from akubectlcontext set todefault, you’ve just created a policy in thedefaultnamespace that selects nothing. Always double-check the policy’s namespace.Label Your Pods, Seriously: Network Policies are utterly dependent on labels. If your pods don’t have consistent, predictable labels, you’re going to have a bad time. This is not the place for creativity. Be boring and consistent with your labels (
app,component,tier).The CNI Actually Matters: This is the big one. Network Policies are just an API object. They are enforced by the Container Network Interface (CNI) plugin you installed in your cluster, like Calico, Cilium, or WeaveNet. If your cluster doesn’t have a CNI that supports Network Policies, applying these manifests will do absolutely nothing. It’s like writing a speeding ticket and then not having any police to enforce it. Always confirm your CNI provider first.
The power is immense, but the responsibility is yours. Start with a default-deny policy for a namespace and then slowly add rules to allow only what is necessary. It’s the only way to be sure.