diff --git a/hosts/bicep/services/git-mirrors/default.nix b/hosts/bicep/services/git-mirrors/default.nix index 089023a..0ced93d 100644 --- a/hosts/bicep/services/git-mirrors/default.nix +++ b/hosts/bicep/services/git-mirrors/default.nix @@ -1,4 +1,7 @@ { config, ... }: +let + cfg = config.services.gickup; +in { sops.secrets."gickup/github-token" = { owner = "gickup"; @@ -7,6 +10,8 @@ services.gickup = { enable = true; + dataDir = "/data/gickup"; + destinationSettings = { structured = true; zip = false; @@ -32,11 +37,11 @@ "github:yushijinhun/authlib-injector" = defaultGithubConfig; "github:Git-Mediawiki/Git-Mediawiki" = defaultGithubConfig; - # "gitlab:mx-puppet/discord/better-discord.js" = defaultGitlabConfig; - # "gitlab:mx-puppet/discord/matrix-discord-parser" = defaultGitlabConfig; - # "gitlab:mx-puppet/discord/discord-markdown" = defaultGitlabConfig; - # "gitlab:mx-puppet/discord/mx-puppet-discord" = defaultGitlabConfig; - # "gitlab:mx-puppet/mx-puppet-bridge" = defaultGitlabConfig; + "gitlab:mx-puppet/discord/better-discord.js" = defaultGitlabConfig; + "gitlab:mx-puppet/discord/matrix-discord-parser" = defaultGitlabConfig; + "gitlab:mx-puppet/discord/discord-markdown" = defaultGitlabConfig; + "gitlab:mx-puppet/discord/mx-puppet-discord" = defaultGitlabConfig; + "gitlab:mx-puppet/mx-puppet-bridge" = defaultGitlabConfig; "any:glibc" = { settings.url = "https://sourceware.org/git/glibc.git"; @@ -44,24 +49,28 @@ }; }; - # services.cgit = let - # domain = "mirrors.pvv.ntnu.no"; - # in { - # ${domain} = { - # enable = true; - # group = "gickup"; - # scanPath = "/var/lib/gickup"; - # settings = { - # enable-commit-graph = true; - # enable-follow-links = true; - # enable-http-clone = true; - # enable-remote-branches = true; - # clone-url = "https://${domain}/$CGIT_REPO_URL"; - # remove-suffix = true; - # root-title = "https://${domain}"; - # root-desc = "PVV's repository mirroring service"; - # snapshots = "all"; - # }; - # }; - # }; + services.cgit = let + domain = "bicep.pvv.ntnu.no"; + in { + ${domain} = { + enable = true; + group = "gickup"; + scanPath = "${cfg.dataDir}/linktree"; + settings = { + enable-commit-graph = true; + enable-follow-links = true; + enable-http-clone = true; + enable-remote-branches = true; + clone-url = "https://${domain}/$CGIT_REPO_URL"; + remove-suffix = true; + root-title = "https://${domain}"; + root-desc = "PVV's repository mirroring service"; + snapshots = "all"; + }; + }; + }; + + systemd.services."fcgiwrap-cgit" = { + serviceConfig.BindReadOnlyPaths = [ cfg.dataDir ]; + }; } diff --git a/modules/gickup.nix b/modules/gickup.nix index ed58917..73d8dec 100644 --- a/modules/gickup.nix +++ b/modules/gickup.nix @@ -11,6 +11,13 @@ in gitPackage = lib.mkPackageOption pkgs "git" { }; gitLfsPackage = lib.mkPackageOption pkgs "git-lfs" { }; + dataDir = lib.mkOption { + type = lib.types.path; + description = "The directory to mirror repositories to."; + default = "/var/lib/gickup"; + example = "/data/gickup"; + }; + destinationSettings = lib.mkOption { description = '' Settings for destination local, see gickup configuration file @@ -135,7 +142,22 @@ in }; config = lib.mkIf cfg.enable { - services.gickup.destinationSettings.path = "/var/lib/gickup"; + users.users.gickup = { + isSystemUser = true; + group = "gickup"; + home = "/var/lib/gickup"; + }; + + users.groups.gickup = { }; + + services.gickup.destinationSettings.path = "/var/lib/gickup/raw"; + + systemd.tmpfiles.settings."10-gickup" = lib.mkIf (cfg.dataDir != "/var/lib/gickup") { + ${cfg.dataDir}.d = { + inherit (cfg) user group; + mode = "0755"; + }; + }; systemd.slices."system-gickup" = { description = "Gickup git repository mirroring service"; @@ -176,92 +198,192 @@ in systemd.targets.timers.wants = map ({ slug, ... }: "gickup@${slug}.timer") (lib.attrValues cfg.instances); - systemd.services."gickup@" = let - configDir = lib.pipe cfg.instances [ - (lib.mapAttrsToList (name: instance: { - name = "${instance.slug}.yml"; - path = format.generate "gickup-configuration-${name}.yml" { - destination.local = [ cfg.destinationSettings ]; - source.${instance.type} = [ - ( - (lib.optionalAttrs (instance.type != "any") { - user = instance.owner; - includeorgs = [ instance.owner ]; - include = [ instance.repo ]; - }) - // - instance.settings - ) - ]; - }; - })) - (pkgs.linkFarm "gickup-configuration-files") - ]; - in { - description = "Gickup git repository mirroring service for %i"; - after = [ "network.target" ]; - - path = [ - cfg.gitPackage - cfg.gitLfsPackage - ]; - - serviceConfig = { - Slice = "system-gickup.slice"; - ExecStart = "'${pkgs.gickup}/bin/gickup' '${configDir}/%i.yml'"; - - User = "gickup"; - Group = "gickup"; - - SyslogIdentifier = "gickup-%i"; - StateDirectory = "gickup"; - # WorkingDirectory = "gickup"; - # RuntimeDirectory = "gickup"; - # RuntimeDirectoryMode = "0700"; - - # Hardening options - AmbientCapabilities = []; - LockPersonality = true; - NoNewPrivileges = true; - PrivateDevices = true; - PrivateMounts = true; - PrivateTmp = true; - PrivateUsers = true; - ProcSubset = "pid"; - ProtectClock = true; - ProtectControlGroups = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - # ProtectProc = "invisible"; - # ProtectSystem = "strict"; - RemoveIPC = true; - RestrictAddressFamilies = [ - "AF_INET" - "AF_INET6" + systemd.services = { + "gickup@" = let + configDir = lib.pipe cfg.instances [ + (lib.mapAttrsToList (name: instance: { + name = "${instance.slug}.yml"; + path = format.generate "gickup-configuration-${name}.yml" { + destination.local = [ cfg.destinationSettings ]; + source.${instance.type} = [ + ( + (lib.optionalAttrs (instance.type != "any") { + user = instance.owner; + includeorgs = [ instance.owner ]; + include = [ instance.repo ]; + }) + // + instance.settings + ) + ]; + }; + })) + (pkgs.linkFarm "gickup-configuration-files") ]; - RestrictNamespaces = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - SystemCallArchitectures = "native"; - # SystemCallFilter = [ - # "@system-service" - # "~@resources" - # "~@privileged" - # ]; - UMask = "0002"; - CapabilityBoundingSet = []; + in { + description = "Gickup git repository mirroring service for %i"; + after = [ "network.target" ]; + + path = [ + cfg.gitPackage + cfg.gitLfsPackage + ]; + + serviceConfig = { + Type = "oneshot"; + ExecStart = "'${pkgs.gickup}/bin/gickup' '${configDir}/%i.yml'"; + + User = "gickup"; + Group = "gickup"; + + BindPaths = lib.optionals (cfg.dataDir != "/var/lib/gickup") [ + "${cfg.dataDir}:/var/lib/gickup" + ]; + + Slice = "system-gickup.slice"; + + SyslogIdentifier = "gickup-%i"; + StateDirectory = "gickup"; + # WorkingDirectory = "gickup"; + # RuntimeDirectory = "gickup"; + # RuntimeDirectoryMode = "0700"; + + # Hardening options + AmbientCapabilities = []; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + # ProtectProc = "invisible"; + # ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + # SystemCallFilter = [ + # "@system-service" + # "~@resources" + # "~@privileged" + # ]; + UMask = "0002"; + CapabilityBoundingSet = []; + }; }; - }; - users.users.gickup = { - isSystemUser = true; - group = "gickup"; - home = "/var/lib/gickup"; - }; + # TODO: update symlink for one repo at a time + "gickup-linktree" = { + serviceConfig = { + Type = "oneshot"; + ExecStart = let + script = pkgs.writeShellApplication { + name = "gickup-update-symlink-tree.sh"; + runtimeInputs = [ pkgs.coreutils ]; + text = '' + shopt -s nullglob - users.groups.gickup = { }; + RAW_DIR=/var/lib/gickup/raw + TARGET_DIR=/var/lib/gickup/linktree + + for repository in "$RAW_DIR"/*/*/*; do + REPOSITORY_RELATIVE_DIRS=''${repository#"''${RAW_DIR}/"} + + echo "Checking $REPOSITORY_RELATIVE_DIRS" + + declare -a REVISIONS + readarray -t REVISIONS < <(ls "$repository" | sort --numeric-sort --reverse) + + if [[ "''${#REVISIONS[@]}" == 0 ]]; then + echo "Found no revisions for $repository, continuing" + continue + fi + + LAST_REVISION="''${REVISIONS[0]}" + SYMLINK_PATH="''${TARGET_DIR}/''${REPOSITORY_RELATIVE_DIRS}" + + mkdir -p $(dirname "$SYMLINK_PATH") + + EXPECTED_SYMLINK_TARGET="''${repository}/''${LAST_REVISION}" + EXISTING_SYMLINK_TARGET=$(realpath "$SYMLINK_PATH") + + if [[ "$EXISTING_SYMLINK_TARGET" != "$EXPECTED_SYMLINK_TARGET" ]]; then + echo "Updating symlink for $REPOSITORY_RELATIVE_DIRS" + rm "$SYMLINK_PATH" ||: + ln -s "$EXPECTED_SYMLINK_TARGET" "$SYMLINK_PATH" + else + echo "Symlink already up to date, continuing..." + fi + + echo "---" + done + ''; + }; + in lib.getExe script; + + User = "gickup"; + Group = "gickup"; + + BindPaths = lib.optionals (cfg.dataDir != "/var/lib/gickup") [ + "${cfg.dataDir}:/var/lib/gickup" + ]; + + Slice = "system-gickup.slice"; + + StateDirectory = "gickup"; + + # Hardening options + # TODO: + PrivateNetwork = true; + }; + }; + + # "gickup-enforce-readonly-copies" = { + # # TODO: + # }; + + # "gickup-hardlink" = { + # serviceConfig = { + # Type = "oneshot"; + # ExecStart = let + # script = pkgs.writeShellApplication { + # name = "gickup-hardlink-files.sh"; + # runtimeInputs = [ pkgs.coreutils pkgs.jdupes ]; + # text = '' + + # ''; + # }; + # in lib.getExe script; + + # User = "gickup"; + # Group = "gickup"; + + # BindPaths = lib.optionals (cfg.dataDir != "/var/lib/gickup") [ + # "${cfg.dataDir}:/var/lib/gickup" + # ]; + + # Slice = "system-gickup.slice"; + + # StateDirectory = "gickup"; + + # # Hardening options + # # TODO: + # PrivateNetwork = true; + # }; + # }; + }; }; }