Connecting to PIA using WireGuard on OPNsense

So I’ve been using PIA (Private Internet Access) as my VPN provider since 2014, over the years you could say they’ve had a bit of a bumpy road but nothing has come to light showing them giving out any logs/details to authorities, plus this has been tested twice in court to my knowledge and TorrentFreak talk about the second case on their blog. I’ve always connected to PIA via OpenVPN, which has worked well for many years and offering their service on different ports has also allowed me to get around the odd captive portal.

I’ve been running my own router in form of pfsense and in recent years OPNsense with OpenVPN connection to PIA to tunnel certain traffic as and when required. In the last year PIA announced their WireGuard service allowing users to now connect to their NextGen VPN servers using this latest VPN technology. You can read about how WireGuard works on their website. Since WireGuard is more performant, due to having less overheads plus each OS will eventually have a kernel module allowing even better performance over OpenVPN, than it may already have, allowing higher speeds, lower latency. Linux already has a kernel module, FreeBSD is also now having one developed but currently its not mature enough to be included in the FreeBSD source code. OPNsense currently uses the WireGuard-go implementation which is fast enough for my needs as my internet connection is only 50/5 (VDSL line) but I know of people getting 500/30 speeds over WireGuard-go on OPNsense no problem. I have no doubt once the kernel module is ready for FreeBSD, OPNsense will put this in to replace the go version.

So how do we connect OPNsense to PIA’s NextGen WireGuard VPN servers? So some VPN providers its very simple, you give your VPN provider your WireGuard public key, and then return they give you the connection details you require to connect to their WireGuard servers. When your a small VPN company this is very simple, since they have 16,777,214 IPs in the 10.0.0.0/8 IP range to play with, so after you take away a few thousand addresses for their management networks, still give them a lot of IPs they can permanently assign to their WireGuard users. Assuming they have servers just for serving WireGuard clients, the total number of addresses would be even smaller if they’re running OpenVPN on the same servers. So when your the size of PIA and each server hosts multiple VPN protocols and users have multiple devices, the issue of getting close to running out of IPs, which becomes a bit of an issue, if they are globally unique IPs. So PIA uses a different strategy to connect to their servers via WireGuard, they have an API on each VPN server, which you then ask for WireGuard connection details from, that allows you to connect to that one and only server. If you want to connect to another server you repeat this process again for another VPN server.

Since connecting to PIA’s WireGuard servers require an API, you need their client or script to do this process for you. PIA provide a github repo that contains manual connection scripts, that allows you to connect to their WireGuard servers without needing the official PIA client. So what we need is a OPNsense version of this script, so it can handle the API part of the connection process.

Now after a lot of searching it was very apparent such a script didn’t exist for OPNsense. I decided I would look in to getting a script created so I can take advantage of their WireGuard servers and if anyone else was interested in using PIA on OPNsense using WireGuard they too could do it. After a weekend of reading through the manual connections, talking to PIA staff in their IRC channel, I made this following script (Linked Below). I’ve iterated the script a little bit since I did my first commit to make sure any bugs were ironed out, it’s now pretty robust, unless your trying to do something special with it. The github repo contains a README of how to setup the script. It will take care of maintaining the connection to PIA and rotate servers when required. Just give the script some connection details and a region it’ll take care of the rest.

Repo: https://github.com/FingerlessGlov3s/OPNsensePIAWireguard

Hopefully others will find this script useful and I will keep it updated as and when required, if you get any issues or errors setting up the script don’t hesitate to open up an issue.

