1 { config, lib, pkgs, ... }:
4 cfg = config.services.gns3-server;
6 settingsFormat = pkgs.formats.ini { };
7 configFile = settingsFormat.generate "gns3-server.conf" cfg.settings;
11 doc = ./gns3-server.md;
12 maintainers = [ lib.maintainers.anthonyroussel ];
16 services.gns3-server = {
17 enable = lib.mkEnableOption "GNS3 Server daemon";
19 package = lib.mkPackageOption pkgs "gns3-server" { };
22 enable = lib.mkEnableOption "password based HTTP authentication to access the GNS3 Server";
25 type = lib.types.nullOr lib.types.str;
28 description = ''Username used to access the GNS3 Server.'';
31 passwordFile = lib.mkOption {
32 type = lib.types.nullOr lib.types.path;
34 example = "/run/secrets/gns3-server-password";
36 A file containing the password to access the GNS3 Server.
39 This should be a string, not a nix path, since nix paths
40 are copied into the world-readable nix store.
46 settings = lib.mkOption {
47 type = lib.types.submodule { freeformType = settingsFormat.type; };
49 example = { host = "127.0.0.1"; port = 3080; };
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.
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.'';
65 debug = lib.mkEnableOption "debug logging";
69 enable = lib.mkEnableOption "SSL encryption";
71 certFile = lib.mkOption {
72 type = lib.types.nullOr lib.types.path;
74 example = "/var/lib/gns3/ssl/server.pem";
76 Path to the SSL certificate file. This certificate will
77 be offered to, and may be verified by, clients.
81 keyFile = lib.mkOption {
82 type = lib.types.nullOr lib.types.path;
84 example = "/var/lib/gns3/ssl/server.key";
85 description = "Private key file for the certificate.";
90 enable = lib.mkEnableOption ''Dynamips support'';
91 package = lib.mkPackageOption pkgs "dynamips" { };
95 enable = lib.mkEnableOption ''uBridge support'';
96 package = lib.mkPackageOption pkgs "ubridge" { };
100 enable = lib.mkEnableOption ''VPCS support'';
101 package = lib.mkPackageOption pkgs "vpcs" { };
108 enableDocker = config.virtualisation.docker.enable;
109 enableLibvirtd = config.virtualisation.libvirtd.enable;
112 in lib.mkIf cfg.enable {
115 assertion = cfg.ssl.enable -> cfg.ssl.certFile != null;
116 message = "Please provide a certificate to use for SSL encryption.";
119 assertion = cfg.ssl.enable -> cfg.ssl.keyFile != null;
120 message = "Please provide a private key to use for SSL encryption.";
123 assertion = cfg.auth.enable -> cfg.auth.user != null;
124 message = "Please provide a username to use for HTTP authentication.";
127 assertion = cfg.auth.enable -> cfg.auth.passwordFile != null;
128 message = "Please provide a password file to use for HTTP authentication.";
132 users.groups.gns3 = { };
134 users.groups.ubridge = lib.mkIf cfg.ubridge.enable { };
141 security.wrappers.ubridge = lib.mkIf cfg.ubridge.enable {
142 capabilities = "cap_net_raw,cap_net_admin=eip";
145 permissions = "u=rwx,g=rx,o=r";
146 source = lib.getExe cfg.ubridge.package;
149 services.gns3-server.settings = lib.mkMerge [
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";
159 (lib.mkIf (cfg.ubridge.enable) {
160 Server.ubridge_path = lib.mkDefault "/run/wrappers/bin/ubridge";
162 (lib.mkIf (cfg.auth.enable) {
164 auth = lib.mkDefault (lib.boolToString cfg.auth.enable);
165 user = lib.mkDefault cfg.auth.user;
166 password = lib.mkDefault "@AUTH_PASSWORD@";
169 (lib.mkIf (cfg.vpcs.enable) {
170 VPCS.vpcs_path = lib.mkDefault (lib.getExe cfg.vpcs.package);
172 (lib.mkIf (cfg.dynamips.enable) {
173 Dynamips.dynamips_path = lib.mkDefault (lib.getExe cfg.dynamips.package);
177 systemd.services.gns3-server = let
178 commandArgs = lib.cli.toGNUCommandLineShell { } {
179 config = "/etc/gns3/gns3_server.conf";
180 pid = "/run/gns3/server.pid";
182 ssl = cfg.ssl.enable;
183 # These are implicitly not set if `null`
184 certfile = cfg.ssl.certFile;
185 certkey = cfg.ssl.keyFile;
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.
199 install -m660 ${configFile} /etc/gns3/gns3_server.conf
201 ${lib.optionalString cfg.auth.enable ''
202 ${pkgs.replace-secret}/bin/replace-secret \
204 "''${CREDENTIALS_DIRECTORY}/AUTH_PASSWORD" \
205 /etc/gns3/gns3_server.conf
209 path = lib.optional flags.enableLibvirtd pkgs.qemu;
211 reloadTriggers = [ configFile ];
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}";
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";
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";
234 WorkingDirectory = "%S/gns3";
236 # Required for ubridge integration to work
238 # GNS3 needs to run SUID binaries (ubridge)
239 # but NoNewPrivileges breaks execution of SUID binaries
241 NoNewPrivileges = false;
242 RestrictSUIDSGID = false;
243 PrivateUsers = false;
247 # ubridge needs access to tun/tap devices
250 ] ++ lib.optionals flags.enableLibvirtd [
253 DevicePolicy = "closed";
254 LockPersonality = true;
255 MemoryDenyWriteExecute = true;
257 # Don't restrict ProcSubset because python3Packages.psutil requires read access to /proc/stat
258 # ProcSubset = "pid";
260 ProtectControlGroups = true;
262 ProtectHostname = true;
263 ProtectKernelLogs = true;
264 ProtectKernelModules = true;
265 ProtectKernelTunables = true;
266 ProtectProc = "invisible";
267 ProtectSystem = "strict";
268 RestrictAddressFamilies = [
275 RestrictNamespaces = true;
276 RestrictRealtime = true;