nixos/gns3-server: init

Changed files
+297
nixos
doc
manual
release-notes
modules
+2
nixos/doc/manual/release-notes/rl-2405.section.md
···
- [maubot](https://github.com/maubot/maubot), a plugin-based Matrix bot framework. Available as [services.maubot](#opt-services.maubot.enable).
+
- [GNS3](https://www.gns3.com/), a network software emulator. Available as [services.gns3-server](#opt-services.gns3-server.enable).
+
- [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).
## Backward Incompatibilities {#sec-release-24.05-incompatibilities}
+1
nixos/modules/module-list.nix
···
./services/networking/ghostunnel.nix
./services/networking/git-daemon.nix
./services/networking/globalprotect-vpn.nix
+
./services/networking/gns3-server.nix
./services/networking/gnunet.nix
./services/networking/go-autoconfig.nix
./services/networking/go-neb.nix
+31
nixos/modules/services/networking/gns3-server.md
···
+
# GNS3 Server {#module-services-gns3-server}
+
+
[GNS3](https://www.gns3.com/), a network software emulator.
+
+
## Basic Usage {#module-services-gns3-server-basic-usage}
+
+
A minimal configuration looks like this:
+
+
```nix
+
{
+
services.gns3-server = {
+
enable = true;
+
+
auth = {
+
enable = true;
+
user = "gns3";
+
passwordFile = "/var/lib/secrets/gns3_password";
+
};
+
+
ssl = {
+
enable = true;
+
certFile = "/var/lib/gns3/ssl/cert.pem";
+
keyFile = "/var/lib/gns3/ssl/key.pem";
+
};
+
+
dynamips.enable = true;
+
ubridge.enable = true;
+
vpcs.enable = true;
+
};
+
}
+
```
+263
nixos/modules/services/networking/gns3-server.nix
···
+
{ config, lib, pkgs, ... }:
+
+
let
+
cfg = config.services.gns3-server;
+
+
settingsFormat = pkgs.formats.ini { };
+
configFile = settingsFormat.generate "gns3-server.conf" cfg.settings;
+
+
in {
+
meta = {
+
doc = ./gns3-server.md;
+
maintainers = [ lib.maintainers.anthonyroussel ];
+
};
+
+
options = {
+
services.gns3-server = {
+
enable = lib.mkEnableOption (lib.mdDoc "GNS3 Server daemon");
+
+
package = lib.mkPackageOptionMD pkgs "gns3-server" { };
+
+
auth = {
+
enable = lib.mkEnableOption (lib.mdDoc "password based HTTP authentication to access the GNS3 Server");
+
+
user = lib.mkOption {
+
type = lib.types.nullOr lib.types.str;
+
default = null;
+
example = "gns3";
+
description = lib.mdDoc ''Username used to access the GNS3 Server.'';
+
};
+
+
passwordFile = lib.mkOption {
+
type = lib.types.nullOr lib.types.path;
+
default = null;
+
example = "/run/secrets/gns3-server-password";
+
description = lib.mdDoc ''
+
A file containing the password to access the GNS3 Server.
+
+
::: {.warning}
+
This should be a string, not a nix path, since nix paths
+
are copied into the world-readable nix store.
+
:::
+
'';
+
};
+
};
+
+
settings = lib.mkOption {
+
type = lib.types.submodule { freeformType = settingsFormat.type; };
+
default = {};
+
example = { host = "127.0.0.1"; port = 3080; };
+
description = lib.mdDoc ''
+
The global options in `config` file in ini format.
+
+
Refer to <https://docs.gns3.com/docs/using-gns3/administration/gns3-server-configuration-file/>
+
for all available options.
+
'';
+
};
+
+
log = {
+
file = lib.mkOption {
+
type = lib.types.nullOr lib.types.path;
+
default = "/var/log/gns3/server.log";
+
description = lib.mdDoc ''Path of the file GNS3 Server should log to.'';
+
};
+
+
debug = lib.mkEnableOption (lib.mdDoc "debug logging");
+
};
+
+
ssl = {
+
enable = lib.mkEnableOption (lib.mdDoc "SSL encryption");
+
+
certFile = lib.mkOption {
+
type = lib.types.nullOr lib.types.path;
+
default = null;
+
example = "/var/lib/gns3/ssl/server.pem";
+
description = lib.mdDoc ''
+
Path to the SSL certificate file. This certificate will
+
be offered to, and may be verified by, clients.
+
'';
+
};
+
+
keyFile = lib.mkOption {
+
type = lib.types.nullOr lib.types.path;
+
default = null;
+
example = "/var/lib/gns3/ssl/server.key";
+
description = lib.mdDoc "Private key file for the certificate.";
+
};
+
};
+
+
dynamips = {
+
enable = lib.mkEnableOption (lib.mdDoc ''Whether to enable Dynamips support.'');
+
package = lib.mkPackageOptionMD pkgs "dynamips" { };
+
};
+
+
ubridge = {
+
enable = lib.mkEnableOption (lib.mdDoc ''Whether to enable uBridge support.'');
+
package = lib.mkPackageOptionMD pkgs "ubridge" { };
+
};
+
+
vpcs = {
+
enable = lib.mkEnableOption (lib.mdDoc ''Whether to enable VPCS support.'');
+
package = lib.mkPackageOptionMD pkgs "vpcs" { };
+
};
+
};
+
};
+
+
config = let
+
flags = {
+
enableDocker = config.virtualisation.docker.enable;
+
enableLibvirtd = config.virtualisation.libvirtd.enable;
+
};
+
+
in lib.mkIf cfg.enable {
+
assertions = [
+
{
+
assertion = cfg.ssl.enable -> cfg.ssl.certFile != null;
+
message = "Please provide a certificate to use for SSL encryption.";
+
}
+
{
+
assertion = cfg.ssl.enable -> cfg.ssl.keyFile != null;
+
message = "Please provide a private key to use for SSL encryption.";
+
}
+
{
+
assertion = cfg.auth.enable -> cfg.auth.user != null;
+
message = "Please provide a username to use for HTTP authentication.";
+
}
+
{
+
assertion = cfg.auth.enable -> cfg.auth.passwordFile != null;
+
message = "Please provide a password file to use for HTTP authentication.";
+
}
+
];
+
+
users.groups.ubridge = lib.mkIf cfg.ubridge.enable { };
+
+
security.wrappers.ubridge = lib.mkIf cfg.ubridge.enable {
+
capabilities = "cap_net_raw,cap_net_admin=eip";
+
group = "ubridge";
+
owner = "root";
+
permissions = "u=rwx,g=rx,o=r";
+
source = lib.getExe cfg.ubridge.package;
+
};
+
+
services.gns3-server.settings = lib.mkMerge [
+
{
+
Server = {
+
appliances_path = lib.mkDefault "/var/lib/gns3/appliances";
+
configs_path = lib.mkDefault "/var/lib/gns3/configs";
+
images_path = lib.mkDefault "/var/lib/gns3/images";
+
projects_path = lib.mkDefault "/var/lib/gns3/projects";
+
symbols_path = lib.mkDefault "/var/lib/gns3/symbols";
+
};
+
}
+
(lib.mkIf (cfg.ubridge.enable) {
+
Server.ubridge_path = lib.mkDefault (lib.getExe cfg.ubridge.package);
+
})
+
(lib.mkIf (cfg.auth.enable) {
+
Server = {
+
auth = lib.mkDefault (lib.boolToString cfg.auth.enable);
+
user = lib.mkDefault cfg.auth.user;
+
password = lib.mkDefault "@AUTH_PASSWORD@";
+
};
+
})
+
(lib.mkIf (cfg.vpcs.enable) {
+
VPCS.vpcs_path = lib.mkDefault (lib.getExe cfg.vpcs.package);
+
})
+
(lib.mkIf (cfg.dynamips.enable) {
+
Dynamips.dynamips_path = lib.mkDefault (lib.getExe cfg.dynamips.package);
+
})
+
];
+
+
systemd.services.gns3-server = let
+
commandArgs = lib.cli.toGNUCommandLineShell { } {
+
config = "/etc/gns3/gns3_server.conf";
+
pid = "/run/gns3/server.pid";
+
log = cfg.log.file;
+
ssl = cfg.ssl.enable;
+
# These are implicitly not set if `null`
+
certfile = cfg.ssl.certFile;
+
certkey = cfg.ssl.keyFile;
+
};
+
in
+
{
+
description = "GNS3 Server";
+
+
after = [ "network.target" "network-online.target" ];
+
wantedBy = [ "multi-user.target" ];
+
wants = [ "network-online.target" ];
+
+
# configFile cannot be stored in RuntimeDirectory, because GNS3
+
# uses the `--config` base path to stores supplementary configuration files at runtime.
+
#
+
preStart = ''
+
install -m660 ${configFile} /etc/gns3/gns3_server.conf
+
+
${lib.optionalString cfg.auth.enable ''
+
${pkgs.replace-secret}/bin/replace-secret \
+
'@AUTH_PASSWORD@' \
+
"''${CREDENTIALS_DIRECTORY}/AUTH_PASSWORD" \
+
/etc/gns3/gns3_server.conf
+
''}
+
'';
+
+
path = lib.optional flags.enableLibvirtd pkgs.qemu;
+
+
reloadTriggers = [ configFile ];
+
+
serviceConfig = {
+
ConfigurationDirectory = "gns3";
+
ConfigurationDirectoryMode = "0750";
+
DynamicUser = true;
+
Environment = "HOME=%S/gns3";
+
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+
ExecStart = "${lib.getExe cfg.package} ${commandArgs}";
+
Group = "gns3";
+
LimitNOFILE = 16384;
+
LoadCredential = lib.mkIf cfg.auth.enable [ "AUTH_PASSWORD:${cfg.auth.passwordFile}" ];
+
LogsDirectory = "gns3";
+
LogsDirectoryMode = "0750";
+
PIDFile = "/run/gns3/server.pid";
+
Restart = "on-failure";
+
RestartSec = 5;
+
RuntimeDirectory = "gns3";
+
StateDirectory = "gns3";
+
StateDirectoryMode = "0750";
+
SupplementaryGroups = lib.optional flags.enableDocker "docker"
+
++ lib.optional flags.enableLibvirtd "libvirtd"
+
++ lib.optional cfg.ubridge.enable "ubridge";
+
User = "gns3";
+
WorkingDirectory = "%S/gns3";
+
+
# Hardening
+
DeviceAllow = lib.optional flags.enableLibvirtd "/dev/kvm";
+
DevicePolicy = "closed";
+
LockPersonality = true;
+
MemoryDenyWriteExecute = true;
+
NoNewPrivileges = true;
+
PrivateTmp = true;
+
PrivateUsers = true;
+
# Don't restrict ProcSubset because python3Packages.psutil requires read access to /proc/stat
+
# ProcSubset = "pid";
+
ProtectClock = true;
+
ProtectControlGroups = true;
+
ProtectHome = true;
+
ProtectHostname = true;
+
ProtectKernelLogs = true;
+
ProtectKernelModules = true;
+
ProtectKernelTunables = true;
+
ProtectProc = "invisible";
+
ProtectSystem = "strict";
+
RestrictAddressFamilies = [
+
"AF_INET"
+
"AF_INET6"
+
"AF_NETLINK"
+
"AF_UNIX"
+
"AF_PACKET"
+
];
+
RestrictNamespaces = true;
+
RestrictRealtime = true;
+
RestrictSUIDSGID = true;
+
UMask = "0077";
+
};
+
};
+
};
+
}