losslesscut-bin: 3.61.1 -> 3.64.0 (#373227)
[NixPkgs.git] / nixos / modules / services / networking / consul.nix
blob74fcaaaddcdc45f2ba9d81813d2b049ea4301aa9
2   config,
3   lib,
4   pkgs,
5   utils,
6   ...
7 }:
8 let
10   dataDir = "/var/lib/consul";
11   cfg = config.services.consul;
13   configOptions = {
14     data_dir = dataDir;
15     ui_config = {
16       enabled = cfg.webUi;
17     };
18   } // cfg.extraConfig;
20   configFiles = [
21     "/etc/consul.json"
22     "/etc/consul-addrs.json"
23   ] ++ cfg.extraConfigFiles;
25   devices = lib.attrValues (lib.filterAttrs (_: i: i != null) cfg.interface);
26   systemdDevices = lib.forEach devices (
27     i: "sys-subsystem-net-devices-${utils.escapeSystemdPath i}.device"
28   );
31   options = {
33     services.consul = {
35       enable = lib.mkOption {
36         type = lib.types.bool;
37         default = false;
38         description = ''
39           Enables the consul daemon.
40         '';
41       };
43       package = lib.mkPackageOption pkgs "consul" { };
45       webUi = lib.mkOption {
46         type = lib.types.bool;
47         default = false;
48         description = ''
49           Enables the web interface on the consul http port.
50         '';
51       };
53       leaveOnStop = lib.mkOption {
54         type = lib.types.bool;
55         default = false;
56         description = ''
57           If enabled, causes a leave action to be sent when closing consul.
58           This allows a clean termination of the node, but permanently removes
59           it from the cluster. You probably don't want this option unless you
60           are running a node which going offline in a permanent / semi-permanent
61           fashion.
62         '';
63       };
65       interface = {
67         advertise = lib.mkOption {
68           type = lib.types.nullOr lib.types.str;
69           default = null;
70           description = ''
71             The name of the interface to pull the advertise_addr from.
72           '';
73         };
75         bind = lib.mkOption {
76           type = lib.types.nullOr lib.types.str;
77           default = null;
78           description = ''
79             The name of the interface to pull the bind_addr from.
80           '';
81         };
82       };
84       forceAddrFamily = lib.mkOption {
85         type = lib.types.enum [
86           "any"
87           "ipv4"
88           "ipv6"
89         ];
90         default = "any";
91         description = ''
92           Whether to bind ipv4/ipv6 or both kind of addresses.
93         '';
94       };
96       forceIpv4 = lib.mkOption {
97         type = lib.types.nullOr lib.types.bool;
98         default = null;
99         description = ''
100           Deprecated: Use consul.forceAddrFamily instead.
101           Whether we should force the interfaces to only pull ipv4 addresses.
102         '';
103       };
105       dropPrivileges = lib.mkOption {
106         type = lib.types.bool;
107         default = true;
108         description = ''
109           Whether the consul agent should be run as a non-root consul user.
110         '';
111       };
113       extraConfig = lib.mkOption {
114         default = { };
115         type = lib.types.attrsOf lib.types.anything;
116         description = ''
117           Extra configuration options which are serialized to json and added
118           to the config.json file.
119         '';
120       };
122       extraConfigFiles = lib.mkOption {
123         default = [ ];
124         type = lib.types.listOf lib.types.str;
125         description = ''
126           Additional configuration files to pass to consul
127           NOTE: These will not trigger the service to be restarted when altered.
128         '';
129       };
131       alerts = {
132         enable = lib.mkEnableOption "consul-alerts";
134         package = lib.mkPackageOption pkgs "consul-alerts" { };
136         listenAddr = lib.mkOption {
137           description = "Api listening address.";
138           default = "localhost:9000";
139           type = lib.types.str;
140         };
142         consulAddr = lib.mkOption {
143           description = "Consul api listening address";
144           default = "localhost:8500";
145           type = lib.types.str;
146         };
148         watchChecks = lib.mkOption {
149           description = "Whether to enable check watcher.";
150           default = true;
151           type = lib.types.bool;
152         };
154         watchEvents = lib.mkOption {
155           description = "Whether to enable event watcher.";
156           default = true;
157           type = lib.types.bool;
158         };
159       };
161     };
163   };
165   config = lib.mkIf cfg.enable (
166     lib.mkMerge [
167       {
169         users.users.consul = {
170           description = "Consul agent daemon user";
171           isSystemUser = true;
172           group = "consul";
173           # The shell is needed for health checks
174           shell = "/run/current-system/sw/bin/bash";
175         };
176         users.groups.consul = { };
178         environment = {
179           etc."consul.json".text = builtins.toJSON configOptions;
180           # We need consul.d to exist for consul to start
181           etc."consul.d/dummy.json".text = "{ }";
182           systemPackages = [ cfg.package ];
183         };
185         warnings = lib.flatten [
186           (lib.optional (cfg.forceIpv4 != null) ''
187             The option consul.forceIpv4 is deprecated, please use
188             consul.forceAddrFamily instead.
189           '')
190         ];
192         systemd.services.consul = {
193           wantedBy = [ "multi-user.target" ];
194           after = [ "network.target" ] ++ systemdDevices;
195           bindsTo = systemdDevices;
196           restartTriggers =
197             [ config.environment.etc."consul.json".source ]
198             ++ lib.mapAttrsToList (_: d: d.source) (
199               lib.filterAttrs (n: _: lib.hasPrefix "consul.d/" n) config.environment.etc
200             );
202           serviceConfig =
203             {
204               ExecStart =
205                 "@${lib.getExe cfg.package} consul agent -config-dir /etc/consul.d"
206                 + lib.concatMapStrings (n: " -config-file ${n}") configFiles;
207               ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
208               PermissionsStartOnly = true;
209               User = if cfg.dropPrivileges then "consul" else null;
210               Restart = "on-failure";
211               TimeoutStartSec = "infinity";
212             }
213             // (lib.optionalAttrs (cfg.leaveOnStop) {
214               ExecStop = "${lib.getExe cfg.package} leave";
215             });
217           path = with pkgs; [
218             iproute2
219             gawk
220             cfg.package
221           ];
222           preStart =
223             let
224               family =
225                 if cfg.forceAddrFamily == "ipv6" then
226                   "-6"
227                 else if cfg.forceAddrFamily == "ipv4" then
228                   "-4"
229                 else
230                   "";
231             in
232             ''
233               mkdir -m 0700 -p ${dataDir}
234               chown -R consul ${dataDir}
236               # Determine interface addresses
237               getAddrOnce () {
238                 ip ${family} addr show dev "$1" scope global \
239                   | awk -F '[ /\t]*' '/inet/ {print $3}' | head -n 1
240               }
241               getAddr () {
242                 ADDR="$(getAddrOnce $1)"
243                 LEFT=60 # Die after 1 minute
244                 while [ -z "$ADDR" ]; do
245                   sleep 1
246                   LEFT=$(expr $LEFT - 1)
247                   if [ "$LEFT" -eq "0" ]; then
248                     echo "Address lookup timed out"
249                     exit 1
250                   fi
251                   ADDR="$(getAddrOnce $1)"
252                 done
253                 echo "$ADDR"
254               }
255               echo "{" > /etc/consul-addrs.json
256               delim=" "
257             ''
258             + lib.concatStrings (
259               lib.flip lib.mapAttrsToList cfg.interface (
260                 name: i:
261                 lib.optionalString (i != null) ''
262                   echo "$delim \"${name}_addr\": \"$(getAddr "${i}")\"" >> /etc/consul-addrs.json
263                   delim=","
264                 ''
265               )
266             )
267             + ''
268               echo "}" >> /etc/consul-addrs.json
269             '';
270         };
271       }
273       # deprecated
274       (lib.mkIf (cfg.forceIpv4 != null && cfg.forceIpv4) {
275         services.consul.forceAddrFamily = "ipv4";
276       })
278       (lib.mkIf (cfg.alerts.enable) {
279         systemd.services.consul-alerts = {
280           wantedBy = [ "multi-user.target" ];
281           after = [ "consul.service" ];
283           path = [ cfg.package ];
285           serviceConfig = {
286             ExecStart = ''
287               ${lib.getExe cfg.alerts.package} start \
288                 --alert-addr=${cfg.alerts.listenAddr} \
289                 --consul-addr=${cfg.alerts.consulAddr} \
290                 ${lib.optionalString cfg.alerts.watchChecks "--watch-checks"} \
291                 ${lib.optionalString cfg.alerts.watchEvents "--watch-events"}
292             '';
293             User = if cfg.dropPrivileges then "consul" else null;
294             Restart = "on-failure";
295           };
296         };
297       })
299     ]
300   );