NK-WP-0056: sso ingress for the bounded executor; README and handoff follow-up
All checks were successful
All checks were successful
netpol-sso.yaml admits only executor pods to LLDAP 17170, KeyCape 8080 and Authelia 9091 (never LDAP 3890); test_netpol.py pins it. Prepared, not applied. README reflects T13. Live reads: OpenBao open to all namespaces; approval-engine and audit-core need pod-scoped rules (requested: hub messages 39a3bffa, deea5bf8). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 5502@bnt-lap001 Assistant-Session: 68b3b02f-5d0f-4e71-8421-796f62e50196
This commit is contained in:
parent
cd0ce905a2
commit
387f8b1c8b
4 changed files with 134 additions and 2 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
Runs one founder-approved Bounded Execution Admission (`canon/standards/bounded-execution-admission_v0.1.md`)
|
Runs one founder-approved Bounded Execution Admission (`canon/standards/bounded-execution-admission_v0.1.md`)
|
||||||
exactly once. Founder decision 6d1adce9: net-kingdom writes the executor; `rapp-bounded-executor` deploys it as one
|
exactly once. Founder decision 6d1adce9: net-kingdom writes the executor; `rapp-bounded-executor` deploys it as one
|
||||||
Job per admission in `sso` (T12).
|
Job per admission in `sso`.
|
||||||
|
|
||||||
`executor.py` is the core runner (T11):
|
`executor.py` is the core runner (T11):
|
||||||
|
|
||||||
|
|
@ -44,7 +44,14 @@ without guessing.
|
||||||
`synthetic_pilot.py` runs a rotation and a deliberate revert on the T08 stack plus OpenBao:
|
`synthetic_pilot.py` runs a rotation and a deliberate revert on the T08 stack plus OpenBao:
|
||||||
`uv run bounded-executor/synthetic_pilot.py`.
|
`uv run bounded-executor/synthetic_pilot.py`.
|
||||||
|
|
||||||
T13 adds the CLI, the Containerfile, the image workflow and the rapp package.
|
`cli.py` (T13) is the Job's entry point: one admission, one approval id, a non-secret config. Exit codes: `0`
|
||||||
|
completed, `3` reverted, `4` refused before any effect, `5` stopped, unknown or failed revert, `6` consumed but not in
|
||||||
|
custody. `Containerfile` builds the image, which `.forgejo/workflows/bounded-executor-image.yaml` publishes by digest.
|
||||||
|
|
||||||
|
Deployment is in `rapp-bounded-executor`, which pins the digest and renders one ConfigMap and Job per admission
|
||||||
|
(`make run`, founder-run). That package also holds the ServiceAccount and Role, copied from
|
||||||
|
`sso-mfa/k8s/bounded-executor/rbac.yaml`, and the executor's egress-only NetworkPolicy. The receiving side in `sso` is
|
||||||
|
`allow-bounded-executor-to-*` in `sso-mfa/k8s/network-policies/netpol-sso.yaml`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 -m pytest bounded-executor/tests # or: make bounded-executor-test
|
python3 -m pytest bounded-executor/tests # or: make bounded-executor-test
|
||||||
|
|
|
||||||
45
sso-mfa/k8s/bounded-executor/test_netpol.py
Normal file
45
sso-mfa/k8s/bounded-executor/test_netpol.py
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
"""The sso namespace admits the bounded executor exactly where its pilot steps go (NK-WP-0056). No cluster."""
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
NETPOL = pathlib.Path(__file__).resolve().parents[1] / "network-policies" / "netpol-sso.yaml"
|
||||||
|
EXECUTOR = {"app.kubernetes.io/name": "bounded-executor"}
|
||||||
|
EXPECTED = {("lldap", 17170), ("keycape", 8080), ("authelia", 9091)}
|
||||||
|
|
||||||
|
|
||||||
|
def policies():
|
||||||
|
return [d for d in yaml.safe_load_all(NETPOL.read_text()) if d]
|
||||||
|
|
||||||
|
|
||||||
|
def executor_ingress():
|
||||||
|
for pol in policies():
|
||||||
|
for rule in pol["spec"].get("ingress", []):
|
||||||
|
if any(peer.get("podSelector", {}).get("matchLabels") == EXECUTOR for peer in rule["from"]):
|
||||||
|
yield pol, rule
|
||||||
|
|
||||||
|
|
||||||
|
class ExecutorIngress(unittest.TestCase):
|
||||||
|
def test_exactly_the_pilot_paths(self): # in particular never LDAP :3890
|
||||||
|
found = set()
|
||||||
|
for pol, rule in executor_ingress():
|
||||||
|
target = pol["spec"]["podSelector"]["matchLabels"]["app.kubernetes.io/name"]
|
||||||
|
found |= {(target, p["port"]) for p in rule["ports"]}
|
||||||
|
self.assertEqual(found, EXPECTED)
|
||||||
|
|
||||||
|
def test_peers_are_in_namespace_executor_pods_only(self):
|
||||||
|
for pol, rule in executor_ingress():
|
||||||
|
self.assertEqual(pol["metadata"]["namespace"], "sso")
|
||||||
|
self.assertEqual(pol["spec"]["policyTypes"], ["Ingress"])
|
||||||
|
for peer in rule["from"]:
|
||||||
|
self.assertEqual(peer, {"podSelector": {"matchLabels": EXECUTOR}})
|
||||||
|
|
||||||
|
def test_default_deny_is_kept(self):
|
||||||
|
deny = [p for p in policies() if p["metadata"]["name"] == "default-deny-all"]
|
||||||
|
self.assertEqual(deny[0]["spec"], {"podSelector": {}, "policyTypes": ["Ingress", "Egress"]})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
# user-engine → authelia :9091 (read GET /api/state for an existing sign-in)
|
# user-engine → authelia :9091 (read GET /api/state for an existing sign-in)
|
||||||
# Traefik → authelia :9091 (login portal, user-facing)
|
# Traefik → authelia :9091 (login portal, user-facing)
|
||||||
# Traefik → lldap :17170 (admin web UI; IP-restricted at Traefik layer)
|
# Traefik → lldap :17170 (admin web UI; IP-restricted at Traefik layer)
|
||||||
|
# bounded-executor → lldap :17170, keycape :8080, authelia :9091 (NK-WP-0056 Job)
|
||||||
#
|
#
|
||||||
# Allowed egress paths:
|
# Allowed egress paths:
|
||||||
# keycape → authelia :9091 (OIDC callback orchestration)
|
# keycape → authelia :9091 (OIDC callback orchestration)
|
||||||
|
|
@ -374,6 +375,72 @@ spec:
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
|
|
||||||
---
|
---
|
||||||
|
# ── Bounded executor → LLDAP :17170, KeyCape :8080, Authelia :9091 ───────────
|
||||||
|
# NK-WP-0056: the per-admission executor Job (rapp-bounded-executor, label
|
||||||
|
# app.kubernetes.io/name=bounded-executor) self-rotates a bind through the LLDAP
|
||||||
|
# web API and runs the synthetic journey in-cluster against KeyCape and Authelia.
|
||||||
|
# The executor's own egress-only policy lives in rapp-bounded-executor. No pod
|
||||||
|
# exists unless an admitted Job is running.
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: allow-bounded-executor-to-lldap-api
|
||||||
|
namespace: sso
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: lldap
|
||||||
|
policyTypes:
|
||||||
|
- Ingress
|
||||||
|
ingress:
|
||||||
|
- from:
|
||||||
|
- podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: bounded-executor
|
||||||
|
ports:
|
||||||
|
- port: 17170
|
||||||
|
protocol: TCP
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: allow-bounded-executor-to-keycape
|
||||||
|
namespace: sso
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: keycape
|
||||||
|
policyTypes:
|
||||||
|
- Ingress
|
||||||
|
ingress:
|
||||||
|
- from:
|
||||||
|
- podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: bounded-executor
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
protocol: TCP
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: allow-bounded-executor-to-authelia
|
||||||
|
namespace: sso
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: authelia
|
||||||
|
policyTypes:
|
||||||
|
- Ingress
|
||||||
|
ingress:
|
||||||
|
- from:
|
||||||
|
- podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: bounded-executor
|
||||||
|
ports:
|
||||||
|
- port: 9091
|
||||||
|
protocol: TCP
|
||||||
|
---
|
||||||
# ── Allow egress DNS (all pods) ──────────────────────────────────────────────
|
# ── Allow egress DNS (all pods) ──────────────────────────────────────────────
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: NetworkPolicy
|
kind: NetworkPolicy
|
||||||
|
|
|
||||||
|
|
@ -576,6 +576,7 @@ Left:
|
||||||
- DONE 2026-10-31, founder: full server dry run with the admin kubeconfig. The ServiceAccount, Role, RoleBinding and
|
- DONE 2026-10-31, founder: full server dry run with the admin kubeconfig. The ServiceAccount, Role, RoleBinding and
|
||||||
NetworkPolicy were applied server-side as a dry run, and the synthetic BEA-2026-0001 ConfigMap and Job were created
|
NetworkPolicy were applied server-side as a dry run, and the synthetic BEA-2026-0001 ConfigMap and Job were created
|
||||||
as a dry run. Nothing was persisted.
|
as a dry run. Nothing was persisted.
|
||||||
|
|
||||||
**Done 2026-10-31.**
|
**Done 2026-10-31.**
|
||||||
|
|
||||||
- The image workflow for `ba9d266` succeeded, and the digest
|
- The image workflow for `ba9d266` succeeded, and the digest
|
||||||
|
|
@ -589,6 +590,18 @@ Follow-up outside the done criteria:
|
||||||
|
|
||||||
- Founder: create the Forgejo remote `coulomb/rapp-bounded-executor` and push.
|
- Founder: create the Forgejo remote `coulomb/rapp-bounded-executor` and push.
|
||||||
|
|
||||||
|
Follow-up 2026-10-31 for T10/T06 reachability:
|
||||||
|
|
||||||
|
- `netpol-sso.yaml` gained `allow-bounded-executor-to-{lldap-api,keycape,authelia}`, which admit only executor pods,
|
||||||
|
on 17170, 8080 and 9091; never LDAP 3890. Tests: `test_netpol.py`, plus a rapp test that its in-namespace
|
||||||
|
egress equals these admissions. Prepared in source, not applied.
|
||||||
|
- Live reads (limited identity):
|
||||||
|
- OpenBao's chart policy admits every namespace on 8200, so nothing is needed there.
|
||||||
|
- approval-engine is deployed but admits only secrets-engine and namespaces labelled
|
||||||
|
`railiance.io/approval-engine-client=true`, and none are.
|
||||||
|
- audit-core has no ingress from `sso`.
|
||||||
|
- Both owners were asked for pod-scoped rules; see the rapp's `docs/platform-handoffs.md`.
|
||||||
|
|
||||||
## Pilot: unattended bind rotation under one admission
|
## Pilot: unattended bind rotation under one admission
|
||||||
|
|
||||||
```task
|
```task
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue