166 lines
5.5 KiB
Nix
166 lines
5.5 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
|
|
with lib;
|
|
|
|
let
|
|
cfg = config.services.wireguard-netns;
|
|
|
|
vpnOptions =
|
|
{ name, ... }:
|
|
{
|
|
options = {
|
|
name = mkOption {
|
|
type = types.str;
|
|
default = name;
|
|
description = "Name of the network namespace and WireGuard interface";
|
|
};
|
|
|
|
dns = mkOption {
|
|
type = types.str;
|
|
description = "Network DNS";
|
|
example = "1.1.1.1";
|
|
};
|
|
|
|
address = mkOption {
|
|
type = types.str;
|
|
description = "Address";
|
|
example = "10.2.0.1/32";
|
|
};
|
|
|
|
conf = mkOption {
|
|
type = types.str;
|
|
description = "Path to sops secret containing WireGuard configuration";
|
|
example = "wg0/conf";
|
|
};
|
|
};
|
|
};
|
|
|
|
in
|
|
{
|
|
options.services.wireguard-netns = {
|
|
enable = mkEnableOption "WireGuard network namespaces";
|
|
|
|
namespaces = mkOption {
|
|
type = types.attrsOf (types.submodule vpnOptions);
|
|
default = { };
|
|
description = "WireGuard VPN configurations in separate network namespaces";
|
|
example = literalExpression ''
|
|
{
|
|
wg0ns = {
|
|
dns = "10.2.0.1";
|
|
address = "10.2.0.2/32";
|
|
conf = "wg0/conf";
|
|
};
|
|
wg1ns = {
|
|
dns = "10.3.0.1";
|
|
address = "10.3.0.2/32";
|
|
conf = "wg1/conf";
|
|
};
|
|
}
|
|
'';
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
systemd.services = listToAttrs (
|
|
flatten (
|
|
imap1 (
|
|
index: elem:
|
|
let
|
|
name = elem.name;
|
|
vpnCfg = elem.value;
|
|
ns = "${name}ns";
|
|
nsIP = "10.200.${toString index}.2/24";
|
|
hostIP = "10.200.${toString index}.1/24";
|
|
vethHost = "veth-${name}";
|
|
vethNS = "veth-${name}-ns";
|
|
proxyIP = "10.200.${toString index}.2"; # IP without CIDR for binding
|
|
proxyPort = 1080;
|
|
in
|
|
[
|
|
{
|
|
name = "netns@${ns}";
|
|
value = {
|
|
description = "${ns} network namespace";
|
|
before = [ "network.target" ];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
ExecStart = pkgs.writers.writeBash "${ns}-up" ''
|
|
${pkgs.coreutils}/bin/mkdir -p /etc/netns/${ns}
|
|
echo "nameserver ${vpnCfg.dns}" > /etc/netns/${ns}/resolv.conf
|
|
${pkgs.iproute2}/bin/ip netns add ${ns}
|
|
|
|
${pkgs.iproute2}/bin/ip link add ${vethHost} type veth peer name ${vethNS}
|
|
${pkgs.iproute2}/bin/ip link set ${vethNS} netns ${ns}
|
|
|
|
${pkgs.iproute2}/bin/ip addr add ${hostIP} dev ${vethHost}
|
|
${pkgs.iproute2}/bin/ip link set ${vethHost} up
|
|
|
|
${pkgs.iproute2}/bin/ip -n ${ns} addr add ${nsIP} dev ${vethNS}
|
|
${pkgs.iproute2}/bin/ip -n ${ns} link set ${vethNS} up
|
|
'';
|
|
ExecStop = pkgs.writers.writeBash "${ns}-down" ''
|
|
${pkgs.iproute2}/bin/ip link del ${vethHost} || true
|
|
${pkgs.iproute2}/bin/ip netns del ${ns}
|
|
'';
|
|
};
|
|
};
|
|
}
|
|
|
|
{
|
|
name = name;
|
|
value = {
|
|
description = "${name} network interface";
|
|
bindsTo = [ "netns@${ns}.service" ];
|
|
requires = [ "network-online.target" ];
|
|
after = [ "netns@${ns}.service" ];
|
|
wants = [ "network-online.target" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
ExecStart = pkgs.writers.writeBash "${name}-up" ''
|
|
${pkgs.iproute2}/bin/ip link add ${name} type wireguard
|
|
${pkgs.iproute2}/bin/ip link set ${name} netns ${ns}
|
|
${pkgs.iproute2}/bin/ip -n ${ns} address add ${vpnCfg.address} dev ${name}
|
|
${pkgs.iproute2}/bin/ip netns exec ${ns} \
|
|
${pkgs.wireguard-tools}/bin/wg setconf ${name} ${config.sops.secrets.${vpnCfg.conf}.path}
|
|
${pkgs.iproute2}/bin/ip -n ${ns} link set lo up
|
|
${pkgs.iproute2}/bin/ip -n ${ns} link set ${name} up
|
|
${pkgs.iproute2}/bin/ip -n ${ns} route add default dev ${name}
|
|
'';
|
|
ExecStop = pkgs.writers.writeBash "${name}-down" ''
|
|
${pkgs.iproute2}/bin/ip -n ${ns} route del default dev ${name} || true
|
|
${pkgs.iproute2}/bin/ip -n ${ns} link del ${name} || true
|
|
'';
|
|
};
|
|
};
|
|
}
|
|
|
|
{
|
|
name = "socks-${name}";
|
|
value = {
|
|
description = "SOCKS5 proxy for ${name} namespace";
|
|
after = [ "${name}.service" ];
|
|
requires = [ "${name}.service" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
Restart = "always";
|
|
RestartSec = "5s";
|
|
ExecStart = "${pkgs.iproute2}/bin/ip netns exec ${ns} ${pkgs.microsocks}/bin/microsocks -i ${proxyIP} -p ${toString proxyPort}";
|
|
};
|
|
};
|
|
}
|
|
]
|
|
) (lib.attrsToList cfg.namespaces)
|
|
)
|
|
);
|
|
};
|
|
}
|