{ config, lib, pkgs, ... }: with lib; let cfg = config.fudo.system; portMappingOpts = { name, ... }: { options = with types; { internal-port = mkOption { type = port; description = "Port on localhost to recieve traffic"; }; external-port = mkOption { type = port; description = "External port on which to listen for traffic."; }; protocols = mkOption { type = listOf str; description = "Protocols for which to forward ports. Default is tcp-only."; default = [ "tcp" ]; }; }; }; in { options.fudo.system = with types; { internal-port-map = mkOption { type = attrsOf (submodule portMappingOpts); description = "Sets of external ports to internal (i.e. localhost) ports to forward."; default = { }; example = { sshmap = { internal-port = 2222; external-port = 22; protocol = "udp"; }; }; }; }; config = mkIf (cfg.internal-port-map != { }) { # FIXME: FUCK ME THIS IS WAY HARDER THAN IT SHOULD BE # boot.kernel.sysctl = mkIf (cfg.internal-port-map != { }) { # "net.ipv4.conf.all.route_localnet" = "1"; # }; # fudo.system.services.forward-internal-ports = let # ip-line = op: src-port: target-port: protocol: '' # ${ipt} -t nat -${op} PREROUTING -p ${protocol} --dport ${ # toString src-port # } -j REDIRECT --to-ports ${toString target-port} # ${ipt} -t nat -${op} OUTPUT -p ${protocol} -s lo --dport ${ # toString src-port # } -j REDIRECT --to-ports ${toString target-port} # ''; # ip-forward-line = ip-line "I"; # ip-unforward-line = ip-line "D"; # traceOut = obj: builtins.trace obj obj; # concatMapAttrsToList = f: attrs: concatLists (mapAttrsToList f attrs); # portmap-entries = concatMapAttrsToList (name: opts: # map (protocol: { # src = opts.external-port; # target = opts.internal-port; # protocol = protocol; # }) opts.protocols) cfg.internal-port-map; # make-entries = f: { src, target, protocol, ... }: f src target protocol; # forward-entries = map (make-entries ip-forward-line) portmap-entries; # unforward-entries = map (make-entries ip-unforward-line) portmap-entries; # forward-ports-script = pkgs.writeShellScript "forward-internal-ports.sh" # (concatStringsSep "\n" forward-entries); # unforward-ports-script = # pkgs.writeShellScript "unforward-internal-ports.sh" # (concatStringsSep "\n" # (map (make-entries ip-unforward-line) portmap-entries)); # in { # wantedBy = [ "multi-user.target" ]; # after = [ "firewall.service" "nat.service" ]; # type = "oneshot"; # description = "Rules for forwarding external ports to local ports."; # execStart = "${forward-ports-script}"; # execStop = "${unforward-ports-script}"; # requiredCapabilities = # [ "CAP_DAC_READ_SEARCH" "CAP_NET_ADMIN" "CAP_NET_RAW" ]; # }; # networking.firewall = let # iptables = "ip46tables"; # ip-forward-line = protocols: internal: external: # concatStringsSep "\n" (map (protocol: '' # ${iptables} -t nat -I PREROUTING -p ${protocol} --dport ${ # toString external # } -j REDIRECT --to-ports ${toString internal} # ${iptables} -t nat -I OUTPUT -s lo -p ${protocol} --dport ${ # toString external # } -j REDIRECT --to-ports ${toString internal} # '') protocols); # ip-unforward-line = protocols: internal: external: # concatStringsSep "\n" (map (protocol: '' # ${iptables} -t nat -D PREROUTING -p ${protocol} --dport ${ # toString external # } -j REDIRECT --to-ports ${toString internal} # ${iptables} -t nat -D OUTPUT -s lo -p ${protocol} --dport ${ # toString external # } -j REDIRECT --to-ports ${toString internal} # '') protocols); # in { # enable = true; # extraCommands = concatStringsSep "\n" (mapAttrsToList (name: opts: # ip-forward-line opts.protocols opts.internal-port opts.external-port) # cfg.internal-port-map); # extraStopCommands = concatStringsSep "\n" (mapAttrsToList (name: opts: # ip-unforward-line opts.protocols opts.internal-port opts.external-port) # cfg.internal-port-map); # }; # networking.nat.forwardPorts = # let portmaps = (attrValues opts.external-port); # in concatMap (opts: # map (protocol: { # destination = "127.0.0.1:${toString opts.internal-port}"; # sourcePort = opts.external-port; # proto = protocol; # }) opts.protocols) (attrValues cfg.internal-port-map); # services.xinetd = mkIf ((length (attrNames cfg.internal-port-map)) > 0) { # enable = true; # services = let # svcs = mapAttrsToList (name: opts: opts // { name = name; }) # cfg.internal-port-map; # svcs-protocols = concatMap # (svc: map (protocol: svc // { protocol = protocol; }) svc.protocols) # svcs; # in map (opts: { # name = opts.name; # unlisted = true; # port = opts.external-port; # server = "${pkgs.coreutils}/bin/false"; # extraConfig = "redirect = localhost ${toString opts.internal-port}"; # protocol = opts.protocol; # }) svcs-protocols; # }; }; }