diff --git a/11-Features.md b/11-Features.md index a4c9483..7764b5e 100644 --- a/11-Features.md +++ b/11-Features.md @@ -1776,8 +1776,9 @@ has its own once-per-daemon guard, so a benign INFO can no longer suppress a ser **`SSH_AUTH_SOCK` is now blocked, and that is a downgrade, not a win.** It was once allowed on purpose, because a worktree's remote was `ssh://git@git.ltms.dev` and a member could not push without the agent socket. Members now push over HTTPS with `WORKER_GITEA_TOKEN`, so the live config -sets `sshAuthSock: block`, and `MemberEnvAllowList.derive` drops the name even if an operator lists -it under `allow:` — the config cannot re-grant it by accident. +sets `sshAgentEnv: omit` (spelled `sshAuthSock: block` before #266; both still parse), and +`MemberEnvAllowList.derive` drops the name even if an operator lists it under `allow:` — the config +cannot re-grant it by accident. Do not read that as the problem being solved. #184 measured a member with the socket blanked pushing **fine anyway**: the forge key is a readable, passphrase-free *file*, and `ssh -G` finds it outside @@ -1800,7 +1801,7 @@ anything that re-exports them afterwards silently undoes it. ```yaml memberCredentials: policy: allow-list # default is "deny-by-default" - sshAuthSock: block # "allow" only if a member must use the operator's ssh-agent + sshAgentEnv: omit # "inherit" only if a member must use the operator's ssh-agent ``` **Why it exists.** A control that lives inside a sourced file can always be undone by a file sourced @@ -3128,7 +3129,7 @@ What each control actually does: |---|---|---| | Environment | `memberCredentials` + the `ZDOTDIR` scrub | **Real**, for what the member *inherits*. It does not protect the values — the member can source the same files again. | | Files | worktree provisioning neutralizes some config files | **Not a boundary.** File modes do not separate processes with the same uid. A member can read the original by absolute path. | -| Sockets | `sshAuthSock: block` | **Not a boundary.** It omits one variable. It does not revoke access to the socket, and it cannot remove a readable key file. | +| Sockets | `sshAgentEnv: omit` | **Not a boundary.** It omits one variable. It does not revoke access to the socket, and it cannot remove a readable key file. | | Git config | the worktree's HTTPS rewrite + cleared `credential.helper` | **Routing, not enforcement.** Normal git commands go the intended way; a member can still call `ssh` directly. | | Process table | secrets kept out of argv | **No boundary.** argv is visible to any local user, and a different uid would not fix that. | @@ -3152,6 +3153,39 @@ grants access to them. Open work, ranked, is on #184. +## The ssh-agent setting is named after what it does — `sshAgentEnv: omit` + +**What.** `memberCredentials.sshAuthSock: block|allow` is now +`memberCredentials.sshAgentEnv: omit|inherit`. + +**The knob.** All eight spellings parse, silently, with no deprecation warning: + +```yaml +memberCredentials: + sshAgentEnv: omit # canonical; "inherit" passes SSH_AUTH_SOCK through + # sshAuthSock: block # the old key and old values still work, unchanged +``` + +Old configs need no edit. If both keys appear, `sshAgentEnv` wins. **An unrecognised value +normalises to `omit`**, so a typo fails closed rather than handing a member the agent. + +**Why it exists.** `block` named an effect fleetd does not have. It omits the variable from the +member's environment; it does not deny access to the socket. A member runs as the same OS user, so +the socket stays reachable and the path is discoverable. An operator scanning a config file reads a +value name and stops — that is the point of a good one — so `block` was actively misleading, and the +honest explanation sat in a comment most readers never reach. + +What the setting still buys is real and worth keeping: a member does not pick up the operator's +agent by default, which removes a whole class of accident. It is an accident-reducer, not a deny. + +**The gotcha: the shim is load-bearing, not cosmetic.** `fleetd.yaml` is gitignored, so no worker +can see it and no test in the repo covers it. The live config on this host still says +`sshAuthSock: block`. A rename without the read-both shim would have silently dropped the setting at +the next restart — trading a naming bug for a real credential regression, in a file the test suite +cannot reach. This was verified against the running daemon rather than argued: after redeploy, +`GET /member-credentials` still reported `policy: allow-list` with 39 known / 7 allowed / 34 blocked, +and a real member spawned on the new jar reported `SSH_AUTH_SOCK: unset`. + ## fleetd states the member trust model at startup **What.** Every boot, `fleetd` logs one INFO line saying what kind of boundary members run inside.