Private Internet Access Client (OpenVPN+Iptables+DNS over TLS on Alpine Linux)
Lightweight VPN client to tunnel to private internet access servers
| Image size | RAM usage | CPU usage |
|---|---|---|
| 19.8MB | 14MB to 80MB | Low to Medium |
Click to show base components
- Alpine 3.10 for a tiny image
- OpenVPN 2.4.7 to tunnel to PIA servers
- IPtables 1.8.3 enforces the container to communicate only through the VPN or with other containers in its virtual network (acts as a killswitch)
- Unbound 1.9.1 configured with Cloudflare's 1.1.1.1 DNS over TLS
- Files and blocking lists built periodically used with Unbound (see
BLOCK_MALICIOUSandBLOCK_NSAenvironment variables) - TinyProxy 1.10.0
Features
-
Configure everything with environment variables
- Destination region
- Internet protocol
- Level of encryption
- PIA Username and password
- DNS over TLS
- Malicious DNS blocking
- Internal firewall
- Web HTTP proxy
- Run openvpn without root
-
Connect other containers to it, see this
-
The iptables firewall allows traffic only with needed PIA servers (IP addresses, port, protocol) combinations
-
OpenVPN reconnects automatically on failure
-
Docker healthcheck pings the DNS 1.1.1.1 to verify the connection is up
-
Unbound DNS runs without root
-
OpenVPN can run without root but this disallows OpenVPN reconnecting, it can be set with
NONROOT=yes -
ARM compatible
-
Port forwarding
-
HTTP proxy for LAN devices
Setup
-
Requirements
- A Private Internet Access username and password - Sign up
- Firewall requirements
- Allow outbound TCP 853 to 1.1.1.1 to allow Unbound to resolve the PIA domain name at start. You can then block it once the container is started.
- For UDP strong encryption, allow outbound UDP 1197
- For UDP normal encryption, allow outbound UDP 1198
- For TCP strong encryption, allow outbound TCP 501
- For TCP normal encryption, allow outbound TCP 502
-
Ensure
/dev/net/tunis setup on your host with either:insmod /lib/modules/tun.ko # or... modprobe tun -
CLICK IF YOU HAVE AN ARM DEVICE
-
If you have a ARM 32 bit v6 architecture
docker build -t qmcgaw/private-internet-access \ --build-arg BASE_IMAGE=arm32v6/alpine \ https://github.com/qdm12/private-internet-access-docker.git -
If you have a ARM 32 bit v7 architecture
docker build -t qmcgaw/private-internet-access \ --build-arg BASE_IMAGE=arm32v7/alpine \ https://github.com/qdm12/private-internet-access-docker.git -
If you have a ARM 64 bit v8 architecture
docker build -t qmcgaw/private-internet-access \ --build-arg BASE_IMAGE=arm64v8/alpine \ https://github.com/qdm12/private-internet-access-docker.git
-
-
Launch the container with:
docker run -d --init --name=pia --cap-add=NET_ADMIN --device=/dev/net/tun \ -e REGION="CA Montreal" -e USER=js89ds7 -e PASSWORD=8fd9s239G \ qmcgaw/private-internet-accessor use docker-compose.yml with:
docker-compose up -dNote that you can change all the environment variables.
If you want to use the HTTP proxy, add
-p 8888:8888/tcpso that it is accessible from LAN devices.
Testing
Check the PIA IP address matches your expectations
docker run --rm --network=container:pia alpine:3.10 wget -qO- https://ipinfo.io
Environment variables
| Environment variable | Default | Description |
|---|---|---|
REGION |
CA Montreal |
One of the PIA regions |
PROTOCOL |
udp |
tcp or udp |
ENCRYPTION |
strong |
normal or strong |
USER |
Your PIA username | |
PASSWORD |
Your PIA password | |
NONROOT |
no |
Run OpenVPN without root, yes or no |
DOT |
on |
on or off, to activate DNS over TLS to 1.1.1.1 |
BLOCK_MALICIOUS |
off |
on or off, blocks malicious hostnames and IPs |
BLOCK_NSA |
off |
on or off, blocks NSA hostnames |
UNBLOCK |
comma separated string (i.e. web.com,web2.ca) to unblock hostnames |
|
FIREWALL |
on |
on or off, to switch the internal killswitch firewall (should be left on) |
EXTRA_SUBNETS |
comma separated subnets allowed in the container firewall (i.e. 192.168.1.0/24,192.168.10.121,10.0.0.5/28) |
|
PROXY |
on |
on or off, to switch the internal HTTP proxy |
PROXY_LOG_LEVEL |
Critical |
Info, Warning, Error or Critical |
PROXY_USER |
Username to use to connect to the HTTP proxy | |
PROXY_PASSWORD |
Passsword to use to connect to the HTTP proxy |
Connect to it
There are various ways to achieve this, depending on your use case.
-
Connect other containers to PIA
Add
--network=container:piawhen launching the container -
Connect containers from another docker-compose.yml
Add
network_mode: "container:pia"to your docker-compose.yml -
Connect containers in the same docker-compose.yml as PIA
Add
network_mode: "service:pia"to your docker-compose.yml (no need fordepends_on) -
Access ports of containers connected to PIA
To access port
8000of containerxyzand9000of containerabcconnected to PIA, you will need a reverse proxy such asqmcgaw/caddy-scratch(you can build it for ARM, see its readme)-
Create the file Caddyfile
touch Caddyfile chown 1000 Caddyfile # chown 1000 because caddy-scratch runs as user ID 1000 by default chmod 600 Caddyfilewith this content:
:8000 { proxy / xyz:8000 } :9000 { proxy / abc:9000 }You can of course make more complicated Caddyfile (such as proxying
/xyzto xyz:8000 and/abcto abc:9000, just ask me!) -
Run Caddy with
docker run -d -p 8000:8000/tcp -p 9000:9000/tcp \ --link pia:xyz --link pia:abc \ -v $(pwd)/Caddyfile:/Caddyfile:ro \ qmcgaw/caddy-scratchWARNING: Make sure the Docker network in which Caddy runs is the same as the one of PIA. It can be the default
bridgenetwork. -
You can now access xyz:8000 at localhost:8000 and abc:9000 at localhost:9000
For more containers, add more
--link pia:xxxand modify the Caddyfile accordinglyIf you want to user a docker-compose.yml, you can use this example - make sure PIA is launched and connected first:
version: '3' services: piaproxy: image: qmcgaw/caddy-scratch container_name: piaproxy ports: - 8000:8000/tcp - 9000:9000/tcp external_links: - pia:xyz - pia:abc volumes: - ./Caddyfile:/Caddyfile:ro abc: image: abc container_name: abc network_mode: "container:pia" xyz: image: xyz container_name: xyz network_mode: "container:pia" -
-
Access ports of containers connected to PIA, all in the same docker-compose.yml
To access port
8000of containerxyzand9000of containerabcconnected to PIA, you could use:version: '3' services: pia: image: qmcgaw/private-internet-access container_name: pia cap_add: - NET_ADMIN devices: - /dev/net/tun environment: - USER=js89ds7 - PASSWORD=8fd9s239G ports: - 8000:8000/tcp - 9000:9000/tcp abc: image: abc container_name: abc network_mode: "service:pia" xyz: image: xyz container_name: xyz network_mode: "service:pia" -
Access ports of containers connected to PIA, all in the same docker-compose.yml, using a reverse proxy
To access port
8000of containerxyzand9000of containerabcconnected to PIA, you will need a reverse proxy such asqmcgaw/caddy-scratch(you can build it for ARM, see its readme)-
Create the file Caddyfile
touch Caddyfile chown 1000 Caddyfile # chown 1000 because caddy-scratch runs as user ID 1000 by default chmod 600 Caddyfilewith this content:
:8000 { proxy / xyz:8000 } :9000 { proxy / abc:9000 }You can of course make more complicated Caddyfile (such as proxying
/xyzto xyz:8000 and/abcto abc:9000, just ask me!) -
Use this example:
version: '3' services: pia: image: qmcgaw/private-internet-access container_name: pia cap_add: - NET_ADMIN devices: - /dev/net/tun environment: - USER=js89ds7 - PASSWORD=8fd9s239G piaproxy: image: qmcgaw/caddy-scratch container_name: piaproxy ports: - 8000:8000/tcp - 9000:9000/tcp external_links: - pia:xyz - pia:abc volumes: - ./Caddyfile:/Caddyfile:ro abc: image: abc container_name: abc network_mode: "service:pia" xyz: image: xyz container_name: xyz network_mode: "service:pia"
-
-
Connect to the PIA through an HTTP proxy (i.e. with Chrome, Kodi, etc.)
-
Setup a HTTP proxy client, such as SwitchyOmega for Chrome
-
Make sure the PIA container:
- Has port 8888 published
-p 8888:8888/tcp - Has your LAN in
EXTRA_SUBNETS
- Has port 8888 published
-
With your HTTP proxy client, connect to the Docker host (i.e.
192.168.1.10) on port8888. You might need to enter your credentials if you set them with the environment variablesPROXY_USERandPROXY_PASSWORD. -
If you set
PROXY_LOG_LEVELtoInfo, you can check the log output of tinyproxy with:docker exec -it pia cat /var/log/tinyproxy/tinyproxy.logPROXY_LOG_LEVELdefaults toCriticalto avoid logging everything, for privacy purposes as well as to save storage.
-
Port forwarding
On a running PIA container, say pia, simply run:
docker exec -it pia /portforward.sh
And it will indicate you the port forwarded for your current public IP address.
Note that not all regions support port forwarding.
For the paranoids
-
You can review the code which essential consists in the Dockerfile and entrypoint.sh
-
Build the images yourself:
docker build -t qmcgaw/private-internet-access https://github.com/qdm12/private-internet-access-docker.git -
The download and unziping of PIA openvpn files is done at build for the ones not able to download the zip files
-
Checksums for PIA openvpn zip files are not used as these files change often (but HTTPS is used)
-
Use
-e ENCRYPTION=strong -e BLOCK_MALICIOUS=on -
DNS Leaks tests might not work because of this (TLDR: DNS server is a local caching intermediary)
TODOs
License
This repository is under an MIT license
