1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8let 9 cfg = config.services.woodpecker-agents; 10 11 agentModule = lib.types.submodule { 12 options = { 13 enable = lib.mkEnableOption "this Woodpecker-Agent. Agents execute tasks generated by a Server, every install will need one server and at least one agent"; 14 15 package = lib.mkPackageOption pkgs "woodpecker-agent" { }; 16 17 environment = lib.mkOption { 18 default = { }; 19 type = lib.types.attrsOf lib.types.str; 20 example = lib.literalExpression '' 21 { 22 WOODPECKER_SERVER = "localhost:9000"; 23 WOODPECKER_BACKEND = "docker"; 24 DOCKER_HOST = "unix:///run/podman/podman.sock"; 25 } 26 ''; 27 description = "woodpecker-agent config environment variables, for other options read the [documentation](https://woodpecker-ci.org/docs/administration/agent-config)"; 28 }; 29 30 extraGroups = lib.mkOption { 31 type = lib.types.listOf lib.types.str; 32 default = [ ]; 33 example = [ "podman" ]; 34 description = '' 35 Additional groups for the systemd service. 36 ''; 37 }; 38 39 path = lib.mkOption { 40 type = lib.types.listOf lib.types.package; 41 default = [ ]; 42 example = [ "" ]; 43 description = '' 44 Additional packages that should be added to the agent's `PATH`. 45 Mostly useful for the `local` backend. 46 ''; 47 }; 48 49 environmentFile = lib.mkOption { 50 type = lib.types.listOf lib.types.path; 51 default = [ ]; 52 example = [ "/var/secrets/woodpecker-agent.env" ]; 53 description = '' 54 File to load environment variables 55 from. This is helpful for specifying secrets. 56 Example content of environmentFile: 57 ``` 58 WOODPECKER_AGENT_SECRET=your-shared-secret-goes-here 59 ``` 60 ''; 61 }; 62 }; 63 }; 64 65 mkAgentService = name: agentCfg: { 66 name = "woodpecker-agent-${name}"; 67 value = { 68 description = "Woodpecker-Agent Service - ${name}"; 69 wantedBy = [ "multi-user.target" ]; 70 after = [ "network-online.target" ]; 71 wants = [ "network-online.target" ]; 72 serviceConfig = { 73 DynamicUser = true; 74 SupplementaryGroups = agentCfg.extraGroups; 75 EnvironmentFile = agentCfg.environmentFile; 76 ExecStart = lib.getExe agentCfg.package; 77 Restart = "on-failure"; 78 RestartSec = 15; 79 CapabilityBoundingSet = ""; 80 NoNewPrivileges = true; 81 ProtectSystem = "strict"; 82 PrivateTmp = true; 83 PrivateDevices = true; 84 PrivateUsers = true; 85 ProtectHostname = true; 86 ProtectClock = true; 87 ProtectKernelTunables = true; 88 ProtectKernelModules = true; 89 ProtectKernelLogs = true; 90 ProtectControlGroups = true; 91 RestrictAddressFamilies = [ "AF_UNIX AF_INET AF_INET6" ]; 92 LockPersonality = true; 93 MemoryDenyWriteExecute = true; 94 RestrictRealtime = true; 95 RestrictSUIDSGID = true; 96 PrivateMounts = true; 97 SystemCallArchitectures = "native"; 98 SystemCallFilter = "~@clock @privileged @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @setuid @swap"; 99 BindReadOnlyPaths = [ 100 "-/etc/resolv.conf" 101 "-/etc/nsswitch.conf" 102 "-/etc/ssl/certs" 103 "-/etc/static/ssl/certs" 104 "-/etc/hosts" 105 "-/etc/localtime" 106 ]; 107 }; 108 inherit (agentCfg) environment path; 109 }; 110 }; 111in 112{ 113 meta.maintainers = with lib.maintainers; [ ambroisie ]; 114 115 options = { 116 services.woodpecker-agents = { 117 agents = lib.mkOption { 118 default = { }; 119 type = lib.types.attrsOf agentModule; 120 example = lib.literalExpression '' 121 { 122 podman = { 123 environment = { 124 WOODPECKER_SERVER = "localhost:9000"; 125 WOODPECKER_BACKEND = "docker"; 126 DOCKER_HOST = "unix:///run/podman/podman.sock"; 127 }; 128 129 extraGroups = [ "podman" ]; 130 131 environmentFile = [ "/run/secrets/woodpecker/agent-secret.txt" ]; 132 }; 133 134 exec = { 135 environment = { 136 WOODPECKER_SERVER = "localhost:9000"; 137 WOODPECKER_BACKEND = "local"; 138 }; 139 140 environmentFile = [ "/run/secrets/woodpecker/agent-secret.txt" ]; 141 142 path = [ 143 # Needed to clone repos 144 git 145 git-lfs 146 woodpecker-plugin-git 147 # Used by the runner as the default shell 148 bash 149 # Most likely to be used in pipeline definitions 150 coreutils 151 ]; 152 }; 153 } 154 ''; 155 description = "woodpecker-agents configurations"; 156 }; 157 }; 158 }; 159 160 config = { 161 systemd.services = 162 let 163 mkServices = lib.mapAttrs' mkAgentService; 164 enabledAgents = lib.filterAttrs (_: agent: agent.enable) cfg.agents; 165 in 166 mkServices enabledAgents; 167 }; 168}