servo: split out a "bridgedWireguardNamespace" helper for configuring ovpns VPN
i can re-use this to forward traffic over doof
This commit is contained in:
parent
dd47a5083c
commit
7825ddc123
|
@ -3,7 +3,7 @@
|
|||
let
|
||||
portOpts = with lib; types.submodule {
|
||||
options = {
|
||||
visibleTo.ovpn = mkOption {
|
||||
visibleTo.ovpns = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
|
@ -12,11 +12,81 @@ let
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
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
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
options = with lib; {
|
||||
sane.ports.ports = mkOption {
|
||||
# add the `visibleTo.ovpn` option
|
||||
# add the `visibleTo.ovpns` option
|
||||
type = types.attrsOf portOpts;
|
||||
};
|
||||
};
|
||||
|
@ -79,36 +149,13 @@ in
|
|||
# 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;
|
||||
networking.wireguard.interfaces.wg-ovpns = let
|
||||
ip = "${pkgs.iproute2}/bin/ip";
|
||||
in-ns = "${ip} netns exec ovpns";
|
||||
iptables = "${pkgs.iptables}/bin/iptables";
|
||||
veth-host-ip = "10.0.1.5";
|
||||
veth-local-ip = "10.0.1.6";
|
||||
vpn-ip = "185.157.162.178";
|
||||
# DNS = 46.227.67.134, 192.165.9.158, 2a07:a880:4601:10f0:cd45::1, 2001:67c:750:1:cafe:cd45::1
|
||||
vpn-dns = "46.227.67.134";
|
||||
bridgePort = port: proto: ''
|
||||
${in-ns} ${iptables} -A PREROUTING -t nat -p ${proto} --dport ${port} -m iprange --dst-range ${vpn-ip} \
|
||||
-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.ovpn)
|
||||
config.sane.ports.ports
|
||||
)
|
||||
;
|
||||
in {
|
||||
networking.wireguard.interfaces.wg-ovpns = bridgedWireguardNamespace {
|
||||
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
|
||||
interfaceNamespace = "ovpns";
|
||||
ips = [
|
||||
"185.157.162.178/32"
|
||||
];
|
||||
name = "ovpns";
|
||||
ip4 = "185.157.162.178";
|
||||
peers = [
|
||||
{
|
||||
publicKey = "SkkEZDCBde22KTs/Hc7FWvDBfdOCQA4YtBEuC3n5KGs=";
|
||||
|
@ -126,48 +173,9 @@ in
|
|||
# dynamicEndpointRefreshRestartSeconds = 5;
|
||||
}
|
||||
];
|
||||
preSetup = ''
|
||||
${ip} netns add ovpns || (test -e /run/netns/ovpns && echo "ovpns already exists")
|
||||
'';
|
||||
postShutdown = ''
|
||||
${in-ns} ip link del ovpns-veth-b || echo "couldn't delete ovpns-veth-b"
|
||||
${ip} link del ovpns-veth-a || echo "couldn't delete ovpns-veth-a"
|
||||
${ip} netns delete ovpns || echo "couldn't delete ovpns"
|
||||
# restore rules/routes
|
||||
${ip} rule del from ${veth-host-ip} lookup ovpns pref 50 || echo "couldn't delete init -> ovpns rule"
|
||||
${ip} route del default via ${veth-local-ip} dev ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns || echo "couldn't delete init -> ovpns route"
|
||||
${ip} rule add from all lookup local pref 0
|
||||
${ip} rule del from all lookup local pref 100
|
||||
'';
|
||||
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 ovpns-veth-a type veth peer name ovpns-veth-b
|
||||
${ip} addr add ${veth-host-ip}/24 dev ovpns-veth-a
|
||||
${ip} link set ovpns-veth-a up
|
||||
|
||||
# mv veth-b into the ovpns namespace
|
||||
${ip} link set ovpns-veth-b netns ovpns
|
||||
${in-ns} ip addr add ${veth-local-ip}/24 dev ovpns-veth-b
|
||||
${in-ns} ip link set ovpns-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 ovpns 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 ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns
|
||||
# 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 ${vpn-dns}:53
|
||||
'' + (lib.concatStringsSep "\n" bridgeStatements);
|
||||
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
|
||||
};
|
||||
|
||||
# create a new routing table that we can use to proxy traffic out of the root namespace
|
||||
|
|
|
@ -55,7 +55,7 @@ in
|
|||
# protocol = [ "tcp" "udp" ];
|
||||
# # visibleTo.lan = true;
|
||||
# # visibleTo.wan = true;
|
||||
# visibleTo.ovpn = true; # forward traffic from the VPN to the root NS
|
||||
# visibleTo.ovpns = true; # forward traffic from the VPN to the root NS
|
||||
# description = "colin-stun-turn";
|
||||
# };
|
||||
# "5349" = {
|
||||
|
@ -63,7 +63,7 @@ in
|
|||
# protocol = [ "tcp" ];
|
||||
# # visibleTo.lan = true;
|
||||
# # visibleTo.wan = true;
|
||||
# visibleTo.ovpn = true;
|
||||
# visibleTo.ovpns = true;
|
||||
# description = "colin-stun-turn-over-tls";
|
||||
# };
|
||||
# }
|
||||
|
@ -76,7 +76,7 @@ in
|
|||
# protocol = [ "tcp" "udp" ];
|
||||
# # visibleTo.lan = true;
|
||||
# # visibleTo.wan = true;
|
||||
# visibleTo.ovpn = true;
|
||||
# visibleTo.ovpns = true;
|
||||
# description = "colin-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
|
||||
# };
|
||||
# })
|
||||
|
|
|
@ -18,7 +18,7 @@ in
|
|||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
visibleTo.ovpn = true; # so that letsencrypt can procure a cert for the mx record
|
||||
visibleTo.ovpns = true; # so that letsencrypt can procure a cert for the mx record
|
||||
description = "colin-http-uninsane.org";
|
||||
};
|
||||
sane.ports.ports."443" = {
|
||||
|
|
|
@ -22,8 +22,7 @@
|
|||
|
||||
sane.ports.ports."50300" = {
|
||||
protocol = [ "tcp" ];
|
||||
# not visible to WAN: i run this in a separate netns
|
||||
visibleTo.ovpn = true;
|
||||
# visibleTo.ovpns = true; #< not needed: it runs in the ovpns namespace
|
||||
description = "colin-soulseek";
|
||||
};
|
||||
|
||||
|
|
|
@ -197,7 +197,7 @@ lib.mkIf false #< TODO: re-enable once confident of sandboxing
|
|||
sane.dns.zones."uninsane.org".inet.CNAME."bt" = "native";
|
||||
sane.ports.ports."51413" = {
|
||||
protocol = [ "tcp" "udp" ];
|
||||
visibleTo.ovpn = true;
|
||||
# visibleTo.ovpns = true; #< not needed: it runs in the ovpns namespace
|
||||
description = "colin-bittorrent";
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ in
|
|||
protocol = [ "udp" "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
visibleTo.ovpn = true;
|
||||
visibleTo.ovpns = true;
|
||||
description = "colin-dns-hosting";
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user