#!/bin/sh export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/root/bin # Private Internet Access Advanced Port Forward Script for pfSense # v1.04 (30th March 2015) # Written by Andy Fox # Pre-requisites for this script: # pfSense v2.1 (Port forward NAT return destination broken in earlier versions) # curl - pkg_add -r curl # xmlstarlet - pkg_add -r xmlstarlet # For pfSense v2.2, pkg_add has been replaced by pkg # Please run 'pkg', and follow prompts for installation. # Once pkg is installed, please run 'pkg update', followed by # pkg install curl # pkg install xmlstarlet # Fixed in this revision (v1.03) # - If VPN is down, we get a NULL return for the port number, but still # attempt to modify the port mapping in the config file. This results in # a blank config.xml being generated. # Fixed in this revision (v1.04) # Added -k to curl to bypass certificate checks # Add your PIA username and password USERNAME="username" PASSWORD="password" PIACLIENTID=/cf/conf/pia_client_id CONFFILE=/cf/conf/config.xml INTERFACE=ovpnc1 # Check to see if we have a valid PIA Client ID file. # If not, create one. Linux is included for illustration only. if [ ! -e $PIACLIENTID ]; then # OSX/FreeBSD (pfSense) head -n 100 /dev/urandom | md5 > $PIACLIENTID # Linux #head -n 100 /dev/urandom | md5sum | tr -d " -" > $PIACLIENTID logger "pia-port: Created new PIA Client ID." fi # Find out the tunnelling device for your VPN and get your IP address. # There are several options presented here. Personally, I prefer to use # the interface which I know relates to my VPN tunnel for forwarding. #DEVICE=`ifconfig | grep -o "tun[0-9]"` #LOCAL_IP=`ifconfig $DEVICE | grep -Po "(?<=addr.)[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*"` LOCAL_IP=`ifconfig $INTERFACE | grep "inet " | cut -d\ -f2` # Get the port number for the forwarded port PORT=`curl --insecure --interface $INTERFACE -d "user=$USERNAME&pass=$PASSWORD&client_id=$(cat $PIACLIENTID)&local_ip=$LOCAL_IP" https://www.privateinternetaccess.com/vpninfo/port_forward_assignment` # Capture return code from curl, for use in diagnosis later. RETURN=$? PORTNUM=`echo $PORT | grep -oE "[0-9]+"` # Some error detection. If PORTNUM is longer than 14 characters, we know that # an error has been returned. We log it to syslog, and exit. len=${#PORT} if [ $len -gt 14 ]; then logger "pia-port: $PORT" logger "pia-port: curl exit code: $RETURN" exit 0 fi # If port number is empty, then we know that VPN must be down, or that the # curl command to retrieve port has failed somehow. Log it, and exit. if [ $len -eq 0 ]; then logger "pia-port: No port returned from PIA." logger "pia-port: curl exit code: $RETURN" exit 0 fi #logger "pia-port: Port number acquired: $PORTNUM" # Get current NAT port number using xmlstarlet to parse the config file. CURPORT=`xml sel -t -v '//rule[descr="NAT Torrent"]/destination/port' $CONFFILE` #logger "pia-port: Current port forward: $CURPORT" # The port mapping doesn't always change. # We don't want to force pfSense to re-read it's config if we don't need to. if [ "$CURPORT" = "$PORTNUM" ]; then logger "pia-port: Current Port: $PORTNUM, PIA Port: $PORTNUM - Port not changed. Exiting." # Create the pia_port file, as it's possible it could have been # removed by external means. echo $PORTNUM > /usr/local/www/pia_port.txt exit 0 fi # Port forward has changed, so we update the rules in the config file. xml ed -u '//rule[descr="Torrent"]/destination/port' -v $PORTNUM -u '//rule[descr="Torrent"]/local-port' -v $PORTNUM -u '//rule[descr="NAT Torrent"]/destination/port' -v $PORTNUM $CONFFILE > /tmp/config.pia # Put the config file in the correct location. cp /tmp/config.pia $CONFFILE # Create a file in the pfSense web server root that contains the current port. # This can then be read by other hosts in order to update the open port in # whatever torrent client is in use. echo $PORTNUM > /usr/local/www/pia_port.txt # Force pfSense to re-read it's config rm /tmp/config.cache logger "pia-port: New port number ($PORTNUM) inserted into config file."