mirror of https://github.com/wg-easy/wg-easy
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
254 lines
8.5 KiB
254 lines
8.5 KiB
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
|
|
let
|
|
cfg = config.services.wg-easy;
|
|
boolString = value: if value then "true" else "false";
|
|
listString = values: lib.concatStringsSep "," values;
|
|
validListItems =
|
|
values: lib.all (value: value != "" && builtins.match ".*,.*" value == null) values;
|
|
interfaceNamePattern = "[A-Za-z0-9_.-]{1,15}";
|
|
in
|
|
{
|
|
options.services.wg-easy = {
|
|
enable = lib.mkEnableOption "wg-easy";
|
|
|
|
package = lib.mkOption {
|
|
type = lib.types.package;
|
|
default = pkgs.callPackage ./package.nix { };
|
|
defaultText = lib.literalExpression "pkgs.callPackage ./package.nix { }";
|
|
description = "wg-easy package to run.";
|
|
};
|
|
|
|
interfaceName = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "wg0";
|
|
description = "WireGuard interface name managed by wg-easy.";
|
|
};
|
|
|
|
wireguardPort = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 51820;
|
|
description = "UDP port used by the WireGuard interface.";
|
|
};
|
|
|
|
uiPort = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 51821;
|
|
description = "TCP port used by the wg-easy web UI.";
|
|
};
|
|
|
|
stateDir = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "/var/lib/wg-easy";
|
|
description = "Writable directory for the SQLite database and generated WireGuard config.";
|
|
};
|
|
|
|
externalInterface = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
description = "Optional egress network interface used in wg-easy NAT hooks.";
|
|
};
|
|
|
|
enableIPv6 = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = "Enable IPv6 addresses and IPv6 firewall hooks in wg-easy.";
|
|
};
|
|
|
|
defaultDns = lib.mkOption {
|
|
type = lib.types.nullOr (lib.types.listOf lib.types.str);
|
|
default = null;
|
|
description = "DNS servers to stamp onto new clients. Leave null to use wg-easy's global default.";
|
|
};
|
|
|
|
defaultAllowedIps = lib.mkOption {
|
|
type = lib.types.nullOr (lib.types.listOf lib.types.str);
|
|
default = null;
|
|
description = "Allowed IPs to stamp onto new client configs. Leave null to use wg-easy's global default.";
|
|
};
|
|
|
|
defaultServerAllowedIps = lib.mkOption {
|
|
type = lib.types.nullOr (lib.types.listOf lib.types.str);
|
|
default = null;
|
|
description = "Additional server-side peer AllowedIPs to stamp onto new clients.";
|
|
};
|
|
|
|
defaultFirewallAllowedIps = lib.mkOption {
|
|
type = lib.types.nullOr (lib.types.listOf lib.types.str);
|
|
default = null;
|
|
description = "Firewall-enforced allowed destinations to stamp onto new clients.";
|
|
};
|
|
|
|
defaultPersistentKeepalive = lib.mkOption {
|
|
type = lib.types.nullOr (lib.types.ints.between 0 65535);
|
|
default = null;
|
|
description = "Persistent keepalive to stamp onto new clients. Leave null to preserve wg-easy's configured default.";
|
|
};
|
|
|
|
firewallEnabled = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.bool;
|
|
default = null;
|
|
description = "Enable or disable wg-easy per-client firewall filtering. Leave null to preserve wg-easy's configured value.";
|
|
};
|
|
|
|
forceUpdateClients = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = "Overwrite existing clients with configured Nix defaults on service startup.";
|
|
};
|
|
|
|
insecure = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = "Serve the UI as insecure HTTP. Keep false when using HTTPS through a reverse proxy.";
|
|
};
|
|
|
|
openFirewall = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = "Open the WireGuard UDP port in the NixOS firewall.";
|
|
};
|
|
|
|
openWebUIFirewall = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = "Open the wg-easy web UI TCP port in the NixOS firewall.";
|
|
};
|
|
|
|
extraEnvironment = lib.mkOption {
|
|
type = lib.types.attrsOf lib.types.str;
|
|
default = { };
|
|
description = "Additional environment variables for the wg-easy service.";
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
assertions = [
|
|
{
|
|
assertion = builtins.match interfaceNamePattern cfg.interfaceName != null;
|
|
message = "services.wg-easy.interfaceName must be 1-15 characters using only letters, numbers, '.', '_', or '-'.";
|
|
}
|
|
{
|
|
assertion =
|
|
cfg.externalInterface == null || builtins.match interfaceNamePattern cfg.externalInterface != null;
|
|
message = "services.wg-easy.externalInterface must be 1-15 characters using only letters, numbers, '.', '_', or '-'.";
|
|
}
|
|
{
|
|
assertion = lib.hasPrefix "/" cfg.stateDir;
|
|
message = "services.wg-easy.stateDir must be an absolute path.";
|
|
}
|
|
{
|
|
assertion = cfg.defaultDns == null || validListItems cfg.defaultDns;
|
|
message = "services.wg-easy.defaultDns entries must be non-empty and must not contain commas.";
|
|
}
|
|
{
|
|
assertion =
|
|
cfg.defaultAllowedIps == null
|
|
|| (cfg.defaultAllowedIps != [ ] && validListItems cfg.defaultAllowedIps);
|
|
message = "services.wg-easy.defaultAllowedIps must be null or a non-empty list without commas.";
|
|
}
|
|
{
|
|
assertion = cfg.defaultServerAllowedIps == null || validListItems cfg.defaultServerAllowedIps;
|
|
message = "services.wg-easy.defaultServerAllowedIps entries must be non-empty and must not contain commas.";
|
|
}
|
|
{
|
|
assertion =
|
|
cfg.defaultFirewallAllowedIps == null
|
|
|| (cfg.defaultFirewallAllowedIps != [ ] && validListItems cfg.defaultFirewallAllowedIps);
|
|
message = "services.wg-easy.defaultFirewallAllowedIps must be null or a non-empty list without commas.";
|
|
}
|
|
];
|
|
|
|
boot.kernel.sysctl = {
|
|
"net.ipv4.ip_forward" = lib.mkDefault 1;
|
|
"net.ipv4.conf.all.src_valid_mark" = lib.mkDefault 1;
|
|
}
|
|
// lib.optionalAttrs cfg.enableIPv6 {
|
|
"net.ipv6.conf.all.disable_ipv6" = lib.mkDefault 0;
|
|
"net.ipv6.conf.all.forwarding" = lib.mkDefault 1;
|
|
"net.ipv6.conf.default.forwarding" = lib.mkDefault 1;
|
|
};
|
|
|
|
networking.firewall.allowedUDPPorts = lib.mkIf cfg.openFirewall [ cfg.wireguardPort ];
|
|
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openWebUIFirewall [ cfg.uiPort ];
|
|
|
|
systemd.tmpfiles.rules = [
|
|
"d ${cfg.stateDir} 0700 root root -"
|
|
];
|
|
|
|
systemd.services.wg-easy = {
|
|
description = "WireGuard Easy";
|
|
documentation = [ "https://wg-easy.github.io/wg-easy/latest/" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
wants = [ "network-online.target" ];
|
|
after = [ "network-online.target" ];
|
|
|
|
environment = {
|
|
DISABLE_IPV6 = boolString (!cfg.enableIPv6);
|
|
INSECURE = boolString cfg.insecure;
|
|
PORT = toString cfg.uiPort;
|
|
WG_FORCE_UPDATE_CLIENTS = boolString cfg.forceUpdateClients;
|
|
WG_INTERFACE_NAME = cfg.interfaceName;
|
|
WG_PORT = toString cfg.wireguardPort;
|
|
WG_STATE_DIR = cfg.stateDir;
|
|
}
|
|
// lib.optionalAttrs (cfg.defaultDns != null) {
|
|
WG_DEFAULT_DNS = listString cfg.defaultDns;
|
|
}
|
|
// lib.optionalAttrs (cfg.defaultAllowedIps != null) {
|
|
WG_DEFAULT_ALLOWED_IPS = listString cfg.defaultAllowedIps;
|
|
}
|
|
// lib.optionalAttrs (cfg.defaultServerAllowedIps != null) {
|
|
WG_DEFAULT_SERVER_ALLOWED_IPS = listString cfg.defaultServerAllowedIps;
|
|
}
|
|
// lib.optionalAttrs (cfg.defaultFirewallAllowedIps != null) {
|
|
WG_DEFAULT_FIREWALL_ALLOWED_IPS = listString cfg.defaultFirewallAllowedIps;
|
|
}
|
|
// lib.optionalAttrs (cfg.defaultPersistentKeepalive != null) {
|
|
WG_DEFAULT_PERSISTENT_KEEPALIVE = toString cfg.defaultPersistentKeepalive;
|
|
}
|
|
// lib.optionalAttrs (cfg.firewallEnabled != null) {
|
|
WG_FIREWALL_ENABLED = boolString cfg.firewallEnabled;
|
|
}
|
|
// lib.optionalAttrs (cfg.externalInterface != null) {
|
|
WG_DEVICE = cfg.externalInterface;
|
|
}
|
|
// cfg.extraEnvironment;
|
|
|
|
path = with pkgs; [
|
|
bash
|
|
coreutils
|
|
gawk
|
|
gnugrep
|
|
gnused
|
|
iproute2
|
|
iptables
|
|
kmod
|
|
nftables
|
|
procps
|
|
wireguard-go
|
|
wireguard-tools
|
|
];
|
|
|
|
serviceConfig = {
|
|
ExecStart = lib.getExe cfg.package;
|
|
Restart = "on-failure";
|
|
RestartSec = "5s";
|
|
User = "root";
|
|
AmbientCapabilities = [
|
|
"CAP_NET_ADMIN"
|
|
"CAP_SYS_MODULE"
|
|
];
|
|
CapabilityBoundingSet = [
|
|
"CAP_NET_ADMIN"
|
|
"CAP_SYS_MODULE"
|
|
];
|
|
};
|
|
};
|
|
};
|
|
}
|
|
|