Prepare private dispatcher queue on Kubernetes fsGroup volumes
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a10780-6d41-7952-9718-dfbc1f41236f
This commit is contained in:
parent
c69c4c6f2c
commit
1321361d85
4 changed files with 81 additions and 5 deletions
|
|
@ -65,3 +65,9 @@ Live observation: State Hub's API Service selects app=state-hub. This worker
|
||||||
does not carry that label, so it cannot enter the API endpoints. At inspection
|
does not carry that label, so it cannot enter the API endpoints. At inspection
|
||||||
there were no other State Hub NetworkPolicies; recheck effective selection before
|
there were no other State Hub NetworkPolicies; recheck effective selection before
|
||||||
activation. Its own policy allows only DNS and the exact intake peer on 8081.
|
activation. Its own policy allows only DNS and the exact intake peer on 8081.
|
||||||
|
|
||||||
|
The approved Kubernetes deployment prepares the private queue with a non-root
|
||||||
|
init container using the identical image. It removes inherited setgid only on
|
||||||
|
an empty, owned 2700 directory and otherwise requires 0700. It never changes
|
||||||
|
existing nonempty evidence custody. This covers local-path fsGroup behavior
|
||||||
|
that a plain Docker volume does not reproduce.
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
"name": "state-hub-memo-dispatch",
|
"name": "state-hub-memo-dispatch",
|
||||||
"namespace": "state-hub",
|
"namespace": "state-hub",
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"state-hub.coulomb.social/admission": "proposal; STATE-WP-0096 / INFD-WP-0007 approved rollout required"
|
"state-hub.coulomb.social/admission": "approved; INFD-WP-0007 cutover"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
"name": "state-hub-memo-dispatch",
|
"name": "state-hub-memo-dispatch",
|
||||||
"namespace": "state-hub",
|
"namespace": "state-hub",
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"state-hub.coulomb.social/admission": "proposal; STATE-WP-0096 / INFD-WP-0007 approved rollout required"
|
"state-hub.coulomb.social/admission": "approved; INFD-WP-0007 cutover"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
|
|
@ -131,6 +131,42 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"initContainers": [
|
||||||
|
{
|
||||||
|
"name": "prepare-private-queue",
|
||||||
|
"image": "forgejo.coulomb.social/coulomb/state-hub-memo-dispatch@sha256:410fb6b6c9fd7a7ee0b63d727fd3249fb3ae13943421d6a5ce88b7ad2520bd2f",
|
||||||
|
"command": [
|
||||||
|
"python",
|
||||||
|
"-c",
|
||||||
|
"import os, stat\np='/data/private'\ntry:\n os.mkdir(p, 0o700)\nexcept FileExistsError:\n pass\nfd=os.open(p, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)\ntry:\n info=os.fstat(fd)\n if info.st_uid != os.getuid():\n raise SystemExit('queue owner refused')\n mode=stat.S_IMODE(info.st_mode)\n if mode == 0o2700 and not os.listdir(fd):\n os.fchmod(fd, 0o700)\n elif mode != 0o700:\n raise SystemExit('queue mode refused')\nfinally:\n os.close(fd)\n"
|
||||||
|
],
|
||||||
|
"securityContext": {
|
||||||
|
"allowPrivilegeEscalation": false,
|
||||||
|
"readOnlyRootFilesystem": true,
|
||||||
|
"capabilities": {
|
||||||
|
"drop": [
|
||||||
|
"ALL"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"requests": {
|
||||||
|
"cpu": "5m",
|
||||||
|
"memory": "24Mi"
|
||||||
|
},
|
||||||
|
"limits": {
|
||||||
|
"cpu": "100m",
|
||||||
|
"memory": "96Mi"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"volumeMounts": [
|
||||||
|
{
|
||||||
|
"name": "queue",
|
||||||
|
"mountPath": "/data"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,7 +179,7 @@
|
||||||
"name": "state-hub-memo-dispatch",
|
"name": "state-hub-memo-dispatch",
|
||||||
"namespace": "state-hub",
|
"namespace": "state-hub",
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"state-hub.coulomb.social/admission": "proposal; STATE-WP-0096 / INFD-WP-0007 approved rollout required"
|
"state-hub.coulomb.social/admission": "approved; INFD-WP-0007 cutover"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
|
|
|
||||||
|
|
@ -124,3 +124,26 @@ def test_transport_suppresses_proxy_redirect_and_oversized_response(monkeypatch)
|
||||||
transport('POST',origin+'/v1/memos',{}, {})
|
transport('POST',origin+'/v1/memos',{}, {})
|
||||||
finally:
|
finally:
|
||||||
server.shutdown();server.server_close();thread.join(timeout=2)
|
server.shutdown();server.server_close();thread.join(timeout=2)
|
||||||
|
|
||||||
|
|
||||||
|
def test_kubernetes_queue_init_handles_setgid_without_taking_over_data(tmp_path):
|
||||||
|
manifest = json.loads((Path(__file__).parents[1] / 'deploy/informed-decision/dispatcher.candidate.json').read_text())
|
||||||
|
pod = next(item for item in manifest['items'] if item['kind'] == 'Deployment')['spec']['template']['spec']
|
||||||
|
init = pod['initContainers'][0]
|
||||||
|
assert init['image'] == pod['containers'][0]['image']
|
||||||
|
tmp_path.chmod(0o2770)
|
||||||
|
private = tmp_path / 'private'
|
||||||
|
code = init['command'][2].replace('/data/private', str(private))
|
||||||
|
def run():
|
||||||
|
return subprocess.run([sys.executable, '-c', code], capture_output=True).returncode
|
||||||
|
assert run() == 0
|
||||||
|
assert stat.S_IMODE(private.stat().st_mode) == 0o700
|
||||||
|
retained = private / 'retained'
|
||||||
|
retained.write_text('existing evidence')
|
||||||
|
assert run() == 0
|
||||||
|
private.chmod(0o2700)
|
||||||
|
assert run() != 0 # Existing nonempty custody is never repaired silently.
|
||||||
|
retained.unlink()
|
||||||
|
assert run() == 0 # Empty directory left by the initial failed startup.
|
||||||
|
private.chmod(0o755)
|
||||||
|
assert run() != 0
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ status: active
|
||||||
owner: codex
|
owner: codex
|
||||||
topic_slug: netkingdom
|
topic_slug: netkingdom
|
||||||
created: "2026-10-04"
|
created: "2026-10-04"
|
||||||
updated: "2026-10-04"
|
updated: "2026-10-05"
|
||||||
related: [INFD-WP-0005, INFD-WP-0007]
|
related: [INFD-WP-0005, INFD-WP-0007]
|
||||||
state_hub_workstream_id: "4cca4a91-3b21-51f0-a61f-902ccb1289c6"
|
state_hub_workstream_id: "4cca4a91-3b21-51f0-a61f-902ccb1289c6"
|
||||||
---
|
---
|
||||||
|
|
@ -53,7 +53,7 @@ caller identity.
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: STATE-WP-0096-T03
|
id: STATE-WP-0096-T03
|
||||||
status: todo
|
status: progress
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "1aeb4da7-5411-5634-ae6a-2e4bf0e8738b"
|
state_hub_task_id: "1aeb4da7-5411-5634-ae6a-2e4bf0e8738b"
|
||||||
```
|
```
|
||||||
|
|
@ -85,3 +85,14 @@ Final Forgejo image 410fb6b (source 4a59b23) passed five installed-container
|
||||||
checks and Trivy HIGH/CRITICAL scan (zero findings). Owner tests: 28 passed.
|
checks and Trivy HIGH/CRITICAL scan (zero findings). Owner tests: 28 passed.
|
||||||
Cross-service suite: 663 passed, one skipped. Both projected audiences and final
|
Cross-service suite: 663 passed, one skipped. Both projected audiences and final
|
||||||
image pin passed server dry-run. Receipts: docs/evidence/2026-10-04-memo-dispatch/.
|
image pin passed server dry-run. Receipts: docs/evidence/2026-10-04-memo-dispatch/.
|
||||||
|
|
||||||
|
## Approved native cutover — 2026-10-05
|
||||||
|
|
||||||
|
Founder approved the cutover (State Hub decision d2eaa2e9-6d11-4676-a07f-e3b7e59a88e3).
|
||||||
|
The worker was deployed by a pinned manual Argo application. Native startup
|
||||||
|
exposed inherited setgid on the newly created private queue (2700 instead of
|
||||||
|
0700). A same-image, non-root init container normalizes only an empty owned
|
||||||
|
2700 directory; existing nonempty or unsafe custody fails closed. Regression
|
||||||
|
coverage exercises first start under setgid, restart preservation, the empty
|
||||||
|
failed-start repair, and rejection of nonempty or world-readable custody.
|
||||||
|
Human acceptance remains pending.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue