vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / networking / nebula.nix
blob477731f3f5a49c1e86a5e7bc82786ab0c336e793
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
7   cfg = config.services.nebula;
8   enabledNetworks = filterAttrs (n: v: v.enable) cfg.networks;
10   format = pkgs.formats.yaml {};
12   nameToId = netName: "nebula-${netName}";
14   resolveFinalPort = netCfg:
15     if netCfg.listen.port == null then
16       if (netCfg.isLighthouse || netCfg.isRelay) then
17         4242
18       else
19         0
20     else
21       netCfg.listen.port;
24   # Interface
26   options = {
27     services.nebula = {
28       networks = mkOption {
29         description = "Nebula network definitions.";
30         default = {};
31         type = types.attrsOf (types.submodule {
32           options = {
33             enable = mkOption {
34               type = types.bool;
35               default = true;
36               description = "Enable or disable this network.";
37             };
39             package = mkPackageOption pkgs "nebula" { };
41             ca = mkOption {
42               type = types.path;
43               description = "Path to the certificate authority certificate.";
44               example = "/etc/nebula/ca.crt";
45             };
47             cert = mkOption {
48               type = types.path;
49               description = "Path to the host certificate.";
50               example = "/etc/nebula/host.crt";
51             };
53             key = mkOption {
54               type = types.oneOf [types.nonEmptyStr types.path];
55               description = "Path or reference to the host key.";
56               example = "/etc/nebula/host.key";
57             };
59             staticHostMap = mkOption {
60               type = types.attrsOf (types.listOf (types.str));
61               default = {};
62               description = ''
63                 The static host map defines a set of hosts with fixed IP addresses on the internet (or any network).
64                 A host can have multiple fixed IP addresses defined here, and nebula will try each when establishing a tunnel.
65               '';
66               example = { "192.168.100.1" = [ "100.64.22.11:4242" ]; };
67             };
69             isLighthouse = mkOption {
70               type = types.bool;
71               default = false;
72               description = "Whether this node is a lighthouse.";
73             };
75             isRelay = mkOption {
76               type = types.bool;
77               default = false;
78               description = "Whether this node is a relay.";
79             };
81             lighthouses = mkOption {
82               type = types.listOf types.str;
83               default = [];
84               description = ''
85                 List of IPs of lighthouse hosts this node should report to and query from. This should be empty on lighthouse
86                 nodes. The IPs should be the lighthouse's Nebula IPs, not their external IPs.
87               '';
88               example = [ "192.168.100.1" ];
89             };
91             relays = mkOption {
92               type = types.listOf types.str;
93               default = [];
94               description = ''
95                 List of IPs of relays that this node should allow traffic from.
96               '';
97               example = [ "192.168.100.1" ];
98             };
100             listen.host = mkOption {
101               type = types.str;
102               default = "0.0.0.0";
103               description = "IP address to listen on.";
104             };
106             listen.port = mkOption {
107               type = types.nullOr types.port;
108               default = null;
109               defaultText = lib.literalExpression ''
110                 if (config.services.nebula.networks.''${name}.isLighthouse ||
111                     config.services.nebula.networks.''${name}.isRelay) then
112                   4242
113                 else
114                   0;
115               '';
116               description = "Port number to listen on.";
117             };
119             tun.disable = mkOption {
120               type = types.bool;
121               default = false;
122               description = ''
123                 When tun is disabled, a lighthouse can be started without a local tun interface (and therefore without root).
124               '';
125             };
127             tun.device = mkOption {
128               type = types.nullOr types.str;
129               default = null;
130               description = "Name of the tun device. Defaults to nebula.\${networkName}.";
131             };
133             firewall.outbound = mkOption {
134               type = types.listOf types.attrs;
135               default = [];
136               description = "Firewall rules for outbound traffic.";
137               example = [ { port = "any"; proto = "any"; host = "any"; } ];
138             };
140             firewall.inbound = mkOption {
141               type = types.listOf types.attrs;
142               default = [];
143               description = "Firewall rules for inbound traffic.";
144               example = [ { port = "any"; proto = "any"; host = "any"; } ];
145             };
147             settings = mkOption {
148               type = format.type;
149               default = {};
150               description = ''
151                 Nebula configuration. Refer to
152                 <https://github.com/slackhq/nebula/blob/master/examples/config.yml>
153                 for details on supported values.
154               '';
155               example = literalExpression ''
156                 {
157                   lighthouse.dns = {
158                     host = "0.0.0.0";
159                     port = 53;
160                   };
161                 }
162               '';
163             };
164           };
165         });
166       };
167     };
168   };
170   # Implementation
171   config = mkIf (enabledNetworks != {}) {
172     systemd.services = mkMerge (mapAttrsToList (netName: netCfg:
173       let
174         networkId = nameToId netName;
175         settings = recursiveUpdate {
176           pki = {
177             ca = netCfg.ca;
178             cert = netCfg.cert;
179             key = netCfg.key;
180           };
181           static_host_map = netCfg.staticHostMap;
182           lighthouse = {
183             am_lighthouse = netCfg.isLighthouse;
184             hosts = netCfg.lighthouses;
185           };
186           relay = {
187             am_relay = netCfg.isRelay;
188             relays = netCfg.relays;
189             use_relays = true;
190           };
191           listen = {
192             host = netCfg.listen.host;
193             port = resolveFinalPort netCfg;
194           };
195           tun = {
196             disabled = netCfg.tun.disable;
197             dev = if (netCfg.tun.device != null) then netCfg.tun.device else "nebula.${netName}";
198           };
199           firewall = {
200             inbound = netCfg.firewall.inbound;
201             outbound = netCfg.firewall.outbound;
202           };
203         } netCfg.settings;
204         configFile = format.generate "nebula-config-${netName}.yml" (
205           warnIf
206             ((settings.lighthouse.am_lighthouse || settings.relay.am_relay) && settings.listen.port == 0)
207             ''
208               Nebula network '${netName}' is configured as a lighthouse or relay, and its port is ${builtins.toString settings.listen.port}.
209               You will likely experience connectivity issues: https://nebula.defined.net/docs/config/listen/#listenport
210             ''
211             settings
212           );
213         in
214         {
215           # Create the systemd service for Nebula.
216           "nebula@${netName}" = {
217             description = "Nebula VPN service for ${netName}";
218             wants = [ "basic.target" ];
219             after = [ "basic.target" "network.target" ];
220             before = [ "sshd.service" ];
221             wantedBy = [ "multi-user.target" ];
222             serviceConfig = {
223               Type = "notify";
224               Restart = "always";
225               ExecStart = "${netCfg.package}/bin/nebula -config ${configFile}";
226               UMask = "0027";
227               CapabilityBoundingSet = "CAP_NET_ADMIN";
228               AmbientCapabilities = "CAP_NET_ADMIN";
229               LockPersonality = true;
230               NoNewPrivileges = true;
231               PrivateDevices = false; # needs access to /dev/net/tun (below)
232               DeviceAllow = "/dev/net/tun rw";
233               DevicePolicy = "closed";
234               PrivateTmp = true;
235               PrivateUsers = false; # CapabilityBoundingSet needs to apply to the host namespace
236               ProtectClock = true;
237               ProtectControlGroups = true;
238               ProtectHome = true;
239               ProtectHostname = true;
240               ProtectKernelLogs = true;
241               ProtectKernelModules = true;
242               ProtectKernelTunables = true;
243               ProtectProc = "invisible";
244               ProtectSystem = true;
245               RestrictNamespaces = true;
246               RestrictSUIDSGID = true;
247               User = networkId;
248               Group = networkId;
249             };
250             unitConfig.StartLimitIntervalSec = 0; # ensure Restart=always is always honoured (networks can go down for arbitrarily long)
251           };
252         }) enabledNetworks);
254     # Open the chosen ports for UDP.
255     networking.firewall.allowedUDPPorts =
256       unique (filter (port: port > 0) (mapAttrsToList (netName: netCfg: resolveFinalPort netCfg) enabledNetworks));
258     # Create the service users and groups.
259     users.users = mkMerge (mapAttrsToList (netName: netCfg:
260       {
261         ${nameToId netName} = {
262           group = nameToId netName;
263           description = "Nebula service user for network ${netName}";
264           isSystemUser = true;
265         };
266       }) enabledNetworks);
268     users.groups = mkMerge (mapAttrsToList (netName: netCfg: {
269       ${nameToId netName} = {};
270     }) enabledNetworks);
271   };
273   meta.maintainers = with maintainers; [ numinit ];