wireguard: add creation and destination namespaces
The two new options make it possible to create the interface in one namespace and move it to a different one, as explained at https://www.wireguard.com/netns/.
This commit is contained in:
parent
b943338ea5
commit
412f6a967d
|
@ -112,6 +112,32 @@ let
|
||||||
Determines whether to add allowed IPs as routes or not.
|
Determines whether to add allowed IPs as routes or not.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
socketNamespace = mkOption {
|
||||||
|
default = null;
|
||||||
|
type = with types; nullOr str;
|
||||||
|
example = "container";
|
||||||
|
description = ''The pre-existing network namespace in which the
|
||||||
|
WireGuard interface is created, and which retains the socket even if the
|
||||||
|
interface is moved via <option>interfaceNamespace</option>. When
|
||||||
|
<literal>null</literal>, the interface is created in the init namespace.
|
||||||
|
See <link
|
||||||
|
xlink:href="https://www.wireguard.com/netns/">documentation</link>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
interfaceNamespace = mkOption {
|
||||||
|
default = null;
|
||||||
|
type = with types; nullOr str;
|
||||||
|
example = "init";
|
||||||
|
description = ''The pre-existing network namespace the WireGuard
|
||||||
|
interface is moved to. The special value <literal>init</literal> means
|
||||||
|
the init namespace. When <literal>null</literal>, the interface is not
|
||||||
|
moved.
|
||||||
|
See <link
|
||||||
|
xlink:href="https://www.wireguard.com/netns/">documentation</link>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -239,6 +265,10 @@ let
|
||||||
if peer.presharedKey != null
|
if peer.presharedKey != null
|
||||||
then pkgs.writeText "wg-psk" peer.presharedKey
|
then pkgs.writeText "wg-psk" peer.presharedKey
|
||||||
else peer.presharedKeyFile;
|
else peer.presharedKeyFile;
|
||||||
|
src = interfaceCfg.socketNamespace;
|
||||||
|
dst = interfaceCfg.interfaceNamespace;
|
||||||
|
ip = nsWrap "ip" src dst;
|
||||||
|
wg = nsWrap "wg" src dst;
|
||||||
in nameValuePair "wireguard-${interfaceName}-peer-${unitName}"
|
in nameValuePair "wireguard-${interfaceName}-peer-${unitName}"
|
||||||
{
|
{
|
||||||
description = "WireGuard Peer - ${interfaceName} - ${peer.publicKey}";
|
description = "WireGuard Peer - ${interfaceName} - ${peer.publicKey}";
|
||||||
|
@ -255,16 +285,16 @@ let
|
||||||
};
|
};
|
||||||
|
|
||||||
script = let
|
script = let
|
||||||
wg_setup = "wg set ${interfaceName} peer ${peer.publicKey}" +
|
wg_setup = "${wg} set ${interfaceName} peer ${peer.publicKey}" +
|
||||||
optionalString (psk != null) " preshared-key ${psk}" +
|
optionalString (psk != null) " preshared-key ${psk}" +
|
||||||
optionalString (peer.endpoint != null) " endpoint ${peer.endpoint}" +
|
optionalString (peer.endpoint != null) " endpoint ${peer.endpoint}" +
|
||||||
optionalString (peer.persistentKeepalive != null) " persistent-keepalive ${toString peer.persistentKeepalive}" +
|
optionalString (peer.persistentKeepalive != null) " persistent-keepalive ${toString peer.persistentKeepalive}" +
|
||||||
optionalString (peer.allowedIPs != []) " allowed-ips ${concatStringsSep "," peer.allowedIPs}";
|
optionalString (peer.allowedIPs != []) " allowed-ips ${concatStringsSep "," peer.allowedIPs}";
|
||||||
route_setup =
|
route_setup =
|
||||||
optionalString (interfaceCfg.allowedIPsAsRoutes != false)
|
optionalString interfaceCfg.allowedIPsAsRoutes
|
||||||
(concatMapStringsSep "\n"
|
(concatMapStringsSep "\n"
|
||||||
(allowedIP:
|
(allowedIP:
|
||||||
"ip route replace ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}"
|
"${ip} route replace ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}"
|
||||||
) peer.allowedIPs);
|
) peer.allowedIPs);
|
||||||
in ''
|
in ''
|
||||||
${wg_setup}
|
${wg_setup}
|
||||||
|
@ -272,13 +302,13 @@ let
|
||||||
'';
|
'';
|
||||||
|
|
||||||
postStop = let
|
postStop = let
|
||||||
route_destroy = optionalString (interfaceCfg.allowedIPsAsRoutes != false)
|
route_destroy = optionalString interfaceCfg.allowedIPsAsRoutes
|
||||||
(concatMapStringsSep "\n"
|
(concatMapStringsSep "\n"
|
||||||
(allowedIP:
|
(allowedIP:
|
||||||
"ip route delete ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}"
|
"${ip} route delete ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}"
|
||||||
) peer.allowedIPs);
|
) peer.allowedIPs);
|
||||||
in ''
|
in ''
|
||||||
wg set ${interfaceName} peer ${peer.publicKey} remove
|
${wg} set ${interfaceName} peer ${peer.publicKey} remove
|
||||||
${route_destroy}
|
${route_destroy}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
@ -287,6 +317,13 @@ let
|
||||||
# exactly one way to specify the private key must be set
|
# exactly one way to specify the private key must be set
|
||||||
#assert (values.privateKey != null) != (values.privateKeyFile != null);
|
#assert (values.privateKey != null) != (values.privateKeyFile != null);
|
||||||
let privKey = if values.privateKeyFile != null then values.privateKeyFile else pkgs.writeText "wg-key" values.privateKey;
|
let privKey = if values.privateKeyFile != null then values.privateKeyFile else pkgs.writeText "wg-key" values.privateKey;
|
||||||
|
src = values.socketNamespace;
|
||||||
|
dst = values.interfaceNamespace;
|
||||||
|
ipPreMove = nsWrap "ip" src null;
|
||||||
|
ipPostMove = nsWrap "ip" src dst;
|
||||||
|
wg = nsWrap "wg" src dst;
|
||||||
|
ns = if dst == "init" then "1" else dst;
|
||||||
|
|
||||||
in
|
in
|
||||||
nameValuePair "wireguard-${name}"
|
nameValuePair "wireguard-${name}"
|
||||||
{
|
{
|
||||||
|
@ -307,26 +344,33 @@ let
|
||||||
|
|
||||||
${values.preSetup}
|
${values.preSetup}
|
||||||
|
|
||||||
ip link add dev ${name} type wireguard
|
${ipPreMove} link add dev ${name} type wireguard
|
||||||
|
${optionalString (values.interfaceNamespace != null && values.interfaceNamespace != values.socketNamespace) "${ipPreMove} link set ${name} netns ${ns}"}
|
||||||
|
|
||||||
${concatMapStringsSep "\n" (ip:
|
${concatMapStringsSep "\n" (ip:
|
||||||
"ip address add ${ip} dev ${name}"
|
"${ipPostMove} address add ${ip} dev ${name}"
|
||||||
) values.ips}
|
) values.ips}
|
||||||
|
|
||||||
wg set ${name} private-key ${privKey} ${
|
${wg} set ${name} private-key ${privKey} ${
|
||||||
optionalString (values.listenPort != null) " listen-port ${toString values.listenPort}"}
|
optionalString (values.listenPort != null) " listen-port ${toString values.listenPort}"}
|
||||||
|
|
||||||
ip link set up dev ${name}
|
${ipPostMove} link set up dev ${name}
|
||||||
|
|
||||||
${values.postSetup}
|
${values.postSetup}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
postStop = ''
|
postStop = ''
|
||||||
ip link del dev ${name}
|
${ipPostMove} link del dev ${name}
|
||||||
${values.postShutdown}
|
${values.postShutdown}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nsWrap = cmd: src: dst:
|
||||||
|
let
|
||||||
|
nsList = filter (ns: ns != null) [ src dst ];
|
||||||
|
ns = last nsList;
|
||||||
|
in
|
||||||
|
if (length nsList > 0 && ns != "init") then "ip netns exec ${ns} ${cmd}" else cmd;
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -280,6 +280,7 @@ in
|
||||||
virtualbox = handleTestOn ["x86_64-linux"] ./virtualbox.nix {};
|
virtualbox = handleTestOn ["x86_64-linux"] ./virtualbox.nix {};
|
||||||
wireguard = handleTest ./wireguard {};
|
wireguard = handleTest ./wireguard {};
|
||||||
wireguard-generated = handleTest ./wireguard/generated.nix {};
|
wireguard-generated = handleTest ./wireguard/generated.nix {};
|
||||||
|
wireguard-namespaces = handleTest ./wireguard/namespaces.nix {};
|
||||||
wordpress = handleTest ./wordpress.nix {};
|
wordpress = handleTest ./wordpress.nix {};
|
||||||
xautolock = handleTest ./xautolock.nix {};
|
xautolock = handleTest ./xautolock.nix {};
|
||||||
xdg-desktop-portal = handleTest ./xdg-desktop-portal.nix {};
|
xdg-desktop-portal = handleTest ./xdg-desktop-portal.nix {};
|
||||||
|
|
80
nixos/tests/wireguard/namespaces.nix
Normal file
80
nixos/tests/wireguard/namespaces.nix
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
let
|
||||||
|
listenPort = 12345;
|
||||||
|
socketNamespace = "foo";
|
||||||
|
interfaceNamespace = "bar";
|
||||||
|
node = {
|
||||||
|
networking.wireguard.interfaces.wg0 = {
|
||||||
|
listenPort = listenPort;
|
||||||
|
ips = [ "10.10.10.1/24" ];
|
||||||
|
privateKeyFile = "/etc/wireguard/private";
|
||||||
|
generatePrivateKeyFile = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
in
|
||||||
|
|
||||||
|
import ../make-test.nix ({ pkgs, ...} : {
|
||||||
|
name = "wireguard-with-namespaces";
|
||||||
|
meta = with pkgs.stdenv.lib.maintainers; {
|
||||||
|
maintainers = [ asymmetric ];
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
# interface should be created in the socketNamespace
|
||||||
|
# and not moved from there
|
||||||
|
peer0 = pkgs.lib.attrsets.recursiveUpdate node {
|
||||||
|
networking.wireguard.interfaces.wg0 = {
|
||||||
|
preSetup = ''
|
||||||
|
ip netns add ${socketNamespace}
|
||||||
|
'';
|
||||||
|
inherit socketNamespace;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# interface should be created in the init namespace
|
||||||
|
# and moved to the interfaceNamespace
|
||||||
|
peer1 = pkgs.lib.attrsets.recursiveUpdate node {
|
||||||
|
networking.wireguard.interfaces.wg0 = {
|
||||||
|
preSetup = ''
|
||||||
|
ip netns add ${interfaceNamespace}
|
||||||
|
'';
|
||||||
|
inherit interfaceNamespace;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# interface should be created in the socketNamespace
|
||||||
|
# and moved to the interfaceNamespace
|
||||||
|
peer2 = pkgs.lib.attrsets.recursiveUpdate node {
|
||||||
|
networking.wireguard.interfaces.wg0 = {
|
||||||
|
preSetup = ''
|
||||||
|
ip netns add ${socketNamespace}
|
||||||
|
ip netns add ${interfaceNamespace}
|
||||||
|
'';
|
||||||
|
inherit socketNamespace interfaceNamespace;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# interface should be created in the socketNamespace
|
||||||
|
# and moved to the init namespace
|
||||||
|
peer3 = pkgs.lib.attrsets.recursiveUpdate node {
|
||||||
|
networking.wireguard.interfaces.wg0 = {
|
||||||
|
preSetup = ''
|
||||||
|
ip netns add ${socketNamespace}
|
||||||
|
'';
|
||||||
|
inherit socketNamespace;
|
||||||
|
interfaceNamespace = "init";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = ''
|
||||||
|
startAll();
|
||||||
|
|
||||||
|
$peer0->waitForUnit("wireguard-wg0.service");
|
||||||
|
$peer1->waitForUnit("wireguard-wg0.service");
|
||||||
|
$peer2->waitForUnit("wireguard-wg0.service");
|
||||||
|
$peer3->waitForUnit("wireguard-wg0.service");
|
||||||
|
|
||||||
|
$peer0->succeed("ip -n ${socketNamespace} link show wg0");
|
||||||
|
$peer1->succeed("ip -n ${interfaceNamespace} link show wg0");
|
||||||
|
$peer2->succeed("ip -n ${interfaceNamespace} link show wg0");
|
||||||
|
$peer3->succeed("ip link show wg0");
|
||||||
|
'';
|
||||||
|
})
|
Loading…
Reference in a new issue