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}