{ config, pkgs, lib, ... }: let organizations = [ "Drift" "Projects" "Kurs" ]; cfg = config.services.gitea; program = pkgs.writers.writePython3 "gitea-web-secret-provider" { libraries = with pkgs.python3Packages; [ requests ]; flakeIgnore = [ "E501" # Line over 80 chars lol "E201" # "whitespace after {" "E202" # "whitespace after }" "E251" # unexpected spaces around keyword / parameter equals "W391" # Newline at end of file ]; makeWrapperArgs = [ "--prefix PATH : ${(lib.makeBinPath [ pkgs.openssh ])}" ]; } (lib.pipe ./gitea-web-secret-provider.py [ builtins.readFile (lib.splitString "\n") (lib.drop 2) lib.concatLines ]); commonHardening = { NoNewPrivileges = true; PrivateTmp = true; PrivateDevices = true; ProtectSystem = true; ProtectHome = true; ProtectControlGroups = true; ProtectKernelModules = true; ProtectKernelTunables = true; RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; RestrictRealtime = true; RestrictSUIDSGID = true; MemoryDenyWriteExecute = true; LockPersonality = true; }; in { sops.secrets."gitea/web-secret-provider/token" = { owner = "gitea"; group = "gitea"; restartUnits = [ "gitea-web-secret-provider@" ] ++ (map (org: "gitea-web-secret-provider@${org}") organizations); }; systemd.tmpfiles.settings."10-gitea-web-secret-provider" = { "/var/lib/gitea-web/authorized_keys.d".d = { user = "gitea"; group = "gitea"; mode = "700"; }; "/var/lib/gitea-web/web".d = { user = "gitea"; group = "nginx"; mode = "750"; }; } // (builtins.listToAttrs (map (org: { name = "/var/lib/gitea-web/web/${org}"; value = { d = { user = "gitea"; group = "nginx"; mode = "750"; }; }; }) organizations)); systemd.slices.system-giteaweb = { description = "Gitea web directories"; }; # https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html#Specifiers # %i - instance name (after the @) # %d - secrets directory # %S - /var/lib systemd.services = { "gitea-web-secret-provider@" = { description = "Ensure all repos in %i has an SSH key to push web content"; requires = [ "gitea.service" "network.target" ]; serviceConfig = { Slice = "system-giteaweb.slice"; Type = "oneshot"; ExecStart = let args = lib.cli.toGNUCommandLineShell { } { org = "%i"; token-path = "%d/token"; api-url = "${cfg.settings.server.ROOT_URL}api/v1"; key-dir = "%S/gitea-web/keys/%i"; authorized-keys-path = "%S/gitea-web/authorized_keys.d/%i"; rrsync-path = "${pkgs.rrsync}/bin/rrsync"; web-dir = "%S/gitea-web/web"; }; in "${program} ${args}"; User = "gitea"; Group = "gitea"; StateDirectory = "%i"; LoadCredential = [ "token:${config.sops.secrets."gitea/web-secret-provider/token".path}" ]; } // commonHardening; }; "gitea-web-chown@" = { description = "Ensure all gitea-web content has correct ownership"; serviceConfig = { Slice = "system-giteaweb.slice"; Type = "oneshot"; ExecStart = "${pkgs.coreutils}/bin/chown -R gitea:nginx '%S/gitea-web/web/%i'"; PrivateNetwork = true; } // commonHardening; }; }; systemd.timers = { "gitea-web-secret-provider@" = { description = "Ensure all repos in %i has an SSH key to push web content"; timerConfig = { RandomizedDelaySec = "1h"; Persistent = true; Unit = "gitea-web-secret-provider@%i.service"; OnCalendar = "daily"; }; }; "gitea-web-chown@" = { description = "Ensure all gitea-web content is owned by the gitea user"; timerConfig = { RandomizedDelaySec = "10m"; Persistent = true; Unit = "gitea-web-chown@%i.service"; OnCalendar = "hourly"; }; }; }; systemd.targets.timers.wants = lib.mapCartesianProduct ({ timer, org }: "${timer}@${org}.timer") { timer = [ "gitea-web-secret-provider" "gitea-web-chown" ]; org = organizations; }; services.openssh.authorizedKeysFiles = map (org: "/var/lib/gitea-web/authorized_keys.d/${org}") organizations; }