Last active
December 30, 2025 20:33
-
-
Save Mic92/3187fadd602ce304bbe4a1bb035ab45d to your computer and use it in GitHub Desktop.
Declarative Authelia users for NixOS - secrets resolved at runtime
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Declarative Authelia users with secret file references | |
| # Secrets are resolved at service start, never stored in the Nix store | |
| { | |
| config, | |
| lib, | |
| pkgs, | |
| ... | |
| }: | |
| let | |
| cfg = config.services.authelia.instances.main.declarativeUsers; | |
| authelia-users-gen = pkgs.writers.writePython3 "authelia-users-gen" { } ./authelia-users-gen.py; | |
| usersConfig = { | |
| users = lib.mapAttrs (name: user: { | |
| inherit (user) displayname email groups passwordHashFile; | |
| }) cfg.users; | |
| }; | |
| configJson = pkgs.writeText "authelia-users-config.json" (builtins.toJSON usersConfig); | |
| in | |
| { | |
| options.services.authelia.instances.main.declarativeUsers = { | |
| enable = lib.mkEnableOption "declarative users"; | |
| users = lib.mkOption { | |
| type = lib.types.attrsOf ( | |
| lib.types.submodule { | |
| options = { | |
| displayname = lib.mkOption { type = lib.types.str; }; | |
| email = lib.mkOption { type = lib.types.str; }; | |
| groups = lib.mkOption { | |
| type = lib.types.listOf lib.types.str; | |
| default = [ ]; | |
| }; | |
| passwordHashFile = lib.mkOption { type = lib.types.str; }; | |
| }; | |
| } | |
| ); | |
| default = { }; | |
| }; | |
| }; | |
| config = lib.mkIf cfg.enable { | |
| services.authelia.instances.main.settings.authentication_backend.file.path = | |
| "/run/authelia-main/users.yaml"; | |
| systemd.services.authelia-main = { | |
| preStart = lib.mkAfter '' | |
| ${authelia-users-gen} ${configJson} /run/authelia-main/users.yaml | |
| ''; | |
| serviceConfig.RuntimeDirectory = lib.mkDefault "authelia-main"; | |
| }; | |
| }; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| authelia-users-gen: Generate Authelia users.yaml from config with secret file references. | |
| Usage: authelia-users-gen <config.json> <output.yaml> | |
| Input JSON format: | |
| { | |
| "users": { | |
| "pinpox": { | |
| "displayname": "Pablo", | |
| "email": "pinpox@example.com", | |
| "groups": ["admins"], | |
| "passwordHashFile": "/run/secrets/pinpox-hash" | |
| } | |
| } | |
| } | |
| """ | |
| import json | |
| import os | |
| import sys | |
| from pathlib import Path | |
| def main() -> None: | |
| if len(sys.argv) != 3: | |
| print(f"Usage: {sys.argv[0]} <config.json> <output.yaml>", file=sys.stderr) | |
| sys.exit(1) | |
| config_path, output_path = sys.argv[1], sys.argv[2] | |
| config = json.loads(Path(config_path).read_text()) | |
| users = {} | |
| for username, user in config["users"].items(): | |
| hash_file = Path(user["passwordHashFile"]) | |
| if not hash_file.exists(): | |
| print(f"Error: {hash_file} not found", file=sys.stderr) | |
| sys.exit(1) | |
| users[username] = { | |
| "displayname": user["displayname"], | |
| "email": user["email"], | |
| "groups": user.get("groups", []), | |
| "password": hash_file.read_text().strip(), | |
| } | |
| # Write with 0600 permissions from the start | |
| fd = os.open(output_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) | |
| with os.fdopen(fd, "w") as f: | |
| json.dump({"users": users}, f, indent=2) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment