summary refs log tree commit diff stats
path: root/system/services/nix-sync/default.nix
diff options
context:
space:
mode:
Diffstat (limited to 'system/services/nix-sync/default.nix')
-rw-r--r--system/services/nix-sync/default.nix193
1 files changed, 193 insertions, 0 deletions
diff --git a/system/services/nix-sync/default.nix b/system/services/nix-sync/default.nix
new file mode 100644
index 0000000..18511b3
--- /dev/null
+++ b/system/services/nix-sync/default.nix
@@ -0,0 +1,193 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}: let
+  cfg = config.services.nix-sync;
+
+  mkUnit = name: repo: let
+    esa = lib.strings.escapeShellArg;
+    execStartScript = lib.writeShellScript "git-sync-exec" ''
+      cd ${esa cfg.cachePath}/${esa repo.path};
+
+      while true; do
+        origin="$(git rev-parse @{u})";
+        branch="$(git rev-parse @)";
+
+        if ! [ "$origin" = "$branch" ]; then
+          git pull;
+
+          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 -r ${esa repo.path};
+          ln -s "$out_path" ${esa repo.path};
+          rm "$out-paths";
+        fi
+        sleep ${esa repo.interval};
+      done
+    '';
+    execStartPreScript = ''
+      if ! stat ${esa cfg.cachePath}/${esa repo.path}/.git; then
+          mkdir --parents ${esa cfg.cachePath}/${esa repo.path};
+          git clone ${esa repo.uri} ${esa cfg.cachePath}/${esa repo.path};
+
+          out_paths=$(mktemp);
+          nix build ${esa cfg.cachePath}/${esa repo.path} --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 repo.path};
+          rm "$out-paths";
+      fi
+    '';
+  in {
+    description = "Nix Sync ${name}";
+    wantedBy = ["default.target"];
+    after = ["network.target"];
+    path = with pkgs; [openssh git nix mktemp coreutils];
+    preStart = execStartPreScript;
+
+    serviceConfig = {
+      ExecStart = execStartScript;
+      Restart = "on-abort";
+      # User and group
+      User = cfg.user;
+      Group = cfg.group;
+      # Runtime directory and mode
+      RuntimeDirectory = "nginx";
+      RuntimeDirectoryMode = "0750";
+      # Cache directory and mode
+      CacheDirectory = "nginx";
+      CacheDirectoryMode = "0750";
+      # Logs directory and mode
+      LogsDirectory = "nginx";
+      LogsDirectoryMode = "0750";
+      # Proc filesystem
+      ProcSubset = "pid";
+      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 = ["${repo.path}" "${cfg.cachePath}/${repo.path}"];
+      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 = "git-sync-${name}";
+      value = mkUnit name repo;
+    })
+    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 = "git+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.
+        '';
+      };
+
+      interval = lib.mkOption {
+        type = lib.types.int;
+        default = 500;
+        description = ''
+          The interval, specified in seconds, at which the synchronization will
+          be triggered even without filesystem changes.
+        '';
+      };
+    };
+  });
+in {
+  options = {
+    services.git-sync = {
+      enable = lib.mkEnableOption "git-sync services";
+
+      package = lib.mkOption {
+        type = lib.types.package;
+        default = pkgs.git-sync;
+        defaultText = lib.literalExpression "pkgs.git-sync";
+        description = ''
+          Package containing the <command>git-sync</command> program.
+        '';
+      };
+
+      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.
+        '';
+      };
+
+      repositories = lib.mkOption {
+        type = with lib.types; attrsOf repositoryType;
+        description = ''
+          The repositories that should be synchronized.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services = services;
+  };
+}
+# vim: ts=2
+