Is this code for updating the firewall automatically correct?

I use AI to explain my idea more good but it’s my idea:

Automatically Update sys-firewall with Mullvad Server IPs

I created a script that automatically extracts Mullvad VPN server IP addresses and configures sys-firewall to only allow traffic to those IPs. This restricts all network activity through your firewall to only reach Mullvad servers.

How It Works

  1. Extracts IPs — Connects to sys-vpn-mullvad-app and runs mullvad relay list , then parses out all IPv4 and IPv6 addresses
  2. Generates firewall rules — Creates nftables rules that allow traffic only to those IPs
  3. Blocks everything else — Adds a final drop rule to block non-Mullvad destinations
  4. Applies to sys-firewall — Copies the rules to /rw/config/qubes-firewall-user-script (persists across reboots)
  5. Reloads firewall — Applies the new rules immediately

Usage

  1. Create the script in a regular qube (e.g., work ):

bash

nano ~/mullvad-firewall-update.sh# Paste the script below
  1. Copy it to dom0:

bash

qvm-run --pass-io work 'cat ~/mullvad-firewall-update.sh' > /tmp/mullvad-firewall-update.shchmod +x /tmp/mullvad-firewall-update.sh
  1. Run from dom0:

bash

/tmp/mullvad-firewall-update.sh

The Script

bash

#!/bin/bash
# Qubes Mullvad Firewall IP Whitelist Generator# Run this script in dom0# Purpose: Extract Mullvad server IPs and restrict sys-firewall to only allow traffic to those IPs
set -e  # Exit on error
echo "Extracting Mullvad server IPs from sys-vpn-mullvad-app..."IPS=$(qvm-run sys-vpn-mullvad-app 'mullvad relay list | grep -oP "\(\K[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}(?=,)" | sort -u')
echo "Extracting IPv6 addresses..."IPS6=$(qvm-run sys-vpn-mullvad-app 'mullvad relay list | grep -oP "2604:[a-f0-9:]+(?=\))" | sort -u')
# Create nftables rules scriptcat > /tmp/qubes-firewall-user-script << 'EOF'#!/bin/bash# Mullvad server IPs only - auto-generated# This restricts all traffic through sys-firewall to only Mullvad VPN servers
EOF
# Add IPv4 allow rulesecho "# IPv4 Allow rules:" >> /tmp/qubes-firewall-user-scriptwhile read ip; do    if [ ! -z "$ip" ]; then        echo "nft add rule filter forward ip daddr $ip accept" >> /tmp/qubes-firewall-user-script    fidone <<< "$IPS"
# Add IPv6 allow rulesecho "" >> /tmp/qubes-firewall-user-scriptecho "# IPv6 Allow rules:" >> /tmp/qubes-firewall-user-scriptwhile read ip; do    if [ ! -z "$ip" ]; then        echo "nft add rule filter forward ip6 daddr $ip accept" >> /tmp/qubes-firewall-user-script    fidone <<< "$IPS6"
# Add default drop rule for everything elseecho "" >> /tmp/qubes-firewall-user-scriptecho "# Drop all other traffic" >> /tmp/qubes-firewall-user-scriptecho "nft add rule filter forward drop" >> /tmp/qubes-firewall-user-script
# Copy script from dom0 to sys-firewallecho "Copying firewall script to sys-firewall..."cat /tmp/qubes-firewall-user-script | qvm-run --pass-io --user=root sys-firewall 'cat > /tmp/qubes-firewall-user-script'
# Move to correct location and make executableecho "Installing firewall rules..."qvm-run --user=root sys-firewall 'mv /tmp/qubes-firewall-user-script /rw/config/qubes-firewall-user-script && chmod +x /rw/config/qubes-firewall-user-script'
# Reload firewall rulesqvm-run --user=root sys-firewall 'systemctl reload qubes-firewall'
echo ""echo "✓ Done! Firewall updated."echo "  IPv4 IPs: $(echo "$IPS" | wc -l)"echo "  IPv6 IPs: $(echo "$IPS6" | wc -l)"echo ""echo "WARNING: All non-Mullvad traffic through sys-firewall is now blocked!"echo "To revert, delete /rw/config/qubes-firewall-user-script in sys-firewall and reload."
# Optional: Clean uprm /tmp/qubes-firewall-user-script

Optional: Automate with Cron

To update the IP list automatically (useful since Mullvad adds/removes servers), add to dom0’s crontab:

bash

sudo crontab -e

Add this line (runs daily at midnight):

0 0 * * * /tmp/mullvad-firewall-update.sh

Important Notes

  • This is restrictive — Only Mullvad server IPs will be allowed. If something breaks, you know why.
  • To revert — Connect to sys-firewall and delete /rw/config/qubes-firewall-user-script , then reload the firewall
  • Requires Mullvad CLI — Must be installed in sys-vpn-mullvad-app
  • Updates needed — Run the script whenever you want to refresh the IP list

Is this script logic correct? Any feedback before I create a full community guide?

@another.snap.payload

Check your scripts with shellcheck.
Separate code from data as much as possible.
Have error checks.

As a whole, the whole concept of a script writing another script and then running it is quite error prone and dangerous. Suppose ‘mullvad relay list’ fails to provide correct output for whatever reason. - The script will just proceed with unknown result.

Further:

    echo nft add rule filter forward ip daddr $ip accept >> /tmp/qubes-firewall-user-script

Have you actually tested this?

# nft add rule filter forward ip daddr 1.1.1.1 accept
Error: No such file or directory; did you mean table 'qubes' in family ip?
add rule filter forward ip daddr 1.1.1.1 accept
         ^^^^^^

Instead of all this gym, you can simply create a second firewall qube and chain it with sys-firewall.

Consider the possibility of using include files (see man nftables) with variables that you fill in with addresses. The structure of the file can be:

define allowed_addresses = {
	0.0.0.0/8,
	10.0.0.0/8,
	...
}

https://wiki.nftables.org/wiki-nftables/index.php/Scripting

Before creating a community guide, is the coding logic correct? I am inexperienced.

Test your stuff before guiding others.