|
|
|
@@ -117,9 +117,11 @@ public abstract class HerdrPeerLauncher implements PeerLauncher {
|
|
|
|
|
*
|
|
|
|
|
* <p>fleetd #185 stage 2: that mirroring assumption holds only while the member pane runs under
|
|
|
|
|
* the SAME OS user as the daemon. When {@code memberHerdrSocket:} is configured, member panes
|
|
|
|
|
* run on a second herdr owned by a different user — different {@code $HOME}, different {@code
|
|
|
|
|
* secrets.sh}, different environment entirely — so this field's data no longer describes what a
|
|
|
|
|
* member pane inherits. See {@link #logCredentialGap} for how that mode is handled.
|
|
|
|
|
* are routed to a second herdr, and fleetd has no channel to confirm what OS user that herdr
|
|
|
|
|
* runs as — it may be a different user with a different {@code $HOME} and {@code secrets.sh},
|
|
|
|
|
* or the same one the daemon runs as. Either way this field's data can no longer be trusted to
|
|
|
|
|
* describe what a member pane inherits. See {@link #logCredentialGap} for how that mode is
|
|
|
|
|
* handled.
|
|
|
|
|
*/
|
|
|
|
|
private final Supplier<Set<String>> hostEnvNames;
|
|
|
|
|
|
|
|
|
@@ -1533,21 +1535,24 @@ public abstract class HerdrPeerLauncher implements PeerLauncher {
|
|
|
|
|
/**
|
|
|
|
|
* fleetd #213: {@code memberHerdrSocket} is configured and the member login shell IS zsh, but
|
|
|
|
|
* {@code worktreeRoot} and/or {@code worktreeGroup} is missing, so the generated ZDOTDIR cannot
|
|
|
|
|
* be placed anywhere the member's OS user can reach — {@code java.io.tmpdir} is fleetd's own
|
|
|
|
|
* 0700 temp dir, unreadable by another uid, which is the exact gap this ticket exists to close.
|
|
|
|
|
* Say so once per launcher instance, instead of either generating a directory nothing can read
|
|
|
|
|
* (protection theatre) or refusing to spawn (turning a degraded credential control into an
|
|
|
|
|
* outage for an opt-in feature).
|
|
|
|
|
* be placed anywhere fleetd can be sure the member's OS user can reach — {@code java.io.tmpdir}
|
|
|
|
|
* is fleetd's own 0700 temp dir, which is unreadable if the member pane runs as a different OS
|
|
|
|
|
* user, and fleetd has no channel to confirm whether it does or not. Rather than gamble on that,
|
|
|
|
|
* this treats memberHerdrSocket as reason enough to require an explicitly shared location, which
|
|
|
|
|
* is the exact gap this ticket exists to close. Say so once per launcher instance, instead of
|
|
|
|
|
* either generating a directory that might not be readable (protection theatre) or refusing to
|
|
|
|
|
* spawn (turning a degraded credential control into an outage for an opt-in feature).
|
|
|
|
|
*/
|
|
|
|
|
private void warnCannotShareScrubDirectory() {
|
|
|
|
|
if (cannotShareScrubDirWarned.compareAndSet(false, true)) {
|
|
|
|
|
log.warn("memberCredentials policy=allow-list: memberHerdrSocket is configured and the "
|
|
|
|
|
+ "member login shell is zsh, but worktreeRoot and/or worktreeGroup is not "
|
|
|
|
|
+ "configured — the generated ZDOTDIR cannot be placed where the member's OS "
|
|
|
|
|
+ "user can read it (java.io.tmpdir is fleetd's own, unreadable by another uid), "
|
|
|
|
|
+ "so the scrub cannot be guaranteed to run. Falling back to the CB-596 sentinel "
|
|
|
|
|
+ "overlay. Configure both worktreeRoot and worktreeGroup to enable the "
|
|
|
|
|
+ "allow-list scrub under memberHerdrSocket.");
|
|
|
|
|
+ "configured — the generated ZDOTDIR cannot be placed where fleetd can be sure "
|
|
|
|
|
+ "the member's OS user can read it (java.io.tmpdir is fleetd's own 0700 dir, "
|
|
|
|
|
+ "unreadable if the member runs as a different OS user — fleetd has no channel "
|
|
|
|
|
+ "to confirm whether it does), so the scrub cannot be guaranteed to run. Falling "
|
|
|
|
|
+ "back to the CB-596 sentinel overlay. Configure both worktreeRoot and "
|
|
|
|
|
+ "worktreeGroup to enable the allow-list scrub under memberHerdrSocket.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1626,10 +1631,12 @@ public abstract class HerdrPeerLauncher implements PeerLauncher {
|
|
|
|
|
private final AtomicBoolean unknownMemberEnvironmentWarned = new AtomicBoolean();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* fleetd #185 stage 2: whether {@code memberHerdrSocket:} is configured, i.e. member panes run
|
|
|
|
|
* on a second herdr owned by a different OS user than the daemon's own process. Re-read from the
|
|
|
|
|
* live config on every call (same hot-reload shape as {@link #memberCredentials}), never cached,
|
|
|
|
|
* so a config reload takes effect on the next spawn without a restart.
|
|
|
|
|
* fleetd #185 stage 2: whether {@code memberHerdrSocket:} is configured, i.e. member panes are
|
|
|
|
|
* routed to a second herdr. This tests only that the config key is set — fleetd has no channel
|
|
|
|
|
* to confirm what OS user that second herdr runs as, so a {@code true} result means "member
|
|
|
|
|
* panes may run under a different OS user," not that they do. Re-read from the live config on
|
|
|
|
|
* every call (same hot-reload shape as {@link #memberCredentials}), never cached, so a config
|
|
|
|
|
* reload takes effect on the next spawn without a restart.
|
|
|
|
|
*
|
|
|
|
|
* <p>{@link #config} is {@code null} on any call site that never threaded the full config
|
|
|
|
|
* through (every production {@code HerdrPeerLauncher} does; a handful of older tests do not) —
|
|
|
|
@@ -1652,14 +1659,15 @@ public abstract class HerdrPeerLauncher implements PeerLauncher {
|
|
|
|
|
* fleetd #185 stage 2: the single replacement WARN for {@link #logCredentialGap}'s usual
|
|
|
|
|
* conclusions when {@code memberHerdrSocket:} is configured. {@link #hostEnvNames} (and
|
|
|
|
|
* everything derived from it — {@code known}/{@code allow} coverage, the allow-list scrub's
|
|
|
|
|
* derived set) describes the DAEMON's own environment; under this config key member panes run as
|
|
|
|
|
* a different OS user with a different environment entirely, so neither "every member pane
|
|
|
|
|
* inherits them UNBLOCKED" nor "the scrub blanks them" is evidence-backed here — both would be
|
|
|
|
|
* reporting on the wrong process. Logged once, names the config key, and states the honest
|
|
|
|
|
* conclusion: the gap for member panes is UNKNOWN, not clean, so {@code memberCredentials} cannot
|
|
|
|
|
* be verified from this daemon. The one count it does report is scoped explicitly to fleetd's own
|
|
|
|
|
* environment, never presented as if it said anything about the member's — see {@link
|
|
|
|
|
* #logCredentialGap}'s javadoc for why this branch exists.
|
|
|
|
|
* derived set) describes the DAEMON's own environment; under this config key member panes are
|
|
|
|
|
* routed to a second herdr, and fleetd has no channel to confirm what OS user that herdr runs
|
|
|
|
|
* as or to read its environment, so neither "every member pane inherits them UNBLOCKED" nor
|
|
|
|
|
* "the scrub blanks them" is evidence-backed here — both would be reporting on the wrong
|
|
|
|
|
* process. Logged once, names the config key, and states the honest conclusion: the gap for
|
|
|
|
|
* member panes is UNKNOWN, not clean, so {@code memberCredentials} cannot be verified from this
|
|
|
|
|
* daemon. The one count it does report is scoped explicitly to fleetd's own environment, never
|
|
|
|
|
* presented as if it said anything about the member's — see {@link #logCredentialGap}'s javadoc
|
|
|
|
|
* for why this branch exists.
|
|
|
|
|
*/
|
|
|
|
|
private void warnUnknownMemberEnvironment(FleetConfig.MemberCredentials creds) {
|
|
|
|
|
if (!unknownMemberEnvironmentWarned.compareAndSet(false, true)) {
|
|
|
|
@@ -1672,13 +1680,13 @@ public abstract class HerdrPeerLauncher implements PeerLauncher {
|
|
|
|
|
.filter(name -> CREDENTIAL_SHAPED_NAME.matcher(name).matches())
|
|
|
|
|
.filter(name -> !covered.contains(name))
|
|
|
|
|
.count();
|
|
|
|
|
log.warn("memberCredentials gap: memberHerdrSocket is configured, so member panes run under "
|
|
|
|
|
+ "a different OS user than fleetd's own process, with a different environment "
|
|
|
|
|
+ "entirely — fleetd has no channel to read that user's environment. {} of the "
|
|
|
|
|
+ "{} names in fleetd's OWN environment are credential-shaped and not on "
|
|
|
|
|
+ "known:/allow:, but that count describes fleetd's process, not the member "
|
|
|
|
|
+ "herdr's. The credential gap for member panes is UNKNOWN, not clean, and "
|
|
|
|
|
+ "memberCredentials cannot be verified from here.",
|
|
|
|
|
log.warn("memberCredentials gap: memberHerdrSocket is configured, so member panes are routed "
|
|
|
|
|
+ "to a second herdr — fleetd has no channel to confirm what OS user that herdr "
|
|
|
|
|
+ "runs as, so it cannot tell whether those panes inherit its own environment or "
|
|
|
|
|
+ "a different one entirely. {} of the {} names in fleetd's OWN environment are "
|
|
|
|
|
+ "credential-shaped and not on known:/allow:, but that count describes fleetd's "
|
|
|
|
|
+ "process, not the member herdr's. The credential gap for member panes is "
|
|
|
|
|
+ "UNKNOWN, not clean, and memberCredentials cannot be verified from here.",
|
|
|
|
|
gapInFleetdsOwnEnv, hostNames.size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|