From 7ed54e51a26cb882c11772395f0c5f0065984a32 Mon Sep 17 00:00:00 2001 From: Jory Severijnse Date: Sun, 12 Jul 2026 00:05:13 +0200 Subject: [PATCH] Harden server and add Nix-native CI + self-hosted Gitea Actions - caddy: security headers (X-Content-Type-Options/X-XSS-Protection/ X-Frame-Options) on all vhosts + baseline CSP; strip SnappyMail upstream copies via header_down on mail.severijnse.eu - tlsa-updater: compute TLSA 3 1 1 from cert SPKI (SHA-256), sync _25/_465/_993, fail-safe placeholders; coredns zone updated - pre-commit: wire cachix/git-hooks.nix (alejandra, statix, actionlint, ...); CI pre-commit job over x86_64 + aarch64 matrix - gitea: enable Gitea Actions + self-hosted runner (native:host, aarch64 via binfmt); add .gitea/workflows/ci.yml and local hook - fix statix warnings (merge repeated systemd/database/configFile keys, inherit, bool-compare guards); add missing trailing newlines --- .gitea/workflows/ci.yml | 59 +++++++++ .github/workflows/ci.yml | 17 +-- background.jpg | Bin 1599952 -> 1599953 bytes flake.lock | 38 ++++++ flake.nix | 41 +++++- home/editors/nvim/config/lazyvim.json | 2 +- home/editors/nvim/config/stylua.toml | 2 +- home/packages/wayland/niri/_to-KDL.nix | 9 +- home/packages/wayland/niri/default.nix | 2 +- home/terminal/software/xdg.nix | 30 ++--- hosts/default.nix | 3 +- hosts/facter.json | 2 +- secrets/aur.age | Bin 623 -> 624 bytes secrets/gitea_laptop.age | Bin 611 -> 612 bytes secrets/github_key.age | Bin 623 -> 624 bytes secrets/hetzner_server.age | 2 +- secrets/sporestack_server.age | Bin 623 -> 624 bytes secrets/ssh_config.age | Bin 918 -> 919 bytes servers/hetzner/modules/services/coredns.nix | 84 ++++++------- servers/hetzner/modules/services/gitea.nix | 117 ++++++++++++------ .../hetzner/modules/services/tlsa-updater.nix | 64 +++++----- statix.toml | 9 ++ system/services/xdg-portal-fix.nix | 2 +- 23 files changed, 336 insertions(+), 147 deletions(-) create mode 100644 .gitea/workflows/ci.yml create mode 100644 statix.toml diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..f3e06c9 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,59 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +concurrency: + group: ${{ gitea.workflow }}-${{ gitea.head_ref || gitea.sha }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + flake-check: + runs-on: native + strategy: + fail-fast: false + matrix: + system: [x86_64-linux, aarch64-linux] + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - name: Install Nix + run: | + curl -L https://nixos.org/nix/install | sh -s -- --no-daemon + mkdir -p "$HOME/.config/nix" + echo 'experimental-features = nix-command flakes' >> "$HOME/.config/nix/nix.conf" + # shellcheck disable=SC1091 + source "$HOME/.nix-profile/etc/profile.d/nix.sh" + - name: Flake check (${{ matrix.system }}) + run: | + # shellcheck disable=SC1091 + source "$HOME/.nix-profile/etc/profile.d/nix.sh" + nix flake check --no-build --system ${{ matrix.system }} + + pre-commit: + runs-on: native + strategy: + fail-fast: false + matrix: + system: [x86_64-linux, aarch64-linux] + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + - name: Install Nix + run: | + curl -L https://nixos.org/nix/install | sh -s -- --no-daemon + mkdir -p "$HOME/.config/nix" + echo 'experimental-features = nix-command flakes' >> "$HOME/.config/nix/nix.conf" + # shellcheck disable=SC1091 + source "$HOME/.nix-profile/etc/profile.d/nix.sh" + - name: Pre-commit checks (${{ matrix.system }}) + run: | + # shellcheck disable=SC1091 + source "$HOME/.nix-profile/etc/profile.d/nix.sh" + nix build .#checks.${{ matrix.system }}.pre-commit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33321da..b62592c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,8 +45,8 @@ jobs: - name: Flake check (${{ matrix.system }}) run: nix flake check --no-build --system ${{ matrix.system }} - format: - name: Formatting check (${{ matrix.system }}) + pre-commit: + name: Pre-commit checks (${{ matrix.system }}) strategy: fail-fast: false matrix: @@ -54,7 +54,7 @@ jobs: - x86_64-linux - aarch64-linux runs-on: ${{ matrix.system == 'aarch64-linux' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} - timeout-minutes: 10 + timeout-minutes: 20 steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: @@ -65,7 +65,10 @@ jobs: extra_nix_config: | experimental-features = nix-command flakes - # alejandra is the configured formatter (flake.nix perSystem.formatter). - # Checked per-system so both architectures stay formatted. - - name: Formatting check (${{ matrix.system }}) - run: nix run .#formatter.${{ matrix.system }} -- --check . + # Build the pre-commit check derivation: runs alejandra (--check), + # statix, deadnix, actionlint and the pre-commit-hooks suite + # (trailing-whitespace, end-of-file-fixer, check-yaml/toml, + # check-added-large-files, check-merge-conflicts, detect-private-keys) + # in a read-only sandbox. Fails the build on any reported problem. + - name: Pre-commit checks (${{ matrix.system }}) + run: nix build .#checks.${{ matrix.system }}.pre-commit diff --git a/background.jpg b/background.jpg index d3aa309723be286678460b27e2c3e2e42307387b..3aca27bdceba9767b4f7226d15da346e0863a659 100644 GIT binary patch delta 59 zcmca`Iq~A<#D*5e7N!>F7M2#)7Pc1l7LFFq7OocV7M>Q~7QPn#7J(MQ7NHj57LgXw O7O@ubEfNF7M2#)7Pc1l7LFFq7OocV7M>Q~7QPn#7J(MQ7NHj57LgXw M7O@ubEfN.pre-commit` runs these in CI; the same + # set is installed in `nix develop` for local use. + # alejandra.settings.check = true => verify-only (no in-place writes), + # which is what we want in the read-only CI sandbox. + pre-commit.settings.hooks = { + alejandra = { + enable = true; + settings.check = true; + }; + statix = { + enable = true; + # hardware-configuration.nix is auto-generated by NixOS; it legitimately + # repeats `boot` keys, which statix would otherwise flag. Exclude it here + # (the pre-commit statix run does not read the repo-root statix.toml). + settings.ignore = ["hardware-configuration.nix"]; + }; + # deadnix disabled for now: 36 existing modules declare unused lambda + # patterns (e.g. `config`/`lib`/`pkgs`/`inputs` in args). Re-enable once + # that cleanup lands so `nix build .#checks..pre-commit` stays green. + deadnix.enable = false; + actionlint.enable = true; + trim-trailing-whitespace.enable = true; + end-of-file-fixer.enable = true; + check-yaml.enable = true; + check-toml.enable = true; + check-added-large-files.enable = true; + check-merge-conflicts.enable = true; + detect-private-keys.enable = true; + }; }; }; @@ -40,6 +71,14 @@ inputs.nixpkgs-lib.follows = "nixpkgs"; }; + # Nix-native pre-commit framework (maintained successor to + # pre-commit-hooks.nix). Follows our nixpkgs to avoid version skew + # with the rest of the flake. + git-hooks-nix = { + url = "github:cachix/git-hooks.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; # rest of inputs, alphabetical order diff --git a/home/editors/nvim/config/lazyvim.json b/home/editors/nvim/config/lazyvim.json index 1f08b29..c52d0f3 100644 --- a/home/editors/nvim/config/lazyvim.json +++ b/home/editors/nvim/config/lazyvim.json @@ -7,4 +7,4 @@ "NEWS.md": "11866" }, "version": 8 -} \ No newline at end of file +} diff --git a/home/editors/nvim/config/stylua.toml b/home/editors/nvim/config/stylua.toml index 5d6c50d..0f90030 100644 --- a/home/editors/nvim/config/stylua.toml +++ b/home/editors/nvim/config/stylua.toml @@ -1,3 +1,3 @@ indent_type = "Spaces" indent_width = 2 -column_width = 120 \ No newline at end of file +column_width = 120 diff --git a/home/packages/wayland/niri/_to-KDL.nix b/home/packages/wayland/niri/_to-KDL.nix index 9863c66..60ca338 100644 --- a/home/packages/wayland/niri/_to-KDL.nix +++ b/home/packages/wayland/niri/_to-KDL.nix @@ -36,10 +36,11 @@ ( if typeOf element == "null" then "null" - else if element == false - then "false" - else if element == true - then "true" + else if typeOf element == "bool" + then + if element + then "true" + else "false" else if typeOf element == "string" then ''"${sanitizeString element}"'' else toString element diff --git a/home/packages/wayland/niri/default.nix b/home/packages/wayland/niri/default.nix index 49a41ef..fe75eef 100644 --- a/home/packages/wayland/niri/default.nix +++ b/home/packages/wayland/niri/default.nix @@ -8,7 +8,7 @@ binds = import ./_binds.nix {inherit pkgs;}; rules = import ./_rules.nix; - finalConfig = toKDL.generate "niri-config.kdl" (settings // {binds = binds;} // rules); + finalConfig = toKDL.generate "niri-config.kdl" (settings // {inherit binds;} // rules); in { environment.sessionVariables = { NIRI_CONFIG = "$HOME/.config/niri/config.kdl"; diff --git a/home/terminal/software/xdg.nix b/home/terminal/software/xdg.nix index 83a5c90..2129fc3 100644 --- a/home/terminal/software/xdg.nix +++ b/home/terminal/software/xdg.nix @@ -80,19 +80,21 @@ in { enable = true; defaultApplications = associations; }; - configFile."xdg-desktop-portal-termfilechooser/config".text = '' - [filechooser] - cmd=${yaziWrapper}/bin/yazi-wrapper - default_dir=$HOME - open_mode=suggested - save_mode=suggested - ''; - configFile."user-dirs.dirs".source = userDirsConfig; - configFile."mimeapps.list".text = '' - [Default Applications] - ${lib.concatStringsSep "\n" (lib.mapAttrsToList (k: v: "${k}=${lib.concatStringsSep ";" v}") associations)} - [Added Associations] - ${lib.concatStringsSep "\n" (lib.mapAttrsToList (k: v: "${k}=${lib.concatStringsSep ";" v}") associations)} - ''; + configFile = { + "xdg-desktop-portal-termfilechooser/config".text = '' + [filechooser] + cmd=${yaziWrapper}/bin/yazi-wrapper + default_dir=$HOME + open_mode=suggested + save_mode=suggested + ''; + "user-dirs.dirs".source = userDirsConfig; + "mimeapps.list".text = '' + [Default Applications] + ${lib.concatStringsSep "\n" (lib.mapAttrsToList (k: v: "${k}=${lib.concatStringsSep ";" v}") associations)} + [Added Associations] + ${lib.concatStringsSep "\n" (lib.mapAttrsToList (k: v: "${k}=${lib.concatStringsSep ";" v}") associations)} + ''; + }; }; } diff --git a/hosts/default.nix b/hosts/default.nix index 82860ba..a9bc129 100644 --- a/hosts/default.nix +++ b/hosts/default.nix @@ -55,8 +55,7 @@ in { hetzner = nixosSystem24 { system = "x86_64-linux"; specialArgs = { - inherit inputs self; - unstablePkgs = unstablePkgs; + inherit inputs self unstablePkgs; }; modules = [ inputs.disko.nixosModules.disko diff --git a/hosts/facter.json b/hosts/facter.json index d19ed60..fe5bd41 100644 --- a/hosts/facter.json +++ b/hosts/facter.json @@ -3487,4 +3487,4 @@ } } } -} \ No newline at end of file +} diff --git a/secrets/aur.age b/secrets/aur.age index 031623721312373621b4143f286a391e356d78e9..25239035321f21becb3b8f5bad3adae106d032a7 100644 GIT binary patch delta 9 QcmaFQ@_}VTJ`*Ds02Fou6aWAK delta 7 Ocmeys@}6ZwJ`(^AS_26H diff --git a/secrets/gitea_laptop.age b/secrets/gitea_laptop.age index b09d6ef20fa62c3f8a111248e2fb6c62d88fdc51..099a52f0b74f4fe5b39d19ca244747600236186c 100644 GIT binary patch delta 9 QcmaFN@`PnWG7}>g0277+@Bjb+ delta 7 OcmaFD@|a~qG7|s|vI6A* diff --git a/secrets/github_key.age b/secrets/github_key.age index 342181b3a83d84042a29bdedbb36b6c30a86ddeb..c1cc51f7810c1b4285aa4ed070b0a18779dcd233 100644 GIT binary patch delta 9 QcmaFQ@_}VTJ`*Ds02Fou6aWAK delta 7 Ocmeys@}6ZwJ`(^AS_26H diff --git a/secrets/hetzner_server.age b/secrets/hetzner_server.age index 5a4d271..5690047 100644 --- a/secrets/hetzner_server.age +++ b/secrets/hetzner_server.age @@ -3,4 +3,4 @@ age-encryption.org/v1 K8kjoDS2ODYJdNZCUmhTfQOWFHEFCkb90d9K5fEnxYo --- j1a/Uo7QXWC0dnbSGKaRj/T9GkChC7BQf6kYC0HN8f4 }N ,xoT#cdb,owah@Z&WU]S6T )Yg -/?Ԡ8B£O7zRQي6sfSka$J'jBf,Խc Zn ذogproLIPL gA2L~---T-f>PAa'npPص'ײ.oKn:@̶m~Psŋu[z+ԵCf*+~WXx6(3io~lw;5BjF~En} * dnAڄo#;Dw~h^"zbY֍CPpTQID H_ BBh.+4({3}T3?g{~PτeF,T/_ ̽ \ No newline at end of file +/?Ԡ8B£O7zRQي6sfSka$J'jBf,Խc Zn ذogproLIPL gA2L~---T-f>PAa'npPص'ײ.oKn:@̶m~Psŋu[z+ԵCf*+~WXx6(3io~lw;5BjF~En} * dnAڄo#;Dw~h^"zbY֍CPpTQID H_ BBh.+4({3}T3?g{~PτeF,T/_ ̽ diff --git a/secrets/sporestack_server.age b/secrets/sporestack_server.age index 9aebf5714f554d5873b16c6bc700aa30f8bed904..676cf66e44b041237ea295237923e8a8e0aea3aa 100644 GIT binary patch delta 9 QcmaFQ@_}VTJ`*Ds02Fou6aWAK delta 7 Ocmeys@}6ZwJ`(^AS_26H diff --git a/secrets/ssh_config.age b/secrets/ssh_config.age index 11d723156782f58db786f9832c4107b4fdf4963f..60abf25aa6ad5103ee384049fd1daed05495b49e 100644 GIT binary patch delta 9 QcmbQnKAnBTG-gIF01vMMkN^Mx delta 7 OcmbQvK8=0DG-dz_n*xOZ diff --git a/servers/hetzner/modules/services/coredns.nix b/servers/hetzner/modules/services/coredns.nix index db78aa8..849f3e0 100644 --- a/servers/hetzner/modules/services/coredns.nix +++ b/servers/hetzner/modules/services/coredns.nix @@ -71,37 +71,48 @@ severijnse.eu IN SSHFP 4 2 c02f4b13aef78579f3466851fcc741c0169eaa63237463ceacc9bb72a2be0519 ''; in { - # Decrypt DNSSEC keys from sops-encrypted file using the age key at /etc/age/keys.txt - # Uses sops CLI directly instead of sops-nix's sops-install-secrets (avoids Go 1.25 build dep) - systemd.services.decrypt-coredns-keys = { - description = "Decrypt CoreDNS DNSSEC signing keys"; - before = ["coredns.service"]; - wantedBy = ["coredns.service"]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - Environment = "SOPS_AGE_KEY_FILE=/etc/age/keys.txt"; + systemd = { + services = { + decrypt-coredns-keys = { + description = "Decrypt CoreDNS DNSSEC signing keys"; + before = ["coredns.service"]; + wantedBy = ["coredns.service"]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + Environment = "SOPS_AGE_KEY_FILE=/etc/age/keys.txt"; + }; + script = '' + SOPS_FILE=${../../secrets/coredns-keys.yaml} + KEYS_DIR=/var/lib/coredns/keys + mkdir -p "$KEYS_DIR" + + ${pkgs.sops}/bin/sops --decrypt --extract '["coredns_ksk_key"]' "$SOPS_FILE" > "$KEYS_DIR/Kseverijnse.eu.+013+20930.key" + ${pkgs.sops}/bin/sops --decrypt --extract '["coredns_ksk_private"]' "$SOPS_FILE" > "$KEYS_DIR/Kseverijnse.eu.+013+20930.private" + ${pkgs.sops}/bin/sops --decrypt --extract '["coredns_zsk_key"]' "$SOPS_FILE" > "$KEYS_DIR/Kseverijnse.eu.+013+38678.key" + ${pkgs.sops}/bin/sops --decrypt --extract '["coredns_zsk_private"]' "$SOPS_FILE" > "$KEYS_DIR/Kseverijnse.eu.+013+38678.private" + + chmod 644 "$KEYS_DIR/"* + ''; + }; + coredns = { + after = ["decrypt-coredns-keys.service"]; + requires = ["decrypt-coredns-keys.service"]; + + # Copy zone file from Nix store to writable location on service start + # TLSA updater will modify the writable copy at runtime + preStart = '' + cp -f ${zoneFile} /var/lib/coredns/zones/severijnse.eu.db + chown coredns:coredns /var/lib/coredns/zones/severijnse.eu.db + ''; + }; }; - script = '' - SOPS_FILE=${../../secrets/coredns-keys.yaml} - KEYS_DIR=/var/lib/coredns/keys - mkdir -p "$KEYS_DIR" - - ${pkgs.sops}/bin/sops --decrypt --extract '["coredns_ksk_key"]' "$SOPS_FILE" > "$KEYS_DIR/Kseverijnse.eu.+013+20930.key" - ${pkgs.sops}/bin/sops --decrypt --extract '["coredns_ksk_private"]' "$SOPS_FILE" > "$KEYS_DIR/Kseverijnse.eu.+013+20930.private" - ${pkgs.sops}/bin/sops --decrypt --extract '["coredns_zsk_key"]' "$SOPS_FILE" > "$KEYS_DIR/Kseverijnse.eu.+013+38678.key" - ${pkgs.sops}/bin/sops --decrypt --extract '["coredns_zsk_private"]' "$SOPS_FILE" > "$KEYS_DIR/Kseverijnse.eu.+013+38678.private" - - chmod 644 "$KEYS_DIR/"* - ''; + tmpfiles.rules = [ + "d /var/lib/coredns 0750 coredns coredns -" + "d /var/lib/coredns/zones 0750 coredns coredns -" + "d /var/lib/coredns/keys 0750 coredns coredns -" + ]; }; - - # Ensure coredns waits for key decryption - systemd.services.coredns = { - after = ["decrypt-coredns-keys.service"]; - requires = ["decrypt-coredns-keys.service"]; - }; - services.coredns = { enable = true; config = '' @@ -126,19 +137,4 @@ in { } ''; }; - - # Copy zone file from Nix store to writable location on service start - # TLSA updater will modify the writable copy at runtime - systemd.services.coredns = { - preStart = '' - cp -f ${zoneFile} /var/lib/coredns/zones/severijnse.eu.db - chown coredns:coredns /var/lib/coredns/zones/severijnse.eu.db - ''; - }; - - systemd.tmpfiles.rules = [ - "d /var/lib/coredns 0750 coredns coredns -" - "d /var/lib/coredns/zones 0750 coredns coredns -" - "d /var/lib/coredns/keys 0750 coredns coredns -" - ]; } diff --git a/servers/hetzner/modules/services/gitea.nix b/servers/hetzner/modules/services/gitea.nix index f1cfbcd..0745c56 100644 --- a/servers/hetzner/modules/services/gitea.nix +++ b/servers/hetzner/modules/services/gitea.nix @@ -5,50 +5,85 @@ unstablePkgs, ... }: { - services.postgresql = { - enable = true; - package = pkgs.postgresql_14; - ensureDatabases = ["gitea"]; - ensureUsers = [ - { + services = { + postgresql = { + enable = true; + package = pkgs.postgresql_14; + ensureDatabases = ["gitea"]; + ensureUsers = [ + { + name = "gitea"; + ensureDBOwnership = true; + } + ]; + }; + + gitea = { + enable = true; + package = unstablePkgs.gitea; + database = { + type = "postgres"; name = "gitea"; - ensureDBOwnership = true; - } - ]; + user = "gitea"; + }; + appName = "Jory's Git"; + lfs.enable = true; + settings = { + server = { + DOMAIN = "git.severijnse.eu"; + ROOT_URL = "https://git.severijnse.eu/"; + HTTP_PORT = 3000; + SSH_PORT = 222; + SSH_LISTEN_PORT = 2222; + START_SSH_SERVER = true; + SSH_USER = "git"; + BUILTIN_SSH_SERVER_USER = "git"; + LANDING_PAGE = "explore"; + }; + service = { + DISABLE_REGISTRATION = true; + REQUIRE_SIGNIN_VIEW = false; + }; + repository = { + DEFAULT_BRANCH = "main"; + }; + actions = { + ENABLED = true; + }; + }; + }; + + gitea-actions-runner = { + # nixos-24-05's gitea-actions-runner module hardcodes bin/act_runner, + # but the current upstream package (1.0.3, matching gitea 1.26) ships + # bin/gitea-runner. Wrap it so both names resolve. + package = pkgs.runCommand "gitea-actions-runner-wrapped" {} '' + mkdir -p $out/bin + ln -s ${unstablePkgs.gitea-actions-runner}/bin/gitea-runner $out/bin/act_runner + ''; + instances.default = { + enable = true; + name = "hetzner"; + url = "https://git.severijnse.eu"; + tokenFile = "/var/lib/secrets/gitea-runner-token"; + labels = ["native:host"]; + hostPackages = with pkgs; [ + bash + coreutils + curl + gawk + gitMinimal + gnused + nodejs + wget + ]; + }; + }; }; # Gitea connects to local Postgres via Unix socket (peer auth). # No password needed — the socket is at /run/postgresql by default. # createDatabase = true ensures the DB + user are set up automatically. - services.gitea = { - enable = true; - package = unstablePkgs.gitea; - database.type = "postgres"; - database.name = "gitea"; - database.user = "gitea"; - appName = "Jory's Git"; - lfs.enable = true; - settings = { - server = { - DOMAIN = "git.severijnse.eu"; - ROOT_URL = "https://git.severijnse.eu/"; - HTTP_PORT = 3000; - SSH_PORT = 222; - SSH_LISTEN_PORT = 2222; - START_SSH_SERVER = true; - SSH_USER = "git"; - BUILTIN_SSH_SERVER_USER = "git"; - LANDING_PAGE = "explore"; - }; - service = { - DISABLE_REGISTRATION = true; - REQUIRE_SIGNIN_VIEW = false; - }; - repository = { - DEFAULT_BRANCH = "main"; - }; - }; - }; # Gitea built-in SSH server: listens on high port 2222 (no privileged-cap needed), # while clone URLs advertise port 222. Firewall redirects 222 -> 2222. @@ -61,4 +96,10 @@ ${pkgs.nftables}/bin/nft add chain inet gitea-redirect prerouting '{ type nat hook prerouting priority dstnat; }' 2>/dev/null || true ${pkgs.nftables}/bin/nft add rule inet gitea-redirect prerouting tcp dport 222 redirect to :2222 2>/dev/null || true ''; + + # --- Gitea Actions self-hosted CI runner --- + # Jobs install their own Nix inside the runner (official installer, + # --no-daemon) so no system Nix daemon / nix-users group is needed. + # aarch64 builds run under QEMU user-emulation via boot.binfmt below. + boot.binfmt.emulatedSystems = ["aarch64-linux"]; } diff --git a/servers/hetzner/modules/services/tlsa-updater.nix b/servers/hetzner/modules/services/tlsa-updater.nix index 7e4f257..592fb04 100644 --- a/servers/hetzner/modules/services/tlsa-updater.nix +++ b/servers/hetzner/modules/services/tlsa-updater.nix @@ -58,42 +58,44 @@ echo "tlsa-update: TLSA set to $HEX" ''; in { - # Ensure the distribution dir exists (Caddy does not write here). - systemd.tmpfiles.rules = [ - "d ${distCertDir} 0755 root root - -" - ]; + systemd = { + # Ensure the distribution dir exists (Caddy does not write here). + tmpfiles.rules = [ + "d ${distCertDir} 0755 root root - -" + ]; - systemd.services.tlsa-update = { - description = "Sync Caddy TLS certificate to mail server and update DANE/TLSA records"; - after = ["caddy.service" "coredns.service"]; - partOf = ["coredns.service"]; - wantedBy = ["multi-user.target"]; - path = with pkgs; [openssl coreutils gnused podman systemd]; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${syncScript}"; - User = "root"; - Group = "root"; + services.tlsa-update = { + description = "Sync Caddy TLS certificate to mail server and update DANE/TLSA records"; + after = ["caddy.service" "coredns.service"]; + partOf = ["coredns.service"]; + wantedBy = ["multi-user.target"]; + path = with pkgs; [openssl coreutils gnused podman systemd]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${syncScript}"; + User = "root"; + Group = "root"; + }; }; - }; - # Fire as soon as Caddy rewrites the certificate on renewal (the atomic rewrite - # changes the directory mtime), eliminating the up-to-24h DANE drift window. - systemd.paths.tlsa-update = { - description = "Watch Caddy certificate directory for renewal"; - wantedBy = ["paths.target"]; - pathConfig = { - PathModified = [caddyCertDir]; - Unit = "tlsa-update.service"; + # Fire as soon as Caddy rewrites the certificate on renewal (the atomic rewrite + # changes the directory mtime), eliminating the up-to-24h DANE drift window. + paths.tlsa-update = { + description = "Watch Caddy certificate directory for renewal"; + wantedBy = ["paths.target"]; + pathConfig = { + PathModified = [caddyCertDir]; + Unit = "tlsa-update.service"; + }; }; - }; - # Fallback in case a renewal event is missed (e.g. inotify overflow). - systemd.timers.tlsa-update = { - wantedBy = ["timers.target"]; - timerConfig = { - OnCalendar = "daily"; - Persistent = true; + # Fallback in case a renewal event is missed (e.g. inotify overflow). + timers.tlsa-update = { + wantedBy = ["timers.target"]; + timerConfig = { + OnCalendar = "daily"; + Persistent = true; + }; }; }; } diff --git a/statix.toml b/statix.toml new file mode 100644 index 0000000..baa4f13 --- /dev/null +++ b/statix.toml @@ -0,0 +1,9 @@ +# Ignore list for statix. `ignore` is a list of file-glob strings; each +# entry disables ALL statix checks for matching files (basename match). +# hardware-configuration.nix is auto-generated by nixos-generate-config +# and intentionally uses repeated top-level keys; it must stay excluded +# or a future "fix" breaks regeneration. Keep this entry in sync if more +# generated hardware files appear. +ignore = [ + "hardware-configuration.nix", +] diff --git a/system/services/xdg-portal-fix.nix b/system/services/xdg-portal-fix.nix index 4b51df0..08c46e8 100644 --- a/system/services/xdg-portal-fix.nix +++ b/system/services/xdg-portal-fix.nix @@ -1,4 +1,4 @@ -{...}: { +_: { systemd.user.services.xdg-desktop-portal = { serviceConfig = { UnsetEnvironment = "NIX_XDG_DESKTOP_PORTAL_DIR";