From 837c41a2ae026100d8b5d847863fd063c94aadaa Mon Sep 17 00:00:00 2001 From: Dennis Wuitz Date: Wed, 10 Apr 2024 00:17:42 +0200 Subject: [PATCH] add gitea-actions-runner module --- modules/gitea-actions-runner.nix | 239 +++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 modules/gitea-actions-runner.nix diff --git a/modules/gitea-actions-runner.nix b/modules/gitea-actions-runner.nix new file mode 100644 index 00000000..ac0c1d00 --- /dev/null +++ b/modules/gitea-actions-runner.nix @@ -0,0 +1,239 @@ +{ config, pkgs, lib, ... }: +let + cfg = config.services.gitea-actions; + storeDeps = pkgs.buildEnv { + name = "store-deps"; + paths = ((with pkgs; [ + bash + cacert + coreutils + curl + findutils + gawk + git + gnugrep + jq + nix + nodejs + openssh + ]) ++ cfg.storeDependencies); + }; +in { + options = { + services.gitea-actions = { + enable = lib.mkEnableOption "gitea-actions"; + numInstances = lib.mkOption { + type = lib.types.ints.unsigned; + default = 2; + description = "Number of instances of the gitea-actions-runner service to create"; + }; + + storeDependencies = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = []; + description = "List of packages to symlink into the container"; + }; + + additionalFlakeConfig = lib.mkOption { + type = lib.types.str; + default = ""; + example = "accept-flake-config = true"; + description = "Additional configuration to add to the nix.conf file"; + }; + + kvm = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Enable KVM passthrough for the container"; + }; + }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + systemd.services.gitea-runner-nix-image = { + wantedBy = [ "multi-user.target" ]; + after = [ "podman.service" ]; + requires = [ "podman.service" ]; + script = '' + set -eu -o pipefail + mkdir -p etc/nix + + # Create an unpriveleged user that we can use also without the run-as-user.sh script + touch etc/passwd etc/group + groupid=$(cut -d: -f3 < <(getent group gitea-actions)) + userid=$(cut -d: -f3 < <(getent passwd gitea-actions)) + groupadd --prefix $(pwd) --gid "$groupid" gitea-actions + emptypassword='$y$j9T$dLJlazrLCVKcOQ/zmu60E1$bAkbdgDaiz7niknOCasvKW3Tjxeca6WA/1fNe4UpeeC' + useradd --prefix $(pwd) -p "$emptypassword" -m -d /tmp -u "$userid" -g "$groupid" -G gitea-actions gitea-actions + + cat < etc/nix/nix.conf + experimental-features = nix-command flakes + ${cfg.additionalFlakeConfig} + NIX_CONFIG + + cat < etc/nsswitch.conf + passwd: files mymachines systemd + group: files mymachines systemd + shadow: files + + hosts: files mymachines dns myhostname + networks: files + + ethers: files + services: files + protocols: files + rpc: files + NSSWITCH + + # list the content as it will be imported into the container + tar -cv . | tar -tvf - + tar -cv . | podman import - gitea-runner-nix + ''; + + path = [ + config.virtualisation.podman.package + pkgs.getent + pkgs.gnutar + pkgs.shadow + ]; + + serviceConfig = { + RuntimeDirectory = "gitea-runner-nix-image"; + WorkingDirectory = "/run/gitea-runner-nix-image"; + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + + users = { + groups.gitea-actions = { }; + users.gitea-actions = { + group = "gitea-actions"; + description = "Used for running nix ci jobs"; + home = "/run/gitea-runner-nix-image"; + isSystemUser = true; + }; + }; + } + { + systemd.services = lib.genAttrs (builtins.genList (n: "gitea-runner-nix${builtins.toString n}-token") cfg.numInstances) (name: { + wantedBy = [ "multi-user.target" ]; + after =lib.optional config.services.gitea.enable "gitea.service"; + unitConfig.ConditionPathExists = [ "!/var/lib/gitea-registration/${name}" ]; + script = '' + set -euo pipefail + token=$(${lib.getExe config.services.gitea.package} actions generate-runner-token) + echo "TOKEN=$token" > /var/lib/gitea-registration/${name} + ''; + + environment = { + GITEA_CUSTOM = "/var/lib/gitea/custom"; + GITEA_WORK_DIR = "/var/lib/gitea"; + }; + + serviceConfig = { + User = "gitea"; + Group = "gitea"; + StateDirectory = "gitea-registration"; + Type = "oneshot"; + RemainAfterExit = true; + }; + }); + + virtualisation = { + podman.enable = true; + containers = { + containersConf.settings.containers.dns_servers = config.networking.nameservers; + storage.settings = { + storage.driver = if config.boot.zfs.enabled then "zfs" else "overlay"; + storage.options.zfs.fsname = lib.mkIf config.boot.zfs.enabled "zroot/root/podman"; + storage.graphroot = "/var/lib/containers/storage"; + storage.runroot = "/run/containers/storage"; + }; + }; + }; + } + { + systemd.services = lib.genAttrs (builtins.genList (n: "gitea-runner-nix${builtins.toString n}") cfg.numInstances) (name: { + after = [ + "${name}-token.service" + "gitea-runner-nix-image.service" + ]; + + requires = [ + "${name}-token.service" + "gitea-runner-nix-image.service" + ]; + + serviceConfig = { + AmbientCapabilities = ""; + CapabilityBoundingSet = ""; + DeviceAllow = ""; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + UMask = "0066"; + ProtectProc = "invisible"; + PrivateNetwork = false; + MemoryDenyWriteExecute = false; + ProcSubset = "all"; + LockPersonality = false; + DynamicUser = true; + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + "~@raw-io" + "~@reboot" + "~@swap" + "~capset" + "~setdomainname" + "~sethostname" + ]; + + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + "AF_NETLINK" + ]; + }; + }); + + services.gitea-actions-runner.instances = lib.genAttrs (builtins.genList (n: "nix${builtins.toString n}") cfg.numInstances) (iname: { + enable = true; + name = "nixos-runner"; + url = config.services.gitea.settings.server.ROOT_URL; + tokenFile = "/var/lib/gitea-registration/gitea-runner-${iname}-token"; + labels = [ "nix:docker://gitea-runner-nix" ]; + settings.container = { + options = "-e NIX_BUILD_SHELL=/bin/bash -e PAGER=cat -e PATH=/bin -e SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt${lib.optionalString cfg.kvm " --device /dev/kvm"} -v /nix:/nix -v ${storeDeps}/bin:/bin -v ${storeDeps}/etc/ssl:/etc/ssl --user gitea-actions"; + network = "host"; + valid_volumes = [ + "/nix" + "${storeDeps}/bin" + "${storeDeps}/etc/ssl" + ]; + }; + }); + } + ]); +}