I have updated the NAP-PMP call to use the same private port as the public port. This allowed me to simplify the main script and firewall rules.
This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.
Show posts Menu[Interface]
PrivateKey = <Windows private key> # Replace with the private key obtained above
Address = 192.168.3.2/32 # Use a different network than your LAN. For example, if your router is 192.168.1.1, use 192.168.3.2 here.
DNS = 10.2.0.1 # ProtonVPN's DNS and NAT-PMP server
# Sets the route metric of the WireGuard adapter to a lower priority, enabling split tunneling.
PostUp = powershell -command "$ii = (Get-NetAdapter -Name %WIREGUARD_TUNNEL_NAME%).ifIndex; route add 0.0.0.0 mask 0.0.0.0 0.0.0.0 IF $ii metric 9999"
# Removes the route added above.
PreDown = powershell -command "$ii = (Get-NetAdapter -Name %WIREGUARD_TUNNEL_NAME%).ifIndex; route delete 0.0.0.0 IF $ii"
Table = off # Prevents the automatic addition of routes.
[Peer]
PublicKey = <OPNsense Public Key> # To be added later.
AllowedIPs = 0.0.0.0/0, ::/0 # Allows traffic from qBittorrent.
Endpoint = 192.168.3.1:666 # Gateway and port in OPNsense.
PersistentKeepalive = 25
Set-ExecutionPolicy RemoteSigned
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned -Force
$regPath = 'HKLM:\Software\WireGuard'
New-Item $regPath -Force | Out-Null
New-ItemProperty $regPath -Name DangerousScriptExecution -Value 1 -Force | Out-Null
C:\Windows\System32\OpenSSH\ssh.exe root@192.168.1.1
opnsense-code ports
cd /usr/ports/net/libnatpmp
make install clean
cd /usr
rm -rf ports
pkg install jq
#!/bin/sh
export PATH=$PATH:/usr/local/bin
# OPNsense
readonly VPN_GATEWAY="10.2.0.1"
readonly OPNSENSE_URL="https://192.168.1.1"
readonly FORWARDED_PORT_ALIAS_NAME="Forwarded_Port"
readonly API_KEY="<api key for OPNsense>"
readonly API_SECRET="<api secret for OPNsense>"
# qBittorrent
readonly QBT_URL="http://192.168.3.2:8080"
readonly QBT_USERNAME="<qBittorrent client username>"
readonly QBT_PASSWORD="<qBittorrent client password>"
# Function to log in to the torrent client and return session ID
login_torrent_client() {
curl -s -k -X POST "$QBT_URL/api/v2/auth/login" \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d "username=$QBT_USERNAME&password=$QBT_PASSWORD" -i | \
grep "set-cookie: SID" | awk -F'[=;]' '{print $2}' | tr -d ' '
}
# Function to get the current port from the torrent client
get_torrent_port() {
local sid="$1"
local port=$(curl -s -k -X GET "$QBT_URL/api/v2/app/preferences" -H "Cookie: SID=$sid" 2>/dev/null | jq -r '.listen_port' 2>/dev/null)
if ! echo "$port" | grep -qE '^[0-9]+$'; then
echo ""
else
echo "$port"
fi
}
# Function to set the port in the torrent client
set_torrent_port() {
local port="$1"
local sid="$2"
curl -s -k -X POST "$QBT_URL/api/v2/app/setPreferences" \
-H "Cookie: SID=$sid" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "json={\"listen_port\":$port}"
}
# Function to get the alias UUID from OPNsense
get_alias_uuid() {
local alias_id="$1"
response=$(curl -s -k -X GET "$OPNSENSE_URL/api/firewall/alias/getAliasUUID/$alias_id" -u "$API_KEY:$API_SECRET")
echo "$response" | jq -r '.uuid'
}
# Function to get the current value of the alias
get_alias_value() {
local uuid="$1"
response=$(curl -s -k -X GET "$OPNSENSE_URL/api/firewall/alias/get" -u "$API_KEY:$API_SECRET")
echo "$response" | jq -r --arg uuid "$uuid" '.alias.aliases.alias[$uuid].content | keys | .[0]'
}
# Function to set the alias value (forwarded port)
set_alias_value() {
local uuid="$1"
local forwarded_port="$2"
curl -s -k -X POST "$OPNSENSE_URL/api/firewall/alias/setItem/$uuid" -u "$API_KEY:$API_SECRET" \
-H "Content-Type: application/json" \
-d "{\"alias\": {\"content\": \"$forwarded_port\"}}" >/dev/null 2>&1
}
# Function to apply the changes - this takes a few seconds to update
apply_changes() {
curl -s -k -X POST "$OPNSENSE_URL/api/firewall/alias/reconfigure" -u "$API_KEY:$API_SECRET" -d '{}' >/dev/null 2>&1
}
# Initialize last public port variable
LAST_PUBLIC_PORT=1
# Get session id from qBittorrent
SID=$(login_torrent_client)
while true; do
#echo "Last public port was $LAST_PUBLIC_PORT."
# Retry NAT-PMP requests until UDP and TCP ports match
while true; do
UDP_PUBLIC_PORT=$(natpmpc -g "$VPN_GATEWAY" -a "$LAST_PUBLIC_PORT" 0 udp 60 | grep -o 'Mapped public port [0-9]*' | awk '{print $4}')
TCP_PUBLIC_PORT=$(natpmpc -g "$VPN_GATEWAY" -a "$LAST_PUBLIC_PORT" 0 tcp 60 | grep -o 'Mapped public port [0-9]*' | awk '{print $4}')
if [ -n "$UDP_PUBLIC_PORT" ] && [ -n "$TCP_PUBLIC_PORT" ] && [ "$UDP_PUBLIC_PORT" -eq "$TCP_PUBLIC_PORT" ]; then
PUBLIC_PORT="$UDP_PUBLIC_PORT"
break
else
echo "UDP ($UDP_PUBLIC_PORT) and TCP ($TCP_PUBLIC_PORT) ports differ or missing, retrying..."
sleep 1
fi
done
# Update the port in OPNsense if it's different from the last one
if [ "$PUBLIC_PORT" -ne "$LAST_PUBLIC_PORT" ]; then
LAST_PUBLIC_PORT="$PUBLIC_PORT"
ALIAS_UUID=$(get_alias_uuid "$FORWARDED_PORT_ALIAS_NAME")
CURRENT_PORT_VALUE=$(get_alias_value "$ALIAS_UUID")
if [ "$CURRENT_PORT_VALUE" != "$PUBLIC_PORT" ]; then
set_alias_value "$ALIAS_UUID" "$PUBLIC_PORT"
apply_changes
echo "OPNsense forwarded port alias changed."
fi
fi
# Update the torrent client port if needed
CURRENT_TORRENT_PORT=$(get_torrent_port "$SID")
if [ -z "$CURRENT_TORRENT_PORT" ]; then
SID=$(login_torrent_client) # Re-log if session has expired
CURRENT_TORRENT_PORT=$(get_torrent_port "$SID")
fi
if [ "$CURRENT_TORRENT_PORT" -ne "$PUBLIC_PORT" ]; then
set_torrent_port "$PUBLIC_PORT" "$SID"
echo "Updated torrent client port to $PUBLIC_PORT."
fi
# Wait 45 seconds before requesting another port forward - recommended by Proton VPN
sleep 45
done
cd /usr/local/bin
vi nat-pmp.sh
chmod +x /usr/local/bin/nat-pmp.sh
/usr/local/bin/nat-pmp.sh
#!/bin/sh
PROVIDE: nat_pmp
REQUIRE: NETWORKING
KEYWORD: shutdown
. /etc/rc.subr
name="nat_pmp"
rcvar="nat_pmp_enable"
command="/usr/local/bin/nat-pmp.sh"
pidfile="/var/run/${name}.pid"
start_cmd="${name}_start"
stop_cmd="${name}_stop"
nat_pmp_start() {
echo "Starting NAT-PMP script..."
/usr/sbin/daemon -p ${pidfile} -f ${command}
}
nat_pmp_stop() {
echo "Stopping NAT-PMP script..."
kill cat ${pidfile} && rm -f ${pidfile}
}
load_rc_config $name
run_rc_command "$1"
cd /usr/local/etc/rc.d
vi nat-pmp
chmod +x /usr/local/etc/rc.d/nat-pmp
echo "nat_pmp_enable=YES" >> /etc/rc.conf
service nat-pmp start
service nat-pmp stop
natpmpc -g 10.2.0.1 -a 1 0 udp 60
You should see output that lists the "Mapped public port" followed by a port number.