Merge pull request #252790 from anthonyroussel/gns3-nixos-module
nixos/gns3-server: init
This commit is contained in:
commit
2ba8c30b4e
7 changed files with 360 additions and 3 deletions
|
@ -24,6 +24,8 @@ In addition to numerous new and upgraded packages, this release has the followin
|
|||
|
||||
- [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).
|
||||
The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been marked deprecated and will be dropped after 24.05 due to lack of maintenance of the anki-sync-server softwares.
|
||||
|
||||
|
|
|
@ -946,6 +946,7 @@
|
|||
./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
Normal file
31
nixos/modules/services/networking/gns3-server.md
Normal file
|
@ -0,0 +1,31 @@
|
|||
# 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
Normal file
263
nixos/modules/services/networking/gns3-server.nix
Normal file
|
@ -0,0 +1,263 @@
|
|||
{ 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";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -342,6 +342,7 @@ in {
|
|||
gnome-extensions = handleTest ./gnome-extensions.nix {};
|
||||
gnome-flashback = handleTest ./gnome-flashback.nix {};
|
||||
gnome-xorg = handleTest ./gnome-xorg.nix {};
|
||||
gns3-server = handleTest ./gns3-server.nix {};
|
||||
gnupg = handleTest ./gnupg.nix {};
|
||||
go-neb = handleTest ./go-neb.nix {};
|
||||
gobgpd = handleTest ./gobgpd.nix {};
|
||||
|
|
55
nixos/tests/gns3-server.nix
Normal file
55
nixos/tests/gns3-server.nix
Normal file
|
@ -0,0 +1,55 @@
|
|||
import ./make-test-python.nix ({ pkgs, lib, ... }: {
|
||||
name = "gns3-server";
|
||||
meta.maintainers = [ lib.maintainers.anthonyroussel ];
|
||||
|
||||
nodes.machine =
|
||||
{ ... }:
|
||||
let
|
||||
tls-cert = pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
|
||||
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -days 365 \
|
||||
-subj '/CN=localhost'
|
||||
install -D -t $out key.pem cert.pem
|
||||
'';
|
||||
in {
|
||||
services.gns3-server = {
|
||||
enable = true;
|
||||
auth = {
|
||||
enable = true;
|
||||
user = "user";
|
||||
passwordFile = pkgs.writeText "gns3-auth-password-file" "password";
|
||||
};
|
||||
ssl = {
|
||||
enable = true;
|
||||
certFile = "${tls-cert}/cert.pem";
|
||||
keyFile = "${tls-cert}/key.pem";
|
||||
};
|
||||
dynamips.enable = true;
|
||||
ubridge.enable = true;
|
||||
vpcs.enable = true;
|
||||
};
|
||||
|
||||
security.pki.certificateFiles = [ "${tls-cert}/cert.pem" ];
|
||||
};
|
||||
|
||||
testScript = let
|
||||
createProject = pkgs.writeText "createProject.json" (builtins.toJSON {
|
||||
name = "test_project";
|
||||
});
|
||||
in
|
||||
''
|
||||
start_all()
|
||||
|
||||
machine.wait_for_unit("gns3-server.service")
|
||||
machine.wait_for_open_port(3080)
|
||||
|
||||
with subtest("server is listening"):
|
||||
machine.succeed("curl -sSfL -u user:password https://localhost:3080/v2/version")
|
||||
|
||||
with subtest("create dummy project"):
|
||||
machine.succeed("curl -sSfL -u user:password https://localhost:3080/v2/projects -d @${createProject}")
|
||||
|
||||
with subtest("logging works"):
|
||||
log_path = "/var/log/gns3/server.log"
|
||||
machine.wait_for_file(log_path)
|
||||
'';
|
||||
})
|
|
@ -8,6 +8,7 @@
|
|||
, fetchFromGitHub
|
||||
, pkgsStatic
|
||||
, stdenv
|
||||
, nixosTests
|
||||
, testers
|
||||
, gns3-server
|
||||
}:
|
||||
|
@ -75,9 +76,12 @@ python3.pkgs.buildPythonApplication {
|
|||
"--reruns 3"
|
||||
];
|
||||
|
||||
passthru.tests.version = testers.testVersion {
|
||||
package = gns3-server;
|
||||
command = "${lib.getExe gns3-server} --version";
|
||||
passthru.tests = {
|
||||
inherit (nixosTests) gns3-server;
|
||||
version = testers.testVersion {
|
||||
package = gns3-server;
|
||||
command = "${lib.getExe gns3-server} --version";
|
||||
};
|
||||
};
|
||||
|
||||
meta = with lib; {
|
||||
|
|
Loading…
Reference in a new issue