32 thoughts on “Connecting to PIA using WireGuard on OPNsense

  1. Tried to follow the instructions on the GitHub but getting timeout errors when I run:

    PIAWireguard.py debug

    I have the following:
    OPNsense version 22.1.7_1
    Python version 3.8

    ———- MESSAGES START ———-
    Traceback (most recent call last):
    File “/usr/local/lib/python3.8/site-packages/urllib3/connection.py”, line 174, in _new_conn
    conn = connection.create_connection(
    File “/usr/local/lib/python3.8/site-packages/urllib3/util/connection.py”, line 95, in create_connection
    raise err
    File “/usr/local/lib/python3.8/site-packages/urllib3/util/connection.py”, line 85, in create_connection
    sock.connect(sa)
    TimeoutError: [Errno 60] Operation timed out

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
    File “/usr/local/lib/python3.8/site-packages/urllib3/connectionpool.py”, line 703, in urlopen
    httplib_response = self._make_request(
    File “/usr/local/lib/python3.8/site-packages/urllib3/connectionpool.py”, line 386, in _make_request
    self._validate_conn(conn)
    File “/usr/local/lib/python3.8/site-packages/urllib3/connectionpool.py”, line 1040, in _validate_conn
    conn.connect()
    File “/usr/local/lib/python3.8/site-packages/urllib3/connection.py”, line 358, in connect
    self.sock = conn = self._new_conn()
    File “/usr/local/lib/python3.8/site-packages/urllib3/connection.py”, line 186, in _new_conn
    raise NewConnectionError(
    urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 60] Operation timed out

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
    File “/usr/local/lib/python3.8/site-packages/requests/adapters.py”, line 440, in send
    resp = conn.urlopen(
    File “/usr/local/lib/python3.8/site-packages/urllib3/connectionpool.py”, line 785, in urlopen
    retries = retries.increment(
    File “/usr/local/lib/python3.8/site-packages/urllib3/util/retry.py”, line 592, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
    urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host=’127.0.0.1′, port=443): Max retries exceeded with url: /api/wireguard/server/searchServer/ (Caused by NewConnectionError(‘: Failed to establish a new connection: [Errno 60] Operation timed out’))

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
    File “/conf/PIAWireguard.py”, line 219, in
    r = requests.get(f'{opnsenseURL}/api/wireguard/server/searchServer/’, auth=(config[‘opnsenseKey’], config[‘opnsenseSecret’]), verify=urlVerify)
    File “/usr/local/lib/python3.8/site-packages/requests/api.py”, line 75, in get
    return request(‘get’, url, params=params, **kwargs)
    File “/usr/local/lib/python3.8/site-packages/requests/api.py”, line 61, in request
    return session.request(method=method, url=url, **kwargs)
    File “/usr/local/lib/python3.8/site-packages/requests/sessions.py”, line 529, in request
    resp = self.send(prep, **send_kwargs)
    File “/usr/local/lib/python3.8/site-packages/requests/sessions.py”, line 645, in send
    r = adapter.send(request, **kwargs)
    File “/usr/local/lib/python3.8/site-packages/requests/adapters.py”, line 519, in send
    raise ConnectionError(e, request=request)
    requests.exceptions.ConnectionError: HTTPSConnectionPool(host=’127.0.0.1′, port=443): Max retries exceeded with url: /api/wireguard/server/searchServer/ (Caused by NewConnectionError(‘: Failed to establish a new connection: [Errno 60] Operation timed out’))
    ———- MESSAGES END ———-

      1. That was it. I set the correct webui port number in the JSON config file and it worked like a charm!

        Now I have to get my firewall and NAT settings right.

        Thanks very much!

  2. Hi, thanks you for the script, i do not know how can i upload the script on the step 3, can you help me please?

  3. Hello, thank you for the hard work. It is much appreciated!
    While I did had this solution up and running last week, I cant seem to get working after upgrading OPNsense to 22.7.3_2. I’ve tried multiple servers, which all have the same results. I’m not sure out to resolve this cert issue (if that’s what this is). Please advise.
    Thanks again.

    GitHub Step 5:
    /conf/PIAWireguard.py debug

    Traceback (most recent call last):
    File “/usr/local/lib/python3.9/site-packages/urllib3/util/ssl_.py”, line 402, in ssl_wrap_socket
    context.load_verify_locations(ca_certs, ca_cert_dir, ca_cert_data)
    ssl.SSLError: [X509: NO_CERTIFICATE_OR_CRL_FOUND] no certificate or crl found (_ssl.c:4298)

    During handling of the above exception, another exception occurred:
    Traceback (most recent call last):
    File “/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py”, line 703, in urlopen
    httplib_response = self._make_request(
    File “/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py”, line 386, in _make_request
    self._validate_conn(conn)
    File “/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py”, line 1042, in _validate_conn
    conn.connect()
    File “/usr/local/lib/python3.9/site-packages/urllib3/connection.py”, line 414, in connect
    self.sock = ssl_wrap_socket(
    File “/usr/local/lib/python3.9/site-packages/urllib3/util/ssl_.py”, line 404, in ssl_wrap_socket
    raise SSLError(e)
    urllib3.exceptions.SSLError: [X509: NO_CERTIFICATE_OR_CRL_FOUND] no certificate or crl found (_ssl.c:4298)

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
    File “/usr/local/lib/python3.9/site-packages/requests/adapters.py”, line 489, in send
    resp = conn.urlopen(
    File “/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py”, line 787, in urlopen
    retries = retries.increment(
    File “/usr/local/lib/python3.9/site-packages/urllib3/util/retry.py”, line 592, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
    urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host=’vancouver412′, port=443): Max retries exceeded with url: /authv3/generateToken (Caused by SSLError(SSLError(136, ‘[X509: NO_CERTIFICATE_OR_CRL_FOUND] no certificate or crl found (_ssl.c:4298)’)))

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
    File “/conf/PIAWireguard.py”, line 551, in
    generateTokenResponse = requests.get(f”https://{piaMetaCn}/authv3/generateToken”, auth=(config[‘piaUsername’], config[‘piaPassword’]), verify=piaCA)
    File “/usr/local/lib/python3.9/site-packages/requests/api.py”, line 73, in get
    return request(“get”, url, params=params, **kwargs)
    File “/usr/local/lib/python3.9/site-packages/requests/api.py”, line 59, in request
    return session.request(method=method, url=url, **kwargs)
    File “/usr/local/lib/python3.9/site-packages/requests/sessions.py”, line 587, in request
    resp = self.send(prep, **send_kwargs)
    File “/usr/local/lib/python3.9/site-packages/requests/sessions.py”, line 701, in send
    r = adapter.send(request, **kwargs)
    File “/usr/local/lib/python3.9/site-packages/requests/adapters.py”, line 563, in send
    raise SSLError(e, request=request)
    requests.exceptions.SSLError: HTTPSConnectionPool(host=’vancouver412′, port=443): Max retries exceeded with url: /authv3/generateToken (Caused by SSLError(SSLError(136, ‘[X509: NO_CERTIFICATE_OR_CRL_FOUND] no certificate or crl found (_ssl.c:4298)’)))

    1. Hi Rich,

      Sounds like your missing some files, double check you’ve uploaded the `ca.rsa.4096.crt` which is used to verify the certificate of the VPN servers.

      1. Thank you for your response.
        Unfortunately, I do have the ca.rsa.4096.crt uploaded and still run into the same issue. I had purged everything and started over from the beginning but ended up in the same spot, with the error messages above.
        I am not sure how to to resolve this, so I am thinking about just standing up a new OPNsense device and starting from scratch. If that works, then maybe I can compare device until I find the difference which is causing this issue. Please let me know if you think of something else later on. I really do appreciate the work that you did on this, as I’m sure others do as well.
        Thanks again!

      1. Never mind, I found the issue. My OPNsense router config changed and I was using OpenDNS servers for resolution which were blocking the PIA URLs.

  4. Thank you for this mate.

    I would appreciate some help if possible.

    Step 5 when i run the 3rd command /conf/PIAWireguard.py debug i get the following output:

    root@OPNsense:~ # /conf/PIAWireguard.py debug
    Failed to import config file /conf/PIAWireguard.json

    any guidance will be much appreciated.

      1. Just wanted to let you know this helped a great deal. I have it up and running now. Thank You. Just need to figure out how to get port forwarding working now to a specific ip on my network. I tried the URL to get the forwarded port on your instructions but did not come up as a valid web address. Is there any work around you are aware of?

          1. Great thank you. Completely had forgotten my instance number was dif.

  5. Hi,

    On the final thing now which is configuring the port forwarding. There seems to be various guides out there which is quite confusing. (sorry totally new to Opnsense, come from a consumer grade router\firewall).

    Do you know of a working method I could follow step by step please?

    All I want is to forward the open wireguard ive been assigned to one internal IP. which is mycloud\plex server.

    Thanks again

  6. Hi,
    Networking noob here. I just installed opnsense and currently have everything set at default and want to route all traffic through PIA VPN. Will this script help me do this or do I also need to add PIA as a VPN client in opnsense? If I use the script, what rules do I setup if I want all traffic to go thru the VPN tunnel? Thanks in advance

    1. After following the installation readme, this will setup the VPN Client ready to be used. In your Firewall Rules, you will need to create rules that allow a client to connect to the internet, then you select the PIA gateway on the rule, instead of the default. You may also need to create your own outbound NAT rules.

      Hope this helps.

      1. I followed your instructions and they worked perfectly. Thanks again for your work. I also followed the instructions in the link at the the to setup a single rule to route all my LAN traffic through the tunnel. Apparently if I use the unbound DNS, there might be DNS leaks. A few potential solutions are mentioned, any advice you can provide as to how to setup one of these solutions (ideally the easiest one). Thanks

        1. If you don’t need local names to resolve for your network and only internet DNS names, just use the DHCP settings and tell the clients to go to Cloudflare/Google/Quad9 DNS directly. Then that traffic will just route out your VPN connection.

          1. Ok Thanks. Also how can i pause the VPN tunnel temporarily for testing purposes?

            Also i noticed recently that opnsense seems to be limiting my network speed to 100Mbps even when using ethernet connections, could that have something to do with the MTU changes i performed with the VPN?

          2. Hey,

            When you mean pause the VPN tunnel, you could disable the tunnel if that’s what you mean? Or do you mean to not send traffic over the tunnel temporarily?

            Your MSS changes should only be on the WireGuard interface and it wouldn’t be enough to make a 1000mbps become 100mbps, if you have turned on Netflow in the Reporting section, this has been known to cause bottlenecks on lower end hardware.

  7. By pausing, I mean temporarily being able to access the internet without the VPN so disabling the tunnel should work. Do I do this by disabling the rule I made in the last step?

    Netflow isn’t on and the hardware is sufficient so the must be something else wrong. I might try a fresh install. Thanks

    1. Yeah disabling the rule which tells that traffic to use the VPN is the easiest way and best way.

      If it is at exactly 100mbit, double check the link speed on the ports in “Interfaces: Overview” the “Media” bit will say the link speed information.

  8. Thanks a LOT for this script and excellent tutorial on your GitHub. I am by no means a beginner when it comes to networking, but I haven’t worked with VPNs at all and the IPSec tunnels I have experience with are CISCO SD-WAN exclusive…. soooo that was all new for me. Your guidance made it incredibly easy to just get it to work without any hassle at all.

    Thanks again for that.

  9. I follow the directions and it’s connecting, but I’m see interface out errors and it does not seem to be passing traffic.

  10. Thank you for the great script and guide. It worked flawlessly for me setting up my first WG interface. I then switched up the json to make another interface with a dedicated IP, and it appears to work except no traffic will go through the tunnel. The Dashboard shows 100% packet loss and I get dpinger sendto error 65 on the tunnel’s gateway. Despite the gateway and interface both receiving IPs as expected and seeing no errors from the script. (Status does, however, show Offline.)

    I think all my settings and rules are the same on both the working and nonworking interfaces; only difference is one has a dedicated IP. Is there an extra step where I need to account for the dedicated IP in Opnsense settings?

    1. I know there’s many people who’ve got multiple tunnels setup with this script, so I’m guessing there’s an issue with your DIP? Can you trying changing it to a non DIP tunnel, to see if it then works. Could be an issue with the server your DIP is on maybe.

  11. Followed this today and it worked perfectly thanks sooooo much for saving me loads of time!

Leave a Reply to Muz Cancel reply

Your email address will not be published. Required fields are marked *