fix(indeehub): inspect nginx through fixed system service

This commit is contained in:
archipelago
2026-10-07 20:50:21 -04:00
parent fd98b38364
commit b03d3c890d
3 changed files with 48 additions and 1 deletions
@@ -261,3 +261,35 @@ recaptured as a new baseline, never described as preserved across that shutdown.
A direct-kernel initramfs-only recovery boot installed an absent-marker manager
startup condition before normal boot, and `ConditionResult=no` verified the old
manager never started. The subsequent diagnostic boot shut down gracefully.
### Actual RPC preflight and nginx namespace evidence (2026-10-08 UTC)
Corrected VM executable built successfully from frozen inputs (normal binary
SHA256 `3cbe5a5c3a74e6463ab0874c74e23dfca68f0bfc3fe54883f0edf69abb37ac0d`;
stripped transfer SHA256 `753f8ba6ac342c8f4b9a19c8079a51dfd1da4dcb517d4ea4d0e54035c22f5788`).
Receipt: `/tmp/archy-indeehub-corrected-executable-20261007.json`. It predates the
later rental guard and nginx helper correction; not a release artifact.
Actual manager startup completed a62-app reconcile pass with all seven IndeeHub
members `NoOp`, every baseline container identity preserved, and API/manager
HTTP200. Subsequent real update attempts initially met the legitimate background
lifecycle lock. A temporary behavior-preserving fixture tracer confirmed later
admission succeeded. One early diagnostic teardown interrupted asynchronous
preflight and is invalid as source-defect evidence. The corrected diagnostic
waited for its terminal refusal before restoring the deliberately withheld plan
and removing the tracer; all seven original identities remained, no supervised
journal existed. No speculative lifecycle-lock patch was made.
The admitted reviewed-plan RPC then reached `Prepared → Editing → Restoring`.
All seven recovery images exist; target startup never began. The controller
remained `Prepared` because `sudo nginx -T` attempted to open `/run/nginx.pid`
inside the manager's read-only mount namespace. Holds and the unresolved journal
were preserved; this is not a successful rollback or migration receipt.
A manager-equivalent hardened VM probe passed with the fixed command
`sudo -n /usr/bin/systemd-run --quiet --wait --pipe --collect -- /usr/sbin/nginx -T`.
It validated the required ingress guards without printing effective configuration
or widening manager write access. The controller uses that fixed command now;
25 pure controller tests pass, including refusal before fence creation on dump
failure. A new helper hash requires a matching backend rebuild. Candidate-helper
qualification remains separate from actual backend transaction acceptance.
+7 -1
View File
@@ -167,7 +167,13 @@ class Controller:
def close_ingress(self):
# The deployed native AppGate and legacy nginx guards consume this exact
# sentinel. This code never edits arbitrary nginx configuration.
config=self.run(['sudo','-n','nginx','-T']).decode()
# nginx -T tests its pid file as well as reading configuration. The
# manager's strict mount namespace makes /run/nginx.pid read-only, even
# after sudo. Use one fixed read-only command in PID1's fresh service
# context; do not broaden the manager's writable paths or detach the
# controller that owns the inherited lifecycle lock.
config=self.run(['sudo','-n','/usr/bin/systemd-run','--quiet','--wait',
'--pipe','--collect','--','/usr/sbin/nginx','-T']).decode()
validate_nginx_guards(config)
self.fence.parent.mkdir(mode=0o755,exist_ok=True)
self.fence.parent.chmod(0o755)
@@ -155,6 +155,15 @@ class MaintenanceTests(unittest.TestCase):
module.atomic(c.data/'update-transactions'/'supervised'/(self.operation+'.json'),{'phase':'Restored','target_startup_began':True})
with self.assertRaisesRegex(RuntimeError,'Data compatibility'):c.release('restored')
self.assertTrue(c.fence.exists())
def test_nginx_namespace_failure_cannot_create_admission_fence(self):
def failed_dump(argv,timeout,output):
self.assertEqual(argv,['sudo','-n','/usr/bin/systemd-run','--quiet','--wait',
'--pipe','--collect','--','/usr/sbin/nginx','-T'])
raise module.subprocess.CalledProcessError(1,argv)
self.controller.runner=failed_dump
with self.assertRaises(module.subprocess.CalledProcessError):self.controller.close_ingress()
self.assertFalse(self.controller.fence.exists())
self.assertIsNone(self.controller.record)
def test_every_legacy_sublocation_must_be_fenced(self):
guard='if (-f /var/lib/archipelago/app-maintenance/indeedhub) { return 503; }'
blocks=[f'location /app/indeedhub/{suffix} {{\n {guard}\n proxy_pass http://127.0.0.1:7778/;\n}}' for suffix in ('','_next/','ws/')]