ports: actually forward ovpns
ports into the root namespace
This commit is contained in:
parent
cdfcf1a46d
commit
827d9626d6
|
@ -1,6 +1,24 @@
|
||||||
{ config, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
portOpts = with lib; types.submodule {
|
||||||
|
options = {
|
||||||
|
visibleTo.ovpn = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
{
|
{
|
||||||
|
options = with lib; {
|
||||||
|
sane.ports.ports = mkOption {
|
||||||
|
# add the `visibleTo.ovpn` option
|
||||||
|
type = types.attrsOf portOpts;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
networking.domain = "uninsane.org";
|
networking.domain = "uninsane.org";
|
||||||
|
|
||||||
sane.ports.openFirewall = true;
|
sane.ports.openFirewall = true;
|
||||||
|
@ -83,6 +101,14 @@
|
||||||
vpn-ip = "185.157.162.178";
|
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
|
# 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";
|
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))
|
||||||
|
[]
|
||||||
|
config.sane.ports.ports;
|
||||||
in {
|
in {
|
||||||
privateKeyFile = config.sops.secrets.wg_ovpns_privkey.path;
|
privateKeyFile = config.sops.secrets.wg_ovpns_privkey.path;
|
||||||
# wg is active only in this namespace.
|
# wg is active only in this namespace.
|
||||||
|
@ -109,10 +135,10 @@
|
||||||
# dynamicEndpointRefreshRestartSeconds = 5;
|
# dynamicEndpointRefreshRestartSeconds = 5;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
preSetup = "" + ''
|
preSetup = ''
|
||||||
${ip} netns add ovpns || echo "ovpns already exists"
|
${ip} netns add ovpns || echo "ovpns already exists"
|
||||||
'';
|
'';
|
||||||
postShutdown = "" + ''
|
postShutdown = ''
|
||||||
${in-ns} ip link del ovpns-veth-b || echo "couldn't delete ovpns-veth-b"
|
${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} link del ovpns-veth-a || echo "couldn't delete ovpns-veth-a"
|
||||||
${ip} netns delete ovpns || echo "couldn't delete ovpns"
|
${ip} netns delete ovpns || echo "couldn't delete ovpns"
|
||||||
|
@ -122,7 +148,7 @@
|
||||||
${ip} rule add from all lookup local pref 0
|
${ip} rule add from all lookup local pref 0
|
||||||
${ip} rule del from all lookup local pref 100
|
${ip} rule del from all lookup local pref 100
|
||||||
'';
|
'';
|
||||||
postSetup = "" + ''
|
postSetup = ''
|
||||||
# DOCS:
|
# DOCS:
|
||||||
# - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/>
|
# - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/>
|
||||||
# - iptables primer: <https://danielmiessler.com/study/iptables/>
|
# - iptables primer: <https://danielmiessler.com/study/iptables/>
|
||||||
|
@ -146,25 +172,11 @@
|
||||||
${ip} rule add from all lookup local pref 100
|
${ip} rule add from all lookup local pref 100
|
||||||
${ip} rule del from all lookup local pref 0
|
${ip} rule del from all lookup local pref 0
|
||||||
|
|
||||||
# bridge HTTP traffic:
|
|
||||||
# any external port-80 request sent to the VPN addr will be forwarded to the rootns.
|
|
||||||
# this exists so LetsEncrypt can procure a cert for the MX over http.
|
|
||||||
# TODO: we could use _acme_challence.mx.uninsane.org CNAME to avoid this forwarding
|
|
||||||
# - <https://community.letsencrypt.org/t/where-does-letsencrypt-resolve-dns-from/37607/8>
|
|
||||||
${in-ns} ${iptables} -A PREROUTING -t nat -p tcp --dport 80 -m iprange --dst-range ${vpn-ip} \
|
|
||||||
-j DNAT --to-destination ${veth-host-ip}:80
|
|
||||||
|
|
||||||
# we also bridge DNS traffic
|
|
||||||
${in-ns} ${iptables} -A PREROUTING -t nat -p udp --dport 53 -m iprange --dst-range ${vpn-ip} \
|
|
||||||
-j DNAT --to-destination ${veth-host-ip}
|
|
||||||
${in-ns} ${iptables} -A PREROUTING -t nat -p tcp --dport 53 -m iprange --dst-range ${vpn-ip} \
|
|
||||||
-j DNAT --to-destination ${veth-host-ip}
|
|
||||||
|
|
||||||
# in order to access DNS in this netns, we need to route it to the VPN's nameservers
|
# 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.
|
# - 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 \
|
${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
|
-j DNAT --to-destination ${vpn-dns}:53
|
||||||
'';
|
'' + (lib.concatStringsSep "\n" bridgeStatements);
|
||||||
};
|
};
|
||||||
|
|
||||||
# create a new routing table that we can use to proxy traffic out of the root namespace
|
# create a new routing table that we can use to proxy traffic out of the root namespace
|
||||||
|
@ -217,4 +229,5 @@
|
||||||
# # test with:
|
# # test with:
|
||||||
# # curl --interface hurricane http://[2607:f8b0:400a:80b::2004]
|
# # curl --interface hurricane http://[2607:f8b0:400a:80b::2004]
|
||||||
# # ping 2607:f8b0:400a:80b::2004
|
# # ping 2607:f8b0:400a:80b::2004
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,17 +30,18 @@ in
|
||||||
|
|
||||||
sane.ports.ports."25" = {
|
sane.ports.ports."25" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.ovpn = true;
|
# XXX visibleTo.lan effectively means "open firewall, but don't configure any NAT/forwarding"
|
||||||
|
visibleTo.lan = true;
|
||||||
description = "colin-smtp-mx.uninsane.org";
|
description = "colin-smtp-mx.uninsane.org";
|
||||||
};
|
};
|
||||||
sane.ports.ports."465" = {
|
sane.ports.ports."465" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.ovpn = true;
|
visibleTo.lan = true;
|
||||||
description = "colin-smtps-mx.uninsane.org";
|
description = "colin-smtps-mx.uninsane.org";
|
||||||
};
|
};
|
||||||
sane.ports.ports."587" = {
|
sane.ports.ports."587" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.ovpn = true;
|
visibleTo.lan = true;
|
||||||
description = "colin-smtps-submission-mx.uninsane.org";
|
description = "colin-smtps-submission-mx.uninsane.org";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ in lib.mkMerge [
|
||||||
protocol = [ "udp" "tcp" ];
|
protocol = [ "udp" "tcp" ];
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
visibleTo.wan = true;
|
visibleTo.wan = true;
|
||||||
|
visibleTo.ovpn = true;
|
||||||
description = "colin-dns-hosting";
|
description = "colin-dns-hosting";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,13 +17,6 @@ let
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
};
|
};
|
||||||
visibleTo.ovpn = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
# XXX: behaves more or less the same as `lan` visibility.
|
|
||||||
# OVPN passes everything by default.
|
|
||||||
# TODO: have *this* drive what we forward from wireguard namespace to main namespace
|
|
||||||
};
|
|
||||||
description = mkOption {
|
description = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = "colin-${config.net.hostName}";
|
default = "colin-${config.net.hostName}";
|
||||||
|
@ -38,7 +31,7 @@ let
|
||||||
# gives networking.firewall value for a given "${port}" = portCfg.
|
# gives networking.firewall value for a given "${port}" = portCfg.
|
||||||
firewallConfigForPort = port: portCfg:
|
firewallConfigForPort = port: portCfg:
|
||||||
# any form of visibility means we need to open the firewall
|
# any form of visibility means we need to open the firewall
|
||||||
lib.mkIf (portCfg.visibleTo.lan || portCfg.visibleTo.wan || portCfg.visibleTo.ovpn) {
|
lib.mkIf (lib.foldlAttrs (acc: _: vis: acc || vis) false portCfg.visibleTo) {
|
||||||
allowedTCPPorts = lib.optional (lib.elem "tcp" portCfg.protocol) (lib.toInt port);
|
allowedTCPPorts = lib.optional (lib.elem "tcp" portCfg.protocol) (lib.toInt port);
|
||||||
allowedUDPPorts = lib.optional (lib.elem "udp" portCfg.protocol) (lib.toInt port);
|
allowedUDPPorts = lib.optional (lib.elem "udp" portCfg.protocol) (lib.toInt port);
|
||||||
};
|
};
|
||||||
|
@ -57,10 +50,7 @@ let
|
||||||
ExecStart =
|
ExecStart =
|
||||||
let
|
let
|
||||||
portFwd = "${pkgs.sane-scripts.ip-port-forward}/bin/sane-ip-port-forward";
|
portFwd = "${pkgs.sane-scripts.ip-port-forward}/bin/sane-ip-port-forward";
|
||||||
forwards = lib.flatten [
|
forwards = builtins.map (proto: "${proto}:${port}:${portCfg.description}") portCfg.protocol;
|
||||||
(lib.optional (lib.elem "udp" portCfg.protocol) "udp:${port}:${portCfg.description}")
|
|
||||||
(lib.optional (lib.elem "tcp" portCfg.protocol) "tcp:${port}:${portCfg.description}")
|
|
||||||
];
|
|
||||||
in ''
|
in ''
|
||||||
${portFwd} -v -d ${builtins.toString cfg.upnpLeaseDuration} \
|
${portFwd} -v -d ${builtins.toString cfg.upnpLeaseDuration} \
|
||||||
${lib.escapeShellArgs forwards}
|
${lib.escapeShellArgs forwards}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user