Compare commits

..

13 Commits

Author SHA1 Message Date
Quentin McGaw
90e5742211 Reduce readme size 2020-07-25 11:55:35 -04:00
Quentin McGaw
8f547500d0 Purevpn support (#208)
Fixes #192
2020-07-25 11:19:45 -04:00
Quentin McGaw
0811b8b099 Server filtering fixes for Mullvad and Nordvpn 2020-07-23 02:16:12 +00:00
Quentin McGaw
c5c53a2ff8 FatalOnError fixes 2020-07-23 02:15:37 +00:00
Quentin McGaw
0ce129b63d Make all variables behave like server filters 2020-07-23 01:48:18 +00:00
Quentin McGaw
fec1249293 Uniformize server selection filtering 2020-07-23 01:46:28 +00:00
Quentin McGaw
a5c35455d1 Update PIA IP addresses 2020-07-20 02:32:02 +00:00
Quentin McGaw
28e0abc922 FIREWALL_VPN_INPUT_PORTS variable, fixes #196 2020-07-20 02:07:13 +00:00
Quentin McGaw
a13be8f45e Firewall simplifications
- Only a map of allowed input port to interface
- port forwarded is in the map of allowed input ports
- port forwarded has the interface tun0 in this map
- Always allow tcp and udp for allowed input ports
- Port forward state is in openvpn looper only
- Shadowsocks input port allowed on default interface only
- Tinyproxy input port allowed on default interface only
2020-07-20 00:39:59 +00:00
Quentin McGaw
85bd4f2e8d Get default route and local subnet only at start 2020-07-20 00:35:53 +00:00
Quentin McGaw
4baf0420d6 Openvpn get settings http route 2020-07-19 14:26:24 +00:00
Quentin McGaw
29f74df450 Fix #202 2020-07-19 14:22:23 +00:00
Quentin McGaw
fab9939b26 Simplify DNS loop a bit and fixes #199 2020-07-17 01:16:49 +00:00
47 changed files with 1530 additions and 584 deletions

View File

@@ -42,17 +42,18 @@ ENV VPNSP=pia \
UID=1000 \ UID=1000 \
GID=1000 \ GID=1000 \
IP_STATUS_FILE="/ip" \ IP_STATUS_FILE="/ip" \
# PIA, Windscribe, Surfshark, Cyberghost, Vyprvpn, NordVPN only # PIA, Windscribe, Surfshark, Cyberghost, Vyprvpn, NordVPN, PureVPN only
USER= \ USER= \
PASSWORD= \ PASSWORD= \
REGION="Austria" \ REGION= \
# PIA only # PIA only
PIA_ENCRYPTION=strong \ PIA_ENCRYPTION=strong \
PORT_FORWARDING=off \ PORT_FORWARDING=off \
PORT_FORWARDING_STATUS_FILE="/forwarded_port" \ PORT_FORWARDING_STATUS_FILE="/forwarded_port" \
# Mullvad only # Mullvad and PureVPN only
COUNTRY=Sweden \ COUNTRY= \
CITY= \ CITY= \
# Mullvad only
ISP= \ ISP= \
# Mullvad and Windscribe only # Mullvad and Windscribe only
PORT= \ PORT= \
@@ -82,6 +83,7 @@ ENV VPNSP=pia \
# Firewall # Firewall
FIREWALL=on \ FIREWALL=on \
EXTRA_SUBNETS= \ EXTRA_SUBNETS= \
FIREWALL_VPN_INPUT_PORTS= \
FIREWALL_DEBUG=off \ FIREWALL_DEBUG=off \
# Tinyproxy # Tinyproxy
TINYPROXY=off \ TINYPROXY=off \
@@ -99,7 +101,7 @@ ENTRYPOINT ["/entrypoint"]
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
HEALTHCHECK --interval=10m --timeout=10s --start-period=30s --retries=2 CMD /entrypoint healthcheck HEALTHCHECK --interval=10m --timeout=10s --start-period=30s --retries=2 CMD /entrypoint healthcheck
RUN apk add -q --progress --no-cache --update openvpn ca-certificates iptables ip6tables unbound tinyproxy tzdata && \ RUN apk add -q --progress --no-cache --update openvpn ca-certificates iptables ip6tables unbound tinyproxy tzdata && \
echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories && \ echo "http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories && \
apk add -q --progress --no-cache --update shadowsocks-libev && \ apk add -q --progress --no-cache --update shadowsocks-libev && \
rm -rf /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-* /etc/tinyproxy/tinyproxy.conf && \ rm -rf /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-* /etc/tinyproxy/tinyproxy.conf && \
deluser openvpn && \ deluser openvpn && \

View File

@@ -1,7 +1,7 @@
# Gluetun VPN client # Gluetun VPN client
*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access, *Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access,
Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN and NordVPN VPN servers, using Go, OpenVPN, Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN and PureVPN VPN servers, using Go, OpenVPN,
iptables, DNS over TLS, ShadowSocks and Tinyproxy* iptables, DNS over TLS, ShadowSocks and Tinyproxy*
**ANNOUNCEMENT**: *[Video of the Git history of Gluetun](https://youtu.be/khipOYJtGJ0)* **ANNOUNCEMENT**: *[Video of the Git history of Gluetun](https://youtu.be/khipOYJtGJ0)*
@@ -20,22 +20,11 @@ iptables, DNS over TLS, ShadowSocks and Tinyproxy*
[![Image version](https://images.microbadger.com/badges/version/qmcgaw/private-internet-access.svg)](https://microbadger.com/images/qmcgaw/private-internet-access) [![Image version](https://images.microbadger.com/badges/version/qmcgaw/private-internet-access.svg)](https://microbadger.com/images/qmcgaw/private-internet-access)
[![Join Slack channel](https://img.shields.io/badge/slack-@qdm12-yellow.svg?logo=slack)](https://join.slack.com/t/qdm12/shared_invite/enQtOTE0NjcxNTM1ODc5LTYyZmVlOTM3MGI4ZWU0YmJkMjUxNmQ4ODQ2OTAwYzMxMTlhY2Q1MWQyOWUyNjc2ODliNjFjMDUxNWNmNzk5MDk) [![Join Slack channel](https://img.shields.io/badge/slack-@qdm12-yellow.svg?logo=slack)](https://join.slack.com/t/qdm12/shared_invite/enQtOTE0NjcxNTM1ODc5LTYyZmVlOTM3MGI4ZWU0YmJkMjUxNmQ4ODQ2OTAwYzMxMTlhY2Q1MWQyOWUyNjc2ODliNjFjMDUxNWNmNzk5MDk)
<details><summary>Click to show base components</summary><p>
- [Alpine 3.12](https://alpinelinux.org) for a tiny image (37MB of packages, 6.7MB of Go binary and 5.6MB for Alpine)
- [OpenVPN 2.4.9](https://pkgs.alpinelinux.org/package/v3.11/main/x86_64/openvpn) to tunnel to your VPN provider servers
- [IPtables 1.8.4](https://pkgs.alpinelinux.org/package/v3.11/main/x86_64/iptables) enforces the container to communicate only through the VPN or with other containers in its virtual network (acts as a killswitch)
- [Unbound 1.10.1](https://pkgs.alpinelinux.org/package/v3.11/main/x86_64/unbound) configured with Cloudflare's [1.1.1.1](https://1.1.1.1) DNS over TLS (configurable with 5 different providers)
- [Files and blocking lists built periodically](https://github.com/qdm12/updated/tree/master/files) used with Unbound (see `BLOCK_MALICIOUS`, `BLOCK_SURVEILLANCE` and `BLOCK_ADS` environment variables)
- [TinyProxy 1.10.0](https://pkgs.alpinelinux.org/package/v3.11/main/x86_64/tinyproxy)
- [Shadowsocks 3.3.4](https://pkgs.alpinelinux.org/package/edge/testing/x86/shadowsocks-libev)
</p></details>
## Features ## Features
- Based on Alpine 3.12 for a small Docker image of 52MB - Based on Alpine 3.12 for a small Docker image of 52MB
- Supports **Private Internet Access**, **Mullvad**, **Windscribe**, **Surfshark**, **Cyberghost**, **Vyprvpn** and **NordVPN** servers - Supports **Private Internet Access**, **Mullvad**, **Windscribe**, **Surfshark**, **Cyberghost**, **Vyprvpn**, **NordVPN** and **PureVPN** servers
- Supports Openvpn only for now
- DNS over TLS baked in with service provider(s) of your choice - DNS over TLS baked in with service provider(s) of your choice
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours - DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
- Choose the vpn network protocol, `udp` or `tcp` - Choose the vpn network protocol, `udp` or `tcp`
@@ -45,19 +34,7 @@ iptables, DNS over TLS, ShadowSocks and Tinyproxy*
- [Connect other containers to it](https://github.com/qdm12/private-internet-access-docker#connect-to-it) - [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) - [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 🎆 - Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7 🎆
- VPN server side port forwarding for Private Internet Access and Vyprvpn
### VPN provider specifics
- **Private Internet Access**: pick the [region](https://www.privateinternetaccess.com/pages/network/), the level of encryption and enable port forwarding
- **Mullvad**: Pick the [country, city and ISP](https://mullvad.net/en/servers/#openvpn) and optionally a custom port to use (i.e. `53` (udp) or `80` (tcp))
- **Windscribe**: Pick the [region](https://windscribe.com/status), and optionally a custom port to use
- **Surfshark**: Pick the [region](https://github.com/qdm12/private-internet-access-docker/wiki/Surfshark) or a multi hop region name
- **Cyberghost**: Pick the [region](https://github.com/qdm12/private-internet-access-docker/wiki/Cyberghost) and server group.
- **VyprVPN**: Pick the [region](https://www.vyprvpn.com/server-locations), port forwarding works by default
- **NordVPN**: Pick the region and optionally the server number
### Extra niche features
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers - Possibility of split horizon DNS by selecting multiple DNS over TLS providers
- Subprograms all drop root privileges once launched - Subprograms all drop root privileges once launched
- Subprograms output streams are all merged together - Subprograms output streams are all merged together
@@ -66,22 +43,10 @@ iptables, DNS over TLS, ShadowSocks and Tinyproxy*
## Setup ## Setup
1. Requirements 1. Requirements
- A VPN account with one of the service providers: - A VPN account with one of the service providers supported
- Private Internet Access: **username** and **password** ([sign up](https://www.privateinternetaccess.com/pages/buy-vpn/))
- Mullvad: user ID ([sign up](https://mullvad.net/en/account/))
- Windscribe: **username** and **password** | Signup up using my affiliate link below
[![https://windscribe.com/?affid=mh7nyafu](https://raw.githubusercontent.com/qdm12/private-internet-access-docker/master/doc/windscribe.jpg)](https://windscribe.com/?affid=mh7nyafu)
- Surfshark: **username** and **password** ([sign up](https://order.surfshark.com/))
- Cyberghost: **username**, **password** and **device client key file** ([sign up](https://www.cyberghostvpn.com/en_US/buy/cyberghost-vpn-4))
- Vyprvpn: **username** and **password**
- NordVPN: **username** and **password**
- If you have a host or router firewall, please refer [to the firewall documentation](https://github.com/qdm12/private-internet-access-docker/blob/master/doc/firewall.md) - If you have a host or router firewall, please refer [to the firewall documentation](https://github.com/qdm12/private-internet-access-docker/blob/master/doc/firewall.md)
1. On some devices you may need to setup your tunnel kernel module on your host with `insmod /lib/modules/tun.ko` or `modprobe tun` 1. On some devices you may need to setup your tunnel kernel module on your host with `insmod /lib/modules/tun.ko` or `modprobe tun`
- *Synology users*: please read [this part of the Wiki](https://github.com/qdm12/private-internet-access-docker/wiki/Common-issues#synology) - *Synology users*: please read [this part of the Wiki](https://github.com/qdm12/private-internet-access-docker/wiki/Common-issues#synology)
1. Launch the container with: 1. Launch the container with:
```bash ```bash
@@ -125,7 +90,7 @@ Want more testing? ▶ [see the Wiki](https://github.com/qdm12/private-internet-
| Variable | Default | Choices | Description | | Variable | Default | Choices | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 🏁 `VPNSP` | `private internet access` | `private internet access`, `mullvad`, `windscribe`, `surfshark`, `vyprvpn`, `nordvpn` | VPN Service Provider | | 🏁 `VPNSP` | `private internet access` | `private internet access`, `mullvad`, `windscribe`, `surfshark`, `vyprvpn`, `nordvpn`, `purevpn` | VPN Service Provider |
| `IP_STATUS_FILE` | `/ip` | Any filepath | Filepath to store the public IP address assigned | | `IP_STATUS_FILE` | `/ip` | Any filepath | Filepath to store the public IP address assigned |
| `PROTOCOL` | `udp` | `udp` or `tcp` | Network protocol to use | | `PROTOCOL` | `udp` | `udp` or `tcp` | Network protocol to use |
| `OPENVPN_VERBOSITY` | `1` | `0` to `6` | Openvpn verbosity level | | `OPENVPN_VERBOSITY` | `1` | `0` to `6` | Openvpn verbosity level |
@@ -134,13 +99,15 @@ Want more testing? ▶ [see the Wiki](https://github.com/qdm12/private-internet-
| `OPENVPN_CIPHER` | | i.e. `aes-256-gcm` | Specify a custom cipher to use. It will also set `ncp-disable` if using AES GCM for PIA | | `OPENVPN_CIPHER` | | i.e. `aes-256-gcm` | Specify a custom cipher to use. It will also set `ncp-disable` if using AES GCM for PIA |
| `OPENVPN_AUTH` | | i.e. `sha256` | Specify a custom auth algorithm to use | | `OPENVPN_AUTH` | | i.e. `sha256` | Specify a custom auth algorithm to use |
*For all providers below, server location parameters are all optional. By default a random server is picked using the filter settings provided.*
- Private Internet Access - Private Internet Access
| Variable | Default | Choices | Description | | Variable | Default | Choices | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 🏁 `USER` | | | Your username | | 🏁 `USER` | | | Your username |
| 🏁 `PASSWORD` | | | Your password | | 🏁 `PASSWORD` | | | Your password |
| `REGION` | `Austria` | One of the [PIA regions](https://www.privateinternetaccess.com/pages/network/) | VPN server region | | `REGION` | | One of the [PIA regions](https://www.privateinternetaccess.com/pages/network/) | VPN server region |
| `PIA_ENCRYPTION` | `strong` | `normal`, `strong` | Encryption preset | | `PIA_ENCRYPTION` | `strong` | `normal`, `strong` | Encryption preset |
| `PORT_FORWARDING` | `off` | `on`, `off` | Enable port forwarding on the VPN server | | `PORT_FORWARDING` | `off` | `on`, `off` | Enable port forwarding on the VPN server |
| `PORT_FORWARDING_STATUS_FILE` | `/forwarded_port` | Any filepath | Filepath to store the forwarded port number | | `PORT_FORWARDING_STATUS_FILE` | `/forwarded_port` | Any filepath | Filepath to store the forwarded port number |
@@ -150,7 +117,7 @@ Want more testing? ▶ [see the Wiki](https://github.com/qdm12/private-internet-
| Variable | Default | Choices | Description | | Variable | Default | Choices | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 🏁 `USER` | | | Your user ID | | 🏁 `USER` | | | Your user ID |
| `COUNTRY` | `Sweden` | One of the [Mullvad countries](https://mullvad.net/en/servers/#openvpn) | VPN server country | | `COUNTRY` | | One of the [Mullvad countries](https://mullvad.net/en/servers/#openvpn) | VPN server country |
| `CITY` | | One of the [Mullvad cities](https://mullvad.net/en/servers/#openvpn) | VPN server city | | `CITY` | | One of the [Mullvad cities](https://mullvad.net/en/servers/#openvpn) | VPN server city |
| `ISP` | | One of the [Mullvad ISP](https://mullvad.net/en/servers/#openvpn) | VPN server ISP | | `ISP` | | One of the [Mullvad ISP](https://mullvad.net/en/servers/#openvpn) | VPN server ISP |
| `PORT` | | `80` or `443` for TCP; or `53` for UDP. Leave blank for default Mullvad server port | Custom VPN port to use | | `PORT` | | `80` or `443` for TCP; or `53` for UDP. Leave blank for default Mullvad server port | Custom VPN port to use |
@@ -161,7 +128,7 @@ Want more testing? ▶ [see the Wiki](https://github.com/qdm12/private-internet-
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 🏁 `USER` | | | Your username | | 🏁 `USER` | | | Your username |
| 🏁 `PASSWORD` | | | Your password | | 🏁 `PASSWORD` | | | Your password |
| `REGION` | `Austria` | One of the [Windscribe regions](https://windscribe.com/status) | VPN server region | | `REGION` | | One of the [Windscribe regions](https://windscribe.com/status) | VPN server region |
| `PORT` | | One from the [this list of ports](https://windscribe.com/getconfig/openvpn) | Custom VPN port to use | | `PORT` | | One from the [this list of ports](https://windscribe.com/getconfig/openvpn) | Custom VPN port to use |
- Surfshark - Surfshark
@@ -170,7 +137,7 @@ Want more testing? ▶ [see the Wiki](https://github.com/qdm12/private-internet-
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 🏁 `USER` | | | Your **service** username, found at the bottom of the [manual setup page](https://account.surfshark.com/setup/manual) | | 🏁 `USER` | | | Your **service** username, found at the bottom of the [manual setup page](https://account.surfshark.com/setup/manual) |
| 🏁 `PASSWORD` | | | Your **service** password | | 🏁 `PASSWORD` | | | Your **service** password |
| `REGION` | `Austria` | One of the [Surfshark regions (subdomains)](https://github.com/qdm12/private-internet-access-docker/wiki/surfshark) | VPN server region | | `REGION` | | One of the [Surfshark regions](https://github.com/qdm12/private-internet-access-docker/wiki/surfshark) | VPN server region |
- Cyberghost - Cyberghost
@@ -179,7 +146,7 @@ Want more testing? ▶ [see the Wiki](https://github.com/qdm12/private-internet-
| 🏁 `USER` | | | Your username | | 🏁 `USER` | | | Your username |
| 🏁 `PASSWORD` | | | Your password | | 🏁 `PASSWORD` | | | Your password |
| 🏁 `CLIENT_KEY` | | | Your device client key content, **see below** | | 🏁 `CLIENT_KEY` | | | Your device client key content, **see below** |
| `REGION` | `Austria` | One of the [Cyberghost countries](https://github.com/qdm12/private-internet-access-docker/wiki/Cyberghost#regions) | VPN server country | | `REGION` | | One of the [Cyberghost countries](https://github.com/qdm12/private-internet-access-docker/wiki/Cyberghost#regions) | VPN server country |
| `CYBERGHOST_GROUP` | `Premium UDP Europe` | One of the [server groups](https://github.com/qdm12/private-internet-access-docker/wiki/Cyberghost#server-groups) | Server group | | `CYBERGHOST_GROUP` | `Premium UDP Europe` | One of the [server groups](https://github.com/qdm12/private-internet-access-docker/wiki/Cyberghost#server-groups) | Server group |
To specify your client key, you can either: To specify your client key, you can either:
@@ -199,7 +166,7 @@ Want more testing? ▶ [see the Wiki](https://github.com/qdm12/private-internet-
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 🏁 `USER` | | | Your username | | 🏁 `USER` | | | Your username |
| 🏁 `PASSWORD` | | | Your password | | 🏁 `PASSWORD` | | | Your password |
| `REGION` | `Austria` | One of the [VyprVPN regions](https://www.vyprvpn.com/server-locations) | VPN server region | | `REGION` | | One of the [VyprVPN regions](https://www.vyprvpn.com/server-locations) | VPN server region |
- NordVPN - NordVPN
@@ -207,9 +174,18 @@ Want more testing? ▶ [see the Wiki](https://github.com/qdm12/private-internet-
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 🏁 `USER` | | | Your username | | 🏁 `USER` | | | Your username |
| 🏁 `PASSWORD` | | | Your password | | 🏁 `PASSWORD` | | | Your password |
| 🏁 `REGION` | `Austria` (wrong) | One of the NordVPN server country, i.e. `Switzerland` | VPN server country | | `REGION` | | One of the NordVPN server country, i.e. `Switzerland` | VPN server country |
| `SERVER_NUMBER` | | Server integer number | Optional server number. For example `251` for `Italy #251` | | `SERVER_NUMBER` | | Server integer number | Optional server number. For example `251` for `Italy #251` |
- PureVPN
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| 🏁 `USER` | | | Your user ID |
| 🏁 `REGION` | | One of the [PureVPN regions](https://support.purevpn.com/vpn-servers) | VPN server region |
| `COUNTRY` | | One of the [PureVPN countries](https://support.purevpn.com/vpn-servers) | VPN server country |
| `CITY` | | One of the [PureVPN cities](https://support.purevpn.com/vpn-servers) | VPN server city |
### DNS over TLS ### DNS over TLS
None of the following values are required. None of the following values are required.
@@ -240,6 +216,7 @@ That one is important if you want to connect to the container from your LAN for
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `FIREWALL` | `on` | `on` or `off` | Turn on or off the container built-in firewall. You should use it for **debugging purposes** only. | | `FIREWALL` | `on` | `on` or `off` | Turn on or off the container built-in firewall. You should use it for **debugging purposes** only. |
| `EXTRA_SUBNETS` | | i.e. `192.168.1.0/24,192.168.10.121,10.0.0.5/28` | Comma separated subnets allowed in the container firewall | | `EXTRA_SUBNETS` | | i.e. `192.168.1.0/24,192.168.10.121,10.0.0.5/28` | Comma separated subnets allowed in the container firewall |
| `FIREWALL_VPN_INPUT_PORTS` | | i.e. `1000,8080` | Comma separated list of ports to allow from the VPN server side (useful for **vyprvpn** port forwarding) |
| `FIREWALL_DEBUG` | `off` | `on` or `off` | Prints every firewall related command. You should use it for **debugging purposes** only. | | `FIREWALL_DEBUG` | `off` | `on` or `off` | Prints every firewall related command. You should use it for **debugging purposes** only. |
### Shadowsocks ### Shadowsocks
@@ -368,15 +345,13 @@ There are various ways to achieve this, depending on your use case.
Note that [not all regions support port forwarding](https://www.privateinternetaccess.com/helpdesk/kb/articles/how-do-i-enable-port-forwarding-on-my-vpn). Note that [not all regions support port forwarding](https://www.privateinternetaccess.com/helpdesk/kb/articles/how-do-i-enable-port-forwarding-on-my-vpn).
When `PORT_FORWARDING=on`, a port will be forwarded on the VPN server side and written to the file specified by `PORT_FORWARDING_STATUS_FILE=/forwarded_port`. When `PORT_FORWARDING=on`, a port will be forwarded on the VPN server side and written to the file specified by `PORT_FORWARDING_STATUS_FILE=/forwarded_port`.
It can be useful to mount this file as a volume to read it from other containers, for example to configure a torrenting client. It can be useful to mount this file as a volume to read it from other containers, for example to configure a torrenting client.
You can also use the HTTP control server (see below) to get the port forwarded.
## HTTP control server ## HTTP control server
A built-in HTTP server listens on port `8000` to modify the state of the container. You have the following routes available: See [its Wiki page](https://github.com/qdm12/private-internet-access-docker/wiki/HTTP-control-server)
- `http://<your-docker-host-ip>:8000/openvpn/actions/restart` restarts the openvpn process
- `http://<your-docker-host-ip>:8000/unbound/actions/restart` re-downloads the DNS files (crypto and block lists) and restarts the unbound process
## Development and contributing ## Development and contributing

View File

@@ -16,6 +16,7 @@ import (
"github.com/qdm12/golibs/network" "github.com/qdm12/golibs/network"
"github.com/qdm12/private-internet-access-docker/internal/alpine" "github.com/qdm12/private-internet-access-docker/internal/alpine"
"github.com/qdm12/private-internet-access-docker/internal/cli" "github.com/qdm12/private-internet-access-docker/internal/cli"
"github.com/qdm12/private-internet-access-docker/internal/constants"
"github.com/qdm12/private-internet-access-docker/internal/dns" "github.com/qdm12/private-internet-access-docker/internal/dns"
"github.com/qdm12/private-internet-access-docker/internal/firewall" "github.com/qdm12/private-internet-access-docker/internal/firewall"
gluetunLogging "github.com/qdm12/private-internet-access-docker/internal/logging" gluetunLogging "github.com/qdm12/private-internet-access-docker/internal/logging"
@@ -56,8 +57,8 @@ func _main(background context.Context, args []string) int {
ctx, cancel := context.WithCancel(background) ctx, cancel := context.WithCancel(background)
defer cancel() defer cancel()
logger := createLogger() logger := createLogger()
wg := &sync.WaitGroup{}
fatalOnError := makeFatalOnError(logger, cancel, wg) fatalOnError := makeFatalOnError(logger, cancel)
client := network.NewClient(15 * time.Second) client := network.NewClient(15 * time.Second)
// Create configurators // Create configurators
@@ -104,6 +105,18 @@ func _main(background context.Context, args []string) int {
routingConf.SetDebug() routingConf.SetDebug()
} }
defaultInterface, defaultGateway, err := routingConf.DefaultRoute()
if err != nil {
fatalOnError(err)
}
localSubnet, err := routingConf.LocalSubnet()
if err != nil {
fatalOnError(err)
}
firewallConf.SetNetworkInformation(defaultInterface, defaultGateway, localSubnet)
if err := ovpnConf.CheckTUN(); err != nil { if err := ovpnConf.CheckTUN(); err != nil {
logger.Warn(err) logger.Warn(err)
err = ovpnConf.CreateTUN() err = ovpnConf.CreateTUN()
@@ -125,10 +138,19 @@ func _main(background context.Context, args []string) int {
err = firewallConf.SetAllowedSubnets(ctx, allSettings.Firewall.AllowedSubnets) err = firewallConf.SetAllowedSubnets(ctx, allSettings.Firewall.AllowedSubnets)
fatalOnError(err) fatalOnError(err)
for _, vpnPort := range allSettings.Firewall.VPNInputPorts {
err = firewallConf.SetAllowedPort(ctx, vpnPort, string(constants.TUN))
fatalOnError(err)
}
wg := &sync.WaitGroup{}
openvpnLooper := openvpn.NewLooper(allSettings.VPNSP, allSettings.OpenVPN, uid, gid, openvpnLooper := openvpn.NewLooper(allSettings.VPNSP, allSettings.OpenVPN, uid, gid,
ovpnConf, firewallConf, logger, client, fileManager, streamMerger, fatalOnError) ovpnConf, firewallConf, logger, client, fileManager, streamMerger, fatalOnError)
restartOpenvpn := openvpnLooper.Restart restartOpenvpn := openvpnLooper.Restart
portForward := openvpnLooper.PortForward portForward := openvpnLooper.PortForward
getOpenvpnSettings := openvpnLooper.GetSettings
getPortForwarded := openvpnLooper.GetPortForwarded
// wait for restartOpenvpn // wait for restartOpenvpn
go openvpnLooper.Run(ctx, wg) go openvpnLooper.Run(ctx, wg)
@@ -144,11 +166,11 @@ func _main(background context.Context, args []string) int {
go publicIPLooper.RunRestartTicker(ctx) go publicIPLooper.RunRestartTicker(ctx)
setPublicIPPeriod(allSettings.PublicIPPeriod) // call after RunRestartTicker setPublicIPPeriod(allSettings.PublicIPPeriod) // call after RunRestartTicker
tinyproxyLooper := tinyproxy.NewLooper(tinyProxyConf, firewallConf, allSettings.TinyProxy, logger, streamMerger, uid, gid) tinyproxyLooper := tinyproxy.NewLooper(tinyProxyConf, firewallConf, allSettings.TinyProxy, logger, streamMerger, uid, gid, defaultInterface)
restartTinyproxy := tinyproxyLooper.Restart restartTinyproxy := tinyproxyLooper.Restart
go tinyproxyLooper.Run(ctx, wg) go tinyproxyLooper.Run(ctx, wg)
shadowsocksLooper := shadowsocks.NewLooper(shadowsocksConf, firewallConf, allSettings.ShadowSocks, allSettings.DNS, logger, streamMerger, uid, gid) shadowsocksLooper := shadowsocks.NewLooper(shadowsocksConf, firewallConf, allSettings.ShadowSocks, allSettings.DNS, logger, streamMerger, uid, gid, defaultInterface)
restartShadowsocks := shadowsocksLooper.Restart restartShadowsocks := shadowsocksLooper.Restart
go shadowsocksLooper.Run(ctx, wg) go shadowsocksLooper.Run(ctx, wg)
@@ -176,7 +198,7 @@ func _main(background context.Context, args []string) int {
} }
}() }()
httpServer := server.New("0.0.0.0:8000", logger, restartOpenvpn, restartUnbound) httpServer := server.New("0.0.0.0:8000", logger, restartOpenvpn, restartUnbound, getOpenvpnSettings, getPortForwarded)
go httpServer.Run(ctx, wg) go httpServer.Run(ctx, wg)
// Start openvpn for the first time // Start openvpn for the first time
@@ -229,18 +251,11 @@ func _main(background context.Context, args []string) int {
return 0 return 0
} }
func makeFatalOnError(logger logging.Logger, cancel context.CancelFunc, wg *sync.WaitGroup) func(err error) { func makeFatalOnError(logger logging.Logger, cancel context.CancelFunc) func(err error) {
return func(err error) { return func(err error) {
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
cancel() cancel()
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
go func() {
wg.Wait()
cancel()
}()
<-ctx.Done() // either timeout or wait group completed
os.Exit(1)
} }
} }
} }

View File

@@ -0,0 +1,111 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"net/http"
"os"
"sort"
"strings"
"time"
"github.com/qdm12/golibs/network"
)
func main() {
os.Exit(_main())
}
func _main() int {
provider := flag.String("provider", "purevpn", "VPN provider to map location to subdomain, can be 'purevpn'")
flag.Parse()
client := network.NewClient(5 * time.Second)
switch *provider {
case "purevpn":
servers, warnings, err := purevpn(client)
if err != nil {
fmt.Println(err)
return 1
}
for _, server := range servers {
fmt.Printf(
"{subdomain: %q, region: %q, country: %q, city: %q},\n",
server.subdomain, server.region, server.country, server.city,
)
}
fmt.Print("\n\n")
for _, warning := range warnings {
fmt.Println(warning)
}
default:
fmt.Printf("Provider %q is not supported\n", *provider)
return 1
}
return 0
}
type purevpnServer struct {
region string
country string
city string
subdomain string // without -tcp or -udp suffix
}
func purevpn(client network.Client) (servers []purevpnServer, warnings []string, err error) {
content, status, err := client.GetContent("https://support.purevpn.com/vpn-servers")
if err != nil {
return nil, nil, err
} else if status != http.StatusOK {
return nil, nil, fmt.Errorf("HTTP status %d from Purevpn", status)
}
const jsonPrefix = "<script>var servers = "
const jsonSuffix = "</script>"
s := string(content)
jsonPrefixIndex := strings.Index(s, jsonPrefix)
if jsonPrefixIndex == -1 {
return nil, nil, fmt.Errorf("cannot find prefix %s in html", jsonPrefix)
}
if len(s[jsonPrefixIndex:]) == len(jsonPrefix) {
return nil, nil, fmt.Errorf("no body after json prefix %s", jsonPrefix)
}
s = s[jsonPrefixIndex+len(jsonPrefix):]
endIndex := strings.Index(s, jsonSuffix)
s = s[:endIndex]
var data []struct {
Region string `json:"region_name"`
Country string `json:"country_name"`
City string `json:"city_name"`
TCP string `json:"tcp"`
UDP string `json:"udp"`
}
if err := json.Unmarshal([]byte(s), &data); err != nil {
return nil, nil, err
}
sort.Slice(data, func(i, j int) bool {
if data[i].Region == data[j].Region {
if data[i].Country == data[j].Country {
return data[i].City < data[j].City
}
return data[i].Country < data[j].Country
}
return data[i].Region < data[j].Region
})
for i := range data {
if data[i].UDP == "" && data[i].TCP == "" {
warnings = append(warnings, fmt.Sprintf("server %s %s %s does not support TCP and UDP for openvpn", data[i].Region, data[i].Country, data[i].City))
continue
}
if data[i].UDP == "" || data[i].TCP == "" {
warnings = append(warnings, fmt.Sprintf("server %s %s %s does not support TCP or udp for openvpn", data[i].Region, data[i].Country, data[i].City))
}
servers = append(servers, purevpnServer{
region: data[i].Region,
country: data[i].Country,
city: data[i].City,
subdomain: strings.TrimSuffix(data[i].TCP, "-tcp.pointtoserver.com"),
})
}
return servers, warnings, nil
}

View File

@@ -18,7 +18,7 @@ func main() {
func _main(ctx context.Context) int { func _main(ctx context.Context) int {
resolverAddress := flag.String("resolver", "1.1.1.1", "DNS Resolver IP address to use") resolverAddress := flag.String("resolver", "1.1.1.1", "DNS Resolver IP address to use")
provider := flag.String("provider", "pia", "VPN provider to resolve for, 'pia', 'windscribe', 'cyberghost' or 'vyprvpn'") provider := flag.String("provider", "pia", "VPN provider to resolve for, 'pia', 'windscribe', 'cyberghost', 'vyprvpn' or 'purevpn'")
region := flag.String("region", "all", "Comma separated list of VPN provider region names to resolve for, use 'all' to resolve all") region := flag.String("region", "all", "Comma separated list of VPN provider region names to resolve for, use 'all' to resolve all")
flag.Parse() flag.Parse()
@@ -43,6 +43,9 @@ func _main(ctx context.Context) int {
case "vyprvpn": case "vyprvpn":
domain = "vyprvpn.com" domain = "vyprvpn.com"
servers = vyprvpnServers() servers = vyprvpnServers()
case "purevpn":
domain = "pointtoserver.com"
servers = purevpnServers()
default: default:
fmt.Printf("Provider %q is not supported\n", *provider) fmt.Printf("Provider %q is not supported\n", *provider)
return 1 return 1
@@ -135,6 +138,11 @@ func formatLine(provider string, s server, ips []net.IP) string {
"{Region: %q, IPs: []net.IP{%s}},", "{Region: %q, IPs: []net.IP{%s}},",
s.region, ipString, s.region, ipString,
) )
case "purevpn":
return fmt.Sprintf(
"{Region: %q, Country: %q, City: %q, IPs: []net.IP{%s}},",
s.region, s.country, s.city, ipString,
)
} }
return "" return ""
} }
@@ -197,6 +205,8 @@ type server struct {
subdomain string subdomain string
region string region string
group string // only for cyberghost group string // only for cyberghost
country string // only for purevpn
city string // only for purevpn
} }
func piaServers() []server { func piaServers() []server {
@@ -747,3 +757,164 @@ func vyprvpnServers() []server {
{subdomain: "vn1", region: "Vietnam"}, {subdomain: "vn1", region: "Vietnam"},
} }
} }
func purevpnServers() []server {
servers := []server{
{subdomain: "vlus-dz1-ovpn", region: "Africa", country: "Algeria", city: "Algiers"},
{subdomain: "vlus-ao1-ovpn", region: "Africa", country: "Angola", city: "Benguela"},
{subdomain: "vleu-cv-ovpn", region: "Africa", country: "Cape Verde", city: "Praia"},
{subdomain: "vlus-eg1-ovpn", region: "Africa", country: "Egypt", city: "Cairo"},
{subdomain: "et1-ovpn", region: "Africa", country: "Ethiopia", city: "Addis Ababa"},
{subdomain: "gh1-ovpn", region: "Africa", country: "Ghana", city: "Accra"},
{subdomain: "ke1-ovpn", region: "Africa", country: "Kenya", city: "Mombasa"},
{subdomain: "vlus-mg1-ovpn", region: "Africa", country: "Madagascar", city: "Antananarivo"},
{subdomain: "vlus-mr1-ovpn", region: "Africa", country: "Mauritania", city: "Nouakchott"},
{subdomain: "mu1-ovpn", region: "Africa", country: "Mauritius", city: "Port Louis"},
{subdomain: "ma1-ovpn", region: "Africa", country: "Morocco", city: "Rabat"},
{subdomain: "vlus-ne1-ovpn", region: "Africa", country: "Niger", city: "Niamey"},
{subdomain: "ng1-ovpn", region: "Africa", country: "Nigeria", city: "Suleja"},
{subdomain: "vlus-sn1-ovpn", region: "Africa", country: "Senegal", city: "Dakar"},
{subdomain: "sc1-ovpn", region: "Africa", country: "Seychelles", city: "Victoria"},
{subdomain: "za2-ovpn", region: "Africa", country: "South Africa", city: "Johannesburg"},
{subdomain: "vlus-tz1-ovpn", region: "Africa", country: "Tanzania", city: "Dar Es Salaam"},
{subdomain: "vlus-tn1-ovpn", region: "Africa", country: "Tunisia", city: "Tunis"},
{subdomain: "vlus-af1-ovpn", region: "Asia", country: "Afghanistan", city: "Kabul"},
{subdomain: "sg2-ovpn", region: "Asia", country: "Armenia", city: "Singapore"},
{subdomain: "az1-ovpn", region: "Asia", country: "Azerbaijan", city: "Baku"},
{subdomain: "vlus-bd1-ovpn", region: "Asia", country: "Bangladesh", city: "Dhaka"},
{subdomain: "bn2-ovpn", region: "Asia", country: "Brunei Darussalam", city: "Bandar Seri Begawan"},
{subdomain: "kh1-ovpn", region: "Asia", country: "Cambodia", city: "Phnom Penh"},
{subdomain: "hk2-ovpn", region: "Asia", country: "Hong Kong (SAR)", city: "Hong Kong"},
{subdomain: "in2-ovpn", region: "Asia", country: "India", city: "Chennai"},
{subdomain: "idn1-ovpn", region: "Asia", country: "Indonesia", city: "Jakarta"},
{subdomain: "jp-tk1-ovpn", region: "Asia", country: "Japan", city: "Tokyo"},
{subdomain: "vlus-kz1-ovpn", region: "Asia", country: "Kazakhstan", city: "Almaty"},
{subdomain: "kr2-ovpn", region: "Asia", country: "Korea, South", city: "Seoul"},
{subdomain: "vlus-kg1-ovpn", region: "Asia", country: "Kyrgyzstan", city: "Bishkek"},
{subdomain: "vlus-la1-ovpn", region: "Asia", country: "Laos", city: "Vientiane"},
{subdomain: "mo1-ovpn", region: "Asia", country: "Macao", city: "Beyrouth"},
{subdomain: "my2-ovpn", region: "Asia", country: "Malaysia", city: "Johor Baharu"},
{subdomain: "my-kl2-ovpn", region: "Asia", country: "Malaysia", city: "Kuala Lumpur"},
{subdomain: "vlus-mn1-ovpn", region: "Asia", country: "Mongolia", city: "Ulaanbaatar"},
{subdomain: "pk1-ovpn", region: "Asia", country: "Pakistan", city: "Islamabad"},
{subdomain: "vlus-pg1-ovpn", region: "Asia", country: "Papua New Guinea", city: "Port Moresby"},
{subdomain: "vlap-ph2-ovpn", region: "Asia", country: "Philippines", city: "Manila"},
{subdomain: "vlus-lk1-ovpn", region: "Asia", country: "Sri Lanka", city: "Colombo"},
{subdomain: "tw2-ovpn", region: "Asia", country: "Taiwan", city: "Taipei"},
{subdomain: "vlus-tj-ovpn", region: "Asia", country: "Tajikistan", city: "Dushanbe"},
{subdomain: "vlap-th2-ovpn", region: "Asia", country: "Thailand", city: "Bangkok"},
{subdomain: "tr2-ovpn", region: "Asia", country: "Turkey", city: "Istanbul"},
{subdomain: "vlus-tm1-ovpn", region: "Asia", country: "Turkmenistan", city: "Ashgabat"},
{subdomain: "vlus-uz-ovpn", region: "Asia", country: "Uzbekistan", city: "Tashkent"},
{subdomain: "vlap-vn2-ovpn", region: "Asia", country: "Vietnam", city: "Hanoi"},
{subdomain: "al1-ovpn", region: "Europe", country: "Albania", city: "Tirane"},
{subdomain: "vleu-am1-ovpn", region: "Europe", country: "Armenia", city: "Yerevan"},
{subdomain: "at2-ovpn", region: "Europe", country: "Austria", city: "Vienna"},
{subdomain: "vleu-be2-ovpn", region: "Europe", country: "Belgium", city: "Brussels"},
{subdomain: "ba1-ovpn", region: "Europe", country: "Bosnia and Herzegovina", city: "Sarajevo"},
{subdomain: "bg2-ovpn", region: "Europe", country: "Bulgaria", city: "Sofia"},
{subdomain: "vlus-hr1-ovpn", region: "Europe", country: "Croatia", city: "Zagreb"},
{subdomain: "cy1-ovpn", region: "Europe", country: "Cyprus", city: "Nicosia"},
{subdomain: "dk2-ovpn", region: "Europe", country: "Denmark", city: "Copenhagen"},
{subdomain: "ee1-ovpn", region: "Europe", country: "Estonia", city: "Tallinn"},
{subdomain: "fr2-ovpn", region: "Europe", country: "France", city: "Paris"},
{subdomain: "vlus-ge1-ovpn", region: "Europe", country: "Georgia", city: "Tbilisi"},
{subdomain: "de2-ovpn", region: "Europe", country: "Germany", city: "Frankfurt"},
{subdomain: "de2-ovpn", region: "Europe", country: "Germany", city: "Munich"},
{subdomain: "de-ao1-ovpn", region: "Europe", country: "Germany", city: "Nuremberg"},
{subdomain: "gr2-ovpn", region: "Europe", country: "Greece", city: "Thessaloniki"},
{subdomain: "hu2-ovpn", region: "Europe", country: "Hungary", city: "Budapest"},
{subdomain: "is1-ovpn", region: "Europe", country: "Iceland", city: "Reykjavik"},
{subdomain: "ie2-ovpn", region: "Europe", country: "Ireland", city: "Dublin"},
{subdomain: "im1-ovpn", region: "Europe", country: "Isle of Man", city: "Onchan"},
{subdomain: "vlus-it1-ovpn", region: "Europe", country: "Italy", city: "Milano"},
{subdomain: "lv1-ovpn", region: "Europe", country: "Latvia", city: "RIGA"},
{subdomain: "li1-ovpn", region: "Europe", country: "Liechtenstein", city: "Vaduz"},
{subdomain: "lt1-ovpn", region: "Europe", country: "Lithuania", city: "Vilnius"},
{subdomain: "lu2-ovpn", region: "Europe", country: "Luxembourg", city: "Luxembourg"},
{subdomain: "mt1-ovpn", region: "Europe", country: "Malta", city: "Sliema"},
{subdomain: "mn1-ovpn", region: "Europe", country: "Monaco", city: "Monaco"},
{subdomain: "vleu-me1-ovpn", region: "Europe", country: "Montenegro", city: "Podgorica"},
{subdomain: "nl2-ovpn", region: "Europe", country: "Netherlands", city: "Amsterdam"},
{subdomain: "vleu-no2-ovpn", region: "Europe", country: "Norway", city: "Oslo"},
{subdomain: "pl2-ovpn", region: "Europe", country: "Poland", city: "Warsaw"},
{subdomain: "pt2-ovpn", region: "Europe", country: "Portugal", city: "Lisbon"},
{subdomain: "ro2-ovpn", region: "Europe", country: "Romania", city: "Bucharest"},
{subdomain: "rs2-ovpn", region: "Europe", country: "Serbia", city: "Niš"},
{subdomain: "sk1-ovpn", region: "Europe", country: "Slovakia", city: "Bratislava"},
{subdomain: "si1-ovpn", region: "Europe", country: "Slovenia", city: "Ljubljana"},
{subdomain: "es-ovpn", region: "Europe", country: "Spain", city: "Barcelona"},
{subdomain: "vlus-se1-ovpn", region: "Europe", country: "Sweden", city: "Stockholm"},
{subdomain: "ch2-ovpn", region: "Europe", country: "Switzerland", city: "Zurich"},
{subdomain: "ukg2-ovpn", region: "Europe", country: "United Kingdom", city: "Gosport"},
{subdomain: "ukl2-ovpn", region: "Europe", country: "United Kingdom", city: "London"},
{subdomain: "ukm2-ovpn", region: "Europe", country: "United Kingdom", city: "Maidenhead"},
{subdomain: "vlus-uk-man1-ovpn", region: "Europe", country: "United Kingdom", city: "Manchester"},
{subdomain: "bh-ovpn", region: "Middle East", country: "Bahrain", city: "Manama"},
{subdomain: "vlus-jo1-ovpn", region: "Middle East", country: "Jordan", city: "Amman"},
{subdomain: "vlus-kw1-ovpn", region: "Middle East", country: "Kuwait", city: "Kuwait"},
{subdomain: "om1-ovpn", region: "Middle East", country: "Oman", city: "Salalah"},
{subdomain: "qa1-ovpn", region: "Middle East", country: "Qatar", city: "Doha"},
{subdomain: "sa1-ovpn", region: "Middle East", country: "Saudi Arabia", city: "Jeddah"},
{subdomain: "ae2-ovpn", region: "Middle East", country: "United Arab Emirates", city: "Dubai"},
{subdomain: "aw1-ovpn", region: "North America", country: "Aruba", city: "Oranjestad"},
{subdomain: "vleu-bb-ovpn", region: "North America", country: "Barbados", city: "Bridgetown"},
{subdomain: "bz1-ovpn", region: "North America", country: "Belize", city: "Belmopan"},
{subdomain: "vleu-bm-ovpn", region: "North America", country: "Bermuda", city: "Hamilton"},
{subdomain: "caq1-ovpn", region: "North America", country: "Canada", city: "Montreal"},
{subdomain: "cato-ovpn", region: "North America", country: "Canada", city: "Toronto"},
{subdomain: "cav2-ovpn", region: "North America", country: "Canada", city: "Vancouver"},
{subdomain: "vleu-ky-ovpn", region: "North America", country: "Cayman Islands", city: "George Town"},
{subdomain: "vlus-cr1-ovpn", region: "North America", country: "Costa Rica", city: "San Jose"},
{subdomain: "vleu-dm-ovpn", region: "North America", country: "Dominica", city: "Roseau"},
{subdomain: "vleu-do-ovpn", region: "North America", country: "Dominican Republic", city: "Santo Domingo"},
{subdomain: "vleu-sv-ovpn", region: "North America", country: "El Salvador", city: "San Salvador"},
{subdomain: "vleu-gd-ovpn", region: "North America", country: "Grenada", city: "St George's"},
{subdomain: "vleu-gt-ovpn", region: "North America", country: "Guatemala", city: "Guatemala"},
{subdomain: "vleu-ht1-ovpn", region: "North America", country: "Haiti", city: "PORT-AU-PRINCE"},
{subdomain: "vleu-hn-ovpn", region: "North America", country: "Honduras", city: "TEGUCIGALPA"},
{subdomain: "jm1-ovpn", region: "North America", country: "Jamaica", city: "Kingston"},
{subdomain: "vlus-mx2-ovpn", region: "North America", country: "Mexico", city: "Mexico City"},
{subdomain: "vleu-ms-ovpn", region: "North America", country: "Montserrat", city: "plymouth"},
{subdomain: "pr1-ovpn", region: "North America", country: "Puerto Rico", city: "San Juan"},
{subdomain: "vleu-lc-ovpn", region: "North America", country: "Saint Lucia", city: "Castries"},
{subdomain: "bs1-ovpn", region: "North America", country: "The Bahamas", city: "Freeport"},
{subdomain: "vleu-tt-ovpn", region: "North America", country: "Trinidad and Tobago", city: "Port of Spain"},
{subdomain: "vleu-tc-ovpn", region: "North America", country: "Turks and Caicos Islands", city: "Balfour Town"},
{subdomain: "usva-ovpn", region: "North America", country: "United States", city: "Ashburn"},
{subdomain: "usil2-ovpn", region: "North America", country: "United States", city: "Chicago"},
{subdomain: "usoh1-ovpn", region: "North America", country: "United States", city: "Columbus"},
{subdomain: "usga2-ovpn", region: "North America", country: "United States", city: "Georgia"},
{subdomain: "ustx2-ovpn", region: "North America", country: "United States", city: "Houston"},
{subdomain: "usla2-ovpn", region: "North America", country: "United States", city: "Los Angeles"},
{subdomain: "usfl2-ovpn", region: "North America", country: "United States", city: "Miami"},
{subdomain: "usnj2-ovpn", region: "North America", country: "United States", city: "New Jersey"},
{subdomain: "usny2-ovpn", region: "North America", country: "United States", city: "New York"},
{subdomain: "usphx2-ovpn", region: "North America", country: "United States", city: "Phoenix"},
{subdomain: "usut2-ovpn", region: "North America", country: "United States", city: "Salt Lake City"},
{subdomain: "ussf2-ovpn", region: "North America", country: "United States", city: "San Francisco"},
{subdomain: "ussa-ovpn", region: "North America", country: "United States", city: "Seattle"},
{subdomain: "uswdc2-ovpn", region: "North America", country: "United States", city: "Washington, D.C."},
{subdomain: "au-bn-ovpn", region: "Oceania", country: "Australia", city: "Brisbane"},
{subdomain: "au-me1-ovpn", region: "Oceania", country: "Australia", city: "Melbourne"},
{subdomain: "au2-pe-ovpn", region: "Oceania", country: "Australia", city: "Perth"},
{subdomain: "au-sd2-ovpn", region: "Oceania", country: "Australia", city: "Sydney"},
{subdomain: "nz2-ovpn", region: "Oceania", country: "New Zealand", city: "Auckland"},
{subdomain: "vlus-ar1-ovpn", region: "South America", country: "Argentina", city: "Buenos Aires"},
{subdomain: "vleu-bo-ovpn", region: "South America", country: "Bolivia", city: "Sucre"},
{subdomain: "br2-ovpn", region: "South America", country: "Brazil", city: "Sao Paulo"},
{subdomain: "vg1-ovpn", region: "South America", country: "British Virgin Island", city: "Road Town"},
{subdomain: "vlbr-cl-ovpn", region: "South America", country: "Chile", city: "Santiago"},
{subdomain: "co1-ovpn", region: "South America", country: "Colombia", city: "Bogota"},
{subdomain: "ec1-ovpn", region: "South America", country: "Ecuador", city: "Quito"},
{subdomain: "vleu-gy-ovpn", region: "South America", country: "Guyana", city: "Georgetown"},
{subdomain: "pa2-ovpn", region: "South America", country: "Panama", city: "Panama City"},
{subdomain: "vleu-py-ovpn", region: "South America", country: "Paraguay", city: "Asuncion"},
{subdomain: "pe1-ovpn", region: "South America", country: "Peru", city: "Lima"},
{subdomain: "vleu-sr-ovpn", region: "South America", country: "Suriname", city: "Paramaribo"},
}
for i := range servers {
servers[i].subdomain += "-udp"
}
return servers
}

View File

@@ -13,7 +13,7 @@ const (
func MullvadCountryChoices() (choices []string) { func MullvadCountryChoices() (choices []string) {
uniqueChoices := map[string]struct{}{} uniqueChoices := map[string]struct{}{}
for _, server := range mullvadServers() { for _, server := range MullvadServers() {
uniqueChoices[server.Country] = struct{}{} uniqueChoices[server.Country] = struct{}{}
} }
for choice := range uniqueChoices { for choice := range uniqueChoices {
@@ -27,7 +27,7 @@ func MullvadCountryChoices() (choices []string) {
func MullvadCityChoices() (choices []string) { func MullvadCityChoices() (choices []string) {
uniqueChoices := map[string]struct{}{} uniqueChoices := map[string]struct{}{}
for _, server := range mullvadServers() { for _, server := range MullvadServers() {
uniqueChoices[server.City] = struct{}{} uniqueChoices[server.City] = struct{}{}
} }
for choice := range uniqueChoices { for choice := range uniqueChoices {
@@ -41,7 +41,7 @@ func MullvadCityChoices() (choices []string) {
func MullvadISPChoices() (choices []string) { func MullvadISPChoices() (choices []string) {
uniqueChoices := map[string]struct{}{} uniqueChoices := map[string]struct{}{}
for _, server := range mullvadServers() { for _, server := range MullvadServers() {
uniqueChoices[server.ISP] = struct{}{} uniqueChoices[server.ISP] = struct{}{}
} }
for choice := range uniqueChoices { for choice := range uniqueChoices {
@@ -53,25 +53,7 @@ func MullvadISPChoices() (choices []string) {
return choices return choices
} }
func MullvadServerFilter(country, city, isp string) (servers []models.MullvadServer) { func MullvadServers() []models.MullvadServer {
for _, server := range mullvadServers() {
if len(country) == 0 {
server.Country = ""
}
if len(city) == 0 {
server.City = ""
}
if len(isp) == 0 {
server.ISP = ""
}
if server.Country == country && server.City == city && server.ISP == isp {
servers = append(servers, server)
}
}
return servers
}
func mullvadServers() []models.MullvadServer {
return []models.MullvadServer{ return []models.MullvadServer{
{ {
Country: "united arab emirates", Country: "united arab emirates",

View File

@@ -26,56 +26,56 @@ func PIAGeoChoices() (choices []string) {
func PIAServers() []models.PIAServer { func PIAServers() []models.PIAServer {
return []models.PIAServer{ return []models.PIAServer{
{Region: "AU Melbourne", IPs: []net.IP{{27, 50, 82, 131}, {27, 50, 82, 133}, {43, 250, 204, 81}, {43, 250, 204, 83}, {43, 250, 204, 87}, {43, 250, 204, 89}, {43, 250, 204, 91}, {43, 250, 204, 93}, {43, 250, 204, 97}, {43, 250, 204, 99}, {43, 250, 204, 101}, {43, 250, 204, 103}, {43, 250, 204, 107}, {43, 250, 204, 111}, {43, 250, 204, 117}, {43, 250, 204, 119}, {43, 250, 204, 123}, {43, 250, 204, 125}, {118, 127, 62, 227}, {221, 121, 139, 175}}}, {Region: "AU Melbourne", IPs: []net.IP{{27, 50, 82, 131}, {27, 50, 82, 133}, {43, 250, 204, 83}, {43, 250, 204, 87}, {43, 250, 204, 89}, {43, 250, 204, 91}, {43, 250, 204, 93}, {43, 250, 204, 95}, {43, 250, 204, 97}, {43, 250, 204, 101}, {43, 250, 204, 103}, {43, 250, 204, 105}, {43, 250, 204, 107}, {43, 250, 204, 109}, {43, 250, 204, 111}, {43, 250, 204, 113}, {43, 250, 204, 115}, {43, 250, 204, 117}, {43, 250, 204, 123}, {43, 250, 204, 125}, {118, 127, 62, 227}, {221, 121, 139, 175}}},
{Region: "AU Perth", IPs: []net.IP{{43, 250, 205, 59}, {43, 250, 205, 89}, {43, 250, 205, 91}, {43, 250, 205, 93}, {43, 250, 205, 95}}}, {Region: "AU Perth", IPs: []net.IP{{43, 250, 205, 59}, {43, 250, 205, 91}, {43, 250, 205, 95}}},
{Region: "AU Sydney", IPs: []net.IP{{27, 50, 77, 247}, {103, 13, 102, 113}, {103, 13, 102, 115}, {103, 13, 102, 117}, {103, 13, 102, 119}, {103, 13, 102, 121}, {103, 13, 102, 123}, {103, 13, 102, 127}, {118, 127, 60, 53}, {118, 127, 60, 61}, {221, 121, 145, 131}, {221, 121, 145, 133}, {221, 121, 145, 143}, {221, 121, 145, 151}, {221, 121, 146, 203}, {221, 121, 152, 215}}}, {Region: "AU Sydney", IPs: []net.IP{{103, 13, 102, 113}, {103, 13, 102, 115}, {103, 13, 102, 117}, {103, 13, 102, 119}, {103, 13, 102, 121}, {103, 13, 102, 123}, {103, 13, 102, 127}, {118, 127, 60, 53}, {118, 127, 60, 61}, {221, 121, 145, 133}, {221, 121, 145, 143}, {221, 121, 146, 203}, {221, 121, 152, 215}}},
{Region: "Austria", IPs: []net.IP{{185, 210, 219, 147}, {185, 210, 219, 154}, {185, 210, 219, 156}, {185, 216, 34, 226}, {185, 216, 34, 228}, {185, 216, 34, 229}, {185, 216, 34, 230}, {185, 216, 34, 232}, {185, 216, 34, 233}, {185, 216, 34, 236}, {185, 216, 34, 237}, {185, 216, 34, 238}}}, {Region: "Austria", IPs: []net.IP{{185, 210, 219, 147}, {185, 210, 219, 154}, {185, 210, 219, 155}, {185, 210, 219, 156}, {185, 216, 34, 226}, {185, 216, 34, 227}, {185, 216, 34, 228}, {185, 216, 34, 229}, {185, 216, 34, 230}, {185, 216, 34, 232}, {185, 216, 34, 236}, {185, 216, 34, 237}, {185, 216, 34, 238}}},
{Region: "Belgium", IPs: []net.IP{{77, 243, 191, 19}, {77, 243, 191, 21}, {77, 243, 191, 22}, {77, 243, 191, 23}, {77, 243, 191, 26}, {77, 243, 191, 27}, {185, 104, 186, 26}, {185, 232, 21, 26}, {185, 232, 21, 29}}}, {Region: "Belgium", IPs: []net.IP{{77, 243, 191, 19}, {77, 243, 191, 20}, {77, 243, 191, 21}, {77, 243, 191, 22}, {77, 243, 191, 26}, {77, 243, 191, 27}, {185, 232, 21, 26}, {185, 232, 21, 27}, {185, 232, 21, 28}, {185, 232, 21, 29}}},
{Region: "CA Montreal", IPs: []net.IP{{199, 229, 249, 142}}}, {Region: "CA Montreal", IPs: []net.IP{{199, 229, 249, 132}, {199, 229, 249, 134}, {199, 229, 249, 137}, {199, 229, 249, 142}, {199, 229, 249, 144}, {199, 229, 249, 146}, {199, 229, 249, 149}, {199, 229, 249, 151}, {199, 229, 249, 152}, {199, 229, 249, 155}, {199, 229, 249, 159}, {199, 229, 249, 165}, {199, 229, 249, 169}, {199, 229, 249, 172}, {199, 229, 249, 176}, {199, 229, 249, 182}, {199, 229, 249, 183}, {199, 229, 249, 184}, {199, 229, 249, 185}, {199, 229, 249, 191}}},
{Region: "CA Toronto", IPs: []net.IP{{172, 98, 67, 37}, {172, 98, 67, 41}, {172, 98, 67, 45}, {172, 98, 67, 47}, {172, 98, 67, 48}, {172, 98, 67, 55}, {172, 98, 67, 56}, {172, 98, 67, 61}, {172, 98, 67, 64}, {172, 98, 67, 65}, {172, 98, 67, 68}, {172, 98, 67, 71}, {172, 98, 67, 73}, {172, 98, 67, 81}, {172, 98, 67, 85}, {172, 98, 67, 89}, {172, 98, 67, 91}, {172, 98, 67, 93}, {172, 98, 67, 95}, {172, 98, 67, 99}}}, {Region: "CA Toronto", IPs: []net.IP{{172, 98, 67, 53}, {172, 98, 67, 71}, {172, 98, 67, 87}, {172, 98, 67, 100}}},
{Region: "CA Vancouver", IPs: []net.IP{{172, 83, 40, 107}}}, {Region: "CA Vancouver", IPs: []net.IP{{107, 181, 189, 71}, {107, 181, 189, 74}, {107, 181, 189, 75}, {107, 181, 189, 85}, {107, 181, 189, 86}, {172, 83, 40, 18}, {172, 83, 40, 20}, {172, 83, 40, 23}, {172, 83, 40, 24}, {172, 83, 40, 98}, {172, 83, 40, 99}, {172, 83, 40, 100}, {172, 83, 40, 101}, {172, 83, 40, 105}, {172, 83, 40, 106}, {172, 83, 40, 109}, {172, 83, 40, 110}, {172, 83, 40, 111}, {172, 83, 40, 113}, {172, 83, 40, 115}}},
{Region: "Czech Republic", IPs: []net.IP{{89, 238, 186, 226}, {89, 238, 186, 227}, {89, 238, 186, 228}, {89, 238, 186, 229}, {89, 238, 186, 230}, {185, 216, 35, 66}, {185, 216, 35, 67}, {185, 216, 35, 69}, {185, 216, 35, 70}, {185, 242, 6, 27}, {185, 242, 6, 28}, {185, 242, 6, 29}, {185, 242, 6, 30}}}, {Region: "Czech Republic", IPs: []net.IP{{89, 238, 186, 226}, {89, 238, 186, 227}, {89, 238, 186, 228}, {89, 238, 186, 230}, {185, 216, 35, 66}, {185, 216, 35, 67}, {185, 216, 35, 68}, {185, 216, 35, 69}, {185, 216, 35, 70}, {185, 242, 6, 26}, {185, 242, 6, 27}, {185, 242, 6, 28}, {185, 242, 6, 29}, {185, 242, 6, 30}}},
{Region: "DE Berlin", IPs: []net.IP{{185, 230, 127, 227}, {185, 230, 127, 228}, {185, 230, 127, 231}, {185, 230, 127, 232}, {185, 230, 127, 233}, {185, 230, 127, 234}, {185, 230, 127, 236}, {185, 230, 127, 239}, {185, 230, 127, 240}, {185, 230, 127, 241}, {193, 176, 86, 124}, {193, 176, 86, 130}, {193, 176, 86, 142}, {193, 176, 86, 146}, {193, 176, 86, 150}, {193, 176, 86, 154}, {193, 176, 86, 166}, {193, 176, 86, 174}, {193, 176, 86, 178}, {193, 176, 86, 182}}}, {Region: "DE Berlin", IPs: []net.IP{{185, 230, 127, 226}, {185, 230, 127, 229}, {185, 230, 127, 230}, {185, 230, 127, 233}, {185, 230, 127, 234}, {185, 230, 127, 235}, {185, 230, 127, 237}, {185, 230, 127, 239}, {185, 230, 127, 240}, {185, 230, 127, 242}, {185, 230, 127, 243}, {193, 176, 86, 122}, {193, 176, 86, 125}, {193, 176, 86, 134}, {193, 176, 86, 146}, {193, 176, 86, 150}, {193, 176, 86, 154}, {193, 176, 86, 162}, {193, 176, 86, 166}, {193, 176, 86, 174}}},
{Region: "DE Frankfurt", IPs: []net.IP{{185, 220, 70, 134}, {185, 220, 70, 135}, {185, 220, 70, 136}, {185, 220, 70, 139}, {185, 220, 70, 140}, {185, 220, 70, 141}, {185, 220, 70, 143}, {185, 220, 70, 144}, {185, 220, 70, 145}, {185, 220, 70, 147}, {185, 220, 70, 148}, {185, 220, 70, 152}, {185, 220, 70, 153}, {185, 220, 70, 155}, {185, 220, 70, 163}, {185, 220, 70, 164}, {185, 220, 70, 167}, {185, 220, 70, 170}, {185, 220, 70, 171}, {185, 220, 70, 173}}}, {Region: "DE Frankfurt", IPs: []net.IP{{185, 220, 70, 133}, {185, 220, 70, 134}, {185, 220, 70, 135}, {185, 220, 70, 136}, {185, 220, 70, 137}, {185, 220, 70, 138}, {185, 220, 70, 141}, {185, 220, 70, 143}, {185, 220, 70, 144}, {185, 220, 70, 147}, {185, 220, 70, 148}, {185, 220, 70, 149}, {185, 220, 70, 150}, {185, 220, 70, 152}, {185, 220, 70, 163}, {185, 220, 70, 167}, {185, 220, 70, 170}, {185, 220, 70, 171}, {185, 220, 70, 172}, {185, 220, 70, 173}}},
{Region: "Denmark", IPs: []net.IP{{82, 102, 20, 162}, {82, 102, 20, 163}, {82, 102, 20, 164}, {82, 102, 20, 165}, {82, 102, 20, 167}, {82, 102, 20, 168}, {82, 102, 20, 169}, {82, 102, 20, 170}, {82, 102, 20, 171}, {82, 102, 20, 172}, {82, 102, 20, 173}, {82, 102, 20, 174}, {82, 102, 20, 175}, {82, 102, 20, 178}, {82, 102, 20, 179}, {82, 102, 20, 180}, {82, 102, 20, 181}, {82, 102, 20, 182}, {82, 102, 20, 183}}}, {Region: "Denmark", IPs: []net.IP{{82, 102, 20, 162}, {82, 102, 20, 164}, {82, 102, 20, 165}, {82, 102, 20, 167}, {82, 102, 20, 169}, {82, 102, 20, 170}, {82, 102, 20, 171}, {82, 102, 20, 172}, {82, 102, 20, 173}, {82, 102, 20, 174}, {82, 102, 20, 175}, {82, 102, 20, 176}, {82, 102, 20, 177}, {82, 102, 20, 178}, {82, 102, 20, 179}, {82, 102, 20, 180}, {82, 102, 20, 181}, {82, 102, 20, 183}, {82, 102, 20, 184}, {82, 102, 20, 230}}},
{Region: "Finlan", IPs: []net.IP{{196, 244, 191, 2}, {196, 244, 191, 10}, {196, 244, 191, 42}, {196, 244, 191, 50}, {196, 244, 191, 58}, {196, 244, 191, 66}, {196, 244, 191, 74}, {196, 244, 191, 82}, {196, 244, 191, 90}, {196, 244, 191, 98}, {196, 244, 191, 106}, {196, 244, 191, 114}, {196, 244, 191, 138}, {196, 244, 191, 146}}}, {Region: "Finlan", IPs: []net.IP{{196, 244, 191, 2}, {196, 244, 191, 10}, {196, 244, 191, 18}, {196, 244, 191, 26}, {196, 244, 191, 34}, {196, 244, 191, 42}, {196, 244, 191, 50}, {196, 244, 191, 58}, {196, 244, 191, 66}, {196, 244, 191, 82}, {196, 244, 191, 90}, {196, 244, 191, 98}, {196, 244, 191, 106}, {196, 244, 191, 114}, {196, 244, 191, 122}, {196, 244, 191, 138}, {196, 244, 191, 146}}},
{Region: "France", IPs: []net.IP{{194, 187, 249, 35}, {194, 187, 249, 37}, {194, 187, 249, 39}, {194, 187, 249, 42}, {194, 187, 249, 45}, {194, 187, 249, 48}, {194, 187, 249, 49}, {194, 187, 249, 52}, {194, 187, 249, 53}, {194, 187, 249, 56}, {194, 187, 249, 59}, {194, 187, 249, 60}, {194, 187, 249, 61}, {194, 187, 249, 178}, {194, 187, 249, 179}, {194, 187, 249, 180}, {194, 187, 249, 183}, {194, 187, 249, 184}, {194, 187, 249, 187}, {194, 187, 249, 190}}}, {Region: "France", IPs: []net.IP{{194, 99, 106, 147}, {194, 99, 106, 148}, {194, 99, 106, 149}, {194, 187, 249, 35}, {194, 187, 249, 36}, {194, 187, 249, 37}, {194, 187, 249, 38}, {194, 187, 249, 44}, {194, 187, 249, 46}, {194, 187, 249, 50}, {194, 187, 249, 53}, {194, 187, 249, 57}, {194, 187, 249, 58}, {194, 187, 249, 60}, {194, 187, 249, 61}, {194, 187, 249, 62}, {194, 187, 249, 178}, {194, 187, 249, 183}, {194, 187, 249, 184}, {194, 187, 249, 186}}},
{Region: "Hong Kong", IPs: []net.IP{{84, 17, 37, 1}, {84, 17, 37, 45}, {119, 81, 135, 2}, {119, 81, 135, 28}, {119, 81, 135, 29}, {119, 81, 135, 47}, {119, 81, 135, 51}, {119, 81, 135, 53}, {119, 81, 253, 212}, {119, 81, 253, 218}, {119, 81, 253, 226}, {119, 81, 253, 227}, {119, 81, 253, 229}, {119, 81, 253, 230}, {119, 81, 253, 241}, {161, 202, 39, 202}, {161, 202, 39, 240}, {161, 202, 39, 251}, {161, 202, 44, 94}}}, {Region: "Hong Kong", IPs: []net.IP{{84, 17, 37, 23}, {84, 17, 37, 45}, {119, 81, 135, 5}, {119, 81, 135, 18}, {119, 81, 135, 26}, {119, 81, 135, 28}, {119, 81, 135, 29}, {119, 81, 135, 47}, {119, 81, 135, 51}, {119, 81, 135, 53}, {119, 81, 253, 214}, {119, 81, 253, 218}, {119, 81, 253, 229}, {119, 81, 253, 230}, {119, 81, 253, 241}, {119, 81, 253, 242}, {161, 202, 39, 202}, {161, 202, 39, 240}, {161, 202, 39, 251}, {161, 202, 44, 94}}},
{Region: "Hungary", IPs: []net.IP{{185, 128, 26, 18}, {185, 128, 26, 19}, {185, 128, 26, 20}, {185, 128, 26, 21}, {185, 128, 26, 22}, {185, 128, 26, 23}, {185, 128, 26, 24}, {185, 189, 114, 98}}}, {Region: "Hungary", IPs: []net.IP{{185, 128, 26, 18}, {185, 128, 26, 19}, {185, 128, 26, 20}, {185, 128, 26, 21}, {185, 128, 26, 22}, {185, 128, 26, 23}, {185, 128, 26, 24}, {185, 189, 114, 98}}},
{Region: "India", IPs: []net.IP{{150, 242, 12, 155}, {150, 242, 12, 171}, {150, 242, 12, 187}}}, {Region: "India", IPs: []net.IP{{150, 242, 12, 155}, {150, 242, 12, 171}, {150, 242, 12, 187}}},
{Region: "Ireland", IPs: []net.IP{{23, 92, 127, 2}, {23, 92, 127, 10}, {23, 92, 127, 18}, {23, 92, 127, 34}, {23, 92, 127, 42}, {23, 92, 127, 58}}}, {Region: "Ireland", IPs: []net.IP{{23, 92, 127, 2}, {23, 92, 127, 10}, {23, 92, 127, 18}, {23, 92, 127, 34}, {23, 92, 127, 42}, {23, 92, 127, 50}, {23, 92, 127, 58}, {23, 92, 127, 66}}},
{Region: "Israel", IPs: []net.IP{{31, 168, 172, 136}, {31, 168, 172, 142}, {31, 168, 172, 143}, {31, 168, 172, 145}, {31, 168, 172, 147}}}, {Region: "Israel", IPs: []net.IP{{31, 168, 172, 136}, {31, 168, 172, 142}, {31, 168, 172, 143}, {31, 168, 172, 145}, {31, 168, 172, 146}, {31, 168, 172, 147}}},
{Region: "Italy", IPs: []net.IP{{82, 102, 21, 98}, {82, 102, 21, 210}, {82, 102, 21, 211}, {82, 102, 21, 212}, {82, 102, 21, 213}, {82, 102, 21, 214}, {82, 102, 21, 215}, {82, 102, 21, 216}, {82, 102, 21, 217}, {82, 102, 21, 218}, {82, 102, 21, 219}}}, {Region: "Italy", IPs: []net.IP{{82, 102, 21, 98}, {82, 102, 21, 210}, {82, 102, 21, 212}, {82, 102, 21, 213}, {82, 102, 21, 214}, {82, 102, 21, 215}, {82, 102, 21, 216}, {82, 102, 21, 217}, {82, 102, 21, 218}, {82, 102, 21, 219}}},
{Region: "Japan", IPs: []net.IP{{103, 208, 220, 135}, {103, 208, 220, 136}, {103, 208, 220, 138}, {103, 208, 220, 139}, {103, 208, 220, 141}}}, {Region: "Japan", IPs: []net.IP{{103, 208, 220, 130}, {103, 208, 220, 131}, {103, 208, 220, 132}, {103, 208, 220, 133}, {103, 208, 220, 134}, {103, 208, 220, 135}, {103, 208, 220, 136}, {103, 208, 220, 137}, {103, 208, 220, 138}, {103, 208, 220, 139}, {103, 208, 220, 140}, {103, 208, 220, 141}, {103, 208, 220, 142}, {103, 208, 220, 143}}},
{Region: "Luxembourg", IPs: []net.IP{{92, 223, 89, 133}, {92, 223, 89, 134}, {92, 223, 89, 135}, {92, 223, 89, 136}, {92, 223, 89, 137}, {92, 223, 89, 138}, {92, 223, 89, 140}, {92, 223, 89, 142}}}, {Region: "Luxembourg", IPs: []net.IP{{92, 223, 89, 133}, {92, 223, 89, 134}, {92, 223, 89, 135}, {92, 223, 89, 136}, {92, 223, 89, 137}, {92, 223, 89, 138}, {92, 223, 89, 140}, {92, 223, 89, 142}}},
{Region: "Mexico", IPs: []net.IP{{169, 57, 0, 197}, {169, 57, 0, 200}, {169, 57, 0, 203}, {169, 57, 0, 205}, {169, 57, 0, 207}, {169, 57, 0, 210}, {169, 57, 0, 211}, {169, 57, 0, 212}, {169, 57, 0, 213}, {169, 57, 0, 217}, {169, 57, 0, 218}, {169, 57, 0, 221}, {169, 57, 0, 229}, {169, 57, 0, 230}, {169, 57, 0, 233}, {169, 57, 0, 236}, {169, 57, 0, 238}, {169, 57, 0, 243}, {169, 57, 0, 247}, {169, 57, 0, 248}}}, {Region: "Mexico", IPs: []net.IP{{169, 57, 0, 197}, {169, 57, 0, 200}, {169, 57, 0, 203}, {169, 57, 0, 205}, {169, 57, 0, 207}, {169, 57, 0, 210}, {169, 57, 0, 211}, {169, 57, 0, 212}, {169, 57, 0, 213}, {169, 57, 0, 217}, {169, 57, 0, 218}, {169, 57, 0, 221}, {169, 57, 0, 229}, {169, 57, 0, 230}, {169, 57, 0, 233}, {169, 57, 0, 236}, {169, 57, 0, 238}, {169, 57, 0, 243}, {169, 57, 0, 247}, {169, 57, 0, 248}}},
{Region: "Netherlands", IPs: []net.IP{{46, 166, 138, 145}, {46, 166, 138, 146}, {46, 166, 138, 155}, {46, 166, 138, 162}, {46, 166, 186, 248}, {46, 166, 188, 208}, {46, 166, 188, 210}, {46, 166, 188, 215}, {46, 166, 190, 178}, {46, 166, 190, 185}, {46, 166, 190, 186}, {46, 166, 190, 195}, {46, 166, 190, 230}, {109, 201, 138, 239}, {109, 201, 152, 5}, {109, 201, 152, 26}, {109, 201, 152, 227}, {109, 201, 154, 141}, {109, 201, 154, 142}, {185, 107, 44, 25}}}, {Region: "Netherlands", IPs: []net.IP{{46, 166, 137, 219}, {46, 166, 138, 131}, {46, 166, 186, 218}, {46, 166, 186, 238}, {46, 166, 186, 249}, {46, 166, 188, 214}, {46, 166, 188, 241}, {46, 166, 190, 215}, {85, 159, 236, 219}, {109, 201, 152, 15}, {109, 201, 152, 16}, {109, 201, 152, 25}, {109, 201, 152, 238}, {109, 201, 154, 147}}},
{Region: "New Zealand", IPs: []net.IP{{43, 250, 207, 3}}}, {Region: "New Zealand", IPs: []net.IP{{43, 250, 207, 3}, {43, 250, 207, 7}}},
{Region: "Norway", IPs: []net.IP{{82, 102, 27, 50}, {82, 102, 27, 51}, {82, 102, 27, 52}, {82, 102, 27, 53}, {82, 102, 27, 54}, {82, 102, 27, 56}, {82, 102, 27, 74}, {82, 102, 27, 75}, {82, 102, 27, 76}, {82, 102, 27, 77}, {82, 102, 27, 78}, {82, 102, 27, 114}, {82, 102, 27, 117}, {82, 102, 27, 118}, {82, 102, 27, 125}, {82, 102, 27, 126}, {185, 206, 225, 222}, {185, 253, 97, 226}, {185, 253, 97, 227}, {185, 253, 97, 228}}}, {Region: "Norway", IPs: []net.IP{{82, 102, 27, 50}, {82, 102, 27, 51}, {82, 102, 27, 52}, {82, 102, 27, 53}, {82, 102, 27, 55}, {82, 102, 27, 56}, {82, 102, 27, 57}, {82, 102, 27, 74}, {82, 102, 27, 77}, {82, 102, 27, 78}, {82, 102, 27, 114}, {82, 102, 27, 115}, {82, 102, 27, 116}, {82, 102, 27, 117}, {82, 102, 27, 124}, {82, 102, 27, 125}, {82, 102, 27, 126}, {185, 206, 225, 222}, {185, 253, 97, 227}, {185, 253, 97, 228}}},
{Region: "Poland", IPs: []net.IP{{185, 244, 214, 14}, {185, 244, 214, 194}, {185, 244, 214, 196}, {185, 244, 214, 197}, {185, 244, 214, 198}, {185, 244, 214, 199}, {185, 244, 214, 200}}}, {Region: "Poland", IPs: []net.IP{{185, 244, 214, 14}, {185, 244, 214, 194}, {185, 244, 214, 196}, {185, 244, 214, 197}, {185, 244, 214, 198}, {185, 244, 214, 199}, {185, 244, 214, 200}}},
{Region: "Romania", IPs: []net.IP{{86, 105, 25, 66}, {86, 105, 25, 67}, {86, 105, 25, 68}, {86, 105, 25, 69}, {86, 105, 25, 70}, {86, 105, 25, 74}, {86, 105, 25, 75}, {86, 105, 25, 76}, {86, 105, 25, 77}, {86, 105, 25, 78}, {89, 33, 8, 42}, {94, 176, 148, 34}, {94, 176, 148, 35}, {185, 45, 12, 126}, {185, 210, 218, 100}, {185, 210, 218, 103}}}, {Region: "Romania", IPs: []net.IP{{86, 105, 25, 66}, {86, 105, 25, 67}, {86, 105, 25, 68}, {86, 105, 25, 69}, {86, 105, 25, 70}, {86, 105, 25, 74}, {86, 105, 25, 75}, {86, 105, 25, 76}, {86, 105, 25, 77}, {86, 105, 25, 78}, {94, 176, 148, 34}, {185, 45, 12, 126}, {185, 210, 218, 98}, {185, 210, 218, 99}, {185, 210, 218, 100}, {185, 210, 218, 101}, {185, 210, 218, 104}, {185, 210, 218, 105}, {185, 210, 218, 108}}},
{Region: "Singapore", IPs: []net.IP{{37, 120, 208, 66}, {37, 120, 208, 67}, {37, 120, 208, 68}, {37, 120, 208, 70}, {37, 120, 208, 71}, {37, 120, 208, 72}, {37, 120, 208, 73}, {37, 120, 208, 74}, {37, 120, 208, 75}, {37, 120, 208, 76}, {37, 120, 208, 77}, {37, 120, 208, 78}, {37, 120, 208, 79}, {37, 120, 208, 80}, {37, 120, 208, 81}, {37, 120, 208, 82}, {37, 120, 208, 83}}}, {Region: "Singapore", IPs: []net.IP{{37, 120, 208, 66}, {37, 120, 208, 67}, {37, 120, 208, 68}, {37, 120, 208, 70}, {37, 120, 208, 72}, {37, 120, 208, 73}, {37, 120, 208, 74}, {37, 120, 208, 75}, {37, 120, 208, 76}, {37, 120, 208, 77}, {37, 120, 208, 78}, {37, 120, 208, 79}, {37, 120, 208, 81}, {37, 120, 208, 82}, {37, 120, 208, 83}}},
{Region: "Spain", IPs: []net.IP{{37, 120, 148, 86}, {185, 230, 124, 50}, {185, 230, 124, 51}, {185, 230, 124, 52}, {185, 230, 124, 53}, {185, 230, 124, 54}, {194, 99, 104, 26}, {194, 99, 104, 27}, {194, 99, 104, 28}, {194, 99, 104, 29}, {194, 99, 104, 30}, {195, 206, 107, 250}}}, {Region: "Spain", IPs: []net.IP{{185, 230, 124, 53}, {194, 99, 104, 26}, {194, 99, 104, 30}}},
{Region: "Sweden", IPs: []net.IP{{45, 12, 220, 163}, {45, 12, 220, 168}, {45, 12, 220, 169}, {45, 12, 220, 173}, {45, 12, 220, 178}, {45, 12, 220, 180}, {45, 12, 220, 184}, {45, 12, 220, 190}, {45, 12, 220, 194}, {45, 12, 220, 197}, {45, 12, 220, 204}, {45, 12, 220, 205}, {45, 12, 220, 215}, {45, 12, 220, 218}, {45, 12, 220, 227}, {45, 12, 220, 233}, {45, 12, 220, 240}, {45, 12, 220, 254}, {45, 83, 91, 21}, {45, 83, 91, 36}}}, {Region: "Sweden", IPs: []net.IP{{45, 12, 220, 182}, {45, 12, 220, 183}, {45, 12, 220, 194}, {45, 12, 220, 195}, {45, 12, 220, 203}, {45, 12, 220, 206}, {45, 12, 220, 209}, {45, 12, 220, 217}, {45, 12, 220, 228}, {45, 12, 220, 234}, {45, 12, 220, 240}, {45, 12, 220, 243}, {45, 12, 220, 245}, {45, 12, 220, 248}, {45, 12, 220, 250}, {45, 12, 220, 253}}},
{Region: "Switzerland", IPs: []net.IP{{82, 102, 24, 171}, {82, 102, 24, 250}, {82, 102, 24, 252}, {91, 132, 136, 52}, {91, 132, 136, 54}, {91, 132, 136, 210}, {185, 156, 175, 82}, {185, 156, 175, 88}, {185, 156, 175, 90}, {185, 156, 175, 94}, {185, 212, 170, 179}, {185, 212, 170, 182}, {185, 212, 170, 187}, {185, 230, 125, 34}, {185, 230, 125, 36}, {185, 230, 125, 45}, {185, 230, 125, 48}, {185, 230, 125, 52}, {185, 230, 125, 90}, {212, 102, 36, 1}}}, {Region: "Switzerland", IPs: []net.IP{{82, 102, 24, 174}, {82, 102, 24, 250}, {91, 132, 136, 42}, {91, 132, 136, 43}, {91, 132, 136, 52}, {185, 156, 175, 83}, {185, 156, 175, 84}, {185, 156, 175, 85}, {185, 156, 175, 91}, {185, 156, 175, 92}, {185, 156, 175, 93}, {185, 230, 125, 36}, {185, 230, 125, 46}, {185, 230, 125, 48}, {185, 230, 125, 50}, {185, 230, 125, 52}, {185, 230, 125, 86}, {185, 230, 125, 90}, {195, 206, 105, 210}, {212, 102, 36, 1}}},
{Region: "UAE", IPs: []net.IP{{45, 9, 250, 42}, {45, 9, 250, 46}, {45, 9, 250, 62}}}, {Region: "UAE", IPs: []net.IP{{45, 9, 250, 42}, {45, 9, 250, 46}, {45, 9, 250, 62}}},
{Region: "UK London", IPs: []net.IP{{89, 238, 150, 9}, {89, 238, 150, 11}, {89, 238, 150, 15}, {89, 238, 150, 20}, {89, 238, 154, 18}, {89, 238, 154, 20}, {89, 238, 154, 23}, {89, 238, 154, 120}, {89, 238, 154, 121}, {89, 238, 154, 162}, {89, 238, 154, 167}, {89, 238, 154, 174}, {89, 238, 154, 179}, {89, 238, 154, 236}, {89, 238, 154, 243}, {89, 238, 154, 245}, {89, 238, 154, 249}, {89, 238, 154, 250}, {89, 238, 154, 251}, {89, 238, 154, 254}}}, {Region: "UK London", IPs: []net.IP{{89, 238, 150, 7}, {89, 238, 150, 13}, {89, 238, 150, 18}, {89, 238, 154, 18}, {89, 238, 154, 19}, {89, 238, 154, 20}, {89, 238, 154, 24}, {89, 238, 154, 115}, {89, 238, 154, 118}, {89, 238, 154, 120}, {89, 238, 154, 165}, {89, 238, 154, 166}, {89, 238, 154, 171}, {89, 238, 154, 233}, {89, 238, 154, 238}, {89, 238, 154, 243}, {89, 238, 154, 245}, {89, 238, 154, 250}, {89, 238, 154, 251}, {89, 238, 154, 254}}},
{Region: "UK Manchester", IPs: []net.IP{{89, 238, 137, 36}, {89, 238, 137, 37}, {89, 238, 137, 38}, {89, 238, 137, 39}, {89, 238, 137, 40}, {89, 238, 137, 41}, {89, 238, 139, 4}, {89, 238, 139, 5}, {89, 238, 139, 7}, {89, 238, 139, 8}, {89, 238, 139, 9}, {89, 238, 139, 10}, {89, 238, 139, 11}, {89, 238, 139, 13}, {89, 238, 139, 53}, {89, 238, 139, 55}, {89, 238, 139, 56}, {89, 238, 139, 57}, {89, 238, 139, 58}, {89, 249, 67, 220}}}, {Region: "UK Manchester", IPs: []net.IP{{89, 238, 137, 36}, {89, 238, 137, 37}, {89, 238, 137, 39}, {89, 238, 137, 40}, {89, 238, 137, 42}, {89, 238, 139, 4}, {89, 238, 139, 5}, {89, 238, 139, 6}, {89, 238, 139, 8}, {89, 238, 139, 9}, {89, 238, 139, 10}, {89, 238, 139, 11}, {89, 238, 139, 12}, {89, 238, 139, 13}, {89, 238, 139, 52}, {89, 238, 139, 54}, {89, 238, 139, 55}, {89, 238, 139, 57}, {89, 238, 139, 58}, {89, 249, 67, 220}}},
{Region: "UK Southampton", IPs: []net.IP{{31, 24, 226, 141}, {31, 24, 226, 145}, {31, 24, 226, 146}, {31, 24, 226, 205}, {31, 24, 226, 207}, {31, 24, 226, 208}, {31, 24, 226, 209}, {31, 24, 226, 217}, {31, 24, 226, 220}, {31, 24, 226, 223}, {31, 24, 226, 225}, {31, 24, 226, 226}, {31, 24, 226, 228}, {31, 24, 226, 232}, {31, 24, 226, 233}, {31, 24, 226, 234}, {31, 24, 226, 237}, {31, 24, 226, 244}, {31, 24, 226, 245}, {31, 24, 231, 197}}}, {Region: "UK Southampton", IPs: []net.IP{{31, 24, 226, 134}, {31, 24, 226, 188}, {31, 24, 226, 189}, {31, 24, 226, 202}, {31, 24, 226, 204}, {31, 24, 226, 205}, {31, 24, 226, 209}, {31, 24, 226, 220}, {31, 24, 226, 222}, {31, 24, 226, 226}, {31, 24, 226, 227}, {31, 24, 226, 228}, {31, 24, 226, 230}, {31, 24, 226, 234}, {31, 24, 226, 237}, {31, 24, 226, 240}, {31, 24, 226, 241}, {31, 24, 226, 244}, {31, 24, 226, 245}, {31, 24, 226, 246}}},
{Region: "US Atlanta", IPs: []net.IP{{66, 115, 168, 2}, {66, 115, 168, 4}, {66, 115, 168, 7}, {66, 115, 168, 10}, {66, 115, 168, 13}, {66, 115, 168, 17}, {66, 115, 168, 19}, {66, 115, 168, 21}, {66, 115, 168, 23}, {66, 115, 168, 26}, {66, 115, 168, 27}, {66, 115, 168, 28}, {66, 115, 169, 198}, {66, 115, 169, 202}, {66, 115, 169, 204}, {66, 115, 169, 226}, {66, 115, 169, 229}, {66, 115, 169, 231}, {66, 115, 169, 242}, {66, 115, 169, 244}}}, {Region: "US Atlanta", IPs: []net.IP{{156, 146, 46, 1}, {156, 146, 46, 198}}},
{Region: "US California", IPs: []net.IP{{91, 207, 175, 46}, {91, 207, 175, 62}, {91, 207, 175, 100}, {91, 207, 175, 102}, {91, 207, 175, 115}, {91, 207, 175, 121}, {91, 207, 175, 170}, {91, 207, 175, 181}, {91, 207, 175, 204}, {91, 207, 175, 206}, {91, 207, 175, 213}, {91, 207, 175, 226}, {91, 207, 175, 242}, {91, 207, 175, 250}, {91, 207, 175, 252}, {185, 245, 87, 181}, {185, 245, 87, 197}, {185, 245, 87, 215}, {212, 103, 49, 164}, {212, 103, 49, 168}}}, {Region: "US California", IPs: []net.IP{{91, 207, 175, 38}, {91, 207, 175, 42}, {91, 207, 175, 55}, {91, 207, 175, 60}, {91, 207, 175, 71}, {91, 207, 175, 109}, {91, 207, 175, 110}, {91, 207, 175, 118}, {91, 207, 175, 125}, {91, 207, 175, 163}, {91, 207, 175, 172}, {91, 207, 175, 182}, {91, 207, 175, 204}, {91, 207, 175, 205}, {91, 207, 175, 234}, {185, 245, 87, 170}, {185, 245, 87, 181}, {185, 245, 87, 195}, {185, 245, 87, 199}, {185, 245, 87, 220}}},
{Region: "US Chicago", IPs: []net.IP{{104, 200, 153, 97}, {199, 116, 115, 130}, {199, 116, 115, 131}, {199, 116, 115, 132}, {199, 116, 115, 133}, {199, 116, 115, 134}, {199, 116, 115, 135}, {199, 116, 115, 136}, {199, 116, 115, 137}, {199, 116, 115, 138}, {199, 116, 115, 139}, {199, 116, 115, 140}, {199, 116, 115, 141}, {199, 116, 115, 142}, {199, 116, 115, 143}, {199, 116, 115, 144}, {199, 116, 115, 145}, {199, 116, 115, 146}, {199, 116, 115, 147}, {199, 116, 115, 148}}}, {Region: "US Chicago", IPs: []net.IP{{156, 146, 50, 65}, {156, 146, 50, 134}, {156, 146, 50, 198}, {156, 146, 51, 11}, {212, 102, 58, 113}, {212, 102, 59, 54}}},
{Region: "US Denver", IPs: []net.IP{{174, 128, 225, 106}, {174, 128, 225, 186}, {174, 128, 226, 18}, {174, 128, 227, 226}, {174, 128, 236, 98}, {174, 128, 236, 106}, {174, 128, 242, 234}, {174, 128, 244, 74}, {174, 128, 245, 106}, {198, 148, 82, 82}, {198, 148, 90, 58}, {199, 115, 98, 146}, {199, 115, 98, 154}, {199, 115, 99, 218}}}, {Region: "US Denver", IPs: []net.IP{{174, 128, 225, 186}, {174, 128, 226, 18}, {174, 128, 236, 98}, {174, 128, 242, 234}, {174, 128, 243, 98}, {174, 128, 243, 106}, {174, 128, 244, 66}, {174, 128, 244, 74}, {174, 128, 245, 106}, {174, 128, 245, 122}, {174, 128, 250, 18}, {174, 128, 250, 26}, {198, 148, 90, 58}, {199, 115, 97, 202}, {199, 115, 98, 154}, {199, 115, 99, 82}, {199, 115, 99, 218}, {199, 115, 101, 178}, {199, 115, 101, 186}, {199, 115, 103, 10}}},
{Region: "US East", IPs: []net.IP{{193, 37, 253, 38}, {193, 37, 253, 115}, {194, 59, 251, 6}, {194, 59, 251, 13}, {194, 59, 251, 14}, {194, 59, 251, 17}, {194, 59, 251, 28}, {194, 59, 251, 29}, {194, 59, 251, 58}, {194, 59, 251, 66}, {194, 59, 251, 74}, {194, 59, 251, 81}, {194, 59, 251, 104}, {194, 59, 251, 111}, {194, 59, 251, 112}, {194, 59, 251, 149}, {194, 59, 251, 166}, {194, 59, 251, 216}, {194, 59, 251, 227}, {194, 59, 251, 240}}}, {Region: "US East", IPs: []net.IP{{193, 37, 253, 38}, {193, 37, 253, 86}, {194, 59, 251, 7}, {194, 59, 251, 13}, {194, 59, 251, 22}, {194, 59, 251, 48}, {194, 59, 251, 59}, {194, 59, 251, 103}, {194, 59, 251, 131}, {194, 59, 251, 133}, {194, 59, 251, 136}, {194, 59, 251, 137}, {194, 59, 251, 152}, {194, 59, 251, 169}, {194, 59, 251, 174}, {194, 59, 251, 190}, {194, 59, 251, 213}, {194, 59, 251, 219}, {194, 59, 251, 233}, {194, 59, 251, 239}}},
{Region: "US Florida", IPs: []net.IP{{193, 37, 252, 3}, {193, 37, 252, 12}, {193, 37, 252, 41}, {193, 37, 252, 45}, {193, 37, 252, 46}, {193, 37, 252, 52}, {193, 37, 252, 54}, {193, 37, 252, 55}, {193, 37, 252, 56}, {193, 37, 252, 58}, {193, 37, 252, 59}, {193, 37, 252, 75}, {193, 37, 252, 76}, {193, 37, 252, 102}, {193, 37, 252, 108}, {193, 37, 252, 115}, {193, 37, 252, 116}, {193, 37, 252, 118}, {193, 37, 252, 170}, {193, 37, 252, 174}}}, {Region: "US Florida", IPs: []net.IP{{193, 37, 252, 7}, {193, 37, 252, 8}, {193, 37, 252, 9}, {193, 37, 252, 17}, {193, 37, 252, 19}, {193, 37, 252, 34}, {193, 37, 252, 39}, {193, 37, 252, 45}, {193, 37, 252, 58}, {193, 37, 252, 59}, {193, 37, 252, 67}, {193, 37, 252, 68}, {193, 37, 252, 76}, {193, 37, 252, 82}, {193, 37, 252, 106}, {193, 37, 252, 115}, {193, 37, 252, 116}, {193, 37, 252, 117}, {193, 37, 252, 125}, {193, 37, 252, 126}}},
{Region: "US Houston", IPs: []net.IP{{74, 81, 88, 18}, {74, 81, 88, 26}, {74, 81, 88, 66}, {74, 81, 88, 82}, {74, 81, 88, 114}, {74, 81, 88, 130}, {74, 81, 88, 162}, {205, 251, 148, 34}, {205, 251, 148, 66}, {205, 251, 148, 82}, {205, 251, 148, 98}, {205, 251, 148, 130}, {205, 251, 148, 178}, {205, 251, 150, 146}, {205, 251, 150, 154}, {205, 251, 150, 162}, {205, 251, 150, 170}, {205, 251, 150, 194}, {205, 251, 150, 218}, {205, 251, 150, 234}}}, {Region: "US Houston", IPs: []net.IP{{74, 81, 88, 18}, {74, 81, 88, 26}, {74, 81, 88, 42}, {74, 81, 88, 58}, {74, 81, 88, 66}, {74, 81, 88, 82}, {74, 81, 88, 90}, {74, 81, 88, 114}, {74, 81, 88, 162}, {205, 251, 148, 50}, {205, 251, 148, 58}, {205, 251, 148, 98}, {205, 251, 148, 138}, {205, 251, 148, 178}, {205, 251, 150, 154}, {205, 251, 150, 162}, {205, 251, 150, 194}, {205, 251, 150, 218}, {205, 251, 150, 226}, {205, 251, 151, 26}}},
{Region: "US Las Vegas", IPs: []net.IP{{162, 251, 236, 2}, {162, 251, 236, 3}, {162, 251, 236, 4}, {162, 251, 236, 5}, {162, 251, 236, 7}, {162, 251, 236, 8}, {162, 251, 236, 9}, {199, 127, 56, 82}, {199, 127, 56, 83}, {199, 127, 56, 84}, {199, 127, 56, 86}, {199, 127, 56, 88}, {199, 127, 56, 89}, {199, 127, 56, 91}, {199, 127, 56, 114}, {199, 127, 56, 115}, {199, 127, 56, 118}, {199, 127, 56, 119}, {199, 127, 56, 120}, {199, 127, 56, 121}}}, {Region: "US Las Vegas", IPs: []net.IP{{162, 251, 236, 2}, {162, 251, 236, 3}, {162, 251, 236, 4}, {162, 251, 236, 7}, {162, 251, 236, 8}, {162, 251, 236, 9}, {199, 127, 56, 83}, {199, 127, 56, 84}, {199, 127, 56, 86}, {199, 127, 56, 87}, {199, 127, 56, 88}, {199, 127, 56, 89}, {199, 127, 56, 91}, {199, 127, 56, 114}, {199, 127, 56, 115}, {199, 127, 56, 116}, {199, 127, 56, 117}, {199, 127, 56, 118}, {199, 127, 56, 119}, {199, 127, 56, 120}}},
{Region: "US New York City", IPs: []net.IP{{107, 182, 231, 24}, {107, 182, 231, 34}, {173, 244, 223, 122}, {209, 95, 50, 13}, {209, 95, 50, 17}, {209, 95, 50, 18}, {209, 95, 50, 27}, {209, 95, 50, 49}, {209, 95, 50, 50}, {209, 95, 50, 56}, {209, 95, 50, 84}, {209, 95, 50, 87}, {209, 95, 50, 89}, {209, 95, 50, 93}, {209, 95, 50, 134}, {209, 95, 50, 139}, {209, 95, 50, 147}, {209, 95, 50, 148}, {209, 95, 50, 162}, {209, 95, 50, 163}}}, {Region: "US New York City", IPs: []net.IP{{107, 182, 230, 194}, {107, 182, 231, 24}, {107, 182, 231, 30}, {107, 182, 231, 34}, {107, 182, 231, 37}, {107, 182, 231, 38}, {107, 182, 231, 51}, {209, 95, 50, 12}, {209, 95, 50, 27}, {209, 95, 50, 50}, {209, 95, 50, 65}, {209, 95, 50, 66}, {209, 95, 50, 90}, {209, 95, 50, 93}, {209, 95, 50, 103}, {209, 95, 50, 104}, {209, 95, 50, 133}, {209, 95, 50, 144}, {209, 95, 50, 146}, {209, 95, 50, 162}}},
{Region: "US Seattle", IPs: []net.IP{{104, 200, 154, 12}, {104, 200, 154, 38}, {104, 200, 154, 39}, {104, 200, 154, 50}, {104, 200, 154, 65}, {104, 200, 154, 66}, {104, 200, 154, 68}, {104, 200, 154, 70}, {104, 200, 154, 72}, {104, 200, 154, 73}, {104, 200, 154, 74}, {104, 200, 154, 75}, {104, 200, 154, 78}, {104, 200, 154, 79}, {104, 200, 154, 84}, {104, 200, 154, 86}, {104, 200, 154, 88}, {104, 200, 154, 90}, {104, 200, 154, 91}, {104, 200, 154, 98}}}, {Region: "US Seattle", IPs: []net.IP{{104, 200, 154, 11}, {104, 200, 154, 21}, {104, 200, 154, 22}, {104, 200, 154, 44}, {104, 200, 154, 47}, {104, 200, 154, 56}, {104, 200, 154, 59}, {104, 200, 154, 62}, {104, 200, 154, 66}, {104, 200, 154, 67}, {104, 200, 154, 70}, {104, 200, 154, 81}, {104, 200, 154, 84}, {104, 200, 154, 87}, {104, 200, 154, 90}, {104, 200, 154, 91}, {104, 200, 154, 96}, {104, 200, 154, 97}, {104, 200, 154, 98}, {104, 200, 154, 99}}},
{Region: "US Silicon Valley", IPs: []net.IP{{199, 116, 118, 131}, {199, 116, 118, 132}, {199, 116, 118, 133}, {199, 116, 118, 137}, {199, 116, 118, 140}, {199, 116, 118, 155}, {199, 116, 118, 156}, {199, 116, 118, 170}, {199, 116, 118, 173}, {199, 116, 118, 178}, {199, 116, 118, 184}, {199, 116, 118, 201}, {199, 116, 118, 202}, {199, 116, 118, 209}, {199, 116, 118, 210}, {199, 116, 118, 228}, {199, 116, 118, 237}, {199, 116, 118, 238}, {199, 116, 118, 244}, {199, 116, 118, 250}}}, {Region: "US Silicon Valley", IPs: []net.IP{{199, 116, 118, 130}, {199, 116, 118, 133}, {199, 116, 118, 137}, {199, 116, 118, 145}, {199, 116, 118, 159}, {199, 116, 118, 160}, {199, 116, 118, 178}, {199, 116, 118, 181}, {199, 116, 118, 182}, {199, 116, 118, 202}, {199, 116, 118, 205}, {199, 116, 118, 209}, {199, 116, 118, 211}, {199, 116, 118, 213}, {199, 116, 118, 217}, {199, 116, 118, 218}, {199, 116, 118, 236}, {199, 116, 118, 238}, {199, 116, 118, 244}, {199, 116, 118, 252}}},
{Region: "US Texas", IPs: []net.IP{{162, 216, 46, 5}, {162, 216, 46, 10}, {162, 216, 46, 14}, {162, 216, 46, 18}, {162, 216, 46, 36}, {162, 216, 46, 38}, {162, 216, 46, 39}, {162, 216, 46, 42}, {162, 216, 46, 58}, {162, 216, 46, 71}, {162, 216, 46, 82}, {162, 216, 46, 85}, {162, 216, 46, 104}, {162, 216, 46, 105}, {162, 216, 46, 132}, {162, 216, 46, 138}, {162, 216, 46, 150}, {162, 216, 46, 151}, {162, 216, 46, 153}, {162, 216, 46, 170}}}, {Region: "US Texas", IPs: []net.IP{{162, 216, 46, 33}, {162, 216, 46, 40}, {162, 216, 46, 44}, {162, 216, 46, 57}, {162, 216, 46, 58}, {162, 216, 46, 70}, {162, 216, 46, 81}, {162, 216, 46, 96}, {162, 216, 46, 101}, {162, 216, 46, 112}, {162, 216, 46, 119}, {162, 216, 46, 123}, {162, 216, 46, 129}, {162, 216, 46, 132}, {162, 216, 46, 133}, {162, 216, 46, 141}, {162, 216, 46, 148}, {162, 216, 46, 154}, {162, 216, 46, 156}, {162, 216, 46, 168}}},
{Region: "US Washington DC", IPs: []net.IP{{70, 32, 0, 24}, {70, 32, 0, 30}, {70, 32, 0, 54}, {70, 32, 0, 64}, {70, 32, 0, 68}, {70, 32, 0, 77}, {70, 32, 0, 111}, {70, 32, 0, 120}, {70, 32, 0, 121}, {70, 32, 0, 137}, {70, 32, 0, 153}, {70, 32, 0, 155}, {70, 32, 0, 165}, {70, 32, 0, 166}, {70, 32, 0, 167}, {70, 32, 0, 170}, {70, 32, 0, 172}, {70, 32, 0, 177}, {70, 32, 0, 178}, {70, 32, 0, 179}}}, {Region: "US Washington DC", IPs: []net.IP{{70, 32, 0, 24}, {70, 32, 0, 46}, {70, 32, 0, 53}, {70, 32, 0, 55}, {70, 32, 0, 61}, {70, 32, 0, 74}, {70, 32, 0, 97}, {70, 32, 0, 103}, {70, 32, 0, 104}, {70, 32, 0, 133}, {70, 32, 0, 135}, {70, 32, 0, 140}, {70, 32, 0, 153}, {70, 32, 0, 155}, {70, 32, 0, 166}, {70, 32, 0, 167}, {70, 32, 0, 168}, {70, 32, 0, 172}, {70, 32, 0, 177}, {70, 32, 0, 179}}},
{Region: "US West", IPs: []net.IP{{104, 200, 151, 3}, {104, 200, 151, 11}, {104, 200, 151, 14}, {104, 200, 151, 15}, {104, 200, 151, 21}, {104, 200, 151, 28}, {104, 200, 151, 29}, {104, 200, 151, 34}, {104, 200, 151, 36}, {104, 200, 151, 42}, {104, 200, 151, 44}, {104, 200, 151, 47}, {104, 200, 151, 50}, {104, 200, 151, 51}, {104, 200, 151, 54}, {104, 200, 151, 60}, {104, 200, 151, 72}, {104, 200, 151, 75}, {104, 200, 151, 77}, {104, 200, 151, 78}}}, {Region: "US West", IPs: []net.IP{{104, 200, 151, 4}, {104, 200, 151, 10}, {104, 200, 151, 11}, {104, 200, 151, 14}, {104, 200, 151, 15}, {104, 200, 151, 17}, {104, 200, 151, 18}, {104, 200, 151, 21}, {104, 200, 151, 26}, {104, 200, 151, 30}, {104, 200, 151, 34}, {104, 200, 151, 42}, {104, 200, 151, 48}, {104, 200, 151, 54}, {104, 200, 151, 55}, {104, 200, 151, 73}, {104, 200, 151, 78}, {104, 200, 151, 82}, {104, 200, 151, 84}, {104, 200, 151, 86}}},
} }
} }

View File

@@ -0,0 +1,198 @@
package constants
import (
"net"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
const (
PurevpnCertificateAuthority = "MIIE6DCCA9CgAwIBAgIJAMjXFoeo5uSlMA0GCSqGSIb3DQEBCwUAMIGoMQswCQYDVQQGEwJISzEQMA4GA1UECBMHQ2VudHJhbDELMAkGA1UEBxMCSEsxGDAWBgNVBAoTD1NlY3VyZS1TZXJ2ZXJDQTELMAkGA1UECxMCSVQxGDAWBgNVBAMTD1NlY3VyZS1TZXJ2ZXJDQTEYMBYGA1UEKRMPU2VjdXJlLVNlcnZlckNBMR8wHQYJKoZIhvcNAQkBFhBtYWlsQGhvc3QuZG9tYWluMB4XDTE2MDExNTE1MzQwOVoXDTI2MDExMjE1MzQwOVowgagxCzAJBgNVBAYTAkhLMRAwDgYDVQQIEwdDZW50cmFsMQswCQYDVQQHEwJISzEYMBYGA1UEChMPU2VjdXJlLVNlcnZlckNBMQswCQYDVQQLEwJJVDEYMBYGA1UEAxMPU2VjdXJlLVNlcnZlckNBMRgwFgYDVQQpEw9TZWN1cmUtU2VydmVyQ0ExHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDluufhyLlyvXzPUL16kAWAdivl1roQv3QHbuRshyKacf/1Er1JqEbtW3Mx9Fvr/u27qU2W8lQI6DaJhU2BfijPe/KHkib55mvHzIVvoexxya26nk79F2c+d9PnuuMdThWQO3El5a/i2AASnM7T7piIBT2WRZW2i8RbfJaTT7G7LP7OpMKIV1qyBg/cWoO7cIWQW4jmzqrNryIkF0AzStLN1DxvnQZwgXBGv0CwuAkfQuNSLu0PQgPp0PhdukNZFllv5D29IhPr0Z+kwPtrAgPQo+lHlOBHBMUpDT4XChTPeAvMaUSBsqmonAE8UUHEabWrqYN/kWNHCNkYXMkiVmK1AgMBAAGjggERMIIBDTAdBgNVHQ4EFgQU456ijsFrYnzHBShLAPpOUqQ+Z2cwgd0GA1UdIwSB1TCB0oAU456ijsFrYnzHBShLAPpOUqQ+Z2ehga6kgaswgagxCzAJBgNVBAYTAkhLMRAwDgYDVQQIEwdDZW50cmFsMQswCQYDVQQHEwJISzEYMBYGA1UEChMPU2VjdXJlLVNlcnZlckNBMQswCQYDVQQLEwJJVDEYMBYGA1UEAxMPU2VjdXJlLVNlcnZlckNBMRgwFgYDVQQpEw9TZWN1cmUtU2VydmVyQ0ExHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQDI1xaHqObkpTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCvga2HMwOtUxWH/inL2qk24KX2pxLg939JNhqoyNrUpbDHag5xPQYXUmUpKrNJZ0z+o/ZnNUPHydTSXE7Z7E45J0GDN5E7g4pakndKnDLSjp03NgGsCGW+cXnz6UBPM5FStFvGdDeModeSUyoS9fjk+mYROvmiy5EiVDP91sKGcPLR7Ym0M7zl2aaqV7bb98HmMoBOxpeZQinof67nKrCsgz/xjktWFgcmPl4/PQSsmqQD0fTtWxGuRX+FzwvF2OCMCAJgp1RqJNlk2g50/kBIoJVPPCfjDFeDU5zGaWGSQ9+z1L6/z7VXdjUiHL0ouOcHwbiS4ZjTr9nMn6WdAHU2"
PurevpnCertificate = "MIIEnzCCA4egAwIBAgIBAzANBgkqhkiG9w0BAQsFADCBqDELMAkGA1UEBhMCSEsxEDAOBgNVBAgTB0NlbnRyYWwxCzAJBgNVBAcTAkhLMRgwFgYDVQQKEw9TZWN1cmUtU2VydmVyQ0ExCzAJBgNVBAsTAklUMRgwFgYDVQQDEw9TZWN1cmUtU2VydmVyQ0ExGDAWBgNVBCkTD1NlY3VyZS1TZXJ2ZXJDQTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRvbWFpbjAeFw0xNjAxMTUxNjE1MzhaFw0yNjAxMTIxNjE1MzhaMIGdMQswCQYDVQQGEwJISzEQMA4GA1UECBMHQ2VudHJhbDELMAkGA1UEBxMCSEsxFjAUBgNVBAoTDVNlY3VyZS1DbGllbnQxCzAJBgNVBAsTAklUMRYwFAYDVQQDEw1TZWN1cmUtQ2xpZW50MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRvbWFpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxsnyn4v6xxDPnuDaYS0b9M1N8nxgg7OBPBlK+FWRxdTQ8yxt5U5CZGm7riVp7fya2J2iPZIgmHQEv/KbxztsHAVlYSfYYlalrnhEL3bDP2tY+N43AwB1k5BrPq2s1pPLT2XG951drDKG4PUuFHUP1sHzW5oQlfVCmxgIMAP8OYkCAwEAAaOCAV8wggFbMAkGA1UdEwQCMAAwLQYJYIZIAYb4QgENBCAWHkVhc3ktUlNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU9MwUnUDbQKKZKjoeieD2OD5NlAEwgd0GA1UdIwSB1TCB0oAU456ijsFrYnzHBShLAPpOUqQ+Z2ehga6kgaswgagxCzAJBgNVBAYTAkhLMRAwDgYDVQQIEwdDZW50cmFsMQswCQYDVQQHEwJISzEYMBYGA1UEChMPU2VjdXJlLVNlcnZlckNBMQswCQYDVQQLEwJJVDEYMBYGA1UEAxMPU2VjdXJlLVNlcnZlckNBMRgwFgYDVQQpEw9TZWN1cmUtU2VydmVyQ0ExHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQDI1xaHqObkpTATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADggEBAFyFo2VUX/UFixsdPdK9/Yt6mkCWc+XS1xbapGXXb9U1d+h1iBCIV9odUHgNCXWpz1hR5Uu/OCzaZ0asLE4IFMZlQmJs8sMT0c1tfPPGW45vxbL0lhqnQ8PNcBH7huNK7VFjUh4szXRKmaQPaM4S91R3L4CaNfVeHfAg7mN2m9Zn5Gto1Q1/CFMGKu2hxwGEw5p+X1czBWEvg/O09ckx/ggkkI1NcZsNiYQ+6Pz8DdGGX3+05YwLZu94+O6iIMrzxl/il0eK83g3YPbsOrASARvw6w/8sOnJCK5eOacl21oww875KisnYdWjHB1FiI+VzQ1/gyoDsL5kPTJVuu2CoG8="
PurevpnKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMbJ8p+L+scQz57g2mEtG/TNTfJ8YIOzgTwZSvhVkcXU0PMsbeVOQmRpu64lae38mtidoj2SIJh0BL/ym8c7bBwFZWEn2GJWpa54RC92wz9rWPjeNwMAdZOQaz6trNaTy09lxvedXawyhuD1LhR1D9bB81uaEJX1QpsYCDAD/DmJAgMBAAECgYEAvTHbDupE5U0krUvHzBEIuHblptGlcfNYHoDcD3oxYR3pOGeiuElBexv+mgHVzcFLBrsQfJUlHLPfCWi3xmjRvDQcr7N7U1u7NIzazy/PpRBaKolMRiM1KMYi2DG0i4ZONwFT8bvNHOIrZzCLY54KDrqOn55OzC70WYjWh4t5evkCQQDkkzZUAeskBC9+JP/zLps8jhwfoLBWGw/zbC9ePDmX0N8MTZdcUpg6KUTf1wbkLUyVtIRjS2ao6qu1jWG6K0x3AkEA3qPWyaWQWCynhNDqu2U1cPb2kh5AJip+gqxO3emikAdajsSxeoyEC2AfyBITbeB1tvCUZH17J4i/0+OFTEQp/wJAb/zEOGJ8PzghwK8GC7JA8mk51DEZVAaMSRovFv9wxDXcoh191AjPdmdzzCuAv9iF1i8MUc3GbWoUWK39PIYsPwJAWh63sqfx5b8tj/WBDpnJKBDPfhYAoXJSA1L8GZeY1fQkE+ZKcPCwAmrGcpXeh3t0Krj3WDXyw+32uC5Apr5wwQJAPZwOOReaC4YNfBPZN9BdHvVjOYGGUffpI+X+hWpLRnQFJteAi+eqwyk0Oi0SkJB+a7jcerK2d7q7xhec5WHlng=="
PurevpnOpenvpnStaticKeyV1 = "e30af995f56d07426d9ba1f824730521d4283db4b4d0cdda9c6e8759a3799dcb7939b6a5989160c9660de0f6125cbb1f585b41c074b2fe88ecfcf17eab9a33be1352379cdf74952b588fb161a93e13df9135b2b29038231e02d657a6225705e6868ccb0c384ed11614690a1894bfbeb274cebf1fe9c2329bdd5c8a40fe8820624d2ea7540cd79ab76892db51fc371a3ac5fc9573afecb3fffe3281e61d72e91579d9b03d8cbf7909b3aebf4d90850321ee6b7d0a7846d15c27d8290e031e951e19438a4654663cad975e138f5bc5af89c737ad822f27e19057731f41e1e254cc9c95b7175c622422cde9f1f2cfd3510add94498b4d7133d3729dd214a16b27fb"
)
func PurevpnRegionChoices() (choices []string) {
servers := PurevpnServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
}
return choices
}
func PurevpnCountryChoices() (choices []string) {
servers := PurevpnServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
}
return choices
}
func PurevpnCityChoices() (choices []string) {
servers := PurevpnServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
}
return choices
}
func PurevpnServers() []models.PurevpnServer {
return []models.PurevpnServer{
{Region: "Africa", Country: "Algeria", City: "Algiers", IPs: []net.IP{{172, 94, 64, 2}}},
{Region: "Africa", Country: "Angola", City: "Benguela", IPs: []net.IP{{45, 115, 26, 2}}},
{Region: "Africa", Country: "Cape Verde", City: "Praia", IPs: []net.IP{{45, 74, 25, 2}}},
{Region: "Africa", Country: "Egypt", City: "Cairo", IPs: []net.IP{{192, 198, 120, 122}}},
{Region: "Africa", Country: "Ethiopia", City: "Addis Ababa", IPs: []net.IP{{104, 250, 178, 4}}},
{Region: "Africa", Country: "Ghana", City: "Accra", IPs: []net.IP{{196, 251, 67, 4}}},
{Region: "Africa", Country: "Kenya", City: "Mombasa", IPs: []net.IP{{154, 127, 57, 151}}},
{Region: "Africa", Country: "Madagascar", City: "Antananarivo", IPs: []net.IP{{206, 123, 156, 131}}},
{Region: "Africa", Country: "Mauritania", City: "Nouakchott", IPs: []net.IP{{206, 123, 158, 63}}},
{Region: "Africa", Country: "Mauritius", City: "Port Louis", IPs: []net.IP{{104, 250, 181, 4}}},
{Region: "Africa", Country: "Morocco", City: "Rabat", IPs: []net.IP{{104, 243, 250, 126}}},
{Region: "Africa", Country: "Niger", City: "Niamey", IPs: []net.IP{{206, 123, 157, 131}}},
{Region: "Africa", Country: "Nigeria", City: "Suleja", IPs: []net.IP{{102, 165, 25, 38}}},
{Region: "Africa", Country: "Senegal", City: "Dakar", IPs: []net.IP{{206, 123, 158, 131}}},
{Region: "Africa", Country: "Seychelles", City: "Victoria", IPs: []net.IP{{172, 111, 128, 126}}},
{Region: "Africa", Country: "South Africa", City: "Johannesburg", IPs: []net.IP{{102, 165, 3, 34}}},
{Region: "Africa", Country: "Tanzania", City: "Dar Es Salaam", IPs: []net.IP{{102, 135, 0, 2}}},
{Region: "Africa", Country: "Tunisia", City: "Tunis", IPs: []net.IP{{206, 123, 159, 4}}},
{Region: "Asia", Country: "Afghanistan", City: "Kabul", IPs: []net.IP{{172, 111, 208, 2}}},
{Region: "Asia", Country: "Armenia", City: "Singapore", IPs: []net.IP{{37, 120, 208, 147}}},
{Region: "Asia", Country: "Azerbaijan", City: "Baku", IPs: []net.IP{{104, 250, 177, 4}}},
{Region: "Asia", Country: "Bangladesh", City: "Dhaka", IPs: []net.IP{{206, 123, 154, 190}}},
{Region: "Asia", Country: "Brunei Darussalam", City: "Bandar Seri Begawan", IPs: []net.IP{{119, 81, 75, 84}, {119, 81, 242, 8}}},
{Region: "Asia", Country: "Cambodia", City: "Phnom Penh", IPs: []net.IP{{104, 250, 176, 122}}},
{Region: "Asia", Country: "Hong Kong (SAR)", City: "Hong Kong", IPs: []net.IP{{46, 243, 250, 4}}},
{Region: "Asia", Country: "India", City: "Chennai", IPs: []net.IP{{129, 227, 107, 242}}},
{Region: "Asia", Country: "Indonesia", City: "Jakarta", IPs: []net.IP{{103, 55, 9, 2}}},
{Region: "Asia", Country: "Japan", City: "Tokyo", IPs: []net.IP{{172, 94, 56, 2}}},
{Region: "Asia", Country: "Kazakhstan", City: "Almaty", IPs: []net.IP{{206, 123, 152, 4}}},
{Region: "Asia", Country: "Korea, South", City: "Seoul", IPs: []net.IP{{45, 115, 25, 2}}},
{Region: "Asia", Country: "Kyrgyzstan", City: "Bishkek", IPs: []net.IP{{206, 123, 151, 131}}},
{Region: "Asia", Country: "Laos", City: "Vientiane", IPs: []net.IP{{206, 123, 153, 4}}},
{Region: "Asia", Country: "Macao", City: "Beyrouth", IPs: []net.IP{{104, 243, 240, 121}}},
{Region: "Asia", Country: "Malaysia", City: "Johor Baharu", IPs: []net.IP{{43, 226, 230, 4}}},
{Region: "Asia", Country: "Malaysia", City: "Kuala Lumpur", IPs: []net.IP{{104, 250, 160, 4}}},
{Region: "Asia", Country: "Mongolia", City: "Ulaanbaatar", IPs: []net.IP{{206, 123, 153, 131}}},
{Region: "Asia", Country: "Pakistan", City: "Islamabad", IPs: []net.IP{{104, 250, 187, 3}}},
{Region: "Asia", Country: "Papua New Guinea", City: "Port Moresby", IPs: []net.IP{{206, 123, 155, 131}}},
{Region: "Asia", Country: "Philippines", City: "Manila", IPs: []net.IP{{36, 255, 97, 3}}},
{Region: "Asia", Country: "Sri Lanka", City: "Colombo", IPs: []net.IP{{206, 123, 154, 4}}},
{Region: "Asia", Country: "Taiwan", City: "Taipei", IPs: []net.IP{{203, 69, 105, 5}}},
{Region: "Asia", Country: "Tajikistan", City: "Dushanbe", IPs: []net.IP{{206, 123, 151, 4}}},
{Region: "Asia", Country: "Thailand", City: "Bangkok", IPs: []net.IP{{104, 37, 6, 4}}},
{Region: "Asia", Country: "Turkey", City: "Istanbul", IPs: []net.IP{{82, 102, 22, 212}}},
{Region: "Asia", Country: "Turkmenistan", City: "Ashgabat", IPs: []net.IP{{206, 123, 152, 131}}},
{Region: "Asia", Country: "Uzbekistan", City: "Tashkent", IPs: []net.IP{{206, 123, 150, 131}}},
{Region: "Asia", Country: "Vietnam", City: "Hanoi", IPs: []net.IP{{192, 253, 249, 132}}},
{Region: "Europe", Country: "Albania", City: "Tirane", IPs: []net.IP{{46, 243, 224, 2}}},
{Region: "Europe", Country: "Armenia", City: "Yerevan", IPs: []net.IP{{172, 94, 35, 4}}},
{Region: "Europe", Country: "Austria", City: "Vienna", IPs: []net.IP{{172, 94, 125, 4}}},
{Region: "Europe", Country: "Belgium", City: "Brussels", IPs: []net.IP{{172, 111, 223, 4}, {172, 111, 244, 4}}},
{Region: "Europe", Country: "Bosnia and Herzegovina", City: "Sarajevo", IPs: []net.IP{{104, 250, 169, 122}}},
{Region: "Europe", Country: "Bulgaria", City: "Sofia", IPs: []net.IP{{37, 120, 152, 52}}},
{Region: "Europe", Country: "Croatia", City: "Zagreb", IPs: []net.IP{{104, 250, 163, 2}}},
{Region: "Europe", Country: "Cyprus", City: "Nicosia", IPs: []net.IP{{188, 72, 119, 4}}},
{Region: "Europe", Country: "Denmark", City: "Copenhagen", IPs: []net.IP{{172, 111, 223, 4}}},
{Region: "Europe", Country: "Estonia", City: "Tallinn", IPs: []net.IP{{185, 166, 87, 2}, {188, 72, 111, 4}}},
{Region: "Europe", Country: "France", City: "Paris", IPs: []net.IP{{172, 111, 223, 4}}},
{Region: "Europe", Country: "Georgia", City: "Tbilisi", IPs: []net.IP{{141, 101, 156, 2}}},
{Region: "Europe", Country: "Germany", City: "Frankfurt", IPs: []net.IP{{82, 102, 16, 107}}},
{Region: "Europe", Country: "Germany", City: "Munich", IPs: []net.IP{{82, 102, 16, 107}}},
{Region: "Europe", Country: "Germany", City: "Nuremberg", IPs: []net.IP{{172, 94, 125, 2}}},
{Region: "Europe", Country: "Greece", City: "Thessaloniki", IPs: []net.IP{{5, 172, 199, 2}}},
{Region: "Europe", Country: "Hungary", City: "Budapest", IPs: []net.IP{{217, 138, 192, 136}}},
{Region: "Europe", Country: "Iceland", City: "Reykjavik", IPs: []net.IP{{192, 253, 250, 1}}},
{Region: "Europe", Country: "Ireland", City: "Dublin", IPs: []net.IP{{78, 153, 208, 173}}},
{Region: "Europe", Country: "Isle of Man", City: "Onchan", IPs: []net.IP{{46, 243, 144, 2}}},
{Region: "Europe", Country: "Italy", City: "Milano", IPs: []net.IP{{45, 9, 251, 2}}},
{Region: "Europe", Country: "Latvia", City: "RIGA", IPs: []net.IP{{185, 118, 76, 5}}},
{Region: "Europe", Country: "Liechtenstein", City: "Vaduz", IPs: []net.IP{{104, 250, 164, 4}}},
{Region: "Europe", Country: "Lithuania", City: "Vilnius", IPs: []net.IP{{188, 72, 116, 3}}},
{Region: "Europe", Country: "Luxembourg", City: "Luxembourg", IPs: []net.IP{{94, 242, 225, 132}}},
{Region: "Europe", Country: "Malta", City: "Sliema", IPs: []net.IP{{46, 243, 241, 4}}},
{Region: "Europe", Country: "Monaco", City: "Monaco", IPs: []net.IP{{104, 250, 168, 132}}},
{Region: "Europe", Country: "Montenegro", City: "Podgorica", IPs: []net.IP{{104, 250, 165, 121}}},
{Region: "Europe", Country: "Netherlands", City: "Amsterdam", IPs: []net.IP{{37, 120, 192, 212}}},
{Region: "Europe", Country: "Norway", City: "Oslo", IPs: []net.IP{{82, 102, 22, 212}}},
{Region: "Europe", Country: "Poland", City: "Warsaw", IPs: []net.IP{{5, 253, 206, 251}}},
{Region: "Europe", Country: "Portugal", City: "Lisbon", IPs: []net.IP{{5, 154, 174, 3}}},
{Region: "Europe", Country: "Romania", City: "Bucharest", IPs: []net.IP{{188, 240, 220, 35}}},
{Region: "Europe", Country: "Serbia", City: "Niš", IPs: []net.IP{{152, 89, 160, 201}}},
{Region: "Europe", Country: "Slovakia", City: "Bratislava", IPs: []net.IP{{188, 72, 112, 3}}},
{Region: "Europe", Country: "Slovenia", City: "Ljubljana", IPs: []net.IP{{104, 243, 246, 129}}},
{Region: "Europe", Country: "Spain", City: "Barcelona", IPs: []net.IP{{185, 230, 124, 147}}},
{Region: "Europe", Country: "Sweden", City: "Stockholm", IPs: []net.IP{{45, 74, 46, 2}}},
{Region: "Europe", Country: "Switzerland", City: "Zurich", IPs: []net.IP{{45, 12, 222, 98}, {45, 12, 222, 99}}},
{Region: "Europe", Country: "United Kingdom", City: "Gosport", IPs: []net.IP{{45, 141, 154, 70}}},
{Region: "Europe", Country: "United Kingdom", City: "London", IPs: []net.IP{{193, 9, 113, 66}}},
{Region: "Europe", Country: "United Kingdom", City: "Maidenhead", IPs: []net.IP{{188, 72, 89, 4}}},
{Region: "Europe", Country: "United Kingdom", City: "Manchester", IPs: []net.IP{{172, 111, 183, 2}}},
{Region: "Middle East", Country: "Bahrain", City: "Manama", IPs: []net.IP{{46, 243, 150, 4}}},
{Region: "Middle East", Country: "Jordan", City: "Amman", IPs: []net.IP{{172, 111, 152, 3}}},
{Region: "Middle East", Country: "Kuwait", City: "Kuwait", IPs: []net.IP{{206, 123, 146, 4}}},
{Region: "Middle East", Country: "Oman", City: "Salalah", IPs: []net.IP{{46, 243, 148, 125}}},
{Region: "Middle East", Country: "Qatar", City: "Doha", IPs: []net.IP{{46, 243, 147, 2}}},
{Region: "Middle East", Country: "Saudi Arabia", City: "Jeddah", IPs: []net.IP{{45, 74, 1, 4}}},
{Region: "Middle East", Country: "United Arab Emirates", City: "Dubai", IPs: []net.IP{{104, 37, 6, 4}}},
{Region: "North America", Country: "Aruba", City: "Oranjestad", IPs: []net.IP{{104, 243, 246, 129}}},
{Region: "North America", Country: "Barbados", City: "Bridgetown", IPs: []net.IP{{172, 94, 97, 2}}},
{Region: "North America", Country: "Belize", City: "Belmopan", IPs: []net.IP{{104, 243, 241, 4}}},
{Region: "North America", Country: "Bermuda", City: "Hamilton", IPs: []net.IP{{172, 94, 76, 2}}},
{Region: "North America", Country: "Canada", City: "Montreal", IPs: []net.IP{{172, 94, 7, 2}}},
{Region: "North America", Country: "Canada", City: "Toronto", IPs: []net.IP{{172, 94, 7, 2}}},
{Region: "North America", Country: "Canada", City: "Vancouver", IPs: []net.IP{{172, 94, 34, 4}}},
{Region: "North America", Country: "Cayman Islands", City: "George Town", IPs: []net.IP{{172, 94, 113, 2}}},
{Region: "North America", Country: "Costa Rica", City: "San Jose", IPs: []net.IP{{104, 243, 245, 1}}},
{Region: "North America", Country: "Dominica", City: "Roseau", IPs: []net.IP{{45, 74, 22, 2}}},
{Region: "North America", Country: "Dominican Republic", City: "Santo Domingo", IPs: []net.IP{{45, 74, 23, 129}}},
{Region: "North America", Country: "El Salvador", City: "San Salvador", IPs: []net.IP{{45, 74, 17, 129}}},
{Region: "North America", Country: "Grenada", City: "St George's", IPs: []net.IP{{45, 74, 21, 129}}},
{Region: "North America", Country: "Guatemala", City: "Guatemala", IPs: []net.IP{{45, 74, 17, 129}}},
{Region: "North America", Country: "Haiti", City: "PORT-AU-PRINCE", IPs: []net.IP{{45, 74, 24, 2}}},
{Region: "North America", Country: "Honduras", City: "TEGUCIGALPA", IPs: []net.IP{{45, 74, 18, 2}}},
{Region: "North America", Country: "Jamaica", City: "Kingston", IPs: []net.IP{{104, 250, 182, 126}}},
{Region: "North America", Country: "Mexico", City: "Mexico City", IPs: []net.IP{{104, 243, 243, 131}}},
{Region: "North America", Country: "Montserrat", City: "plymouth", IPs: []net.IP{{45, 74, 26, 190}}},
{Region: "North America", Country: "Puerto Rico", City: "San Juan", IPs: []net.IP{{104, 37, 2, 2}}},
{Region: "North America", Country: "Saint Lucia", City: "Castries", IPs: []net.IP{{45, 74, 23, 2}}},
{Region: "North America", Country: "The Bahamas", City: "Freeport", IPs: []net.IP{{104, 243, 242, 2}}},
{Region: "North America", Country: "Trinidad and Tobago", City: "Port of Spain", IPs: []net.IP{{45, 74, 21, 2}}},
{Region: "North America", Country: "Turks and Caicos Islands", City: "Balfour Town", IPs: []net.IP{{172, 94, 60, 4}}},
{Region: "North America", Country: "United States", City: "Ashburn", IPs: []net.IP{{46, 243, 249, 2}}},
{Region: "North America", Country: "United States", City: "Chicago", IPs: []net.IP{{37, 230, 169, 4}}},
{Region: "North America", Country: "United States", City: "Columbus", IPs: []net.IP{{172, 94, 115, 2}}},
{Region: "North America", Country: "United States", City: "Georgia", IPs: []net.IP{{141, 101, 168, 4}}},
{Region: "North America", Country: "United States", City: "Houston", IPs: []net.IP{{172, 94, 1, 4}}},
{Region: "North America", Country: "United States", City: "Los Angeles", IPs: []net.IP{{172, 111, 147, 4}}},
{Region: "North America", Country: "United States", City: "Miami", IPs: []net.IP{{172, 94, 108, 4}}},
{Region: "North America", Country: "United States", City: "New Jersey", IPs: []net.IP{{141, 101, 168, 4}}},
{Region: "North America", Country: "United States", City: "New York", IPs: []net.IP{{172, 111, 147, 4}}},
{Region: "North America", Country: "United States", City: "Phoenix", IPs: []net.IP{{172, 94, 26, 4}}},
{Region: "North America", Country: "United States", City: "Salt Lake City", IPs: []net.IP{{141, 101, 168, 4}}},
{Region: "North America", Country: "United States", City: "San Francisco", IPs: []net.IP{{172, 111, 147, 4}}},
{Region: "North America", Country: "United States", City: "Seattle", IPs: []net.IP{{172, 94, 86, 2}}},
{Region: "North America", Country: "United States", City: "Washington, D.C.", IPs: []net.IP{{37, 230, 169, 4}}},
{Region: "Oceania", Country: "Australia", City: "Brisbane", IPs: []net.IP{{172, 111, 236, 2}}},
{Region: "Oceania", Country: "Australia", City: "Melbourne", IPs: []net.IP{{118, 127, 62, 2}}},
{Region: "Oceania", Country: "Australia", City: "Perth", IPs: []net.IP{{172, 94, 123, 4}}},
{Region: "Oceania", Country: "Australia", City: "Sydney", IPs: []net.IP{{46, 243, 245, 4}}},
{Region: "Oceania", Country: "New Zealand", City: "Auckland", IPs: []net.IP{{43, 228, 156, 4}}},
{Region: "South America", Country: "Argentina", City: "Buenos Aires", IPs: []net.IP{{104, 243, 244, 1}}},
{Region: "South America", Country: "Bolivia", City: "Sucre", IPs: []net.IP{{172, 94, 77, 2}}},
{Region: "South America", Country: "Brazil", City: "Sao Paulo", IPs: []net.IP{{104, 243, 244, 2}}},
{Region: "South America", Country: "British Virgin Island", City: "Road Town", IPs: []net.IP{{104, 250, 184, 130}}},
{Region: "South America", Country: "Chile", City: "Santiago", IPs: []net.IP{{191, 96, 183, 251}}},
{Region: "South America", Country: "Colombia", City: "Bogota", IPs: []net.IP{{172, 111, 132, 1}}},
{Region: "South America", Country: "Ecuador", City: "Quito", IPs: []net.IP{{104, 250, 180, 126}}},
{Region: "South America", Country: "Guyana", City: "Georgetown", IPs: []net.IP{{45, 74, 20, 129}}},
{Region: "South America", Country: "Panama", City: "Panama City", IPs: []net.IP{{104, 243, 243, 131}}},
{Region: "South America", Country: "Paraguay", City: "Asuncion", IPs: []net.IP{{45, 74, 19, 129}}},
{Region: "South America", Country: "Peru", City: "Lima", IPs: []net.IP{{172, 111, 131, 1}}},
{Region: "South America", Country: "Suriname", City: "Paramaribo", IPs: []net.IP{{45, 74, 20, 4}}},
}
}

View File

@@ -19,6 +19,8 @@ const (
Vyprvpn models.VPNProvider = "vyprvpn" Vyprvpn models.VPNProvider = "vyprvpn"
// NordVPN is a VPN provider // NordVPN is a VPN provider
Nordvpn models.VPNProvider = "nordvpn" Nordvpn models.VPNProvider = "nordvpn"
// PureVPN is a VPN provider
Purevpn models.VPNProvider = "purevpn"
) )
const ( const (

View File

@@ -93,23 +93,59 @@ func (l *looper) logAndWait(ctx context.Context, err error) {
<-ctx.Done() <-ctx.Done()
} }
func (l *looper) waitForFirstStart(ctx context.Context) {
for {
select {
case <-l.stop:
l.setEnabled(false)
l.logger.Info("not started yet")
case <-l.restart:
if l.isEnabled() {
return
}
l.logger.Info("not restarting because disabled")
case <-l.start:
l.setEnabled(true)
return
case <-ctx.Done():
return
}
}
}
func (l *looper) waitForSubsequentStart(ctx context.Context, unboundCancel context.CancelFunc) {
if l.isEnabled() {
return
}
for {
// wait for a signal to re-enable
select {
case <-l.stop:
l.logger.Info("already disabled")
case <-l.restart:
if !l.isEnabled() {
l.logger.Info("not restarting because disabled")
} else {
return
}
case <-l.start:
l.setEnabled(true)
return
case <-ctx.Done():
unboundCancel()
return
}
}
}
func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) { func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
wg.Add(1) wg.Add(1)
defer wg.Done() defer wg.Done()
l.fallbackToUnencryptedDNS() l.fallbackToUnencryptedDNS()
waitForStart := true l.waitForFirstStart(ctx)
for waitForStart { if ctx.Err() != nil {
select {
case <-l.stop:
l.logger.Info("not started yet")
case <-l.restart:
waitForStart = false
case <-l.start:
waitForStart = false
case <-ctx.Done():
return return
} }
}
defer l.logger.Warn("loop exited") defer l.logger.Warn("loop exited")
var unboundCtx context.Context var unboundCtx context.Context
@@ -118,20 +154,7 @@ func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
triggeredRestart := false triggeredRestart := false
l.setEnabled(true) l.setEnabled(true)
for ctx.Err() == nil { for ctx.Err() == nil {
for !l.isEnabled() { l.waitForSubsequentStart(ctx, unboundCancel)
// wait for a signal to re-enable
select {
case <-l.stop:
l.logger.Info("already disabled")
case <-l.restart:
l.setEnabled(true)
case <-l.start:
l.setEnabled(true)
case <-ctx.Done():
unboundCancel()
return
}
}
settings := l.GetSettings() settings := l.GetSettings()

View File

@@ -62,15 +62,6 @@ func (c *configurator) fallbackToDisabled(ctx context.Context) {
} }
func (c *configurator) enable(ctx context.Context) (err error) { //nolint:gocognit func (c *configurator) enable(ctx context.Context) (err error) { //nolint:gocognit
defaultInterface, defaultGateway, err := c.routing.DefaultRoute()
if err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
localSubnet, err := c.routing.LocalSubnet()
if err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
if err = c.setAllPolicies(ctx, "DROP"); err != nil { if err = c.setAllPolicies(ctx, "DROP"); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err) return fmt.Errorf("cannot enable firewall: %w", err)
} }
@@ -95,50 +86,36 @@ func (c *configurator) enable(ctx context.Context) (err error) { //nolint:gocogn
return fmt.Errorf("cannot enable firewall: %w", err) return fmt.Errorf("cannot enable firewall: %w", err)
} }
for _, conn := range c.vpnConnections { for _, conn := range c.vpnConnections {
if err = c.acceptOutputTrafficToVPN(ctx, defaultInterface, conn, remove); err != nil { if err = c.acceptOutputTrafficToVPN(ctx, c.defaultInterface, conn, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err) return fmt.Errorf("cannot enable firewall: %w", err)
} }
} }
if err = c.acceptOutputThroughInterface(ctx, string(constants.TUN), remove); err != nil { if err = c.acceptOutputThroughInterface(ctx, string(constants.TUN), remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err) return fmt.Errorf("cannot enable firewall: %w", err)
} }
if err := c.acceptInputFromSubnetToSubnet(ctx, "*", localSubnet, localSubnet, remove); err != nil { if err := c.acceptInputFromSubnetToSubnet(ctx, "*", c.localSubnet, c.localSubnet, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err) return fmt.Errorf("cannot enable firewall: %w", err)
} }
if err := c.acceptOutputFromSubnetToSubnet(ctx, "*", localSubnet, localSubnet, remove); err != nil { if err := c.acceptOutputFromSubnetToSubnet(ctx, "*", c.localSubnet, c.localSubnet, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err) return fmt.Errorf("cannot enable firewall: %w", err)
} }
for _, subnet := range c.allowedSubnets { for _, subnet := range c.allowedSubnets {
if err := c.acceptInputFromSubnetToSubnet(ctx, defaultInterface, subnet, localSubnet, remove); err != nil { if err := c.acceptInputFromSubnetToSubnet(ctx, c.defaultInterface, subnet, c.localSubnet, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err) return fmt.Errorf("cannot enable firewall: %w", err)
} }
if err := c.acceptOutputFromSubnetToSubnet(ctx, defaultInterface, localSubnet, subnet, remove); err != nil { if err := c.acceptOutputFromSubnetToSubnet(ctx, c.defaultInterface, c.localSubnet, subnet, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err) return fmt.Errorf("cannot enable firewall: %w", err)
} }
} }
// Re-ensure all routes exist // Re-ensure all routes exist
for _, subnet := range c.allowedSubnets { for _, subnet := range c.allowedSubnets {
if err := c.routing.AddRouteVia(ctx, subnet, defaultGateway, defaultInterface); err != nil { if err := c.routing.AddRouteVia(ctx, subnet, c.defaultGateway, c.defaultInterface); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err) return fmt.Errorf("cannot enable firewall: %w", err)
} }
} }
for port := range c.allowedPorts { for port, intf := range c.allowedInputPorts {
// TODO restrict interface if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
if err := c.acceptInputToPort(ctx, "*", constants.TCP, port, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
if err := c.acceptInputToPort(ctx, "*", constants.UDP, port, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
}
if c.portForwarded > 0 {
const tun = string(constants.TUN)
if err := c.acceptInputToPort(ctx, tun, constants.TCP, c.portForwarded, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
if err := c.acceptInputToPort(ctx, tun, constants.UDP, c.portForwarded, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err) return fmt.Errorf("cannot enable firewall: %w", err)
} }
} }

View File

@@ -18,10 +18,11 @@ type Configurator interface {
SetEnabled(ctx context.Context, enabled bool) (err error) SetEnabled(ctx context.Context, enabled bool) (err error)
SetVPNConnections(ctx context.Context, connections []models.OpenVPNConnection) (err error) SetVPNConnections(ctx context.Context, connections []models.OpenVPNConnection) (err error)
SetAllowedSubnets(ctx context.Context, subnets []net.IPNet) (err error) SetAllowedSubnets(ctx context.Context, subnets []net.IPNet) (err error)
SetAllowedPort(ctx context.Context, port uint16) error SetAllowedPort(ctx context.Context, port uint16, intf string) (err error)
RemoveAllowedPort(ctx context.Context, port uint16) (err error) RemoveAllowedPort(ctx context.Context, port uint16) (err error)
SetPortForward(ctx context.Context, port uint16) (err error)
SetDebug() SetDebug()
// SetNetworkInformation is meant to be called only once
SetNetworkInformation(defaultInterface string, defaultGateway net.IP, localSubnet net.IPNet)
} }
type configurator struct { //nolint:maligned type configurator struct { //nolint:maligned
@@ -31,13 +32,16 @@ type configurator struct { //nolint:maligned
fileManager files.FileManager // for custom iptables rules fileManager files.FileManager // for custom iptables rules
iptablesMutex sync.Mutex iptablesMutex sync.Mutex
debug bool debug bool
defaultInterface string
defaultGateway net.IP
localSubnet net.IPNet
networkInfoMutex sync.Mutex
// State // State
enabled bool enabled bool
vpnConnections []models.OpenVPNConnection vpnConnections []models.OpenVPNConnection
allowedSubnets []net.IPNet allowedSubnets []net.IPNet
allowedPorts map[uint16]struct{} allowedInputPorts map[uint16]string // port to interface mapping
portForwarded uint16
stateMutex sync.Mutex stateMutex sync.Mutex
} }
@@ -48,10 +52,18 @@ func NewConfigurator(logger logging.Logger, routing routing.Routing, fileManager
logger: logger.WithPrefix("firewall: "), logger: logger.WithPrefix("firewall: "),
routing: routing, routing: routing,
fileManager: fileManager, fileManager: fileManager,
allowedPorts: make(map[uint16]struct{}), allowedInputPorts: make(map[uint16]string),
} }
} }
func (c *configurator) SetDebug() { func (c *configurator) SetDebug() {
c.debug = true c.debug = true
} }
func (c *configurator) SetNetworkInformation(defaultInterface string, defaultGateway net.IP, localSubnet net.IPNet) {
c.networkInfoMutex.Lock()
defer c.networkInfoMutex.Unlock()
c.defaultInterface = defaultInterface
c.defaultGateway = defaultGateway
c.localSubnet = localSubnet
}

View File

@@ -134,14 +134,15 @@ func (c *configurator) acceptOutputFromSubnetToSubnet(ctx context.Context, intf
} }
// Used for port forwarding, with intf set to tun // Used for port forwarding, with intf set to tun
func (c *configurator) acceptInputToPort(ctx context.Context, intf string, protocol models.NetworkProtocol, port uint16, remove bool) error { func (c *configurator) acceptInputToPort(ctx context.Context, intf string, port uint16, remove bool) error {
interfaceFlag := "-i " + intf interfaceFlag := "-i " + intf
if intf == "*" { // all interfaces if intf == "*" { // all interfaces
interfaceFlag = "" interfaceFlag = ""
} }
return c.runIptablesInstruction(ctx, return c.runIptablesInstructions(ctx, []string{
fmt.Sprintf("%s INPUT %s -p %s --dport %d -j ACCEPT", appendOrDelete(remove), interfaceFlag, protocol, port), fmt.Sprintf("%s INPUT %s -p tcp --dport %d -j ACCEPT", appendOrDelete(remove), interfaceFlag, port),
) fmt.Sprintf("%s INPUT %s -p udp --dport %d -j ACCEPT", appendOrDelete(remove), interfaceFlag, port),
})
} }
func (c *configurator) runUserPostRules(ctx context.Context, filepath string, remove bool) error { func (c *configurator) runUserPostRules(ctx context.Context, filepath string, remove bool) error {

View File

@@ -3,11 +3,9 @@ package firewall
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/qdm12/private-internet-access-docker/internal/constants"
) )
func (c *configurator) SetAllowedPort(ctx context.Context, port uint16) (err error) { func (c *configurator) SetAllowedPort(ctx context.Context, port uint16, intf string) (err error) {
c.stateMutex.Lock() c.stateMutex.Lock()
defer c.stateMutex.Unlock() defer c.stateMutex.Unlock()
@@ -16,25 +14,28 @@ func (c *configurator) SetAllowedPort(ctx context.Context, port uint16) (err err
} }
if !c.enabled { if !c.enabled {
c.logger.Info("firewall disabled, only updating allowed ports internal list") c.logger.Info("firewall disabled, only updating allowed ports internal state")
c.allowedPorts[port] = struct{}{} c.allowedInputPorts[port] = intf
return nil return nil
} }
c.logger.Info("setting allowed port %d through firewall...", port) c.logger.Info("setting allowed input port %d through interface %s...", port, intf)
if _, ok := c.allowedPorts[port]; ok { if existingIntf, ok := c.allowedInputPorts[port]; ok {
if intf == existingIntf {
return nil return nil
} }
const remove = true
if err := c.acceptInputToPort(ctx, existingIntf, port, remove); err != nil {
return fmt.Errorf("cannot remove old allowed port %d through interface %s: %w", port, existingIntf, err)
}
}
const remove = false const remove = false
if err := c.acceptInputToPort(ctx, "*", constants.TCP, port, remove); err != nil { if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
return fmt.Errorf("cannot set allowed port %d through firewall: %w", port, err) return fmt.Errorf("cannot set allowed port %d through interface %s: %w", port, intf, err)
} }
if err := c.acceptInputToPort(ctx, "*", constants.UDP, port, remove); err != nil { c.allowedInputPorts[port] = intf
return fmt.Errorf("cannot set allowed port %d through firewall: %w", port, err)
}
c.allowedPorts[port] = struct{}{}
return nil return nil
} }
@@ -49,63 +50,22 @@ func (c *configurator) RemoveAllowedPort(ctx context.Context, port uint16) (err
if !c.enabled { if !c.enabled {
c.logger.Info("firewall disabled, only updating allowed ports internal list") c.logger.Info("firewall disabled, only updating allowed ports internal list")
delete(c.allowedPorts, port) delete(c.allowedInputPorts, port)
return nil return nil
} }
c.logger.Info("removing allowed port %d through firewall...", port) c.logger.Info("removing allowed port %d through firewall...", port)
if _, ok := c.allowedPorts[port]; !ok { intf, ok := c.allowedInputPorts[port]
if !ok {
return nil return nil
} }
const remove = true const remove = true
if err := c.acceptInputToPort(ctx, "*", constants.TCP, port, remove); err != nil { if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
return fmt.Errorf("cannot remove allowed port %d through firewall: %w", port, err) return fmt.Errorf("cannot remove allowed port %d through interface %s: %w", port, intf, err)
} }
if err := c.acceptInputToPort(ctx, "*", constants.UDP, port, remove); err != nil { delete(c.allowedInputPorts, port)
return fmt.Errorf("cannot remove allowed port %d through firewall: %w", port, err)
}
delete(c.allowedPorts, port)
return nil return nil
} }
// Use 0 to remove
func (c *configurator) SetPortForward(ctx context.Context, port uint16) (err error) {
c.stateMutex.Lock()
defer c.stateMutex.Unlock()
if port == c.portForwarded {
return nil
}
if !c.enabled {
c.logger.Info("firewall disabled, only updating port forwarded internally")
c.portForwarded = port
return nil
}
const tun = string(constants.TUN)
if c.portForwarded > 0 {
if err := c.acceptInputToPort(ctx, tun, constants.TCP, c.portForwarded, true); err != nil {
return fmt.Errorf("cannot remove outdated port forward rule from firewall: %w", err)
}
if err := c.acceptInputToPort(ctx, tun, constants.UDP, c.portForwarded, true); err != nil {
return fmt.Errorf("cannot remove outdated port forward rule from firewall: %w", err)
}
}
if port == 0 { // not changing port
c.portForwarded = 0
return nil
}
if err := c.acceptInputToPort(ctx, tun, constants.TCP, port, false); err != nil {
return fmt.Errorf("cannot accept port forwarded through firewall: %w", err)
}
if err := c.acceptInputToPort(ctx, tun, constants.UDP, port, false); err != nil {
return fmt.Errorf("cannot accept port forwarded through firewall: %w", err)
}
return nil
}

View File

@@ -12,9 +12,7 @@ func (c *configurator) SetAllowedSubnets(ctx context.Context, subnets []net.IPNe
if !c.enabled { if !c.enabled {
c.logger.Info("firewall disabled, only updating allowed subnets internal list and updating routes") c.logger.Info("firewall disabled, only updating allowed subnets internal list and updating routes")
if err := c.updateSubnetRoutes(ctx, c.allowedSubnets, subnets); err != nil { c.updateSubnetRoutes(ctx, c.allowedSubnets, subnets)
return err
}
c.allowedSubnets = make([]net.IPNet, len(subnets)) c.allowedSubnets = make([]net.IPNet, len(subnets))
copy(c.allowedSubnets, subnets) copy(c.allowedSubnets, subnets)
return nil return nil
@@ -28,17 +26,8 @@ func (c *configurator) SetAllowedSubnets(ctx context.Context, subnets []net.IPNe
return nil return nil
} }
defaultInterface, defaultGateway, err := c.routing.DefaultRoute() c.removeSubnets(ctx, subnetsToRemove, c.defaultInterface, c.localSubnet)
if err != nil { if err := c.addSubnets(ctx, subnetsToAdd, c.defaultInterface, c.defaultGateway, c.localSubnet); err != nil {
return fmt.Errorf("cannot set allowed subnets through firewall: %w", err)
}
localSubnet, err := c.routing.LocalSubnet()
if err != nil {
return fmt.Errorf("cannot set allowed subnets through firewall: %w", err)
}
c.removeSubnets(ctx, subnetsToRemove, defaultInterface, localSubnet)
if err := c.addSubnets(ctx, subnetsToAdd, defaultInterface, defaultGateway, localSubnet); err != nil {
return fmt.Errorf("cannot set allowed subnets through firewall: %w", err) return fmt.Errorf("cannot set allowed subnets through firewall: %w", err)
} }
@@ -135,15 +124,12 @@ func (c *configurator) addSubnets(ctx context.Context, subnets []net.IPNet, defa
return nil return nil
} }
func (c *configurator) updateSubnetRoutes(ctx context.Context, oldSubnets, newSubnets []net.IPNet) error { // updateSubnetRoutes does not return an error in order to try to run as many route commands as possible
func (c *configurator) updateSubnetRoutes(ctx context.Context, oldSubnets, newSubnets []net.IPNet) {
subnetsToAdd := findSubnetsToAdd(oldSubnets, newSubnets) subnetsToAdd := findSubnetsToAdd(oldSubnets, newSubnets)
subnetsToRemove := findSubnetsToRemove(oldSubnets, newSubnets) subnetsToRemove := findSubnetsToRemove(oldSubnets, newSubnets)
if len(subnetsToAdd) == 0 && len(subnetsToRemove) == 0 { if len(subnetsToAdd) == 0 && len(subnetsToRemove) == 0 {
return nil return
}
defaultInterface, defaultGateway, err := c.routing.DefaultRoute()
if err != nil {
return err
} }
for _, subnet := range subnetsToRemove { for _, subnet := range subnetsToRemove {
if err := c.routing.DeleteRouteVia(ctx, subnet); err != nil { if err := c.routing.DeleteRouteVia(ctx, subnet); err != nil {
@@ -151,9 +137,8 @@ func (c *configurator) updateSubnetRoutes(ctx context.Context, oldSubnets, newSu
} }
} }
for _, subnet := range subnetsToAdd { for _, subnet := range subnetsToAdd {
if err := c.routing.AddRouteVia(ctx, subnet, defaultGateway, defaultInterface); err != nil { if err := c.routing.AddRouteVia(ctx, subnet, c.defaultGateway, c.defaultInterface); err != nil {
c.logger.Error("cannot add route for subnet: %s", err) c.logger.Error("cannot add route for subnet: %s", err)
} }
} }
return nil
} }

View File

@@ -26,13 +26,8 @@ func (c *configurator) SetVPNConnections(ctx context.Context, connections []mode
return nil return nil
} }
defaultInterface, _, err := c.routing.DefaultRoute() c.removeConnections(ctx, connectionsToRemove, c.defaultInterface)
if err != nil { if err := c.addConnections(ctx, connectionsToAdd, c.defaultInterface); err != nil {
return fmt.Errorf("cannot set VPN connections through firewall: %w", err)
}
c.removeConnections(ctx, connectionsToRemove, defaultInterface)
if err := c.addConnections(ctx, connectionsToAdd, defaultInterface); err != nil {
return fmt.Errorf("cannot set VPN connections through firewall: %w", err) return fmt.Errorf("cannot set VPN connections through firewall: %w", err)
} }

View File

@@ -1,5 +1,10 @@
package models package models
import (
"fmt"
"strings"
)
type ( type (
// VPNDevice is the device name used to tunnel using Openvpn // VPNDevice is the device name used to tunnel using Openvpn
VPNDevice string VPNDevice string
@@ -14,7 +19,45 @@ type (
// TinyProxyLogLevel is the log level for TinyProxy // TinyProxyLogLevel is the log level for TinyProxy
TinyProxyLogLevel string TinyProxyLogLevel string
// VPNProvider is the name of the VPN provider to be used // VPNProvider is the name of the VPN provider to be used
VPNProvider string // TODO VPNProvider string
// NetworkProtocol contains the network protocol to be used to communicate with the VPN servers // NetworkProtocol contains the network protocol to be used to communicate with the VPN servers
NetworkProtocol string NetworkProtocol string
) )
func marshalJSONString(s string) (data []byte, err error) {
return []byte(fmt.Sprintf("%q", s)), nil
}
func unmarshalJSONString(data []byte) (s string) {
s = string(data)
s = strings.TrimPrefix(s, "\"")
s = strings.TrimSuffix(s, "\"")
return s
}
func (v *VPNProvider) MarshalJSON() ([]byte, error) {
return marshalJSONString(string(*v))
}
func (v *VPNProvider) UnmarshalJSON(data []byte) error {
*v = VPNProvider(unmarshalJSONString(data))
return nil
}
func (n *NetworkProtocol) MarshalJSON() ([]byte, error) {
return marshalJSONString(string(*n))
}
func (n *NetworkProtocol) UnmarshalJSON(data []byte) error {
*n = NetworkProtocol(unmarshalJSONString(data))
return nil
}
func (f *Filepath) MarshalJSON() ([]byte, error) {
return marshalJSONString(string(*f))
}
func (f *Filepath) UnmarshalJSON(data []byte) error {
*f = Filepath(unmarshalJSONString(data))
return nil
}

View File

@@ -0,0 +1,41 @@
package models
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_VPNProvider_JSON(t *testing.T) {
t.Parallel()
v := VPNProvider("name")
data, err := v.MarshalJSON()
require.NoError(t, err)
assert.Equal(t, []byte{0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22}, data)
err = v.UnmarshalJSON(data)
require.NoError(t, err)
assert.Equal(t, VPNProvider("name"), v)
}
func Test_NetworkProtocol_JSON(t *testing.T) {
t.Parallel()
v := NetworkProtocol("name")
data, err := v.MarshalJSON()
require.NoError(t, err)
assert.Equal(t, []byte{0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22}, data)
err = v.UnmarshalJSON(data)
require.NoError(t, err)
assert.Equal(t, NetworkProtocol("name"), v)
}
func Test_Filepath_JSON(t *testing.T) {
t.Parallel()
v := Filepath("name")
data, err := v.MarshalJSON()
require.NoError(t, err)
assert.Equal(t, []byte{0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22}, data)
err = v.UnmarshalJSON(data)
require.NoError(t, err)
assert.Equal(t, Filepath("name"), v)
}

View File

@@ -8,48 +8,50 @@ import (
// ProviderSettings contains settings specific to a VPN provider // ProviderSettings contains settings specific to a VPN provider
type ProviderSettings struct { type ProviderSettings struct {
Name VPNProvider Name VPNProvider `json:"name"`
ServerSelection ServerSelection ServerSelection ServerSelection `json:"serverSelection"`
ExtraConfigOptions ExtraConfigOptions ExtraConfigOptions ExtraConfigOptions `json:"extraConfig"`
PortForwarding PortForwarding PortForwarding PortForwarding `json:"portForwarding"`
} }
type ServerSelection struct { //nolint:maligned type ServerSelection struct { //nolint:maligned
// Common // Common
Protocol NetworkProtocol Protocol NetworkProtocol `json:"networkProtocol"`
TargetIP net.IP TargetIP net.IP `json:"targetIP,omitempty"`
// Cyberghost, PIA, Surfshark, Windscribe, Vyprvpn, NordVPN // Cyberghost, PIA, Surfshark, Windscribe, Vyprvpn, NordVPN
Region string Region string `json:"region"`
// Cyberghost // Cyberghost
Group string Group string `json:"group"`
// Mullvad, PureVPN
Country string `json:"country"`
City string `json:"city"`
// Mullvad // Mullvad
Country string ISP string `json:"isp"`
City string Owned bool `json:"owned"`
ISP string
Owned bool
// Mullvad, Windscribe // Mullvad, Windscribe
CustomPort uint16 CustomPort uint16 `json:"customPort"`
// PIA
EncryptionPreset string
// NordVPN // NordVPN
Number uint16 Number uint16 `json:"number"`
// PIA
EncryptionPreset string `json:"encryptionPreset"`
} }
type ExtraConfigOptions struct { type ExtraConfigOptions struct {
ClientKey string // Cyberghost ClientKey string `json:"-"` // Cyberghost
EncryptionPreset string // PIA EncryptionPreset string `json:"encryptionPreset"` // PIA
} }
// PortForwarding contains settings for port forwarding // PortForwarding contains settings for port forwarding
type PortForwarding struct { type PortForwarding struct {
Enabled bool Enabled bool `json:"enabled"`
Filepath Filepath Filepath Filepath `json:"filepath"`
} }
func (p *PortForwarding) String() string { func (p *PortForwarding) String() string {
@@ -110,6 +112,12 @@ func (p *ProviderSettings) String() string {
"Region: "+p.ServerSelection.Region, "Region: "+p.ServerSelection.Region,
"Number: "+number, "Number: "+number,
) )
case "purevpn":
settingsList = append(settingsList,
"Region: "+p.ServerSelection.Region,
"Country: "+p.ServerSelection.Country,
"City: "+p.ServerSelection.City,
)
default: default:
settingsList = append(settingsList, settingsList = append(settingsList,
"<Missing String method, please implement me!>", "<Missing String method, please implement me!>",

View File

@@ -44,3 +44,10 @@ type NordvpnServer struct { //nolint:maligned
TCP bool TCP bool
UDP bool UDP bool
} }
type PurevpnServer struct {
Region string
Country string
City string
IPs []net.IP
}

View File

@@ -23,6 +23,7 @@ type Looper interface {
PortForward() PortForward()
GetSettings() (settings settings.OpenVPN) GetSettings() (settings settings.OpenVPN)
SetSettings(settings settings.OpenVPN) SetSettings(settings settings.OpenVPN)
GetPortForwarded() (portForwarded uint16)
} }
type looper struct { type looper struct {
@@ -30,6 +31,8 @@ type looper struct {
provider models.VPNProvider provider models.VPNProvider
settings settings.OpenVPN settings settings.OpenVPN
settingsMutex sync.RWMutex settingsMutex sync.RWMutex
portForwarded uint16
portForwardedMutex sync.RWMutex
// Fixed parameters // Fixed parameters
uid int uid int
gid int gid int
@@ -98,7 +101,10 @@ func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
settings := l.GetSettings() settings := l.GetSettings()
providerConf := provider.New(l.provider) providerConf := provider.New(l.provider)
connections, err := providerConf.GetOpenVPNConnections(settings.Provider.ServerSelection) connections, err := providerConf.GetOpenVPNConnections(settings.Provider.ServerSelection)
if err != nil {
l.fatalOnError(err) l.fatalOnError(err)
return
}
lines := providerConf.BuildConf( lines := providerConf.BuildConf(
connections, connections,
settings.Verbosity, settings.Verbosity,
@@ -109,14 +115,19 @@ func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
settings.Auth, settings.Auth,
settings.Provider.ExtraConfigOptions, settings.Provider.ExtraConfigOptions,
) )
err = l.fileManager.WriteLinesToFile(string(constants.OpenVPNConf), lines, files.Ownership(l.uid, l.gid), files.Permissions(0400)) if err := l.fileManager.WriteLinesToFile(string(constants.OpenVPNConf), lines, files.Ownership(l.uid, l.gid), files.Permissions(0400)); err != nil {
l.fatalOnError(err) l.fatalOnError(err)
return
}
err = l.conf.WriteAuthFile(settings.User, settings.Password, l.uid, l.gid) if err := l.conf.WriteAuthFile(settings.User, settings.Password, l.uid, l.gid); err != nil {
l.fatalOnError(err) l.fatalOnError(err)
return
}
if err := l.fw.SetVPNConnections(ctx, connections); err != nil { if err := l.fw.SetVPNConnections(ctx, connections); err != nil {
l.fatalOnError(err) l.fatalOnError(err)
return
} }
openvpnCtx, openvpnCancel := context.WithCancel(context.Background()) openvpnCtx, openvpnCancel := context.WithCancel(context.Background())
@@ -187,10 +198,19 @@ func (l *looper) portForward(ctx context.Context, providerConf provider.Provider
port, err = providerConf.GetPortForward(client) port, err = providerConf.GetPortForward(client)
if err != nil { if err != nil {
l.logAndWait(ctx, err) l.logAndWait(ctx, err)
continue
} }
}
l.logger.Info("port forwarded is %d", port) l.logger.Info("port forwarded is %d", port)
l.portForwardedMutex.Lock()
if err := l.fw.RemoveAllowedPort(ctx, l.portForwarded); err != nil {
l.logger.Error(err)
} }
if err := l.fw.SetAllowedPort(ctx, port, string(constants.TUN)); err != nil {
l.logger.Error(err)
}
l.portForwarded = port
l.portForwardedMutex.Unlock()
filepath := settings.Provider.PortForwarding.Filepath filepath := settings.Provider.PortForwarding.Filepath
l.logger.Info("writing forwarded port to %s", filepath) l.logger.Info("writing forwarded port to %s", filepath)
@@ -201,8 +221,10 @@ func (l *looper) portForward(ctx context.Context, providerConf provider.Provider
if err != nil { if err != nil {
l.logger.Error(err) l.logger.Error(err)
} }
}
if err := l.fw.SetPortForward(ctx, port); err != nil {
l.logger.Error(err) func (l *looper) GetPortForwarded() (portForwarded uint16) {
} l.portForwardedMutex.RLock()
defer l.portForwardedMutex.RUnlock()
return l.portForwarded
} }

View File

@@ -10,14 +10,15 @@ import (
// GetCyberghostGroup obtains the server group for the Cyberghost server from the // GetCyberghostGroup obtains the server group for the Cyberghost server from the
// environment variable CYBERGHOST_GROUP // environment variable CYBERGHOST_GROUP
func (p *reader) GetCyberghostGroup() (group string, err error) { func (p *reader) GetCyberghostGroup() (group string, err error) {
s, err := p.envParams.GetValueIfInside("CYBERGHOST_GROUP", constants.CyberghostGroupChoices()) s, err := p.envParams.GetValueIfInside("CYBERGHOST_GROUP", constants.CyberghostGroupChoices(), libparams.Default("Premium UDP Europe"))
return s, err return s, err
} }
// GetCyberghostRegion obtains the country name for the Cyberghost server from the // GetCyberghostRegion obtains the country name for the Cyberghost server from the
// environment variable REGION // environment variable REGION
func (p *reader) GetCyberghostRegion() (region string, err error) { func (p *reader) GetCyberghostRegion() (region string, err error) {
s, err := p.envParams.GetValueIfInside("REGION", constants.CyberghostRegionChoices()) choices := append(constants.CyberghostRegionChoices(), "")
s, err := p.envParams.GetValueIfInside("REGION", choices)
return s, err return s, err
} }

View File

@@ -3,6 +3,7 @@ package params
import ( import (
"fmt" "fmt"
"net" "net"
"strconv"
"strings" "strings"
libparams "github.com/qdm12/golibs/params" libparams "github.com/qdm12/golibs/params"
@@ -35,6 +36,30 @@ func (r *reader) GetExtraSubnets() (extraSubnets []net.IPNet, err error) {
return extraSubnets, nil return extraSubnets, nil
} }
// GetAllowedVPNInputPorts obtains a list of input ports to allow from the
// VPN server side in the firewall, from the environment variable FIREWALL_VPN_INPUT_PORTS
func (r *reader) GetVPNInputPorts() (ports []uint16, err error) {
s, err := r.envParams.GetEnv("FIREWALL_VPN_INPUT_PORTS", libparams.Default(""))
if err != nil {
return nil, err
}
if len(s) == 0 {
return nil, nil
}
portsStr := strings.Split(s, ",")
ports = make([]uint16, len(portsStr))
for i := range portsStr {
portInt, err := strconv.Atoi(portsStr[i])
if err != nil {
return nil, fmt.Errorf("VPN input port %q is not valid (%s)", portInt, err)
} else if portInt <= 0 || portInt > 65535 {
return nil, fmt.Errorf("VPN input port %d must be between 1 and 65535", portInt)
}
ports[i] = uint16(portInt)
}
return ports, nil
}
// GetFirewallDebug obtains if the firewall should run in debug verbose mode from the environment variable FIREWALL_DEBUG // GetFirewallDebug obtains if the firewall should run in debug verbose mode from the environment variable FIREWALL_DEBUG
func (r *reader) GetFirewallDebug() (debug bool, err error) { func (r *reader) GetFirewallDebug() (debug bool, err error) {
return r.envParams.GetOnOff("FIREWALL_DEBUG", libparams.Default("off")) return r.envParams.GetOnOff("FIREWALL_DEBUG", libparams.Default("off"))

View File

@@ -8,7 +8,8 @@ import (
// GetNordvpnRegion obtains the region (country) for the NordVPN server from the // GetNordvpnRegion obtains the region (country) for the NordVPN server from the
// environment variable REGION // environment variable REGION
func (r *reader) GetNordvpnRegion() (region string, err error) { func (r *reader) GetNordvpnRegion() (region string, err error) {
return r.envParams.GetValueIfInside("REGION", constants.NordvpnRegionChoices()) choices := append(constants.NordvpnRegionChoices(), "")
return r.envParams.GetValueIfInside("REGION", choices)
} }
// GetNordvpnRegion obtains the server number (optional) for the NordVPN server from the // GetNordvpnRegion obtains the server number (optional) for the NordVPN server from the

View File

@@ -42,6 +42,7 @@ type Reader interface {
// Firewall getters // Firewall getters
GetFirewall() (enabled bool, err error) GetFirewall() (enabled bool, err error)
GetExtraSubnets() (extraSubnets []net.IPNet, err error) GetExtraSubnets() (extraSubnets []net.IPNet, err error)
GetVPNInputPorts() (ports []uint16, err error)
GetFirewallDebug() (debug bool, err error) GetFirewallDebug() (debug bool, err error)
// VPN getters // VPN getters
@@ -85,6 +86,11 @@ type Reader interface {
GetNordvpnRegion() (region string, err error) GetNordvpnRegion() (region string, err error)
GetNordvpnNumber() (number uint16, err error) GetNordvpnNumber() (number uint16, err error)
// PureVPN getters
GetPurevpnRegion() (region string, err error)
GetPurevpnCountry() (country string, err error)
GetPurevpnCity() (city string, err error)
// Shadowsocks getters // Shadowsocks getters
GetShadowSocks() (activated bool, err error) GetShadowSocks() (activated bool, err error)
GetShadowSocksLog() (activated bool, err error) GetShadowSocksLog() (activated bool, err error)
@@ -130,7 +136,7 @@ func NewReader(logger logging.Logger, fileManager files.FileManager) Reader {
// GetVPNSP obtains the VPN service provider to use from the environment variable VPNSP // GetVPNSP obtains the VPN service provider to use from the environment variable VPNSP
func (r *reader) GetVPNSP() (vpnServiceProvider models.VPNProvider, err error) { func (r *reader) GetVPNSP() (vpnServiceProvider models.VPNProvider, err error) {
s, err := r.envParams.GetValueIfInside("VPNSP", []string{"pia", "private internet access", "mullvad", "windscribe", "surfshark", "cyberghost", "vyprvpn", "nordvpn"}) s, err := r.envParams.GetValueIfInside("VPNSP", []string{"pia", "private internet access", "mullvad", "windscribe", "surfshark", "cyberghost", "vyprvpn", "nordvpn", "purevpn"})
if s == "pia" { if s == "pia" {
s = "private internet access" s = "private internet access"
} }

View File

@@ -2,7 +2,6 @@ package params
import ( import (
"fmt" "fmt"
"math/rand"
libparams "github.com/qdm12/golibs/params" libparams "github.com/qdm12/golibs/params"
"github.com/qdm12/private-internet-access-docker/internal/constants" "github.com/qdm12/private-internet-access-docker/internal/constants"
@@ -60,9 +59,5 @@ func (r *reader) GetPIAEncryptionPreset() (preset string, err error) {
// environment variable REGION // environment variable REGION
func (r *reader) GetPIARegion() (region string, err error) { func (r *reader) GetPIARegion() (region string, err error) {
choices := append(constants.PIAGeoChoices(), "") choices := append(constants.PIAGeoChoices(), "")
s, err := r.envParams.GetValueIfInside("REGION", choices) return r.envParams.GetValueIfInside("REGION", choices)
if len(s) == 0 { // Suggestion by @rorph https://github.com/rorph
s = choices[rand.Int()%len(choices)] //nolint:gosec
}
return s, err
} }

View File

@@ -0,0 +1,26 @@
package params
import (
"github.com/qdm12/private-internet-access-docker/internal/constants"
)
// GetPurevpnRegion obtains the region (continent) for the PureVPN server from the
// environment variable REGION
func (r *reader) GetPurevpnRegion() (region string, err error) {
choices := append(constants.PurevpnRegionChoices(), "")
return r.envParams.GetValueIfInside("REGION", choices)
}
// GetPurevpnCountry obtains the country for the PureVPN server from the
// environment variable COUNTRY
func (r *reader) GetPurevpnCountry() (country string, err error) {
choices := append(constants.PurevpnCountryChoices(), "")
return r.envParams.GetValueIfInside("COUNTRY", choices)
}
// GetPurevpnCity obtains the city for the PureVPN server from the
// environment variable CITY
func (r *reader) GetPurevpnCity() (city string, err error) {
choices := append(constants.PurevpnCityChoices(), "")
return r.envParams.GetValueIfInside("CITY", choices)
}

View File

@@ -7,6 +7,6 @@ import (
// GetSurfsharkRegion obtains the region for the Surfshark server from the // GetSurfsharkRegion obtains the region for the Surfshark server from the
// environment variable REGION // environment variable REGION
func (r *reader) GetSurfsharkRegion() (region string, err error) { func (r *reader) GetSurfsharkRegion() (region string, err error) {
s, err := r.envParams.GetValueIfInside("REGION", constants.SurfsharkRegionChoices()) choices := append(constants.SurfsharkRegionChoices(), "")
return s, err return r.envParams.GetValueIfInside("REGION", choices)
} }

View File

@@ -7,5 +7,6 @@ import (
// GetVyprvpnRegion obtains the region for the Vyprvpn server from the // GetVyprvpnRegion obtains the region for the Vyprvpn server from the
// environment variable REGION // environment variable REGION
func (r *reader) GetVyprvpnRegion() (region string, err error) { func (r *reader) GetVyprvpnRegion() (region string, err error) {
return r.envParams.GetValueIfInside("REGION", constants.VyprvpnRegionChoices()) choices := append(constants.VyprvpnRegionChoices(), "")
return r.envParams.GetValueIfInside("REGION", choices)
} }

View File

@@ -11,8 +11,8 @@ import (
// GetWindscribeRegion obtains the region for the Windscribe server from the // GetWindscribeRegion obtains the region for the Windscribe server from the
// environment variable REGION // environment variable REGION
func (r *reader) GetWindscribeRegion() (region string, err error) { func (r *reader) GetWindscribeRegion() (region string, err error) {
s, err := r.envParams.GetValueIfInside("REGION", constants.WindscribeRegionChoices()) choices := append(constants.WindscribeRegionChoices(), "")
return s, err return r.envParams.GetValueIfInside("REGION", choices)
} }
// GetMullvadPort obtains the port to reach the Mullvad server on from the // GetMullvadPort obtains the port to reach the Mullvad server on from the

View File

@@ -2,7 +2,6 @@ package provider
import ( import (
"fmt" "fmt"
"net"
"strings" "strings"
"github.com/qdm12/golibs/network" "github.com/qdm12/golibs/network"
@@ -16,32 +15,48 @@ func newCyberghost() *cyberghost {
return &cyberghost{} return &cyberghost{}
} }
func (c *cyberghost) filterServers(region, group string) (servers []models.CyberghostServer) {
allServers := constants.CyberghostServers()
for i, server := range allServers {
if len(region) == 0 {
server.Region = ""
}
if len(group) == 0 {
server.Group = ""
}
if strings.EqualFold(server.Region, region) && strings.EqualFold(server.Group, group) {
servers = append(servers, allServers[i])
}
}
return servers
}
func (c *cyberghost) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) { func (c *cyberghost) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) {
var IPs []net.IP servers := c.filterServers(selection.Region, selection.Group)
for _, server := range constants.CyberghostServers() { if len(servers) == 0 {
if strings.EqualFold(server.Region, selection.Region) && strings.EqualFold(server.Group, selection.Group) { return nil, fmt.Errorf("no server found for region %q and group %q", selection.Region, selection.Group)
IPs = server.IPs
}
}
if len(IPs) == 0 {
return nil, fmt.Errorf("no IP found for group %q and region %q", selection.Group, selection.Region)
} }
for _, server := range servers {
for _, IP := range server.IPs {
if selection.TargetIP != nil { if selection.TargetIP != nil {
found := false if selection.TargetIP.Equal(IP) {
for i := range IPs { return []models.OpenVPNConnection{{IP: IP, Port: 443, Protocol: selection.Protocol}}, nil
if IPs[i].Equal(selection.TargetIP) {
found = true
break
} }
} } else {
if !found {
return nil, fmt.Errorf("target IP address %q not found in IP addresses", selection.TargetIP)
}
IPs = []net.IP{selection.TargetIP}
}
for _, IP := range IPs {
connections = append(connections, models.OpenVPNConnection{IP: IP, Port: 443, Protocol: selection.Protocol}) connections = append(connections, models.OpenVPNConnection{IP: IP, Port: 443, Protocol: selection.Protocol})
} }
}
}
if selection.TargetIP != nil {
return nil, fmt.Errorf("target IP %s not found in IP addresses", selection.TargetIP)
}
if len(connections) > 64 {
connections = connections[:64]
}
return connections, nil return connections, nil
} }

View File

@@ -2,6 +2,7 @@ package provider
import ( import (
"fmt" "fmt"
"strings"
"github.com/qdm12/golibs/network" "github.com/qdm12/golibs/network"
"github.com/qdm12/private-internet-access-docker/internal/constants" "github.com/qdm12/private-internet-access-docker/internal/constants"
@@ -14,11 +15,33 @@ func newMullvad() *mullvad {
return &mullvad{} return &mullvad{}
} }
func (m *mullvad) filterServers(country, city, isp string) (servers []models.MullvadServer) {
allServers := constants.MullvadServers()
for i, server := range allServers {
if len(country) == 0 {
server.Country = ""
}
if len(city) == 0 {
server.City = ""
}
if len(isp) == 0 {
server.ISP = ""
}
if strings.EqualFold(server.Country, country) &&
strings.EqualFold(server.City, city) &&
strings.EqualFold(server.ISP, isp) {
servers = append(servers, allServers[i])
}
}
return servers
}
func (m *mullvad) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) { func (m *mullvad) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) {
servers := constants.MullvadServerFilter(selection.Country, selection.City, selection.ISP) servers := m.filterServers(selection.Country, selection.City, selection.ISP)
if len(servers) == 0 { if len(servers) == 0 {
return nil, fmt.Errorf("no server found for country %q, city %q and ISP %q", selection.Country, selection.City, selection.ISP) return nil, fmt.Errorf("no server found for country %q, city %q and ISP %q", selection.Country, selection.City, selection.ISP)
} }
for _, server := range servers { for _, server := range servers {
port := server.DefaultPort port := server.DefaultPort
if selection.CustomPort > 0 { if selection.CustomPort > 0 {
@@ -34,9 +57,15 @@ func (m *mullvad) GetOpenVPNConnections(selection models.ServerSelection) (conne
} }
} }
} }
if selection.TargetIP != nil { if selection.TargetIP != nil {
return nil, fmt.Errorf("target IP address %q not found in IP addresses", selection.TargetIP) return nil, fmt.Errorf("target IP address %q not found in IP addresses", selection.TargetIP)
} }
if len(connections) > 64 {
connections = connections[:64]
}
return connections, nil return connections, nil
} }

View File

@@ -2,7 +2,6 @@ package provider
import ( import (
"fmt" "fmt"
"net"
"strings" "strings"
"github.com/qdm12/golibs/network" "github.com/qdm12/golibs/network"
@@ -16,60 +15,34 @@ func newNordvpn() *nordvpn {
return &nordvpn{} return &nordvpn{}
} }
func findServers(selection models.ServerSelection) (servers []models.NordvpnServer) { func (n *nordvpn) filterServers(region string, protocol models.NetworkProtocol, number uint16) (servers []models.NordvpnServer) {
for _, server := range constants.NordvpnServers() { allServers := constants.NordvpnServers()
if strings.EqualFold(server.Region, selection.Region) { for i, server := range allServers {
if (selection.Protocol == constants.TCP && !server.TCP) || (selection.Protocol == constants.UDP && !server.UDP) { if len(region) == 0 {
server.Region = ""
}
if number == 0 {
server.Number = 0
}
if protocol == constants.TCP && !server.TCP {
continue
} else if protocol == constants.UDP && !server.UDP {
continue continue
} }
if selection.Number > 0 && server.Number == selection.Number { if strings.EqualFold(server.Region, region) && server.Number == number {
return []models.NordvpnServer{server} servers = append(servers, allServers[i])
}
servers = append(servers, server)
} }
} }
return servers return servers
} }
func extractIPsFromServers(servers []models.NordvpnServer) (ips []net.IP) {
ips = make([]net.IP, len(servers))
for i := range servers {
ips[i] = servers[i].IP
}
return ips
}
func targetIPInIps(targetIP net.IP, ips []net.IP) error {
for i := range ips {
if targetIP.Equal(ips[i]) {
return nil
}
}
ipsString := make([]string, len(ips))
for i := range ips {
ipsString[i] = ips[i].String()
}
return fmt.Errorf("target IP address %s not found in IP addresses %s", targetIP, strings.Join(ipsString, ", "))
}
func (n *nordvpn) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) { //nolint:dupl func (n *nordvpn) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) { //nolint:dupl
servers := findServers(selection) servers := n.filterServers(selection.Region, selection.Protocol, selection.Number)
ips := extractIPsFromServers(servers) if len(servers) == 0 {
if len(ips) == 0 { return nil, fmt.Errorf("no server found for region %q, protocol %s and number %d", selection.Region, selection.Protocol, selection.Number)
if selection.Number > 0 {
return nil, fmt.Errorf("no IP found for region %q, protocol %s and number %d", selection.Region, selection.Protocol, selection.Number)
}
return nil, fmt.Errorf("no IP found for region %q, protocol %s", selection.Region, selection.Protocol)
}
var IP net.IP
if selection.TargetIP != nil {
if err := targetIPInIps(selection.TargetIP, ips); err != nil {
return nil, err
}
IP = selection.TargetIP
} else {
IP = ips[0]
} }
var port uint16 var port uint16
switch { switch {
case selection.Protocol == constants.UDP: case selection.Protocol == constants.UDP:
@@ -79,7 +52,26 @@ func (n *nordvpn) GetOpenVPNConnections(selection models.ServerSelection) (conne
default: default:
return nil, fmt.Errorf("protocol %q is unknown", selection.Protocol) return nil, fmt.Errorf("protocol %q is unknown", selection.Protocol)
} }
return []models.OpenVPNConnection{{IP: IP, Port: port, Protocol: selection.Protocol}}, nil
for _, server := range servers {
if selection.TargetIP != nil {
if selection.TargetIP.Equal(server.IP) {
return []models.OpenVPNConnection{{IP: server.IP, Port: port, Protocol: selection.Protocol}}, nil
}
} else {
connections = append(connections, models.OpenVPNConnection{IP: server.IP, Port: port, Protocol: selection.Protocol})
}
}
if selection.TargetIP != nil {
return nil, fmt.Errorf("target IP %s not found in IP addresses", selection.TargetIP)
}
if len(connections) > 64 {
connections = connections[:64]
}
return connections, nil
} }
func (n *nordvpn) BuildConf(connections []models.OpenVPNConnection, verbosity, uid, gid int, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { //nolint:dupl func (n *nordvpn) BuildConf(connections []models.OpenVPNConnection, verbosity, uid, gid int, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { //nolint:dupl
@@ -97,7 +89,6 @@ func (n *nordvpn) BuildConf(connections []models.OpenVPNConnection, verbosity, u
"remote-cert-tls server", "remote-cert-tls server",
// Nordvpn specific // Nordvpn specific
"resolv-retry infinite",
"tun-mtu 1500", "tun-mtu 1500",
"tun-mtu-extra 32", "tun-mtu-extra 32",
"mssfix 1450", "mssfix 1450",

View File

@@ -4,7 +4,6 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net"
"net/http" "net/http"
"strings" "strings"
@@ -24,29 +23,24 @@ func newPrivateInternetAccess() *pia {
} }
} }
func (p *pia) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) { func (p *pia) filterServers(region string) (servers []models.PIAServer) {
var IPs []net.IP if len(region) == 0 {
return constants.PIAServers()
}
for _, server := range constants.PIAServers() { for _, server := range constants.PIAServers() {
if strings.EqualFold(server.Region, selection.Region) { if strings.EqualFold(server.Region, region) {
IPs = server.IPs return []models.PIAServer{server}
} }
} }
if len(IPs) == 0 { return nil
return nil, fmt.Errorf("no IP found for region %q", selection.Region) }
}
if selection.TargetIP != nil { func (p *pia) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) {
found := false servers := p.filterServers(selection.Region)
for i := range IPs { if len(servers) == 0 {
if IPs[i].Equal(selection.TargetIP) { return nil, fmt.Errorf("no server found for region %q", selection.Region)
found = true
break
}
}
if !found {
return nil, fmt.Errorf("target IP address %q not found in IP addresses", selection.TargetIP)
}
IPs = []net.IP{selection.TargetIP}
} }
var port uint16 var port uint16
switch selection.Protocol { switch selection.Protocol {
case constants.TCP: case constants.TCP:
@@ -67,9 +61,27 @@ func (p *pia) GetOpenVPNConnections(selection models.ServerSelection) (connectio
if port == 0 { if port == 0 {
return nil, fmt.Errorf("combination of protocol %q and encryption %q does not yield any port number", selection.Protocol, selection.EncryptionPreset) return nil, fmt.Errorf("combination of protocol %q and encryption %q does not yield any port number", selection.Protocol, selection.EncryptionPreset)
} }
for _, IP := range IPs {
for _, server := range servers {
for _, IP := range server.IPs {
if selection.TargetIP != nil {
if selection.TargetIP.Equal(IP) {
return []models.OpenVPNConnection{{IP: IP, Port: port, Protocol: selection.Protocol}}, nil
}
} else {
connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: selection.Protocol}) connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: selection.Protocol})
} }
}
}
if selection.TargetIP != nil {
return nil, fmt.Errorf("target IP %s not found in IP addresses", selection.TargetIP)
}
if len(connections) > 64 {
connections = connections[:64]
}
return connections, nil return connections, nil
} }

View File

@@ -29,6 +29,8 @@ func New(provider models.VPNProvider) Provider {
return newVyprvpn() return newVyprvpn()
case constants.Nordvpn: case constants.Nordvpn:
return newNordvpn() return newNordvpn()
case constants.Purevpn:
return newPurevpn()
default: default:
return nil // should never occur return nil // should never occur
} }

View File

@@ -0,0 +1,159 @@
package provider
import (
"fmt"
"strings"
"github.com/qdm12/golibs/network"
"github.com/qdm12/private-internet-access-docker/internal/constants"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
type purevpn struct{}
func newPurevpn() *purevpn {
return &purevpn{}
}
func (p *purevpn) filterServers(region, country, city string) (servers []models.PurevpnServer) {
allServers := constants.PurevpnServers()
for i, server := range allServers {
if len(region) == 0 {
server.Region = ""
}
if len(country) == 0 {
server.Country = ""
}
if len(city) == 0 {
server.City = ""
}
if strings.EqualFold(server.Region, region) &&
strings.EqualFold(server.Country, country) &&
strings.EqualFold(server.City, city) {
servers = append(servers, allServers[i])
}
}
return servers
}
func (p *purevpn) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) { //nolint:dupl
servers := p.filterServers(selection.Region, selection.Country, selection.City)
if len(servers) == 0 {
return nil, fmt.Errorf("no server found for region %q, country %q and city %q", selection.Region, selection.Country, selection.City)
}
var port uint16
switch {
case selection.Protocol == constants.UDP:
port = 53
case selection.Protocol == constants.TCP:
port = 80
default:
return nil, fmt.Errorf("protocol %q is unknown", selection.Protocol)
}
for _, server := range servers {
for _, IP := range server.IPs {
if selection.TargetIP != nil {
if IP.Equal(selection.TargetIP) {
return []models.OpenVPNConnection{{IP: IP, Port: port, Protocol: selection.Protocol}}, nil
}
} else {
connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: selection.Protocol})
}
}
}
if selection.TargetIP != nil {
return nil, fmt.Errorf("target IP address %q not found in IP addresses", selection.TargetIP)
}
if len(connections) > 64 {
connections = connections[:64]
}
return connections, nil
}
func (p *purevpn) BuildConf(connections []models.OpenVPNConnection, verbosity, uid, gid int, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { //nolint:dupl
if len(cipher) == 0 {
cipher = aes256cbc
}
lines = []string{
"client",
"dev tun",
"nobind",
"persist-key",
"remote-cert-tls server",
// Purevpn specific
"key-direction 1",
"remote-cert-tls server",
"cipher AES-256-CBC",
"route-method exe",
"route-delay 0",
"route 0.0.0.0 0.0.0.0",
"script-security 2",
// Added constant values
"auth-nocache",
"mute-replay-warnings",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"remote-random",
"suppress-timestamps",
// Modified variables
fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", string(connections[0].Protocol)),
fmt.Sprintf("cipher %s", cipher),
}
if !root {
lines = append(lines, "user nonrootuser")
}
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.PurevpnCertificateAuthority,
"-----END CERTIFICATE-----",
"</ca>",
}...)
lines = append(lines, []string{
"<cert>",
"-----BEGIN CERTIFICATE-----",
constants.PurevpnCertificate,
"-----END CERTIFICATE-----",
"</cert>",
}...)
lines = append(lines, []string{
"<key>",
"-----BEGIN PRIVATE KEY-----",
constants.PurevpnKey,
"-----END PRIVATE KEY-----",
"</key>",
"",
}...)
lines = append(lines, []string{
"<tls-auth>",
"-----BEGIN OpenVPN Static key V1-----",
constants.PurevpnOpenvpnStaticKeyV1,
"-----END OpenVPN Static key V1-----",
"</tls-auth>",
"",
}...)
if len(auth) > 0 {
lines = append(lines, "auth "+auth)
}
if connections[0].Protocol == constants.UDP {
lines = append(lines, "explicit-exit-notify")
}
return lines
}
func (p *purevpn) GetPortForward(client network.Client) (port uint16, err error) {
panic("port forwarding is not supported for purevpn")
}

View File

@@ -2,7 +2,6 @@ package provider
import ( import (
"fmt" "fmt"
"net"
"strings" "strings"
"github.com/qdm12/golibs/network" "github.com/qdm12/golibs/network"
@@ -16,29 +15,24 @@ func newSurfshark() *surfshark {
return &surfshark{} return &surfshark{}
} }
func (s *surfshark) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) { //nolint:dupl func (s *surfshark) filterServers(region string) (servers []models.SurfsharkServer) {
var IPs []net.IP if len(region) == 0 {
return constants.SurfsharkServers()
}
for _, server := range constants.SurfsharkServers() { for _, server := range constants.SurfsharkServers() {
if strings.EqualFold(server.Region, selection.Region) { if strings.EqualFold(server.Region, region) {
IPs = server.IPs return []models.SurfsharkServer{server}
} }
} }
if len(IPs) == 0 { return nil
return nil, fmt.Errorf("no IP found for region %q", selection.Region) }
}
if selection.TargetIP != nil { func (s *surfshark) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) { //nolint:dupl
found := false servers := s.filterServers(selection.Region)
for i := range IPs { if len(servers) == 0 {
if IPs[i].Equal(selection.TargetIP) { return nil, fmt.Errorf("no server found for region %q", selection.Region)
found = true
break
}
}
if !found {
return nil, fmt.Errorf("target IP address %q not found in IP addresses", selection.TargetIP)
}
IPs = []net.IP{selection.TargetIP}
} }
var port uint16 var port uint16
switch { switch {
case selection.Protocol == constants.TCP: case selection.Protocol == constants.TCP:
@@ -48,9 +42,27 @@ func (s *surfshark) GetOpenVPNConnections(selection models.ServerSelection) (con
default: default:
return nil, fmt.Errorf("protocol %q is unknown", selection.Protocol) return nil, fmt.Errorf("protocol %q is unknown", selection.Protocol)
} }
for _, IP := range IPs {
for _, server := range servers {
for _, IP := range server.IPs {
if selection.TargetIP != nil {
if selection.TargetIP.Equal(IP) {
return []models.OpenVPNConnection{{IP: IP, Port: port, Protocol: selection.Protocol}}, nil
}
} else {
connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: selection.Protocol}) connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: selection.Protocol})
} }
}
}
if selection.TargetIP != nil {
return nil, fmt.Errorf("target IP %s not found in IP addresses", selection.TargetIP)
}
if len(connections) > 64 {
connections = connections[:64]
}
return connections, nil return connections, nil
} }

View File

@@ -2,7 +2,6 @@ package provider
import ( import (
"fmt" "fmt"
"net"
"strings" "strings"
"github.com/qdm12/golibs/network" "github.com/qdm12/golibs/network"
@@ -16,29 +15,24 @@ func newVyprvpn() *vyprvpn {
return &vyprvpn{} return &vyprvpn{}
} }
func (s *vyprvpn) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) { func (v *vyprvpn) filterServers(region string) (servers []models.VyprvpnServer) {
var IPs []net.IP if len(region) == 0 {
return constants.VyprvpnServers()
}
for _, server := range constants.VyprvpnServers() { for _, server := range constants.VyprvpnServers() {
if strings.EqualFold(server.Region, selection.Region) { if strings.EqualFold(server.Region, region) {
IPs = server.IPs return []models.VyprvpnServer{server}
} }
} }
if len(IPs) == 0 { return nil
return nil, fmt.Errorf("no IP found for region %q", selection.Region) }
}
if selection.TargetIP != nil { func (v *vyprvpn) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) {
found := false servers := v.filterServers(selection.Region)
for i := range IPs { if len(servers) == 0 {
if IPs[i].Equal(selection.TargetIP) { return nil, fmt.Errorf("no server found for region %q", selection.Region)
found = true
break
}
}
if !found {
return nil, fmt.Errorf("target IP address %q not found in IP addresses", selection.TargetIP)
}
IPs = []net.IP{selection.TargetIP}
} }
var port uint16 var port uint16
switch { switch {
case selection.Protocol == constants.TCP: case selection.Protocol == constants.TCP:
@@ -48,13 +42,31 @@ func (s *vyprvpn) GetOpenVPNConnections(selection models.ServerSelection) (conne
default: default:
return nil, fmt.Errorf("protocol %q is unknown", selection.Protocol) return nil, fmt.Errorf("protocol %q is unknown", selection.Protocol)
} }
for _, IP := range IPs {
for _, server := range servers {
for _, IP := range server.IPs {
if selection.TargetIP != nil {
if selection.TargetIP.Equal(IP) {
return []models.OpenVPNConnection{{IP: IP, Port: port, Protocol: selection.Protocol}}, nil
}
} else {
connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: selection.Protocol}) connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: selection.Protocol})
} }
}
}
if selection.TargetIP != nil {
return nil, fmt.Errorf("target IP %s not found in IP addresses", selection.TargetIP)
}
if len(connections) > 64 {
connections = connections[:64]
}
return connections, nil return connections, nil
} }
func (s *vyprvpn) BuildConf(connections []models.OpenVPNConnection, verbosity, uid, gid int, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { func (v *vyprvpn) BuildConf(connections []models.OpenVPNConnection, verbosity, uid, gid int, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(cipher) == 0 { if len(cipher) == 0 {
cipher = aes256cbc cipher = aes256cbc
} }
@@ -105,6 +117,6 @@ func (s *vyprvpn) BuildConf(connections []models.OpenVPNConnection, verbosity, u
return lines return lines
} }
func (s *vyprvpn) GetPortForward(client network.Client) (port uint16, err error) { func (v *vyprvpn) GetPortForward(client network.Client) (port uint16, err error) {
panic("port forwarding is not supported for vyprvpn") panic("port forwarding is not supported for vyprvpn")
} }

View File

@@ -2,7 +2,6 @@ package provider
import ( import (
"fmt" "fmt"
"net"
"strings" "strings"
"github.com/qdm12/golibs/network" "github.com/qdm12/golibs/network"
@@ -16,29 +15,24 @@ func newWindscribe() *windscribe {
return &windscribe{} return &windscribe{}
} }
func (w *windscribe) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) { func (w *windscribe) filterServers(region string) (servers []models.WindscribeServer) {
var IPs []net.IP if len(region) == 0 {
return constants.WindscribeServers()
}
for _, server := range constants.WindscribeServers() { for _, server := range constants.WindscribeServers() {
if strings.EqualFold(server.Region, selection.Region) { if strings.EqualFold(server.Region, region) {
IPs = server.IPs return []models.WindscribeServer{server}
} }
} }
if len(IPs) == 0 { return nil
return nil, fmt.Errorf("no IP found for region %q", selection.Region) }
}
if selection.TargetIP != nil { func (w *windscribe) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) {
found := false servers := w.filterServers(selection.Region)
for i := range IPs { if len(servers) == 0 {
if IPs[i].Equal(selection.TargetIP) { return nil, fmt.Errorf("no server found for region %q", selection.Region)
found = true
break
}
}
if !found {
return nil, fmt.Errorf("target IP address %q not found in IP addresses", selection.TargetIP)
}
IPs = []net.IP{selection.TargetIP}
} }
var port uint16 var port uint16
switch { switch {
case selection.CustomPort > 0: case selection.CustomPort > 0:
@@ -50,9 +44,27 @@ func (w *windscribe) GetOpenVPNConnections(selection models.ServerSelection) (co
default: default:
return nil, fmt.Errorf("protocol %q is unknown", selection.Protocol) return nil, fmt.Errorf("protocol %q is unknown", selection.Protocol)
} }
for _, IP := range IPs {
for _, server := range servers {
for _, IP := range server.IPs {
if selection.TargetIP != nil {
if selection.TargetIP.Equal(IP) {
return []models.OpenVPNConnection{{IP: IP, Port: port, Protocol: selection.Protocol}}, nil
}
} else {
connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: selection.Protocol}) connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: selection.Protocol})
} }
}
}
if selection.TargetIP != nil {
return nil, fmt.Errorf("target IP %s not found in IP addresses", selection.TargetIP)
}
if len(connections) > 64 {
connections = connections[:64]
}
return connections, nil return connections, nil
} }

View File

@@ -0,0 +1,36 @@
package server
import (
"encoding/json"
"net/http"
)
func (s *server) handleGetPortForwarded(w http.ResponseWriter) {
port := s.getPortForwarded()
data, err := json.Marshal(struct {
Port uint16 `json:"port"`
}{port})
if err != nil {
s.logger.Warn(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
if _, err := w.Write(data); err != nil {
s.logger.Warn(err)
w.WriteHeader(http.StatusInternalServerError)
}
}
func (s *server) handleGetOpenvpnSettings(w http.ResponseWriter) {
settings := s.getOpenvpnSettings()
data, err := json.Marshal(settings)
if err != nil {
s.logger.Warn(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
if _, err := w.Write(data); err != nil {
s.logger.Warn(err)
w.WriteHeader(http.StatusInternalServerError)
}
}

View File

@@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/private-internet-access-docker/internal/settings"
) )
type Server interface { type Server interface {
@@ -19,14 +20,19 @@ type server struct {
logger logging.Logger logger logging.Logger
restartOpenvpn func() restartOpenvpn func()
restartUnbound func() restartUnbound func()
getOpenvpnSettings func() settings.OpenVPN
getPortForwarded func() uint16
} }
func New(address string, logger logging.Logger, restartOpenvpn, restartUnbound func()) Server { func New(address string, logger logging.Logger, restartOpenvpn, restartUnbound func(),
getOpenvpnSettings func() settings.OpenVPN, getPortForwarded func() uint16) Server {
return &server{ return &server{
address: address, address: address,
logger: logger.WithPrefix("http server: "), logger: logger.WithPrefix("http server: "),
restartOpenvpn: restartOpenvpn, restartOpenvpn: restartOpenvpn,
restartUnbound: restartUnbound, restartUnbound: restartUnbound,
getOpenvpnSettings: getOpenvpnSettings,
getPortForwarded: getPortForwarded,
} }
} }
@@ -61,6 +67,10 @@ func (s *server) makeHandler() http.HandlerFunc {
s.restartOpenvpn() s.restartOpenvpn()
case "/unbound/actions/restart": case "/unbound/actions/restart":
s.restartUnbound() s.restartUnbound()
case "/openvpn/portforwarded":
s.handleGetPortForwarded(w)
case "/openvpn/settings":
s.handleGetOpenvpnSettings(w)
default: default:
routeDoesNotExist(s.logger, w, r) routeDoesNotExist(s.logger, w, r)
} }

View File

@@ -1,6 +1,7 @@
package settings package settings
import ( import (
"fmt"
"net" "net"
"strings" "strings"
@@ -10,6 +11,7 @@ import (
// Firewall contains settings to customize the firewall operation // Firewall contains settings to customize the firewall operation
type Firewall struct { type Firewall struct {
AllowedSubnets []net.IPNet AllowedSubnets []net.IPNet
VPNInputPorts []uint16
Enabled bool Enabled bool
Debug bool Debug bool
} }
@@ -22,9 +24,15 @@ func (f *Firewall) String() string {
if !f.Enabled { if !f.Enabled {
return "Firewall settings: disabled" return "Firewall settings: disabled"
} }
vpnInputPorts := make([]string, len(f.VPNInputPorts))
for i, port := range f.VPNInputPorts {
vpnInputPorts[i] = fmt.Sprintf("%d", port)
}
settingsList := []string{ settingsList := []string{
"Firewall settings:", "Firewall settings:",
"Allowed subnets: " + strings.Join(allowedSubnets, ", "), "Allowed subnets: " + strings.Join(allowedSubnets, ", "),
"VPN input ports: " + strings.Join(vpnInputPorts, ", "),
} }
if f.Debug { if f.Debug {
settingsList = append(settingsList, "Debug: on") settingsList = append(settingsList, "Debug: on")
@@ -38,6 +46,10 @@ func GetFirewallSettings(paramsReader params.Reader) (settings Firewall, err err
if err != nil { if err != nil {
return settings, err return settings, err
} }
settings.VPNInputPorts, err = paramsReader.GetVPNInputPorts()
if err != nil {
return settings, err
}
settings.Enabled, err = paramsReader.GetFirewall() settings.Enabled, err = paramsReader.GetFirewall()
if err != nil { if err != nil {
return settings, err return settings, err

View File

@@ -11,13 +11,13 @@ import (
// OpenVPN contains settings to configure the OpenVPN client // OpenVPN contains settings to configure the OpenVPN client
type OpenVPN struct { type OpenVPN struct {
User string User string `json:"user"`
Password string Password string `json:"-"`
Verbosity int Verbosity int `json:"verbosity"`
Root bool Root bool `json:"runAsRoot"`
Cipher string Cipher string `json:"cipher"`
Auth string Auth string `json:"auth"`
Provider models.ProviderSettings Provider models.ProviderSettings `json:"provider"`
} }
// GetOpenVPNSettings obtains the OpenVPN settings using the params functions // GetOpenVPNSettings obtains the OpenVPN settings using the params functions
@@ -66,6 +66,8 @@ func GetOpenVPNSettings(paramsReader params.Reader, vpnProvider models.VPNProvid
settings.Provider, err = GetVyprvpnSettings(paramsReader) settings.Provider, err = GetVyprvpnSettings(paramsReader)
case constants.Nordvpn: case constants.Nordvpn:
settings.Provider, err = GetNordvpnSettings(paramsReader) settings.Provider, err = GetNordvpnSettings(paramsReader)
case constants.Purevpn:
settings.Provider, err = GetPurevpnSettings(paramsReader)
default: default:
err = fmt.Errorf("VPN service provider %q is not valid", vpnProvider) err = fmt.Errorf("VPN service provider %q is not valid", vpnProvider)
} }

View File

@@ -0,0 +1,27 @@
package settings
import (
"encoding/json"
"testing"
"github.com/qdm12/private-internet-access-docker/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_OpenVPN_JSON(t *testing.T) {
t.Parallel()
in := OpenVPN{
Root: true,
Provider: models.ProviderSettings{
Name: "name",
},
}
data, err := json.Marshal(in)
require.NoError(t, err)
assert.Equal(t, `{"user":"","verbosity":0,"runAsRoot":true,"cipher":"","auth":"","provider":{"name":"name","serverSelection":{"networkProtocol":"","region":"","group":"","country":"","city":"","isp":"","owned":false,"customPort":0,"number":0,"encryptionPreset":""},"extraConfig":{"encryptionPreset":""},"portForwarding":{"enabled":false,"filepath":""}}}`, string(data))
var out OpenVPN
err = json.Unmarshal(data, &out)
require.NoError(t, err)
assert.Equal(t, in, out)
}

View File

@@ -175,3 +175,29 @@ func GetNordvpnSettings(paramsReader params.Reader) (settings models.ProviderSet
} }
return settings, nil return settings, nil
} }
// GetPurevpnSettings obtains Purevpn settings from environment variables using the params package.
func GetPurevpnSettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
settings.Name = constants.Mullvad
settings.ServerSelection.Protocol, err = paramsReader.GetNetworkProtocol()
if err != nil {
return settings, err
}
settings.ServerSelection.TargetIP, err = paramsReader.GetTargetIP()
if err != nil {
return settings, err
}
settings.ServerSelection.Region, err = paramsReader.GetPurevpnRegion()
if err != nil {
return settings, err
}
settings.ServerSelection.Country, err = paramsReader.GetPurevpnCountry()
if err != nil {
return settings, err
}
settings.ServerSelection.City, err = paramsReader.GetPurevpnCity()
if err != nil {
return settings, err
}
return settings, nil
}

View File

@@ -30,6 +30,7 @@ type looper struct {
streamMerger command.StreamMerger streamMerger command.StreamMerger
uid int uid int
gid int gid int
defaultInterface string
restart chan struct{} restart chan struct{}
start chan struct{} start chan struct{}
stop chan struct{} stop chan struct{}
@@ -44,7 +45,7 @@ func (l *looper) logAndWait(ctx context.Context, err error) {
} }
func NewLooper(conf Configurator, firewallConf firewall.Configurator, settings settings.ShadowSocks, dnsSettings settings.DNS, func NewLooper(conf Configurator, firewallConf firewall.Configurator, settings settings.ShadowSocks, dnsSettings settings.DNS,
logger logging.Logger, streamMerger command.StreamMerger, uid, gid int) Looper { logger logging.Logger, streamMerger command.StreamMerger, uid, gid int, defaultInterface string) Looper {
return &looper{ return &looper{
conf: conf, conf: conf,
firewallConf: firewallConf, firewallConf: firewallConf,
@@ -54,6 +55,7 @@ func NewLooper(conf Configurator, firewallConf firewall.Configurator, settings s
streamMerger: streamMerger, streamMerger: streamMerger,
uid: uid, uid: uid,
gid: gid, gid: gid,
defaultInterface: defaultInterface,
restart: make(chan struct{}), restart: make(chan struct{}),
start: make(chan struct{}), start: make(chan struct{}),
stop: make(chan struct{}), stop: make(chan struct{}),
@@ -141,7 +143,7 @@ func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
continue continue
} }
} }
if err := l.firewallConf.SetAllowedPort(ctx, settings.Port); err != nil { if err := l.firewallConf.SetAllowedPort(ctx, settings.Port, l.defaultInterface); err != nil {
l.logger.Error(err) l.logger.Error(err)
continue continue
} }

View File

@@ -29,6 +29,7 @@ type looper struct {
streamMerger command.StreamMerger streamMerger command.StreamMerger
uid int uid int
gid int gid int
defaultInterface string
restart chan struct{} restart chan struct{}
start chan struct{} start chan struct{}
stop chan struct{} stop chan struct{}
@@ -43,7 +44,7 @@ func (l *looper) logAndWait(ctx context.Context, err error) {
} }
func NewLooper(conf Configurator, firewallConf firewall.Configurator, settings settings.TinyProxy, func NewLooper(conf Configurator, firewallConf firewall.Configurator, settings settings.TinyProxy,
logger logging.Logger, streamMerger command.StreamMerger, uid, gid int) Looper { logger logging.Logger, streamMerger command.StreamMerger, uid, gid int, defaultInterface string) Looper {
return &looper{ return &looper{
conf: conf, conf: conf,
firewallConf: firewallConf, firewallConf: firewallConf,
@@ -52,6 +53,7 @@ func NewLooper(conf Configurator, firewallConf firewall.Configurator, settings s
streamMerger: streamMerger, streamMerger: streamMerger,
uid: uid, uid: uid,
gid: gid, gid: gid,
defaultInterface: defaultInterface,
restart: make(chan struct{}), restart: make(chan struct{}),
start: make(chan struct{}), start: make(chan struct{}),
stop: make(chan struct{}), stop: make(chan struct{}),
@@ -133,7 +135,7 @@ func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
continue continue
} }
} }
if err := l.firewallConf.SetAllowedPort(ctx, settings.Port); err != nil { if err := l.firewallConf.SetAllowedPort(ctx, settings.Port, l.defaultInterface); err != nil {
l.logger.Error(err) l.logger.Error(err)
continue continue
} }