Netgate Discussion Forum
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Search
    • Register
    • Login

    PIA automatic port-forward update for Transmission daemon

    Scheduled Pinned Locked Moved NAT
    9 Posts 4 Posters 3.7k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • H
      HolyK
      last edited by HolyK

      Hi!

      This guide explains how to remotely update port number in Transmission daemon if PIA VPN restarts with new assigned forwarding port.

      I know that there is already a similar thread but it is quite old with several updates and variants of the solution. So i am sharing mine which takes slightly different approach. Yet main credits goes to original script author which i used as starting point.

      #Key points:
      -Working with pfSense 2.4.4-RELEASE-p3 + Transmission-daemon 2.94_3
      -Automatic NAT rule update upon PIA forward port change
      -Immediate port update of Transmission daemon without restart
      -pfSense updates the port remotely instead of transmission "fetching" it. No CRON job needed, no delay.
      -Key-based SSH connections.

      #Future ideas:
      -Email notifications in case of failure
      -Force-restart OpenVPN in case the the port gets closed from PIA side unnoticed (i never had such issue but few people faced such behavior)

      Hope this helps ... so here we go :]


      You need to have PIA VPN already configured. Use for example this guide

      I. pfSense side
      1.Enable SSH on pfSense
      System -> Advanced => tick "Enable Secure Shell"
      85d1ae08-9dc4-4986-8338-111ad412a287-image.png

      2.Create custom user
      Go to System -> User manager -> Add
      -Fill Username, password
      -Add "admins" group
      -Grant "WebCfg - All pages" and "User - System: Shell account access" priviledges
      -(Optional) generate SSH keys for your custom user
      3ccb6e6e-be2d-45c9-bd6e-f7af46d5f38d-image.png

      3.Install SUDO package
      -Go to System -> Package Manager => install SUDO package
      -Go to System -> sudo => create user permissions as bellow
      0a59318d-1fac-4b69-9cef-8a0dd38611d6-image.png

      4.Create Alias for port forward
      -Go to Firewall -> Aliases -> Ports
      -Create new port with name "Trans_Port"
      -Give it the current port (if you have it) or non-zero value
      b1685292-073a-4563-b9e7-91977e217a1c-image.png

      5.Create Alias for Transmission IP address
      -Go to Firewall -> Aliases -> IP
      -Create new port with name "Trans_IP"
      -Define IP or FQDN of your Transmisson daemon server
      89068de1-54a7-4475-9590-60848ddccfb4-image.png

      6.Create NAT rule for port-forward using the ALIAS instead of specific port/IP
      -Go to Firewall -> NAT
      -Create new rule like bellow (blue values could be different depending on your current VPN configuration)
      b6b5539f-1172-44db-bf8d-81c2e76bb7a3-image.png

      7.Generate SSH keys for enhanced security
      -SSH to the pfSense box with the user created in step 2.

      sudo su -
      #<enter your user password>
      #Enter an option: 8 for shell
      mkdir .ssh
      chmod 700 .ssh
      cd .ssh
      ssh-keygen -b 4096 -f ~/.ssh/id_rsa
      #When prompted for "Enter passphrase" just hit ENTER twice
      #Files id_rsa and id_rsa.pub will be generated.
      cat id_rsa.pub
      #Store the content of the file somewhere as it will be required later on
      

      8.Create custom devd config file
      -Still under root user from previous step do

      mkdir /usr/local/etc/devd
      cd /usr/local/etc/devd
      vi piaport.conf
      

      -paste following code and save ( :wq )

      notify 0 {
              match "system"          "IFNET";
              match "subsystem"       "(ovpnc1)";
              match "type"            "LINK_UP";
              action "logger $subsystem is UP";
              action "/home/custom/piaportforward/piaportfwd.sh";
      };
      
      notify 0 {
              match "system"          "IFNET";
              match "subsystem"       "(ovpnc1)";
              match "type"            "LINK_DOWN";
              action "logger $subsystem is DOWN";
      };
      

      Note: The "ovpnc1" is a technical name of the OpenVPN interface from within the pfSense UI
      f5515897-bed2-4718-bfbd-4093ab56c8a9-image.png

      9.Create the custom port-update script
      -Still under root user from previous step do

      mkdir /home/custom
      mkdir /home/custom/piaportforward
      cd /home/custom/piaportforward
      touch piaportfwd.sh
      chmod u+x piaportfwd.sh
      vi piaportfwd.sh
      

      -Paste the code bellow OR just unzip/upload the file [ piaportfwd.zip ] and chmod +x it.
      -No customization is necessary. Everything is fetched from config file.

      #!/bin/sh
      export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/root/bin
      
      # Vers: 1.2
      # Date: 1.2.2020
      
      ############# Update following if necessary ################
      
      # OpenVPN interface name
      OVPNIFACE='ovpnc1'
      
      # Alias name for port forward
      PORTALIAS='Trans_Port'
      
      # Alias name for Transmission IP
      IPALIAS='Trans_IP'
      
      # Email notification
      # TBD !!!
      
      ############## Other vars - do not touch ###################
      
      # pfSense config file and tempconfig location
      CONFFILE='/cf/conf/config.xml'
      TMPCONFFILE='/tmp/tmpconfig.xml'
      
      # Fetch remote Transmission IP from config
      TRANSIP=`xml sel -t -v "//alias[name=\"$IPALIAS\"]/address" $CONFFILE`
      
      ######################## MAIN CODE #########################
      
      # Wait for VPN interface to get fully UP
      sleep 10
      
      # Get the Unique ID of the client system
      # More details at: https://www.privateinternetaccess.com/forum/discussion/23431/new-pia-port-forwarding-api
      PIA_CLIENTID=`head -n 100 /dev/urandom | shasum -a 256 | tr -d " -"`
      logger "[PIA] Client-ID: $PIA_CLIENTID"
      
      # Request port-forward from PIA API
      PIA_PORT=$(curl --interface $OVPNIFACE "http://209.222.18.222:2000/\?client_id=$PIA_CLIENTID" 2>/dev/null | awk -F ':' '{ print $2 }'| awk -F '}' '{ print $1 }')
      
      if [ "$PIA_PORT" == "" ]; then
          PIA_PORT='0'
          logger "[PIA] Port forwarding is already activated on this connection, has expired, or you are not connected to a PIA region that supports port forwarding."
          exit 0
      elif ! [ "$PIA_PORT" -eq "$PIA_PORT" ] 2> /dev/null; then
          logger "[PIA] Fatal error! Value $PIA_PORT is not a number. PIA API has most probably changed. Manual check necessary."
          # EMAIL
          exit 1
      elif [ "$PIA_PORT" -lt 1024 ] || [ "$PIA_PORT" -gt 65535  ]; then
          logger "[PIA] Fatal error! Value $PIA_PORT outside allowed port range. PIA API has most probably changed. Manual check necessary."
          # EMAIL
          exit 1
      fi
      logger "[PIA] Acquired forwarding port: $PIA_PORT"
      
      # Get current NAT port number using xmlstarlet to parse the config file.
      NATPORT=`xml sel -t -v "//alias[name=\"$PORTALIAS\"]/address" $CONFFILE`
      logger "[PIA] Current NAT rule port: $NATPORT"
      
      # If the acquired port is the same as already configured do not pointlessly reload config.
      if [ "$NATPORT" -eq "$PIA_PORT" ]; then
      	logger "[PIA] Acquired port $PIA_PORT equals the already configured port $NATPORT - no action required."
      	exit 0
      fi
      
      # If the port has changed update the tempconfig file.
      xml ed -u "//alias[name=\"$PORTALIAS\"]/address" -v $PIA_PORT $CONFFILE > $TMPCONFFILE
      
      # Validate the XML file just to ensure we don't nuke whole configuration
      xml val -q $TMPCONFFILE
      XMLVAL=$?
      if [ "$XMLVAL" -eq 1 ]; then
      	logger "[PIA] Fatal error! Updated tempconf file $TMPCONFFILE does not have valid XML format. Verify that the port alias is correct in script header and exists in pfSense Alias list"
        # EMAIL
      	exit 1
      fi
      
      # If the updated tempconfig is valid backup and replace the real config file.
      cp $CONFFILE ${CONFFILE}.bck 
      cp $TMPCONFFILE $CONFFILE
      
      # Force pfSense to re-read it's config and reload the rules.
      rm /tmp/config.cache
      /etc/rc.filter_configure
      logger "[PIA] New port $PIA_PORT udpated in pfSense config file."
      
      # Check if Transmission host is reachable
      ping -c1 -t1 -q $TRANSIP
      PINGRC=$?
      if [ "$PINGRC" -gt 0  ]; then
      	logger "[PIA] Error! Transmission host $TRANSIP is not reachable!"
        # EMAIL
      	exit 1
      fi
      
      # Update remote Transmission config with new port
      ssh transmission@${TRANSIP} "./transportupdate.sh ${PIA_PORT}"
      TRANSRC=$?
      
      if [ "$TRANSRC" -gt 0  ]; then
      	logger "[PIA] Error! Unable to remotely update Transmission port over SSH!"
        # EMAIL
      	exit 1
      fi
      logger "[PIA] New port successfully updated in remote Transmission system."
      
      exit 0
      
      

      -Disconnect form pfSense
      -(Optional) Disable SSH via WebUI under System -> Advanced => un-tick "Enable Secure Shell"

      II. Transmission host side
      -This part is for FreeBSD host systems but it will work just fine for any Linux host.
      -If there is something already configured on your side please read the steps anyway just to be sure there are no tiny difference.

      1.Enable SSH daemon
      -This is mainly for FreeBSD hosts (like FreeNAS) with Jails
      -Access the Jail via jexec and add following lines to /etc/rc.conf

      # Enable SSHd
      sshd_enable="YES"
      

      -Start sshd service by service sshd start

      2.Secure Transmission RPC Protocol
      -This is optional but recommended for security purpose
      -STOP the transmission daemon by service transmission stop
      -Edit /usr/local/etc/transmission/settings.json
      -Note that the location of settings.json may vary. The above path is from FeeBSD port.
      -Update/add following parameters. Replace username, password. Ensure that IP address of your pfSense is in whitelist.

      "rpc-authentication-required": true,
      "rpc-username": "SomeUserName",
      "rpc-password": "SomePassword",
      "rpc-whitelist": "127.0.0.1,10.10.10.1,10.10.10.5",
      

      -Start the transmission again service transmission start

      3.Create local port-update script
      -This needs to be done under transmission user, not as root!

      su - transmission
      touch ~/transportupdate.sh
      chmod u+x ~/transportupdate.sh
      vi ~/transportupdate.sh
      

      -Paste the code bellow OR just unzip/upload the file [ transportupdate.zip ] and chmod +x it.
      -UPDATE the USERNAME='username' and PASSWORD='password' at the beginning of the file as per the credentials configured in step II.2.

      #!/bin/sh
      export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/root/bin
      
      # Vers: 1.2
      # Date: 1.2.2020
      
      ############ Update these please #############
      
      # Transmission-remote WEB credentials
      USERNAME='username'
      PASSWORD='password'
      
      # Transmission-remote binary is usually under known environment location.
      # Validate the command is known by " which transmission-remote "
      # If the "transmission-remote" is not known try to " find / -name transmission-remote "
      # Then update the variable bellow with full-path to the binary
      TRANSREMOTE='transmission-remote'
      #TRANSREMOTE='/usr/local/bin/transmission-remote'
      
      ############ Rest of the code - do not touch #############
      
      # Port numbers
      NEWPORT="$1"
      
      # Verify that received new port is a valid number.
      if ! [ "$NEWPORT" -eq "$NEWPORT" ] 2> /dev/null; then
          logger "Non-numeric port ( $NEWPORT ) received from remote host. Aborting!"
          # EMAIL
          exit 1
      fi
      
      # Check if Transmission is running
      service transmission status
      TRANSSVCRC=$?
      if [ "$TRANSSVCRC" -gt 0  ]; then
        logger "Transmission service is not running. Port update aborted!"
        # EMAIL
      	exit 1
      else
        # Configure new port received from remote system
        $TRANSREMOTE --auth ${USERNAME}:${PASSWORD} -p ${NEWPORT}
        TRANSREMOTERC=$?
        if [ "$TRANSREMOTERC" -gt 0  ]; then
          logger "Error when calling transmission-remote binary. Port was NOT updated!"
          # EMAIL
      	 exit 1
        fi
        logger "Transmission port succesfully updated. New port is: ${NEWPORT}"
        exit 0
      fi
      
      

      4.Create/Upload public SSH key for pfSense connection
      -Still under transmission user

      mkdir ~/.ssh
      chmod 700 ~/.ssh
      cd ~/.ssh
      touch authorized_keys
      chmod 644 authorized_keys
      vi authorized_keys
      

      -Paste the content of id_rsa.pub generated in step I.7. and save ( :wq )

      5.Restart OpenVPN in pfSense
      3b0aef62-fca2-49c2-9520-d3c42e255eb0-image.png
      .
      -Wait for ~15secs and check Status -> System logs to see results
      da84729f-af4c-49ca-a2f7-c44125e731b2-image.png
      .
      -All OK, port changed
      f5fc0214-eca5-473a-a049-c87f1c0783f0-image.png
      .
      DONE !! :]

      1 Reply Last reply Reply Quote 0
      • D
        dl_sdk
        last edited by

        This is a great writeup - thanks for the effort you put into it.

        I'm getting an error with piaportfwd.sh and the xml command within it (I ran the command directly on the command line to show the problem with xml not being found);

        [2.4.5-RELEASE][root@router]: xml sel -t -v "//alias[name='Trans_IP'']/address" /cf/conf/config.xml
        xml: Command not found.
        

        Do you have any ideas on how to address this?

        H 1 Reply Last reply Reply Quote 0
        • H
          HolyK @dl_sdk
          last edited by HolyK

          @dl_sdk Hi! Thanks, i did not realized the xmlstarlet is not pfSense default package. I guess I've installed it manually some time ago. Anyway the fix is simple:

          pkg install xmlstarlet
          

          Then relog your session and the script should work for you just fine.

          I'll update the guide above as well.
          //I can't do that "Error. You are only allowed to edit posts for 3600 second(s) after posting".

          1 Reply Last reply Reply Quote 0
          • D
            dl_sdk
            last edited by

            pkg install xmlstarlet worked!

            That lead to [PIA] Error! Unable to remotely update Transmission port over SSH! in the logs.

            I could ssh from pfsense to the transmission host without issue. On the transmission host, calling the transport update script, got;

            transmission@404-03:~$ ./transportupdate.sh 59227
            Unit transmission.service could not be found.
            

            Once I changed the service status to the below, things worked nicely.

            # Check if Transmission is running
            service transmission-daemon status
            

            Thank you once more for this.

            H 1 Reply Last reply Reply Quote 0
            • H
              HolyK @dl_sdk
              last edited by

              @dl_sdk Hey, glad you figured it out. Yes the service name vary across distros/packages. In my case it is just a pkg inside BSD Jail. I could change that to get the real service name from service -e output but then it might fail on the service command itself.
              Anyway thanks for posting the command which works for you so other ppl can adjust the script if necessary.

              F 1 Reply Last reply Reply Quote 0
              • F
                fm808
                last edited by

                if you have issues parsing the port from the API: i have noticed sometimes the query to the api is too fast, i added a sleep 15 before the API call in the script and now it seems to pull the port more reliably:

                # Get the Unique ID of the client system
                # More details at: https://www.privateinternetaccess.com/forum/discussion/23431>
                PIA_CLIENTID=`head -n 100 /dev/urandom | shasum -a 256 | tr -d " -"`
                logger "[PIA] Client-ID: $PIA_CLIENTID"
                
                sleep 15
                
                # Request port-forward from PIA API
                PIA_PORT=$(curl --interface $OVPNIFACE "http://209.222.18.222:2000/\?client_id=>
                
                
                

                hope it helps!

                1 Reply Last reply Reply Quote 0
                • A
                  Apocracy
                  last edited by

                  Seems to no longer be working with the move to "Next Gen" servers?

                  F 1 Reply Last reply Reply Quote 0
                  • F
                    fm808 @Apocracy
                    last edited by

                    @Apocracy no it works, i am using it right now, i think right now only 2 or 3 servers support port forwarding, Canada and Germany don't work, they said they are working on a fix

                    1 Reply Last reply Reply Quote 0
                    • F
                      fm808 @HolyK
                      last edited by fm808

                      This post is deleted!
                      1 Reply Last reply Reply Quote 0
                      • First post
                        Last post
                      Copyright 2025 Rubicon Communications LLC (Netgate). All rights reserved.