aboutsummaryrefslogtreecommitdiffstats
path: root/system/services/nix-sync
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-08-02 22:39:02 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-08-02 23:13:29 +0200
commit30e649a6d43c4ef2473a1820930cbe7d43e28432 (patch)
treef34df66d41344a9289628d9c8f9e002614f97c16 /system/services/nix-sync
parentbuild(flake): Update (diff)
downloadnixos-server-30e649a6d43c4ef2473a1820930cbe7d43e28432.zip
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.
Diffstat (limited to 'system/services/nix-sync')
-rw-r--r--system/services/nix-sync/default.nix299
1 files changed, 0 insertions, 299 deletions
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
- <link xlink:href="https://git-scm.com/docs/git-clone#_git_urls"/>
- 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.";
- };
-}