Open firewall ports only towards local network?

I recently set up KDE Connect to transfer files from my phone, and it requires a range of open ports, which I added to my configuration.nix based on the documentation:

  networking.firewall.allowedTCPPortRanges = [ { from = 1714; to = 1764; } ];
  networking.firewall.allowedUDPPortRanges = [ { from = 1714; to = 1764; } ];

(Which works!)

However, given the use case, I would prefer to restrict this so that only devices on my local network (i.e., my phone) can access these ports, and not the whole entire internet. Is there a straightforward way to accomplish this?

I found a stackoverflow question about this for the general Linux case, but since I have zero experience with iptables or firewall configuration generally, and NixOS often implicitly expects you to do things in certain ways, it seemed prudent to ask first rather than resort to trial-and-error. (Maybe I just need to copy the commands from the top answer there into extraCommands? But maybe, given other things NixOS has going on, that would do the wrong thing. And again I have no experience with any of this.)

4 Likes

You can use normal iptables commands in networking.firewall.extraCommands, with a few caveats:

  • iptables operates only on IPv4 packets, and ip6tables on IPv6;
  • the commands are executed every time a configuration is activated, and using -A will keep adding those rules to the firewall;
  • even if you remove a rule from extraCommands, it’ll be still in force until a reboot.

To add rules to both IPv4 and IPv6, NixOS supplies an ip46tables function to the firewall setup that calls both iptables commands. It won’t help in your case, however, as you want to specify a network.

To sidestep the other two issues, you can add rules to the nixos-fw chain instead of directly to INPUT. It is the chain that allowed(TCP|UDP)Port(Range)s use, and it is rebuilt on each activation. However, I’m not sure if it is considered publicly usable or just an implementation detail.

All in all, I’d go with the following for simplicity (after adjusting the source network, and switching to ip6tables if needed):

networking.firewall.extraCommands = ''
  iptables -A nixos-fw -p tcp --source 192.0.2.0/24 --dport 1714:1764 -j nixos-fw-accept
  iptables -A nixos-fw -p udp --source 192.0.2.0/24 --dport 1714:1764 -j nixos-fw-accept
'';
6 Likes

Thank you so much! (As a side effect, I now understand a bit more about iptables as well.)

Another thing to note is that if you add rules via networking.firewall.extraStopCommands, you might want to remove them with have networking.firewall.extraCommands - and you want to do that idempotently.

There are cases in which the rules removed by extraStopCommands don’t match the ones added by extraCommands (for example, if the systemd unit failed to reach the point where the rules are added).
If that happens, the teardown will fail to happen cleanly.

So it’s a good idea to make the extraStopCommands idempotent by appending || true:

networking.firewall.extraStopCommands = ''
  iptables -D nixos-fw -p tcp --source 192.0.2.0/24 --dport 1714:1764 -j nixos-fw-accept || true
  iptables -D nixos-fw -p udp --source 192.0.2.0/24 --dport 1714:1764 -j nixos-fw-accept || true
'';

You can see this in many places across nixpkgs.

5 Likes