nixos/gns3-server: init

Changed files
+297
nixos
doc
manual
release-notes
modules
+2
nixos/doc/manual/release-notes/rl-2405.section.md
··· 16 16 17 17 - [maubot](https://github.com/maubot/maubot), a plugin-based Matrix bot framework. Available as [services.maubot](#opt-services.maubot.enable). 18 18 19 + - [GNS3](https://www.gns3.com/), a network software emulator. Available as [services.gns3-server](#opt-services.gns3-server.enable). 20 + 19 21 - [Anki Sync Server](https://docs.ankiweb.net/sync-server.html), the official sync server built into recent versions of Anki. Available as [services.anki-sync-server](#opt-services.anki-sync-server.enable). 20 22 21 23 ## Backward Incompatibilities {#sec-release-24.05-incompatibilities}
+1
nixos/modules/module-list.nix
··· 939 939 ./services/networking/ghostunnel.nix 940 940 ./services/networking/git-daemon.nix 941 941 ./services/networking/globalprotect-vpn.nix 942 + ./services/networking/gns3-server.nix 942 943 ./services/networking/gnunet.nix 943 944 ./services/networking/go-autoconfig.nix 944 945 ./services/networking/go-neb.nix
+31
nixos/modules/services/networking/gns3-server.md
··· 1 + # GNS3 Server {#module-services-gns3-server} 2 + 3 + [GNS3](https://www.gns3.com/), a network software emulator. 4 + 5 + ## Basic Usage {#module-services-gns3-server-basic-usage} 6 + 7 + A minimal configuration looks like this: 8 + 9 + ```nix 10 + { 11 + services.gns3-server = { 12 + enable = true; 13 + 14 + auth = { 15 + enable = true; 16 + user = "gns3"; 17 + passwordFile = "/var/lib/secrets/gns3_password"; 18 + }; 19 + 20 + ssl = { 21 + enable = true; 22 + certFile = "/var/lib/gns3/ssl/cert.pem"; 23 + keyFile = "/var/lib/gns3/ssl/key.pem"; 24 + }; 25 + 26 + dynamips.enable = true; 27 + ubridge.enable = true; 28 + vpcs.enable = true; 29 + }; 30 + } 31 + ```
+263
nixos/modules/services/networking/gns3-server.nix
··· 1 + { config, lib, pkgs, ... }: 2 + 3 + let 4 + cfg = config.services.gns3-server; 5 + 6 + settingsFormat = pkgs.formats.ini { }; 7 + configFile = settingsFormat.generate "gns3-server.conf" cfg.settings; 8 + 9 + in { 10 + meta = { 11 + doc = ./gns3-server.md; 12 + maintainers = [ lib.maintainers.anthonyroussel ]; 13 + }; 14 + 15 + options = { 16 + services.gns3-server = { 17 + enable = lib.mkEnableOption (lib.mdDoc "GNS3 Server daemon"); 18 + 19 + package = lib.mkPackageOptionMD pkgs "gns3-server" { }; 20 + 21 + auth = { 22 + enable = lib.mkEnableOption (lib.mdDoc "password based HTTP authentication to access the GNS3 Server"); 23 + 24 + user = lib.mkOption { 25 + type = lib.types.nullOr lib.types.str; 26 + default = null; 27 + example = "gns3"; 28 + description = lib.mdDoc ''Username used to access the GNS3 Server.''; 29 + }; 30 + 31 + passwordFile = lib.mkOption { 32 + type = lib.types.nullOr lib.types.path; 33 + default = null; 34 + example = "/run/secrets/gns3-server-password"; 35 + description = lib.mdDoc '' 36 + A file containing the password to access the GNS3 Server. 37 + 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 + }; 45 + 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 = lib.mdDoc '' 51 + The global options in `config` file in ini format. 52 + 53 + Refer to <https://docs.gns3.com/docs/using-gns3/administration/gns3-server-configuration-file/> 54 + for all available options. 55 + ''; 56 + }; 57 + 58 + log = { 59 + file = lib.mkOption { 60 + type = lib.types.nullOr lib.types.path; 61 + default = "/var/log/gns3/server.log"; 62 + description = lib.mdDoc ''Path of the file GNS3 Server should log to.''; 63 + }; 64 + 65 + debug = lib.mkEnableOption (lib.mdDoc "debug logging"); 66 + }; 67 + 68 + ssl = { 69 + enable = lib.mkEnableOption (lib.mdDoc "SSL encryption"); 70 + 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 = lib.mdDoc '' 76 + Path to the SSL certificate file. This certificate will 77 + be offered to, and may be verified by, clients. 78 + ''; 79 + }; 80 + 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 = lib.mdDoc "Private key file for the certificate."; 86 + }; 87 + }; 88 + 89 + dynamips = { 90 + enable = lib.mkEnableOption (lib.mdDoc ''Whether to enable Dynamips support.''); 91 + package = lib.mkPackageOptionMD pkgs "dynamips" { }; 92 + }; 93 + 94 + ubridge = { 95 + enable = lib.mkEnableOption (lib.mdDoc ''Whether to enable uBridge support.''); 96 + package = lib.mkPackageOptionMD pkgs "ubridge" { }; 97 + }; 98 + 99 + vpcs = { 100 + enable = lib.mkEnableOption (lib.mdDoc ''Whether to enable VPCS support.''); 101 + package = lib.mkPackageOptionMD pkgs "vpcs" { }; 102 + }; 103 + }; 104 + }; 105 + 106 + config = let 107 + flags = { 108 + enableDocker = config.virtualisation.docker.enable; 109 + enableLibvirtd = config.virtualisation.libvirtd.enable; 110 + }; 111 + 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 + ]; 131 + 132 + users.groups.ubridge = lib.mkIf cfg.ubridge.enable { }; 133 + 134 + security.wrappers.ubridge = lib.mkIf cfg.ubridge.enable { 135 + capabilities = "cap_net_raw,cap_net_admin=eip"; 136 + group = "ubridge"; 137 + owner = "root"; 138 + permissions = "u=rwx,g=rx,o=r"; 139 + source = lib.getExe cfg.ubridge.package; 140 + }; 141 + 142 + services.gns3-server.settings = lib.mkMerge [ 143 + { 144 + Server = { 145 + appliances_path = lib.mkDefault "/var/lib/gns3/appliances"; 146 + configs_path = lib.mkDefault "/var/lib/gns3/configs"; 147 + images_path = lib.mkDefault "/var/lib/gns3/images"; 148 + projects_path = lib.mkDefault "/var/lib/gns3/projects"; 149 + symbols_path = lib.mkDefault "/var/lib/gns3/symbols"; 150 + }; 151 + } 152 + (lib.mkIf (cfg.ubridge.enable) { 153 + Server.ubridge_path = lib.mkDefault (lib.getExe cfg.ubridge.package); 154 + }) 155 + (lib.mkIf (cfg.auth.enable) { 156 + Server = { 157 + auth = lib.mkDefault (lib.boolToString cfg.auth.enable); 158 + user = lib.mkDefault cfg.auth.user; 159 + password = lib.mkDefault "@AUTH_PASSWORD@"; 160 + }; 161 + }) 162 + (lib.mkIf (cfg.vpcs.enable) { 163 + VPCS.vpcs_path = lib.mkDefault (lib.getExe cfg.vpcs.package); 164 + }) 165 + (lib.mkIf (cfg.dynamips.enable) { 166 + Dynamips.dynamips_path = lib.mkDefault (lib.getExe cfg.dynamips.package); 167 + }) 168 + ]; 169 + 170 + systemd.services.gns3-server = let 171 + commandArgs = lib.cli.toGNUCommandLineShell { } { 172 + config = "/etc/gns3/gns3_server.conf"; 173 + pid = "/run/gns3/server.pid"; 174 + log = cfg.log.file; 175 + ssl = cfg.ssl.enable; 176 + # These are implicitly not set if `null` 177 + certfile = cfg.ssl.certFile; 178 + certkey = cfg.ssl.keyFile; 179 + }; 180 + in 181 + { 182 + description = "GNS3 Server"; 183 + 184 + after = [ "network.target" "network-online.target" ]; 185 + wantedBy = [ "multi-user.target" ]; 186 + wants = [ "network-online.target" ]; 187 + 188 + # configFile cannot be stored in RuntimeDirectory, because GNS3 189 + # uses the `--config` base path to stores supplementary configuration files at runtime. 190 + # 191 + preStart = '' 192 + install -m660 ${configFile} /etc/gns3/gns3_server.conf 193 + 194 + ${lib.optionalString cfg.auth.enable '' 195 + ${pkgs.replace-secret}/bin/replace-secret \ 196 + '@AUTH_PASSWORD@' \ 197 + "''${CREDENTIALS_DIRECTORY}/AUTH_PASSWORD" \ 198 + /etc/gns3/gns3_server.conf 199 + ''} 200 + ''; 201 + 202 + path = lib.optional flags.enableLibvirtd pkgs.qemu; 203 + 204 + reloadTriggers = [ configFile ]; 205 + 206 + serviceConfig = { 207 + ConfigurationDirectory = "gns3"; 208 + ConfigurationDirectoryMode = "0750"; 209 + DynamicUser = true; 210 + Environment = "HOME=%S/gns3"; 211 + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 212 + ExecStart = "${lib.getExe cfg.package} ${commandArgs}"; 213 + Group = "gns3"; 214 + LimitNOFILE = 16384; 215 + LoadCredential = lib.mkIf cfg.auth.enable [ "AUTH_PASSWORD:${cfg.auth.passwordFile}" ]; 216 + LogsDirectory = "gns3"; 217 + LogsDirectoryMode = "0750"; 218 + PIDFile = "/run/gns3/server.pid"; 219 + Restart = "on-failure"; 220 + RestartSec = 5; 221 + RuntimeDirectory = "gns3"; 222 + StateDirectory = "gns3"; 223 + StateDirectoryMode = "0750"; 224 + SupplementaryGroups = lib.optional flags.enableDocker "docker" 225 + ++ lib.optional flags.enableLibvirtd "libvirtd" 226 + ++ lib.optional cfg.ubridge.enable "ubridge"; 227 + User = "gns3"; 228 + WorkingDirectory = "%S/gns3"; 229 + 230 + # Hardening 231 + DeviceAllow = lib.optional flags.enableLibvirtd "/dev/kvm"; 232 + DevicePolicy = "closed"; 233 + LockPersonality = true; 234 + MemoryDenyWriteExecute = true; 235 + NoNewPrivileges = true; 236 + PrivateTmp = true; 237 + PrivateUsers = true; 238 + # Don't restrict ProcSubset because python3Packages.psutil requires read access to /proc/stat 239 + # ProcSubset = "pid"; 240 + ProtectClock = true; 241 + ProtectControlGroups = true; 242 + ProtectHome = true; 243 + ProtectHostname = true; 244 + ProtectKernelLogs = true; 245 + ProtectKernelModules = true; 246 + ProtectKernelTunables = true; 247 + ProtectProc = "invisible"; 248 + ProtectSystem = "strict"; 249 + RestrictAddressFamilies = [ 250 + "AF_INET" 251 + "AF_INET6" 252 + "AF_NETLINK" 253 + "AF_UNIX" 254 + "AF_PACKET" 255 + ]; 256 + RestrictNamespaces = true; 257 + RestrictRealtime = true; 258 + RestrictSUIDSGID = true; 259 + UMask = "0077"; 260 + }; 261 + }; 262 + }; 263 + }