From 30e649a6d43c4ef2473a1820930cbe7d43e28432 Mon Sep 17 00:00:00 2001 From: Benedikt Peetz Date: Fri, 2 Aug 2024 22:39:02 +0200 Subject: refactor(nixos/{nginx, nix-sync}): Migrate from `system/services` Nix-sync was sort-of mixed into the nginx configuration, thus separating it completely seemed reasonable. --- modules/nixos/vhack/default.nix | 2 + modules/nixos/vhack/nginx/default.nix | 68 +++++++ modules/nixos/vhack/nginx/redirects.nix | 6 + modules/nixos/vhack/nix-sync/default.nix | 61 +++++++ modules/nixos/vhack/nix-sync/hosts.nix | 48 +++++ modules/nixos/vhack/nix-sync/module.nix | 299 +++++++++++++++++++++++++++++++ system/services/default.nix | 2 - system/services/nginx/default.nix | 79 -------- system/services/nginx/hosts.nix | 48 ----- system/services/nginx/redirects.nix | 6 - system/services/nix-sync/default.nix | 299 ------------------------------- 11 files changed, 484 insertions(+), 434 deletions(-) create mode 100644 modules/nixos/vhack/nginx/default.nix create mode 100644 modules/nixos/vhack/nginx/redirects.nix create mode 100644 modules/nixos/vhack/nix-sync/default.nix create mode 100644 modules/nixos/vhack/nix-sync/hosts.nix create mode 100644 modules/nixos/vhack/nix-sync/module.nix delete mode 100644 system/services/nginx/default.nix delete mode 100644 system/services/nginx/hosts.nix delete mode 100644 system/services/nginx/redirects.nix delete mode 100644 system/services/nix-sync/default.nix diff --git a/modules/nixos/vhack/default.nix b/modules/nixos/vhack/default.nix index 1c98f58..cb0131f 100644 --- a/modules/nixos/vhack/default.nix +++ b/modules/nixos/vhack/default.nix @@ -2,6 +2,8 @@ imports = [ ./etesync ./git-server + ./nginx + ./nix-sync ./peertube ]; } diff --git a/modules/nixos/vhack/nginx/default.nix b/modules/nixos/vhack/nginx/default.nix new file mode 100644 index 0000000..6a82147 --- /dev/null +++ b/modules/nixos/vhack/nginx/default.nix @@ -0,0 +1,68 @@ +{ + lib, + config, + ... +}: let + importedRedirects = import ./redirects.nix {}; + mkRedirect = { + key, + value, + }: { + name = key; + value = { + forceSSL = true; + enableACME = true; + locations."/".return = "301 ${value}"; + }; + }; + + redirects = builtins.listToAttrs (builtins.map mkRedirect importedRedirects); + + cfg = config.vhack.nginx; +in { + options.vhack.nginx = { + enable = lib.mkEnableOption '' + a default nginx config. + ''; + + selfsign = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Whether to selfsign the acme certificates. This should only + really be useful for tests. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + security.acme = { + acceptTerms = true; + defaults = { + email = "admin@vhack.eu"; + webroot = "/var/lib/acme/acme-challenge"; + + # Avoid spamming the acme server, if we run in a test, and only really want self-signed + # certificates + server = lib.mkIf cfg.selfsign "https://127.0.0.1"; + }; + }; + + networking.firewall = { + allowedTCPPorts = [80 443]; + }; + services.nginx = { + enable = true; + # The merge here is fine, as no domain should be specified twice + virtualHosts = + { + "gallery.s-schoeffel.de" = { + forceSSL = true; + enableACME = true; + root = "/srv/gallery.s-schoeffel.de"; + }; + } + // redirects; + }; + }; +} diff --git a/modules/nixos/vhack/nginx/redirects.nix b/modules/nixos/vhack/nginx/redirects.nix new file mode 100644 index 0000000..a021e72 --- /dev/null +++ b/modules/nixos/vhack/nginx/redirects.nix @@ -0,0 +1,6 @@ +{...}: [ + { + key = "source.vhack.eu"; + value = "https://codeberg.org/vhack.eu/nixos-server"; + } +] diff --git a/modules/nixos/vhack/nix-sync/default.nix b/modules/nixos/vhack/nix-sync/default.nix new file mode 100644 index 0000000..a624e0e --- /dev/null +++ b/modules/nixos/vhack/nix-sync/default.nix @@ -0,0 +1,61 @@ +{ + config, + lib, + ... +}: let + cfg = config.vhack.nix-sync; + + mkNixSyncRepository = { + domain, + root ? "", + url, + extraSettings ? {}, + }: { + name = "${domain}"; + value = { + path = "/etc/nginx/websites/${domain}/${root}"; + uri = "${url}"; + inherit extraSettings; + }; + }; + nixSyncRepositories = builtins.listToAttrs (builtins.map mkNixSyncRepository domains); + + mkVirtHost = { + domain, + root ? "", + url, + extraSettings ? {}, + }: { + name = "${domain}"; + value = + lib.recursiveUpdate { + forceSSL = true; + enableACME = true; + root = "/etc/nginx/websites/${domain}/${root}"; + } + extraSettings; + }; + virtHosts = builtins.listToAttrs (builtins.map mkVirtHost domains); + + domains = import ./hosts.nix {}; +in { + imports = [ + ./module.nix + ]; + + options.vhack.nix-sync = { + enable = lib.mkEnableOption '' + a website git ops solution. + ''; + }; + + config = lib.mkIf cfg.enable { + services.nix-sync = { + enable = true; + repositories = nixSyncRepositories; + }; + + vhack.nginx.enable = true; + services.nginx.virtualHosts = virtHosts; + }; +} diff --git a/modules/nixos/vhack/nix-sync/hosts.nix b/modules/nixos/vhack/nix-sync/hosts.nix new file mode 100644 index 0000000..98dbbf1 --- /dev/null +++ b/modules/nixos/vhack/nix-sync/hosts.nix @@ -0,0 +1,48 @@ +{...}: let + extraWkdSettings = { + locations."/.well-known/openpgpkey/hu/".extraConfig = '' + default_type application/octet-stream; + + # Came from: https://www.uriports.com/blog/setting-up-openpgp-web-key-directory/ + # No idea if it is actually necessary + # add_header Access-Control-Allow-Origin * always; + ''; + }; +in [ + { + domain = "vhack.eu"; + url = "https://codeberg.org/vhack.eu/website.git"; + } + { + domain = "b-peetz.de"; + url = "https://codeberg.org/bpeetz/b-peetz.de.git"; + } + + # Trinitrix + { + domain = "trinitrix.vhack.eu"; + url = "https://codeberg.org/trinitrix/website.git"; + } + + # WKD + { + domain = "openpgpkey.b-peetz.de"; + url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; + extraSettings = extraWkdSettings; + } + { + domain = "openpgpkey.s-schoeffel.de"; + url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; + extraSettings = extraWkdSettings; + } + { + domain = "openpgpkey.sils.li"; + url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; + extraSettings = extraWkdSettings; + } + { + domain = "openpgpkey.vhack.eu"; + url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; + extraSettings = extraWkdSettings; + } +] diff --git a/modules/nixos/vhack/nix-sync/module.nix b/modules/nixos/vhack/nix-sync/module.nix new file mode 100644 index 0000000..a3ab0af --- /dev/null +++ b/modules/nixos/vhack/nix-sync/module.nix @@ -0,0 +1,299 @@ +{ + config, + lib, + pkgs, + ... +}: let + cfg = config.services.nix-sync; + esa = lib.strings.escapeShellArg; + + mkTimer = name: repo: { + description = "Nix sync ${name} timer"; + wantedBy = ["timers.target"]; + timerConfig = { + OnUnitActiveSec = repo.interval; + }; + wants = ["network-online.target"]; + after = ["network-online.target"]; + }; + + parents = path: let + split_path = builtins.split "/" path; + filename = builtins.elemAt split_path (builtins.length split_path - 1); + path_build = + lib.strings.removeSuffix "/" (builtins.replaceStrings [filename] [""] path); + final_path = + if filename == "" + then parents path_build + else path_build; + in + final_path; + + mkUnit = name: repo: let + optionalPathSeparator = + if lib.strings.hasPrefix "/" repo.path + then "" + else "/"; + /* + * `ln` tries to create a symlink in the directory, if the target ends with a '/', + * thus remove it. + */ + repoPath = lib.strings.removeSuffix "/" repo.path; + + repoCachePath = cfg.cachePath + optionalPathSeparator + repo.path; + execStartScript = pkgs.writeScript "nix-sync-exec" '' + #! /usr/bin/env dash + export XDG_CACHE_HOME="$CACHE_DIRECTORY"; + cd ${esa repoCachePath}; + + git fetch + origin="$(git rev-parse @{u})"; + branch="$(git rev-parse @)"; + + if ! [ "$origin" = "$branch" ]; then + git pull --rebase; + + out_paths=$(mktemp); + nix build . --print-out-paths --experimental-features 'nix-command flakes' > "$out_paths"; + [ "$(wc -l < "$out_paths")" -gt 1 ] && (echo "To many out-paths"; exit 1) + out_path="$(cat "$out_paths")"; + rm ${esa repoPath}; + ln -s "$out_path" ${esa repoPath}; + rm "$out_paths"; + fi + ''; + execStartPreScript = '' + export XDG_CACHE_HOME="$CACHE_DIRECTORY"; + + if ! [ -d ${esa repoCachePath}/.git ]; then + mkdir --parents ${esa repoCachePath}; + git clone ${esa repo.uri} ${esa repoCachePath}; + + out_paths=$(mktemp); + nix build ${esa repoCachePath} --print-out-paths --experimental-features 'nix-command flakes' > "$out_paths"; + [ "$(wc -l < "$out_paths")" -gt 1 ] && (echo "To many out-paths"; exit 1) + out_path="$(cat "$out_paths")"; + ln -s "$out_path" ${esa repoPath}; + rm "$out_paths"; + fi + + if ! [ -L ${esa repoPath} ]; then + cd ${esa repoCachePath}; + + git pull --rebase; + + out_paths=$(mktemp); + nix build . --print-out-paths --experimental-features 'nix-command flakes' > "$out_paths"; + [ "$(wc -l < "$out_paths")" -gt 1 ] && { echo "To many out-paths"; exit 1; } + out_path="$(cat "$out_paths")"; + + if [ -d ${esa repoPath} ]; then + rm -d ${esa repoPath}; + else + mkdir --parents "$(dirname ${esa repoPath})"; + fi + [ -e ${esa repoPath} ] && rm ${esa repoPath}; + + ln -s "$out_path" ${esa repoPath}; + rm "$out_paths"; + fi + ''; + in { + description = "Nix Sync ${name}"; + wantedBy = ["default.target"]; + after = ["network.target"]; + path = with pkgs; [openssh git nix mktemp coreutils dash]; + preStart = execStartPreScript; + + serviceConfig = { + TimeoutSec = 0; + ExecStart = execStartScript; + Restart = "on-abort"; + # User and group + User = cfg.user; + Group = cfg.group; + # Runtime directory and mode + RuntimeDirectory = "nix-sync"; + RuntimeDirectoryMode = "0750"; + # Cache directory and mode + CacheDirectory = "nix-sync"; + CacheDirectoryMode = "0750"; + # Logs directory and mode + LogsDirectory = "nix-sync"; + LogsDirectoryMode = "0750"; + # Proc filesystem + ProcSubset = "all"; + ProtectProc = "invisible"; + # New file permissions + UMask = "0027"; # 0640 / 0750 + # Capabilities + AmbientCapabilities = ["CAP_CHOWN"]; + CapabilityBoundingSet = ["CAP_CHOWN"]; + # Security + NoNewPrivileges = true; + # Sandboxing (sorted by occurrence in https://www.freedesktop.org/software/systemd/man/systemd.exec.html) + ReadWritePaths = ["${esa (parents repo.path)}" "-${esa (parents repoCachePath)}" "-${esa cfg.cachePath}"]; + ReadOnlyPaths = ["/nix"]; # TODO: Should be irrelevant, as we have ProtectSystem=Strict <2024-06-01> + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + ProtectHostname = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"]; + RestrictNamespaces = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RemoveIPC = true; + PrivateMounts = true; + # System Call Filtering + SystemCallArchitectures = "native"; + SystemCallFilter = ["~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid"]; + }; + }; + + services = + lib.mapAttrs' (name: repo: { + name = "nix-sync-${name}"; + value = mkUnit name repo; + }) + cfg.repositories; + timers = + lib.mapAttrs' (name: repo: { + name = "nix-sync-${name}"; + value = mkTimer name repo; + }) + cfg.repositories; + + # generate the websites directory, so systemd can mount it read write + generatedDirectories = + lib.mapAttrsToList ( + _: repo: "d ${esa (parents repo.path)} 0755 ${cfg.user} ${cfg.group}" + ) + cfg.repositories; + + repositoryType = lib.types.submodule ({name, ...}: { + options = { + name = lib.mkOption { + internal = true; + default = name; + type = lib.types.str; + description = "The name that should be given to this unit."; + }; + + path = lib.mkOption { + type = lib.types.str; + description = "The path at which to sync the repository"; + }; + + uri = lib.mkOption { + type = lib.types.str; + example = "ssh://user@example.com:/~[user]/path/to/repo.git"; + description = '' + The URI of the remote to be synchronized. This is only used in the + event that the directory does not already exist. See + + for the supported URIs. + ''; + }; + + extraSettings = lib.mkOption { + type = lib.types.attrsOf lib.types.anything; + example = lib.literalExpression '' + { + locations."/.well-known/openpgpkey/hu/" = { + extraConfig = \'\' + default_type application/octet-stream; + + add_header Access-Control-Allow-Origin * always; + \'\'; + }; + } + ''; + description = '' + Extra config to add the the nginx virtual host. + ''; + }; + + interval = lib.mkOption { + type = lib.types.int; + default = 500; + description = '' + The interval, specified in seconds, at which the synchronization will + be triggered. + ''; + }; + }; + }); +in { + options = { + services.nix-sync = { + enable = lib.mkEnableOption "nix-sync services"; + + user = lib.mkOption { + type = lib.types.str; + default = "nix-sync"; + description = lib.mdDoc "User account under which nix-sync units runs."; + }; + + group = lib.mkOption { + type = lib.types.str; + default = "nix-sync"; + description = lib.mdDoc "Group account under which nix-sync units runs."; + }; + + cachePath = lib.mkOption { + type = lib.types.str; + default = "/var/lib/nix-sync"; + description = lib.mdDoc '' + Where to cache git directories. Should not end with a slash ("/") + ''; + }; + + repositories = lib.mkOption { + type = with lib.types; attrsOf repositoryType; + description = '' + The repositories that should be synchronized. + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + assertions = [ + { + assertion = !lib.strings.hasSuffix "/" cfg.cachePath; + message = "Your cachePath ('${cfg.cachePath}') ends with a slash ('/'), please use: '${lib.strings.removeSuffix "/" cfg.cachePath}'."; + } + ]; + systemd = { + tmpfiles.rules = + generatedDirectories; + + inherit services timers; + }; + users.users = + if cfg.user == "nix-sync" + then { + nix-sync = { + group = "${cfg.group}"; + isSystemUser = true; + }; + } + else lib.warnIf (cfg.user != "nix-sync") "The user (${cfg.user}) is not \"nix-sync\", thus you are responible for generating it."; + users.groups = + if cfg.group == "nix-sync" + then { + nix-sync = { + members = ["${cfg.user}"]; + }; + } + else lib.warnIf (cfg.group != "nix-sync") "The group (${cfg.group}) is not \"nix-sync\", thus you are responible for generating it."; + }; +} diff --git a/system/services/default.nix b/system/services/default.nix index 8b8151a..3155272 100644 --- a/system/services/default.nix +++ b/system/services/default.nix @@ -10,9 +10,7 @@ ./minecraft ./miniflux ./murmur - ./nginx ./nix - ./nix-sync ./openssh ./restic ./rust-motd diff --git a/system/services/nginx/default.nix b/system/services/nginx/default.nix deleted file mode 100644 index b804754..0000000 --- a/system/services/nginx/default.nix +++ /dev/null @@ -1,79 +0,0 @@ -{lib, ...}: let - domains = import ./hosts.nix {}; - importedRedirects = import ./redirects.nix {}; - mkRedirect = { - key, - value, - }: { - name = key; - value = { - forceSSL = true; - enableACME = true; - locations."/".return = "301 ${value}"; - }; - }; - mkVirtHost = { - domain, - root ? "", - url, - extraSettings ? {}, - }: { - name = "${domain}"; - value = - lib.recursiveUpdate { - forceSSL = true; - enableACME = true; - root = "/etc/nginx/websites/${domain}/${root}"; - } - extraSettings; - }; - - mkNixSyncRepository = { - domain, - root ? "", - url, - extraSettings ? {}, - }: { - name = "${domain}"; - value = { - path = "/etc/nginx/websites/${domain}/${root}"; - uri = "${url}"; - inherit extraSettings; - }; - }; - - virtHosts = builtins.listToAttrs (builtins.map mkVirtHost domains); - nixSyncRepositories = builtins.listToAttrs (builtins.map mkNixSyncRepository domains); - redirects = builtins.listToAttrs (builtins.map mkRedirect importedRedirects); -in { - security.acme = { - acceptTerms = true; - defaults = { - email = "admin@vhack.eu"; - webroot = "/var/lib/acme/acme-challenge"; - }; - }; - - networking.firewall = { - allowedTCPPorts = [80 443]; - }; - services.nginx = { - enable = true; - # The merge here is fine, as no domain should be specified twice - virtualHosts = - { - "gallery.s-schoeffel.de" = { - forceSSL = true; - enableACME = true; - root = "/srv/gallery.s-schoeffel.de"; - }; - } - // virtHosts - // redirects; - }; - - services.nix-sync = { - enable = true; - repositories = nixSyncRepositories; - }; -} diff --git a/system/services/nginx/hosts.nix b/system/services/nginx/hosts.nix deleted file mode 100644 index 98dbbf1..0000000 --- a/system/services/nginx/hosts.nix +++ /dev/null @@ -1,48 +0,0 @@ -{...}: let - extraWkdSettings = { - locations."/.well-known/openpgpkey/hu/".extraConfig = '' - default_type application/octet-stream; - - # Came from: https://www.uriports.com/blog/setting-up-openpgp-web-key-directory/ - # No idea if it is actually necessary - # add_header Access-Control-Allow-Origin * always; - ''; - }; -in [ - { - domain = "vhack.eu"; - url = "https://codeberg.org/vhack.eu/website.git"; - } - { - domain = "b-peetz.de"; - url = "https://codeberg.org/bpeetz/b-peetz.de.git"; - } - - # Trinitrix - { - domain = "trinitrix.vhack.eu"; - url = "https://codeberg.org/trinitrix/website.git"; - } - - # WKD - { - domain = "openpgpkey.b-peetz.de"; - url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; - extraSettings = extraWkdSettings; - } - { - domain = "openpgpkey.s-schoeffel.de"; - url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; - extraSettings = extraWkdSettings; - } - { - domain = "openpgpkey.sils.li"; - url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; - extraSettings = extraWkdSettings; - } - { - domain = "openpgpkey.vhack.eu"; - url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; - extraSettings = extraWkdSettings; - } -] diff --git a/system/services/nginx/redirects.nix b/system/services/nginx/redirects.nix deleted file mode 100644 index a021e72..0000000 --- a/system/services/nginx/redirects.nix +++ /dev/null @@ -1,6 +0,0 @@ -{...}: [ - { - key = "source.vhack.eu"; - value = "https://codeberg.org/vhack.eu/nixos-server"; - } -] diff --git a/system/services/nix-sync/default.nix b/system/services/nix-sync/default.nix deleted file mode 100644 index a3ab0af..0000000 --- a/system/services/nix-sync/default.nix +++ /dev/null @@ -1,299 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: let - cfg = config.services.nix-sync; - esa = lib.strings.escapeShellArg; - - mkTimer = name: repo: { - description = "Nix sync ${name} timer"; - wantedBy = ["timers.target"]; - timerConfig = { - OnUnitActiveSec = repo.interval; - }; - wants = ["network-online.target"]; - after = ["network-online.target"]; - }; - - parents = path: let - split_path = builtins.split "/" path; - filename = builtins.elemAt split_path (builtins.length split_path - 1); - path_build = - lib.strings.removeSuffix "/" (builtins.replaceStrings [filename] [""] path); - final_path = - if filename == "" - then parents path_build - else path_build; - in - final_path; - - mkUnit = name: repo: let - optionalPathSeparator = - if lib.strings.hasPrefix "/" repo.path - then "" - else "/"; - /* - * `ln` tries to create a symlink in the directory, if the target ends with a '/', - * thus remove it. - */ - repoPath = lib.strings.removeSuffix "/" repo.path; - - repoCachePath = cfg.cachePath + optionalPathSeparator + repo.path; - execStartScript = pkgs.writeScript "nix-sync-exec" '' - #! /usr/bin/env dash - export XDG_CACHE_HOME="$CACHE_DIRECTORY"; - cd ${esa repoCachePath}; - - git fetch - origin="$(git rev-parse @{u})"; - branch="$(git rev-parse @)"; - - if ! [ "$origin" = "$branch" ]; then - git pull --rebase; - - out_paths=$(mktemp); - nix build . --print-out-paths --experimental-features 'nix-command flakes' > "$out_paths"; - [ "$(wc -l < "$out_paths")" -gt 1 ] && (echo "To many out-paths"; exit 1) - out_path="$(cat "$out_paths")"; - rm ${esa repoPath}; - ln -s "$out_path" ${esa repoPath}; - rm "$out_paths"; - fi - ''; - execStartPreScript = '' - export XDG_CACHE_HOME="$CACHE_DIRECTORY"; - - if ! [ -d ${esa repoCachePath}/.git ]; then - mkdir --parents ${esa repoCachePath}; - git clone ${esa repo.uri} ${esa repoCachePath}; - - out_paths=$(mktemp); - nix build ${esa repoCachePath} --print-out-paths --experimental-features 'nix-command flakes' > "$out_paths"; - [ "$(wc -l < "$out_paths")" -gt 1 ] && (echo "To many out-paths"; exit 1) - out_path="$(cat "$out_paths")"; - ln -s "$out_path" ${esa repoPath}; - rm "$out_paths"; - fi - - if ! [ -L ${esa repoPath} ]; then - cd ${esa repoCachePath}; - - git pull --rebase; - - out_paths=$(mktemp); - nix build . --print-out-paths --experimental-features 'nix-command flakes' > "$out_paths"; - [ "$(wc -l < "$out_paths")" -gt 1 ] && { echo "To many out-paths"; exit 1; } - out_path="$(cat "$out_paths")"; - - if [ -d ${esa repoPath} ]; then - rm -d ${esa repoPath}; - else - mkdir --parents "$(dirname ${esa repoPath})"; - fi - [ -e ${esa repoPath} ] && rm ${esa repoPath}; - - ln -s "$out_path" ${esa repoPath}; - rm "$out_paths"; - fi - ''; - in { - description = "Nix Sync ${name}"; - wantedBy = ["default.target"]; - after = ["network.target"]; - path = with pkgs; [openssh git nix mktemp coreutils dash]; - preStart = execStartPreScript; - - serviceConfig = { - TimeoutSec = 0; - ExecStart = execStartScript; - Restart = "on-abort"; - # User and group - User = cfg.user; - Group = cfg.group; - # Runtime directory and mode - RuntimeDirectory = "nix-sync"; - RuntimeDirectoryMode = "0750"; - # Cache directory and mode - CacheDirectory = "nix-sync"; - CacheDirectoryMode = "0750"; - # Logs directory and mode - LogsDirectory = "nix-sync"; - LogsDirectoryMode = "0750"; - # Proc filesystem - ProcSubset = "all"; - ProtectProc = "invisible"; - # New file permissions - UMask = "0027"; # 0640 / 0750 - # Capabilities - AmbientCapabilities = ["CAP_CHOWN"]; - CapabilityBoundingSet = ["CAP_CHOWN"]; - # Security - NoNewPrivileges = true; - # Sandboxing (sorted by occurrence in https://www.freedesktop.org/software/systemd/man/systemd.exec.html) - ReadWritePaths = ["${esa (parents repo.path)}" "-${esa (parents repoCachePath)}" "-${esa cfg.cachePath}"]; - ReadOnlyPaths = ["/nix"]; # TODO: Should be irrelevant, as we have ProtectSystem=Strict <2024-06-01> - ProtectSystem = "strict"; - ProtectHome = true; - PrivateTmp = true; - PrivateDevices = true; - ProtectHostname = true; - ProtectClock = true; - ProtectKernelTunables = true; - ProtectKernelModules = true; - ProtectKernelLogs = true; - ProtectControlGroups = true; - RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"]; - RestrictNamespaces = true; - LockPersonality = true; - MemoryDenyWriteExecute = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - RemoveIPC = true; - PrivateMounts = true; - # System Call Filtering - SystemCallArchitectures = "native"; - SystemCallFilter = ["~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid"]; - }; - }; - - services = - lib.mapAttrs' (name: repo: { - name = "nix-sync-${name}"; - value = mkUnit name repo; - }) - cfg.repositories; - timers = - lib.mapAttrs' (name: repo: { - name = "nix-sync-${name}"; - value = mkTimer name repo; - }) - cfg.repositories; - - # generate the websites directory, so systemd can mount it read write - generatedDirectories = - lib.mapAttrsToList ( - _: repo: "d ${esa (parents repo.path)} 0755 ${cfg.user} ${cfg.group}" - ) - cfg.repositories; - - repositoryType = lib.types.submodule ({name, ...}: { - options = { - name = lib.mkOption { - internal = true; - default = name; - type = lib.types.str; - description = "The name that should be given to this unit."; - }; - - path = lib.mkOption { - type = lib.types.str; - description = "The path at which to sync the repository"; - }; - - uri = lib.mkOption { - type = lib.types.str; - example = "ssh://user@example.com:/~[user]/path/to/repo.git"; - description = '' - The URI of the remote to be synchronized. This is only used in the - event that the directory does not already exist. See - - for the supported URIs. - ''; - }; - - extraSettings = lib.mkOption { - type = lib.types.attrsOf lib.types.anything; - example = lib.literalExpression '' - { - locations."/.well-known/openpgpkey/hu/" = { - extraConfig = \'\' - default_type application/octet-stream; - - add_header Access-Control-Allow-Origin * always; - \'\'; - }; - } - ''; - description = '' - Extra config to add the the nginx virtual host. - ''; - }; - - interval = lib.mkOption { - type = lib.types.int; - default = 500; - description = '' - The interval, specified in seconds, at which the synchronization will - be triggered. - ''; - }; - }; - }); -in { - options = { - services.nix-sync = { - enable = lib.mkEnableOption "nix-sync services"; - - user = lib.mkOption { - type = lib.types.str; - default = "nix-sync"; - description = lib.mdDoc "User account under which nix-sync units runs."; - }; - - group = lib.mkOption { - type = lib.types.str; - default = "nix-sync"; - description = lib.mdDoc "Group account under which nix-sync units runs."; - }; - - cachePath = lib.mkOption { - type = lib.types.str; - default = "/var/lib/nix-sync"; - description = lib.mdDoc '' - Where to cache git directories. Should not end with a slash ("/") - ''; - }; - - repositories = lib.mkOption { - type = with lib.types; attrsOf repositoryType; - description = '' - The repositories that should be synchronized. - ''; - }; - }; - }; - - config = lib.mkIf cfg.enable { - assertions = [ - { - assertion = !lib.strings.hasSuffix "/" cfg.cachePath; - message = "Your cachePath ('${cfg.cachePath}') ends with a slash ('/'), please use: '${lib.strings.removeSuffix "/" cfg.cachePath}'."; - } - ]; - systemd = { - tmpfiles.rules = - generatedDirectories; - - inherit services timers; - }; - users.users = - if cfg.user == "nix-sync" - then { - nix-sync = { - group = "${cfg.group}"; - isSystemUser = true; - }; - } - else lib.warnIf (cfg.user != "nix-sync") "The user (${cfg.user}) is not \"nix-sync\", thus you are responible for generating it."; - users.groups = - if cfg.group == "nix-sync" - then { - nix-sync = { - members = ["${cfg.user}"]; - }; - } - else lib.warnIf (cfg.group != "nix-sync") "The group (${cfg.group}) is not \"nix-sync\", thus you are responible for generating it."; - }; -} -- cgit 1.4.1