2023-10-17 09:42:13 +00:00
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
|
|
|
|
let
|
|
|
|
portOpts = with lib; types.submodule {
|
|
|
|
options = {
|
2024-06-17 06:54:27 +00:00
|
|
|
visibleTo.ovpns = mkOption {
|
2023-10-17 09:42:13 +00:00
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
2024-06-17 06:15:00 +00:00
|
|
|
description = ''
|
|
|
|
whether to forward inbound traffic on the OVPN vpn port to the corresponding localhost port.
|
|
|
|
'';
|
2023-10-17 09:42:13 +00:00
|
|
|
};
|
2024-06-17 06:58:06 +00:00
|
|
|
visibleTo.doof = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = ''
|
|
|
|
whether to forward inbound traffic on the doofnet vpn port to the corresponding localhost port.
|
|
|
|
'';
|
|
|
|
};
|
2023-10-17 09:42:13 +00:00
|
|
|
};
|
|
|
|
};
|
2024-06-17 06:54:27 +00:00
|
|
|
|
|
|
|
bridgedWireguardNamespace = { name, ip4, peers, privateKeyFile, vethSubnet, vpnDns }: let
|
|
|
|
ip = "${pkgs.iproute2}/bin/ip";
|
|
|
|
in-ns = "${ip} netns exec ${name}";
|
|
|
|
iptables = "${pkgs.iptables}/bin/iptables";
|
|
|
|
veth-host-ip = "${vethSubnet}.5"; # "x.y.z.5", "x.y.z.6" is legacy: some things elsewhere assume this for ovpns
|
|
|
|
veth-local-ip = "${vethSubnet}.6";
|
|
|
|
bridgePort = port: proto: ''
|
|
|
|
${in-ns} ${iptables} -A PREROUTING -t nat -p ${proto} --dport ${port} -m iprange --dst-range ${ip4} \
|
|
|
|
-j DNAT --to-destination ${veth-host-ip}
|
|
|
|
'';
|
|
|
|
bridgeStatements = lib.foldlAttrs
|
|
|
|
(acc: port: portCfg: acc ++ (builtins.map (bridgePort port) portCfg.protocol))
|
|
|
|
[]
|
|
|
|
(lib.filterAttrs
|
|
|
|
(port: portCfg: portCfg.visibleTo."${name}")
|
|
|
|
config.sane.ports.ports
|
|
|
|
)
|
|
|
|
;
|
|
|
|
in {
|
|
|
|
inherit peers privateKeyFile; #< passthrough wireguard config
|
|
|
|
interfaceNamespace = name;
|
|
|
|
ips = [ "${ip4}/32" ];
|
|
|
|
|
|
|
|
preSetup = ''
|
|
|
|
${ip} netns add ${name} || (test -e /run/netns/${name} && echo "${name} already exists")
|
|
|
|
'';
|
|
|
|
|
|
|
|
postSetup = ''
|
|
|
|
# DOCS:
|
|
|
|
# - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/>
|
|
|
|
# - iptables primer: <https://danielmiessler.com/study/iptables/>
|
|
|
|
# create veth pair
|
|
|
|
${ip} link add ${name}-veth-a type veth peer name ${name}-veth-b
|
|
|
|
${ip} addr add ${veth-host-ip}/24 dev ${name}-veth-a
|
|
|
|
${ip} link set ${name}-veth-a up
|
|
|
|
|
|
|
|
# move veth-b into the namespace
|
|
|
|
${ip} link set ${name}-veth-b netns ${name}
|
|
|
|
${in-ns} ip addr add ${veth-local-ip}/24 dev ${name}-veth-b
|
|
|
|
${in-ns} ip link set ${name}-veth-b up
|
|
|
|
|
|
|
|
# make it so traffic originating from the host side of the veth
|
|
|
|
# is sent over the veth no matter its destination.
|
|
|
|
${ip} rule add from ${veth-host-ip} lookup ${name} pref 50
|
|
|
|
|
|
|
|
# for traffic originating at the host veth to the WAN, use the veth as our gateway
|
|
|
|
# not sure if the metric 1002 matters.
|
|
|
|
${ip} route add default via ${veth-local-ip} dev ${name}-veth-a proto kernel src ${veth-host-ip} metric 1002 table ${name}
|
|
|
|
# give the default route lower priority
|
|
|
|
${ip} rule add from all lookup local pref 100
|
|
|
|
${ip} rule del from all lookup local pref 0
|
|
|
|
|
|
|
|
# in order to access DNS in this netns, we need to route it to the VPN's nameservers
|
|
|
|
# - alternatively, we could fix DNS servers like 1.1.1.1.
|
|
|
|
${in-ns} ${iptables} -A OUTPUT -t nat -p udp --dport 53 -m iprange --dst-range 127.0.0.53 \
|
|
|
|
-j DNAT --to-destination ${vpnDns}:53
|
|
|
|
'' + (lib.concatStringsSep "\n" bridgeStatements);
|
|
|
|
|
|
|
|
postShutdown = ''
|
|
|
|
${in-ns} ip link del ${name}-veth-b || echo "couldn't delete ${name}-veth-b"
|
|
|
|
${ip} link del ${name}-veth-a || echo "couldn't delete ${name}-veth-a"
|
|
|
|
${ip} netns delete ${name} || echo "couldn't delete ${name}"
|
|
|
|
# restore rules/routes
|
|
|
|
${ip} rule del from ${veth-host-ip} lookup ${name} pref 50 || echo "couldn't delete init -> ${name} rule"
|
|
|
|
${ip} route del default via ${veth-local-ip} dev ${name}-veth-a proto kernel src ${veth-host-ip} metric 1002 table ${name} || echo "couldn't delete init -> ${name} route"
|
|
|
|
${ip} rule add from all lookup local pref 0
|
|
|
|
${ip} rule del from all lookup local pref 100
|
|
|
|
'';
|
|
|
|
};
|
2023-10-17 09:42:13 +00:00
|
|
|
in
|
2022-04-27 08:48:40 +00:00
|
|
|
{
|
2023-10-17 09:42:13 +00:00
|
|
|
options = with lib; {
|
|
|
|
sane.ports.ports = mkOption {
|
2024-06-17 06:54:27 +00:00
|
|
|
# add the `visibleTo.ovpns` option
|
2023-10-17 09:42:13 +00:00
|
|
|
type = types.attrsOf portOpts;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
config = {
|
|
|
|
networking.domain = "uninsane.org";
|
|
|
|
|
|
|
|
sane.ports.openFirewall = true;
|
|
|
|
sane.ports.openUpnp = true;
|
|
|
|
|
|
|
|
# unless we add interface-specific settings for each VPN, we have to define nameservers globally.
|
|
|
|
# networking.nameservers = [
|
|
|
|
# "1.1.1.1"
|
|
|
|
# "9.9.9.9"
|
|
|
|
# ];
|
|
|
|
|
|
|
|
# services.resolved.extraConfig = ''
|
|
|
|
# # docs: `man resolved.conf`
|
|
|
|
# # DNS servers to use via the `wg-ovpns` interface.
|
|
|
|
# # i hope that from the root ns, these aren't visible.
|
|
|
|
# DNS=46.227.67.134%wg-ovpns 192.165.9.158%wg-ovpns
|
|
|
|
# FallbackDNS=1.1.1.1 9.9.9.9
|
|
|
|
# '';
|
|
|
|
|
2024-05-20 05:08:32 +00:00
|
|
|
# tun-sea config
|
2024-05-20 05:47:04 +00:00
|
|
|
sane.dns.zones."uninsane.org".inet.A."doof.tunnel" = "205.201.63.12";
|
2024-06-17 06:58:06 +00:00
|
|
|
# sane.dns.zones."uninsane.org".inet.AAAA."doof.tunnel" = "2602:fce8:106::51"; #< TODO: enable IPv6
|
|
|
|
networking.wireguard.interfaces.wg-doof = bridgedWireguardNamespace {
|
2024-05-20 05:08:32 +00:00
|
|
|
privateKeyFile = config.sops.secrets.wg_doof_privkey.path;
|
|
|
|
# wg is active only in this namespace.
|
|
|
|
# run e.g. ip netns exec doof <some command like ping/curl/etc, it'll go through wg>
|
|
|
|
# sudo ip netns exec doof ping www.google.com
|
2024-06-17 06:58:06 +00:00
|
|
|
name = "doof";
|
|
|
|
ip4 = "205.201.63.12";
|
|
|
|
# ip6 = "2602:fce8:106::51/128" #< TODO: enable IPv6
|
2024-05-20 05:08:32 +00:00
|
|
|
peers = [
|
|
|
|
{
|
|
|
|
publicKey = "nuESyYEJ3YU0hTZZgAd7iHBz1ytWBVM5PjEL1VEoTkU=";
|
|
|
|
# TODO: configure DNS within the doof ns and use tun-sea.doof.net endpoint
|
|
|
|
# endpoint = "tun-sea.doof.net:53263";
|
|
|
|
endpoint = "205.201.63.44:53263";
|
|
|
|
allowedIPs = [ "0.0.0.0/0" "::/0" ];
|
|
|
|
persistentKeepalive = 25; #< keep the NAT alive
|
|
|
|
}
|
|
|
|
];
|
2024-06-17 06:58:06 +00:00
|
|
|
|
|
|
|
vethSubnet = "10.0.2"; #< 10.0.2.x is used for forwarding traffic between the root namespace and the VPN namespace
|
|
|
|
vpnDns = "1.1.1.1"; #< DNS requests inside the namespace are forwarded here (TODO: forward to the init namespace resolver)
|
2024-05-20 05:08:32 +00:00
|
|
|
};
|
|
|
|
|
2023-10-17 09:42:13 +00:00
|
|
|
# OVPN CONFIG (https://www.ovpn.com):
|
|
|
|
# DOCS: https://nixos.wiki/wiki/WireGuard
|
|
|
|
# if you `systemctl restart wireguard-wg-ovpns`, make sure to also restart any other services in `NetworkNamespacePath = .../ovpns`.
|
|
|
|
# TODO: why not create the namespace as a seperate operation (nix config for that?)
|
|
|
|
networking.wireguard.enable = true;
|
2024-06-17 06:54:27 +00:00
|
|
|
networking.wireguard.interfaces.wg-ovpns = bridgedWireguardNamespace {
|
2023-10-17 09:42:13 +00:00
|
|
|
privateKeyFile = config.sops.secrets.wg_ovpns_privkey.path;
|
|
|
|
# wg is active only in this namespace.
|
|
|
|
# run e.g. ip netns exec ovpns <some command like ping/curl/etc, it'll go through wg>
|
|
|
|
# sudo ip netns exec ovpns ping www.google.com
|
2024-06-17 06:54:27 +00:00
|
|
|
name = "ovpns";
|
|
|
|
ip4 = "185.157.162.178";
|
2023-10-17 09:42:13 +00:00
|
|
|
peers = [
|
|
|
|
{
|
|
|
|
publicKey = "SkkEZDCBde22KTs/Hc7FWvDBfdOCQA4YtBEuC3n5KGs=";
|
|
|
|
endpoint = "185.157.162.10:9930";
|
|
|
|
# alternatively: use hostname, but that presents bootstrapping issues (e.g. if host net flakes)
|
|
|
|
# endpoint = "vpn36.prd.amsterdam.ovpn.com:9930";
|
|
|
|
allowedIPs = [ "0.0.0.0/0" ];
|
|
|
|
# nixOS says this is important for keeping NATs active
|
|
|
|
persistentKeepalive = 25;
|
|
|
|
# re-executes wg this often. docs hint that this might help wg notice DNS/hostname changes.
|
|
|
|
# so, maybe that helps if we specify endpoint as a domain name
|
|
|
|
# dynamicEndpointRefreshSeconds = 30;
|
|
|
|
# when refresh fails, try it again after this period instead.
|
|
|
|
# TODO: not avail until nixpkgs upgrade
|
|
|
|
# dynamicEndpointRefreshRestartSeconds = 5;
|
|
|
|
}
|
|
|
|
];
|
2024-06-17 06:54:27 +00:00
|
|
|
|
|
|
|
vethSubnet = "10.0.1"; #< 10.0.1.x is used for forwarding traffic between the root namespace and the VPN namespace
|
|
|
|
vpnDns = "46.227.67.134"; #< DNS requests inside the namespace are forwarded here
|
2023-10-17 09:42:13 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
# create a new routing table that we can use to proxy traffic out of the root namespace
|
2024-06-17 07:31:28 +00:00
|
|
|
# through the wireguard namespaces, and to the WAN via VPN.
|
|
|
|
# i think the numbers here aren't particularly important.
|
2023-10-17 09:42:13 +00:00
|
|
|
networking.iproute2.rttablesExtraConfig = ''
|
2024-06-17 07:31:28 +00:00
|
|
|
11 ovpns
|
|
|
|
12 doof
|
2022-12-11 16:46:55 +00:00
|
|
|
'';
|
2023-10-17 09:42:13 +00:00
|
|
|
networking.iproute2.enable = true;
|
2022-05-02 08:23:09 +00:00
|
|
|
};
|
2022-04-27 08:48:40 +00:00
|
|
|
}
|