netsudo is a sudo-like CLI for temporary, audited access across pfSense firewall boundaries.
It is designed for segmented homelabs and small labs where firewall policy should stay closed by default, but trusted administrators sometimes need short-lived access to a VLAN, host, or service.
- Grants access from one source IP for a short time window.
- Enforces named profiles with maximum durations.
- Updates pfSense aliases and fixed firewall rules instead of disabling firewall policy.
- Logs every grant, revoke, and expiry.
- Revokes access by removing the source IP and killing matching firewall states.
- Uses SSH to pfSense by default, so no REST API package is required on the firewall.
netsudo allow admin --for 20m --reason "fix Wazuh agent"
netsudo allow service-jellyfin --for 2h
netsudo status
netsudo revoke last
netsudo revoke allallow all does not disable the firewall. It means "allow this source IP to the destinations and ports defined by the all profile, for a limited time."
pfSense is configured once with:
- a source alias per profile, such as
NETSUDO_ADMIN_SRC - a destination alias per profile, such as
NETSUDO_ADMIN_DST - optional port aliases
- pass rules that reference those aliases
The source aliases contain a placeholder IP by default. A grant replaces the alias contents with the active source IPs for that profile and reloads the pfSense filter. Expiry/revoke removes the IP again.
When --source is used, netsudo dynamically grants that source IP for the request window. Profiles can set sources to limit which source IPs are allowed.
When --destination is used, netsudo creates temporary grant-specific aliases and rules instead of adding the source to the broad profile source alias. That keeps the grant limited to the requested host/CIDR and removes those objects on revoke or expiry.
netsudo supports Python 3.10 and newer.
-
Install the required system packages.
Fedora:
sudo dnf install -y git python3 python3-pip openssh-clients
Ubuntu/Debian:
sudo apt update sudo apt install -y git python3 python3-pip openssh-client
-
Clone the repository:
git clone https://github.com/monjaka/netsudo.git cd netsudo -
Install
netsudofor your user:python3 -m pip install --user .Run this from inside the cloned
netsudodirectory. This is the standard install method for now.On newer Ubuntu/Debian releases, if pip exits with
externally-managed-environment, use:python3 -m pip install --user --break-system-packages .That still installs
netsudoin your user site-packages; it does not install pfSense credentials or change firewall state. -
Check the installed command:
netsudo --version
-
Run the installer. This creates
netsudo.tomland can set up the SSH key:netsudo-install --config ./netsudo.toml
The installer writes the local config file. If it asks whether to run setup now, answer
nounless you are ready to apply the generated config to pfSense immediately. -
Review
netsudo.tomland edit the profile sources, destinations, ports, and durations for your network. -
Apply the config to pfSense if you skipped setup in the installer or changed
netsudo.tomlafter setup:netsudo-install --config ./netsudo.toml --setup-only
This is the same setup action the installer can run for you. It reads
netsudo.toml, copies the helper to pfSense, uploads policy rendered from that file, and creates/updates pfSense aliases and rules. It does not editnetsudo.toml.
For development or quick testing only:
cd netsudo
python3 scripts/install.py
python3 -m netsudo.cli --versionAfter completing the installation steps above, netsudo.toml should already exist and the pfSense helper should already be installed.
Grant access:
netsudo allow admin --for 20m --reason "maintenance"Grant access for another device by specifying its source IP:
netsudo allow admin --source 192.168.6.60 --for 20m --reason "workstation maintenance"Grant access to a narrower destination inside the profile scope:
netsudo allow admin --source 192.168.6.60 --destination 192.168.115.100 --for 20m --reason "check Wazuh"
netsudo allow admin --destination 192.168.9.0/24 --destination 192.168.115.100 --for 30m --reason "maintenance"Check grants:
netsudo statusRevoke:
netsudo revoke lastIf you later change netsudo.toml, apply the updated policy to pfSense again:
netsudo-install --config ./netsudo.toml --setup-onlyIf netsudo allow says a destination is outside the profile's allowed destinations, edit that profile in netsudo.toml and widen destinations to the boundary you are willing to delegate to that profile.
You do not need to list every VLAN. Use a broader internal CIDR, then keep each grant narrow with --destination:
[profiles.admin]
sources = ["192.168.0.0/16"]
destinations = ["192.168.0.0/16"]With that scope, this grant is accepted because both 192.168.6.60 and 192.168.115.100 are inside the configured boundaries:
netsudo allow admin --source 192.168.6.60 --destination 192.168.115.100 --for 20m --reason "check Wazuh"Then apply config changes to pfSense:
netsudo-install --config ./netsudo.toml --setup-onlyBy default, interfaces = ["auto"] makes the pfSense helper resolve rule placement from the requested source IP and the firewall's own interface networks. For example, access from 192.168.6.60 resolves to the pfSense interface whose subnet contains 192.168.6.60. Use explicit interface names only if auto resolution cannot represent a special case.
Use a dedicated pfSense SSH user.
Do not store pfSense admin passwords in netsudo.toml. The intended model is local sudo plus a dedicated SSH key.
For profiles with require_sudo = true, run netsudo allow ... as your normal user. netsudo will invoke local sudo itself when needed. This avoids the common sudo: netsudo: command not found problem caused by pip --user installing the command under ~/.local/bin.
The installer can generate a dedicated SSH key and install the public key on pfSense. It may prompt for the pfSense account password through ssh or ssh-copy-id, but it does not store that password.
After setup, the installer can restrict the dedicated key in pfSense authorized_keys with a forced command and disabled forwarding/PTY options. A restricted key is intended for day-to-day allow, status, revoke, and prune operations. Bootstrap tasks such as copying a new helper or policy still require an unrestricted admin SSH path.
If you did not restrict the SSH key during setup, you can do it later:
netsudo-install --config ./netsudo.toml --restrict-key-onlyTo uninstall local netsudo config/key files and optionally remove helper files from pfSense:
netsudo-install --config ./netsudo.toml --uninstall
python3 -m pip uninstall netsudoThe uninstall flow prompts before deleting local files or pfSense helper files. It does not remove pfSense aliases/rules from config.xml; remove NETSUDO_* firewall objects in pfSense if you want a fully clean firewall config.
See SECURITY.md and docs/security-model.md for operational guidance.
netsudo render-policy --config ./netsudo.toml
netsudo install-helper --config ./netsudo.toml
netsudo-install --config ./netsudo.toml --setup-only
netsudo allow admin --for 20m --reason "maintenance"
netsudo allow admin --source 192.168.6.60 --destination 192.168.115.100 --for 20m --reason "maintenance"
netsudo status
netsudo revoke last
netsudo pruneThis is an alpha implementation. Review generated pfSense rules before relying on it in a production network.