Kubernetes NetworkPolicy allow loadbalancer

Viewed 3546

I have a Kubernetes cluster running on Google Kubernetes Engine (GKE) with network policy support enabled. I created an nginx deployment and load balancer for it:

kubectl run nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=LoadBalancer

Then I created this network policy to make sure other pods in the cluster won't be able to connect to it anymore:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: access-nginx
spec:
  podSelector:
    matchLabels:
      run: nginx
  ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            name: kube-system
    ports:
    - protocol: TCP
      port: 80

Now other pods in my cluster can't reach it (as intended):

kubectl run busybox --rm -ti --image=busybox /bin/sh
If you don't see a command prompt, try pressing enter.
/ # wget --spider --timeout=1 nginx
Connecting to nginx (10.63.254.50:80)
wget: download timed out

However, it surprised me that using my external browser I also can't connect anymore to it through the load balancer:

open http://$(kubectl get svc nginx --output=jsonpath={.status.loadBalancer.ingress[0].ip})

If I delete the policy it starts to work again.

So, my question is: how do I block other pods from reaching nginx, but keep access through the load balancer open?

3 Answers

I recently had to do something similar. I needed a policy that didn't allow pods from other namespaces to talk to prod, but did allow the LoadBalancer services to reach pods in prod. Here's what worked (based on Ahmet's post):

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: isolate-prod
  namespace: prod
spec:
  podSelector: {}
  ingress:
  - from:
    - podSelector: {}
  - from:
    - ipBlock:
        cidr: '0.0.0.0/0'
        except: ['10.0.0.0/8']

I'd like to share solution that builds on the excellent answers of @ahmetb-google and @tammer-saleh

The situation: 1 cluster, 4 namespaces, a public HTTPS-terminating Ingress for 3 of the namespaces that allows specific traffic inbound and routes it appropriately.

Goal: Block all inter-namespace traffic, allow only public traffic coming in via the Ingress.

Problem: When deploying a "deny from other namespaces" rule, that it also denied traffic from my Ingress so the pods were not accessible from the outside.

Solution:

I created an additional policy to allow only port 80 traffic targetting pods with the label role=web. It uses the allow/except trick to still block traffic from other namespaces but allow it from the public ingresses.

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-http-from-ingress
spec:
  podSelector:
    matchLabels:
      role: web
  ingress:
    - from:
      - ipBlock:
          cidr: '0.0.0.0/0'
          except: ['10.0.0.0/8']
      ports:
        - port: 80

With this deployed, traffic still flows from the public, via the Ingress, to the web-serving pods. All inter-namespace traffic is blocked, including HTTP.

This is a useful setup when for example you're using namespaces for different deployment stages (production, testing, edge, etc) and you have private HTTP APIs that you would not want to accidentally hit cross-stage.

Related