carlos / platform Public

rootaws: an inherited disabled shared-file sentinel is not an override

A root child of a scrubbed root parent inherits AWS_CONFIG_FILE and
AWS_SHARED_CREDENTIALS_FILE set to /dev/null/carlos-root-aws. The child's
scrub counted them as overrides and logged the ALERT on stderr. A
guard-armed box's updater reads the candidate's version through the guard
with combined output, so it refused every router update (found by
platform-d8's deployment harness at G4). The candidate must carry the fix,
since the refusing updater is the old binary. Census: envelope-only overlay
claim-rootaws-sentinel, ruled.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 1d8aad2ab663be0bc2bb6ce5247cf5cb329ab106 parent 7af8815
4 files changed, +57 −1
  • internal/provision/plan14_root_reads_test.go +7 −0
  • internal/provision/testdata/plan14-root-reads-claim-rootaws-sentinel.tsv +2 −0
  • internal/rootaws/environment.go +10 −1
  • internal/rootaws/environment_test.go +38 −0
diff --git a/internal/provision/plan14_root_reads_test.go b/internal/provision/plan14_root_reads_test.go
index 9ff2cb52b..dc02805f8 100644
--- a/internal/provision/plan14_root_reads_test.go
+++ b/internal/provision/plan14_root_reads_test.go
@@ -972,6 +972,13 @@ func TestPlan14RootReadCensus(t *testing.T) {
t.Fatal(err)
}
seal = "393e7f456d8f02b2eee0c683880e5f7bf696b6ee0c87f20e5c113434065bec29"
+ // Census overlay claim-rootaws-sentinel (rootaws's scrub treats its own
+ // inherited disabled-file sentinels as no override, so a guard-armed
+ // box's updater can read a candidate's version), loaded last. Envelope
+ // only, ruled by platform-1e. The seal stands.
+ if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-rootaws-sentinel.tsv"), &inv, true); err != nil {
+ t.Fatal(err)
+ }
errs := append(compareRootReads(got, inv), checkRootReadLegacy(inv, seal)...)
errs = append(errs, checkRootReadDeclarations(got, inv)...)
if len(errs) > 0 {
diff --git a/internal/provision/testdata/plan14-root-reads-claim-rootaws-sentinel.tsv b/internal/provision/testdata/plan14-root-reads-claim-rootaws-sentinel.tsv
new file mode 100644
index 000000000..8c8032e9b
--- /dev/null
+++ b/internal/provision/testdata/plan14-root-reads-claim-rootaws-sentinel.tsv
@@ -0,0 +1,2 @@
+# Census overlay claim-rootaws-sentinel (a scrubbed root parent's own disabled shared-file sentinels are not an override in a root child, so the child's scrub stays silent; found by platform-d8's deployment harness at G4: a guard-armed box refused every router update because the candidate's version printed the ALERT into the guarded updater's combined output), loaded LAST, after claim-empty-cohort-fix2. RULED (platform-1e, 2026-10-20): accepted. Envelope only: ScrubEnvironment has no classified root-read unit; no row changes and the seal stands.
+@coverage internal/rootaws ad641674b84d17a9f6a7b1acb4cf88b68cc2a882812a28c8f622d4fd9616b470
diff --git a/internal/rootaws/environment.go b/internal/rootaws/environment.go
index 343ea793b..6023800b3 100644
--- a/internal/rootaws/environment.go
+++ b/internal/rootaws/environment.go
@@ -57,7 +57,7 @@ func scrubEnvironment(euid int) {
}
}
for _, key := range keys {
- if _, set := os.LookupEnv(key); set {
+ if value, set := os.LookupEnv(key); set && !inheritedSentinel(key, value) {
changed = true
}
_ = os.Unsetenv(key)
@@ -77,6 +77,15 @@ func scrubEnvironment(euid int) {
}
}
+// inheritedSentinel reports a shared-file key that already holds the value
+// this scrub sets: a root child of a scrubbed root parent inherits it, and
+// that is not an override. Alerting on it would put the alert on the child's
+// stderr, where a guarded updater reading `version` with combined output
+// refuses the candidate.
+func inheritedSentinel(key, value string) bool {
+ return (key == "AWS_CONFIG_FILE" || key == "AWS_SHARED_CREDENTIALS_FILE") && value == disabledSharedFile
+}
+
// imdsHosts are the IPv4 and IPv6 instance metadata addresses.
var imdsHosts = []string{"169.254.169.254", "fd00:ec2::254"}
diff --git a/internal/rootaws/environment_test.go b/internal/rootaws/environment_test.go
index 3d4dc9774..5bf35f28b 100644
--- a/internal/rootaws/environment_test.go
+++ b/internal/rootaws/environment_test.go
@@ -149,3 +149,41 @@ func TestScrubEnvironmentKeepsProxyButBypassesIMDS(t *testing.T) {
t.Fatal("non-root NO_PROXY changed")
}
}
+
+// A root child of a root process inherits the parent's own disabled shared
+// files. That is not an override: it must stay silent, or a guarded updater
+// that reads a candidate's `version` with its output combined refuses the
+// candidate (an armed box could then take no router update at all).
+func TestScrubEnvironmentInheritedSentinelIsNotAnOverride(t *testing.T) {
+ for _, key := range rootScrubbedEnvironment {
+ t.Setenv(key, "")
+ os.Unsetenv(key)
+ }
+ t.Setenv("AWS_CONFIG_FILE", disabledSharedFile)
+ t.Setenv("AWS_SHARED_CREDENTIALS_FILE", disabledSharedFile)
+ var logs bytes.Buffer
+ old := log.Writer()
+ log.SetOutput(&logs)
+ defer log.SetOutput(old)
+ scrubEnvironment(0)
+ if logs.Len() != 0 {
+ t.Fatalf("alert for the inherited sentinel: %q", logs.String())
+ }
+ if os.Getenv("AWS_CONFIG_FILE") != disabledSharedFile || os.Getenv("AWS_SHARED_CREDENTIALS_FILE") != disabledSharedFile {
+ t.Fatal("shared files not disabled for root")
+ }
+ // Any other value for either key is still an override.
+ for _, key := range []string{"AWS_CONFIG_FILE", "AWS_SHARED_CREDENTIALS_FILE"} {
+ logs.Reset()
+ t.Setenv("AWS_CONFIG_FILE", disabledSharedFile)
+ t.Setenv("AWS_SHARED_CREDENTIALS_FILE", disabledSharedFile)
+ t.Setenv(key, disabledSharedFile+"x")
+ scrubEnvironment(0)
+ if strings.Count(logs.String(), "ALERT:") != 1 {
+ t.Fatalf("%s set to another file did not alert: %q", key, logs.String())
+ }
+ if os.Getenv(key) != disabledSharedFile {
+ t.Fatalf("%s not reset to the sentinel", key)
+ }
+ }
+}