Support Mullvad over openvpn (#85)

Additional changes:
- Allow empty value for PIA region
- Most settings are lowercased
- `OPENVPN_VERBOSITY` environment variable
- openvpn also tunnels IPv6, and unbound supports ipv6
- auth kept only on disk, not in memory
- readme reworked
- CI script fixed and improved
- Added v2 Docker tag
- Shadowsocks log defaults to `off`
This commit is contained in:
Quentin McGaw
2020-02-29 21:05:20 -05:00
committed by GitHub
33 changed files with 1351 additions and 245 deletions

View File

@@ -7,8 +7,8 @@ WORKDIR /tmp/gobuild
ENV CGO_ENABLED=0
COPY go.mod go.sum ./
RUN go mod download 2>&1
COPY internal/ ./internal/
COPY cmd/main.go .
COPY internal/ ./internal/
RUN go test ./...
RUN go build -ldflags="-s -w" -o entrypoint main.go
@@ -29,11 +29,23 @@ LABEL \
org.opencontainers.image.source="https://github.com/qdm12/private-internet-access-docker" \
org.opencontainers.image.title="PIA client" \
org.opencontainers.image.description="VPN client to tunnel to private internet access servers using OpenVPN, IPtables, DNS over TLS and Alpine Linux"
ENV USER= \
PASSWORD= \
ENCRYPTION=strong \
ENV VPNSP=pia \
USER= \
PROTOCOL=udp \
OPENVPN_VERBOSITY=1 \
TZ= \
# PIA only
PASSWORD= \
REGION="CA Montreal" \
ENCRYPTION=strong \
PORT_FORWARDING=off \
PORT_FORWARDING_STATUS_FILE="/forwarded_port" \
# Mullvad only
COUNTRY=Sweden \
CITY= \
ISP= \
PORT= \
# DNS over TLS
DOT=on \
DOT_PROVIDERS=cloudflare \
DOT_PRIVATE_ADDRESS=127.0.0.1/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,::1/128,fc00::/7,fe80::/10,::ffff:0:0/96 \
@@ -45,19 +57,19 @@ ENV USER= \
BLOCK_SURVEILLANCE=off \
BLOCK_ADS=off \
UNBLOCK= \
# Firewall
EXTRA_SUBNETS= \
PORT_FORWARDING=off \
PORT_FORWARDING_STATUS_FILE="/forwarded_port" \
# Tinyproxy
TINYPROXY=off \
TINYPROXY_LOG=Info \
TINYPROXY_PORT=8888 \
TINYPROXY_USER= \
TINYPROXY_PASSWORD= \
# Shadowsocks
SHADOWSOCKS=off \
SHADOWSOCKS_LOG=on \
SHADOWSOCKS_LOG=off \
SHADOWSOCKS_PORT=8388 \
SHADOWSOCKS_PASSWORD= \
TZ=
SHADOWSOCKS_PASSWORD=
ENTRYPOINT /entrypoint
EXPOSE 8888/tcp 8388/tcp 8388/udp
HEALTHCHECK --interval=3m --timeout=3s --start-period=20s --retries=1 CMD /entrypoint healthcheck

267
README.md
View File

@@ -1,8 +1,8 @@
# Private Internet Access Client
*Lightweight swiss-knife-like VPN client to tunnel to private internet access servers, using OpenVPN, iptables, DNS over TLS, ShadowSocks, Tinyproxy and more*
*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access or Mullvad VPN servers, using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and Tinyproxy*
**ANNOUCEMENT**: *Total rewrite in Go: see the new features [below](#Features)* (in case something break use the image with tag `:old`)
**ANNOUCEMENT**: *Support for [Mullvad](http://mullvad.net)*
<a href="https://hub.docker.com/r/qmcgaw/private-internet-access">
<img width="100%" height="320" src="https://raw.githubusercontent.com/qdm12/private-internet-access-docker/master/title.svg?sanitize=true">
@@ -34,59 +34,60 @@
## Features
- **New features**
- Choice to block ads, malicious and surveillance at the DNS level
- All program output streams are merged (openvpn, unbound, shadowsocks, tinyproxy, etc.)
- Choice of DNS over TLS provider(s)
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
- Download block lists and cryptographic files at start instead of at build time
- Can work as a Kubernetes sidecar container, thanks @rorph
- Pick a random region if no region is given, thanks @rorph
- <details><summary>Configure everything with environment variables</summary><p>
- [Destination region](https://www.privateinternetaccess.com/pages/network)
- Internet protocol
- Level of encryption
- PIA Username and password
- DNS over TLS
- DNS blocking: ads, malicious, surveillance
- Internal firewall
- Socks5 proxy
- Web HTTP proxy
</p></details>
- Connect
- [Other containers to it](https://github.com/qdm12/private-internet-access-docker#connect-to-it)
- [LAN devices to it](https://github.com/qdm12/private-internet-access-docker#connect-to-it)
- Killswitch using *iptables* to allow traffic only with needed PIA servers and LAN devices
- Port forwarding
- Based on Alpine 3.11 for a small Docker image below 50MB
- Supports **Private Internet Access** and **Mullvad** servers
- DNS over TLS baked in with service provider(s) of your choice
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses
- Choose the vpn network protocol, `udp` or `tcp`
- Built in firewall kill switch to allow traffic only with needed PIA servers and LAN devices
- Built in SOCKS5 proxy (Shadowsocks, tunnels TCP+UDP)
- Built in HTTP proxy (Tinyproxy, tunnels TCP)
- [Connect other containers to it](https://github.com/qdm12/private-internet-access-docker#connect-to-it)
- [Connect LAN devices to it](https://github.com/qdm12/private-internet-access-docker#connect-to-it)
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, ppc64le and even that s390x 🎆
- Sub programs drop root privileges once launched: Openvpn, Unbound, Shadowsocks, Tinyproxy
### Private Internet Access
- Pick the [region](https://www.privateinternetaccess.com/pages/network/)
- Pick the level of encryption
- Enable port forwarding
### Mullvad
- Pick the [country, city and ISP](https://mullvad.net/en/servers/#openvpn)
- Pick the port to use (i.e. `53` (udp) or `80` (tcp))
### Extra niche features
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
- Subprograms all drop root privileges once launched
- Subprograms output streams are all merged together
- Can work as a Kubernetes sidecar container, thanks @rorph
## Setup
1. <details><summary>Requirements</summary><p>
- A Private Internet Access **username** and **password** - [Sign up](https://www.privateinternetaccess.com/pages/buy-vpn/)
- Docker API 1.25 to support `init`
- If you use Docker Compose, docker-compose >= 1.22.0, to support `init: true`
1. Requirements
- Docker 1.13, in order to have Docker API 1.25 which supports `init` (and, if you use docker-compose, docker-compose version 1.22.0)
- A Private Internet Access **username** and **password** ([sign up](https://www.privateinternetaccess.com/pages/buy-vpn/)) or Mullvad user ID ([sign up](https://mullvad.net/en/account/))
- <details><summary>External firewall requirements, if you have one</summary><p>
- At start only
- Allow outbound TCP 443 to github.com and privateinternetaccess.com
- If `DOT=on`, allow outbound TCP 853 to 1.1.1.1 to allow Unbound to resolve the PIA domain name.
- If `DOT=off`, allow outbound UDP 53 to your DNS provider to resolve the PIA domain name.
- For UDP strong encryption, allow outbound UDP 1197 to the corresponding VPN server IPs
- For UDP normal encryption, allow outbound UDP 1198 to the corresponding VPN server IPs
- For TCP strong encryption, allow outbound TCP 501 to the corresponding VPN server IPs
- For TCP normal encryption, allow outbound TCP 502 to the corresponding VPN server IPs
- Allow outbound TCP 443 to github.com
- If `DOT=on`, allow outbound TCP 853 to allow Unbound to resolve github.com and the PIA subdomain name if you use PIA.
- If `DOT=off` and `VPNSP=pia`, allow outbound UDP 53 to your DNS provider to resolve the PIA subdomain name.
- If `VPNSP=pia`, `ENCRYPTION=strong` and `PROTOCOL=udp`: allow outbound UDP 1197 to the corresponding VPN server IPs
- If `VPNSP=pia`, `ENCRYPTION=normal` and `PROTOCOL=udp`: allow outbound UDP 1198 to the corresponding VPN server IPs
- If `VPNSP=pia`, `ENCRYPTION=strong` and `PROTOCOL=tcp`: allow outbound TCP 501 to the corresponding VPN server IPs
- If `VPNSP=pia`, `ENCRYPTION=normal` and `PROTOCOL=tcp`: allow outbound TCP 502 to the corresponding VPN server IPs
- If `VPNSP=mullvad` and `PORT=`, please refer to the mapping of Mullvad servers in [these source code lines](https://github.com/qdm12/private-internet-access-docker/blob/master/internal/constants/mullvad.go#L64-L667) to find the corresponding UDP port number and IP address(es) of your choice
- If `VPNSP=mullvad` and `PORT=53`, allow outbound UDP 53 to the corresponding VPN server IPs, which you can fine in [the mapping of Mullvad servers](https://github.com/qdm12/private-internet-access-docker/blob/master/internal/constants/mullvad.go#L64-L667)
- If `VPNSP=mullvad` and `PORT=80`, allow outbound TCP 80 to the corresponding VPN server IPs, which you can fine in [the mapping of Mullvad servers](https://github.com/qdm12/private-internet-access-docker/blob/master/internal/constants/mullvad.go#L64-L667)
- If `VPNSP=mullvad` and `PORT=443`, allow outbound TCP 443 to the corresponding VPN server IPs, which you can fine in [the mapping of Mullvad servers](https://github.com/qdm12/private-internet-access-docker/blob/master/internal/constants/mullvad.go#L64-L667)
- If `SHADOWSOCKS=on`, allow inbound TCP 8388 and UDP 8388 from your LAN
- If `TINYPROXY=on`, allow inbound TCP 8888 from your LAN
</p></details>
</p></details>
1. Launch the container with:
```bash
@@ -102,29 +103,38 @@
```
Note that you can:
- Change the many [environment variables](#environment-variables) available
- Use `-p 8888:8888/tcp` to access the HTTP web proxy (and put your LAN in `EXTRA_SUBNETS` environment variable)
- Use `-p 8388:8388/tcp -p 8388:8388/udp` to access the SOCKS5 proxy (and put your LAN in `EXTRA_SUBNETS` environment variable)
- Pass additional arguments to *openvpn* using Docker's command function (commands after the image name)
1. You can update the image with `docker pull qmcgaw/private-internet-access:latest`. There are also docker tags available:
- `qmcgaw/private-internet-access:v1` linked to the [v1 release](https://github.com/qdm12/private-internet-access-docker/releases/tag/v1.0)
1. You can update the image with `docker pull qmcgaw/private-internet-access:latest`. There are also docker tags for older versions available:
- `qmcgaw/private-internet-access:v2` linked to the [v2 release](https://github.com/qdm12/private-internet-access-docker/releases/tag/v2.0) (Golang based, only PIA)
- `qmcgaw/private-internet-access:v1` linked to the [v1 release](https://github.com/qdm12/private-internet-access-docker/releases/tag/v1.0) (shell scripting based, no support, only PIA)
- `qmcgaw/private-internet-access:old` tag, which is the latest shell scripting version (shell scripting based, no support, only PIA)
## Testing
Check the PIA IP address matches your expectations
```sh
docker run --rm --network=container:pia alpine:3.10 wget -qO- https://ipinfo.io
docker run --rm --network=container:pia alpine:3.11 wget -qO- https://ipinfo.io
```
## Environment variables
| Environment variable | Default | Description |
| --- | --- | --- |
| `REGION` | `CA Montreal` | One of the [PIA regions](https://www.privateinternetaccess.com/pages/network/) |
| `VPNSP` | `pia` | VPN Service Provider, one of `pia`, `mullvad` |
| `REGION` | `CA Montreal` | (PIA only) one of the [PIA regions](https://www.privateinternetaccess.com/pages/network/) |
| `COUNTRY` | `Sweden` | (Mullvad only) one of the [Mullvad countries](https://mullvad.net/en/servers/#openvpn) |
| `CITY` | | (Mullvad only, *optional*) one of the [Mullvad cities](https://mullvad.net/en/servers/#openvpn) |
| `ISP` | | (Mullvad only, *optional*) one of the [Mullvad ISP](https://mullvad.net/en/servers/#openvpn) |
| `PORT` | | (Mullvad only, *optional*) For TCP, `80` or `443`, or `53` for UDP. Leave blank for default Mullvad server port |
| `PROTOCOL` | `udp` | `tcp` or `udp` |
| `ENCRYPTION` | `strong` | `normal` or `strong` |
| `USER` | | Your PIA username |
| `ENCRYPTION` | `strong` | (PIA only) `normal` or `strong` |
| `USER` | | PIA username **or** Mullvad user ID |
| `PASSWORD` | | Your PIA password |
| `DOT` | `on` | `on` or `off`, to activate DNS over TLS to 1.1.1.1 |
| `DOT_PROVIDERS` | `cloudflare` | Comma delimited list of DNS over TLS providers from `cloudflare`, `google`, `quad9`, `quadrant`, `cleanbrowsing`, `securedns`, `libredns` |
@@ -138,18 +148,19 @@ docker run --rm --network=container:pia alpine:3.10 wget -qO- https://ipinfo.io
| `BLOCK_ADS` | `off` | `on` or `off`, blocks ads hostnames and IPs |
| `UNBLOCK` | | comma separated string (i.e. `web.com,web2.ca`) to unblock hostnames |
| `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`) |
| `PORT_FORWARDING` | `off` | Set to `on` to forward a port on PIA server |
| `PORT_FORWARDING_STATUS_FILE` | `/forwarded_port` | File path to store the forwarded port number |
| `PORT_FORWARDING` | `off` | (PIA only) Set to `on` to forward a port on PIA server |
| `PORT_FORWARDING_STATUS_FILE` | `/forwarded_port` | (PIA only) File path to store the forwarded port number |
| `TINYPROXY` | `off` | `on` or `off`, to enable the internal HTTP proxy tinyproxy |
| `TINYPROXY_LOG` | `Info` | `Info`, `Connect`, `Notice`, `Warning`, `Error` or `Critical` |
| `TINYPROXY_PORT` | `8888` | `1024` to `65535` internal port for HTTP proxy |
| `TINYPROXY_USER` | | Username to use to connect to the HTTP proxy |
| `TINYPROXY_PASSWORD` | | Passsword to use to connect to the HTTP proxy |
| `SHADOWSOCKS` | `off` | `on` or `off`, to enable the internal SOCKS5 proxy Shadowsocks |
| `SHADOWSOCKS_LOG` | `on` | `on` or `off` to enable logging for Shadowsocks |
| `SHADOWSOCKS_LOG` | `off` | `on` or `off` to enable logging for Shadowsocks |
| `SHADOWSOCKS_PORT` | `8388` | `1024` to `65535` internal port for SOCKS5 proxy |
| `SHADOWSOCKS_PASSWORD` | | Passsword to use to connect to the SOCKS5 proxy |
| `TZ` | | Specify a timezone to use i.e. `Europe/London` |
| `OPENVPN_VERBOSITY` | `1` | Openvpn verbosity level from 0 to 6 |
## Connect to it
@@ -198,7 +209,7 @@ There are various ways to achieve this, depending on your use case.
- Enter port TCP (and UDP, if available) `8388` as the server port
- Use the password you have set with `SHADOWSOCKS_PASSWORD`
- Choose the encryption method/algorithm `chacha20-ietf-poly1305`
1. If you set `SHADOWSOCKS_LOG` to `on`, more information will be logged in the Docker logs
1. If you set `SHADOWSOCKS_LOG` to `on`, (a lot) more information will be logged in the Docker logs
</p></details>
- <details><summary>Access ports of containers connected to PIA</summary><p>
@@ -239,65 +250,104 @@ There are various ways to achieve this, depending on your use case.
</p></details>
## Port forwarding
## Private Internet Access port forwarding
By setting `PORT_FORWARDING` environment variable to `on`, the forwarded port will be read and written to the file specified in `PORT_FORWARDING_STATUS_FILE` (by default, this is set to `/forwarded_port`). If the location for this file does not exist, it will be created automatically.
Note that [not all regions support port forwarding](https://www.privateinternetaccess.com/helpdesk/kb/articles/how-do-i-enable-port-forwarding-on-my-vpn).
You can mount this file as a volume to read it from other containers.
When `PORT_FORWARDING=on`, a port will be forwarded on the PIA server side and written to the file specified by `PORT_FORWARDING_STATUS_FILE=/forwarded_port`.
Note that not all regions support port forwarding.
It can be useful to mount this file as a volume to read it from other containers, for example to configure a torrenting client.
## For the paranoids
## FAQ
- You can review the code which consists in:
- [Dockerfile](https://github.com/qdm12/private-internet-access-docker/blob/master/Dockerfile)
- [main.go](https://github.com/qdm12/private-internet-access-docker/blob/master/cmd/main.go), the main code entrypoint
- [internal package](https://github.com/qdm12/private-internet-access-docker/blob/master/internal)
- [github.com/qdm12/golibs](https://github.com/qdm12/golibs) dependency
- [github.com/qdm12/files](https://github.com/qdm12/files) for files downloaded at start (DNS root hints, block lists, etc.)
- Build the image yourself:
<details><summary>Private Internet Access: Why do I see openvpn warnings at start?</summary><p>
```bash
You might see some warnings similar to:
```s
openvpn: Sat Feb 22 15:55:02 2020 WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this
openvpn: Sat Feb 22 15:55:02 2020 WARNING: 'link-mtu' is used inconsistently, local='link-mtu 1569', remote='link-mtu 1542'
openvpn: Sat Feb 22 15:55:02 2020 WARNING: 'cipher' is used inconsistently, local='cipher AES-256-CBC', remote='cipher BF-CBC'
openvpn: Sat Feb 22 15:55:02 2020 WARNING: 'auth' is used inconsistently, local='auth SHA256', remote='auth SHA1'
openvpn: Sat Feb 22 15:55:02 2020 WARNING: 'keysize' is used inconsistently, local='keysize 256', remote='keysize 128'
openvpn: Sat Feb 22 15:55:02 2020 WARNING: 'comp-lzo' is present in remote config but missing in local config, remote='comp-lzo'
openvpn: Sat Feb 22 15:55:02 2020 [a121ce520d670b71bfd3aa475485539b] Peer Connection Initiated with [AF_INET]xx.xx.xx.xx:1197
```
It is mainly because the option [disable-occ](https://openvpn.net/community-resources/reference-manual-for-openvpn-2-4/) was removed for transparency with you.
Private Internet Access explains [here why](https://www.privateinternetaccess.com/helpdesk/kb/articles/why-do-i-get-cipher-auth-warnings-when-i-connect) the warnings show up.
</p></details>
<details><summary>What files does it download at start before tunneling?</summary><p>
At start, the Go entrypoint only downloads, depending on your settings:
- If `DOT=on`: [DNS over TLS named root](https://github.com/qdm12/files/blob/master/named.root.updated) for Unbound
- If `DOT=on`: [DNS over TLS root key](https://github.com/qdm12/files/blob/master/root.key.updated) for Unbound
- If `BLOCK_MALICIOUS=on`: [Malicious hostnames and IP addresses block lists](https://github.com/qdm12/files) for Unbound
- If `BLOCK_SURVEILLANCE=on`: [Surveillance hostnames and IP addresses block lists](https://github.com/qdm12/files) for Unbound
- If `BLOCK_ADS=on`: [Ads hostnames and IP addresses block lists](https://github.com/qdm12/files) for Unbound
</p></details>
<details><summary>How to build Docker images of older or alternate versions</summary><p>
First, install [Git](https://git-scm.com/).
The following will build the Docker image locally and replace the previous one you built or pulled.
- Build the latest image
```sh
docker build -t qmcgaw/private-internet-access https://github.com/qdm12/private-internet-access-docker.git
```
- The download and parsing of all needed files is done at start (openvpn config files, Unbound files, block lists, etc.)
- Use `-e ENCRYPTION=strong -e BLOCK_MALICIOUS=on`
- Find a [commit](https://github.com/qdm12/private-internet-access-docker/commits/master) you want to build for, in example `095623925a9cc0e5cf89d5b9b510714792267d9b`, then:
```sh
docker build -t qmcgaw/private-internet-access https://github.com/qdm12/private-internet-access-docker.git#095623925a9cc0e5cf89d5b9b510714792267d9b
```
- Find a [branch](https://github.com/qdm12/private-internet-access-docker/branches) you want to build for, in example `mullvad`, then:
```sh
docker build -t qmcgaw/private-internet-access https://github.com/qdm12/private-internet-access-docker.git#mullvad
```
</p></details>
<details><summary>What's all this Go code?</summary><p>
The Go code is a big rewrite of the previous shell entrypoint, it allows for:
- better testing
- better maintainability
- ease of implementing new features
- faster boot
- asynchronous/parallel operations
It is mostly made of the [internal directory](https://github.com/qdm12/private-internet-access-docker/tree/master/internal) and the entry Go file [cmd/main.go](https://github.com/qdm12/private-internet-access-docker/blob/master/cmd/main.go).
</p></details>
<details><summary>How to test DNS over TLS?</summary><p>
- You can test DNSSEC using [internet.nl/connection](https://www.internet.nl/connection/)
- Check DNS leak tests with [https://www.dnsleaktest.com](https://www.dnsleaktest.com)
- DNS Leaks tests might not work because of [this](https://github.com/qdm12/cloudflare-dns-server#verify-dns-connection) (*TLDR*: DNS server is a local caching intermediary)
- Some other DNS leaks tests might not work because of [this](https://github.com/qdm12/cloudflare-dns-server#verify-dns-connection) (*TLDR*: Unbound DNS server is a local caching intermediary)
## Troubleshooting
</p></details>
- If openvpn fails to start, you may need to:
- Install the tun kernel module on your host with `insmod /lib/modules/tun.ko` or `modprobe tun`
- Add `--device=/dev/net/tun` to your docker run command (equivalent for docker-compose, kubernetes, etc.)
<details><summary>How to fix OpenVPN failing to start?</summary><p>
- Fallback to a previous Docker image tags:
- `v1` tag, stable shell scripting based (no support)
- `old` tag, latest shell scripting version (no support)
- `v2`... waiting for `latest` to become more stable
You can try:
- Fallback to a precise previous version
1. Clone the repository on your machine
- Installing the tun kernel module on your host with `insmod /lib/modules/tun.ko` or `modprobe tun`
- Adding `--device=/dev/net/tun` to your docker run command (equivalent for docker-compose, kubernetes, etc.)
```sh
git clone https://github.com/qdm12/private-internet-access-docker.git pia
cd pia
```
1. Look up which commit you want to go back to [here](https://github.com/qdm12/private-internet-access-docker/commits/master), i.e. `942cc7d4d10545b6f5f89c907b7dd1dbc39368e0`
1. Revert to this commit locally
```sh
git reset --hard 942cc7d4d10545b6f5f89c907b7dd1dbc39368e0
```
1. Build the Docker image
```sh
docker build -t qmcgaw/private-internet-access .
```
</p></details>
## Development
@@ -309,11 +359,26 @@ Note that not all regions support port forwarding.
1. In Visual Studio Code, press on `F1` and select `Remote-Containers: Open Folder in Container...`
1. Your dev environment is ready to go!... and it's running in a container :+1:
The Go code is in the Go file [cmd/main.go](https://github.com/qdm12/private-internet-access-docker/blob/master/cmd/main.go) and the [internal directory](https://github.com/qdm12/private-internet-access-docker/tree/master/internal),
you might want to start reading the main.go file.
### Contributors
Thanks for all the contributions, wether small or not so small!
- [@JeordyR](https://github.com/JeordyR) for testing the Mullvad version and opening a [PR with a few fixes](https://github.com/qdm12/private-internet-access-docker/pull/84/files) 👍
- [@rorph](https://github.com/rorph) for a [PR to pick a random region for PIA](https://github.com/qdm12/private-internet-access-docker/pull/70) and a [PR to make the container work with kubernetes](https://github.com/qdm12/private-internet-access-docker/pull/69)
- [@JesterEE](https://github.com/JesterEE) for a [PR to fix silly line endings in block lists back then](https://github.com/qdm12/private-internet-access-docker/pull/55) 📎
- [@elmerfdz](https://github.com/elmerfdz) for a [PR to add timezone information to have correct log timestampts](https://github.com/qdm12/private-internet-access-docker/pull/51) 🕙
- [@Juggels](https://github.com/Juggels) for a [PR to write the PIA forwarded port to a file](https://github.com/qdm12/private-internet-access-docker/pull/43)
- [@gdlx](https://github.com/gdlx) for a [PR to fix and improve PIA port forwarding script](https://github.com/qdm12/private-internet-access-docker/pull/32)
- [@janaz](https://github.com/janaz) for keeping an eye on [updating things in the Dockerfile](https://github.com/qdm12/private-internet-access-docker/pull/8)
## TODOs
- Support other VPN providers
- Mullvad
- Windscribe
<details><summary>Expand me</summary><p>
- Support Windscribe
- Gotify support for notificactions
- Periodic update of malicious block lists with Unbound restart
- Improve healthcheck
@@ -321,7 +386,7 @@ Note that not all regions support port forwarding.
- Check for DNS provider somehow if this is even possible
- Support for other VPN protocols
- Wireguard (wireguard-go)
- Show new versions/commits at start
- Show new versions/commits available at start
- Colors & emojis
- Setup
- Logging streams
@@ -335,6 +400,8 @@ Note that not all regions support port forwarding.
- wireguard-go
- Openvpn to replace openvpn
</p></details>
## License
This repository is under an [MIT license](https://github.com/qdm12/private-internet-access-docker/master/license)

26
ci.sh
View File

@@ -1,18 +1,32 @@
#!/bin/bash
if [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ "$TRAVIS_BRANCH" != "master" ]; then
ARCHS=linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6,linux/ppc64le,linux/s390x
echo -e "\n\nPull request: $TRAVIS_PULL_REQUEST\nRelease tag: $TRAVIS_TAG\nBranch: $TRAVIS_BRANCH\n\nTarget arch: $ARCHS\n\n"
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
echo -e "\n\nBuilding pull request without pushing to Docker Hub\n\n"
docker buildx build \
--progress plain \
--platform=linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6,linux/ppc64le,linux/s390x \
--platform="$ARCHS" \
.
exit $?
fi
echo $DOCKER_PASSWORD | docker login -u qmcgaw --password-stdin &> /dev/null
TAG="${TRAVIS_TAG:-latest}"
echo "Building Docker images for \"$DOCKER_REPO:$TAG\""
echo $DOCKER_PASSWORD | docker login -u qmcgaw --password-stdin 2>&1
TAG="$TRAVIS_TAG"
if [ -z "$TAG" ]; then
TAG=latest
if [ "$TRAVIS_BRANCH" != "master" ]; then
TAG="$TRAVIS_BRANCH"
fi
fi
echo -e "\n\nBuilding Docker images for \"$DOCKER_REPO:$TAG\"\n\n"
docker buildx build \
--progress plain \
--platform=linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6,linux/ppc64le,linux/s390x \
--platform="$ARCHS" \
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg VCS_REF=`git rev-parse --short HEAD` \
--build-arg VERSION=$TAG \

View File

@@ -18,6 +18,8 @@ import (
"github.com/qdm12/private-internet-access-docker/internal/env"
"github.com/qdm12/private-internet-access-docker/internal/firewall"
"github.com/qdm12/private-internet-access-docker/internal/healthcheck"
"github.com/qdm12/private-internet-access-docker/internal/models"
"github.com/qdm12/private-internet-access-docker/internal/mullvad"
"github.com/qdm12/private-internet-access-docker/internal/openvpn"
"github.com/qdm12/private-internet-access-docker/internal/params"
"github.com/qdm12/private-internet-access-docker/internal/pia"
@@ -53,6 +55,7 @@ func main() {
dnsConf := dns.NewConfigurator(logger, client, fileManager)
firewallConf := firewall.NewConfigurator(logger, fileManager)
piaConf := pia.NewConfigurator(client, fileManager, firewallConf, logger)
mullvadConf := mullvad.NewConfigurator(client, fileManager, logger)
tinyProxyConf := tinyproxy.NewConfigurator(fileManager, logger)
shadowsocksConf := shadowsocks.NewConfigurator(fileManager, logger)
ctx, cancel := context.WithCancel(context.Background())
@@ -75,7 +78,16 @@ func main() {
e.FatalOnError(err)
}
err = ovpnConf.WriteAuthFile(allSettings.PIA.User, allSettings.PIA.Password, uid, gid)
var openVPNUser, openVPNPassword string
switch allSettings.VPNSP {
case "pia":
openVPNUser = allSettings.PIA.User
openVPNPassword = allSettings.PIA.Password
case "mullvad":
openVPNUser = allSettings.Mullvad.User
openVPNPassword = "m"
}
err = ovpnConf.WriteAuthFile(openVPNUser, openVPNPassword, uid, gid)
e.FatalOnError(err)
// Temporarily reset chain policies allowing Kubernetes sidecar to
@@ -115,8 +127,19 @@ func main() {
e.FatalOnError(err)
}
VPNIPs, port, err := piaConf.BuildConf(allSettings.PIA.Region, allSettings.OpenVPN.NetworkProtocol, allSettings.PIA.Encryption, uid, gid)
e.FatalOnError(err)
var connections []models.OpenVPNConnection
switch allSettings.VPNSP {
case "pia":
connections, err = piaConf.GetOpenVPNConnections(allSettings.PIA.Region, allSettings.OpenVPN.NetworkProtocol, allSettings.PIA.Encryption)
e.FatalOnError(err)
err = piaConf.BuildConf(connections, allSettings.PIA.Encryption, allSettings.OpenVPN.Verbosity, uid, gid)
e.FatalOnError(err)
case "mullvad":
connections, err = mullvadConf.GetOpenVPNConnections(allSettings.Mullvad.Country, allSettings.Mullvad.City, allSettings.Mullvad.ISP, allSettings.OpenVPN.NetworkProtocol, allSettings.Mullvad.Port)
e.FatalOnError(err)
err = mullvadConf.BuildConf(connections, allSettings.OpenVPN.Verbosity, uid, gid)
e.FatalOnError(err)
}
defaultInterface, defaultGateway, defaultSubnet, err := firewallConf.GetDefaultRoute()
e.FatalOnError(err)
@@ -128,7 +151,7 @@ func main() {
e.FatalOnError(err)
err = firewallConf.CreateGeneralRules()
e.FatalOnError(err)
err = firewallConf.CreateVPNRules(constants.TUN, VPNIPs, defaultInterface, port, allSettings.OpenVPN.NetworkProtocol)
err = firewallConf.CreateVPNRules(constants.TUN, defaultInterface, connections)
e.FatalOnError(err)
err = firewallConf.CreateLocalSubnetsRules(defaultSubnet, allSettings.Firewall.AllowedSubnets, defaultInterface)
e.FatalOnError(err)
@@ -163,7 +186,7 @@ func main() {
go streamMerger.Merge("shadowsocks", stream)
}
if allSettings.PIA.PortForwarding.Enabled {
if allSettings.VPNSP == "pia" && allSettings.PIA.PortForwarding.Enabled {
time.AfterFunc(10*time.Second, func() {
port, err := piaConf.GetPortForward()
if err != nil {

View File

@@ -1,7 +1,6 @@
version: "3.7"
services:
pia:
build: https://github.com/qdm12/private-internet-access-docker.git
image: qmcgaw/private-internet-access
container_name: pia
cap_add:
@@ -14,24 +13,40 @@ services:
- 8388:8388/udp
# command:
environment:
# More variables are available, see the readme table
- VPNSP=pia
- USER=js89ds7
- PROTOCOL=udp
- OPENVPN_VERBOSITY=1
- TZ=
# PIA only
- REGION=CA Montreal
- PASSWORD=8fd9s239G
- ENCRYPTION=strong
- PROTOCOL=udp
- REGION=CA Montreal
- PORT_FORWARDING=off
# Mullvad only
- COUNTRY=Sweden
- CITY=
- ISP=
- PORT=
# DNS over TLS
- DOT=on
- DOT_PROVIDERS=cloudflare
- DOT_VERBOSITY=1
- BLOCK_MALICIOUS=on
- BLOCK_SURVEILLANCE=off
- BLOCK_ADS=off
- UNBLOCK=
# Firewall
- EXTRA_SUBNETS=
# Shadowsocks
- SHADOWSOCKS=off
- SHADOWSOCKS_PASSWORD=
# Tinyproxy
- TINYPROXY=off
- TINYPROXY_LOG=Info
- TINYPROXY_USER=
- TINYPROXY_PASSWORD=
- SHADOWSOCKS=off
- SHADOWSOCKS_LOG=on
- SHADOWSOCKS_PORT=8388
- SHADOWSOCKS_PASSWORD=
restart: always

2
go.mod
View File

@@ -4,7 +4,7 @@ go 1.13
require (
github.com/kyokomi/emoji v2.1.0+incompatible
github.com/qdm12/golibs v0.0.0-20200208153322-66b2eb719e21
github.com/qdm12/golibs v0.0.0-20200224235252-bc16caae82ea
github.com/stretchr/testify v1.4.0
golang.org/x/sys v0.0.0-20190412213103-97732733099d
)

4
go.sum
View File

@@ -61,8 +61,8 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/qdm12/golibs v0.0.0-20200208153322-66b2eb719e21 h1:Nza/Ar6tPYhDzkiNzbaJZHl4+GUXTqbtjGXuWenkqpQ=
github.com/qdm12/golibs v0.0.0-20200208153322-66b2eb719e21/go.mod h1:YULaFjj6VGmhjak6f35sUWwEleHUmngN5IQ3kdvd6XE=
github.com/qdm12/golibs v0.0.0-20200224235252-bc16caae82ea h1:ILUt8UU795dKZ2r7p3G44w1/vcGM/FUFXCcePYXYfS8=
github.com/qdm12/golibs v0.0.0-20200224235252-bc16caae82ea/go.mod h1:YULaFjj6VGmhjak6f35sUWwEleHUmngN5IQ3kdvd6XE=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

View File

@@ -0,0 +1,667 @@
package constants
import (
"net"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
const (
MullvadCertificate = "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"
)
func MullvadCountryChoices() (choices []string) {
uniqueChoices := map[string]struct{}{}
for _, server := range mullvadServers() {
uniqueChoices[string(server.Country)] = struct{}{}
}
for choice := range uniqueChoices {
choices = append(choices, choice)
}
return choices
}
func MullvadCityChoices() (choices []string) {
uniqueChoices := map[string]struct{}{}
for _, server := range mullvadServers() {
uniqueChoices[string(server.City)] = struct{}{}
}
for choice := range uniqueChoices {
choices = append(choices, choice)
}
return choices
}
func MullvadProviderChoices() (choices []string) {
uniqueChoices := map[string]struct{}{}
for _, server := range mullvadServers() {
uniqueChoices[string(server.Provider)] = struct{}{}
}
for choice := range uniqueChoices {
choices = append(choices, choice)
}
return choices
}
func MullvadServerFilter(country models.MullvadCountry, city models.MullvadCity, provider models.MullvadProvider) (servers []models.MullvadServer) {
for _, server := range mullvadServers() {
if len(country) == 0 {
server.Country = ""
}
if len(city) == 0 {
server.City = ""
}
if len(provider) == 0 {
server.Provider = ""
}
if server.Country == country && server.City == city && server.Provider == provider {
servers = append(servers, server)
}
}
return servers
}
func mullvadServers() []models.MullvadServer {
return []models.MullvadServer{
{
Country: models.MullvadCountry("united arab emirates"),
City: models.MullvadCity("dubai"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{45, 9, 249, 34}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("albania"),
City: models.MullvadCity("tirana"),
Provider: models.MullvadProvider("iregister"),
IPs: []net.IP{{31, 171, 154, 210}},
DefaultPort: 1197,
},
{
Country: models.MullvadCountry("austria"),
City: models.MullvadCity("wien"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{37, 120, 155, 250}, {217, 64, 127, 138}, {217, 64, 127, 202}},
DefaultPort: 1196,
},
{
Country: models.MullvadCountry("australia"),
City: models.MullvadCity("adelaide"),
Provider: models.MullvadProvider("intergrid"),
IPs: []net.IP{{116, 206, 231, 58}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("australia"),
City: models.MullvadCity("brisbane"),
Provider: models.MullvadProvider("intergrid"),
IPs: []net.IP{{43, 245, 160, 162}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("australia"),
City: models.MullvadCity("canberra"),
Provider: models.MullvadProvider("intergrid"),
IPs: []net.IP{{116, 206, 229, 98}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("australia"),
City: models.MullvadCity("melbourne"),
Provider: models.MullvadProvider("intergrid"),
IPs: []net.IP{{116, 206, 228, 202}, {116, 206, 228, 242}, {116, 206, 230, 98}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("australia"),
City: models.MullvadCity("perth"),
Provider: models.MullvadProvider("intergrid"),
IPs: []net.IP{{103, 77, 235, 66}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("australia"),
City: models.MullvadCity("sydney"),
Provider: models.MullvadProvider("intergrid"),
IPs: []net.IP{{43, 245, 162, 130}, {103, 77, 232, 130}, {103, 77, 232, 146}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("australia"),
City: models.MullvadCity("sydney"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{217, 138, 204, 82}, {217, 138, 204, 98}, {217, 138, 204, 66}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("belgium"),
City: models.MullvadCity("brussels"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{37, 120, 218, 146}, {37, 120, 218, 138}, {91, 207, 57, 50}, {37, 120, 143, 138}, {185, 104, 186, 202}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("bulgaria"),
City: models.MullvadCity("sofia"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{185, 94, 192, 42}, {185, 94, 192, 66}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("brazil"),
City: models.MullvadCity("sao paulo"),
Provider: models.MullvadProvider("qnax"),
IPs: []net.IP{{191, 101, 62, 178}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("brazil"),
City: models.MullvadCity("sao paulo"),
Provider: models.MullvadProvider("heficed"),
IPs: []net.IP{{177, 67, 80, 186}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("canada"),
City: models.MullvadCity("montreal"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{139, 28, 218, 114}, {217, 138, 200, 194}, {217, 138, 200, 186}, {87, 101, 92, 146}, {176, 113, 74, 178}, {37, 120, 205, 114}, {87, 101, 92, 138}, {37, 120, 205, 122}, {217, 138, 200, 210}, {217, 138, 200, 202}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("canada"),
City: models.MullvadCity("toronto"),
Provider: models.MullvadProvider("amanah"),
IPs: []net.IP{{184, 75, 214, 130}, {162, 219, 176, 250}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("canada"),
City: models.MullvadCity("vancouver"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{172, 83, 40, 34}, {172, 83, 40, 38}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("canada"),
City: models.MullvadCity("vancouver"),
Provider: models.MullvadProvider("esecuredata"),
IPs: []net.IP{{71, 19, 249, 81}, {176, 113, 74, 186}, {71, 19, 248, 240}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("switzerland"),
City: models.MullvadCity("zurich"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{193, 32, 127, 82}, {193, 32, 127, 81}, {193, 32, 127, 83}, {193, 32, 127, 84}},
Owned: true,
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("zwitzerland"),
City: models.MullvadCity("zurich"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{185, 212, 170, 50}, {185, 183, 104, 82}, {185, 9, 18, 98}, {82, 102, 24, 130}, {82, 102, 24, 186}, {185, 212, 170, 162}, {185, 9, 18, 114}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("switzerland"),
City: models.MullvadCity("zurich"),
Provider: models.MullvadProvider("privateLayer"),
IPs: []net.IP{{179, 43, 128, 170}, {81, 17, 20, 34}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("czech republic"),
City: models.MullvadCity("prague"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{217, 138, 199, 82}, {217, 138, 199, 74}},
DefaultPort: 1197,
},
{
Country: models.MullvadCountry("germany"),
City: models.MullvadCity("frankfurt"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{185, 213, 155, 132}, {185, 213, 155, 140}, {185, 213, 155, 136}, {185, 213, 155, 133}, {185, 213, 155, 144}, {185, 213, 155, 143}, {185, 213, 155, 138}, {185, 213, 155, 142}, {185, 213, 155, 139}, {185, 213, 155, 135}, {185, 213, 155, 145}, {185, 213, 155, 137}, {185, 213, 155, 131}, {185, 213, 155, 134}, {185, 213, 155, 141}},
Owned: true,
DefaultPort: 1197,
},
{
Country: models.MullvadCountry("germany"),
City: models.MullvadCity("frankfurt"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{82, 102, 16, 90}, {185, 104, 184, 186}, {77, 243, 183, 202}},
DefaultPort: 1197,
},
{
Country: models.MullvadCountry("denmark"),
City: models.MullvadCity("copenhagen"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{141, 98, 254, 71}, {141, 98, 254, 72}},
Owned: true,
DefaultPort: 1195,
},
{
Country: models.MullvadCountry("denmark"),
City: models.MullvadCity("copenhagen"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{185, 206, 224, 114}, {185, 206, 224, 119}},
DefaultPort: 1195,
},
{
Country: models.MullvadCountry("denmark"),
City: models.MullvadCity("copenhagen"),
Provider: models.MullvadProvider("blix"),
IPs: []net.IP{{134, 90, 149, 138}},
DefaultPort: 1195,
},
{
Country: models.MullvadCountry("denmark"),
City: models.MullvadCity("copenhagen"),
Provider: models.MullvadProvider("asergo"),
IPs: []net.IP{{82, 103, 140, 213}},
DefaultPort: 1195,
},
{
Country: models.MullvadCountry("spain"),
City: models.MullvadCity("madrid"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{195, 206, 107, 146}, {45, 152, 183, 42}, {89, 238, 178, 74}, {45, 152, 183, 26}, {89, 238, 178, 34}},
DefaultPort: 1195,
},
{
Country: models.MullvadCountry("finland"),
City: models.MullvadCity("helsinki"),
Provider: models.MullvadProvider("creanova"),
IPs: []net.IP{{185, 204, 1, 174}, {185, 204, 1, 176}, {185, 212, 149, 201}, {185, 204, 1, 175}, {185, 204, 1, 173}, {185, 204, 1, 172}, {185, 204, 1, 171}},
Owned: true,
DefaultPort: 1196,
},
{
Country: models.MullvadCountry("france"),
City: models.MullvadCity("paris"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{193, 32, 126, 83}, {193, 32, 126, 82}, {193, 32, 126, 81}, {193, 32, 126, 84}},
Owned: true,
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("france"),
City: models.MullvadCity("paris"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{185, 189, 113, 82}, {185, 156, 173, 218}, {185, 128, 25, 162}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("uk"),
City: models.MullvadCity("london"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{141, 98, 252, 133}, {141, 98, 252, 139}, {141, 98, 252, 137}, {141, 98, 252, 143}, {141, 98, 252, 142}, {141, 98, 252, 132}, {141, 98, 252, 134}, {141, 98, 252, 140}, {141, 98, 252, 141}, {141, 98, 252, 136}, {141, 98, 252, 144}, {141, 98, 252, 131}, {141, 98, 252, 135}, {141, 98, 252, 138}},
Owned: true,
DefaultPort: 1196,
},
{
Country: models.MullvadCountry("uk"),
City: models.MullvadCity("london"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{185, 200, 118, 105}, {185, 212, 168, 244}},
DefaultPort: 1196,
},
{
Country: models.MullvadCountry("uk"),
City: models.MullvadCity("manchester"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{89, 238, 130, 66}, {81, 92, 205, 10}, {89, 238, 130, 74}, {81, 92, 205, 18}, {81, 92, 205, 26}, {89, 238, 183, 244}, {89, 238, 132, 36}, {217, 151, 98, 68}, {37, 120, 159, 164}, {89, 238, 183, 60}},
DefaultPort: 1196,
},
{
Country: models.MullvadCountry("greece"),
City: models.MullvadCity("athens"),
Provider: models.MullvadProvider("aweb"),
IPs: []net.IP{{185, 226, 67, 168}},
DefaultPort: 1302,
},
{
Country: models.MullvadCountry("hong kong"),
City: models.MullvadCity("hong kong"),
Provider: models.MullvadProvider("leaseweb"),
IPs: []net.IP{{209, 58, 185, 53}, {209, 58, 184, 146}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("hungary"),
City: models.MullvadCity("budapest"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{185, 94, 190, 138}, {185, 189, 114, 10}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("ireland"),
City: models.MullvadCity("dublin"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{217, 138, 222, 90}, {217, 138, 222, 82}},
DefaultPort: 1197,
},
{
Country: models.MullvadCountry("israel"),
City: models.MullvadCity("tel aviv"),
Provider: models.MullvadProvider("hqserv"),
IPs: []net.IP{{185, 191, 207, 210}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("italy"),
City: models.MullvadCity("milan"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{217, 138, 197, 106}, {217, 64, 113, 180}, {217, 138, 197, 98}, {217, 138, 197, 114}, {217, 64, 113, 183}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("japan"),
City: models.MullvadCity("tokyo"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{37, 120, 210, 138}, {193, 148, 16, 218}, {37, 120, 210, 146}, {185, 242, 4, 50}, {37, 120, 210, 122}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("luxembourg"),
City: models.MullvadCity("luxembourg"),
Provider: models.MullvadProvider("evoluso"),
IPs: []net.IP{{92, 223, 89, 160}, {92, 223, 89, 182}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("latvia"),
City: models.MullvadCity("riga"),
Provider: models.MullvadProvider("makonix"),
IPs: []net.IP{{31, 170, 22, 2}},
DefaultPort: 1300,
},
{
Country: models.MullvadCountry("moldova"),
City: models.MullvadCity("chisinau"),
Provider: models.MullvadProvider("trabia"),
IPs: []net.IP{{178, 175, 142, 194}},
DefaultPort: 1197,
},
{
Country: models.MullvadCountry("netherlands"),
City: models.MullvadCity("amsterdam"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{185, 65, 134, 139}, {185, 65, 134, 133}, {185, 65, 134, 148}, {185, 65, 134, 147}, {185, 65, 134, 141}, {185, 65, 134, 140}, {185, 65, 134, 145}, {185, 65, 134, 132}, {185, 65, 134, 146}, {185, 65, 134, 143}, {185, 65, 134, 134}, {185, 65, 134, 136}, {185, 65, 134, 135}, {185, 65, 134, 142}, {185, 65, 134, 144}},
Owned: true,
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("norway"),
City: models.MullvadCity("oslo"),
Provider: models.MullvadProvider("blix"),
IPs: []net.IP{{91, 90, 44, 13}, {91, 90, 44, 18}, {91, 90, 44, 12}, {91, 90, 44, 15}, {91, 90, 44, 16}, {91, 90, 44, 17}, {91, 90, 44, 14}, {91, 90, 44, 11}},
Owned: true,
DefaultPort: 1302,
},
{
Country: models.MullvadCountry("new zealand"),
City: models.MullvadCity("auckland"),
Provider: models.MullvadProvider("intergrid"),
IPs: []net.IP{{103, 231, 91, 114}},
DefaultPort: 1195,
},
{
Country: models.MullvadCountry("poland"),
City: models.MullvadCity("warsaw"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{37, 120, 211, 202}, {37, 120, 156, 162}, {185, 244, 214, 210}, {37, 120, 211, 186}, {185, 244, 214, 215}, {37, 120, 211, 194}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("portugal"),
City: models.MullvadCity("lisbon"),
Provider: models.MullvadProvider("dotsi"),
IPs: []net.IP{{5, 206, 231, 214}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("romania"),
City: models.MullvadCity("bucharest"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{89, 40, 181, 146}, {185, 181, 100, 202}, {89, 40, 181, 82}, {185, 45, 13, 10}, {89, 40, 181, 210}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("serbia"),
City: models.MullvadCity("belgrade"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{141, 98, 103, 50}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("serbia"),
City: models.MullvadCity("nis"),
Provider: models.MullvadProvider("ninet"),
IPs: []net.IP{{176, 104, 107, 118}},
DefaultPort: 1301,
},
{
Country: models.MullvadCountry("sweden"),
City: models.MullvadCity("gothenburg"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{185, 213, 154, 139}, {185, 213, 154, 141}, {185, 213, 154, 140}, {185, 213, 154, 132}, {185, 213, 154, 135}, {185, 213, 154, 138}, {185, 213, 154, 133}, {185, 213, 154, 131}, {185, 213, 154, 134}, {185, 213, 154, 142}, {185, 213, 154, 137}},
Owned: true,
DefaultPort: 1302,
},
{
Country: models.MullvadCountry("sweden"),
City: models.MullvadCity("helsingborg"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{185, 213, 152, 133}, {185, 213, 152, 132}, {185, 213, 152, 138}, {185, 213, 152, 131}, {185, 213, 152, 137}},
Owned: true,
DefaultPort: 1302,
},
{
Country: models.MullvadCountry("sweden"),
City: models.MullvadCity("malmo"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{193, 138, 218, 138}, {45, 83, 220, 87}, {141, 98, 255, 94}, {141, 98, 255, 85}, {141, 98, 255, 87}, {141, 98, 255, 92}, {45, 83, 220, 84}, {141, 98, 255, 86}, {45, 83, 220, 81}, {193, 138, 218, 135}, {193, 138, 218, 131}, {193, 138, 218, 136}, {141, 98, 255, 88}, {141, 98, 255, 91}, {193, 138, 218, 133}, {45, 83, 220, 89}, {45, 83, 220, 88}, {141, 98, 255, 84}, {141, 98, 255, 89}, {193, 138, 218, 134}, {45, 83, 220, 86}, {141, 98, 255, 83}, {45, 83, 220, 85}, {141, 98, 255, 90}, {141, 98, 255, 93}, {193, 138, 218, 132}, {193, 138, 218, 137}, {45, 83, 220, 91}},
Owned: true,
DefaultPort: 1302,
},
{
Country: models.MullvadCountry("sweden"),
City: models.MullvadCity("stockholm"),
Provider: models.MullvadProvider("31173"),
IPs: []net.IP{{185, 65, 135, 150}, {185, 65, 135, 153}, {185, 65, 135, 151}, {185, 65, 135, 149}, {185, 65, 135, 141}, {185, 65, 135, 144}, {185, 65, 135, 145}, {185, 65, 135, 140}, {185, 65, 135, 134}, {185, 65, 135, 139}, {185, 65, 135, 131}, {185, 65, 135, 152}, {185, 65, 135, 146}, {185, 65, 135, 138}, {185, 65, 135, 143}, {185, 65, 135, 135}, {185, 65, 135, 154}, {185, 65, 135, 136}, {185, 65, 135, 133}, {185, 65, 135, 132}},
Owned: true,
DefaultPort: 1302,
},
{
Country: models.MullvadCountry("singapore"),
City: models.MullvadCity("singapore"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{37, 120, 208, 218}, {37, 120, 208, 234}, {37, 120, 208, 226}, {185, 128, 24, 50}},
DefaultPort: 1196,
},
{
Country: models.MullvadCountry("singapore"),
City: models.MullvadCity("singapore"),
Provider: models.MullvadProvider("leaseweb"),
IPs: []net.IP{{103, 254, 153, 82}},
DefaultPort: 1196,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("atlanta"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{208, 84, 153, 142}, {107, 152, 108, 62}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("atlanta"),
Provider: models.MullvadProvider("quadranet"),
IPs: []net.IP{{104, 129, 24, 242}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("atlanta"),
Provider: models.MullvadProvider("micfo"),
IPs: []net.IP{{155, 254, 96, 2}, {155, 254, 96, 18}, {155, 254, 96, 34}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("chicago"),
Provider: models.MullvadProvider("tzulo"),
IPs: []net.IP{{68, 235, 43, 18}, {68, 235, 43, 26}, {68, 235, 43, 42}, {68, 235, 43, 50}, {68, 235, 43, 58}, {68, 235, 43, 66}, {68, 235, 43, 74}}, // 3 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("chicago"),
Provider: models.MullvadProvider("quadranet"),
IPs: []net.IP{}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("dallas"),
Provider: models.MullvadProvider("quadranet"),
IPs: []net.IP{{96, 44, 145, 18}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("dallas"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{104, 200, 142, 50}, {107, 152, 102, 106}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("denver"),
Provider: models.MullvadProvider("tzulo"),
IPs: []net.IP{{198, 54, 128, 74}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("los angeles"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{45, 152, 182, 66}, {45, 152, 182, 74}, {45, 83, 89, 162}, {185, 230, 126, 146}}, // 7 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("los angeles"),
Provider: models.MullvadProvider("tzulo"),
IPs: []net.IP{{198, 54, 129, 74}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("los angeles"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{104, 200, 152, 66}, {107, 181, 168, 130}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("los angeles"),
Provider: models.MullvadProvider("choopa"),
IPs: []net.IP{{104, 238, 143, 58}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("miami"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{37, 120, 215, 130}, {193, 37, 252, 138}, {193, 37, 252, 154}, {37, 120, 215, 138}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("miami"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{172, 98, 76, 114}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("miami"),
Provider: models.MullvadProvider("micfo"),
IPs: []net.IP{}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("new york"),
Provider: models.MullvadProvider("m247"),
IPs: []net.IP{{185, 232, 22, 66}, {185, 232, 22, 98}, {193, 148, 18, 250}, {185, 232, 22, 10}, {217, 138, 206, 10}, {193, 148, 18, 218}, {193, 148, 18, 226}, {193, 148, 18, 194}, {87, 101, 95, 98}, {87, 101, 95, 114}, {87, 101, 95, 122}, {212, 103, 48, 226}, {176, 113, 72, 226}, {217, 138, 198, 250}, {217, 138, 206, 58}}, // 5 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("new york"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{107, 182, 226, 206}, {107, 182, 226, 218}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("phoenix"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("phoenix"),
Provider: models.MullvadProvider("micfo"),
IPs: []net.IP{{192, 200, 24, 82}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("piscataway"),
Provider: models.MullvadProvider("choopa"),
IPs: []net.IP{{108, 61, 78, 138}, {108, 61, 48, 115}, {66, 55, 147, 59}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("seattle"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{104, 200, 129, 202}, {104, 200, 129, 150}, {104, 200, 129, 110}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("seattle"),
Provider: models.MullvadProvider("micfo"),
IPs: []net.IP{{104, 128, 136, 146}},
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("san francisco"),
Provider: models.MullvadProvider("micfo"),
IPs: []net.IP{{209, 209, 238, 34}}, // 1 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("salt lake city"),
Provider: models.MullvadProvider("100tb"),
IPs: []net.IP{{107, 182, 238, 229}, {107, 182, 235, 233}, {67, 212, 238, 236}, {67, 212, 238, 237}, {67, 212, 238, 239}, {107, 182, 239, 185}, {107, 182, 239, 170}}, // 2 missing
DefaultPort: 1194,
},
{
Country: models.MullvadCountry("usa"),
City: models.MullvadCity("secaucus"),
Provider: models.MullvadProvider("quadranet"),
IPs: []net.IP{{23, 226, 131, 154}}, // 1 missing
DefaultPort: 1194,
},
}
}

View File

@@ -1,9 +1,6 @@
package constants
import (
"fmt"
"strings"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
@@ -21,12 +18,15 @@ const (
PIACertificate_STRONG = "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"
)
func PIAGeoChoices() []string {
return []string{"AU Melbourne", "AU Perth", "AU Sydney", "Austria", "Belgium", "CA Montreal", "CA Toronto", "CA Vancouver", "Czech Republic", "DE Berlin", "DE Frankfurt", "Denmark", "Finland", "France", "Hong Kong", "Hungary", "India", "Ireland", "Israel", "Italy", "Japan", "Luxembourg", "Mexico", "Netherlands", "New Zealand", "Norway", "Poland", "Romania", "Singapore", "Spain", "Sweden", "Switzerland", "UAE", "UK London", "UK Manchester", "UK Southampton", "US Atlanta", "US California", "US Chicago", "US Denver", "US East", "US Florida", "US Houston", "US Las Vegas", "US New York City", "US Seattle", "US Silicon Valley", "US Texas", "US Washington DC", "US West"}
func PIAGeoChoices() (regions []string) {
for region := range PIAGeoToSubdomainMapping() {
regions = append(regions, string(region))
}
return regions
}
func PIAGeoToSubdomainMapping(region models.PIARegion) (subdomain string, err error) {
mapping := map[models.PIARegion]string{
func PIAGeoToSubdomainMapping() map[models.PIARegion]string {
return map[models.PIARegion]string{
models.PIARegion("AU Melbourne"): "au-melbourne",
models.PIARegion("AU Perth"): "au-perth",
models.PIARegion("AU Sydney"): "au-sydney",
@@ -78,11 +78,6 @@ func PIAGeoToSubdomainMapping(region models.PIARegion) (subdomain string, err er
models.PIARegion("US Washington DC"): "us-washingtondc",
models.PIARegion("US West"): "us-west",
}
subdomain, ok := mapping[region]
if !ok {
return "", fmt.Errorf("PIA region %q does not exist and can only be one of ", strings.Join(PIAGeoChoices(), ","))
}
return subdomain, nil
}
const (

View File

@@ -2,9 +2,9 @@ package constants
const (
// Annoucement is a message annoucement
Annoucement = "Total rewrite in Go with many new features"
// AnnoucementExpiration is the expiration time of the annoucement in unix timestamp
AnnoucementExpiration = 1582761600
Annoucement = "Support for Mullvad"
// AnnoucementExpiration is the expiration date of the annoucement in format yyyy-mm-dd
AnnoucementExpiration = "2020-03-15"
)
const (

View File

@@ -0,0 +1,17 @@
package constants
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func Test_AnnoucementExpiration(t *testing.T) {
t.Parallel()
if len(AnnoucementExpiration) == 0 {
return
}
_, err := time.Parse("2006-01-02", AnnoucementExpiration)
assert.NoError(t, err)
}

View File

@@ -60,7 +60,7 @@ func generateUnboundConf(settings settings.DNS, client network.Client, logger lo
"harden-algo-downgrade": "yes",
// Network
"do-ip4": "yes",
"do-ip6": "no",
"do-ip6": "yes",
"interface": "127.0.0.1",
"port": "53",
// Other

View File

@@ -43,7 +43,7 @@ server:
cache-max-ttl: 9000
cache-min-ttl: 3600
do-ip4: yes
do-ip6: no
do-ip6: yes
harden-algo-downgrade: yes
harden-below-nxdomain: yes
harden-referral-path: yes

View File

@@ -18,8 +18,7 @@ type Configurator interface {
Clear() error
BlockAll() error
CreateGeneralRules() error
CreateVPNRules(dev models.VPNDevice, serverIPs []net.IP, defaultInterface string,
port uint16, protocol models.NetworkProtocol) error
CreateVPNRules(dev models.VPNDevice, defaultInterface string, connections []models.OpenVPNConnection) error
CreateLocalSubnetsRules(subnet net.IPNet, extraSubnets []net.IPNet, defaultInterface string) error
AddRoutesVia(subnets []net.IPNet, defaultGateway net.IP, defaultInterface string) error
GetDefaultRoute() (defaultInterface string, defaultGateway net.IP, defaultSubnet net.IPNet, err error)

View File

@@ -77,14 +77,13 @@ func (c *configurator) CreateGeneralRules() error {
})
}
func (c *configurator) CreateVPNRules(dev models.VPNDevice, serverIPs []net.IP,
defaultInterface string, port uint16, protocol models.NetworkProtocol) error {
for _, serverIP := range serverIPs {
func (c *configurator) CreateVPNRules(dev models.VPNDevice, defaultInterface string, connections []models.OpenVPNConnection) error {
for _, connection := range connections {
c.logger.Info("%s: allowing output traffic to VPN server %s through %s on port %s %d",
logPrefix, serverIP, defaultInterface, protocol, port)
logPrefix, connection.IP, defaultInterface, connection.Protocol, connection.Port)
if err := c.runIptablesInstruction(
fmt.Sprintf("-A OUTPUT -d %s -o %s -p %s -m %s --dport %d -j ACCEPT",
serverIP, defaultInterface, protocol, protocol, port)); err != nil {
connection.IP, defaultInterface, connection.Protocol, connection.Protocol, connection.Port)); err != nil {
return err
}
}

View File

@@ -11,6 +11,12 @@ type (
PIAEncryption string
// PIARegion is used to define the list of regions available for PIA
PIARegion string
// MullvadCountry is used as the country for a Mullvad server
MullvadCountry string
// MullvadCity is used as the city for a Mullvad server
MullvadCity string
// MullvadProvider is used as the Internet service provider for a Mullvad server
MullvadProvider string
// URL is an HTTP(s) URL address
URL string
// Filepath is a local filesytem file path

View File

@@ -0,0 +1,12 @@
package models
import "net"
type MullvadServer struct {
Country MullvadCountry
City MullvadCity
Provider MullvadProvider
Owned bool
IPs []net.IP
DefaultPort uint16
}

View File

@@ -0,0 +1,9 @@
package models
import "net"
type OpenVPNConnection struct {
IP net.IP
Port uint16
Protocol NetworkProtocol
}

74
internal/mullvad/conf.go Normal file
View File

@@ -0,0 +1,74 @@
package mullvad
import (
"fmt"
"github.com/qdm12/golibs/files"
"github.com/qdm12/private-internet-access-docker/internal/constants"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
func (c *configurator) GetOpenVPNConnections(country models.MullvadCountry, city models.MullvadCity, provider models.MullvadProvider, protocol models.NetworkProtocol, customPort uint16) (connections []models.OpenVPNConnection, err error) {
servers := constants.MullvadServerFilter(country, city, provider)
if len(servers) == 0 {
return nil, fmt.Errorf("no server found for country %q, city %q and ISP %q", country, city, provider)
}
for _, server := range servers {
port := server.DefaultPort
if customPort > 0 {
port = customPort
}
for _, IP := range server.IPs {
connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: protocol})
}
}
return connections, nil
}
func (c *configurator) BuildConf(connections []models.OpenVPNConnection, verbosity, uid, gid int) (err error) {
if len(connections) == 0 {
return fmt.Errorf("at least one connection string is expected")
}
lines := []string{
"client",
"dev tun",
"nobind",
"persist-key",
"persist-tun",
"tls-client",
"remote-cert-tls server",
"ping 300",
// Mullvad specific
// "sndbuf 524288"
// "rcvbuf 524288"
"cipher AES-256-CBC",
"tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA",
"tun-ipv6",
// Added constant values
"mute-replay-warnings",
"auth-nocache",
"user nonrootuser",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"remote-random",
// Modified variables
fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", string(connections[0].Protocol)),
}
for _, connection := range connections {
lines = append(lines, fmt.Sprintf("remote %s %d", connection.IP.String(), connection.Port))
}
lines = append(lines, []string{
"<ca>",
"-----BEGIN CERTIFICATE-----",
constants.MullvadCertificate,
"-----END CERTIFICATE-----",
"</ca>",
"",
}...)
return c.fileManager.WriteLinesToFile(string(constants.OpenVPNConf), lines, files.Ownership(uid, gid), files.Permissions(0400))
}

View File

@@ -0,0 +1,27 @@
package mullvad
import (
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/network"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
const logPrefix = "Mullvad configurator"
// Configurator contains methods to download, read and modify the openvpn configuration to connect as a client
type Configurator interface {
GetOpenVPNConnections(country models.MullvadCountry, city models.MullvadCity, provider models.MullvadProvider, protocol models.NetworkProtocol, customPort uint16) (connections []models.OpenVPNConnection, err error)
BuildConf(connections []models.OpenVPNConnection, verbosity, uid, gid int) (err error)
}
type configurator struct {
client network.Client
fileManager files.FileManager
logger logging.Logger
}
// NewConfigurator returns a new Configurator object
func NewConfigurator(client network.Client, fileManager files.FileManager, logger logging.Logger) Configurator {
return &configurator{client, fileManager, logger}
}

View File

@@ -0,0 +1,40 @@
package params
import (
"strings"
libparams "github.com/qdm12/golibs/params"
"github.com/qdm12/private-internet-access-docker/internal/constants"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
// GetMullvadCountry obtains the country for the Mullvad server from the
// environment variable COUNTRY
func (p *paramsReader) GetMullvadCountry() (country models.MullvadCountry, err error) {
choices := append(constants.MullvadCountryChoices(), "")
s, err := p.envParams.GetValueIfInside("COUNTRY", choices)
return models.MullvadCountry(strings.ToLower(s)), err
}
// GetMullvadCity obtains the city for the Mullvad server from the
// environment variable CITY
func (p *paramsReader) GetMullvadCity() (country models.MullvadCity, err error) {
choices := append(constants.MullvadCityChoices(), "")
s, err := p.envParams.GetValueIfInside("CITY", choices)
return models.MullvadCity(strings.ToLower(s)), err
}
// GetMullvadISP obtains the ISP for the Mullvad server from the
// environment variable ISP
func (p *paramsReader) GetMullvadISP() (country models.MullvadProvider, err error) {
choices := append(constants.MullvadProviderChoices(), "")
s, err := p.envParams.GetValueIfInside("ISP", choices)
return models.MullvadProvider(strings.ToLower(s)), err
}
// GetMullvadPort obtains the port to reach the Mullvad server on from the
// environment variable PORT
func (p *paramsReader) GetMullvadPort() (port uint16, err error) {
n, err := p.envParams.GetEnvIntRange("PORT", 0, 65535, libparams.Default("0"))
return uint16(n), err
}

View File

@@ -5,9 +5,37 @@ import (
"github.com/qdm12/private-internet-access-docker/internal/models"
)
// GetUser obtains the user to use to connect to the VPN servers
func (p *paramsReader) GetUser() (s string, err error) {
defer func() {
unsetenvErr := p.unsetEnv("USER")
if err == nil {
err = unsetenvErr
}
}()
return p.envParams.GetEnv("USER", libparams.CaseSensitiveValue(), libparams.Compulsory())
}
// GetPassword obtains the password to use to connect to the VPN servers
func (p *paramsReader) GetPassword() (s string, err error) {
defer func() {
unsetenvErr := p.unsetEnv("PASSWORD")
if err == nil {
err = unsetenvErr
}
}()
return p.envParams.GetEnv("PASSWORD", libparams.CaseSensitiveValue(), libparams.Compulsory())
}
// GetNetworkProtocol obtains the network protocol to use to connect to the
// VPN servers from the environment variable PROTOCOL
func (p *paramsReader) GetNetworkProtocol() (protocol models.NetworkProtocol, err error) {
s, err := p.envParams.GetValueIfInside("PROTOCOL", []string{"tcp", "udp"}, libparams.Default("udp"))
return models.NetworkProtocol(s), err
}
// GetOpenVPNVerbosity obtains the verbosity level for verbosity between 0 and 6
// from the environment variable OPENVPN_VERBOSITY
func (p *paramsReader) GetOpenVPNVerbosity() (verbosity int, err error) {
return p.envParams.GetEnvIntRange("OPENVPN_VERBOSITY", 0, 6, libparams.Default("1"))
}

View File

@@ -12,6 +12,8 @@ import (
// ParamsReader contains methods to obtain parameters
type ParamsReader interface {
GetVPNSP() (vpnServiceProvider string, err error)
// DNS over TLS getters
GetDNSOverTLS() (DNSOverTLS bool, err error)
GetDNSOverTLSProviders() (providers []models.DNSProvider, err error)
@@ -29,16 +31,23 @@ type ParamsReader interface {
GetExtraSubnets() (extraSubnets []net.IPNet, err error)
// VPN getters
GetNetworkProtocol() (protocol models.NetworkProtocol, err error)
// PIA getters
GetUser() (s string, err error)
GetPassword() (s string, err error)
GetNetworkProtocol() (protocol models.NetworkProtocol, err error)
GetOpenVPNVerbosity() (verbosity int, err error)
// PIA getters
GetPortForwarding() (activated bool, err error)
GetPortForwardingStatusFilepath() (filepath models.Filepath, err error)
GetPIAEncryption() (models.PIAEncryption, error)
GetPIARegion() (models.PIARegion, error)
// Mullvad getters
GetMullvadCountry() (country models.MullvadCountry, err error)
GetMullvadCity() (country models.MullvadCity, err error)
GetMullvadISP() (country models.MullvadProvider, err error)
GetMullvadPort() (port uint16, err error)
// Shadowsocks getters
GetShadowSocks() (activated bool, err error)
GetShadowSocksLog() (activated bool, err error)
@@ -75,3 +84,9 @@ func NewParamsReader(logger logging.Logger) ParamsReader {
unsetEnv: os.Unsetenv,
}
}
// GetVPNSP obtains the VPN service provider to use from the environment variable VPNSP
func (p *paramsReader) GetVPNSP() (vpnServiceProvider string, err error) {
s, err := p.envParams.GetValueIfInside("VPNSP", []string{"pia", "mullvad"})
return s, err
}

View File

@@ -9,40 +9,6 @@ import (
"github.com/qdm12/private-internet-access-docker/internal/models"
)
// GetUser obtains the user to use to connect to the VPN servers
func (p *paramsReader) GetUser() (s string, err error) {
defer func() {
unsetenvErr := p.unsetEnv("USER")
if err == nil {
err = unsetenvErr
}
}()
s, err = p.envParams.GetEnv("USER")
if err != nil {
return "", err
} else if len(s) == 0 {
return s, fmt.Errorf("USER environment variable cannot be empty")
}
return s, nil
}
// GetPassword obtains the password to use to connect to the VPN servers
func (p *paramsReader) GetPassword() (s string, err error) {
defer func() {
unsetenvErr := p.unsetEnv("PASSWORD")
if err == nil {
err = unsetenvErr
}
}()
s, err = p.envParams.GetEnv("PASSWORD")
if err != nil {
return "", err
} else if len(s) == 0 {
return s, fmt.Errorf("PASSWORD environment variable cannot be empty")
}
return s, nil
}
// GetPortForwarding obtains if port forwarding on the VPN provider server
// side is enabled or not from the environment variable PORT_FORWARDING
func (p *paramsReader) GetPortForwarding() (activated bool, err error) {
@@ -62,7 +28,7 @@ func (p *paramsReader) GetPortForwarding() (activated bool, err error) {
// GetPortForwardingStatusFilepath obtains the port forwarding status file path
// from the environment variable PORT_FORWARDING_STATUS_FILE
func (p *paramsReader) GetPortForwardingStatusFilepath() (filepath models.Filepath, err error) {
filepathStr, err := p.envParams.GetPath("PORT_FORWARDING_STATUS_FILE", libparams.Default("/forwarded_port"))
filepathStr, err := p.envParams.GetPath("PORT_FORWARDING_STATUS_FILE", libparams.Default("/forwarded_port"), libparams.CaseSensitiveValue())
return models.Filepath(filepathStr), err
}
@@ -76,7 +42,7 @@ func (p *paramsReader) GetPIAEncryption() (models.PIAEncryption, error) {
// GetPIARegion obtains the region for the PIA server from the
// environment variable REGION
func (p *paramsReader) GetPIARegion() (region models.PIARegion, err error) {
choices := constants.PIAGeoChoices()
choices := append(constants.PIAGeoChoices(), "")
s, err := p.envParams.GetValueIfInside("REGION", choices)
if len(s) == 0 { // Suggestion by @rorph https://github.com/rorph
s = choices[rand.Int()%len(choices)]

View File

@@ -36,5 +36,5 @@ func (p *paramsReader) GetShadowSocksPort() (port uint16, err error) {
// SHADOWSOCKS_PASSWORD
func (p *paramsReader) GetShadowSocksPassword() (password string, err error) {
defer p.unsetEnv("SHADOWSOCKS_PASSWORD")
return p.envParams.GetEnv("SHADOWSOCKS_PASSWORD")
return p.envParams.GetEnv("SHADOWSOCKS_PASSWORD", libparams.CaseSensitiveValue())
}

View File

@@ -65,7 +65,7 @@ func (p *paramsReader) GetTinyProxyUser() (user string, err error) {
defer p.unsetEnv("PROXY_USER")
defer p.unsetEnv("TINYPROXY_USER")
// Retro-compatibility
user, err = p.envParams.GetEnv("PROXY_USER")
user, err = p.envParams.GetEnv("PROXY_USER", libparams.CaseSensitiveValue())
if err != nil {
return user, err
}
@@ -73,7 +73,7 @@ func (p *paramsReader) GetTinyProxyUser() (user string, err error) {
p.logger.Warn("You are using the old environment variable PROXY_USER, please consider changing it to TINYPROXY_USER")
return user, nil
}
return p.envParams.GetEnv("TINYPROXY_USER")
return p.envParams.GetEnv("TINYPROXY_USER", libparams.CaseSensitiveValue())
}
// GetTinyProxyPassword obtains the TinyProxy server password from the environment variable
@@ -82,7 +82,7 @@ func (p *paramsReader) GetTinyProxyPassword() (password string, err error) {
defer p.unsetEnv("PROXY_PASSWORD")
defer p.unsetEnv("TINYPROXY_PASSWORD")
// Retro-compatibility
password, err = p.envParams.GetEnv("PROXY_PASSWORD")
password, err = p.envParams.GetEnv("PROXY_PASSWORD", libparams.CaseSensitiveValue())
if err != nil {
return password, err
}
@@ -90,5 +90,5 @@ func (p *paramsReader) GetTinyProxyPassword() (password string, err error) {
p.logger.Warn("You are using the old environment variable PROXY_PASSWORD, please consider changing it to TINYPROXY_PASSWORD")
return password, nil
}
return p.envParams.GetEnv("TINYPROXY_PASSWORD")
return p.envParams.GetEnv("TINYPROXY_PASSWORD", libparams.CaseSensitiveValue())
}

View File

@@ -1,20 +1,20 @@
package params
import (
"github.com/qdm12/golibs/params"
libparams "github.com/qdm12/golibs/params"
)
func (p *paramsReader) GetVersion() string {
version, _ := p.envParams.GetEnv("VERSION", params.Default("?"))
version, _ := p.envParams.GetEnv("VERSION", libparams.Default("?"), libparams.CaseSensitiveValue())
return version
}
func (p *paramsReader) GetBuildDate() string {
buildDate, _ := p.envParams.GetEnv("BUILD_DATE", params.Default("?"))
buildDate, _ := p.envParams.GetEnv("BUILD_DATE", libparams.Default("?"), libparams.CaseSensitiveValue())
return buildDate
}
func (p *paramsReader) GetVcsRef() string {
buildDate, _ := p.envParams.GetEnv("VCS_REF", params.Default("?"))
buildDate, _ := p.envParams.GetEnv("VCS_REF", libparams.Default("?"), libparams.CaseSensitiveValue())
return buildDate
}

View File

@@ -2,45 +2,70 @@ package pia
import (
"fmt"
"net"
"strings"
"github.com/qdm12/golibs/files"
"github.com/qdm12/private-internet-access-docker/internal/constants"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
func (c *configurator) BuildConf(region models.PIARegion, protocol models.NetworkProtocol,
encryption models.PIAEncryption, uid, gid int) (IPs []net.IP, port uint16, err error) {
var X509CRL, certificate string // depends on encryption
var cipherAlgo, authAlgo string // depends on encryption
func (c *configurator) GetOpenVPNConnections(region models.PIARegion, protocol models.NetworkProtocol, encryption models.PIAEncryption) (connections []models.OpenVPNConnection, err error) {
geoMapping := constants.PIAGeoToSubdomainMapping()
var subdomain string
for r, s := range geoMapping {
if strings.ToLower(string(region)) == strings.ToLower(string(r)) {
subdomain = s
break
}
}
if len(subdomain) == 0 {
return nil, fmt.Errorf("region %q has no associated PIA subdomain", region)
}
if err != nil {
return nil, err
}
IPs, err := c.lookupIP(subdomain + ".privateinternetaccess.com")
if err != nil {
return nil, err
}
var port uint16
switch protocol {
case constants.TCP:
switch encryption {
case constants.PIAEncryptionNormal:
port = 502
case constants.PIAEncryptionStrong:
port = 501
}
case constants.UDP:
switch encryption {
case constants.PIAEncryptionNormal:
port = 1198
case constants.PIAEncryptionStrong:
port = 1197
}
}
if port == 0 {
return nil, fmt.Errorf("combination of protocol %q and encryption %q does not yield any port number", protocol, encryption)
}
for _, IP := range IPs {
connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: protocol})
}
return connections, nil
}
func (c *configurator) BuildConf(connections []models.OpenVPNConnection, encryption models.PIAEncryption, verbosity, uid, gid int) (err error) {
var X509CRL, certificate, cipherAlgo, authAlgo string
if encryption == constants.PIAEncryptionNormal {
cipherAlgo = "aes-128-cbc"
authAlgo = "sha1"
X509CRL = constants.PIAX509CRL_NORMAL
certificate = constants.PIACertificate_NORMAL
if protocol == constants.UDP {
port = 1198
} else {
port = 502
}
} else { // strong
} else { // strong encryption
cipherAlgo = "aes-256-cbc"
authAlgo = "sha256"
X509CRL = constants.PIAX509CRL_STRONG
certificate = constants.PIACertificate_STRONG
if protocol == constants.UDP {
port = 1197
} else {
port = 501
}
}
subdomain, err := constants.PIAGeoToSubdomainMapping(region)
if err != nil {
return nil, 0, err
}
IPs, err = c.lookupIP(subdomain + ".privateinternetaccess.com")
if err != nil {
return nil, 0, err
}
lines := []string{
"client",
@@ -50,25 +75,30 @@ func (c *configurator) BuildConf(region models.PIARegion, protocol models.Networ
"persist-tun",
"tls-client",
"remote-cert-tls server",
"compress",
"verb 1", // TODO env variable
"ping 300", // Ping every 5 minutes to prevent a timeout error
// PIA specific
"reneg-sec 0",
"compress", // allow PIA server to choose the compression to use
// Added constant values
"tun-ipv6",
"auth-nocache",
"mute-replay-warnings",
"user nonrootuser",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"disable-occ",
"remote-random",
// Modified variables
fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", string(protocol)),
fmt.Sprintf("proto %s", string(connections[0].Protocol)),
fmt.Sprintf("cipher %s", cipherAlgo),
fmt.Sprintf("auth %s", authAlgo),
}
for _, IP := range IPs {
lines = append(lines, fmt.Sprintf("remote %s %d", IP.String(), port))
for _, connection := range connections {
lines = append(lines, fmt.Sprintf("remote %s %d", connection.IP.String(), connection.Port))
}
lines = append(lines, []string{
"<crl-verify>",
@@ -85,6 +115,5 @@ func (c *configurator) BuildConf(region models.PIARegion, protocol models.Networ
"</ca>",
"",
}...)
err = c.fileManager.WriteLinesToFile(string(constants.OpenVPNConf), lines, files.Ownership(uid, gid), files.Permissions(0400))
return IPs, port, err
return c.fileManager.WriteLinesToFile(string(constants.OpenVPNConf), lines, files.Ownership(uid, gid), files.Permissions(0400))
}

View File

@@ -16,8 +16,9 @@ const logPrefix = "PIA configurator"
// Configurator contains methods to download, read and modify the openvpn configuration to connect as a client
type Configurator interface {
BuildConf(region models.PIARegion, protocol models.NetworkProtocol,
encryption models.PIAEncryption, uid, gid int) (IPs []net.IP, port uint16, err error)
GetOpenVPNConnections(region models.PIARegion, protocol models.NetworkProtocol,
encryption models.PIAEncryption) (connections []models.OpenVPNConnection, err error)
BuildConf(connections []models.OpenVPNConnection, encryption models.PIAEncryption, verbosity, uid, gid int) (err error)
GetPortForward() (port uint16, err error)
WritePortForward(filepath models.Filepath, port uint16) (err error)
AllowPortForwardFirewall(device models.VPNDevice, port uint16) (err error)

View File

@@ -0,0 +1,57 @@
package settings
import (
"strings"
"github.com/qdm12/private-internet-access-docker/internal/models"
"github.com/qdm12/private-internet-access-docker/internal/params"
)
// Mullvad contains the settings to connect to a Mullvad server
type Mullvad struct {
User string
Country models.MullvadCountry
City models.MullvadCity
ISP models.MullvadProvider
Port uint16
}
func (m *Mullvad) String() string {
settingsList := []string{
"Mullvad settings:",
"User: [redacted]",
"Country: " + string(m.Country),
"City: " + string(m.City),
"ISP: " + string(m.ISP),
"Port: " + string(m.Port),
}
return strings.Join(settingsList, "\n |--")
}
// GetMullvadSettings obtains Mullvad settings from environment variables using the params package.
func GetMullvadSettings(params params.ParamsReader) (settings Mullvad, err error) {
settings.User, err = params.GetUser()
if err != nil {
return settings, err
}
// Remove spaces in user ID to simplify user's life, thanks @JeordyR
settings.User = strings.ReplaceAll(settings.User, " ", "")
settings.Country, err = params.GetMullvadCountry()
if err != nil {
return settings, err
}
settings.City, err = params.GetMullvadCity()
if err != nil {
return settings, err
}
settings.ISP, err = params.GetMullvadISP()
if err != nil {
return settings, err
}
settings.Port, err = params.GetMullvadPort()
if err != nil {
return settings, err
}
return settings, nil
}

View File

@@ -1,6 +1,7 @@
package settings
import (
"fmt"
"strings"
"github.com/qdm12/private-internet-access-docker/internal/models"
@@ -10,6 +11,7 @@ import (
// OpenVPN contains settings to configure the OpenVPN client
type OpenVPN struct {
NetworkProtocol models.NetworkProtocol
Verbosity int
}
// GetOpenVPNSettings obtains the OpenVPN settings using the params functions
@@ -18,6 +20,10 @@ func GetOpenVPNSettings(params params.ParamsReader) (settings OpenVPN, err error
if err != nil {
return settings, err
}
settings.Verbosity, err = params.GetOpenVPNVerbosity()
if err != nil {
return settings, err
}
return settings, nil
}
@@ -25,6 +31,7 @@ func (o *OpenVPN) String() string {
settingsList := []string{
"OpenVPN settings:",
"Network protocol: " + string(o.NetworkProtocol),
"Verbosity level: " + fmt.Sprintf("%d", o.Verbosity),
}
return strings.Join(settingsList, "\n|--")
}

View File

@@ -1,6 +1,7 @@
package settings
import (
"fmt"
"strings"
"github.com/qdm12/private-internet-access-docker/internal/params"
@@ -8,8 +9,10 @@ import (
// Settings contains all settings for the program to run
type Settings struct {
VPNSP string
OpenVPN OpenVPN
PIA PIA
Mullvad Mullvad
DNS DNS
Firewall Firewall
TinyProxy TinyProxy
@@ -17,10 +20,17 @@ type Settings struct {
}
func (s *Settings) String() string {
var vpnServiceProvider string
switch s.VPNSP {
case "pia":
vpnServiceProvider = s.PIA.String()
case "mullvad":
vpnServiceProvider = s.Mullvad.String()
}
return strings.Join([]string{
"Settings summary below:",
s.OpenVPN.String(),
s.PIA.String(),
vpnServiceProvider,
s.DNS.String(),
s.Firewall.String(),
s.TinyProxy.String(),
@@ -32,13 +42,27 @@ func (s *Settings) String() string {
// GetAllSettings obtains all settings for the program and returns an error as soon
// as an error is encountered reading them.
func GetAllSettings(params params.ParamsReader) (settings Settings, err error) {
settings.VPNSP, err = params.GetVPNSP()
if err != nil {
return settings, err
}
settings.OpenVPN, err = GetOpenVPNSettings(params)
if err != nil {
return settings, err
}
settings.PIA, err = GetPIASettings(params)
if err != nil {
return settings, err
switch settings.VPNSP {
case "pia":
settings.PIA, err = GetPIASettings(params)
if err != nil {
return settings, err
}
case "mullvad":
settings.Mullvad, err = GetMullvadSettings(params)
if err != nil {
return settings, err
}
default:
return settings, fmt.Errorf("VPN service provider %q is not valid", settings.VPNSP)
}
settings.DNS, err = GetDNSSettings(params)
if err != nil {

View File

@@ -39,11 +39,14 @@ func title() []string {
}
func annoucement() []string {
timestamp := time.Now().UnixNano() / 1000000000
if timestamp < constants.AnnoucementExpiration {
return []string{emoji.Sprint(":mega: ") + constants.Annoucement}
if len(constants.Annoucement) == 0 {
return nil
}
return nil
expirationDate, _ := time.Parse("2006-01-02", constants.AnnoucementExpiration) // error covered by a unit test
if time.Now().After(expirationDate) {
return nil
}
return []string{emoji.Sprint(":mega: ") + constants.Annoucement}
}
func links() []string {