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