at 24.11-pre 4.0 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.miniupnpd; 7 configFile = pkgs.writeText "miniupnpd.conf" '' 8 ext_ifname=${cfg.externalInterface} 9 enable_natpmp=${if cfg.natpmp then "yes" else "no"} 10 enable_upnp=${if cfg.upnp then "yes" else "no"} 11 12 ${concatMapStrings (range: '' 13 listening_ip=${range} 14 '') cfg.internalIPs} 15 16 ${lib.optionalString (firewall == "nftables") '' 17 upnp_table_name=miniupnpd 18 upnp_nat_table_name=miniupnpd 19 ''} 20 21 ${cfg.appendConfig} 22 ''; 23 firewall = if config.networking.nftables.enable then "nftables" else "iptables"; 24 miniupnpd = pkgs.miniupnpd.override { inherit firewall; }; 25 firewallScripts = lib.optionals (firewall == "iptables") 26 ([ "iptables"] ++ lib.optional (config.networking.enableIPv6) "ip6tables"); 27in 28{ 29 options = { 30 services.miniupnpd = { 31 enable = mkEnableOption "MiniUPnP daemon"; 32 33 externalInterface = mkOption { 34 type = types.str; 35 description = '' 36 Name of the external interface. 37 ''; 38 }; 39 40 internalIPs = mkOption { 41 type = types.listOf types.str; 42 example = [ "192.168.1.1/24" "enp1s0" ]; 43 description = '' 44 The IP address ranges to listen on. 45 ''; 46 }; 47 48 natpmp = mkEnableOption "NAT-PMP support"; 49 50 upnp = mkOption { 51 default = true; 52 type = types.bool; 53 description = '' 54 Whether to enable UPNP support. 55 ''; 56 }; 57 58 appendConfig = mkOption { 59 type = types.lines; 60 default = ""; 61 description = '' 62 Configuration lines appended to the MiniUPnP config. 63 ''; 64 }; 65 }; 66 }; 67 68 config = mkIf cfg.enable { 69 networking.firewall.extraCommands = lib.mkIf (firewallScripts != []) (builtins.concatStringsSep "\n" (map (fw: '' 70 EXTIF=${cfg.externalInterface} ${pkgs.bash}/bin/bash -x ${miniupnpd}/etc/miniupnpd/${fw}_init.sh 71 '') firewallScripts)); 72 73 networking.firewall.extraStopCommands = lib.mkIf (firewallScripts != []) (builtins.concatStringsSep "\n" (map (fw: '' 74 EXTIF=${cfg.externalInterface} ${pkgs.bash}/bin/bash -x ${miniupnpd}/etc/miniupnpd/${fw}_removeall.sh 75 '') firewallScripts)); 76 77 networking.nftables = lib.mkIf (firewall == "nftables") { 78 # see nft_init in ${miniupnpd-nftables}/etc/miniupnpd 79 tables.miniupnpd = { 80 family = "inet"; 81 # The following is omitted because it's expected that the firewall is to be responsible for it. 82 # 83 # chain forward { 84 # type filter hook forward priority filter; policy drop; 85 # jump miniupnpd 86 # } 87 # 88 # Otherwise, it quickly gets ugly with (potentially) two forward chains with "policy drop". 89 # This means the chain "miniupnpd" never actually gets triggered and is simply there to satisfy 90 # miniupnpd. If you're doing it yourself (without networking.firewall), the easiest way to get 91 # it to work is adding a rule "ct status dnat accept" - this is what networking.firewall does. 92 # If you don't want to simply accept forwarding for all "ct status dnat" packets, override 93 # upnp_table_name with whatever your table is, create a chain "miniupnpd" in your table and 94 # jump into it from your forward chain. 95 content = '' 96 chain miniupnpd {} 97 chain prerouting_miniupnpd { 98 type nat hook prerouting priority dstnat; policy accept; 99 } 100 chain postrouting_miniupnpd { 101 type nat hook postrouting priority srcnat; policy accept; 102 } 103 ''; 104 }; 105 }; 106 107 systemd.services.miniupnpd = { 108 description = "MiniUPnP daemon"; 109 after = [ "network.target" ]; 110 wantedBy = [ "multi-user.target" ]; 111 serviceConfig = { 112 ExecStart = "${miniupnpd}/bin/miniupnpd -f ${configFile}"; 113 PIDFile = "/run/miniupnpd.pid"; 114 Type = "forking"; 115 }; 116 }; 117 }; 118}