diff --git a/nix/host-modules/swarm-authelia.nix b/nix/host-modules/swarm-authelia.nix index 3c3d3965..cd63c2a6 100644 --- a/nix/host-modules/swarm-authelia.nix +++ b/nix/host-modules/swarm-authelia.nix @@ -967,25 +967,12 @@ in server.address = "tcp://127.0.0.1:${toString cfg.port}"; log.level = "info"; - # Authelia reads this file once, at STARTUP. `watch` makes it - # re-read on change, and that is load-bearing rather than a - # convenience. - # - # `swarm-authelia-bridge` writes this file to create agent - # identities and **cannot** restart authelia: running - # unprivileged inside the container is the whole reason it is - # allowed to write the file at all, so "restart the unit" is - # exactly the privilege it was designed not to hold. Without - # `watch` every identity it creates is real on disk and - # invisible to the running authelia until something unrelated - # bounces the unit. - # - # `swarmctl` does restart authelia after writing — but that is - # a `systemctl -M` shellout that can fail, and when it did the - # symptom was a login refused for a user whose record was - # already correct on disk, with nothing implicating the - # reload. This setting is what makes correctness stop - # depending on that restart succeeding. + # `watch` is load-bearing, not a convenience: authelia reads + # this file once at startup, and `swarm-authelia-bridge` writes + # it to create agent identities while being unable to restart + # authelia — running unprivileged is the whole reason it may + # write the file at all. Without this, an identity it creates is + # real on disk and invisible until something unrelated restarts. authentication_backend.file = { path = cfg.usersFile; watch = true; diff --git a/nix/host-modules/swarm-controller.nix b/nix/host-modules/swarm-controller.nix index 06c42b5f..44464fd4 100644 --- a/nix/host-modules/swarm-controller.nix +++ b/nix/host-modules/swarm-controller.nix @@ -29,9 +29,6 @@ let # parameters baked into a hash have to match the verifier's. SWARMCTL_AUTHELIA_BIN = "${autheliaCfg.package}/bin/authelia"; SWARMCTL_AUTHELIA_USERS_FILE = autheliaCfg.hostUsersFile; - # No MACHINE/UNIT here any more: `swarmctl` no longer restarts authelia, - # because authelia watches the users file itself. Those two values existed - # solely to name a `systemctl -M` target. }; natsCfg = config.services.hyperhive.swarm.nats; diff --git a/swarmctl/src/main.rs b/swarmctl/src/main.rs index fd0b88b3..925605f6 100644 --- a/swarmctl/src/main.rs +++ b/swarmctl/src/main.rs @@ -343,33 +343,11 @@ fn publish(paths: &Paths, store: &UserStore) -> Result<()> { write_atomic(&paths.store, &format!("{store_json}\n"))?; write_atomic(&paths.users_file, &rendered)?; - // No restart. Authelia is configured with - // `authentication_backend.file.watch`, so it re-reads this file itself. - // - // This used to shell out to `systemctl -M restart `. That - // was the wrong shape twice over: it could fail (it did — a login refused - // for a user whose record was already correct on disk, with nothing in - // either log implicating the reload), and it only ever worked for THIS - // writer. `swarm-authelia-bridge` writes the same file to create agent - // identities and cannot restart anything: running unprivileged inside the - // container is the whole reason it may write the file at all. A reload - // that depends on which process did the writing is not a reload. - // - // The three objections that previously kept `watch` out of this path are - // all now answered, and they were good objections — recorded here so the - // next reader does not have to re-earn them: - // - // - "could not be verified against the pinned build" — it is now: - // authelia v4.39.20's own `validate-config` accepts `watch` and - // *rejects* a deliberate misspelling of it, so the key is recognised - // rather than silently swallowed. - // - "does the watch survive the rename(2) used above" — yes: authelia - // watches the containing *directory*, so a rename into it is observed. - // A watch on the old inode alone would indeed have missed it. - // - "can it observe a partially written file" — it cannot, and that is - // structural rather than a debounce we are trusting: every writer of - // this file goes through `write_atomic` below, so no partial content is - // ever visible under the final name. (Authelia debounces as well.) + // No restart: authelia watches this file + // (`authentication_backend.file.watch`). Deliberately not `swarmctl`'s job + // — `swarm-authelia-bridge` writes the same file and *cannot* restart + // anything, since running unprivileged is the whole reason it may write + // it. A reload that depends on which process wrote is not a reload. Ok(()) }