---
id: w7/m4/t002
title: Close the pod→node path — assess node-public-IP reachability and add a deny
worker: worker7
status: done
estimate: 60m
depends_on: [w7/m4/t001]
---

## Objective

The m1 egress rule allows `0.0.0.0/0` (minus RFC1918/CGNAT), so a tenant pod can still reach a **node's public IP** — including kubelet (`:10250`) and any nodePort bound on the public interface. The Hetzner cloud firewall only filters ingress from the internet, not pod→node traffic that originates inside the cluster, so it does not cover this. Assess what is actually reachable and add a deny that closes it without breaking legitimate external egress.

## Context

- CNI is **Cilium** (WireGuard node-to-node encryption live post-m19; `deploy/gitops` / `app-cluster.yml`). App-generated policies today are vanilla `networking.k8s.io/v1` NetworkPolicy (`app_controller.go` `reconcileNetworkPolicy`) — portable by design.
- Cilium exposes node/host identities as the `host` and `remote-node` entities, denyable via a `CiliumNetworkPolicy` `toEntities` rule — the portable-NetworkPolicy IPBlock model can't name "the nodes" (their public IPs aren't a contiguous owned range).
- kubelet read API is RBAC-gated but should not be reachable from tenant pods at all; nodePorts on the public interface likewise.

## Steps

1. From a tenant pod on the prod-shaped cluster, probe a node's public IP on `:10250` and a known nodePort — confirm current reachability (the hole) before changing anything.
2. Decide the mechanism: a platform-side `CiliumNetworkPolicy` denying tenant egress to the `host`/`remote-node` entities (Cilium is the CNI, so a Cilium-native platform policy is acceptable even though per-App policies stay vanilla), or an operator-emitted equivalent. Record the decision + rationale in `docs/ADR022-tenant-isolation.md`.
3. Implement the deny (platform gitops manifest or operator-generated policy, whichever the decision picks) scoped to the apps namespace / workspace-labeled pods.
4. Re-probe: node `:10250` / nodePort now blocked; same-workspace + DNS + external egress still work.

## Files

- `docs/ADR022-tenant-isolation.md` (decision + mechanism)
- `deploy/gitops/…` (if a platform CiliumNetworkPolicy) **or** `lego/operator/internal/controller/app_controller.go` (if operator-emitted)

## Acceptance criteria

- [ ] A tenant pod's connection to a node's public IP on `:10250` and on a nodePort is **blocked**.
- [ ] Same-workspace pod/service traffic, DNS, and genuine external egress are unaffected.
- [ ] The mechanism choice (Cilium entity deny vs operator-emitted) and its rationale are recorded in `docs/ADR022-tenant-isolation.md`.

## Out of scope

- Link-local / metadata (t001).
- Encrypting or firewalling node↔node traffic (already WireGuard; not this milestone).
