the-custodian/docs/changes/CUST-WP-0073/existing-k3s-host-r1/PROPOSAL.md
codex 9f1ea088ef
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Reject generated-only commits / generated-commit-guard (push) Successful in 10s
Pin existing k3s hardening candidate and runtime review gates
2026-10-19 14:02:59 +02:00

94 lines
5.7 KiB
Markdown

# Existing Railiance01 k3s service hardening — review addendum
Part of CUST73-CREDENTIAL-BOUNDARY-R1, next review revision; original revision-2
packet remains unchanged. No execution approval, mutation or restart occurred.
This is the existing-host mode change only, not Custodian assignment admission.
## Observed target and exact proposed change
Existing SSH configuration resolves railiance01 to 92.205.62.239 as tegwick,
without proxy/local commands. The read used that existing access, no certificate
issuance and no sudo. Warden ssh-cert-host-access route was checked first.
Sanitized observation refreshed at remote UTC 2026-10-19T12:00:53.721961+00:00,
with local transport start/finish timestamps separately retained. Strict SSH
host-key checking was required. The expected IPv4 is present on the host;
observation.json binds hostname and hashed machine-id. These are captured facts,
not timeless target assertions; rerun identity checks before any approval/execution.
k3s is loaded/active/running, main PID935, fragment
/etc/systemd/system/k3s.service, no drop-ins, no pending daemon reload. Its
loaded ExecStart and fragment each have exactly one mode644 flag. The fragment
is regular root:root0644, device2049/inode7875, SHA256:
`b5398c385430ce225faadc1f3724597fe4b05dd999a04b8abbca64d4b0d68c0e`.
The default config.yaml and config.yaml.d are absent.
Admin kubeconfig metadata is root:root0644, device2049/inode786464; its contents
were not read. The service.env file is root:root0600 and inaccessible without
privilege; only its metadata was read, not contents or a hash.
The exact proposed unit transformation changes the single mode argument from
644 to 600, preserving every other byte and total length. Two bytes change.
The privately computed candidate SHA256 is:
`866ea58bfab00215e5bd3352287ebb0230d4ddb7dd3ed4b1cd853e74c6698d7d`.
No candidate file was written on the host and raw unit content was never emitted.
`unit_transform.py` provides the pure guarded transform, with pinned original and
candidate hashes, root owner/mode/device/inode checks, O_NOFOLLOW, link-count and
concurrent-read checks. It has no write/reload/restart code. Five tests cover
exact change and refusal on drift, absent/duplicate flag or wrong candidate.
## Execution sequence to include in owner-reviewed privileged revision
Owner: railiance-cluster for unit/install config, railiance-infra for account
recovery. The narrow approved executor must:
1. Verify fresh host identity, service state, original fragment identity/hash,
no new drop-ins/config paths, and no pending reload. Refuse drift; do not
auto-rebase this proposal onto another service revision.
2. Retain private owner-only predecessor bytes and metadata outside agent reach.
Confirm separate attended console/admin recovery and single-node headroom.
3. After the protected-environment gate below, generate the candidate in private
root staging and require exact candidate digest, root ownership and mode0644.
Recheck the original object before atomic replacement. Converge only the
admin kubeconfig owner/group/mode to root:root0600 without reading its content.
4. Run systemctl daemon-reload; verify loaded mode600 and no unrelated fragment
or drop-in change. Restart only k3s in the approved interruption window.
No reinstall, package upgrade, generic bootstrap or kubeconfig export.
5. Verify active/running service, fresh mode600 ExecStart, kubeconfig root:root0600
after readiness, cluster API readiness through attended admin access and no
workload rollout regression. Receipts expose only booleans/state and pinned
hashes. Do not capture kubectl error dumps, unit/env/config text or credentials.
6. Keep future-installer source correction from the original packet coupled to
this change, so a future installer does not reintroduce644 or controller export.
## Explicit remaining gate
The protected /etc/systemd/system/k3s.service.env cannot be read by the current
non-sudo observation path. No privilege escalation or content retrieval is
requested here. The owner must independently return only nonsecret predicates:
whether alternate config selection or K3S_KUBECONFIG_MODE is defined, whether it
can conflict with the selected launch change, and whether additional config/drop-in
sources exist. Do not return values or raw environment. Unknown means refusal,
not an assumption that the default config absence covers every launch input.
Fresh exact action, execution identity, expected interruption bound, validation
and recovery must be bound into the supervisor-approved revision before use.
## Abort and recovery
Before any write: a failed predicate aborts with no effect. After fragment
replacement but before service restart: the owner can CAS-restore the private
preimage only if the candidate digest is still exact, then daemon-reload; retain
kubeconfig0600 and do not restart the old mode644 service. This rollback returns
the reviewed predecessor unit, not a claim that credential separation succeeded.
After restart: prefer attended diagnosis with mode600 retained. Do not
unconditionally restore644 or expose admin credentials to recover readiness.
If restoring the old launch is necessary, stop all agent admission and obtain
explicit supervisor disposition acknowledging mode644 regeneration; use retained
console access, exact candidate/preimage CAS and immediate post-start0600
convergence. This exceptional fallback is not authorized by this preparation.
Preserve failure evidence and re-establish persistent600 before agent admission.
No change to old SSH principals, workstation kubeconfigs, native provider
credentials or actual interactive assignment isolation is established here.
Estimated extra infrastructure allocation is zero; restart can interrupt the
single control plane. Monetary loss is unquantified; autopilot remains disabled.