Port Forwarding Without The Application (Advanced Users)
You want it, we've got it. (i.e., ask and ye' shall receive)
If you are looking for port forwarding, the best bet is to use our application.
If you are a highly advanced user who wishes to enable port forwarding without using our state of the art application, you can still do so by utilizing our simple, SSL secured API interface:
Vars: user=username
pass=password
client_id=a random string that no one should be able to guess, use the same string every time
local_ip=the 10.x.x.x IP you get assigned after connecting to the VPN
Make client_id:
osx: head -n 100 /dev/urandom | md5 > ~/.pia_client_id
linux: head -n 100 /dev/urandom | md5sum > ~/.pia_client_id
EDIT: linux: head -n 100 /dev/urandom | md5sum | tr -d " -" > ~/.pia_client_id
(Thanks rcbarnes)
curl -d "user=USERNAME&pass=PASSWORD&client_id=$(cat ~/.pia_client_id)&local_ip=LOCAL_IP" https://www.privateinternetaccess.com/vpninfo/port_forward_assignment
RETURNS:
{ "port": 23423 }
You can easily make a script and call it in the 'up' section of the OpenVPN configuration. You will need to save the JSON output and act accordingly. Please remember that port forwarding to the router, for example, means that a port forward from the router to the device behind the router will be required. This is for highly advanced users.
Please make the call to this API at least once every hour.
Edit: Please make sure that the HTTPS call is routed out over the OpenVPN tunnel, if you're utilizing a setup where not all traffic is routed, you will need to write your code to properly route the call over the tunnel!

Comments
I will report back once I learn or verify anything.
# Detect (and save) your tunnel's IP address, needed for building the URL to POST
ifconfig tun11|grep -oE "inet addr: *10\.[0-9]+\.[0-9]+\.[0-9]+"|tr -d "a-z :"|tee /tmp/vpn_ip
# Detect (and save) your world-visible IP, assuming the default assignment of vlan2 for VPN tunnel #1
route -n|grep "vlan2$"|grep -v "0\.0\.0\.0"|cut -d " " -f 1|tee /tmp/vpn_external_ip
# Extract (and save) the port number returned from the POST curl command which is suggested in the original post (replace [...] with the command, of course)
[...] 2>/dev/null|grep -oE "[0-9]+"|tee /tmp/vpn_port_opened
head -n 100 /dev/urandom | md5sum | tr -d " -" > ~/.pia_client_id
instead of :
head -n 100 /dev/urandom | md5sum > ~/.pia_client_id
which creates a client ID string that's easier/safer to use than the one the original command wrote. Particularly, my string does not contain the unnecessary space and hyphen, which obviates the need to encode the space to make the URL standards-compliant, as well as preventing that space from turning an insufficiently quoted URL into separate strings (with possible havoc from the apparent argument created by starting the second string with '-'!) when read by the shell.
when i do this:
do shell script "curl -d 'user=USERNAMEpass=PASSWORD&client_id=$(cat ~/.pia_client_id)&local_ip=LOCAL_IP' https://www.privateinternetaccess.com/vpninfo/port_forward_assignment"
i get a port prompted in the applescript editor. it differs from running the command directly in the terminal
curl -d "user=USERNAMEpass=PASSWORD&client_id=$(cat ~/.pia_client_id)&local_ip=LOCAL_IP" https://www.privateinternetaccess.com/vpninfo/port_forward_assignment
i get the "correct" one. the problem is somehow with the quotation marks of the original curl command in the applescript editor
Single quotes will stop the shell from expanding the embedded commands/variables - they will be interpreted as literal strings instead.
set vpn_ip to do shell script "ifconfig tun0 | grep inet | awk '{print $2}'"
set vpn_port to do shell script "curl -d \"user=USERNAMEpass=PASSWORD&client_id=$(cat ~/.pia_client_id)&local_ip=" & vpn_ip & "\" https://www.privateinternetaccess.com/vpninfo/port_forward_assignment 2>/dev/null|grep -oE \"[0-9]+\""
do shell script "defaults delete org.m0k.transmission BindPort"
do shell script "defaults write org.m0k.transmission BindPort " & vpn_port & ""
set vpn_ip to do shell script "ifconfig tun0 | grep inet | awk '{print $2}'" # get current ip of vpn interface
do shell script "defaults delete org.m0k.transmission BindAddressIPv4" #delete previously written ipv4binding value
do shell script "defaults write org.m0k.transmission BindAddressIPv4 " & vpn_ip & "" #write current vpn ip into transmission.plist
and now added the port forwarding
put all that in a applescript, save as application and then use it to start transmission... voila
vpn ip & vpn port bound to transmission
Save this as an AppleScript and use Viscosity to run it when the VPN tunnel is established. Configure Transmission with "randomize port on launch" UNchecked and "automatically map port" checked in the Network preferences.
tell application "Growl" #getting growl to display the succeeful outcome
set the allNotificationsList to {"IPv4 Binding"}
set the enabledNotificationsList to {"IPv4 Binding"}
register as application "IPv4 Binding" all notifications allNotificationsList default notifications enabledNotificationsList
notify with name "IPv4 Binding" title "IPv4 Binding" description ip4binding application name ¬
"IPv4 Binding" icon of application "Transmission.app"
end tell
tell application "Growl" #getting growl to display the succeeful outcome
set the allNotificationsList to {"Port Binding"}
set the enabledNotificationsList to {"Port Binding"}
register as application "Port Binding" all notifications allNotificationsList default notifications enabledNotificationsList
notify with name "Port Binding" title "Port Binding" description vpn_port application name ¬
"Port Binding" icon of application "Transmission.app"
end tell
import random
import urllib
import socket
import string
user = '<username>'
pw = '<pw>'
local_ip = socket.gethostbyname(socket.gethostname())
pia_client_id = "".join( [random.choice(string.letters) for i in xrange(32)] )
data = {'user': user, 'pass': pw, 'client_id': pia_client_id, 'local_ip': local_ip }
params = urllib.urlencode(data)
answer = urllib.urlopen('https://www.privateinternetaccess.com/vpninfo/port_forward_assignment', params).read()
m = re.search('(\d+)', answer)
print m.group(1)
I know these are shared IPs, but is there any chance we could eventually make a handful of requests with a small set of randomizer strings (instead of only one such string per system now), in order to open up multiple port forwarding s? The unprivileged ports (or even just the high-numbered ports for short-lived connections) number in the tens of thousands so I hope they are not a scant resource. :-P
I ask because with only one, I have to select a single one of my desktop's services to enable at a time when on the VPN, but that means seeding and backing up my system are mutually exclusive, not to mention blocking inbound SSH connections (which I use very regularly) if I forget to switch back to that before I leave my apartment.
In short: the new port forwarding mechanism is awesome, thanks! (And can we eventually use it to open a few more application's inbound ports?)
To clarify, I am using openvpn on linux and connecting to the ca.privateinternetacess.com endpoint.