fix(network-policies): default-deny CNP for external-secrets namespace #237

Merged
k.renner merged 2 commits from fix/issue-194 into master 2026-07-14 11:03:33 +00:00
Member

Fixes #194

What

Adds a default-deny-ingress CiliumNetworkPolicy for the external-secrets namespace (the cluster-wide ESO controller — currently the only namespace-scoped gap this issue found that isn't already being closed by another open PR, see below).

  • Ingress: intra-namespace (controller/webhook/cert-controller) + host/remote-node (kube-apiserver drives the ExternalSecret/SecretStore admission webhook and the controller's watch/create calls) + the standard ICMP allow.
  • Egress: intra-namespace, DNS, the openbao KV API on :8200 (verified — kubernetes/infrastructure/post-crds/openbao-clustersecretstore.yaml is the only ClusterSecretStore in this cluster, and openbao-ns.yaml already has a matching ingress grant for the external-secrets namespace), and host/remote-node for kube-apiserver (Secret writes, leader-election Lease, webhook CA-bundle updates). No world egress — nothing outside the cluster is a SecretStore target.
  • Registered in kubernetes/infrastructure/network-policies/kustomization.yaml.

Scope: dropped from this PR, already covered by other open PRs

The original finding (issue #194) also flagged woodpecker-ci (no CNP) and openbao (ingress-only, egress unenforced) — this branch initially fixed both, but:

  • PR #235 (fix/issue-193-woodpecker-ci-hardening) already adds kubernetes/infrastructure/network-policies/woodpecker-ci-ns.yaml, and deliberately leaves egress default-allow because enforcing egress in this namespace previously broke crowdsec (commit 66dbe9f) — context this branch didn't have. Rather than ship a second, conflicting, more-restrictive egress policy for the same file, that's deferred entirely to #235.
  • PR #231 (fix/issue-202-openbao-hardening) already adds an egress block to openbao-ns.yaml as part of a broader hardening pass (also including a world allowance for OIDC that this branch's version didn't have). Deferred to #231 to avoid a duplicate/conflicting diff on the same file.

Also NOT covered: music-assistant

Issue #194 asked to "cover music-assistant for consistency." Its only workload (kubernetes/apps/music-assistant/deployment.yaml) runs hostNetwork: true (required for L2 adjacency on the IoT VLAN — Snapcast/SlimProto/Sendspin audio-sync timing). A namespace-scoped CiliumNetworkPolicy cannot enforce anything against a hostNetwork pod: Cilium classifies its traffic under the node's reserved:host identity, not a distinct pod endpoint, so a CNP here would apply cleanly but enforce nothing — the same documented limitation already called out in rook-ceph-ns.yaml for the Ceph daemons ("outside Cilium endpoint policy entirely — rely on UDM-side firewalling"). Confirmed against the wiki (existing notes independently record this as a deliberate, already-known exception for this exact workload) and against this cluster's config (no CiliumClusterwideNetworkPolicy/host-firewall deployed anywhere). Real enforcement would need host-firewall, which also touches the Ceph daemons and CNI components sharing hostNetwork — out of this issue's scope. Noted in a comment on #194 rather than merged here.

Verification

kubectl kustomize kubernetes/infrastructure/network-policies renders cleanly (no cluster access needed); openbao-ns.yaml in this branch is now byte-identical to origin/master (the only diff left is the new external-secrets-ns.yaml file + its kustomization.yaml entry).

🤖 Generated with Claude Code

Fixes #194 ## What Adds a `default-deny-ingress` CiliumNetworkPolicy for the `external-secrets` namespace (the cluster-wide ESO controller — currently the only namespace-scoped gap this issue found that isn't already being closed by another open PR, see below). - Ingress: intra-namespace (controller/webhook/cert-controller) + host/remote-node (kube-apiserver drives the ExternalSecret/SecretStore admission webhook and the controller's watch/create calls) + the standard ICMP allow. - Egress: intra-namespace, DNS, the openbao KV API on `:8200` (verified — `kubernetes/infrastructure/post-crds/openbao-clustersecretstore.yaml` is the *only* `ClusterSecretStore` in this cluster, and `openbao-ns.yaml` already has a matching ingress grant for the `external-secrets` namespace), and host/remote-node for kube-apiserver (Secret writes, leader-election Lease, webhook CA-bundle updates). No `world` egress — nothing outside the cluster is a SecretStore target. - Registered in `kubernetes/infrastructure/network-policies/kustomization.yaml`. ## Scope: dropped from this PR, already covered by other open PRs The original finding (issue #194) also flagged `woodpecker-ci` (no CNP) and `openbao` (ingress-only, egress unenforced) — this branch initially fixed both, but: - **PR #235** (`fix/issue-193-woodpecker-ci-hardening`) already adds `kubernetes/infrastructure/network-policies/woodpecker-ci-ns.yaml`, and deliberately leaves egress default-allow because enforcing egress in this namespace previously broke crowdsec (commit `66dbe9f`) — context this branch didn't have. Rather than ship a second, conflicting, more-restrictive egress policy for the same file, that's deferred entirely to #235. - **PR #231** (`fix/issue-202-openbao-hardening`) already adds an egress block to `openbao-ns.yaml` as part of a broader hardening pass (also including a `world` allowance for OIDC that this branch's version didn't have). Deferred to #231 to avoid a duplicate/conflicting diff on the same file. ## Also NOT covered: music-assistant Issue #194 asked to "cover music-assistant for consistency." Its only workload (`kubernetes/apps/music-assistant/deployment.yaml`) runs `hostNetwork: true` (required for L2 adjacency on the IoT VLAN — Snapcast/SlimProto/Sendspin audio-sync timing). A namespace-scoped CiliumNetworkPolicy cannot enforce anything against a hostNetwork pod: Cilium classifies its traffic under the node's `reserved:host` identity, not a distinct pod endpoint, so a CNP here would apply cleanly but enforce nothing — the same documented limitation already called out in `rook-ceph-ns.yaml` for the Ceph daemons ("outside Cilium endpoint policy entirely — rely on UDM-side firewalling"). Confirmed against the wiki (existing notes independently record this as a deliberate, already-known exception for this exact workload) and against this cluster's config (no `CiliumClusterwideNetworkPolicy`/host-firewall deployed anywhere). Real enforcement would need host-firewall, which also touches the Ceph daemons and CNI components sharing hostNetwork — out of this issue's scope. Noted in a comment on #194 rather than merged here. ## Verification `kubectl kustomize kubernetes/infrastructure/network-policies` renders cleanly (no cluster access needed); `openbao-ns.yaml` in this branch is now byte-identical to `origin/master` (the only diff left is the new `external-secrets-ns.yaml` file + its `kustomization.yaml` entry). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-bot added 2 commits 2026-07-14 10:15:31 +00:00
Closes the gaps from #194: external-secrets and woodpecker-ci had no
CiliumNetworkPolicy at all (Cilium defaults to allow-all absent one), and
openbao-ns.yaml had an ingress-only CNP with no egress block, which leaves
egress from openbao pods completely unenforced rather than merely permissive.

- external-secrets-ns.yaml: default-deny-ingress; egress scoped to the
  namespace, DNS, and the openbao KV API on :8200 (the only
  ClusterSecretStore in this cluster) plus host/remote-node for
  kube-apiserver (webhook admission, Secret watch/create, leader election).
- woodpecker-ci-ns.yaml: this namespace hosts only ephemeral, sometimes
  privileged, pipeline-defined step pods (no static workloads) — the goal
  is blocking lateral movement into unrelated namespaces while still
  allowing the git clone (east-west via k8s_gateway -> traefik, since
  gitea.renner.uno now has an in-cluster Ingress) and the broad internet
  egress every CI system legitimately needs (image pulls, package
  managers).
- openbao-ns.yaml: added an egress block (DNS, intra-namespace, host/
  remote-node for the k8s auth method + kubelet) so egress enforcement is
  actually turned on, not just missing.

music-assistant is deliberately NOT covered here: its only workload runs
hostNetwork: true, so a namespace-scoped CiliumNetworkPolicy would select
no enforceable pod-network endpoint and be a no-op (same documented
limitation as the rook-ceph Ceph daemons). Left for a comment on #194
rather than shipping a policy that looks protective but isn't.

Co-Authored-By: Claude Bot <claude-bot@renner.uno>
PR #235 (fix/issue-193-woodpecker-ci-hardening) already adds
kubernetes/infrastructure/network-policies/woodpecker-ci-ns.yaml, with a
deliberately different (and better-informed) egress design: default-allow,
because a past attempt at egress enforcement in this namespace broke
crowdsec (66dbe9f) — history this branch didn't have context on.

PR #231 (fix/issue-202-openbao-hardening) already adds an egress block to
openbao-ns.yaml as part of a broader OpenBao hardening pass, including a
"world" allowance (OIDC) this branch's version omitted.

Dropping both here to avoid shipping a conflicting/duplicate policy;
external-secrets-ns.yaml (this issue's third gap, not covered by any other
open PR) is the only change left on this branch.

Co-Authored-By: Claude Bot <claude-bot@renner.uno>
k.renner merged commit 53d08c3e1c into master 2026-07-14 11:03:33 +00:00
Sign in to join this conversation.