NetApp / trident

Storage orchestrator for containers
Apache License 2.0
760 stars 222 forks source link

Document/include a default NetworkPolicy #638

Open mac-chaffee opened 3 years ago

mac-chaffee commented 3 years ago

Describe the solution you'd like NetworkPolicies are recommended by the CIS Benchmarks for Kubernetes (login required), as well as NSA/CISA (page 14)

They can restrict other pods from sending traffic to pods in the trident namespace, which I think might be a good secure default.

Describe alternatives you've considered I've tried installing my own networkpolicy separately:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-namespace-isolation
spec:
  podSelector: {}  # Empty to select all pods
  ingress:
  - from:
    # Allow traffic from all pods in this namespace
    - podSelector: {}

But that doesn't work properly since the DaemonSet pods use host networking, and with the above policy applied, they fail to register themselves:

time="2021-09-03T20:09:16Z" level=warning msg="Could not update Trident controller with node registration, will retry." error="could not log into the Trident CSI Controller: error communicating with Trident CSI Controller; Put \"https://10.21.55.192:34571/trident/v1/node/k8s-node15\": dial tcp 10.21.55.192:34571: i/o timeout (Client.Timeout exceeded while awaiting headers)" increment=5.161863686s requestID=624ac1aa-cfda-47fc-9d0c-6a9f2dd4490d requestSource=Internal

Even allowing that 34571 port, or 8443, or the whole node subnet don't seem to work. So it seems crafting a good networkpolicy for Trident requires a bit of domain-specific knowledge. So maybe including one in the helm chart (could be disabled by default) would be a good idea.

Additional context Here's an example of another helm chart that ships with a NetworkPolicy: https://github.com/elastic/helm-charts/blob/13c7014a25f2894673f0c272def1422720453f9f/elasticsearch/values.yaml#L283

bswartz commented 3 years ago

For the node (daemonset) pods, they are privileged, and you can't apply a network policy to them. They have root on the node and access to the host's network namespace. If you wanted to protect them you'd need a host-level firewall which would operate at the level below Kubernetes. Fortunately they don't listen on the network and there is no network attack surface to worry about.

mac-chaffee commented 3 years ago

I'm more worried about having a NetworkPolicy to protect the trident controller API. It is protected by client TLS by default, but having a NetworkPolicy would be a nice defense-in-depth layer. Because if the client cert is leaked, someone with access to that API could wreak havoc: https://github.com/NetApp/trident/blob/a86d4ae04a29c66be0c24cf38d805fec27096f4e/frontend/rest/controller_routes.go

Although the fact that the daemonset pods use host networking does complicate the NetworkPolicy, since we'd have to know the node CIDR.

The following NetworkPolicy works in my cluster to restrict other pods from accessing the controller:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-node-ips
spec:
  ingress:
  # Allow all intra-namespace traffic
  - from:
    - podSelector: {}
 # Allow all traffic from node IPs
  - from:
    - ipBlock:
        cidr: <CIDR that contains all your node IPs>
  podSelector:
    matchLabels:
      app: controller.csi.trident.netapp.io
  policyTypes:
  - Ingress

(the rule allowing inter-namespace traffic might not even be necessary) So maybe this network policy could be documented somewhere rather than included in the helm chart.

mac-chaffee commented 2 years ago

I also noticed that if the host has access to IP addresses/LIFs on the SVM, then user pods also have access. So users could curl <managementLIF>, or even read/write to arbitrary NFS volumes (even in non-privileged pods if they use a user-space NFS client). This is probably worth a mention in the documentation.

If you're using Calico as your CNI, you can apply the following GlobalNetworkPolicy to only allow pods in the trident namespace to access LIFs:

apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: block-storage-access
spec:
  namespaceSelector: "kubernetes.io/metadata.name != 'trident'"
  selector: "all()"
  types:
  - Egress
  egress:
  - action: Allow
    destination:
      notNets:
      - <managmentLIF>/32
      - <dataLIF>/32
      - ...