How to temporarily open a TCP port in NixOS?

I’m looking for a way to temporarily open a port in NixOS. I want to test a web app I develop on my mobile phone. The app is served from my laptop using miniserve, like this:

miniserve --index=index.html dist

I can access it from the laptop at http://localhost:8080/ and the IP address of the WIFI, i.e. http://192.168.1.56:8080/. But the same address on my phone doesn’t respond. I’m pretty sure it’s because the firewall settings on my NixOS laptop.

I could open the ports in /etc/nixos/configuration.nix and run sudo nixos-rebuild switch, but that’s not convenient and also not secure (I would most likely forget to close the port later). Is there a way to temporarily open a port from the command line? Preferably without super user access. Perfect solution would be to integrate it with Direnv, but I’ll appreciate anything more convenient than nixos-rebuild switch.

Use iptables to add or remove firewall rules imperitively. I’m fairly certain NixOS doesn’t automatically save/restore iptables rules, so any rules you create this way will not persist across reboots.

  • List firewall rules: sudo iptables -L
  • Open port 8080: sudo iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
  • Identify firewall rule number: sudo iptables -L INPUT --line-numbers
  • Remove firewall rule: sudo iptables -D INPUT <RULE_NUM>

I know of no method to alter firewall rules without root privileges.

1 Like

If you’re trying to avoid typing in a password just to enable or disable the firewall rule, you can use a script to temporarily enable the port.

# file: ~/bin/run-with-port

#!/usr/bin/env bash
# Usage: sudo run-with-port <port> <cmd> <args...>

set -ueo pipefail

open-port() {
  local port=$1
  iptables -A INPUT -p tcp --dport $port -j ACCEPT
}

close-port() {
  local port=${1:-0}
  iptables -D INPUT -p tcp --dport $port -j ACCEPT
}


if [[ -z "$1" ]]; then
  echo "Port not given" >&2
  exit 1
fi

PORT=$1
shift;  # Drop port argument

if [[ 0 -eq $# ]]; then
  echo "No command given" >&2
  exit 1
fi

open-port $PORT

# Ensure port closes if error occurs.
trap "close-port $PORT" EXIT

# Run the command as user, not root.
runuser -u $SUDO_USER -- "$@"

# Trap will close port.

I just wrote this, tested that it opens and closes the port, but not much more thoroughly than that.

You can update your sudoer config to allow running the run-with-port script without a password, and you now have:

  1. No need to type password to open port.
  2. Can run any single command while port is open.
  3. Port is closed after command stops, even if command errors.
1 Like

The much easier option: pick a port range (10100 to 10110 as an example) and open that in the firewall from your configuration.nix with a nice comment explaining why. When you are working on a project that requires external access, have it listen on one (or more) of the ports in the range. There is no harm leaving a port open in the firewall if there is nothing listening.

1 Like