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.
|
||||
'';
|
||||
};
|
||||
|
||||
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
|
||||
then pkgs.writeText "wg-psk" peer.presharedKey
|
||||
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}"
|
||||
{
|
||||
description = "WireGuard Peer - ${interfaceName} - ${peer.publicKey}";
|
||||
|
@ -255,16 +285,16 @@ 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 (peer.endpoint != null) " endpoint ${peer.endpoint}" +
|
||||
optionalString (peer.persistentKeepalive != null) " persistent-keepalive ${toString peer.persistentKeepalive}" +
|
||||
optionalString (peer.allowedIPs != []) " allowed-ips ${concatStringsSep "," peer.allowedIPs}";
|
||||
route_setup =
|
||||
optionalString (interfaceCfg.allowedIPsAsRoutes != false)
|
||||
optionalString interfaceCfg.allowedIPsAsRoutes
|
||||
(concatMapStringsSep "\n"
|
||||
(allowedIP:
|
||||
"ip route replace ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}"
|
||||
"${ip} route replace ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}"
|
||||
) peer.allowedIPs);
|
||||
in ''
|
||||
${wg_setup}
|
||||
|
@ -272,13 +302,13 @@ let
|
|||
'';
|
||||
|
||||
postStop = let
|
||||
route_destroy = optionalString (interfaceCfg.allowedIPsAsRoutes != false)
|
||||
route_destroy = optionalString interfaceCfg.allowedIPsAsRoutes
|
||||
(concatMapStringsSep "\n"
|
||||
(allowedIP:
|
||||
"ip route delete ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}"
|
||||
"${ip} route delete ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}"
|
||||
) peer.allowedIPs);
|
||||
in ''
|
||||
wg set ${interfaceName} peer ${peer.publicKey} remove
|
||||
${wg} set ${interfaceName} peer ${peer.publicKey} remove
|
||||
${route_destroy}
|
||||
'';
|
||||
};
|
||||
|
@ -287,6 +317,13 @@ let
|
|||
# exactly one way to specify the private key must be set
|
||||
#assert (values.privateKey != null) != (values.privateKeyFile != null);
|
||||
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
|
||||
nameValuePair "wireguard-${name}"
|
||||
{
|
||||
|
@ -307,26 +344,33 @@ let
|
|||
|
||||
${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:
|
||||
"ip address add ${ip} dev ${name}"
|
||||
"${ipPostMove} address add ${ip} dev ${name}"
|
||||
) values.ips}
|
||||
|
||||
wg set ${name} private-key ${privKey} ${
|
||||
${wg} set ${name} private-key ${privKey} ${
|
||||
optionalString (values.listenPort != null) " listen-port ${toString values.listenPort}"}
|
||||
|
||||
ip link set up dev ${name}
|
||||
${ipPostMove} link set up dev ${name}
|
||||
|
||||
${values.postSetup}
|
||||
'';
|
||||
|
||||
postStop = ''
|
||||
ip link del dev ${name}
|
||||
${ipPostMove} link del dev ${name}
|
||||
${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
|
||||
|
||||
{
|
||||
|
|
|
@ -280,6 +280,7 @@ in
|
|||
virtualbox = handleTestOn ["x86_64-linux"] ./virtualbox.nix {};
|
||||
wireguard = handleTest ./wireguard {};
|
||||
wireguard-generated = handleTest ./wireguard/generated.nix {};
|
||||
wireguard-namespaces = handleTest ./wireguard/namespaces.nix {};
|
||||
wordpress = handleTest ./wordpress.nix {};
|
||||
xautolock = handleTest ./xautolock.nix {};
|
||||
xdg-desktop-portal = handleTest ./xdg-desktop-portal.nix {};
|
||||
|
|
|
@ -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 New Issue