Files
nixarr/util/upnp/default.nix
T
2024-02-20 16:56:29 +01:00

106 lines
3.4 KiB
Nix

# TODO: Dir creation and file permissions in nix
{
pkgs,
config,
lib,
...
}:
with lib; let
cfg = config.util.upnp;
in {
options.util.upnp = {
enable = mkEnableOption "Enable port forwarding using UPNP.";
openTcpPorts = mkOption {
type = with types; listOf port;
default = [];
description = lib.mdDoc ''
What TCP ports to open using UPNP.
'';
example = [46382 38473];
};
openUdpPorts = mkOption {
type = with types; listOf port;
default = [];
description = lib.mdDoc ''
What UDP ports to open using UPNP.
'';
example = [46382 38473];
};
};
config = mkIf cfg.enable {
# UPNPC firewall access, if not set, then upnpc will fail with "No IGD
# UPnP Device found !"
#
# Alternatively, I also tried allowing all traffic from the router. But
# I assume that the official way is cleaner/more secure:
# ```nix
# networking.firewall.extraCommands = ''
# iptables -I INPUT -p udp -s 192.168.1.1 -j ACCEPT
# iptables -I OUTPUT -p udp -d 192.168.1.1 -j ACCEPT
# '';
# ```
#
# See:
# https://github.com/miniupnp/miniupnp/blob/8ced59d384de13689d3b1c32405bcb562030b241/miniupnpc/README
#
# TODO: Understand this properly
networking.firewall.extraCommands = ''
# Rules for IPv4:
${pkgs.ipset}/bin/ipset -exist create upnp hash:ip,port timeout 3
iptables -A OUTPUT -d 239.255.255.250/32 -p udp -m udp --dport 1900 -j SET --add-set upnp src,src --exist
iptables -A INPUT -p udp -m set --match-set upnp dst,dst -j ACCEPT
iptables -A INPUT -d 239.255.255.250/32 -p udp -m udp --dport 1900 -j ACCEPT
# Rules for IPv6:
${pkgs.ipset}/bin/ipset -exist create upnp6 hash:ip,port timeout 3 family inet6
ip6tables -A OUTPUT -d ff02::c/128 -p udp -m udp --dport 1900 -j SET --add-set upnp6 src,src --exist
ip6tables -A OUTPUT -d ff05::c/128 -p udp -m udp --dport 1900 -j SET --add-set upnp6 src,src --exist
ip6tables -A INPUT -p udp -m set --match-set upnp6 dst,dst -j ACCEPT
ip6tables -A INPUT -d ff02::c/128 -p udp -m udp --dport 1900 -j ACCEPT
ip6tables -A INPUT -d ff05::c/128 -p udp -m udp --dport 1900 -j ACCEPT
'';
systemd = {
services.upnpc = let
upnp-ports = pkgs.writeShellApplication {
name = "upnp-ports";
runtimeInputs = with pkgs; [miniupnpc];
text = (
strings.concatMapStrings (x: "upnpc -r ${builtins.toString x} UDP" + "\n") cfg.openUpdPorts
++ strings.concatMapStrings (x: "upnpc -r ${builtins.toString x} TCP" + "\n") cfg.openTcpPorts
++ ''echo "Successfully requested upnp ports to be opened".''
);
};
in
mkIf cfg.upnp.enable {
enable = true;
description = "Sets port on router";
script = "${upnp-ports}/bin/upnp-ports";
serviceConfig = {
User = "root";
Type = "oneshot";
};
};
timers = {
upnpc = mkIf cfg.upnp.enable {
description = "Sets port on router";
wantedBy = ["timers.target"];
timerConfig = {
OnCalendar = "hourly";
Persistent = "true"; # Run service immediately if last window was missed
RandomizedDelaySec = "5m"; # Run service OnCalendar +- 1h
};
};
};
};
};
}