Skip to content

Instantly share code, notes, and snippets.

@c0deaddict
Created August 10, 2021 13:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save c0deaddict/53aedbb69c8cbfebfec8f4428dc03102 to your computer and use it in GitHub Desktop.
Save c0deaddict/53aedbb69c8cbfebfec8f4428dc03102 to your computer and use it in GitHub Desktop.
NixOS container in Wireguard VPN network namespace
{ lib, config, pkgs, ... }:
with lib;
let
veth = "veth-vpn";
hostIp = "10.0.0.1/24";
guestIp = "10.0.0.2/24";
in
{
sops.secrets = {
wireguard-vpn-private-key = { sopsFile = ./secrets.yaml; };
};
# https://mth.st/blog/nixos-wireguard-netns/
systemd.services."netns@" = {
description = "%I network namespace";
before = ["network.target"];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
PrivateNetwork = true;
ExecStart = "${pkgs.writers.writeDash "netns-up" ''
${pkgs.iproute}/bin/ip netns add $1
${pkgs.utillinux}/bin/umount /var/run/netns/$1
${pkgs.utillinux}/bin/mount --bind /proc/self/ns/net /var/run/netns/$1
''} %I";
ExecStop = "${pkgs.iproute}/bin/ip netns del %I";
};
};
systemd.services."wireguard-wg0" = {
bindsTo = ["netns@vpn.service"];
after = ["netns@vpn.service"];
};
networking.wireguard.interfaces = {
wg0 = {
ips = [ "10.0.0.2/32" ];
privateKeyFile = config.sops.secrets.wireguard-vpn-private-key.path;
socketNamespace = "init";
interfaceNamespace = "vpn";
peers = [{
publicKey = "<<wireguard pubkey>>"
# Forward all traffic via VPN.
allowedIPs = [ "0.0.0.0/0" "::/0" ];
endpoint = "my.vpn-provider.com:51820";
persistentKeepalive = 15;
}];
};
};
# https://developers.redhat.com/blog/2018/10/22/introduction-to-linux-interfaces-for-virtual-networking#veth
systemd.services.${veth} = let
ns = "vpn";
ipHost = "${pkgs.iproute}/bin/ip";
ipGuest = "${ipHost} netns exec ${ns} ${pkgs.iproute}/bin/ip";
in {
description = "Veth interface for download";
bindsTo = [ "netns@${ns}.service" ];
after = [ "netns@${ns}.service" ];
wantedBy = [ "network.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = pkgs.writers.writeDash "veth-up" ''
${ipHost} link add ${veth} type veth peer name veth1 netns ${ns}
${ipHost} addr add ${hostIp} dev ${veth}
${ipHost} link set dev ${veth} up
${ipGuest} addr add ${guestIp} dev veth1
${ipGuest} link set dev veth1 up
'';
ExecStop = pkgs.writers.writeDash "veth-down" ''
${ipHost} link del ${veth}
'';
};
};
systemd.services."container@download" = {
bindsTo = [ "${veth}.service" ];
after = [ "${veth}.service" ];
};
containers.test = {
autoStart = true;
extraFlags = [ "--network-namespace-path=/run/netns/vpn" ];
bindMounts = {
"/etc/resolv.conf" = {
hostPath = toString (pkgs.writeText "resolv.conf" ''
nameserver 9.9.9.9
nameserver 1.1.1.1
'');
isReadOnly = true;
};
};
config = { config, pkgs, ... }: {
environment.systemPackages = with pkgs; [
wireguard
traceroute
ldns
];
};
};
systemd.services."test" = {
description = "Test container in VPN namespace";
bindsTo = [ "wireguard-wg0.service"];
after = [ "wireguard-wg0.service" "netns@vpn.service" "${veth}.service"];
unitConfig.JoinsNamespaceOf = "netns@vpn.service";
serviceConfig = {
Type = "simple";
ExecStart = pkgs.writers.writeDash "test" ''
cat /etc/resolv.conf
${pkgs.ldns}/bin/drill google.nl
${pkgs.traceroute}/bin/traceroute google.nl
tail -f /dev/null
'';
PrivateNetwork = true;
BindReadOnlyPaths = let
resolv = pkgs.writeText "resolv.conf" ''
nameserver 9.9.9.9
nameserver 1.1.1.1
'';
in [ "${resolv}:/etc/resolv.conf" ];
};
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment