Compare commits

...

307 Commits

Author SHA1 Message Date
Quentin McGaw
fc1d96087e Fix SS TCP relay error handling 2020-08-16 23:25:44 +00:00
Quentin McGaw
cf1919c27f Update list of ciphers for Shadowsocks 2020-08-16 23:09:48 +00:00
Quentin McGaw
02930b6a95 Shadowsocks in Go, refers to #211 2020-08-16 23:07:15 +00:00
Quentin McGaw
c59447c646 Bump versions and binary build changes
- Go version 1.15
- Golangci-lint 1.30
- Trim path of binary built
2020-08-16 23:06:35 +00:00
Quentin McGaw
78323f0a33 Update PIA IP addresses, fixes #215 2020-08-08 16:27:51 +00:00
Quentin McGaw
cd60fe4406 Add PIA US Dallas region, refers to #212 2020-07-28 02:40:38 +00:00
Quentin McGaw
a2a9410053 Fix #212 2020-07-28 00:31:19 +00:00
Quentin McGaw
f95f6201b1 Rename repo to Gluetun, refers to #112 2020-07-26 12:07:06 +00:00
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
Quentin McGaw
b4a4e441c1 Fix #199 when ticker period is 0 2020-07-16 12:00:25 +00:00
Quentin McGaw
e8526141be Fix issues in readme 2020-07-16 01:52:09 +00:00
Quentin McGaw
9abb630692 Get and set settings for DNS 2020-07-16 01:45:05 +00:00
Quentin McGaw
9b92ece5a1 Fix race condition for public ip loop 2020-07-16 01:44:48 +00:00
Quentin McGaw
87a3e54044 Set and get settings for openvpn 2020-07-16 01:26:37 +00:00
Quentin McGaw
76b730e2a6 Improve openvpn logging
- Show Initialization Sequence completed in green
- Show all other openvpn logs in the openvpn color
2020-07-16 01:20:47 +00:00
Quentin McGaw
51af8d1ab0 PUBLICIP_PERIOD environment variable 2020-07-16 01:12:54 +00:00
Quentin McGaw
002ffacd35 Shadowsocks get and set settings 2020-07-16 00:05:00 +00:00
Quentin McGaw
404cee9371 Tinyproxy set and get settings 2020-07-16 00:05:00 +00:00
Quentin McGaw
f89e7aa8dc Update readme list of VPN providers supported 2020-07-16 00:04:58 +00:00
Quentin McGaw
a0312ec916 Shadowsocks and Tinyproxy Start and Stop 2020-07-16 00:04:15 +00:00
Quentin McGaw
83cf59b93e Start and Stop for dns over tls 2020-07-16 00:04:15 +00:00
Quentin McGaw
ad5de13c25 Logging filtering for openvpn process 2020-07-16 00:04:14 +00:00
Quentin McGaw
1281026850 Nordvpn support (#189), fix #178 2020-07-15 18:14:45 -04:00
Quentin McGaw
616ba0c538 Replace explicit channels with functions 2020-07-15 01:34:46 +00:00
Quentin McGaw
8c7c8f7d5a Fix Cyberghost port, fixes #179 and #104 2020-07-14 23:08:57 +00:00
Quentin McGaw
78877483e9 Fix #198 2020-07-14 22:31:15 +00:00
Quentin McGaw
de7f12d958 Gluetun git history video 😉 2020-07-14 00:38:55 +00:00
Quentin McGaw
7e7312459d Cyberghost: read client key as file, see #179 2020-07-14 00:17:31 +00:00
Quentin McGaw
e3a677c22b Add openvpnconfig command, for #179 2020-07-13 23:43:26 +00:00
Quentin McGaw
2f955e0190 Simplified provider object creation 2020-07-13 23:34:03 +00:00
Quentin McGaw
618441b008 Vyprvpn support, fix #181 (#193) 2020-07-13 08:04:35 -04:00
Quentin McGaw
4a7d341c57 Fixing extra subnets firewall rules
- Fix #194
- Fix #190
- Refers to #188
2020-07-13 02:17:49 +00:00
Quentin McGaw
95ad58687d Remove duplicate firewall rule for tun0 2020-07-13 02:15:32 +00:00
Quentin McGaw
0fc69e068e FIREWALL_DEBUG variable, refers to #190, #194 2020-07-13 02:14:56 +00:00
Quentin McGaw
7252ac722c Logging improvements (#195)
- Fix (and test) filtering of lines
- Filter out shadowsocks cannot resolve error
- Change tinyproxy color
- Deduct logging level according to message content
2020-07-12 21:19:44 -04:00
Quentin McGaw
4cd6b33044 Add missing subnets setup, fixes #190
- Also setup subnet routes when firewall is disabled
2020-07-12 21:21:41 +00:00
Quentin McGaw
0731b1cb82 Remove horrible firewall debug 1 printing 2020-07-12 19:22:48 +00:00
Quentin McGaw
07efea612b Minor changes
- Remove unneeded string conversions
- Remove resolve-retry infinite openvpn configuration (unneeded)
- Add cover on single tests and single test files for Vscode
2020-07-12 19:15:05 +00:00
Quentin McGaw
6afa4f69a0 Fix routing reading issues
- Detect VPN gateway properly
- Fix local subnet detection, refers to #188
- Split LocalSubnet from DefaultRoute (2 different routes actually)
2020-07-12 19:05:48 +00:00
Quentin McGaw
2acf627918 Fixes #191 2020-07-12 15:21:32 +00:00
Quentin McGaw
4eb7c4ac36 Support for username and password changes 2020-07-12 14:55:03 +00:00
Quentin McGaw
b4c838e6ab Changes to cyberghost openvpn configuration
- Comment out redirect-gateway def1
- Add explicit-exit-notify 2 to ensure disconnection server side
- Remove ping-exit instructions
- Remove resolv-retry infinite (unneeded)
2020-07-12 14:47:37 +00:00
Quentin McGaw
8b096af04e DNS_KEEP_NAMESERVER variable, refers to #188 2020-07-11 23:51:53 +00:00
Quentin McGaw
78b63174ce Update VPN server IP addresses
- PIA updated
- Surfshark updated
- Windscribe updated (US East and Slovenia removed)
- Capital letters fixed for regions of Windscribe
- Resolver program updated to match latest format for constants
2020-07-11 22:02:25 +00:00
Quentin McGaw
11fca08028 Port forwarded firewall fix 2020-07-11 21:30:26 +00:00
Quentin McGaw
515e72a0ed Update versions in readme 2020-07-11 21:10:41 +00:00
Quentin McGaw
2f9d1f09d3 Minor changes
- Remove https://ip4.ddnss.de/meineip.php
- Logs when falling back to plaintext DNS
2020-07-11 21:04:46 +00:00
Quentin McGaw
b1596bc7e4 Firewall refactoring
- Ability to enable and disable rules in various loops
- Simplified code overall
- Port forwarding moved into openvpn loop
- Route addition and removal improved
2020-07-11 21:03:55 +00:00
Quentin McGaw
ccf11990f1 Fix several async issues
- race conditions between ctx.Done and waitError channel
- Sleep for retry cancels on cancelation of context
- Stops the any loop at the start if the context was canceled
- Mentions when loops exit
- Wait for errors on triggered loop restarts
2020-07-11 20:59:30 +00:00
Quentin McGaw
1ac06ee4a8 Fatal waits for subprocesses to complete 2020-07-09 22:04:04 +00:00
Quentin McGaw
dc1c7eab81 Fix #187 (silly me #2) 2020-07-09 11:56:43 +00:00
Quentin McGaw
5bf471767d Remove unneeded waiter object 2020-07-08 23:43:03 +00:00
Quentin McGaw
3d25db1bed Waits properly for all subprocess to exit 2020-07-08 23:42:54 +00:00
Quentin McGaw
99e386abc8 Using a waitgroup to wait for all programs to exit 2020-07-08 23:36:02 +00:00
Quentin McGaw
8669748289 Shadowsocks loop 2020-07-08 23:29:40 +00:00
Quentin McGaw
a39d885e34 Minor fixes
- Not logging program name twice for wait errors
- Wait for tinyproxy to exit
2020-07-08 23:29:22 +00:00
Quentin McGaw
7d36993450 Tinyproxy run loop 2020-07-08 23:20:33 +00:00
Quentin McGaw
0d53461706 Quick fix: uid and gid for openvpn 2020-07-08 22:51:27 +00:00
Quentin McGaw
758f316816 Small constants changes
- ifconfig.me/ip added, see #186
- Remove trailing space for a Surfshark region
2020-07-08 22:47:48 +00:00
Quentin McGaw
ad73a027f3 Gets public IP every hour 2020-07-08 22:47:12 +00:00
Quentin McGaw
2c96f91043 Merge branch 'master' of github.com:qdm12/private-internet-access-docker 2020-07-08 22:33:40 +00:00
Quentin McGaw
53b7fafc49 Public IP loop, fixes #186
- Uses common http client with 15 seconds timeout
- Repeats on fail after 5 seconds sleep time
2020-07-08 22:33:28 +00:00
Quentin McGaw
7450ffce2b uid and gid variables (no change at runtime) 2020-07-08 22:13:59 +00:00
Quentin McGaw
765f06e5a8 Write auth file in openvpn loop 2020-07-08 22:11:23 +00:00
Rick van Hattem
e304b4a829 Updated readme to match the Dockerfile (#185) 2020-07-08 09:19:56 -04:00
Quentin McGaw
3ae4523280 Merge branch 'master' of github.com:qdm12/private-internet-access-docker 2020-07-08 13:14:50 +00:00
Quentin McGaw
7a136db085 Simplified loop mechanism for openvpn and dns
- Refers to #91
- http control server starts without waiting for unbound and/or openvpn
- Trying to get rid of waiter and use channels directly
- Simpler main.go
- More robust logic overall
2020-07-08 13:14:39 +00:00
Rick van Hattem
e809e178b9 Added Surfshark documentation for authentication (#182) 2020-07-07 21:14:05 -04:00
Rick van Hattem
dd529a48fa Stripped extraneous whitespace (#180) 2020-07-07 17:33:35 -04:00
Rick van Hattem
2c6eae4e90 Set Surfshark ping-restart to valid value (#183)
No idea why this was here, but thanks for fixing it 🥇
2020-07-07 17:30:06 -04:00
Quentin McGaw
18e99d07d0 Move provider settings in openvpn settings 2020-07-05 20:05:38 +00:00
Quentin McGaw
a4b0e0ff86 Fix provider settings logging 2020-07-05 20:05:15 +00:00
Quentin McGaw
7e36fbbd00 Refers to #179 (Cyberghost)
- Fix <crt> -> <cert>
- Fix certificates (ca.crt and client.crt)
2020-07-05 16:20:40 +00:00
Quentin McGaw
d228216d1c Fix #179 2020-07-04 17:48:22 +00:00
Quentin McGaw
c9368e352c DNS_PLAINTEXT_ADDRESS, fixes #176 2020-06-26 14:40:46 +00:00
Quentin McGaw
d947d9fe30 Rename dev extension ms-vscode.go to golang.go 2020-06-26 14:37:11 +00:00
Quentin McGaw
613ded51ab Upgrade to Alpine 3.12 2020-06-26 14:36:31 +00:00
Quentin McGaw
3b43b7c2f6 Add script security 2 to run vpn provider scripts
Fix Bug: ... #176
2020-06-21 20:21:13 -04:00
Quentin McGaw
cdbb7bf771 Fix #175 2020-06-18 18:05:51 +00:00
Quentin McGaw
5a6cf0fe3a Remove firewall nat chain clearing, refers to #171 2020-06-16 12:20:33 +00:00
Quentin McGaw
082a5bdf51 Fix http control server not working when DOT=off 2020-06-16 00:11:22 +00:00
Quentin McGaw
7369808b84 Refactor (#174)
- Goal was to simplify main.go complexity
- Use common structures and interfaces for all vpn providers
- Moved files around
- Removed some alias models
2020-06-13 14:08:29 -04:00
Quentin McGaw
4f502abcf8 Cyberghost support (#168)
* Host finder CLI for cyberghost
* Resolver program updated with Cyberghost data
* Gluetun cli clientkey subcommand
2020-06-13 10:43:47 -04:00
Quentin McGaw
bdcadf09ec Fix: Shadowsocks nameserver when DOT=off 2020-06-13 13:57:26 +00:00
Quentin McGaw
8cae369186 Add FIREWALL variable, refers to #171 2020-06-12 17:11:21 +00:00
Quentin McGaw
a3d75f3d8b Replace p with r for params reader (lint issue) 2020-06-12 17:07:32 +00:00
Quentin McGaw
1a06d01ae2 Fix #172 kernel module for Synology 2020-06-10 01:34:20 +00:00
Quentin McGaw
634cef2bb2 Only resolve github.com for healthcheck, fix #170 2020-06-10 01:25:10 +00:00
Quentin McGaw
6107f5c4ab Fix #173 2020-06-10 01:16:58 +00:00
Quentin McGaw
6ae9dc5c2c Update list of donators in readme 👍 2020-06-08 08:31:18 -04:00
Quentin McGaw
ea3a173e3b Remove persist-tun, fix #171
- Now openvpn tries other vpn ip addresses available on restart
- In case of  a bad ip address, it will try other random ones
2020-06-08 11:08:07 +00:00
Quentin McGaw
69217f61a1 Update PIA servers IP addresses, refers to #171 2020-06-08 11:06:11 +00:00
Quentin McGaw
e33a6a8503 Fix #170 2020-06-05 19:32:12 -04:00
Quentin McGaw
0fb065eb61 Fix bad comparison in healthcheck 2020-06-05 12:46:44 +00:00
Quentin McGaw
f6a2aac475 Fix #170 2020-06-04 22:13:49 +00:00
Quentin McGaw
900fa261d8 Better healthcheck (#169), fixes #133
* Changed healthcheck to get and compare IP address
* Change default healthcheck frequency and retries
2020-06-03 21:52:44 -04:00
Quentin McGaw
cfb4dd84bc Replace pia with gluetun wherever possible
- in Readme documentation
- Changed splash title string
- Changed Dockerfile labels
- Changed commands and docker-compose service & container name
2020-06-03 02:11:35 +00:00
Quentin McGaw
4f72f60a3e .dockerignore updated 2020-06-02 23:12:04 +00:00
Quentin McGaw
f262ee6454 Resolver cli changes
- Max of 10 simultaneous goroutines to avoid being throttled by DNS
- All template formatting moved to formatLine function
- resolveRepeat is synchronous to avoid being throttled by DNS
2020-06-02 23:10:04 +00:00
Quentin McGaw
20a3327815 Minor changes
- PORT is for Mullvad but also Windscribe (docker-compose, README.md)
- Windscribe configurator does not need lookupIP anymore
2020-06-02 23:07:29 +00:00
Quentin McGaw
3ab1298b1f Main function improved
- More explicit cli operation
- Using ctx and os.Args injected for eventual later testing
- Returning exit code
- Cli code moved to cli package
2020-06-02 23:03:18 +00:00
Quentin McGaw
a7739b6f5d Add multi hop regions and ips for Surfshark 2020-05-31 19:50:28 +00:00
Quentin McGaw
263368af89 Remove cipher and auth restrictive checks 2020-05-29 12:01:45 +00:00
Quentin McGaw
96e57d2c32 Surfshark (#167) 2020-05-29 07:24:27 -04:00
Quentin McGaw
85a93bdd34 Remove timestamps from program logs
- Using suppress-timestamps for openvpn
- Using custom regexp for tinyproxy, shadowsocks and unbound
- Time is now only shown once per line
- Log level of subprogram is preserverd
2020-05-29 11:17:14 +00:00
Quentin McGaw
cc80d224c2 Review current openvpn configuration
- tls-client unneeded if client is specified
- Moved settings around in config file
2020-05-29 10:29:07 +00:00
Quentin McGaw
c85cca7fdc Workflow adjustments:
- Paths ignore for all docker build
- Remove security workflow (CVEs should be fixed by Alpine)
2020-05-29 10:20:45 +00:00
Quentin McGaw
3f6d3d7c2a Openvpn files parser tool binary 2020-05-29 10:13:42 +00:00
Quentin McGaw
09a0ba1228 DNS Resolver tool binary 2020-05-29 10:13:29 +00:00
Quentin McGaw
6b81ed6bde Unneeded Docker init, Go entrypoint does it 2020-05-29 10:12:38 +00:00
Quentin McGaw
64e447b262 Minor code and comments changes 2020-05-29 00:09:49 +00:00
Quentin McGaw
d0926111e0 Env variables cleanup in Docker config files 2020-05-29 00:03:10 +00:00
Quentin McGaw
aac4298f69 Moved main.go to cmd/gluetun/main.go 2020-05-28 23:59:35 +00:00
Quentin McGaw
f4018d3411 Fix PIA region case sensitivity, refers to #163 2020-05-28 01:07:32 +00:00
Quentin McGaw
0710199409 Updated IP addresses for Windscribe and PIA
- Updated test functions to resolve subdomains
- IP addresses are in increasing order
- One line per region/subdomain
2020-05-27 00:52:52 +00:00
Quentin McGaw
43c15b3e68 Removed double message in port forwarding logging 2020-05-18 13:39:01 +00:00
Quentin McGaw
ab223a5e06 User specified iptables rules (#161) 2020-05-18 09:37:34 -04:00
Quentin McGaw
fd5e7af3ff Improve environment variables table 2020-05-17 18:34:57 -04:00
Quentin McGaw
886d4ad1a9 Update readme for new wiki 2020-05-17 17:54:37 -04:00
Quentin McGaw
40a72b6189 Update golangci-lint to 1.27.0 2020-05-17 17:49:40 -04:00
Quentin McGaw
5eb1859f41 Fix #156 falls back to dns on ipv4 only 2020-05-09 00:54:49 +00:00
Quentin McGaw
b45fa026dd Improved openvpn run loop 2020-05-09 00:43:09 +00:00
Quentin McGaw
da739a0c3d Fix waitForAll context for graceful exits 2020-05-07 12:58:37 +00:00
Quentin McGaw
0dc400b540 Fix Unbound run loop logic
- Plain DNS is used only for the first resolving of github.com to obtain block lists and crypto files required by Unbound
- DNS over TLS is used at all time by the system and the Go program thereafter, even between periodic restarts
- Downtime during a periodic update is < 1 second
- On an Unbound start or unexpected exit error, the container falls back on the unencrypted version of the DNS in order to try restarting Unbound
2020-05-07 12:56:49 +00:00
Quentin McGaw
d12668d57f Fix logic to disable DNS periodic update 2020-05-05 22:02:23 +00:00
Quentin McGaw
c39affeb12 GolangCi-lint 1.
26.0
2020-05-05 18:01:12 +00:00
Quentin McGaw
d73765a5f5 DNS_UPDATE_PERIOD environment variable 2020-05-05 18:00:56 +00:00
Quentin McGaw
37282c014b Removed greetings workflow (not working on forks) 2020-05-04 23:05:03 +00:00
Quentin McGaw
adeccf8548 ip6tables package, refers to #153 2020-05-04 12:27:15 +00:00
Quentin McGaw
a97cbcc4e4 Refers to #153 2020-05-04 12:24:34 +00:00
Quentin McGaw
89187b6b86 Fix #142 2020-05-03 16:28:59 +00:00
Quentin McGaw
754bab9763 Unbound restart logic
- Update files and restart unbound every 24hours
- HTTP route to force update & restart of Unbound
- Fix #151
2020-05-02 17:08:15 +00:00
Quentin McGaw
0d7f6dab1a Remove unneeded functionNotSet in server 2020-05-02 17:04:01 +00:00
Quentin McGaw
507374ca4e Improve openvpn run loop logs 2020-05-02 17:03:11 +00:00
Quentin McGaw
318c3c9032 Control server announcement 2020-05-02 17:02:39 +00:00
Quentin McGaw
c068484fa0 Initial DNS IP is ipv4/6 depending on DOT_IPV6 2020-05-02 15:41:28 +00:00
Quentin McGaw
7cd35737ba Defaults DOT_IPV6 to off 2020-05-02 15:40:40 +00:00
Quentin McGaw
0247a1ff01 Refers to #142 2020-05-02 14:59:22 +00:00
Quentin McGaw
363fabc810 Reduced main.go code complexity 2020-05-02 14:48:18 +00:00
Quentin McGaw
6049b10209 Update firewall documentation 2020-05-02 13:13:19 +00:00
Quentin McGaw
bc05ff34fd Launch DNS over TLS after tunneling
- No data is downloaded before tunneling
- Fixes #127
2020-05-02 13:11:41 +00:00
Quentin McGaw
8e77842f1e FatalOnError cancels parent context 2020-05-02 13:05:09 +00:00
Quentin McGaw
41168f88cd Improved connected signaling mechanism 2020-05-01 03:15:49 +00:00
Quentin McGaw
88ad10d429 PIA and Windscribe hardcoded IP addresses
- Allows to not need to resolve subdomains at start before tunneling
- Allows for Unbound to be configured and started after tunneling
- Refers to #127
2020-05-01 03:14:16 +00:00
Quentin McGaw
f4cd1896c9 Go HTTP control server with restart openvpn route
- Fix #147
- Dockerfile updated
- Documentation updated
- Using contexts to restart openvpn
- Code foundation for more http routes
2020-04-30 23:41:57 +00:00
Quentin McGaw
944e6a107b Logs wait function errors as soon as they happen 2020-04-30 23:39:28 +00:00
Quentin McGaw
b6135d2476 Support consoles without /dev/stdout 2020-04-30 23:34:35 +00:00
Quentin McGaw
c9b6e79792 Clear ip status file on termination 2020-04-30 12:55:07 +00:00
Quentin McGaw
94255aaa38 Better onConnected logic
- First port forward after 5 seconds
- Public IP obtained ASAP
- Logging in main only
- Allow port forward firewall with 1 second timeout local context
2020-04-30 12:54:48 +00:00
Quentin McGaw
ac706bd156 Wait for openvpn to exit on program termination 2020-04-30 11:01:22 +00:00
Quentin McGaw
d864a9f580 Refixes #148 2020-04-30 10:48:26 +00:00
Quentin McGaw
a32318d246 Improve issue templates 2020-04-30 10:38:31 +00:00
Quentin McGaw
45a7a5b9e2 Fix build 2020-04-30 00:46:29 +00:00
Quentin McGaw
9af2a7a640 Retry port forwarding after 5 seconds, fix #148 2020-04-30 00:02:27 +00:00
Quentin McGaw
eb62ad06db Better context handling (prep for restart logic) 2020-04-29 23:59:23 +00:00
Quentin McGaw
a033637e85 Better exit handling 2020-04-29 01:27:42 +00:00
Quentin McGaw
b0ea739c20 Restarts openvpn on failure without Docker restart (#144) 2020-04-28 21:22:18 -04:00
Quentin McGaw
352af84977 Workflows adjustments (#124)
* reviewdog/action-misspell@master
* Improve issue templates
2020-04-28 08:48:06 -04:00
Quentin McGaw
eb149ee040 Fix bad links in FAQ 2020-04-28 08:43:21 -04:00
Quentin McGaw
9b3166a2e2 Add @Frepke to sponsors in readme 2020-04-28 08:43:13 -04:00
Quentin McGaw
e94f4283e1 Port forwards 1 second after openvpn connects 2020-04-27 12:10:36 +00:00
Quentin McGaw
ef0959a15e Update Golangci-lint to v1.25.0 2020-04-26 13:28:26 +00:00
Quentin McGaw
36424c08ac Better checks for user provided private addresses 2020-04-26 13:28:14 +00:00
Quentin McGaw
97ea5f63b8 Removes port forward status file at exit 2020-04-19 20:45:34 +00:00
Quentin McGaw
88c9d3d687 Waits 300ms before first DNS resolution try 2020-04-19 20:44:33 +00:00
Quentin McGaw
f1569dac05 Each stream uses a different color, fixes #136 2020-04-19 20:40:31 +00:00
Quentin McGaw
4cb32ef9dc Reliably wait for all processes to exit 2020-04-19 20:10:48 +00:00
Quentin McGaw
e805d42197 Updated dependencies 2020-04-19 18:13:48 +00:00
Quentin McGaw
cbd11bfdf2 Thanks @Ralph521 ! 2020-04-15 12:33:50 +00:00
Quentin McGaw
422bd8d428 Log stderr stream from shadowsocks 2020-04-14 12:22:14 +00:00
Quentin McGaw
58459f0336 PIA_ENCRYPTION in readme, thanks @Frepke 2020-04-14 07:43:14 -04:00
Quentin McGaw
6f6e227b94 Add section for sponsors, donations in readme 2020-04-13 00:32:57 +00:00
Quentin McGaw
e015cd4a27 Windscribe affiliate link, refers to #109 2020-04-13 00:21:41 +00:00
Quentin McGaw
768147095f Golangcilint in build pipeline and fix lint errors
- Fix bad permissions bits for files
- VPNSP is 'private internet access' instead of 'pia' (retro compatible)
- Check errors of deferred unsetEnv functions in params package
-  Other lint errors fixing and code simplifications
2020-04-12 20:05:28 +00:00
Quentin McGaw
8f6b6306d6 Formatting of files (goimport) 2020-04-12 20:01:33 +00:00
Quentin McGaw
fb4c9b8a58 Vscode workspace settings 2020-04-12 19:53:50 +00:00
Quentin McGaw
3d7cfb125a Using WithPrefix for loggers 2020-04-12 19:07:19 +00:00
Quentin McGaw
d42de99879 Updated golibs and using gomock+mockgen for tests 2020-04-12 18:09:46 +00:00
Quentin McGaw
68203c221d Refactored documentation for Docker hub workflow
- Readme size lowered to 18KB
- Documents created in doc/ directory
- faq.md, firewall.md, development.md
- Title svg as absolute path for Docker hub
- Re-enabled Docker hub description workflow
2020-04-12 13:42:41 +00:00
Quentin McGaw
3ac3e5022c IP_STATUS_FILE and routing improvements (#130)
- Obtains VPN public IP address from routing table
- Logs and writes VPN Public IP address to `/ip` as soon as VPN is up
- Obtain port forward, logs it and writes it as soon as VPN is up
- Routing fully refactored and tested
- Routing reads from `/proc/net/route`
- Routing mutates the routes using `ip route ...`
2020-04-12 08:55:13 -04:00
Quentin McGaw
da8391e9ae Using %s instead of %w for format print 2020-04-12 02:42:32 +00:00
Quentin McGaw
ebdf241888 Show DNS lookup error, refers to #127 2020-04-09 13:17:55 +00:00
Quentin McGaw
60cec716b2 Clears port forward status file at exit, fix #125 2020-04-09 12:11:36 +00:00
Quentin McGaw
e7a475a303 Stops on port forwarding error, fixes #120 2020-03-31 12:03:26 +00:00
Quentin McGaw
67588e0072 Merge branch 'master' of github.com:qdm12/private-internet-access-docker 2020-03-30 12:01:45 +00:00
Quentin McGaw
bfa3d749ac Fix test 2020-03-30 12:01:35 +00:00
Quentin McGaw
7e79d9696f TZ setting shown at start (#119) 2020-03-30 07:58:40 -04:00
Quentin McGaw
f251c6aa4d Using UID and GID given for Tinyproxy, fixes #118 2020-03-30 11:56:38 +00:00
Quentin McGaw
d2117cd043 Improved environment variables table 2020-03-30 00:48:54 +00:00
Quentin McGaw
0235df74a0 SHADOWSOCKS_METHOD environment variable (#117) 2020-03-29 20:06:27 -04:00
Quentin McGaw
e5adccd9c5 Custom UID and GID for subprocesses and files written (#116) Fix #116
- Environment variables `UID` and `GID`, both defaulting to `1000`
- All subprocesses (openvpn, tinyproxy, etc.) run using the UID and GID given
- All files are written with an ownership for the UID and GID given
- Port forwarded file has also ownership for UID, GID and read permission only
2020-03-29 19:52:49 -04:00
Quentin McGaw
76cea56864 Windscribe support (#114) 2020-03-29 16:42:06 -04:00
Quentin McGaw
643745d33e OPENVPN_AUTH variable, refers to #94 2020-03-29 16:22:21 -04:00
Quentin McGaw
3d6a580102 Workflow minor fixes
- Renamed bad name in PR docker build
- Removed escaped \n in greetings
- Fixed up misspell action
2020-03-29 11:30:37 -04:00
Quentin McGaw
d4a1828c1d Issue templates updated 2020-03-29 11:29:59 -04:00
Quentin McGaw
bdf96d864e Check custom cipher value for each vpn provider 2020-03-27 01:10:54 +00:00
Quentin McGaw
15a549be11 OPENVPN_CIPHER variable (#100), refers to #94 and #59 2020-03-26 20:29:32 -04:00
Quentin McGaw
d534f92432 Workflows changes
- Simple Docker build for PRs
- Only run buildx for latest tag on pushes to master branch
2020-03-26 20:17:10 -04:00
Quentin McGaw
d0c61662b5 Revise workflows (#99) 2020-03-26 08:35:48 -04:00
Quentin McGaw
98b076e2cb Rename ENCRYPTION to PIA_ENCRYPTION (#98) 2020-03-26 08:11:50 -04:00
github-actions[bot]
0b997fe6c8 Typos fixes (#95)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2020-03-25 18:28:28 -04:00
Quentin McGaw
b0c0bd6364 Updated readme build status badge 2020-03-25 18:23:09 -04:00
Quentin McGaw
c61a418430 Docker build with workflows instead of Travis CI 2020-03-25 18:22:02 -04:00
Quentin McGaw
e6bbaa2ba6 Security analysis workflow 2020-03-25 18:21:36 -04:00
Quentin McGaw
17ccf98c75 Docker hub description sync workflow 2020-03-25 18:21:26 -04:00
Quentin McGaw
4db67c70b8 Misspell workflow 2020-03-25 18:21:14 -04:00
Quentin McGaw
3250a20ffc Issue templates 2020-03-25 18:21:01 -04:00
Quentin McGaw
6c12fdff2b Labels and greetings Github workflows 2020-03-25 18:20:52 -04:00
Quentin McGaw
f033204844 Contribution doc updated
- Codeowners file
- Contributing doc file
- Readme dev and contributing updated
2020-03-25 18:19:57 -04:00
Quentin McGaw
e334cf6c5f Minor changes 2020-03-23 20:37:56 +00:00
Quentin McGaw
9435db8e1e Fix #90 add env variable OPENVPN_TARGET_IP 2020-03-18 23:49:40 +00:00
Quentin McGaw
d2b361b998 Fix #81, new env variable OPENVPN_ROOT 2020-03-18 23:05:47 +00:00
Quentin McGaw (desktop)
9d786bf338 Update to Go 1.14 2020-03-18 01:04:44 +00:00
Quentin McGaw (desktop)
3339455a97 Simpler readme svg title 2020-03-18 01:04:33 +00:00
Quentin McGaw (desktop)
0eb2e5a120 Removed unecessary tun-ipv6 from pia ovpn config 2020-03-05 01:03:33 +00:00
Quentin McGaw (desktop)
d0f678c315 Add DNS over TLS ipv6 upstream servers, see #88 2020-03-05 00:54:33 +00:00
Quentin McGaw (desktop)
0c48d2d5a0 DOT_IPV6 environment variable added, refers to #88 2020-03-05 00:51:04 +00:00
Quentin McGaw (desktop)
47a197be48 Add ipv6 documentation, refers to #86 2020-03-05 00:01:04 +00:00
Quentin McGaw (desktop)
28edae383b Changed openvpn Mullvad settings, refers to #86 2020-03-04 23:52:41 +00:00
Quentin McGaw (desktop)
939b58c457 Ortograph! Renamed annoucement to announcement 2020-03-04 23:47:21 +00:00
Quentin McGaw
fa0272d5ad Support Mullvad over openvpn (#85)
Additional changes:
- Allow empty value for PIA region
- Most settings are lowercased
- `OPENVPN_VERBOSITY` environment variable
- openvpn also tunnels IPv6, and unbound supports ipv6
- auth kept only on disk, not in memory
- readme reworked
- CI script fixed and improved
- Added v2 Docker tag
- Shadowsocks log defaults to `off`
2020-02-29 21:05:20 -05:00
Quentin McGaw (desktop)
839c6f05dd Upgraded golibs 2020-03-01 02:03:57 +00:00
Quentin McGaw (desktop)
9ada201b82 Fixed and improved CI script 2020-02-25 11:47:08 +00:00
Quentin McGaw (desktop)
dd0170afb1 Annoucement expiration time parsing 2020-02-25 11:46:52 +00:00
Quentin McGaw (desktop)
9239e840c4 Fixed CI script for pull requests 2020-02-23 19:11:03 -05:00
Quentin McGaw (desktop)
96713b26cb README: contributors section 2020-02-22 17:56:04 +00:00
Quentin McGaw (desktop)
3ad60349db Applied JeordyR's changes 2020-02-22 17:55:49 +00:00
Quentin McGaw (desktop)
5ee4e2fde0 Shadowsocks log defaults to off 2020-02-22 17:43:17 +00:00
Quentin McGaw (desktop)
ce4fd8bc68 TODOs are in a collapsible (most users don't care) 2020-02-22 17:35:50 +00:00
Quentin McGaw (desktop)
90fc12a941 Added v2 Docker tag 2020-02-22 17:34:41 +00:00
Quentin McGaw (desktop)
16995e1d93 Changed CI script 2020-02-22 17:32:05 +00:00
Quentin McGaw (desktop)
9669938703 Port forwarding section reworked 2020-02-22 17:15:42 +00:00
Quentin McGaw (desktop)
ac60cf8ab8 Minor changes 2020-02-22 17:07:06 +00:00
Quentin McGaw (desktop)
f5a32e690f README: Updated and refactored features section 2020-02-22 17:06:35 +00:00
Quentin McGaw (desktop)
4e622a92a5 README: Rework requirements 2020-02-22 17:05:52 +00:00
Quentin McGaw (desktop)
d1412f43fd Changed announcement 2020-02-22 17:04:50 +00:00
Quentin McGaw (desktop)
1b3a135920 Reworked readme's FAQ 2020-02-22 16:35:26 +00:00
Quentin McGaw (desktop)
53db4813fa Allow PIA server to choose compression 2020-02-22 16:34:28 +00:00
Quentin McGaw (desktop)
2f09ed9069 tls-client openvpn client instruction added 2020-02-22 16:34:05 +00:00
Quentin McGaw (desktop)
9202d6c15f Do not keep auth in memory, only on disk 2020-02-22 16:33:37 +00:00
Quentin McGaw (desktop)
023f1c7e8e Tunnels IPv6 2020-02-22 16:32:59 +00:00
Quentin McGaw (desktop)
1aebe1a4c1 Simplified GetUser and GetPassword using libparams 2020-02-22 15:53:50 +00:00
Quentin McGaw (desktop)
f45f40eee1 Added OPENVPN_VERBOSITY environment variable 2020-02-22 15:48:09 +00:00
Quentin McGaw (desktop)
ab5d60754f Read some values with case sensitivity 2020-02-22 15:43:33 +00:00
Quentin McGaw (desktop)
83e8bb780a Fix reading region without case sensitivity 2020-02-22 13:52:33 +00:00
Quentin McGaw (desktop)
095623925a Might fix #82
- Allow any input traffic on Shadowsocks port if Shadowsocks is enabled
- Allow any input traffic on TinyProxy port if TinyProxy is enabled
2020-02-16 23:58:03 +00:00
Quentin McGaw (desktop)
888d8bbf87 Lowercased all Mullvad server constants 2020-02-16 22:39:05 +00:00
Quentin McGaw (desktop)
fbf04677f1 Minor fixes 2020-02-16 22:27:49 +00:00
Quentin McGaw (desktop)
2051aa1b04 Wire up everything for Mullvad and PIA in main.go 2020-02-16 22:16:11 +00:00
Quentin McGaw (desktop)
fc88ee135d Added Mullvad settings setup 2020-02-16 22:15:52 +00:00
Quentin McGaw (desktop)
a6f9a1a3d1 Fix GetVPNSP 2020-02-16 22:15:06 +00:00
Quentin McGaw (desktop)
f181ff0005 Added Mullvad environment variables and getters 2020-02-16 20:30:29 +00:00
Quentin McGaw (desktop)
71dcf23013 Allow empty value for PIA region 2020-02-16 20:29:37 +00:00
Quentin McGaw (desktop)
95ee3b4276 Moved GetUser and GetPassword to openvpn params getters 2020-02-16 19:55:01 +00:00
Quentin McGaw (desktop)
c42d13f14f Added Mullvad configuration builder 2020-02-16 19:51:08 +00:00
Quentin McGaw (desktop)
ce11745f6f Using struct for VPN connection settings 2020-02-16 19:50:21 +00:00
Quentin McGaw (desktop)
f6b91bd74f Added Mullvad constants and lists 2020-02-16 17:34:04 +00:00
Quentin McGaw (desktop)
5c69ddc05f Added Mullvad server model 2020-02-16 17:33:52 +00:00
Quentin McGaw (desktop)
ded635bd56 Fatal container exit if openvpn or unbound exits 2020-02-13 13:23:22 +00:00
Quentin McGaw (desktop)
66667f94e1 Refactored region parsing for PIA 2020-02-10 18:17:22 +00:00
Quentin McGaw (desktop)
77c6eeb765 Fixes #80 2020-02-10 18:14:14 +00:00
Quentin McGaw (desktop)
040b5afca6 Fix readme environment variables table formatting 2020-02-08 23:24:41 +00:00
Quentin McGaw (desktop)
321579333d Added simple healthcheck 2020-02-08 21:50:17 +00:00
Quentin McGaw (desktop)
a76aa5276d Added DOT_PRIVATE_ADDRESS environment variable 2020-02-08 21:28:33 +00:00
Quentin McGaw (desktop)
0264f8726a Added DOT_CACHING environment variable 2020-02-08 21:28:03 +00:00
Quentin McGaw (desktop)
247dc01f8a Minor changes
- Added missing environment variables to Dockerfile
- Constant ca certificates filepath
- Removed dns/os.go unused file
- Formatting improvements
- Added comments
- Readme TODOs update
2020-02-08 21:08:49 +00:00
Quentin McGaw (desktop)
6734779e90 Merges streams from start and exits cleanly 2020-02-08 17:51:30 +00:00
Quentin McGaw (desktop)
e527f14bd2 Fixes #72
- Using custom DNS internally (without TLS) to download Unbound files
- Using then Unbound with DNS over TLS internally and system wide
- Works even if you host system DNS is broken
- Waits a few milliseconds for Unbound to start up
2020-02-08 17:47:25 +00:00
Quentin McGaw (desktop)
a40f68f1df Refactored DNS provider data structures 2020-02-08 17:13:19 +00:00
Quentin McGaw (desktop)
84f49c5827 Removed 'TinyProxy settings' showing twice 2020-02-08 15:48:11 +00:00
Quentin McGaw (desktop)
792f70ffa7 No need to map /dev/net/tun device anymore 2020-02-08 15:46:59 +00:00
Quentin McGaw (desktop)
7f35daa418 Fixes #79 2020-02-08 15:34:41 +00:00
Quentin McGaw (desktop)
86ed6736a5 Fixes #79 Create TUN device if it does not exist 2020-02-08 15:30:28 +00:00
Quentin McGaw (desktop)
6620ba52d2 Renaming
- FileOwnership option to Ownership
- FilePermissions option to Permissions
2020-02-08 15:29:27 +00:00
Quentin McGaw (desktop)
1f873e7d66 Fixes mix of parameter (Shadowsocks, Tinyproxy) 2020-02-08 14:09:20 +00:00
Quentin McGaw (desktop)
fc9ebd561c Fixes #77 bad tinyproxy configuration generation 2020-02-08 14:08:51 +00:00
Quentin McGaw (desktop)
63fd72524e Tinyproxy log level parameter fix #77 2020-02-08 00:10:52 +00:00
Quentin McGaw (desktop)
ed5a90ef25 Fixes #73 2020-02-07 14:21:26 +00:00
Quentin McGaw (desktop)
7f103b2749 Fixed tinyproxy log level 2020-02-07 14:15:52 +00:00
Quentin McGaw (desktop)
69796e1ff9 Build openvpn configuration from scratch 2020-02-07 13:55:24 +00:00
Quentin McGaw (desktop)
6a9cd7ed9c Increase http client timeout to 15 seconds 2020-02-07 13:55:07 +00:00
Quentin McGaw
64649039d9 Rewrite of the entrypoint in Golang (#71)
- General improvements
    - Parallel download of only needed files at start
    - Prettier console output with all streams merged (openvpn, unbound, shadowsocks etc.)
    - Simplified Docker final image
    - Faster bootup
- DNS over TLS
    - Finer grain blocking at DNS level: malicious, ads and surveillance
    - Choose your DNS over TLS providers
    - Ability to use multiple DNS over TLS providers for DNS split horizon
    - Environment variables for DNS logging
    - DNS block lists needed are downloaded and built automatically at start, in parallel
- PIA
    - A random region is selected if the REGION parameter is left empty (thanks @rorph for your PR)
    - Routing and iptables adjusted so it can work as a Kubernetes pod sidecar (thanks @rorph for your PR)
2020-02-06 20:42:46 -05:00
Quentin McGaw (desktop)
3de4ffcf66 Merge branch 'master' of github.com:qdm12/private-internet-access-docker 2020-01-19 10:59:13 -05:00
Quentin McGaw (desktop)
60a69f316b Fixed Slack invite link 2020-01-19 10:59:00 -05:00
Quentin McGaw
9b26a39690 Fixed CI for branches and PRs (#64) 2019-12-20 07:40:39 -05:00
Quentin McGaw
73cef63e73 New SVG icon (#63) 2019-12-20 07:28:33 -05:00
Quentin McGaw (desktop)
90f506d2b7 Merge branch 'master' of github.com:qdm12/private-internet-access-docker 2019-12-20 12:05:55 +00:00
Quentin McGaw (desktop)
07cb909061 Updated announcement to Medium article 2019-12-20 12:05:19 +00:00
Quentin McGaw (desktop)
af5c7c648d Fixed SHADOWSOCKS env variable check 2019-12-20 12:05:02 +00:00
Quentin McGaw
fd248098a6 Create FUNDING.yml 2019-12-14 17:59:25 -05:00
Quentin McGaw (desktop)
a21bb009e5 openvpn runs without root by default 2019-11-24 11:04:55 -05:00
Quentin McGaw (desktop)
8b313cf211 Small changes and cleanup 2019-11-24 11:04:37 -05:00
Quentin McGaw
adf82d844a Further cleanup and readme rework, fixes #39 (#58)
Further cleanup and readme rework, also fixes #39 with release `v1`
2019-11-23 20:01:29 -05:00
Quentin McGaw
0af0632304 Building Docker images for all CPU architectures (#57)
* Created Travis config to build images for all CPU architectures
* Updated readme
2019-11-23 18:01:18 -05:00
Quentin McGaw (desktop)
9a2d0ec3ef Simplified ARM build instructions 2019-11-21 20:45:21 -05:00
148 changed files with 20046 additions and 1082 deletions

View File

@@ -0,0 +1,115 @@
{
"name": "pia-dev",
"dockerComposeFile": [
"docker-compose.yml"
],
"service": "vscode",
"runServices": [
"vscode"
],
"shutdownAction": "stopCompose",
"postCreateCommand": "go mod download",
"workspaceFolder": "/workspace",
"extensions": [
"golang.go",
"IBM.output-colorizer",
"eamodio.gitlens",
"mhutchie.git-graph",
"davidanson.vscode-markdownlint",
"shardulm94.trailing-spaces",
"alefragnani.Bookmarks",
"Gruntfuggly.todo-tree",
"mohsen1.prettify-json",
"quicktype.quicktype",
"spikespaz.vscode-smoothtype",
"stkb.rewrap",
"vscode-icons-team.vscode-icons"
],
"settings": {
// General settings
"files.eol": "\n",
// Docker
"remote.extensionKind": {
"ms-azuretools.vscode-docker": "workspace"
},
// Golang general settings
"go.useLanguageServer": true,
"go.autocompleteUnimportedPackages": true,
"go.gotoSymbol.includeImports": true,
"go.gotoSymbol.includeGoroot": true,
"gopls": {
"completeUnimported": true,
"deepCompletion": true,
"usePlaceholders": false
},
"go.lintTool": "golangci-lint",
"go.lintFlags": [
"--fast",
"--enable",
"staticcheck",
"--enable",
"bodyclose",
"--enable",
"dogsled",
"--enable",
"gochecknoglobals",
"--enable",
"gochecknoinits",
"--enable",
"gocognit",
"--enable",
"goconst",
"--enable",
"gocritic",
"--enable",
"gocyclo",
"--enable",
"golint",
"--enable",
"gosec",
"--enable",
"interfacer",
"--enable",
"maligned",
"--enable",
"misspell",
"--enable",
"nakedret",
"--enable",
"prealloc",
"--enable",
"scopelint",
"--enable",
"unconvert",
"--enable",
"unparam",
"--enable",
"whitespace"
],
// Golang on save
"go.buildOnSave": "workspace",
"go.lintOnSave": "workspace",
"go.vetOnSave": "workspace",
"editor.formatOnSave": true,
"[go]": {
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
// Golang testing
"go.toolsEnvVars": {
"GOFLAGS": "-tags=integration"
},
"gopls.env": {
"GOFLAGS": "-tags=integration"
},
"go.testEnvVars": {},
"go.testFlags": [
"-v",
// "-race"
],
"go.testTimeout": "600s",
"go.coverOnSingleTestFile": true,
"go.coverOnSingleTest": true
}
}

View File

@@ -0,0 +1,15 @@
version: "3.7"
services:
vscode:
image: qmcgaw/godevcontainer
volumes:
- ../:/workspace
- ~/.ssh:/home/vscode/.ssh:ro
- ~/.ssh:/root/.ssh:ro
- /var/run/docker.sock:/var/run/docker.sock
cap_add:
- SYS_PTRACE
security_opt:
- seccomp:unconfined
entrypoint: zsh -c "while sleep 1000; do :; done"

View File

@@ -1,4 +1,11 @@
.devcontainer
.git
readme
*.yml
*.md
.github
.vscode
cmd
!cmd/gluetun
doc
docker-compose.yml
LICENSE
README.md
title.svg

1
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1 @@
@qdm12

29
.github/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,29 @@
# Contributing
Contributions are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [open source license of this project](../LICENSE).
## Submitting a pull request
1. [Fork](https://github.com/qdm12/gluetun/fork) and clone the repository
1. Create a new branch `git checkout -b my-branch-name`
1. Modify the code
1. Ensure the docker build succeeds `docker build .`
1. Commit your modifications
1. Push to your fork and [submit a pull request](https://github.com/qdm12/gluetun/compare)
## Resources
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
## Contributors
Thanks for all the contributions, whether small or not so small!
- [@JeordyR](https://github.com/JeordyR) for testing the Mullvad version and opening a [PR with a few fixes](https://github.com/qdm12/gluetun/pull/84/files) 👍
- [@rorph](https://github.com/rorph) for a [PR to pick a random region for PIA](https://github.com/qdm12/gluetun/pull/70) and a [PR to make the container work with kubernetes](https://github.com/qdm12/gluetun/pull/69)
- [@JesterEE](https://github.com/JesterEE) for a [PR to fix silly line endings in block lists back then](https://github.com/qdm12/gluetun/pull/55) 📎
- [@elmerfdz](https://github.com/elmerfdz) for a [PR to add timezone information to have correct log timestampts](https://github.com/qdm12/gluetun/pull/51) 🕙
- [@Juggels](https://github.com/Juggels) for a [PR to write the PIA forwarded port to a file](https://github.com/qdm12/gluetun/pull/43)
- [@gdlx](https://github.com/gdlx) for a [PR to fix and improve PIA port forwarding script](https://github.com/qdm12/gluetun/pull/32)
- [@janaz](https://github.com/janaz) for keeping an eye on [updating things in the Dockerfile](https://github.com/qdm12/gluetun/pull/8)

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
github: [qdm12]

55
.github/ISSUE_TEMPLATE/bug.md vendored Normal file
View File

@@ -0,0 +1,55 @@
---
name: Bug
about: Report a bug
title: 'Bug: ...'
labels: ":bug: bug"
assignees: qdm12
---
**TLDR**: *Describe your issue in a one liner here*
1. Is this urgent?
- [ ] Yes
- [x] No
2. What VPN service provider are you using?
- [x] PIA
- [ ] Mullvad
- [ ] Windscribe
- [ ] Surfshark
- [ ] Cyberghost
3. What's the version of the program?
**See the line at the top of your logs**
`Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)`
4. What are you using to run the container?
- [ ] Docker run
- [x] Docker Compose
- [ ] Kubernetes
- [ ] Docker stack
- [ ] Docker swarm
- [ ] Podman
- [ ] Other:
5. Extra information
Logs:
```log
```
Configuration file:
```yml
```
Host OS:

View File

@@ -0,0 +1,14 @@
---
name: Feature request
about: Suggest a feature to add to this project
title: 'Feature request: ...'
labels: ":bulb: feature request"
assignees: qdm12
---
1. What's the feature?
2. Why do you need this feature?
3. Extra information?

55
.github/ISSUE_TEMPLATE/help.md vendored Normal file
View File

@@ -0,0 +1,55 @@
---
name: Help
about: Ask for help
title: 'Help: ...'
labels: ":pray: help wanted"
assignees:
---
**TLDR**: *Describe your issue in a one liner here*
1. Is this urgent?
- [ ] Yes
- [x] No
2. What VPN service provider are you using?
- [x] PIA
- [ ] Mullvad
- [ ] Windscribe
- [ ] Surfshark
- [ ] Cyberghost
3. What's the version of the program?
**See the line at the top of your logs**
`Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)`
4. What are you using to run the container?
- [ ] Docker run
- [x] Docker Compose
- [ ] Kubernetes
- [ ] Docker stack
- [ ] Docker swarm
- [ ] Podman
- [ ] Other:
5. Extra information
Logs:
```log
```
Configuration file:
```yml
```
Host OS:

51
.github/labels.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
- name: ":robot: bot"
color: "69cde9"
description: ""
- name: ":bug: bug"
color: "b60205"
description: ""
- name: ":game_die: dependencies"
color: "0366d6"
description: ""
- name: ":memo: documentation"
color: "c5def5"
description: ""
- name: ":busts_in_silhouette: duplicate"
color: "cccccc"
description: ""
- name: ":sparkles: enhancement"
color: "0054ca"
description: ""
- name: ":bulb: feature request"
color: "0e8a16"
description: ""
- name: ":mega: feedback"
color: "03a9f4"
description: ""
- name: ":rocket: future maybe"
color: "fef2c0"
description: ""
- name: ":hatching_chick: good first issue"
color: "7057ff"
description: ""
- name: ":pray: help wanted"
color: "4caf50"
description: ""
- name: ":hand: hold"
color: "24292f"
description: ""
- name: ":no_entry_sign: invalid"
color: "e6e6e6"
description: ""
- name: ":interrobang: maybe bug"
color: "ff5722"
description: ""
- name: ":thinking: needs more info"
color: "795548"
description: ""
- name: ":question: question"
color: "3f51b5"
description: ""
- name: ":coffin: wontfix"
color: "ffffff"
description: ""

34
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Docker build
on:
pull_request:
branches: [master]
paths-ignore:
- .devcontainer
- .github/ISSUE_TEMPLATE
- .github/workflows/buildx-release.yml
- .github/workflows/buildx-branch.yml
- .github/workflows/buildx-latest.yml
- .github/workflows/dockerhub-description.yml
- .github/workflows/labels.yml
- .github/workflows/misspell.yml
- .github/CODEOWNERS
- .github/CONTRIBUTING.md
- .github/FUNDING.yml
- .github/labels.yml
- .vscode
- cmd/ovpnparser
- cmd/resolver
- doc
- .gitignore
- docker-compose.yml
- LICENSE
- README.md
- title.svg
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Build image
run: docker build .

50
.github/workflows/buildx-branch.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: Buildx branch
on:
push:
branches:
- '*'
- '*/*'
- '!master'
paths-ignore:
- .devcontainer
- .github/ISSUE_TEMPLATE
- .github/workflows/build.yml
- .github/workflows/buildx-release.yml
- .github/workflows/buildx-latest.yml
- .github/workflows/dockerhub-description.yml
- .github/workflows/labels.yml
- .github/workflows/misspell.yml
- .github/CODEOWNERS
- .github/CONTRIBUTING.md
- .github/FUNDING.yml
- .github/labels.yml
- .vscode
- cmd/ovpnparser
- cmd/resolver
- doc
- .gitignore
- docker-compose.yml
- LICENSE
- README.md
- title.svg
jobs:
buildx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Buildx setup
uses: crazy-max/ghaction-docker-buildx@v1
- name: Dockerhub login
run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u qmcgaw --password-stdin 2>&1
- name: Run Buildx
run: |
docker buildx build \
--progress plain \
--platform=linux/amd64 \
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg VCS_REF=`git rev-parse --short HEAD` \
--build-arg VERSION=${GITHUB_REF##*/} \
-t qmcgaw/private-internet-access:${GITHUB_REF##*/} \
--push \
.
- run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/private-internet-access/tQFy7AxtSUNANPe6aoVChYdsI_I= || exit 0

47
.github/workflows/buildx-latest.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
name: Buildx latest
on:
push:
branches: [master]
paths-ignore:
- .devcontainer
- .github/ISSUE_TEMPLATE
- .github/workflows/build.yml
- .github/workflows/buildx-branch.yml
- .github/workflows/buildx-release.yml
- .github/workflows/dockerhub-description.yml
- .github/workflows/labels.yml
- .github/workflows/misspell.yml
- .github/CODEOWNERS
- .github/CONTRIBUTING.md
- .github/FUNDING.yml
- .github/labels.yml
- .vscode
- cmd/ovpnparser
- cmd/resolver
- doc
- .gitignore
- docker-compose.yml
- LICENSE
- README.md
- title.svg
jobs:
buildx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Buildx setup
uses: crazy-max/ghaction-docker-buildx@v1
- name: Dockerhub login
run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u qmcgaw --password-stdin 2>&1
- name: Run Buildx
run: |
docker buildx build \
--progress plain \
--platform=linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6 \
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg VCS_REF=`git rev-parse --short HEAD` \
--build-arg VERSION=latest \
-t qmcgaw/private-internet-access:latest \
--push \
.
- run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/private-internet-access/tQFy7AxtSUNANPe6aoVChYdsI_I= || exit 0

47
.github/workflows/buildx-release.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
name: Buildx release
on:
release:
types: [published]
paths-ignore:
- .devcontainer
- .github/ISSUE_TEMPLATE
- .github/workflows/build.yml
- .github/workflows/buildx-branch.yml
- .github/workflows/buildx-latest.yml
- .github/workflows/dockerhub-description.yml
- .github/workflows/labels.yml
- .github/workflows/misspell.yml
- .github/CODEOWNERS
- .github/CONTRIBUTING.md
- .github/FUNDING.yml
- .github/labels.yml
- .vscode
- cmd/ovpnparser
- cmd/resolver
- doc
- .gitignore
- docker-compose.yml
- LICENSE
- README.md
- title.svg
jobs:
buildx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Buildx setup
uses: crazy-max/ghaction-docker-buildx@v1
- name: Dockerhub login
run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u qmcgaw --password-stdin 2>&1
- name: Run Buildx
run: |
docker buildx build \
--progress plain \
--platform=linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6 \
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg VCS_REF=`git rev-parse --short HEAD` \
--build-arg VERSION=${GITHUB_REF##*/} \
-t qmcgaw/private-internet-access:${GITHUB_REF##*/} \
--push \
.
- run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/private-internet-access/tQFy7AxtSUNANPe6aoVChYdsI_I= || exit 0

View File

@@ -0,0 +1,19 @@
name: Docker Hub description
on:
push:
branches: [master]
paths:
- README.md
- .github/workflows/dockerhub-description.yml
jobs:
dockerHubDescription:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Docker Hub Description
uses: peter-evans/dockerhub-description@v2.1.0
env:
DOCKERHUB_USERNAME: qmcgaw
DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }}
DOCKERHUB_REPOSITORY: qmcgaw/private-internet-access

18
.github/workflows/labels.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: labels
on:
push:
branches: ["master"]
paths:
- '.github/labels.yml'
- '.github/workflows/labels.yml'
jobs:
labeler:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Labeler
if: success()
uses: crazy-max/ghaction-github-labeler@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

16
.github/workflows/misspell.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: Misspells
on:
pull_request:
branches: [master]
push:
branches: [master]
jobs:
misspell:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: reviewdog/action-misspell@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
locale: "US"
level: error

47
.golangci.yml Normal file
View File

@@ -0,0 +1,47 @@
linters-settings:
maligned:
suggest-new: true
misspell:
locale: US
linters:
disable-all: true
enable:
- bodyclose
- deadcode
- dogsled
- dupl
- errcheck
- gochecknoglobals
- gochecknoinits
- gocognit
- goconst
- gocritic
- gocyclo
- goimports
- golint
- gosec
- gosimple
- govet
- ineffassign
- interfacer
- maligned
- misspell
- nakedret
- prealloc
- rowserrcheck
- scopelint
- staticcheck
- structcheck
- typecheck
- unconvert
- unparam
- unused
- varcheck
- whitespace
run:
skip-dirs:
- .devcontainer
- .github
- postgres

View File

@@ -3,6 +3,7 @@
"shardulm94.trailing-spaces",
"ms-azuretools.vscode-docker",
"davidanson.vscode-markdownlint",
"IBM.output-colorizer"
"IBM.output-colorizer",
"golang.go"
]
}

91
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,91 @@
{
// General settings
"files.eol": "\n",
// Docker
"remote.extensionKind": {
"ms-azuretools.vscode-docker": "workspace"
},
// Golang general settings
"go.useLanguageServer": true,
"go.autocompleteUnimportedPackages": true,
"go.gotoSymbol.includeImports": true,
"go.gotoSymbol.includeGoroot": true,
"gopls": {
"completeUnimported": true,
"deepCompletion": true,
"usePlaceholders": false
},
"go.lintTool": "golangci-lint",
"go.lintFlags": [
"--fast",
"--enable",
"rowserrcheck",
"--enable",
"bodyclose",
"--enable",
"dogsled",
"--enable",
"dupl",
"--enable",
"gochecknoglobals",
"--enable",
"gochecknoinits",
"--enable",
"gocognit",
"--enable",
"goconst",
"--enable",
"gocritic",
"--enable",
"gocyclo",
"--enable",
"goimports",
"--enable",
"golint",
"--enable",
"gosec",
"--enable",
"interfacer",
"--enable",
"maligned",
"--enable",
"misspell",
"--enable",
"nakedret",
"--enable",
"prealloc",
"--enable",
"scopelint",
"--enable",
"unconvert",
"--enable",
"unparam",
"--enable",
"whitespace"
],
// Golang on save
"go.buildOnSave": "workspace",
"go.lintOnSave": "workspace",
"go.vetOnSave": "workspace",
"editor.formatOnSave": true,
"[go]": {
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
// Golang testing
"go.toolsEnvVars": {
"GOFLAGS": "-tags="
},
"gopls.env": {
"GOFLAGS": "-tags="
},
"go.testEnvVars": {},
"go.testFlags": [
"-v",
// "-race"
],
"go.testTimeout": "600s",
"go.coverOnSingleTestFile": true,
"go.coverOnSingleTest": true
}

View File

@@ -1,81 +1,108 @@
ARG BASE_IMAGE=alpine
ARG ALPINE_VERSION=3.10
FROM ${BASE_IMAGE}:${ALPINE_VERSION}
ARG BUILD_DATE
ARG VCS_REF
LABEL \
org.opencontainers.image.authors="quentin.mcgaw@gmail.com" \
org.opencontainers.image.created=$BUILD_DATE \
org.opencontainers.image.version="" \
org.opencontainers.image.revision=$VCS_REF \
org.opencontainers.image.url="https://github.com/qdm12/private-internet-access-docker" \
org.opencontainers.image.documentation="https://github.com/qdm12/private-internet-access-docker" \
org.opencontainers.image.source="https://github.com/qdm12/private-internet-access-docker" \
org.opencontainers.image.title="PIA client" \
org.opencontainers.image.description="VPN client to tunnel to private internet access servers using OpenVPN, IPtables, DNS over TLS and Alpine Linux" \
image-size="23.3MB" \
ram-usage="13MB to 80MB" \
cpu-usage="Low to Medium"
ENV USER= \
PASSWORD= \
ENCRYPTION=strong \
PROTOCOL=udp \
REGION="CA Montreal" \
NONROOT=no \
DOT=on \
BLOCK_MALICIOUS=off \
BLOCK_NSA=off \
UNBLOCK= \
EXTRA_SUBNETS= \
PORT_FORWARDING=off \
PORT_FORWARDING_STATUS_FILE="/forwarded_port" \
TINYPROXY=off \
TINYPROXY_LOG=Critical \
TINYPROXY_PORT=8888 \
TINYPROXY_USER= \
TINYPROXY_PASSWORD= \
SHADOWSOCKS=off \
SHADOWSOCKS_LOG=on \
SHADOWSOCKS_PORT=8388 \
SHADOWSOCKS_PASSWORD= \
TZ=
ENTRYPOINT /entrypoint.sh
EXPOSE 8888/tcp 8388/tcp 8388/udp
HEALTHCHECK --interval=3m --timeout=3s --start-period=20s --retries=1 CMD /healthcheck.sh
RUN apk add -q --progress --no-cache --update openvpn wget ca-certificates iptables unbound unzip tinyproxy jq tzdata && \
echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories && \
apk add -q --progress --no-cache --update shadowsocks-libev && \
wget -q https://www.privateinternetaccess.com/openvpn/openvpn.zip \
https://www.privateinternetaccess.com/openvpn/openvpn-strong.zip \
https://www.privateinternetaccess.com/openvpn/openvpn-tcp.zip \
https://www.privateinternetaccess.com/openvpn/openvpn-strong-tcp.zip && \
mkdir -p /openvpn/target && \
unzip -q openvpn.zip -d /openvpn/udp-normal && \
unzip -q openvpn-strong.zip -d /openvpn/udp-strong && \
unzip -q openvpn-tcp.zip -d /openvpn/tcp-normal && \
unzip -q openvpn-strong-tcp.zip -d /openvpn/tcp-strong && \
apk del -q --progress --purge unzip && \
rm -rf /*.zip /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-anchor /usr/sbin/unbound-checkconf /usr/sbin/unbound-control /usr/sbin/unbound-control-setup /usr/sbin/unbound-host /etc/tinyproxy/tinyproxy.conf && \
adduser nonrootuser -D -H --uid 1000 && \
wget -q https://raw.githubusercontent.com/qdm12/files/master/named.root.updated -O /etc/unbound/root.hints && \
wget -q https://raw.githubusercontent.com/qdm12/files/master/root.key.updated -O /etc/unbound/root.key && \
cd /tmp && \
wget -q https://raw.githubusercontent.com/qdm12/files/master/malicious-hostnames.updated -O malicious-hostnames && \
wget -q https://raw.githubusercontent.com/qdm12/files/master/surveillance-hostnames.updated -O nsa-hostnames && \
wget -q https://raw.githubusercontent.com/qdm12/files/master/malicious-ips.updated -O malicious-ips && \
while read hostname; do echo "local-zone: \""$hostname"\" static" >> blocks-malicious.conf; done < malicious-hostnames && \
while read ip; do echo "private-address: $ip" >> blocks-malicious.conf; done < malicious-ips && \
tar -cjf /etc/unbound/blocks-malicious.bz2 blocks-malicious.conf && \
while read hostname; do echo "local-zone: \""$hostname"\" static" >> blocks-nsa.conf; done < nsa-hostnames && \
tar -cjf /etc/unbound/blocks-nsa.bz2 blocks-nsa.conf && \
rm -f /tmp/*
COPY unbound.conf /etc/unbound/unbound.conf
COPY tinyproxy.conf /etc/tinyproxy/tinyproxy.conf
COPY shadowsocks.json /etc/shadowsocks.json
COPY entrypoint.sh healthcheck.sh portforward.sh /
RUN chown nonrootuser -R /etc/unbound /etc/tinyproxy && \
chmod 700 /etc/unbound /etc/tinyproxy && \
chmod 600 /etc/unbound/unbound.conf /etc/tinyproxy/tinyproxy.conf /etc/shadowsocks.json && \
chmod 500 /entrypoint.sh /healthcheck.sh /portforward.sh && \
chmod 400 /etc/unbound/root.hints /etc/unbound/root.key /etc/unbound/*.bz2
ARG ALPINE_VERSION=3.12
ARG GO_VERSION=1.15
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS builder
RUN apk --update add git
ENV CGO_ENABLED=0
ARG GOLANGCI_LINT_VERSION=v1.30.0
RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s ${GOLANGCI_LINT_VERSION}
WORKDIR /tmp/gobuild
COPY .golangci.yml .
COPY go.mod go.sum ./
RUN go mod download 2>&1
COPY cmd/gluetun/main.go .
COPY internal/ ./internal/
RUN go test ./...
RUN golangci-lint run --timeout=10m
RUN go build -trimpath -ldflags="-s -w" -o entrypoint main.go
FROM alpine:${ALPINE_VERSION}
ARG VERSION
ARG BUILD_DATE
ARG VCS_REF
ENV VERSION=$VERSION \
BUILD_DATE=$BUILD_DATE \
VCS_REF=$VCS_REF
LABEL \
org.opencontainers.image.authors="quentin.mcgaw@gmail.com" \
org.opencontainers.image.created=$BUILD_DATE \
org.opencontainers.image.version=$VERSION \
org.opencontainers.image.revision=$VCS_REF \
org.opencontainers.image.url="https://github.com/qdm12/gluetun" \
org.opencontainers.image.documentation="https://github.com/qdm12/gluetun" \
org.opencontainers.image.source="https://github.com/qdm12/gluetun" \
org.opencontainers.image.title="VPN client for PIA, Mullvad, Windscribe, Surfshark and Cyberghost" \
org.opencontainers.image.description="VPN client to tunnel to PIA, Mullvad, Windscribe, Surfshark and Cyberghost servers using OpenVPN, IPtables, DNS over TLS and Alpine Linux"
ENV VPNSP=pia \
PROTOCOL=udp \
OPENVPN_VERBOSITY=1 \
OPENVPN_ROOT=no \
OPENVPN_TARGET_IP= \
TZ= \
UID=1000 \
GID=1000 \
IP_STATUS_FILE="/ip" \
# PIA, Windscribe, Surfshark, Cyberghost, Vyprvpn, NordVPN, PureVPN only
USER= \
PASSWORD= \
REGION= \
# PIA only
PIA_ENCRYPTION=strong \
PORT_FORWARDING=off \
PORT_FORWARDING_STATUS_FILE="/forwarded_port" \
# Mullvad and PureVPN only
COUNTRY= \
CITY= \
# Mullvad only
ISP= \
# Mullvad and Windscribe only
PORT= \
# Cyberghost only
CYBERGHOST_GROUP="Premium UDP Europe" \
# NordVPN only
SERVER_NUMBER= \
# Openvpn
OPENVPN_CIPHER= \
OPENVPN_AUTH= \
# DNS over TLS
DOT=on \
DOT_PROVIDERS=cloudflare \
DOT_PRIVATE_ADDRESS=127.0.0.1/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,::1/128,fc00::/7,fe80::/10,::ffff:0:0/96 \
DOT_VERBOSITY=1 \
DOT_VERBOSITY_DETAILS=0 \
DOT_VALIDATION_LOGLEVEL=0 \
DOT_CACHING=on \
DOT_IPV6=off \
BLOCK_MALICIOUS=on \
BLOCK_SURVEILLANCE=off \
BLOCK_ADS=off \
UNBLOCK= \
DNS_UPDATE_PERIOD=24h \
DNS_PLAINTEXT_ADDRESS=1.1.1.1 \
DNS_KEEP_NAMESERVER=off \
# Firewall
FIREWALL=on \
EXTRA_SUBNETS= \
FIREWALL_VPN_INPUT_PORTS= \
FIREWALL_DEBUG=off \
# Tinyproxy
TINYPROXY=off \
TINYPROXY_LOG=Info \
TINYPROXY_PORT=8888 \
TINYPROXY_USER= \
TINYPROXY_PASSWORD= \
# Shadowsocks
SHADOWSOCKS=off \
SHADOWSOCKS_LOG=off \
SHADOWSOCKS_PORT=8388 \
SHADOWSOCKS_PASSWORD= \
SHADOWSOCKS_METHOD=chacha20-ietf-poly1305
ENTRYPOINT ["/entrypoint"]
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
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 && \
rm -rf /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-* /etc/tinyproxy/tinyproxy.conf && \
deluser openvpn && \
deluser tinyproxy && \
deluser unbound
COPY --from=builder /tmp/gobuild/entrypoint /entrypoint

702
README.md
View File

@@ -1,324 +1,378 @@
# Private Internet Access Client (OpenVPN+Iptables+DNS over TLS on Alpine Linux)
*Lightweight VPN client to tunnel to private internet access servers*
[![PIA Docker OpenVPN](https://github.com/qdm12/private-internet-access-docker/raw/master/readme/title.png)](https://hub.docker.com/r/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/enQtODMwMDQyMTAxMjY1LTU1YjE1MTVhNTBmNTViNzJiZmQwZWRmMDhhZjEyNjVhZGM4YmIxOTMxOTYzN2U0N2U2YjQ2MDk3YmYxN2NiNTc)
[![Build Status](https://travis-ci.org/qdm12/private-internet-access-docker.svg?branch=master)](https://travis-ci.org/qdm12/private-internet-access-docker)
[![Docker Build Status](https://img.shields.io/docker/build/qmcgaw/private-internet-access.svg)](https://hub.docker.com/r/qmcgaw/private-internet-access)
[![GitHub last commit](https://img.shields.io/github/last-commit/qdm12/private-internet-access-docker.svg)](https://github.com/qdm12/private-internet-access-docker/issues)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/private-internet-access-docker.svg)](https://github.com/qdm12/private-internet-access-docker/issues)
[![GitHub issues](https://img.shields.io/github/issues/qdm12/private-internet-access-docker.svg)](https://github.com/qdm12/private-internet-access-docker/issues)
[![Docker Pulls](https://img.shields.io/docker/pulls/qmcgaw/private-internet-access.svg)](https://hub.docker.com/r/qmcgaw/private-internet-access)
[![Docker Stars](https://img.shields.io/docker/stars/qmcgaw/private-internet-access.svg)](https://hub.docker.com/r/qmcgaw/private-internet-access)
[![Docker Automated](https://img.shields.io/docker/automated/qmcgaw/private-internet-access.svg)](https://hub.docker.com/r/qmcgaw/private-internet-access)
[![Image size](https://images.microbadger.com/badges/image/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)
| Image size | RAM usage | CPU usage |
| --- | --- | --- |
| 23.3MB | 14MB to 80MB | Low to Medium |
<details><summary>Click to show base components</summary><p>
- [Alpine 3.10](https://alpinelinux.org) for a tiny image
- [OpenVPN 2.4.7](https://pkgs.alpinelinux.org/package/v3.10/main/x86_64/openvpn) to tunnel to PIA servers
- [IPtables 1.8.3](https://pkgs.alpinelinux.org/package/v3.10/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.9.1](https://pkgs.alpinelinux.org/package/v3.10/main/x86_64/unbound) configured with Cloudflare's [1.1.1.1](https://1.1.1.1) DNS over TLS
- [Files and blocking lists built periodically](https://github.com/qdm12/updated/tree/master/files) used with Unbound (see `BLOCK_MALICIOUS` and `BLOCK_NSA` environment variables)
- [TinyProxy 1.10.0](https://pkgs.alpinelinux.org/package/v3.10/main/x86_64/tinyproxy)
</p></details>
## Features
- <details><summary>Configure everything with environment variables</summary><p>
- [Destination region](https://www.privateinternetaccess.com/pages/network)
- Internet protocol
- Level of encryption
- PIA Username and password
- DNS over TLS
- Malicious DNS blocking
- Internal firewall
- Web HTTP proxy
- Run openvpn without root
</p></details>
- Connect other containers to it, [see this](https://github.com/qdm12/private-internet-access-docker#connect-to-it)
- **ARM** compatible
- Port forwarding
- The *iptables* firewall allows traffic only with needed PIA servers (IP addresses, port, protocol) combinations
- OpenVPN reconnects automatically on failure
- Docker healthcheck pings the DNS 1.1.1.1 to verify the connection is up
- Unbound DNS runs *without root*
- OpenVPN can run *without root* but this disallows OpenVPN reconnecting, it can be set with `NONROOT=yes`
- Connect your LAN devices
- HTTP Web proxy *tinyproxy*
- SOCKS5 proxy *shadowsocks*
## Setup
1. <details><summary>Requirements</summary><p>
- A Private Internet Access **username** and **password** - [Sign up](https://www.privateinternetaccess.com/pages/buy-vpn/)
- External firewall requirements, if you have one
- Allow outbound TCP 853 to 1.1.1.1 to allow Unbound to resolve the PIA domain name at start. You can then block it once the container is started.
- For UDP strong encryption, allow outbound UDP 1197
- For UDP normal encryption, allow outbound UDP 1198
- For TCP strong encryption, allow outbound TCP 501
- For TCP normal encryption, allow outbound TCP 502
- For the built-in web HTTP proxy, allow inbound TCP 8888
- For the built-in SOCKS5 proxy, allow inbound TCP 8388 and UDP 8388
- Docker API 1.25 to support `init`
- If you use Docker Compose, docker-compose >= 1.22.0, to support `init: true`
</p></details>
1. Ensure `/dev/net/tun` is setup on your host with either:
```sh
insmod /lib/modules/tun.ko
# or...
modprobe tun
```
1. <details><summary>CLICK IF YOU HAVE AN ARM DEVICE</summary><p>
- If you have a ARM 32 bit v6 architecture
```sh
docker build -t qmcgaw/private-internet-access \
--build-arg BASE_IMAGE=arm32v6/alpine \
https://github.com/qdm12/private-internet-access-docker.git
```
- If you have a ARM 32 bit v7 architecture
```sh
docker build -t qmcgaw/private-internet-access \
--build-arg BASE_IMAGE=arm32v7/alpine \
https://github.com/qdm12/private-internet-access-docker.git
```
- If you have a ARM 64 bit v8 architecture
```sh
docker build -t qmcgaw/private-internet-access \
--build-arg BASE_IMAGE=arm64v8/alpine \
https://github.com/qdm12/private-internet-access-docker.git
```
</p></details>
1. Launch the container with:
```bash
docker run -d --init --name=pia --cap-add=NET_ADMIN --device=/dev/net/tun \
-e REGION="CA Montreal" -e USER=js89ds7 -e PASSWORD=8fd9s239G \
qmcgaw/private-internet-access
```
or use [docker-compose.yml](https://github.com/qdm12/private-internet-access-docker/blob/master/docker-compose.yml) with:
```bash
docker-compose up -d
```
Note that you can:
- Change the many [environment variables](#environment-variables) available
- Use `-p 8888:8888/tcp` to access the HTTP web proxy (and put your LAN in `EXTRA_SUBNETS` environment variable)
- Use `-p 8388:8388/tcp -p 8388:8388/udp` to access the SOCKS5 proxy (and put your LAN in `EXTRA_SUBNETS` environment variable)
- Pass additional arguments to *openvpn* using Docker's command function (commands after the image name)
## Testing
Check the PIA IP address matches your expectations
```sh
docker run --rm --network=container:pia alpine:3.10 wget -qO- https://ipinfo.io
```
## Environment variables
| Environment variable | Default | Description |
| --- | --- | --- |
| `REGION` | `CA Montreal` | One of the [PIA regions](https://www.privateinternetaccess.com/pages/network/) |
| `PROTOCOL` | `udp` | `tcp` or `udp` |
| `ENCRYPTION` | `strong` | `normal` or `strong` |
| `USER` | | Your PIA username |
| `PASSWORD` | | Your PIA password |
| `NONROOT` | `no` | Run OpenVPN without root, `yes` or `no` |
| `DOT` | `on` | `on` or `off`, to activate DNS over TLS to 1.1.1.1 |
| `BLOCK_MALICIOUS` | `off` | `on` or `off`, blocks malicious hostnames and IPs |
| `BLOCK_NSA` | `off` | `on` or `off`, blocks NSA hostnames |
| `UNBLOCK` | | comma separated string (i.e. `web.com,web2.ca`) to unblock hostnames |
| `EXTRA_SUBNETS` | | comma separated subnets allowed in the container firewall (i.e. `192.168.1.0/24,192.168.10.121,10.0.0.5/28`) |
| `PORT_FORWARDING` | `off` | Set to `on` to forward a port on PIA server |
| `PORT_FORWARDING_STATUS_FILE` | `/forwarded_port` | File path to store the forwarded port number |
| `TINYPROXY` | `on` | `on` or `off`, to enable the internal HTTP proxy tinyproxy |
| `TINYPROXY_LOG` | `Critical` | `Info`, `Warning`, `Error` or `Critical` |
| `TINYPROXY_PORT` | `8888` | `1024` to `65535` internal port for HTTP proxy |
| `TINYPROXY_USER` | | Username to use to connect to the HTTP proxy |
| `TINYPROXY_PASSWORD` | | Passsword to use to connect to the HTTP proxy |
| `SHADOWSOCKS` | `on` | `on` or `off`, to enable the internal SOCKS5 proxy Shadowsocks |
| `SHADOWSOCKS_LOG` | `on` | `on` or `off` to enable logging for Shadowsocks |
| `SHADOWSOCKS_PORT` | `8388` | `1024` to `65535` internal port for SOCKS5 proxy |
| `SHADOWSOCKS_PASSWORD` | | Passsword to use to connect to the SOCKS5 proxy |
| `TZ` | | Specify a timezone to use e.g. `Europe/London` |
## Connect to it
There are various ways to achieve this, depending on your use case.
- <details><summary>Connect containers in the same docker-compose.yml as PIA</summary><p>
Add `network_mode: "service:pia"` to your *docker-compose.yml* (no need for `depends_on`)
</p></details>
- <details><summary>Connect other containers to PIA</summary><p>
Add `--network=container:pia` when launching the container, provided PIA is already running
</p></details>
- <details><summary>Connect containers from another docker-compose.yml</summary><p>
Add `network_mode: "container:pia"` to your *docker-compose.yml*, provided PIA is already running
</p></details>
- <details><summary>Connect LAN devices through the built-in HTTP proxy *Tinyproxy* (i.e. with Chrome, Kodi, etc.)</summary><p>
1. Setup a HTTP proxy client, such as [SwitchyOmega for Chrome](https://chrome.google.com/webstore/detail/proxy-switchyomega/padekgcemlokbadohgkifijomclgjgif?hl=en)
1. Ensure the PIA container is launched with:
- port `8888` published `-p 8888:8888/tcp`
- your LAN subnet, i.e. `192.168.1.0/24`, set as `-e EXTRA_SUBNETS=192.168.1.0/24`
1. With your HTTP proxy client, connect to the Docker host (i.e. `192.168.1.10`) on port `8888`. You need to enter your credentials if you set them with `TINYPROXY_USER` and `TINYPROXY_PASSWORD`.
1. If you set `TINYPROXY_LOG` to `Info`, more information will be logged in the Docker logs, merged with the OpenVPN logs.
`TINYPROXY_LOG` defaults to `Critical` to avoid logging everything, for privacy purposes.
</p></details>
- <details><summary>Connect LAN devices through the built-in SOCKS5 proxy *Shadowsocks* (per app, system wide, etc.)</summary><p>
1. Setup a SOCKS5 proxy client, there is a list of [ShadowSocks clients for **all platforms**](https://shadowsocks.org/en/download/clients.html)
- **note** some clients do not tunnel UDP so your DNS queries will be done locally and not through PIA and its built in DNS over TLS
- Clients that support such UDP tunneling are, as far as I know:
- iOS: Potatso Lite
- OSX: ShadowsocksX
- Android: Shadowsocks by Max Lv
1. Ensure the PIA container is launched with:
- port `8388` published `-p 8388:8388/tcp -p 8388:8388/udp`
- your LAN subnet, i.e. `192.168.1.0/24`, set as `-e EXTRA_SUBNETS=192.168.1.0/24`
1. With your SOCKS5 proxy client
- Enter the Docker host (i.e. `192.168.1.10`) as the server IP
- Enter port TCP (and UDP, if available) `8388` as the server port
- Use the password you have set with `SHADOWSOCKS_PASSWORD`
- Choose the encryption method/algorithm `chacha20-ietf-poly1305`
1. If you set `SHADOWSOCKS_LOG` to `on`, more information will be logged in the Docker logs, merged with the OpenVPN logs.
</p></details>
- <details><summary>Access ports of containers connected to PIA</summary><p>
In example, to access port `8000` of container `xyz` and `9000` of container `abc` connected to PIA,
publish ports `8000` and `9000` for the PIA container and access them as you would with any other container
</p></details>
- <details><summary>Access ports of containers connected to PIA, all in the same docker-compose.yml</summary><p>
In example, to access port `8000` of container `xyz` and `9000` of container `abc` connected to PIA, publish port `8000` and `9000` for the PIA container.
The docker-compose.yml file would look like:
```yml
version: '3.7'
services:
pia:
image: qmcgaw/private-internet-access
container_name: pia
init: true
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun
environment:
- USER=js89ds7
- PASSWORD=8fd9s239G
ports:
- 8000:8000/tcp
- 9000:9000/tcp
abc:
image: abc
container_name: abc
network_mode: "service:pia"
xyz:
image: xyz
container_name: xyz
network_mode: "service:pia"
```
</p></details>
## Port forwarding
By setting `PORT_FORWARDING` environment variable to `on`, the forwarded port will be read and written to the file specified in `PORT_FORWARDING_STATUS_FILE` (by default, this is set to `/forwarded_port`). If the location for this file does not exist, it will be created automatically.
You can mount this file as a volume to read it from other containers.
Note that not all regions support port forwarding.
## For the paranoids
- You can review the code which essential consists in the [Dockerfile](https://github.com/qdm12/private-internet-access-docker/blob/master/Dockerfile) and [entrypoint.sh](https://github.com/qdm12/private-internet-access-docker/blob/master/entrypoint.sh)
- Build the images yourself:
```bash
docker build -t qmcgaw/private-internet-access https://github.com/qdm12/private-internet-access-docker.git
```
- The download and unziping of PIA openvpn files is done at build for the ones not able to download the zip files
- Checksums for PIA openvpn zip files are not used as these files change often (but HTTPS is used)
- Use `-e ENCRYPTION=strong -e BLOCK_MALICIOUS=on`
- You can test DNSSEC using [internet.nl/connection](https://www.internet.nl/connection/)
- Check DNS leak tests with [https://www.dnsleaktest.com](https://www.dnsleaktest.com)
- DNS Leaks tests might not work because of [this](https://github.com/qdm12/cloudflare-dns-server#verify-dns-connection) (*TLDR*: DNS server is a local caching intermediary)
## Troubleshooting
- Password problems `AUTH: Received control message: AUTH_FAILED`
- Your password may contain a special character such as `$`.
You need to escape it with `\` in your run command or docker-compose.yml.
For example you would set `-e PASSWORD=mypa\$\$word`.
- Fallback to a previous version
1. Clone the repository on your machine
```sh
git clone https://github.com/qdm12/private-internet-access-docker.git pia
cd pia
```
1. Look up which commit you want to go back to [here](https://github.com/qdm12/private-internet-access-docker/commits/master), i.e. `942cc7d4d10545b6f5f89c907b7dd1dbc39368e0`
1. Revert to this commit locally
```sh
git reset --hard 942cc7d4d10545b6f5f89c907b7dd1dbc39368e0
```
1. Build the Docker image
```sh
docker build -t qmcgaw/private-internet-access .
```
## TODOs
- Shadowsocks
- Get logs from file and merge with docker stdout
- Mix Logs of Unbound
- Maybe use `--inactive 3600 --ping 10 --ping-exit 60` as default behavior
- Try without tun
## License
This repository is under an [MIT license](https://github.com/qdm12/private-internet-access-docker/master/license)
# Gluetun VPN client
*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access,
Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN and PureVPN VPN servers, using Go, OpenVPN,
iptables, DNS over TLS, ShadowSocks and Tinyproxy*
**ANNOUNCEMENT**: *[Video of the Git history of Gluetun](https://youtu.be/khipOYJtGJ0)*
<img height="250" src="https://raw.githubusercontent.com/qdm12/gluetun/master/title.svg?sanitize=true">
[![Build status](https://github.com/qdm12/gluetun/workflows/Buildx%20latest/badge.svg)](https://github.com/qdm12/gluetun/actions?query=workflow%3A%22Buildx+latest%22)
[![Docker Pulls](https://img.shields.io/docker/pulls/qmcgaw/private-internet-access.svg)](https://hub.docker.com/r/qmcgaw/private-internet-access)
[![Docker Stars](https://img.shields.io/docker/stars/qmcgaw/private-internet-access.svg)](https://hub.docker.com/r/qmcgaw/private-internet-access)
[![GitHub last commit](https://img.shields.io/github/last-commit/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/issues)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/issues)
[![GitHub issues](https://img.shields.io/github/issues/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/issues)
[![Image size](https://images.microbadger.com/badges/image/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)
## Features
- Based on Alpine 3.12 for a small Docker image of 52MB
- 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 fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
- Choose the vpn network protocol, `udp` or `tcp`
- Built in firewall kill switch to allow traffic only with needed the VPN servers and LAN devices
- Built in SOCKS5 proxy (Shadowsocks, tunnels TCP+UDP)
- Built in HTTP proxy (Tinyproxy, tunnels TCP)
- [Connect other containers to it](https://github.com/qdm12/gluetun#connect-to-it)
- [Connect LAN devices to it](https://github.com/qdm12/gluetun#connect-to-it)
- 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
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
- Subprograms all drop root privileges once launched
- Subprograms output streams are all merged together
- Can work as a Kubernetes sidecar container, thanks @rorph
## Setup
1. Requirements
- A VPN account with one of the service providers supported
- If you have a host or router firewall, please refer [to the firewall documentation](https://github.com/qdm12/gluetun/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`
- *Synology users*: please read [this part of the Wiki](https://github.com/qdm12/gluetun/wiki/Common-issues#synology)
1. Launch the container with:
```bash
docker run -d --name gluetun --cap-add=NET_ADMIN \
-e REGION="CA Montreal" -e USER=js89ds7 -e PASSWORD=8fd9s239G \
qmcgaw/private-internet-access
```
or use [docker-compose.yml](https://github.com/qdm12/gluetun/blob/master/docker-compose.yml) with:
```bash
docker-compose up -d
```
Note that you can:
- Change the many [environment variables](#environment-variables) available
- Use `-p 8888:8888/tcp` to access the HTTP web proxy (and put your LAN in `EXTRA_SUBNETS` environment variable, in example `192.168.1.0/24`)
- Use `-p 8388:8388/tcp -p 8388:8388/udp` to access the SOCKS5 proxy (and put your LAN in `EXTRA_SUBNETS` environment variable, in example `192.168.1.0/24`)
- Use `-p 8000:8000/tcp` to access the [HTTP control server](#HTTP-control-server) built-in
**If you encounter an issue with the tun device not being available, see [the FAQ](https://github.com/qdm12/gluetun/blob/master/doc/faq.md#how-to-fix-openvpn-failing-to-start)**
1. You can update the image with `docker pull qmcgaw/private-internet-access:latest`. See the [wiki](https://github.com/qdm12/gluetun/wiki/Common-issues#use-a-release-tag) for more information on other tags available.
## Testing
Check the VPN IP address matches your expectations
```sh
docker run --rm --network=container:gluetun alpine:3.12 wget -qO- https://ipinfo.io
```
Want more testing? ▶ [see the Wiki](https://github.com/qdm12/gluetun/wiki/Testing)
## Environment variables
**TLDR**; only set the 🏁 marked environment variables to get started.
### VPN
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| 🏁 `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 |
| `PROTOCOL` | `udp` | `udp` or `tcp` | Network protocol to use |
| `OPENVPN_VERBOSITY` | `1` | `0` to `6` | Openvpn verbosity level |
| `OPENVPN_ROOT` | `no` | `yes` or `no` | Run OpenVPN as root |
| `OPENVPN_TARGET_IP` | | Valid IP address | Specify a target VPN server (or gateway) IP address to use |
| `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 |
*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
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| 🏁 `USER` | | | Your username |
| 🏁 `PASSWORD` | | | Your password |
| `REGION` | | One of the [PIA regions](https://www.privateinternetaccess.com/pages/network/) | VPN server region |
| `PIA_ENCRYPTION` | `strong` | `normal`, `strong` | Encryption preset |
| `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 |
- Mullvad
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| 🏁 `USER` | | | Your user ID |
| `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 |
| `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 |
- Windscribe
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| 🏁 `USER` | | | Your username |
| 🏁 `PASSWORD` | | | Your password |
| `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 |
- Surfshark
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| 🏁 `USER` | | | Your **service** username, found at the bottom of the [manual setup page](https://account.surfshark.com/setup/manual) |
| 🏁 `PASSWORD` | | | Your **service** password |
| `REGION` | | One of the [Surfshark regions](https://github.com/qdm12/gluetun/wiki/surfshark) | VPN server region |
- Cyberghost
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| 🏁 `USER` | | | Your username |
| 🏁 `PASSWORD` | | | Your password |
| 🏁 `CLIENT_KEY` | | | Your device client key content, **see below** |
| `REGION` | | One of the [Cyberghost countries](https://github.com/qdm12/gluetun/wiki/Cyberghost#regions) | VPN server country |
| `CYBERGHOST_GROUP` | `Premium UDP Europe` | One of the [server groups](https://github.com/qdm12/gluetun/wiki/Cyberghost#server-groups) | Server group |
To specify your client key, you can either:
- Bind mount it at `/files/client.key`, for example with `-v /yourpath/client.key:/files/client.key:ro`
- Convert it to a single line value using:
```sh
docker run -it --rm -v /yourpath/client.key:/files/client.key:ro qmcgaw/private-internet-access clientkey
```
And use the line produced as the value for the environment variable `CLIENT_KEY`.
- Vyprvpn
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| 🏁 `USER` | | | Your username |
| 🏁 `PASSWORD` | | | Your password |
| `REGION` | | One of the [VyprVPN regions](https://www.vyprvpn.com/server-locations) | VPN server region |
- NordVPN
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| 🏁 `USER` | | | Your username |
| 🏁 `PASSWORD` | | | Your password |
| `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` |
- 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
None of the following values are required.
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| `DOT` | `on` | `on`, `off` | Activate DNS over TLS with Unbound |
| `DOT_PROVIDERS` | `cloudflare` | `cloudflare`, `google`, `quad9`, `quadrant`, `cleanbrowsing`, `securedns`, `libredns` | Comma delimited list of DNS over TLS providers |
| `DOT_CACHING` | `on` | `on`, `off` | Unbound caching |
| `DOT_IPV6` | `off` | `on`, `off` | DNS IPv6 resolution |
| `DOT_PRIVATE_ADDRESS` | All private CIDRs ranges | | Comma separated list of CIDRs or single IP addresses Unbound won't resolve to. Note that the default setting prevents DNS rebinding |
| `DOT_VERBOSITY` | `1` | `0` to `5` | Unbound verbosity level |
| `DOT_VERBOSITY_DETAILS` | `0` | `0` to `4` | Unbound details verbosity level |
| `DOT_VALIDATION_LOGLEVEL` | `0` | `0` to `2` | Unbound validation log level |
| `DNS_UPDATE_PERIOD` | `24h` | i.e. `0`, `30s`, `5m`, `24h` | Period to update block lists and cryptographic files and restart Unbound. Set to `0` to deactivate updates |
| `BLOCK_MALICIOUS` | `on` | `on`, `off` | Block malicious hostnames and IPs with Unbound |
| `BLOCK_SURVEILLANCE` | `off` | `on`, `off` | Block surveillance hostnames and IPs with Unbound |
| `BLOCK_ADS` | `off` | `on`, `off` | Block ads hostnames and IPs with Unbound |
| `UNBLOCK` | |i.e. `domain1.com,x.domain2.co.uk` | Comma separated list of domain names to leave unblocked with Unbound |
| `DNS_PLAINTEXT_ADDRESS` | `1.1.1.1` | Any IP address | IP address to use as DNS resolver if `DOT` is `off` |
| `DNS_KEEP_NAMESERVER` | `off` | `on` or `off` | Keep the nameservers in /etc/resolv.conf untouched, but disabled DNS blocking features |
### Firewall
That one is important if you want to connect to the container from your LAN for example, using Shadowsocks or Tinyproxy.
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| `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 |
| `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. |
### Shadowsocks
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| `SHADOWSOCKS` | `off` | `on`, `off` | Enable the internal SOCKS5 proxy Shadowsocks |
| `SHADOWSOCKS_LOG` | `off` | `on`, `off` | Enable logging |
| `SHADOWSOCKS_PORT` | `8388` | `1024` to `65535` | Internal port number for Shadowsocks to listen on |
| `SHADOWSOCKS_PASSWORD` | | | Password to use to connect to Shadowsocks |
| `SHADOWSOCKS_METHOD` | `chacha20-ietf-poly1305` | `chacha20-ietf-poly1305`, `aes-128-gcm`, `aes-256-gcm` | Method to use for Shadowsocks |
### Tinyproxy
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| `TINYPROXY` | `off` | `on`, `off` | Enable the internal HTTP proxy tinyproxy |
| `TINYPROXY_LOG` | `Info` | `Info`, `Connect`, `Notice`, `Warning`, `Error`, `Critical` | Tinyproxy log level |
| `TINYPROXY_PORT` | `8888` | `1024` to `65535` | Internal port number for Tinyproxy to listen on |
| `TINYPROXY_USER` | | | Username to use to connect to Tinyproxy |
| `TINYPROXY_PASSWORD` | | | Password to use to connect to Tinyproxy |
### System
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| `TZ` | | i.e. `Europe/London` | Specify a timezone to use to have correct log times |
| `UID` | `1000` | | User ID to run as non root and for ownership of files written |
| `GID` | `1000` | | Group ID to run as non root and for ownership of files written |
### Other
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| `PUBLICIP_PERIOD` | `12h` | Valid duration | Period to check for public IP address. Set to `0` to disable. |
## Connect to it
There are various ways to achieve this, depending on your use case.
- <details><summary>Connect containers in the same docker-compose.yml as Gluetun</summary><p>
Add `network_mode: "service:gluetun"` to your *docker-compose.yml* (no need for `depends_on`)
</p></details>
- <details><summary>Connect other containers to Gluetun</summary><p>
Add `--network=container:gluetun` when launching the container, provided Gluetun is already running
</p></details>
- <details><summary>Connect containers from another docker-compose.yml</summary><p>
Add `network_mode: "container:gluetun"` to your *docker-compose.yml*, provided Gluetun is already running
</p></details>
- <details><summary>Connect LAN devices through the built-in HTTP proxy *Tinyproxy* (i.e. with Chrome, Kodi, etc.)</summary><p>
You might want to use Shadowsocks instead which tunnels UDP as well as TCP, whereas Tinyproxy only tunnels TCP.
1. Setup a HTTP proxy client, such as [SwitchyOmega for Chrome](https://chrome.google.com/webstore/detail/proxy-switchyomega/padekgcemlokbadohgkifijomclgjgif?hl=en)
1. Ensure the Gluetun container is launched with:
- port `8888` published `-p 8888:8888/tcp`
- your LAN subnet, i.e. `192.168.1.0/24`, set as `-e EXTRA_SUBNETS=192.168.1.0/24`
1. With your HTTP proxy client, connect to the Docker host (i.e. `192.168.1.10`) on port `8888`. You need to enter your credentials if you set them with `TINYPROXY_USER` and `TINYPROXY_PASSWORD`.
1. If you set `TINYPROXY_LOG` to `Info`, more information will be logged in the Docker logs
</p></details>
- <details><summary>Connect LAN devices through the built-in SOCKS5 proxy *Shadowsocks* (per app, system wide, etc.)</summary><p>
1. Setup a SOCKS5 proxy client, there is a list of [ShadowSocks clients for **all platforms**](https://shadowsocks.org/en/download/clients.html)
- **note** some clients do not tunnel UDP so your DNS queries will be done locally and not through Gluetun and its built in DNS over TLS
- Clients that support such UDP tunneling are, as far as I know:
- iOS: Potatso Lite
- OSX: ShadowsocksX
- Android: Shadowsocks by Max Lv
1. Ensure the Gluetun container is launched with:
- port `8388` published `-p 8388:8388/tcp -p 8388:8388/udp`
- your LAN subnet, i.e. `192.168.1.0/24`, set as `-e EXTRA_SUBNETS=192.168.1.0/24`
1. With your SOCKS5 proxy client
- Enter the Docker host (i.e. `192.168.1.10`) as the server IP
- Enter port TCP (and UDP, if available) `8388` as the server port
- Use the password you have set with `SHADOWSOCKS_PASSWORD`
- Choose the encryption method/algorithm to the method you specified in `SHADOWSOCKS_METHOD`
1. If you set `SHADOWSOCKS_LOG` to `on`, (a lot) more information will be logged in the Docker logs
</p></details>
- <details><summary>Access ports of containers connected to Gluetun</summary><p>
In example, to access port `8000` of container `xyz` and `9000` of container `abc` connected to Gluetun,
publish ports `8000` and `9000` for the Gluetun container and access them as you would with any other container
</p></details>
- <details><summary>Access ports of containers connected to Gluetun, all in the same docker-compose.yml</summary><p>
In example, to access port `8000` of container `xyz` and `9000` of container `abc` connected to Gluetun, publish port `8000` and `9000` for the Gluetun container.
The docker-compose.yml file would look like:
```yml
version: '3.7'
services:
gluetun:
image: qmcgaw/private-internet-access
container_name: gluetun
cap_add:
- NET_ADMIN
environment:
- USER=js89ds7
- PASSWORD=8fd9s239G
ports:
- 8000:8000/tcp
- 9000:9000/tcp
abc:
image: abc
container_name: abc
network_mode: "service:gluetun"
xyz:
image: xyz
container_name: xyz
network_mode: "service:gluetun"
```
</p></details>
## Private Internet Access port forwarding
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`.
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
See [its Wiki page](https://github.com/qdm12/gluetun/wiki/HTTP-control-server)
## Development and contributing
- Contribute with code: see [the Wiki](https://github.com/qdm12/gluetun/wiki/Contributing).
- [The list of existing contributors 👍](https://github.com/qdm12/gluetun/blob/master/.github/CONTRIBUTING.md#Contributors)
- [Github workflows](https://github.com/qdm12/gluetun/actions) to know what's building
- [List of issues and feature requests](https://github.com/qdm12/gluetun/issues)
## License
This repository is under an [MIT license](https://github.com/qdm12/gluetun/master/license)
## Support
Sponsor me on [Github](https://github.com/sponsors/qdm12), donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw) or subscribe to a VPN provider through one of my affiliate links:
[![https://github.com/sponsors/qdm12](https://raw.githubusercontent.com/qdm12/gluetun/master/doc/sponsors.jpg)](https://github.com/sponsors/qdm12)
[![https://www.paypal.me/qmcgaw](https://raw.githubusercontent.com/qdm12/gluetun/master/doc/paypal.jpg)](https://www.paypal.me/qmcgaw)
[![https://windscribe.com/?affid=mh7nyafu](https://raw.githubusercontent.com/qdm12/gluetun/master/doc/windscribe.jpg)](https://windscribe.com/?affid=mh7nyafu)
Feel also free to have a look at [the Kanban board](https://github.com/qdm12/gluetun/projects/1) and [contribute](#Development-and-contributing) to the code or the issues discussion.
Many thanks to @Frepke, @Ralph521, G. Mendez, M. Otmar Weber, J. Perez and A. Cooper for supporting me financially 🥇👍

325
cmd/gluetun/main.go Normal file
View File

@@ -0,0 +1,325 @@
package main
import (
"context"
"fmt"
"os"
"os/signal"
"strings"
"sync"
"syscall"
"time"
"github.com/qdm12/gluetun/internal/alpine"
"github.com/qdm12/gluetun/internal/cli"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/dns"
"github.com/qdm12/gluetun/internal/firewall"
gluetunLogging "github.com/qdm12/gluetun/internal/logging"
"github.com/qdm12/gluetun/internal/openvpn"
"github.com/qdm12/gluetun/internal/params"
"github.com/qdm12/gluetun/internal/publicip"
"github.com/qdm12/gluetun/internal/routing"
"github.com/qdm12/gluetun/internal/server"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/gluetun/internal/shadowsocks"
"github.com/qdm12/gluetun/internal/tinyproxy"
"github.com/qdm12/golibs/command"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/network"
)
func main() {
ctx := context.Background()
os.Exit(_main(ctx, os.Args))
}
func _main(background context.Context, args []string) int {
if len(args) > 1 { // cli operation
var err error
switch args[1] {
case "healthcheck":
err = cli.HealthCheck()
case "clientkey":
err = cli.ClientKey(args[2:])
case "openvpnconfig":
err = cli.OpenvpnConfig()
default:
err = fmt.Errorf("command %q is unknown", args[1])
}
if err != nil {
fmt.Println(err)
return 1
}
return 0
}
ctx, cancel := context.WithCancel(background)
defer cancel()
logger := createLogger()
fatalOnError := makeFatalOnError(logger, cancel)
client := network.NewClient(15 * time.Second)
// Create configurators
fileManager := files.NewFileManager()
alpineConf := alpine.NewConfigurator(fileManager)
ovpnConf := openvpn.NewConfigurator(logger, fileManager)
dnsConf := dns.NewConfigurator(logger, client, fileManager)
routingConf := routing.NewRouting(logger, fileManager)
firewallConf := firewall.NewConfigurator(logger, routingConf, fileManager)
tinyProxyConf := tinyproxy.NewConfigurator(fileManager, logger)
streamMerger := command.NewStreamMerger()
paramsReader := params.NewReader(logger, fileManager)
fmt.Println(gluetunLogging.Splash(
paramsReader.GetVersion(),
paramsReader.GetVcsRef(),
paramsReader.GetBuildDate()))
printVersions(ctx, logger, map[string]func(ctx context.Context) (string, error){
"OpenVPN": ovpnConf.Version,
"Unbound": dnsConf.Version,
"IPtables": firewallConf.Version,
"TinyProxy": tinyProxyConf.Version,
})
allSettings, err := settings.GetAllSettings(paramsReader)
fatalOnError(err)
logger.Info(allSettings.String())
// Should never change
uid, gid := allSettings.System.UID, allSettings.System.GID
err = alpineConf.CreateUser("nonrootuser", uid)
fatalOnError(err)
err = fileManager.SetOwnership("/etc/unbound", uid, gid)
fatalOnError(err)
err = fileManager.SetOwnership("/etc/tinyproxy", uid, gid)
fatalOnError(err)
if allSettings.Firewall.Debug {
firewallConf.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 {
logger.Warn(err)
err = ovpnConf.CreateTUN()
fatalOnError(err)
}
connectedCh := make(chan struct{})
signalConnected := func() {
connectedCh <- struct{}{}
}
defer close(connectedCh)
go collectStreamLines(ctx, streamMerger, logger, signalConnected)
if allSettings.Firewall.Enabled {
err := firewallConf.SetEnabled(ctx, true) // disabled by default
fatalOnError(err)
}
err = firewallConf.SetAllowedSubnets(ctx, allSettings.Firewall.AllowedSubnets)
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,
ovpnConf, firewallConf, logger, client, fileManager, streamMerger, fatalOnError)
restartOpenvpn := openvpnLooper.Restart
portForward := openvpnLooper.PortForward
getOpenvpnSettings := openvpnLooper.GetSettings
getPortForwarded := openvpnLooper.GetPortForwarded
// wait for restartOpenvpn
go openvpnLooper.Run(ctx, wg)
unboundLooper := dns.NewLooper(dnsConf, allSettings.DNS, logger, streamMerger, uid, gid)
restartUnbound := unboundLooper.Restart
// wait for restartUnbound
go unboundLooper.Run(ctx, wg)
publicIPLooper := publicip.NewLooper(client, logger, fileManager, allSettings.System.IPStatusFilepath, allSettings.PublicIPPeriod, uid, gid)
restartPublicIP := publicIPLooper.Restart
setPublicIPPeriod := publicIPLooper.SetPeriod
go publicIPLooper.Run(ctx)
go publicIPLooper.RunRestartTicker(ctx)
setPublicIPPeriod(allSettings.PublicIPPeriod) // call after RunRestartTicker
tinyproxyLooper := tinyproxy.NewLooper(tinyProxyConf, firewallConf, allSettings.TinyProxy, logger, streamMerger, uid, gid, defaultInterface)
restartTinyproxy := tinyproxyLooper.Restart
go tinyproxyLooper.Run(ctx, wg)
shadowsocksLooper := shadowsocks.NewLooper(firewallConf, allSettings.ShadowSocks, logger, defaultInterface)
restartShadowsocks := shadowsocksLooper.Restart
go shadowsocksLooper.Run(ctx, wg)
if allSettings.TinyProxy.Enabled {
restartTinyproxy()
}
if allSettings.ShadowSocks.Enabled {
restartShadowsocks()
}
go func() {
var restartTickerContext context.Context
var restartTickerCancel context.CancelFunc = func() {}
for {
select {
case <-ctx.Done():
restartTickerCancel()
return
case <-connectedCh: // blocks until openvpn is connected
restartTickerCancel()
restartTickerContext, restartTickerCancel = context.WithCancel(ctx)
go unboundLooper.RunRestartTicker(restartTickerContext)
onConnected(allSettings, logger, routingConf, portForward, restartUnbound, restartPublicIP)
}
}
}()
httpServer := server.New("0.0.0.0:8000", logger, restartOpenvpn, restartUnbound, getOpenvpnSettings, getPortForwarded)
go httpServer.Run(ctx, wg)
// Start openvpn for the first time
restartOpenvpn()
signalsCh := make(chan os.Signal, 1)
signal.Notify(signalsCh,
syscall.SIGINT,
syscall.SIGTERM,
os.Interrupt,
)
shutdownErrorsCount := 0
select {
case signal := <-signalsCh:
logger.Warn("Caught OS signal %s, shutting down", signal)
cancel()
case <-ctx.Done():
logger.Warn("context canceled, shutting down")
}
logger.Info("Clearing ip status file %s", allSettings.System.IPStatusFilepath)
if err := fileManager.Remove(string(allSettings.System.IPStatusFilepath)); err != nil {
logger.Error(err)
shutdownErrorsCount++
}
if allSettings.OpenVPN.Provider.PortForwarding.Enabled {
logger.Info("Clearing forwarded port status file %s", allSettings.OpenVPN.Provider.PortForwarding.Filepath)
if err := fileManager.Remove(string(allSettings.OpenVPN.Provider.PortForwarding.Filepath)); err != nil {
logger.Error(err)
shutdownErrorsCount++
}
}
waiting, waited := context.WithTimeout(context.Background(), time.Second)
go func() {
defer waited()
wg.Wait()
}()
<-waiting.Done()
if waiting.Err() == context.DeadlineExceeded {
if shutdownErrorsCount > 0 {
logger.Warn("Shutdown had %d errors", shutdownErrorsCount)
}
logger.Warn("Shutdown timed out")
return 1
}
if shutdownErrorsCount > 0 {
logger.Warn("Shutdown had %d errors")
return 1
}
logger.Info("Shutdown successful")
return 0
}
func makeFatalOnError(logger logging.Logger, cancel context.CancelFunc) func(err error) {
return func(err error) {
if err != nil {
logger.Error(err)
cancel()
}
}
}
func createLogger() logging.Logger {
logger, err := logging.NewLogger(logging.ConsoleEncoding, logging.InfoLevel, -1)
if err != nil {
panic(err)
}
return logger
}
func printVersions(ctx context.Context, logger logging.Logger, versionFunctions map[string]func(ctx context.Context) (string, error)) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
for name, f := range versionFunctions {
version, err := f(ctx)
if err != nil {
logger.Error(err)
} else {
logger.Info("%s version: %s", name, version)
}
}
}
func collectStreamLines(ctx context.Context, streamMerger command.StreamMerger, logger logging.Logger, signalConnected func()) {
// Blocking line merging paramsReader for all programs: openvpn, tinyproxy, unbound and shadowsocks
logger.Info("Launching standard output merger")
streamMerger.CollectLines(ctx, func(line string) {
line, level := gluetunLogging.PostProcessLine(line)
if line == "" {
return
}
switch level {
case logging.InfoLevel:
logger.Info(line)
case logging.WarnLevel:
logger.Warn(line)
case logging.ErrorLevel:
logger.Error(line)
}
if strings.Contains(line, "Initialization Sequence Completed") {
signalConnected()
}
}, func(err error) {
logger.Warn(err)
})
}
func onConnected(allSettings settings.Settings, logger logging.Logger, routingConf routing.Routing,
portForward, restartUnbound, restartPublicIP func(),
) {
restartUnbound()
restartPublicIP()
if allSettings.OpenVPN.Provider.PortForwarding.Enabled {
time.AfterFunc(5*time.Second, portForward)
}
defaultInterface, _, err := routingConf.DefaultRoute()
if err != nil {
logger.Warn(err)
} else {
vpnGatewayIP, err := routingConf.VPNGatewayIP(defaultInterface)
if err != nil {
logger.Warn(err)
} else {
logger.Info("Gateway VPN IP address: %s", vpnGatewayIP)
}
}
}

365
cmd/hostfinder/main.go Normal file
View File

@@ -0,0 +1,365 @@
package main
import (
"context"
"flag"
"fmt"
"net"
"os"
"sort"
)
func main() {
ctx := context.Background()
os.Exit(_main(ctx))
}
func _main(ctx context.Context) int {
fmt.Println("Host finder for Cyberghost")
resolverAddress := flag.String("resolver", "1.1.1.1", "DNS Resolver IP address to use")
flag.Parse()
resolver := newResolver(*resolverAddress)
lookupIP := newLookupIP(resolver)
const domain = "cg-dialup.net"
groups := getCyberghostGroups()
countryCodes := getCountryCodes()
type result struct {
groupName string
region string
subdomain string
exists bool
}
resultsChannel := make(chan result)
const maxGoroutines = 10
guard := make(chan struct{}, maxGoroutines)
fmt.Print("Subdomains found: ")
for groupName, groupID := range groups {
for country, countryCode := range countryCodes {
go func(groupName, groupID, country, countryCode string) {
r := result{
region: country,
groupName: groupName,
subdomain: fmt.Sprintf("%s-%s", groupID, countryCode),
}
fqdn := fmt.Sprintf("%s.%s", r.subdomain, domain)
guard <- struct{}{}
ips, err := lookupIP(ctx, fqdn)
<-guard
if err == nil && len(ips) > 0 {
r.exists = true
}
resultsChannel <- r
}(groupName, groupID, country, countryCode)
}
}
results := make([]result, len(groups)*len(countryCodes))
for i := range results {
results[i] = <-resultsChannel
fmt.Printf("%s ", results[i].subdomain)
}
fmt.Print("\n\n")
sort.Slice(results, func(i, j int) bool {
return results[i].region < results[j].region
})
for _, r := range results {
if r.exists {
// Use in resolver program
fmt.Printf("{subdomain: %q, region: %q, group: %q},\n", r.subdomain, r.region, r.groupName)
}
}
return 0
}
func newResolver(ip string) *net.Resolver {
return &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
d := net.Dialer{}
return d.DialContext(ctx, "udp", net.JoinHostPort(ip, "53"))
},
}
}
type lookupIPFunc func(ctx context.Context, host string) (ips []net.IP, err error)
func newLookupIP(r *net.Resolver) lookupIPFunc {
return func(ctx context.Context, host string) (ips []net.IP, err error) {
addresses, err := r.LookupIPAddr(ctx, host)
if err != nil {
return nil, err
}
ips = make([]net.IP, len(addresses))
for i := range addresses {
ips[i] = addresses[i].IP
}
return ips, nil
}
}
func getCyberghostGroups() map[string]string {
return map[string]string{
"Premium UDP Europe": "87-1",
"Premium UDP USA": "94-1",
"Premium UDP Asia": "95-1",
"NoSpy UDP Europe": "87-8",
"Premium TCP Europe": "97-1",
"Premium TCP USA": "93-1",
"Premium TCP Asia": "96-1",
"NoSpy TCP Europe": "97-8",
}
}
func getCountryCodes() map[string]string {
return map[string]string{
"Afghanistan": "af",
"Aland Islands": "ax",
"Albania": "al",
"Algeria": "dz",
"American Samoa": "as",
"Andorra": "ad",
"Angola": "ao",
"Anguilla": "ai",
"Antarctica": "aq",
"Antigua and Barbuda": "ag",
"Argentina": "ar",
"Armenia": "am",
"Aruba": "aw",
"Australia": "au",
"Austria": "at",
"Azerbaijan": "az",
"Bahamas": "bs",
"Bahrain": "bh",
"Bangladesh": "bd",
"Barbados": "bb",
"Belarus": "by",
"Belgium": "be",
"Belize": "bz",
"Benin": "bj",
"Bermuda": "bm",
"Bhutan": "bt",
"Bolivia": "bo",
"Bonaire": "bq",
"Bosnia and Herzegovina": "ba",
"Botswana": "bw",
"Bouvet Island": "bv",
"Brazil": "br",
"British Indian Ocean Territory": "io",
"British Virgin Islands": "vg",
"Brunei Darussalam": "bn",
"Bulgaria": "bg",
"Burkina Faso": "bf",
"Burundi": "bi",
"Cambodia": "kh",
"Cameroon": "cm",
"Canada": "ca",
"Cape Verde": "cv",
"Cayman Islands": "ky",
"Central African Republic": "cf",
"Chad": "td",
"Chile": "cl",
"China": "cn",
"Christmas Island": "cx",
"Cocos Islands": "cc",
"Colombia": "co",
"Comoros": "km",
"Congo": "cg",
"Cook Islands": "ck",
"Costa Rica": "cr",
"Cote d'Ivoire": "ci",
"Croatia": "hr",
"Cuba": "cu",
"Curacao": "cw",
"Cyprus": "cy",
"Czech Republic": "cz",
"Democratic Republic of the Congo": "cd",
"Denmark": "dk",
"Djibouti": "dj",
"Dominica": "dm",
"Dominican Republic": "do",
"Ecuador": "ec",
"Egypt": "eg",
"El Salvador": "sv",
"Equatorial Guinea": "gq",
"Eritrea": "er",
"Estonia": "ee",
"Ethiopia": "et",
"Falkland Islands": "fk",
"Faroe Islands": "fo",
"Fiji": "fj",
"Finland": "fi",
"France": "fr",
"French Guiana": "gf",
"French Polynesia": "pf",
"French Southern Territories": "tf",
"Gabon": "ga",
"Gambia": "gm",
"Georgia": "ge",
"Germany": "de",
"Ghana": "gh",
"Gibraltar": "gi",
"Greece": "gr",
"Greenland": "gl",
"Grenada": "gd",
"Guadeloupe": "gp",
"Guam": "gu",
"Guatemala": "gt",
"Guernsey": "gg",
"Guinea-Bissau": "gw",
"Guinea": "gn",
"Guyana": "gy",
"Haiti": "ht",
"Heard Island and McDonald Islands": "hm",
"Honduras": "hn",
"Hong Kong": "hk",
"Hungary": "hu",
"Iceland": "is",
"India": "in",
"Indonesia": "id",
"Iran": "ir",
"Iraq": "iq",
"Ireland": "ie",
"Isle of Man": "im",
"Israel": "il",
"Italy": "it",
"Jamaica": "jm",
"Japan": "jp",
"Jersey": "je",
"Jordan": "jo",
"Kazakhstan": "kz",
"Kenya": "ke",
"Kiribati": "ki",
"Korea": "kr",
"Kuwait": "kw",
"Kyrgyzstan": "kg",
"Lao People's Democratic Republic": "la",
"Latvia": "lv",
"Lebanon": "lb",
"Lesotho": "ls",
"Liberia": "lr",
"Libya": "ly",
"Liechtenstein": "li",
"Lithuania": "lt",
"Luxembourg": "lu",
"Macao": "mo",
"Macedonia": "mk",
"Madagascar": "mg",
"Malawi": "mw",
"Malaysia": "my",
"Maldives": "mv",
"Mali": "ml",
"Malta": "mt",
"Marshall Islands": "mh",
"Martinique": "mq",
"Mauritania": "mr",
"Mauritius": "mu",
"Mayotte": "yt",
"Mexico": "mx",
"Micronesia": "fm",
"Moldova": "md",
"Monaco": "mc",
"Mongolia": "mn",
"Montenegro": "me",
"Montserrat": "ms",
"Morocco": "ma",
"Mozambique": "mz",
"Myanmar": "mm",
"Namibia": "na",
"Nauru": "nr",
"Nepal": "np",
"Netherlands": "nl",
"New Caledonia": "nc",
"New Zealand": "nz",
"Nicaragua": "ni",
"Niger": "ne",
"Nigeria": "ng",
"Niue": "nu",
"Norfolk Island": "nf",
"Northern Mariana Islands": "mp",
"Norway": "no",
"Oman": "om",
"Pakistan": "pk",
"Palau": "pw",
"Palestine, State of": "ps",
"Panama": "pa",
"Papua New Guinea": "pg",
"Paraguay": "py",
"Peru": "pe",
"Philippines": "ph",
"Pitcairn": "pn",
"Poland": "pl",
"Portugal": "pt",
"Puerto Rico": "pr",
"Qatar": "qa",
"Reunion": "re",
"Romania": "ro",
"Russian Federation": "ru",
"Rwanda": "rw",
"Saint Barthelemy": "bl",
"Saint Helena": "sh",
"Saint Kitts and Nevis": "kn",
"Saint Lucia": "lc",
"Saint Martin": "mf",
"Saint Pierre and Miquelon": "pm",
"Saint Vincent and the Grenadines": "vc",
"Samoa": "ws",
"San Marino": "sm",
"Sao Tome and Principe": "st",
"Saudi Arabia": "sa",
"Senegal": "sn",
"Serbia": "rs",
"Seychelles": "sc",
"Sierra Leone": "sl",
"Singapore": "sg",
"Sint Maarten": "sx",
"Slovakia": "sk",
"Slovenia": "si",
"Solomon Islands": "sb",
"Somalia": "so",
"South Africa": "za",
"South Georgia and the South Sandwich Islands": "gs",
"South Sudan": "ss",
"Spain": "es",
"Sri Lanka": "lk",
"Sudan": "sd",
"Suriname": "sr",
"Svalbard and Jan Mayen": "sj",
"Swaziland": "sz",
"Sweden": "se",
"Switzerland": "ch",
"Syrian Arab Republic": "sy",
"Taiwan": "tw",
"Tajikistan": "tj",
"Tanzania": "tz",
"Thailand": "th",
"Timor-Leste": "tl",
"Togo": "tg",
"Tokelau": "tk",
"Tonga": "to",
"Trinidad and Tobago": "tt",
"Tunisia": "tn",
"Turkey": "tr",
"Turkmenistan": "tm",
"Turks and Caicos Islands": "tc",
"Tuvalu": "tv",
"Uganda": "ug",
"Ukraine": "ua",
"United Arab Emirates": "ae",
"United Kingdom": "gb",
"United States Minor Outlying Islands": "um",
"United States": "us",
"Uruguay": "uy",
"US Virgin Islands": "vi",
"Uzbekistan": "uz",
"Vanuatu": "vu",
"Vatican City State": "va",
"Venezuela": "ve",
"Vietnam": "vn",
"Wallis and Futuna": "wf",
"Western Sahara": "eh",
"Yemen": "ye",
"Zambia": "zm",
"Zimbabwe": "zw",
}
}

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
}

106
cmd/mapper/main.go Normal file
View File

@@ -0,0 +1,106 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"net"
"net/http"
"os"
"sort"
"strconv"
"strings"
"time"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/network"
)
func main() {
os.Exit(_main())
}
func _main() int {
provider := flag.String("provider", "nordvpn", "VPN provider to map region to IP addresses using their API, can be 'nordvpn'")
flag.Parse()
client := network.NewClient(30 * time.Second) // big file so 30 seconds
switch *provider {
case "nordvpn":
servers, ignoredServers, err := nordvpn(client)
if err != nil {
fmt.Println(err)
return 1
}
for _, server := range servers {
fmt.Printf(
"{Region: %q, Number: %d, TCP: %t, UDP: %t, IP: net.IP{%s}},\n",
server.Region, server.Number, server.TCP, server.UDP, strings.ReplaceAll(server.IP.String(), ".", ", "),
)
}
fmt.Print("\n\n")
for _, serverName := range ignoredServers {
fmt.Printf("ignored server %q because it does not support both UDP and TCP\n", serverName)
}
default:
fmt.Printf("Provider %q is not supported\n", *provider)
return 1
}
return 0
}
func nordvpn(client network.Client) (servers []models.NordvpnServer, ignoredServers []string, err error) {
content, status, err := client.GetContent("https://nordvpn.com/api/server")
if err != nil {
return nil, nil, err
} else if status != http.StatusOK {
return nil, nil, fmt.Errorf("HTTP status %d from NordVPN API", status)
}
response := []struct {
IPAddress string `json:"ip_address"`
Name string `json:"name"`
Country string `json:"country"`
Features struct {
UDP bool `json:"openvpn_udp"`
TCP bool `json:"openvpn_tcp"`
} `json:"features"`
}{}
if err := json.Unmarshal(content, &response); err != nil {
return nil, nil, err
}
for _, element := range response {
if !element.Features.TCP && !element.Features.UDP {
ignoredServers = append(ignoredServers, element.Name)
}
ip := net.ParseIP(element.IPAddress)
if ip == nil {
return nil, nil, fmt.Errorf("IP address %q is not valid for server %q", element.IPAddress, element.Name)
}
i := strings.IndexRune(element.Name, '#')
if i < 0 {
return nil, nil, fmt.Errorf("No ID in server name %q", element.Name)
}
idString := element.Name[i+1:]
idUint64, err := strconv.ParseUint(idString, 10, 16)
if err != nil {
return nil, nil, fmt.Errorf("Bad ID in server name %q", element.Name)
}
id := uint16(idUint64)
server := models.NordvpnServer{
Region: element.Country,
Number: id,
IP: ip,
TCP: element.Features.TCP,
UDP: element.Features.UDP,
}
servers = append(servers, server)
}
sort.Slice(servers, func(i, j int) bool {
if servers[i].Region == servers[j].Region {
return servers[i].Number < servers[j].Number
}
return servers[i].Region < servers[j].Region
})
return servers, ignoredServers, nil
}

152
cmd/ovpnparser/main.go Normal file
View File

@@ -0,0 +1,152 @@
package main
import (
"archive/zip"
"bytes"
"flag"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"path/filepath"
"sort"
"strings"
"time"
"github.com/qdm12/golibs/network"
)
func main() {
os.Exit(_main())
}
// Find subdomains from .ovpn files contained in a .zip file
func _main() int {
provider := flag.String("provider", "surfshark", "VPN provider to parse openvpn files for, can be 'surfshark' or 'vyprvpn")
flag.Parse()
var urls []string
var suffix string
switch *provider {
case "surfshark":
urls = []string{
"https://account.surfshark.com/api/v1/server/configurations",
"https://v2uploads.zopim.io/p/2/L/p2LbwLkvfQoSdzOl6VEltzQA6StiZqrs/12500634259669c77012765139bcfe4f4c90db1e.zip",
}
suffix = ".prod.surfshark.com"
case "vyprvpn":
urls = []string{
"https://support.vyprvpn.com/hc/article_attachments/360052617332/Vypr_OpenVPN_20200320.zip",
}
suffix = ".vyprvpn.com"
default:
fmt.Printf("Provider %q is not supported\n", *provider)
return 1
}
contents, err := fetchAndExtractFiles(urls...)
if err != nil {
fmt.Println(err)
return 1
}
uniqueSubdomainsToFilename := make(map[string]string)
for fileName, content := range contents {
subdomain, err := extractInformation(content, suffix)
if err != nil {
fmt.Println(err)
return 1
} else if len(subdomain) > 0 {
fileName = strings.TrimSuffix(fileName, ".ovpn")
fileName = strings.ReplaceAll(fileName, " - ", " ")
uniqueSubdomainsToFilename[subdomain] = fileName
}
}
type subdomainFilename struct {
subdomain string
fileName string
}
subdomains := make([]subdomainFilename, len(uniqueSubdomainsToFilename))
i := 0
for subdomain, fileName := range uniqueSubdomainsToFilename {
subdomains[i] = subdomainFilename{
subdomain: subdomain,
fileName: fileName,
}
i++
}
sort.Slice(subdomains, func(i, j int) bool {
return subdomains[i].subdomain < subdomains[j].subdomain
})
fmt.Println("Subdomain Filename")
for i := range subdomains {
fmt.Printf("%s %s\n", subdomains[i].subdomain, subdomains[i].fileName)
}
return 0
}
func fetchAndExtractFiles(urls ...string) (contents map[string][]byte, err error) {
client := network.NewClient(10 * time.Second)
contents = make(map[string][]byte)
for _, url := range urls {
zipBytes, status, err := client.GetContent(url)
if err != nil {
return nil, err
} else if status != http.StatusOK {
return nil, fmt.Errorf("Getting %s results in HTTP status code %d", url, status)
}
newContents, err := zipExtractAll(zipBytes)
if err != nil {
return nil, err
}
for fileName, content := range newContents {
contents[fileName] = content
}
}
return contents, nil
}
func zipExtractAll(zipBytes []byte) (contents map[string][]byte, err error) {
r, err := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))
if err != nil {
return nil, err
}
contents = map[string][]byte{}
for _, zf := range r.File {
fileName := filepath.Base(zf.Name)
if !strings.HasSuffix(fileName, ".ovpn") {
continue
}
f, err := zf.Open()
if err != nil {
return nil, err
}
defer f.Close()
contents[fileName], err = ioutil.ReadAll(f)
if err != nil {
return nil, err
}
if err := f.Close(); err != nil {
return nil, err
}
}
return contents, nil
}
func extractInformation(content []byte, suffix string) (subdomain string, err error) {
lines := strings.Split(string(content), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "remote ") {
words := strings.Fields(line)
if len(words) < 2 {
return "", fmt.Errorf("not enough words on line %q", line)
}
host := words[1]
if net.ParseIP(host) != nil {
return "", nil // ignore IP addresses
}
return strings.TrimSuffix(host, suffix), nil
}
}
return "", fmt.Errorf("could not find remote line in: %s", string(content))
}

919
cmd/resolver/main.go Normal file
View File

@@ -0,0 +1,919 @@
package main
import (
"bytes"
"context"
"flag"
"fmt"
"net"
"os"
"sort"
"strings"
)
func main() {
ctx := context.Background()
os.Exit(_main(ctx))
}
func _main(ctx context.Context) int {
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', 'vyprvpn' or 'purevpn'")
region := flag.String("region", "all", "Comma separated list of VPN provider region names to resolve for, use 'all' to resolve all")
flag.Parse()
resolver := newResolver(*resolverAddress)
lookupIP := newLookupIP(resolver)
var domain string
var servers []server
switch *provider {
case "pia":
domain = "privateinternetaccess.com"
servers = piaServers()
case "windscribe":
domain = "windscribe.com"
servers = windscribeServers()
case "surfshark":
domain = "prod.surfshark.com"
servers = surfsharkServers()
case "cyberghost":
domain = "cg-dialup.net"
servers = cyberghostServers()
case "vyprvpn":
domain = "vyprvpn.com"
servers = vyprvpnServers()
case "purevpn":
domain = "pointtoserver.com"
servers = purevpnServers()
default:
fmt.Printf("Provider %q is not supported\n", *provider)
return 1
}
if *region != "all" {
regions := strings.Split(*region, ",")
uniqueRegions := make(map[string]struct{})
for _, r := range regions {
uniqueRegions[r] = struct{}{}
}
for i := range servers {
if _, ok := uniqueRegions[servers[i].region]; !ok {
servers[i] = servers[len(servers)-1]
servers = servers[:len(servers)-1]
}
}
}
stringChannel := make(chan string)
errorChannel := make(chan error)
const maxGoroutines = 10
guard := make(chan struct{}, maxGoroutines)
for _, s := range servers {
go func(s server) {
guard <- struct{}{}
ips, err := resolveRepeat(ctx, lookupIP, s.subdomain+"."+domain, 3)
<-guard
if err != nil {
errorChannel <- err
return
}
stringChannel <- formatLine(*provider, s, ips)
}(s)
}
var lines []string
var errors []error
for range servers {
select {
case err := <-errorChannel:
errors = append(errors, err)
case s := <-stringChannel:
lines = append(lines, s)
}
}
sort.Slice(lines, func(i, j int) bool {
return lines[i] < lines[j]
})
for _, s := range lines {
fmt.Println(s)
}
if len(errors) > 0 {
fmt.Printf("\n%d errors occurred, described below\n\n", len(errors))
for _, err := range errors {
fmt.Println(err)
}
return 1
}
return 0
}
func formatLine(provider string, s server, ips []net.IP) string {
ipStrings := make([]string, len(ips))
for i := range ips {
ipStrings[i] = fmt.Sprintf("{%s}", strings.ReplaceAll(ips[i].String(), ".", ", "))
}
ipString := strings.Join(ipStrings, ", ")
switch provider {
case "pia":
return fmt.Sprintf(
"{Region: %q, IPs: []net.IP{%s}},",
s.region, ipString,
)
case "windscribe":
return fmt.Sprintf(
"{Region: %q, IPs: []net.IP{%s}},",
s.region, ipString,
)
case "surfshark":
return fmt.Sprintf(
"{Region: %q, IPs: []net.IP{%s}},",
s.region, ipString,
)
case "cyberghost":
return fmt.Sprintf(
"{Region: %q, Group: %q, IPs: []net.IP{%s}},",
s.region, s.group, ipString,
)
case "vyprvpn":
return fmt.Sprintf(
"{Region: %q, IPs: []net.IP{%s}},",
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 ""
}
type lookupIPFunc func(ctx context.Context, host string) (ips []net.IP, err error)
func newLookupIP(r *net.Resolver) lookupIPFunc {
return func(ctx context.Context, host string) (ips []net.IP, err error) {
addresses, err := r.LookupIPAddr(ctx, host)
if err != nil {
return nil, err
}
ips = make([]net.IP, len(addresses))
for i := range addresses {
ips[i] = addresses[i].IP
}
return ips, nil
}
}
func newResolver(ip string) *net.Resolver {
return &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
d := net.Dialer{}
return d.DialContext(ctx, "udp", net.JoinHostPort(ip, "53"))
},
}
}
func resolveRepeat(ctx context.Context, lookupIP lookupIPFunc, host string, n int) (ips []net.IP, err error) {
for i := 0; i < n; i++ {
newIPs, err := lookupIP(ctx, host)
if err != nil {
return nil, err
}
ips = append(ips, newIPs...)
}
return uniqueSortedIPs(ips), nil
}
func uniqueSortedIPs(ips []net.IP) []net.IP {
uniqueIPs := make(map[string]struct{})
for _, ip := range ips {
uniqueIPs[ip.String()] = struct{}{}
}
ips = make([]net.IP, len(uniqueIPs))
i := 0
for ip := range uniqueIPs {
ips[i] = net.ParseIP(ip)
i++
}
sort.Slice(ips, func(i, j int) bool {
return bytes.Compare(ips[i], ips[j]) < 0
})
return ips
}
type server struct {
subdomain string
region string
group string // only for cyberghost
country string // only for purevpn
city string // only for purevpn
}
func piaServers() []server {
return []server{
{subdomain: "au-melbourne", region: "AU Melbourne"},
{subdomain: "au-perth", region: "AU Perth"},
{subdomain: "au-sydney", region: "AU Sydney"},
{subdomain: "austria", region: "Austria"},
{subdomain: "belgium", region: "Belgium"},
{subdomain: "ca-montreal", region: "CA Montreal"},
{subdomain: "ca-toronto", region: "CA Toronto"},
{subdomain: "ca-vancouver", region: "CA Vancouver"},
{subdomain: "czech", region: "Czech Republic"},
{subdomain: "de-berlin", region: "DE Berlin"},
{subdomain: "de-frankfurt", region: "DE Frankfurt"},
{subdomain: "denmark", region: "Denmark"},
{subdomain: "fi", region: "Finlan"},
{subdomain: "france", region: "France"},
{subdomain: "hungary", region: "Hungary"},
{subdomain: "in", region: "India"},
{subdomain: "ireland", region: "Ireland"},
{subdomain: "israel", region: "Israel"},
{subdomain: "italy", region: "Italy"},
{subdomain: "japan", region: "Japan"},
{subdomain: "lu", region: "Luxembourg"},
{subdomain: "mexico", region: "Mexico"},
{subdomain: "nl", region: "Netherlands"},
{subdomain: "nz", region: "New Zealand"},
{subdomain: "no", region: "Norway"},
{subdomain: "poland", region: "Poland"},
{subdomain: "ro", region: "Romania"},
{subdomain: "sg", region: "Singapore"},
{subdomain: "spain", region: "Spain"},
{subdomain: "sweden", region: "Sweden"},
{subdomain: "swiss", region: "Switzerland"},
{subdomain: "ae", region: "UAE"},
{subdomain: "uk-london", region: "UK London"},
{subdomain: "uk-manchester", region: "UK Manchester"},
{subdomain: "uk-southampton", region: "UK Southampton"},
{subdomain: "us-atlanta", region: "US Atlanta"},
{subdomain: "us-california", region: "US California"},
{subdomain: "us-chicago", region: "US Chicago"},
{subdomain: "us-dallas", region: "US Dallas"},
{subdomain: "us-denver", region: "US Denver"},
{subdomain: "us-east", region: "US East"},
{subdomain: "us-florida", region: "US Florida"},
{subdomain: "us-houston", region: "US Houston"},
{subdomain: "us-lasvegas", region: "US Las Vegas"},
{subdomain: "us-newyorkcity", region: "US New York City"},
{subdomain: "us-seattle", region: "US Seattle"},
{subdomain: "us-siliconvalley", region: "US Silicon Valley"},
{subdomain: "us-washingtondc", region: "US Washington DC"},
{subdomain: "us-west", region: "US West"},
}
}
func windscribeServers() []server {
return []server{
{subdomain: "al", region: "Albania"},
{subdomain: "ar", region: "Argentina"},
{subdomain: "au", region: "Australia"},
{subdomain: "at", region: "Austria"},
{subdomain: "az", region: "Azerbaijan"},
{subdomain: "be", region: "Belgium"},
{subdomain: "ba", region: "Bosnia"},
{subdomain: "br", region: "Brazil"},
{subdomain: "bg", region: "Bulgaria"},
{subdomain: "ca", region: "Canada East"},
{subdomain: "ca-west", region: "Canada West"},
{subdomain: "co", region: "Colombia"},
{subdomain: "hr", region: "Croatia"},
{subdomain: "cy", region: "Cyprus"},
{subdomain: "cz", region: "Czech republic"},
{subdomain: "dk", region: "Denmark"},
{subdomain: "ee", region: "Estonia"},
{subdomain: "aq", region: "Fake antarctica"},
{subdomain: "fi", region: "Finland"},
{subdomain: "fr", region: "France"},
{subdomain: "ge", region: "Georgia"},
{subdomain: "de", region: "Germany"},
{subdomain: "gr", region: "Greece"},
{subdomain: "hk", region: "Hong kong"},
{subdomain: "hu", region: "Hungary"},
{subdomain: "is", region: "Iceland"},
{subdomain: "in", region: "India"},
{subdomain: "id", region: "Indonesia"},
{subdomain: "ie", region: "Ireland"},
{subdomain: "il", region: "Israel"},
{subdomain: "it", region: "Italy"},
{subdomain: "jp", region: "Japan"},
{subdomain: "lv", region: "Latvia"},
{subdomain: "lt", region: "Lithuania"},
{subdomain: "mk", region: "Macedonia"},
{subdomain: "my", region: "Malaysia"},
{subdomain: "mx", region: "Mexico"},
{subdomain: "md", region: "Moldova"},
{subdomain: "nl", region: "Netherlands"},
{subdomain: "nz", region: "New zealand"},
{subdomain: "no", region: "Norway"},
{subdomain: "ph", region: "Philippines"},
{subdomain: "pl", region: "Poland"},
{subdomain: "pt", region: "Portugal"},
{subdomain: "ro", region: "Romania"},
{subdomain: "ru", region: "Russia"},
{subdomain: "rs", region: "Serbia"},
{subdomain: "sg", region: "Singapore"},
{subdomain: "sk", region: "Slovakia"},
{subdomain: "si", region: "Slovenia"},
{subdomain: "za", region: "South Africa"},
{subdomain: "kr", region: "South Korea"},
{subdomain: "es", region: "Spain"},
{subdomain: "se", region: "Sweden"},
{subdomain: "ch", region: "Switzerland"},
{subdomain: "th", region: "Thailand"},
{subdomain: "tn", region: "Tunisia"},
{subdomain: "tr", region: "Turkey"},
{subdomain: "ua", region: "Ukraine"},
{subdomain: "ae", region: "United Arab Emirates"},
{subdomain: "uk", region: "United Kingdom"},
{subdomain: "us-central", region: "US Central"},
{subdomain: "us-east", region: "US East"},
{subdomain: "us-west", region: "US West"},
{subdomain: "vn", region: "Vietnam"},
{subdomain: "wf-ca", region: "Windflix CA"},
{subdomain: "wf-jp", region: "Windflix JP"},
{subdomain: "wf-uk", region: "Windflix UK"},
{subdomain: "wf-us", region: "Windflix US"},
}
}
func surfsharkServers() []server {
return []server{
{subdomain: "ae-dub", region: "United Arab Emirates"},
{subdomain: "al-tia", region: "Albania"},
{subdomain: "at-vie", region: "Austria"},
{subdomain: "au-adl", region: "Australia Adelaide"},
{subdomain: "au-bne", region: "Australia Brisbane"},
{subdomain: "au-mel", region: "Australia Melbourne"},
{subdomain: "au-per", region: "Australia Perth"},
{subdomain: "au-syd", region: "Australia Sydney"},
{subdomain: "au-us", region: "Australia US"},
{subdomain: "az-bak", region: "Azerbaijan"},
{subdomain: "ba-sjj", region: "Bosnia and Herzegovina"},
{subdomain: "be-bru", region: "Belgium"},
{subdomain: "bg-sof", region: "Bulgaria"},
{subdomain: "br-sao", region: "Brazil"},
{subdomain: "ca-mon", region: "Canada Montreal"},
{subdomain: "ca-tor", region: "Canada Toronto"},
{subdomain: "ca-us", region: "Canada US"},
{subdomain: "ca-van", region: "Canada Vancouver"},
{subdomain: "ch-zur", region: "Switzerland"},
{subdomain: "cl-san", region: "Chile"},
{subdomain: "co-bog", region: "Colombia"},
{subdomain: "cr-sjn", region: "Costa Rica"},
{subdomain: "cy-nic", region: "Cyprus"},
{subdomain: "cz-prg", region: "Czech Republic"},
{subdomain: "de-ber", region: "Germany Berlin"},
{subdomain: "de-fra", region: "Germany Frankfurt am Main"},
{subdomain: "de-fra-st001", region: "Germany Frankfurt am Main st001"},
{subdomain: "de-fra-st002", region: "Germany Frankfurt am Main st002"},
{subdomain: "de-fra-st003", region: "Germany Frankfurt am Main st003"},
{subdomain: "de-muc", region: "Germany Munich"},
{subdomain: "de-nue", region: "Germany Nuremberg"},
{subdomain: "de-sg", region: "Germany Singapour"},
{subdomain: "de-uk", region: "Germany UK"},
{subdomain: "dk-cph", region: "Denmark"},
{subdomain: "ee-tll", region: "Estonia"},
{subdomain: "es-bcn", region: "Spain Barcelona"},
{subdomain: "es-mad", region: "Spain Madrid"},
{subdomain: "es-vlc", region: "Spain Valencia"},
{subdomain: "fi-hel", region: "Finland"},
{subdomain: "fr-bod", region: "France Bordeaux"},
{subdomain: "fr-mrs", region: "France Marseilles"},
{subdomain: "fr-par", region: "France Paris"},
{subdomain: "fr-se", region: "France Sweden"},
{subdomain: "gr-ath", region: "Greece"},
{subdomain: "hk-hkg", region: "Hong Kong"},
{subdomain: "hr-zag", region: "Croatia"},
{subdomain: "hu-bud", region: "Hungary"},
{subdomain: "id-jak", region: "Indonesia"},
{subdomain: "ie-dub", region: "Ireland"},
{subdomain: "il-tlv", region: "Israel"},
{subdomain: "in-chn", region: "India Chennai"},
{subdomain: "in-idr", region: "India Indore"},
{subdomain: "in-mum", region: "India Mumbai"},
{subdomain: "in-uk", region: "India UK"},
{subdomain: "is-rkv", region: "Iceland"},
{subdomain: "it-mil", region: "Italy Milan"},
{subdomain: "it-rom", region: "Italy Rome"},
{subdomain: "jp-tok", region: "Japan Tokyo"},
{subdomain: "jp-tok-st001", region: "Japan Tokyo st001"},
{subdomain: "jp-tok-st002", region: "Japan Tokyo st002"},
{subdomain: "jp-tok-st003", region: "Japan Tokyo st003"},
{subdomain: "jp-tok-st004", region: "Japan Tokyo st004"},
{subdomain: "jp-tok-st005", region: "Japan Tokyo st005"},
{subdomain: "jp-tok-st006", region: "Japan Tokyo st006"},
{subdomain: "jp-tok-st007", region: "Japan Tokyo st007"},
{subdomain: "kr-seo", region: "Korea"},
{subdomain: "kz-ura", region: "Kazakhstan"},
{subdomain: "lu-ste", region: "Luxembourg"},
{subdomain: "lv-rig", region: "Latvia"},
{subdomain: "ly-tip", region: "Libya"},
{subdomain: "md-chi", region: "Moldova"},
{subdomain: "mk-skp", region: "North Macedonia"},
{subdomain: "my-kul", region: "Malaysia"},
{subdomain: "ng-lag", region: "Nigeria"},
{subdomain: "nl-ams", region: "Netherlands Amsterdam"},
{subdomain: "nl-ams-st001", region: "Netherlands Amsterdam st001"},
{subdomain: "nl-us", region: "Netherlands US"},
{subdomain: "no-osl", region: "Norway"},
{subdomain: "nz-akl", region: "New Zealand"},
{subdomain: "ph-mnl", region: "Philippines"},
{subdomain: "pl-gdn", region: "Poland Gdansk"},
{subdomain: "pl-waw", region: "Poland Warsaw"},
{subdomain: "pt-lis", region: "Portugal Lisbon"},
{subdomain: "pt-lou", region: "Portugal Loule"},
{subdomain: "pt-opo", region: "Portugal Porto"},
{subdomain: "py-asu", region: "Paraguay"},
{subdomain: "ro-buc", region: "Romania"},
{subdomain: "rs-beg", region: "Serbia"},
{subdomain: "ru-mos", region: "Russia Moscow"},
{subdomain: "ru-spt", region: "Russia St. Petersburg"},
{subdomain: "se-sto", region: "Sweden"},
{subdomain: "sg-hk", region: "Singapore Hong Kong"},
{subdomain: "sg-nl", region: "Singapore Netherlands"},
{subdomain: "sg-sng", region: "Singapore"},
{subdomain: "sg-sng-st001", region: "Singapore st001"},
{subdomain: "sg-sng-st002", region: "Singapore st002"},
{subdomain: "sg-sng-st003", region: "Singapore st003"},
{subdomain: "sg-sng-st004", region: "Singapore st004"},
{subdomain: "si-lju", region: "Slovenia"},
{subdomain: "sk-bts", region: "Slovekia"},
{subdomain: "th-bkk", region: "Thailand"},
{subdomain: "tr-bur", region: "Turkey"},
{subdomain: "tw-tai", region: "Taiwan"},
{subdomain: "ua-iev", region: "Ukraine"},
{subdomain: "uk-de", region: "UK Germany"},
{subdomain: "uk-fr", region: "UK France"},
{subdomain: "uk-gla", region: "UK Glasgow"},
{subdomain: "uk-lon", region: "UK London"},
{subdomain: "uk-lon-st001", region: "UK London st001"},
{subdomain: "uk-lon-st002", region: "UK London st002"},
{subdomain: "uk-lon-st003", region: "UK London st003"},
{subdomain: "uk-lon-st004", region: "UK London st004"},
{subdomain: "uk-lon-st005", region: "UK London st005"},
{subdomain: "uk-man", region: "UK Manchester"},
{subdomain: "us-atl", region: "US Atlanta"},
{subdomain: "us-bdn", region: "US Bend"},
{subdomain: "us-bos", region: "US Boston"},
{subdomain: "us-buf", region: "US Buffalo"},
{subdomain: "us-chi", region: "US Chicago"},
{subdomain: "us-clt", region: "US Charlotte"},
{subdomain: "us-dal", region: "US Dallas"},
{subdomain: "us-den", region: "US Denver"},
{subdomain: "us-dtw", region: "US Gahanna"},
{subdomain: "us-hou", region: "US Houston"},
{subdomain: "us-kan", region: "US Kansas City"},
{subdomain: "us-las", region: "US Las Vegas"},
{subdomain: "us-lax", region: "US Los Angeles"},
{subdomain: "us-ltm", region: "US Latham"},
{subdomain: "us-mia", region: "US Miami"},
{subdomain: "us-mnz", region: "US Maryland"},
{subdomain: "us-nl", region: "US Netherlands"},
{subdomain: "us-nyc", region: "US New York City"},
{subdomain: "us-nyc-mp001", region: "US New York City mp001"},
{subdomain: "us-nyc-st001", region: "US New York City st001"},
{subdomain: "us-nyc-st002", region: "US New York City st002"},
{subdomain: "us-nyc-st003", region: "US New York City st003"},
{subdomain: "us-nyc-st004", region: "US New York City st004"},
{subdomain: "us-nyc-st005", region: "US New York City st005"},
{subdomain: "us-orl", region: "US Orlando"},
{subdomain: "us-phx", region: "US Phoenix"},
{subdomain: "us-pt", region: "US Portugal"},
{subdomain: "us-sea", region: "US Seatle"},
{subdomain: "us-sfo", region: "US San Francisco"},
{subdomain: "us-slc", region: "US Salt Lake City"},
{subdomain: "us-stl", region: "US Saint Louis"},
{subdomain: "us-tpa", region: "US Tampa"},
{subdomain: "vn-hcm", region: "Vietnam"},
{subdomain: "za-jnb", region: "South Africa"},
}
}
func cyberghostServers() []server {
return []server{
{subdomain: "97-1-al", region: "Albania", group: "Premium TCP Europe"},
{subdomain: "87-1-al", region: "Albania", group: "Premium UDP Europe"},
{subdomain: "87-1-dz", region: "Algeria", group: "Premium UDP Europe"},
{subdomain: "97-1-dz", region: "Algeria", group: "Premium TCP Europe"},
{subdomain: "97-1-ad", region: "Andorra", group: "Premium TCP Europe"},
{subdomain: "87-1-ad", region: "Andorra", group: "Premium UDP Europe"},
{subdomain: "94-1-ar", region: "Argentina", group: "Premium UDP USA"},
{subdomain: "93-1-ar", region: "Argentina", group: "Premium TCP USA"},
{subdomain: "87-1-am", region: "Armenia", group: "Premium UDP Europe"},
{subdomain: "97-1-am", region: "Armenia", group: "Premium TCP Europe"},
{subdomain: "95-1-au", region: "Australia", group: "Premium UDP Asia"},
{subdomain: "96-1-au", region: "Australia", group: "Premium TCP Asia"},
{subdomain: "97-1-at", region: "Austria", group: "Premium TCP Europe"},
{subdomain: "87-1-at", region: "Austria", group: "Premium UDP Europe"},
{subdomain: "93-1-bs", region: "Bahamas", group: "Premium TCP USA"},
{subdomain: "94-1-bs", region: "Bahamas", group: "Premium UDP USA"},
{subdomain: "95-1-bd", region: "Bangladesh", group: "Premium UDP Asia"},
{subdomain: "96-1-bd", region: "Bangladesh", group: "Premium TCP Asia"},
{subdomain: "97-1-by", region: "Belarus", group: "Premium TCP Europe"},
{subdomain: "87-1-by", region: "Belarus", group: "Premium UDP Europe"},
{subdomain: "97-1-be", region: "Belgium", group: "Premium TCP Europe"},
{subdomain: "87-1-be", region: "Belgium", group: "Premium UDP Europe"},
{subdomain: "87-1-ba", region: "Bosnia and Herzegovina", group: "Premium UDP Europe"},
{subdomain: "97-1-ba", region: "Bosnia and Herzegovina", group: "Premium TCP Europe"},
{subdomain: "94-1-br", region: "Brazil", group: "Premium UDP USA"},
{subdomain: "93-1-br", region: "Brazil", group: "Premium TCP USA"},
{subdomain: "87-1-bg", region: "Bulgaria", group: "Premium UDP Europe"},
{subdomain: "97-1-bg", region: "Bulgaria", group: "Premium TCP Europe"},
{subdomain: "96-1-kh", region: "Cambodia", group: "Premium TCP Asia"},
{subdomain: "95-1-kh", region: "Cambodia", group: "Premium UDP Asia"},
{subdomain: "93-1-ca", region: "Canada", group: "Premium TCP USA"},
{subdomain: "94-1-ca", region: "Canada", group: "Premium UDP USA"},
{subdomain: "93-1-cl", region: "Chile", group: "Premium TCP USA"},
{subdomain: "94-1-cl", region: "Chile", group: "Premium UDP USA"},
{subdomain: "96-1-cn", region: "China", group: "Premium TCP Asia"},
{subdomain: "95-1-cn", region: "China", group: "Premium UDP Asia"},
{subdomain: "94-1-co", region: "Colombia", group: "Premium UDP USA"},
{subdomain: "93-1-co", region: "Colombia", group: "Premium TCP USA"},
{subdomain: "93-1-cr", region: "Costa Rica", group: "Premium TCP USA"},
{subdomain: "94-1-cr", region: "Costa Rica", group: "Premium UDP USA"},
{subdomain: "87-1-cy", region: "Cyprus", group: "Premium UDP Europe"},
{subdomain: "97-1-cy", region: "Cyprus", group: "Premium TCP Europe"},
{subdomain: "97-1-cz", region: "Czech Republic", group: "Premium TCP Europe"},
{subdomain: "87-1-cz", region: "Czech Republic", group: "Premium UDP Europe"},
{subdomain: "87-1-dk", region: "Denmark", group: "Premium UDP Europe"},
{subdomain: "97-1-dk", region: "Denmark", group: "Premium TCP Europe"},
{subdomain: "87-1-eg", region: "Egypt", group: "Premium UDP Europe"},
{subdomain: "97-1-eg", region: "Egypt", group: "Premium TCP Europe"},
{subdomain: "87-1-ee", region: "Estonia", group: "Premium UDP Europe"},
{subdomain: "97-1-ee", region: "Estonia", group: "Premium TCP Europe"},
{subdomain: "97-1-fi", region: "Finland", group: "Premium TCP Europe"},
{subdomain: "87-1-fi", region: "Finland", group: "Premium UDP Europe"},
{subdomain: "87-1-fr", region: "France", group: "Premium UDP Europe"},
{subdomain: "97-1-fr", region: "France", group: "Premium TCP Europe"},
{subdomain: "87-1-ge", region: "Georgia", group: "Premium UDP Europe"},
{subdomain: "97-1-ge", region: "Georgia", group: "Premium TCP Europe"},
{subdomain: "97-1-de", region: "Germany", group: "Premium TCP Europe"},
{subdomain: "87-1-de", region: "Germany", group: "Premium UDP Europe"},
{subdomain: "87-1-gr", region: "Greece", group: "Premium UDP Europe"},
{subdomain: "97-1-gr", region: "Greece", group: "Premium TCP Europe"},
{subdomain: "97-1-gl", region: "Greenland", group: "Premium TCP Europe"},
{subdomain: "87-1-gl", region: "Greenland", group: "Premium UDP Europe"},
{subdomain: "96-1-hk", region: "Hong Kong", group: "Premium TCP Asia"},
{subdomain: "95-1-hk", region: "Hong Kong", group: "Premium UDP Asia"},
{subdomain: "87-1-hu", region: "Hungary", group: "Premium UDP Europe"},
{subdomain: "97-1-hu", region: "Hungary", group: "Premium TCP Europe"},
{subdomain: "97-1-is", region: "Iceland", group: "Premium TCP Europe"},
{subdomain: "87-1-is", region: "Iceland", group: "Premium UDP Europe"},
{subdomain: "87-1-in", region: "India", group: "Premium UDP Europe"},
{subdomain: "97-1-in", region: "India", group: "Premium TCP Europe"},
{subdomain: "95-1-id", region: "Indonesia", group: "Premium UDP Asia"},
{subdomain: "96-1-id", region: "Indonesia", group: "Premium TCP Asia"},
{subdomain: "87-1-ir", region: "Iran", group: "Premium UDP Europe"},
{subdomain: "97-1-ir", region: "Iran", group: "Premium TCP Europe"},
{subdomain: "87-1-ie", region: "Ireland", group: "Premium UDP Europe"},
{subdomain: "97-1-ie", region: "Ireland", group: "Premium TCP Europe"},
{subdomain: "87-1-im", region: "Isle of Man", group: "Premium UDP Europe"},
{subdomain: "97-1-im", region: "Isle of Man", group: "Premium TCP Europe"},
{subdomain: "87-1-il", region: "Israel", group: "Premium UDP Europe"},
{subdomain: "97-1-il", region: "Israel", group: "Premium TCP Europe"},
{subdomain: "97-1-it", region: "Italy", group: "Premium TCP Europe"},
{subdomain: "87-1-it", region: "Italy", group: "Premium UDP Europe"},
{subdomain: "95-1-jp", region: "Japan", group: "Premium UDP Asia"},
{subdomain: "96-1-jp", region: "Japan", group: "Premium TCP Asia"},
{subdomain: "97-1-kz", region: "Kazakhstan", group: "Premium TCP Europe"},
{subdomain: "87-1-kz", region: "Kazakhstan", group: "Premium UDP Europe"},
{subdomain: "95-1-ke", region: "Kenya", group: "Premium UDP Asia"},
{subdomain: "96-1-ke", region: "Kenya", group: "Premium TCP Asia"},
{subdomain: "95-1-kr", region: "Korea", group: "Premium UDP Asia"},
{subdomain: "96-1-kr", region: "Korea", group: "Premium TCP Asia"},
{subdomain: "97-1-lv", region: "Latvia", group: "Premium TCP Europe"},
{subdomain: "87-1-lv", region: "Latvia", group: "Premium UDP Europe"},
{subdomain: "97-1-li", region: "Liechtenstein", group: "Premium TCP Europe"},
{subdomain: "87-1-li", region: "Liechtenstein", group: "Premium UDP Europe"},
{subdomain: "97-1-lt", region: "Lithuania", group: "Premium TCP Europe"},
{subdomain: "87-1-lt", region: "Lithuania", group: "Premium UDP Europe"},
{subdomain: "87-1-lu", region: "Luxembourg", group: "Premium UDP Europe"},
{subdomain: "97-1-lu", region: "Luxembourg", group: "Premium TCP Europe"},
{subdomain: "96-1-mo", region: "Macao", group: "Premium TCP Asia"},
{subdomain: "95-1-mo", region: "Macao", group: "Premium UDP Asia"},
{subdomain: "97-1-mk", region: "Macedonia", group: "Premium TCP Europe"},
{subdomain: "87-1-mk", region: "Macedonia", group: "Premium UDP Europe"},
{subdomain: "95-1-my", region: "Malaysia", group: "Premium UDP Asia"},
{subdomain: "96-1-my", region: "Malaysia", group: "Premium TCP Asia"},
{subdomain: "87-1-mt", region: "Malta", group: "Premium UDP Europe"},
{subdomain: "97-1-mt", region: "Malta", group: "Premium TCP Europe"},
{subdomain: "93-1-mx", region: "Mexico", group: "Premium TCP USA"},
{subdomain: "94-1-mx", region: "Mexico", group: "Premium UDP USA"},
{subdomain: "87-1-md", region: "Moldova", group: "Premium UDP Europe"},
{subdomain: "97-1-md", region: "Moldova", group: "Premium TCP Europe"},
{subdomain: "87-1-mc", region: "Monaco", group: "Premium UDP Europe"},
{subdomain: "97-1-mc", region: "Monaco", group: "Premium TCP Europe"},
{subdomain: "96-1-mn", region: "Mongolia", group: "Premium TCP Asia"},
{subdomain: "95-1-mn", region: "Mongolia", group: "Premium UDP Asia"},
{subdomain: "87-1-me", region: "Montenegro", group: "Premium UDP Europe"},
{subdomain: "97-1-me", region: "Montenegro", group: "Premium TCP Europe"},
{subdomain: "97-1-ma", region: "Morocco", group: "Premium TCP Europe"},
{subdomain: "87-1-ma", region: "Morocco", group: "Premium UDP Europe"},
{subdomain: "97-1-nl", region: "Netherlands", group: "Premium TCP Europe"},
{subdomain: "87-1-nl", region: "Netherlands", group: "Premium UDP Europe"},
{subdomain: "95-1-nz", region: "New Zealand", group: "Premium UDP Asia"},
{subdomain: "96-1-nz", region: "New Zealand", group: "Premium TCP Asia"},
{subdomain: "87-1-ng", region: "Nigeria", group: "Premium UDP Europe"},
{subdomain: "97-1-ng", region: "Nigeria", group: "Premium TCP Europe"},
{subdomain: "97-1-no", region: "Norway", group: "Premium TCP Europe"},
{subdomain: "87-1-no", region: "Norway", group: "Premium UDP Europe"},
{subdomain: "97-1-pk", region: "Pakistan", group: "Premium TCP Europe"},
{subdomain: "87-1-pk", region: "Pakistan", group: "Premium UDP Europe"},
{subdomain: "97-1-pa", region: "Panama", group: "Premium TCP Europe"},
{subdomain: "87-1-pa", region: "Panama", group: "Premium UDP Europe"},
{subdomain: "95-1-ph", region: "Philippines", group: "Premium UDP Asia"},
{subdomain: "96-1-ph", region: "Philippines", group: "Premium TCP Asia"},
{subdomain: "97-1-pl", region: "Poland", group: "Premium TCP Europe"},
{subdomain: "87-1-pl", region: "Poland", group: "Premium UDP Europe"},
{subdomain: "97-1-pt", region: "Portugal", group: "Premium TCP Europe"},
{subdomain: "87-1-pt", region: "Portugal", group: "Premium UDP Europe"},
{subdomain: "97-1-qa", region: "Qatar", group: "Premium TCP Europe"},
{subdomain: "87-1-qa", region: "Qatar", group: "Premium UDP Europe"},
{subdomain: "87-1-ro", region: "Romania", group: "Premium UDP Europe"},
{subdomain: "87-8-ro", region: "Romania", group: "NoSpy UDP Europe"},
{subdomain: "97-8-ro", region: "Romania", group: "NoSpy TCP Europe"},
{subdomain: "97-1-ro", region: "Romania", group: "Premium TCP Europe"},
{subdomain: "87-1-ru", region: "Russian Federation", group: "Premium UDP Europe"},
{subdomain: "97-1-ru", region: "Russian Federation", group: "Premium TCP Europe"},
{subdomain: "97-1-sa", region: "Saudi Arabia", group: "Premium TCP Europe"},
{subdomain: "87-1-sa", region: "Saudi Arabia", group: "Premium UDP Europe"},
{subdomain: "97-1-rs", region: "Serbia", group: "Premium TCP Europe"},
{subdomain: "87-1-rs", region: "Serbia", group: "Premium UDP Europe"},
{subdomain: "95-1-sg", region: "Singapore", group: "Premium UDP Asia"},
{subdomain: "96-1-sg", region: "Singapore", group: "Premium TCP Asia"},
{subdomain: "87-1-sk", region: "Slovakia", group: "Premium UDP Europe"},
{subdomain: "97-1-sk", region: "Slovakia", group: "Premium TCP Europe"},
{subdomain: "87-1-si", region: "Slovenia", group: "Premium UDP Europe"},
{subdomain: "97-1-si", region: "Slovenia", group: "Premium TCP Europe"},
{subdomain: "87-1-za", region: "South Africa", group: "Premium UDP Europe"},
{subdomain: "95-1-za", region: "South Africa", group: "Premium UDP Asia"},
{subdomain: "97-1-za", region: "South Africa", group: "Premium TCP Europe"},
{subdomain: "96-1-za", region: "South Africa", group: "Premium TCP Asia"},
{subdomain: "97-1-es", region: "Spain", group: "Premium TCP Europe"},
{subdomain: "87-1-es", region: "Spain", group: "Premium UDP Europe"},
{subdomain: "97-1-lk", region: "Sri Lanka", group: "Premium TCP Europe"},
{subdomain: "87-1-lk", region: "Sri Lanka", group: "Premium UDP Europe"},
{subdomain: "97-1-se", region: "Sweden", group: "Premium TCP Europe"},
{subdomain: "87-1-se", region: "Sweden", group: "Premium UDP Europe"},
{subdomain: "87-1-ch", region: "Switzerland", group: "Premium UDP Europe"},
{subdomain: "97-1-ch", region: "Switzerland", group: "Premium TCP Europe"},
{subdomain: "96-1-tw", region: "Taiwan", group: "Premium TCP Asia"},
{subdomain: "95-1-tw", region: "Taiwan", group: "Premium UDP Asia"},
{subdomain: "96-1-th", region: "Thailand", group: "Premium TCP Asia"},
{subdomain: "95-1-th", region: "Thailand", group: "Premium UDP Asia"},
{subdomain: "87-1-tr", region: "Turkey", group: "Premium UDP Europe"},
{subdomain: "97-1-tr", region: "Turkey", group: "Premium TCP Europe"},
{subdomain: "97-1-ua", region: "Ukraine", group: "Premium TCP Europe"},
{subdomain: "87-1-ua", region: "Ukraine", group: "Premium UDP Europe"},
{subdomain: "87-1-ae", region: "United Arab Emirates", group: "Premium UDP Europe"},
{subdomain: "97-1-ae", region: "United Arab Emirates", group: "Premium TCP Europe"},
{subdomain: "97-1-gb", region: "United Kingdom", group: "Premium TCP Europe"},
{subdomain: "87-1-gb", region: "United Kingdom", group: "Premium UDP Europe"},
{subdomain: "94-1-us", region: "United States", group: "Premium UDP USA"},
{subdomain: "93-1-us", region: "United States", group: "Premium TCP USA"},
{subdomain: "87-1-ve", region: "Venezuela", group: "Premium UDP Europe"},
{subdomain: "97-1-ve", region: "Venezuela", group: "Premium TCP Europe"},
{subdomain: "95-1-vn", region: "Vietnam", group: "Premium UDP Asia"},
{subdomain: "96-1-vn", region: "Vietnam", group: "Premium TCP Asia"},
}
}
func vyprvpnServers() []server {
return []server{
{subdomain: "ae1", region: "Dubai"},
{subdomain: "ar1", region: "Argentina"},
{subdomain: "at1", region: "Austria"},
{subdomain: "au1", region: "Australia Sydney"},
{subdomain: "au2", region: "Australia Melbourne"},
{subdomain: "au3", region: "Australia Perth"},
{subdomain: "be1", region: "Belgium"},
{subdomain: "bg1", region: "Bulgaria"},
{subdomain: "bh1", region: "Bahrain"},
{subdomain: "br1", region: "Brazil"},
{subdomain: "ca1", region: "Canada"},
{subdomain: "ch1", region: "Switzerland"},
{subdomain: "co1", region: "Columbia"},
{subdomain: "cr1", region: "Costa Rica"},
{subdomain: "cz1", region: "Czech Republic"},
{subdomain: "de1", region: "Germany"},
{subdomain: "dk1", region: "Denmark"},
{subdomain: "dz1", region: "Algeria"},
{subdomain: "eg1", region: "Egypt"},
{subdomain: "es1", region: "Spain"},
{subdomain: "eu1", region: "Netherlands"},
{subdomain: "fi1", region: "Finland"},
{subdomain: "fr1", region: "France"},
{subdomain: "gr1", region: "Greece"},
{subdomain: "hk1", region: "Hong Kong"},
{subdomain: "id1", region: "Indonesia"},
{subdomain: "ie1", region: "Ireland"},
{subdomain: "il1", region: "Israel"},
{subdomain: "in1", region: "India"},
{subdomain: "is1", region: "Iceland"},
{subdomain: "it1", region: "Italy"},
{subdomain: "jp1", region: "Japan"},
{subdomain: "kr1", region: "South Korea"},
{subdomain: "li1", region: "Liechtenstein"},
{subdomain: "lt1", region: "Lithuania"},
{subdomain: "lu1", region: "Luxembourg"},
{subdomain: "lv1", region: "Latvia"},
{subdomain: "mh1", region: "Marshall Islands"},
{subdomain: "mo1", region: "Macao"},
{subdomain: "mv1", region: "Maldives"},
{subdomain: "mx1", region: "Mexico"},
{subdomain: "my1", region: "Malaysia"},
{subdomain: "no1", region: "Norway"},
{subdomain: "nz1", region: "New Zealand"},
{subdomain: "pa1", region: "Panama"},
{subdomain: "ph1", region: "Philippines"},
{subdomain: "pk1", region: "Pakistan"},
{subdomain: "pl1", region: "Poland"},
{subdomain: "pt1", region: "Portugal"},
{subdomain: "qa1", region: "Qatar"},
{subdomain: "ro1", region: "Romania"},
{subdomain: "ru1", region: "Russia"},
{subdomain: "sa1", region: "Saudi Arabia"},
{subdomain: "se1", region: "Sweden"},
{subdomain: "sg1", region: "Singapore"},
{subdomain: "si1", region: "Slovenia"},
{subdomain: "sk1", region: "Slovakia"},
{subdomain: "sv1", region: "El Salvador"},
{subdomain: "th1", region: "Thailand"},
{subdomain: "tr1", region: "Turkey"},
{subdomain: "tw1", region: "Taiwan"},
{subdomain: "ua1", region: "Ukraine"},
{subdomain: "uk1", region: "United Kingdom"},
{subdomain: "us1", region: "USA Los Angeles"},
{subdomain: "us2", region: "USA Washington DC"},
{subdomain: "us3", region: "USA Austin"},
{subdomain: "us4", region: "USA Miami"},
{subdomain: "us5", region: "USA New York"},
{subdomain: "us6", region: "USA Chicago"},
{subdomain: "us7", region: "USA San Francisco"},
{subdomain: "us8", region: "USA Seattle"},
{subdomain: "uy1", region: "Uruguay"},
{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
}

BIN
doc/paypal.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
doc/sponsors.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
doc/windscribe.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,38 +1,39 @@
version: "3.7"
services:
pia:
build: https://github.com/qdm12/private-internet-access-docker.git
gluetun:
image: qmcgaw/private-internet-access
container_name: pia
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun
network_mode: bridge
init: true
ports:
- 8888:8888/tcp
- 8388:8388/tcp
- 8388:8388/udp
- 8888:8888/tcp # Tinyproxy
- 8388:8388/tcp # Shadowsocks
- 8388:8388/udp # Shadowsocks
- 8000:8000/tcp # Built-in HTTP control server
# command:
environment:
# More variables are available, see the readme table
- VPNSP=private internet access
# Timezone for accurate logs times
- TZ=
# All VPN providers
- USER=js89ds7
# All VPN providers but Mullvad
- PASSWORD=8fd9s239G
- ENCRYPTION=strong
- PROTOCOL=udp
- REGION=CA Montreal
- NONROOT=no
- DOT=on
- BLOCK_MALICIOUS=on
- BLOCK_NSA=off
- UNBLOCK=
- FIREWALL=on
# Cyberghost only
- CLIENT_KEY=
# All VPN providers but Mullvad
- REGION=Austria
# Mullvad only
- COUNTRY=Sweden
# Allow for example your LAN, set to: 192.168.1.0/24
- EXTRA_SUBNETS=
- TINYPROXY=on
- TINYPROXY_LOG=Critical
- TINYPROXY_USER=
- TINYPROXY_PASSWORD=
- SHADOWSOCKS=on
- SHADOWSOCKS_LOG=on
- SHADOWSOCKS_PASSWORD=
restart: always

View File

@@ -1,492 +0,0 @@
#!/bin/sh
exitOnError(){
# $1 must be set to $?
status=$1
message=$2
[ "$message" != "" ] || message="Undefined error"
if [ $status != 0 ]; then
printf "[ERROR] $message, with status $status\n"
exit $status
fi
}
exitIfUnset(){
# $1 is the name of the variable to check - not the variable itself
var="$(eval echo "\$$1")"
if [ -z "$var" ]; then
printf "[ERROR] Environment variable $1 is not set\n"
exit 1
fi
}
exitIfNotIn(){
# $1 is the name of the variable to check - not the variable itself
# $2 is a string of comma separated possible values
var="$(eval echo "\$$1")"
for value in ${2//,/ }
do
if [ "$var" = "$value" ]; then
return 0
fi
done
printf "[ERROR] Environment variable $1 cannot be '$var' and must be one of the following: "
for value in ${2//,/ }
do
printf "$value "
done
printf "\n"
exit 1
}
printf " =========================================\n"
printf " =========================================\n"
printf " ============= PIA CONTAINER =============\n"
printf " =========================================\n"
printf " =========================================\n"
printf " == by github.com/qdm12 - Quentin McGaw ==\n\n"
printf "OpenVPN version: $(openvpn --version | head -n 1 | grep -oE "OpenVPN [0-9\.]* " | cut -d" " -f2)\n"
printf "Unbound version: $(unbound -h | grep "Version" | cut -d" " -f2)\n"
printf "Iptables version: $(iptables --version | cut -d" " -f2)\n"
printf "TinyProxy version: $(tinyproxy -v | cut -d" " -f2)\n"
printf "ShadowSocks version: $(ss-server --help | head -n 2 | tail -n 1 | cut -d" " -f 2)\n"
############################################
# BACKWARD COMPATIBILITY PARAMETERS
############################################
[ "$PORT_FORWARDING" == "false" ] && PORT_FORWARDING=on
[ "$PORT_FORWARDING" == "true" ] && PORT_FORWARDING=off
if [ -z $TINYPROXY ] && [ ! -z $PROXY ]; then
TINYPROXY=$PROXY
fi
if [ -z $TINYPROXY_LOG ] && [ ! -z $PROXY_LOG_LEVEL ]; then
TINYPROXY_LOG=$PROXY_LOG_LEVEL
fi
if [ -z $TINYPROXY_PORT ] && [ ! -z $PROXY_PORT ]; then
TINYPROXY_PORT=$PROXY_PORT
fi
if [ -z $TINYPROXY_USER ] && [ ! -z $PROXY_USER ]; then
TINYPROXY_USER=$PROXY_USER
fi
if [ -z $TINYPROXY_PASSWORD ] && [ ! -z $PROXY_PASSWORD ]; then
TINYPROXY_PASSWORD=$PROXY_PASSWORD
fi
############################################
# CHECK PARAMETERS
############################################
exitIfUnset USER
exitIfUnset PASSWORD
exitIfNotIn ENCRYPTION "normal,strong"
exitIfNotIn PROTOCOL "tcp,udp"
exitIfNotIn NONROOT "yes,no"
cat "/openvpn/$PROTOCOL-$ENCRYPTION/$REGION.ovpn" &> /dev/null
exitOnError $? "/openvpn/$PROTOCOL-$ENCRYPTION/$REGION.ovpn is not accessible"
for EXTRA_SUBNET in ${EXTRA_SUBNETS//,/ }; do
if [ $(echo "$EXTRA_SUBNET" | grep -Eo '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(/([0-2]?[0-9])|([3]?[0-1]))?$') = "" ]; then
printf "Extra subnet $EXTRA_SUBNET is not a valid IPv4 subnet of the form 255.255.255.255/31 or 255.255.255.255\n"
exit 1
fi
done
exitIfNotIn DOT "on,off"
exitIfNotIn BLOCK_MALICIOUS "on,off"
exitIfNotIn BLOCK_NSA "on,off"
if [ "$DOT" == "off" ]; then
if [ "$BLOCK_MALICIOUS" == "on" ]; then
printf "DOT is off so BLOCK_MALICIOUS cannot be on\n"
exit 1
elif [ "$BLOCK_NSA" == "on" ]; then
printf "DOT is off so BLOCK_NSA cannot be on\n"
exit 1
fi
fi
exitIfNotIn PORT_FORWARDING "on,off"
if [ "$PORT_FORWARDING" == "on" ] && [ -z "$PORT_FORWARDING_STATUS_FILE" ]; then
printf "PORT_FORWARDING is on but PORT_FORWARDING_STATUS_FILE is not set\n"
exit 1
fi
exitIfNotIn TINYPROXY "on,off"
if [ "$TINYPROXY" == "on" ]; then
exitIfNotIn TINYPROXY_LOG "Info,Warning,Error,Critical"
if [ -z $TINYPROXY_PORT ]; then
TINYPROXY_PORT=8888
fi
if [ `echo $TINYPROXY_PORT | grep -E "^[0-9]+$"` != $TINYPROXY_PORT ]; then
printf "TINYPROXY_PORT is not a valid number\n"
exit 1
elif [ $TINYPROXY_PORT -lt 1024 ]; then
printf "TINYPROXY_PORT cannot be a privileged port under port 1024\n"
exit 1
elif [ $TINYPROXY_PORT -gt 65535 ]; then
printf "TINYPROXY_PORT cannot be a port higher than the maximum port 65535\n"
exit 1
fi
if [ ! -z "$TINYPROXY_USER" ] && [ -z "$TINYPROXY_PASSWORD" ]; then
printf "TINYPROXY_USER is set but TINYPROXY_PASSWORD is not set\n"
exit 1
elif [ -z "$TINYPROXY_USER" ] && [ ! -z "$TINYPROXY_PASSWORD" ]; then
printf "TINYPROXY_USER is not set but TINYPROXY_PASSWORD is set\n"
exit 1
fi
fi
exitIfNotIn SHADOWSOCKS "on,off"
if [ "$SHADOWSOCKS" == "on" ]; then
exitIfNotIn SHADOWSOCKS_LOG "on,off"
if [ -z $SHADOWSOCKS_PORT ]; then
SHADOWSOCKS_PORT=8388
fi
if [ `echo $SHADOWSOCKS_PORT | grep -E "^[0-9]+$"` != $SHADOWSOCKS_PORT ]; then
printf "SHADOWSOCKS_PORT is not a valid number\n"
exit 1
elif [ $SHADOWSOCKS_PORT -lt 1024 ]; then
printf "SHADOWSOCKS_PORT cannot be a privileged port under port 1024\n"
exit 1
elif [ $SHADOWSOCKS_PORT -gt 65535 ]; then
printf "SHADOWSOCKS_PORT cannot be a port higher than the maximum port 65535\n"
exit 1
fi
if [ -z $SHADOWSOCKS_PASSWORD ]; then
printf "SHADOWSOCKS_PASSWORD is not set\n"
exit 1
fi
fi
############################################
# SHOW PARAMETERS
############################################
printf "\n"
printf "OpenVPN parameters:\n"
printf " * Region: $REGION\n"
printf " * Encryption: $ENCRYPTION\n"
printf " * Protocol: $PROTOCOL\n"
printf " * Running without root: $NONROOT\n"
printf "DNS over TLS:\n"
printf " * Activated: $DOT\n"
if [ "$DOT" = "on" ]; then
printf " * Malicious hostnames DNS blocking: $BLOCK_MALICIOUS\n"
printf " * NSA related DNS blocking: $BLOCK_NSA\n"
printf " * Unblocked hostnames: $UNBLOCK\n"
fi
printf "Local network parameters:\n"
printf " * Extra subnets: $EXTRA_SUBNETS\n"
printf " * Tinyproxy HTTP proxy: $TINYPROXY\n"
if [ "$TINYPROXY" == "on" ]; then
printf " * Tinyproxy port: $TINYPROXY_PORT\n"
tinyproxy_auth=yes
if [ -z $TINYPROXY_USER ]; then
tinyproxy_auth=no
fi
printf " * Tinyproxy has authentication: $tinyproxy_auth\n"
unset -v tinyproxy_auth
fi
printf " * ShadowSocks SOCKS5 proxy: $SHADOWSOCKS\n"
printf "PIA parameters:\n"
printf " * Remote port forwarding: $PORT_FORWARDING\n"
[ "$PORT_FORWARDING" == "on" ] && printf " * Remote port forwarding status file: $PORT_FORWARDING_STATUS_FILE\n"
printf "\n"
#####################################################
# Writes to protected file and remove USER, PASSWORD
#####################################################
if [ -f /auth.conf ]; then
printf "[INFO] /auth.conf already exists\n"
else
printf "[INFO] Writing USER and PASSWORD to protected file /auth.conf..."
echo "$USER" > /auth.conf
exitOnError $?
echo "$PASSWORD" >> /auth.conf
exitOnError $?
chown nonrootuser /auth.conf
exitOnError $?
chmod 400 /auth.conf
exitOnError $?
printf "DONE\n"
printf "[INFO] Clearing environment variables USER and PASSWORD..."
unset -v USER
unset -v PASSWORD
printf "DONE\n"
fi
############################################
# CHECK FOR TUN DEVICE
############################################
if [ "$(cat /dev/net/tun 2>&1 /dev/null)" != "cat: read error: File descriptor in bad state" ]; then
printf "[WARNING] TUN device is not available, creating it..."
mkdir -p /dev/net
mknod /dev/net/tun c 10 200
exitOnError $?
chmod 0666 /dev/net/tun
printf "DONE\n"
fi
############################################
# BLOCKING MALICIOUS HOSTNAMES AND IPs WITH UNBOUND
############################################
if [ "$DOT" == "on" ]; then
rm -f /etc/unbound/blocks-malicious.conf
if [ "$BLOCK_MALICIOUS" = "on" ]; then
tar -xjf /etc/unbound/blocks-malicious.bz2 -C /etc/unbound/
printf "[INFO] $(cat /etc/unbound/blocks-malicious.conf | grep "local-zone" | wc -l ) malicious hostnames and $(cat /etc/unbound/blocks-malicious.conf | grep "private-address" | wc -l) malicious IP addresses blacklisted\n"
else
echo "" > /etc/unbound/blocks-malicious.conf
fi
if [ "$BLOCK_NSA" = "on" ]; then
tar -xjf /etc/unbound/blocks-nsa.bz2 -C /etc/unbound/
printf "[INFO] $(cat /etc/unbound/blocks-nsa.conf | grep "local-zone" | wc -l ) NSA hostnames blacklisted\n"
cat /etc/unbound/blocks-nsa.conf >> /etc/unbound/blocks-malicious.conf
rm /etc/unbound/blocks-nsa.conf
sort -u -o /etc/unbound/blocks-malicious.conf /etc/unbound/blocks-malicious.conf
fi
for hostname in ${UNBLOCK//,/ }
do
printf "[INFO] Unblocking hostname $hostname\n"
sed -i "/$hostname/d" /etc/unbound/blocks-malicious.conf
done
fi
############################################
# SETTING DNS OVER TLS TO 1.1.1.1 / 1.0.0.1
############################################
if [ "$DOT" == "on" ]; then
printf "[INFO] Launching Unbound to connect to Cloudflare DNS 1.1.1.1 over TLS..."
unbound
exitOnError $?
printf "DONE\n"
printf "[INFO] Changing DNS to localhost..."
printf "`sed '/^nameserver /d' /etc/resolv.conf`\nnameserver 127.0.0.1\n" > /etc/resolv.conf
exitOnError $?
printf "DONE\n"
fi
############################################
# Reading chosen OpenVPN configuration
############################################
printf "[INFO] Reading OpenVPN configuration...\n"
CONNECTIONSTRING=$(grep -i "/openvpn/$PROTOCOL-$ENCRYPTION/$REGION.ovpn" -e 'privateinternetaccess.com')
exitOnError $?
PORT=$(echo $CONNECTIONSTRING | cut -d' ' -f3)
if [ "$PORT" = "" ]; then
printf "[ERROR] Port not found in /openvpn/$PROTOCOL-$ENCRYPTION/$REGION.ovpn\n"
exit 1
fi
PIADOMAIN=$(echo $CONNECTIONSTRING | cut -d' ' -f2)
if [ "$PIADOMAIN" = "" ]; then
printf "[ERROR] Domain not found in /openvpn/$PROTOCOL-$ENCRYPTION/$REGION.ovpn\n"
exit 1
fi
printf " * Port: $PORT\n"
printf " * Domain: $PIADOMAIN\n"
printf "[INFO] Detecting IP addresses corresponding to $PIADOMAIN...\n"
VPNIPS=$(nslookup $PIADOMAIN localhost | tail -n +3 | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}')
exitOnError $?
for ip in $VPNIPS; do
printf " $ip\n";
done
############################################
# Writing target OpenVPN files
############################################
TARGET_PATH="/openvpn/target"
printf "[INFO] Creating target OpenVPN files in $TARGET_PATH..."
rm -rf $TARGET_PATH/*
cd "/openvpn/$PROTOCOL-$ENCRYPTION"
cp -f *.crt "$TARGET_PATH"
exitOnError $? "Cannot copy crt file to $TARGET_PATH"
cp -f *.pem "$TARGET_PATH"
exitOnError $? "Cannot copy pem file to $TARGET_PATH"
cp -f "$REGION.ovpn" "$TARGET_PATH/config.ovpn"
exitOnError $? "Cannot copy $REGION.ovpn file to $TARGET_PATH"
sed -i "/$CONNECTIONSTRING/d" "$TARGET_PATH/config.ovpn"
exitOnError $? "Cannot delete '$CONNECTIONSTRING' from $TARGET_PATH/config.ovpn"
sed -i '/resolv-retry/d' "$TARGET_PATH/config.ovpn"
exitOnError $? "Cannot delete 'resolv-retry' from $TARGET_PATH/config.ovpn"
for ip in $VPNIPS; do
echo "remote $ip $PORT" >> "$TARGET_PATH/config.ovpn"
exitOnError $? "Cannot add 'remote $ip $PORT' to $TARGET_PATH/config.ovpn"
done
# Uses the username/password from this file to get the token from PIA
echo "auth-user-pass /auth.conf" >> "$TARGET_PATH/config.ovpn"
exitOnError $? "Cannot add 'auth-user-pass /auth.conf' to $TARGET_PATH/config.ovpn"
# Reconnects automatically on failure
echo "auth-retry nointeract" >> "$TARGET_PATH/config.ovpn"
exitOnError $? "Cannot add 'auth-retry nointeract' to $TARGET_PATH/config.ovpn"
# Prevents auth_failed infinite loops - make it interact? Remove persist-tun? nobind?
echo "pull-filter ignore \"auth-token\"" >> "$TARGET_PATH/config.ovpn"
exitOnError $? "Cannot add 'pull-filter ignore \"auth-token\"' to $TARGET_PATH/config.ovpn"
# Runs openvpn without root, as nonrootuser if specified
if [ "$NONROOT" = "yes" ]; then
echo "user nonrootuser" >> "$TARGET_PATH/config.ovpn"
exitOnError $? "Cannot add 'user nonrootuser' to $TARGET_PATH/config.ovpn"
fi
echo "mute-replay-warnings" >> "$TARGET_PATH/config.ovpn"
exitOnError $? "Cannot add 'mute-replay-warnings' to $TARGET_PATH/config.ovpn"
# Note: TUN device re-opening will restart the container due to permissions
printf "DONE\n"
############################################
# NETWORKING
############################################
printf "[INFO] Finding network properties...\n"
printf " * Detecting default gateway..."
DEFAULT_GATEWAY=$(ip r | grep 'default via' | cut -d" " -f 3)
exitOnError $?
printf "$DEFAULT_GATEWAY\n"
printf " * Detecting local interface..."
INTERFACE=$(ip r | grep 'default via' | cut -d" " -f 5)
exitOnError $?
printf "$INTERFACE\n"
printf " * Detecting local subnet..."
SUBNET=$(ip r | grep -v 'default via' | grep $INTERFACE | tail -n 1 | cut -d" " -f 1)
exitOnError $?
printf "$SUBNET\n"
for EXTRASUBNET in ${EXTRA_SUBNETS//,/ }
do
printf " * Adding $EXTRASUBNET as route via $INTERFACE..."
ip route add $EXTRASUBNET via $DEFAULT_GATEWAY dev $INTERFACE
exitOnError $?
printf "DONE\n"
done
printf " * Detecting target VPN interface..."
VPN_DEVICE=$(cat $TARGET_PATH/config.ovpn | grep 'dev ' | cut -d" " -f 2)0
exitOnError $?
printf "$VPN_DEVICE\n"
############################################
# FIREWALL
############################################
printf "[INFO] Setting firewall\n"
printf " * Blocking everyting\n"
printf " * Deleting all iptables rules..."
iptables --flush
exitOnError $?
iptables --delete-chain
exitOnError $?
iptables -t nat --flush
exitOnError $?
iptables -t nat --delete-chain
exitOnError $?
printf "DONE\n"
printf " * Block input traffic..."
iptables -P INPUT DROP
exitOnError $?
printf "DONE\n"
printf " * Block output traffic..."
iptables -F OUTPUT
exitOnError $?
iptables -P OUTPUT DROP
exitOnError $?
printf "DONE\n"
printf " * Block forward traffic..."
iptables -P FORWARD DROP
exitOnError $?
printf "DONE\n"
printf " * Creating general rules\n"
printf " * Accept established and related input and output traffic..."
iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
exitOnError $?
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
exitOnError $?
printf "DONE\n"
printf " * Accept local loopback input and output traffic..."
iptables -A OUTPUT -o lo -j ACCEPT
exitOnError $?
iptables -A INPUT -i lo -j ACCEPT
exitOnError $?
printf "DONE\n"
printf " * Creating VPN rules\n"
for ip in $VPNIPS; do
printf " * Accept output traffic to VPN server $ip through $INTERFACE, port $PROTOCOL $PORT..."
iptables -A OUTPUT -d $ip -o $INTERFACE -p $PROTOCOL -m $PROTOCOL --dport $PORT -j ACCEPT
exitOnError $?
printf "DONE\n"
done
printf " * Accept all output traffic through $VPN_DEVICE..."
iptables -A OUTPUT -o $VPN_DEVICE -j ACCEPT
exitOnError $?
printf "DONE\n"
printf " * Creating local subnet rules\n"
printf " * Accept input and output traffic to and from $SUBNET..."
iptables -A INPUT -s $SUBNET -d $SUBNET -j ACCEPT
iptables -A OUTPUT -s $SUBNET -d $SUBNET -j ACCEPT
printf "DONE\n"
for EXTRASUBNET in ${EXTRA_SUBNETS//,/ }
do
printf " * Accept input traffic through $INTERFACE from $EXTRASUBNET to $SUBNET..."
iptables -A INPUT -i $INTERFACE -s $EXTRASUBNET -d $SUBNET -j ACCEPT
exitOnError $?
printf "DONE\n"
# iptables -A OUTPUT -d $EXTRASUBNET -j ACCEPT
# iptables -A OUTPUT -o $INTERFACE -s $SUBNET -d $EXTRASUBNET -j ACCEPT
done
############################################
# TINYPROXY LAUNCH
############################################
if [ "$TINYPROXY" == "on" ]; then
printf "[INFO] Setting TinyProxy log level to $TINYPROXY_LOG..."
sed -i "/LogLevel /c\LogLevel $TINYPROXY_LOG" /etc/tinyproxy/tinyproxy.conf
exitOnError $?
printf "DONE\n"
printf "[INFO] Setting TinyProxy port to $TINYPROXY_PORT..."
sed -i "/Port /c\Port $TINYPROXY_PORT" /etc/tinyproxy/tinyproxy.conf
exitOnError $?
printf "DONE\n"
if [ ! -z "$TINYPROXY_USER" ]; then
printf "[INFO] Setting TinyProxy credentials..."
echo "BasicAuth $TINYPROXY_USER $TINYPROXY_PASSWORD" >> /etc/tinyproxy/tinyproxy.conf
unset -v TINYPROXY_USER
unset -v TINYPROXY_PASSWORD
printf "DONE\n"
fi
tinyproxy -d &
fi
############################################
# SHADOWSOCKS
############################################
if [ "$SHADOWSOCKS" == "on" ]; then
ARGS="-c /etc/shadowsocks.json"
if [ "$SHADOWSOCKS_LOG" == " on" ]; then
printf "[INFO] Setting ShadowSocks logging..."
ARGS="$ARGS -v"
printf "DONE\n"
fi
printf "[INFO] Setting ShadowSocks port to $SHADOWSOCKS_PORT..."
jq ".port_password = {\"$SHADOWSOCKS_PORT\":\"\"}" /etc/shadowsocks.json > /tmp/shadowsocks.json && mv /tmp/shadowsocks.json /etc/shadowsocks.json
exitOnError $?
printf "DONE\n"
printf "[INFO] Setting ShadowSocks password..."
jq ".port_password[\"$SHADOWSOCKS_PORT\"] = \"$SHADOWSOCKS_PASSWORD\"" /etc/shadowsocks.json > /tmp/shadowsocks.json && mv /tmp/shadowsocks.json /etc/shadowsocks.json
exitOnError $?
printf "DONE\n"
ARGS="$ARGS -s `jq --raw-output '.server' /etc/shadowsocks.json`"
unset -v SERVER
ARGS="$ARGS -p $SHADOWSOCKS_PORT"
ARGS="$ARGS -k $SHADOWSOCKS_PASSWORD"
ss-server $ARGS &
unset -v ARGS
fi
############################################
# READ FORWARDED PORT
############################################
if [ "$PORT_FORWARDING" == "on" ]; then
sleep 10 && /portforward.sh &
fi
############################################
# OPENVPN LAUNCH
############################################
printf "[INFO] Launching OpenVPN\n"
cd "$TARGET_PATH"
openvpn --config config.ovpn "$@"
status=$?
printf "\n =========================================\n"
printf " OpenVPN exit with status $status\n"
printf " =========================================\n\n"

13
go.mod Normal file
View File

@@ -0,0 +1,13 @@
module github.com/qdm12/gluetun
go 1.15
require (
github.com/fatih/color v1.9.0
github.com/golang/mock v1.4.4
github.com/kyokomi/emoji v2.2.4+incompatible
github.com/qdm12/golibs v0.0.0-20200712151944-a0325873bf5a
github.com/qdm12/ss-server v0.0.0-20200816232420-d227473c740e
github.com/stretchr/testify v1.6.1
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed
)

142
go.sum Normal file
View File

@@ -0,0 +1,142 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb h1:D4uzjWwKYQ5XnAvUbuvHW93esHg7F8N/OYeBBcJoTr0=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0 h1:8JV+dzJJiK46XqGLqqLav8ZfEiJECp8jlOFhpiCdZ+0=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.17.2 h1:azEQ8Fnx0jmtFF2fxsnmd6I0x6rsweUF63qqSO1NmKk=
github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonreference v0.17.0 h1:yJW3HCkTHg7NOA+gZ83IPHzUSnUzGXhGmsdiCcMexbA=
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/loads v0.17.0 h1:H22nMs3GDQk4SwAaFQ+jLNw+0xoFeCueawhZlv8MBYs=
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
github.com/go-openapi/runtime v0.17.2 h1:/ZK67ikFhQAMFFH/aPu2MaGH7QjP4wHBvHYOVIzDAw0=
github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q=
github.com/go-openapi/spec v0.17.0 h1:XNvrt8FlSVP8T1WuhbAFF6QDhJc0zsoWzX4wXARhhpE=
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/strfmt v0.17.0 h1:1isAxYf//QDTnVzbLAMrUK++0k1EjeLJU/gTOR0o3Mc=
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/validate v0.17.0 h1:pqoViQz3YLOGIhAmD0N4Lt6pa/3Gnj3ymKqQwq8iS6U=
github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gotify/go-api-client/v2 v2.0.4 h1:0w8skCr8aLBDKaQDg31LKKHUGF7rt7zdRpR+6cqIAlE=
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kyokomi/emoji v2.2.4+incompatible h1:np0woGKwx9LiHAQmwZx79Oc0rHpNw3o+3evou4BEPv4=
github.com/kyokomi/emoji v2.2.4+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee h1:P6U24L02WMfj9ymZTxl7CxS73JC99x3ukk+DBkgQGQs=
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee/go.mod h1:3uODdxMgOaPYeWU7RzZLxVtJHZ/x1f/iHkBZuKJDzuY=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/qdm12/golibs v0.0.0-20200712151944-a0325873bf5a h1:IyS72qFm+iXipadmUKXmpJScKXXK2GrD8yYfxXsnIYs=
github.com/qdm12/golibs v0.0.0-20200712151944-a0325873bf5a/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
github.com/qdm12/ss-server v0.0.0-20200816232420-d227473c740e h1:bUHhk0t90A78sjxGKUVo2doAYwySvs8XcvGCINghPlE=
github.com/qdm12/ss-server v0.0.0-20200816232420-d227473c740e/go.mod h1:ABVUkxubboL3vqBkOwDV9glX1/x7SnYrckBe5d+M/zw=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed h1:J22ig1FUekjjkmZUM7pTKixYm8DvrYsvrBZdunYeIuQ=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@@ -1,6 +0,0 @@
#!/bin/sh
out="$(ping -W 3 -c 1 -q -s 8 1.1.1.1)"
[ $? != 0 ] || exit 0
printf "$out"
exit 1

View File

@@ -1,5 +0,0 @@
#!/bin/bash
docker build --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg VCS_REF=`git rev-parse --short HEAD` \
-t $IMAGE_NAME .

View File

@@ -1,3 +0,0 @@
#!/bin/bash
curl -X POST https://hooks.microbadger.com/images/qmcgaw/${DOCKER_REPO}/tQFy7AxtSUNANPe6aoVChYdsI_I= || exit 0

25
internal/alpine/alpine.go Normal file
View File

@@ -0,0 +1,25 @@
package alpine
import (
"os/user"
"github.com/qdm12/golibs/files"
)
type Configurator interface {
CreateUser(username string, uid int) error
}
type configurator struct {
fileManager files.FileManager
lookupUID func(uid string) (*user.User, error)
lookupUser func(username string) (*user.User, error)
}
func NewConfigurator(fileManager files.FileManager) Configurator {
return &configurator{
fileManager: fileManager,
lookupUID: user.LookupId,
lookupUser: user.Lookup,
}
}

38
internal/alpine/users.go Normal file
View File

@@ -0,0 +1,38 @@
package alpine
import (
"fmt"
"os/user"
)
// CreateUser creates a user in Alpine with the given UID
func (c *configurator) CreateUser(username string, uid int) error {
UIDStr := fmt.Sprintf("%d", uid)
u, err := c.lookupUID(UIDStr)
_, unknownUID := err.(user.UnknownUserIdError)
if err != nil && !unknownUID {
return fmt.Errorf("cannot create user: %w", err)
} else if u != nil {
if u.Username == username {
return nil
}
return fmt.Errorf("user with ID %d exists with username %q instead of %q", uid, u.Username, username)
}
u, err = c.lookupUser(username)
_, unknownUsername := err.(user.UnknownUserError)
if err != nil && !unknownUsername {
return fmt.Errorf("cannot create user: %w", err)
} else if u != nil {
return fmt.Errorf("cannot create user: user with name %s already exists for ID %s instead of %d", username, u.Uid, uid)
}
passwd, err := c.fileManager.ReadFile("/etc/passwd")
if err != nil {
return fmt.Errorf("cannot create user: %w", err)
}
passwd = append(passwd, []byte(fmt.Sprintf("%s:x:%d:::/dev/null:/sbin/nologin\n", username, uid))...)
if err := c.fileManager.WriteToFile("/etc/passwd", passwd); err != nil {
return fmt.Errorf("cannot create user: %w", err)
}
return nil
}

74
internal/cli/cli.go Normal file
View File

@@ -0,0 +1,74 @@
package cli
import (
"flag"
"fmt"
"strings"
"net"
"github.com/qdm12/gluetun/internal/params"
"github.com/qdm12/gluetun/internal/provider"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
)
func ClientKey(args []string) error {
flagSet := flag.NewFlagSet("clientkey", flag.ExitOnError)
filepath := flagSet.String("path", "/files/client.key", "file path to the client.key file")
if err := flagSet.Parse(args); err != nil {
return err
}
fileManager := files.NewFileManager()
data, err := fileManager.ReadFile(*filepath)
if err != nil {
return err
}
s := string(data)
s = strings.ReplaceAll(s, "\n", "")
s = strings.ReplaceAll(s, "\r", "")
s = strings.TrimPrefix(s, "-----BEGIN PRIVATE KEY-----")
s = strings.TrimSuffix(s, "-----END PRIVATE KEY-----")
fmt.Println(s)
return nil
}
func HealthCheck() error {
ips, err := net.LookupIP("github.com")
if err != nil {
return fmt.Errorf("cannot resolve github.com (%s)", err)
} else if len(ips) == 0 {
return fmt.Errorf("resolved no IP addresses for github.com")
}
return nil
}
func OpenvpnConfig() error {
logger, err := logging.NewLogger(logging.ConsoleEncoding, logging.InfoLevel, -1)
if err != nil {
return err
}
paramsReader := params.NewReader(logger, files.NewFileManager())
allSettings, err := settings.GetAllSettings(paramsReader)
if err != nil {
return err
}
providerConf := provider.New(allSettings.OpenVPN.Provider.Name)
connections, err := providerConf.GetOpenVPNConnections(allSettings.OpenVPN.Provider.ServerSelection)
if err != nil {
return err
}
lines := providerConf.BuildConf(
connections,
allSettings.OpenVPN.Verbosity,
allSettings.System.UID,
allSettings.System.GID,
allSettings.OpenVPN.Root,
allSettings.OpenVPN.Cipher,
allSettings.OpenVPN.Auth,
allSettings.OpenVPN.Provider.ExtraConfigOptions,
)
fmt.Println(strings.Join(lines, "\n"))
return nil
}

View File

@@ -0,0 +1,23 @@
package constants
import "github.com/fatih/color"
func ColorUnbound() *color.Color {
return color.New(color.FgCyan)
}
func ColorTinyproxy() *color.Color {
return color.New(color.FgHiGreen)
}
func ColorShadowsocks() *color.Color {
return color.New(color.FgHiYellow)
}
func ColorShadowsocksError() *color.Color {
return color.New(color.FgHiRed)
}
func ColorOpenvpn() *color.Color {
return color.New(color.FgHiMagenta)
}

View File

@@ -0,0 +1,230 @@
package constants
import (
"net"
"sort"
"github.com/qdm12/gluetun/internal/models"
)
const (
CyberghostCertificate = "MIIGWjCCBEKgAwIBAgIJAJxUG61mxDS7MA0GCSqGSIb3DQEBDQUAMHsxCzAJBgNVBAYTAlJPMRIwEAYDVQQHEwlCdWNoYXJlc3QxGDAWBgNVBAoTD0N5YmVyR2hvc3QgUy5BLjEbMBkGA1UEAxMSQ3liZXJHaG9zdCBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJpbmZvQGN5YmVyZ2hvc3Qucm8wHhcNMTcwNjE5MDgxNzI1WhcNMzcwNjE0MDgxNzI1WjB7MQswCQYDVQQGEwJSTzESMBAGA1UEBxMJQnVjaGFyZXN0MRgwFgYDVQQKEw9DeWJlckdob3N0IFMuQS4xGzAZBgNVBAMTEkN5YmVyR2hvc3QgUm9vdCBDQTEhMB8GCSqGSIb3DQEJARYSaW5mb0BjeWJlcmdob3N0LnJvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7O8+mji2FlQhJXn/G4VLrKPjGtxgQBAdjo0dZEQzKX08q14dLkslmOLgShStWKrOiLXGAvB1rPvvk613jtA0KjQLpgyLy9lIWohQKYjj5jrJYXMZMkbSHBYI9L8L7iezBEFYrjYKdDo51nq99wRFhKdbyKKjDh3e2L2SVEZLT1ogkK5gWzjvH+mjjtjUUicK+YjGwWOz6I+KKaG4Ve/D/cE6nCLbhHIMMnargZEu7sqA6BFeS4kEP/ZdCZoTSX2n43XV1q63nJt/v0KDetbZDciFVW9h9SVPG4qT44p0550N+Mom7zTX7S/ID5T9dplgU8sRGtIMrG0cIMD9zmpFgUnMusCrR7jJFr0sMAveTbgZg95LmstV6R6WKZkSFdUrE0DHl4dHoZvTFX+1LhwhHgjgDLaosX0vhG/C/7LpoVWimd6RRQT3M9o4Fa1TuhfvBzQ20QHrmRV/yKvGNK0xckZ6EZ/QY7Z55ORU15Tgab4ebnblYPWoEmn0mIYP3LFFeoR5OS1EX7+j4kPv+bwPGsmpHjxmZyq2Y7sJBpbOCJgbkn52WZdPBIRDpPdIHQ8pAJC4T0iMK9xvAwWNl/V6EYYNpR97osyEDXn+BTdAHlhJ5fck9KlwI9mb1Kg1bhbvbmaIAiOLenSULYf3j6rI1ygo3R2cCyybtuAq8M7z0OECAwEAAaOB4DCB3TAdBgNVHQ4EFgQU6tdK1g/He5qzjeAoM5eHt4in9iUwga0GA1UdIwSBpTCBooAU6tdK1g/He5qzjeAoM5eHt4in9iWhf6R9MHsxCzAJBgNVBAYTAlJPMRIwEAYDVQQHEwlCdWNoYXJlc3QxGDAWBgNVBAoTD0N5YmVyR2hvc3QgUy5BLjEbMBkGA1UEAxMSQ3liZXJHaG9zdCBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJpbmZvQGN5YmVyZ2hvc3Qucm+CCQCcVButZsQ0uzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4ICAQDNyQ92kj4qiNjnHk99qvnFw9qGfwB9ofaPL74zh0G5hEe3Wgb2o4fqUGnvUNgOu53gJksz3DcPQ8t40wfmm9I1Z8tiM9qrqvkuQ+nKcLgdooXtEsTybPIYDZ2cWR/5E0TKRvC7RFzKgQ4D77Vbi4TdaHiDV7ZNfU1iLCoBGcYm80hcUHEs5KIVLwUmcSOTmbZBySJxcSD0yUpS7nlZGwLY6VQrU+JFwDSisbXT4DXf3iSzp7FzW0/u/SFvWsPHrjE0hkPoZPalYvouaJEHKAhip0ZwSmitlxbBnmm8+K/3c9mLA5/uXrirfpuhhs8V3lyV2mczVtSiTl6gpi88gc//JY80JeHdupjO25T3XEzY9cpxecmkWaUEjLMx4wVoXQuUiPonfILM6OLwi+zUS8gQErdFeGvcQXbncPa4SdJuHkF8lgiX2i8S8fPGdXvU37E9bdAXwP5nZriYq1s0D59Qfvz+vLXVkmyZp6ztxjKjKolemPMak0Y5c1Q4RjNF6tmQoFuy/ACSkWy14Tzu2dFp7UiVbGg1FOvKhfs48zC2/IUQv1arqmPT/9LVq3B2DVT9UKXRUXX/f/jSSsVjkz4uUe2jUyL+XHX1nSmROTPHSAJ+oKf0BLnfqUxFkEUTwLnayssP2nwGgq35b7wEbTFIXdrjHGFUVQIDeERz8UThew=="
CyberghostClientCertificate = "MIIGrDCCBJSgAwIBAgIEAdTnfTANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJSTzESMBAGA1UEBxMJQnVjaGFyZXN0MRgwFgYDVQQKEw9DeWJlckdob3N0IFMuQS4xGzAZBgNVBAMTEkN5YmVyR2hvc3QgUm9vdCBDQTEhMB8GCSqGSIb3DQEJARYSaW5mb0BjeWJlcmdob3N0LnJvMB4XDTIwMDcwNDE1MjkzNloXDTMwMDcwMjE1MjkzNlowfTELMAkGA1UEBhMCUk8xEjAQBgNVBAcMCUJ1Y2hhcmVzdDEYMBYGA1UECgwPQ3liZXJHaG9zdCBTLkEuMR0wGwYDVQQDDBRjLmoua2xhdmVyQGdtYWlsLmNvbTEhMB8GCSqGSIb3DQEJARYSaW5mb0BjeWJlcmdob3N0LnJvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAobp2NlGUHMNBe08YEOnVG3QJjF3ZaXbRhE/II9rmtgJTNZtDohGChvFlNRsExKzVrKxHCeuJkVffwzQ6fYk4/M1RdYLJUh0UVw3e4WdApw8E7TJZxDYm4SHQNXUvt1Rt5TjslcXxIpDZgrMSc/kHROYEL9tdgdzPZErUJehXyJPhEzIrzmAJh501x7WwKPz9ctSVlItyavqEWFF2vyUa6X9DYmD9mQTz5c+VXNO5DkXmPFBIaEVDnvFtcjGJ56yEvFnWVukL+OUX7ezowrIOFOcp9udjgpeiHq+XvsQ6ER0DJt25MiEId3NjkxtZ8BitDftTcLN/kt81hWKT7adMVc3kpIZ80cxrwRCttMd7sHAzKI9u7pMxv10eUOsIEY87ewBe3l6KvEnjA+9uIjim6gLLebDIaEH50Ee9PzNJ8fqQ2u54Ab4bt00/H1sUnJ6Ss/+WsQDOK1BsPRKKcnHZntOlHrs2Tu5+txKNU2cOapI8SjVULUNKrRXASbpfWnLUfri/HO742bJb/TjkOJcOxta3hTPFAhaRWBusVlB41XVHeuH5DAhugYXeSNK6/6Ul8YvKUNH/7QbxuGIGXfth19Xl4QLI1umyEjZopSlt3tOiO2V1soVNSQCCfxXVoCTMESMLjhkjWdmBDhdy2GTW7S4YoJfqVKiS18rYkN7I4ZMCAwEAAaOCATQwggEwMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMDQGCWCGSAGG+EIBDQQnFiVDeWJlckdob3N0IEdlbmVyYXRlZCBVc2VyIENlcnRpZmljYXRlMBEGCWCGSAGG+EIBAQQEAwIHgDAdBgNVHQ4EFgQULwUtU5s6pL2NN9gPeEnKX0dhwiswga0GA1UdIwSBpTCBooAU6tdK1g/He5qzjeAoM5eHt4in9iWhf6R9MHsxCzAJBgNVBAYTAlJPMRIwEAYDVQQHEwlCdWNoYXJlc3QxGDAWBgNVBAoTD0N5YmVyR2hvc3QgUy5BLjEbMBkGA1UEAxMSQ3liZXJHaG9zdCBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJpbmZvQGN5YmVyZ2hvc3Qucm+CCQCcVButZsQ0uzANBgkqhkiG9w0BAQsFAAOCAgEAystGIMYhQWaEdTqlnLCytrr8657t+PuidZMNNIaPB3wN2Fi2xKf14DTg03mqxjmPPb+f+PVNIOV5PdWD4jcQwOP1GEboGV0DFzlRGeAtDcvKwdee4oASJbZq1CETqDaohQTxKEWC+UBk2F36nOaEI6Sab+Mb4cR9//PAwvzOqrXuGF5NuIOX7eFtCMQSgQq6lRRqTQjekm0Dxigx4JA92Jo2qZRwCJ0T3IXBJGL831HCFJbDWv8PV3lsfFb/i2+vr54uywFQVWWp18dYi97gipfuQ4zRg2Ldx5aXSmnhhKpg5ioZvtk043QofF12YORhobElqavRbvvhZvlCouvcuoq9QKi7IPe5SJZkZ1X7ezMesCwBzwFpt6vRUAcslsNFbcYS1iSENlY/PTcDqBhbKuc9yAhq+/aUgaY/8VF5RWVzSRZufbf3BPwOkE4K0UybaobO/YX0JOkCacAD+4tdR6YSXNIMMRAOCBQvxbxFXaHzhwhzBAjdsC56FrJKwXvQrRLU3tF4P0zFMeNTay8uTtUXugDK7EnklLESuYdpUJ8bUMlAUhJBi6UFI9/icMudxXvLRvhnBW9EtKib5JnVFUovcEUt+3EJbyst05nkL4YPjQS4TC9DHdo5SyRAy1TpiOCYTbretAFZRhh6ycUN5hBeN8GMQxiMreMtDV4PEIQ="
)
func CyberghostRegionChoices() (choices []string) {
uniqueChoices := map[string]struct{}{}
for _, server := range CyberghostServers() {
uniqueChoices[server.Region] = struct{}{}
}
for choice := range uniqueChoices {
choices = append(choices, choice)
}
sort.Slice(choices, func(i, j int) bool {
return choices[i] < choices[j]
})
return choices
}
func CyberghostGroupChoices() (choices []string) {
uniqueChoices := map[string]struct{}{}
for _, server := range CyberghostServers() {
uniqueChoices[server.Group] = struct{}{}
}
for choice := range uniqueChoices {
choices = append(choices, choice)
}
sort.Slice(choices, func(i, j int) bool {
return choices[i] < choices[j]
})
return choices
}
func CyberghostServers() []models.CyberghostServer {
return []models.CyberghostServer{
{Region: "Albania", Group: "Premium TCP Europe", IPs: []net.IP{{31, 171, 152, 99}, {31, 171, 152, 100}, {31, 171, 152, 102}, {31, 171, 152, 105}, {31, 171, 152, 107}, {31, 171, 152, 109}, {31, 171, 152, 133}, {31, 171, 152, 135}, {31, 171, 152, 136}, {31, 171, 152, 139}}},
{Region: "Albania", Group: "Premium UDP Europe", IPs: []net.IP{{31, 171, 152, 99}, {31, 171, 152, 101}, {31, 171, 152, 103}, {31, 171, 152, 105}, {31, 171, 152, 106}, {31, 171, 152, 133}, {31, 171, 152, 135}, {31, 171, 152, 137}, {31, 171, 152, 138}, {31, 171, 152, 139}}},
{Region: "Algeria", Group: "Premium TCP Europe", IPs: []net.IP{{45, 133, 91, 7}, {45, 133, 91, 9}, {45, 133, 91, 12}, {45, 133, 91, 15}, {45, 133, 91, 17}, {45, 133, 91, 18}, {45, 133, 91, 20}, {45, 133, 91, 23}, {45, 133, 91, 24}, {45, 133, 91, 26}}},
{Region: "Algeria", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 91, 7}, {45, 133, 91, 10}, {45, 133, 91, 11}, {45, 133, 91, 15}, {45, 133, 91, 17}, {45, 133, 91, 18}, {45, 133, 91, 21}, {45, 133, 91, 25}, {45, 133, 91, 26}, {45, 133, 91, 29}}},
{Region: "Andorra", Group: "Premium TCP Europe", IPs: []net.IP{{45, 139, 49, 10}, {45, 139, 49, 14}, {45, 139, 49, 15}, {45, 139, 49, 16}, {45, 139, 49, 17}, {45, 139, 49, 18}, {45, 139, 49, 19}, {45, 139, 49, 23}, {45, 139, 49, 25}, {45, 139, 49, 28}}},
{Region: "Andorra", Group: "Premium UDP Europe", IPs: []net.IP{{45, 139, 49, 7}, {45, 139, 49, 9}, {45, 139, 49, 10}, {45, 139, 49, 12}, {45, 139, 49, 15}, {45, 139, 49, 16}, {45, 139, 49, 17}, {45, 139, 49, 25}, {45, 139, 49, 27}, {45, 139, 49, 28}}},
{Region: "Argentina", Group: "Premium TCP USA", IPs: []net.IP{{190, 106, 130, 16}, {190, 106, 130, 17}, {190, 106, 130, 20}, {190, 106, 130, 22}, {190, 106, 130, 23}, {190, 106, 130, 34}, {190, 106, 130, 37}, {190, 106, 130, 38}, {190, 106, 130, 44}, {190, 106, 130, 45}}},
{Region: "Argentina", Group: "Premium UDP USA", IPs: []net.IP{{190, 106, 130, 15}, {190, 106, 130, 16}, {190, 106, 130, 18}, {190, 106, 130, 19}, {190, 106, 130, 20}, {190, 106, 130, 34}, {190, 106, 130, 36}, {190, 106, 130, 37}, {190, 106, 130, 43}, {190, 106, 130, 52}}},
{Region: "Armenia", Group: "Premium TCP Europe", IPs: []net.IP{{45, 139, 50, 10}, {45, 139, 50, 12}, {45, 139, 50, 14}, {45, 139, 50, 18}, {45, 139, 50, 19}, {45, 139, 50, 20}, {45, 139, 50, 21}, {45, 139, 50, 26}, {45, 139, 50, 27}, {45, 139, 50, 29}}},
{Region: "Armenia", Group: "Premium UDP Europe", IPs: []net.IP{{45, 139, 50, 8}, {45, 139, 50, 10}, {45, 139, 50, 11}, {45, 139, 50, 14}, {45, 139, 50, 18}, {45, 139, 50, 20}, {45, 139, 50, 24}, {45, 139, 50, 26}, {45, 139, 50, 27}, {45, 139, 50, 29}}},
{Region: "Australia", Group: "Premium TCP Asia", IPs: []net.IP{{27, 50, 79, 3}, {27, 50, 79, 4}, {27, 50, 79, 5}, {27, 50, 79, 6}, {27, 50, 79, 9}, {27, 50, 79, 12}, {27, 50, 79, 14}, {103, 13, 101, 171}, {202, 130, 33, 114}, {202, 130, 33, 118}}},
{Region: "Australia", Group: "Premium UDP Asia", IPs: []net.IP{{27, 50, 79, 3}, {27, 50, 79, 6}, {27, 50, 79, 9}, {27, 50, 79, 10}, {27, 50, 79, 11}, {27, 50, 79, 13}, {103, 13, 101, 174}, {202, 130, 33, 114}, {202, 130, 33, 117}, {202, 130, 33, 118}}},
{Region: "Austria", Group: "Premium TCP Europe", IPs: []net.IP{{89, 187, 168, 133}, {89, 187, 168, 144}, {89, 187, 168, 150}, {89, 187, 168, 151}, {89, 187, 168, 162}, {89, 187, 168, 163}, {89, 187, 168, 164}, {89, 187, 168, 167}, {89, 187, 168, 178}, {89, 187, 168, 182}}},
{Region: "Austria", Group: "Premium UDP Europe", IPs: []net.IP{{89, 187, 168, 138}, {89, 187, 168, 139}, {89, 187, 168, 149}, {89, 187, 168, 150}, {89, 187, 168, 161}, {89, 187, 168, 165}, {89, 187, 168, 167}, {89, 187, 168, 168}, {89, 187, 168, 174}, {89, 187, 168, 182}}},
{Region: "Bahamas", Group: "Premium TCP USA", IPs: []net.IP{{45, 132, 143, 8}, {45, 132, 143, 10}, {45, 132, 143, 11}, {45, 132, 143, 19}, {45, 132, 143, 24}, {45, 132, 143, 28}, {45, 132, 143, 31}, {45, 132, 143, 42}, {45, 132, 143, 43}, {45, 132, 143, 44}}},
{Region: "Bahamas", Group: "Premium UDP USA", IPs: []net.IP{{45, 132, 143, 1}, {45, 132, 143, 2}, {45, 132, 143, 3}, {45, 132, 143, 5}, {45, 132, 143, 7}, {45, 132, 143, 18}, {45, 132, 143, 23}, {45, 132, 143, 30}, {45, 132, 143, 32}, {45, 132, 143, 48}}},
{Region: "Bangladesh", Group: "Premium TCP Asia", IPs: []net.IP{{45, 132, 142, 3}, {45, 132, 142, 8}, {45, 132, 142, 12}, {45, 132, 142, 20}, {45, 132, 142, 22}, {45, 132, 142, 26}, {45, 132, 142, 27}, {45, 132, 142, 37}, {45, 132, 142, 39}, {45, 132, 142, 42}}},
{Region: "Bangladesh", Group: "Premium UDP Asia", IPs: []net.IP{{45, 132, 142, 6}, {45, 132, 142, 8}, {45, 132, 142, 13}, {45, 132, 142, 18}, {45, 132, 142, 33}, {45, 132, 142, 35}, {45, 132, 142, 38}, {45, 132, 142, 41}, {45, 132, 142, 42}, {45, 132, 142, 45}}},
{Region: "Belarus", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 194, 3}, {45, 132, 194, 4}, {45, 132, 194, 26}, {45, 132, 194, 28}, {45, 132, 194, 34}, {45, 132, 194, 38}, {45, 132, 194, 39}, {45, 132, 194, 42}, {45, 132, 194, 44}, {45, 132, 194, 48}}},
{Region: "Belarus", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 194, 4}, {45, 132, 194, 5}, {45, 132, 194, 9}, {45, 132, 194, 10}, {45, 132, 194, 20}, {45, 132, 194, 25}, {45, 132, 194, 29}, {45, 132, 194, 31}, {45, 132, 194, 40}, {45, 132, 194, 45}}},
{Region: "Belgium", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 143, 52}, {37, 120, 143, 55}, {37, 120, 143, 58}, {185, 210, 217, 10}, {185, 210, 217, 54}, {185, 210, 217, 244}, {185, 210, 217, 251}, {185, 232, 21, 119}, {193, 9, 114, 228}, {193, 9, 114, 230}}},
{Region: "Belgium", Group: "Premium UDP Europe", IPs: []net.IP{{5, 253, 205, 22}, {5, 253, 205, 23}, {37, 120, 143, 165}, {185, 210, 217, 14}, {185, 210, 217, 248}, {185, 210, 217, 254}, {193, 9, 114, 212}, {193, 9, 114, 213}, {193, 9, 114, 219}, {193, 9, 114, 228}}},
{Region: "Bosnia and Herzegovina", Group: "Premium TCP Europe", IPs: []net.IP{{185, 99, 3, 57}, {185, 99, 3, 58}, {185, 99, 3, 72}, {185, 99, 3, 73}, {185, 99, 3, 74}, {185, 99, 3, 130}, {185, 99, 3, 131}, {185, 99, 3, 134}, {185, 99, 3, 135}, {185, 99, 3, 136}}},
{Region: "Bosnia and Herzegovina", Group: "Premium UDP Europe", IPs: []net.IP{{185, 99, 3, 57}, {185, 99, 3, 58}, {185, 99, 3, 72}, {185, 99, 3, 73}, {185, 99, 3, 74}, {185, 99, 3, 130}, {185, 99, 3, 131}, {185, 99, 3, 134}, {185, 99, 3, 135}, {185, 99, 3, 136}}},
{Region: "Brazil", Group: "Premium TCP USA", IPs: []net.IP{{45, 231, 207, 65}, {45, 231, 207, 67}, {45, 231, 207, 68}, {45, 231, 207, 69}, {45, 231, 207, 75}, {177, 67, 81, 170}, {181, 41, 203, 98}, {181, 41, 203, 100}, {181, 41, 203, 102}, {181, 41, 203, 110}}},
{Region: "Brazil", Group: "Premium UDP USA", IPs: []net.IP{{45, 231, 207, 77}, {177, 67, 81, 163}, {177, 67, 81, 164}, {177, 67, 81, 165}, {177, 67, 81, 167}, {177, 67, 81, 170}, {177, 67, 81, 173}, {177, 67, 81, 174}, {181, 41, 203, 103}, {181, 41, 203, 104}}},
{Region: "Bulgaria", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 152, 99}, {37, 120, 152, 102}, {37, 120, 152, 103}, {37, 120, 152, 104}, {37, 120, 152, 105}, {37, 120, 152, 106}, {37, 120, 152, 107}, {37, 120, 152, 108}, {37, 120, 152, 109}, {37, 120, 152, 110}}},
{Region: "Bulgaria", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 152, 99}, {37, 120, 152, 100}, {37, 120, 152, 101}, {37, 120, 152, 103}, {37, 120, 152, 104}, {37, 120, 152, 105}, {37, 120, 152, 107}, {37, 120, 152, 108}, {37, 120, 152, 109}, {37, 120, 152, 110}}},
{Region: "Cambodia", Group: "Premium TCP Asia", IPs: []net.IP{{188, 215, 235, 36}, {188, 215, 235, 38}, {188, 215, 235, 39}, {188, 215, 235, 40}, {188, 215, 235, 43}, {188, 215, 235, 46}, {188, 215, 235, 47}, {188, 215, 235, 48}, {188, 215, 235, 51}, {188, 215, 235, 54}}},
{Region: "Cambodia", Group: "Premium UDP Asia", IPs: []net.IP{{188, 215, 235, 35}, {188, 215, 235, 36}, {188, 215, 235, 37}, {188, 215, 235, 38}, {188, 215, 235, 45}, {188, 215, 235, 46}, {188, 215, 235, 52}, {188, 215, 235, 53}, {188, 215, 235, 55}, {188, 215, 235, 58}}},
{Region: "Canada", Group: "Premium TCP USA", IPs: []net.IP{{37, 120, 205, 8}, {37, 120, 205, 28}, {104, 245, 145, 163}, {104, 245, 146, 38}, {104, 245, 146, 101}, {176, 113, 74, 44}, {176, 113, 74, 52}, {176, 113, 74, 67}, {176, 113, 74, 126}, {176, 113, 74, 217}}},
{Region: "Canada", Group: "Premium UDP USA", IPs: []net.IP{{37, 120, 205, 40}, {54, 39, 11, 194}, {104, 245, 145, 164}, {104, 245, 146, 41}, {139, 28, 218, 86}, {139, 28, 218, 87}, {176, 113, 74, 19}, {176, 113, 74, 25}, {176, 113, 74, 30}, {176, 113, 74, 195}}},
{Region: "Chile", Group: "Premium TCP USA", IPs: []net.IP{{190, 105, 239, 129}, {190, 105, 239, 130}, {190, 105, 239, 131}, {190, 105, 239, 132}, {190, 105, 239, 133}, {190, 105, 239, 134}, {190, 105, 239, 135}, {190, 105, 239, 136}, {190, 105, 239, 137}, {190, 105, 239, 138}}},
{Region: "Chile", Group: "Premium UDP USA", IPs: []net.IP{{190, 105, 239, 129}, {190, 105, 239, 130}, {190, 105, 239, 131}, {190, 105, 239, 132}, {190, 105, 239, 133}, {190, 105, 239, 134}, {190, 105, 239, 135}, {190, 105, 239, 136}, {190, 105, 239, 137}, {190, 105, 239, 138}}},
{Region: "China", Group: "Premium TCP Asia", IPs: []net.IP{{45, 132, 193, 2}, {45, 132, 193, 3}, {45, 132, 193, 9}, {45, 132, 193, 10}, {45, 132, 193, 12}, {45, 132, 193, 13}, {45, 132, 193, 32}, {45, 132, 193, 36}, {45, 132, 193, 43}, {45, 132, 193, 45}}},
{Region: "China", Group: "Premium UDP Asia", IPs: []net.IP{{45, 132, 193, 2}, {45, 132, 193, 3}, {45, 132, 193, 4}, {45, 132, 193, 14}, {45, 132, 193, 19}, {45, 132, 193, 22}, {45, 132, 193, 30}, {45, 132, 193, 35}, {45, 132, 193, 36}, {45, 132, 193, 42}}},
{Region: "Colombia", Group: "Premium TCP USA", IPs: []net.IP{{190, 105, 229, 19}, {190, 105, 229, 20}, {190, 105, 229, 21}, {190, 105, 229, 22}}},
{Region: "Colombia", Group: "Premium UDP USA", IPs: []net.IP{{190, 105, 229, 19}, {190, 105, 229, 20}, {190, 105, 229, 21}, {190, 105, 229, 22}}},
{Region: "Costa Rica", Group: "Premium TCP USA", IPs: []net.IP{{143, 202, 160, 67}, {143, 202, 160, 69}, {143, 202, 160, 70}, {143, 202, 160, 72}, {143, 202, 160, 73}, {143, 202, 160, 74}, {143, 202, 160, 75}, {143, 202, 160, 76}, {143, 202, 160, 77}, {143, 202, 160, 78}}},
{Region: "Costa Rica", Group: "Premium UDP USA", IPs: []net.IP{{143, 202, 160, 67}, {143, 202, 160, 68}, {143, 202, 160, 69}, {143, 202, 160, 70}, {143, 202, 160, 71}, {143, 202, 160, 73}, {143, 202, 160, 74}, {143, 202, 160, 75}, {143, 202, 160, 76}, {143, 202, 160, 78}}},
{Region: "Cyprus", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 137, 8}, {45, 132, 137, 11}, {45, 132, 137, 12}, {45, 132, 137, 15}, {45, 132, 137, 17}, {45, 132, 137, 18}, {45, 132, 137, 19}, {45, 132, 137, 23}, {45, 132, 137, 26}, {45, 132, 137, 28}}},
{Region: "Cyprus", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 137, 8}, {45, 132, 137, 10}, {45, 132, 137, 16}, {45, 132, 137, 19}, {45, 132, 137, 20}, {45, 132, 137, 21}, {45, 132, 137, 22}, {45, 132, 137, 23}, {45, 132, 137, 25}, {45, 132, 137, 28}}},
{Region: "Czech Republic", Group: "Premium TCP Europe", IPs: []net.IP{{195, 181, 160, 66}, {195, 181, 160, 72}, {195, 181, 161, 7}, {195, 181, 161, 9}, {195, 181, 161, 10}, {195, 181, 161, 11}, {195, 181, 161, 14}, {195, 181, 161, 17}, {195, 181, 161, 23}, {195, 181, 161, 25}}},
{Region: "Czech Republic", Group: "Premium UDP Europe", IPs: []net.IP{{185, 216, 35, 231}, {185, 216, 35, 232}, {185, 216, 35, 235}, {195, 181, 160, 75}, {195, 181, 161, 2}, {195, 181, 161, 8}, {195, 181, 161, 15}, {195, 181, 161, 16}, {195, 181, 161, 23}, {195, 181, 161, 25}}},
{Region: "Denmark", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 145, 93}, {37, 120, 194, 39}, {37, 120, 194, 41}, {37, 120, 194, 53}, {95, 174, 65, 166}, {95, 174, 65, 167}, {95, 174, 65, 174}, {185, 206, 224, 230}, {185, 206, 224, 235}, {185, 206, 224, 253}}},
{Region: "Denmark", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 145, 84}, {37, 120, 145, 88}, {37, 120, 194, 38}, {37, 120, 194, 58}, {95, 174, 65, 163}, {95, 174, 65, 165}, {185, 206, 224, 227}, {185, 206, 224, 243}, {185, 206, 224, 245}, {185, 206, 224, 253}}},
{Region: "Egypt", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 139, 7}, {45, 132, 139, 22}, {45, 132, 139, 24}, {45, 132, 139, 27}, {45, 132, 139, 28}, {45, 132, 139, 29}, {188, 214, 122, 36}, {188, 214, 122, 41}, {188, 214, 122, 52}, {188, 214, 122, 56}}},
{Region: "Egypt", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 139, 7}, {45, 132, 139, 17}, {45, 132, 139, 18}, {45, 132, 139, 22}, {45, 132, 139, 23}, {188, 214, 122, 41}, {188, 214, 122, 48}, {188, 214, 122, 49}, {188, 214, 122, 51}, {188, 214, 122, 61}}},
{Region: "Estonia", Group: "Premium TCP Europe", IPs: []net.IP{{77, 247, 111, 6}, {77, 247, 111, 11}, {77, 247, 111, 52}, {77, 247, 111, 53}, {77, 247, 111, 55}, {77, 247, 111, 56}, {77, 247, 111, 57}, {77, 247, 111, 60}, {77, 247, 111, 61}, {77, 247, 111, 62}}},
{Region: "Estonia", Group: "Premium UDP Europe", IPs: []net.IP{{77, 247, 111, 3}, {77, 247, 111, 4}, {77, 247, 111, 5}, {77, 247, 111, 7}, {77, 247, 111, 11}, {77, 247, 111, 12}, {77, 247, 111, 52}, {77, 247, 111, 53}, {77, 247, 111, 55}, {77, 247, 111, 59}}},
{Region: "Finland", Group: "Premium TCP Europe", IPs: []net.IP{{194, 34, 133, 171}, {194, 34, 133, 172}, {194, 34, 133, 176}, {194, 34, 133, 179}, {194, 34, 133, 180}, {194, 34, 133, 195}, {194, 34, 133, 196}, {194, 34, 133, 204}, {194, 34, 133, 207}, {194, 34, 133, 208}}},
{Region: "Finland", Group: "Premium UDP Europe", IPs: []net.IP{{194, 34, 133, 163}, {194, 34, 133, 164}, {194, 34, 133, 167}, {194, 34, 133, 178}, {194, 34, 133, 192}, {194, 34, 133, 201}, {194, 34, 133, 205}, {194, 34, 133, 206}, {194, 34, 133, 208}, {194, 34, 133, 214}}},
{Region: "France", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 60, 21}, {84, 17, 60, 33}, {84, 17, 60, 89}, {84, 17, 60, 92}, {84, 17, 60, 114}, {84, 17, 61, 23}, {84, 17, 61, 43}, {84, 17, 61, 111}, {84, 17, 61, 235}, {151, 106, 8, 36}}},
{Region: "France", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 60, 8}, {84, 17, 60, 54}, {84, 17, 60, 161}, {84, 17, 60, 188}, {84, 17, 61, 32}, {84, 17, 61, 101}, {84, 17, 61, 163}, {84, 17, 61, 187}, {84, 17, 61, 213}, {151, 106, 8, 46}}},
{Region: "Georgia", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 138, 7}, {45, 132, 138, 8}, {45, 132, 138, 9}, {45, 132, 138, 12}, {45, 132, 138, 14}, {45, 132, 138, 18}, {45, 132, 138, 19}, {45, 132, 138, 20}, {45, 132, 138, 23}, {45, 132, 138, 27}}},
{Region: "Georgia", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 138, 7}, {45, 132, 138, 8}, {45, 132, 138, 9}, {45, 132, 138, 10}, {45, 132, 138, 17}, {45, 132, 138, 18}, {45, 132, 138, 25}, {45, 132, 138, 26}, {45, 132, 138, 27}, {45, 132, 138, 28}}},
{Region: "Germany", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 217, 110}, {84, 17, 48, 75}, {84, 17, 48, 100}, {84, 17, 48, 182}, {84, 17, 49, 129}, {154, 28, 188, 50}, {154, 28, 188, 128}, {178, 162, 208, 155}, {178, 162, 209, 72}, {178, 162, 216, 49}}},
{Region: "Germany", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 217, 5}, {37, 120, 217, 27}, {84, 17, 48, 20}, {84, 17, 48, 64}, {84, 17, 48, 68}, {84, 17, 48, 182}, {84, 17, 49, 141}, {84, 17, 49, 144}, {154, 28, 188, 90}, {154, 28, 188, 143}}},
{Region: "Greece", Group: "Premium TCP Europe", IPs: []net.IP{{154, 57, 3, 130}, {154, 57, 3, 132}, {154, 57, 3, 135}, {154, 57, 3, 138}, {154, 57, 3, 140}, {188, 123, 126, 168}, {188, 123, 126, 170}, {188, 123, 126, 176}, {188, 123, 126, 177}, {188, 123, 126, 178}}},
{Region: "Greece", Group: "Premium UDP Europe", IPs: []net.IP{{154, 57, 3, 130}, {154, 57, 3, 132}, {154, 57, 3, 133}, {154, 57, 3, 137}, {188, 123, 126, 167}, {188, 123, 126, 170}, {188, 123, 126, 172}, {188, 123, 126, 173}, {188, 123, 126, 174}, {188, 123, 126, 177}}},
{Region: "Greenland", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 209, 8}, {45, 131, 209, 9}, {45, 131, 209, 12}, {45, 131, 209, 16}, {45, 131, 209, 18}, {45, 131, 209, 20}, {45, 131, 209, 22}, {45, 131, 209, 23}, {45, 131, 209, 26}, {45, 131, 209, 27}}},
{Region: "Greenland", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 209, 6}, {45, 131, 209, 11}, {45, 131, 209, 19}, {45, 131, 209, 20}, {45, 131, 209, 21}, {45, 131, 209, 23}, {45, 131, 209, 26}, {45, 131, 209, 27}, {45, 131, 209, 28}, {45, 131, 209, 29}}},
{Region: "Hong Kong", Group: "Premium TCP Asia", IPs: []net.IP{{84, 17, 56, 130}, {84, 17, 56, 136}, {84, 17, 56, 148}, {84, 17, 56, 149}, {84, 17, 56, 152}, {84, 17, 56, 172}, {84, 17, 56, 173}, {84, 17, 56, 175}, {84, 17, 56, 176}, {84, 17, 56, 181}}},
{Region: "Hong Kong", Group: "Premium UDP Asia", IPs: []net.IP{{84, 17, 56, 133}, {84, 17, 56, 140}, {84, 17, 56, 146}, {84, 17, 56, 151}, {84, 17, 56, 162}, {84, 17, 56, 166}, {84, 17, 56, 172}, {84, 17, 56, 174}, {84, 17, 56, 176}, {84, 17, 56, 182}}},
{Region: "Hungary", Group: "Premium TCP Europe", IPs: []net.IP{{185, 104, 187, 83}, {185, 104, 187, 85}, {185, 104, 187, 86}, {185, 104, 187, 92}, {185, 189, 114, 116}, {185, 189, 114, 119}, {185, 189, 114, 120}, {185, 189, 114, 121}, {185, 189, 114, 124}, {185, 189, 114, 125}}},
{Region: "Hungary", Group: "Premium UDP Europe", IPs: []net.IP{{185, 104, 187, 83}, {185, 104, 187, 85}, {185, 104, 187, 93}, {185, 189, 114, 115}, {185, 189, 114, 118}, {185, 189, 114, 119}, {185, 189, 114, 120}, {185, 189, 114, 121}, {185, 189, 114, 123}, {185, 189, 114, 125}}},
{Region: "Iceland", Group: "Premium TCP Europe", IPs: []net.IP{{213, 167, 139, 19}, {213, 167, 139, 20}, {213, 167, 139, 21}, {213, 167, 139, 22}, {213, 167, 139, 23}, {213, 167, 139, 24}, {213, 167, 139, 25}, {213, 167, 139, 26}, {213, 167, 139, 28}, {213, 167, 139, 30}}},
{Region: "Iceland", Group: "Premium UDP Europe", IPs: []net.IP{{213, 167, 139, 19}, {213, 167, 139, 21}, {213, 167, 139, 22}, {213, 167, 139, 24}, {213, 167, 139, 25}, {213, 167, 139, 26}, {213, 167, 139, 27}, {213, 167, 139, 28}, {213, 167, 139, 29}, {213, 167, 139, 30}}},
{Region: "India", Group: "Premium TCP Europe", IPs: []net.IP{{43, 241, 71, 115}, {43, 241, 71, 118}, {43, 241, 71, 120}, {43, 241, 71, 122}, {43, 241, 71, 125}, {43, 241, 71, 147}, {43, 241, 71, 148}, {43, 241, 71, 149}, {43, 241, 71, 154}, {43, 241, 71, 156}}},
{Region: "India", Group: "Premium UDP Europe", IPs: []net.IP{{43, 241, 71, 116}, {43, 241, 71, 117}, {43, 241, 71, 118}, {43, 241, 71, 122}, {43, 241, 71, 125}, {43, 241, 71, 147}, {43, 241, 71, 148}, {43, 241, 71, 153}, {43, 241, 71, 155}, {43, 241, 71, 156}}},
{Region: "Indonesia", Group: "Premium TCP Asia", IPs: []net.IP{{113, 20, 29, 243}, {113, 20, 29, 244}, {113, 20, 29, 246}, {113, 20, 29, 247}, {113, 20, 29, 248}, {113, 20, 29, 249}, {113, 20, 29, 250}, {113, 20, 29, 251}, {113, 20, 29, 252}, {113, 20, 29, 254}}},
{Region: "Indonesia", Group: "Premium UDP Asia", IPs: []net.IP{{113, 20, 29, 243}, {113, 20, 29, 245}, {113, 20, 29, 246}, {113, 20, 29, 247}, {113, 20, 29, 248}, {113, 20, 29, 249}, {113, 20, 29, 250}, {113, 20, 29, 251}, {113, 20, 29, 252}, {113, 20, 29, 253}}},
{Region: "Iran", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 4, 10}, {45, 131, 4, 16}, {45, 131, 4, 17}, {45, 131, 4, 18}, {45, 131, 4, 20}, {45, 131, 4, 21}, {45, 131, 4, 22}, {45, 131, 4, 24}, {45, 131, 4, 25}, {45, 131, 4, 28}}},
{Region: "Iran", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 4, 6}, {45, 131, 4, 8}, {45, 131, 4, 12}, {45, 131, 4, 14}, {45, 131, 4, 15}, {45, 131, 4, 20}, {45, 131, 4, 23}, {45, 131, 4, 24}, {45, 131, 4, 27}, {45, 131, 4, 28}}},
{Region: "Ireland", Group: "Premium TCP Europe", IPs: []net.IP{{84, 247, 48, 3}, {84, 247, 48, 4}, {84, 247, 48, 5}, {84, 247, 48, 8}, {84, 247, 48, 9}, {84, 247, 48, 10}, {84, 247, 48, 11}, {84, 247, 48, 12}, {84, 247, 48, 19}, {84, 247, 48, 21}}},
{Region: "Ireland", Group: "Premium UDP Europe", IPs: []net.IP{{84, 247, 48, 3}, {84, 247, 48, 13}, {84, 247, 48, 14}, {84, 247, 48, 20}, {84, 247, 48, 21}, {84, 247, 48, 23}, {84, 247, 48, 24}, {84, 247, 48, 25}, {84, 247, 48, 26}, {84, 247, 48, 27}}},
{Region: "Isle of Man", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 140, 7}, {45, 132, 140, 8}, {45, 132, 140, 10}, {45, 132, 140, 11}, {45, 132, 140, 15}, {45, 132, 140, 17}, {45, 132, 140, 18}, {45, 132, 140, 19}, {45, 132, 140, 27}, {45, 132, 140, 28}}},
{Region: "Isle of Man", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 140, 7}, {45, 132, 140, 9}, {45, 132, 140, 10}, {45, 132, 140, 16}, {45, 132, 140, 19}, {45, 132, 140, 20}, {45, 132, 140, 21}, {45, 132, 140, 25}, {45, 132, 140, 26}, {45, 132, 140, 28}}},
{Region: "Israel", Group: "Premium TCP Europe", IPs: []net.IP{{160, 116, 0, 163}, {160, 116, 0, 164}, {160, 116, 0, 166}, {160, 116, 0, 167}, {160, 116, 0, 169}, {160, 116, 0, 170}, {160, 116, 0, 171}, {160, 116, 0, 172}, {160, 116, 0, 173}, {160, 116, 0, 174}}},
{Region: "Israel", Group: "Premium UDP Europe", IPs: []net.IP{{160, 116, 0, 163}, {160, 116, 0, 165}, {160, 116, 0, 166}, {160, 116, 0, 167}, {160, 116, 0, 168}, {160, 116, 0, 169}, {160, 116, 0, 170}, {160, 116, 0, 172}, {160, 116, 0, 173}, {160, 116, 0, 174}}},
{Region: "Italy", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 58, 11}, {84, 17, 58, 19}, {87, 101, 94, 70}, {87, 101, 94, 116}, {185, 217, 71, 133}, {185, 217, 71, 137}, {212, 102, 55, 100}, {212, 102, 55, 123}, {212, 102, 55, 139}, {212, 102, 55, 184}}},
{Region: "Italy", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 58, 7}, {84, 17, 58, 100}, {84, 17, 58, 103}, {84, 17, 58, 104}, {87, 101, 94, 116}, {87, 101, 94, 124}, {185, 217, 71, 132}, {185, 217, 71, 134}, {212, 102, 55, 156}, {212, 102, 55, 167}}},
{Region: "Japan", Group: "Premium TCP Asia", IPs: []net.IP{{156, 146, 35, 5}, {156, 146, 35, 8}, {156, 146, 35, 9}, {156, 146, 35, 17}, {156, 146, 35, 20}, {156, 146, 35, 33}, {156, 146, 35, 36}, {156, 146, 35, 42}, {156, 146, 35, 45}, {156, 146, 35, 50}}},
{Region: "Japan", Group: "Premium UDP Asia", IPs: []net.IP{{156, 146, 35, 6}, {156, 146, 35, 8}, {156, 146, 35, 19}, {156, 146, 35, 22}, {156, 146, 35, 27}, {156, 146, 35, 32}, {156, 146, 35, 35}, {156, 146, 35, 37}, {156, 146, 35, 41}, {156, 146, 35, 47}}},
{Region: "Kazakhstan", Group: "Premium TCP Europe", IPs: []net.IP{{45, 133, 88, 7}, {45, 133, 88, 11}, {45, 133, 88, 12}, {45, 133, 88, 13}, {45, 133, 88, 14}, {45, 133, 88, 20}, {45, 133, 88, 23}, {45, 133, 88, 24}, {45, 133, 88, 26}, {45, 133, 88, 28}}},
{Region: "Kazakhstan", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 88, 8}, {45, 133, 88, 12}, {45, 133, 88, 13}, {45, 133, 88, 15}, {45, 133, 88, 19}, {45, 133, 88, 20}, {45, 133, 88, 24}, {45, 133, 88, 25}, {45, 133, 88, 26}, {45, 133, 88, 28}}},
{Region: "Kenya", Group: "Premium TCP Asia", IPs: []net.IP{{62, 12, 118, 195}, {62, 12, 118, 196}, {62, 12, 118, 197}, {62, 12, 118, 198}, {62, 12, 118, 199}, {62, 12, 118, 200}, {62, 12, 118, 201}, {62, 12, 118, 202}, {62, 12, 118, 203}, {62, 12, 118, 204}}},
{Region: "Kenya", Group: "Premium UDP Asia", IPs: []net.IP{{62, 12, 118, 195}, {62, 12, 118, 196}, {62, 12, 118, 197}, {62, 12, 118, 198}, {62, 12, 118, 199}, {62, 12, 118, 200}, {62, 12, 118, 201}, {62, 12, 118, 202}, {62, 12, 118, 203}, {62, 12, 118, 204}}},
{Region: "Korea", Group: "Premium TCP Asia", IPs: []net.IP{{27, 255, 75, 227}, {27, 255, 75, 229}, {27, 255, 75, 233}, {27, 255, 75, 234}, {27, 255, 75, 235}, {27, 255, 75, 236}, {27, 255, 75, 248}, {27, 255, 75, 249}, {27, 255, 75, 251}, {27, 255, 75, 254}}},
{Region: "Korea", Group: "Premium UDP Asia", IPs: []net.IP{{27, 255, 75, 228}, {27, 255, 75, 229}, {27, 255, 75, 230}, {27, 255, 75, 231}, {27, 255, 75, 234}, {27, 255, 75, 235}, {27, 255, 75, 244}, {27, 255, 75, 245}, {27, 255, 75, 246}, {27, 255, 75, 247}}},
{Region: "Latvia", Group: "Premium TCP Europe", IPs: []net.IP{{109, 248, 148, 244}, {109, 248, 148, 251}, {109, 248, 148, 252}, {109, 248, 148, 253}, {109, 248, 149, 19}, {109, 248, 149, 20}, {109, 248, 149, 21}, {109, 248, 149, 22}, {109, 248, 149, 24}, {109, 248, 149, 27}}},
{Region: "Latvia", Group: "Premium UDP Europe", IPs: []net.IP{{109, 248, 148, 248}, {109, 248, 148, 252}, {109, 248, 148, 253}, {109, 248, 148, 254}, {109, 248, 149, 19}, {109, 248, 149, 20}, {109, 248, 149, 21}, {109, 248, 149, 24}, {109, 248, 149, 28}, {109, 248, 149, 29}}},
{Region: "Liechtenstein", Group: "Premium TCP Europe", IPs: []net.IP{{45, 139, 48, 6}, {45, 139, 48, 7}, {45, 139, 48, 9}, {45, 139, 48, 16}, {45, 139, 48, 17}, {45, 139, 48, 19}, {45, 139, 48, 20}, {45, 139, 48, 22}, {45, 139, 48, 23}, {45, 139, 48, 27}}},
{Region: "Liechtenstein", Group: "Premium UDP Europe", IPs: []net.IP{{45, 139, 48, 9}, {45, 139, 48, 12}, {45, 139, 48, 15}, {45, 139, 48, 16}, {45, 139, 48, 17}, {45, 139, 48, 18}, {45, 139, 48, 24}, {45, 139, 48, 26}, {45, 139, 48, 28}, {45, 139, 48, 29}}},
{Region: "Lithuania", Group: "Premium TCP Europe", IPs: []net.IP{{85, 206, 162, 211}, {85, 206, 162, 213}, {85, 206, 162, 214}, {85, 206, 162, 216}, {85, 206, 162, 219}, {85, 206, 162, 220}, {85, 206, 165, 18}, {85, 206, 165, 20}, {85, 206, 165, 24}, {85, 206, 165, 25}}},
{Region: "Lithuania", Group: "Premium UDP Europe", IPs: []net.IP{{85, 206, 162, 210}, {85, 206, 162, 211}, {85, 206, 162, 214}, {85, 206, 162, 215}, {85, 206, 162, 220}, {85, 206, 162, 221}, {85, 206, 162, 222}, {85, 206, 165, 18}, {85, 206, 165, 25}, {85, 206, 165, 26}}},
{Region: "Luxembourg", Group: "Premium TCP Europe", IPs: []net.IP{{5, 253, 204, 5}, {5, 253, 204, 6}, {5, 253, 204, 9}, {5, 253, 204, 10}, {5, 253, 204, 12}, {5, 253, 204, 14}, {5, 253, 204, 20}, {5, 253, 204, 23}, {5, 253, 204, 29}, {5, 253, 204, 30}}},
{Region: "Luxembourg", Group: "Premium UDP Europe", IPs: []net.IP{{5, 253, 204, 7}, {5, 253, 204, 11}, {5, 253, 204, 20}, {5, 253, 204, 22}, {5, 253, 204, 23}, {5, 253, 204, 26}, {5, 253, 204, 27}, {5, 253, 204, 28}, {5, 253, 204, 29}, {5, 253, 204, 30}}},
{Region: "Macao", Group: "Premium TCP Asia", IPs: []net.IP{{45, 137, 197, 8}, {45, 137, 197, 9}, {45, 137, 197, 12}, {45, 137, 197, 14}, {45, 137, 197, 17}, {45, 137, 197, 33}, {45, 137, 197, 35}, {45, 137, 197, 42}, {45, 137, 197, 45}, {45, 137, 197, 47}}},
{Region: "Macao", Group: "Premium UDP Asia", IPs: []net.IP{{45, 137, 197, 2}, {45, 137, 197, 7}, {45, 137, 197, 18}, {45, 137, 197, 19}, {45, 137, 197, 28}, {45, 137, 197, 30}, {45, 137, 197, 33}, {45, 137, 197, 35}, {45, 137, 197, 44}, {45, 137, 197, 47}}},
{Region: "Macedonia", Group: "Premium TCP Europe", IPs: []net.IP{{185, 225, 28, 3}, {185, 225, 28, 4}, {185, 225, 28, 5}, {185, 225, 28, 6}, {185, 225, 28, 7}, {185, 225, 28, 8}, {185, 225, 28, 9}, {185, 225, 28, 10}, {185, 225, 28, 11}, {185, 225, 28, 12}}},
{Region: "Macedonia", Group: "Premium UDP Europe", IPs: []net.IP{{185, 225, 28, 3}, {185, 225, 28, 4}, {185, 225, 28, 5}, {185, 225, 28, 6}, {185, 225, 28, 7}, {185, 225, 28, 8}, {185, 225, 28, 9}, {185, 225, 28, 10}, {185, 225, 28, 11}, {185, 225, 28, 12}}},
{Region: "Malaysia", Group: "Premium TCP Asia", IPs: []net.IP{{139, 5, 177, 69}, {139, 5, 177, 70}, {139, 5, 177, 71}, {139, 5, 177, 72}, {139, 5, 177, 73}, {139, 5, 177, 74}, {139, 5, 177, 75}, {139, 5, 177, 76}, {139, 5, 177, 77}, {139, 5, 177, 78}}},
{Region: "Malaysia", Group: "Premium UDP Asia", IPs: []net.IP{{139, 5, 177, 69}, {139, 5, 177, 70}, {139, 5, 177, 71}, {139, 5, 177, 72}, {139, 5, 177, 73}, {139, 5, 177, 74}, {139, 5, 177, 75}, {139, 5, 177, 76}, {139, 5, 177, 77}, {139, 5, 177, 78}}},
{Region: "Malta", Group: "Premium TCP Europe", IPs: []net.IP{{45, 137, 198, 6}, {45, 137, 198, 8}, {45, 137, 198, 9}, {45, 137, 198, 11}, {45, 137, 198, 16}, {45, 137, 198, 18}, {45, 137, 198, 22}, {45, 137, 198, 23}, {45, 137, 198, 25}, {45, 137, 198, 27}}},
{Region: "Malta", Group: "Premium UDP Europe", IPs: []net.IP{{45, 137, 198, 8}, {45, 137, 198, 10}, {45, 137, 198, 12}, {45, 137, 198, 13}, {45, 137, 198, 16}, {45, 137, 198, 17}, {45, 137, 198, 21}, {45, 137, 198, 23}, {45, 137, 198, 26}, {45, 137, 198, 28}}},
{Region: "Mexico", Group: "Premium TCP USA", IPs: []net.IP{{45, 133, 180, 99}, {45, 133, 180, 106}, {45, 133, 180, 107}, {45, 133, 180, 108}, {45, 133, 180, 115}, {45, 133, 180, 118}, {45, 133, 180, 119}, {45, 133, 180, 121}, {45, 133, 180, 122}, {45, 133, 180, 123}}},
{Region: "Mexico", Group: "Premium UDP USA", IPs: []net.IP{{45, 133, 180, 99}, {45, 133, 180, 101}, {45, 133, 180, 104}, {45, 133, 180, 105}, {45, 133, 180, 106}, {45, 133, 180, 115}, {45, 133, 180, 117}, {45, 133, 180, 118}, {45, 133, 180, 120}, {45, 133, 180, 121}}},
{Region: "Moldova", Group: "Premium TCP Europe", IPs: []net.IP{{178, 175, 130, 243}, {178, 175, 130, 245}, {178, 175, 130, 250}, {178, 175, 130, 251}, {178, 175, 130, 253}, {178, 175, 130, 254}, {178, 175, 142, 131}, {178, 175, 142, 132}, {178, 175, 142, 133}, {178, 175, 142, 134}}},
{Region: "Moldova", Group: "Premium UDP Europe", IPs: []net.IP{{178, 175, 130, 243}, {178, 175, 130, 244}, {178, 175, 130, 245}, {178, 175, 130, 246}, {178, 175, 130, 250}, {178, 175, 130, 251}, {178, 175, 130, 253}, {178, 175, 130, 254}, {178, 175, 142, 132}, {178, 175, 142, 133}}},
{Region: "Monaco", Group: "Premium TCP Europe", IPs: []net.IP{{45, 137, 199, 7}, {45, 137, 199, 9}, {45, 137, 199, 11}, {45, 137, 199, 12}, {45, 137, 199, 14}, {45, 137, 199, 16}, {45, 137, 199, 20}, {45, 137, 199, 24}, {45, 137, 199, 25}, {45, 137, 199, 29}}},
{Region: "Monaco", Group: "Premium UDP Europe", IPs: []net.IP{{45, 137, 199, 6}, {45, 137, 199, 7}, {45, 137, 199, 10}, {45, 137, 199, 16}, {45, 137, 199, 18}, {45, 137, 199, 22}, {45, 137, 199, 23}, {45, 137, 199, 24}, {45, 137, 199, 25}, {45, 137, 199, 27}}},
{Region: "Mongolia", Group: "Premium TCP Asia", IPs: []net.IP{{45, 139, 51, 4}, {45, 139, 51, 13}, {45, 139, 51, 14}, {45, 139, 51, 17}, {45, 139, 51, 18}, {45, 139, 51, 22}, {45, 139, 51, 23}, {45, 139, 51, 32}, {45, 139, 51, 44}, {45, 139, 51, 48}}},
{Region: "Mongolia", Group: "Premium UDP Asia", IPs: []net.IP{{45, 139, 51, 3}, {45, 139, 51, 6}, {45, 139, 51, 11}, {45, 139, 51, 12}, {45, 139, 51, 17}, {45, 139, 51, 20}, {45, 139, 51, 28}, {45, 139, 51, 30}, {45, 139, 51, 37}, {45, 139, 51, 41}}},
{Region: "Montenegro", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 208, 6}, {45, 131, 208, 7}, {45, 131, 208, 8}, {45, 131, 208, 9}, {45, 131, 208, 10}, {45, 131, 208, 13}, {45, 131, 208, 14}, {45, 131, 208, 20}, {45, 131, 208, 24}, {45, 131, 208, 28}}},
{Region: "Montenegro", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 208, 6}, {45, 131, 208, 8}, {45, 131, 208, 9}, {45, 131, 208, 12}, {45, 131, 208, 16}, {45, 131, 208, 20}, {45, 131, 208, 22}, {45, 131, 208, 24}, {45, 131, 208, 26}, {45, 131, 208, 28}}},
{Region: "Morocco", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 211, 6}, {45, 131, 211, 9}, {45, 131, 211, 11}, {45, 131, 211, 13}, {45, 131, 211, 18}, {45, 131, 211, 21}, {45, 131, 211, 22}, {45, 131, 211, 23}, {45, 131, 211, 28}, {45, 131, 211, 29}}},
{Region: "Morocco", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 211, 9}, {45, 131, 211, 10}, {45, 131, 211, 13}, {45, 131, 211, 18}, {45, 131, 211, 20}, {45, 131, 211, 21}, {45, 131, 211, 25}, {45, 131, 211, 26}, {45, 131, 211, 28}, {45, 131, 211, 29}}},
{Region: "Netherlands", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 47, 3}, {84, 17, 47, 20}, {84, 17, 47, 39}, {84, 17, 47, 42}, {84, 17, 47, 74}, {84, 17, 47, 111}, {139, 28, 217, 199}, {185, 132, 177, 136}, {190, 2, 149, 196}, {195, 181, 172, 69}}},
{Region: "Netherlands", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 47, 4}, {84, 17, 47, 8}, {84, 17, 47, 12}, {84, 17, 47, 38}, {84, 17, 47, 68}, {185, 132, 177, 235}, {190, 2, 149, 28}, {190, 2, 149, 30}, {190, 2, 149, 208}, {195, 181, 172, 79}}},
{Region: "New Zealand", Group: "Premium TCP Asia", IPs: []net.IP{{103, 231, 91, 131}, {103, 231, 91, 132}, {103, 231, 91, 133}, {103, 231, 91, 134}, {103, 231, 91, 135}, {103, 231, 91, 136}, {103, 231, 91, 137}, {103, 231, 91, 138}, {103, 231, 91, 139}, {103, 231, 91, 140}}},
{Region: "New Zealand", Group: "Premium UDP Asia", IPs: []net.IP{{103, 231, 91, 131}, {103, 231, 91, 132}, {103, 231, 91, 133}, {103, 231, 91, 134}, {103, 231, 91, 135}, {103, 231, 91, 136}, {103, 231, 91, 137}, {103, 231, 91, 138}, {103, 231, 91, 139}, {103, 231, 91, 140}}},
{Region: "Nigeria", Group: "Premium TCP Europe", IPs: []net.IP{{45, 137, 196, 6}, {45, 137, 196, 10}, {45, 137, 196, 12}, {45, 137, 196, 13}, {45, 137, 196, 15}, {45, 137, 196, 16}, {45, 137, 196, 17}, {45, 137, 196, 20}, {45, 137, 196, 24}, {45, 137, 196, 29}}},
{Region: "Nigeria", Group: "Premium UDP Europe", IPs: []net.IP{{45, 137, 196, 6}, {45, 137, 196, 7}, {45, 137, 196, 8}, {45, 137, 196, 9}, {45, 137, 196, 12}, {45, 137, 196, 13}, {45, 137, 196, 14}, {45, 137, 196, 20}, {45, 137, 196, 28}, {45, 137, 196, 29}}},
{Region: "Norway", Group: "Premium TCP Europe", IPs: []net.IP{{45, 12, 223, 131}, {45, 12, 223, 135}, {45, 12, 223, 136}, {45, 12, 223, 138}, {185, 206, 225, 231}, {185, 206, 225, 233}, {185, 253, 97, 235}, {185, 253, 97, 236}, {185, 253, 97, 247}, {185, 253, 97, 253}}},
{Region: "Norway", Group: "Premium UDP Europe", IPs: []net.IP{{45, 12, 223, 132}, {45, 12, 223, 137}, {45, 12, 223, 138}, {45, 12, 223, 141}, {185, 206, 225, 27}, {185, 206, 225, 30}, {185, 253, 97, 245}, {185, 253, 97, 246}, {185, 253, 97, 248}, {185, 253, 97, 249}}},
{Region: "Pakistan", Group: "Premium TCP Europe", IPs: []net.IP{{103, 76, 3, 244}, {103, 76, 3, 245}, {103, 76, 3, 246}, {103, 76, 3, 247}, {103, 76, 3, 248}, {103, 76, 3, 249}, {103, 76, 3, 250}, {103, 76, 3, 251}, {103, 76, 3, 252}, {103, 76, 3, 253}}},
{Region: "Pakistan", Group: "Premium UDP Europe", IPs: []net.IP{{103, 76, 3, 244}, {103, 76, 3, 245}, {103, 76, 3, 246}, {103, 76, 3, 247}, {103, 76, 3, 248}, {103, 76, 3, 249}, {103, 76, 3, 250}, {103, 76, 3, 251}, {103, 76, 3, 252}, {103, 76, 3, 253}}},
{Region: "Panama", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 210, 9}, {45, 131, 210, 10}, {45, 131, 210, 14}, {45, 131, 210, 15}, {45, 131, 210, 16}, {45, 131, 210, 21}, {45, 131, 210, 22}, {45, 131, 210, 23}, {45, 131, 210, 24}, {45, 131, 210, 26}}},
{Region: "Panama", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 210, 6}, {45, 131, 210, 8}, {45, 131, 210, 9}, {45, 131, 210, 12}, {45, 131, 210, 14}, {45, 131, 210, 18}, {45, 131, 210, 19}, {45, 131, 210, 25}, {45, 131, 210, 28}, {45, 131, 210, 29}}},
{Region: "Philippines", Group: "Premium TCP Asia", IPs: []net.IP{{188, 214, 125, 39}, {188, 214, 125, 45}, {188, 214, 125, 47}, {188, 214, 125, 49}, {188, 214, 125, 50}, {188, 214, 125, 51}, {188, 214, 125, 53}, {188, 214, 125, 57}, {188, 214, 125, 58}, {188, 214, 125, 61}}},
{Region: "Philippines", Group: "Premium UDP Asia", IPs: []net.IP{{188, 214, 125, 35}, {188, 214, 125, 36}, {188, 214, 125, 37}, {188, 214, 125, 48}, {188, 214, 125, 55}, {188, 214, 125, 56}, {188, 214, 125, 57}, {188, 214, 125, 58}, {188, 214, 125, 59}, {188, 214, 125, 62}}},
{Region: "Poland", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 156, 11}, {37, 120, 156, 17}, {37, 120, 156, 23}, {37, 120, 156, 25}, {37, 120, 156, 26}, {51, 75, 56, 35}, {51, 75, 56, 37}, {51, 75, 56, 41}, {54, 37, 238, 36}, {54, 37, 238, 43}}},
{Region: "Poland", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 156, 8}, {37, 120, 156, 10}, {37, 120, 156, 12}, {37, 120, 156, 18}, {37, 120, 156, 19}, {37, 120, 156, 24}, {51, 75, 56, 33}, {51, 75, 56, 40}, {51, 75, 56, 41}, {54, 37, 238, 33}}},
{Region: "Portugal", Group: "Premium TCP Europe", IPs: []net.IP{{89, 26, 243, 47}, {89, 26, 243, 48}, {89, 26, 243, 50}, {89, 26, 243, 51}, {89, 26, 243, 54}, {89, 26, 243, 58}, {89, 26, 243, 60}, {185, 90, 57, 172}, {185, 90, 57, 176}, {185, 90, 57, 179}}},
{Region: "Portugal", Group: "Premium UDP Europe", IPs: []net.IP{{89, 26, 243, 47}, {89, 26, 243, 50}, {89, 26, 243, 51}, {89, 26, 243, 53}, {89, 26, 243, 58}, {185, 90, 57, 173}, {185, 90, 57, 175}, {185, 90, 57, 176}, {185, 90, 57, 178}, {185, 90, 57, 179}}},
{Region: "Qatar", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 7, 6}, {45, 131, 7, 9}, {45, 131, 7, 10}, {45, 131, 7, 13}, {45, 131, 7, 16}, {45, 131, 7, 17}, {45, 131, 7, 20}, {45, 131, 7, 22}, {45, 131, 7, 23}, {45, 131, 7, 28}}},
{Region: "Qatar", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 7, 8}, {45, 131, 7, 9}, {45, 131, 7, 14}, {45, 131, 7, 15}, {45, 131, 7, 16}, {45, 131, 7, 17}, {45, 131, 7, 23}, {45, 131, 7, 27}, {45, 131, 7, 28}, {45, 131, 7, 29}}},
{Region: "Romania", Group: "NoSpy TCP Europe", IPs: []net.IP{{85, 9, 20, 134}, {85, 9, 20, 135}, {85, 9, 20, 137}, {85, 9, 20, 138}, {85, 9, 20, 139}, {85, 9, 20, 147}, {85, 9, 20, 149}, {85, 9, 20, 154}, {85, 9, 20, 248}, {85, 9, 20, 249}}},
{Region: "Romania", Group: "NoSpy UDP Europe", IPs: []net.IP{{85, 9, 20, 134}, {85, 9, 20, 138}, {85, 9, 20, 139}, {85, 9, 20, 144}, {85, 9, 20, 145}, {85, 9, 20, 147}, {85, 9, 20, 148}, {85, 9, 20, 151}, {85, 9, 20, 248}, {85, 9, 20, 249}}},
{Region: "Romania", Group: "Premium TCP Europe", IPs: []net.IP{{193, 176, 84, 44}, {193, 176, 84, 49}, {193, 176, 84, 51}, {193, 176, 84, 52}, {193, 176, 84, 88}, {193, 176, 84, 121}, {193, 176, 84, 126}, {193, 176, 85, 72}, {193, 176, 85, 74}, {193, 176, 85, 101}}},
{Region: "Romania", Group: "Premium UDP Europe", IPs: []net.IP{{193, 176, 84, 46}, {193, 176, 84, 47}, {193, 176, 84, 48}, {193, 176, 84, 49}, {193, 176, 84, 90}, {193, 176, 84, 125}, {193, 176, 85, 68}, {193, 176, 85, 72}, {193, 176, 85, 78}, {193, 176, 85, 91}}},
{Region: "Russian Federation", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 192, 42}, {45, 132, 192, 43}, {45, 132, 192, 49}, {45, 132, 192, 60}, {45, 132, 192, 63}, {45, 132, 192, 66}, {45, 132, 192, 78}, {45, 132, 192, 80}, {45, 132, 192, 85}, {45, 132, 192, 96}}},
{Region: "Russian Federation", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 192, 11}, {45, 132, 192, 15}, {45, 132, 192, 24}, {45, 132, 192, 32}, {45, 132, 192, 43}, {45, 132, 192, 49}, {45, 132, 192, 55}, {45, 132, 192, 63}, {45, 132, 192, 65}, {45, 132, 192, 95}}},
{Region: "Saudi Arabia", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 6, 7}, {45, 131, 6, 10}, {45, 131, 6, 12}, {45, 131, 6, 13}, {45, 131, 6, 21}, {45, 131, 6, 23}, {45, 131, 6, 24}, {45, 131, 6, 25}, {45, 131, 6, 26}, {45, 131, 6, 28}}},
{Region: "Saudi Arabia", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 6, 7}, {45, 131, 6, 9}, {45, 131, 6, 14}, {45, 131, 6, 15}, {45, 131, 6, 18}, {45, 131, 6, 19}, {45, 131, 6, 22}, {45, 131, 6, 24}, {45, 131, 6, 28}, {45, 131, 6, 29}}},
{Region: "Serbia", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 193, 179}, {37, 120, 193, 181}, {37, 120, 193, 182}, {37, 120, 193, 183}, {37, 120, 193, 184}, {37, 120, 193, 186}, {37, 120, 193, 190}, {141, 98, 103, 37}, {141, 98, 103, 43}, {141, 98, 103, 46}}},
{Region: "Serbia", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 193, 179}, {37, 120, 193, 182}, {37, 120, 193, 183}, {37, 120, 193, 188}, {37, 120, 193, 189}, {37, 120, 193, 190}, {141, 98, 103, 35}, {141, 98, 103, 40}, {141, 98, 103, 42}, {141, 98, 103, 46}}},
{Region: "Singapore", Group: "Premium TCP Asia", IPs: []net.IP{{37, 120, 151, 55}, {37, 120, 151, 60}, {37, 120, 151, 131}, {37, 120, 151, 139}, {84, 17, 39, 168}, {84, 17, 39, 175}, {84, 17, 39, 176}, {84, 17, 39, 179}, {84, 17, 39, 182}, {84, 17, 39, 185}}},
{Region: "Singapore", Group: "Premium UDP Asia", IPs: []net.IP{{37, 120, 151, 51}, {37, 120, 151, 57}, {37, 120, 151, 131}, {37, 120, 151, 132}, {37, 120, 151, 140}, {37, 120, 151, 141}, {84, 17, 39, 165}, {84, 17, 39, 169}, {84, 17, 39, 171}, {84, 17, 39, 180}}},
{Region: "Slovakia", Group: "Premium TCP Europe", IPs: []net.IP{{185, 245, 85, 227}, {185, 245, 85, 228}, {185, 245, 85, 229}, {185, 245, 85, 230}, {185, 245, 85, 231}, {185, 245, 85, 232}, {185, 245, 85, 233}, {185, 245, 85, 234}, {185, 245, 85, 235}, {185, 245, 85, 236}}},
{Region: "Slovakia", Group: "Premium UDP Europe", IPs: []net.IP{{185, 245, 85, 227}, {185, 245, 85, 228}, {185, 245, 85, 229}, {185, 245, 85, 230}, {185, 245, 85, 231}, {185, 245, 85, 232}, {185, 245, 85, 233}, {185, 245, 85, 234}, {185, 245, 85, 235}, {185, 245, 85, 236}}},
{Region: "Slovenia", Group: "Premium TCP Europe", IPs: []net.IP{{146, 247, 25, 79}, {146, 247, 25, 80}, {146, 247, 25, 81}, {146, 247, 25, 83}, {146, 247, 25, 84}, {146, 247, 25, 85}, {146, 247, 25, 86}, {146, 247, 25, 87}, {146, 247, 25, 88}, {146, 247, 25, 90}}},
{Region: "Slovenia", Group: "Premium UDP Europe", IPs: []net.IP{{146, 247, 25, 80}, {146, 247, 25, 81}, {146, 247, 25, 82}, {146, 247, 25, 83}, {146, 247, 25, 84}, {146, 247, 25, 85}, {146, 247, 25, 86}, {146, 247, 25, 87}, {146, 247, 25, 88}, {146, 247, 25, 89}}},
{Region: "South Africa", Group: "Premium TCP Asia", IPs: []net.IP{{165, 73, 248, 211}, {165, 73, 248, 214}, {165, 73, 248, 222}, {165, 73, 248, 227}, {165, 73, 248, 229}, {165, 73, 248, 230}, {165, 73, 248, 231}, {165, 73, 248, 234}, {165, 73, 248, 236}, {165, 73, 248, 237}}},
{Region: "South Africa", Group: "Premium TCP Europe", IPs: []net.IP{{197, 85, 7, 26}, {197, 85, 7, 27}, {197, 85, 7, 28}, {197, 85, 7, 29}, {197, 85, 7, 30}, {197, 85, 7, 31}, {197, 85, 7, 131}, {197, 85, 7, 132}, {197, 85, 7, 133}, {197, 85, 7, 134}}},
{Region: "South Africa", Group: "Premium UDP Asia", IPs: []net.IP{{165, 73, 248, 212}, {165, 73, 248, 215}, {165, 73, 248, 217}, {165, 73, 248, 218}, {165, 73, 248, 219}, {165, 73, 248, 222}, {165, 73, 248, 227}, {165, 73, 248, 230}, {165, 73, 248, 234}, {165, 73, 248, 237}}},
{Region: "South Africa", Group: "Premium UDP Europe", IPs: []net.IP{{197, 85, 7, 26}, {197, 85, 7, 27}, {197, 85, 7, 28}, {197, 85, 7, 29}, {197, 85, 7, 30}, {197, 85, 7, 31}, {197, 85, 7, 131}, {197, 85, 7, 132}, {197, 85, 7, 133}, {197, 85, 7, 134}}},
{Region: "Spain", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 142, 163}, {37, 120, 142, 166}, {37, 120, 142, 167}, {84, 17, 62, 138}, {84, 17, 62, 141}, {84, 17, 62, 145}, {84, 17, 62, 147}, {84, 17, 62, 152}, {185, 93, 3, 113}, {185, 93, 182, 139}}},
{Region: "Spain", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 142, 150}, {37, 120, 142, 157}, {37, 120, 142, 165}, {84, 17, 62, 139}, {84, 17, 62, 142}, {185, 93, 3, 109}, {185, 93, 3, 111}, {185, 93, 3, 113}, {185, 93, 182, 136}, {185, 93, 182, 138}}},
{Region: "Sri Lanka", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 136, 6}, {45, 132, 136, 7}, {45, 132, 136, 8}, {45, 132, 136, 12}, {45, 132, 136, 13}, {45, 132, 136, 17}, {45, 132, 136, 21}, {45, 132, 136, 22}, {45, 132, 136, 26}, {45, 132, 136, 27}}},
{Region: "Sri Lanka", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 136, 7}, {45, 132, 136, 9}, {45, 132, 136, 10}, {45, 132, 136, 11}, {45, 132, 136, 12}, {45, 132, 136, 15}, {45, 132, 136, 20}, {45, 132, 136, 21}, {45, 132, 136, 27}, {45, 132, 136, 29}}},
{Region: "Sweden", Group: "Premium TCP Europe", IPs: []net.IP{{46, 246, 65, 151}, {46, 246, 65, 156}, {46, 246, 65, 157}, {46, 246, 65, 167}, {46, 246, 65, 203}, {46, 246, 65, 221}, {91, 132, 138, 51}, {188, 126, 64, 101}, {188, 126, 66, 3}, {188, 126, 66, 8}}},
{Region: "Sweden", Group: "Premium UDP Europe", IPs: []net.IP{{46, 246, 65, 167}, {46, 246, 65, 170}, {46, 246, 65, 172}, {46, 246, 65, 181}, {46, 246, 65, 214}, {46, 246, 65, 215}, {91, 132, 138, 57}, {188, 126, 66, 8}, {188, 126, 66, 24}, {188, 126, 66, 29}}},
{Region: "Switzerland", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 52, 14}, {84, 17, 52, 39}, {84, 17, 52, 68}, {84, 17, 52, 73}, {84, 17, 52, 84}, {89, 187, 165, 131}, {185, 32, 222, 8}, {185, 32, 222, 105}, {195, 225, 118, 38}, {195, 225, 118, 56}}},
{Region: "Switzerland", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 52, 15}, {84, 17, 52, 20}, {84, 17, 52, 36}, {84, 17, 52, 52}, {84, 17, 52, 62}, {84, 17, 52, 75}, {91, 132, 136, 174}, {91, 132, 136, 205}, {91, 132, 136, 206}, {185, 32, 222, 113}}},
{Region: "Taiwan", Group: "Premium TCP Asia", IPs: []net.IP{{45, 133, 181, 102}, {45, 133, 181, 105}, {45, 133, 181, 106}, {45, 133, 181, 108}, {45, 133, 181, 110}, {45, 133, 181, 112}, {45, 133, 181, 114}, {45, 133, 181, 117}, {45, 133, 181, 120}, {45, 133, 181, 124}}},
{Region: "Taiwan", Group: "Premium UDP Asia", IPs: []net.IP{{45, 133, 181, 102}, {45, 133, 181, 103}, {45, 133, 181, 110}, {45, 133, 181, 111}, {45, 133, 181, 112}, {45, 133, 181, 114}, {45, 133, 181, 118}, {45, 133, 181, 120}, {45, 133, 181, 125}, {45, 133, 181, 126}}},
{Region: "Thailand", Group: "Premium TCP Asia", IPs: []net.IP{{119, 59, 98, 238}, {119, 59, 98, 239}, {119, 59, 98, 243}, {119, 59, 98, 244}, {119, 59, 98, 248}, {119, 59, 98, 249}, {119, 59, 121, 165}, {119, 59, 121, 166}, {119, 59, 121, 171}, {119, 59, 121, 173}}},
{Region: "Thailand", Group: "Premium UDP Asia", IPs: []net.IP{{119, 59, 98, 214}, {119, 59, 98, 238}, {119, 59, 98, 244}, {119, 59, 98, 247}, {119, 59, 121, 163}, {119, 59, 121, 165}, {119, 59, 121, 166}, {119, 59, 121, 167}, {119, 59, 121, 173}, {119, 59, 121, 175}}},
{Region: "Turkey", Group: "Premium TCP Europe", IPs: []net.IP{{188, 213, 34, 9}, {188, 213, 34, 22}, {188, 213, 34, 24}, {188, 213, 34, 27}, {188, 213, 34, 28}, {188, 213, 34, 35}, {188, 213, 34, 41}, {188, 213, 34, 44}, {188, 213, 34, 45}, {188, 213, 34, 46}}},
{Region: "Turkey", Group: "Premium UDP Europe", IPs: []net.IP{{188, 213, 34, 3}, {188, 213, 34, 13}, {188, 213, 34, 18}, {188, 213, 34, 25}, {188, 213, 34, 27}, {188, 213, 34, 30}, {188, 213, 34, 35}, {188, 213, 34, 37}, {188, 213, 34, 38}, {188, 213, 34, 44}}},
{Region: "Ukraine", Group: "Premium TCP Europe", IPs: []net.IP{{31, 28, 161, 21}, {31, 28, 163, 34}, {31, 28, 163, 35}, {31, 28, 163, 38}, {31, 28, 163, 50}, {62, 149, 7, 164}, {62, 149, 7, 167}, {62, 149, 29, 50}, {62, 149, 29, 53}, {62, 149, 29, 56}}},
{Region: "Ukraine", Group: "Premium UDP Europe", IPs: []net.IP{{31, 28, 161, 20}, {31, 28, 161, 28}, {31, 28, 161, 30}, {31, 28, 163, 37}, {31, 28, 163, 45}, {31, 28, 163, 53}, {31, 28, 163, 56}, {31, 28, 163, 59}, {62, 149, 7, 168}, {62, 149, 29, 56}}},
{Region: "United Arab Emirates", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 5, 6}, {45, 131, 5, 9}, {45, 131, 5, 12}, {45, 131, 5, 14}, {45, 131, 5, 16}, {45, 131, 5, 17}, {45, 131, 5, 18}, {45, 131, 5, 27}, {45, 131, 5, 28}, {45, 131, 5, 29}}},
{Region: "United Arab Emirates", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 5, 8}, {45, 131, 5, 14}, {45, 131, 5, 15}, {45, 131, 5, 20}, {45, 131, 5, 22}, {45, 131, 5, 23}, {45, 131, 5, 24}, {45, 131, 5, 26}, {45, 131, 5, 28}, {45, 131, 5, 29}}},
{Region: "United Kingdom", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 133, 134}, {37, 120, 133, 140}, {84, 17, 51, 32}, {84, 17, 51, 75}, {89, 238, 138, 243}, {89, 238, 183, 230}, {95, 154, 200, 144}, {95, 154, 200, 186}, {141, 98, 100, 56}, {141, 98, 100, 61}}},
{Region: "United Kingdom", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 133, 134}, {37, 120, 159, 35}, {81, 92, 206, 172}, {84, 17, 51, 34}, {84, 17, 51, 110}, {84, 17, 51, 115}, {89, 238, 135, 59}, {95, 154, 200, 147}, {95, 154, 200, 149}, {141, 98, 100, 58}}},
{Region: "United States", Group: "Premium TCP USA", IPs: []net.IP{{23, 105, 160, 184}, {38, 131, 126, 150}, {84, 17, 35, 32}, {89, 187, 182, 7}, {89, 187, 182, 31}, {176, 113, 72, 153}, {176, 113, 72, 167}, {192, 96, 203, 150}, {199, 115, 119, 238}, {212, 102, 41, 4}}},
{Region: "United States", Group: "Premium UDP USA", IPs: []net.IP{{23, 19, 68, 56}, {23, 82, 78, 29}, {23, 105, 191, 36}, {23, 105, 191, 51}, {84, 17, 35, 8}, {89, 187, 171, 138}, {108, 62, 96, 33}, {176, 113, 72, 151}, {192, 96, 203, 154}, {199, 115, 117, 70}}},
{Region: "Venezuela", Group: "Premium TCP Europe", IPs: []net.IP{{45, 133, 89, 6}, {45, 133, 89, 9}, {45, 133, 89, 11}, {45, 133, 89, 15}, {45, 133, 89, 17}, {45, 133, 89, 18}, {45, 133, 89, 22}, {45, 133, 89, 24}, {45, 133, 89, 26}, {45, 133, 89, 29}}},
{Region: "Venezuela", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 89, 8}, {45, 133, 89, 9}, {45, 133, 89, 15}, {45, 133, 89, 17}, {45, 133, 89, 21}, {45, 133, 89, 22}, {45, 133, 89, 25}, {45, 133, 89, 26}, {45, 133, 89, 27}, {45, 133, 89, 29}}},
{Region: "Vietnam", Group: "Premium TCP Asia", IPs: []net.IP{{45, 117, 79, 118}, {45, 117, 79, 123}, {45, 117, 79, 124}, {45, 117, 79, 125}, {103, 238, 214, 131}, {103, 238, 214, 134}, {103, 238, 214, 135}, {103, 238, 214, 136}, {103, 238, 214, 137}, {103, 238, 214, 140}}},
{Region: "Vietnam", Group: "Premium UDP Asia", IPs: []net.IP{{45, 117, 79, 114}, {45, 117, 79, 116}, {45, 117, 79, 117}, {45, 117, 79, 118}, {45, 117, 79, 123}, {103, 238, 214, 134}, {103, 238, 214, 135}, {103, 238, 214, 136}, {103, 238, 214, 137}, {103, 238, 214, 140}}},
}
}

89
internal/constants/dns.go Normal file
View File

@@ -0,0 +1,89 @@
package constants
import (
"net"
"github.com/qdm12/gluetun/internal/models"
)
const (
// Cloudflare is a DNS over TLS provider
Cloudflare models.DNSProvider = "cloudflare"
// Google is a DNS over TLS provider
Google models.DNSProvider = "google"
// Quad9 is a DNS over TLS provider
Quad9 models.DNSProvider = "quad9"
// Quadrant is a DNS over TLS provider
Quadrant models.DNSProvider = "quadrant"
// CleanBrowsing is a DNS over TLS provider
CleanBrowsing models.DNSProvider = "cleanbrowsing"
// SecureDNS is a DNS over TLS provider
SecureDNS models.DNSProvider = "securedns"
// LibreDNS is a DNS over TLS provider
LibreDNS models.DNSProvider = "libredns"
)
// DNSProviderMapping returns a constant mapping of dns provider name
// to their data such as IP addresses or TLS host name.
func DNSProviderMapping() map[models.DNSProvider]models.DNSProviderData {
return map[models.DNSProvider]models.DNSProviderData{
Cloudflare: {
IPs: []net.IP{{1, 1, 1, 1}, {1, 0, 0, 1}, {0x26, 0x6, 0x47, 0x0, 0x47, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11}, {0x26, 0x6, 0x47, 0x0, 0x47, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x01}},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("cloudflare-dns.com"),
},
Google: {
IPs: []net.IP{{8, 8, 8, 8}, {8, 8, 4, 4}, {0x20, 0x1, 0x48, 0x60, 0x48, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88, 0x88}, {0x20, 0x1, 0x48, 0x60, 0x48, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88, 0x44}},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("dns.google"),
},
Quad9: {
IPs: []net.IP{{9, 9, 9, 9}, {149, 112, 112, 112}, {0x26, 0x20, 0x0, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe}, {0x26, 0x20, 0x0, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9}},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("dns.quad9.net"),
},
Quadrant: {
IPs: []net.IP{{12, 159, 2, 159}, {0x20, 0x1, 0x18, 0x90, 0x14, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x59}},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("dns-tls.qis.io"),
},
CleanBrowsing: {
IPs: []net.IP{{185, 228, 168, 9}, {185, 228, 169, 9}, {0x2a, 0xd, 0x2a, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}, {0x2a, 0xd, 0x2a, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("security-filter-dns.cleanbrowsing.org"),
},
SecureDNS: {
IPs: []net.IP{{146, 185, 167, 43}, {0x2a, 0x3, 0xb0, 0xc0, 0x0, 0x0, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0xe, 0x9a, 0x30, 0x1}},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("dot.securedns.eu"),
},
LibreDNS: {
IPs: []net.IP{{116, 203, 115, 192}},
SupportsTLS: true,
Host: models.DNSHost("dot.libredns.gr"),
},
}
}
// Block lists URLs
const (
AdsBlockListHostnamesURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/ads-hostnames.updated"
AdsBlockListIPsURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/ads-ips.updated"
MaliciousBlockListHostnamesURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/malicious-hostnames.updated"
MaliciousBlockListIPsURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/malicious-ips.updated"
SurveillanceBlockListHostnamesURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/surveillance-hostnames.updated"
SurveillanceBlockListIPsURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/surveillance-ips.updated"
)
// DNS certificates to fetch
// TODO obtain from source directly, see qdm12/updated)
const (
NamedRootURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/named.root.updated"
RootKeyURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/root.key.updated"
)

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
package constants
import (
"github.com/qdm12/gluetun/internal/models"
)
const (
TUN models.VPNDevice = "tun0"
TAP models.VPNDevice = "tap0"
)

View File

@@ -0,0 +1,30 @@
package constants
import (
"github.com/qdm12/gluetun/internal/models"
)
const (
// UnboundConf is the file path to the Unbound configuration file
UnboundConf models.Filepath = "/etc/unbound/unbound.conf"
// ResolvConf is the file path to the system resolv.conf file
ResolvConf models.Filepath = "/etc/resolv.conf"
// CACertificates is the file path to the CA certificates file
CACertificates models.Filepath = "/etc/ssl/certs/ca-certificates.crt"
// OpenVPNAuthConf is the file path to the OpenVPN auth file
OpenVPNAuthConf models.Filepath = "/etc/openvpn/auth.conf"
// OpenVPNConf is the file path to the OpenVPN client configuration file
OpenVPNConf models.Filepath = "/etc/openvpn/target.ovpn"
// TunnelDevice is the file path to tun device
TunnelDevice models.Filepath = "/dev/net/tun"
// NetRoute is the path to the file containing information on the network route
NetRoute models.Filepath = "/proc/net/route"
// TinyProxyConf is the filepath to the tinyproxy configuration file
TinyProxyConf models.Filepath = "/etc/tinyproxy/tinyproxy.conf"
// ShadowsocksConf is the filepath to the shadowsocks configuration file
ShadowsocksConf models.Filepath = "/etc/shadowsocks.json"
// RootHints is the filepath to the root.hints file used by Unbound
RootHints models.Filepath = "/etc/unbound/root.hints"
// RootKey is the filepath to the root.key file used by Unbound
RootKey models.Filepath = "/etc/unbound/root.key"
)

83
internal/constants/pia.go Normal file
View File

@@ -0,0 +1,83 @@
package constants
import (
"net"
"github.com/qdm12/gluetun/internal/models"
)
const (
PIAEncryptionPresetNormal = "normal"
PIAEncryptionPresetStrong = "strong"
PiaX509CRLNormal = "MIICWDCCAUAwDQYJKoZIhvcNAQENBQAwgegxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRlaW50ZXJuZXRhY2Nlc3MuY29tFw0xNjA3MDgxOTAwNDZaFw0zNjA3MDMxOTAwNDZaMCYwEQIBARcMMTYwNzA4MTkwMDQ2MBECAQYXDDE2MDcwODE5MDA0NjANBgkqhkiG9w0BAQ0FAAOCAQEAQZo9X97ci8EcPYu/uK2HB152OZbeZCINmYyluLDOdcSvg6B5jI+ffKN3laDvczsG6CxmY3jNyc79XVpEYUnq4rT3FfveW1+Ralf+Vf38HdpwB8EWB4hZlQ205+21CALLvZvR8HcPxC9KEnev1mU46wkTiov0EKc+EdRxkj5yMgv0V2Reze7AP+NQ9ykvDScH4eYCsmufNpIjBLhpLE2cuZZXBLcPhuRzVoU3l7A9lvzG9mjA5YijHJGHNjlWFqyrn1CfYS6koa4TGEPngBoAziWRbDGdhEgJABHrpoaFYaL61zqyMR6jC0K2ps9qyZAN74LEBedEfK7tBOzWMwr58A=="
PiaX509CRLStrong = "MIIDWDCCAUAwDQYJKoZIhvcNAQENBQAwgegxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRlaW50ZXJuZXRhY2Nlc3MuY29tFw0xNjA3MDgxOTAwNDZaFw0zNjA3MDMxOTAwNDZaMCYwEQIBARcMMTYwNzA4MTkwMDQ2MBECAQYXDDE2MDcwODE5MDA0NjANBgkqhkiG9w0BAQ0FAAOCAgEAppFfEpGsasjB1QgJcosGpzbf2kfRhM84o2TlqY1ua+Gi5TMdKydA3LJcNTjlI9a0TYAJfeRX5IkpoglSUuHuJgXhP3nEvX10mjXDpcu/YvM8TdE5JV2+EGqZ80kFtBeOq94WcpiVKFTR4fO+VkOK9zwspFfb1cNs9rHvgJ1QMkRUF8PpLN6AkntHY0+6DnigtSaKqldqjKTDTv2OeH3nPoh80SGrt0oCOmYKfWTJGpggMGKvIdvU3vH9+EuILZKKIskt+1dwdfA5Bkz1GLmiQG7+9ZZBQUjBG9Dos4hfX/rwJ3eU8oUIm4WoTz9rb71SOEuUUjP5NPy9HNx2vx+cVvLsTF4ZDZaUztW9o9JmIURDtbeyqxuHN3prlPWB6aj73IIm2dsDQvs3XXwRIxs8NwLbJ6CyEuvEOVCskdM8rdADWx1J0lRNlOJ0Z8ieLLEmYAA834VN1SboB6wJIAPxQU3rcBhXqO9y8aa2oRMg8NxZ5gr+PnKVMqag1x0IxbIgLxtkXQvxXxQHEMSODzvcOfK/nBRBsqTj30P+R87sU8titOoxNeRnBDRNhdEy/QGAqGh62ShPpQUCJdnKRiRTjnil9hMQHevoSuFKeEMO30FQL7BZyo37GFU+q1WPCplVZgCP9hC8Rn5K2+f6KLFo5bhtowSmu+GY1yZtg+RTtsA="
PIACertificateNormal = "MIIFqzCCBJOgAwIBAgIJAKZ7D5Yv87qDMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzM1MThaFw0zNDA0MTIxNzM1MThaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPXDL1L9tX6DGf36liA7UBTy5I869z0UVo3lImfOs/GSiFKPtInlesP65577nd7UNzzXlH/P/CnFPdBWlLp5ze3HRBCc/Avgr5CdMRkEsySL5GHBZsx6w2cayQ2EcRhVTwWpcdldeNO+pPr9rIgPrtXqT4SWViTQRBeGM8CDxAyTopTsobjSiYZCF9Ta1gunl0G/8Vfp+SXfYCC+ZzWvP+L1pFhPRqzQQ8k+wMZIovObK1s+nlwPaLyayzw9a8sUnvWB/5rGPdIYnQWPgoNlLN9HpSmsAcw2z8DXI9pIxbr74cb3/HSfuYGOLkRqrOk6h4RCOfuWoTrZup1uEOn+fw8CAwEAAaOCAVQwggFQMB0GA1UdDgQWBBQv63nQ/pJAt5tLy8VJcbHe22ZOsjCCAR8GA1UdIwSCARYwggESgBQv63nQ/pJAt5tLy8VJcbHe22ZOsqGB7qSB6zCB6DELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRMwEQYDVQQHEwpMb3NBbmdlbGVzMSAwHgYDVQQKExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UECxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAMTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQpExdQcml2YXRlIEludGVybmV0IEFjY2VzczEvMC0GCSqGSIb3DQEJARYgc2VjdXJlQHByaXZhdGVpbnRlcm5ldGFjY2Vzcy5jb22CCQCmew+WL/O6gzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQAna5PgrtxfwTumD4+3/SYvwoD66cB8IcK//h1mCzAduU8KgUXocLx7QgJWo9lnZ8xUryXvWab2usg4fqk7FPi00bED4f4qVQFVfGfPZIH9QQ7/48bPM9RyfzImZWUCenK37pdw4Bvgoys2rHLHbGen7f28knT2j/cbMxd78tQc20TIObGjo8+ISTRclSTRBtyCGohseKYpTS9himFERpUgNtefvYHbn70mIOzfOJFTVqfrptf9jXa9N8Mpy3ayfodz1wiqdteqFXkTYoSDctgKMiZ6GdocK9nMroQipIQtpnwd4yBDWIyC6Bvlkrq5TQUtYDQ8z9v+DMO6iwyIDRiU"
PIACertificateStrong = "MIIHqzCCBZOgAwIBAgIJAJ0u+vODZJntMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzQwMzNaFw0zNDA0MTIxNzQwMzNaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALVkhjumaqBbL8aSgj6xbX1QPTfTd1qHsAZd2B97m8Vw31c/2yQgZNf5qZY0+jOIHULNDe4R9TIvyBEbvnAg/OkPw8n/+ScgYOeH876VUXzjLDBnDb8DLr/+w9oVsuDeFJ9KV2UFM1OYX0SnkHnrYAN2QLF98ESK4NCSU01h5zkcgmQ+qKSfA9Ny0/UpsKPBFqsQ25NvjDWFhCpeqCHKUJ4Be27CDbSl7lAkBuHMPHJs8f8xPgAbHRXZOxVCpayZ2SNDfCwsnGWpWFoMGvdMbygngCn6jA/W1VSFOlRlfLuuGe7QFfDwA0jaLCxuWt/BgZylp7tAzYKR8lnWmtUCPm4+BtjyVDYtDCiGBD9Z4P13RFWvJHw5aapx/5W/CuvVyI7pKwvc2IT+KPxCUhH1XI8ca5RN3C9NoPJJf6qpg4g0rJH3aaWkoMRrYvQ+5PXXYUzjtRHImghRGd/ydERYoAZXuGSbPkm9Y/p2X8unLcW+F0xpJD98+ZI+tzSsI99Zs5wijSUGYr9/j18KHFTMQ8n+1jauc5bCCegN27dPeKXNSZ5riXFL2XX6BkY68y58UaNzmeGMiUL9BOV1iV+PMb7B7PYs7oFLjAhh0EdyvfHkrh/ZV9BEhtFa7yXp8XR0J6vz1YV9R6DYJmLjOEbhU8N0gc3tZm4Qz39lIIG6w3FDAgMBAAGjggFUMIIBUDAdBgNVHQ4EFgQUrsRtyWJftjpdRM0+925Y6Cl08SUwggEfBgNVHSMEggEWMIIBEoAUrsRtyWJftjpdRM0+925Y6Cl08SWhge6kgeswgegxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRlaW50ZXJuZXRhY2Nlc3MuY29tggkAnS7684Nkme0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOCAgEAJsfhsPk3r8kLXLxY+v+vHzbr4ufNtqnL9/1Uuf8NrsCtpXAoyZ0YqfbkWx3NHTZ7OE9ZRhdMP/RqHQE1p4N4Sa1nZKhTKasV6KhHDqSCt/dvEm89xWm2MVA7nyzQxVlHa9AkcBaemcXEiyT19XdpiXOP4Vhs+J1R5m8zQOxZlV1GtF9vsXmJqWZpOVPmZ8f35BCsYPvv4yMewnrtAC8PFEK/bOPeYcKN50bol22QYaZuLfpkHfNiFTnfMh8sl/ablPyNY7DUNiP5DRcMdIwmfGQxR5WEQoHL3yPJ42LkB5zs6jIm26DGNXfwura/mi105+ENH1CaROtRYwkiHb08U6qLXXJz80mWJkT90nr8Asj35xN2cUppg74nG3YVav/38P48T56hG1NHbYF5uOCske19F6wi9maUoto/3vEr0rnXJUp2KODmKdvBI7co245lHBABWikk8VfejQSlCtDBXn644ZMtAdoxKNfR2WTFVEwJiyd1Fzx0yujuiXDROLhISLQDRjVVAvawrAtLZWYK31bY7KlezPlQnl/D9Asxe85l8jO5+0LdJ6VyOs/Hd4w52alDW/MFySDZSfQHMTIc30hLBJ8OnCEIvluVQQ2UQvoW+no177N9L2Y+M9TcTA62ZyMXShHQGeh20rb4kK8f+iFX8NxtdHVSkxMEFSfDDyQ="
)
func PIAGeoChoices() (choices []string) {
servers := PIAServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
}
return choices
}
func PIAServers() []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, 85}, {43, 250, 204, 87}, {43, 250, 204, 89}, {43, 250, 204, 95}, {43, 250, 204, 97}, {43, 250, 204, 99}, {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, 119}, {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, 91}, {43, 250, 205, 95}}},
{Region: "AU Sydney", IPs: []net.IP{{27, 50, 70, 87}, {27, 50, 77, 247}, {27, 50, 77, 251}, {27, 50, 81, 117}, {103, 13, 102, 113}, {103, 13, 102, 119}, {103, 13, 102, 121}, {103, 13, 102, 123}, {103, 13, 102, 127}, {118, 127, 60, 51}, {118, 127, 60, 53}, {118, 127, 60, 61}, {221, 121, 145, 133}, {221, 121, 145, 143}, {221, 121, 145, 145}, {221, 121, 145, 147}, {221, 121, 145, 159}, {221, 121, 146, 203}, {221, 121, 146, 217}, {221, 121, 152, 215}}},
{Region: "Austria", IPs: []net.IP{{89, 187, 168, 6}, {156, 146, 60, 129}}},
{Region: "Belgium", IPs: []net.IP{{77, 243, 191, 18}, {77, 243, 191, 19}, {77, 243, 191, 20}, {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, 27}, {185, 232, 21, 28}, {185, 232, 21, 29}}},
{Region: "CA Montreal", IPs: []net.IP{{199, 229, 249, 167}, {199, 229, 249, 177}, {199, 229, 249, 188}, {199, 229, 249, 190}, {199, 229, 249, 194}, {199, 229, 249, 196}}},
{Region: "CA Toronto", IPs: []net.IP{{172, 98, 67, 43}, {172, 98, 67, 44}, {172, 98, 67, 52}, {172, 98, 67, 80}, {172, 98, 67, 88}, {172, 98, 67, 91}, {172, 98, 67, 143}}},
{Region: "CA Vancouver", IPs: []net.IP{{107, 181, 189, 72}, {107, 181, 189, 73}, {107, 181, 189, 75}, {107, 181, 189, 76}, {107, 181, 189, 83}, {107, 181, 189, 84}, {107, 181, 189, 86}, {107, 181, 189, 87}, {172, 83, 40, 18}, {172, 83, 40, 19}, {172, 83, 40, 24}, {172, 83, 40, 26}, {172, 83, 40, 99}, {172, 83, 40, 100}, {172, 83, 40, 101}, {172, 83, 40, 104}, {172, 83, 40, 105}, {172, 83, 40, 107}, {172, 83, 40, 110}, {172, 83, 40, 113}}},
{Region: "Czech Republic", IPs: []net.IP{{212, 102, 39, 1}}},
{Region: "DE Berlin", IPs: []net.IP{{185, 230, 127, 226}, {185, 230, 127, 227}, {185, 230, 127, 229}, {185, 230, 127, 230}, {185, 230, 127, 231}, {185, 230, 127, 235}, {185, 230, 127, 238}, {185, 230, 127, 239}, {185, 230, 127, 241}, {185, 230, 127, 242}, {193, 176, 86, 125}, {193, 176, 86, 134}, {193, 176, 86, 138}, {193, 176, 86, 142}, {193, 176, 86, 150}, {193, 176, 86, 154}, {193, 176, 86, 158}, {193, 176, 86, 170}, {193, 176, 86, 178}, {194, 36, 108, 6}}},
{Region: "DE Frankfurt", IPs: []net.IP{{195, 181, 170, 225}, {212, 102, 57, 138}}},
{Region: "Denmark", IPs: []net.IP{{82, 102, 20, 163}, {82, 102, 20, 166}, {82, 102, 20, 167}, {82, 102, 20, 168}, {82, 102, 20, 169}, {82, 102, 20, 172}, {82, 102, 20, 173}, {82, 102, 20, 175}, {82, 102, 20, 177}, {82, 102, 20, 179}, {82, 102, 20, 182}, {82, 102, 20, 183}, {82, 102, 20, 230}, {188, 126, 94, 34}}},
{Region: "Finlan", IPs: []net.IP{{188, 126, 89, 4}, {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, 114}, {196, 244, 191, 138}, {196, 244, 191, 146}}},
{Region: "France", IPs: []net.IP{{156, 146, 63, 1}, {156, 146, 63, 65}}},
{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}}},
{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, 42}}},
{Region: "Israel", IPs: []net.IP{{31, 168, 172, 142}, {31, 168, 172, 145}, {31, 168, 172, 146}, {31, 168, 172, 147}}},
{Region: "Italy", IPs: []net.IP{{156, 146, 41, 129}, {156, 146, 41, 193}}},
{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, 135}, {92, 223, 89, 136}, {92, 223, 89, 137}, {92, 223, 89, 138}, {92, 223, 89, 140}}},
{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{{89, 187, 174, 198}, {212, 102, 35, 101}, {212, 102, 35, 102}, {212, 102, 35, 103}, {212, 102, 35, 104}}},
{Region: "New Zealand", IPs: []net.IP{{43, 250, 207, 1}, {43, 250, 207, 3}}},
{Region: "Norway", IPs: []net.IP{{82, 102, 27, 50}, {82, 102, 27, 51}, {82, 102, 27, 53}, {82, 102, 27, 54}, {82, 102, 27, 55}, {82, 102, 27, 56}, {82, 102, 27, 57}, {82, 102, 27, 74}, {82, 102, 27, 75}, {82, 102, 27, 77}, {82, 102, 27, 114}, {82, 102, 27, 115}, {82, 102, 27, 116}, {82, 102, 27, 117}, {82, 102, 27, 118}, {82, 102, 27, 126}, {185, 206, 225, 222}, {185, 253, 97, 226}}},
{Region: "Poland", IPs: []net.IP{{185, 244, 214, 195}, {185, 244, 214, 197}, {185, 244, 214, 198}, {185, 244, 214, 199}}},
{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, 75}, {86, 105, 25, 76}, {86, 105, 25, 77}, {94, 176, 148, 34}, {94, 176, 148, 35}, {185, 45, 12, 126}, {185, 210, 218, 99}, {185, 210, 218, 101}, {185, 210, 218, 102}, {185, 210, 218, 103}, {185, 210, 218, 104}, {185, 210, 218, 105}}},
{Region: "Singapore", IPs: []net.IP{{156, 146, 56, 193}, {156, 146, 57, 38}}},
{Region: "Spain", IPs: []net.IP{{212, 102, 49, 185}}},
{Region: "Sweden", IPs: []net.IP{{45, 12, 220, 187}, {45, 12, 220, 208}, {45, 12, 220, 216}, {45, 12, 220, 238}, {45, 12, 220, 242}, {45, 12, 220, 245}, {45, 83, 91, 27}}},
{Region: "Switzerland", IPs: []net.IP{{156, 146, 62, 129}, {156, 146, 62, 193}, {212, 102, 36, 166}}},
{Region: "UAE", IPs: []net.IP{{45, 9, 250, 46}, {45, 9, 250, 62}}},
{Region: "UK London", IPs: []net.IP{{37, 235, 96, 198}, {37, 235, 97, 11}, {212, 102, 52, 1}, {212, 102, 52, 134}, {212, 102, 52, 199}, {212, 102, 53, 129}}},
{Region: "UK Manchester", IPs: []net.IP{{89, 238, 139, 4}, {89, 238, 139, 8}, {89, 238, 139, 9}, {89, 238, 139, 54}, {89, 238, 139, 58}}},
{Region: "UK Southampton", IPs: []net.IP{{31, 24, 226, 136}, {31, 24, 226, 145}, {31, 24, 226, 146}, {31, 24, 226, 189}, {31, 24, 226, 202}, {31, 24, 226, 203}, {31, 24, 226, 205}, {31, 24, 226, 206}, {31, 24, 226, 209}, {31, 24, 226, 222}, {31, 24, 226, 230}, {31, 24, 226, 232}, {31, 24, 226, 233}, {31, 24, 226, 235}, {31, 24, 226, 238}, {31, 24, 226, 239}, {31, 24, 226, 241}, {31, 24, 226, 243}, {31, 24, 226, 245}, {31, 24, 226, 254}}},
{Region: "US Atlanta", IPs: []net.IP{{66, 115, 169, 197}, {66, 115, 169, 199}, {66, 115, 169, 201}, {66, 115, 169, 203}, {66, 115, 169, 204}, {66, 115, 169, 209}, {66, 115, 169, 210}, {66, 115, 169, 212}, {66, 115, 169, 213}, {156, 146, 46, 1}, {156, 146, 46, 134}, {156, 146, 46, 198}, {156, 146, 47, 11}}},
{Region: "US California", IPs: []net.IP{{37, 235, 108, 144}, {37, 235, 108, 208}, {89, 187, 187, 129}, {89, 187, 187, 159}, {89, 187, 187, 162}}},
{Region: "US Chicago", IPs: []net.IP{{156, 146, 50, 1}, {156, 146, 50, 65}, {156, 146, 50, 134}, {156, 146, 50, 198}, {156, 146, 51, 11}, {212, 102, 58, 113}, {212, 102, 59, 54}, {212, 102, 59, 129}}},
{Region: "US Dallas", IPs: []net.IP{{104, 18, 4, 18}, {104, 18, 5, 18}}},
{Region: "US Denver", IPs: []net.IP{{174, 128, 225, 106}, {174, 128, 225, 186}, {174, 128, 226, 10}, {174, 128, 227, 226}, {174, 128, 236, 106}, {174, 128, 242, 250}, {174, 128, 243, 98}, {174, 128, 243, 106}, {174, 128, 244, 74}, {174, 128, 245, 98}, {174, 128, 245, 106}, {174, 128, 246, 10}, {174, 128, 250, 18}, {199, 115, 97, 202}, {199, 115, 98, 234}, {199, 115, 99, 82}, {199, 115, 101, 186}, {199, 115, 102, 146}, {199, 115, 103, 2}, {199, 115, 103, 10}}},
{Region: "US East", IPs: []net.IP{{156, 146, 58, 198}, {156, 146, 58, 199}, {156, 146, 58, 201}, {156, 146, 58, 202}, {156, 146, 58, 203}, {156, 146, 58, 204}, {156, 146, 58, 205}, {156, 146, 58, 206}, {156, 146, 58, 207}, {156, 146, 58, 208}, {156, 146, 58, 209}, {193, 37, 253, 24}, {193, 37, 253, 102}, {193, 37, 253, 113}, {193, 37, 253, 141}, {193, 37, 253, 254}, {194, 59, 251, 13}, {194, 59, 251, 22}, {194, 59, 251, 49}, {194, 59, 251, 57}}},
{Region: "US Florida", IPs: []net.IP{{156, 146, 42, 1}, {156, 146, 42, 65}, {156, 146, 42, 134}, {156, 146, 43, 11}, {156, 146, 43, 75}, {156, 146, 43, 121}, {156, 146, 43, 122}, {212, 102, 61, 19}, {212, 102, 61, 83}}},
{Region: "US Houston", IPs: []net.IP{{74, 81, 88, 18}, {74, 81, 88, 26}, {74, 81, 88, 34}, {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, 122}, {205, 251, 148, 34}, {205, 251, 148, 42}, {205, 251, 148, 74}, {205, 251, 148, 90}, {205, 251, 148, 98}, {205, 251, 148, 162}, {205, 251, 150, 186}, {205, 251, 150, 202}, {205, 251, 150, 234}, {205, 251, 151, 42}}},
{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, 6}, {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, 87}, {199, 127, 56, 88}, {199, 127, 56, 89}, {199, 127, 56, 90}, {199, 127, 56, 91}, {199, 127, 56, 115}, {199, 127, 56, 117}, {199, 127, 56, 118}, {199, 127, 56, 119}}},
{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, 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, 143}, {199, 116, 118, 149}, {199, 116, 118, 153}, {199, 116, 118, 154}, {199, 116, 118, 156}, {199, 116, 118, 168}, {199, 116, 118, 173}, {199, 116, 118, 174}, {199, 116, 118, 176}, {199, 116, 118, 181}, {199, 116, 118, 187}, {199, 116, 118, 209}, {199, 116, 118, 212}, {199, 116, 118, 215}, {199, 116, 118, 218}, {199, 116, 118, 221}, {199, 116, 118, 222}, {199, 116, 118, 239}, {199, 116, 118, 244}, {199, 116, 118, 250}}},
{Region: "US Washington DC", IPs: []net.IP{{70, 32, 0, 46}, {70, 32, 0, 50}, {70, 32, 0, 51}, {70, 32, 0, 52}, {70, 32, 0, 53}, {70, 32, 0, 57}, {70, 32, 0, 64}, {70, 32, 0, 65}, {70, 32, 0, 77}, {70, 32, 0, 101}, {70, 32, 0, 104}, {70, 32, 0, 114}, {70, 32, 0, 116}, {70, 32, 0, 118}, {70, 32, 0, 120}, {70, 32, 0, 130}, {70, 32, 0, 139}, {70, 32, 0, 167}, {70, 32, 0, 172}, {70, 32, 0, 173}}},
{Region: "US West", IPs: []net.IP{{104, 200, 151, 4}, {104, 200, 151, 11}, {104, 200, 151, 12}, {104, 200, 151, 13}, {104, 200, 151, 16}, {104, 200, 151, 20}, {104, 200, 151, 21}, {104, 200, 151, 31}, {104, 200, 151, 38}, {104, 200, 151, 39}, {104, 200, 151, 42}, {104, 200, 151, 52}, {104, 200, 151, 55}, {104, 200, 151, 61}, {104, 200, 151, 72}, {104, 200, 151, 73}, {104, 200, 151, 74}, {104, 200, 151, 78}, {104, 200, 151, 79}, {104, 200, 151, 83}}},
}
}
const (
PIAPortForwardURL models.URL = "http://209.222.18.222:2000"
)

View File

@@ -0,0 +1,198 @@
package constants
import (
"net"
"github.com/qdm12/gluetun/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

@@ -0,0 +1,13 @@
package constants
const (
// Announcement is a message announcement
Announcement = "Video of the Git history of Gluetun (2020 is crazy): https://youtu.be/khipOYJtGJ0"
// AnnouncementExpiration is the expiration date of the announcement in format yyyy-mm-dd
AnnouncementExpiration = "2020-07-30"
)
const (
// IssueLink is the link for users to use to create issues
IssueLink = "https://github.com/qdm12/gluetun/issues/new"
)

View File

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

View File

@@ -0,0 +1,174 @@
package constants
import (
"net"
"github.com/qdm12/gluetun/internal/models"
)
const (
SurfsharkCertificate = "MIIFTTCCAzWgAwIBAgIJAMs9S3fqwv+mMA0GCSqGSIb3DQEBCwUAMD0xCzAJBgNVBAYTAlZHMRIwEAYDVQQKDAlTdXJmc2hhcmsxGjAYBgNVBAMMEVN1cmZzaGFyayBSb290IENBMB4XDTE4MDMxNDA4NTkyM1oXDTI4MDMxMTA4NTkyM1owPTELMAkGA1UEBhMCVkcxEjAQBgNVBAoMCVN1cmZzaGFyazEaMBgGA1UEAwwRU3VyZnNoYXJrIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDEGMNj0aisM63oSkmVJyZPaYX7aPsZtzsxo6m6p5Wta3MGASoryRsBuRaH6VVa0fwbI1nw5ubyxkuaNa4v3zHVwuSq6F1p8S811+1YP1av+jqDcMyojH0ujZSHIcb/i5LtaHNXBQ3qN48Cc7sqBnTIIFpmb5HthQ/4pW+a82b1guM5dZHsh7q+LKQDIGmvtMtO1+NEnmj81BApFayiaD1ggvwDI4x7o/Y3ksfWSCHnqXGyqzSFLh8QuQrTmWUm84YHGFxoI1/8AKdIyVoB6BjcaMKtKs/pbctk6vkzmYf0XmGovDKPQF6MwUekchLjB5gSBNnptSQ9kNgnTLqi0OpSwI6ixX52Ksva6UM8P01ZIhWZ6ua/T/tArgODy5JZMW+pQ1A6L0b7egIeghpwKnPRG+5CzgO0J5UE6gv000mqbmC3CbiS8xi2xuNgruAyY2hUOoV9/BuBev8ttE5ZCsJH3YlG6NtbZ9hPc61GiBSx8NJnX5QHyCnfic/X87eST/amZsZCAOJ5v4EPSaKrItt+HrEFWZQIq4fJmHJNNbYvWzCE08AL+5/6Z+lxb/Bm3dapx2zdit3x2e+miGHekuiE8lQWD0rXD4+T+nDRi3X+kyt8Ex/8qRiUfrisrSHFzVMRungIMGdO9O/zCINFrb7wahm4PqU2f12Z9TRCOTXciQIDAQABo1AwTjAdBgNVHQ4EFgQUYRpbQwyDahLMN3F2ony3+UqOYOgwHwYDVR0jBBgwFoAUYRpbQwyDahLMN3F2ony3+UqOYOgwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAn9zV7F/XVnFNZhHFrt0ZS1Yqz+qM9CojLmiyblMFh0p7t+Hh+VKVgMwrz0LwDH4UsOosXA28eJPmech6/bjfymkoXISy/NUSTFpUChGO9RabGGxJsT4dugOw9MPaIVZffny4qYOc/rXDXDSfF2b+303lLPI43y9qoe0oyZ1vtk/UKG75FkWfFUogGNbpOkuz+et5Y0aIEiyg0yh6/l5Q5h8+yom0HZnREHhqieGbkaGKLkyu7zQ4D4tRK/mBhd8nv+09GtPEG+D5LPbabFVxKjBMP4Vp24WuSUOqcGSsURHevawPVBfgmsxf1UCjelaIwngdh6WfNCRXa5QQPQTKubQvkvXONCDdhmdXQccnRX1nJWhPYi0onffvjsWUfztRypsKzX4dvM9k7xnIcGSGEnCC4RCgt1UiZIj7frcCMssbA6vJ9naM0s7JF7N3VKeHJtqe1OCRHMYnWUZt9vrqX6IoIHlZCoLlv39wFW9QNxelcAOCVbD+19MZ0ZXt7LitjIqe7yF5WxDQN4xru087FzQ4Hfj7eH1SNLLyKZkA1eecjmRoi/OoqAt7afSnwtQLtMUc2bQDg6rHt5C0e4dCLqP/9PGZTSJiwmtRHJ/N5qYWIh9ju83APvLm/AGBTR2pXmj9G3KdVOkpIC7L35dI623cSEC3Q3UZutsEm/UplsM="
SurfsharkOpenvpnStaticKeyV1 = "b02cb1d7c6fee5d4f89b8de72b51a8d0c7b282631d6fc19be1df6ebae9e2779e6d9f097058a31c97f57f0c35526a44ae09a01d1284b50b954d9246725a1ead1ff224a102ed9ab3da0152a15525643b2eee226c37041dc55539d475183b889a10e18bb94f079a4a49888da566b99783460ece01daaf93548beea6c827d9674897e7279ff1a19cb092659e8c1860fbad0db4ad0ad5732f1af4655dbd66214e552f04ed8fd0104e1d4bf99c249ac229ce169d9ba22068c6c0ab742424760911d4636aafb4b85f0c952a9ce4275bc821391aa65fcd0d2394f006e3fba0fd34c4bc4ab260f4b45dec3285875589c97d3087c9134d3a3aa2f904512e85aa2dc2202498"
)
func SurfsharkRegionChoices() (choices []string) {
servers := SurfsharkServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
}
return choices
}
func SurfsharkServers() []models.SurfsharkServer {
return []models.SurfsharkServer{
{Region: "Albania", IPs: []net.IP{{31, 171, 152, 197}, {31, 171, 154, 147}, {31, 171, 154, 149}, {31, 171, 154, 163}, {31, 171, 154, 165}}},
{Region: "Australia Adelaide", IPs: []net.IP{{45, 248, 79, 19}, {45, 248, 79, 21}, {45, 248, 79, 27}, {45, 248, 79, 29}, {45, 248, 79, 51}, {45, 248, 79, 53}, {45, 248, 79, 67}, {45, 248, 79, 69}}},
{Region: "Australia Brisbane", IPs: []net.IP{{45, 248, 77, 235}, {45, 248, 77, 237}, {144, 48, 39, 11}, {144, 48, 39, 13}, {144, 48, 39, 67}, {144, 48, 39, 69}, {144, 48, 39, 83}, {144, 48, 39, 85}, {144, 48, 39, 107}, {144, 48, 39, 109}, {144, 48, 39, 123}, {144, 48, 39, 125}, {144, 48, 39, 131}, {144, 48, 39, 133}}},
{Region: "Australia Melbourne", IPs: []net.IP{{103, 192, 80, 131}, {103, 192, 80, 133}, {103, 192, 80, 139}, {103, 192, 80, 141}, {103, 192, 80, 147}, {103, 192, 80, 149}, {144, 48, 38, 19}, {144, 48, 38, 21}, {144, 48, 38, 139}, {144, 48, 38, 141}, {144, 48, 38, 147}, {144, 48, 38, 149}, {144, 48, 38, 179}, {144, 48, 38, 183}}},
{Region: "Australia Perth", IPs: []net.IP{{45, 248, 78, 43}, {45, 248, 78, 45}, {124, 150, 139, 27}, {124, 150, 139, 29}, {124, 150, 139, 35}, {124, 150, 139, 37}, {124, 150, 139, 43}, {124, 150, 139, 45}, {124, 150, 139, 123}, {124, 150, 139, 125}, {124, 150, 139, 179}, {124, 150, 139, 181}}},
{Region: "Australia Sydney", IPs: []net.IP{{45, 125, 247, 43}, {45, 125, 247, 45}, {45, 125, 247, 91}, {45, 125, 247, 93}, {45, 125, 247, 195}, {45, 125, 247, 197}, {45, 248, 76, 171}, {45, 248, 76, 173}, {103, 25, 59, 51}, {103, 25, 59, 53}, {103, 25, 59, 83}, {103, 25, 59, 85}, {180, 149, 228, 115}, {180, 149, 228, 117}}},
{Region: "Australia US", IPs: []net.IP{{45, 76, 117, 108}}},
{Region: "Austria", IPs: []net.IP{{5, 253, 207, 51}, {5, 253, 207, 53}, {5, 253, 207, 83}, {5, 253, 207, 85}, {37, 120, 212, 75}, {37, 120, 212, 77}, {37, 120, 212, 131}, {37, 120, 212, 133}, {37, 120, 212, 141}, {37, 120, 212, 147}, {37, 120, 212, 149}}},
{Region: "Azerbaijan", IPs: []net.IP{{94, 20, 21, 85}, {94, 20, 21, 87}}},
{Region: "Belgium", IPs: []net.IP{{5, 253, 205, 99}, {5, 253, 205, 101}, {5, 253, 205, 179}, {5, 253, 205, 181}, {5, 253, 205, 211}, {5, 253, 205, 213}, {5, 253, 205, 227}, {5, 253, 205, 229}, {37, 120, 218, 19}, {37, 120, 218, 21}, {37, 120, 218, 27}, {37, 120, 218, 251}, {37, 120, 218, 253}, {89, 249, 73, 195}, {89, 249, 73, 197}, {185, 104, 186, 75}, {185, 104, 186, 77}, {185, 232, 21, 51}}},
{Region: "Bosnia and Herzegovina", IPs: []net.IP{{185, 99, 3, 7}, {185, 99, 3, 12}, {185, 99, 3, 205}, {185, 99, 3, 207}, {185, 99, 3, 212}, {185, 99, 3, 214}, {185, 212, 111, 6}, {185, 212, 111, 41}}},
{Region: "Brazil", IPs: []net.IP{{191, 96, 73, 210}, {191, 96, 73, 212}, {191, 96, 73, 216}, {194, 41, 113, 3}, {194, 41, 113, 5}}},
{Region: "Bulgaria", IPs: []net.IP{{37, 120, 152, 35}, {37, 120, 152, 37}, {37, 120, 152, 39}, {37, 120, 152, 195}, {37, 120, 152, 197}, {217, 138, 202, 19}, {217, 138, 202, 21}}},
{Region: "Canada Montreal", IPs: []net.IP{{172, 98, 82, 83}, {172, 98, 82, 85}, {172, 98, 82, 229}, {172, 98, 82, 231}, {172, 98, 82, 245}, {198, 8, 85, 3}, {198, 8, 85, 5}, {198, 8, 85, 19}, {198, 8, 85, 21}, {198, 8, 85, 35}, {198, 8, 85, 37}, {198, 8, 85, 45}, {198, 8, 85, 47}, {198, 8, 85, 67}, {198, 8, 85, 69}, {198, 8, 85, 72}, {198, 8, 85, 77}, {198, 8, 85, 79}, {198, 8, 85, 82}, {198, 8, 85, 84}, {198, 8, 85, 87}, {198, 8, 85, 89}, {198, 8, 85, 131}, {198, 8, 85, 133}}},
{Region: "Canada Toronto", IPs: []net.IP{{68, 71, 244, 131}, {68, 71, 244, 134}, {68, 71, 244, 195}, {68, 71, 244, 197}, {68, 71, 244, 200}, {68, 71, 244, 202}, {68, 71, 244, 205}, {68, 71, 244, 207}, {68, 71, 244, 210}, {68, 71, 244, 212}, {68, 71, 244, 215}, {68, 71, 244, 220}, {68, 71, 244, 222}, {104, 200, 138, 5}, {104, 200, 138, 99}, {104, 200, 138, 147}, {104, 200, 138, 149}, {104, 200, 138, 152}, {104, 200, 138, 154}, {104, 200, 138, 163}}},
{Region: "Canada US", IPs: []net.IP{{159, 203, 57, 80}}},
{Region: "Canada Vancouver", IPs: []net.IP{{66, 115, 147, 67}, {66, 115, 147, 69}, {66, 115, 147, 72}, {66, 115, 147, 74}, {66, 115, 147, 77}, {66, 115, 147, 79}, {66, 115, 147, 82}, {66, 115, 147, 84}, {66, 115, 147, 87}, {66, 115, 147, 89}, {66, 115, 147, 92}, {66, 115, 147, 94}, {104, 200, 132, 37}, {104, 200, 132, 39}, {107, 181, 177, 179}, {107, 181, 177, 181}, {107, 181, 177, 183}, {172, 83, 40, 147}, {172, 83, 40, 149}, {208, 78, 41, 195}, {208, 78, 41, 197}, {208, 78, 41, 200}, {208, 78, 41, 202}}},
{Region: "Chile", IPs: []net.IP{{31, 169, 121, 16}}},
{Region: "Colombia", IPs: []net.IP{{45, 129, 32, 3}, {45, 129, 32, 5}, {45, 129, 32, 10}, {45, 129, 32, 20}, {45, 129, 32, 22}}},
{Region: "Costa Rica", IPs: []net.IP{{176, 227, 241, 19}, {176, 227, 241, 21}}},
{Region: "Croatia", IPs: []net.IP{{89, 164, 99, 111}}},
{Region: "Cyprus", IPs: []net.IP{{195, 47, 194, 34}, {195, 47, 194, 36}, {195, 47, 194, 42}}},
{Region: "Czech Republic", IPs: []net.IP{{185, 152, 64, 151}, {185, 152, 64, 178}, {193, 9, 112, 179}, {193, 9, 112, 181}, {193, 9, 112, 183}, {193, 9, 112, 195}, {193, 9, 112, 197}}},
{Region: "Denmark", IPs: []net.IP{{37, 120, 145, 19}, {37, 120, 145, 21}, {37, 120, 194, 91}, {37, 120, 194, 93}, {37, 120, 194, 99}, {37, 120, 194, 101}, {37, 120, 194, 107}, {37, 120, 194, 109}, {37, 120, 194, 115}, {37, 120, 194, 117}, {37, 120, 194, 123}, {37, 120, 194, 163}, {37, 120, 194, 165}, {45, 12, 221, 163}, {45, 12, 221, 165}, {45, 12, 221, 167}, {45, 12, 221, 179}, {45, 12, 221, 181}, {45, 12, 221, 183}, {95, 174, 65, 67}, {95, 174, 65, 69}, {95, 174, 65, 71}, {95, 174, 65, 73}}},
{Region: "Estonia", IPs: []net.IP{{165, 231, 163, 3}, {165, 231, 163, 5}, {165, 231, 163, 7}, {165, 231, 163, 19}, {165, 231, 163, 21}, {165, 231, 163, 23}, {185, 174, 159, 51}, {185, 174, 159, 53}, {185, 174, 159, 59}, {185, 174, 159, 61}, {185, 174, 159, 67}, {185, 174, 159, 69}}},
{Region: "Finland", IPs: []net.IP{{196, 244, 191, 163}, {196, 244, 191, 165}, {196, 244, 191, 181}, {196, 244, 191, 195}, {196, 244, 191, 197}}},
{Region: "France Bordeaux", IPs: []net.IP{{185, 108, 106, 51}, {185, 108, 106, 53}, {185, 108, 106, 67}, {185, 108, 106, 69}, {185, 108, 106, 140}, {185, 108, 106, 142}, {185, 108, 106, 144}, {185, 108, 106, 146}, {185, 108, 106, 148}, {185, 108, 106, 150}, {185, 108, 106, 152}, {185, 108, 106, 154}, {185, 108, 106, 156}, {185, 108, 106, 158}, {185, 108, 106, 160}, {185, 108, 106, 162}, {185, 108, 106, 164}, {185, 108, 106, 166}}},
{Region: "France Marseilles", IPs: []net.IP{{185, 166, 84, 3}, {185, 166, 84, 5}, {185, 166, 84, 11}, {185, 166, 84, 13}, {185, 166, 84, 17}, {185, 166, 84, 21}, {185, 166, 84, 23}, {185, 166, 84, 27}, {185, 166, 84, 29}, {185, 166, 84, 36}, {185, 166, 84, 38}, {185, 166, 84, 55}, {185, 166, 84, 61}, {185, 166, 84, 63}, {185, 166, 84, 65}, {185, 166, 84, 75}, {185, 166, 84, 77}, {185, 166, 84, 79}, {185, 166, 84, 81}, {185, 166, 84, 83}, {185, 166, 84, 85}, {185, 166, 84, 87}, {185, 166, 84, 89}, {185, 166, 84, 91}, {185, 166, 84, 93}}},
{Region: "France Paris", IPs: []net.IP{{45, 83, 90, 179}, {45, 83, 90, 183}, {45, 89, 174, 59}, {45, 89, 174, 61}, {45, 89, 174, 83}, {45, 89, 174, 85}, {45, 89, 174, 91}, {45, 89, 174, 99}, {45, 89, 174, 101}, {45, 89, 174, 103}, {84, 17, 43, 178}, {84, 17, 43, 180}, {84, 17, 43, 183}, {84, 17, 43, 185}, {84, 17, 60, 235}, {84, 17, 60, 250}, {84, 247, 51, 235}, {84, 247, 51, 243}, {84, 247, 51, 251}, {84, 247, 51, 253}, {185, 246, 211, 69}, {217, 138, 207, 243}, {217, 138, 207, 245}, {217, 138, 207, 251}, {217, 138, 207, 253}}},
{Region: "France Sweden", IPs: []net.IP{{199, 247, 8, 20}}},
{Region: "Germany Berlin", IPs: []net.IP{{37, 120, 217, 131}, {37, 120, 217, 133}, {37, 120, 217, 147}, {37, 120, 217, 149}, {37, 120, 217, 179}, {37, 120, 217, 181}, {152, 89, 163, 19}, {152, 89, 163, 21}, {152, 89, 163, 23}, {152, 89, 163, 227}, {152, 89, 163, 229}, {152, 89, 163, 243}, {193, 176, 86, 195}, {193, 176, 86, 197}, {217, 138, 216, 59}, {217, 138, 216, 61}, {217, 138, 216, 219}, {217, 138, 216, 221}, {217, 138, 216, 227}, {217, 138, 216, 229}, {217, 138, 216, 235}, {217, 138, 216, 243}, {217, 138, 216, 245}, {217, 138, 216, 251}, {217, 138, 216, 253}}},
{Region: "Germany Frankfurt am Main st001", IPs: []net.IP{{45, 87, 212, 179}}},
{Region: "Germany Frankfurt am Main st002", IPs: []net.IP{{45, 87, 212, 181}}},
{Region: "Germany Frankfurt am Main st003", IPs: []net.IP{{45, 87, 212, 183}}},
{Region: "Germany Frankfurt am Main", IPs: []net.IP{{37, 120, 196, 51}, {37, 120, 196, 53}, {37, 120, 196, 59}, {37, 120, 196, 61}, {37, 120, 196, 171}, {37, 120, 197, 11}, {45, 87, 212, 211}, {45, 87, 212, 213}, {74, 119, 145, 51}, {82, 102, 16, 99}, {82, 102, 16, 101}, {84, 16, 240, 176}, {89, 187, 169, 104}, {89, 187, 169, 119}, {185, 59, 220, 144}, {185, 59, 220, 150}, {185, 59, 220, 168}, {185, 59, 220, 172}, {185, 93, 180, 99}, {185, 93, 180, 101}, {185, 102, 219, 6}, {185, 102, 219, 47}, {185, 102, 219, 49}, {185, 158, 135, 36}, {185, 220, 70, 83}}},
{Region: "Germany Munich", IPs: []net.IP{{178, 238, 231, 49}, {178, 238, 231, 51}, {178, 238, 231, 55}}},
{Region: "Germany Nuremberg", IPs: []net.IP{{62, 171, 151, 182}}},
{Region: "Germany Singapour", IPs: []net.IP{{159, 89, 14, 157}}},
{Region: "Germany UK", IPs: []net.IP{{46, 101, 250, 73}}},
{Region: "Greece", IPs: []net.IP{{194, 150, 167, 28}, {194, 150, 167, 30}, {194, 150, 167, 32}, {194, 150, 167, 34}, {194, 150, 167, 36}, {194, 150, 167, 38}, {194, 150, 167, 40}, {194, 150, 167, 42}, {194, 150, 167, 44}, {194, 150, 167, 46}, {194, 150, 167, 48}, {194, 150, 167, 50}, {194, 150, 167, 52}, {194, 150, 167, 54}, {194, 150, 167, 58}}},
{Region: "Hong Kong", IPs: []net.IP{{64, 120, 121, 212}, {64, 120, 121, 214}, {64, 120, 121, 232}, {64, 120, 121, 234}, {64, 120, 121, 236}, {64, 120, 121, 238}, {64, 120, 121, 244}, {64, 120, 121, 248}, {84, 17, 37, 154}, {84, 17, 37, 156}, {84, 17, 37, 158}, {84, 17, 37, 160}, {84, 17, 57, 66}, {84, 17, 57, 68}, {84, 17, 57, 71}, {84, 17, 57, 185}, {209, 58, 186, 10}, {209, 58, 186, 14}, {212, 102, 42, 194}, {212, 102, 42, 199}, {212, 102, 42, 201}, {212, 102, 42, 204}, {212, 102, 42, 206}, {212, 102, 42, 209}, {212, 102, 42, 211}}},
{Region: "Hungary", IPs: []net.IP{{37, 120, 144, 147}, {37, 120, 144, 149}, {37, 120, 144, 151}, {37, 120, 144, 195}, {37, 120, 144, 197}, {37, 120, 144, 199}, {37, 120, 144, 211}, {37, 120, 144, 213}, {37, 120, 144, 215}, {37, 120, 144, 243}}},
{Region: "Iceland", IPs: []net.IP{{82, 221, 128, 156}, {82, 221, 128, 166}, {82, 221, 128, 169}, {82, 221, 143, 241}, {82, 221, 143, 243}}},
{Region: "India Chennai", IPs: []net.IP{{103, 94, 27, 99}, {103, 94, 27, 101}, {103, 94, 27, 115}, {103, 94, 27, 117}, {103, 94, 27, 179}, {103, 94, 27, 181}, {103, 94, 27, 227}, {103, 94, 27, 229}, {103, 108, 117, 116}, {103, 108, 117, 118}, {103, 108, 117, 120}, {103, 108, 117, 131}, {103, 108, 117, 133}, {103, 108, 117, 147}, {103, 108, 117, 149}}},
{Region: "India Indore", IPs: []net.IP{{103, 39, 132, 187}, {103, 39, 132, 189}, {103, 73, 189, 219}, {103, 73, 189, 221}, {137, 59, 52, 107}, {137, 59, 52, 109}}},
{Region: "India Mumbai", IPs: []net.IP{{103, 221, 233, 61}, {103, 221, 233, 82}, {103, 221, 233, 86}, {103, 221, 233, 88}, {103, 221, 233, 104}, {165, 231, 253, 147}}},
{Region: "India UK", IPs: []net.IP{{134, 209, 148, 122}}},
{Region: "Indonesia", IPs: []net.IP{{103, 120, 66, 214}, {103, 120, 66, 216}, {103, 120, 66, 219}, {103, 120, 66, 221}, {103, 227, 255, 211}, {103, 227, 255, 213}}},
{Region: "Ireland", IPs: []net.IP{{185, 108, 128, 114}, {185, 108, 128, 118}, {185, 108, 128, 120}, {185, 108, 128, 159}, {185, 108, 128, 161}, {185, 108, 128, 183}, {217, 138, 222, 43}, {217, 138, 222, 45}, {217, 138, 222, 51}, {217, 138, 222, 53}}},
{Region: "Israel", IPs: []net.IP{{87, 239, 255, 107}, {87, 239, 255, 109}, {87, 239, 255, 114}, {87, 239, 255, 116}, {87, 239, 255, 119}, {87, 239, 255, 121}}},
{Region: "Italy Milan", IPs: []net.IP{{37, 120, 201, 21}, {84, 17, 58, 134}, {84, 17, 58, 150}, {84, 17, 58, 154}, {84, 17, 58, 159}, {84, 17, 58, 192}, {84, 17, 58, 195}, {84, 17, 58, 205}, {84, 17, 58, 207}, {95, 174, 64, 67}, {95, 174, 64, 71}, {95, 174, 64, 73}, {212, 102, 54, 135}, {212, 102, 54, 147}, {212, 102, 54, 150}, {212, 102, 54, 152}, {212, 102, 54, 160}, {212, 102, 54, 165}, {212, 102, 54, 167}, {212, 102, 54, 175}, {212, 102, 54, 177}, {212, 102, 54, 180}, {212, 102, 54, 182}, {212, 102, 55, 66}, {212, 102, 55, 68}}},
{Region: "Italy Rome", IPs: []net.IP{{37, 120, 207, 3}, {37, 120, 207, 5}, {82, 102, 26, 61}, {82, 102, 26, 93}, {82, 102, 26, 99}, {82, 102, 26, 101}, {87, 101, 94, 211}, {87, 101, 94, 227}, {87, 101, 94, 229}, {87, 101, 94, 231}, {185, 217, 71, 3}, {185, 217, 71, 21}, {185, 217, 71, 51}, {185, 217, 71, 53}, {185, 217, 71, 187}, {185, 217, 71, 189}, {185, 217, 71, 195}, {185, 217, 71, 197}, {185, 217, 71, 229}, {185, 217, 71, 235}, {185, 217, 71, 237}, {185, 217, 71, 251}, {217, 138, 219, 243}, {217, 138, 219, 251}, {217, 138, 219, 253}}},
{Region: "Japan Tokyo st001", IPs: []net.IP{{45, 87, 213, 19}}},
{Region: "Japan Tokyo st002", IPs: []net.IP{{45, 87, 213, 21}}},
{Region: "Japan Tokyo st003", IPs: []net.IP{{45, 87, 213, 23}}},
{Region: "Japan Tokyo st004", IPs: []net.IP{{217, 138, 212, 19}}},
{Region: "Japan Tokyo st005", IPs: []net.IP{{217, 138, 212, 21}}},
{Region: "Japan Tokyo st006", IPs: []net.IP{{82, 102, 28, 123}}},
{Region: "Japan Tokyo st007", IPs: []net.IP{{82, 102, 28, 125}}},
{Region: "Japan Tokyo", IPs: []net.IP{{45, 87, 213, 3}, {45, 87, 213, 5}, {45, 87, 213, 7}, {45, 87, 213, 83}, {45, 87, 213, 103}, {45, 87, 213, 243}, {45, 87, 213, 245}, {84, 17, 34, 24}, {84, 17, 34, 26}, {84, 17, 34, 44}, {84, 17, 34, 46}, {89, 187, 161, 2}, {89, 187, 161, 4}, {89, 187, 161, 239}, {89, 187, 161, 241}, {103, 208, 221, 227}, {103, 208, 221, 229}, {185, 242, 4, 163}}},
{Region: "Kazakhstan", IPs: []net.IP{{45, 136, 56, 53}, {45, 136, 56, 54}, {45, 136, 56, 57}, {45, 136, 56, 61}}},
{Region: "Korea", IPs: []net.IP{{61, 14, 210, 227}, {61, 14, 210, 229}, {61, 14, 210, 232}, {61, 14, 210, 234}, {61, 14, 210, 237}, {61, 14, 210, 242}, {61, 14, 210, 244}, {61, 97, 243, 112}, {61, 97, 243, 119}, {61, 97, 243, 124}, {103, 249, 28, 215}, {103, 249, 28, 227}, {103, 249, 28, 229}, {103, 249, 28, 231}, {103, 249, 31, 26}, {103, 249, 31, 28}}},
{Region: "Latvia", IPs: []net.IP{{91, 203, 70, 186}, {91, 203, 70, 188}, {188, 92, 78, 135}, {188, 92, 78, 137}, {188, 92, 78, 140}, {188, 92, 78, 142}, {188, 92, 78, 145}, {188, 92, 78, 147}}},
{Region: "Libya", IPs: []net.IP{{41, 208, 72, 158}, {41, 208, 72, 204}, {41, 208, 72, 207}}},
{Region: "Luxembourg", IPs: []net.IP{{185, 153, 151, 60}, {185, 153, 151, 62}, {185, 153, 151, 73}, {185, 153, 151, 75}, {185, 153, 151, 78}, {185, 153, 151, 80}, {185, 153, 151, 82}, {185, 153, 151, 83}, {185, 153, 151, 85}, {185, 153, 151, 89}, {185, 153, 151, 91}}},
{Region: "Malaysia", IPs: []net.IP{{42, 0, 30, 162}, {42, 0, 30, 164}, {42, 0, 30, 177}, {42, 0, 30, 179}, {42, 0, 30, 209}, {42, 0, 30, 213}, {42, 0, 30, 215}, {223, 25, 247, 206}}},
{Region: "Moldova", IPs: []net.IP{{178, 175, 128, 235}, {178, 175, 128, 237}}},
{Region: "Netherlands Amsterdam st001", IPs: []net.IP{{81, 19, 209, 51}}},
{Region: "Netherlands Amsterdam", IPs: []net.IP{{81, 19, 208, 56}, {81, 19, 208, 66}, {81, 19, 208, 68}, {81, 19, 209, 20}, {81, 19, 209, 57}, {81, 19, 209, 113}, {81, 19, 209, 120}, {81, 19, 209, 124}, {89, 46, 223, 72}, {89, 46, 223, 78}, {89, 46, 223, 104}, {89, 46, 223, 212}, {89, 46, 223, 214}, {89, 46, 223, 217}, {89, 46, 223, 222}, {89, 46, 223, 229}, {89, 187, 174, 229}, {89, 187, 174, 231}, {185, 59, 222, 92}, {185, 59, 222, 94}, {185, 59, 222, 166}, {185, 59, 222, 168}, {212, 102, 35, 194}, {212, 102, 35, 196}}},
{Region: "Netherlands US", IPs: []net.IP{{188, 166, 98, 91}}},
{Region: "New Zealand", IPs: []net.IP{{180, 149, 231, 3}, {180, 149, 231, 11}, {180, 149, 231, 13}, {180, 149, 231, 43}, {180, 149, 231, 45}, {180, 149, 231, 69}, {180, 149, 231, 115}, {180, 149, 231, 117}, {180, 149, 231, 119}, {180, 149, 231, 163}}},
{Region: "Nigeria", IPs: []net.IP{{102, 165, 23, 38}, {102, 165, 23, 42}, {102, 165, 23, 44}}},
{Region: "North Macedonia", IPs: []net.IP{{185, 225, 28, 67}, {185, 225, 28, 69}, {185, 225, 28, 83}, {185, 225, 28, 85}, {185, 225, 28, 91}, {185, 225, 28, 93}, {185, 225, 28, 99}, {185, 225, 28, 101}, {185, 225, 28, 107}, {185, 225, 28, 109}, {185, 225, 28, 243}, {185, 225, 28, 245}}},
{Region: "Norway", IPs: []net.IP{{45, 12, 223, 67}, {45, 12, 223, 69}, {45, 12, 223, 71}, {45, 12, 223, 195}, {45, 12, 223, 197}, {45, 12, 223, 211}, {45, 12, 223, 213}, {84, 247, 50, 27}, {84, 247, 50, 29}, {84, 247, 50, 67}, {84, 247, 50, 69}, {95, 174, 66, 35}, {95, 174, 66, 37}, {95, 174, 66, 39}}},
{Region: "Paraguay", IPs: []net.IP{{181, 40, 18, 47}, {181, 40, 18, 56}, {186, 16, 32, 163}, {186, 16, 32, 168}, {186, 16, 32, 173}}},
{Region: "Philippines", IPs: []net.IP{{45, 134, 224, 10}}},
{Region: "Poland Gdansk", IPs: []net.IP{{5, 187, 49, 187}, {5, 187, 49, 189}, {5, 187, 53, 53}, {5, 187, 53, 55}}},
{Region: "Poland Warsaw", IPs: []net.IP{{5, 253, 206, 67}, {5, 253, 206, 69}, {5, 253, 206, 71}, {5, 253, 206, 227}, {5, 253, 206, 229}, {84, 17, 55, 132}, {185, 246, 208, 72}, {185, 246, 208, 77}, {185, 246, 208, 105}, {185, 246, 208, 107}, {185, 246, 208, 176}, {185, 246, 208, 182}}},
{Region: "Portugal Lisbon", IPs: []net.IP{{5, 154, 174, 26}, {5, 154, 174, 65}, {5, 154, 174, 67}, {5, 154, 174, 75}, {5, 154, 174, 77}, {5, 154, 174, 99}, {5, 154, 174, 101}, {5, 154, 174, 171}, {5, 154, 174, 173}, {5, 154, 174, 181}, {5, 154, 174, 187}, {5, 154, 174, 189}, {5, 154, 174, 213}, {5, 154, 174, 219}, {5, 154, 174, 221}, {5, 154, 174, 227}, {5, 154, 174, 229}}},
{Region: "Portugal Loule", IPs: []net.IP{{94, 126, 172, 57}, {176, 61, 146, 86}, {176, 61, 146, 95}, {176, 61, 146, 106}, {176, 61, 146, 108}, {176, 61, 146, 111}, {176, 61, 146, 113}, {176, 61, 146, 116}, {176, 61, 148, 60}}},
{Region: "Portugal Porto", IPs: []net.IP{{194, 39, 127, 21}, {194, 39, 127, 23}, {194, 39, 127, 36}, {194, 39, 127, 38}, {194, 39, 127, 233}, {194, 39, 127, 240}, {194, 39, 127, 244}}},
{Region: "Romania", IPs: []net.IP{{45, 89, 175, 51}, {45, 89, 175, 53}, {45, 89, 175, 55}, {86, 106, 137, 147}, {86, 106, 137, 149}}},
{Region: "Russia Moscow", IPs: []net.IP{{213, 183, 56, 18}, {213, 183, 56, 166}}},
{Region: "Russia St. Petersburg", IPs: []net.IP{{213, 183, 54, 23}, {213, 183, 54, 110}, {213, 183, 54, 143}, {213, 183, 54, 165}}},
{Region: "Serbia", IPs: []net.IP{{37, 120, 193, 51}, {37, 120, 193, 53}, {152, 89, 160, 119}, {152, 89, 160, 123}, {152, 89, 160, 125}, {152, 89, 160, 211}, {152, 89, 160, 213}, {152, 89, 160, 215}}},
{Region: "Singapore Hong Kong", IPs: []net.IP{{206, 189, 83, 129}}},
{Region: "Singapore Netherlands", IPs: []net.IP{{104, 248, 148, 18}}},
{Region: "Singapore st001", IPs: []net.IP{{217, 138, 201, 91}}},
{Region: "Singapore st002", IPs: []net.IP{{217, 138, 201, 93}}},
{Region: "Singapore st003", IPs: []net.IP{{84, 247, 49, 19}}},
{Region: "Singapore st004", IPs: []net.IP{{84, 247, 49, 21}}},
{Region: "Singapore", IPs: []net.IP{{89, 187, 162, 186}, {89, 187, 162, 188}, {89, 187, 163, 130}, {89, 187, 163, 132}, {89, 187, 163, 134}, {89, 187, 163, 136}, {89, 187, 163, 195}, {89, 187, 163, 197}, {89, 187, 163, 200}, {89, 187, 163, 202}, {89, 187, 163, 205}, {89, 187, 163, 207}, {89, 187, 163, 210}, {89, 187, 163, 212}, {89, 187, 163, 217}, {103, 254, 153, 169}, {103, 254, 155, 241}, {156, 146, 56, 130}, {156, 146, 56, 132}, {156, 146, 56, 137}, {209, 58, 170, 146}, {209, 58, 170, 159}, {209, 58, 170, 164}, {209, 58, 170, 169}, {209, 58, 170, 172}}},
{Region: "Slovekia", IPs: []net.IP{{37, 120, 221, 3}, {37, 120, 221, 5}, {193, 37, 255, 35}, {193, 37, 255, 37}, {193, 37, 255, 39}, {193, 37, 255, 41}}},
{Region: "Slovenia", IPs: []net.IP{{195, 158, 249, 36}, {195, 158, 249, 38}, {195, 158, 249, 40}, {195, 158, 249, 48}, {195, 158, 249, 50}, {195, 158, 249, 52}}},
{Region: "South Africa", IPs: []net.IP{{154, 127, 49, 226}, {154, 127, 49, 228}, {154, 127, 49, 230}, {154, 127, 49, 232}}},
{Region: "Spain Barcelona", IPs: []net.IP{{37, 120, 142, 131}, {37, 120, 142, 133}, {37, 120, 142, 135}, {37, 120, 142, 179}, {37, 120, 142, 181}, {185, 188, 61, 3}, {185, 188, 61, 5}, {185, 188, 61, 7}, {185, 188, 61, 13}, {185, 188, 61, 15}, {185, 188, 61, 19}, {185, 188, 61, 21}, {185, 188, 61, 23}, {185, 188, 61, 25}, {185, 188, 61, 27}}},
{Region: "Spain Madrid", IPs: []net.IP{{37, 120, 148, 213}, {37, 120, 148, 215}, {82, 102, 17, 181}, {84, 17, 62, 163}, {84, 17, 62, 165}, {84, 17, 62, 179}, {84, 17, 62, 181}, {89, 37, 95, 9}, {89, 37, 95, 11}, {89, 37, 95, 15}, {89, 37, 95, 17}, {89, 37, 95, 19}, {89, 37, 95, 21}, {89, 37, 95, 23}, {89, 37, 95, 27}, {188, 208, 141, 114}, {188, 208, 141, 116}, {212, 102, 48, 2}, {212, 102, 48, 4}, {212, 102, 48, 8}, {212, 102, 48, 10}, {212, 102, 48, 13}, {212, 102, 48, 15}, {212, 102, 48, 18}, {212, 102, 48, 20}}},
{Region: "Spain Valencia", IPs: []net.IP{{185, 153, 150, 44}, {185, 153, 150, 46}, {185, 153, 150, 48}, {185, 153, 150, 50}, {185, 153, 150, 52}, {185, 153, 150, 54}, {185, 153, 150, 56}, {185, 153, 150, 58}, {196, 196, 150, 67}, {196, 196, 150, 69}, {196, 196, 150, 71}, {196, 196, 150, 83}, {196, 196, 150, 99}, {196, 196, 150, 101}}},
{Region: "Sweden", IPs: []net.IP{{45, 83, 91, 131}, {45, 83, 91, 133}, {45, 83, 91, 135}, {45, 83, 91, 147}, {45, 83, 91, 149}, {45, 83, 91, 151}, {46, 227, 69, 19}, {46, 227, 69, 21}, {185, 76, 9, 34}, {185, 76, 9, 36}}},
{Region: "Switzerland", IPs: []net.IP{{37, 120, 213, 3}, {45, 12, 222, 243}, {45, 12, 222, 245}, {84, 17, 53, 86}, {84, 17, 53, 166}, {84, 17, 53, 168}, {84, 17, 53, 210}, {84, 17, 53, 212}, {84, 17, 53, 214}, {84, 17, 53, 216}, {84, 17, 53, 219}, {84, 17, 53, 221}, {84, 17, 53, 223}, {84, 17, 53, 227}, {84, 39, 112, 35}}},
{Region: "Taiwan", IPs: []net.IP{{2, 58, 241, 3}, {2, 58, 241, 5}, {2, 58, 242, 43}, {2, 58, 242, 155}, {2, 58, 243, 51}, {2, 58, 243, 53}, {103, 51, 140, 70}, {103, 98, 75, 73}}},
{Region: "Thailand", IPs: []net.IP{{45, 64, 186, 132}, {45, 64, 186, 134}, {103, 253, 74, 3}, {103, 253, 74, 7}}},
{Region: "Turkey", IPs: []net.IP{{185, 195, 79, 3}, {185, 195, 79, 5}}},
{Region: "UK France", IPs: []net.IP{{188, 166, 168, 247}}},
{Region: "UK Germany", IPs: []net.IP{{45, 77, 58, 16}}},
{Region: "UK Glasgow", IPs: []net.IP{{185, 108, 105, 3}, {185, 108, 105, 7}, {185, 108, 105, 11}, {185, 108, 105, 13}, {185, 108, 105, 15}, {185, 108, 105, 18}, {185, 108, 105, 20}, {185, 108, 105, 22}, {185, 108, 105, 31}, {185, 108, 105, 33}, {185, 108, 105, 35}, {185, 108, 105, 38}, {185, 108, 105, 40}, {185, 108, 105, 57}, {185, 108, 105, 143}, {185, 108, 105, 145}, {185, 108, 105, 151}, {185, 108, 105, 153}, {185, 108, 105, 155}, {185, 108, 105, 157}, {185, 108, 105, 159}, {185, 108, 105, 161}}},
{Region: "UK London st001", IPs: []net.IP{{217, 146, 82, 83}}},
{Region: "UK London st002", IPs: []net.IP{{185, 134, 22, 80}}},
{Region: "UK London st003", IPs: []net.IP{{185, 134, 22, 92}}},
{Region: "UK London st004", IPs: []net.IP{{185, 44, 76, 186}}},
{Region: "UK London st005", IPs: []net.IP{{185, 44, 76, 188}}},
{Region: "UK London", IPs: []net.IP{{5, 226, 137, 10}, {5, 226, 139, 65}, {5, 226, 139, 149}, {5, 226, 139, 225}, {81, 19, 210, 234}, {81, 19, 223, 189}, {89, 34, 99, 83}, {89, 35, 29, 71}, {178, 239, 166, 218}, {178, 239, 166, 227}, {178, 239, 166, 250}, {178, 239, 172, 111}, {185, 16, 206, 75}, {185, 38, 148, 228}, {185, 38, 150, 41}, {185, 38, 150, 88}, {185, 44, 76, 55}, {185, 44, 76, 167}, {185, 44, 76, 172}, {185, 114, 224, 53}, {185, 114, 224, 115}, {185, 125, 207, 155}, {185, 134, 22, 191}, {185, 193, 36, 212}, {195, 140, 215, 42}}},
{Region: "UK Manchester", IPs: []net.IP{{37, 120, 200, 3}, {37, 120, 200, 5}, {37, 120, 200, 7}, {86, 106, 136, 67}, {86, 106, 136, 69}, {86, 106, 136, 75}, {86, 106, 136, 77}, {86, 106, 136, 83}, {86, 106, 136, 85}, {86, 106, 136, 93}, {89, 238, 140, 227}, {89, 238, 140, 229}, {89, 238, 143, 103}, {185, 195, 202, 197}, {193, 148, 17, 83}, {193, 148, 17, 85}, {193, 148, 17, 131}, {193, 148, 17, 133}, {195, 12, 48, 213}, {195, 12, 48, 215}, {195, 12, 48, 217}, {217, 138, 196, 51}, {217, 138, 196, 53}, {217, 138, 196, 91}, {217, 138, 196, 93}}},
{Region: "US Atlanta", IPs: []net.IP{{66, 115, 154, 135}, {66, 115, 154, 147}, {66, 115, 154, 149}, {66, 115, 154, 151}, {66, 115, 166, 147}, {66, 115, 166, 149}, {66, 115, 166, 151}, {66, 115, 169, 35}, {66, 115, 175, 35}, {66, 115, 175, 37}, {66, 115, 175, 40}, {66, 115, 175, 42}, {66, 115, 175, 45}, {66, 115, 175, 47}, {66, 115, 175, 50}, {66, 115, 175, 52}, {185, 93, 0, 143}, {185, 93, 0, 146}}},
{Region: "US Bend", IPs: []net.IP{{45, 43, 14, 73}, {45, 43, 14, 75}, {45, 43, 14, 83}, {45, 43, 14, 85}, {45, 43, 14, 93}, {45, 43, 14, 103}, {45, 43, 14, 105}, {154, 16, 168, 184}, {154, 16, 168, 188}}},
{Region: "US Boston", IPs: []net.IP{{173, 237, 207, 21}, {192, 34, 83, 230}, {199, 217, 107, 22}}},
{Region: "US Buffalo", IPs: []net.IP{{107, 174, 20, 130}, {107, 174, 20, 132}, {107, 174, 20, 134}, {107, 175, 104, 82}, {107, 175, 104, 86}, {172, 93, 153, 146}, {172, 93, 153, 148}, {172, 93, 153, 150}}},
{Region: "US Charlotte", IPs: []net.IP{{66, 11, 124, 140}, {155, 254, 28, 141}, {155, 254, 29, 163}, {155, 254, 31, 182}, {155, 254, 31, 184}, {192, 154, 253, 67}, {192, 154, 253, 69}, {192, 154, 254, 135}, {192, 154, 254, 137}, {192, 154, 255, 52}, {192, 154, 255, 54}}},
{Region: "US Chicago", IPs: []net.IP{{74, 119, 146, 115}, {74, 119, 146, 117}, {74, 119, 146, 119}, {74, 119, 146, 131}, {74, 119, 146, 179}, {74, 119, 146, 181}, {74, 119, 146, 195}, {74, 119, 146, 197}, {74, 119, 146, 199}, {74, 119, 146, 211}, {89, 187, 182, 173}, {89, 187, 182, 175}, {107, 152, 100, 19}, {107, 152, 100, 21}, {107, 152, 100, 26}, {184, 170, 250, 67}, {184, 170, 250, 69}, {184, 170, 250, 72}, {184, 170, 250, 74}, {184, 170, 250, 147}, {184, 170, 250, 149}, {184, 170, 250, 152}, {184, 170, 250, 154}}},
{Region: "US Dallas", IPs: []net.IP{{66, 115, 177, 131}, {66, 115, 177, 133}, {66, 115, 177, 136}, {66, 115, 177, 138}, {66, 115, 177, 141}, {66, 115, 177, 143}, {66, 115, 177, 146}, {66, 115, 177, 148}, {66, 115, 177, 151}, {66, 115, 177, 153}, {66, 115, 177, 158}, {89, 187, 175, 165}, {89, 187, 175, 167}, {107, 181, 173, 163}, {172, 241, 114, 87}, {212, 102, 40, 66}, {212, 102, 40, 68}, {212, 102, 40, 71}, {212, 102, 40, 73}, {212, 102, 40, 76}, {212, 102, 40, 78}, {212, 102, 40, 81}}},
{Region: "US Denver", IPs: []net.IP{{174, 128, 245, 3}, {174, 128, 245, 5}, {174, 128, 245, 149}, {174, 128, 245, 151}, {212, 102, 44, 66}, {212, 102, 44, 68}, {212, 102, 44, 71}, {212, 102, 44, 73}, {212, 102, 44, 76}, {212, 102, 44, 78}, {212, 102, 44, 81}, {212, 102, 44, 83}, {212, 102, 44, 86}, {212, 102, 44, 88}, {212, 102, 44, 93}, {212, 102, 44, 98}}},
{Region: "US Gahanna", IPs: []net.IP{{104, 244, 208, 35}, {104, 244, 208, 37}, {104, 244, 208, 99}, {104, 244, 208, 101}, {104, 244, 208, 107}, {104, 244, 208, 109}, {104, 244, 208, 213}, {104, 244, 208, 215}, {104, 244, 208, 227}, {104, 244, 208, 229}, {104, 244, 208, 231}, {104, 244, 209, 51}, {104, 244, 209, 53}, {104, 244, 209, 99}, {104, 244, 209, 101}, {104, 244, 211, 139}, {104, 244, 211, 141}, {104, 244, 211, 171}, {104, 244, 211, 173}, {104, 244, 211, 179}}},
{Region: "US Houston", IPs: []net.IP{{104, 148, 30, 35}, {104, 148, 30, 37}, {104, 148, 30, 39}, {104, 148, 30, 51}, {104, 148, 30, 85}, {104, 148, 30, 87}, {199, 10, 64, 69}, {199, 10, 64, 83}, {199, 10, 64, 85}, {199, 10, 64, 99}, {199, 10, 64, 101}, {199, 10, 64, 115}, {199, 10, 64, 117}}},
{Region: "US Kansas City", IPs: []net.IP{{63, 141, 236, 243}, {63, 141, 236, 245}, {63, 141, 248, 179}, {63, 141, 248, 181}, {107, 150, 39, 43}, {107, 150, 39, 45}, {173, 208, 202, 59}, {173, 208, 202, 61}, {198, 204, 231, 147}, {198, 204, 231, 149}, {204, 12, 208, 115}, {204, 12, 208, 117}}},
{Region: "US Las Vegas", IPs: []net.IP{{89, 187, 187, 149}, {185, 242, 5, 213}}},
{Region: "US Latham", IPs: []net.IP{{45, 43, 19, 66}, {45, 43, 19, 68}, {45, 43, 19, 74}, {45, 43, 19, 76}, {45, 43, 19, 82}, {45, 43, 19, 84}, {45, 43, 19, 90}, {45, 43, 19, 92}, {154, 16, 169, 3}, {154, 16, 169, 5}, {154, 16, 169, 7}}},
{Region: "US Los Angeles", IPs: []net.IP{{38, 95, 110, 73}, {89, 187, 187, 66}, {89, 187, 187, 68}, {89, 187, 187, 73}, {89, 187, 187, 78}, {89, 187, 187, 81}, {89, 187, 187, 83}, {89, 187, 187, 86}, {89, 187, 187, 88}, {172, 83, 44, 83}, {184, 170, 243, 199}, {184, 170, 243, 211}, {192, 111, 134, 67}, {192, 111, 134, 78}, {192, 111, 134, 80}, {192, 111, 134, 195}, {192, 111, 134, 200}, {192, 111, 134, 202}, {192, 111, 134, 207}, {192, 111, 134, 210}, {192, 111, 134, 212}, {192, 111, 134, 215}, {192, 111, 134, 217}, {192, 111, 134, 220}, {192, 111, 134, 222}}},
{Region: "US Maryland", IPs: []net.IP{{23, 82, 8, 173}, {23, 105, 160, 134}, {23, 105, 163, 94}, {207, 244, 67, 147}, {207, 244, 67, 149}, {207, 244, 84, 40}, {207, 244, 84, 58}, {207, 244, 127, 116}, {207, 244, 127, 118}}},
{Region: "US Miami", IPs: []net.IP{{89, 187, 173, 201}, {107, 181, 164, 35}, {107, 181, 164, 39}, {107, 181, 164, 211}, {172, 83, 42, 3}, {172, 83, 42, 19}, {172, 83, 42, 23}, {172, 83, 42, 35}, {172, 83, 42, 37}, {172, 83, 42, 39}, {172, 83, 42, 51}, {172, 83, 42, 53}, {172, 83, 42, 55}, {172, 83, 42, 83}, {172, 83, 42, 85}, {172, 83, 42, 133}, {172, 83, 42, 136}, {172, 83, 42, 141}, {172, 83, 42, 146}, {172, 83, 42, 148}, {172, 83, 42, 151}, {172, 83, 42, 156}, {172, 83, 42, 158}, {193, 37, 252, 197}, {212, 102, 61, 130}}},
{Region: "US Netherlands", IPs: []net.IP{{142, 93, 58, 71}}},
{Region: "US New York City mp001", IPs: []net.IP{{45, 55, 60, 159}}},
{Region: "US New York City st001", IPs: []net.IP{{92, 119, 177, 19}}},
{Region: "US New York City st002", IPs: []net.IP{{92, 119, 177, 21}}},
{Region: "US New York City st003", IPs: []net.IP{{92, 119, 177, 23}}},
{Region: "US New York City st004", IPs: []net.IP{{193, 148, 18, 51}}},
{Region: "US New York City st005", IPs: []net.IP{{193, 148, 18, 53}}},
{Region: "US New York City", IPs: []net.IP{{37, 120, 202, 3}, {84, 17, 35, 66}, {84, 17, 35, 73}, {84, 17, 35, 76}, {84, 17, 35, 78}, {84, 17, 35, 83}, {84, 17, 35, 88}, {84, 17, 35, 91}, {89, 187, 177, 120}, {89, 187, 177, 122}, {89, 187, 178, 92}, {98, 142, 220, 35}, {98, 142, 220, 37}, {107, 152, 101, 163}, {172, 98, 75, 35}, {172, 98, 78, 227}, {172, 98, 78, 229}, {192, 40, 59, 227}, {192, 40, 59, 238}, {192, 40, 59, 240}, {199, 36, 221, 83}, {199, 36, 221, 101}, {199, 36, 221, 104}}},
{Region: "US Orlando", IPs: []net.IP{{198, 147, 22, 83}, {198, 147, 22, 85}, {198, 147, 22, 87}, {198, 147, 22, 131}, {198, 147, 22, 133}, {198, 147, 22, 135}, {198, 147, 22, 147}, {198, 147, 22, 149}, {198, 147, 22, 151}, {198, 147, 22, 163}, {198, 147, 22, 165}, {198, 147, 22, 167}, {198, 147, 22, 195}, {198, 147, 22, 197}, {198, 147, 22, 211}, {198, 147, 22, 213}}},
{Region: "US Phoenix", IPs: []net.IP{{23, 83, 128, 235}, {23, 83, 128, 243}, {107, 181, 184, 115}, {107, 181, 184, 117}, {172, 98, 87, 35}, {172, 98, 87, 37}, {184, 170, 240, 147}, {184, 170, 240, 149}, {184, 170, 240, 151}, {184, 170, 240, 179}, {184, 170, 240, 181}, {199, 58, 187, 3}, {199, 58, 187, 5}, {199, 58, 187, 8}, {199, 58, 187, 10}, {199, 58, 187, 13}, {199, 58, 187, 15}, {199, 58, 187, 18}, {199, 58, 187, 20}, {199, 58, 187, 23}, {199, 58, 187, 25}, {199, 58, 187, 67}, {199, 58, 187, 69}}},
{Region: "US Portugal", IPs: []net.IP{{142, 93, 81, 242}}},
{Region: "US Saint Louis", IPs: []net.IP{{148, 72, 169, 209}, {148, 72, 169, 211}, {148, 72, 169, 213}, {148, 72, 170, 108}, {148, 72, 174, 36}, {148, 72, 174, 38}, {148, 72, 174, 41}, {148, 72, 174, 43}, {148, 72, 174, 51}, {148, 72, 174, 53}}},
{Region: "US Salt Lake City", IPs: []net.IP{{104, 200, 131, 5}, {104, 200, 131, 9}, {104, 200, 131, 167}, {104, 200, 131, 170}, {104, 200, 131, 172}, {104, 200, 131, 229}, {104, 200, 131, 249}}},
{Region: "US San Francisco", IPs: []net.IP{{107, 181, 166, 55}, {107, 181, 166, 83}, {107, 181, 166, 85}, {107, 181, 166, 227}, {185, 174, 157, 83}, {185, 174, 157, 85}, {198, 8, 81, 37}, {209, 58, 128, 48}, {209, 58, 128, 50}}},
{Region: "US Seatle", IPs: []net.IP{{84, 17, 41, 71}, {84, 17, 41, 75}, {84, 17, 41, 77}, {84, 17, 41, 79}, {84, 17, 41, 81}, {84, 17, 41, 85}, {104, 200, 129, 243}, {104, 200, 129, 245}, {198, 8, 80, 87}, {198, 8, 80, 227}, {198, 8, 80, 229}, {199, 229, 250, 165}}},
{Region: "US Tampa", IPs: []net.IP{{66, 206, 23, 3}, {74, 50, 117, 106}, {74, 50, 117, 119}, {74, 50, 117, 121}, {162, 220, 56, 98}, {162, 220, 56, 100}, {162, 220, 63, 226}, {162, 220, 63, 232}, {162, 220, 63, 246}, {162, 220, 63, 248}, {209, 216, 92, 195}, {209, 216, 92, 202}, {209, 216, 92, 205}, {209, 216, 92, 207}, {209, 216, 92, 210}, {209, 216, 92, 212}, {209, 216, 92, 215}, {209, 216, 92, 217}, {209, 216, 92, 220}, {209, 216, 92, 222}, {209, 216, 92, 225}, {209, 216, 92, 227}}},
{Region: "Ukraine", IPs: []net.IP{{45, 9, 238, 23}, {45, 9, 238, 30}, {45, 9, 238, 38}}},
{Region: "United Arab Emirates", IPs: []net.IP{{45, 9, 249, 243}, {45, 9, 249, 245}, {45, 9, 249, 247}, {45, 9, 250, 99}, {45, 9, 250, 101}, {45, 9, 250, 103}}},
{Region: "Vietnam", IPs: []net.IP{{202, 143, 110, 29}, {202, 143, 110, 32}, {202, 143, 110, 34}, {202, 143, 110, 37}, {202, 143, 111, 142}, {202, 143, 111, 211}, {202, 143, 111, 213}}},
}
}

View File

@@ -0,0 +1,20 @@
package constants
import (
"github.com/qdm12/gluetun/internal/models"
)
const (
// TinyProxyInfoLevel is the info log level for TinyProxy
TinyProxyInfoLevel models.TinyProxyLogLevel = "Info"
// TinyProxyConnectLevel is the info log level for TinyProxy
TinyProxyConnectLevel models.TinyProxyLogLevel = "Connect"
// TinyProxyNoticeLevel is the info log level for TinyProxy
TinyProxyNoticeLevel models.TinyProxyLogLevel = "Notice"
// TinyProxyWarnLevel is the warning log level for TinyProxy
TinyProxyWarnLevel models.TinyProxyLogLevel = "Warning"
// TinyProxyErrorLevel is the error log level for TinyProxy
TinyProxyErrorLevel models.TinyProxyLogLevel = "Error"
// TinyProxyCriticalLevel is the critical log level for TinyProxy
TinyProxyCriticalLevel models.TinyProxyLogLevel = "Critical"
)

31
internal/constants/vpn.go Normal file
View File

@@ -0,0 +1,31 @@
package constants
import (
"github.com/qdm12/gluetun/internal/models"
)
const (
// PrivateInternetAccess is a VPN provider
PrivateInternetAccess models.VPNProvider = "private internet access"
// Mullvad is a VPN provider
Mullvad models.VPNProvider = "mullvad"
// Windscribe is a VPN provider
Windscribe models.VPNProvider = "windscribe"
// Surfshark is a VPN provider
Surfshark models.VPNProvider = "surfshark"
// Cyberghost is a VPN provider
Cyberghost models.VPNProvider = "cyberghost"
// Vyprvpn is a VPN provider
Vyprvpn models.VPNProvider = "vyprvpn"
// NordVPN is a VPN provider
Nordvpn models.VPNProvider = "nordvpn"
// PureVPN is a VPN provider
Purevpn models.VPNProvider = "purevpn"
)
const (
// TCP is a network protocol (reliable and slower than UDP)
TCP models.NetworkProtocol = "tcp"
// UDP is a network protocol (unreliable and faster than TCP)
UDP models.NetworkProtocol = "udp"
)

View File

@@ -0,0 +1,98 @@
package constants
import (
"net"
"github.com/qdm12/gluetun/internal/models"
)
const (
VyprvpnCertificate = "MIIGDjCCA/agAwIBAgIJAL2ON5xbane/MA0GCSqGSIb3DQEBDQUAMIGTMQswCQYDVQQGEwJDSDEQMA4GA1UECAwHTHVjZXJuZTEPMA0GA1UEBwwGTWVnZ2VuMRkwFwYDVQQKDBBHb2xkZW4gRnJvZyBHbWJIMSEwHwYDVQQDDBhHb2xkZW4gRnJvZyBHbWJIIFJvb3QgQ0ExIzAhBgkqhkiG9w0BCQEWFGFkbWluQGdvbGRlbmZyb2cuY29tMB4XDTE5MTAxNzIwMTQxMFoXDTM5MTAxMjIwMTQxMFowgZMxCzAJBgNVBAYTAkNIMRAwDgYDVQQIDAdMdWNlcm5lMQ8wDQYDVQQHDAZNZWdnZW4xGTAXBgNVBAoMEEdvbGRlbiBGcm9nIEdtYkgxITAfBgNVBAMMGEdvbGRlbiBGcm9nIEdtYkggUm9vdCBDQTEjMCEGCSqGSIb3DQEJARYUYWRtaW5AZ29sZGVuZnJvZy5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCtuddaZrpWZ+nUuJpG+ohTquO3XZtq6d4U0E2oiPeIiwm+WWLY49G+GNJb5aVrlrBojaykCAc2sU6NeUlpg3zuqrDqLcz7PAE4OdNiOdrLBF1o9ZHrcITDZN304eAY5nbyHx5V6x/QoDVCi4g+5OVTA+tZjpcl4wRIpgknWznO73IKCJ6YckpLn1BsFrVCb2ehHYZLg7Js58FzMySIxBmtkuPeHQXL61DFHh3cTFcMxqJjzh7EGsWRyXfbAaBGYnT+TZwzpLXXt8oBGpNXG8YBDrPdK0A+lzMnJ4nS0rgHDSRF0brx+QYk/6CgM510uFzB7zytw9UTD3/5TvKlCUmTGGgI84DbJ3DEvjxbgiQnJXCUZKKYSHwrK79Y4Qn+lXu4Bu0ZTCJBje0GUVMTPAvBCeDvzSe0iRcVSNMJVM68d4kD1PpSY/zWfCz5hiOjHWuXinaoZ0JJqRF8kGbJsbDlDYDtVvh/Cd4aWN6Q/2XLpszBsG5i8sdkS37nzkdlRwNEIZwsKfcXwdTOlDinR1LUG68LmzJAwfNE47xbrZUsdGGfG+HSPsrqFFiLGe7Y4e2+a7vGdSY9qR9PAzyx0ijCCrYzZDIsb2dwjLctUx6a3LNV8cpfhKX+s6tfMldGufPI7byHT1Ybf0NtMS1d1RjD6IbqedXQdCKtaw68kTX//wIDAQABo2MwYTAdBgNVHQ4EFgQU2EbQvBd1r/EADr2jCPMXsH7zEXEwHwYDVR0jBBgwFoAU2EbQvBd1r/EADr2jCPMXsH7zEXEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQENBQADggIBAAViCPieIronV+9asjZyo5oSZSNWUkWRYdezjezsf49+fwT12iRgnkSEQeoj5caqcOfNm/eRpN4G7jhhCcxy9RGF+GurIlZ4v0mChZbx1jcxqr9/3/Z2TqvHALyWngBYDv6pv1iWcd9a4+QL9kj1Tlp8vUDIcHMtDQkEHnkhC+MnjyrdsdNE5wjlLljjFR2Qy5a6/kWwZ1JQVYof1J1EzY6mU7YLMHOdjfmeci5i0vg8+9kGMsc/7Wm69L1BeqpDB3ZEAgmOtda2jwOevJ4sABmRoSThFp4DeMcxb62HW1zZCCpgzWv/33+pZdPvnZHSz7RGoxH4Ln7eBf3oo2PMlu7wCsid3HUdgkRf2Og1RJIrFfEjb7jga1JbKX2Qo/FH3txzdUimKiDRv3ccFmEOqjndUG6hP+7/EsI43oCPYOvZR+u5GdOkhYrDGZlvjXeJ1CpQxTR/EX+Vt7F8YG+i2LkO7lhPLb+LzgPAxVPCcEMHruuUlE1BYxxzRMOW4X4kjHvJjZGISxa9lgTY3e0mnoQNQVBHKfzI2vGLwvcrFcCIrVxeEbj2dryfByyhZlrNPFbXyf7P4OSfk+fVh6Is1IF1wksfLY/6gWvcmXB8JwmKFDa9s5NfzXnzP3VMrNUWXN3G8Eee6qzKKTDsJ70OrgAx9j9a+dMLfe1vP5t6GQj5"
)
func VyprvpnRegionChoices() (choices []string) {
servers := VyprvpnServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
}
return choices
}
func VyprvpnServers() []models.VyprvpnServer {
return []models.VyprvpnServer{
{Region: "Algeria", IPs: []net.IP{{209, 99, 75, 20}}},
{Region: "Argentina", IPs: []net.IP{{209, 99, 109, 19}}},
{Region: "Australia Melbourne", IPs: []net.IP{{209, 99, 117, 19}}},
{Region: "Australia Perth", IPs: []net.IP{{209, 99, 1, 19}}},
{Region: "Australia Sydney", IPs: []net.IP{{209, 99, 117, 18}}},
{Region: "Austria", IPs: []net.IP{{128, 90, 96, 18}}},
{Region: "Bahrain", IPs: []net.IP{{209, 99, 115, 19}}},
{Region: "Belgium", IPs: []net.IP{{128, 90, 96, 20}}},
{Region: "Brazil", IPs: []net.IP{{209, 99, 109, 20}}},
{Region: "Bulgaria", IPs: []net.IP{{128, 90, 96, 22}}},
{Region: "Canada", IPs: []net.IP{{209, 99, 21, 18}}},
{Region: "Columbia", IPs: []net.IP{{209, 99, 109, 21}}},
{Region: "Costa Rica", IPs: []net.IP{{209, 99, 109, 22}}},
{Region: "Czech Republic", IPs: []net.IP{{128, 90, 96, 24}}},
{Region: "Denmark", IPs: []net.IP{{128, 90, 96, 28}}},
{Region: "Dubai", IPs: []net.IP{{128, 90, 45, 104}}},
{Region: "Egypt", IPs: []net.IP{{128, 90, 228, 43}}},
{Region: "El Salvador", IPs: []net.IP{{209, 99, 61, 20}}},
{Region: "Finland", IPs: []net.IP{{128, 90, 96, 32}}},
{Region: "France", IPs: []net.IP{{128, 90, 96, 34}}},
{Region: "Germany", IPs: []net.IP{{128, 90, 96, 26}}},
{Region: "Greece", IPs: []net.IP{{128, 90, 228, 59}}},
{Region: "Hong Kong", IPs: []net.IP{{128, 90, 227, 18}}},
{Region: "Iceland", IPs: []net.IP{{209, 99, 22, 20}}},
{Region: "India", IPs: []net.IP{{209, 99, 115, 20}}},
{Region: "Indonesia", IPs: []net.IP{{209, 99, 1, 20}}},
{Region: "Ireland", IPs: []net.IP{{209, 99, 22, 19}}},
{Region: "Israel", IPs: []net.IP{{128, 90, 228, 20}}},
{Region: "Italy", IPs: []net.IP{{128, 90, 96, 36}}},
{Region: "Japan", IPs: []net.IP{{209, 99, 113, 18}}},
{Region: "Latvia", IPs: []net.IP{{128, 90, 96, 44}}},
{Region: "Liechtenstein", IPs: []net.IP{{128, 90, 96, 38}}},
{Region: "Lithuania", IPs: []net.IP{{128, 90, 96, 40}}},
{Region: "Luxembourg", IPs: []net.IP{{128, 90, 96, 42}}},
{Region: "Macao", IPs: []net.IP{{128, 90, 227, 36}}},
{Region: "Malaysia", IPs: []net.IP{{209, 99, 1, 21}}},
{Region: "Maldives", IPs: []net.IP{{209, 99, 1, 26}}},
{Region: "Marshall Islands", IPs: []net.IP{{209, 99, 1, 25}}},
{Region: "Mexico", IPs: []net.IP{{209, 99, 61, 19}}},
{Region: "Netherlands", IPs: []net.IP{{128, 90, 96, 16}}},
{Region: "New Zealand", IPs: []net.IP{{209, 99, 117, 20}}},
{Region: "Norway", IPs: []net.IP{{128, 90, 96, 46}}},
{Region: "Pakistan", IPs: []net.IP{{128, 90, 228, 67}}},
{Region: "Panama", IPs: []net.IP{{209, 99, 109, 23}}},
{Region: "Philippines", IPs: []net.IP{{209, 99, 1, 22}}},
{Region: "Poland", IPs: []net.IP{{128, 90, 96, 48}}},
{Region: "Portugal", IPs: []net.IP{{128, 90, 96, 50}}},
{Region: "Qatar", IPs: []net.IP{{209, 99, 115, 21}}},
{Region: "Romania", IPs: []net.IP{{128, 90, 96, 52}}},
{Region: "Russia", IPs: []net.IP{{128, 90, 96, 54}}},
{Region: "Saudi Arabia", IPs: []net.IP{{209, 99, 115, 22}}},
{Region: "Singapore", IPs: []net.IP{{209, 99, 1, 18}}},
{Region: "Slovakia", IPs: []net.IP{{128, 90, 96, 60}}},
{Region: "Slovenia", IPs: []net.IP{{128, 90, 96, 58}}},
{Region: "South Korea", IPs: []net.IP{{209, 99, 113, 19}}},
{Region: "Spain", IPs: []net.IP{{128, 90, 96, 30}}},
{Region: "Sweden", IPs: []net.IP{{128, 90, 96, 56}}},
{Region: "Switzerland", IPs: []net.IP{{209, 99, 60, 18}}},
{Region: "Taiwan", IPs: []net.IP{{128, 90, 227, 27}}},
{Region: "Thailand", IPs: []net.IP{{209, 99, 1, 23}}},
{Region: "Turkey", IPs: []net.IP{{128, 90, 96, 62}}},
{Region: "USA Austin", IPs: []net.IP{{209, 99, 61, 18}}},
{Region: "USA Chicago", IPs: []net.IP{{209, 99, 93, 18}}},
{Region: "USA Los Angeles", IPs: []net.IP{{209, 99, 67, 18}}},
{Region: "USA Miami", IPs: []net.IP{{209, 99, 109, 18}}},
{Region: "USA New York", IPs: []net.IP{{209, 99, 63, 18}}},
{Region: "USA San Francisco", IPs: []net.IP{{209, 99, 95, 18}}},
{Region: "USA Seattle", IPs: []net.IP{{209, 99, 94, 18}}},
{Region: "USA Washington DC", IPs: []net.IP{{209, 99, 62, 18}}},
{Region: "Ukraine", IPs: []net.IP{{128, 90, 96, 64}}},
{Region: "United Kingdom", IPs: []net.IP{{209, 99, 22, 18}}},
{Region: "Uruguay", IPs: []net.IP{{209, 99, 61, 21}}},
{Region: "Vietnam", IPs: []net.IP{{209, 99, 1, 24}}},
}
}

View File

@@ -0,0 +1,94 @@
package constants
import (
"net"
"github.com/qdm12/gluetun/internal/models"
)
const (
WindscribeCertificate = "MIIF3DCCA8SgAwIBAgIJAMsOivWTmu9fMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEQMA4GA1UEBwwHVG9yb250bzEbMBkGA1UECgwSV2luZHNjcmliZSBMaW1pdGVkMRMwEQYDVQQLDApPcGVyYXRpb25zMRswGQYDVQQDDBJXaW5kc2NyaWJlIE5vZGUgQ0EwHhcNMTYwMzA5MDMyNjIwWhcNNDAxMDI5MDMyNjIwWjB7MQswCQYDVQQGEwJDQTELMAkGA1UECAwCT04xEDAOBgNVBAcMB1Rvcm9udG8xGzAZBgNVBAoMEldpbmRzY3JpYmUgTGltaXRlZDETMBEGA1UECwwKT3BlcmF0aW9uczEbMBkGA1UEAwwSV2luZHNjcmliZSBOb2RlIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAruBtLR1Vufd71LeQEqChgHS4AQJ0fSRner0gmZPEr2TL5uWboOEWXFFoEUTthF+P/N8yy3xRZ8HhG/zKlmJ1xw+7KZRbTADD6shJPj3/uvTIO80sU+9LmsyKSWuPhQ1NkgNA7rrMTfz9eHJ2MVDs4XCpYWyX9iuAQrHSY6aPq+4TpCbUgprkM3Gwjh9RSt9IoDoc4CF2bWSaVepUcL9yz/SXLPzFx2OT9rFrDhL3ryHRzJQ/tA+VD8A7lo8bhOcDqiXgEFmVOZNMLw+r167Qq1Ck7X86yr2mnW/6HK2gJOvY0/SPKukfGJAiYZKdG+fe4ekyYcAVhDfPJg7rF9wUqPwUzejJyAs1K18JwX94Y8fnD6vQobjpC3qfHtwQP7Uj2AcI6QC8ytWDegV6UIkHXAMXBQSX5suSQoE11deG32cy7nyp5vhgy31rTyNoopqlcCAhPm6k0jVVQbvXhLcpTSL8iCCoMdrP28i/xsfvktBAkl5giHMdK6hxqWgPI+Bx9uPIhRp3fJ2z8AgFm8g1ARB2ZzQ+OZZ2RUIkJuUKhi2kUhgKSAQ+eF89aoqDjp/J1miZqGRzt4DovSZfQOeL01RkKHEibAPYCfgHG2ZSwoLoeaxE2vNZiX4dpXiOQYTOIXOwEPZzPvfTQf9T4Kxvx3jzQnt3PzjlMCqKk3Aipm8CAwEAAaNjMGEwHQYDVR0OBBYEFEH2v9F2z938Ebngsj9RkVSSgs45MB8GA1UdIwQYMBaAFEH2v9F2z938Ebngsj9RkVSSgs45MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQAgI6NgYkVo5rB6yKStgHjjZsINsgEvoMuHwkM0YaV22XtKNiHdsiOmY/PGCRemFobTEHk5XHcvcOTWv/D1qVf8fI21WAoNQVH7h8KEsr4uMGKCB6Lu8l6xALXRMjo1xb6JKBWXwIAzUu691rUD2exT1E+A5t+xw+gzqV8rWTMIoUaH7O1EKjN6ryGW71Khiik8/ETrP3YT32ZbS2P902iMKw9rpmuS0wWhnO5k/iO/6YNA1ZMV5JG5oZvZQYEDk7enLD9HvqazofMuy/Sz/n62ZCDdQsnabzxl04wwv5Y3JZbV/6bOM520GgdJEoDxviY05ax2Mz05otyBzrAVjFw9RZt/Ls8ATifu9BusZ2ootvscdIuE3x+ZCl5lvANcFEnvgGw0qpCeASLpsfxwq1dRgIn7BOiTauFv4eoeFAQvCD+l+EKGWKu3M2y19DgYX94N2+Xs2bwChroaO5e4iFemMLMuWKZvYgnqS9OAtRSYWbNX/wliiPz7u13yj+qSWgMfu8WPYNQlMZJXuGWUvKLEXCUExlu7/o8D4HpsVs30E0pUdaqN0vExB1KegxPWWrmLcYnPG3knXpkC3ZBZ5P/el/2eyhZRy9ydiITF8gM3L08E8aeqvzZMw2FDSmousydIzlXgeS5VuEf+lUFA2h8oZYGQgrLt+ot8MbLhJlkp4Q=="
WindscribeOpenvpnStaticKeyV1 = "5801926a57ac2ce27e3dfd1dd6ef82042d82bd4f3f0021296f57734f6f1ea714a6623845541c4b0c3dea0a050fe6746cb66dfab14cda27e5ae09d7c155aa554f399fa4a863f0e8c1af787e5c602a801d3a2ec41e395a978d56729457fe6102d7d9e9119aa83643210b33c678f9d4109e3154ac9c759e490cb309b319cf708cae83ddadc3060a7a26564d1a24411cd552fe6620ea16b755697a4fc5e6e9d0cfc0c5c4a1874685429046a424c026db672e4c2c492898052ba59128d46200b40f880027a8b6610a4d559bdc9346d33a0a6b08e75c7fd43192b162bfd0aef0c716b31584827693f676f9a5047123466f0654eade34972586b31c6ce7e395f4b478cb"
)
func WindscribeRegionChoices() (choices []string) {
servers := WindscribeServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
}
return choices
}
func WindscribeServers() []models.WindscribeServer {
return []models.WindscribeServer{
{Region: "Albania", IPs: []net.IP{{31, 171, 152, 179}}},
{Region: "Argentina", IPs: []net.IP{{167, 250, 6, 121}, {190, 105, 236, 19}, {190, 105, 236, 32}, {190, 105, 236, 50}, {200, 85, 152, 110}}},
{Region: "Australia", IPs: []net.IP{{43, 245, 160, 35}, {45, 121, 208, 160}, {45, 121, 209, 160}, {45, 121, 210, 208}, {103, 62, 50, 208}, {103, 77, 233, 67}, {103, 77, 234, 211}, {103, 108, 92, 83}, {116, 90, 72, 243}, {116, 206, 228, 67}, {116, 206, 229, 131}}},
{Region: "Austria", IPs: []net.IP{{89, 187, 168, 66}, {217, 64, 127, 11}}},
{Region: "Azerbaijan", IPs: []net.IP{{85, 132, 61, 123}}},
{Region: "Belgium", IPs: []net.IP{{185, 232, 21, 131}, {194, 187, 251, 147}}},
{Region: "Bosnia", IPs: []net.IP{{185, 99, 3, 24}}},
{Region: "Brazil", IPs: []net.IP{{177, 54, 144, 68}, {177, 67, 80, 59}, {189, 1, 172, 12}}},
{Region: "Bulgaria", IPs: []net.IP{{185, 94, 192, 35}}},
{Region: "Canada East", IPs: []net.IP{{23, 154, 160, 177}, {66, 70, 148, 80}, {104, 227, 235, 129}, {104, 254, 92, 11}, {104, 254, 92, 91}, {144, 168, 163, 160}, {144, 168, 163, 193}, {184, 75, 212, 91}, {192, 190, 19, 65}, {192, 190, 19, 97}, {198, 8, 85, 195}, {198, 8, 85, 210}, {199, 204, 208, 158}}},
{Region: "Canada West", IPs: []net.IP{{104, 218, 61, 1}, {104, 218, 61, 33}, {162, 221, 207, 95}, {208, 78, 41, 1}, {208, 78, 41, 131}, {208, 78, 41, 163}}},
{Region: "Colombia", IPs: []net.IP{{138, 121, 203, 203}, {138, 186, 141, 155}}},
{Region: "Croatia", IPs: []net.IP{{85, 10, 56, 252}}},
{Region: "Cyprus", IPs: []net.IP{{157, 97, 132, 43}}},
{Region: "Czech republic", IPs: []net.IP{{185, 156, 174, 11}, {185, 246, 210, 2}}},
{Region: "Denmark", IPs: []net.IP{{134, 90, 149, 147}, {185, 206, 224, 195}}},
{Region: "Estonia", IPs: []net.IP{{46, 22, 211, 251}, {196, 196, 216, 131}}},
{Region: "Fake antarctica", IPs: []net.IP{{23, 154, 160, 212}, {23, 154, 160, 222}}},
{Region: "Finland", IPs: []net.IP{{185, 112, 82, 227}, {194, 34, 133, 82}}},
{Region: "France", IPs: []net.IP{{45, 89, 174, 35}, {82, 102, 18, 35}, {84, 17, 42, 2}, {84, 17, 42, 34}, {185, 156, 173, 187}}},
{Region: "Georgia", IPs: []net.IP{{188, 93, 90, 83}}},
{Region: "Germany", IPs: []net.IP{{45, 87, 212, 51}, {89, 249, 65, 19}, {185, 130, 184, 195}, {195, 181, 170, 66}, {195, 181, 175, 98}, {217, 138, 194, 115}}},
{Region: "Greece", IPs: []net.IP{{78, 108, 38, 155}, {185, 226, 64, 111}, {188, 123, 126, 146}}},
{Region: "Hong kong", IPs: []net.IP{{84, 17, 57, 114}, {103, 10, 197, 99}}},
{Region: "Hungary", IPs: []net.IP{{185, 104, 187, 43}}},
{Region: "Iceland", IPs: []net.IP{{82, 221, 139, 38}, {185, 165, 170, 2}}},
{Region: "India", IPs: []net.IP{{103, 205, 140, 227}, {169, 38, 68, 188}, {169, 38, 72, 12}, {169, 38, 72, 14}}},
{Region: "Indonesia", IPs: []net.IP{{45, 127, 134, 91}}},
{Region: "Ireland", IPs: []net.IP{{185, 24, 232, 146}, {185, 104, 219, 2}}},
{Region: "Israel", IPs: []net.IP{{160, 116, 0, 27}, {185, 191, 205, 139}}},
{Region: "Italy", IPs: []net.IP{{37, 120, 135, 83}, {37, 120, 207, 19}, {84, 17, 59, 66}, {87, 101, 94, 195}, {89, 40, 182, 3}}},
{Region: "Japan", IPs: []net.IP{{89, 187, 161, 114}, {193, 148, 16, 243}}},
{Region: "Latvia", IPs: []net.IP{{85, 254, 72, 23}, {89, 111, 33, 220}}},
{Region: "Lithuania", IPs: []net.IP{{85, 206, 163, 225}}},
{Region: "Macedonia", IPs: []net.IP{{185, 225, 28, 51}}},
{Region: "Malaysia", IPs: []net.IP{{103, 106, 250, 31}, {103, 212, 69, 232}}},
{Region: "Mexico", IPs: []net.IP{{143, 255, 57, 67}, {190, 103, 179, 211}, {190, 103, 179, 217}, {201, 131, 125, 107}}},
{Region: "Moldova", IPs: []net.IP{{178, 175, 144, 123}}},
{Region: "Netherlands", IPs: []net.IP{{37, 120, 192, 19}, {46, 166, 143, 98}, {72, 11, 157, 35}, {72, 11, 157, 67}, {84, 17, 46, 2}, {185, 212, 171, 131}, {185, 253, 96, 3}}},
{Region: "New zealand", IPs: []net.IP{{103, 62, 49, 113}, {103, 108, 94, 163}}},
{Region: "Norway", IPs: []net.IP{{37, 120, 203, 67}, {185, 206, 225, 131}}},
{Region: "Philippines", IPs: []net.IP{{103, 103, 0, 118}, {141, 98, 215, 211}}},
{Region: "Poland", IPs: []net.IP{{5, 133, 11, 116}, {84, 17, 55, 98}, {185, 244, 214, 35}}},
{Region: "Portugal", IPs: []net.IP{{94, 46, 13, 215}, {185, 15, 21, 66}}},
{Region: "Romania", IPs: []net.IP{{89, 46, 103, 147}, {91, 207, 102, 147}}},
{Region: "Russia", IPs: []net.IP{{94, 242, 62, 19}, {94, 242, 62, 67}, {95, 213, 193, 195}, {95, 213, 193, 227}, {185, 22, 175, 132}, {188, 124, 42, 99}, {188, 124, 42, 115}}},
{Region: "Serbia", IPs: []net.IP{{141, 98, 103, 19}}},
{Region: "Singapore", IPs: []net.IP{{82, 102, 25, 131}, {89, 187, 162, 130}, {103, 62, 48, 224}, {185, 200, 117, 163}}},
{Region: "Slovakia", IPs: []net.IP{{185, 245, 85, 3}}},
{Region: "South Africa", IPs: []net.IP{{129, 232, 167, 211}, {165, 73, 248, 91}, {197, 242, 156, 53}, {197, 242, 157, 235}}},
{Region: "South Korea", IPs: []net.IP{{27, 255, 92, 52}, {61, 97, 244, 39}, {103, 212, 223, 3}, {218, 232, 76, 179}}},
{Region: "Spain", IPs: []net.IP{{37, 120, 142, 227}, {89, 238, 178, 43}, {185, 253, 99, 131}, {217, 138, 218, 99}}},
{Region: "Sweden", IPs: []net.IP{{31, 13, 191, 67}, {79, 142, 76, 198}, {195, 181, 166, 129}}},
{Region: "Switzerland", IPs: []net.IP{{31, 7, 57, 242}, {37, 120, 213, 163}, {84, 17, 53, 2}, {89, 187, 165, 98}, {185, 156, 175, 179}}},
{Region: "Thailand", IPs: []net.IP{{27, 254, 130, 221}, {202, 129, 16, 147}, {202, 129, 16, 155}}},
{Region: "Tunisia", IPs: []net.IP{{41, 231, 5, 23}}},
{Region: "Turkey", IPs: []net.IP{{45, 123, 118, 156}, {45, 123, 119, 11}, {79, 98, 131, 43}, {176, 53, 113, 163}, {185, 125, 33, 227}}},
{Region: "US Central", IPs: []net.IP{{67, 212, 238, 196}, {69, 12, 94, 67}, {104, 129, 18, 3}, {104, 129, 18, 131}, {104, 223, 92, 163}, {107, 150, 31, 3}, {107, 150, 31, 67}, {107, 150, 31, 131}, {107, 161, 86, 131}, {107, 182, 234, 240}, {161, 129, 70, 195}, {162, 222, 198, 67}, {172, 241, 26, 78}, {172, 241, 131, 129}, {198, 12, 76, 211}, {198, 54, 128, 116}, {198, 55, 125, 195}, {199, 115, 96, 83}, {204, 44, 112, 67}, {204, 44, 112, 131}, {206, 217, 139, 19}, {206, 217, 139, 195}, {206, 217, 143, 131}}},
{Region: "US East", IPs: []net.IP{{23, 82, 8, 143}, {23, 82, 136, 93}, {23, 83, 91, 170}, {23, 105, 170, 130}, {23, 105, 170, 139}, {23, 105, 170, 151}, {23, 226, 141, 195}, {45, 87, 214, 35}, {67, 21, 32, 145}, {67, 219, 146, 67}, {68, 235, 35, 12}, {68, 235, 35, 172}, {68, 235, 50, 227}, {76, 72, 175, 99}, {86, 106, 87, 83}, {104, 168, 34, 147}, {104, 223, 127, 195}, {107, 150, 29, 131}, {142, 234, 200, 176}, {156, 96, 59, 102}, {162, 222, 195, 67}, {167, 160, 167, 195}, {167, 160, 172, 3}, {173, 44, 36, 67}, {173, 208, 45, 33}, {185, 232, 22, 195}, {198, 12, 64, 35}, {198, 147, 22, 225}, {199, 217, 104, 227}, {199, 217, 105, 227}, {206, 217, 128, 3}, {206, 217, 129, 227}, {217, 138, 255, 163}, {217, 138, 255, 179}}},
{Region: "US West", IPs: []net.IP{{23, 83, 130, 166}, {23, 83, 131, 187}, {23, 94, 74, 99}, {37, 120, 147, 163}, {64, 120, 2, 174}, {66, 115, 176, 3}, {82, 102, 30, 67}, {89, 187, 185, 34}, {89, 187, 187, 98}, {104, 129, 3, 67}, {104, 129, 3, 163}, {104, 129, 56, 67}, {104, 129, 56, 131}, {104, 152, 222, 33}, {167, 88, 60, 227}, {167, 88, 60, 243}, {172, 241, 214, 202}, {172, 241, 250, 131}, {172, 255, 125, 141}, {185, 236, 200, 35}, {192, 3, 20, 51}, {198, 12, 116, 195}, {198, 23, 242, 147}, {209, 58, 129, 121}, {212, 103, 49, 67}, {216, 45, 53, 131}, {217, 138, 217, 51}, {217, 138, 217, 211}}},
{Region: "Ukraine", IPs: []net.IP{{45, 141, 156, 11}, {45, 141, 156, 50}}},
{Region: "United Arab Emirates", IPs: []net.IP{{45, 9, 249, 43}}},
{Region: "United Kingdom", IPs: []net.IP{{2, 58, 29, 17}, {2, 58, 29, 145}, {81, 92, 207, 69}, {84, 17, 50, 130}, {89, 44, 201, 99}, {89, 238, 135, 133}, {89, 238, 150, 229}, {185, 212, 168, 133}, {212, 102, 63, 32}, {212, 102, 63, 62}, {217, 138, 254, 51}}},
{Region: "Vietnam", IPs: []net.IP{{103, 9, 76, 197}, {103, 9, 79, 186}, {103, 9, 79, 219}}},
{Region: "Windflix CA", IPs: []net.IP{{104, 218, 60, 111}, {104, 254, 92, 99}}},
{Region: "Windflix JP", IPs: []net.IP{{5, 181, 235, 67}}},
{Region: "Windflix UK", IPs: []net.IP{{45, 9, 248, 3}, {81, 92, 200, 85}, {89, 47, 62, 83}}},
{Region: "Windflix US", IPs: []net.IP{{38, 132, 101, 211}, {38, 132, 122, 131}, {38, 132, 122, 195}, {77, 81, 136, 99}, {185, 232, 22, 131}, {217, 138, 206, 211}}},
}
}

41
internal/dns/command.go Normal file
View File

@@ -0,0 +1,41 @@
package dns
import (
"context"
"fmt"
"io"
"strings"
"github.com/qdm12/gluetun/internal/constants"
)
func (c *configurator) Start(ctx context.Context, verbosityDetailsLevel uint8) (stdout io.ReadCloser, waitFn func() error, err error) {
c.logger.Info("starting unbound")
args := []string{"-d", "-c", string(constants.UnboundConf)}
if verbosityDetailsLevel > 0 {
args = append(args, "-"+strings.Repeat("v", int(verbosityDetailsLevel)))
}
// Only logs to stderr
_, stdout, waitFn, err = c.commander.Start(ctx, "unbound", args...)
return stdout, waitFn, err
}
func (c *configurator) Version(ctx context.Context) (version string, err error) {
output, err := c.commander.Run(ctx, "unbound", "-V")
if err != nil {
return "", fmt.Errorf("unbound version: %w", err)
}
for _, line := range strings.Split(output, "\n") {
if strings.Contains(line, "Version ") {
words := strings.Fields(line)
if len(words) < 2 {
continue
}
version = words[1]
}
}
if version == "" {
return "", fmt.Errorf("unbound version was not found in %q", output)
}
return version, nil
}

View File

@@ -0,0 +1,73 @@
package dns
import (
"context"
"fmt"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/golibs/command/mock_command"
"github.com/qdm12/golibs/logging/mock_logging"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/qdm12/gluetun/internal/constants"
)
func Test_Start(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
logger := mock_logging.NewMockLogger(mockCtrl)
logger.EXPECT().Info("starting unbound").Times(1)
commander := mock_command.NewMockCommander(mockCtrl)
commander.EXPECT().Start(context.Background(), "unbound", "-d", "-c", string(constants.UnboundConf), "-vv").
Return(nil, nil, nil, nil).Times(1)
c := &configurator{commander: commander, logger: logger}
stdout, waitFn, err := c.Start(context.Background(), 2)
assert.Nil(t, stdout)
assert.Nil(t, waitFn)
assert.NoError(t, err)
}
func Test_Version(t *testing.T) {
t.Parallel()
tests := map[string]struct {
runOutput string
runErr error
version string
err error
}{
"no data": {
err: fmt.Errorf(`unbound version was not found in ""`),
},
"2 lines with version": {
runOutput: "Version \nVersion 1.0-a hello\n",
version: "1.0-a",
},
"run error": {
runErr: fmt.Errorf("error"),
err: fmt.Errorf("unbound version: error"),
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
commander := mock_command.NewMockCommander(mockCtrl)
commander.EXPECT().Run(context.Background(), "unbound", "-V").
Return(tc.runOutput, tc.runErr).Times(1)
c := &configurator{commander: commander}
version, err := c.Version(context.Background())
if tc.err != nil {
require.Error(t, err)
assert.Equal(t, tc.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, tc.version, version)
})
}
}

289
internal/dns/conf.go Normal file
View File

@@ -0,0 +1,289 @@
package dns
import (
"fmt"
"net/http"
"sort"
"strings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/network"
)
func (c *configurator) MakeUnboundConf(settings settings.DNS, uid, gid int) (err error) {
c.logger.Info("generating Unbound configuration")
lines, warnings := generateUnboundConf(settings, c.client, c.logger)
for _, warning := range warnings {
c.logger.Warn(warning)
}
return c.fileManager.WriteLinesToFile(
string(constants.UnboundConf),
lines,
files.Ownership(uid, gid),
files.Permissions(0400))
}
// MakeUnboundConf generates an Unbound configuration from the user provided settings
func generateUnboundConf(settings settings.DNS, client network.Client, logger logging.Logger) (lines []string, warnings []error) {
doIPv6 := "no"
if settings.IPv6 {
doIPv6 = "yes"
}
serverSection := map[string]string{
// Logging
"verbosity": fmt.Sprintf("%d", settings.VerbosityLevel),
"val-log-level": fmt.Sprintf("%d", settings.ValidationLogLevel),
"use-syslog": "no",
// Performance
"num-threads": "1",
"prefetch": "yes",
"prefetch-key": "yes",
"key-cache-size": "16m",
"key-cache-slabs": "4",
"msg-cache-size": "4m",
"msg-cache-slabs": "4",
"rrset-cache-size": "4m",
"rrset-cache-slabs": "4",
"cache-min-ttl": "3600",
"cache-max-ttl": "9000",
// Privacy
"rrset-roundrobin": "yes",
"hide-identity": "yes",
"hide-version": "yes",
// Security
"tls-cert-bundle": fmt.Sprintf("%q", constants.CACertificates),
"root-hints": fmt.Sprintf("%q", constants.RootHints),
"trust-anchor-file": fmt.Sprintf("%q", constants.RootKey),
"harden-below-nxdomain": "yes",
"harden-referral-path": "yes",
"harden-algo-downgrade": "yes",
// Network
"do-ip4": "yes",
"do-ip6": doIPv6,
"interface": "127.0.0.1",
"port": "53",
// Other
"username": "\"nonrootuser\"",
}
// Block lists
hostnamesLines, ipsLines, warnings := buildBlocked(client,
settings.BlockMalicious, settings.BlockAds, settings.BlockSurveillance,
settings.AllowedHostnames, settings.PrivateAddresses,
)
logger.Info("%d hostnames blocked overall", len(hostnamesLines))
logger.Info("%d IP addresses blocked overall", len(ipsLines))
sort.Slice(hostnamesLines, func(i, j int) bool { // for unit tests really
return hostnamesLines[i] < hostnamesLines[j]
})
sort.Slice(ipsLines, func(i, j int) bool { // for unit tests really
return ipsLines[i] < ipsLines[j]
})
// Server
lines = append(lines, "server:")
serverLines := make([]string, len(serverSection))
i := 0
for k, v := range serverSection {
serverLines[i] = " " + k + ": " + v
i++
}
sort.Slice(serverLines, func(i, j int) bool {
return serverLines[i] < serverLines[j]
})
lines = append(lines, serverLines...)
lines = append(lines, hostnamesLines...)
lines = append(lines, ipsLines...)
// Forward zone
lines = append(lines, "forward-zone:")
forwardZoneSection := map[string]string{
"name": "\".\"",
"forward-tls-upstream": "yes",
}
if settings.Caching {
forwardZoneSection["forward-no-cache"] = "no"
} else {
forwardZoneSection["forward-no-cache"] = "yes"
}
forwardZoneLines := make([]string, len(forwardZoneSection))
i = 0
for k, v := range forwardZoneSection {
forwardZoneLines[i] = " " + k + ": " + v
i++
}
sort.Slice(forwardZoneLines, func(i, j int) bool {
return forwardZoneLines[i] < forwardZoneLines[j]
})
for _, provider := range settings.Providers {
providerData := constants.DNSProviderMapping()[provider]
for _, IP := range providerData.IPs {
forwardZoneLines = append(forwardZoneLines,
fmt.Sprintf(" forward-addr: %s@853#%s", IP, providerData.Host))
}
}
lines = append(lines, forwardZoneLines...)
return lines, warnings
}
func buildBlocked(client network.Client, blockMalicious, blockAds, blockSurveillance bool,
allowedHostnames, privateAddresses []string) (hostnamesLines, ipsLines []string, errs []error) {
chHostnames := make(chan []string)
chIPs := make(chan []string)
chErrors := make(chan []error)
go func() {
lines, errs := buildBlockedHostnames(client, blockMalicious, blockAds, blockSurveillance, allowedHostnames)
chHostnames <- lines
chErrors <- errs
}()
go func() {
lines, errs := buildBlockedIPs(client, blockMalicious, blockAds, blockSurveillance, privateAddresses)
chIPs <- lines
chErrors <- errs
}()
n := 2
for n > 0 {
select {
case lines := <-chHostnames:
hostnamesLines = append(hostnamesLines, lines...)
case lines := <-chIPs:
ipsLines = append(ipsLines, lines...)
case routineErrs := <-chErrors:
errs = append(errs, routineErrs...)
n--
}
}
return hostnamesLines, ipsLines, errs
}
func getList(client network.Client, url string) (results []string, err error) {
content, status, err := client.GetContent(url)
if err != nil {
return nil, err
} else if status != http.StatusOK {
return nil, fmt.Errorf("HTTP status code is %d and not 200", status)
}
results = strings.Split(string(content), "\n")
// remove empty lines
last := len(results) - 1
for i := range results {
if len(results[i]) == 0 {
results[i] = results[last]
last--
}
}
results = results[:last+1]
if len(results) == 0 {
return nil, nil
}
return results, nil
}
func buildBlockedHostnames(client network.Client, blockMalicious, blockAds, blockSurveillance bool,
allowedHostnames []string) (lines []string, errs []error) {
chResults := make(chan []string)
chError := make(chan error)
listsLeftToFetch := 0
if blockMalicious {
listsLeftToFetch++
go func() {
results, err := getList(client, string(constants.MaliciousBlockListHostnamesURL))
chResults <- results
chError <- err
}()
}
if blockAds {
listsLeftToFetch++
go func() {
results, err := getList(client, string(constants.AdsBlockListHostnamesURL))
chResults <- results
chError <- err
}()
}
if blockSurveillance {
listsLeftToFetch++
go func() {
results, err := getList(client, string(constants.SurveillanceBlockListHostnamesURL))
chResults <- results
chError <- err
}()
}
uniqueResults := make(map[string]struct{})
for listsLeftToFetch > 0 {
select {
case results := <-chResults:
for _, result := range results {
uniqueResults[result] = struct{}{}
}
case err := <-chError:
listsLeftToFetch--
if err != nil {
errs = append(errs, err)
}
}
}
for _, allowedHostname := range allowedHostnames {
delete(uniqueResults, allowedHostname)
}
for result := range uniqueResults {
lines = append(lines, " local-zone: \""+result+"\" static")
}
return lines, errs
}
func buildBlockedIPs(client network.Client, blockMalicious, blockAds, blockSurveillance bool,
privateAddresses []string) (lines []string, errs []error) {
chResults := make(chan []string)
chError := make(chan error)
listsLeftToFetch := 0
if blockMalicious {
listsLeftToFetch++
go func() {
results, err := getList(client, string(constants.MaliciousBlockListIPsURL))
chResults <- results
chError <- err
}()
}
if blockAds {
listsLeftToFetch++
go func() {
results, err := getList(client, string(constants.AdsBlockListIPsURL))
chResults <- results
chError <- err
}()
}
if blockSurveillance {
listsLeftToFetch++
go func() {
results, err := getList(client, string(constants.SurveillanceBlockListIPsURL))
chResults <- results
chError <- err
}()
}
uniqueResults := make(map[string]struct{})
for listsLeftToFetch > 0 {
select {
case results := <-chResults:
for _, result := range results {
uniqueResults[result] = struct{}{}
}
case err := <-chError:
listsLeftToFetch--
if err != nil {
errs = append(errs, err)
}
}
}
for _, privateAddress := range privateAddresses {
uniqueResults[privateAddress] = struct{}{}
}
for result := range uniqueResults {
lines = append(lines, " private-address: "+result)
}
return lines, errs
}

530
internal/dns/conf_test.go Normal file
View File

@@ -0,0 +1,530 @@
package dns
import (
"fmt"
"strings"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging/mock_logging"
"github.com/qdm12/golibs/network/mock_network"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_generateUnboundConf(t *testing.T) {
t.Parallel()
settings := settings.DNS{
Providers: []models.DNSProvider{constants.Cloudflare, constants.Quad9},
AllowedHostnames: []string{"a"},
PrivateAddresses: []string{"9.9.9.9"},
BlockMalicious: true,
BlockSurveillance: false,
BlockAds: false,
VerbosityLevel: 2,
ValidationLogLevel: 3,
Caching: true,
IPv6: true,
}
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
client := mock_network.NewMockClient(mockCtrl)
client.EXPECT().GetContent(string(constants.MaliciousBlockListHostnamesURL)).
Return([]byte("b\na\nc"), 200, nil).Times(1)
client.EXPECT().GetContent(string(constants.MaliciousBlockListIPsURL)).
Return([]byte("c\nd\n"), 200, nil).Times(1)
logger := mock_logging.NewMockLogger(mockCtrl)
logger.EXPECT().Info("%d hostnames blocked overall", 2).Times(1)
logger.EXPECT().Info("%d IP addresses blocked overall", 3).Times(1)
lines, warnings := generateUnboundConf(settings, client, logger)
require.Len(t, warnings, 0)
expected := `
server:
cache-max-ttl: 9000
cache-min-ttl: 3600
do-ip4: yes
do-ip6: yes
harden-algo-downgrade: yes
harden-below-nxdomain: yes
harden-referral-path: yes
hide-identity: yes
hide-version: yes
interface: 127.0.0.1
key-cache-size: 16m
key-cache-slabs: 4
msg-cache-size: 4m
msg-cache-slabs: 4
num-threads: 1
port: 53
prefetch-key: yes
prefetch: yes
root-hints: "/etc/unbound/root.hints"
rrset-cache-size: 4m
rrset-cache-slabs: 4
rrset-roundrobin: yes
tls-cert-bundle: "/etc/ssl/certs/ca-certificates.crt"
trust-anchor-file: "/etc/unbound/root.key"
use-syslog: no
username: "nonrootuser"
val-log-level: 3
verbosity: 2
local-zone: "b" static
local-zone: "c" static
private-address: 9.9.9.9
private-address: c
private-address: d
forward-zone:
forward-no-cache: no
forward-tls-upstream: yes
name: "."
forward-addr: 1.1.1.1@853#cloudflare-dns.com
forward-addr: 1.0.0.1@853#cloudflare-dns.com
forward-addr: 2606:4700:4700::1111@853#cloudflare-dns.com
forward-addr: 2606:4700:4700::1001@853#cloudflare-dns.com
forward-addr: 9.9.9.9@853#dns.quad9.net
forward-addr: 149.112.112.112@853#dns.quad9.net
forward-addr: 2620:fe::fe@853#dns.quad9.net
forward-addr: 2620:fe::9@853#dns.quad9.net`
assert.Equal(t, expected, "\n"+strings.Join(lines, "\n"))
}
func Test_buildBlocked(t *testing.T) {
t.Parallel()
type blockParams struct {
blocked bool
content []byte
clientErr error
}
tests := map[string]struct {
malicious blockParams
ads blockParams
surveillance blockParams
allowedHostnames []string
privateAddresses []string
hostnamesLines []string
ipsLines []string
errsString []string
}{
"none blocked": {},
"all blocked without lists": {
malicious: blockParams{
blocked: true,
},
ads: blockParams{
blocked: true,
},
surveillance: blockParams{
blocked: true,
},
},
"all blocked with lists": {
malicious: blockParams{
blocked: true,
content: []byte("malicious"),
},
ads: blockParams{
blocked: true,
content: []byte("ads"),
},
surveillance: blockParams{
blocked: true,
content: []byte("surveillance"),
},
hostnamesLines: []string{
" local-zone: \"ads\" static",
" local-zone: \"malicious\" static",
" local-zone: \"surveillance\" static"},
ipsLines: []string{
" private-address: ads",
" private-address: malicious",
" private-address: surveillance"},
},
"all blocked with allowed hostnames": {
malicious: blockParams{
blocked: true,
content: []byte("malicious"),
},
ads: blockParams{
blocked: true,
content: []byte("ads"),
},
surveillance: blockParams{
blocked: true,
content: []byte("surveillance"),
},
allowedHostnames: []string{"ads"},
hostnamesLines: []string{
" local-zone: \"malicious\" static",
" local-zone: \"surveillance\" static"},
ipsLines: []string{
" private-address: ads",
" private-address: malicious",
" private-address: surveillance"},
},
"all blocked with private addresses": {
malicious: blockParams{
blocked: true,
content: []byte("malicious"),
},
ads: blockParams{
blocked: true,
content: []byte("ads"),
},
surveillance: blockParams{
blocked: true,
content: []byte("surveillance"),
},
privateAddresses: []string{"ads", "192.100.1.5"},
hostnamesLines: []string{
" local-zone: \"ads\" static",
" local-zone: \"malicious\" static",
" local-zone: \"surveillance\" static"},
ipsLines: []string{
" private-address: 192.100.1.5",
" private-address: ads",
" private-address: malicious",
" private-address: surveillance"},
},
"all blocked with lists and one error": {
malicious: blockParams{
blocked: true,
content: []byte("malicious"),
},
ads: blockParams{
blocked: true,
content: []byte("ads"),
clientErr: fmt.Errorf("ads error"),
},
surveillance: blockParams{
blocked: true,
content: []byte("surveillance"),
},
hostnamesLines: []string{
" local-zone: \"malicious\" static",
" local-zone: \"surveillance\" static"},
ipsLines: []string{
" private-address: malicious",
" private-address: surveillance"},
errsString: []string{"ads error", "ads error"},
},
"all blocked with errors": {
malicious: blockParams{
blocked: true,
clientErr: fmt.Errorf("malicious"),
},
ads: blockParams{
blocked: true,
clientErr: fmt.Errorf("ads"),
},
surveillance: blockParams{
blocked: true,
clientErr: fmt.Errorf("surveillance"),
},
errsString: []string{"malicious", "malicious", "ads", "ads", "surveillance", "surveillance"},
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
client := mock_network.NewMockClient(mockCtrl)
if tc.malicious.blocked {
client.EXPECT().GetContent(string(constants.MaliciousBlockListHostnamesURL)).
Return(tc.malicious.content, 200, tc.malicious.clientErr).Times(1)
client.EXPECT().GetContent(string(constants.MaliciousBlockListIPsURL)).
Return(tc.malicious.content, 200, tc.malicious.clientErr).Times(1)
}
if tc.ads.blocked {
client.EXPECT().GetContent(string(constants.AdsBlockListHostnamesURL)).
Return(tc.ads.content, 200, tc.ads.clientErr).Times(1)
client.EXPECT().GetContent(string(constants.AdsBlockListIPsURL)).
Return(tc.ads.content, 200, tc.ads.clientErr).Times(1)
}
if tc.surveillance.blocked {
client.EXPECT().GetContent(string(constants.SurveillanceBlockListHostnamesURL)).
Return(tc.surveillance.content, 200, tc.surveillance.clientErr).Times(1)
client.EXPECT().GetContent(string(constants.SurveillanceBlockListIPsURL)).
Return(tc.surveillance.content, 200, tc.surveillance.clientErr).Times(1)
}
hostnamesLines, ipsLines, errs := buildBlocked(client, tc.malicious.blocked, tc.ads.blocked, tc.surveillance.blocked,
tc.allowedHostnames, tc.privateAddresses)
var errsString []string
for _, err := range errs {
errsString = append(errsString, err.Error())
}
assert.ElementsMatch(t, tc.errsString, errsString)
assert.ElementsMatch(t, tc.hostnamesLines, hostnamesLines)
assert.ElementsMatch(t, tc.ipsLines, ipsLines)
})
}
}
func Test_getList(t *testing.T) {
t.Parallel()
tests := map[string]struct {
content []byte
status int
clientErr error
results []string
err error
}{
"no result": {nil, 200, nil, nil, nil},
"bad status": {nil, 500, nil, nil, fmt.Errorf("HTTP status code is 500 and not 200")},
"network error": {nil, 200, fmt.Errorf("error"), nil, fmt.Errorf("error")},
"results": {[]byte("a\nb\nc\n"), 200, nil, []string{"a", "b", "c"}, nil},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
client := mock_network.NewMockClient(mockCtrl)
client.EXPECT().GetContent("irrelevant_url").
Return(tc.content, tc.status, tc.clientErr).Times(1)
results, err := getList(client, "irrelevant_url")
if tc.err != nil {
require.Error(t, err)
assert.Equal(t, tc.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, tc.results, results)
})
}
}
func Test_buildBlockedHostnames(t *testing.T) {
t.Parallel()
type blockParams struct {
blocked bool
content []byte
clientErr error
}
tests := map[string]struct {
malicious blockParams
ads blockParams
surveillance blockParams
allowedHostnames []string
lines []string
errsString []string
}{
"nothing blocked": {
lines: nil,
errsString: nil,
},
"only malicious blocked": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
clientErr: nil,
},
lines: []string{
" local-zone: \"site_a\" static",
" local-zone: \"site_b\" static"},
errsString: nil,
},
"all blocked with some duplicates": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
},
ads: blockParams{
blocked: true,
content: []byte("site_a\nsite_c"),
},
surveillance: blockParams{
blocked: true,
content: []byte("site_c\nsite_a"),
},
lines: []string{
" local-zone: \"site_a\" static",
" local-zone: \"site_b\" static",
" local-zone: \"site_c\" static"},
errsString: nil,
},
"all blocked with one errored": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
},
ads: blockParams{
blocked: true,
content: []byte("site_a\nsite_c"),
},
surveillance: blockParams{
blocked: true,
clientErr: fmt.Errorf("surveillance error"),
},
lines: []string{
" local-zone: \"site_a\" static",
" local-zone: \"site_b\" static",
" local-zone: \"site_c\" static"},
errsString: []string{"surveillance error"},
},
"blocked with allowed hostnames": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
},
ads: blockParams{
blocked: true,
content: []byte("site_c\nsite_d"),
},
allowedHostnames: []string{"site_b", "site_c"},
lines: []string{
" local-zone: \"site_a\" static",
" local-zone: \"site_d\" static"},
},
}
for name, tc := range tests { //nolint:dupl
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
client := mock_network.NewMockClient(mockCtrl)
if tc.malicious.blocked {
client.EXPECT().GetContent(string(constants.MaliciousBlockListHostnamesURL)).
Return(tc.malicious.content, 200, tc.malicious.clientErr).Times(1)
}
if tc.ads.blocked {
client.EXPECT().GetContent(string(constants.AdsBlockListHostnamesURL)).
Return(tc.ads.content, 200, tc.ads.clientErr).Times(1)
}
if tc.surveillance.blocked {
client.EXPECT().GetContent(string(constants.SurveillanceBlockListHostnamesURL)).
Return(tc.surveillance.content, 200, tc.surveillance.clientErr).Times(1)
}
lines, errs := buildBlockedHostnames(client,
tc.malicious.blocked, tc.ads.blocked, tc.surveillance.blocked, tc.allowedHostnames)
var errsString []string
for _, err := range errs {
errsString = append(errsString, err.Error())
}
assert.ElementsMatch(t, tc.errsString, errsString)
assert.ElementsMatch(t, tc.lines, lines)
})
}
}
func Test_buildBlockedIPs(t *testing.T) {
t.Parallel()
type blockParams struct {
blocked bool
content []byte
clientErr error
}
tests := map[string]struct {
malicious blockParams
ads blockParams
surveillance blockParams
privateAddresses []string
lines []string
errsString []string
}{
"nothing blocked": {
lines: nil,
errsString: nil,
},
"only malicious blocked": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
clientErr: nil,
},
lines: []string{
" private-address: site_a",
" private-address: site_b"},
errsString: nil,
},
"all blocked with some duplicates": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
},
ads: blockParams{
blocked: true,
content: []byte("site_a\nsite_c"),
},
surveillance: blockParams{
blocked: true,
content: []byte("site_c\nsite_a"),
},
lines: []string{
" private-address: site_a",
" private-address: site_b",
" private-address: site_c"},
errsString: nil,
},
"all blocked with one errored": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
},
ads: blockParams{
blocked: true,
content: []byte("site_a\nsite_c"),
},
surveillance: blockParams{
blocked: true,
clientErr: fmt.Errorf("surveillance error"),
},
lines: []string{
" private-address: site_a",
" private-address: site_b",
" private-address: site_c"},
errsString: []string{"surveillance error"},
},
"blocked with private addresses": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
},
ads: blockParams{
blocked: true,
content: []byte("site_c"),
},
privateAddresses: []string{"site_c", "site_d"},
lines: []string{
" private-address: site_a",
" private-address: site_b",
" private-address: site_c",
" private-address: site_d"},
},
}
for name, tc := range tests { //nolint:dupl
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
client := mock_network.NewMockClient(mockCtrl)
if tc.malicious.blocked {
client.EXPECT().GetContent(string(constants.MaliciousBlockListIPsURL)).
Return(tc.malicious.content, 200, tc.malicious.clientErr).Times(1)
}
if tc.ads.blocked {
client.EXPECT().GetContent(string(constants.AdsBlockListIPsURL)).
Return(tc.ads.content, 200, tc.ads.clientErr).Times(1)
}
if tc.surveillance.blocked {
client.EXPECT().GetContent(string(constants.SurveillanceBlockListIPsURL)).
Return(tc.surveillance.content, 200, tc.surveillance.clientErr).Times(1)
}
lines, errs := buildBlockedIPs(client,
tc.malicious.blocked, tc.ads.blocked, tc.surveillance.blocked, tc.privateAddresses)
var errsString []string
for _, err := range errs {
errsString = append(errsString, err.Error())
}
assert.ElementsMatch(t, tc.errsString, errsString)
assert.ElementsMatch(t, tc.lines, lines)
})
}
}

42
internal/dns/dns.go Normal file
View File

@@ -0,0 +1,42 @@
package dns
import (
"context"
"io"
"net"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/command"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/network"
)
type Configurator interface {
DownloadRootHints(uid, gid int) error
DownloadRootKey(uid, gid int) error
MakeUnboundConf(settings settings.DNS, uid, gid int) (err error)
UseDNSInternally(IP net.IP)
UseDNSSystemWide(ip net.IP, keepNameserver bool) error
Start(ctx context.Context, logLevel uint8) (stdout io.ReadCloser, waitFn func() error, err error)
WaitForUnbound() (err error)
Version(ctx context.Context) (version string, err error)
}
type configurator struct {
logger logging.Logger
client network.Client
fileManager files.FileManager
commander command.Commander
lookupIP func(host string) ([]net.IP, error)
}
func NewConfigurator(logger logging.Logger, client network.Client, fileManager files.FileManager) Configurator {
return &configurator{
logger: logger.WithPrefix("dns configurator: "),
client: client,
fileManager: fileManager,
commander: command.NewCommander(),
lookupIP: net.LookupIP,
}
}

297
internal/dns/loop.go Normal file
View File

@@ -0,0 +1,297 @@
package dns
import (
"context"
"net"
"sync"
"time"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/command"
"github.com/qdm12/golibs/logging"
)
type Looper interface {
Run(ctx context.Context, wg *sync.WaitGroup)
RunRestartTicker(ctx context.Context)
Restart()
Start()
Stop()
GetSettings() (settings settings.DNS)
SetSettings(settings settings.DNS)
}
type looper struct {
conf Configurator
settings settings.DNS
settingsMutex sync.RWMutex
logger logging.Logger
streamMerger command.StreamMerger
uid int
gid int
restart chan struct{}
start chan struct{}
stop chan struct{}
updateTicker chan struct{}
}
func NewLooper(conf Configurator, settings settings.DNS, logger logging.Logger,
streamMerger command.StreamMerger, uid, gid int) Looper {
return &looper{
conf: conf,
settings: settings,
logger: logger.WithPrefix("dns over tls: "),
uid: uid,
gid: gid,
streamMerger: streamMerger,
restart: make(chan struct{}),
start: make(chan struct{}),
stop: make(chan struct{}),
updateTicker: make(chan struct{}),
}
}
func (l *looper) Restart() { l.restart <- struct{}{} }
func (l *looper) Start() { l.start <- struct{}{} }
func (l *looper) Stop() { l.stop <- struct{}{} }
func (l *looper) GetSettings() (settings settings.DNS) {
l.settingsMutex.RLock()
defer l.settingsMutex.RUnlock()
return l.settings
}
func (l *looper) SetSettings(settings settings.DNS) {
l.settingsMutex.Lock()
defer l.settingsMutex.Unlock()
updatePeriodDiffers := l.settings.UpdatePeriod != settings.UpdatePeriod
l.settings = settings
l.settingsMutex.Unlock()
if updatePeriodDiffers {
l.updateTicker <- struct{}{}
}
}
func (l *looper) isEnabled() bool {
l.settingsMutex.RLock()
defer l.settingsMutex.RUnlock()
return l.settings.Enabled
}
func (l *looper) setEnabled(enabled bool) {
l.settingsMutex.Lock()
defer l.settingsMutex.Unlock()
l.settings.Enabled = enabled
}
func (l *looper) logAndWait(ctx context.Context, err error) {
l.logger.Warn(err)
l.logger.Info("attempting restart in 10 seconds")
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
<-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) {
wg.Add(1)
defer wg.Done()
l.fallbackToUnencryptedDNS()
l.waitForFirstStart(ctx)
if ctx.Err() != nil {
return
}
defer l.logger.Warn("loop exited")
var unboundCtx context.Context
var unboundCancel context.CancelFunc = func() {}
var waitError chan error
triggeredRestart := false
l.setEnabled(true)
for ctx.Err() == nil {
l.waitForSubsequentStart(ctx, unboundCancel)
settings := l.GetSettings()
// Setup
if err := l.conf.DownloadRootHints(l.uid, l.gid); err != nil {
l.logAndWait(ctx, err)
continue
}
if err := l.conf.DownloadRootKey(l.uid, l.gid); err != nil {
l.logAndWait(ctx, err)
continue
}
if err := l.conf.MakeUnboundConf(settings, l.uid, l.gid); err != nil {
l.logAndWait(ctx, err)
continue
}
if triggeredRestart {
triggeredRestart = false
unboundCancel()
<-waitError
close(waitError)
}
unboundCtx, unboundCancel = context.WithCancel(context.Background())
stream, waitFn, err := l.conf.Start(unboundCtx, settings.VerbosityDetailsLevel)
if err != nil {
unboundCancel()
l.fallbackToUnencryptedDNS()
l.logAndWait(ctx, err)
continue
}
// Started successfully
go l.streamMerger.Merge(unboundCtx, stream, command.MergeName("unbound"))
l.conf.UseDNSInternally(net.IP{127, 0, 0, 1}) // use Unbound
if err := l.conf.UseDNSSystemWide(net.IP{127, 0, 0, 1}, settings.KeepNameserver); err != nil { // use Unbound
l.logger.Error(err)
}
if err := l.conf.WaitForUnbound(); err != nil {
unboundCancel()
l.fallbackToUnencryptedDNS()
l.logAndWait(ctx, err)
continue
}
waitError = make(chan error)
go func() {
err := waitFn() // blocking
waitError <- err
}()
stayHere := true
for stayHere {
select {
case <-ctx.Done():
l.logger.Warn("context canceled: exiting loop")
unboundCancel()
<-waitError
close(waitError)
return
case <-l.restart: // triggered restart
l.logger.Info("restarting")
// unboundCancel occurs next loop run when the setup is complete
triggeredRestart = true
stayHere = false
case <-l.start:
l.logger.Info("already started")
case <-l.stop:
l.logger.Info("stopping")
unboundCancel()
<-waitError
close(waitError)
l.setEnabled(false)
stayHere = false
case err := <-waitError: // unexpected error
close(waitError)
unboundCancel()
l.fallbackToUnencryptedDNS()
l.logAndWait(ctx, err)
stayHere = false
}
}
}
unboundCancel()
}
func (l *looper) fallbackToUnencryptedDNS() {
settings := l.GetSettings()
// Try with user provided plaintext ip address
targetIP := settings.PlaintextAddress
if targetIP != nil {
l.logger.Info("falling back on plaintext DNS at address %s", targetIP)
l.conf.UseDNSInternally(targetIP)
if err := l.conf.UseDNSSystemWide(targetIP, settings.KeepNameserver); err != nil {
l.logger.Error(err)
}
return
}
// Try with any IPv4 address from the providers chosen
for _, provider := range settings.Providers {
data := constants.DNSProviderMapping()[provider]
for _, targetIP = range data.IPs {
if targetIP.To4() != nil {
l.logger.Info("falling back on plaintext DNS at address %s", targetIP)
l.conf.UseDNSInternally(targetIP)
if err := l.conf.UseDNSSystemWide(targetIP, settings.KeepNameserver); err != nil {
l.logger.Error(err)
}
return
}
}
}
// No IPv4 address found
l.logger.Error("no ipv4 DNS address found for providers %s", settings.Providers)
}
func (l *looper) RunRestartTicker(ctx context.Context) {
ticker := time.NewTicker(time.Hour)
settings := l.GetSettings()
if settings.UpdatePeriod > 0 {
ticker = time.NewTicker(settings.UpdatePeriod)
} else {
ticker.Stop()
}
for {
select {
case <-ctx.Done():
ticker.Stop()
return
case <-ticker.C:
l.restart <- struct{}{}
case <-l.updateTicker:
ticker.Stop()
period := l.GetSettings().UpdatePeriod
ticker = time.NewTicker(period)
}
}
}

View File

@@ -0,0 +1,49 @@
package dns
import (
"context"
"net"
"strings"
"github.com/qdm12/gluetun/internal/constants"
)
// UseDNSInternally is to change the Go program DNS only
func (c *configurator) UseDNSInternally(ip net.IP) {
c.logger.Info("using DNS address %s internally", ip.String())
net.DefaultResolver = &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
d := net.Dialer{}
return d.DialContext(ctx, "udp", net.JoinHostPort(ip.String(), "53"))
},
}
}
// UseDNSSystemWide changes the nameserver to use for DNS system wide
func (c *configurator) UseDNSSystemWide(ip net.IP, keepNameserver bool) error {
c.logger.Info("using DNS address %s system wide", ip.String())
data, err := c.fileManager.ReadFile(string(constants.ResolvConf))
if err != nil {
return err
}
s := strings.TrimSuffix(string(data), "\n")
lines := strings.Split(s, "\n")
if len(lines) == 1 && lines[0] == "" {
lines = nil
}
found := false
if !keepNameserver { // default
for i := range lines {
if strings.HasPrefix(lines[i], "nameserver ") {
lines[i] = "nameserver " + ip.String()
found = true
}
}
}
if !found {
lines = append(lines, "nameserver "+ip.String())
}
data = []byte(strings.Join(lines, "\n"))
return c.fileManager.WriteToFile(string(constants.ResolvConf), data)
}

View File

@@ -0,0 +1,74 @@
package dns
import (
"fmt"
"net"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/files/mock_files"
"github.com/qdm12/golibs/logging/mock_logging"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_UseDNSSystemWide(t *testing.T) {
t.Parallel()
tests := map[string]struct {
data []byte
writtenData []byte
readErr error
writeErr error
err error
}{
"no data": {
writtenData: []byte("nameserver 127.0.0.1"),
},
"read error": {
readErr: fmt.Errorf("error"),
err: fmt.Errorf("error"),
},
"write error": {
writtenData: []byte("nameserver 127.0.0.1"),
writeErr: fmt.Errorf("error"),
err: fmt.Errorf("error"),
},
"lines without nameserver": {
data: []byte("abc\ndef\n"),
writtenData: []byte("abc\ndef\nnameserver 127.0.0.1"),
},
"lines with nameserver": {
data: []byte("abc\nnameserver abc def\ndef\n"),
writtenData: []byte("abc\nnameserver 127.0.0.1\ndef"),
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
fileManager := mock_files.NewMockFileManager(mockCtrl)
fileManager.EXPECT().ReadFile(string(constants.ResolvConf)).
Return(tc.data, tc.readErr).Times(1)
if tc.readErr == nil {
fileManager.EXPECT().WriteToFile(string(constants.ResolvConf), tc.writtenData).
Return(tc.writeErr).Times(1)
}
logger := mock_logging.NewMockLogger(mockCtrl)
logger.EXPECT().Info("using DNS address %s system wide", "127.0.0.1").Times(1)
c := &configurator{
fileManager: fileManager,
logger: logger,
}
err := c.UseDNSSystemWide(net.IP{127, 0, 0, 1}, false)
if tc.err != nil {
require.Error(t, err)
assert.Equal(t, tc.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
})
}
}

39
internal/dns/roots.go Normal file
View File

@@ -0,0 +1,39 @@
package dns
import (
"fmt"
"net/http"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/files"
)
func (c *configurator) DownloadRootHints(uid, gid int) error {
c.logger.Info("downloading root hints from %s", constants.NamedRootURL)
content, status, err := c.client.GetContent(string(constants.NamedRootURL))
if err != nil {
return err
} else if status != http.StatusOK {
return fmt.Errorf("HTTP status code is %d for %s", status, constants.NamedRootURL)
}
return c.fileManager.WriteToFile(
string(constants.RootHints),
content,
files.Ownership(uid, gid),
files.Permissions(0400))
}
func (c *configurator) DownloadRootKey(uid, gid int) error {
c.logger.Info("downloading root key from %s", constants.RootKeyURL)
content, status, err := c.client.GetContent(string(constants.RootKeyURL))
if err != nil {
return err
} else if status != http.StatusOK {
return fmt.Errorf("HTTP status code is %d for %s", status, constants.RootKeyURL)
}
return c.fileManager.WriteToFile(
string(constants.RootKey),
content,
files.Ownership(uid, gid),
files.Permissions(0400))
}

141
internal/dns/roots_test.go Normal file
View File

@@ -0,0 +1,141 @@
package dns
import (
"fmt"
"net/http"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/files/mock_files"
"github.com/qdm12/golibs/logging/mock_logging"
"github.com/qdm12/golibs/network/mock_network"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/qdm12/gluetun/internal/constants"
)
func Test_DownloadRootHints(t *testing.T) { //nolint:dupl
t.Parallel()
tests := map[string]struct {
content []byte
status int
clientErr error
writeErr error
err error
}{
"no data": {
status: http.StatusOK,
},
"bad status": {
status: http.StatusBadRequest,
err: fmt.Errorf("HTTP status code is 400 for https://raw.githubusercontent.com/qdm12/files/master/named.root.updated"),
},
"client error": {
clientErr: fmt.Errorf("error"),
err: fmt.Errorf("error"),
},
"write error": {
status: http.StatusOK,
writeErr: fmt.Errorf("error"),
err: fmt.Errorf("error"),
},
"data": {
content: []byte("content"),
status: http.StatusOK,
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
logger := mock_logging.NewMockLogger(mockCtrl)
logger.EXPECT().Info("downloading root hints from %s", constants.NamedRootURL).Times(1)
client := mock_network.NewMockClient(mockCtrl)
client.EXPECT().GetContent(string(constants.NamedRootURL)).
Return(tc.content, tc.status, tc.clientErr).Times(1)
fileManager := mock_files.NewMockFileManager(mockCtrl)
if tc.clientErr == nil && tc.status == http.StatusOK {
fileManager.EXPECT().WriteToFile(
string(constants.RootHints),
tc.content,
gomock.AssignableToTypeOf(files.Ownership(0, 0)),
gomock.AssignableToTypeOf(files.Ownership(0, 0))).
Return(tc.writeErr).Times(1)
}
c := &configurator{logger: logger, client: client, fileManager: fileManager}
err := c.DownloadRootHints(1000, 1000)
if tc.err != nil {
require.Error(t, err)
assert.Equal(t, tc.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
})
}
}
func Test_DownloadRootKey(t *testing.T) { //nolint:dupl
t.Parallel()
tests := map[string]struct {
content []byte
status int
clientErr error
writeErr error
err error
}{
"no data": {
status: http.StatusOK,
},
"bad status": {
status: http.StatusBadRequest,
err: fmt.Errorf("HTTP status code is 400 for https://raw.githubusercontent.com/qdm12/files/master/root.key.updated"),
},
"client error": {
clientErr: fmt.Errorf("error"),
err: fmt.Errorf("error"),
},
"write error": {
status: http.StatusOK,
writeErr: fmt.Errorf("error"),
err: fmt.Errorf("error"),
},
"data": {
content: []byte("content"),
status: http.StatusOK,
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
logger := mock_logging.NewMockLogger(mockCtrl)
logger.EXPECT().Info("downloading root key from %s", constants.RootKeyURL).Times(1)
client := mock_network.NewMockClient(mockCtrl)
client.EXPECT().GetContent(string(constants.RootKeyURL)).
Return(tc.content, tc.status, tc.clientErr).Times(1)
fileManager := mock_files.NewMockFileManager(mockCtrl)
if tc.clientErr == nil && tc.status == http.StatusOK {
fileManager.EXPECT().WriteToFile(
string(constants.RootKey),
tc.content,
gomock.AssignableToTypeOf(files.Ownership(0, 0)),
gomock.AssignableToTypeOf(files.Ownership(0, 0)),
).Return(tc.writeErr).Times(1)
}
c := &configurator{logger: logger, client: client, fileManager: fileManager}
err := c.DownloadRootKey(1000, 1001)
if tc.err != nil {
require.Error(t, err)
assert.Equal(t, tc.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
})
}
}

21
internal/dns/wait.go Normal file
View File

@@ -0,0 +1,21 @@
package dns
import (
"fmt"
"time"
)
func (c *configurator) WaitForUnbound() (err error) {
const maxTries = 10
const hostToResolve = "github.com"
time.Sleep(300 * time.Millisecond)
for try := 1; try <= maxTries; try++ {
_, err := c.lookupIP(hostToResolve)
if err == nil {
return nil
}
c.logger.Warn("could not resolve %s (try %d of %d): %s", hostToResolve, try, maxTries, err)
time.Sleep(maxTries * 50 * time.Millisecond)
}
return fmt.Errorf("Unbound does not seem to be working after %d tries", maxTries)
}

128
internal/firewall/enable.go Normal file
View File

@@ -0,0 +1,128 @@
package firewall
import (
"context"
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (c *configurator) SetEnabled(ctx context.Context, enabled bool) (err error) {
c.stateMutex.Lock()
defer c.stateMutex.Unlock()
if enabled == c.enabled {
if enabled {
c.logger.Info("already enabled")
} else {
c.logger.Info("already disabled")
}
return nil
}
if !enabled {
c.logger.Info("disabling...")
if err = c.disable(ctx); err != nil {
return err
}
c.enabled = false
c.logger.Info("disabled successfully")
return nil
}
c.logger.Info("enabling...")
if err := c.enable(ctx); err != nil {
return err
}
c.enabled = true
c.logger.Info("enabled successfully")
return nil
}
func (c *configurator) disable(ctx context.Context) (err error) {
if err = c.clearAllRules(ctx); err != nil {
return fmt.Errorf("cannot disable firewall: %w", err)
}
if err = c.setAllPolicies(ctx, "ACCEPT"); err != nil {
return fmt.Errorf("cannot disable firewall: %w", err)
}
return nil
}
// To use in defered call when enabling the firewall
func (c *configurator) fallbackToDisabled(ctx context.Context) {
if ctx.Err() != nil {
return
}
if err := c.SetEnabled(ctx, false); err != nil {
c.logger.Error(err)
}
}
func (c *configurator) enable(ctx context.Context) (err error) { //nolint:gocognit
if err = c.setAllPolicies(ctx, "DROP"); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
const remove = false
defer func() {
if err != nil {
c.fallbackToDisabled(ctx)
}
}()
// Loopback traffic
if err = c.acceptInputThroughInterface(ctx, "lo", remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
if err = c.acceptOutputThroughInterface(ctx, "lo", remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
if err = c.acceptEstablishedRelatedTraffic(ctx, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
for _, conn := range c.vpnConnections {
if err = c.acceptOutputTrafficToVPN(ctx, c.defaultInterface, conn, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
}
if err = c.acceptOutputThroughInterface(ctx, string(constants.TUN), remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
if err := c.acceptInputFromSubnetToSubnet(ctx, "*", c.localSubnet, c.localSubnet, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
if err := c.acceptOutputFromSubnetToSubnet(ctx, "*", c.localSubnet, c.localSubnet, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
for _, subnet := range c.allowedSubnets {
if err := c.acceptInputFromSubnetToSubnet(ctx, c.defaultInterface, subnet, c.localSubnet, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
if err := c.acceptOutputFromSubnetToSubnet(ctx, c.defaultInterface, c.localSubnet, subnet, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
}
// Re-ensure all routes exist
for _, subnet := range c.allowedSubnets {
if err := c.routing.AddRouteVia(ctx, subnet, c.defaultGateway, c.defaultInterface); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
}
for port, intf := range c.allowedInputPorts {
if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
}
if err := c.runUserPostRules(ctx, "/iptables/post-rules.txt", remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
return nil
}

View File

@@ -0,0 +1,69 @@
package firewall
import (
"context"
"net"
"sync"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/routing"
"github.com/qdm12/golibs/command"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
)
// Configurator allows to change firewall rules and modify network routes
type Configurator interface {
Version(ctx context.Context) (string, error)
SetEnabled(ctx context.Context, enabled bool) (err error)
SetVPNConnections(ctx context.Context, connections []models.OpenVPNConnection) (err error)
SetAllowedSubnets(ctx context.Context, subnets []net.IPNet) (err error)
SetAllowedPort(ctx context.Context, port uint16, intf string) (err error)
RemoveAllowedPort(ctx context.Context, port uint16) (err error)
SetDebug()
// SetNetworkInformation is meant to be called only once
SetNetworkInformation(defaultInterface string, defaultGateway net.IP, localSubnet net.IPNet)
}
type configurator struct { //nolint:maligned
commander command.Commander
logger logging.Logger
routing routing.Routing
fileManager files.FileManager // for custom iptables rules
iptablesMutex sync.Mutex
debug bool
defaultInterface string
defaultGateway net.IP
localSubnet net.IPNet
networkInfoMutex sync.Mutex
// State
enabled bool
vpnConnections []models.OpenVPNConnection
allowedSubnets []net.IPNet
allowedInputPorts map[uint16]string // port to interface mapping
stateMutex sync.Mutex
}
// NewConfigurator creates a new Configurator instance
func NewConfigurator(logger logging.Logger, routing routing.Routing, fileManager files.FileManager) Configurator {
return &configurator{
commander: command.NewCommander(),
logger: logger.WithPrefix("firewall: "),
routing: routing,
fileManager: fileManager,
allowedInputPorts: make(map[uint16]string),
}
}
func (c *configurator) SetDebug() {
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

@@ -0,0 +1,184 @@
package firewall
import (
"context"
"fmt"
"net"
"strings"
"github.com/qdm12/gluetun/internal/models"
)
func appendOrDelete(remove bool) string {
if remove {
return "--delete"
}
return "--append"
}
// flipRule changes an append rule in a delete rule or a delete rule into an
// append rule.
func flipRule(rule string) string {
switch {
case strings.HasPrefix(rule, "-A"):
return strings.Replace(rule, "-A", "-D", 1)
case strings.HasPrefix(rule, "--append"):
return strings.Replace(rule, "--append", "-D", 1)
case strings.HasPrefix(rule, "-D"):
return strings.Replace(rule, "-D", "-A", 1)
case strings.HasPrefix(rule, "--delete"):
return strings.Replace(rule, "--delete", "-A", 1)
}
return rule
}
// Version obtains the version of the installed iptables
func (c *configurator) Version(ctx context.Context) (string, error) {
output, err := c.commander.Run(ctx, "iptables", "--version")
if err != nil {
return "", err
}
words := strings.Fields(output)
if len(words) < 2 {
return "", fmt.Errorf("iptables --version: output is too short: %q", output)
}
return words[1], nil
}
func (c *configurator) runIptablesInstructions(ctx context.Context, instructions []string) error {
for _, instruction := range instructions {
if err := c.runIptablesInstruction(ctx, instruction); err != nil {
return err
}
}
return nil
}
func (c *configurator) runIptablesInstruction(ctx context.Context, instruction string) error {
c.iptablesMutex.Lock() // only one iptables command at once
defer c.iptablesMutex.Unlock()
if c.debug {
fmt.Printf("iptables %s\n", instruction)
}
flags := strings.Fields(instruction)
if output, err := c.commander.Run(ctx, "iptables", flags...); err != nil {
return fmt.Errorf("failed executing \"iptables %s\": %s: %w", instruction, output, err)
}
return nil
}
func (c *configurator) clearAllRules(ctx context.Context) error {
return c.runIptablesInstructions(ctx, []string{
"--flush", // flush all chains
"--delete-chain", // delete all chains
})
}
func (c *configurator) setAllPolicies(ctx context.Context, policy string) error {
switch policy {
case "ACCEPT", "DROP":
default:
return fmt.Errorf("policy %q not recognized", policy)
}
return c.runIptablesInstructions(ctx, []string{
fmt.Sprintf("--policy INPUT %s", policy),
fmt.Sprintf("--policy OUTPUT %s", policy),
fmt.Sprintf("--policy FORWARD %s", policy),
})
}
func (c *configurator) acceptInputThroughInterface(ctx context.Context, intf string, remove bool) error {
return c.runIptablesInstruction(ctx, fmt.Sprintf(
"%s INPUT -i %s -j ACCEPT", appendOrDelete(remove), intf,
))
}
func (c *configurator) acceptOutputThroughInterface(ctx context.Context, intf string, remove bool) error {
return c.runIptablesInstruction(ctx, fmt.Sprintf(
"%s OUTPUT -o %s -j ACCEPT", appendOrDelete(remove), intf,
))
}
func (c *configurator) acceptEstablishedRelatedTraffic(ctx context.Context, remove bool) error {
return c.runIptablesInstructions(ctx, []string{
fmt.Sprintf("%s OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT", appendOrDelete(remove)),
fmt.Sprintf("%s INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT", appendOrDelete(remove)),
})
}
func (c *configurator) acceptOutputTrafficToVPN(ctx context.Context, defaultInterface string, connection models.OpenVPNConnection, remove bool) error {
return c.runIptablesInstruction(ctx,
fmt.Sprintf("%s OUTPUT -d %s -o %s -p %s -m %s --dport %d -j ACCEPT",
appendOrDelete(remove), connection.IP, defaultInterface, connection.Protocol, connection.Protocol, connection.Port))
}
func (c *configurator) acceptInputFromSubnetToSubnet(ctx context.Context, intf string, sourceSubnet, destinationSubnet net.IPNet, remove bool) error {
interfaceFlag := "-i " + intf
if intf == "*" { // all interfaces
interfaceFlag = ""
}
return c.runIptablesInstruction(ctx, fmt.Sprintf(
"%s INPUT %s -s %s -d %s -j ACCEPT", appendOrDelete(remove), interfaceFlag, sourceSubnet.String(), destinationSubnet.String(),
))
}
// Thanks to @npawelek
func (c *configurator) acceptOutputFromSubnetToSubnet(ctx context.Context, intf string, sourceSubnet, destinationSubnet net.IPNet, remove bool) error {
interfaceFlag := "-o " + intf
if intf == "*" { // all interfaces
interfaceFlag = ""
}
return c.runIptablesInstruction(ctx, fmt.Sprintf(
"%s OUTPUT %s -s %s -d %s -j ACCEPT", appendOrDelete(remove), interfaceFlag, sourceSubnet.String(), destinationSubnet.String(),
))
}
// Used for port forwarding, with intf set to tun
func (c *configurator) acceptInputToPort(ctx context.Context, intf string, port uint16, remove bool) error {
interfaceFlag := "-i " + intf
if intf == "*" { // all interfaces
interfaceFlag = ""
}
return c.runIptablesInstructions(ctx, []string{
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 {
exists, err := c.fileManager.FileExists(filepath)
if err != nil {
return err
} else if !exists {
return nil
}
b, err := c.fileManager.ReadFile(filepath)
if err != nil {
return err
}
lines := strings.Split(string(b), "\n")
successfulRules := []string{}
defer func() {
// transaction-like rollback
if err == nil || ctx.Err() != nil {
return
}
for _, rule := range successfulRules {
_ = c.runIptablesInstruction(ctx, flipRule(rule))
}
}()
for _, line := range lines {
if !strings.HasPrefix(line, "iptables ") {
continue
}
rule := strings.TrimPrefix(line, "iptables ")
if remove {
rule = flipRule(rule)
}
if err = c.runIptablesInstruction(ctx, rule); err != nil {
return fmt.Errorf("cannot run custom rule: %w", err)
}
successfulRules = append(successfulRules, rule)
}
return nil
}

View File

@@ -0,0 +1,71 @@
package firewall
import (
"context"
"fmt"
)
func (c *configurator) SetAllowedPort(ctx context.Context, port uint16, intf string) (err error) {
c.stateMutex.Lock()
defer c.stateMutex.Unlock()
if port == 0 {
return nil
}
if !c.enabled {
c.logger.Info("firewall disabled, only updating allowed ports internal state")
c.allowedInputPorts[port] = intf
return nil
}
c.logger.Info("setting allowed input port %d through interface %s...", port, intf)
if existingIntf, ok := c.allowedInputPorts[port]; ok {
if intf == existingIntf {
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
if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
return fmt.Errorf("cannot set allowed port %d through interface %s: %w", port, intf, err)
}
c.allowedInputPorts[port] = intf
return nil
}
func (c *configurator) RemoveAllowedPort(ctx context.Context, port uint16) (err error) {
c.stateMutex.Lock()
defer c.stateMutex.Unlock()
if port == 0 {
return nil
}
if !c.enabled {
c.logger.Info("firewall disabled, only updating allowed ports internal list")
delete(c.allowedInputPorts, port)
return nil
}
c.logger.Info("removing allowed port %d through firewall...", port)
intf, ok := c.allowedInputPorts[port]
if !ok {
return nil
}
const remove = true
if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
return fmt.Errorf("cannot remove allowed port %d through interface %s: %w", port, intf, err)
}
delete(c.allowedInputPorts, port)
return nil
}

View File

@@ -0,0 +1,144 @@
package firewall
import (
"context"
"fmt"
"net"
)
func (c *configurator) SetAllowedSubnets(ctx context.Context, subnets []net.IPNet) (err error) {
c.stateMutex.Lock()
defer c.stateMutex.Unlock()
if !c.enabled {
c.logger.Info("firewall disabled, only updating allowed subnets internal list and updating routes")
c.updateSubnetRoutes(ctx, c.allowedSubnets, subnets)
c.allowedSubnets = make([]net.IPNet, len(subnets))
copy(c.allowedSubnets, subnets)
return nil
}
c.logger.Info("setting allowed subnets through firewall...")
subnetsToAdd := findSubnetsToAdd(c.allowedSubnets, subnets)
subnetsToRemove := findSubnetsToRemove(c.allowedSubnets, subnets)
if len(subnetsToAdd) == 0 && len(subnetsToRemove) == 0 {
return nil
}
c.removeSubnets(ctx, subnetsToRemove, c.defaultInterface, c.localSubnet)
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)
}
return nil
}
func findSubnetsToAdd(oldSubnets, newSubnets []net.IPNet) (subnetsToAdd []net.IPNet) {
for _, newSubnet := range newSubnets {
found := false
for _, oldSubnet := range oldSubnets {
if subnetsAreEqual(oldSubnet, newSubnet) {
found = true
break
}
}
if !found {
subnetsToAdd = append(subnetsToAdd, newSubnet)
}
}
return subnetsToAdd
}
func findSubnetsToRemove(oldSubnets, newSubnets []net.IPNet) (subnetsToRemove []net.IPNet) {
for _, oldSubnet := range oldSubnets {
found := false
for _, newSubnet := range newSubnets {
if subnetsAreEqual(oldSubnet, newSubnet) {
found = true
break
}
}
if !found {
subnetsToRemove = append(subnetsToRemove, oldSubnet)
}
}
return subnetsToRemove
}
func subnetsAreEqual(a, b net.IPNet) bool {
return a.IP.Equal(b.IP) && a.Mask.String() == b.Mask.String()
}
func removeSubnetFromSubnets(subnets []net.IPNet, subnet net.IPNet) []net.IPNet {
L := len(subnets)
for i := range subnets {
if subnetsAreEqual(subnet, subnets[i]) {
subnets[i] = subnets[L-1]
subnets = subnets[:L-1]
break
}
}
return subnets
}
func (c *configurator) removeSubnets(ctx context.Context, subnets []net.IPNet, defaultInterface string,
localSubnet net.IPNet) {
const remove = true
for _, subnet := range subnets {
failed := false
if err := c.acceptInputFromSubnetToSubnet(ctx, defaultInterface, subnet, localSubnet, remove); err != nil {
failed = true
c.logger.Error("cannot remove outdated allowed subnet through firewall: %s", err)
}
if err := c.acceptOutputFromSubnetToSubnet(ctx, defaultInterface, subnet, localSubnet, remove); err != nil {
failed = true
c.logger.Error("cannot remove outdated allowed subnet through firewall: %s", err)
}
if err := c.routing.DeleteRouteVia(ctx, subnet); err != nil {
failed = true
c.logger.Error("cannot remove outdated allowed subnet route: %s", err)
}
if failed {
continue
}
c.allowedSubnets = removeSubnetFromSubnets(c.allowedSubnets, subnet)
}
}
func (c *configurator) addSubnets(ctx context.Context, subnets []net.IPNet, defaultInterface string,
defaultGateway net.IP, localSubnet net.IPNet) error {
const remove = false
for _, subnet := range subnets {
if err := c.acceptInputFromSubnetToSubnet(ctx, defaultInterface, subnet, localSubnet, remove); err != nil {
return fmt.Errorf("cannot add allowed subnet through firewall: %w", err)
}
if err := c.acceptOutputFromSubnetToSubnet(ctx, defaultInterface, localSubnet, subnet, remove); err != nil {
return fmt.Errorf("cannot add allowed subnet through firewall: %w", err)
}
if err := c.routing.AddRouteVia(ctx, subnet, defaultGateway, defaultInterface); err != nil {
return fmt.Errorf("cannot add route for allowed subnet: %w", err)
}
c.allowedSubnets = append(c.allowedSubnets, subnet)
}
return nil
}
// 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)
subnetsToRemove := findSubnetsToRemove(oldSubnets, newSubnets)
if len(subnetsToAdd) == 0 && len(subnetsToRemove) == 0 {
return
}
for _, subnet := range subnetsToRemove {
if err := c.routing.DeleteRouteVia(ctx, subnet); err != nil {
c.logger.Error("cannot remove outdated route for subnet: %s", err)
}
}
for _, subnet := range subnetsToAdd {
if err := c.routing.AddRouteVia(ctx, subnet, c.defaultGateway, c.defaultInterface); err != nil {
c.logger.Error("cannot add route for subnet: %s", err)
}
}
}

101
internal/firewall/vpn.go Normal file
View File

@@ -0,0 +1,101 @@
package firewall
import (
"context"
"fmt"
"github.com/qdm12/gluetun/internal/models"
)
func (c *configurator) SetVPNConnections(ctx context.Context, connections []models.OpenVPNConnection) (err error) {
c.stateMutex.Lock()
defer c.stateMutex.Unlock()
if !c.enabled {
c.logger.Info("firewall disabled, only updating VPN connections internal list")
c.vpnConnections = make([]models.OpenVPNConnection, len(connections))
copy(c.vpnConnections, connections)
return nil
}
c.logger.Info("setting VPN connections through firewall...")
connectionsToAdd := findConnectionsToAdd(c.vpnConnections, connections)
connectionsToRemove := findConnectionsToRemove(c.vpnConnections, connections)
if len(connectionsToAdd) == 0 && len(connectionsToRemove) == 0 {
return nil
}
c.removeConnections(ctx, connectionsToRemove, c.defaultInterface)
if err := c.addConnections(ctx, connectionsToAdd, c.defaultInterface); err != nil {
return fmt.Errorf("cannot set VPN connections through firewall: %w", err)
}
return nil
}
func removeConnectionFromConnections(connections []models.OpenVPNConnection, connection models.OpenVPNConnection) []models.OpenVPNConnection {
L := len(connections)
for i := range connections {
if connection.Equal(connections[i]) {
connections[i] = connections[L-1]
connections = connections[:L-1]
break
}
}
return connections
}
func findConnectionsToAdd(oldConnections, newConnections []models.OpenVPNConnection) (connectionsToAdd []models.OpenVPNConnection) {
for _, newConnection := range newConnections {
found := false
for _, oldConnection := range oldConnections {
if oldConnection.Equal(newConnection) {
found = true
break
}
}
if !found {
connectionsToAdd = append(connectionsToAdd, newConnection)
}
}
return connectionsToAdd
}
func findConnectionsToRemove(oldConnections, newConnections []models.OpenVPNConnection) (connectionsToRemove []models.OpenVPNConnection) {
for _, oldConnection := range oldConnections {
found := false
for _, newConnection := range newConnections {
if oldConnection.Equal(newConnection) {
found = true
break
}
}
if !found {
connectionsToRemove = append(connectionsToRemove, oldConnection)
}
}
return connectionsToRemove
}
func (c *configurator) removeConnections(ctx context.Context, connections []models.OpenVPNConnection, defaultInterface string) {
for _, conn := range connections {
const remove = true
if err := c.acceptOutputTrafficToVPN(ctx, defaultInterface, conn, remove); err != nil {
c.logger.Error("cannot remove outdated VPN connection through firewall: %s", err)
continue
}
c.vpnConnections = removeConnectionFromConnections(c.vpnConnections, conn)
}
}
func (c *configurator) addConnections(ctx context.Context, connections []models.OpenVPNConnection, defaultInterface string) error {
const remove = false
for _, conn := range connections {
if err := c.acceptOutputTrafficToVPN(ctx, defaultInterface, conn, remove); err != nil {
return err
}
c.vpnConnections = append(c.vpnConnections, conn)
}
return nil
}

121
internal/logging/line.go Normal file
View File

@@ -0,0 +1,121 @@
package logging
import (
"fmt"
"regexp"
"strings"
"github.com/fatih/color"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/logging"
)
var regularExpressions = struct { //nolint:gochecknoglobals
unboundPrefix *regexp.Regexp
shadowsocksPrefix *regexp.Regexp
shadowsocksErrorPrefix *regexp.Regexp
tinyproxyLoglevel *regexp.Regexp
tinyproxyPrefix *regexp.Regexp
}{
unboundPrefix: regexp.MustCompile(`unbound: \[[0-9]{10}\] unbound\[[0-9]+:0\] `),
shadowsocksPrefix: regexp.MustCompile(`shadowsocks:[ ]+2[0-9]{3}\-[0-1][0-9]\-[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9] `),
shadowsocksErrorPrefix: regexp.MustCompile(`shadowsocks error:[ ]+2[0-9]{3}\-[0-1][0-9]\-[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9] `),
tinyproxyLoglevel: regexp.MustCompile(`INFO|CONNECT|NOTICE|WARNING|ERROR|CRITICAL`),
tinyproxyPrefix: regexp.MustCompile(`tinyproxy: .+[ ]+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9] \[[0-9]+\]: `),
}
func PostProcessLine(s string) (filtered string, level logging.Level) {
switch {
case strings.HasPrefix(s, "openvpn: "):
for _, ignored := range []string{
"openvpn: WARNING: you are using user/group/chroot/setcon without persist-tun -- this may cause restarts to fail",
"openvpn: NOTE: UID/GID downgrade will be delayed because of --client, --pull, or --up-delay",
} {
if s == ignored {
return "", ""
}
}
switch {
case strings.HasPrefix(s, "openvpn: NOTE: "):
filtered = strings.TrimPrefix(s, "openvpn: NOTE: ")
filtered = "openvpn: " + filtered
level = logging.InfoLevel
case strings.HasPrefix(s, "openvpn: WARNING: "):
filtered = strings.TrimPrefix(s, "openvpn: WARNING: ")
filtered = "openvpn: " + filtered
level = logging.WarnLevel
case strings.HasPrefix(s, "openvpn: Options error: "):
filtered = strings.TrimPrefix(s, "openvpn: Options error: ")
filtered = "openvpn: " + filtered
level = logging.ErrorLevel
case s == "openvpn: Initialization Sequence Completed":
return color.HiGreenString(s), logging.InfoLevel
default:
filtered = s
level = logging.InfoLevel
}
filtered = constants.ColorOpenvpn().Sprintf(filtered)
return filtered, level
case strings.HasPrefix(s, "unbound: "):
prefix := regularExpressions.unboundPrefix.FindString(s)
filtered = s[len(prefix):]
switch {
case strings.HasPrefix(filtered, "notice: "):
filtered = strings.TrimPrefix(filtered, "notice: ")
level = logging.InfoLevel
case strings.HasPrefix(filtered, "info: "):
filtered = strings.TrimPrefix(filtered, "info: ")
level = logging.InfoLevel
case strings.HasPrefix(filtered, "warn: "):
filtered = strings.TrimPrefix(filtered, "warn: ")
level = logging.WarnLevel
case strings.HasPrefix(filtered, "error: "):
filtered = strings.TrimPrefix(filtered, "error: ")
level = logging.ErrorLevel
default:
level = logging.ErrorLevel
}
filtered = fmt.Sprintf("unbound: %s", filtered)
filtered = constants.ColorUnbound().Sprintf(filtered)
return filtered, level
case strings.HasPrefix(s, "shadowsocks: "):
prefix := regularExpressions.shadowsocksPrefix.FindString(s)
filtered = s[len(prefix):]
switch {
case strings.HasPrefix(filtered, "INFO: "):
level = logging.InfoLevel
filtered = strings.TrimPrefix(filtered, "INFO: ")
default:
level = logging.WarnLevel
}
filtered = fmt.Sprintf("shadowsocks: %s", filtered)
filtered = constants.ColorShadowsocks().Sprintf(filtered)
return filtered, level
case strings.HasPrefix(s, "shadowsocks error: "):
if strings.Contains(s, "ERROR: unable to resolve") { // caused by DNS blocking
return "", logging.ErrorLevel
}
prefix := regularExpressions.shadowsocksErrorPrefix.FindString(s)
filtered = s[len(prefix):]
filtered = strings.TrimPrefix(filtered, "ERROR: ")
filtered = fmt.Sprintf("shadowsocks: %s", filtered)
filtered = constants.ColorShadowsocksError().Sprintf(filtered)
return filtered, logging.ErrorLevel
case strings.HasPrefix(s, "tinyproxy: "):
logLevel := regularExpressions.tinyproxyLoglevel.FindString(s)
prefix := regularExpressions.tinyproxyPrefix.FindString(s)
filtered = fmt.Sprintf("tinyproxy: %s", s[len(prefix):])
filtered = constants.ColorTinyproxy().Sprintf(filtered)
switch logLevel {
case "INFO", "CONNECT", "NOTICE":
return filtered, logging.InfoLevel
case "WARNING":
return filtered, logging.WarnLevel
case "ERROR", "CRITICAL":
return filtered, logging.ErrorLevel
default:
return filtered, logging.ErrorLevel
}
}
return s, logging.InfoLevel
}

View File

@@ -0,0 +1,117 @@
package logging
import (
"testing"
"github.com/qdm12/golibs/logging"
"github.com/stretchr/testify/assert"
)
func Test_PostProcessLine(t *testing.T) {
t.Parallel()
tests := map[string]struct {
s string
filtered string
level logging.Level
}{
"empty string": {"", "", logging.InfoLevel},
"random string": {"asdasqdb", "asdasqdb", logging.InfoLevel},
"unbound notice": {
"unbound: [1594595249] unbound[75:0] notice: init module 0: validator",
"unbound: init module 0: validator",
logging.InfoLevel},
"unbound info": {
"unbound: [1594595249] unbound[75:0] info: init module 0: validator",
"unbound: init module 0: validator",
logging.InfoLevel},
"unbound warn": {
"unbound: [1594595249] unbound[75:0] warn: init module 0: validator",
"unbound: init module 0: validator",
logging.WarnLevel},
"unbound error": {
"unbound: [1594595249] unbound[75:0] error: init module 0: validator",
"unbound: init module 0: validator",
logging.ErrorLevel},
"unbound unknown": {
"unbound: [1594595249] unbound[75:0] BLA: init module 0: validator",
"unbound: BLA: init module 0: validator",
logging.ErrorLevel},
"shadowsocks stdout info": {
"shadowsocks: 2020-07-12 23:07:25 INFO: UDP relay enabled",
"shadowsocks: UDP relay enabled",
logging.InfoLevel},
"shadowsocks stdout other": {
"shadowsocks: 2020-07-12 23:07:25 BLABLA: UDP relay enabled",
"shadowsocks: BLABLA: UDP relay enabled",
logging.WarnLevel},
"shadowsocks stderr": {
"shadowsocks error: 2020-07-12 23:07:25 Some error",
"shadowsocks: Some error",
logging.ErrorLevel},
"shadowsocks stderr unable to resolve muted": {
"shadowsocks error: 2020-07-12 23:07:25 ERROR: unable to resolve",
"",
logging.ErrorLevel},
"tinyproxy info": {
"tinyproxy: INFO Jul 12 23:07:25 [32]: Reloading config file",
"tinyproxy: Reloading config file",
logging.InfoLevel},
"tinyproxy connect": {
"tinyproxy: CONNECT Jul 12 23:07:25 [32]: Reloading config file",
"tinyproxy: Reloading config file",
logging.InfoLevel},
"tinyproxy notice": {
"tinyproxy: NOTICE Jul 12 23:07:25 [32]: Reloading config file",
"tinyproxy: Reloading config file",
logging.InfoLevel},
"tinyproxy warning": {
"tinyproxy: WARNING Jul 12 23:07:25 [32]: Reloading config file",
"tinyproxy: Reloading config file",
logging.WarnLevel},
"tinyproxy error": {
"tinyproxy: ERROR Jul 12 23:07:25 [32]: Reloading config file",
"tinyproxy: Reloading config file",
logging.ErrorLevel},
"tinyproxy critical": {
"tinyproxy: CRITICAL Jul 12 23:07:25 [32]: Reloading config file",
"tinyproxy: Reloading config file",
logging.ErrorLevel},
"tinyproxy unknown": {
"tinyproxy: BLABLA Jul 12 23:07:25 [32]: Reloading config file",
"tinyproxy: Reloading config file",
logging.ErrorLevel},
"openvpn unknown": {
"openvpn: message",
"openvpn: message",
logging.InfoLevel},
"openvpn note": {
"openvpn: NOTE: message",
"openvpn: message",
logging.InfoLevel},
"openvpn warning": {
"openvpn: WARNING: message",
"openvpn: message",
logging.WarnLevel},
"openvpn options error": {
"openvpn: Options error: message",
"openvpn: message",
logging.ErrorLevel},
"openvpn ignored message": {
"openvpn: NOTE: UID/GID downgrade will be delayed because of --client, --pull, or --up-delay",
"",
""},
"openvpn success": {
"openvpn: Initialization Sequence Completed",
"openvpn: Initialization Sequence Completed",
logging.InfoLevel},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
filtered, level := PostProcessLine(tc.s)
assert.Equal(t, tc.filtered, filtered)
assert.Equal(t, tc.level, level)
})
}
}

View File

@@ -0,0 +1,59 @@
package logging
import (
"fmt"
"strings"
"time"
"github.com/kyokomi/emoji"
"github.com/qdm12/gluetun/internal/constants"
)
// Splash returns the welcome spash message
func Splash(version, vcsRef, buildDate string) string {
lines := title()
lines = append(lines, "")
lines = append(lines, fmt.Sprintf("Running version %s built on %s (commit %s)", version, buildDate, vcsRef))
lines = append(lines, "")
lines = append(lines, announcement()...)
lines = append(lines, "")
lines = append(lines, links()...)
return strings.Join(lines, "\n")
}
func title() []string {
return []string{
"=========================================",
"================ Gluetun ================",
"=========================================",
"==== A mix of OpenVPN, DNS over TLS, ====",
"======= Shadowsocks and Tinyproxy =======",
"========= all glued up with Go ==========",
"=========================================",
"=========== For tunneling to ============",
"======== your favorite VPN server =======",
"=========================================",
"=== Made with " + emoji.Sprint(":heart:") + " by github.com/qdm12 ====",
"=========================================",
}
}
func announcement() []string {
if len(constants.Announcement) == 0 {
return nil
}
expirationDate, _ := time.Parse("2006-01-02", constants.AnnouncementExpiration) // error covered by a unit test
if time.Now().After(expirationDate) {
return nil
}
return []string{emoji.Sprint(":mega: ") + constants.Announcement}
}
func links() []string {
return []string{
emoji.Sprint(":wrench: ") + "Need help? " + constants.IssueLink,
emoji.Sprint(":computer: ") + "Email? quentin.mcgaw@gmail.com",
emoji.Sprint(":coffee: ") + "Slack? Join from the Slack button on Github",
emoji.Sprint(":money_with_wings: ") + "Help me? https://github.com/sponsors/qdm12",
}
}

63
internal/models/alias.go Normal file
View File

@@ -0,0 +1,63 @@
package models
import (
"fmt"
"strings"
)
type (
// VPNDevice is the device name used to tunnel using Openvpn
VPNDevice string
// DNSProvider is a DNS over TLS server provider name
DNSProvider string
// DNSHost is the DNS host to use for TLS validation
DNSHost string
// URL is an HTTP(s) URL address
URL string
// Filepath is a local filesytem file path
Filepath string
// TinyProxyLogLevel is the log level for TinyProxy
TinyProxyLogLevel string
// VPNProvider is the name of the VPN provider to be used
VPNProvider string
// NetworkProtocol contains the network protocol to be used to communicate with the VPN servers
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)
}

11
internal/models/dns.go Normal file
View File

@@ -0,0 +1,11 @@
package models
import "net"
// DNSProviderData contains information for a DNS provider
type DNSProviderData struct {
IPs []net.IP
SupportsTLS bool
SupportsIPv6 bool
Host DNSHost
}

View File

@@ -0,0 +1,13 @@
package models
import "net"
type OpenVPNConnection struct {
IP net.IP
Port uint16
Protocol NetworkProtocol
}
func (o *OpenVPNConnection) Equal(other OpenVPNConnection) bool {
return o.IP.Equal(other.IP) && o.Port == other.Port && o.Protocol == other.Protocol
}

View File

@@ -0,0 +1,132 @@
package models
import (
"fmt"
"net"
"strings"
)
// ProviderSettings contains settings specific to a VPN provider
type ProviderSettings struct {
Name VPNProvider `json:"name"`
ServerSelection ServerSelection `json:"serverSelection"`
ExtraConfigOptions ExtraConfigOptions `json:"extraConfig"`
PortForwarding PortForwarding `json:"portForwarding"`
}
type ServerSelection struct { //nolint:maligned
// Common
Protocol NetworkProtocol `json:"networkProtocol"`
TargetIP net.IP `json:"targetIP,omitempty"`
// Cyberghost, PIA, Surfshark, Windscribe, Vyprvpn, NordVPN
Region string `json:"region"`
// Cyberghost
Group string `json:"group"`
// Mullvad, PureVPN
Country string `json:"country"`
City string `json:"city"`
// Mullvad
ISP string `json:"isp"`
Owned bool `json:"owned"`
// Mullvad, Windscribe
CustomPort uint16 `json:"customPort"`
// NordVPN
Number uint16 `json:"number"`
// PIA
EncryptionPreset string `json:"encryptionPreset"`
}
type ExtraConfigOptions struct {
ClientKey string `json:"-"` // Cyberghost
EncryptionPreset string `json:"encryptionPreset"` // PIA
}
// PortForwarding contains settings for port forwarding
type PortForwarding struct {
Enabled bool `json:"enabled"`
Filepath Filepath `json:"filepath"`
}
func (p *PortForwarding) String() string {
if p.Enabled {
return fmt.Sprintf("on, saved in %s", p.Filepath)
}
return "off"
}
func (p *ProviderSettings) String() string {
settingsList := []string{
fmt.Sprintf("%s settings:", strings.Title(string(p.Name))),
"Network protocol: " + string(p.ServerSelection.Protocol),
}
customPort := ""
if p.ServerSelection.CustomPort > 0 {
customPort = fmt.Sprintf("%d", p.ServerSelection.CustomPort)
}
number := ""
if p.ServerSelection.Number > 0 {
number = fmt.Sprintf("%d", p.ServerSelection.Number)
}
switch strings.ToLower(string(p.Name)) {
case "private internet access":
settingsList = append(settingsList,
"Region: "+p.ServerSelection.Region,
"Encryption preset: "+p.ExtraConfigOptions.EncryptionPreset,
"Port forwarding: "+p.PortForwarding.String(),
)
case "mullvad":
settingsList = append(settingsList,
"Country: "+p.ServerSelection.Country,
"City: "+p.ServerSelection.City,
"ISP: "+p.ServerSelection.ISP,
"Custom port: "+customPort,
)
case "windscribe":
settingsList = append(settingsList,
"Region: "+p.ServerSelection.Region,
"Custom port: "+customPort,
)
case "surfshark":
settingsList = append(settingsList,
"Region: "+p.ServerSelection.Region,
)
case "cyberghost":
settingsList = append(settingsList,
"ClientKey: [redacted]",
"Group: "+p.ServerSelection.Group,
"Region: "+p.ServerSelection.Region,
)
case "vyprvpn":
settingsList = append(settingsList,
"Region: "+p.ServerSelection.Region,
)
case "nordvpn":
settingsList = append(settingsList,
"Region: "+p.ServerSelection.Region,
"Number: "+number,
)
case "purevpn":
settingsList = append(settingsList,
"Region: "+p.ServerSelection.Region,
"Country: "+p.ServerSelection.Country,
"City: "+p.ServerSelection.City,
)
default:
settingsList = append(settingsList,
"<Missing String method, please implement me!>",
)
}
if p.ServerSelection.TargetIP != nil {
settingsList = append(settingsList,
"Target IP address: "+string(p.ServerSelection.TargetIP),
)
}
return strings.Join(settingsList, "\n |--")
}

View File

@@ -0,0 +1,53 @@
package models
import "net"
type PIAServer struct {
IPs []net.IP
Region string
}
type MullvadServer struct {
IPs []net.IP
Country string
City string
ISP string
Owned bool
DefaultPort uint16
}
type WindscribeServer struct {
Region string
IPs []net.IP
}
type SurfsharkServer struct {
Region string
IPs []net.IP
}
type CyberghostServer struct {
Region string
Group string
IPs []net.IP
}
type VyprvpnServer struct {
Region string
IPs []net.IP
}
type NordvpnServer struct { //nolint:maligned
Region string
Number uint16
IP net.IP
TCP bool
UDP bool
}
type PurevpnServer struct {
Region string
Country string
City string
IPs []net.IP
}

31
internal/openvpn/auth.go Normal file
View File

@@ -0,0 +1,31 @@
package openvpn
import (
"strings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/files"
)
// WriteAuthFile writes the OpenVPN auth file to disk with the right permissions
func (c *configurator) WriteAuthFile(user, password string, uid, gid int) error {
exists, err := c.fileManager.FileExists(string(constants.OpenVPNAuthConf))
if err != nil {
return err
} else if exists {
data, err := c.fileManager.ReadFile(string(constants.OpenVPNAuthConf))
if err != nil {
return err
}
lines := strings.Split(string(data), "\n")
if len(lines) > 1 && lines[0] == user && lines[1] == password {
return nil
}
c.logger.Info("username and password changed", constants.OpenVPNAuthConf)
}
return c.fileManager.WriteLinesToFile(
string(constants.OpenVPNAuthConf),
[]string{user, password},
files.Ownership(uid, gid),
files.Permissions(0400))
}

View File

@@ -0,0 +1,29 @@
package openvpn
import (
"context"
"fmt"
"io"
"strings"
"github.com/qdm12/gluetun/internal/constants"
)
func (c *configurator) Start(ctx context.Context) (stdout io.ReadCloser, waitFn func() error, err error) {
c.logger.Info("starting openvpn")
stdout, _, waitFn, err = c.commander.Start(ctx, "openvpn", "--config", string(constants.OpenVPNConf))
return stdout, waitFn, err
}
func (c *configurator) Version(ctx context.Context) (string, error) {
output, err := c.commander.Run(ctx, "openvpn", "--version")
if err != nil && err.Error() != "exit status 1" {
return "", err
}
firstLine := strings.Split(output, "\n")[0]
words := strings.Fields(firstLine)
if len(words) < 2 {
return "", fmt.Errorf("openvpn --version: first line is too short: %q", firstLine)
}
return words[1], nil
}

230
internal/openvpn/loop.go Normal file
View File

@@ -0,0 +1,230 @@
package openvpn
import (
"context"
"fmt"
"sync"
"time"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/command"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/network"
)
type Looper interface {
Run(ctx context.Context, wg *sync.WaitGroup)
Restart()
PortForward()
GetSettings() (settings settings.OpenVPN)
SetSettings(settings settings.OpenVPN)
GetPortForwarded() (portForwarded uint16)
}
type looper struct {
// Variable parameters
provider models.VPNProvider
settings settings.OpenVPN
settingsMutex sync.RWMutex
portForwarded uint16
portForwardedMutex sync.RWMutex
// Fixed parameters
uid int
gid int
// Configurators
conf Configurator
fw firewall.Configurator
// Other objects
logger logging.Logger
client network.Client
fileManager files.FileManager
streamMerger command.StreamMerger
fatalOnError func(err error)
// Internal channels
restart chan struct{}
portForwardSignals chan struct{}
}
func NewLooper(provider models.VPNProvider, settings settings.OpenVPN,
uid, gid int,
conf Configurator, fw firewall.Configurator,
logger logging.Logger, client network.Client, fileManager files.FileManager,
streamMerger command.StreamMerger, fatalOnError func(err error)) Looper {
return &looper{
provider: provider,
settings: settings,
uid: uid,
gid: gid,
conf: conf,
fw: fw,
logger: logger.WithPrefix("openvpn: "),
client: client,
fileManager: fileManager,
streamMerger: streamMerger,
fatalOnError: fatalOnError,
restart: make(chan struct{}),
portForwardSignals: make(chan struct{}),
}
}
func (l *looper) Restart() { l.restart <- struct{}{} }
func (l *looper) PortForward() { l.portForwardSignals <- struct{}{} }
func (l *looper) GetSettings() (settings settings.OpenVPN) {
l.settingsMutex.RLock()
defer l.settingsMutex.RUnlock()
return l.settings
}
func (l *looper) SetSettings(settings settings.OpenVPN) {
l.settingsMutex.Lock()
defer l.settingsMutex.Unlock()
l.settings = settings
}
func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
wg.Add(1)
defer wg.Done()
select {
case <-l.restart:
case <-ctx.Done():
return
}
defer l.logger.Warn("loop exited")
for ctx.Err() == nil {
settings := l.GetSettings()
providerConf := provider.New(l.provider)
connections, err := providerConf.GetOpenVPNConnections(settings.Provider.ServerSelection)
if err != nil {
l.fatalOnError(err)
return
}
lines := providerConf.BuildConf(
connections,
settings.Verbosity,
l.uid,
l.gid,
settings.Root,
settings.Cipher,
settings.Auth,
settings.Provider.ExtraConfigOptions,
)
if err := l.fileManager.WriteLinesToFile(string(constants.OpenVPNConf), lines, files.Ownership(l.uid, l.gid), files.Permissions(0400)); err != nil {
l.fatalOnError(err)
return
}
if err := l.conf.WriteAuthFile(settings.User, settings.Password, l.uid, l.gid); err != nil {
l.fatalOnError(err)
return
}
if err := l.fw.SetVPNConnections(ctx, connections); err != nil {
l.fatalOnError(err)
return
}
openvpnCtx, openvpnCancel := context.WithCancel(context.Background())
stream, waitFn, err := l.conf.Start(openvpnCtx)
if err != nil {
openvpnCancel()
l.logAndWait(ctx, err)
continue
}
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
case <-l.portForwardSignals:
l.portForward(ctx, providerConf, l.client)
}
}
}(openvpnCtx)
go l.streamMerger.Merge(openvpnCtx, stream, command.MergeName("openvpn"))
waitError := make(chan error)
go func() {
err := waitFn() // blocking
waitError <- err
}()
select {
case <-ctx.Done():
l.logger.Warn("context canceled: exiting loop")
openvpnCancel()
<-waitError
close(waitError)
return
case <-l.restart: // triggered restart
l.logger.Info("restarting")
openvpnCancel()
<-waitError
close(waitError)
case err := <-waitError: // unexpected error
openvpnCancel()
close(waitError)
l.logAndWait(ctx, err)
}
}
}
func (l *looper) logAndWait(ctx context.Context, err error) {
l.logger.Error(err)
l.logger.Info("retrying in 30 seconds")
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel() // just for the linter
<-ctx.Done()
}
func (l *looper) portForward(ctx context.Context, providerConf provider.Provider, client network.Client) {
settings := l.GetSettings()
if !settings.Provider.PortForwarding.Enabled {
return
}
var port uint16
err := fmt.Errorf("")
for err != nil {
if ctx.Err() != nil {
return
}
port, err = providerConf.GetPortForward(client)
if err != nil {
l.logAndWait(ctx, err)
}
}
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
l.logger.Info("writing forwarded port to %s", filepath)
err = l.fileManager.WriteLinesToFile(
string(filepath), []string{fmt.Sprintf("%d", port)},
files.Ownership(l.uid, l.gid), files.Permissions(0400),
)
if err != nil {
l.logger.Error(err)
}
}
func (l *looper) GetPortForwarded() (portForwarded uint16) {
l.portForwardedMutex.RLock()
defer l.portForwardedMutex.RUnlock()
return l.portForwarded
}

View File

@@ -0,0 +1,40 @@
package openvpn
import (
"context"
"io"
"os"
"github.com/qdm12/golibs/command"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
"golang.org/x/sys/unix"
)
type Configurator interface {
Version(ctx context.Context) (string, error)
WriteAuthFile(user, password string, uid, gid int) error
CheckTUN() error
CreateTUN() error
Start(ctx context.Context) (stdout io.ReadCloser, waitFn func() error, err error)
}
type configurator struct {
fileManager files.FileManager
logger logging.Logger
commander command.Commander
openFile func(name string, flag int, perm os.FileMode) (*os.File, error)
mkDev func(major uint32, minor uint32) uint64
mkNod func(path string, mode uint32, dev int) error
}
func NewConfigurator(logger logging.Logger, fileManager files.FileManager) Configurator {
return &configurator{
fileManager: fileManager,
logger: logger.WithPrefix("openvpn configurator: "),
commander: command.NewCommander(),
openFile: os.OpenFile,
mkDev: unix.Mkdev,
mkNod: unix.Mknod,
}
}

37
internal/openvpn/tun.go Normal file
View File

@@ -0,0 +1,37 @@
package openvpn
import (
"fmt"
"os"
"github.com/qdm12/gluetun/internal/constants"
"golang.org/x/sys/unix"
)
// CheckTUN checks the tunnel device is present and accessible
func (c *configurator) CheckTUN() error {
c.logger.Info("checking for device %s", constants.TunnelDevice)
f, err := c.openFile(string(constants.TunnelDevice), os.O_RDWR, 0)
if err != nil {
return fmt.Errorf("TUN device is not available: %w", err)
}
if err := f.Close(); err != nil {
c.logger.Warn("Could not close TUN device file: %s", err)
}
return nil
}
func (c *configurator) CreateTUN() error {
c.logger.Info("creating %s", constants.TunnelDevice)
if err := c.fileManager.CreateDir("/dev/net"); err != nil {
return err
}
dev := c.mkDev(10, 200)
if err := c.mkNod(string(constants.TunnelDevice), unix.S_IFCHR, int(dev)); err != nil {
return err
}
if err := c.fileManager.SetUserPermissions(string(constants.TunnelDevice), 0666); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,42 @@
package params
import (
"strings"
"github.com/qdm12/gluetun/internal/constants"
libparams "github.com/qdm12/golibs/params"
)
// GetCyberghostGroup obtains the server group for the Cyberghost server from the
// environment variable CYBERGHOST_GROUP
func (p *reader) GetCyberghostGroup() (group string, err error) {
s, err := p.envParams.GetValueIfInside("CYBERGHOST_GROUP", constants.CyberghostGroupChoices(), libparams.Default("Premium UDP Europe"))
return s, err
}
// GetCyberghostRegion obtains the country name for the Cyberghost server from the
// environment variable REGION
func (p *reader) GetCyberghostRegion() (region string, err error) {
choices := append(constants.CyberghostRegionChoices(), "")
s, err := p.envParams.GetValueIfInside("REGION", choices)
return s, err
}
// GetCyberghostClientKey obtains the one line client key to use for openvpn from the
// environment variable CLIENT_KEY
func (p *reader) GetCyberghostClientKey() (clientKey string, err error) {
clientKey, err = p.envParams.GetEnv("CLIENT_KEY", libparams.CaseSensitiveValue())
if err != nil {
return "", err
} else if len(clientKey) > 0 {
return clientKey, nil
}
content, err := p.fileManager.ReadFile("/files/client.key")
if err != nil {
return "", err
}
s := string(content)
s = strings.ReplaceAll(s, "\n", "")
s = strings.ReplaceAll(s, "\r", "")
return s, nil
}

165
internal/params/dns.go Normal file
View File

@@ -0,0 +1,165 @@
package params
import (
"fmt"
"net"
"strings"
"time"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
libparams "github.com/qdm12/golibs/params"
)
// GetDNSOverTLS obtains if the DNS over TLS should be enabled
// from the environment variable DOT
func (r *reader) GetDNSOverTLS() (DNSOverTLS bool, err error) { //nolint:gocritic
return r.envParams.GetOnOff("DOT", libparams.Default("on"))
}
// GetDNSOverTLSProviders obtains the DNS over TLS providers to use
// from the environment variable DOT_PROVIDERS
func (r *reader) GetDNSOverTLSProviders() (providers []models.DNSProvider, err error) {
s, err := r.envParams.GetEnv("DOT_PROVIDERS", libparams.Default("cloudflare"))
if err != nil {
return nil, err
}
for _, word := range strings.Split(s, ",") {
provider := models.DNSProvider(word)
switch provider {
case constants.Cloudflare, constants.Google, constants.Quad9, constants.Quadrant, constants.CleanBrowsing, constants.SecureDNS, constants.LibreDNS:
providers = append(providers, provider)
default:
return nil, fmt.Errorf("DNS over TLS provider %q is not valid", provider)
}
}
return providers, nil
}
// GetDNSOverTLSVerbosity obtains the verbosity level to use for Unbound
// from the environment variable DOT_VERBOSITY
func (r *reader) GetDNSOverTLSVerbosity() (verbosityLevel uint8, err error) {
n, err := r.envParams.GetEnvIntRange("DOT_VERBOSITY", 0, 5, libparams.Default("1"))
return uint8(n), err
}
// GetDNSOverTLSVerbosityDetails obtains the log level to use for Unbound
// from the environment variable DOT_VERBOSITY_DETAILS
func (r *reader) GetDNSOverTLSVerbosityDetails() (verbosityDetailsLevel uint8, err error) {
n, err := r.envParams.GetEnvIntRange("DOT_VERBOSITY_DETAILS", 0, 4, libparams.Default("0"))
return uint8(n), err
}
// GetDNSOverTLSValidationLogLevel obtains the log level to use for Unbound DOT validation
// from the environment variable DOT_VALIDATION_LOGLEVEL
func (r *reader) GetDNSOverTLSValidationLogLevel() (validationLogLevel uint8, err error) {
n, err := r.envParams.GetEnvIntRange("DOT_VALIDATION_LOGLEVEL", 0, 2, libparams.Default("0"))
return uint8(n), err
}
// GetDNSMaliciousBlocking obtains if malicious hostnames/IPs should be blocked
// from being resolved by Unbound, using the environment variable BLOCK_MALICIOUS
func (r *reader) GetDNSMaliciousBlocking() (blocking bool, err error) {
return r.envParams.GetOnOff("BLOCK_MALICIOUS", libparams.Default("on"))
}
// GetDNSSurveillanceBlocking obtains if surveillance hostnames/IPs should be blocked
// from being resolved by Unbound, using the environment variable BLOCK_SURVEILLANCE
// and BLOCK_NSA for retrocompatibility
func (r *reader) GetDNSSurveillanceBlocking() (blocking bool, err error) {
// Retro-compatibility
s, err := r.envParams.GetEnv("BLOCK_NSA")
if err != nil {
return false, err
} else if len(s) != 0 {
r.logger.Warn("You are using the old environment variable BLOCK_NSA, please consider changing it to BLOCK_SURVEILLANCE")
return r.envParams.GetOnOff("BLOCK_NSA", libparams.Compulsory())
}
return r.envParams.GetOnOff("BLOCK_SURVEILLANCE", libparams.Default("off"))
}
// GetDNSAdsBlocking obtains if ads hostnames/IPs should be blocked
// from being resolved by Unbound, using the environment variable BLOCK_ADS
func (r *reader) GetDNSAdsBlocking() (blocking bool, err error) {
return r.envParams.GetOnOff("BLOCK_ADS", libparams.Default("off"))
}
// GetDNSUnblockedHostnames obtains a list of hostnames to unblock from block lists
// from the comma separated list for the environment variable UNBLOCK
func (r *reader) GetDNSUnblockedHostnames() (hostnames []string, err error) {
s, err := r.envParams.GetEnv("UNBLOCK")
if err != nil {
return nil, err
} else if len(s) == 0 {
return nil, nil
}
hostnames = strings.Split(s, ",")
for _, hostname := range hostnames {
if !r.verifier.MatchHostname(hostname) {
return nil, fmt.Errorf("hostname %q does not seem valid", hostname)
}
}
return hostnames, nil
}
// GetDNSOverTLSCaching obtains if Unbound caching should be enable or not
// from the environment variable DOT_CACHING
func (r *reader) GetDNSOverTLSCaching() (caching bool, err error) {
return r.envParams.GetOnOff("DOT_CACHING")
}
// GetDNSOverTLSPrivateAddresses obtains if Unbound caching should be enable or not
// from the environment variable DOT_PRIVATE_ADDRESS
func (r *reader) GetDNSOverTLSPrivateAddresses() (privateAddresses []string, err error) {
s, err := r.envParams.GetEnv("DOT_PRIVATE_ADDRESS")
if err != nil {
return nil, err
} else if len(s) == 0 {
return nil, nil
}
privateAddresses = strings.Split(s, ",")
for _, address := range privateAddresses {
ip := net.ParseIP(address)
_, _, err := net.ParseCIDR(address)
if ip == nil && err != nil {
return nil, fmt.Errorf("private address %q is not a valid IP or CIDR range", address)
}
}
return privateAddresses, nil
}
// GetDNSOverTLSIPv6 obtains if Unbound should resolve ipv6 addresses using ipv6 DNS over TLS
// servers from the environment variable DOT_IPV6
func (r *reader) GetDNSOverTLSIPv6() (ipv6 bool, err error) {
return r.envParams.GetOnOff("DOT_IPV6", libparams.Default("off"))
}
// GetDNSUpdatePeriod obtains the period to use to update the block lists and cryptographic files
// and restart Unbound from the environment variable DNS_UPDATE_PERIOD
func (r *reader) GetDNSUpdatePeriod() (period time.Duration, err error) {
s, err := r.envParams.GetEnv("DNS_UPDATE_PERIOD", libparams.Default("24h"))
if err != nil {
return period, err
}
return time.ParseDuration(s)
}
// GetDNSPlaintext obtains the plaintext DNS address to use if DNS over TLS is disabled
// from the environment variable DNS_PLAINTEXT_ADDRESS
func (r *reader) GetDNSPlaintext() (ip net.IP, err error) {
s, err := r.envParams.GetEnv("DNS_PLAINTEXT_ADDRESS", libparams.Default("1.1.1.1"))
if err != nil {
return nil, err
}
ip = net.ParseIP(s)
if ip == nil {
return nil, fmt.Errorf("DNS plaintext address %q is not a valid IP address", s)
}
return ip, nil
}
// GetDNSKeepNameserver obtains if the nameserver present in /etc/resolv.conf
// should be kept instead of overridden, from the environment variable DNS_KEEP_NAMESERVER
func (r *reader) GetDNSKeepNameserver() (on bool, err error) {
return r.envParams.GetOnOff("DNS_KEEP_NAMESERVER", libparams.Default("off"))
}

View File

@@ -0,0 +1,66 @@
package params
import (
"fmt"
"net"
"strconv"
"strings"
libparams "github.com/qdm12/golibs/params"
)
// GetFirewall obtains if the firewall should be enabled from the environment variable FIREWALL
func (r *reader) GetFirewall() (enabled bool, err error) {
return r.envParams.GetOnOff("FIREWALL", libparams.Default("on"))
}
// GetExtraSubnets obtains the CIDR subnets from the comma separated list of the
// environment variable EXTRA_SUBNETS
func (r *reader) GetExtraSubnets() (extraSubnets []net.IPNet, err error) {
s, err := r.envParams.GetEnv("EXTRA_SUBNETS")
if err != nil {
return nil, err
} else if s == "" {
return nil, nil
}
subnets := strings.Split(s, ",")
for _, subnet := range subnets {
_, cidr, err := net.ParseCIDR(subnet)
if err != nil {
return nil, fmt.Errorf("could not parse subnet %q from environment variable with key EXTRA_SUBNETS: %w", subnet, err)
} else if cidr == nil {
return nil, fmt.Errorf("parsing subnet %q resulted in a nil CIDR", subnet)
}
extraSubnets = append(extraSubnets, *cidr)
}
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
func (r *reader) GetFirewallDebug() (debug bool, err error) {
return r.envParams.GetOnOff("FIREWALL_DEBUG", libparams.Default("off"))
}

View File

@@ -0,0 +1,34 @@
package params
import (
"github.com/qdm12/gluetun/internal/constants"
libparams "github.com/qdm12/golibs/params"
)
// GetMullvadCountry obtains the country for the Mullvad server from the
// environment variable COUNTRY
func (r *reader) GetMullvadCountry() (country string, err error) {
choices := append(constants.MullvadCountryChoices(), "")
return r.envParams.GetValueIfInside("COUNTRY", choices)
}
// GetMullvadCity obtains the city for the Mullvad server from the
// environment variable CITY
func (r *reader) GetMullvadCity() (country string, err error) {
choices := append(constants.MullvadCityChoices(), "")
return r.envParams.GetValueIfInside("CITY", choices)
}
// GetMullvadISP obtains the ISP for the Mullvad server from the
// environment variable ISP
func (r *reader) GetMullvadISP() (isp string, err error) {
choices := append(constants.MullvadISPChoices(), "")
return r.envParams.GetValueIfInside("ISP", choices)
}
// GetMullvadPort obtains the port to reach the Mullvad server on from the
// environment variable PORT
func (r *reader) GetMullvadPort() (port uint16, err error) {
n, err := r.envParams.GetEnvIntRange("PORT", 0, 65535, libparams.Default("0"))
return uint16(n), err
}

View File

@@ -0,0 +1,23 @@
package params
import (
"github.com/qdm12/gluetun/internal/constants"
libparams "github.com/qdm12/golibs/params"
)
// GetNordvpnRegion obtains the region (country) for the NordVPN server from the
// environment variable REGION
func (r *reader) GetNordvpnRegion() (region string, err error) {
choices := append(constants.NordvpnRegionChoices(), "")
return r.envParams.GetValueIfInside("REGION", choices)
}
// GetNordvpnRegion obtains the server number (optional) for the NordVPN server from the
// environment variable SERVER_NUMBER
func (r *reader) GetNordvpnNumber() (number uint16, err error) {
n, err := r.envParams.GetEnvIntRange("SERVER_NUMBER", 0, 65535, libparams.Default("0"))
if err != nil {
return 0, err
}
return uint16(n), nil
}

View File

@@ -0,0 +1,83 @@
package params
import (
"fmt"
"net"
"github.com/qdm12/gluetun/internal/models"
libparams "github.com/qdm12/golibs/params"
)
// GetUser obtains the user to use to connect to the VPN servers
func (r *reader) GetUser() (s string, err error) {
defer func() {
unsetenvErr := r.unsetEnv("USER")
if err == nil {
err = unsetenvErr
}
}()
return r.envParams.GetEnv("USER", libparams.CaseSensitiveValue(), libparams.Compulsory())
}
// GetPassword obtains the password to use to connect to the VPN servers
func (r *reader) GetPassword(required bool) (s string, err error) {
defer func() {
unsetenvErr := r.unsetEnv("PASSWORD")
if err == nil {
err = unsetenvErr
}
}()
options := []libparams.GetEnvSetter{libparams.CaseSensitiveValue()}
if required {
options = append(options, libparams.Compulsory())
}
return r.envParams.GetEnv("PASSWORD", options...)
}
// GetNetworkProtocol obtains the network protocol to use to connect to the
// VPN servers from the environment variable PROTOCOL
func (r *reader) GetNetworkProtocol() (protocol models.NetworkProtocol, err error) {
s, err := r.envParams.GetValueIfInside("PROTOCOL", []string{"tcp", "udp"}, libparams.Default("udp"))
return models.NetworkProtocol(s), err
}
// GetOpenVPNVerbosity obtains the verbosity level for verbosity between 0 and 6
// from the environment variable OPENVPN_VERBOSITY
func (r *reader) GetOpenVPNVerbosity() (verbosity int, err error) {
return r.envParams.GetEnvIntRange("OPENVPN_VERBOSITY", 0, 6, libparams.Default("1"))
}
// GetOpenVPNRoot obtains if openvpn should be run as root
// from the environment variable OPENVPN_ROOT
func (r *reader) GetOpenVPNRoot() (root bool, err error) {
return r.envParams.GetYesNo("OPENVPN_ROOT", libparams.Default("no"))
}
// GetTargetIP obtains the IP address to choose from the list of IP addresses
// available for a particular region, from the environment variable
// OPENVPN_TARGET_IP
func (r *reader) GetTargetIP() (ip net.IP, err error) {
s, err := r.envParams.GetEnv("OPENVPN_TARGET_IP")
if len(s) == 0 {
return nil, nil
} else if err != nil {
return nil, err
}
ip = net.ParseIP(s)
if ip == nil {
return nil, fmt.Errorf("target IP address %q is not valid", s)
}
return ip, nil
}
// GetOpenVPNCipher obtains a custom cipher to use with OpenVPN
// from the environment variable OPENVPN_CIPHER
func (r *reader) GetOpenVPNCipher() (cipher string, err error) {
return r.envParams.GetEnv("OPENVPN_CIPHER")
}
// GetOpenVPNAuth obtains a custom auth algorithm to use with OpenVPN
// from the environment variable OPENVPN_AUTH
func (r *reader) GetOpenVPNAuth() (auth string, err error) {
return r.envParams.GetEnv("OPENVPN_AUTH")
}

144
internal/params/params.go Normal file
View File

@@ -0,0 +1,144 @@
package params
import (
"net"
"os"
"time"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
libparams "github.com/qdm12/golibs/params"
"github.com/qdm12/golibs/verification"
)
// Reader contains methods to obtain parameters
type Reader interface {
GetVPNSP() (vpnServiceProvider models.VPNProvider, err error)
// DNS over TLS getters
GetDNSOverTLS() (DNSOverTLS bool, err error)
GetDNSOverTLSProviders() (providers []models.DNSProvider, err error)
GetDNSOverTLSCaching() (caching bool, err error)
GetDNSOverTLSVerbosity() (verbosityLevel uint8, err error)
GetDNSOverTLSVerbosityDetails() (verbosityDetailsLevel uint8, err error)
GetDNSOverTLSValidationLogLevel() (validationLogLevel uint8, err error)
GetDNSMaliciousBlocking() (blocking bool, err error)
GetDNSSurveillanceBlocking() (blocking bool, err error)
GetDNSAdsBlocking() (blocking bool, err error)
GetDNSUnblockedHostnames() (hostnames []string, err error)
GetDNSOverTLSPrivateAddresses() (privateAddresses []string, err error)
GetDNSOverTLSIPv6() (ipv6 bool, err error)
GetDNSUpdatePeriod() (period time.Duration, err error)
GetDNSPlaintext() (ip net.IP, err error)
GetDNSKeepNameserver() (on bool, err error)
// System
GetUID() (uid int, err error)
GetGID() (gid int, err error)
GetTimezone() (timezone string, err error)
GetIPStatusFilepath() (filepath models.Filepath, err error)
// Firewall getters
GetFirewall() (enabled bool, err error)
GetExtraSubnets() (extraSubnets []net.IPNet, err error)
GetVPNInputPorts() (ports []uint16, err error)
GetFirewallDebug() (debug bool, err error)
// VPN getters
GetUser() (s string, err error)
GetPassword(required bool) (s string, err error)
GetNetworkProtocol() (protocol models.NetworkProtocol, err error)
GetOpenVPNVerbosity() (verbosity int, err error)
GetOpenVPNRoot() (root bool, err error)
GetTargetIP() (ip net.IP, err error)
GetOpenVPNCipher() (cipher string, err error)
GetOpenVPNAuth() (auth string, err error)
// PIA getters
GetPortForwarding() (activated bool, err error)
GetPortForwardingStatusFilepath() (filepath models.Filepath, err error)
GetPIAEncryptionPreset() (preset string, err error)
GetPIARegion() (region string, err error)
// Mullvad getters
GetMullvadCountry() (country string, err error)
GetMullvadCity() (country string, err error)
GetMullvadISP() (country string, err error)
GetMullvadPort() (port uint16, err error)
// Windscribe getters
GetWindscribeRegion() (country string, err error)
GetWindscribePort(protocol models.NetworkProtocol) (port uint16, err error)
// Surfshark getters
GetSurfsharkRegion() (country string, err error)
// Cyberghost getters
GetCyberghostGroup() (group string, err error)
GetCyberghostRegion() (region string, err error)
GetCyberghostClientKey() (clientKey string, err error)
// Vyprvpn getters
GetVyprvpnRegion() (region string, err error)
// NordVPN getters
GetNordvpnRegion() (region string, 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
GetShadowSocks() (activated bool, err error)
GetShadowSocksLog() (activated bool, err error)
GetShadowSocksPort() (port uint16, err error)
GetShadowSocksPassword() (password string, err error)
GetShadowSocksMethod() (method string, err error)
// Tinyproxy getters
GetTinyProxy() (activated bool, err error)
GetTinyProxyLog() (models.TinyProxyLogLevel, error)
GetTinyProxyPort() (port uint16, err error)
GetTinyProxyUser() (user string, err error)
GetTinyProxyPassword() (password string, err error)
// Public IP getters
GetPublicIPPeriod() (period time.Duration, err error)
// Version getters
GetVersion() string
GetBuildDate() string
GetVcsRef() string
}
type reader struct {
envParams libparams.EnvParams
logger logging.Logger
verifier verification.Verifier
unsetEnv func(key string) error
fileManager files.FileManager
}
// Newreader returns a paramsReadeer object to read parameters from
// environment variables
func NewReader(logger logging.Logger, fileManager files.FileManager) Reader {
return &reader{
envParams: libparams.NewEnvParams(),
logger: logger,
verifier: verification.NewVerifier(),
unsetEnv: os.Unsetenv,
fileManager: fileManager,
}
}
// GetVPNSP obtains the VPN service provider to use from the environment variable VPNSP
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", "purevpn"})
if s == "pia" {
s = "private internet access"
}
return models.VPNProvider(s), err
}

63
internal/params/pia.go Normal file
View File

@@ -0,0 +1,63 @@
package params
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
libparams "github.com/qdm12/golibs/params"
)
// GetPortForwarding obtains if port forwarding on the VPN provider server
// side is enabled or not from the environment variable PORT_FORWARDING
func (r *reader) GetPortForwarding() (activated bool, err error) {
s, err := r.envParams.GetEnv("PORT_FORWARDING", libparams.Default("off"))
if err != nil {
return false, err
}
// Custom for retro-compatibility
if s == "false" || s == "off" {
return false, nil
} else if s == "true" || s == "on" {
return true, nil
}
return false, fmt.Errorf("PORT_FORWARDING can only be \"on\" or \"off\"")
}
// GetPortForwardingStatusFilepath obtains the port forwarding status file path
// from the environment variable PORT_FORWARDING_STATUS_FILE
func (r *reader) GetPortForwardingStatusFilepath() (filepath models.Filepath, err error) {
filepathStr, err := r.envParams.GetPath("PORT_FORWARDING_STATUS_FILE", libparams.Default("/forwarded_port"), libparams.CaseSensitiveValue())
return models.Filepath(filepathStr), err
}
// GetPIAEncryptionPreset obtains the encryption level for the PIA connection
// from the environment variable PIA_ENCRYPTION, and using ENCRYPTION for
// retro compatibility
func (r *reader) GetPIAEncryptionPreset() (preset string, err error) {
// Retro-compatibility
s, err := r.envParams.GetValueIfInside("ENCRYPTION", []string{
constants.PIAEncryptionPresetNormal,
constants.PIAEncryptionPresetStrong,
""})
if err != nil {
return "", err
} else if len(s) != 0 {
r.logger.Warn("You are using the old environment variable ENCRYPTION, please consider changing it to PIA_ENCRYPTION")
return s, nil
}
return r.envParams.GetValueIfInside(
"PIA_ENCRYPTION",
[]string{
constants.PIAEncryptionPresetNormal,
constants.PIAEncryptionPresetStrong,
},
libparams.Default(constants.PIAEncryptionPresetStrong))
}
// GetPIARegion obtains the region for the PIA server from the
// environment variable REGION
func (r *reader) GetPIARegion() (region string, err error) {
choices := append(constants.PIAGeoChoices(), "")
return r.envParams.GetValueIfInside("REGION", choices)
}

View File

@@ -0,0 +1,17 @@
package params
import (
"time"
libparams "github.com/qdm12/golibs/params"
)
// GetPublicIPPeriod obtains the period to fetch the IP address periodically.
// Set to 0 to disable
func (r *reader) GetPublicIPPeriod() (period time.Duration, err error) {
s, err := r.envParams.GetEnv("PUBLICIP_PERIOD", libparams.Default("12h"))
if err != nil {
return 0, err
}
return time.ParseDuration(s)
}

View File

@@ -0,0 +1,26 @@
package params
import (
"github.com/qdm12/gluetun/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

@@ -0,0 +1,51 @@
package params
import (
"strconv"
libparams "github.com/qdm12/golibs/params"
)
// GetShadowSocks obtains if ShadowSocks is on from the environment variable
// SHADOWSOCKS
func (r *reader) GetShadowSocks() (activated bool, err error) {
return r.envParams.GetOnOff("SHADOWSOCKS", libparams.Default("off"))
}
// GetShadowSocksLog obtains the ShadowSocks log level from the environment variable
// SHADOWSOCKS_LOG
func (r *reader) GetShadowSocksLog() (activated bool, err error) {
return r.envParams.GetOnOff("SHADOWSOCKS_LOG", libparams.Default("off"))
}
// GetShadowSocksPort obtains the ShadowSocks listening port from the environment variable
// SHADOWSOCKS_PORT
func (r *reader) GetShadowSocksPort() (port uint16, err error) {
portStr, err := r.envParams.GetEnv("SHADOWSOCKS_PORT", libparams.Default("8388"))
if err != nil {
return 0, err
}
if err := r.verifier.VerifyPort(portStr); err != nil {
return 0, err
}
portUint64, err := strconv.ParseUint(portStr, 10, 16)
return uint16(portUint64), err
}
// GetShadowSocksPassword obtains the ShadowSocks server password from the environment variable
// SHADOWSOCKS_PASSWORD
func (r *reader) GetShadowSocksPassword() (password string, err error) {
defer func() {
unsetErr := r.unsetEnv("SHADOWSOCKS_PASSWORD")
if err == nil {
err = unsetErr
}
}()
return r.envParams.GetEnv("SHADOWSOCKS_PASSWORD", libparams.CaseSensitiveValue())
}
// GetShadowSocksMethod obtains the ShadowSocks method to use from the environment variable
// SHADOWSOCKS_METHOD
func (r *reader) GetShadowSocksMethod() (method string, err error) {
return r.envParams.GetEnv("SHADOWSOCKS_METHOD", libparams.Default("chacha20-ietf-poly1305"))
}

View File

@@ -0,0 +1,12 @@
package params
import (
"github.com/qdm12/gluetun/internal/constants"
)
// GetSurfsharkRegion obtains the region for the Surfshark server from the
// environment variable REGION
func (r *reader) GetSurfsharkRegion() (region string, err error) {
choices := append(constants.SurfsharkRegionChoices(), "")
return r.envParams.GetValueIfInside("REGION", choices)
}

Some files were not shown because too many files have changed in this diff Show More