Compare commits

..

787 Commits

Author SHA1 Message Date
Quentin McGaw
c777f8d97d Feat: add verify-x509-name to Windscribe Openvpn config (#529) 2021-07-28 07:18:08 -07:00
Quentin McGaw (desktop)
7d4f5c8906 Fix: Alpine vulnerability with apk 2021-07-27 19:45:23 +00:00
Quentin McGaw (desktop)
da39d07d48 Maint: log line fix for updater version diff 2021-07-27 15:18:10 +00:00
Quentin McGaw (desktop)
b98f2456c0 Fix: deadlock for openvpn, dns and publicip loops 2021-07-27 14:12:22 +00:00
Quentin McGaw (desktop)
564cc2b0bc Maint: move misplaced writeOpenvpnConf in openvpn 2021-07-26 16:30:51 +00:00
Quentin McGaw (desktop)
49885c63c4 Maint: common no port forwarding implementation 2021-07-26 16:29:40 +00:00
Quentin McGaw (desktop)
d7a6caa2ac Maint: routing interface composition 2021-07-26 16:18:53 +00:00
Quentin McGaw (desktop)
73c383fd65 Maint: remove routing configurator from Openvpn Loop 2021-07-26 16:18:35 +00:00
Quentin McGaw (desktop)
10b270f742 Maint: remove routing from firewall configurator 2021-07-26 16:17:01 +00:00
Quentin McGaw (desktop)
7a222923c7 Maint: use narrower interfaces for firewall config 2021-07-26 16:07:50 +00:00
Quentin McGaw (desktop)
430512dd27 Maint: openvpn configurator interface composition 2021-07-26 16:03:04 +00:00
Quentin McGaw (desktop)
d5ba15c23b Maint: improve http proxy loop Run 2021-07-26 01:42:37 +00:00
Quentin McGaw (desktop)
037b43ee10 Maint: add completed status to loopstate 2021-07-26 01:38:49 +00:00
Quentin McGaw (desktop)
ab910403c6 Fix: public IP loop being stuck 2021-07-26 01:35:43 +00:00
Quentin McGaw (laptop)
8105437815 Maint: add missing interface compilation checks 2021-07-24 19:54:15 +00:00
Quentin McGaw (laptop)
7b20cec035 Maint: rename SettingsGetterSetter to SettingsGetSetter 2021-07-24 19:49:50 +00:00
Quentin McGaw (laptop)
8d512852a4 Maint: rework publicip package
- Use loopstate package
- Loop interface composition
- Return concrete struct from constructors
- Split into more files
- Add publicip/state package
2021-07-24 19:49:11 +00:00
Quentin McGaw (laptop)
c8ad9b942a Maint: openvpn loop is a concrete struct 2021-07-24 19:14:49 +00:00
Quentin McGaw (laptop)
8153d4bb2a Maint: better openvpn loop interface composition 2021-07-24 18:56:42 +00:00
Quentin McGaw (laptop)
849dfee200 Maint: http proxy return concrete Loop struct 2021-07-24 18:52:19 +00:00
Quentin McGaw (laptop)
85540d96b6 Maint: interface composition for HTTP proxy loop
- Change SetStatus to ApplyStatus
- Add Runner interface
- Add SettingsGetterSetter alias to state.SettingsGetterSetter
2021-07-24 18:50:17 +00:00
Quentin McGaw (laptop)
7479974d79 Maint: dns package state rework
- Interface composition with loopstate interfaces
- Use loopstate.Manager
- Create dns/state package for handling settings
2021-07-24 18:34:55 +00:00
Quentin McGaw (laptop)
3f1fb52fcb Maint: upgrade qdm12 dependencies
- Upgrade qdm12/golibs
- Upgrade qdm12/dns to v1.11.0
2021-07-24 17:59:22 +00:00
Quentin McGaw (desktop)
7e343d7006 Maint: use loopstate for httpproxy 2021-07-23 20:47:36 +00:00
Quentin McGaw (desktop)
72a5e1f695 Maint: openvpn package split files 2021-07-23 20:46:57 +00:00
Quentin McGaw (desktop)
253310bd1a Maint: loopstate package used in Openvpn state 2021-07-23 20:41:45 +00:00
Quentin McGaw (desktop)
fa6ccb08bd Fix: openvpn loop: unlock read mutex for GetSettingsAndServers 2021-07-23 20:13:02 +00:00
Quentin McGaw (desktop)
762507855e Maint: split httpproxy files 2021-07-23 19:25:48 +00:00
Quentin McGaw (desktop)
54610866f2 Maint: healthcheck package interface rework
- return concrete struct type
- Add compilation checks for implementations
2021-07-23 19:22:41 +00:00
Quentin McGaw (desktop)
c39ff5c233 Maint: move duration formatting to qdm12/golibs 2021-07-23 19:17:23 +00:00
Quentin McGaw (desktop)
2ddc784965 Maint: firewall package interface rework
- return concrete struct type
- split interface is sub-interfaces
2021-07-23 19:12:16 +00:00
Quentin McGaw (desktop)
10aabe8375 Hotfix: cli, alpine and dns interface name changes 2021-07-23 19:11:49 +00:00
Quentin McGaw (desktop)
122647b39d Maint: pass network values to firewall constructor 2021-07-23 19:04:17 +00:00
Quentin McGaw (desktop)
02492c34a7 Maint: dns package interface rework
- return concrete struct type
- split interface is sub-interfaces
2021-07-23 18:57:29 +00:00
Quentin McGaw (desktop)
9436f604ba Maint: split Go files in dns package 2021-07-23 18:55:53 +00:00
Quentin McGaw (desktop)
d9ca0deb08 Maint: cli package interface rework
- return concrete struct type
- split interface is sub-interfaces
2021-07-23 18:52:38 +00:00
Quentin McGaw (desktop)
0b985e8c35 Maint: alpine package interface rework
- return concrete struct type
- split interface is sub-interfaces
2021-07-23 18:51:51 +00:00
Quentin McGaw (desktop)
c5d92ae02c Maint: inject Commander to openvpn and firewall 2021-07-23 18:25:30 +00:00
Quentin McGaw (desktop)
94b60d9f70 Maint: firewall and routing use logger.Debug
- Remove SetVerbose and SetDebug from both
- Log routing teardown
- Default logging level set to info
2021-07-23 18:20:18 +00:00
Quentin McGaw (desktop)
b23eb8f29d Maint: prefer empty string comparison 2021-07-23 17:39:38 +00:00
Quentin McGaw (desktop)
3c44214d01 Maint: pass only single strings to logger methods
- Do not assume formatting from logger's interface
- Allow to change golibs in the future to accept only strings for logger methods
2021-07-23 17:36:08 +00:00
Quentin McGaw (desktop)
21f4cf7ab5 Maint: do not mock os functions
- Use filepaths with /tmp for tests instead
- Only mock functions where filepath can't be specified such as user.Lookup
2021-07-23 16:06:19 +00:00
Quentin McGaw (desktop)
e94684aa39 Fix: version diff for VPN server information 2021-07-23 02:51:49 +00:00
Quentin McGaw (desktop)
a34cc48197 Feat: update all servers for all providers 2021-07-23 02:47:29 +00:00
Quentin McGaw (desktop)
b262d91ccc Feat: add -all flag to update all VPN servers 2021-07-23 02:47:04 +00:00
Quentin McGaw (desktop)
39aa983771 Maint: upgrade golibs and env error wrapping 2021-07-23 02:34:15 +00:00
Quentin McGaw (desktop)
5b9887dade Maint: use qdm12/gosplash 2021-07-22 20:56:47 +00:00
Quentin McGaw (desktop)
c33402ce66 Feat: HEALTH_SERVER_ADDRESS 2021-07-22 20:45:17 +00:00
Quentin McGaw (desktop)
6f58f84151 Maint: improve health code 2021-07-22 20:18:52 +00:00
Quentin McGaw (desktop)
6acb7caf5b Feat: Env variables to set health timeouts
- HEALTH_OPENVPN_DURATION_INITIAL
- HEALTH_OPENVPN_DURATION_ADDITION
2021-07-22 20:13:20 +00:00
Quentin McGaw (desktop)
8beff34cca Maint: remove debug line in health server 2021-07-22 13:43:19 +00:00
TJJP
478e0f74f7 Fix: Windscribe Openvpn config (#528)
See https://blog.windscribe.com/openvpn-security-improvements-and-changes-7b04ea49222

> OpenVPN compression phaseout is in progress and will be completed by August 3rd 2021.
If you downloaded configs from this page before you saw this message, you need to re-download them now, or simply remove the compress or comp-lzo flags from the config that you downloaded after July 20th 2021.
2021-07-21 12:48:10 -04:00
Quentin McGaw (desktop)
b7bd23ab60 Fix: buildDate renamed to created in Dockerfile 2021-07-20 23:10:33 +00:00
Quentin McGaw (desktop)
82533c1453 Maint: improve servers data embedding
- use embed.FS to have immutable data
- use sync.Once to parse only once without data races
2021-07-20 19:01:49 +00:00
Quentin McGaw (desktop)
e0735b57ce Maint: build all images fully in parallel 2021-07-20 15:47:28 +00:00
Quentin McGaw (desktop)
1e0bfc3b0c Maint: rename BUILD_DATE to CREATED 2021-07-20 15:28:02 +00:00
Quentin McGaw (desktop)
cb0e89a38e Maint: use curly braces around BUILDPLATFORM 2021-07-20 15:27:16 +00:00
Quentin McGaw (desktop)
da4d528463 Maint: hardcoded data in JSON embedded file
- Server information, versions and timestamps together in internal/constants/servers.json
- breaking change: updater cli uses -enduser instead of -file
- breaking change: updater cli uses -maintainer instead of -stdout
- Fix: replace special last a character with 'a' from Bogota for PrivateVPN
- Feat: do not write out servers and timestamp if no change was detected
2021-07-20 03:01:26 +00:00
Quentin McGaw (desktop)
394abbbe35 Feat: specify Openvpn flags with OPENVPN_FLAGS 2021-07-19 15:10:53 +00:00
Quentin McGaw (desktop)
fd39bc8518 Maint: upgrade inet.af/netaddr to 2021-07-18 2021-07-19 13:28:13 +00:00
dependabot[bot]
2663e8fba7 Bump docker/build-push-action from 2.4.0 to 2.6.1 (#513)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 2.4.0 to 2.6.1.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v2.4.0...v2.6.1)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-18 19:22:59 -07:00
Quentin McGaw (desktop)
faebac6a77 Maint: rename build.yml to ci.yml for linting 2021-07-19 02:20:23 +00:00
Quentin McGaw (desktop)
bc1b09e997 Maint: remove microbadger hook from CI 2021-07-19 02:18:22 +00:00
Quentin McGaw (desktop)
af358f777b Feat: pull filter ipv6 if OPENVPN_IPV6 is off 2021-07-19 01:46:20 +00:00
Quentin McGaw (desktop)
c0d27b4bfc Maint: rework openvpn restart on unhealthy 2021-07-18 03:17:48 +00:00
Quentin McGaw (desktop)
7e50c95823 Maint: minor DNS loop fixes and changes 2021-07-16 21:21:09 +00:00
Quentin McGaw (desktop)
39068dda17 Maint: rework Openvpn run loop 2021-07-16 21:20:34 +00:00
Quentin McGaw (desktop)
8185979ca4 Fix: deadlock on dns shutdown when starting up 2021-07-16 20:11:57 +00:00
Quentin McGaw (desktop)
7c44188130 Fix: controlled interrupt exit for subprograms
- Openvpn and Unbound do not receive OS signals
- Openvpn and Unbound run in a different process group than the entrypoint
- Openvpn and Unbound are gracefully shutdown by the entrypoint
- Update golibs with a modified command package
- Update dns to v1.9.0 where Unbound is luanched in its own group
2021-07-16 20:04:17 +00:00
Quentin McGaw (desktop)
c2d527bbd3 Fix: openvpn run loop panic about stdout streams 2021-07-16 19:02:04 +00:00
Quentin McGaw (desktop)
ac3ff095a1 Maint: rework DNS run loop
- Fix fragile user triggered logic
- Simplify state
- Lock loop when crashed
2021-07-16 19:00:56 +00:00
Quentin McGaw (desktop)
0ed738cd61 Maint: make all set status context aware 2021-07-16 00:49:59 +00:00
Quentin McGaw (desktop)
6bbb7c8f7d Maint: remove outdated Auth log warning about PIA 2021-07-16 00:49:50 +00:00
Quentin McGaw (desktop)
d29429808c Maint: deduplicate error logs for goshutdown 2021-07-15 23:02:33 +00:00
Quentin McGaw (desktop)
09eccd7cd9 Fix: events routing behavior when version information is disabled 2021-07-15 22:43:30 +00:00
Quentin McGaw (desktop)
bb2b8b4514 Fix: events routing exit when gluetun stops at start 2021-07-15 22:42:58 +00:00
Quentin McGaw (desktop)
e20b9c5774 Doc: simplify metdata and move it at top of readme 2021-07-14 22:17:51 +00:00
Quentin McGaw (desktop)
3badfa197a Doc: use native markdown for svg title image 2021-07-14 22:08:40 +00:00
Quentin McGaw (desktop)
dee372e71b Doc: add video 2021-07-14 00:31:27 +00:00
Quentin McGaw (desktop)
679be6e1bd Feat: clean suffix new lines for credentials 2021-07-06 14:37:59 +00:00
Quentin McGaw (desktop)
92212fdd11 Fix: Cert validation for IPVanish 2021-07-01 18:28:24 +00:00
Quentin McGaw (desktop)
a6fb1ad9ef Feat: update IPVanish server information 2021-07-01 18:28:12 +00:00
Quentin McGaw (desktop)
87d712fbd7 Feature: update ProtonVPN server information 2021-06-28 15:30:35 +00:00
Quentin McGaw (desktop)
023809f099 Feature: upgrade to Alpine 3.14
- Release note: https://wiki.alpinelinux.org/wiki/Release_Notes_for_Alpine_3.14.0
2021-06-25 19:01:00 +00:00
Quentin McGaw (desktop)
ace37370d1 Maint: xcputranslate version as build argument 2021-06-25 18:57:04 +00:00
Quentin McGaw (desktop)
8efbd4fac1 Maint: download golangci-lint from qmcgaw/binpot 2021-06-25 18:56:18 +00:00
Quentin McGaw (desktop)
06c8792887 Doc: clarify setup instructions 2021-06-22 15:21:49 +00:00
Quentin McGaw (desktop)
3ea376a1b2 Doc: maintenance document 2021-06-22 14:42:15 +00:00
Quentin McGaw (desktop)
9667d30907 Doc: add code highlighting how-to to issue templates 2021-06-22 14:41:48 +00:00
Quentin McGaw (desktop)
3f7ccc6c49 Feature: improve Cyberghost updater
- Waits up to 20s for resolutions
- Update server information and timestamp
2021-06-21 20:29:55 +00:00
Quentin McGaw (desktop)
dd97ff5895 Maintenance: cache xcputranslate 2021-06-21 18:50:30 +00:00
Quentin McGaw (desktop)
2e4d80d9bc Maintenance: sleep for cross building 2021-06-21 18:35:40 +00:00
Quentin McGaw (desktop)
1227dc5a2b Maintenance: upgrade xcputranslate to v0.6.0 2021-06-21 18:01:21 +00:00
Quentin McGaw (desktop)
ed828bc733 Hotfix: VPN Unlimited variable choices 2021-06-21 13:32:03 +00:00
Quentin McGaw (desktop)
c25a018c05 Maintenance: CI deduplicate base stage build 2021-06-21 13:01:53 +00:00
Quentin McGaw (desktop)
266596af68 Fix errors introduced with golangci-lint 1.41.1 2021-06-20 16:39:38 +00:00
Quentin McGaw
2c77b73ebc IPVanish support (#475)
- Fix #410 and #416
2021-06-20 09:21:48 -07:00
Quentin McGaw
d81d4bbda3 VPN Unlimited support (#499)
- Fixes #420 
- Revert to docker/build-push-action@v2.4.0
2021-06-20 09:18:03 -07:00
Quentin McGaw (desktop)
400affe429 Maintenance: add revive linter 2021-06-20 16:12:39 +00:00
Quentin McGaw (desktop)
d3c63680e8 Maintenance: ugprade golangci-lint to v1.41.1 2021-06-20 16:12:09 +00:00
Quentin McGaw (desktop)
28de8a834c Maintenance: upgrade golang/mock to v1.6.0 2021-06-19 17:24:41 +00:00
Quentin McGaw (desktop)
208374fc54 Fix: Use name prefix for TLS check for IVPN 2021-06-19 16:34:50 +00:00
Quentin McGaw (desktop)
535a136a27 Feature: add IVPN Bulgaria and Spain servers 2021-06-19 16:34:36 +00:00
Quentin McGaw (desktop)
ba4c3e30a4 Doc: docker-compose.yml does not use secrets 2021-06-17 22:46:30 +00:00
Quentin McGaw (desktop)
16d8a388cb Maintenance: better layer caching
- Install g++ in base image before copying code
- Install xcputranslate in base image before copying code
- Install golangci-lint in base image before copying code
- Install golangci-lint using go get directly
2021-06-15 12:27:32 +00:00
Quentin McGaw (desktop)
5ea31b0b64 Maintenance: set entrypoint for test Docker stage 2021-06-15 12:25:57 +00:00
Quentin McGaw (desktop)
582c6d1c43 Fix: only use and write auth file if user is set
- Apply to custom openvpn configuration without username
2021-06-14 14:25:37 +00:00
Quentin McGaw (desktop)
c63ae3f3af Fix: custom openvpn config settings log 2021-06-14 14:24:38 +00:00
Quentin McGaw (desktop)
4c0df96a95 Maintenance: use github.com/qdm12/goshutdown 2021-06-10 15:03:47 +00:00
Quentin McGaw (desktop)
05c6b9379a Maintenance: prevent exit race condition for loops 2021-06-10 14:13:08 +00:00
Quentin McGaw (desktop)
fb7fdcd925 Fix: change PureVPN default cipher to AES-256-GCM 2021-06-08 00:24:46 +00:00
Quentin McGaw (desktop)
1774e2ad88 Maintenance: update list of linters 2021-06-07 23:31:52 +00:00
Quentin McGaw (desktop)
a402d9135e Fix: remote line for custom OpenVPN config 2021-06-07 19:46:21 +00:00
Quentin McGaw (desktop)
3d2c56d9ee Fix: custom cipher for custom files on Openvpn 2.5 2021-06-07 19:45:19 +00:00
Quentin McGaw (desktop)
f9308e6fed Remove dependency on github.com/kyokomi/emoji 2021-06-06 15:38:49 +00:00
Quentin McGaw (desktop)
6710468020 Maintenance: upgrade Go dependencies
- Upgrade fatih/color to v1.12.0
- Upgrade qdm12/dns to v1.8.0
- Upgrade qdm12/golibs
- Upgrade qdm12/updated
2021-06-03 21:31:50 +00:00
Quentin McGaw (desktop)
ad1981fff6 Maintenance: update PureVPN server information 2021-06-02 14:32:15 +00:00
Quentin McGaw (desktop)
01f9e71912 Fix: none encryption preset for PIA
- Set cipher and auth to `none`
- Add `ncp-disable` OpenVPN option in every case
2021-06-01 13:52:57 +00:00
Quentin McGaw (desktop)
d41b75ee35 Documentation: add discussion link for help issues 2021-06-01 13:44:04 +00:00
Quentin McGaw (desktop)
b829490aac Feature: OPENVPN_VERSION which can be 2.4 or 2.5 2021-05-31 18:54:36 +00:00
Quentin McGaw (desktop)
7002bf8e34 Maintenance: improve printVersion function
- Print program versions in order given
- Exit program on any error as each program is required
2021-05-31 18:47:38 +00:00
Quentin McGaw (desktop)
625ea493fb Maintenance: remove unused openvpn files 2021-05-31 17:55:56 +00:00
Quentin McGaw (desktop)
79b3b2823b Hotfix: remote line for Hidemyass and ivpn 2021-05-31 02:37:20 +00:00
Quentin McGaw (desktop)
9be912e9fd HotFix: IVPN add TCP and UDP fields 2021-05-31 00:41:44 +00:00
Quentin McGaw (desktop)
3c3cd431cd Feature: Support none encryption preset for PIA 2021-05-31 00:32:39 +00:00
Quentin McGaw (desktop)
8b8bab5c58 Feature: IVPN support 2021-05-31 00:11:16 +00:00
Quentin McGaw (desktop)
835fa6c41f Fix: HideMyAss Openvpn remote line 2021-05-30 21:25:55 +00:00
Quentin McGaw (desktop)
8a6cf221a9 Fix: HideMyAss hostnames choices 2021-05-30 20:27:57 +00:00
Quentin McGaw (desktop)
876563c492 Maintenance: improve error wrapping 2021-05-30 16:14:08 +00:00
Quentin McGaw (desktop)
be22c8547f Maintenance: use io instead of ioutil if possible 2021-05-30 03:13:19 +00:00
Quentin McGaw (desktop)
82d98c4859 Maintenance: add more linters to .golangci.yml 2021-05-30 03:09:22 +00:00
Quentin McGaw (desktop)
f1b5341f33 Maintenance: listen on all IP interfaces 2021-05-30 02:58:10 +00:00
Quentin McGaw (desktop)
b3829493ea Maintenance: upgrade ss-server to v0.2.0 2021-05-28 16:26:26 +00:00
Quentin McGaw (desktop)
7db1253967 Maintenance: upgrade golangci-lint to 1.40.1 2021-05-28 16:24:06 +00:00
Quentin McGaw (desktop)
449db40d5f Feature: make Shadowsocks password compulsory 2021-05-28 16:23:44 +00:00
Quentin McGaw
d5d0311bc6 Documentation: issue template warnings 2021-05-25 20:11:23 +00:00
Quentin McGaw
0c4f01a892 Feature: Protonvpn filter servers with FREE_ONLY 2021-05-23 21:51:12 +00:00
Quentin McGaw
bc7246f882 Maintenance: update ProtonVPN server information 2021-05-23 17:40:25 +00:00
Quentin McGaw
da65f3b016 Maintenance: generate Openvpn conf for 2.4 or 2.5 2021-05-23 17:40:14 +00:00
Quentin McGaw
a8c574219d Fix: log level for TLS error from debug to warn 2021-05-23 16:24:04 +00:00
Quentin McGaw
a3751a77aa Fix: log custom port only if set (PIA, Windscribe) 2021-05-19 17:53:11 +00:00
Quentin McGaw
4f521e4dcb Feature: show Alpine version at start 2021-05-19 14:30:43 +00:00
Quentin McGaw
a9589d8d5b Fix: only use Openvpn fast-io when using UDP 2021-05-18 23:46:20 +00:00
Quentin McGaw
13e75aaf20 Maintenance: upgrade to qdm12/dns v1.7.0
- Fix rebinding protection for IPv6 mapped IPv4 networks
- Use netaddr package for DNS blacklisting
2021-05-14 17:54:35 +00:00
Quentin McGaw
0c9bd8aaa0 Maintenance: upgrade golang.org/x/sys 2021-05-14 14:08:55 +00:00
Quentin McGaw
5dba91c9ab Maintenance: qdm12/dns from v1.4.0 to v1.6.0 2021-05-14 14:07:17 +00:00
Quentin McGaw
7d6763cde7 Maintenance: upgrade golibs (affects logger) 2021-05-14 14:07:16 +00:00
dependabot[bot]
dd1b23773e Bump actions/checkout from 2 to 2.3.4 (#453)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 2.3.4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v2.3.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-12 17:25:07 -04:00
Quentin McGaw
33253c0cfc Fix: PIA port forwarding nil url map 2021-05-12 12:47:34 +00:00
Quentin McGaw
0099c06056 Hotfix: remove unused code 2021-05-12 00:57:00 +00:00
Quentin McGaw
1540660cc3 Change: keep firewall on shutdown to avoid leaks 2021-05-11 22:25:42 +00:00
Quentin McGaw
cff5e693d2 Maintenance: shutdown order
- Order of threads to shutdown (control then tickers then health etc.)
- Rely on closing channels instead of waitgroups
- Move exit logs from each package to the shutdown package
2021-05-11 22:24:32 +00:00
Quentin McGaw
5159c1dc83 Maintenance: remove outdated Dockerfile comment 2021-05-11 22:13:16 +00:00
Quentin McGaw
ccc7ad7cbd Change: do not exit on Openvpn config error 2021-05-11 18:23:19 +00:00
Quentin McGaw
c8a61ca687 Maintenance: use signal.NotifyContext 2021-05-11 18:17:59 +00:00
Quentin McGaw
61e36d6aff Maintenance: error wrapping of alpine package 2021-05-11 17:52:29 +00:00
Quentin McGaw
e8c8742bae Maintenance: split each provider in a package
- Fix VyprVPN port
- Fix missing Auth overrides
2021-05-11 17:10:51 +00:00
Quentin McGaw
1cb93d76ed Feature: only teardown routing if changes occurred 2021-05-10 22:16:26 +00:00
Quentin McGaw
dadc939aab Feature: NET_ADMIN tip on routing permission error 2021-05-10 21:31:08 +00:00
Quentin McGaw
c59ea781e3 Maintenance: Protocol selection as boolean in code 2021-05-10 18:18:12 +00:00
Quentin McGaw
810ff62c26 Maintenance: improve error codes in IP routing 2021-05-10 17:33:31 +00:00
Quentin McGaw
5a0418bba6 Feature: re-fetch PIA API to obtain more servers 2021-05-10 16:17:44 +00:00
Quentin McGaw
baf506ae27 Feature: multiple IP addresses per PIA server 2021-05-10 15:44:46 +00:00
Quentin McGaw
52ff03ae41 Feature: 3 IP addresses per Windscribe server 2021-05-10 14:34:42 +00:00
Quentin McGaw
2d95edf8ab Feature: Filter VyprVPN servers by hostname, and:
- Extract if server supports TCP and UDP (never TCP now)
- Filter servers by protocol (unused for now)
2021-05-10 02:12:13 +00:00
Quentin McGaw
95b0fb81d6 Feature: Multiple IPs for each Torguard server
- Fallback on IP from configuration file if DNS resolution fails
- Download both TCP and UDP zip files to detect support for each
- Filter servers by supported network protocol
-
2021-05-10 01:48:52 +00:00
Quentin McGaw
eff65dce00 Feature: filter Surfshark servers by hostname 2021-05-10 01:24:46 +00:00
Quentin McGaw
6c1c069261 Feature: filter by hostname for PureVPN servers
- Record support for TCP and UDP for each hostname
- Fix: each hostname supports only TCP or UDP, not both
- Update PureVPN server information
2021-05-10 00:36:14 +00:00
Quentin McGaw
4fe1e062f2 Feature: filter PIA servers by hostname and name 2021-05-09 16:49:22 +00:00
Quentin McGaw
1fb0840e72 Maintenance: Privado server not found error 2021-05-09 16:32:59 +00:00
Quentin McGaw
689ddf8bf0 Maintenance: fix flakky ip unit test 2021-05-09 03:30:54 +00:00
Quentin McGaw
d243ac49f3 Fix #444 Mullvad servers filtering 2021-05-09 01:56:02 +00:00
Quentin McGaw
de8f018b14 Feature: Snyk code analysis for code and image 2021-05-09 01:11:58 +00:00
Quentin McGaw
8407542600 Feature: filter by country, region and city for Privado 2021-05-09 00:51:34 +00:00
Quentin McGaw
a7a5cca8dd Maintenance: parallelize IP information fetch 2021-05-08 23:37:32 +00:00
Quentin McGaw
d9a70fd094 Maintenance: improve publicip with Result struct 2021-05-08 23:30:29 +00:00
Quentin McGaw
248cc0d3d3 Feature: filter by name and hostname for NordVPN 2021-05-08 22:51:59 +00:00
Quentin McGaw
2924d711cb Maintenance: add empty SERVER_NAME in Dockerfile 2021-05-08 19:32:52 +00:00
Quentin McGaw
d7db105a2f Fix: ProtonVPN SERVER_NAME 2021-05-08 19:32:16 +00:00
Quentin McGaw
2ec2f45c82 Feature: filter by hostname for Mullvad servers 2021-05-08 19:17:36 +00:00
Quentin McGaw
a34769ae02 Feature: filter by hostname for Cyberghost servers 2021-05-08 19:05:11 +00:00
Quentin McGaw
c0e4d805b1 Maintenance: storage package logTimeDiff function 2021-05-08 02:51:39 +00:00
Quentin McGaw
6770336274 Maintenance: add missing server merging logic 2021-05-08 01:15:49 +00:00
Quentin McGaw
8d431dbb34 Feature update all server information 2021-05-08 01:03:09 +00:00
Quentin McGaw
e8e7b83297 Maintenance: refactor servers updater code
- Require at least 80% of number of servers now to pass
- Each provider is in its own package with a common structure
- Unzip package with unzipper interface
- Openvpn package with extraction and download functions
2021-05-08 00:59:42 +00:00
Quentin McGaw
442340dcf2 Feature: create /gluetun if it does not exist 2021-05-06 21:10:28 +00:00
Quentin McGaw
91b037a335 Feature: update hardcoded servers for DNS resolution based VPN providers 2021-05-06 18:51:31 +00:00
Quentin McGaw
d5ef3de64c Feature: more robust updater DNS resolution
- Parallel resolver to resolve multiple hosts
- Repeat resolver to repeat resolution for a single host
- Additional parameters for fault toleration
- Do not update servers if e.g. > 10% DNS resolutions failed
- resolver package in updater package
2021-05-06 18:48:14 +00:00
Quentin McGaw
167a0b0b29 Restart unhealthy (#417) (#441) 2021-05-04 15:36:12 -04:00
Quentin McGaw
954e3c70b2 Feature: Protonvpn support (#437 clone on #434) 2021-04-25 15:44:45 -04:00
Quentin McGaw
b02a80abbd Feature: update PIA server information 2021-04-24 13:54:13 +00:00
Quentin McGaw
04313d3c3b Maintenance: devcontainer changes
- Bind mount for root only
- Support for Windows Hyperv bind mounts
- Run go mod tidy after go mod download
- Use :z flag for possibly shared bind mounts
- Bind mount zsh_history
- Bind mount docker config directory
2021-04-24 13:53:48 +00:00
Quentin McGaw
fb8279f8f0 Fix: remove pull-filter ignore ping-restart 2021-04-19 19:51:00 +00:00
Quentin McGaw
e0e56595c6 Fix: only run ip6tables if it is supported by the Kernel (#431)
- Fix #430
2021-04-19 14:35:29 -04:00
Quentin McGaw
44d8cf9d4e Replace Surfshark default cipher with aes 256 gcm 2021-04-19 18:00:58 +00:00
Quentin McGaw
282c1e53ec Clear firewall rules on shutdown, fix #276 2021-04-19 14:27:38 +00:00
Quentin McGaw
7ba98af1cc Feature/Bugfix: IPv6 blocking (#428)
- Feature/Bugfix: Block all IPv6 traffic with `ip6tables` by default
- Feature: Adapt existing firewall code to handle IPv4 and IPv6, depending on user inputs and environment
- Maintenance: improve error wrapping in the firewall package
2021-04-19 09:24:46 -04:00
Quentin McGaw
d3df5aaa52 Upgrade system and package versions
- Alpine from 3.12 to 3.13 and:
- Openvpn from 2.4.10 to 2.5.1
- Unbound from 1.10.1 to 1.13.0
- Iptables from 1.8.4 to 1.8.6
2021-04-19 00:31:46 +00:00
Quentin McGaw
1c83dcab5e Maintenance: upgrade golangci-lint to 1.39.0 2021-04-19 00:20:43 +00:00
Quentin McGaw
6208081788 Fix: PIA port forwarding (#427)
- Update PIA token URL
- Change base64 decoding to standard decoding
- Add unit tests
- Remove environment variable `GODEBUG=x509ignoreCN=0`
- Fixes #423 
- Fixes #292 
- Closes #264 
- Closes #293
2021-04-17 16:21:17 -04:00
Quentin McGaw
3795e92a82 Hotfix: lint error in Surfshark constants 2021-04-16 22:37:51 +00:00
Quentin McGaw
0636123e7a Feature: add more Surfshark servers
- Add servers missing from surfshark zip file
- Fixes #424 and re-add multihop servers
- Fix logic to try resolving old vpn servers for Surfshark
2021-04-16 22:31:09 +00:00
Michael Robbins
69f9461bcd Fix: restricting route listing to IPv4 only (#419) 2021-04-11 08:50:59 -04:00
Quentin McGaw
d1558a3472 Fix lint error from PR merge 2021-04-09 17:44:22 +00:00
Michael Robbins
8230596f98 Feature: uplift the 'localSubnet' concept to cover all local ethernet interfaces (#413) 2021-04-09 13:08:20 -04:00
Quentin McGaw
cc4117e054 Change PIA settings, refers to #265 2021-04-01 18:53:21 +00:00
Quentin McGaw
a0ddbc037f Update new provider issue template 2021-04-01 18:29:55 +00:00
Quentin McGaw
de82d4e616 Fix: use udp by default for custom openvpn config 2021-03-15 02:13:10 +00:00
Quentin McGaw
fa220f9e93 Feature: custom Openvpn configuration file, fixes #223 (#402) 2021-03-13 08:51:05 -05:00
Quentin McGaw
aca112fa42 CI: Build for all architectures in branches 2021-03-09 00:16:24 +00:00
Quentin McGaw
9f4077d35d Feature: FastestVPN support (#383) 2021-03-05 23:12:19 -05:00
Quentin McGaw
9509b855f1 Feature: PrivateVPN support (#393) 2021-03-05 22:58:57 -05:00
Quentin McGaw
be72f4a046 Feature: Hide My Ass VPN provider support (#401) 2021-03-05 22:45:54 -05:00
Quentin McGaw
8b36ce198f Maintenance: 8.8.8.8 as the cli updater DNS 2021-03-05 22:46:21 +00:00
Quentin McGaw
71de05dc68 Maintenance: updater DNS resolution more resilient 2021-03-05 22:46:14 +00:00
Quentin McGaw
83b5a9457a Maintenance: upgrade golangci-lint to 1.37.0 2021-03-03 01:16:05 +00:00
Quentin McGaw
0b7ada9fd9 Maintenance: use Go 1.16 to build binary 2021-03-03 01:15:14 +00:00
Quentin McGaw
92bcef0b1c Maintenance: unique choices from hardcoded servers 2021-02-26 13:21:55 +00:00
Quentin McGaw
a10c4056d0 Maintenance: simplify env comments in Dockerfile 2021-02-26 13:02:43 +00:00
Quentin McGaw
1fd3ee7149 Maintenance: sort alphabetically providers in code 2021-02-26 12:58:58 +00:00
dependabot[bot]
e3a157bfe1 Maintenance: bump golang/mock from 1.4.4 to 1.5.0 (#394) 2021-02-26 07:39:29 -05:00
Quentin McGaw
b446aa6590 Maintenance: use native HTTP client for updater 2021-02-26 00:42:55 +00:00
Quentin McGaw
c54ee71e1d Maintenance: new logging, shorter with less deps 2021-02-25 23:51:29 +00:00
Quentin McGaw
1748a2ae12 Fix: HTTP proxy password and log settings reading 2021-02-26 03:32:26 +00:00
Quentin McGaw
eff46aa97a Fix firewall settings parsing, fixes #392 2021-02-21 02:39:34 +00:00
Quentin McGaw
9fb186af75 Documentation: update issue templates 2021-02-20 22:29:33 +00:00
Quentin McGaw
f1b1001863 Torguard support (#387)
See discussion on #374
2021-02-17 20:36:30 -05:00
Quentin McGaw
c5af536299 Maintenance: deduplicate PIA servers by protocols 2021-02-16 13:06:58 +00:00
Quentin McGaw
b9b2f691a5 Fix: pia updater for TCP, fixes #388 2021-02-16 13:06:51 +00:00
fgeertsema
bdc8817672 Fix: HTTP proxy: return the response of a redirect, do not follow (#384)
Authored-by: Fernand Geertsema <fernand@web-iq.eu>
2021-02-15 08:40:51 -05:00
Quentin McGaw
a55acb2816 CI: Alpine s390x build removed (periodic crashes) 2021-02-14 18:59:27 +00:00
Quentin McGaw
d686c76db3 Fix: Privado SERVER_HOSTNAME selection 2021-02-14 16:40:48 +00:00
Quentin McGaw
30c1ae651e Documentation: new provider issue template 2021-02-14 16:31:31 +00:00
Quentin McGaw
adaad62fbd Feature: updater: no sleep for last DNS resolution 2021-02-12 21:27:26 +00:00
Quentin McGaw
fe5ec205fc Fix: updater uses plaintext DNS to avoid getting blocked by Unbound (#380) 2021-02-12 14:20:07 -05:00
Quentin McGaw
576400e0d9 Fix: Windscribe SERVER_HOSTNAME, fixes #379 2021-02-12 15:05:35 +00:00
Quentin McGaw
f08a03106f Feature: updater changes to have more VPN IP addresses (#364) 2021-02-11 08:40:25 -05:00
Quentin McGaw
f852b7789e Fix: surfshark: restore ping settings 2021-02-09 03:03:08 +00:00
Quentin McGaw
b0bd06bdc5 Feature: only log health when health state changes 2021-02-09 02:45:50 +00:00
Quentin McGaw
84787f0ea2 Fix: restore nordvpn ping settings, refers to #368 2021-02-09 02:30:39 +00:00
Quentin McGaw
f69b3dbbe6 Fix: Windscribe: TLS keys out of sync (#347)
* Add reneg-sec 0
* Add ncp-disable
2021-02-08 20:49:54 -05:00
Quentin McGaw
ec5ec6f02c Fix: defaults to run openvpn as root 2021-02-08 00:05:54 +00:00
Quentin McGaw
5d681e635b Fix: restore surfshark ping to 15s 2021-02-08 00:05:22 +00:00
Quentin McGaw
3deb65b529 Feature: log out country, region and city of IP 2021-02-08 00:01:14 +00:00
Quentin McGaw
3e527fee8b Fixes #370 (bug introduced) 2021-02-07 18:15:31 +00:00
Quentin McGaw
b1f1f94a76 Maintenance: remove some type aliases 2021-02-06 18:31:14 +00:00
Quentin McGaw
43e140e6cc Fix linting errors 2021-02-06 17:16:58 +00:00
Quentin McGaw
7ca9d445f1 Maintenance: package comments 2021-02-06 16:26:23 +00:00
Quentin McGaw
90aaf71270 Configuration package (#369) 2021-02-06 11:05:50 -05:00
Quentin McGaw
4f2570865c Add pull filter ignore ping restart
- Refers to OpenVPN reports error #368
2021-02-05 03:16:19 +00:00
Quentin McGaw
81556ec2e1 Maintenance: improve DNS settings log 2021-02-01 01:22:46 +00:00
Quentin McGaw
dd5a9c6067 Fix: empty connection for NordVPN and Windscribe 2021-01-31 18:45:58 +00:00
Quentin McGaw
982c50c756 Improve panic message for empty connection 2021-01-31 18:42:58 +00:00
Quentin McGaw
3c7dc9b9ad Feature: Private Internet Access custom port 2021-01-31 01:27:13 +00:00
Quentin McGaw
8f4354936c Fix: remove PureVPN route option (#339) 2021-01-30 19:54:40 -05:00
Quentin McGaw
e50941277f Fix: Alpine to 3.12 for 32 bit compatibility 2021-01-31 00:46:53 +00:00
Quentin McGaw
a72647b925 Feature: update servers information with more IP addresses (#365) 2021-01-30 14:08:14 -05:00
Quentin McGaw
e254849009 Fix: update mechanism for PIA and update servers
- Separate TCP and UDP servers as they have different CNs
- Update server hardcoded information constants
2021-01-30 18:22:15 +00:00
Quentin McGaw
5757f0e201 Fix: Only log subproc error when it's not nil 2021-01-30 18:07:12 +00:00
Quentin McGaw
75fdf7adab Fix: Pass down context to CLI commands 2021-01-30 17:29:27 +00:00
Quentin McGaw
702eafae4c Feature: update Surfshark servers information 2021-01-29 18:53:16 +00:00
Quentin McGaw
4abb8cd87f Add panic checks 2021-01-29 00:32:43 +00:00
Quentin McGaw
5194361f3b Fix public IP on restarts, refers to 359 2021-01-29 00:06:55 +00:00
Quentin McGaw
bc83b75634 (Fix) Lint errors 2021-01-26 01:09:09 +00:00
Quentin McGaw
b1ff95affa Maintenance: Fix exit race condition 2021-01-26 01:04:15 +00:00
Quentin McGaw
a243d48fb1 Maintenance: improve stream merging 2021-01-26 04:17:22 +00:00
Quentin McGaw
937d09f1c3 CI: Fix publish job CI 2021-01-23 16:58:46 +00:00
dependabot[bot]
3769092888 Bump github.com/stretchr/testify from 1.6.1 to 1.7.0 (#344) 2021-01-23 11:25:08 -05:00
Quentin McGaw
75281dee07 CI: remove risv64 as Alpine does not support it 2021-01-23 16:22:28 +00:00
Quentin McGaw
26a7c5eaef Feature: openvpn exits on TLS error 2021-01-22 13:36:56 +00:00
Quentin McGaw
1e8254fabf CI: Update golangci-lint to v1.35.2 2021-01-22 13:32:47 +00:00
Quentin McGaw
43b6509b43 Feature: upgrade to Alpine 3.13 2021-01-22 13:32:05 +00:00
Quentin McGaw
204c5b2446 Feature: add 10s ping with 60s exit ping for all 2021-01-22 13:30:06 +00:00
Quentin McGaw
7ab3347604 CI: Remove duplicate COPY in Dockerfile 2021-01-22 13:21:42 +00:00
Quentin McGaw
2f24a79d4d CI: Go mod tidy check 2021-01-22 13:20:54 +00:00
Quentin McGaw
819b1fe0f6 CI: Merge workflows in build.yml 2021-01-22 13:19:45 +00:00
Quentin McGaw
9f04b2d56c (fix) Update Nordvpn servers timestamp 2021-01-20 03:50:38 +00:00
Quentin McGaw
5eff5fac67 Update Nordvpn servers list 2021-01-20 03:44:55 +00:00
Quentin McGaw
d033d08c4d Maintenance: unit tests utils in provider package 2021-01-20 03:36:36 +00:00
Quentin McGaw
25644d061e Fix build (lint error) 2021-01-19 14:20:57 +00:00
Quentin McGaw
e7372f22cb Feature: OPENVPN_MSSFIX variable 2021-01-19 02:55:38 +00:00
Quentin McGaw
4530dd4fea Maintenance: OpenVPN BuildConf arity reduced 2021-01-19 02:42:16 +00:00
Quentin McGaw
072528af83 CI: Dockerfile fixes
- Pin xcputranslate version
- Update xcputranslate usage
- Set default BUILDPLATFORM to linux/amd64
2021-01-18 00:58:47 +00:00
Quentin McGaw
2c4d577f23 Maintenance: Update golibs and update params 2021-01-10 23:06:09 +00:00
Quentin McGaw
edd67e3473 Remove pull_request_target based workflow 2021-01-08 03:13:40 +00:00
Quentin McGaw
f389642dba Maintenance: Improve Go devcontainer settings 2021-01-08 02:27:48 +00:00
Quentin McGaw
b8690c7f83 CI: Change microbadger hook to gluetun 2021-01-08 02:25:41 +00:00
Quentin McGaw
06b809a492 Maintenance: Improve .dockerignore 2021-01-08 02:24:31 +00:00
Quentin McGaw
2ceda2faaa Documentation: Move sections to Wiki 2021-01-08 02:24:15 +00:00
Quentin McGaw
c7fc3afc21 Fix: DNS_KEEP_NAMESERVER behavior 2021-01-06 21:52:55 +00:00
Quentin McGaw
af57043afd Add docker and gomod to dependabot config 2021-01-06 04:23:09 +00:00
Quentin McGaw
4a85f3660c CI: Further reworking of workflows 2021-01-06 04:22:56 +00:00
dependabot[bot]
69713f34b2 CI: Bump crazy-max/ghaction-github-labeler from v1 to v3.1.1 (#335) 2021-01-05 22:41:01 -05:00
Quentin McGaw
55801597c6 Feature: Supports s390x and ppc64le 2021-01-06 01:31:04 +00:00
Quentin McGaw
ff3cc98d46 Maintenance: dependabot monitoring of Actions 2021-01-06 01:28:52 +00:00
Quentin McGaw
79489796ae CI: Add QEMU setup before buildx 2021-01-06 06:23:03 +00:00
Quentin McGaw
8e495494fd CI: Faster builds with buildx and xcputranslate 2021-01-06 06:10:42 +00:00
Quentin McGaw
1abb716bb6 Maintenance: Fix test data race 2021-01-06 06:09:19 +00:00
Quentin McGaw
3f012dd7a3 CI: rework Github build workflows and Dockerfile 2021-01-06 06:02:31 +00:00
Quentin McGaw
bf6bab7963 (Fix): Exit code 0 when expected (healthcheck fix) 2021-01-04 13:41:56 +00:00
Quentin McGaw
9db10f56ef Maintenance: remove vscode workspace directory 2021-01-04 03:50:31 +00:00
Quentin McGaw
3b91e351b7 Maintenance: using channels instead of wrap functions 2021-01-04 01:49:05 +00:00
Quentin McGaw
657937d272 Maintenance: Add waitgroup to collectStreamLines 2021-01-04 01:46:50 +00:00
Quentin McGaw
d294fbab15 Bug fix: Unbound log line processing, fix #333 2021-01-04 01:44:24 +00:00
Quentin McGaw
cfbf5624e1 Maintenance: rework main function 2021-01-04 01:40:07 +00:00
Quentin McGaw
c833e9a1a8 CI: Remove microbadger web hook for buildx branch and release 2021-01-03 04:03:13 +00:00
Quentin McGaw
f1b261163b Fix DNS_KEEP_NAMESERVER behavior 2021-01-03 03:52:07 +00:00
Quentin McGaw
4553240601 Feature: Improve DNS settings start log 2021-01-03 00:51:47 +00:00
Quentin McGaw
007a4536c7 Code: Access control subnets settings for Unbound 2021-01-03 00:36:03 +00:00
Quentin McGaw
31cf5d4a5a Fix #331 (DNS ready signaling fixed) 2021-01-02 23:51:05 +00:00
Quentin McGaw
3e3bd05c79 CI: Only test and lint on amd64 builds 2021-01-02 23:46:30 +00:00
Quentin McGaw
20deaf2950 Bug fix: DNS setup failure loop behavior 2021-01-02 20:39:43 +00:00
Quentin McGaw
680aef62ee (Bug fix): Fix Unbound
- Set binary filepath
- Set ca certs filepath
2021-01-02 20:39:24 +00:00
Quentin McGaw
f5eb4887a7 (Bug fix): DOT provider check 2021-01-02 19:24:01 +00:00
Quentin McGaw
dc3452c5b7 Feature: Log passed after healthcheck succeeds after a failure 2021-01-02 18:36:15 +00:00
Quentin McGaw
a67efd1ad1 Code maintenance: Using qdm/dns and qdm12/updated 2021-01-02 18:31:39 +00:00
Quentin McGaw
5dcbe79fa8 Move OS package to golibs 2021-01-02 01:57:00 +00:00
Quentin McGaw
574ac9a603 Maintenance: update buildx Github workflow to v3 2021-01-01 20:46:52 +00:00
Quentin McGaw
6871444728 Change: remove decomissioned SecureDNS option 2021-01-01 20:45:11 +00:00
Quentin McGaw
f4db7e3e53 Change: remove LibreDNS, it does'nt support DNSSEC 2021-01-01 20:44:01 +00:00
Quentin McGaw
da92b6bfb9 Bug fix: Privado server selection 2020-12-31 21:57:26 +00:00
Quentin McGaw
d713782fe1 Change: Use SERVER_HOSTNAME instead of HOSTNAME 2020-12-31 21:50:28 +00:00
Quentin McGaw
02cde5f50b Code maintenance: consistent proto type conversion 2020-12-31 21:39:34 +00:00
Quentin McGaw
c5a7a83d3a Bug fix: do not fail if servers.json is empty 2020-12-31 21:19:29 +00:00
Quentin McGaw
6655a1a5e6 Bug fix: Update hardcoded Purevpn server data
- Refers to #320
2020-12-31 21:07:49 +00:00
Quentin McGaw
b8cb181070 Bug fix: PureVPN updater from ZIP files
- Fix #317
- Refers to #320
2020-12-31 21:07:30 +00:00
Quentin McGaw
a56471fe73 Code maintenance: rework ovpn host extraction 2020-12-31 20:35:49 +00:00
Quentin McGaw
8c769812ae Documentation: minor readme improvements 2020-12-31 04:49:18 +00:00
Quentin McGaw
f7a842e4ee Documentation: readme sections moved to Wiki 2020-12-31 04:40:04 +00:00
Quentin McGaw
23c0334f68 Documentation: Add visitors count to readme 2020-12-31 03:49:01 +00:00
Quentin McGaw
e2ee7a0408 Documentation: minor issue template update 2020-12-31 03:00:15 +00:00
Quentin McGaw
8f862b3df7 Bug fix: Remove trail newline from secrets
- Fix #330
2020-12-31 02:03:51 +00:00
Quentin McGaw
ae1f91a997 Documentation: Update Docker image labels 2020-12-30 22:30:59 +00:00
Quentin McGaw
d4fb76770f Documentation: Moare badges and metadata 2020-12-30 22:29:18 +00:00
Quentin McGaw
ea28c791e6 Code maintenance: http proxy starts from Run func 2020-12-30 22:02:47 +00:00
Quentin McGaw
251555f859 Code maintenance: Shadowsocks loop refactor 2020-12-30 22:01:08 +00:00
Quentin McGaw
fa7bda7ee4 Code maintenance: remove unneeded defaultInterface in Shadowsocks 2020-12-30 21:43:45 +00:00
Quentin McGaw
f385c4203a Bug fix: truncate /etc/resolv.conf, fixing DNS
- Refers to #326
- Refers to #329
2020-12-30 20:48:41 +00:00
Quentin McGaw
1e4243dedb Bug fix: Stop DOT if disabled by new settings 2020-12-30 20:38:59 +00:00
Quentin McGaw
5f78ee7b79 Bug fix: missing mutex Unlock in DNS set settings 2020-12-30 20:37:14 +00:00
Quentin McGaw
c6eb5c1785 Bug fix: Plaintext DNS fix (#326, #329) 2020-12-30 20:36:19 +00:00
Quentin McGaw
11338b6382 Feature: faster healthcheck, fix #283 2020-12-30 19:34:11 +00:00
Quentin McGaw
6f3a074e00 Code maintenance: HTTP proxy loop reworked
- Blocking method calls on loop
- Restart proxy when settings change
- Detect server crash error and restart it
2020-12-30 18:44:46 +00:00
Quentin McGaw
e827079604 Code maintenance: updater loop waitgroup 2020-12-30 18:32:58 +00:00
Quentin McGaw
cf66db8d4b Bug fix: Stopping updater loop deadlock 2020-12-30 18:29:28 +00:00
Quentin McGaw
25acbf8501 Feature: Increasing backoff time for crashes
- Fix #247
2020-12-30 17:22:54 +00:00
Quentin McGaw
e4c7a887d2 Bug fix: healthcheck uses DOT via default resolver 2020-12-30 16:43:08 +00:00
Quentin McGaw
fb8a615660 Feature: Update PIA servers using v5 PIA API 2020-12-30 15:54:13 +00:00
Quentin McGaw
1d9d49f406 Bug fix: Privado settings log 2020-12-30 15:34:07 +00:00
Quentin McGaw
0069b59ffe Change: remove redundant dns over tls log 2020-12-30 15:29:40 +00:00
Quentin McGaw
d4ba1b1e09 Bug fix: larger timeout for healtcheck 2020-12-30 15:24:46 +00:00
Quentin McGaw
3a20b84f3a Documentation: readme changes
- Remove videos section (outdated)
- Add quick links section for help and support
- Simplify support section
2020-12-29 23:00:55 +00:00
Quentin McGaw
d52fc777ac Code maintenance: update dockerhub readme workflow 2020-12-29 22:46:44 +00:00
Quentin McGaw
5753a428d8 Documentation: announcement on newer image name 2020-12-29 22:46:18 +00:00
Quentin McGaw
85afef5775 Change: gluetun docker image name 2020-12-29 22:10:44 +00:00
Quentin McGaw
b4fc24995c Code maintenance: Microbadger hook uses continue-on-error 2020-12-29 21:35:09 +00:00
Quentin McGaw
5917bb10e4 Feature: Docker secrets, refers to #306 2020-12-29 20:47:56 +00:00
Quentin McGaw
258e150ebf Code maintenance: GetPassword signature changed 2020-12-29 20:06:24 +00:00
Quentin McGaw
96f2b2b617 Change: PASSWORD changed to OPENVPN_PASSWORD 2020-12-29 20:05:17 +00:00
Quentin McGaw
d556db079b Change: USER changed to OPENVPN_USER 2020-12-29 20:02:58 +00:00
Quentin McGaw
a811a82329 Change: Remove CLIENT_KEY variable 2020-12-29 19:54:58 +00:00
Quentin McGaw
d17a0dae1f Documentation: Missing PUID and PGID update 2020-12-29 19:46:41 +00:00
Quentin McGaw
ef40f2f91b Code maintenance: Use Unset() option for params 2020-12-29 18:29:21 +00:00
Quentin McGaw
a921f9848c Code maintenance: CLI interface abstraction 2020-12-29 18:24:03 +00:00
Quentin McGaw
95ba3261fd Code maintenance: lint bug fix for armv7 2020-12-29 18:16:29 +00:00
Quentin McGaw
fe81eb65c2 Bug fix: Program exit on Openvpn fatal error 2020-12-29 17:50:36 +00:00
Quentin McGaw
8428714cf5 Code maintenance: upgrade golangci-lint to 1.34.1 2020-12-29 17:50:12 +00:00
Quentin McGaw
bedf613cff Code maintenance: storage merging reworked 2020-12-29 17:49:38 +00:00
Quentin McGaw
e643ce5b99 Fix publicip and updater loops exit bugs 2020-12-29 16:44:55 +00:00
Quentin McGaw
cb64302294 Rename UID and GID to PUID and PGID 2020-12-29 16:44:35 +00:00
Quentin McGaw
8d5f2fec09 Code maintenance: use native Go HTTP client 2020-12-29 02:55:34 +00:00
Quentin McGaw
60e98235ca Code maintenance: Better deps injection in main.go 2020-12-29 01:21:54 +00:00
Quentin McGaw
f55fb4055f Code maintenance: OS user abstraction interface 2020-12-29 01:16:53 +00:00
Quentin McGaw
da4e410bb7 Upgrade direct dependencies 2020-12-29 01:06:08 +00:00
Quentin McGaw
cdd1f87437 Code maintenance: Remove unneeded ctrl.Finish() 2020-12-29 01:04:07 +00:00
Quentin McGaw
7058373916 Code maintenance: Unix abstraction interface
- Used for creating the tun device if it does not exist
- Mocks generated for testing
2020-12-29 01:02:47 +00:00
Quentin McGaw
8dd38fd182 Code maintenance: better JSON decoding for HTTP 2020-12-29 00:56:51 +00:00
Quentin McGaw
73479bab26 Code maintenance: OS package for file system
- OS custom internal package for file system interaction
- Remove fileManager external dependency
- Closer API to Go's native API on the OS
- Create directories at startup
- Better testability
- Move Unsetenv to os interface
2020-12-29 00:55:31 +00:00
Quentin McGaw
f5366c33bc Remove unneeded .Times(1) for unit tests mocks 2020-12-28 01:52:30 +00:00
Quentin McGaw
db886163c2 Public IP getter loop refactored 2020-12-28 01:51:55 +00:00
Quentin McGaw
91f5338db0 Fix updater loop bug 2020-12-28 01:50:13 +00:00
Quentin McGaw
82a02287ac Public IP endpoint with GET /ip fixing #319 2020-12-27 21:06:00 +00:00
Quentin McGaw
2dc674559e Re-use username for UID if it exists 2020-12-27 00:36:39 +00:00
Quentin McGaw
38e713fea2 Fix Block-outside-dns #316 2020-12-23 06:46:54 +00:00
Quentin McGaw
2cbb14c36c Fix Purevpn settings display, refers to #317 2020-12-22 14:08:12 +00:00
Quentin McGaw
610e88958e Upgrade golangci-lint to v1.33.0 2020-12-22 13:52:37 +00:00
Quentin McGaw
bb76477467 Fix #316 2020-12-22 13:49:49 +00:00
Quentin McGaw
433a799759 Fix environment variables table for Purevpn 2020-12-22 13:46:52 +00:00
Quentin McGaw
22965ccce3 Fix #315 2020-12-22 06:21:25 +00:00
Quentin McGaw
4257581f55 Loops and HTTP control server rework (#308)
- CRUD REST HTTP server
- `/v1` HTTP server prefix
- Retrocompatible with older routes (redirects to v1 or handles the requests directly)
- DNS, Updater and Openvpn refactored to have a REST-like state with new methods to change their states synchronously
- Openvpn, Unbound and Updater status, see #287
2020-12-19 20:10:34 -05:00
Quentin McGaw
d60d629105 Dev container documentation and cleanup 2020-12-08 06:24:46 +00:00
Quentin McGaw
3f721b1717 Simplify Github workflows triggers 2020-12-07 02:15:50 +00:00
Quentin McGaw
97049bfab4 Add 256x256 png logo for Unraid 2020-12-07 02:11:23 +00:00
Quentin McGaw
84944a87d3 HTTP proxy authentication fixes (#300)
- Only accepts HTTP 1.x protocols
- Only checks the credentials when the method is `CONNECT` or the request URL is absolute
- More logging on authorization failures
- Removes the authorization headers before forwarding the HTTP(s) requests
- Refers to #298
2020-12-01 22:29:31 -05:00
Quentin McGaw
fb62910b17 HTTP proxy 24 hours timeout, fix #303 2020-11-21 01:26:02 +00:00
Quentin McGaw
1cc0f5fee9 Fix #296 (Cyberghost implementation) (#297)
* Reads the client key from /gluetun/client.key
* Read the client certificate from /gluetun/client.crt
* Additional checks for client key and client certificate validity
* Fix client key file parsing if environment variable isn't present
2020-11-19 08:50:55 -05:00
Quentin McGaw
6896cf4258 Update PIA hardcoded servers information 2020-11-14 22:21:08 +00:00
Quentin McGaw
188d63c6b8 Fix #298 2020-11-13 01:14:05 +00:00
Quentin McGaw
cbc5d466f6 Fix Shadowsocks UDP logs 2020-11-13 00:42:14 +00:00
Quentin McGaw
aef14a9f6d Assimilate PIA v4 as PIA in code 2020-11-10 13:35:49 +00:00
Quentin McGaw
f48392064e Update issue templates 2020-11-10 01:29:47 +00:00
Quentin McGaw
994bdd0ca7 Update Gituhb labels 2020-11-10 01:16:12 +00:00
Quentin McGaw
40ed070f21 Filter Privado servers by hostnames only 2020-11-09 23:17:22 +00:00
Quentin McGaw
f1e4b9937b Privado support, fix #285 (#288) 2020-11-08 20:56:49 -05:00
Quentin McGaw
0423388b52 Fix build information setting at build time 2020-11-07 22:31:20 +00:00
Quentin McGaw
096a9c5fc0 Fix #289 2020-11-06 02:54:27 +00:00
Quentin McGaw
7518f74729 Refactor HTTP control server code 2020-11-05 22:26:53 +00:00
Quentin McGaw
854401a150 PureVPN servers json tag fix 2020-11-05 02:22:33 +00:00
Quentin McGaw
a7a7efe9c3 Remove PIA v3 servers support 2020-11-05 02:10:34 +00:00
Quentin McGaw
31883f9adb Windscribe API and more servers filter options, fixes #197 (#282)
- Use Windscribe API to fetch servers information
- More data on servers about region, city and hostname
- Add optional server filters with `REGION`, `CITY` and `HOSTNAME` csv environment variables
2020-11-04 20:38:35 -05:00
Quentin McGaw
3b04677f8f HTTP control server /version endpoint 2020-11-04 14:07:04 +00:00
Quentin McGaw
b5fb2b849a DOT listens on all interfaces, refers to #281 2020-11-04 03:14:27 +00:00
Quentin McGaw
0c9f74ffa4 HTTP proxy written in Go to replace Tinyproxy (#269) 2020-10-31 21:50:31 -04:00
Quentin McGaw
58da55da1e Retrocompatiblity with EXTRA_SUBNETS key 2020-10-29 23:32:15 +00:00
Quentin McGaw
db64dea664 Fix #273 (#277), adding FIREWALL_OUTBOUND_SUBNETS 2020-10-29 19:23:44 -04:00
Quentin McGaw
f7bff247aa Fix #275 2020-10-28 22:09:58 +00:00
Quentin McGaw
edc08c46d4 Health server runs on 127.0.0.1:9999, fix #272 2020-10-27 03:28:25 +00:00
Quentin McGaw
78d83145ba Increase http timeout to 30s for piav4
- Refers to #271
2020-10-26 21:53:18 +00:00
Quentin McGaw
0c81154f36 No firewall setup needed for Shadowsocks 2020-10-26 02:52:10 +00:00
Quentin McGaw
53fe08ea26 Fix Mullvad CSV city bug 2020-10-25 21:13:26 +00:00
Quentin McGaw
a6cb1a7052 Remove x/net Go dependency 2020-10-25 20:45:14 +00:00
Quentin McGaw
c64fe7e45d Routing: use 0.0.0.0/0 instead of nil 2020-10-25 20:41:09 +00:00
Quentin McGaw
a062135148 Add routing verbose option in code 2020-10-25 20:40:17 +00:00
Quentin McGaw
5ae7c15211 Unused shadowsocks code cleanup 2020-10-25 20:38:16 +00:00
Quentin McGaw
f29707fa9f Remove unneeded openvpn remote-random 2020-10-24 22:26:15 +00:00
Quentin McGaw
e97d1e4a9a Set default for DOT_CACHING in code 2020-10-24 22:24:20 +00:00
Quentin McGaw
ed4fcc17b3 Routing improvements (#268)
- Fixes #82 
- Remove `EXTRA_SUBNETS`
- Remove no longer needed iptables rules
- Reduce routing interface arity
- Routing setup is done in main.go instead of in the firewall
- Routing setup gets reverted at shutdown
2020-10-24 18:05:11 -04:00
Quentin McGaw
716eb14da1 Allow empty string for CSV variables with golibs 2020-10-24 19:09:54 +00:00
Quentin McGaw
f92489f99b Fix Nordvpn number empty string allowance 2020-10-24 18:45:44 +00:00
Quentin McGaw
ea3b3bc8a3 Netlink Go library to interact with IP routes (#267) 2020-10-22 18:55:28 -04:00
Quentin McGaw
a80cb8f9ba VSCode development container changes
- SSH directory bind mounted as read write
- Remove linters list from devcontainer, use workspace ones instead
2020-10-22 03:54:18 +00:00
Quentin McGaw
d4813ba21c Fix #265 and refers to #256
- Logs a message about auth failure for PIA v4 servers
2020-10-22 00:36:12 +00:00
Quentin McGaw
bf92008e45 Fix #263 2020-10-20 18:13:59 +00:00
Quentin McGaw
9c73faaaeb Add linters and fix lint issues 2020-10-20 02:45:28 +00:00
Quentin McGaw
f9bef8ecda Fix #102 2020-10-19 00:28:07 +00:00
Quentin McGaw
302adb26d7 Fix servers filtering for PIA 2020-10-18 23:44:16 +00:00
Quentin McGaw
af606463ea Multi options filters, fixes #231 (#262)
* OWNED environment variable for Mullvad
* CSV are now accepted for all servers filtering environment variables
2020-10-18 17:15:42 -04:00
Quentin McGaw
c932f48a95 Fixes #254 new variable FIREWALL_INPUT_PORTS (#260) 2020-10-18 09:22:28 -04:00
Quentin McGaw
84c1f46ae4 Upgrade dependencies
- Use of context for custom http client
- Remove unused nodeid for logger
- Upgrade shadowsocks dependency
2020-10-18 02:24:34 +00:00
Quentin McGaw
b27e637894 HTTP_CONTROL_SERVER_LOG variable, fixes #249 2020-10-17 22:21:20 +00:00
Quentin McGaw
4da9607b4d Do not log healthcheck HTTP requests 2020-10-17 22:17:08 +00:00
Quentin McGaw
8abc22977c Fix #261 add variable HTTP_CONTROL_SERVER_PORT 2020-10-17 22:07:15 +00:00
Quentin McGaw
6f4be72785 Using context for HTTP requests 2020-10-17 21:54:09 +00:00
Quentin McGaw
0d2ca377df PIA port forwarding final fixes (#259)
- Returns an error if the server does not support port forwarding
- TLS verification using the server common name obtained through the API
- Updated readme
- Fixes #236
2020-10-15 22:53:13 -04:00
Quentin McGaw
98f778c3bb Improve timing behavior of ticking in loops 2020-10-15 23:20:36 +00:00
Quentin McGaw
9b9ae69404 Repurpose OPENVPN_TARGET_IP for #229 2020-10-12 20:21:26 +00:00
Quentin McGaw
1c747a10c8 Fix CN data for PIA v4 servers 2020-10-12 19:34:36 +00:00
Quentin McGaw
c4354871f7 Single connection written to openvpn configuration (#258)
- From now only a single OpenVPN connection is written to the OpenVPN configuration file
- If multiple connections are matched given the user parameters (i.e. city, region), it is picked at pseudo random using the current time as the pseudo random seed.
- Not relying on Openvpn picking a random remote address, may refer to #229 
- Program is aware of which connection is to be used, in order to use its matching CN for port forwarding TLS verification with PIA v4 servers, see #236 
- Simplified firewall mechanisms
2020-10-12 15:29:58 -04:00
Quentin McGaw
9f6450502c Obtain PIA v4 server information from API (#257)
- Obtain CN for port forwarding https verification
- Obtain for each server if they support port forwarding
- Obtain for each server their IP address for openvpn UDP and openvpn TCP (one for each)
- Updater program updated to use API
- Hardcoded values updated for PIA v3 and v4 servers
- Clearer separation between pia v3 and v4
- Fixes #250
2020-10-12 13:57:45 -04:00
Quentin McGaw
ae7fc5fe96 Fix guard pattern for max parallel DNS requests 2020-10-12 17:35:46 +00:00
Quentin McGaw
ec157f102b PIA nextgen portforward (#242)
* Split provider/pia.go in piav3.go and piav4.go
* Change port forwarding signature
* Enable port forwarding parameter for PIA v4
* Fix VPN gateway IP obtention
* Setup HTTP client for TLS with custom cert
* Error message for regions not supporting pf
2020-10-12 10:55:08 -04:00
Quentin McGaw
fbecbc1c82 Fix updater guard pattern (#255) 2020-10-01 17:56:14 -04:00
Quentin McGaw
ecf76896a2 Mullvad: configurable ipv6 tunneling (#253)
- Disabled by default
- Wiki pages updated
- Readme updated
2020-09-26 09:33:24 -04:00
Quentin McGaw
ae876b93d7 Update readme Wiki links 2020-09-25 23:12:34 +00:00
Quentin McGaw
606f2cffce Fix Shadowsocks documentation, fixing #245 2020-09-18 19:56:53 +00:00
Quentin McGaw
564d9cbf90 Faster servers information updater (#248)
* Asynchronous repeatResolve
* Parallel cyberghost and PIA (v3) processing, with a 10 goroutines limit
* Add missing vyprvpn cli flag to updater
* Increase DNS repetitions to 5 in order to obtain more IP addresses
* Update old PIA IP addresses
* Add Surfshark servers by API (unused for now)
2020-09-18 15:52:28 -04:00
Quentin McGaw
c5b5ae9ca7 Add warning logs when openvpn cannot connect 2020-09-18 14:28:14 +00:00
Quentin McGaw
4e0bd46dd5 Updated Mullvad servers information 2020-09-18 14:16:17 +00:00
Quentin McGaw
f9b6e854b1 Fix surfshark updater and update server data 2020-09-13 00:41:31 +00:00
Quentin McGaw
1fc1776dbf Simplify main.go 2020-09-12 19:17:19 +00:00
Quentin McGaw
464c7074d0 Get public IP and version only when DNS is ready 2020-09-12 18:50:42 +00:00
Quentin McGaw
cb1520cb18 Write 200 http status codes to restart routes 2020-09-12 18:37:06 +00:00
Quentin McGaw
e0e450ca1c Revisit waitgroup (#241)
* Fix Add to waitgroup out of goroutines calling wg.Done()
* Pass waitgroup to other loop functions
2020-09-12 14:34:15 -04:00
Quentin McGaw
1c012e4c92 Minor readme changes
- Mullvad ipv6 startup error fix
- VPNSP variable in docker run example command
2020-09-12 18:06:52 +00:00
Quentin McGaw
78ce272bd0 Minor Dockerfile changes 2020-09-12 18:06:10 +00:00
Quentin McGaw
a19efbd923 Updater loop with period and http route (#240)
* Updater loop with period and http route
* Using DNS over TLS to update servers
* Better logging
* Remove goroutines for cyberghost updater
* Respects context for servers update (quite slow overall)
* Increase shutdown grace period to 5 seconds
* Update announcement
* Add log lines for each provider update start
2020-09-12 14:04:54 -04:00
Quentin McGaw
ee64cbf1fd Use IP addresses resolved for PIA old servers (#239)
- Fix #238
- Not using raw IPs from PIA as they seem invalid
- Update PIA old server information
2020-09-10 21:09:16 -04:00
Quentin McGaw
5b3cbb6906 Fix #235: DNS over TLS log messages 2020-09-09 21:44:50 +00:00
Quentin McGaw
443c7e36d7 Youtube videos added 2020-09-09 20:52:22 +00:00
Max Isom
22b389b6f8 Fix firewall reference to Wiki (#237) 2020-09-09 16:45:36 -04:00
Quentin McGaw
797fa33971 Servers updater (#232)
* Support for all VPN providers
* Update all VPN providers servers information
* Remove old tooling binaries
2020-09-05 12:57:16 -04:00
Quentin McGaw
9dcc00900e Healthcheck moved to HTTP control server 2020-08-31 01:57:45 +00:00
Quentin McGaw
7c102c0028 Fix #135 2020-08-30 14:48:57 +00:00
Quentin McGaw
aac5274eab Fix #224 2020-08-29 19:14:52 +00:00
Quentin McGaw
049bc5b226 Mullvad updater (#228)
* Add Mullvad to updater cli
* Update hardcoded servers for Mullvad
2020-08-29 13:19:34 -04:00
Quentin McGaw
d463e4cb69 New PIA servers support (#227)
* Adapt storage: SyncServers write to file option, export FlushToFile
* CLI built-in updater for old and new PIA servers
* Update hardcoded IP addresses for PIA old and new servers
* Add PIA old to allServers struct and update timestamps
* Adapt code to work with new and old PIA servers
* Remove PIA subdomains (unneeded) from resolver tool
2020-08-28 08:17:04 -04:00
Quentin McGaw
99ba56f574 Fix server filtering using merged server data 2020-08-27 23:06:28 +00:00
Quentin McGaw
93aaf1ab02 Fix exiting without fatalOnError function 2020-08-27 22:59:58 +00:00
Quentin McGaw
aa9693a84d Persistent server pools (#226)
* GetAllServers with version & timestamp tests
* Storage package to sync servers
* Use storage Sync to get and use servers
2020-08-25 19:38:50 -04:00
Quentin McGaw
6fc2b3dd21 Mullvad servers do not have a default port
- Refers to #218
- Checks for custom port value depending on protocol
- Remove default port from server constants
- Use 443 and 1194 ports respectively for tcp and udp
2020-08-24 01:53:24 +00:00
hyness
7e3e6f166a Add new PIA servers hostnames to resolver tool (#222)
Refers to #216
2020-08-20 19:20:59 -04:00
Quentin McGaw
c614a192a4 Shadowsocks in Go (#220), fixes #211 2020-08-20 19:19:54 -04:00
Quentin McGaw
b10a476622 Default status file base directory /tmp/gluetun 2020-08-18 01:08:24 +00:00
Quentin McGaw
15ddbdefef Bump versions and binary build changes
- Go version 1.15
- Golangci-lint 1.30
- Trim path of binary built
2020-08-17 20:39:49 -04: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
441 changed files with 137598 additions and 1085 deletions

View File

@@ -0,0 +1,5 @@
.dockerignore
devcontainer.json
docker-compose.yml
Dockerfile
README.md

1
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1 @@
FROM qmcgaw/godevcontainer

69
.devcontainer/README.md Normal file
View File

@@ -0,0 +1,69 @@
# Development container
Development container that can be used with VSCode.
It works on Linux, Windows and OSX.
## Requirements
- [VS code](https://code.visualstudio.com/download) installed
- [VS code remote containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) installed
- [Docker](https://www.docker.com/products/docker-desktop) installed and running
- If you don't use Linux or WSL 2, share your home directory `~/` and the directory of your project with Docker Desktop
- [Docker Compose](https://docs.docker.com/compose/install/) installed
- Ensure your host has the following and that they are accessible by Docker:
- `~/.ssh` directory
- `~/.gitconfig` file (can be empty)
## Setup
1. Open the command palette in Visual Studio Code (CTRL+SHIFT+P).
1. Select `Remote-Containers: Open Folder in Container...` and choose the project directory.
1. For Docker running on Windows HyperV, if you want to use SSH keys, bind mount them at `/tmp/.ssh` by changing the `volumes` section in the [docker-compose.yml](docker-compose.yml).
## Customization
### Customize the image
You can make changes to the [Dockerfile](Dockerfile) and then rebuild the image. For example, your Dockerfile could be:
```Dockerfile
FROM qmcgaw/godevcontainer
USER root
RUN apk add curl
USER vscode
```
Note that you may need to use `USER root` to build as root, and then change back to `USER vscode`.
To rebuild the image, either:
- With VSCode through the command palette, select `Remote-Containers: Rebuild and reopen in container`
- With a terminal, go to this directory and `docker-compose build`
### Customize VS code settings
You can customize **settings** and **extensions** in the [devcontainer.json](devcontainer.json) definition file.
### Entrypoint script
You can bind mount a shell script to `/home/vscode/.welcome.sh` to replace the [current welcome script](shell/.welcome.sh).
### Publish a port
To access a port from your host to your development container, publish a port in [docker-compose.yml](docker-compose.yml).
### Run other services
1. Modify [docker-compose.yml](docker-compose.yml) to launch other services at the same time as this development container, such as a test database:
```yml
database:
image: postgres
restart: always
environment:
POSTGRES_PASSWORD: password
```
1. In [devcontainer.json](devcontainer.json), change the line `"runServices": ["vscode"],` to `"runServices": ["vscode", "database"],`.
1. In the VS code command palette, rebuild the container.

View File

@@ -0,0 +1,81 @@
{
"name": "gluetun-dev",
"dockerComposeFile": [
"docker-compose.yml"
],
"service": "vscode",
"runServices": [
"vscode"
],
"shutdownAction": "stopCompose",
"postCreateCommand": "source ~/.windows.sh && go mod download && go mod tidy",
"workspaceFolder": "/workspace",
"extensions": [
"golang.go",
"eamodio.gitlens", // IDE Git information
"davidanson.vscode-markdownlint",
"ms-azuretools.vscode-docker", // Docker integration and linting
"shardulm94.trailing-spaces", // Show trailing spaces
"Gruntfuggly.todo-tree", // Highlights TODO comments
"bierner.emojisense", // Emoji sense for markdown
"stkb.rewrap", // rewrap comments after n characters on one line
"vscode-icons-team.vscode-icons", // Better file extension icons
"github.vscode-pull-request-github", // Github interaction
"redhat.vscode-yaml", // Kubernetes, Drone syntax highlighting
"bajdzis.vscode-database", // Supports connections to mysql or postgres, over SSL, socked
"IBM.output-colorizer", // Colorize your output/test logs
"mohsen1.prettify-json", // Prettify JSON data
],
"settings": {
"files.eol": "\n",
"remote.extensionKind": {
"ms-azuretools.vscode-docker": "workspace"
},
"editor.codeActionsOnSaveTimeout": 3000,
"go.useLanguageServer": true,
"[go]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true,
},
// Optional: Disable snippets, as they conflict with completion ranking.
"editor.snippetSuggestions": "none"
},
"[go.mod]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true,
},
},
"gopls": {
"usePlaceholders": false,
"staticcheck": true
},
"go.autocompleteUnimportedPackages": true,
"go.gotoSymbol.includeImports": true,
"go.gotoSymbol.includeGoroot": true,
"go.lintTool": "golangci-lint",
"go.buildOnSave": "workspace",
"go.lintOnSave": "workspace",
"go.vetOnSave": "workspace",
"editor.formatOnSave": true,
"go.toolsEnvVars": {
"GOFLAGS": "-tags=",
// "CGO_ENABLED": 1 // for the race detector
},
"gopls.env": {
"GOFLAGS": "-tags="
},
"go.testEnvVars": {
"": ""
},
"go.testFlags": [
"-v",
// "-race"
],
"go.testTimeout": "10s",
"go.coverOnSingleTest": true,
"go.coverOnSingleTestFile": true,
"go.coverOnTestPackage": true
}
}

View File

@@ -0,0 +1,30 @@
version: "3.7"
services:
vscode:
build: .
image: godevcontainer
volumes:
- ../:/workspace
# Docker socket to access Docker server
- /var/run/docker.sock:/var/run/docker.sock
# Docker configuration
- ~/.docker:/root/.docker:z
# SSH directory for Linux, OSX and WSL
- ~/.ssh:/root/.ssh:z
# For Windows without WSL, a copy will be made
# from /tmp/.ssh to ~/.ssh to fix permissions
#- ~/.ssh:/tmp/.ssh:ro
# Shell history persistence
- ~/.zsh_history:/root/.zsh_history:z
# Git config
- ~/.gitconfig:/root/.gitconfig:z
environment:
- TZ=
cap_add:
# For debugging with dlv
- SYS_PTRACE
security_opt:
# For debugging with dlv
- seccomp:unconfined
entrypoint: zsh -c "while sleep 1000; do :; done"

View File

@@ -1,4 +1,9 @@
.devcontainer
.git
readme
*.yml
*.md
.github
doc
docker-compose.yml
Dockerfile
LICENSE
README.md
title.svg

1
.github/CODEOWNERS vendored Normal file
View File

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

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

@@ -0,0 +1,18 @@
# 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 .` (you might need `export DOCKER_BUILDKIT=1`)
1. Commit your modifications
1. Push to your fork and [submit a pull request](https://github.com/qdm12/gluetun/compare)
## Resources
- [Gluetun guide on development](https://github.com/qdm12/gluetun/wiki/Development)
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)

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

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

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

@@ -0,0 +1,45 @@
---
name: Bug
about: Report a bug
title: 'Bug: FILL THIS TEXT!'
labels: ":bug: bug"
assignees: qdm12
---
<!---
⚠️ Answer the following or I'll insta-close your issue
-->
**Is this urgent?**: No
**Host OS** (approximate answer is fine too): Ubuntu 18
**CPU arch** or **device name**: amd64
**What VPN provider are you using**:
**What are you using to run your container?**: Docker Compose
**What is 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)
```
**What's the problem** 🤔
That feature doesn't work
**Share your logs... (careful to remove in example tokens)**
```log
PASTE YOUR LOGS
IN THERE
```
<!---
💡 You can highlight your code with https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlight
-->

View File

@@ -0,0 +1,17 @@
---
name: Feature request
about: Suggest a feature to add to this project
title: 'Feature request: FILL THIS TEXT!'
labels: ":bulb: feature request"
assignees: qdm12
---
**What's the feature?** 🧐
- Support this new feature because that and that
**Optional extra information** 🚀
- I tried `docker run something` and it doesn't work
- That [url](https://github.com/qdm12/gluetun) is interesting

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

@@ -0,0 +1,67 @@
---
name: Help
about: Ask for help
title: 'Help: FILL THIS TEXT!'
labels: ":pray: help wanted"
assignees:
---
<!---
⚠️ If this about a Docker configuration problem or another service:
Start a discussion at https://github.com/qdm12/gluetun/discussions/new
OR I WILL INSTA-CLOSE YOUR ISSUE.
-->
<!---
⚠️ Answer the following or I'll insta-close your issue
-->
**Is this urgent?**: No
**Host OS** (approximate answer is fine too): Ubuntu 18
**CPU arch** or **device name**: amd64
**What VPN provider are you using**:
**What is 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)
```
**What's the problem** 🤔
That feature doesn't work
**Share your logs... (careful to remove in example tokens)**
```log
PASTE YOUR LOGS
IN THERE
```
**What are you using to run your container?**: Docker Compose
<!---
💡 You can highlight your code with https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlight
-->
Please also share your configuration file:
```yml
your .yml
content
in here
```
or
```sh
# your docker
# run command
# in here
```

17
.github/ISSUE_TEMPLATE/provider.md vendored Normal file
View File

@@ -0,0 +1,17 @@
---
name: Support a VPN provider
about: Suggest a VPN provider to be supported
title: 'VPN provider support: NAME OF THE PROVIDER'
labels: ":bulb: New provider"
---
One of the following is required:
- Publicly accessible URL to a zip file containing the Openvpn configuration files
- Publicly accessible URL to a structured (JSON etc.) list of servers **and attach** an example Openvpn configuration file for both TCP and UDP
- Publicly accessible URL to the list of servers **and attach** an example Openvpn configuration file for both TCP and UDP
If the list of servers requires to login **or** is hidden behind an interactive configurator,
you can only use a custom Openvpn configuration file.
[The Wiki](https://github.com/qdm12/gluetun/wiki/Openvpn-file) describes how to do so.

15
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: docker
directory: /
schedule:
interval: "daily"
- package-ecosystem: gomod
directory: /
schedule:
interval: "daily"

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

@@ -0,0 +1,90 @@
- name: "Bug :bug:"
color: "b60205"
description: ""
- name: "Feature request :bulb:"
color: "0e8a16"
description: ""
- name: "Help wanted :pray:"
color: "4caf50"
description: ""
- name: "Documentation :memo:"
color: "c5def5"
description: ""
- name: "Needs more info :thinking:"
color: "795548"
description: ""
# VPN providers
- name: ":cloud: Cyberghost"
color: "cfe8d4"
description: ""
- name: ":cloud: HideMyAss"
color: "cfe8d4"
description: ""
- name: ":cloud: IPVanish"
color: "cfe8d4"
description: ""
- name: ":cloud: IVPN"
color: "cfe8d4"
description: ""
- name: ":cloud: FastestVPN"
color: "cfe8d4"
description: ""
- name: ":cloud: Mullvad"
color: "cfe8d4"
description: ""
- name: ":cloud: NordVPN"
color: "cfe8d4"
description: ""
- name: ":cloud: PIA"
color: "cfe8d4"
description: ""
- name: ":cloud: Privado"
color: "cfe8d4"
description: ""
- name: ":cloud: PrivateVPN"
color: "cfe8d4"
description: ""
- name: ":cloud: ProtonVPN"
color: "cfe8d4"
- name: ":cloud: PureVPN"
color: "cfe8d4"
description: ""
- name: ":cloud: Surfshark"
color: "cfe8d4"
description: ""
- name: ":cloud: Torguard"
color: "cfe8d4"
description: ""
- name: ":cloud: VPNUnlimited"
color: "cfe8d4"
description: ""
- name: ":cloud: Vyprvpn"
color: "cfe8d4"
description: ""
- name: ":cloud: Windscribe"
color: "cfe8d4"
description: ""
# Problem category
- name: "Openvpn"
color: "ffc7ea"
description: ""
- name: "Unbound (DNS over TLS)"
color: "ffc7ea"
description: ""
- name: "Firewall"
color: "ffc7ea"
description: ""
- name: "HTTP proxy"
color: "ffc7ea"
description: ""
- name: "Shadowsocks"
color: "ffc7ea"
description: ""
- name: "Healthcheck server"
color: "ffc7ea"
description: ""
- name: "Control server"
color: "ffc7ea"
description: ""

99
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,99 @@
name: CI
on:
push:
paths:
- .github/workflows/ci.yml
- cmd/**
- internal/**
- pkg/**
- .dockerignore
- .golangci.yml
- Dockerfile
- go.mod
- go.sum
jobs:
verify:
runs-on: ubuntu-latest
env:
DOCKER_BUILDKIT: "1"
steps:
- uses: actions/checkout@v2.3.4
- name: Linting
run: docker build --target lint .
- name: Go mod tidy check
run: docker build --target tidy .
- name: Build test image
run: docker build --target test -t test-container .
- name: Run tests in test container
run: |
touch coverage.txt
docker run --rm \
-v "$(pwd)/coverage.txt:/tmp/gobuild/coverage.txt" \
test-container
- name: Code security analysis
uses: snyk/actions/golang@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
- name: Build final image
run: docker build -t final-image .
- name: Image security analysis
uses: snyk/actions/docker@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
image: final-image
publish:
needs: [verify]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.3.4
- uses: docker/setup-qemu-action@v1
- uses: docker/setup-buildx-action@v1
- uses: docker/login-action@v1
with:
username: qmcgaw
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Set variables
id: vars
env:
EVENT_NAME: ${{ github.event_name }}
run: |
BRANCH=${GITHUB_REF#refs/heads/}
TAG=${GITHUB_REF#refs/tags/}
echo ::set-output name=commit::$(git rev-parse --short HEAD)
echo ::set-output name=created::$(date -u +%Y-%m-%dT%H:%M:%SZ)
if [ "$TAG" != "$GITHUB_REF" ]; then
echo ::set-output name=version::$TAG
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
elif [ "$BRANCH" = "master" ]; then
echo ::set-output name=version::latest
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
else
echo ::set-output name=version::$BRANCH
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
fi
- name: Build and push final image
uses: docker/build-push-action@v2.6.1
with:
platforms: ${{ steps.vars.outputs.platforms }}
build-args: |
CREATED=${{ steps.vars.outputs.created }}
COMMIT=${{ steps.vars.outputs.commit }}
VERSION=${{ steps.vars.outputs.version }}
tags: |
qmcgaw/gluetun:${{ steps.vars.outputs.version }}
qmcgaw/private-internet-access:${{ steps.vars.outputs.version }}
push: true

View File

@@ -0,0 +1,21 @@
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.3.4
- name: Docker Hub Description
uses: peter-evans/dockerhub-description@v2
with:
username: qmcgaw
password: ${{ secrets.DOCKERHUB_PASSWORD }}
repository: qmcgaw/gluetun
short-description: Lightweight Swiss-knife VPN client to connect to several VPN providers
readme-filepath: README.md

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

@@ -0,0 +1,15 @@
name: labels
on:
push:
branches: [master]
paths:
- .github/labels.yml
- .github/workflows/labels.yml
jobs:
labeler:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.3.4
- uses: crazy-max/ghaction-github-labeler@v3
with:
yaml-file: .github/labels.yml

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

@@ -0,0 +1,15 @@
name: Misspells
on:
pull_request:
branches: [master]
push:
branches: [master]
jobs:
misspell:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.3.4
- uses: reviewdog/action-misspell@v1
with:
locale: "US"
level: error

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
scratch.txt

88
.golangci.yml Normal file
View File

@@ -0,0 +1,88 @@
linters-settings:
maligned:
suggest-new: true
misspell:
locale: US
issues:
exclude-rules:
- path: _test\.go
linters:
- dupl
- maligned
- goerr113
- path: internal/server/
linters:
- dupl
- path: internal/configuration/
linters:
- dupl
- path: internal/constants/
linters:
- dupl
- text: "exported: exported var Err*"
linters:
- revive
- text: "mnd: Magic number: 0644*"
linters:
- gomnd
- text: "mnd: Magic number: 0400*"
linters:
- gomnd
linters:
disable-all: true
enable:
- asciicheck
- bodyclose
- deadcode
- dogsled
- dupl
- errcheck
- exhaustive
- exportloopref
- gci
- gochecknoglobals
- gochecknoinits
- gocognit
- goconst
- gocritic
- gocyclo
- godot
- goerr113
- goheader
- goimports
- gomnd
- goprintffuncname
- gosec
- gosimple
- govet
- importas
- ineffassign
- lll
- misspell
- nakedret
- nestif
- nilerr
- noctx
- nolintlint
- prealloc
- predeclared
- revive
- rowserrcheck
- sqlclosecheck
- staticcheck
- structcheck
- thelper
- tparallel
- typecheck
- unconvert
- unparam
- unused
- varcheck
- whitespace
run:
skip-dirs:
- .devcontainer
- .github
- doc

View File

@@ -1,8 +0,0 @@
{
"recommendations": [
"shardulm94.trailing-spaces",
"ms-azuretools.vscode-docker",
"davidanson.vscode-markdownlint",
"IBM.output-colorizer"
]
}

View File

@@ -1,81 +1,168 @@
ARG BASE_IMAGE=alpine
ARG ALPINE_VERSION=3.10
ARG ALPINE_VERSION=3.14
ARG GO_ALPINE_VERSION=3.13
ARG GO_VERSION=1.16
ARG XCPUTRANSLATE_VERSION=v0.6.0
ARG GOLANGCI_LINT_VERSION=v1.41.1
ARG BUILDPLATFORM=linux/amd64
FROM ${BASE_IMAGE}:${ALPINE_VERSION}
ARG BUILD_DATE
ARG VCS_REF
FROM --platform=${BUILDPLATFORM} qmcgaw/xcputranslate:${XCPUTRANSLATE_VERSION} AS xcputranslate
FROM --platform=${BUILDPLATFORM} qmcgaw/binpot:golangci-lint-${GOLANGCI_LINT_VERSION} AS golangci-lint
FROM --platform=${BUILDPLATFORM} golang:${GO_VERSION}-alpine${GO_ALPINE_VERSION} AS base
COPY --from=xcputranslate /xcputranslate /usr/local/bin/xcputranslate
RUN apk --update add git g++
ENV CGO_ENABLED=0
COPY --from=golangci-lint /bin /go/bin/golangci-lint
WORKDIR /tmp/gobuild
COPY go.mod go.sum ./
RUN go mod download
COPY cmd/ ./cmd/
COPY internal/ ./internal/
FROM --platform=${BUILDPLATFORM} base AS test
# Note on the go race detector:
# - we set CGO_ENABLED=1 to have it enabled
# - we installed g++ to support the race detector
ENV CGO_ENABLED=1
ENTRYPOINT go test -race -coverpkg=./... -coverprofile=coverage.txt -covermode=atomic ./...
FROM --platform=${BUILDPLATFORM} base AS lint
COPY .golangci.yml ./
RUN golangci-lint run --timeout=10m
FROM --platform=${BUILDPLATFORM} base AS tidy
RUN git init && \
git config user.email ci@localhost && \
git config user.name ci && \
git add -A && git commit -m ci && \
sed -i '/\/\/ indirect/d' go.mod && \
go mod tidy && \
git diff --exit-code -- go.mod
FROM --platform=${BUILDPLATFORM} base AS build
ARG TARGETPLATFORM
ARG VERSION=unknown
ARG CREATED="an unknown date"
ARG COMMIT=unknown
RUN GOARCH="$(xcputranslate translate -field arch -targetplatform ${TARGETPLATFORM})" \
GOARM="$(xcputranslate translate -field arm -targetplatform ${TARGETPLATFORM})" \
go build -trimpath -ldflags="-s -w \
-X 'main.version=$VERSION' \
-X 'main.created=$CREATED' \
-X 'main.commit=$COMMIT' \
" -o entrypoint cmd/gluetun/main.go
FROM alpine:${ALPINE_VERSION}
ARG VERSION=unknown
ARG CREATED="an unknown date"
ARG COMMIT=unknown
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 \
org.opencontainers.image.created=$CREATED \
org.opencontainers.image.version=$VERSION \
org.opencontainers.image.revision=$COMMIT \
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 swiss-knife like client for multiple VPN providers" \
org.opencontainers.image.description="VPN swiss-knife like client to tunnel to multiple VPN servers using OpenVPN, IPtables, DNS over TLS, Shadowsocks, an HTTP proxy and Alpine Linux"
ENV VPNSP=pia \
VERSION_INFORMATION=on \
PROTOCOL=udp \
REGION="CA Montreal" \
NONROOT=no \
DOT=on \
BLOCK_MALICIOUS=off \
BLOCK_NSA=off \
UNBLOCK= \
EXTRA_SUBNETS= \
OPENVPN_VERSION=2.5 \
OPENVPN_VERBOSITY=1 \
OPENVPN_FLAGS= \
OPENVPN_ROOT=yes \
OPENVPN_TARGET_IP= \
OPENVPN_IPV6=off \
OPENVPN_CUSTOM_CONFIG= \
TZ= \
PUID= \
PGID= \
PUBLICIP_FILE="/tmp/gluetun/ip" \
# VPN provider settings
OPENVPN_USER= \
OPENVPN_PASSWORD= \
USER_SECRETFILE=/run/secrets/openvpn_user \
PASSWORD_SECRETFILE=/run/secrets/openvpn_password \
REGION= \
COUNTRY= \
CITY= \
PORT= \
SERVER_HOSTNAME= \
# Mullvad only:
ISP= \
OWNED=no \
# Private Internet Access only:
PIA_ENCRYPTION=strong \
PORT_FORWARDING=off \
PORT_FORWARDING_STATUS_FILE="/forwarded_port" \
TINYPROXY=off \
TINYPROXY_LOG=Critical \
TINYPROXY_PORT=8888 \
TINYPROXY_USER= \
TINYPROXY_PASSWORD= \
PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \
# Cyberghost only:
CYBERGHOST_GROUP="Premium UDP Europe" \
OPENVPN_CLIENTCRT_SECRETFILE=/run/secrets/openvpn_clientcrt \
OPENVPN_CLIENTKEY_SECRETFILE=/run/secrets/openvpn_clientkey \
# Nordvpn only:
SERVER_NUMBER= \
# NordVPN and ProtonVPN only:
SERVER_NAME= \
# ProtonVPN only:
FREE_ONLY= \
# Openvpn
OPENVPN_CIPHER= \
OPENVPN_AUTH= \
# Health
HEALTH_OPENVPN_DURATION_INITIAL=6s \
HEALTH_OPENVPN_DURATION_ADDITION=5s \
HEALTH_SERVER_ADDRESS=127.0.0.1:9999 \
# 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:7f00:1/104,::ffff:a00:0/104,::ffff:a9fe:0/112,::ffff:ac10:0/108,::ffff:c0a8:0/112 \
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 \
FIREWALL_VPN_INPUT_PORTS= \
FIREWALL_INPUT_PORTS= \
FIREWALL_OUTBOUND_SUBNETS= \
FIREWALL_DEBUG=off \
# HTTP proxy
HTTPPROXY= \
HTTPPROXY_LOG=off \
HTTPPROXY_PORT=8888 \
HTTPPROXY_USER= \
HTTPPROXY_PASSWORD= \
HTTPPROXY_USER_SECRETFILE=/run/secrets/httpproxy_user \
HTTPPROXY_PASSWORD_SECRETFILE=/run/secrets/httpproxy_password \
# Shadowsocks
SHADOWSOCKS=off \
SHADOWSOCKS_LOG=on \
SHADOWSOCKS_LOG=off \
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
SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password \
SHADOWSOCKS_METHOD=chacha20-ietf-poly1305 \
UPDATER_PERIOD=0
ENTRYPOINT ["/entrypoint"]
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /entrypoint healthcheck
ARG TARGETPLATFORM
RUN apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.12/main" openvpn==2.4.11-r0 && \
if [ "${TARGETPLATFORM}" != "linux/ppc64le" ]; then apk add --no-cache --update apk-tools==2.12.6-r0; fi && \
mv /usr/sbin/openvpn /usr/sbin/openvpn2.4 && \
apk del openvpn && \
apk add --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \
rm -rf /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-* /etc/openvpn/*.sh /usr/lib/openvpn/plugins/openvpn-plugin-down-root.so && \
deluser openvpn && \
deluser unbound && \
mkdir /gluetun
COPY --from=build /tmp/gobuild/entrypoint /entrypoint

444
README.md
View File

@@ -1,324 +1,120 @@
# 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 Cyberghost, FastestVPN,
HideMyAss, IPVanish, IVPN, Mullvad, NordVPN, Privado, Private Internet Access, PrivateVPN,
ProtonVPN, PureVPN, Surfshark, TorGuard, VPNUnlimited, VyprVPN and Windscribe VPN servers
using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
**ANNOUNCEMENT**:
![Title image](https://raw.githubusercontent.com/qdm12/gluetun/master/title.svg)
[![Build status](https://github.com/qdm12/gluetun/actions/workflows/ci.yml/badge.svg)](https://github.com/qdm12/gluetun/actions/workflows/ci.yml)
[![Docker pulls qmcgaw/gluetun](https://img.shields.io/docker/pulls/qmcgaw/gluetun.svg)](https://hub.docker.com/r/qmcgaw/gluetun)
[![Docker pulls qmcgaw/private-internet-access](https://img.shields.io/docker/pulls/qmcgaw/private-internet-access.svg)](https://hub.docker.com/r/qmcgaw/gluetun)
[![Docker stars qmcgaw/gluetun](https://img.shields.io/docker/stars/qmcgaw/gluetun.svg)](https://hub.docker.com/r/qmcgaw/gluetun)
[![Docker stars qmcgaw/private-internet-access](https://img.shields.io/docker/stars/qmcgaw/private-internet-access.svg)](https://hub.docker.com/r/qmcgaw/gluetun)
![Last release](https://img.shields.io/github/release/qdm12/gluetun?label=Last%20release)
![Last Docker tag](https://img.shields.io/docker/v/qmcgaw/gluetun?sort=semver&label=Last%20Docker%20tag)
[![Last release size](https://img.shields.io/docker/image-size/qmcgaw/gluetun?sort=semver&label=Last%20released%20image)](https://hub.docker.com/r/qmcgaw/gluetun/tags?page=1&ordering=last_updated)
![GitHub last release date](https://img.shields.io/github/release-date/qdm12/gluetun?label=Last%20release%20date)
![Commits since release](https://img.shields.io/github/commits-since/qdm12/gluetun/latest?sort=semver)
[![Latest size](https://img.shields.io/docker/image-size/qmcgaw/gluetun/latest?label=Latest%20image)](https://hub.docker.com/r/qmcgaw/gluetun/tags)
[![GitHub last commit](https://img.shields.io/github/last-commit/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/commits/master)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/graphs/contributors)
[![GitHub closed PRs](https://img.shields.io/github/issues-pr-closed/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/pulls?q=is%3Apr+is%3Aclosed)
[![GitHub issues](https://img.shields.io/github/issues/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/issues)
[![GitHub closed issues](https://img.shields.io/github/issues-closed/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/issues?q=is%3Aissue+is%3Aclosed)
[![Lines of code](https://img.shields.io/tokei/lines/github/qdm12/gluetun)](https://github.com/qdm12/gluetun)
![Code size](https://img.shields.io/github/languages/code-size/qdm12/gluetun)
![GitHub repo size](https://img.shields.io/github/repo-size/qdm12/gluetun)
![Go version](https://img.shields.io/github/go-mod/go-version/qdm12/gluetun)
![Visitors count](https://visitor-badge.laobi.icu/badge?page_id=gluetun.readme)
## Quick links
- Problem or suggestion?
- [Start a discussion](https://github.com/qdm12/gluetun/discussions)
- [Create an issue](https://github.com/qdm12/gluetun/issues)
- [Check the Wiki](https://github.com/qdm12/gluetun/wiki)
- [Join the Slack channel](https://join.slack.com/t/qdm12/shared_invite/enQtOTE0NjcxNTM1ODc5LTYyZmVlOTM3MGI4ZWU0YmJkMjUxNmQ4ODQ2OTAwYzMxMTlhY2Q1MWQyOWUyNjc2ODliNjFjMDUxNWNmNzk5MDk)
- Happy?
- Sponsor me on [github.com/sponsors/qdm12](https://github.com/sponsors/qdm12)
- Donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw)
- Drop me [an email](mailto:quentin.mcgaw@gmail.com)
- Video:
[![Video Gif](https://i.imgur.com/CetWunc.gif)](https://youtu.be/0F6I03LQcI4)
## Features
- Based on Alpine 3.13 for a small Docker image of 54MB
- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** 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 Shadowsocks proxy (protocol based on SOCKS5 with an encryption layer, tunnels TCP+UDP)
- Built in HTTP proxy (tunnels HTTP and HTTPS through TCP)
- [Connect other containers to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
- [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even ppc64le 🎆
- 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. Ensure your `tun` kernel module is setup:
```sh
sudo modprobe tun
# or, if you don't have modprobe, with
sudo insmod /lib/modules/tun.ko
```
1. Extra steps:
- [For Synology users](https://github.com/qdm12/gluetun/wiki/Synology-setup)
- [For 32 bit Operating systems (**Rasberry Pis**)](https://github.com/qdm12/gluetun/wiki/32-bit-setup)
1. Launch the container with:
```bash
docker run -d --name gluetun --cap-add=NET_ADMIN \
-e VPNSP="private internet access" -e REGION="CA Montreal" \
-e OPENVPN_USER=js89ds7 -e OPENVPN_PASSWORD=8fd9s239G \
-v /yourpath:/gluetun \
qmcgaw/gluetun
```
or use [docker-compose.yml](https://github.com/qdm12/gluetun/blob/master/docker-compose.yml) with:
```bash
docker-compose up -d
```
You should probably check the many [environment variables](https://github.com/qdm12/gluetun/wiki/Environment-variables) available to adapt the container to your needs.
## Further setup
The following points are all optional but should give you insights on all the possibilities with this container.
- [Test your setup](https://github.com/qdm12/gluetun/wiki/Test-your-setup)
- [How to connect other containers and devices to Gluetun](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
- [VPN server side port forwarding](https://github.com/qdm12/gluetun/wiki/Port-forwarding)
- [HTTP control server](https://github.com/qdm12/gluetun/wiki/HTTP-Control-server) to automate things, restart Openvpn etc.
- Update the image with `docker pull qmcgaw/gluetun:latest`. See this [Wiki document](https://github.com/qdm12/gluetun/wiki/Docker-image-tags) for Docker tags available.
- Use [Docker secrets](https://github.com/qdm12/gluetun/wiki/Docker-secrets) to read your credentials instead of environment variables
## License
[![MIT](https://img.shields.io/github/license/qdm12/gluetun)](https://github.com/qdm12/gluetun/master/LICENSE)

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

@@ -0,0 +1,517 @@
package main
import (
"context"
"errors"
"fmt"
"net"
"net/http"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"time"
"github.com/qdm12/dns/pkg/unbound"
"github.com/qdm12/gluetun/internal/alpine"
"github.com/qdm12/gluetun/internal/cli"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/dns"
"github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/healthcheck"
"github.com/qdm12/gluetun/internal/httpproxy"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/openvpn"
"github.com/qdm12/gluetun/internal/publicip"
"github.com/qdm12/gluetun/internal/routing"
"github.com/qdm12/gluetun/internal/server"
"github.com/qdm12/gluetun/internal/shadowsocks"
"github.com/qdm12/gluetun/internal/storage"
"github.com/qdm12/gluetun/internal/unix"
"github.com/qdm12/gluetun/internal/updater"
versionpkg "github.com/qdm12/gluetun/internal/version"
"github.com/qdm12/golibs/command"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params"
"github.com/qdm12/goshutdown"
"github.com/qdm12/gosplash"
"github.com/qdm12/updated/pkg/dnscrypto"
)
//nolint:gochecknoglobals
var (
version = "unknown"
commit = "unknown"
created = "an unknown date"
)
var (
errSetupRouting = errors.New("cannot setup routing")
errCreateUser = errors.New("cannot create user")
)
func main() {
buildInfo := models.BuildInformation{
Version: version,
Commit: commit,
Created: created,
}
ctx := context.Background()
ctx, stop := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
ctx, cancel := context.WithCancel(ctx)
logger := logging.NewParent(logging.Settings{
Level: logging.LevelInfo,
})
args := os.Args
unix := unix.New()
cli := cli.New()
env := params.NewEnv()
cmder := command.NewCmder()
errorCh := make(chan error)
go func() {
errorCh <- _main(ctx, buildInfo, args, logger, env, unix, cmder, cli)
}()
select {
case <-ctx.Done():
stop()
logger.Warn("Caught OS signal, shutting down")
case err := <-errorCh:
stop()
close(errorCh)
if err == nil { // expected exit such as healthcheck
os.Exit(0)
}
logger.Error(err.Error())
cancel()
}
const shutdownGracePeriod = 5 * time.Second
timer := time.NewTimer(shutdownGracePeriod)
select {
case <-errorCh:
if !timer.Stop() {
<-timer.C
}
logger.Info("Shutdown successful")
case <-timer.C:
logger.Warn("Shutdown timed out")
}
os.Exit(1)
}
var (
errCommandUnknown = errors.New("command is unknown")
)
//nolint:gocognit,gocyclo
func _main(ctx context.Context, buildInfo models.BuildInformation,
args []string, logger logging.ParentLogger, env params.Env,
unix unix.Unix, cmder command.RunStarter, cli cli.CLIer) error {
if len(args) > 1 { // cli operation
switch args[1] {
case "healthcheck":
return cli.HealthCheck(ctx, env, logger)
case "clientkey":
return cli.ClientKey(args[2:])
case "openvpnconfig":
return cli.OpenvpnConfig(logger)
case "update":
return cli.Update(ctx, args[2:], logger)
default:
return fmt.Errorf("%w: %s", errCommandUnknown, args[1])
}
}
const clientTimeout = 15 * time.Second
httpClient := &http.Client{Timeout: clientTimeout}
// Create configurators
alpineConf := alpine.New()
ovpnConf := openvpn.NewConfigurator(
logger.NewChild(logging.Settings{Prefix: "openvpn configurator: "}),
unix, cmder)
dnsCrypto := dnscrypto.New(httpClient, "", "")
const cacertsPath = "/etc/ssl/certs/ca-certificates.crt"
dnsConf := unbound.NewConfigurator(nil, cmder, dnsCrypto,
"/etc/unbound", "/usr/sbin/unbound", cacertsPath)
announcementExp, err := time.Parse(time.RFC3339, "2021-07-22T00:00:00Z")
if err != nil {
return err
}
splashSettings := gosplash.Settings{
User: "qdm12",
Repository: "gluetun",
Emails: []string{"quentin.mcgaw@gmail.com"},
Version: buildInfo.Version,
Commit: buildInfo.Commit,
BuildDate: buildInfo.Created,
Announcement: "",
AnnounceExp: announcementExp,
// Sponsor information
PaypalUser: "qmcgaw",
GithubSponsor: "qdm12",
}
for _, line := range gosplash.MakeLines(splashSettings) {
fmt.Println(line)
}
err = printVersions(ctx, logger, []printVersionElement{
{name: "Alpine", getVersion: alpineConf.Version},
{name: "OpenVPN 2.4", getVersion: ovpnConf.Version24},
{name: "OpenVPN 2.5", getVersion: ovpnConf.Version25},
{name: "Unbound", getVersion: dnsConf.Version},
{name: "IPtables", getVersion: func(ctx context.Context) (version string, err error) {
return firewall.Version(ctx, cmder)
}},
})
if err != nil {
return err
}
var allSettings configuration.Settings
err = allSettings.Read(env,
logger.NewChild(logging.Settings{Prefix: "configuration: "}))
if err != nil {
return err
}
logger.Info(allSettings.String())
if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil {
return err
}
if err := os.MkdirAll("/gluetun", 0644); err != nil {
return err
}
// TODO run this in a loop or in openvpn to reload from file without restarting
storage := storage.New(
logger.NewChild(logging.Settings{Prefix: "storage: "}),
constants.ServersData)
allServers, err := storage.SyncServers(constants.GetAllServers())
if err != nil {
return err
}
// Should never change
puid, pgid := allSettings.System.PUID, allSettings.System.PGID
const defaultUsername = "nonrootuser"
nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid)
if err != nil {
return fmt.Errorf("%w: %s", errCreateUser, err)
}
if nonRootUsername != defaultUsername {
logger.Info("using existing username " + nonRootUsername + " corresponding to user id " + fmt.Sprint(puid))
}
// set it for Unbound
// TODO remove this when migrating to qdm12/dns v2
allSettings.DNS.Unbound.Username = nonRootUsername
if err := os.Chown("/etc/unbound", puid, pgid); err != nil {
return err
}
firewallLogLevel := logging.LevelInfo
if allSettings.Firewall.Debug {
firewallLogLevel = logging.LevelDebug
}
routingLogger := logger.NewChild(logging.Settings{
Prefix: "routing: ",
Level: firewallLogLevel,
})
routingConf := routing.NewRouting(routingLogger)
defaultInterface, defaultGateway, err := routingConf.DefaultRoute()
if err != nil {
return err
}
localNetworks, err := routingConf.LocalNetworks()
if err != nil {
return err
}
defaultIP, err := routingConf.DefaultIP()
if err != nil {
return err
}
firewallLogger := logger.NewChild(logging.Settings{
Prefix: "firewall: ",
Level: firewallLogLevel,
})
firewallConf := firewall.NewConfig(firewallLogger, cmder,
defaultInterface, defaultGateway, localNetworks, defaultIP)
if err := routingConf.Setup(); err != nil {
if strings.Contains(err.Error(), "operation not permitted") {
logger.Warn("💡 Tip: Are you passing NET_ADMIN capability to gluetun?")
}
return fmt.Errorf("%w: %s", errSetupRouting, err)
}
defer func() {
logger.Info("routing cleanup...")
if err := routingConf.TearDown(); err != nil {
logger.Error("cannot teardown routing: " + err.Error())
}
}()
if err := firewallConf.SetOutboundSubnets(ctx, allSettings.Firewall.OutboundSubnets); err != nil {
return err
}
if err := routingConf.SetOutboundRoutes(allSettings.Firewall.OutboundSubnets); err != nil {
return err
}
if err := ovpnConf.CheckTUN(); err != nil {
logger.Warn(err.Error())
err = ovpnConf.CreateTUN()
if err != nil {
return err
}
}
tunnelReadyCh := make(chan struct{})
defer close(tunnelReadyCh)
if allSettings.Firewall.Enabled {
err := firewallConf.SetEnabled(ctx, true) // disabled by default
if err != nil {
return err
}
}
for _, vpnPort := range allSettings.Firewall.VPNInputPorts {
err = firewallConf.SetAllowedPort(ctx, vpnPort, string(constants.TUN))
if err != nil {
return err
}
}
for _, port := range allSettings.Firewall.InputPorts {
err = firewallConf.SetAllowedPort(ctx, port, defaultInterface)
if err != nil {
return err
}
} // TODO move inside firewall?
// Shutdown settings
const defaultShutdownTimeout = 400 * time.Millisecond
defaultShutdownOnSuccess := func(goRoutineName string) {
logger.Info(goRoutineName + ": terminated ✔️")
}
defaultShutdownOnFailure := func(goRoutineName string, err error) {
logger.Warn(goRoutineName + ": " + err.Error() + " ⚠️")
}
defaultGoRoutineSettings := goshutdown.GoRoutineSettings{Timeout: defaultShutdownTimeout}
defaultGroupSettings := goshutdown.GroupSettings{
Timeout: defaultShutdownTimeout,
OnSuccess: defaultShutdownOnSuccess,
}
controlGroupHandler := goshutdown.NewGroupHandler("control", defaultGroupSettings)
tickersGroupHandler := goshutdown.NewGroupHandler("tickers", defaultGroupSettings)
otherGroupHandler := goshutdown.NewGroupHandler("other", defaultGroupSettings)
openvpnLooper := openvpn.NewLoop(allSettings.OpenVPN, nonRootUsername, puid, pgid, allServers,
ovpnConf, firewallConf, logger, httpClient, tunnelReadyCh)
openvpnHandler, openvpnCtx, openvpnDone := goshutdown.NewGoRoutineHandler(
"openvpn", goshutdown.GoRoutineSettings{Timeout: time.Second})
// wait for restartOpenvpn
go openvpnLooper.Run(openvpnCtx, openvpnDone)
updaterLooper := updater.NewLooper(allSettings.Updater,
allServers, storage, openvpnLooper.SetServers, httpClient,
logger.NewChild(logging.Settings{Prefix: "updater: "}))
updaterHandler, updaterCtx, updaterDone := goshutdown.NewGoRoutineHandler(
"updater", defaultGoRoutineSettings)
// wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker
go updaterLooper.Run(updaterCtx, updaterDone)
tickersGroupHandler.Add(updaterHandler)
unboundLogger := logger.NewChild(logging.Settings{Prefix: "dns over tls: "})
unboundLooper := dns.NewLoop(dnsConf, allSettings.DNS, httpClient,
unboundLogger)
dnsHandler, dnsCtx, dnsDone := goshutdown.NewGoRoutineHandler(
"unbound", defaultGoRoutineSettings)
// wait for unboundLooper.Restart or its ticker launched with RunRestartTicker
go unboundLooper.Run(dnsCtx, dnsDone)
otherGroupHandler.Add(dnsHandler)
publicIPLooper := publicip.NewLoop(httpClient,
logger.NewChild(logging.Settings{Prefix: "ip getter: "}),
allSettings.PublicIP, puid, pgid)
pubIPHandler, pubIPCtx, pubIPDone := goshutdown.NewGoRoutineHandler(
"public IP", defaultGoRoutineSettings)
go publicIPLooper.Run(pubIPCtx, pubIPDone)
otherGroupHandler.Add(pubIPHandler)
pubIPTickerHandler, pubIPTickerCtx, pubIPTickerDone := goshutdown.NewGoRoutineHandler(
"public IP", defaultGoRoutineSettings)
go publicIPLooper.RunRestartTicker(pubIPTickerCtx, pubIPTickerDone)
tickersGroupHandler.Add(pubIPTickerHandler)
httpProxyLooper := httpproxy.NewLoop(
logger.NewChild(logging.Settings{Prefix: "http proxy: "}),
allSettings.HTTPProxy)
httpProxyHandler, httpProxyCtx, httpProxyDone := goshutdown.NewGoRoutineHandler(
"http proxy", defaultGoRoutineSettings)
go httpProxyLooper.Run(httpProxyCtx, httpProxyDone)
otherGroupHandler.Add(httpProxyHandler)
shadowsocksLooper := shadowsocks.NewLooper(allSettings.ShadowSocks,
logger.NewChild(logging.Settings{Prefix: "shadowsocks: "}))
shadowsocksHandler, shadowsocksCtx, shadowsocksDone := goshutdown.NewGoRoutineHandler(
"shadowsocks proxy", defaultGoRoutineSettings)
go shadowsocksLooper.Run(shadowsocksCtx, shadowsocksDone)
otherGroupHandler.Add(shadowsocksHandler)
eventsRoutingHandler, eventsRoutingCtx, eventsRoutingDone := goshutdown.NewGoRoutineHandler(
"events routing", defaultGoRoutineSettings)
go routeReadyEvents(eventsRoutingCtx, eventsRoutingDone, buildInfo, tunnelReadyCh,
unboundLooper, updaterLooper, publicIPLooper, routingConf, logger, httpClient,
allSettings.VersionInformation, allSettings.OpenVPN.Provider.PortForwarding.Enabled, openvpnLooper.PortForward,
)
controlGroupHandler.Add(eventsRoutingHandler)
controlServerAddress := ":" + strconv.Itoa(int(allSettings.ControlServer.Port))
controlServerLogging := allSettings.ControlServer.Log
httpServerHandler, httpServerCtx, httpServerDone := goshutdown.NewGoRoutineHandler(
"http server", defaultGoRoutineSettings)
httpServer := server.New(httpServerCtx, controlServerAddress, controlServerLogging,
logger.NewChild(logging.Settings{Prefix: "http server: "}),
buildInfo, openvpnLooper, unboundLooper, updaterLooper, publicIPLooper)
go httpServer.Run(httpServerCtx, httpServerDone)
controlGroupHandler.Add(httpServerHandler)
healthLogger := logger.NewChild(logging.Settings{Prefix: "healthcheck: "})
healthcheckServer := healthcheck.NewServer(allSettings.Health, healthLogger, openvpnLooper)
healthServerHandler, healthServerCtx, healthServerDone := goshutdown.NewGoRoutineHandler(
"HTTP health server", defaultGoRoutineSettings)
go healthcheckServer.Run(healthServerCtx, healthServerDone)
const orderShutdownTimeout = 3 * time.Second
orderSettings := goshutdown.OrderSettings{
Timeout: orderShutdownTimeout,
OnFailure: defaultShutdownOnFailure,
OnSuccess: defaultShutdownOnSuccess,
}
orderHandler := goshutdown.NewOrder("gluetun", orderSettings)
orderHandler.Append(controlGroupHandler, tickersGroupHandler, healthServerHandler,
openvpnHandler, otherGroupHandler)
// Start openvpn for the first time in a blocking call
// until openvpn is launched
_, _ = openvpnLooper.ApplyStatus(ctx, constants.Running) // TODO option to disable with variable
<-ctx.Done()
if allSettings.OpenVPN.Provider.PortForwarding.Enabled {
logger.Info("Clearing forwarded port status file " + allSettings.OpenVPN.Provider.PortForwarding.Filepath)
if err := os.Remove(allSettings.OpenVPN.Provider.PortForwarding.Filepath); err != nil {
logger.Error(err.Error())
}
}
return orderHandler.Shutdown(context.Background())
}
type printVersionElement struct {
name string
getVersion func(ctx context.Context) (version string, err error)
}
func printVersions(ctx context.Context, logger logging.Logger,
elements []printVersionElement) (err error) {
const timeout = 5 * time.Second
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
for _, element := range elements {
version, err := element.getVersion(ctx)
if err != nil {
return err
}
logger.Info(element.name + " version: " + version)
}
return nil
}
func routeReadyEvents(ctx context.Context, done chan<- struct{}, buildInfo models.BuildInformation,
tunnelReadyCh <-chan struct{},
unboundLooper dns.Looper, updaterLooper updater.Looper, publicIPLooper publicip.Looper,
routing routing.VPNGetter, logger logging.Logger, httpClient *http.Client,
versionInformation, portForwardingEnabled bool, startPortForward func(vpnGateway net.IP)) {
defer close(done)
// for linters only
var restartTickerContext context.Context
var restartTickerCancel context.CancelFunc = func() {}
unboundTickerDone := make(chan struct{})
close(unboundTickerDone)
updaterTickerDone := make(chan struct{})
close(updaterTickerDone)
first := true
for {
select {
case <-ctx.Done():
restartTickerCancel() // for linters only
<-unboundTickerDone
<-updaterTickerDone
return
case <-tunnelReadyCh: // blocks until openvpn is connected
vpnDestination, err := routing.VPNDestinationIP()
if err != nil {
logger.Warn(err.Error())
} else {
logger.Info("VPN routing IP address: " + vpnDestination.String())
}
if unboundLooper.GetSettings().Enabled {
_, _ = unboundLooper.ApplyStatus(ctx, constants.Running)
}
restartTickerCancel() // stop previous restart tickers
<-unboundTickerDone
<-updaterTickerDone
restartTickerContext, restartTickerCancel = context.WithCancel(ctx)
// Runs the Public IP getter job once
_, _ = publicIPLooper.ApplyStatus(ctx, constants.Running)
if versionInformation && first {
first = false
message, err := versionpkg.GetMessage(ctx, buildInfo, httpClient)
if err != nil {
logger.Error("cannot get version information: " + err.Error())
} else {
logger.Info(message)
}
}
unboundTickerDone = make(chan struct{})
updaterTickerDone = make(chan struct{})
go unboundLooper.RunRestartTicker(restartTickerContext, unboundTickerDone)
go updaterLooper.RunRestartTicker(restartTickerContext, updaterTickerDone)
if portForwardingEnabled {
// vpnGateway required only for PIA
vpnGateway, err := routing.VPNLocalGatewayIP()
if err != nil {
logger.Error("cannot get VPN local gateway IP: " + err.Error())
}
logger.Info("VPN gateway IP address: " + vpnGateway.String())
startPortForward(vpnGateway)
}
}
}
}

1720
doc/logo.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 62 KiB

BIN
doc/logo_256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -1,38 +1,24 @@
version: "3.7"
services:
pia:
build: https://github.com/qdm12/private-internet-access-docker.git
image: qmcgaw/private-internet-access
container_name: pia
gluetun:
image: qmcgaw/gluetun
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 # HTTP proxy
- 8388:8388/tcp # Shadowsocks
- 8388:8388/udp # Shadowsocks
- 8000:8000/tcp # Built-in HTTP control server
# command:
volumes:
- /yourpath:/gluetun
environment:
- USER=js89ds7
- PASSWORD=8fd9s239G
- ENCRYPTION=strong
- PROTOCOL=udp
- REGION=CA Montreal
- NONROOT=no
- DOT=on
- BLOCK_MALICIOUS=on
- BLOCK_NSA=off
- UNBLOCK=
- FIREWALL=on
- EXTRA_SUBNETS=
- TINYPROXY=on
- TINYPROXY_LOG=Critical
- TINYPROXY_USER=
- TINYPROXY_PASSWORD=
- SHADOWSOCKS=on
- SHADOWSOCKS_LOG=on
- SHADOWSOCKS_PASSWORD=
# More variables are available, see the readme table
- OPENVPN_USER=
- OPENVPN_PASSWORD=
- VPNSP=private internet access
# Timezone for accurate logs times
- TZ=
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"

18
go.mod Normal file
View File

@@ -0,0 +1,18 @@
module github.com/qdm12/gluetun
go 1.16
require (
github.com/fatih/color v1.12.0
github.com/golang/mock v1.6.0
github.com/qdm12/dns v1.11.0
github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2
github.com/qdm12/goshutdown v0.1.0
github.com/qdm12/gosplash v0.1.0
github.com/qdm12/ss-server v0.2.0
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
github.com/stretchr/testify v1.7.0
github.com/vishvananda/netlink v1.1.0
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722
)

169
go.sum Normal file
View File

@@ -0,0 +1,169 @@
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
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/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
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/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/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
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/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q=
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
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/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
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/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee/go.mod h1:3uODdxMgOaPYeWU7RzZLxVtJHZ/x1f/iHkBZuKJDzuY=
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/dns v1.11.0 h1:jpcD5DZXXQSQe5a263PL09ghukiIdptvXFOZvyKEm6Q=
github.com/qdm12/dns v1.11.0/go.mod h1:FmQsNOUcrrZ4UFzWAiED56AKXeNgaX3ySbmPwEfNjjE=
github.com/qdm12/golibs v0.0.0-20210603202746-e5494e9c2ebb/go.mod h1:15RBzkun0i8XB7ADIoLJWp9ITRgsz3LroEI2FiOXLRg=
github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2 h1:FMeOhe/bGloI0T5Wb6QB7/rfOqgFeI//UF/N/f7PUCI=
github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2/go.mod h1:6aRbg4Z/bTbm9JfxsGXfWKHi7zsOvPfUTK1S5HuAFKg=
github.com/qdm12/goshutdown v0.1.0 h1:lmwnygdXtnr2pa6VqfR/bm8077/BnBef1+7CP96B7Sw=
github.com/qdm12/goshutdown v0.1.0/go.mod h1:/LP3MWLqI+wGH/ijfaUG+RHzBbKXIiVKnrg5vXOCf6Q=
github.com/qdm12/gosplash v0.1.0 h1:Sfl+zIjFZFP7b0iqf2l5UkmEY97XBnaKkH3FNY6Gf7g=
github.com/qdm12/gosplash v0.1.0/go.mod h1:+A3fWW4/rUeDXhY3ieBzwghKdnIPFJgD8K3qQkenJlw=
github.com/qdm12/ss-server v0.2.0 h1:+togLzeeLAJ68MD1JqOWvYi9rl9t/fx1Qh7wKzZhY1g=
github.com/qdm12/ss-server v0.2.0/go.mod h1:+1bWO1EfWNvsGM5Cuep6vneChK2OHniqtAsED9Fh1y0=
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e h1:4q+uFLawkaQRq3yARYLsjJPZd2wYwxn4g6G/5v0xW1g=
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e/go.mod h1:UvJRGkZ9XL3/D7e7JiTTVLm1F3Cymd3/gFpD6frEpBo=
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/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg=
go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 h1:1tk03FUNpulq2cuWpXZWj649rwJpk0d20rxWiopKRmc=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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=
inet.af/netaddr v0.0.0-20210511181906-37180328850c/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722 h1:Qws2rZnQudC58cIagVucPQDLmMi3kAXgxscsgD0v6DU=
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=

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

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

@@ -0,0 +1,29 @@
// Package alpine defines a configurator to interact with the Alpine operating system.
package alpine
import (
"os/user"
)
var _ Alpiner = (*Alpine)(nil)
type Alpiner interface {
UserCreater
VersionGetter
}
type Alpine struct {
alpineReleasePath string
passwdPath string
lookupID func(uid string) (*user.User, error)
lookup func(username string) (*user.User, error)
}
func New() *Alpine {
return &Alpine{
alpineReleasePath: "/etc/alpine-release",
passwdPath: "/etc/passwd",
lookupID: user.LookupId,
lookup: user.Lookup,
}
}

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

@@ -0,0 +1,58 @@
package alpine
import (
"errors"
"fmt"
"os"
"os/user"
"strconv"
)
var (
ErrUserAlreadyExists = errors.New("user already exists")
)
type UserCreater interface {
CreateUser(username string, uid int) (createdUsername string, err error)
}
// CreateUser creates a user in Alpine with the given UID.
func (a *Alpine) CreateUser(username string, uid int) (createdUsername string, err error) {
UIDStr := strconv.Itoa(uid)
u, err := a.lookupID(UIDStr)
_, unknownUID := err.(user.UnknownUserIdError)
if err != nil && !unknownUID {
return "", err
}
if u != nil {
if u.Username == username {
return "", nil
}
return u.Username, nil
}
u, err = a.lookup(username)
_, unknownUsername := err.(user.UnknownUserError)
if err != nil && !unknownUsername {
return "", err
}
if u != nil {
return "", fmt.Errorf("%w: with name %s for ID %s instead of %d",
ErrUserAlreadyExists, username, u.Uid, uid)
}
file, err := os.OpenFile(a.passwdPath, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return "", err
}
s := fmt.Sprintf("%s:x:%d:::/dev/null:/sbin/nologin\n", username, uid)
_, err = file.WriteString(s)
if err != nil {
_ = file.Close()
return "", err
}
return username, file.Close()
}

View File

@@ -0,0 +1,31 @@
package alpine
import (
"context"
"io"
"os"
"strings"
)
type VersionGetter interface {
Version(ctx context.Context) (version string, err error)
}
func (a *Alpine) Version(ctx context.Context) (version string, err error) {
file, err := os.OpenFile(a.alpineReleasePath, os.O_RDONLY, 0)
if err != nil {
return "", err
}
b, err := io.ReadAll(file)
if err != nil {
return "", err
}
if err := file.Close(); err != nil {
return "", err
}
version = strings.ReplaceAll(string(b), "\n", "")
return version, nil
}

7
internal/cli/ci.go Normal file
View File

@@ -0,0 +1,7 @@
package cli
import "context"
func (c *CLI) CI(context context.Context) error {
return nil
}

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

@@ -0,0 +1,21 @@
// Package cli defines an interface CLI to run command line operations.
package cli
var _ CLIer = (*CLI)(nil)
type CLIer interface {
ClientKeyFormatter
HealthChecker
OpenvpnConfigMaker
Updater
}
type CLI struct {
repoServersPath string
}
func New() *CLI {
return &CLI{
repoServersPath: "./internal/constants/servers.json",
}
}

45
internal/cli/clientkey.go Normal file
View File

@@ -0,0 +1,45 @@
package cli
import (
"flag"
"fmt"
"io"
"os"
"strings"
"github.com/qdm12/gluetun/internal/constants"
)
type ClientKeyFormatter interface {
ClientKey(args []string) error
}
func (c *CLI) ClientKey(args []string) error {
flagSet := flag.NewFlagSet("clientkey", flag.ExitOnError)
filepath := flagSet.String("path", constants.ClientKey, "file path to the client.key file")
if err := flagSet.Parse(args); err != nil {
return err
}
file, err := os.OpenFile(*filepath, os.O_RDONLY, 0)
if err != nil {
return err
}
data, err := io.ReadAll(file)
if err != nil {
_ = file.Close()
return err
}
if err := file.Close(); err != nil {
return err
}
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
}

View File

@@ -0,0 +1,40 @@
package cli
import (
"context"
"net"
"net/http"
"time"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/healthcheck"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params"
)
type HealthChecker interface {
HealthCheck(ctx context.Context, env params.Env, logger logging.Logger) error
}
func (c *CLI) HealthCheck(ctx context.Context, env params.Env,
logger logging.Logger) error {
// Extract the health server port from the configuration.
config := configuration.Health{}
err := config.Read(env, logger)
if err != nil {
return err
}
_, port, err := net.SplitHostPort(config.ServerAddress)
if err != nil {
return err
}
const timeout = 10 * time.Second
httpClient := &http.Client{Timeout: timeout}
client := healthcheck.NewClient(httpClient)
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
url := "http://127.0.0.1:" + port
return client.Check(ctx, url)
}

View File

@@ -0,0 +1,39 @@
package cli
import (
"fmt"
"strings"
"time"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/provider"
"github.com/qdm12/gluetun/internal/storage"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params"
)
type OpenvpnConfigMaker interface {
OpenvpnConfig(logger logging.Logger) error
}
func (c *CLI) OpenvpnConfig(logger logging.Logger) error {
var allSettings configuration.Settings
err := allSettings.Read(params.NewEnv(), logger)
if err != nil {
return err
}
allServers, err := storage.New(logger, constants.ServersData).
SyncServers(constants.GetAllServers())
if err != nil {
return err
}
providerConf := provider.New(allSettings.OpenVPN.Provider.Name, allServers, time.Now)
connection, err := providerConf.GetOpenVPNConnection(allSettings.OpenVPN.Provider.ServerSelection)
if err != nil {
return err
}
lines := providerConf.BuildConf(connection, "nonroortuser", allSettings.OpenVPN)
fmt.Println(strings.Join(lines, "\n"))
return nil
}

111
internal/cli/update.go Normal file
View File

@@ -0,0 +1,111 @@
package cli
import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"net/http"
"os"
"time"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/storage"
"github.com/qdm12/gluetun/internal/updater"
"github.com/qdm12/golibs/logging"
)
var (
ErrModeUnspecified = errors.New("at least one of -enduser or -maintainers must be specified")
ErrSyncServers = errors.New("cannot sync hardcoded and persisted servers")
ErrUpdateServerInformation = errors.New("cannot update server information")
ErrWriteToFile = errors.New("cannot write updated information to file")
)
type Updater interface {
Update(ctx context.Context, args []string, logger logging.Logger) error
}
func (c *CLI) Update(ctx context.Context, args []string, logger logging.Logger) error {
options := configuration.Updater{CLI: true}
var endUserMode, maintainerMode, updateAll bool
flagSet := flag.NewFlagSet("update", flag.ExitOnError)
flagSet.BoolVar(&endUserMode, "enduser", false, "Write results to /gluetun/servers.json (for end users)")
flagSet.BoolVar(&maintainerMode, "maintainer", false,
"Write results to ./internal/constants/servers.json to modify the program (for maintainers)")
flagSet.StringVar(&options.DNSAddress, "dns", "8.8.8.8", "DNS resolver address to use")
flagSet.BoolVar(&updateAll, "all", false, "Update servers for all VPN providers")
flagSet.BoolVar(&options.Cyberghost, "cyberghost", false, "Update Cyberghost servers")
flagSet.BoolVar(&options.Fastestvpn, "fastestvpn", false, "Update FastestVPN servers")
flagSet.BoolVar(&options.HideMyAss, "hidemyass", false, "Update HideMyAss servers")
flagSet.BoolVar(&options.Ipvanish, "ipvanish", false, "Update IpVanish servers")
flagSet.BoolVar(&options.Ivpn, "ivpn", false, "Update IVPN servers")
flagSet.BoolVar(&options.Mullvad, "mullvad", false, "Update Mullvad servers")
flagSet.BoolVar(&options.Nordvpn, "nordvpn", false, "Update Nordvpn servers")
flagSet.BoolVar(&options.PIA, "pia", false, "Update Private Internet Access post-summer 2020 servers")
flagSet.BoolVar(&options.Privado, "privado", false, "Update Privado servers")
flagSet.BoolVar(&options.Privatevpn, "privatevpn", false, "Update Private VPN servers")
flagSet.BoolVar(&options.Protonvpn, "protonvpn", false, "Update Protonvpn servers")
flagSet.BoolVar(&options.Purevpn, "purevpn", false, "Update Purevpn servers")
flagSet.BoolVar(&options.Surfshark, "surfshark", false, "Update Surfshark servers")
flagSet.BoolVar(&options.Torguard, "torguard", false, "Update Torguard servers")
flagSet.BoolVar(&options.VPNUnlimited, "vpnunlimited", false, "Update VPN Unlimited servers")
flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers")
flagSet.BoolVar(&options.Windscribe, "windscribe", false, "Update Windscribe servers")
if err := flagSet.Parse(args); err != nil {
return err
}
if !endUserMode && !maintainerMode {
return ErrModeUnspecified
}
if updateAll {
options.EnableAll()
}
const clientTimeout = 10 * time.Second
httpClient := &http.Client{Timeout: clientTimeout}
storage := storage.New(logger, constants.ServersData)
currentServers, err := storage.SyncServers(constants.GetAllServers())
if err != nil {
return fmt.Errorf("%w: %s", ErrSyncServers, err)
}
updater := updater.New(options, httpClient, currentServers, logger)
allServers, err := updater.UpdateServers(ctx)
if err != nil {
return fmt.Errorf("%w: %s", ErrUpdateServerInformation, err)
}
if endUserMode {
if err := storage.FlushToFile(allServers); err != nil {
return fmt.Errorf("%w: %s", ErrWriteToFile, err)
}
}
if maintainerMode {
if err := writeToEmbeddedJSON(c.repoServersPath, allServers); err != nil {
return fmt.Errorf("%w: %s", ErrWriteToFile, err)
}
}
return nil
}
func writeToEmbeddedJSON(repoServersPath string,
allServers models.AllServers) error {
const perms = 0600
f, err := os.OpenFile(repoServersPath,
os.O_TRUNC|os.O_WRONLY|os.O_CREATE, perms)
if err != nil {
return err
}
defer f.Close()
encoder := json.NewEncoder(f)
encoder.SetIndent("", " ")
return encoder.Encode(allServers)
}

View File

@@ -0,0 +1,3 @@
// Package configuration reads initial settings from environment variables
// and secret files.
package configuration

View File

@@ -0,0 +1,6 @@
package configuration
const (
lastIndent = "|--"
indent = " "
)

View File

@@ -0,0 +1,72 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
func (settings *Provider) cyberghostLines() (lines []string) {
lines = append(lines, lastIndent+"Server group: "+settings.ServerSelection.Group)
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if settings.ExtraConfigOptions.ClientKey != "" {
lines = append(lines, lastIndent+"Client key is set")
}
if settings.ExtraConfigOptions.ClientCertificate != "" {
lines = append(lines, lastIndent+"Client certificate is set")
}
return lines
}
func (settings *Provider) readCyberghost(r reader) (err error) {
settings.Name = constants.Cyberghost
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ExtraConfigOptions.ClientKey, err = readClientKey(r)
if err != nil {
return err
}
settings.ExtraConfigOptions.ClientCertificate, err = readClientCertificate(r)
if err != nil {
return err
}
settings.ServerSelection.Group, err = r.env.Inside("CYBERGHOST_GROUP",
constants.CyberghostGroupChoices(), params.Default("Premium UDP Europe"))
if err != nil {
return fmt.Errorf("environment variable CYBERGHOST_GROUP: %w", err)
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.CyberghostRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.CyberghostHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -0,0 +1,117 @@
package configuration
import (
"errors"
"fmt"
"net"
"strings"
"time"
"github.com/qdm12/dns/pkg/blacklist"
"github.com/qdm12/dns/pkg/unbound"
"github.com/qdm12/golibs/params"
)
// DNS contains settings to configure Unbound for DNS over TLS operation.
type DNS struct { //nolint:maligned
Enabled bool
PlaintextAddress net.IP
KeepNameserver bool
UpdatePeriod time.Duration
Unbound unbound.Settings
BlacklistBuild blacklist.BuilderSettings
}
func (settings *DNS) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *DNS) lines() (lines []string) {
lines = append(lines, lastIndent+"DNS:")
if settings.PlaintextAddress != nil {
lines = append(lines, indent+lastIndent+"Plaintext address: "+settings.PlaintextAddress.String())
}
if settings.KeepNameserver {
lines = append(lines, indent+lastIndent+"Keep nameserver (disabled blocking): yes")
}
if !settings.Enabled {
return lines
}
lines = append(lines, indent+lastIndent+"DNS over TLS:")
lines = append(lines, indent+indent+lastIndent+"Unbound:")
for _, line := range settings.Unbound.Lines() {
lines = append(lines, indent+indent+indent+line)
}
lines = append(lines, indent+indent+lastIndent+"Blacklist:")
for _, line := range settings.BlacklistBuild.Lines(indent, lastIndent) {
lines = append(lines, indent+indent+indent+line)
}
if settings.UpdatePeriod > 0 {
lines = append(lines, indent+indent+lastIndent+"Update: every "+settings.UpdatePeriod.String())
}
return lines
}
var (
ErrUnboundSettings = errors.New("failed getting Unbound settings")
ErrBlacklistSettings = errors.New("failed getting DNS blacklist settings")
)
func (settings *DNS) read(r reader) (err error) {
settings.Enabled, err = r.env.OnOff("DOT", params.Default("on"))
if err != nil {
return fmt.Errorf("environment variable DOT: %w", err)
}
// Plain DNS settings
if err := settings.readDNSPlaintext(r.env); err != nil {
return err
}
settings.KeepNameserver, err = r.env.OnOff("DNS_KEEP_NAMESERVER", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable DNS_KEEP_NAMESERVER: %w", err)
}
// DNS over TLS external settings
if err := settings.readBlacklistBuilding(r); err != nil {
return fmt.Errorf("%w: %s", ErrBlacklistSettings, err)
}
settings.UpdatePeriod, err = r.env.Duration("DNS_UPDATE_PERIOD", params.Default("24h"))
if err != nil {
return fmt.Errorf("environment variable DNS_UPDATE_PERIOD: %w", err)
}
// Unbound settings
if err := settings.readUnbound(r); err != nil {
return fmt.Errorf("%w: %s", ErrUnboundSettings, err)
}
return nil
}
var (
ErrDNSAddressNotAnIP = errors.New("DNS plaintext address is not an IP address")
)
func (settings *DNS) readDNSPlaintext(env params.Env) error {
s, err := env.Get("DNS_PLAINTEXT_ADDRESS", params.Default("1.1.1.1"))
if err != nil {
return fmt.Errorf("environment variable DNS_PLAINTEXT_ADDRESS: %w", err)
}
settings.PlaintextAddress = net.ParseIP(s)
if settings.PlaintextAddress == nil {
return fmt.Errorf("%w: %s", ErrDNSAddressNotAnIP, s)
}
return nil
}

View File

@@ -0,0 +1,76 @@
package configuration
import (
"net"
"testing"
"time"
"github.com/qdm12/dns/pkg/blacklist"
"github.com/qdm12/dns/pkg/provider"
"github.com/qdm12/dns/pkg/unbound"
"github.com/stretchr/testify/assert"
)
func Test_DNS_Lines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings DNS
lines []string
}{
"disabled DOT": {
settings: DNS{
PlaintextAddress: net.IP{1, 1, 1, 1},
},
lines: []string{
"|--DNS:",
" |--Plaintext address: 1.1.1.1",
},
},
"enabled DOT": {
settings: DNS{
Enabled: true,
KeepNameserver: true,
Unbound: unbound.Settings{
Providers: []provider.Provider{
provider.Cloudflare(),
},
},
BlacklistBuild: blacklist.BuilderSettings{
BlockMalicious: true,
BlockAds: true,
BlockSurveillance: true,
},
UpdatePeriod: time.Hour,
},
lines: []string{
"|--DNS:",
" |--Keep nameserver (disabled blocking): yes",
" |--DNS over TLS:",
" |--Unbound:",
" |--DNS over TLS providers:",
" |--Cloudflare",
" |--Listening port: 0",
" |--Access control:",
" |--Allowed:",
" |--Caching: disabled",
" |--IPv4 resolution: disabled",
" |--IPv6 resolution: disabled",
" |--Verbosity level: 0/5",
" |--Verbosity details level: 0/4",
" |--Validation log level: 0/2",
" |--Username: ",
" |--Blacklist:",
" |--Blocked categories: malicious, surveillance, ads",
" |--Update: every 1h0m0s",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.lines()
assert.Equal(t, testCase.lines, lines)
})
}
}

View File

@@ -0,0 +1,87 @@
package configuration
import (
"errors"
"fmt"
"github.com/qdm12/golibs/params"
"inet.af/netaddr"
)
func (settings *DNS) readBlacklistBuilding(r reader) (err error) {
settings.BlacklistBuild.BlockMalicious, err = r.env.OnOff("BLOCK_MALICIOUS", params.Default("on"))
if err != nil {
return fmt.Errorf("environment variable BLOCK_MALICIOUS: %w", err)
}
settings.BlacklistBuild.BlockSurveillance, err = r.env.OnOff("BLOCK_SURVEILLANCE", params.Default("on"),
params.RetroKeys([]string{"BLOCK_NSA"}, r.onRetroActive))
if err != nil {
return fmt.Errorf("environment variable BLOCK_SURVEILLANCE (or BLOCK_NSA): %w", err)
}
settings.BlacklistBuild.BlockAds, err = r.env.OnOff("BLOCK_ADS", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable BLOCK_ADS: %w", err)
}
if err := settings.readPrivateAddresses(r.env); err != nil {
return err
}
return settings.readBlacklistUnblockedHostnames(r)
}
var (
ErrInvalidPrivateAddress = errors.New("private address is not a valid IP or CIDR range")
)
func (settings *DNS) readPrivateAddresses(env params.Env) (err error) {
privateAddresses, err := env.CSV("DOT_PRIVATE_ADDRESS")
if err != nil {
return fmt.Errorf("environment variable DOT_PRIVATE_ADDRESS: %w", err)
} else if len(privateAddresses) == 0 {
return nil
}
ips := make([]netaddr.IP, 0, len(privateAddresses))
ipPrefixes := make([]netaddr.IPPrefix, 0, len(privateAddresses))
for _, address := range privateAddresses {
ip, err := netaddr.ParseIP(address)
if err == nil {
ips = append(ips, ip)
continue
}
ipPrefix, err := netaddr.ParseIPPrefix(address)
if err == nil {
ipPrefixes = append(ipPrefixes, ipPrefix)
continue
}
return fmt.Errorf("%w: %s", ErrInvalidPrivateAddress, address)
}
settings.BlacklistBuild.AddBlockedIPs = append(settings.BlacklistBuild.AddBlockedIPs, ips...)
settings.BlacklistBuild.AddBlockedIPPrefixes = append(settings.BlacklistBuild.AddBlockedIPPrefixes, ipPrefixes...)
return nil
}
func (settings *DNS) readBlacklistUnblockedHostnames(r reader) (err error) {
hostnames, err := r.env.CSV("UNBLOCK")
if err != nil {
return fmt.Errorf("environment variable UNBLOCK: %w", err)
} else if len(hostnames) == 0 {
return nil
}
for _, hostname := range hostnames {
if !r.regex.MatchHostname(hostname) {
return fmt.Errorf("%w: %s", ErrInvalidHostname, hostname)
}
}
settings.BlacklistBuild.AllowedHosts = append(settings.BlacklistBuild.AllowedHosts, hostnames...)
return nil
}

View File

@@ -0,0 +1,45 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) fastestvpnLines() (lines []string) {
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
return lines
}
func (settings *Provider) readFastestvpn(r reader) (err error) {
settings.Name = constants.Fastestvpn
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.FastestvpnHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.FastestvpnCountriesChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
return nil
}

View File

@@ -0,0 +1,99 @@
package configuration
import (
"fmt"
"net"
"strings"
"github.com/qdm12/golibs/params"
)
// Firewall contains settings to customize the firewall operation.
type Firewall struct {
VPNInputPorts []uint16
InputPorts []uint16
OutboundSubnets []net.IPNet
Enabled bool
Debug bool
}
func (settings *Firewall) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *Firewall) lines() (lines []string) {
if !settings.Enabled {
lines = append(lines, lastIndent+"Firewall: disabled ⚠️")
return lines
}
lines = append(lines, lastIndent+"Firewall:")
if settings.Debug {
lines = append(lines, indent+lastIndent+"Debug: on")
}
if len(settings.VPNInputPorts) > 0 {
lines = append(lines, indent+lastIndent+"VPN input ports: "+
strings.Join(uint16sToStrings(settings.VPNInputPorts), ", "))
}
if len(settings.InputPorts) > 0 {
lines = append(lines, indent+lastIndent+"Input ports: "+
strings.Join(uint16sToStrings(settings.InputPorts), ", "))
}
if len(settings.OutboundSubnets) > 0 {
lines = append(lines, indent+lastIndent+"Outbound subnets: "+
strings.Join(ipNetsToStrings(settings.OutboundSubnets), ", "))
}
return lines
}
func (settings *Firewall) read(r reader) (err error) {
settings.Enabled, err = r.env.OnOff("FIREWALL", params.Default("on"))
if err != nil {
return fmt.Errorf("environment variable FIREWALL: %w", err)
}
settings.Debug, err = r.env.OnOff("FIREWALL_DEBUG", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable FIREWALL_DEBUG: %w", err)
}
if err := settings.readVPNInputPorts(r.env); err != nil {
return err
}
if err := settings.readInputPorts(r.env); err != nil {
return err
}
return settings.readOutboundSubnets(r)
}
func (settings *Firewall) readVPNInputPorts(env params.Env) (err error) {
settings.VPNInputPorts, err = readCSVPorts(env, "FIREWALL_VPN_INPUT_PORTS")
if err != nil {
return fmt.Errorf("environment variable FIREWALL_VPN_INPUT_PORTS: %w", err)
}
return nil
}
func (settings *Firewall) readInputPorts(env params.Env) (err error) {
settings.InputPorts, err = readCSVPorts(env, "FIREWALL_INPUT_PORTS")
if err != nil {
return fmt.Errorf("environment variable FIREWALL_INPUT_PORTS: %w", err)
}
return nil
}
func (settings *Firewall) readOutboundSubnets(r reader) (err error) {
retroOption := params.RetroKeys([]string{"EXTRA_SUBNETS"}, r.onRetroActive)
settings.OutboundSubnets, err = readCSVIPNets(r.env, "FIREWALL_OUTBOUND_SUBNETS", retroOption)
if err != nil {
return fmt.Errorf("environment variable FIREWALL_OUTBOUND_SUBNETS: %w", err)
}
return nil
}

View File

@@ -0,0 +1,62 @@
package configuration
import (
"fmt"
"strings"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params"
)
// Health contains settings for the healthcheck and health server.
type Health struct {
ServerAddress string
OpenVPN HealthyWait
}
func (settings *Health) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *Health) lines() (lines []string) {
lines = append(lines, lastIndent+"Health:")
lines = append(lines, indent+lastIndent+"Server address: "+settings.ServerAddress)
lines = append(lines, indent+lastIndent+"OpenVPN:")
for _, line := range settings.OpenVPN.lines() {
lines = append(lines, indent+indent+line)
}
return lines
}
// Read is to be used for the healthcheck query mode.
func (settings *Health) Read(env params.Env, logger logging.Logger) (err error) {
reader := newReader(env, logger)
return settings.read(reader)
}
func (settings *Health) read(r reader) (err error) {
var warning string
settings.ServerAddress, warning, err = r.env.ListeningAddress(
"HEALTH_SERVER_ADDRESS", params.Default("127.0.0.1:9999"))
if warning != "" {
r.logger.Warn("environment variable HEALTH_SERVER_ADDRESS: " + warning)
}
if err != nil {
return fmt.Errorf("environment variable HEALTH_SERVER_ADDRESS: %w", err)
}
settings.OpenVPN.Initial, err = r.env.Duration("HEALTH_OPENVPN_DURATION_INITIAL", params.Default("6s"))
if err != nil {
return fmt.Errorf("environment variable HEALTH_OPENVPN_DURATION_INITIAL: %w", err)
}
settings.OpenVPN.Addition, err = r.env.Duration("HEALTH_OPENVPN_DURATION_ADDITION", params.Default("5s"))
if err != nil {
return fmt.Errorf("environment variable HEALTH_OPENVPN_DURATION_ADDITION: %w", err)
}
return nil
}

View File

@@ -0,0 +1,182 @@
package configuration
import (
"errors"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/qdm12/golibs/logging/mock_logging"
"github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Health_String(t *testing.T) {
t.Parallel()
var health Health
const expected = "|--Health:\n |--Server address: \n |--OpenVPN:\n |--Initial duration: 0s"
s := health.String()
assert.Equal(t, expected, s)
}
func Test_Health_lines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Health
lines []string
}{
"empty": {
lines: []string{
"|--Health:",
" |--Server address: ",
" |--OpenVPN:",
" |--Initial duration: 0s",
},
},
"filled settings": {
settings: Health{
ServerAddress: "address:9999",
OpenVPN: HealthyWait{
Initial: time.Second,
Addition: time.Minute,
},
},
lines: []string{
"|--Health:",
" |--Server address: address:9999",
" |--OpenVPN:",
" |--Initial duration: 1s",
" |--Addition duration: 1m0s",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.lines()
assert.Equal(t, testCase.lines, lines)
})
}
}
func Test_Health_read(t *testing.T) {
t.Parallel()
errDummy := errors.New("dummy")
testCases := map[string]struct {
openvpnInitialDuration time.Duration
openvpnInitialErr error
openvpnAdditionDuration time.Duration
openvpnAdditionErr error
serverAddress string
serverAddressWarning string
serverAddressErr error
expected Health
err error
}{
"success": {
openvpnInitialDuration: time.Second,
openvpnAdditionDuration: time.Minute,
serverAddress: "127.0.0.1:9999",
expected: Health{
ServerAddress: "127.0.0.1:9999",
OpenVPN: HealthyWait{
Initial: time.Second,
Addition: time.Minute,
},
},
},
"listening address error": {
openvpnInitialDuration: time.Second,
openvpnAdditionDuration: time.Minute,
serverAddress: "127.0.0.1:9999",
serverAddressWarning: "warning",
serverAddressErr: errDummy,
expected: Health{
ServerAddress: "127.0.0.1:9999",
},
err: errors.New("environment variable HEALTH_SERVER_ADDRESS: dummy"),
},
"initial error": {
openvpnInitialDuration: time.Second,
openvpnInitialErr: errDummy,
openvpnAdditionDuration: time.Minute,
expected: Health{
OpenVPN: HealthyWait{
Initial: time.Second,
},
},
err: errors.New("environment variable HEALTH_OPENVPN_DURATION_INITIAL: dummy"),
},
"addition error": {
openvpnInitialDuration: time.Second,
openvpnAdditionDuration: time.Minute,
openvpnAdditionErr: errDummy,
expected: Health{
OpenVPN: HealthyWait{
Initial: time.Second,
Addition: time.Minute,
},
},
err: errors.New("environment variable HEALTH_OPENVPN_DURATION_ADDITION: dummy"),
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
env := mock_params.NewMockEnv(ctrl)
logger := mock_logging.NewMockLogger(ctrl)
env.EXPECT().ListeningAddress("HEALTH_SERVER_ADDRESS", gomock.Any()).
Return(testCase.serverAddress, testCase.serverAddressWarning,
testCase.serverAddressErr)
if testCase.serverAddressWarning != "" {
logger.EXPECT().Warn("environment variable HEALTH_SERVER_ADDRESS: " + testCase.serverAddressWarning)
}
if testCase.serverAddressErr == nil {
env.EXPECT().
Duration("HEALTH_OPENVPN_DURATION_INITIAL", gomock.Any()).
Return(testCase.openvpnInitialDuration, testCase.openvpnInitialErr)
if testCase.openvpnInitialErr == nil {
env.EXPECT().
Duration("HEALTH_OPENVPN_DURATION_ADDITION", gomock.Any()).
Return(testCase.openvpnAdditionDuration, testCase.openvpnAdditionErr)
}
}
r := reader{
env: env,
logger: logger,
}
var health Health
err := health.read(r)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.expected, health)
})
}
}

View File

@@ -0,0 +1,55 @@
package configuration
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func Test_HealthyWait_String(t *testing.T) {
t.Parallel()
var healthyWait HealthyWait
const expected = "|--Initial duration: 0s"
s := healthyWait.String()
assert.Equal(t, expected, s)
}
func Test_HealthyWait_lines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings HealthyWait
lines []string
}{
"empty": {
lines: []string{
"|--Initial duration: 0s",
},
},
"filled settings": {
settings: HealthyWait{
Initial: time.Second,
Addition: time.Minute,
},
lines: []string{
"|--Initial duration: 1s",
"|--Addition duration: 1m0s",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.lines()
assert.Equal(t, testCase.lines, lines)
})
}
}

View File

@@ -0,0 +1,30 @@
package configuration
import (
"strings"
"time"
)
type HealthyWait struct {
// Initial is the initial duration to wait for the program
// to be healthy before taking action.
Initial time.Duration
// Addition is the duration to add to the Initial duration
// after Initial has expired to wait longer for the program
// to be healthy.
Addition time.Duration
}
func (settings *HealthyWait) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *HealthyWait) lines() (lines []string) {
lines = append(lines, lastIndent+"Initial duration: "+settings.Initial.String())
if settings.Addition > 0 {
lines = append(lines, lastIndent+"Addition duration: "+settings.Addition.String())
}
return lines
}

View File

@@ -0,0 +1,63 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) hideMyAssLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readHideMyAss(r reader) (err error) {
settings.Name = constants.HideMyAss
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.HideMyAssCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.HideMyAssCountryChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.HideMyAssCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.HideMyAssHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -0,0 +1,106 @@
package configuration
import (
"fmt"
"strconv"
"strings"
"github.com/qdm12/golibs/params"
)
// HTTPProxy contains settings to configure the HTTP proxy.
type HTTPProxy struct {
User string
Password string
Port uint16
Enabled bool
Stealth bool
Log bool
}
func (settings *HTTPProxy) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *HTTPProxy) lines() (lines []string) {
if !settings.Enabled {
return nil
}
lines = append(lines, lastIndent+"HTTP proxy:")
lines = append(lines, indent+lastIndent+"Port: "+strconv.Itoa(int(settings.Port)))
if settings.User != "" {
lines = append(lines, indent+lastIndent+"Authentication: enabled")
}
if settings.Log {
lines = append(lines, indent+lastIndent+"Log: enabled")
}
if settings.Stealth {
lines = append(lines, indent+lastIndent+"Stealth: enabled")
}
return lines
}
func (settings *HTTPProxy) read(r reader) (err error) {
settings.Enabled, err = r.env.OnOff("HTTPPROXY", params.Default("off"),
params.RetroKeys([]string{"TINYPROXY", "PROXY"}, r.onRetroActive))
if err != nil {
return fmt.Errorf("environment variable HTTPPROXY (or TINYPROXY, PROXY): %w", err)
}
settings.User, err = r.getFromEnvOrSecretFile("HTTPPROXY_USER", false, // compulsory
[]string{"TINYPROXY_USER", "PROXY_USER"})
if err != nil {
return fmt.Errorf("environment variable HTTPPROXY_USER (or TINYPROXY_USER, PROXY_USER): %w", err)
}
settings.Password, err = r.getFromEnvOrSecretFile("HTTPPROXY_PASSWORD", false,
[]string{"TINYPROXY_PASSWORD", "PROXY_PASSWORD"})
if err != nil {
return fmt.Errorf("environment variable HTTPPROXY_PASSWORD (or TINYPROXY_PASSWORD, PROXY_PASSWORD): %w", err)
}
settings.Stealth, err = r.env.OnOff("HTTPPROXY_STEALTH", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable HTTPPROXY_STEALTH: %w", err)
}
if err := settings.readLog(r); err != nil {
return err
}
var warning string
settings.Port, warning, err = r.env.ListeningPort("HTTPPROXY_PORT", params.Default("8888"),
params.RetroKeys([]string{"TINYPROXY_PORT", "PROXY_PORT"}, r.onRetroActive))
if len(warning) > 0 {
r.logger.Warn(warning)
}
if err != nil {
return fmt.Errorf("environment variable HTTPPROXY_PORT (or TINYPROXY_PORT, PROXY_PORT): %w", err)
}
return nil
}
func (settings *HTTPProxy) readLog(r reader) error {
s, err := r.env.Get("HTTPPROXY_LOG",
params.RetroKeys([]string{"PROXY_LOG_LEVEL", "TINYPROXY_LOG"}, r.onRetroActive))
if err != nil {
return fmt.Errorf("environment variable HTTPPROXY_LOG (or TINYPROXY_LOG, PROXY_LOG_LEVEL): %w", err)
}
switch strings.ToLower(s) {
case "on":
settings.Log = true
// Retro compatibility
case "info", "connect", "notice":
settings.Log = true
}
return nil
}

View File

@@ -0,0 +1,54 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) ipvanishLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readIpvanish(r reader) (err error) {
settings.Name = constants.Ipvanish
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IpvanishCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IpvanishCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -0,0 +1,192 @@
package configuration
import (
"errors"
"net"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Provider_ipvanishLines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Provider
lines []string
}{
"empty settings": {},
"full settings": {
settings: Provider{
ServerSelection: ServerSelection{
Countries: []string{"A", "B"},
Cities: []string{"C", "D"},
Hostnames: []string{"E", "F"},
},
},
lines: []string{
"|--Countries: A, B",
"|--Cities: C, D",
"|--Hostnames: E, F",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.ipvanishLines()
assert.Equal(t, testCase.lines, lines)
})
}
}
func Test_Provider_readIpvanish(t *testing.T) {
t.Parallel()
var errDummy = errors.New("dummy test error")
type singleStringCall struct {
call bool
value string
err error
}
type sliceStringCall struct {
call bool
values []string
err error
}
testCases := map[string]struct {
protocol singleStringCall
targetIP singleStringCall
countries sliceStringCall
cities sliceStringCall
hostnames sliceStringCall
settings Provider
err error
}{
"protocol error": {
protocol: singleStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ipvanish,
},
err: errors.New("environment variable PROTOCOL: dummy test error"),
},
"target IP error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true, value: "something", err: errDummy},
settings: Provider{
Name: constants.Ipvanish,
},
err: errors.New("environment variable OPENVPN_TARGET_IP: dummy test error"),
},
"countries error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ipvanish,
},
err: errors.New("environment variable COUNTRY: dummy test error"),
},
"cities error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ipvanish,
},
err: errors.New("environment variable CITY: dummy test error"),
},
"hostnames error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ipvanish,
},
err: errors.New("environment variable SERVER_HOSTNAME: dummy test error"),
},
"default settings": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true},
settings: Provider{
Name: constants.Ipvanish,
},
},
"set settings": {
protocol: singleStringCall{call: true, value: constants.TCP},
targetIP: singleStringCall{call: true, value: "1.2.3.4"},
countries: sliceStringCall{call: true, values: []string{"A", "B"}},
cities: sliceStringCall{call: true, values: []string{"C", "D"}},
hostnames: sliceStringCall{call: true, values: []string{"E", "F"}},
settings: Provider{
Name: constants.Ipvanish,
ServerSelection: ServerSelection{
TCP: true,
TargetIP: net.IPv4(1, 2, 3, 4),
Countries: []string{"A", "B"},
Cities: []string{"C", "D"},
Hostnames: []string{"E", "F"},
},
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
env := mock_params.NewMockEnv(ctrl)
if testCase.protocol.call {
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
Return(testCase.protocol.value, testCase.protocol.err)
}
if testCase.targetIP.call {
env.EXPECT().Get("OPENVPN_TARGET_IP").
Return(testCase.targetIP.value, testCase.targetIP.err)
}
if testCase.countries.call {
env.EXPECT().CSVInside("COUNTRY", constants.IpvanishCountryChoices()).
Return(testCase.countries.values, testCase.countries.err)
}
if testCase.cities.call {
env.EXPECT().CSVInside("CITY", constants.IpvanishCityChoices()).
Return(testCase.cities.values, testCase.cities.err)
}
if testCase.hostnames.call {
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices()).
Return(testCase.hostnames.values, testCase.hostnames.err)
}
r := reader{env: env}
var settings Provider
err := settings.readIpvanish(r)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.settings, settings)
})
}
}

View File

@@ -0,0 +1,54 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) ivpnLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readIvpn(r reader) (err error) {
settings.Name = constants.Ivpn
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IvpnCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IvpnCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -0,0 +1,192 @@
package configuration
import (
"errors"
"net"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Provider_ivpnLines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Provider
lines []string
}{
"empty settings": {},
"full settings": {
settings: Provider{
ServerSelection: ServerSelection{
Countries: []string{"A", "B"},
Cities: []string{"C", "D"},
Hostnames: []string{"E", "F"},
},
},
lines: []string{
"|--Countries: A, B",
"|--Cities: C, D",
"|--Hostnames: E, F",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.ivpnLines()
assert.Equal(t, testCase.lines, lines)
})
}
}
func Test_Provider_readIvpn(t *testing.T) {
t.Parallel()
var errDummy = errors.New("dummy test error")
type singleStringCall struct {
call bool
value string
err error
}
type sliceStringCall struct {
call bool
values []string
err error
}
testCases := map[string]struct {
protocol singleStringCall
targetIP singleStringCall
countries sliceStringCall
cities sliceStringCall
hostnames sliceStringCall
settings Provider
err error
}{
"protocol error": {
protocol: singleStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable PROTOCOL: dummy test error"),
},
"target IP error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true, value: "something", err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable OPENVPN_TARGET_IP: dummy test error"),
},
"countries error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable COUNTRY: dummy test error"),
},
"cities error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable CITY: dummy test error"),
},
"hostnames error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable SERVER_HOSTNAME: dummy test error"),
},
"default settings": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true},
settings: Provider{
Name: constants.Ivpn,
},
},
"set settings": {
protocol: singleStringCall{call: true, value: constants.TCP},
targetIP: singleStringCall{call: true, value: "1.2.3.4"},
countries: sliceStringCall{call: true, values: []string{"A", "B"}},
cities: sliceStringCall{call: true, values: []string{"C", "D"}},
hostnames: sliceStringCall{call: true, values: []string{"E", "F"}},
settings: Provider{
Name: constants.Ivpn,
ServerSelection: ServerSelection{
TCP: true,
TargetIP: net.IPv4(1, 2, 3, 4),
Countries: []string{"A", "B"},
Cities: []string{"C", "D"},
Hostnames: []string{"E", "F"},
},
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
env := mock_params.NewMockEnv(ctrl)
if testCase.protocol.call {
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
Return(testCase.protocol.value, testCase.protocol.err)
}
if testCase.targetIP.call {
env.EXPECT().Get("OPENVPN_TARGET_IP").
Return(testCase.targetIP.value, testCase.targetIP.err)
}
if testCase.countries.call {
env.EXPECT().CSVInside("COUNTRY", constants.IvpnCountryChoices()).
Return(testCase.countries.values, testCase.countries.err)
}
if testCase.cities.call {
env.EXPECT().CSVInside("CITY", constants.IvpnCityChoices()).
Return(testCase.cities.values, testCase.cities.err)
}
if testCase.hostnames.call {
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices()).
Return(testCase.hostnames.values, testCase.hostnames.err)
}
r := reader{env: env}
var settings Provider
err := settings.readIvpn(r)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.settings, settings)
})
}
}

View File

@@ -0,0 +1,55 @@
package configuration
import (
"encoding/pem"
"errors"
"strings"
"github.com/qdm12/gluetun/internal/constants"
)
func readClientKey(r reader) (clientKey string, err error) {
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTKEY", constants.ClientKey)
if err != nil {
return "", err
}
return extractClientKey(b)
}
var errDecodePEMBlockClientKey = errors.New("cannot decode PEM block from client key")
func extractClientKey(b []byte) (key string, err error) {
pemBlock, _ := pem.Decode(b)
if pemBlock == nil {
return "", errDecodePEMBlockClientKey
}
parsedBytes := pem.EncodeToMemory(pemBlock)
s := string(parsedBytes)
s = strings.ReplaceAll(s, "\n", "")
s = strings.TrimPrefix(s, "-----BEGIN PRIVATE KEY-----")
s = strings.TrimSuffix(s, "-----END PRIVATE KEY-----")
return s, nil
}
func readClientCertificate(r reader) (clientCertificate string, err error) {
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTCRT", constants.ClientCertificate)
if err != nil {
return "", err
}
return extractClientCertificate(b)
}
var errDecodePEMBlockClientCert = errors.New("cannot decode PEM block from client certificate")
func extractClientCertificate(b []byte) (certificate string, err error) {
pemBlock, _ := pem.Decode(b)
if pemBlock == nil {
return "", errDecodePEMBlockClientCert
}
parsedBytes := pem.EncodeToMemory(pemBlock)
s := string(parsedBytes)
s = strings.ReplaceAll(s, "\n", "")
s = strings.TrimPrefix(s, "-----BEGIN CERTIFICATE-----")
s = strings.TrimSuffix(s, "-----END CERTIFICATE-----")
return s, nil
}

View File

@@ -0,0 +1,174 @@
package configuration
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_extractClientKey(t *testing.T) {
t.Parallel()
const validPEM = `
-----BEGIN PRIVATE KEY-----
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
zZjrL52saevO25cigVl+hxcnY8DTpbk=
-----END PRIVATE KEY-----
`
const validKeyString = "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" //nolint:lll
testCases := map[string]struct {
b []byte
key string
err error
}{
"no input": {
err: errDecodePEMBlockClientKey,
},
"bad input": {
b: []byte{1, 2, 3},
err: errDecodePEMBlockClientKey,
},
"valid key": {
b: []byte(validPEM),
key: validKeyString,
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
key, err := extractClientKey(testCase.b)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.key, key)
})
}
}
func Test_extractClientCertificate(t *testing.T) {
t.Parallel()
const validPEM = `
-----BEGIN CERTIFICATE-----
MIIGrDCCBJSgAwIBAgIEAdTnfTANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJS
TzESMBAGA1UEBxMJQnVjaGFyZXN0MRgwFgYDVQQKEw9DeWJlckdob3N0IFMuQS4x
GzAZBgNVBAMTEkN5YmVyR2hvc3QgUm9vdCBDQTEhMB8GCSqGSIb3DQEJARYSaW5m
b0BjeWJlcmdob3N0LnJvMB4XDTIwMDcwNDE1MjkzNloXDTMwMDcwMjE1MjkzNlow
fTELMAkGA1UEBhMCUk8xEjAQBgNVBAcMCUJ1Y2hhcmVzdDEYMBYGA1UECgwPQ3li
ZXJHaG9zdCBTLkEuMR0wGwYDVQQDDBRjLmoua2xhdmVyQGdtYWlsLmNvbTEhMB8G
CSqGSIb3DQEJARYSaW5mb0BjeWJlcmdob3N0LnJvMIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEAobp2NlGUHMNBe08YEOnVG3QJjF3ZaXbRhE/II9rmtgJT
NZtDohGChvFlNRsExKzVrKxHCeuJkVffwzQ6fYk4/M1RdYLJUh0UVw3e4WdApw8E
7TJZxDYm4SHQNXUvt1Rt5TjslcXxIpDZgrMSc/kHROYEL9tdgdzPZErUJehXyJPh
EzIrzmAJh501x7WwKPz9ctSVlItyavqEWFF2vyUa6X9DYmD9mQTz5c+VXNO5DkXm
PFBIaEVDnvFtcjGJ56yEvFnWVukL+OUX7ezowrIOFOcp9udjgpeiHq+XvsQ6ER0D
Jt25MiEId3NjkxtZ8BitDftTcLN/kt81hWKT7adMVc3kpIZ80cxrwRCttMd7sHAz
KI9u7pMxv10eUOsIEY87ewBe3l6KvEnjA+9uIjim6gLLebDIaEH50Ee9PzNJ8fqQ
2u54Ab4bt00/H1sUnJ6Ss/+WsQDOK1BsPRKKcnHZntOlHrs2Tu5+txKNU2cOapI8
SjVULUNKrRXASbpfWnLUfri/HO742bJb/TjkOJcOxta3hTPFAhaRWBusVlB41XVH
euH5DAhugYXeSNK6/6Ul8YvKUNH/7QbxuGIGXfth19Xl4QLI1umyEjZopSlt3tOi
O2V1soVNSQCCfxXVoCTMESMLjhkjWdmBDhdy2GTW7S4YoJfqVKiS18rYkN7I4ZMC
AwEAAaOCATQwggEwMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMDQGCWCGSAGG+EIB
DQQnFiVDeWJlckdob3N0IEdlbmVyYXRlZCBVc2VyIENlcnRpZmljYXRlMBEGCWCG
SAGG+EIBAQQEAwIHgDAdBgNVHQ4EFgQULwUtU5s6pL2NN9gPeEnKX0dhwiswga0G
A1UdIwSBpTCBooAU6tdK1g/He5qzjeAoM5eHt4in9iWhf6R9MHsxCzAJBgNVBAYT
AlJPMRIwEAYDVQQHEwlCdWNoYXJlc3QxGDAWBgNVBAoTD0N5YmVyR2hvc3QgUy5B
LjEbMBkGA1UEAxMSQ3liZXJHaG9zdCBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJp
bmZvQGN5YmVyZ2hvc3Qucm+CCQCcVButZsQ0uzANBgkqhkiG9w0BAQsFAAOCAgEA
ystGIMYhQWaEdTqlnLCytrr8657t+PuidZMNNIaPB3wN2Fi2xKf14DTg03mqxjmP
Pb+f+PVNIOV5PdWD4jcQwOP1GEboGV0DFzlRGeAtDcvKwdee4oASJbZq1CETqDao
hQTxKEWC+UBk2F36nOaEI6Sab+Mb4cR9//PAwvzOqrXuGF5NuIOX7eFtCMQSgQq6
lRRqTQjekm0Dxigx4JA92Jo2qZRwCJ0T3IXBJGL831HCFJbDWv8PV3lsfFb/i2+v
r54uywFQVWWp18dYi97gipfuQ4zRg2Ldx5aXSmnhhKpg5ioZvtk043QofF12YORh
obElqavRbvvhZvlCouvcuoq9QKi7IPe5SJZkZ1X7ezMesCwBzwFpt6vRUAcslsNF
bcYS1iSENlY/PTcDqBhbKuc9yAhq+/aUgaY/8VF5RWVzSRZufbf3BPwOkE4K0Uyb
aobO/YX0JOkCacAD+4tdR6YSXNIMMRAOCBQvxbxFXaHzhwhzBAjdsC56FrJKwXvQ
rRLU3tF4P0zFMeNTay8uTtUXugDK7EnklLESuYdpUJ8bUMlAUhJBi6UFI9/icMud
xXvLRvhnBW9EtKib5JnVFUovcEUt+3EJbyst05nkL4YPjQS4TC9DHdo5SyRAy1Tp
iOCYTbretAFZRhh6ycUN5hBeN8GMQxiMreMtDV4PEIQ=
-----END CERTIFICATE-----
`
const validCertificateString = "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" //nolint:lll
testCases := map[string]struct {
b []byte
certificate string
err error
}{
"no input": {
err: errDecodePEMBlockClientCert,
},
"bad input": {
b: []byte{1, 2, 3},
err: errDecodePEMBlockClientCert,
},
"valid key": {
b: []byte(validPEM),
certificate: validCertificateString,
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
certificate, err := extractClientCertificate(testCase.b)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.certificate, certificate)
})
}
}

View File

@@ -0,0 +1,22 @@
package configuration
import (
"net"
"strconv"
)
func uint16sToStrings(uint16s []uint16) (strings []string) {
strings = make([]string, len(uint16s))
for i := range uint16s {
strings[i] = strconv.Itoa(int(uint16s[i]))
}
return strings
}
func ipNetsToStrings(ipNets []net.IPNet) (strings []string) {
strings = make([]string, len(ipNets))
for i := range ipNets {
strings[i] = ipNets[i].String()
}
return strings
}

View File

@@ -0,0 +1,89 @@
package configuration
import (
"fmt"
"strconv"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
func (settings *Provider) mullvadLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if len(settings.ServerSelection.ISPs) > 0 {
lines = append(lines, lastIndent+"ISPs: "+commaJoin(settings.ServerSelection.ISPs))
}
if settings.ServerSelection.CustomPort > 0 {
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
}
if settings.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, lastIndent+"IPv6: enabled")
}
return lines
}
func (settings *Provider) readMullvad(r reader) (err error) {
settings.Name = constants.Mullvad
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.MullvadCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.MullvadCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.MullvadHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.MullvadISPChoices())
if err != nil {
return fmt.Errorf("environment variable ISP: %w", err)
}
settings.ServerSelection.CustomPort, err = readCustomPort(r.env, settings.ServerSelection.TCP,
[]uint16{80, 443, 1401}, []uint16{53, 1194, 1195, 1196, 1197, 1300, 1301, 1302, 1303, 1400})
if err != nil {
return err
}
settings.ServerSelection.Owned, err = r.env.YesNo("OWNED", params.Default("no"))
if err != nil {
return fmt.Errorf("environment variable OWNED: %w", err)
}
settings.ExtraConfigOptions.OpenVPNIPv6, err = r.env.OnOff("OPENVPN_IPV6", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable OPENVPN_IPV6: %w", err)
}
return nil
}

View File

@@ -0,0 +1,91 @@
package configuration
import (
"fmt"
"strconv"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
func (settings *Provider) nordvpnLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if len(settings.ServerSelection.Names) > 0 {
lines = append(lines, lastIndent+"Names: "+commaJoin(settings.ServerSelection.Hostnames))
}
if numbersUint16 := settings.ServerSelection.Numbers; len(numbersUint16) > 0 {
numbersString := make([]string, len(numbersUint16))
for i, numberUint16 := range numbersUint16 {
numbersString[i] = strconv.Itoa(int(numberUint16))
}
lines = append(lines, lastIndent+"Numbers: "+commaJoin(numbersString))
}
return lines
}
func (settings *Provider) readNordvpn(r reader) (err error) {
settings.Name = constants.Nordvpn
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.NordvpnRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.NordvpnHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.NordvpnHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_NAME: %w", err)
}
settings.ServerSelection.Numbers, err = readNordVPNServerNumbers(r.env)
if err != nil {
return err
}
return nil
}
func readNordVPNServerNumbers(env params.Env) (numbers []uint16, err error) {
const possiblePortsCount = 65537
possibilities := make([]string, possiblePortsCount)
for i := range possibilities {
possibilities[i] = fmt.Sprintf("%d", i)
}
possibilities[65536] = ""
values, err := env.CSVInside("SERVER_NUMBER", possibilities)
if err != nil {
return nil, err
}
numbers = make([]uint16, len(values))
for i := range values {
n, err := strconv.Atoi(values[i])
if err != nil {
return nil, err
}
numbers[i] = uint16(n)
}
return numbers, nil
}

View File

@@ -0,0 +1,204 @@
package configuration
import (
"errors"
"fmt"
"strconv"
"strings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
// OpenVPN contains settings to configure the OpenVPN client.
type OpenVPN struct {
User string `json:"user"`
Password string `json:"password"`
Verbosity int `json:"verbosity"`
Flags []string `json:"flags"`
MSSFix uint16 `json:"mssfix"`
Root bool `json:"run_as_root"`
Cipher string `json:"cipher"`
Auth string `json:"auth"`
Provider Provider `json:"provider"`
Config string `json:"custom_config"`
Version string `json:"version"`
}
func (settings *OpenVPN) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *OpenVPN) lines() (lines []string) {
lines = append(lines, lastIndent+"OpenVPN:")
lines = append(lines, indent+lastIndent+"Version: "+settings.Version)
lines = append(lines, indent+lastIndent+"Verbosity level: "+strconv.Itoa(settings.Verbosity))
if len(settings.Flags) > 0 {
lines = append(lines, indent+lastIndent+"Flags: "+strings.Join(settings.Flags, " "))
}
if settings.Root {
lines = append(lines, indent+lastIndent+"Run as root: enabled")
}
if len(settings.Cipher) > 0 {
lines = append(lines, indent+lastIndent+"Custom cipher: "+settings.Cipher)
}
if len(settings.Auth) > 0 {
lines = append(lines, indent+lastIndent+"Custom auth algorithm: "+settings.Auth)
}
if len(settings.Config) > 0 {
lines = append(lines, indent+lastIndent+"Custom configuration: "+settings.Config)
}
if settings.Provider.Name == "" {
lines = append(lines, indent+lastIndent+"Provider: custom configuration")
} else {
lines = append(lines, indent+lastIndent+"Provider:")
for _, line := range settings.Provider.lines() {
lines = append(lines, indent+indent+line)
}
}
return lines
}
var (
ErrInvalidVPNProvider = errors.New("invalid VPN provider")
)
func (settings *OpenVPN) read(r reader) (err error) {
vpnsp, err := r.env.Inside("VPNSP", []string{
"cyberghost", "fastestvpn", "hidemyass", "ipvanish", "ivpn", "mullvad", "nordvpn",
"privado", "pia", "private internet access", "privatevpn", "protonvpn",
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn", "windscribe"},
params.Default("private internet access"))
if err != nil {
return fmt.Errorf("environment variable VPNSP: %w", err)
}
if vpnsp == "pia" { // retro compatibility
vpnsp = "private internet access"
}
settings.Provider.Name = vpnsp
settings.Config, err = r.env.Get("OPENVPN_CUSTOM_CONFIG", params.CaseSensitiveValue())
if err != nil {
return fmt.Errorf("environment variable OPENVPN_CUSTOM_CONFIG: %w", err)
}
customConfig := settings.Config != ""
if customConfig {
settings.Provider.Name = ""
}
credentialsRequired := !customConfig && settings.Provider.Name != constants.VPNUnlimited
settings.User, err = r.getFromEnvOrSecretFile("OPENVPN_USER", credentialsRequired, []string{"USER"})
if err != nil {
return fmt.Errorf("environment variable OPENVPN_USER: %w", err)
}
// Remove spaces in user ID to simplify user's life, thanks @JeordyR
settings.User = strings.ReplaceAll(settings.User, " ", "")
if settings.Provider.Name == constants.Mullvad {
settings.Password = "m"
} else {
settings.Password, err = r.getFromEnvOrSecretFile("OPENVPN_PASSWORD", credentialsRequired, []string{"PASSWORD"})
if err != nil {
return err
}
}
settings.Version, err = r.env.Inside("OPENVPN_VERSION",
[]string{constants.Openvpn24, constants.Openvpn25}, params.Default(constants.Openvpn25))
if err != nil {
return fmt.Errorf("environment variable OPENVPN_VERSION: %w", err)
}
settings.Verbosity, err = r.env.IntRange("OPENVPN_VERBOSITY", 0, 6, params.Default("1")) //nolint:gomnd
if err != nil {
return fmt.Errorf("environment variable OPENVPN_VERBOSITY: %w", err)
}
settings.Flags = []string{}
flagsStr, err := r.env.Get("OPENVPN_FLAGS")
if err != nil {
return fmt.Errorf("environment variable OPENVPN_FLAGS: %w", err)
}
if flagsStr != "" {
settings.Flags = strings.Fields(flagsStr)
}
settings.Root, err = r.env.YesNo("OPENVPN_ROOT", params.Default("yes"))
if err != nil {
return fmt.Errorf("environment variable OPENVPN_ROOT: %w", err)
}
settings.Cipher, err = r.env.Get("OPENVPN_CIPHER")
if err != nil {
return fmt.Errorf("environment variable OPENVPN_CIPHER: %w", err)
}
settings.Auth, err = r.env.Get("OPENVPN_AUTH")
if err != nil {
return fmt.Errorf("environment variable OPENVPN_AUTH: %w", err)
}
const maxMSSFix = 10000
mssFix, err := r.env.IntRange("OPENVPN_MSSFIX", 0, maxMSSFix, params.Default("0"))
if err != nil {
return fmt.Errorf("environment variable OPENVPN_MSSFIX: %w", err)
}
settings.MSSFix = uint16(mssFix)
return settings.readProvider(r)
}
func (settings *OpenVPN) readProvider(r reader) error {
var readProvider func(r reader) error
switch settings.Provider.Name {
case "": // custom config
readProvider = func(r reader) error { return nil }
case constants.Cyberghost:
readProvider = settings.Provider.readCyberghost
case constants.Fastestvpn:
readProvider = settings.Provider.readFastestvpn
case constants.HideMyAss:
readProvider = settings.Provider.readHideMyAss
case constants.Ipvanish:
readProvider = settings.Provider.readIpvanish
case constants.Ivpn:
readProvider = settings.Provider.readIvpn
case constants.Mullvad:
readProvider = settings.Provider.readMullvad
case constants.Nordvpn:
readProvider = settings.Provider.readNordvpn
case constants.Privado:
readProvider = settings.Provider.readPrivado
case constants.PrivateInternetAccess:
readProvider = settings.Provider.readPrivateInternetAccess
case constants.Privatevpn:
readProvider = settings.Provider.readPrivatevpn
case constants.Protonvpn:
readProvider = settings.Provider.readProtonvpn
case constants.Purevpn:
readProvider = settings.Provider.readPurevpn
case constants.Surfshark:
readProvider = settings.Provider.readSurfshark
case constants.Torguard:
readProvider = settings.Provider.readTorguard
case constants.VPNUnlimited:
readProvider = settings.Provider.readVPNUnlimited
case constants.Vyprvpn:
readProvider = settings.Provider.readVyprvpn
case constants.Windscribe:
readProvider = settings.Provider.readWindscribe
default:
return fmt.Errorf("%w: %s", ErrInvalidVPNProvider, settings.Provider.Name)
}
return readProvider(r)
}

View File

@@ -0,0 +1,65 @@
package configuration
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_OpenVPN_JSON(t *testing.T) {
t.Parallel()
in := OpenVPN{
Root: true,
Flags: []string{},
Provider: Provider{
Name: "name",
},
}
data, err := json.MarshalIndent(in, "", " ")
require.NoError(t, err)
assert.Equal(t, `{
"user": "",
"password": "",
"verbosity": 0,
"flags": [],
"mssfix": 0,
"run_as_root": true,
"cipher": "",
"auth": "",
"provider": {
"name": "name",
"server_selection": {
"tcp": false,
"regions": null,
"group": "",
"countries": null,
"cities": null,
"hostnames": null,
"names": null,
"isps": null,
"owned": false,
"custom_port": 0,
"numbers": null,
"encryption_preset": "",
"free_only": false,
"stream_only": false
},
"extra_config": {
"encryption_preset": "",
"openvpn_ipv6": false
},
"port_forwarding": {
"enabled": false,
"filepath": ""
}
},
"custom_config": "",
"version": ""
}`, string(data))
var out OpenVPN
err = json.Unmarshal(data, &out)
require.NoError(t, err)
assert.Equal(t, in, out)
}

View File

@@ -0,0 +1,63 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) privadoLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readPrivado(r reader) (err error) {
settings.Name = constants.Privado
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivadoCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PrivadoRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivadoCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivadoHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -0,0 +1,98 @@
package configuration
import (
"fmt"
"strconv"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
func (settings *Provider) privateinternetaccessLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if len(settings.ServerSelection.Names) > 0 {
lines = append(lines, lastIndent+"Names: "+commaJoin(settings.ServerSelection.Names))
}
lines = append(lines, lastIndent+"Encryption preset: "+settings.ServerSelection.EncryptionPreset)
if settings.ServerSelection.CustomPort > 0 {
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
}
if settings.PortForwarding.Enabled {
lines = append(lines, lastIndent+"Port forwarding:")
for _, line := range settings.PortForwarding.lines() {
lines = append(lines, indent+line)
}
}
return lines
}
func (settings *Provider) readPrivateInternetAccess(r reader) (err error) {
settings.Name = constants.PrivateInternetAccess
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
encryptionPreset, err := r.env.Inside("PIA_ENCRYPTION",
[]string{constants.PIAEncryptionPresetNone, constants.PIAEncryptionPresetNormal, constants.PIAEncryptionPresetStrong},
params.RetroKeys([]string{"ENCRYPTION"}, r.onRetroActive),
params.Default(constants.PIACertificateStrong),
)
if err != nil {
return fmt.Errorf("environment variable PIA_ENCRYPTION: %w", err)
}
settings.ServerSelection.EncryptionPreset = encryptionPreset
settings.ExtraConfigOptions.EncryptionPreset = encryptionPreset
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PIAGeoChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PIAHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_NAME", constants.PIANameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_NAME: %w", err)
}
settings.ServerSelection.CustomPort, err = readPortOrZero(r.env, "PORT")
if err != nil {
return fmt.Errorf("environment variable PORT: %w", err)
}
settings.PortForwarding.Enabled, err = r.env.OnOff("PORT_FORWARDING", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable PORT_FORWARDING: %w", err)
}
if settings.PortForwarding.Enabled {
settings.PortForwarding.Filepath, err = r.env.Path("PORT_FORWARDING_STATUS_FILE",
params.Default("/tmp/gluetun/forwarded_port"), params.CaseSensitiveValue())
if err != nil {
return fmt.Errorf("environment variable PORT_FORWARDING_STATUS_FILE: %w", err)
}
}
return nil
}

View File

@@ -0,0 +1,54 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) privatevpnLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readPrivatevpn(r reader) (err error) {
settings.Name = constants.Privatevpn
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivatevpnCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivatevpnCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivatevpnHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -0,0 +1,87 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
func (settings *Provider) protonvpnLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Names) > 0 {
lines = append(lines, lastIndent+"Names: "+commaJoin(settings.ServerSelection.Names))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if settings.ServerSelection.FreeOnly {
lines = append(lines, lastIndent+"Free only: yes")
}
return lines
}
func (settings *Provider) readProtonvpn(r reader) (err error) {
settings.Name = constants.Protonvpn
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.CustomPort, err = readPortOrZero(r.env, "PORT")
if err != nil {
return fmt.Errorf("environment variable PORT: %w", err)
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.ProtonvpnCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.ProtonvpnRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.ProtonvpnCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.ProtonvpnNameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_NAME: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.ProtonvpnHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
settings.ServerSelection.FreeOnly, err = r.env.YesNo("FREE_ONLY", params.Default("no"))
if err != nil {
return fmt.Errorf("environment variable FREE_ONLY: %w", err)
}
return nil
}

View File

@@ -0,0 +1,129 @@
package configuration
import (
"fmt"
"net"
"strings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
// Provider contains settings specific to a VPN provider.
type Provider struct {
Name string `json:"name"`
ServerSelection ServerSelection `json:"server_selection"`
ExtraConfigOptions ExtraConfigOptions `json:"extra_config"`
PortForwarding PortForwarding `json:"port_forwarding"`
}
func (settings *Provider) lines() (lines []string) {
lines = append(lines, lastIndent+strings.Title(settings.Name)+" settings:")
selection := settings.ServerSelection
lines = append(lines, indent+lastIndent+"Network protocol: "+protoToString(selection.TCP))
if selection.TargetIP != nil {
lines = append(lines, indent+lastIndent+"Target IP address: "+selection.TargetIP.String())
}
var providerLines []string
switch strings.ToLower(settings.Name) {
case "cyberghost":
providerLines = settings.cyberghostLines()
case "fastestvpn":
providerLines = settings.fastestvpnLines()
case "hidemyass":
providerLines = settings.hideMyAssLines()
case "ipvanish":
providerLines = settings.ipvanishLines()
case "ivpn":
providerLines = settings.ivpnLines()
case "mullvad":
providerLines = settings.mullvadLines()
case "nordvpn":
providerLines = settings.nordvpnLines()
case "privado":
providerLines = settings.privadoLines()
case "privatevpn":
providerLines = settings.privatevpnLines()
case "private internet access":
providerLines = settings.privateinternetaccessLines()
case "protonvpn":
providerLines = settings.protonvpnLines()
case "purevpn":
providerLines = settings.purevpnLines()
case "surfshark":
providerLines = settings.surfsharkLines()
case "torguard":
providerLines = settings.torguardLines()
case strings.ToLower(constants.VPNUnlimited):
providerLines = settings.vpnUnlimitedLines()
case "vyprvpn":
providerLines = settings.vyprvpnLines()
case "windscribe":
providerLines = settings.windscribeLines()
default:
panic(`Missing lines method for provider "` +
settings.Name + `"! Please create a Github issue.`)
}
for _, line := range providerLines {
lines = append(lines, indent+line)
}
return lines
}
func commaJoin(slice []string) string {
return strings.Join(slice, ", ")
}
func readProtocol(env params.Env) (tcp bool, err error) {
protocol, err := env.Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, params.Default(constants.UDP))
if err != nil {
return false, fmt.Errorf("environment variable PROTOCOL: %w", err)
}
return protocol == constants.TCP, nil
}
func protoToString(tcp bool) string {
if tcp {
return constants.TCP
}
return constants.UDP
}
func readTargetIP(env params.Env) (targetIP net.IP, err error) {
targetIP, err = readIP(env, "OPENVPN_TARGET_IP")
if err != nil {
return nil, fmt.Errorf("environment variable OPENVPN_TARGET_IP: %w", err)
}
return targetIP, nil
}
func readCustomPort(env params.Env, tcp bool,
allowedTCP, allowedUDP []uint16) (port uint16, err error) {
port, err = readPortOrZero(env, "PORT")
if err != nil {
return 0, fmt.Errorf("environment variable PORT: %w", err)
} else if port == 0 {
return 0, nil
}
if tcp {
for i := range allowedTCP {
if allowedTCP[i] == port {
return port, nil
}
}
return 0, fmt.Errorf("environment variable PORT: %w: port %d for TCP protocol", ErrInvalidPort, port)
}
for i := range allowedUDP {
if allowedUDP[i] == port {
return port, nil
}
}
return 0, fmt.Errorf("environment variable PORT: %w: port %d for UDP protocol", ErrInvalidPort, port)
}

View File

@@ -0,0 +1,382 @@
package configuration
import (
"errors"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var errDummy = errors.New("dummy")
func Test_Provider_lines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Provider
lines []string
}{
"cyberghost": {
settings: Provider{
Name: constants.Cyberghost,
ServerSelection: ServerSelection{
Group: "group",
Regions: []string{"a", "El country"},
},
ExtraConfigOptions: ExtraConfigOptions{
ClientKey: "a",
ClientCertificate: "a",
},
},
lines: []string{
"|--Cyberghost settings:",
" |--Network protocol: udp",
" |--Server group: group",
" |--Regions: a, El country",
" |--Client key is set",
" |--Client certificate is set",
},
},
"fastestvpn": {
settings: Provider{
Name: constants.Fastestvpn,
ServerSelection: ServerSelection{
Hostnames: []string{"a", "b"},
Countries: []string{"c", "d"},
},
},
lines: []string{
"|--Fastestvpn settings:",
" |--Network protocol: udp",
" |--Hostnames: a, b",
" |--Countries: c, d",
},
},
"hidemyass": {
settings: Provider{
Name: constants.HideMyAss,
ServerSelection: ServerSelection{
Countries: []string{"a", "b"},
Cities: []string{"c", "d"},
Hostnames: []string{"e", "f"},
},
},
lines: []string{
"|--Hidemyass settings:",
" |--Network protocol: udp",
" |--Countries: a, b",
" |--Cities: c, d",
" |--Hostnames: e, f",
},
},
"ipvanish": {
settings: Provider{
Name: constants.Ipvanish,
ServerSelection: ServerSelection{
Countries: []string{"a", "b"},
Cities: []string{"c", "d"},
Hostnames: []string{"e", "f"},
},
},
lines: []string{
"|--Ipvanish settings:",
" |--Network protocol: udp",
" |--Countries: a, b",
" |--Cities: c, d",
" |--Hostnames: e, f",
},
},
"ivpn": {
settings: Provider{
Name: constants.Ivpn,
ServerSelection: ServerSelection{
Countries: []string{"a", "b"},
Cities: []string{"c", "d"},
Hostnames: []string{"e", "f"},
},
},
lines: []string{
"|--Ivpn settings:",
" |--Network protocol: udp",
" |--Countries: a, b",
" |--Cities: c, d",
" |--Hostnames: e, f",
},
},
"mullvad": {
settings: Provider{
Name: constants.Mullvad,
ServerSelection: ServerSelection{
Countries: []string{"a", "b"},
Cities: []string{"c", "d"},
ISPs: []string{"e", "f"},
CustomPort: 1,
},
ExtraConfigOptions: ExtraConfigOptions{
OpenVPNIPv6: true,
},
},
lines: []string{
"|--Mullvad settings:",
" |--Network protocol: udp",
" |--Countries: a, b",
" |--Cities: c, d",
" |--ISPs: e, f",
" |--Custom port: 1",
" |--IPv6: enabled",
},
},
"nordvpn": {
settings: Provider{
Name: constants.Nordvpn,
ServerSelection: ServerSelection{
Regions: []string{"a", "b"},
Numbers: []uint16{1, 2},
},
},
lines: []string{
"|--Nordvpn settings:",
" |--Network protocol: udp",
" |--Regions: a, b",
" |--Numbers: 1, 2",
},
},
"privado": {
settings: Provider{
Name: constants.Privado,
ServerSelection: ServerSelection{
Hostnames: []string{"a", "b"},
},
},
lines: []string{
"|--Privado settings:",
" |--Network protocol: udp",
" |--Hostnames: a, b",
},
},
"privatevpn": {
settings: Provider{
Name: constants.Privatevpn,
ServerSelection: ServerSelection{
Hostnames: []string{"a", "b"},
Countries: []string{"c", "d"},
Cities: []string{"e", "f"},
},
},
lines: []string{
"|--Privatevpn settings:",
" |--Network protocol: udp",
" |--Countries: c, d",
" |--Cities: e, f",
" |--Hostnames: a, b",
},
},
"protonvpn": {
settings: Provider{
Name: constants.Protonvpn,
ServerSelection: ServerSelection{
Countries: []string{"a", "b"},
Regions: []string{"c", "d"},
Cities: []string{"e", "f"},
Names: []string{"g", "h"},
Hostnames: []string{"i", "j"},
},
},
lines: []string{
"|--Protonvpn settings:",
" |--Network protocol: udp",
" |--Countries: a, b",
" |--Regions: c, d",
" |--Cities: e, f",
" |--Names: g, h",
" |--Hostnames: i, j",
},
},
"private internet access": {
settings: Provider{
Name: constants.PrivateInternetAccess,
ServerSelection: ServerSelection{
Regions: []string{"a", "b"},
EncryptionPreset: constants.PIAEncryptionPresetStrong,
CustomPort: 1,
},
PortForwarding: PortForwarding{
Enabled: true,
Filepath: string("/here"),
},
},
lines: []string{
"|--Private Internet Access settings:",
" |--Network protocol: udp",
" |--Regions: a, b",
" |--Encryption preset: strong",
" |--Custom port: 1",
" |--Port forwarding:",
" |--File path: /here",
},
},
"purevpn": {
settings: Provider{
Name: constants.Purevpn,
ServerSelection: ServerSelection{
Regions: []string{"a", "b"},
Countries: []string{"c", "d"},
Cities: []string{"e", "f"},
},
},
lines: []string{
"|--Purevpn settings:",
" |--Network protocol: udp",
" |--Regions: a, b",
" |--Countries: c, d",
" |--Cities: e, f",
},
},
"surfshark": {
settings: Provider{
Name: constants.Surfshark,
ServerSelection: ServerSelection{
Regions: []string{"a", "b"},
},
},
lines: []string{
"|--Surfshark settings:",
" |--Network protocol: udp",
" |--Regions: a, b",
},
},
"torguard": {
settings: Provider{
Name: constants.Torguard,
ServerSelection: ServerSelection{
Countries: []string{"a", "b"},
Cities: []string{"c", "d"},
Hostnames: []string{"e"},
},
},
lines: []string{
"|--Torguard settings:",
" |--Network protocol: udp",
" |--Countries: a, b",
" |--Cities: c, d",
" |--Hostnames: e",
},
},
constants.VPNUnlimited: {
settings: Provider{
Name: constants.VPNUnlimited,
ServerSelection: ServerSelection{
Countries: []string{"a", "b"},
Cities: []string{"c", "d"},
Hostnames: []string{"e", "f"},
FreeOnly: true,
StreamOnly: true,
},
ExtraConfigOptions: ExtraConfigOptions{
ClientKey: "a",
},
},
lines: []string{
"|--Vpn Unlimited settings:",
" |--Network protocol: udp",
" |--Countries: a, b",
" |--Cities: c, d",
" |--Hostnames: e, f",
" |--Free servers only",
" |--Stream servers only",
" |--Client key is set",
},
},
"vyprvpn": {
settings: Provider{
Name: constants.Vyprvpn,
ServerSelection: ServerSelection{
Regions: []string{"a", "b"},
},
},
lines: []string{
"|--Vyprvpn settings:",
" |--Network protocol: udp",
" |--Regions: a, b",
},
},
"windscribe": {
settings: Provider{
Name: constants.Windscribe,
ServerSelection: ServerSelection{
Regions: []string{"a", "b"},
Cities: []string{"c", "d"},
Hostnames: []string{"e", "f"},
CustomPort: 1,
},
},
lines: []string{
"|--Windscribe settings:",
" |--Network protocol: udp",
" |--Regions: a, b",
" |--Cities: c, d",
" |--Hostnames: e, f",
" |--Custom port: 1",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.lines()
assert.Equal(t, testCase.lines, lines)
})
}
}
func Test_readProtocol(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
mockStr string
mockErr error
tcp bool
err error
}{
"error": {
mockErr: errDummy,
err: errors.New("environment variable PROTOCOL: dummy"),
},
"success": {
mockStr: "tcp",
tcp: true,
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
env := mock_params.NewMockEnv(ctrl)
env.EXPECT().
Inside("PROTOCOL", []string{"tcp", "udp"}, gomock.Any()).
Return(testCase.mockStr, testCase.mockErr)
tcp, err := readProtocol(env)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.tcp, tcp)
})
}
}

View File

@@ -0,0 +1,47 @@
package configuration
import (
"fmt"
"strings"
"time"
"github.com/qdm12/golibs/params"
)
type PublicIP struct {
Period time.Duration `json:"period"`
IPFilepath string `json:"ip_filepath"`
}
func (settings *PublicIP) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *PublicIP) lines() (lines []string) {
if settings.Period == 0 {
lines = append(lines, lastIndent+"Public IP getter: disabled")
return lines
}
lines = append(lines, lastIndent+"Public IP getter:")
lines = append(lines, indent+lastIndent+"Fetch period: "+settings.Period.String())
lines = append(lines, indent+lastIndent+"IP file: "+settings.IPFilepath)
return lines
}
func (settings *PublicIP) read(r reader) (err error) {
settings.Period, err = r.env.Duration("PUBLICIP_PERIOD", params.Default("12h"))
if err != nil {
return fmt.Errorf("environment variable PUBLICIP_PERIOD: %w", err)
}
settings.IPFilepath, err = r.env.Path("PUBLICIP_FILE", params.CaseSensitiveValue(),
params.Default("/tmp/gluetun/ip"),
params.RetroKeys([]string{"IP_STATUS_FILE"}, r.onRetroActive))
if err != nil {
return fmt.Errorf("environment variable PUBLICIP_FILE (or IP_STATUS_FILE): %w", err)
}
return nil
}

View File

@@ -0,0 +1,63 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) purevpnLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readPurevpn(r reader) (err error) {
settings.Name = constants.Purevpn
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PurevpnRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PurevpnCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PurevpnCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PurevpnHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -0,0 +1,122 @@
package configuration
import (
"errors"
"fmt"
"net"
"strconv"
"strings"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params"
"github.com/qdm12/golibs/verification"
)
type reader struct {
env params.Env
logger logging.Logger
regex verification.Regex
}
func newReader(env params.Env, logger logging.Logger) reader {
return reader{
env: env,
logger: logger,
regex: verification.NewRegex(),
}
}
func (r *reader) onRetroActive(oldKey, newKey string) {
r.logger.Warn(
"You are using the old environment variable " + oldKey +
", please consider changing it to " + newKey)
}
var (
ErrInvalidPort = errors.New("invalid port")
)
func readCSVPorts(env params.Env, key string) (ports []uint16, err error) {
s, err := env.Get(key)
if err != nil {
return nil, err
} else if s == "" {
return nil, nil
}
portsStr := strings.Split(s, ",")
ports = make([]uint16, len(portsStr))
for i, portStr := range portsStr {
portInt, err := strconv.Atoi(portStr)
if err != nil {
return nil, fmt.Errorf("%w: %s: %s", ErrInvalidPort, portStr, err)
} else if portInt <= 0 || portInt > 65535 {
return nil, fmt.Errorf("%w: %d: must be between 1 and 65535", ErrInvalidPort, portInt)
}
ports[i] = uint16(portInt)
}
return ports, nil
}
var (
ErrInvalidIPNet = errors.New("invalid IP network")
)
func readCSVIPNets(env params.Env, key string, options ...params.OptionSetter) (
ipNets []net.IPNet, err error) {
s, err := env.Get(key, options...)
if err != nil {
return nil, err
} else if s == "" {
return nil, nil
}
ipNetsStr := strings.Split(s, ",")
ipNets = make([]net.IPNet, len(ipNetsStr))
for i, ipNetStr := range ipNetsStr {
_, ipNet, err := net.ParseCIDR(ipNetStr)
if err != nil {
return nil, fmt.Errorf("%w: %s: %s",
ErrInvalidIPNet, ipNetStr, err)
} else if ipNet == nil {
return nil, fmt.Errorf("%w: %s: subnet is nil", ErrInvalidIPNet, ipNetStr)
}
ipNets[i] = *ipNet
}
return ipNets, nil
}
var (
ErrInvalidIP = errors.New("invalid IP address")
)
func readIP(env params.Env, key string) (ip net.IP, err error) {
s, err := env.Get(key)
if s == "" {
return nil, nil
} else if err != nil {
return nil, err
}
ip = net.ParseIP(s)
if ip == nil {
return nil, fmt.Errorf("%w: %s", ErrInvalidIP, s)
}
return ip, nil
}
func readPortOrZero(env params.Env, key string) (port uint16, err error) {
s, err := env.Get(key)
if err != nil {
return 0, err
}
if s == "" || s == "0" {
return 0, nil
}
return env.Port(key)
}

View File

@@ -0,0 +1,119 @@
package configuration
import (
"errors"
"fmt"
"io"
"os"
"strings"
"github.com/qdm12/golibs/params"
)
var (
ErrGetSecretFilepath = errors.New("cannot get secret file path from env")
ErrReadSecretFile = errors.New("cannot read secret file")
ErrSecretFileIsEmpty = errors.New("secret file is empty")
ErrReadNonSecretFile = errors.New("cannot read non secret file")
ErrFilesDoNotExist = errors.New("files do not exist")
)
func cleanSuffix(value string) string {
value = strings.TrimSuffix(value, "\n")
value = strings.TrimSuffix(value, "\r")
return value
}
func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKeys []string) (value string, err error) {
envOptions := []params.OptionSetter{
params.Compulsory(), // to fallback on file reading
params.CaseSensitiveValue(),
params.Unset(),
params.RetroKeys(retroKeys, r.onRetroActive),
}
value, envErr := r.env.Get(envKey, envOptions...)
if envErr == nil {
value = cleanSuffix(value)
return value, nil
}
secretFilepathEnvKey := envKey + "_SECRETFILE"
defaultSecretFile := "/run/secrets/" + strings.ToLower(envKey)
filepath, err := r.env.Get(secretFilepathEnvKey,
params.CaseSensitiveValue(),
params.Default(defaultSecretFile),
)
if err != nil {
return "", fmt.Errorf("%w: environment variable %s: %s",
ErrGetSecretFilepath, secretFilepathEnvKey, err)
}
file, fileErr := os.OpenFile(filepath, os.O_RDONLY, 0)
if os.IsNotExist(fileErr) {
if compulsory {
return "", envErr
}
return "", nil
} else if fileErr != nil {
return "", fmt.Errorf("%w: %s", ErrReadSecretFile, fileErr)
}
b, err := io.ReadAll(file)
if err != nil {
return "", fmt.Errorf("%w: %s", ErrReadSecretFile, err)
}
value = string(b)
value = cleanSuffix(value)
if compulsory && value == "" {
return "", ErrSecretFileIsEmpty
}
return value, nil
}
// Tries to read from the secret file then the non secret file.
func (r *reader) getFromFileOrSecretFile(secretName, filepath string) (
b []byte, err error) {
defaultSecretFile := "/run/secrets/" + strings.ToLower(secretName)
key := strings.ToUpper(secretName) + "_SECRETFILE"
secretFilepath, err := r.env.Get(key,
params.CaseSensitiveValue(),
params.Default(defaultSecretFile),
)
if err != nil {
return b, fmt.Errorf("environment variable %s: %w: %s", key, ErrGetSecretFilepath, err)
}
b, err = readFromFile(secretFilepath)
if err != nil && !os.IsNotExist(err) {
return b, fmt.Errorf("%w: %s", ErrReadSecretFile, err)
} else if err == nil {
return b, nil
}
// Secret file does not exist, try the non secret file
b, err = readFromFile(filepath)
if err != nil && !os.IsNotExist(err) {
return nil, fmt.Errorf("%w: %s", ErrReadSecretFile, err)
} else if err == nil {
return b, nil
}
return nil, fmt.Errorf("%w: %s and %s", ErrFilesDoNotExist, secretFilepath, filepath)
}
func readFromFile(filepath string) (b []byte, err error) {
file, err := os.Open(filepath)
if err != nil {
return nil, err
}
b, err = io.ReadAll(file)
if err != nil {
_ = file.Close()
return nil, err
}
if err := file.Close(); err != nil {
return nil, err
}
return b, nil
}

View File

@@ -0,0 +1,63 @@
package configuration
import (
"net"
)
type ServerSelection struct { //nolint:maligned
// Common
TCP bool `json:"tcp"` // UDP if TCP is false
TargetIP net.IP `json:"target_ip,omitempty"`
// TODO comments
// Cyberghost, PIA, Protonvpn, Surfshark, Windscribe, Vyprvpn, NordVPN
Regions []string `json:"regions"`
// Cyberghost
Group string `json:"group"`
// Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
Countries []string `json:"countries"`
// HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited, Windscribe
Cities []string `json:"cities"`
// Fastestvpn, HideMyAss, IPVanish, IVPN, PrivateVPN, Windscribe, Privado, Protonvpn, VPNUnlimited
Hostnames []string `json:"hostnames"`
Names []string `json:"names"` // Protonvpn
// Mullvad
ISPs []string `json:"isps"`
Owned bool `json:"owned"`
// Mullvad, Windscribe, PIA
CustomPort uint16 `json:"custom_port"`
// NordVPN
Numbers []uint16 `json:"numbers"`
// PIA
EncryptionPreset string `json:"encryption_preset"`
// ProtonVPN
FreeOnly bool `json:"free_only"`
// VPNUnlimited
StreamOnly bool `json:"stream_only"`
}
type ExtraConfigOptions struct {
ClientCertificate string `json:"-"` // Cyberghost
ClientKey string `json:"-"` // Cyberghost, VPNUnlimited
EncryptionPreset string `json:"encryption_preset"` // PIA
OpenVPNIPv6 bool `json:"openvpn_ipv6"` // Mullvad
}
// PortForwarding contains settings for port forwarding.
type PortForwarding struct {
Enabled bool `json:"enabled"`
Filepath string `json:"filepath"`
}
func (p *PortForwarding) lines() (lines []string) {
return []string{
lastIndent + "File path: " + p.Filepath,
}
}

View File

@@ -0,0 +1,50 @@
package configuration
import (
"fmt"
"strconv"
"strings"
"github.com/qdm12/golibs/params"
)
// ControlServer contains settings to customize the control server operation.
type ControlServer struct {
Port uint16
Log bool
}
func (settings *ControlServer) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *ControlServer) lines() (lines []string) {
lines = append(lines, lastIndent+"HTTP control server:")
lines = append(lines, indent+lastIndent+"Listening port: "+strconv.Itoa(int(settings.Port)))
if settings.Log {
lines = append(lines, indent+lastIndent+"Logging: enabled")
}
return lines
}
func (settings *ControlServer) read(r reader) (err error) {
settings.Log, err = r.env.OnOff("HTTP_CONTROL_SERVER_LOG", params.Default("on"))
if err != nil {
return fmt.Errorf("environment variable HTTP_CONTROL_SERVER_LOG: %w", err)
}
var warning string
settings.Port, warning, err = r.env.ListeningPort(
"HTTP_CONTROL_SERVER_PORT", params.Default("8000"))
if len(warning) > 0 {
r.logger.Warn(warning)
}
if err != nil {
return fmt.Errorf("environment variable HTTP_CONTROL_SERVER_PORT: %w", err)
}
return nil
}

View File

@@ -0,0 +1,117 @@
package configuration
import (
"errors"
"fmt"
"strings"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params"
)
// Settings contains all settings for the program to run.
type Settings struct {
OpenVPN OpenVPN
System System
DNS DNS
Firewall Firewall
HTTPProxy HTTPProxy
ShadowSocks ShadowSocks
Updater Updater
PublicIP PublicIP
VersionInformation bool
ControlServer ControlServer
Health Health
}
func (settings *Settings) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *Settings) lines() (lines []string) {
lines = append(lines, "Settings summary below:")
lines = append(lines, settings.OpenVPN.lines()...)
lines = append(lines, settings.DNS.lines()...)
lines = append(lines, settings.Firewall.lines()...)
lines = append(lines, settings.System.lines()...)
lines = append(lines, settings.HTTPProxy.lines()...)
lines = append(lines, settings.ShadowSocks.lines()...)
lines = append(lines, settings.Health.lines()...)
lines = append(lines, settings.ControlServer.lines()...)
lines = append(lines, settings.Updater.lines()...)
lines = append(lines, settings.PublicIP.lines()...)
if settings.VersionInformation {
lines = append(lines, lastIndent+"Github version information: enabled")
}
return lines
}
var (
ErrOpenvpn = errors.New("cannot read Openvpn settings")
ErrSystem = errors.New("cannot read System settings")
ErrDNS = errors.New("cannot read DNS settings")
ErrFirewall = errors.New("cannot read firewall settings")
ErrHTTPProxy = errors.New("cannot read HTTP proxy settings")
ErrShadowsocks = errors.New("cannot read Shadowsocks settings")
ErrControlServer = errors.New("cannot read control server settings")
ErrUpdater = errors.New("cannot read Updater settings")
ErrPublicIP = errors.New("cannot read Public IP getter settings")
ErrHealth = errors.New("cannot read health settings")
)
// Read obtains all configuration options for the program and returns an error as soon
// as an error is encountered reading them.
func (settings *Settings) Read(env params.Env, logger logging.Logger) (err error) {
r := newReader(env, logger)
settings.VersionInformation, err = r.env.OnOff("VERSION_INFORMATION", params.Default("on"))
if err != nil {
return fmt.Errorf("environment variable VERSION_INFORMATION: %w", err)
}
if err := settings.OpenVPN.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrOpenvpn, err)
}
if err := settings.System.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrSystem, err)
}
if err := settings.DNS.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrDNS, err)
}
if err := settings.Firewall.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrFirewall, err)
}
if err := settings.HTTPProxy.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrHTTPProxy, err)
}
if err := settings.ShadowSocks.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrShadowsocks, err)
}
if err := settings.ControlServer.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrControlServer, err)
}
if err := settings.Updater.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrUpdater, err)
}
if ip := settings.DNS.PlaintextAddress; ip != nil {
settings.Updater.DNSAddress = ip.String()
}
if err := settings.PublicIP.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrPublicIP, err)
}
if err := settings.Health.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrHealth, err)
}
return nil
}

View File

@@ -0,0 +1,61 @@
package configuration
import (
"testing"
"github.com/qdm12/gluetun/internal/constants"
"github.com/stretchr/testify/assert"
)
func Test_Settings_lines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Settings
lines []string
}{
"default settings": {
settings: Settings{
OpenVPN: OpenVPN{
Version: constants.Openvpn25,
Provider: Provider{
Name: constants.Mullvad,
},
},
},
lines: []string{
"Settings summary below:",
"|--OpenVPN:",
" |--Version: 2.5",
" |--Verbosity level: 0",
" |--Provider:",
" |--Mullvad settings:",
" |--Network protocol: udp",
"|--DNS:",
"|--Firewall: disabled ⚠️",
"|--System:",
" |--Process user ID: 0",
" |--Process group ID: 0",
" |--Timezone: NOT SET ⚠️ - it can cause time related issues",
"|--Health:",
" |--Server address: ",
" |--OpenVPN:",
" |--Initial duration: 0s",
"|--HTTP control server:",
" |--Listening port: 0",
"|--Public IP getter: disabled",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.lines()
assert.Equal(t, testCase.lines, lines)
})
}
}

View File

@@ -0,0 +1,75 @@
package configuration
import (
"fmt"
"strconv"
"strings"
"github.com/qdm12/golibs/params"
)
// ShadowSocks contains settings to configure the Shadowsocks server.
type ShadowSocks struct {
Method string
Password string
Port uint16
Enabled bool
Log bool
}
func (settings *ShadowSocks) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *ShadowSocks) lines() (lines []string) {
if !settings.Enabled {
return nil
}
lines = append(lines, lastIndent+"Shadowsocks server:")
lines = append(lines, indent+lastIndent+"Listening port: "+strconv.Itoa(int(settings.Port)))
lines = append(lines, indent+lastIndent+"Method: "+settings.Method)
if settings.Log {
lines = append(lines, indent+lastIndent+"Logging: enabled")
}
return lines
}
func (settings *ShadowSocks) read(r reader) (err error) {
settings.Enabled, err = r.env.OnOff("SHADOWSOCKS", params.Default("off"))
if !settings.Enabled {
return nil
} else if err != nil {
return fmt.Errorf("environment variable SHADOWSOCKS: %w", err)
}
settings.Password, err = r.getFromEnvOrSecretFile("SHADOWSOCKS_PASSWORD", settings.Enabled, nil)
if err != nil {
return err
}
settings.Log, err = r.env.OnOff("SHADOWSOCKS_LOG", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable SHADOWSOCKS_LOG: %w", err)
}
settings.Method, err = r.env.Get("SHADOWSOCKS_METHOD", params.Default("chacha20-ietf-poly1305"))
if err != nil {
return fmt.Errorf("environment variable SHADOWSOCKS_METHOD: %w", err)
}
var warning string
settings.Port, warning, err = r.env.ListeningPort("SHADOWSOCKS_PORT", params.Default("8388"))
if len(warning) > 0 {
r.logger.Warn(warning)
}
if err != nil {
return fmt.Errorf("environment variable SHADOWSOCKS_PORT: %w", err)
}
return nil
}

View File

@@ -0,0 +1,45 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) surfsharkLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readSurfshark(r reader) (err error) {
settings.Name = constants.Surfshark
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.SurfsharkRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.SurfsharkHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -0,0 +1,55 @@
package configuration
import (
"fmt"
"strconv"
"strings"
"github.com/qdm12/golibs/params"
)
// System contains settings to configure system related elements.
type System struct {
PUID int
PGID int
Timezone string
}
func (settings *System) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *System) lines() (lines []string) {
lines = append(lines, lastIndent+"System:")
lines = append(lines, indent+lastIndent+"Process user ID: "+strconv.Itoa(settings.PUID))
lines = append(lines, indent+lastIndent+"Process group ID: "+strconv.Itoa(settings.PGID))
if len(settings.Timezone) > 0 {
lines = append(lines, indent+lastIndent+"Timezone: "+settings.Timezone)
} else {
lines = append(lines, indent+lastIndent+"Timezone: NOT SET ⚠️ - it can cause time related issues")
}
return lines
}
func (settings *System) read(r reader) (err error) {
const maxID = 65535
settings.PUID, err = r.env.IntRange("PUID", 0, maxID, params.Default("1000"),
params.RetroKeys([]string{"UID"}, r.onRetroActive))
if err != nil {
return fmt.Errorf("environment variable PUID (or UID): %w", err)
}
settings.PGID, err = r.env.IntRange("PGID", 0, maxID, params.Default("1000"),
params.RetroKeys([]string{"GID"}, r.onRetroActive))
if err != nil {
return fmt.Errorf("environment variable PGID (or GID): %w", err)
}
settings.Timezone, err = r.env.Get("TZ")
if err != nil {
return fmt.Errorf("environment variable TZ: %w", err)
}
return nil
}

View File

@@ -0,0 +1,54 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) torguardLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readTorguard(r reader) (err error) {
settings.Name = constants.Torguard
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.TorguardCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.TorguardCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.TorguardHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -0,0 +1,79 @@
package configuration
import (
"errors"
"fmt"
"strings"
"github.com/qdm12/dns/pkg/provider"
"github.com/qdm12/golibs/params"
"inet.af/netaddr"
)
func (settings *DNS) readUnbound(r reader) (err error) {
if err := settings.readUnboundProviders(r.env); err != nil {
return err
}
settings.Unbound.ListeningPort = 53
settings.Unbound.Caching, err = r.env.OnOff("DOT_CACHING", params.Default("on"))
if err != nil {
return fmt.Errorf("environment variable DOT_CACHING: %w", err)
}
settings.Unbound.IPv4 = true
settings.Unbound.IPv6, err = r.env.OnOff("DOT_IPV6", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable DOT_IPV6: %w", err)
}
verbosityLevel, err := r.env.IntRange("DOT_VERBOSITY", 0, 5, params.Default("1")) //nolint:gomnd
if err != nil {
return fmt.Errorf("environment variable DOT_VERBOSITY: %w", err)
}
settings.Unbound.VerbosityLevel = uint8(verbosityLevel)
verbosityDetailsLevel, err := r.env.IntRange("DOT_VERBOSITY_DETAILS", 0, 4, params.Default("0")) //nolint:gomnd
if err != nil {
return fmt.Errorf("environment variable DOT_VERBOSITY_DETAILS: %w", err)
}
settings.Unbound.VerbosityDetailsLevel = uint8(verbosityDetailsLevel)
validationLogLevel, err := r.env.IntRange("DOT_VALIDATION_LOGLEVEL", 0, 2, params.Default("0")) //nolint:gomnd
if err != nil {
return fmt.Errorf("environment variable DOT_VALIDATION_LOGLEVEL: %w", err)
}
settings.Unbound.ValidationLogLevel = uint8(validationLogLevel)
settings.Unbound.AccessControl.Allowed = []netaddr.IPPrefix{
netaddr.IPPrefixFrom(netaddr.IPv4(0, 0, 0, 0), 0),
netaddr.IPPrefixFrom(netaddr.IPv6Raw([16]byte{}), 0),
}
return nil
}
var (
ErrInvalidDNSOverTLSProvider = errors.New("invalid DNS over TLS provider")
)
func (settings *DNS) readUnboundProviders(env params.Env) (err error) {
s, err := env.Get("DOT_PROVIDERS", params.Default("cloudflare"))
if err != nil {
return fmt.Errorf("environment variable DOT_PROVIDERS: %w", err)
}
for _, field := range strings.Split(s, ",") {
dnsProvider, err := provider.Parse(field)
if err != nil {
return fmt.Errorf("%w: %s", ErrInvalidDNSOverTLSProvider, err)
}
settings.Unbound.Providers = append(settings.Unbound.Providers, dnsProvider)
}
return nil
}
var (
ErrInvalidHostname = errors.New("invalid hostname")
)

View File

@@ -0,0 +1,80 @@
package configuration
import (
"errors"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/dns/pkg/provider"
"github.com/qdm12/dns/pkg/unbound"
"github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_DNS_readUnboundProviders(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
envValue string
envErr error
expected DNS
err error
}{
"bad value": {
envValue: "invalid",
err: errors.New(`invalid DNS over TLS provider: cannot parse provider: "invalid"`),
},
"env error": {
envErr: errors.New("env error"),
err: errors.New("environment variable DOT_PROVIDERS: env error"),
},
"multiple valid values": {
envValue: "cloudflare,google",
expected: DNS{
Unbound: unbound.Settings{
Providers: []provider.Provider{
provider.Cloudflare(),
provider.Google(),
},
},
},
},
"one invalid value in two": {
envValue: "cloudflare,invalid",
expected: DNS{
Unbound: unbound.Settings{
Providers: []provider.Provider{
provider.Cloudflare(),
},
},
},
err: errors.New(`invalid DNS over TLS provider: cannot parse provider: "invalid"`),
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
env := mock_params.NewMockEnv(ctrl)
env.EXPECT().Get("DOT_PROVIDERS", gomock.Any()).
Return(testCase.envValue, testCase.envErr)
var settings DNS
err := settings.readUnboundProviders(env)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.expected, settings)
})
}
}

View File

@@ -0,0 +1,85 @@
package configuration
import (
"fmt"
"strings"
"time"
"github.com/qdm12/golibs/params"
)
type Updater struct {
Period time.Duration `json:"period"`
DNSAddress string `json:"dns_address"`
Cyberghost bool `json:"cyberghost"`
Fastestvpn bool `json:"fastestvpn"`
HideMyAss bool `json:"hidemyass"`
Ipvanish bool `json:"ipvanish"`
Ivpn bool `json:"ivpn"`
Mullvad bool `json:"mullvad"`
Nordvpn bool `json:"nordvpn"`
PIA bool `json:"pia"`
Privado bool `json:"privado"`
Privatevpn bool `json:"privatevpn"`
Protonvpn bool `json:"protonvpn"`
Purevpn bool `json:"purevpn"`
Surfshark bool `json:"surfshark"`
Torguard bool `json:"torguard"`
VPNUnlimited bool `json:"vpnunlimited"`
Vyprvpn bool `json:"vyprvpn"`
Windscribe bool `json:"windscribe"`
// The two below should be used in CLI mode only
CLI bool `json:"-"`
}
func (settings *Updater) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *Updater) lines() (lines []string) {
if settings.Period == 0 {
return nil
}
lines = append(lines, lastIndent+"Updater:")
lines = append(lines, indent+lastIndent+"Period: every "+settings.Period.String())
return lines
}
func (settings *Updater) EnableAll() {
settings.Cyberghost = true
settings.HideMyAss = true
settings.Ipvanish = true
settings.Ivpn = true
settings.Mullvad = true
settings.Nordvpn = true
settings.Privado = true
settings.PIA = true
settings.Privado = true
settings.Privatevpn = true
settings.Protonvpn = true
settings.Purevpn = true
settings.Surfshark = true
settings.Torguard = true
settings.VPNUnlimited = true
settings.Vyprvpn = true
settings.Windscribe = true
}
func (settings *Updater) read(r reader) (err error) {
settings.EnableAll()
// use cloudflare in plaintext to not be blocked by DNS over TLS by default.
// If a plaintext address is set in the DNS settings, this one will be used.
// TODO use custom future encrypted DNS written in Go without blocking
// as it's too much trouble to start another parallel unbound instance for now.
settings.DNSAddress = "1.1.1.1"
settings.Period, err = r.env.Duration("UPDATER_PERIOD", params.Default("0"))
if err != nil {
return fmt.Errorf("environment variable UPDATER_PERIOD: %w", err)
}
return nil
}

View File

@@ -0,0 +1,87 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
func (settings *Provider) vpnUnlimitedLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if settings.ServerSelection.FreeOnly {
lines = append(lines, lastIndent+"Free servers only")
}
if settings.ServerSelection.StreamOnly {
lines = append(lines, lastIndent+"Stream servers only")
}
if settings.ExtraConfigOptions.ClientKey != "" {
lines = append(lines, lastIndent+"Client key is set")
}
return lines
}
func (settings *Provider) readVPNUnlimited(r reader) (err error) {
settings.Name = constants.VPNUnlimited
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ExtraConfigOptions.ClientKey, err = readClientKey(r)
if err != nil {
return err
}
settings.ExtraConfigOptions.ClientCertificate, err = readClientCertificate(r)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.VPNUnlimitedCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.VPNUnlimitedCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.VPNUnlimitedHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
settings.ServerSelection.FreeOnly, err = r.env.YesNo("FREE_ONLY", params.Default("no"))
if err != nil {
return fmt.Errorf("environment variable FREE_ONLY: %w", err)
}
settings.ServerSelection.StreamOnly, err = r.env.YesNo("STREAM_ONLY", params.Default("no"))
if err != nil {
return fmt.Errorf("environment variable STREAM_ONLY: %w", err)
}
return nil
}

View File

@@ -0,0 +1,42 @@
package configuration
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_Provider_vpnUnlimitedLines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Provider
lines []string
}{
"empty settings": {},
"full settings": {
settings: Provider{
ServerSelection: ServerSelection{
Countries: []string{"A", "B"},
Cities: []string{"C", "D"},
Hostnames: []string{"E", "F"},
},
},
lines: []string{
"|--Countries: A, B",
"|--Cities: C, D",
"|--Hostnames: E, F",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.vpnUnlimitedLines()
assert.Equal(t, testCase.lines, lines)
})
}
}

View File

@@ -0,0 +1,36 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) vyprvpnLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
return lines
}
func (settings *Provider) readVyprvpn(r reader) (err error) {
settings.Name = constants.Vyprvpn
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.VyprvpnRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
return nil
}

View File

@@ -0,0 +1,66 @@
package configuration
import (
"fmt"
"strconv"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) windscribeLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if settings.ServerSelection.CustomPort > 0 {
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
}
return lines
}
func (settings *Provider) readWindscribe(r reader) (err error) {
settings.Name = constants.Windscribe
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.WindscribeRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.WindscribeCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.WindscribeHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
settings.ServerSelection.CustomPort, err = readCustomPort(r.env, settings.ServerSelection.TCP,
[]uint16{21, 22, 80, 123, 143, 443, 587, 1194, 3306, 8080, 54783},
[]uint16{53, 80, 123, 443, 1194, 54783})
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,11 @@
package constants
import "github.com/fatih/color"
func ColorUnbound() *color.Color {
return color.New(color.FgCyan)
}
func ColorOpenvpn() *color.Color {
return color.New(color.FgHiMagenta)
}

View File

@@ -0,0 +1,25 @@
// Package constants defines constants shared throughout the program.
// It also defines constant maps and slices using functions.
package constants
import "sort"
func makeChoicesUnique(choices []string) []string {
uniqueChoices := map[string]struct{}{}
for _, choice := range choices {
uniqueChoices[choice] = struct{}{}
}
uniqueChoicesSlice := make([]string, len(uniqueChoices))
i := 0
for choice := range uniqueChoices {
uniqueChoicesSlice[i] = choice
i++
}
sort.Slice(uniqueChoicesSlice, func(i, j int) bool {
return uniqueChoicesSlice[i] < uniqueChoicesSlice[j]
})
return uniqueChoicesSlice
}

View File

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

View File

@@ -0,0 +1,45 @@
package constants
import (
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll
const (
CyberghostCertificate = "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"
)
func CyberghostRegionChoices() (choices []string) {
servers := CyberghostServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
}
return makeUnique(choices)
}
func CyberghostGroupChoices() (choices []string) {
servers := CyberghostServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Group
}
return makeUnique(choices)
}
func CyberghostHostnameChoices() (choices []string) {
servers := CyberghostServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
return makeUnique(choices)
}
// CyberghostServers returns a slice with the server information for each
// of the Cyberghost server.
func CyberghostServers() (servers []models.CyberghostServer) {
servers = make([]models.CyberghostServer, len(allServers.Cyberghost.Servers))
copy(servers, allServers.Cyberghost.Servers)
return servers
}

View File

@@ -0,0 +1,36 @@
package constants
import (
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll
const (
FastestvpnCertificate = "MIIFQjCCAyqgAwIBAgIIUfxepT+rr8owDQYJKoZIhvcNAQEMBQAwPzELMAkGA1UEBhMCS1kxEzARBgNVBAoTCkZhc3Rlc3RWUE4xGzAZBgNVBAMTEkZhc3Rlc3RWUE4gUm9vdCBDQTAeFw0xNzA5MTYwMDAxNDZaFw0yNzA5MTQwMDAxNDZaMD8xCzAJBgNVBAYTAktZMRMwEQYDVQQKEwpGYXN0ZXN0VlBOMRswGQYDVQQDExJGYXN0ZXN0VlBOIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC1Xj+WfPTozFynFqc+c3CVrggIllaXEl5bY5VgFynXkqCTM6lSrfC4pNjGXUbqWe6RnGJbM4/6kUn+lQDjFSQV1rzP2eDS8+r5+X2WXh4AoeNRUWhvSG+HiHD/B2EFK+Nd5BRSdUjpKWAtsCmT2bBt7nT0jN1OdeNrLJeyF8siAqv/oQzKznF9aIe/N01b2M8ZOFTzoXi2fZAckgGWui8NB/lzkVIJqSkAPRL8qiJLuRCPVOX1PFD8vV//R8/QumtfbcYBMo6vCk2HmWdrh5OQHPxb3KJtbtG+Z1j8x6HGEAe17djYepBiRMyCEQvYgfD6tvFylc4IquhqE9yaP60PJod5TxpWnRQ6HIGSeBm+S+rYSMalTZ8+pUqOOA+IQCYpfpx6EKIJL/VsW2C7cXdvudxDhXPI5lR/QidCb9Ohq3WkfxXaYwzrngdg2avmNqId9R4KESuM9GoHW0dszfyBCh5wYfeaffMElfDam3B92NUwyhZwtIiv623WVXY9PPz+EDjSJsIAu2Vi1vdJyA4nD4k9Lwmx/1zTc/UaYVLsiBqL2WdfvFTeoWoV+dNxQXSEPhB8gwi8x4O4lZW0cwVy/6fa8KMY8gZbcbSTr7U5bRERfW8l+jY+mYKQ/M/ccgpxaHiw1/+4LWfbJQ7VhJJrTyN0C36FQzY1URkSXg+53wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmVEL4x6xdCqiqu2OBLs27EA8xGYwDQYJKoZIhvcNAQEMBQADggIBABCpITvO1+R4T9v2+onHiFxU5JjtCZ0zkXqRCMp/Z0UIYbeo1p07pZCPAUjBfGPCkAaR++OiG9sysALdJf8Y6HQKcyuAcWUqQnaIhoZ2JcAP7EKq7uCqsMhcYZD/j3O/3RPtSW5UOx6ItDU+Ua0t9Edho9whNw0VQXmo1JjYoP3FzPjuKoDWTSO1q5eYlZfwcTcs55O2shNkFafPg/6cCm5j6v9nyHrM3sk4LjkrBPUXVx2m/aoz219t8O9Ha9/CdMKXsPO/8gTUzpgnzSgPnGnBmi5xr1nspVN8X4E2f3D+DKqBim3YgslD68NcuFQvJ0/BxZzWVbrr+QXoyzaiCgXuogpIDc2bB6oRXqFnHNz36d4QJmJdWdSaijiS/peQ6EOPgOZ1GuObLWlDCBZLNeQ+N6QaiJxVO4XUj/s22i1IRtwdz84TRHrbWiIpEymsqmb/Ep5r4xV5d6+791axclfOTH7tQrY/SbPtTJI4OEgNekI8YfadQifpelF82MsFFEZuaQn0lj+fvLGtE/zKh3OdLTxRc5TAgBB+0T81+JQosygNr2aFFG0hxar1eyw/gLeG8H+7Ie50pyPvXO4OgB6Key8rSExpilQXlvAT1qX0qS3/K1i/9QkSE9ftIPT6vtwLV2sVQzfyanI4IZgWC6ryhvNLsRn0NFnQclor0+aq"
FastestvpnOpenvpnStaticKeyV1 = "697fe793b32cb5091d30f2326d5d124a9412e93d0a44ef7361395d76528fcbfc82c3859dccea70a93cfa8fae409709bff75f844cf5ff0c237f426d0c20969233db0e706edb6bdf195ec3dc11b3f76bc807a77e74662d9a800c8cd1144ebb67b7f0d3f1281d1baf522bfe03b7c3f963b1364fc0769400e413b61ca7b43ab19fac9e0f77e41efd4bda7fd77b1de2d7d7855cbbe3e620cecceac72c21a825b243e651f44d90e290e09c3ad650de8fca99c858bc7caad584bc69b11e5c9fd9381c69c505ec487a65912c672d83ed0113b5a74ddfbd3ab33b3683cec593557520a72c4d6cce46111f56f3396cc3ce7183edce553c68ea0796cf6c4375fad00aaa2a42"
)
func FastestvpnCountriesChoices() (choices []string) {
servers := FastestvpnServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
}
return choices
}
func FastestvpnHostnameChoices() (choices []string) {
servers := FastestvpnServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
return choices
}
// FastestvpnServers returns the list of all VPN servers for FastestVPN.
func FastestvpnServers() (servers []models.FastestvpnServer) {
servers = make([]models.FastestvpnServer, len(allServers.Fastestvpn.Servers))
copy(servers, allServers.Fastestvpn.Servers)
return servers
}

View File

@@ -0,0 +1,46 @@
package constants
import (
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll
const (
HideMyAssCA = "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"
HideMyAssCertificate = "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"
HideMyAssRSAPrivateKey = "MIIEpAIBAAKCAQEA5XY3ERJYWs/YIeBoybivNlu+M32rJs+CAZsh7BnnetTxytI4ngsMRoqXETuis8udp2hsqEHsglLR9tlk9C8yCuKhxbkpdrXFWdISmUq5sa7/wqg/zJF1AZm5Jy0oHNyTHfG6XW61I/h9IN5dmcR9YLir8DVDBNllbtt0z+DnvOhYJOqC30ENahWkTmNKl1cT7EBrR5slddiBJleAb08z77pwsD310e6jWTBySsBcPy+xu/Jj2QgVil/3mstZZDI+noFzs3SkTFBkha/lNTP7NODBQ6m39iaJxz6ZR1xE3v7XU0H5WnpZIcQ2+kmu5Krk2y1GYMKL+9oaotXFPz9v+QIDAQABAoIBAQCcMcssOMOiFWc3MC3EWo4SP4MKQ9n0Uj5Z34LI151FdJyehlj54+VYQ1Cv71tCbjED2sZUBoP69mtsT/EzcsjqtfiOwgrifrs2+BOm+0HKHKiGlcbP9peiHkT10PxEITWXpYtJvGlbcfOjIxqt6B28cBjCK09ShrVQL9ylAKBearRRUacszppntMNTMtN/uG48ZR9Wm+xAczImdG6CrG5sLI/++JwM5PDChLvn5JgMGyOfQZdjNe1oSOVLmqFeG5uu/FS4oMon9+HtfjHJr4ZgA1yQ2wQh3GvEjlP8zwHxEpRJYbxpj6ZbjHZJ2HLX/Gcd9/cXiN8+fQ2zPIYQyG9dAoGBAPUUmt2nJNvl7gj0GbZZ3XR9o+hvj7bJ74W2NhMrw6kjrrzHTAUQd1sBQS8szAQCLqf2ou1aw9AMMBdsLAHydXxvbH7IBAla7rKr23iethtSfjhTNSgQLJHVZlNHfp3hzNtCQZ7j0qVjrteNotrdVF7kKPHDXAK00ICy6SPNjvrXAoGBAO+vdnO15jLeZbbi3lQNS4r8oCadyqyX7ouKE6MtKNhiPsNPGqHKiGcKs/+QylVgYvSmm7TgpsCAiEYeLSPT+Yq3y7HtwVpULlpfAhEJXmvn/6hGpOizx1WNGWhw7nHPWPDzf+jqCGzHdhK0aEZR3MZZQ+U+uKfGiJ8vrvgB7eGvAoGAWxxp5nU48rcsIw/8bxpBhgkfYk33M5EnBqKSv9XJS5wEXhIJZOiWNrLktNEGl4boKXE7aNoRacreJhcE1UR6AOS7hPZ+6atwiePyF4mJUeb9HZtxa493wk9/Vv6BR9il++1Jz/QKX4oLef8hyBP4Rb60qgxirG7kBLR+j9zfhskCgYEAzA5y5xIeuIIU0H4XUDG9dcebxSSjbwsuYIgeLdb9pjMGQhsvjjyyoh8/nT20tLkJpkXN3FFCRjNnUWLRhWYrVkkh1wqWiYOPrwqh5MU4KN/sDWSPcznTY+drkTpMFoKzsvdrl2zf3VR3FneXKv742bkXj601Ykko+XWMHcLutisCgYBSq8IrsjzfaTQiTGI9a7WWsvzK92bq7Abnfq7swAXWcJd/bnjTQKLrrvt2bmwNvlWKAb3c69BFMn0X4t4PuN0iJQ39D6aQAEaM7HwWAmjf5TbodbmgbGxdsUB4xcCIQQ1mvTkigXWrCg0YAD2GZSoaslXAAVv6nR5qWEIa0Hx9GA=="
)
func HideMyAssCountryChoices() (choices []string) {
servers := HideMyAssServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
}
return makeUnique(choices)
}
func HideMyAssCityChoices() (choices []string) {
servers := HideMyAssServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
}
return makeUnique(choices)
}
func HideMyAssHostnameChoices() (choices []string) {
servers := HideMyAssServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
return makeUnique(choices)
}
// HideMyAssServers returns a slice of all the server information for HideMyAss.
func HideMyAssServers() (servers []models.HideMyAssServer) {
servers = make([]models.HideMyAssServer, len(allServers.HideMyAss.Servers))
copy(servers, allServers.HideMyAss.Servers)
return servers
}

View File

@@ -0,0 +1,44 @@
package constants
import (
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll
const (
IpvanishCA = "MIIErTCCA5WgAwIBAgIJAMYKzSS8uPKDMA0GCSqGSIb3DQEBDQUAMIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCRkwxFDASBgNVBAcTC1dpbnRlciBQYXJrMREwDwYDVQQKEwhJUFZhbmlzaDEVMBMGA1UECxMMSVBWYW5pc2ggVlBOMRQwEgYDVQQDEwtJUFZhbmlzaCBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBpcHZhbmlzaC5jb20wHhcNMTIwMTExMTkzMjIwWhcNMjgxMTAyMTkzMjIwWjCBlTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkZMMRQwEgYDVQQHEwtXaW50ZXIgUGFyazERMA8GA1UEChMISVBWYW5pc2gxFTATBgNVBAsTDElQVmFuaXNoIFZQTjEUMBIGA1UEAxMLSVBWYW5pc2ggQ0ExIzAhBgkqhkiG9w0BCQEWFHN1cHBvcnRAaXB2YW5pc2guY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt9DBWNr/IKOuY3TmDP5x7vYZR0DGxLbXU8TyAzBbjUtFFMbhxlHiXVQrZHmgzih94x7BgXM7tWpmMKYVb+gNaqMdWE680Qm3nOwmhy/dulXDkEHAwD05i/iTx4ZaUdtV2vsKBxRg1vdC4AEiwD7bqV4HOi13xcG971aQ55Mj1KeCdA0aNvpat1LWx2jjWxsfI8s2Lv5Fkoi1HO1+vTnnaEsJZrBgAkLXpItqP29Lik3/OBIvkBIxlKrhiVPixE5qNiD+eSPirsmROvsyIonoJtuY4Dw5K6pcNlKyYiwo1IOFYU3YxffwFJk+bSW4WVBhsdf5dGxq/uOHmuz5gdwxCwIDAQABo4H9MIH6MAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFEv9FCWJHefBcIPX9p8RHCVOGe6uMIHKBgNVHSMEgcIwgb+AFEv9FCWJHefBcIPX9p8RHCVOGe6uoYGbpIGYMIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCRkwxFDASBgNVBAcTC1dpbnRlciBQYXJrMREwDwYDVQQKEwhJUFZhbmlzaDEVMBMGA1UECxMMSVBWYW5pc2ggVlBOMRQwEgYDVQQDEwtJUFZhbmlzaCBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBpcHZhbmlzaC5jb22CCQDGCs0kvLjygzANBgkqhkiG9w0BAQ0FAAOCAQEAI2dkh/43ksV2fdYpVGhYaFZPVqCJoToCez0IvOmLeLGzow+EOSrY508oyjYeNP4VJEjApqo0NrMbKl8g/8bpLBcotOCF1c1HZ+y9v7648uumh01SMjsbBeHOuQcLb+7gX6c0pEmxWv8qj5JiW3/1L1bktnjW5Yp5oFkFSMXjOnIoYKHyKLjN2jtwH6XowUNYpg4qVtKU0CXPdOznWcd9/zSfa393HwJPeeVLbKYaFMC4IEbIUmKYtWyoJ9pJ58smU3pWsHZUg9Zc0LZZNjkNlBdQSLmUHAJ33Bd7pJS0JQeiWviC+4UTmzEWRKa7pDGnYRYNu2cUo0/voStphv8EVA=="
)
func IpvanishCountryChoices() (choices []string) {
servers := IpvanishServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
}
return makeUnique(choices)
}
func IpvanishCityChoices() (choices []string) {
servers := IpvanishServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
}
return makeUnique(choices)
}
func IpvanishHostnameChoices() (choices []string) {
servers := IpvanishServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
return makeUnique(choices)
}
// IpvanishServers returns a slice of all the server information for Ipvanish.
func IpvanishServers() (servers []models.IpvanishServer) {
servers = make([]models.IpvanishServer, len(allServers.Ipvanish.Servers))
copy(servers, allServers.Ipvanish.Servers)
return servers
}

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