mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-12 04:35:41 +03:00
nixos/wg-quick: add AmneziaWG support
Co-authored-by: azahi <azat@bahawi.net>
This commit is contained in:
parent
b0f2335e62
commit
1ce7180d60
3 changed files with 178 additions and 11 deletions
|
@ -11,6 +11,15 @@ let
|
|||
interfaceOpts = { ... }: {
|
||||
options = {
|
||||
|
||||
type = mkOption {
|
||||
example = "amneziawg";
|
||||
default = "wireguard";
|
||||
type = types.enum ["wireguard" "amneziawg"];
|
||||
description = ''
|
||||
The type of the interface. Currently only "wireguard" and "amneziawg" are supported.
|
||||
'';
|
||||
};
|
||||
|
||||
configFile = mkOption {
|
||||
example = "/secret/wg0.conf";
|
||||
default = null;
|
||||
|
@ -151,6 +160,22 @@ let
|
|||
description = "Peers linked to the interface.";
|
||||
type = with types; listOf (submodule peerOpts);
|
||||
};
|
||||
|
||||
extraOptions = mkOption {
|
||||
type = with types; attrsOf (oneOf [ str int ]);
|
||||
default = { };
|
||||
example = {
|
||||
Jc = 5;
|
||||
Jmin = 10;
|
||||
Jmax = 42;
|
||||
S1 = 60;
|
||||
S2 = 90;
|
||||
H4 = 12345;
|
||||
};
|
||||
description = ''
|
||||
Extra options to append to the interface section. Can be used to define AmneziaWG-specific options.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -227,7 +252,7 @@ let
|
|||
|
||||
writeScriptFile = name: text: ((pkgs.writeShellScriptBin name text) + "/bin/${name}");
|
||||
|
||||
generatePrivateKeyScript = privateKeyFile: ''
|
||||
generatePrivateKeyScript = privateKeyFile: wgBin: ''
|
||||
set -e
|
||||
|
||||
# If the parent dir does not already exist, create it.
|
||||
|
@ -236,7 +261,7 @@ let
|
|||
|
||||
if [ ! -f "${privateKeyFile}" ]; then
|
||||
# Write private key file with atomically-correct permissions.
|
||||
(set -e; umask 077; wg genkey > "${privateKeyFile}")
|
||||
(set -e; umask 077; ${wgBin} genkey > "${privateKeyFile}")
|
||||
fi
|
||||
'';
|
||||
|
||||
|
@ -244,11 +269,19 @@ let
|
|||
assert assertMsg (values.configFile != null || ((values.privateKey != null) != (values.privateKeyFile != null))) "Only one of privateKey, configFile or privateKeyFile may be set";
|
||||
assert assertMsg (values.generatePrivateKeyFile == false || values.privateKeyFile != null) "generatePrivateKeyFile requires privateKeyFile to be set";
|
||||
let
|
||||
generateKeyScriptFile = if values.generatePrivateKeyFile then writeScriptFile "generatePrivateKey.sh" (generatePrivateKeyScript values.privateKeyFile) else null;
|
||||
wgBin = {
|
||||
wireguard = "wg";
|
||||
amneziawg = "awg";
|
||||
}.${values.type};
|
||||
generateKeyScriptFile =
|
||||
if values.generatePrivateKeyFile then
|
||||
writeScriptFile "generatePrivateKey.sh" (generatePrivateKeyScript values.privateKeyFile wgBin)
|
||||
else
|
||||
null;
|
||||
preUpFile = if values.preUp != "" then writeScriptFile "preUp.sh" values.preUp else null;
|
||||
postUp =
|
||||
optional (values.privateKeyFile != null) "wg set ${name} private-key <(cat ${values.privateKeyFile})" ++
|
||||
(concatMap (peer: optional (peer.presharedKeyFile != null) "wg set ${name} peer ${peer.publicKey} preshared-key <(cat ${peer.presharedKeyFile})") values.peers) ++
|
||||
optional (values.privateKeyFile != null) "${wgBin} set ${name} private-key <(cat ${values.privateKeyFile})" ++
|
||||
(concatMap (peer: optional (peer.presharedKeyFile != null) "${wgBin} set ${name} peer ${peer.publicKey} preshared-key <(cat ${peer.presharedKeyFile})") values.peers) ++
|
||||
optional (values.postUp != "") values.postUp;
|
||||
postUpFile = if postUp != [] then writeScriptFile "postUp.sh" (concatMapStringsSep "\n" (line: line) postUp) else null;
|
||||
preDownFile = if values.preDown != "" then writeScriptFile "preDown.sh" values.preDown else null;
|
||||
|
@ -276,6 +309,7 @@ let
|
|||
optionalString (postUpFile != null) "PostUp = ${postUpFile}\n" +
|
||||
optionalString (preDownFile != null) "PreDown = ${preDownFile}\n" +
|
||||
optionalString (postDownFile != null) "PostDown = ${postDownFile}\n" +
|
||||
concatLines (mapAttrsToList (n: v: "${n} = ${toString v}") values.extraOptions) +
|
||||
concatMapStringsSep "\n" (peer:
|
||||
assert assertMsg (!((peer.presharedKeyFile != null) && (peer.presharedKey != null))) "Only one of presharedKey or presharedKeyFile may be set";
|
||||
"[Peer]\n" +
|
||||
|
@ -301,7 +335,10 @@ let
|
|||
wantedBy = optional values.autostart "multi-user.target";
|
||||
environment.DEVICE = name;
|
||||
path = [
|
||||
pkgs.wireguard-tools
|
||||
{
|
||||
wireguard = pkgs.wireguard-tools;
|
||||
amneziawg = pkgs.amneziawg-tools;
|
||||
}.${values.type}
|
||||
config.networking.firewall.package # iptables or nftables
|
||||
config.networking.resolvconf.package # openresolv or systemd
|
||||
];
|
||||
|
@ -312,11 +349,11 @@ let
|
|||
};
|
||||
|
||||
script = ''
|
||||
${optionalString (!config.boot.isContainer) "${pkgs.kmod}/bin/modprobe wireguard"}
|
||||
${optionalString (!config.boot.isContainer) "${pkgs.kmod}/bin/modprobe ${values.type}"}
|
||||
${optionalString (values.configFile != null) ''
|
||||
cp ${values.configFile} ${configPath}
|
||||
''}
|
||||
wg-quick up ${configPath}
|
||||
${wgBin}-quick up ${configPath}
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
|
@ -325,7 +362,7 @@ let
|
|||
};
|
||||
|
||||
preStop = ''
|
||||
wg-quick down ${configPath}
|
||||
${wgBin}-quick down ${configPath}
|
||||
'';
|
||||
};
|
||||
in {
|
||||
|
@ -357,8 +394,12 @@ in {
|
|||
###### implementation
|
||||
|
||||
config = mkIf (cfg.interfaces != {}) {
|
||||
boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard;
|
||||
environment.systemPackages = [ pkgs.wireguard-tools ];
|
||||
boot.extraModulePackages =
|
||||
optional (any (x: x.type == "wireguard") (attrValues cfg.interfaces) && (versionOlder kernel.kernel.version "5.6")) kernel.wireguard
|
||||
++ optional (any (x: x.type == "amneziawg") (attrValues cfg.interfaces)) kernel.amneziawg;
|
||||
environment.systemPackages =
|
||||
optional (any (x: x.type == "wireguard") (attrValues cfg.interfaces)) pkgs.wireguard-tools
|
||||
++ optional (any (x: x.type == "amneziawg") (attrValues cfg.interfaces)) pkgs.amneziawg-tools;
|
||||
systemd.services = mapAttrs' generateUnit cfg.interfaces;
|
||||
|
||||
# Prevent networkd from clearing the rules set by wg-quick when restarted (e.g. when waking up from suspend).
|
||||
|
|
125
nixos/tests/wireguard/amneziawg-quick.nix
Normal file
125
nixos/tests/wireguard/amneziawg-quick.nix
Normal file
|
@ -0,0 +1,125 @@
|
|||
import ../make-test-python.nix (
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
kernelPackages ? null,
|
||||
nftables ? false,
|
||||
...
|
||||
}:
|
||||
let
|
||||
wg-snakeoil-keys = import ./snakeoil-keys.nix;
|
||||
peer = import ./make-peer.nix { inherit lib; };
|
||||
commonConfig = {
|
||||
boot.kernelPackages = lib.mkIf (kernelPackages != null) kernelPackages;
|
||||
networking.nftables.enable = nftables;
|
||||
# Make sure iptables doesn't work with nftables enabled
|
||||
boot.blacklistedKernelModules = lib.mkIf nftables [ "nft_compat" ];
|
||||
};
|
||||
extraOptions = {
|
||||
Jc = 5;
|
||||
Jmin = 10;
|
||||
Jmax = 42;
|
||||
S1 = 60;
|
||||
S2 = 90;
|
||||
};
|
||||
in
|
||||
{
|
||||
name = "amneziawg-quick";
|
||||
meta = with pkgs.lib.maintainers; {
|
||||
maintainers = [
|
||||
averyanalex
|
||||
azahi
|
||||
];
|
||||
};
|
||||
|
||||
nodes = {
|
||||
peer0 = peer {
|
||||
ip4 = "192.168.0.1";
|
||||
ip6 = "fd00::1";
|
||||
extraConfig = lib.mkMerge [
|
||||
commonConfig
|
||||
{
|
||||
networking.firewall.allowedUDPPorts = [ 23542 ];
|
||||
networking.wg-quick.interfaces.wg0 = {
|
||||
type = "amneziawg";
|
||||
|
||||
address = [
|
||||
"10.23.42.1/32"
|
||||
"fc00::1/128"
|
||||
];
|
||||
listenPort = 23542;
|
||||
|
||||
inherit (wg-snakeoil-keys.peer0) privateKey;
|
||||
|
||||
peers = lib.singleton {
|
||||
allowedIPs = [
|
||||
"10.23.42.2/32"
|
||||
"fc00::2/128"
|
||||
];
|
||||
|
||||
inherit (wg-snakeoil-keys.peer1) publicKey;
|
||||
};
|
||||
|
||||
dns = [
|
||||
"10.23.42.2"
|
||||
"fc00::2"
|
||||
"wg0"
|
||||
];
|
||||
|
||||
inherit extraOptions;
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
peer1 = peer {
|
||||
ip4 = "192.168.0.2";
|
||||
ip6 = "fd00::2";
|
||||
extraConfig = lib.mkMerge [
|
||||
commonConfig
|
||||
{
|
||||
networking.useNetworkd = true;
|
||||
networking.wg-quick.interfaces.wg0 = {
|
||||
type = "amneziawg";
|
||||
|
||||
address = [
|
||||
"10.23.42.2/32"
|
||||
"fc00::2/128"
|
||||
];
|
||||
inherit (wg-snakeoil-keys.peer1) privateKey;
|
||||
|
||||
peers = lib.singleton {
|
||||
allowedIPs = [
|
||||
"0.0.0.0/0"
|
||||
"::/0"
|
||||
];
|
||||
endpoint = "192.168.0.1:23542";
|
||||
persistentKeepalive = 25;
|
||||
|
||||
inherit (wg-snakeoil-keys.peer0) publicKey;
|
||||
};
|
||||
|
||||
dns = [
|
||||
"10.23.42.1"
|
||||
"fc00::1"
|
||||
"wg0"
|
||||
];
|
||||
|
||||
inherit extraOptions;
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
peer0.wait_for_unit("wg-quick-wg0.service")
|
||||
peer1.wait_for_unit("wg-quick-wg0.service")
|
||||
|
||||
peer1.succeed("ping -c5 fc00::1")
|
||||
peer1.succeed("ping -c5 10.23.42.1")
|
||||
'';
|
||||
}
|
||||
)
|
|
@ -14,6 +14,7 @@ let
|
|||
networkd = callTest ./networkd.nix;
|
||||
wg-quick = callTest ./wg-quick.nix;
|
||||
wg-quick-nftables = args: callTest ./wg-quick.nix ({ nftables = true; } // args);
|
||||
amneziawg-quick = callTest ./amneziawg-quick.nix;
|
||||
generated = callTest ./generated.nix;
|
||||
dynamic-refresh = callTest ./dynamic-refresh.nix;
|
||||
dynamic-refresh-networkd = args: callTest ./dynamic-refresh.nix ({ useNetworkd = true; } // args);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue