network/nix/nixos-module/container/upstream.nix

121 lines
3.9 KiB
Nix

{ hostName, config, lib, ... }:
let
hostConf = config.site.hosts.${hostName};
upstreamInterfaces =
lib.filterAttrs (_: { upstream, ... }: upstream != null)
hostConf.interfaces;
firstUpstreamInterface =
if builtins.length (builtins.attrNames upstreamInterfaces) > 0
then builtins.head (
builtins.attrNames upstreamInterfaces
)
else null;
enabled = firstUpstreamInterface != null;
in
{
systemd.network.networks = {
core = {
# systemd-networkd only requests Prefix Delegation via DHCPv6 on
# the upstream interface if another interface is configured for it.
# without this, the static ipv6 subnet won't be routed to us.
networkConfig.DHCPv6PrefixDelegation = true;
dhcpV6PrefixDelegationConfig = {
SubnetId = "81";
# because we have static addresses, we don't actually use this
Assign = false;
};
};
} // builtins.mapAttrs (_: { upstream, ... }: {
DHCP = "yes";
networkConfig.IPv6AcceptRA = true;
dhcpV6Config.PrefixDelegationHint = "::/56";
# Traffic Shaping
extraConfig = ''
[CAKE]
Parent = root
${lib.optionalString (upstream.provider == "vodafone") ''
# DOCSIS overhead
OverheadBytes = 18
''}
${lib.optionalString (upstream.provider == "dsi") ''
# PPPoE overhead
OverheadBytes = 18
''}
${lib.optionalString (upstream.upBandwidth != null) ''
Bandwidth = ${toString upstream.upBandwidth}K
''}
'';
}) upstreamInterfaces;
networking.nat = lib.optionalAttrs enabled {
enable = true;
internalInterfaces = [ "core" ];
externalInterface = firstUpstreamInterface;
externalIP = upstreamInterfaces.${firstUpstreamInterface}.upstream.staticIpv4Address;
extraCommands = ''
# Prohibit SMTP except for servers
iptables -N fwd_smtp
iptables -A fwd_smtp --source ${config.site.net.serv.subnet4} -j RETURN
iptables -A fwd_smtp -j REJECT
iptables -I FORWARD -p tcp --dport 25 -j fwd_smtp
ip6tables -N fwd_smtp
${lib.concatMapStrings (subnet6: ''
ip6tables -A fwd_smtp --source ${subnet6} -j RETURN
ip6tables -A fwd_smtp --dest ${subnet6} -j RETURN
'') (builtins.attrValues config.site.net.serv.subnets6)}
ip6tables -A fwd_smtp -j REJECT
ip6tables -I FORWARD -p tcp --dport 25 -j fwd_smtp
# Provide IPv6 upstream for everyone, using NAT66 when not from
# our static prefixes
${lib.concatMapStringsSep "\n" (net: ''
ip6tables -t nat -N ${net}_nat || \
ip6tables -t nat -F ${net}_nat
${lib.concatMapStringsSep "\n" (subnet: ''
ip6tables -t nat -A ${net}_nat \
-s ${subnet} \
-j RETURN
'') upstreamInterfaces.${net}.upstream.noNat.subnets6}
ip6tables -t nat -A ${net}_nat -j MASQUERADE
ip6tables -t nat -A POSTROUTING \
-o ${net} \
-j ${net}_nat
'') (builtins.attrNames upstreamInterfaces)}
'';
extraStopCommands = ''
iptables -F FORWARD 2>/dev/null || true
ip6tables -F FORWARD 2>/dev/null || true
ip6tables -t nat -F POSTROUTING 2>/dev/null || true
${lib.concatMapStringsSep "\n" (net: ''
ip6tables -t nat -F ${net}_nat 2>/dev/null || true
ip6tables -t nat -X ${net}_nat 2>/dev/null || true
'') (builtins.attrNames upstreamInterfaces)}
'';
forwardPorts = map ({ destination, sourcePort, reflect, ... }@forwardedPort:
removeAttrs forwardedPort ["reflect"] // {
destination =
if builtins.match ".*:.*" destination != null
then destination
else "${destination}:${toString sourcePort}";
loopbackIPs =
if reflect
then builtins.filter (ip: ip != null) (
map (net:
upstreamInterfaces.${net}.upstream.staticIpv4Address
) (builtins.attrNames upstreamInterfaces)
)
else [];
}
) hostConf.forwardPorts;
};
}