--- id: RAIL-K8S-WP-0003 type: workplan title: "Private-by-default networking until an exposure grant exists" domain: financials repo: rail-kubernetes status: finished owner: codex topic_slug: railiance created: "2026-08-15" updated: "2026-08-22" related: - RMASTER-WP-0023 - ADR-0008 - ADR-0006 state_hub_workstream_id: "55ab4f28-b6a8-53ad-8ea4-2dc8fe91b681" --- # RAIL-K8S-WP-0003 — private-by-default networking Intake from `RMASTER-WP-0023-T05`. Until this rail owns live networking, `railiance-cluster` may implement the same controls and back-link here. ## Goal Enforce ADR-0008 on the Kubernetes rail: ClusterIP is the paved Service; default-deny NetworkPolicy; no public Ingress unless the rapp has `exposure.posture: public`, a grant, `binding_admission: production-approved`, and the reef has granted a public surface. Do not define what production-safe means (ADR-0006). Do not open `6443`. ## T01 — Pave ClusterIP and default-deny ```task id: RAIL-K8S-WP-0003-T01 status: done priority: high state_hub_task_id: "8c4c7c80-0c5e-5498-8054-ec47acb5b4ba" ``` Templates and overlays emit ClusterIP and default-deny NetworkPolicy. Document the operator/tunnel path for debug. Missing `exposure` means `private`. **Done when:** a new rapp on this rail has no public listener unless a grant exists. **Outcome (2026-08-22):** generated overlays now render an explicit `ClusterIP` Service, a default-deny ingress `NetworkPolicy`, and no Ingress by default. The scaffold regression test renders the generated chart and asserts all three properties. ## T02 — Gate public Ingress on the grant ```task id: RAIL-K8S-WP-0003-T02 status: done priority: high state_hub_task_id: "40f6f415-18f1-594e-ac21-35263b2aa532" ``` A public Ingress class is emitted only when the declaration carries a valid grant. An Ingress object is not itself a grant. **Done when:** an ungranted rapp cannot obtain a public Ingress from the paved path. **Outcome (2026-08-22):** Stage 2 renders the chart before server dry-run or apply and validates any Ingress/IngressRoute against both the rapp and reef declarations. Private is the fail-closed default; a public surface requires a matching grant and `production-approved` binding admission. Regression tests cover missing, mismatched, and valid grants. ## Completion evidence - `python3 -m unittest discover -s tests -v`: 6 tests passed. - Generated overlay `tests/stage2-template.sh`: passed, including Helm render. - `bash -n tools/create_railiance_overlay_repo.sh`: passed.