vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / networking / gns3-server.nix
blob71980f52df2d445bea699c8e7b2e2ebe14982f3e
1 { config, lib, pkgs, ... }:
3 let
4   cfg = config.services.gns3-server;
6   settingsFormat = pkgs.formats.ini { };
7   configFile = settingsFormat.generate "gns3-server.conf" cfg.settings;
9 in {
10   meta = {
11     doc = ./gns3-server.md;
12     maintainers = [ lib.maintainers.anthonyroussel ];
13   };
15   options = {
16     services.gns3-server = {
17       enable = lib.mkEnableOption "GNS3 Server daemon";
19       package = lib.mkPackageOption pkgs "gns3-server" { };
21       auth = {
22         enable = lib.mkEnableOption "password based HTTP authentication to access the GNS3 Server";
24         user = lib.mkOption {
25           type = lib.types.nullOr lib.types.str;
26           default = null;
27           example = "gns3";
28           description = ''Username used to access the GNS3 Server.'';
29         };
31         passwordFile = lib.mkOption {
32           type = lib.types.nullOr lib.types.path;
33           default = null;
34           example = "/run/secrets/gns3-server-password";
35           description = ''
36             A file containing the password to access the GNS3 Server.
38             ::: {.warning}
39             This should be a string, not a nix path, since nix paths
40             are copied into the world-readable nix store.
41             :::
42           '';
43         };
44       };
46       settings = lib.mkOption {
47         type = lib.types.submodule { freeformType = settingsFormat.type; };
48         default = {};
49         example = { host = "127.0.0.1"; port = 3080; };
50         description = ''
51           The global options in `config` file in ini format.
53           Refer to <https://docs.gns3.com/docs/using-gns3/administration/gns3-server-configuration-file/>
54           for all available options.
55         '';
56       };
58       log = {
59         file = lib.mkOption {
60           type = lib.types.nullOr lib.types.path;
61           default = "/var/log/gns3/server.log";
62           description = ''Path of the file GNS3 Server should log to.'';
63         };
65         debug = lib.mkEnableOption "debug logging";
66       };
68       ssl = {
69         enable = lib.mkEnableOption "SSL encryption";
71         certFile = lib.mkOption {
72           type = lib.types.nullOr lib.types.path;
73           default = null;
74           example = "/var/lib/gns3/ssl/server.pem";
75           description = ''
76             Path to the SSL certificate file. This certificate will
77             be offered to, and may be verified by, clients.
78           '';
79         };
81         keyFile = lib.mkOption {
82           type = lib.types.nullOr lib.types.path;
83           default = null;
84           example = "/var/lib/gns3/ssl/server.key";
85           description = "Private key file for the certificate.";
86         };
87       };
89       dynamips = {
90         enable = lib.mkEnableOption ''Dynamips support'';
91         package = lib.mkPackageOption pkgs "dynamips" { };
92       };
94       ubridge = {
95         enable = lib.mkEnableOption ''uBridge support'';
96         package = lib.mkPackageOption pkgs "ubridge" { };
97       };
99       vpcs = {
100         enable = lib.mkEnableOption ''VPCS support'';
101         package = lib.mkPackageOption pkgs "vpcs" { };
102       };
103     };
104   };
106   config = let
107     flags = {
108       enableDocker = config.virtualisation.docker.enable;
109       enableLibvirtd = config.virtualisation.libvirtd.enable;
110     };
112   in lib.mkIf cfg.enable {
113     assertions = [
114       {
115         assertion = cfg.ssl.enable -> cfg.ssl.certFile != null;
116         message = "Please provide a certificate to use for SSL encryption.";
117       }
118       {
119         assertion = cfg.ssl.enable -> cfg.ssl.keyFile != null;
120         message = "Please provide a private key to use for SSL encryption.";
121       }
122       {
123         assertion = cfg.auth.enable -> cfg.auth.user != null;
124         message = "Please provide a username to use for HTTP authentication.";
125       }
126       {
127         assertion = cfg.auth.enable -> cfg.auth.passwordFile != null;
128         message = "Please provide a password file to use for HTTP authentication.";
129       }
130     ];
132     users.groups.gns3 = { };
134     users.groups.ubridge = lib.mkIf cfg.ubridge.enable { };
136     users.users.gns3 = {
137       group = "gns3";
138       isSystemUser = true;
139     };
141     security.wrappers.ubridge = lib.mkIf cfg.ubridge.enable {
142       capabilities = "cap_net_raw,cap_net_admin=eip";
143       group = "ubridge";
144       owner = "root";
145       permissions = "u=rwx,g=rx,o=r";
146       source = lib.getExe cfg.ubridge.package;
147     };
149     services.gns3-server.settings = lib.mkMerge [
150       {
151         Server = {
152           appliances_path = lib.mkDefault "/var/lib/gns3/appliances";
153           configs_path = lib.mkDefault "/var/lib/gns3/configs";
154           images_path = lib.mkDefault "/var/lib/gns3/images";
155           projects_path = lib.mkDefault "/var/lib/gns3/projects";
156           symbols_path = lib.mkDefault "/var/lib/gns3/symbols";
157         };
158       }
159       (lib.mkIf (cfg.ubridge.enable) {
160         Server.ubridge_path = lib.mkDefault "/run/wrappers/bin/ubridge";
161       })
162       (lib.mkIf (cfg.auth.enable) {
163         Server = {
164           auth = lib.mkDefault (lib.boolToString cfg.auth.enable);
165           user = lib.mkDefault cfg.auth.user;
166           password = lib.mkDefault "@AUTH_PASSWORD@";
167         };
168       })
169       (lib.mkIf (cfg.vpcs.enable) {
170         VPCS.vpcs_path = lib.mkDefault (lib.getExe cfg.vpcs.package);
171       })
172       (lib.mkIf (cfg.dynamips.enable) {
173         Dynamips.dynamips_path = lib.mkDefault (lib.getExe cfg.dynamips.package);
174       })
175     ];
177     systemd.services.gns3-server = let
178       commandArgs = lib.cli.toGNUCommandLineShell { } {
179         config = "/etc/gns3/gns3_server.conf";
180         pid = "/run/gns3/server.pid";
181         log = cfg.log.file;
182         ssl = cfg.ssl.enable;
183         # These are implicitly not set if `null`
184         certfile = cfg.ssl.certFile;
185         certkey = cfg.ssl.keyFile;
186       };
187     in
188     {
189       description = "GNS3 Server";
191       after = [ "network.target" "network-online.target" ];
192       wantedBy = [ "multi-user.target" ];
193       wants = [ "network-online.target" ];
195       # configFile cannot be stored in RuntimeDirectory, because GNS3
196       # uses the `--config` base path to stores supplementary configuration files at runtime.
197       #
198       preStart = ''
199         install -m660 ${configFile} /etc/gns3/gns3_server.conf
201         ${lib.optionalString cfg.auth.enable ''
202           ${pkgs.replace-secret}/bin/replace-secret \
203             '@AUTH_PASSWORD@' \
204             "''${CREDENTIALS_DIRECTORY}/AUTH_PASSWORD" \
205             /etc/gns3/gns3_server.conf
206         ''}
207       '';
209       path = lib.optional flags.enableLibvirtd pkgs.qemu;
211       reloadTriggers = [ configFile ];
213       serviceConfig = {
214         ConfigurationDirectory = "gns3";
215         ConfigurationDirectoryMode = "0750";
216         Environment = "HOME=%S/gns3";
217         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
218         ExecStart = "${lib.getExe cfg.package} ${commandArgs}";
219         Group = "gns3";
220         LimitNOFILE = 16384;
221         LoadCredential = lib.mkIf cfg.auth.enable [ "AUTH_PASSWORD:${cfg.auth.passwordFile}" ];
222         LogsDirectory = "gns3";
223         LogsDirectoryMode = "0750";
224         PIDFile = "/run/gns3/server.pid";
225         Restart = "on-failure";
226         RestartSec = 5;
227         RuntimeDirectory = "gns3";
228         StateDirectory = "gns3";
229         StateDirectoryMode = "0750";
230         SupplementaryGroups = lib.optional flags.enableDocker "docker"
231           ++ lib.optional flags.enableLibvirtd "libvirtd"
232           ++ lib.optional cfg.ubridge.enable "ubridge";
233         User = "gns3";
234         WorkingDirectory = "%S/gns3";
236         # Required for ubridge integration to work
237         #
238         # GNS3 needs to run SUID binaries (ubridge)
239         # but NoNewPrivileges breaks execution of SUID binaries
240         DynamicUser = false;
241         NoNewPrivileges = false;
242         RestrictSUIDSGID = false;
243         PrivateUsers = false;
245         # Hardening
246         DeviceAllow = [
247           # ubridge needs access to tun/tap devices
248           "/dev/net/tap rw"
249           "/dev/net/tun rw"
250         ] ++ lib.optionals flags.enableLibvirtd [
251           "/dev/kvm"
252         ];
253         DevicePolicy = "closed";
254         LockPersonality = true;
255         MemoryDenyWriteExecute = true;
256         PrivateTmp = true;
257         # Don't restrict ProcSubset because python3Packages.psutil requires read access to /proc/stat
258         # ProcSubset = "pid";
259         ProtectClock = true;
260         ProtectControlGroups = true;
261         ProtectHome = true;
262         ProtectHostname = true;
263         ProtectKernelLogs = true;
264         ProtectKernelModules = true;
265         ProtectKernelTunables = true;
266         ProtectProc = "invisible";
267         ProtectSystem = "strict";
268         RestrictAddressFamilies = [
269           "AF_INET"
270           "AF_INET6"
271           "AF_NETLINK"
272           "AF_UNIX"
273           "AF_PACKET"
274         ];
275         RestrictNamespaces = true;
276         RestrictRealtime = true;
277         UMask = "0022";
278       };
279     };
280   };