Compare commits
411 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9a9319cb4 | ||
|
|
77e4317135 | ||
|
|
b10d97e53a | ||
|
|
648a4c04d7 | ||
|
|
3ca674dca7 | ||
|
|
fa97fd496e | ||
|
|
c76a7ee8da | ||
|
|
80f6b78332 | ||
|
|
8dc54a7c44 | ||
|
|
8f080c537b | ||
|
|
427cf86f44 | ||
|
|
2d244c08e7 | ||
|
|
82c0f523aa | ||
|
|
c07a0b0ada | ||
|
|
e4c306c0ee | ||
|
|
6ffb94f819 | ||
|
|
142238e8b7 | ||
|
|
678e23c7d6 | ||
|
|
0abcebe1d8 | ||
|
|
f398af1169 | ||
|
|
afbea415e3 | ||
|
|
225bd5d25b | ||
|
|
3651cc6161 | ||
|
|
dc674014ff | ||
|
|
0e0e03949d | ||
|
|
f5bf5c236a | ||
|
|
94480ecabb | ||
|
|
31ef9b1d45 | ||
|
|
bf76132fd4 | ||
|
|
8cc2983318 | ||
|
|
caeca18ed7 | ||
|
|
50febb41ff | ||
|
|
79293e067c | ||
|
|
f45be80591 | ||
|
|
d405ba8dca | ||
|
|
ca975b1c01 | ||
|
|
e0e3ca3832 | ||
|
|
e7c952cbf7 | ||
|
|
85ad2dd39a | ||
|
|
0c4f0ec17b | ||
|
|
5ad4136955 | ||
|
|
a432de95a9 | ||
|
|
1d25a0e18c | ||
|
|
29fd95685f | ||
|
|
62a6016882 | ||
|
|
18a4a79763 | ||
|
|
56ea722f93 | ||
|
|
d2ab974933 | ||
|
|
37d7a8b5fe | ||
|
|
e4dcadd825 | ||
|
|
fee99e9fe3 | ||
|
|
8ac4826126 | ||
|
|
7deb12e06d | ||
|
|
d6e218141b | ||
|
|
f44121b044 | ||
|
|
5d8d92462d | ||
|
|
985cf7b7dd | ||
|
|
dcbc10fd57 | ||
|
|
79f243e98d | ||
|
|
cf95692b93 | ||
|
|
d8e008606f | ||
|
|
3cd26a9f61 | ||
|
|
5d74320ee7 | ||
|
|
f9aadeef1c | ||
|
|
625de1c834 | ||
|
|
1c0a3ed1a4 | ||
|
|
03ba9169f4 | ||
|
|
c22e0e9db7 | ||
|
|
6bcbaf085d | ||
|
|
9a1d9c5d74 | ||
|
|
59a3a072e0 | ||
|
|
9f001bbc06 | ||
|
|
b8356b60a6 | ||
|
|
e2e218c74b | ||
|
|
3bf23cbae5 | ||
|
|
da562d8206 | ||
|
|
81bf83db13 | ||
|
|
7a25dcd130 | ||
|
|
877c7e1a9f | ||
|
|
77b2512745 | ||
|
|
749b73ef15 | ||
|
|
e499eca12c | ||
|
|
80f25c34e5 | ||
|
|
61677fbce2 | ||
|
|
dc6171185e | ||
|
|
f7e4331e93 | ||
|
|
1340511b64 | ||
|
|
c3078f84e8 | ||
|
|
9f65157a0d | ||
|
|
89166cdabf | ||
|
|
b872973e8b | ||
|
|
2000e72357 | ||
|
|
836e53642d | ||
|
|
af3f882bb8 | ||
|
|
2ab05b9350 | ||
|
|
1022eb8a6e | ||
|
|
15fe62de32 | ||
|
|
83d87f83f9 | ||
|
|
76a0c1f6c4 | ||
|
|
a1588302a7 | ||
|
|
91ce790b6b | ||
|
|
5d3982c2d2 | ||
|
|
2cf7f7b268 | ||
|
|
8645d978ba | ||
|
|
cc18b158f4 | ||
|
|
0730b6db6e | ||
|
|
3d2a360401 | ||
|
|
0c60dab384 | ||
|
|
f5f0ad7f28 | ||
|
|
f807f756eb | ||
|
|
11af6c10f1 | ||
|
|
40342619e7 | ||
|
|
19bf62c21f | ||
|
|
2ea00d149f | ||
|
|
cc677bde93 | ||
|
|
6627cda96c | ||
|
|
cade2732b0 | ||
|
|
541a4a3271 | ||
|
|
0eccd068e5 | ||
|
|
87f4b9e422 | ||
|
|
bcaf2e42fd | ||
|
|
d39201f9b3 | ||
|
|
8ac2a816c3 | ||
|
|
344f1bf9ee | ||
|
|
f0a006fc43 | ||
|
|
145da0b21d | ||
|
|
094de89a3e | ||
|
|
65ace12def | ||
|
|
9afe455635 | ||
|
|
45ce422a89 | ||
|
|
4a0738cd49 | ||
|
|
6b6caa435f | ||
|
|
f9cb71027c | ||
|
|
82ac568ee3 | ||
|
|
61afdce788 | ||
|
|
119cac5a67 | ||
|
|
c6fedd9214 | ||
|
|
da525e039d | ||
|
|
29d92fd307 | ||
|
|
3863cc439e | ||
|
|
b1cfc03fc5 | ||
|
|
f706071048 | ||
|
|
501ae2741b | ||
|
|
5b75635386 | ||
|
|
2901db3cf3 | ||
|
|
6c2a3e36b5 | ||
|
|
8b125e6e95 | ||
|
|
e1cc14e055 | ||
|
|
d6659552df | ||
|
|
67001fa958 | ||
|
|
ffeeae91ab | ||
|
|
04fad1b781 | ||
|
|
dcaf952986 | ||
|
|
ca3b9e892d | ||
|
|
9f12ffc069 | ||
|
|
0d6800a515 | ||
|
|
b3d8b78205 | ||
|
|
ee82a85543 | ||
|
|
7907146aaf | ||
|
|
1a677ce4f7 | ||
|
|
f1a6594474 | ||
|
|
f1a82d9d9c | ||
|
|
8b52af0d03 | ||
|
|
dbf5c569ea | ||
|
|
06a2d79cb4 | ||
|
|
eb6238ee52 | ||
|
|
f41fec57ed | ||
|
|
c348343b22 | ||
|
|
b69dcb62e3 | ||
|
|
e4a260f148 | ||
|
|
614eb10d67 | ||
|
|
0bfd58a3f5 | ||
|
|
ff56857fc8 | ||
|
|
8d258feff7 | ||
|
|
96ee1bbfb2 | ||
|
|
abaf688ad8 | ||
|
|
bec8ff27ae | ||
|
|
7191d4e911 | ||
|
|
6f59bc3037 | ||
|
|
5c2286f4e8 | ||
|
|
9218c7ef19 | ||
|
|
3d8e61900b | ||
|
|
105d81c018 | ||
|
|
d4ca5cf257 | ||
|
|
05018ec971 | ||
|
|
538bc72c3c | ||
|
|
0027a76c49 | ||
|
|
a0cb6fabfd | ||
|
|
9e5400f52d | ||
|
|
7a1d0ff3ec | ||
|
|
d9fbecaa01 | ||
|
|
ecdf9396a5 | ||
|
|
df51aa40f4 | ||
|
|
996942af47 | ||
|
|
f17a4eae3e | ||
|
|
c515603d2f | ||
|
|
14c3b6429b | ||
|
|
bd110b960b | ||
|
|
3ad4319163 | ||
|
|
97340ec70b | ||
|
|
5140a7b010 | ||
|
|
bd74879303 | ||
|
|
da30ae287f | ||
|
|
6a545aa088 | ||
|
|
384a4bae3a | ||
|
|
e65f924cd7 | ||
|
|
9105b33e9f | ||
|
|
cc2235653a | ||
|
|
a00de75f61 | ||
|
|
836412b032 | ||
|
|
ba16270059 | ||
|
|
2c73672e64 | ||
|
|
74b7c81195 | ||
|
|
a021ff6b22 | ||
|
|
6d1a90cac0 | ||
|
|
1f47c16102 | ||
|
|
abbcf60aed | ||
|
|
f339c882d7 | ||
|
|
982536e9e8 | ||
|
|
c17b351efb | ||
|
|
130bebf2c6 | ||
|
|
83c4ad2e59 | ||
|
|
0bcc6ed597 | ||
|
|
c61f854edc | ||
|
|
2998cf5e48 | ||
|
|
c777f8d97d | ||
|
|
7d4f5c8906 | ||
|
|
da39d07d48 | ||
|
|
b98f2456c0 | ||
|
|
564cc2b0bc | ||
|
|
49885c63c4 | ||
|
|
d7a6caa2ac | ||
|
|
73c383fd65 | ||
|
|
10b270f742 | ||
|
|
7a222923c7 | ||
|
|
430512dd27 | ||
|
|
d5ba15c23b | ||
|
|
037b43ee10 | ||
|
|
ab910403c6 | ||
|
|
8105437815 | ||
|
|
7b20cec035 | ||
|
|
8d512852a4 | ||
|
|
c8ad9b942a | ||
|
|
8153d4bb2a | ||
|
|
849dfee200 | ||
|
|
85540d96b6 | ||
|
|
7479974d79 | ||
|
|
3f1fb52fcb | ||
|
|
7e343d7006 | ||
|
|
72a5e1f695 | ||
|
|
253310bd1a | ||
|
|
fa6ccb08bd | ||
|
|
762507855e | ||
|
|
54610866f2 | ||
|
|
c39ff5c233 | ||
|
|
2ddc784965 | ||
|
|
10aabe8375 | ||
|
|
122647b39d | ||
|
|
02492c34a7 | ||
|
|
9436f604ba | ||
|
|
d9ca0deb08 | ||
|
|
0b985e8c35 | ||
|
|
c5d92ae02c | ||
|
|
94b60d9f70 | ||
|
|
b23eb8f29d | ||
|
|
3c44214d01 | ||
|
|
21f4cf7ab5 | ||
|
|
e94684aa39 | ||
|
|
a34cc48197 | ||
|
|
b262d91ccc | ||
|
|
39aa983771 | ||
|
|
5b9887dade | ||
|
|
c33402ce66 | ||
|
|
6f58f84151 | ||
|
|
6acb7caf5b | ||
|
|
8beff34cca | ||
|
|
478e0f74f7 | ||
|
|
b7bd23ab60 | ||
|
|
82533c1453 | ||
|
|
e0735b57ce | ||
|
|
1e0bfc3b0c | ||
|
|
cb0e89a38e | ||
|
|
da4d528463 | ||
|
|
394abbbe35 | ||
|
|
fd39bc8518 | ||
|
|
2663e8fba7 | ||
|
|
faebac6a77 | ||
|
|
bc1b09e997 | ||
|
|
af358f777b | ||
|
|
c0d27b4bfc | ||
|
|
7e50c95823 | ||
|
|
39068dda17 | ||
|
|
8185979ca4 | ||
|
|
7c44188130 | ||
|
|
c2d527bbd3 | ||
|
|
ac3ff095a1 | ||
|
|
0ed738cd61 | ||
|
|
6bbb7c8f7d | ||
|
|
d29429808c | ||
|
|
09eccd7cd9 | ||
|
|
bb2b8b4514 | ||
|
|
e20b9c5774 | ||
|
|
3badfa197a | ||
|
|
dee372e71b | ||
|
|
679be6e1bd | ||
|
|
92212fdd11 | ||
|
|
a6fb1ad9ef | ||
|
|
87d712fbd7 | ||
|
|
023809f099 | ||
|
|
ace37370d1 | ||
|
|
8efbd4fac1 | ||
|
|
06c8792887 | ||
|
|
3ea376a1b2 | ||
|
|
9667d30907 | ||
|
|
3f7ccc6c49 | ||
|
|
dd97ff5895 | ||
|
|
2e4d80d9bc | ||
|
|
1227dc5a2b | ||
|
|
ed828bc733 | ||
|
|
c25a018c05 | ||
|
|
266596af68 | ||
|
|
2c77b73ebc | ||
|
|
d81d4bbda3 | ||
|
|
400affe429 | ||
|
|
d3c63680e8 | ||
|
|
28de8a834c | ||
|
|
208374fc54 | ||
|
|
535a136a27 | ||
|
|
ba4c3e30a4 | ||
|
|
16d8a388cb | ||
|
|
5ea31b0b64 | ||
|
|
582c6d1c43 | ||
|
|
c63ae3f3af | ||
|
|
4c0df96a95 | ||
|
|
05c6b9379a | ||
|
|
fb7fdcd925 | ||
|
|
1774e2ad88 | ||
|
|
a402d9135e | ||
|
|
3d2c56d9ee | ||
|
|
f9308e6fed | ||
|
|
6710468020 | ||
|
|
ad1981fff6 | ||
|
|
01f9e71912 | ||
|
|
d41b75ee35 | ||
|
|
b829490aac | ||
|
|
7002bf8e34 | ||
|
|
625ea493fb | ||
|
|
79b3b2823b | ||
|
|
9be912e9fd | ||
|
|
3c3cd431cd | ||
|
|
8b8bab5c58 | ||
|
|
835fa6c41f | ||
|
|
8a6cf221a9 | ||
|
|
876563c492 | ||
|
|
be22c8547f | ||
|
|
82d98c4859 | ||
|
|
f1b5341f33 | ||
|
|
b3829493ea | ||
|
|
7db1253967 | ||
|
|
449db40d5f | ||
|
|
d5d0311bc6 | ||
|
|
0c4f01a892 | ||
|
|
bc7246f882 | ||
|
|
da65f3b016 | ||
|
|
a8c574219d | ||
|
|
a3751a77aa | ||
|
|
4f521e4dcb | ||
|
|
a9589d8d5b | ||
|
|
13e75aaf20 | ||
|
|
0c9bd8aaa0 | ||
|
|
5dba91c9ab | ||
|
|
7d6763cde7 | ||
|
|
dd1b23773e | ||
|
|
33253c0cfc | ||
|
|
0099c06056 | ||
|
|
1540660cc3 | ||
|
|
cff5e693d2 | ||
|
|
5159c1dc83 | ||
|
|
ccc7ad7cbd | ||
|
|
c8a61ca687 | ||
|
|
61e36d6aff | ||
|
|
e8c8742bae | ||
|
|
1cb93d76ed | ||
|
|
dadc939aab | ||
|
|
c59ea781e3 | ||
|
|
810ff62c26 | ||
|
|
5a0418bba6 | ||
|
|
baf506ae27 | ||
|
|
52ff03ae41 | ||
|
|
2d95edf8ab | ||
|
|
95b0fb81d6 | ||
|
|
eff65dce00 | ||
|
|
6c1c069261 | ||
|
|
4fe1e062f2 | ||
|
|
1fb0840e72 | ||
|
|
689ddf8bf0 | ||
|
|
d243ac49f3 | ||
|
|
de8f018b14 | ||
|
|
8407542600 | ||
|
|
a7a5cca8dd | ||
|
|
d9a70fd094 | ||
|
|
248cc0d3d3 | ||
|
|
2924d711cb | ||
|
|
d7db105a2f | ||
|
|
2ec2f45c82 | ||
|
|
a34769ae02 | ||
|
|
c0e4d805b1 | ||
|
|
6770336274 | ||
|
|
8d431dbb34 | ||
|
|
e8e7b83297 | ||
|
|
442340dcf2 |
@@ -1 +1,2 @@
|
|||||||
FROM qmcgaw/godevcontainer
|
FROM qmcgaw/godevcontainer
|
||||||
|
RUN apk add wireguard-tools
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"vscode"
|
"vscode"
|
||||||
],
|
],
|
||||||
"shutdownAction": "stopCompose",
|
"shutdownAction": "stopCompose",
|
||||||
"postCreateCommand": "source ~/.windows.sh && go mod download && go mod tidy",
|
"postCreateCommand": "~/.windows.sh && go mod download && go mod tidy",
|
||||||
"workspaceFolder": "/workspace",
|
"workspaceFolder": "/workspace",
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"golang.go",
|
"golang.go",
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ services:
|
|||||||
vscode:
|
vscode:
|
||||||
build: .
|
build: .
|
||||||
image: godevcontainer
|
image: godevcontainer
|
||||||
|
devices:
|
||||||
|
- /dev/net/tun:/dev/net/tun
|
||||||
volumes:
|
volumes:
|
||||||
- ../:/workspace
|
- ../:/workspace
|
||||||
# Docker socket to access Docker server
|
# Docker socket to access Docker server
|
||||||
@@ -23,7 +25,8 @@ services:
|
|||||||
- TZ=
|
- TZ=
|
||||||
cap_add:
|
cap_add:
|
||||||
# For debugging with dlv
|
# For debugging with dlv
|
||||||
- SYS_PTRACE
|
# - SYS_PTRACE
|
||||||
|
- NET_ADMIN
|
||||||
security_opt:
|
security_opt:
|
||||||
# For debugging with dlv
|
# For debugging with dlv
|
||||||
- seccomp:unconfined
|
- seccomp:unconfined
|
||||||
|
|||||||
37
.github/ISSUE_TEMPLATE/bug.md
vendored
37
.github/ISSUE_TEMPLATE/bug.md
vendored
@@ -1,37 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug
|
|
||||||
about: Report a bug
|
|
||||||
title: 'Bug: FILL THIS TEXT!'
|
|
||||||
labels: ":bug: bug"
|
|
||||||
assignees: qdm12
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**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
|
|
||||||
|
|
||||||
```
|
|
||||||
107
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
107
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
name: Bug
|
||||||
|
description: Report a bug
|
||||||
|
title: "Bug: "
|
||||||
|
labels: [":bug: bug"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
- type: dropdown
|
||||||
|
id: urgent
|
||||||
|
attributes:
|
||||||
|
label: Is this urgent?
|
||||||
|
description: |
|
||||||
|
Is this a critical bug, or do you need this fixed urgently?
|
||||||
|
If this is urgent, note you can use one of the [image tags available](https://github.com/qdm12/gluetun/wiki/Docker-image-tags) if that can help.
|
||||||
|
options:
|
||||||
|
- "No"
|
||||||
|
- "Yes"
|
||||||
|
- type: input
|
||||||
|
id: host-os
|
||||||
|
attributes:
|
||||||
|
label: Host OS
|
||||||
|
description: What is your host OS?
|
||||||
|
placeholder: "Debian Buster"
|
||||||
|
- type: dropdown
|
||||||
|
id: cpu-arch
|
||||||
|
attributes:
|
||||||
|
label: CPU arch
|
||||||
|
description: You can find it on Linux with `uname -m`.
|
||||||
|
options:
|
||||||
|
- x86_64
|
||||||
|
- aarch64
|
||||||
|
- armv7l
|
||||||
|
- "386"
|
||||||
|
- s390x
|
||||||
|
- ppc64le
|
||||||
|
- type: dropdown
|
||||||
|
id: vpn-service-provider
|
||||||
|
attributes:
|
||||||
|
label: VPN service provider
|
||||||
|
options:
|
||||||
|
- Custom
|
||||||
|
- Cyberghost
|
||||||
|
- ExpressVPN
|
||||||
|
- FastestVPN
|
||||||
|
- HideMyAss
|
||||||
|
- IPVanish
|
||||||
|
- IVPN
|
||||||
|
- Mullvad
|
||||||
|
- NordVPN
|
||||||
|
- Privado
|
||||||
|
- Private Internet Access
|
||||||
|
- PrivateVPN
|
||||||
|
- ProtonVPN
|
||||||
|
- PureVPN
|
||||||
|
- Surfshark
|
||||||
|
- TorGuard
|
||||||
|
- VPNUnlimited
|
||||||
|
- VyprVPN
|
||||||
|
- WeVPN
|
||||||
|
- Windscribe
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: docker
|
||||||
|
attributes:
|
||||||
|
label: What are you using to run the container
|
||||||
|
options:
|
||||||
|
- docker run
|
||||||
|
- docker-compose
|
||||||
|
- Portainer
|
||||||
|
- Kubernetes
|
||||||
|
- Podman
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: What is the version of Gluetun
|
||||||
|
description: |
|
||||||
|
Copy paste the version line at the top of your logs.
|
||||||
|
It should be in the form `Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)`.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: problem
|
||||||
|
attributes:
|
||||||
|
label: "What's the problem 🤔"
|
||||||
|
placeholder: "That feature does not work..."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Share your logs
|
||||||
|
description: No sensitive information is logged out except when running with `LOG_LEVEL=debug`.
|
||||||
|
render: log
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: config
|
||||||
|
attributes:
|
||||||
|
label: Share your configuration
|
||||||
|
description: Share your configuration such as `docker-compose.yml`. Ensure to remove credentials.
|
||||||
|
render: yml
|
||||||
7
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
7
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
contact_links:
|
||||||
|
- name: Configuration help?
|
||||||
|
url: https://github.com/qdm12/gluetun/discussions/new
|
||||||
|
about: Please create a Github discussion.
|
||||||
|
- name: Unraid template issue
|
||||||
|
url: https://github.com/qdm12/gluetun/discussions/550
|
||||||
|
about: Please read the relevant Github discussion.
|
||||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
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
|
|
||||||
19
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
19
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
name: Feature request
|
||||||
|
description: Suggest a feature to add to Gluetun
|
||||||
|
title: "Feature request: "
|
||||||
|
labels: [":bulb: feature request"]
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: "What's the feature 🧐"
|
||||||
|
placeholder: "Make the tunnel resistant to earth quakes"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: extra
|
||||||
|
attributes:
|
||||||
|
label: "Extra information and references"
|
||||||
|
placeholder: |
|
||||||
|
- I tried `docker run something` and it doesn't work
|
||||||
|
- That [url](https://github.com/qdm12/gluetun) is interesting
|
||||||
53
.github/ISSUE_TEMPLATE/help.md
vendored
53
.github/ISSUE_TEMPLATE/help.md
vendored
@@ -1,53 +0,0 @@
|
|||||||
---
|
|
||||||
name: Help
|
|
||||||
about: Ask for help
|
|
||||||
title: 'Help: FILL THIS TEXT!'
|
|
||||||
labels: ":pray: help wanted"
|
|
||||||
assignees:
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**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
|
|
||||||
|
|
||||||
Please also share your configuration file:
|
|
||||||
|
|
||||||
```yml
|
|
||||||
your .yml
|
|
||||||
content
|
|
||||||
in here
|
|
||||||
```
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# your docker
|
|
||||||
# run command
|
|
||||||
# in here
|
|
||||||
```
|
|
||||||
18
.github/ISSUE_TEMPLATE/wiki issue.yml
vendored
Normal file
18
.github/ISSUE_TEMPLATE/wiki issue.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
name: Wiki issue
|
||||||
|
description: Report a Wiki issue
|
||||||
|
title: "Wiki issue: "
|
||||||
|
labels: ["📄 Wiki issue"]
|
||||||
|
body:
|
||||||
|
- type: input
|
||||||
|
id: url
|
||||||
|
attributes:
|
||||||
|
label: "URL to the Wiki page"
|
||||||
|
placeholder: "https://github.com/qdm12/gluetun/wiki/OpenVPN-options"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: "What's the issue?"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
29
.github/labels.yml
vendored
29
.github/labels.yml
vendored
@@ -14,6 +14,14 @@
|
|||||||
color: "795548"
|
color: "795548"
|
||||||
description: ""
|
description: ""
|
||||||
|
|
||||||
|
# Priority
|
||||||
|
- name: "🚨 Urgent"
|
||||||
|
color: "d5232f"
|
||||||
|
description: ""
|
||||||
|
- name: "💤 Low priority"
|
||||||
|
color: "4285f4"
|
||||||
|
description: ""
|
||||||
|
|
||||||
# VPN providers
|
# VPN providers
|
||||||
- name: ":cloud: Cyberghost"
|
- name: ":cloud: Cyberghost"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
@@ -21,6 +29,15 @@
|
|||||||
- name: ":cloud: HideMyAss"
|
- name: ":cloud: HideMyAss"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
|
- name: ":cloud: IPVanish"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: IVPN"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: ExpressVPN"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
- name: ":cloud: FastestVPN"
|
- name: ":cloud: FastestVPN"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
@@ -30,6 +47,9 @@
|
|||||||
- name: ":cloud: NordVPN"
|
- name: ":cloud: NordVPN"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
|
- name: ":cloud: Perfect Privacy"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
- name: ":cloud: PIA"
|
- name: ":cloud: PIA"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
@@ -50,9 +70,15 @@
|
|||||||
- name: ":cloud: Torguard"
|
- name: ":cloud: Torguard"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
|
- name: ":cloud: VPNUnlimited"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
- name: ":cloud: Vyprvpn"
|
- name: ":cloud: Vyprvpn"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
|
- name: ":cloud: WeVPN"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
- name: ":cloud: Windscribe"
|
- name: ":cloud: Windscribe"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
@@ -61,6 +87,9 @@
|
|||||||
- name: "Openvpn"
|
- name: "Openvpn"
|
||||||
color: "ffc7ea"
|
color: "ffc7ea"
|
||||||
description: ""
|
description: ""
|
||||||
|
- name: "Wireguard"
|
||||||
|
color: "ffc7ea"
|
||||||
|
description: ""
|
||||||
- name: "Unbound (DNS over TLS)"
|
- name: "Unbound (DNS over TLS)"
|
||||||
color: "ffc7ea"
|
color: "ffc7ea"
|
||||||
description: ""
|
description: ""
|
||||||
|
|||||||
100
.github/workflows/build.yml
vendored
100
.github/workflows/build.yml
vendored
@@ -1,100 +0,0 @@
|
|||||||
name: CI
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- .github/workflows/build.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
|
|
||||||
|
|
||||||
- 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 \
|
|
||||||
go test \
|
|
||||||
-race \
|
|
||||||
-coverpkg=./... \
|
|
||||||
-coverprofile=coverage.txt \
|
|
||||||
-covermode=atomic \
|
|
||||||
./...
|
|
||||||
|
|
||||||
# We run this here to use the caching of the previous steps
|
|
||||||
- if: github.event_name == 'push'
|
|
||||||
name: Build final image
|
|
||||||
run: docker build .
|
|
||||||
|
|
||||||
publish:
|
|
||||||
needs: [verify]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- 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=build_date::$(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
|
|
||||||
with:
|
|
||||||
platforms: ${{ steps.vars.outputs.platforms }}
|
|
||||||
build-args: |
|
|
||||||
BUILD_DATE=${{ steps.vars.outputs.build_date }}
|
|
||||||
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
|
|
||||||
|
|
||||||
- if: github.event_name == 'push' && github.event.ref == 'refs/heads/master'
|
|
||||||
name: Microbadger hook
|
|
||||||
run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/gluetun/l-keGI7p4IhX4QuIDMFYKhsZ1L0=
|
|
||||||
continue-on-error: true
|
|
||||||
144
.github/workflows/ci.yml
vendored
Normal file
144
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
name: CI
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- published
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- .github/workflows/ci.yml
|
||||||
|
- cmd/**
|
||||||
|
- internal/**
|
||||||
|
- pkg/**
|
||||||
|
- .dockerignore
|
||||||
|
- .golangci.yml
|
||||||
|
- Dockerfile
|
||||||
|
- go.mod
|
||||||
|
- go.sum
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- .github/workflows/ci.yml
|
||||||
|
- cmd/**
|
||||||
|
- internal/**
|
||||||
|
- pkg/**
|
||||||
|
- .dockerignore
|
||||||
|
- .golangci.yml
|
||||||
|
- Dockerfile
|
||||||
|
- go.mod
|
||||||
|
- go.sum
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
verify:
|
||||||
|
# Only run if it's a push event or if it's a PR from this repository, and it is not dependabot.
|
||||||
|
if: |
|
||||||
|
github.actor != 'dependabot[bot]' &&
|
||||||
|
(github.event_name == 'push' ||
|
||||||
|
github.event_name == 'release' ||
|
||||||
|
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository))
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
DOCKER_BUILDKIT: "1"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2.4.0
|
||||||
|
|
||||||
|
- 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:
|
||||||
|
# Only run if it's a push event or if it's a PR from this repository
|
||||||
|
if: |
|
||||||
|
github.event_name == 'push' ||
|
||||||
|
github.event_name == 'release' ||
|
||||||
|
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)
|
||||||
|
needs: [verify]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2.4.0
|
||||||
|
|
||||||
|
- 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: Check for semver tag
|
||||||
|
id: semvercheck
|
||||||
|
run: |
|
||||||
|
if [[ ${{ github.ref }} =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
MATCH=true
|
||||||
|
else
|
||||||
|
MATCH=false
|
||||||
|
fi
|
||||||
|
if [[ ! ${{ github.ref }} =~ ^refs/tags/v0\. ]]; then
|
||||||
|
MATCH=$MATCH_nonzero
|
||||||
|
fi
|
||||||
|
echo ::set-output name=match::$MATCH
|
||||||
|
|
||||||
|
# extract metadata (tags, labels) for Docker
|
||||||
|
# https://github.com/docker/metadata-action
|
||||||
|
- name: Extract Docker metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v3
|
||||||
|
with:
|
||||||
|
flavor: |
|
||||||
|
latest=${{ github.ref == 'refs/heads/master' }}
|
||||||
|
images: |
|
||||||
|
qmcgaw/gluetun
|
||||||
|
qmcgaw/private-internet-access
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch,enable=${{ github.ref != 'refs/heads/master' }}
|
||||||
|
type=ref,event=pr
|
||||||
|
type=ref,event=tag,enable=${{ !startsWith(steps.semvercheck.outputs.match, 'true') }}
|
||||||
|
type=semver,pattern=v{{major}}.{{minor}}.{{patch}},enable=${{ startsWith(steps.semvercheck.outputs.match, 'true') }}
|
||||||
|
type=semver,pattern=v{{major}}.{{minor}},enable=${{ startsWith(steps.semvercheck.outputs.match, 'true') }}
|
||||||
|
type=semver,pattern=v{{major}},enable=${{ startsWith(steps.semvercheck.outputs.match, 'true_nonzero') }}
|
||||||
|
type=raw,value=latest,enable=${{ !startsWith(steps.semvercheck.outputs.match, 'true') }}
|
||||||
|
|
||||||
|
- name: Short commit
|
||||||
|
id: shortcommit
|
||||||
|
run: echo "::set-output name=value::$(git rev-parse --short HEAD)"
|
||||||
|
|
||||||
|
- name: Build and push final image
|
||||||
|
uses: docker/build-push-action@v2.7.0
|
||||||
|
with:
|
||||||
|
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
build-args: |
|
||||||
|
CREATED=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}
|
||||||
|
COMMIT=${{ steps.shortcommit.outputs.value }}
|
||||||
|
VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
push: true
|
||||||
37
.github/workflows/dependabot.yml
vendored
Normal file
37
.github/workflows/dependabot.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
name: Dependabot
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- .github/workflows/dependabot.yml
|
||||||
|
- cmd/**
|
||||||
|
- internal/**
|
||||||
|
- pkg/**
|
||||||
|
- .dockerignore
|
||||||
|
- .golangci.yml
|
||||||
|
- Dockerfile
|
||||||
|
- go.mod
|
||||||
|
- go.sum
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
verify:
|
||||||
|
if: ${{ github.actor == 'dependabot[bot]' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
DOCKER_BUILDKIT: "1"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2.4.0
|
||||||
|
|
||||||
|
- 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: Build final image
|
||||||
|
run: docker build -t final-image .
|
||||||
2
.github/workflows/dockerhub-description.yml
vendored
2
.github/workflows/dockerhub-description.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2.4.0
|
||||||
- name: Docker Hub Description
|
- name: Docker Hub Description
|
||||||
uses: peter-evans/dockerhub-description@v2
|
uses: peter-evans/dockerhub-description@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
40
.github/workflows/fork.yml
vendored
Normal file
40
.github/workflows/fork.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
name: Fork
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- .github/workflows/fork.yml
|
||||||
|
- cmd/**
|
||||||
|
- internal/**
|
||||||
|
- pkg/**
|
||||||
|
- .dockerignore
|
||||||
|
- .golangci.yml
|
||||||
|
- Dockerfile
|
||||||
|
- go.mod
|
||||||
|
- go.sum
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
verify:
|
||||||
|
if: github.event.pull_request.head.repo.full_name != github.repository && github.actor != 'dependabot[bot]'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
DOCKER_BUILDKIT: "1"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2.4.0
|
||||||
|
|
||||||
|
- name: Linting
|
||||||
|
run: docker build --target lint .
|
||||||
|
|
||||||
|
- 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: Build final image
|
||||||
|
run: docker build -t final-image .
|
||||||
2
.github/workflows/labels.yml
vendored
2
.github/workflows/labels.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
|||||||
labeler:
|
labeler:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2.4.0
|
||||||
- uses: crazy-max/ghaction-github-labeler@v3
|
- uses: crazy-max/ghaction-github-labeler@v3
|
||||||
with:
|
with:
|
||||||
yaml-file: .github/labels.yml
|
yaml-file: .github/labels.yml
|
||||||
|
|||||||
2
.github/workflows/misspell.yml
vendored
2
.github/workflows/misspell.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
|||||||
misspell:
|
misspell:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2.4.0
|
||||||
- uses: reviewdog/action-misspell@v1
|
- uses: reviewdog/action-misspell@v1
|
||||||
with:
|
with:
|
||||||
locale: "US"
|
locale: "US"
|
||||||
|
|||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
scratch.txt
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
linters-settings:
|
linters-settings:
|
||||||
maligned:
|
|
||||||
suggest-new: true
|
|
||||||
misspell:
|
misspell:
|
||||||
locale: US
|
locale: US
|
||||||
|
|
||||||
@@ -10,23 +8,42 @@ issues:
|
|||||||
linters:
|
linters:
|
||||||
- dupl
|
- dupl
|
||||||
- maligned
|
- maligned
|
||||||
|
- goerr113
|
||||||
- path: internal/server/
|
- path: internal/server/
|
||||||
linters:
|
linters:
|
||||||
- dupl
|
- dupl
|
||||||
- path: internal/configuration/
|
- path: internal/configuration/
|
||||||
linters:
|
linters:
|
||||||
- dupl
|
- 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:
|
linters:
|
||||||
disable-all: true
|
|
||||||
enable:
|
enable:
|
||||||
|
# - cyclop
|
||||||
|
# - errorlint
|
||||||
|
# - ireturn
|
||||||
|
# - varnamelen
|
||||||
|
# - wrapcheck
|
||||||
- asciicheck
|
- asciicheck
|
||||||
|
- bidichk
|
||||||
- bodyclose
|
- bodyclose
|
||||||
- deadcode
|
|
||||||
- dogsled
|
- dogsled
|
||||||
- dupl
|
- dupl
|
||||||
- errcheck
|
- durationcheck
|
||||||
|
- errname
|
||||||
- exhaustive
|
- exhaustive
|
||||||
- exportloopref
|
- exportloopref
|
||||||
|
- forcetypeassert
|
||||||
- gci
|
- gci
|
||||||
- gochecknoglobals
|
- gochecknoglobals
|
||||||
- gochecknoinits
|
- gochecknoinits
|
||||||
@@ -35,34 +52,37 @@ linters:
|
|||||||
- gocritic
|
- gocritic
|
||||||
- gocyclo
|
- gocyclo
|
||||||
- godot
|
- godot
|
||||||
|
- goerr113
|
||||||
- goheader
|
- goheader
|
||||||
- goimports
|
- goimports
|
||||||
- golint
|
|
||||||
- gomnd
|
- gomnd
|
||||||
|
- gomoddirectives
|
||||||
- goprintffuncname
|
- goprintffuncname
|
||||||
- gosec
|
- gosec
|
||||||
- gosimple
|
- ifshort
|
||||||
- govet
|
- importas
|
||||||
- ineffassign
|
|
||||||
- interfacer
|
|
||||||
- lll
|
- lll
|
||||||
- maligned
|
- makezero
|
||||||
- misspell
|
- misspell
|
||||||
- nakedret
|
- nakedret
|
||||||
- nestif
|
- nestif
|
||||||
|
- nilerr
|
||||||
|
- nilnil
|
||||||
- noctx
|
- noctx
|
||||||
- nolintlint
|
- nolintlint
|
||||||
- prealloc
|
- prealloc
|
||||||
|
- predeclared
|
||||||
|
- predeclared
|
||||||
|
- promlinter
|
||||||
|
- revive
|
||||||
- rowserrcheck
|
- rowserrcheck
|
||||||
- scopelint
|
|
||||||
- sqlclosecheck
|
- sqlclosecheck
|
||||||
- staticcheck
|
- tenv
|
||||||
- structcheck
|
- thelper
|
||||||
- typecheck
|
- tparallel
|
||||||
- unconvert
|
- unconvert
|
||||||
- unparam
|
- unparam
|
||||||
- unused
|
- wastedassign
|
||||||
- varcheck
|
|
||||||
- whitespace
|
- whitespace
|
||||||
|
|
||||||
run:
|
run:
|
||||||
|
|||||||
145
Dockerfile
145
Dockerfile
@@ -1,31 +1,36 @@
|
|||||||
ARG ALPINE_VERSION=3.13
|
ARG ALPINE_VERSION=3.14
|
||||||
ARG GO_VERSION=1.16
|
ARG GO_ALPINE_VERSION=3.14
|
||||||
|
ARG GO_VERSION=1.17
|
||||||
|
ARG XCPUTRANSLATE_VERSION=v0.6.0
|
||||||
|
ARG GOLANGCI_LINT_VERSION=v1.43.0
|
||||||
ARG BUILDPLATFORM=linux/amd64
|
ARG BUILDPLATFORM=linux/amd64
|
||||||
|
|
||||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
|
FROM --platform=${BUILDPLATFORM} qmcgaw/xcputranslate:${XCPUTRANSLATE_VERSION} AS xcputranslate
|
||||||
RUN apk --update add git
|
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
|
ENV CGO_ENABLED=0
|
||||||
|
COPY --from=golangci-lint /bin /go/bin/golangci-lint
|
||||||
WORKDIR /tmp/gobuild
|
WORKDIR /tmp/gobuild
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
COPY cmd/ ./cmd/
|
COPY cmd/ ./cmd/
|
||||||
COPY internal/ ./internal/
|
COPY internal/ ./internal/
|
||||||
|
|
||||||
FROM --platform=$BUILDPLATFORM base AS test
|
FROM --platform=${BUILDPLATFORM} base AS test
|
||||||
# Note on the go race detector:
|
# Note on the go race detector:
|
||||||
# - we set CGO_ENABLED=1 to have it enabled
|
# - we set CGO_ENABLED=1 to have it enabled
|
||||||
# - we install g++ to support the race detector
|
# - we installed g++ to support the race detector
|
||||||
ENV CGO_ENABLED=1
|
ENV CGO_ENABLED=1
|
||||||
RUN apk --update --no-cache add g++
|
ENTRYPOINT go test -race -coverpkg=./... -coverprofile=coverage.txt -covermode=atomic ./...
|
||||||
|
|
||||||
FROM --platform=$BUILDPLATFORM base AS lint
|
FROM --platform=${BUILDPLATFORM} base AS lint
|
||||||
ARG GOLANGCI_LINT_VERSION=v1.39.0
|
|
||||||
RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | \
|
|
||||||
sh -s -- -b /usr/local/bin ${GOLANGCI_LINT_VERSION}
|
|
||||||
COPY .golangci.yml ./
|
COPY .golangci.yml ./
|
||||||
RUN golangci-lint run --timeout=10m
|
RUN golangci-lint run --timeout=10m
|
||||||
|
|
||||||
FROM --platform=$BUILDPLATFORM base AS tidy
|
FROM --platform=${BUILDPLATFORM} base AS tidy
|
||||||
RUN git init && \
|
RUN git init && \
|
||||||
git config user.email ci@localhost && \
|
git config user.email ci@localhost && \
|
||||||
git config user.name ci && \
|
git config user.name ci && \
|
||||||
@@ -34,27 +39,26 @@ RUN git init && \
|
|||||||
go mod tidy && \
|
go mod tidy && \
|
||||||
git diff --exit-code -- go.mod
|
git diff --exit-code -- go.mod
|
||||||
|
|
||||||
FROM --platform=$BUILDPLATFORM base AS build
|
FROM --platform=${BUILDPLATFORM} base AS build
|
||||||
COPY --from=qmcgaw/xcputranslate:v0.4.0 /xcputranslate /usr/local/bin/xcputranslate
|
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
ARG VERSION=unknown
|
ARG VERSION=unknown
|
||||||
ARG BUILD_DATE="an unknown date"
|
ARG CREATED="an unknown date"
|
||||||
ARG COMMIT=unknown
|
ARG COMMIT=unknown
|
||||||
RUN GOARCH="$(xcputranslate -field arch -targetplatform ${TARGETPLATFORM})" \
|
RUN GOARCH="$(xcputranslate translate -field arch -targetplatform ${TARGETPLATFORM})" \
|
||||||
GOARM="$(xcputranslate -field arm -targetplatform ${TARGETPLATFORM})" \
|
GOARM="$(xcputranslate translate -field arm -targetplatform ${TARGETPLATFORM})" \
|
||||||
go build -trimpath -ldflags="-s -w \
|
go build -trimpath -ldflags="-s -w \
|
||||||
-X 'main.version=$VERSION' \
|
-X 'main.version=$VERSION' \
|
||||||
-X 'main.buildDate=$BUILD_DATE' \
|
-X 'main.created=$CREATED' \
|
||||||
-X 'main.commit=$COMMIT' \
|
-X 'main.commit=$COMMIT' \
|
||||||
" -o entrypoint cmd/gluetun/main.go
|
" -o entrypoint cmd/gluetun/main.go
|
||||||
|
|
||||||
FROM alpine:${ALPINE_VERSION}
|
FROM alpine:${ALPINE_VERSION}
|
||||||
ARG VERSION=unknown
|
ARG VERSION=unknown
|
||||||
ARG BUILD_DATE="an unknown date"
|
ARG CREATED="an unknown date"
|
||||||
ARG COMMIT=unknown
|
ARG COMMIT=unknown
|
||||||
LABEL \
|
LABEL \
|
||||||
org.opencontainers.image.authors="quentin.mcgaw@gmail.com" \
|
org.opencontainers.image.authors="quentin.mcgaw@gmail.com" \
|
||||||
org.opencontainers.image.created=$BUILD_DATE \
|
org.opencontainers.image.created=$CREATED \
|
||||||
org.opencontainers.image.version=$VERSION \
|
org.opencontainers.image.version=$VERSION \
|
||||||
org.opencontainers.image.revision=$COMMIT \
|
org.opencontainers.image.revision=$COMMIT \
|
||||||
org.opencontainers.image.url="https://github.com/qdm12/gluetun" \
|
org.opencontainers.image.url="https://github.com/qdm12/gluetun" \
|
||||||
@@ -63,47 +67,72 @@ LABEL \
|
|||||||
org.opencontainers.image.title="VPN swiss-knife like client for multiple VPN providers" \
|
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"
|
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 \
|
ENV VPNSP=pia \
|
||||||
VERSION_INFORMATION=on \
|
VPN_TYPE=openvpn \
|
||||||
PROTOCOL=udp \
|
# OpenVPN
|
||||||
|
OPENVPN_PROTOCOL=udp \
|
||||||
|
OPENVPN_USER= \
|
||||||
|
OPENVPN_PASSWORD= \
|
||||||
|
OPENVPN_USER_SECRETFILE=/run/secrets/openvpn_user \
|
||||||
|
OPENVPN_PASSWORD_SECRETFILE=/run/secrets/openvpn_password \
|
||||||
|
OPENVPN_VERSION=2.5 \
|
||||||
OPENVPN_VERBOSITY=1 \
|
OPENVPN_VERBOSITY=1 \
|
||||||
|
OPENVPN_FLAGS= \
|
||||||
|
OPENVPN_CIPHER= \
|
||||||
|
OPENVPN_AUTH= \
|
||||||
OPENVPN_ROOT=yes \
|
OPENVPN_ROOT=yes \
|
||||||
OPENVPN_TARGET_IP= \
|
OPENVPN_TARGET_IP= \
|
||||||
OPENVPN_IPV6=off \
|
OPENVPN_IPV6=off \
|
||||||
OPENVPN_CUSTOM_CONFIG= \
|
OPENVPN_CUSTOM_CONFIG= \
|
||||||
TZ= \
|
OPENVPN_INTERFACE=tun0 \
|
||||||
PUID= \
|
OPENVPN_PORT= \
|
||||||
PGID= \
|
# Wireguard
|
||||||
PUBLICIP_FILE="/tmp/gluetun/ip" \
|
WIREGUARD_PRIVATE_KEY= \
|
||||||
# VPN provider settings
|
WIREGUARD_PRESHARED_KEY= \
|
||||||
OPENVPN_USER= \
|
WIREGUARD_PUBLIC_KEY= \
|
||||||
OPENVPN_PASSWORD= \
|
WIREGUARD_ADDRESS= \
|
||||||
USER_SECRETFILE=/run/secrets/openvpn_user \
|
WIREGUARD_ENDPOINT_IP= \
|
||||||
PASSWORD_SECRETFILE=/run/secrets/openvpn_password \
|
WIREGUARD_ENDPOINT_PORT= \
|
||||||
|
WIREGUARD_INTERFACE=wg0 \
|
||||||
|
# VPN server filtering
|
||||||
REGION= \
|
REGION= \
|
||||||
COUNTRY= \
|
COUNTRY= \
|
||||||
CITY= \
|
CITY= \
|
||||||
PORT= \
|
|
||||||
SERVER_HOSTNAME= \
|
SERVER_HOSTNAME= \
|
||||||
# Mullvad only:
|
# # Mullvad only:
|
||||||
ISP= \
|
ISP= \
|
||||||
OWNED=no \
|
OWNED=no \
|
||||||
# Private Internet Access only:
|
# # Private Internet Access only:
|
||||||
PIA_ENCRYPTION=strong \
|
PIA_ENCRYPTION=strong \
|
||||||
PORT_FORWARDING=off \
|
PORT_FORWARDING=off \
|
||||||
PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \
|
PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \
|
||||||
# Cyberghost only:
|
# # Cyberghost only:
|
||||||
CYBERGHOST_GROUP="Premium UDP Europe" \
|
|
||||||
OPENVPN_CLIENTCRT_SECRETFILE=/run/secrets/openvpn_clientcrt \
|
OPENVPN_CLIENTCRT_SECRETFILE=/run/secrets/openvpn_clientcrt \
|
||||||
OPENVPN_CLIENTKEY_SECRETFILE=/run/secrets/openvpn_clientkey \
|
OPENVPN_CLIENTKEY_SECRETFILE=/run/secrets/openvpn_clientkey \
|
||||||
# Nordvpn only:
|
# # Nordvpn only:
|
||||||
SERVER_NUMBER= \
|
SERVER_NUMBER= \
|
||||||
# Openvpn
|
# # PIA and ProtonVPN only:
|
||||||
OPENVPN_CIPHER= \
|
SERVER_NAME= \
|
||||||
OPENVPN_AUTH= \
|
# # ProtonVPN only:
|
||||||
|
FREE_ONLY= \
|
||||||
|
# # Surfshark only:
|
||||||
|
MULTIHOP_ONLY= \
|
||||||
|
# Firewall
|
||||||
|
FIREWALL=on \
|
||||||
|
FIREWALL_VPN_INPUT_PORTS= \
|
||||||
|
FIREWALL_INPUT_PORTS= \
|
||||||
|
FIREWALL_OUTBOUND_SUBNETS= \
|
||||||
|
FIREWALL_DEBUG=off \
|
||||||
|
# Logging
|
||||||
|
LOG_LEVEL=info \
|
||||||
|
# Health
|
||||||
|
HEALTH_SERVER_ADDRESS=127.0.0.1:9999 \
|
||||||
|
HEALTH_ADDRESS_TO_PING=github.com \
|
||||||
|
HEALTH_VPN_DURATION_INITIAL=6s \
|
||||||
|
HEALTH_VPN_DURATION_ADDITION=5s \
|
||||||
# DNS over TLS
|
# DNS over TLS
|
||||||
DOT=on \
|
DOT=on \
|
||||||
DOT_PROVIDERS=cloudflare \
|
DOT_PROVIDERS=cloudflare \
|
||||||
DOT_PRIVATE_ADDRESS=127.0.0.1/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,::1/128,fc00::/7,fe80::/10,::ffff:0:0/96 \
|
DOT_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=1 \
|
||||||
DOT_VERBOSITY_DETAILS=0 \
|
DOT_VERBOSITY_DETAILS=0 \
|
||||||
DOT_VALIDATION_LOGLEVEL=0 \
|
DOT_VALIDATION_LOGLEVEL=0 \
|
||||||
@@ -116,12 +145,6 @@ ENV VPNSP=pia \
|
|||||||
DNS_UPDATE_PERIOD=24h \
|
DNS_UPDATE_PERIOD=24h \
|
||||||
DNS_PLAINTEXT_ADDRESS=1.1.1.1 \
|
DNS_PLAINTEXT_ADDRESS=1.1.1.1 \
|
||||||
DNS_KEEP_NAMESERVER=off \
|
DNS_KEEP_NAMESERVER=off \
|
||||||
# Firewall
|
|
||||||
FIREWALL=on \
|
|
||||||
FIREWALL_VPN_INPUT_PORTS= \
|
|
||||||
FIREWALL_INPUT_PORTS= \
|
|
||||||
FIREWALL_OUTBOUND_SUBNETS= \
|
|
||||||
FIREWALL_DEBUG=off \
|
|
||||||
# HTTP proxy
|
# HTTP proxy
|
||||||
HTTPPROXY= \
|
HTTPPROXY= \
|
||||||
HTTPPROXY_LOG=off \
|
HTTPPROXY_LOG=off \
|
||||||
@@ -133,18 +156,34 @@ ENV VPNSP=pia \
|
|||||||
# Shadowsocks
|
# Shadowsocks
|
||||||
SHADOWSOCKS=off \
|
SHADOWSOCKS=off \
|
||||||
SHADOWSOCKS_LOG=off \
|
SHADOWSOCKS_LOG=off \
|
||||||
SHADOWSOCKS_PORT=8388 \
|
SHADOWSOCKS_ADDRESS=":8388" \
|
||||||
SHADOWSOCKS_PASSWORD= \
|
SHADOWSOCKS_PASSWORD= \
|
||||||
SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password \
|
SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password \
|
||||||
SHADOWSOCKS_METHOD=chacha20-ietf-poly1305 \
|
SHADOWSOCKS_CIPHER=chacha20-ietf-poly1305 \
|
||||||
UPDATER_PERIOD=0
|
# Server data updater
|
||||||
|
UPDATER_PERIOD=0 \
|
||||||
|
# Public IP
|
||||||
|
PUBLICIP_FILE="/tmp/gluetun/ip" \
|
||||||
|
PUBLICIP_PERIOD=12h \
|
||||||
|
# Extras
|
||||||
|
VERSION_INFORMATION=on \
|
||||||
|
TZ= \
|
||||||
|
PUID= \
|
||||||
|
PGID=
|
||||||
ENTRYPOINT ["/entrypoint"]
|
ENTRYPOINT ["/entrypoint"]
|
||||||
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
|
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
|
||||||
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /entrypoint healthcheck
|
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /entrypoint healthcheck
|
||||||
RUN apk add -q --progress --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \
|
ARG TARGETPLATFORM
|
||||||
rm -rf /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-* && \
|
RUN apk add --no-cache --update -l apk-tools && \
|
||||||
|
apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.12/main" openvpn==2.4.11-r0 && \
|
||||||
|
mv /usr/sbin/openvpn /usr/sbin/openvpn2.4 && \
|
||||||
|
apk del openvpn && \
|
||||||
|
apk add --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \
|
||||||
|
mv /usr/sbin/openvpn /usr/sbin/openvpn2.5 && \
|
||||||
|
# Fix vulnerability issue
|
||||||
|
apk add --no-cache --update busybox && \
|
||||||
|
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 openvpn && \
|
||||||
deluser unbound && \
|
deluser unbound && \
|
||||||
mkdir /gluetun
|
mkdir /gluetun
|
||||||
# TODO remove once SAN is added to PIA servers certificates, see https://github.com/pia-foss/manual-connections/issues/10
|
|
||||||
COPY --from=build /tmp/gobuild/entrypoint /entrypoint
|
COPY --from=build /tmp/gobuild/entrypoint /entrypoint
|
||||||
|
|||||||
160
README.md
160
README.md
@@ -1,119 +1,125 @@
|
|||||||
# Gluetun VPN client
|
# Gluetun VPN client
|
||||||
|
|
||||||
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, FastestVPN,
|
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, ExpressVPN, FastestVPN,
|
||||||
HideMyAss, Mullvad, NordVPN, Privado, Private Internet Access, PrivateVPN,
|
HideMyAss, IPVanish, IVPN, Mullvad, NordVPN, Perfect Privacy, Privado, Private Internet Access, PrivateVPN,
|
||||||
ProtonVPN, PureVPN, Surfshark, TorGuard, VyprVPN and Windscribe VPN servers
|
ProtonVPN, PureVPN, Surfshark, TorGuard, VPNUnlimited, VyprVPN, WeVPN and Windscribe VPN servers
|
||||||
using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
using Go, OpenVPN or Wireguard, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
||||||
|
|
||||||
**ANNOUNCEMENT**:
|
**ANNOUNCEMENT**: Wireguard is now supported for all providers supporting it!
|
||||||
|
|
||||||
<img height="250" src="https://raw.githubusercontent.com/qdm12/gluetun/master/title.svg?sanitize=true">
|

|
||||||
|
|
||||||
[](https://hub.docker.com/r/qmcgaw/gluetun/tags?page=1&ordering=last_updated)
|
[](https://github.com/qdm12/gluetun/actions/workflows/ci.yml)
|
||||||
[](https://hub.docker.com/r/qmcgaw/gluetun/tags)
|
|
||||||
|
|
||||||
[](https://hub.docker.com/r/qmcgaw/private-internet-access)
|
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||||
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||||
|
|
||||||
|
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||||
|
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|
[](https://hub.docker.com/r/qmcgaw/gluetun/tags?page=1&ordering=last_updated)
|
||||||
|

|
||||||

|

|
||||||
[](https://github.com/qdm12/gluetun/commits)
|
|
||||||
|
[](https://hub.docker.com/r/qmcgaw/gluetun/tags)
|
||||||
|
|
||||||
|
[](https://github.com/qdm12/gluetun/commits/master)
|
||||||
|
[](https://github.com/qdm12/gluetun/graphs/contributors)
|
||||||
|
[](https://github.com/qdm12/gluetun/pulls?q=is%3Apr+is%3Aclosed)
|
||||||
|
[](https://github.com/qdm12/gluetun/issues)
|
||||||
|
[](https://github.com/qdm12/gluetun/issues?q=is%3Aissue+is%3Aclosed)
|
||||||
|
|
||||||
[](https://github.com/qdm12/gluetun)
|
[](https://github.com/qdm12/gluetun)
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Quick links
|
## Quick links
|
||||||
|
|
||||||
- Problem or suggestion?
|
- [Setup](#Setup)
|
||||||
- [Start a discussion](https://github.com/qdm12/gluetun/discussions)
|
- [Features](#Features)
|
||||||
- [Create an issue](https://github.com/qdm12/gluetun/issues)
|
- Problem?
|
||||||
- [Check the Wiki](https://github.com/qdm12/gluetun/wiki)
|
- [Check the Wiki](https://github.com/qdm12/gluetun/wiki)
|
||||||
|
- [Start a discussion](https://github.com/qdm12/gluetun/discussions)
|
||||||
|
- [Fix the Unraid template](https://github.com/qdm12/gluetun/discussions/550)
|
||||||
|
- Suggestion?
|
||||||
|
- [Create an issue](https://github.com/qdm12/gluetun/issues)
|
||||||
- [Join the Slack channel](https://join.slack.com/t/qdm12/shared_invite/enQtOTE0NjcxNTM1ODc5LTYyZmVlOTM3MGI4ZWU0YmJkMjUxNmQ4ODQ2OTAwYzMxMTlhY2Q1MWQyOWUyNjc2ODliNjFjMDUxNWNmNzk5MDk)
|
- [Join the Slack channel](https://join.slack.com/t/qdm12/shared_invite/enQtOTE0NjcxNTM1ODc5LTYyZmVlOTM3MGI4ZWU0YmJkMjUxNmQ4ODQ2OTAwYzMxMTlhY2Q1MWQyOWUyNjc2ODliNjFjMDUxNWNmNzk5MDk)
|
||||||
- Happy?
|
- Happy?
|
||||||
- Sponsor me on [github.com/sponsors/qdm12](https://github.com/sponsors/qdm12)
|
- Sponsor me on [github.com/sponsors/qdm12](https://github.com/sponsors/qdm12)
|
||||||
- Donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw)
|
- Donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw)
|
||||||
- Drop me [an email](mailto:quentin.mcgaw@gmail.com)
|
- Drop me [an email](mailto:quentin.mcgaw@gmail.com)
|
||||||
|
- Video:
|
||||||
|
|
||||||
|
[](https://youtu.be/0F6I03LQcI4)
|
||||||
|
|
||||||
|
- [Substack Console interview](https://console.substack.com/p/console-72)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Based on Alpine 3.13 for a small Docker image of 52MB
|
- Based on Alpine 3.14 for a small Docker image of 33MB
|
||||||
- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **Vyprvpn**, **Windscribe** servers
|
- Supports: **Cyberghost**, **ExpressVPN**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Perfect Privacy**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **WeVPN**, **Windscribe** servers
|
||||||
- Supports Openvpn only for now
|
- Supports OpenVPN for all providers listed
|
||||||
|
- Supports Wireguard
|
||||||
|
- For **Mullvad**, **Ivpn** and **Windscribe**
|
||||||
|
- For **Torguard**, **VPN Unlimited** and **WeVPN** using [the custom provider](https://github.com/qdm12/gluetun/wiki/Custom-provider)
|
||||||
|
- For custom Wireguard configurations using [the custom provider](https://github.com/qdm12/gluetun/wiki/Custom-provider)
|
||||||
|
- More in progress, see [#134](https://github.com/qdm12/gluetun/issues/134)
|
||||||
- DNS over TLS baked in with service provider(s) of your choice
|
- DNS over TLS baked in with service provider(s) of your choice
|
||||||
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
|
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
|
||||||
- Choose the vpn network protocol, `udp` or `tcp`
|
- Choose the vpn network protocol, `udp` or `tcp`
|
||||||
- Built in firewall kill switch to allow traffic only with needed the VPN servers and LAN devices
|
- 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 Shadowsocks proxy (protocol based on SOCKS5 with an encryption layer, tunnels TCP+UDP)
|
||||||
- Built in HTTP proxy (tunnels HTTP and HTTPS through TCP)
|
- 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 other containers to it](https://github.com/qdm12/gluetun/wiki/Connect-a-container-to-gluetun)
|
||||||
- [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
|
- [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-a-LAN-device-to-gluetun)
|
||||||
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even ppc64le 🎆
|
- 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
|
- [Custom VPN server side port forwarding for Private Internet Access](https://github.com/qdm12/gluetun/wiki/Private-internet-access#vpn-server-port-forwarding)
|
||||||
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
|
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
|
||||||
- Subprograms all drop root privileges once launched
|
- Unbound subprogram drops root privileges once launched
|
||||||
- Subprograms output streams are all merged together
|
|
||||||
- Can work as a Kubernetes sidecar container, thanks @rorph
|
- Can work as a Kubernetes sidecar container, thanks @rorph
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. On some devices you may need to setup your tunnel kernel module on your host with `insmod /lib/modules/tun.ko` or `modprobe tun`
|
🎉 There are now instructions specific to each VPN provider with examples to help you get started as quickly as possible!
|
||||||
- [Synology users Wiki page](https://github.com/qdm12/gluetun/wiki/Synology-setup)
|
|
||||||
1. ⚠️ Raspberry Pi users running 32 bit systems: from image `v3.16.0` you need to do [this](https://github.com/alpinelinux/docker-alpine/issues/135#issuecomment-812287338) on your host to run the container.
|
|
||||||
1. Launch the container with:
|
|
||||||
|
|
||||||
```bash
|
Go to the [Wiki](https://github.com/qdm12/gluetun/wiki)!
|
||||||
docker run -d --name gluetun --cap-add=NET_ADMIN \
|
|
||||||
-e VPNSP="private internet access" -e REGION="CA Montreal" \
|
[🐛 Found a bug in the Wiki?!](https://github.com/qdm12/gluetun/issues/new?assignees=&labels=%F0%9F%93%84+Wiki+issue&template=wiki+issue.yml&title=Wiki+issue%3A+)
|
||||||
-e OPENVPN_USER=js89ds7 -e OPENVPN_PASSWORD=8fd9s239G \
|
|
||||||
-v /yourpath:/gluetun \
|
Here's a docker-compose.yml for the laziest:
|
||||||
qmcgaw/gluetun
|
|
||||||
|
```yml
|
||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
gluetun:
|
||||||
|
image: qmcgaw/gluetun
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
ports:
|
||||||
|
- 8888:8888/tcp # HTTP proxy
|
||||||
|
- 8388:8388/tcp # Shadowsocks
|
||||||
|
- 8388:8388/udp # Shadowsocks
|
||||||
|
volumes:
|
||||||
|
- /yourpath:/gluetun
|
||||||
|
environment:
|
||||||
|
# See https://github.com/qdm12/gluetun/wiki
|
||||||
|
- VPNSP=ivpn
|
||||||
|
- VPN_TYPE=openvpn
|
||||||
|
# OpenVPN:
|
||||||
|
- OPENVPN_USER=
|
||||||
|
- OPENVPN_PASSWORD=
|
||||||
|
# Wireguard:
|
||||||
|
# - WIREGUARD_PRIVATE_KEY=wOEI9rqqbDwnN8/Bpp22sVz48T71vJ4fYmFWujulwUU=
|
||||||
|
# - WIREGUARD_ADDRESS=10.64.222.21/32
|
||||||
|
# Timezone for accurate log times
|
||||||
|
- TZ=
|
||||||
```
|
```
|
||||||
|
|
||||||
or use [docker-compose.yml](https://github.com/qdm12/gluetun/blob/master/docker-compose.yml) with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
echo "your openvpn username" > openvpn_user
|
|
||||||
echo "your openvpn password" > openvpn_password
|
|
||||||
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.
|
|
||||||
|
|
||||||
- Use [Docker secrets](https://github.com/qdm12/gluetun/wiki/Docker-secrets) to read your credentials instead of environment variables
|
|
||||||
- [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.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[](https://github.com/qdm12/gluetun/master/LICENSE)
|
[](https://github.com/qdm12/gluetun/master/LICENSE)
|
||||||
|
|
||||||
## Metadata
|
|
||||||
|
|
||||||
[](https://github.com/qdm12/gluetun/commits)
|
|
||||||
[](https://github.com/qdm12/gluetun/pulls?q=is%3Apr+is%3Aclosed)
|
|
||||||
|
|
||||||
[](https://github.com/qdm12/gluetun/issues)
|
|
||||||
[](https://github.com/qdm12/gluetun/issues?q=is%3Aissue+is%3Aclosed)
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|||||||
@@ -2,15 +2,18 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
nativeos "os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"sync"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
_ "time/tzdata"
|
||||||
|
|
||||||
|
_ "github.com/breml/rootcerts"
|
||||||
"github.com/qdm12/dns/pkg/unbound"
|
"github.com/qdm12/dns/pkg/unbound"
|
||||||
"github.com/qdm12/gluetun/internal/alpine"
|
"github.com/qdm12/gluetun/internal/alpine"
|
||||||
"github.com/qdm12/gluetun/internal/cli"
|
"github.com/qdm12/gluetun/internal/cli"
|
||||||
@@ -20,21 +23,26 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/firewall"
|
"github.com/qdm12/gluetun/internal/firewall"
|
||||||
"github.com/qdm12/gluetun/internal/healthcheck"
|
"github.com/qdm12/gluetun/internal/healthcheck"
|
||||||
"github.com/qdm12/gluetun/internal/httpproxy"
|
"github.com/qdm12/gluetun/internal/httpproxy"
|
||||||
gluetunLogging "github.com/qdm12/gluetun/internal/logging"
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
|
"github.com/qdm12/gluetun/internal/netlink"
|
||||||
"github.com/qdm12/gluetun/internal/openvpn"
|
"github.com/qdm12/gluetun/internal/openvpn"
|
||||||
|
"github.com/qdm12/gluetun/internal/portforward"
|
||||||
"github.com/qdm12/gluetun/internal/publicip"
|
"github.com/qdm12/gluetun/internal/publicip"
|
||||||
"github.com/qdm12/gluetun/internal/routing"
|
"github.com/qdm12/gluetun/internal/routing"
|
||||||
"github.com/qdm12/gluetun/internal/server"
|
"github.com/qdm12/gluetun/internal/server"
|
||||||
"github.com/qdm12/gluetun/internal/shadowsocks"
|
"github.com/qdm12/gluetun/internal/shadowsocks"
|
||||||
"github.com/qdm12/gluetun/internal/storage"
|
"github.com/qdm12/gluetun/internal/storage"
|
||||||
"github.com/qdm12/gluetun/internal/unix"
|
"github.com/qdm12/gluetun/internal/tun"
|
||||||
"github.com/qdm12/gluetun/internal/updater"
|
"github.com/qdm12/gluetun/internal/updater"
|
||||||
versionpkg "github.com/qdm12/gluetun/internal/version"
|
"github.com/qdm12/gluetun/internal/vpn"
|
||||||
|
"github.com/qdm12/golibs/command"
|
||||||
"github.com/qdm12/golibs/logging"
|
"github.com/qdm12/golibs/logging"
|
||||||
"github.com/qdm12/golibs/os"
|
|
||||||
"github.com/qdm12/golibs/os/user"
|
|
||||||
"github.com/qdm12/golibs/params"
|
"github.com/qdm12/golibs/params"
|
||||||
|
"github.com/qdm12/goshutdown"
|
||||||
|
"github.com/qdm12/goshutdown/goroutine"
|
||||||
|
"github.com/qdm12/goshutdown/group"
|
||||||
|
"github.com/qdm12/goshutdown/order"
|
||||||
|
"github.com/qdm12/gosplash"
|
||||||
"github.com/qdm12/updated/pkg/dnscrypto"
|
"github.com/qdm12/updated/pkg/dnscrypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -42,51 +50,56 @@ import (
|
|||||||
var (
|
var (
|
||||||
version = "unknown"
|
version = "unknown"
|
||||||
commit = "unknown"
|
commit = "unknown"
|
||||||
buildDate = "an unknown date"
|
created = "an unknown date"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errSetupRouting = errors.New("cannot setup routing")
|
||||||
|
errCreateUser = errors.New("cannot create user")
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
buildInfo := models.BuildInformation{
|
buildInfo := models.BuildInformation{
|
||||||
Version: version,
|
Version: version,
|
||||||
Commit: commit,
|
Commit: commit,
|
||||||
BuildDate: buildDate,
|
Created: created,
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
background := context.Background()
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
signalCtx, stop := signal.NotifyContext(background, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
|
||||||
|
ctx, cancel := context.WithCancel(background)
|
||||||
|
|
||||||
logger := logging.New(logging.StdLog)
|
logger := logging.New(logging.Settings{
|
||||||
|
Level: logging.LevelInfo,
|
||||||
|
})
|
||||||
|
|
||||||
args := nativeos.Args
|
args := os.Args
|
||||||
os := os.New()
|
tun := tun.New()
|
||||||
osUser := user.New()
|
netLinker := netlink.New()
|
||||||
unix := unix.New()
|
|
||||||
cli := cli.New()
|
cli := cli.New()
|
||||||
|
env := params.New()
|
||||||
|
cmder := command.NewCmder()
|
||||||
|
|
||||||
errorCh := make(chan error)
|
errorCh := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
errorCh <- _main(ctx, buildInfo, args, logger, os, osUser, unix, cli)
|
errorCh <- _main(ctx, buildInfo, args, logger, env, tun, netLinker, cmder, cli)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
signalsCh := make(chan nativeos.Signal, 1)
|
|
||||||
signal.Notify(signalsCh,
|
|
||||||
syscall.SIGINT,
|
|
||||||
syscall.SIGTERM,
|
|
||||||
nativeos.Interrupt,
|
|
||||||
)
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case signal := <-signalsCh:
|
case <-signalCtx.Done():
|
||||||
logger.Warn("Caught OS signal %s, shutting down", signal)
|
stop()
|
||||||
|
fmt.Println("")
|
||||||
|
logger.Warn("Caught OS signal, shutting down")
|
||||||
|
cancel()
|
||||||
case err := <-errorCh:
|
case err := <-errorCh:
|
||||||
|
stop()
|
||||||
close(errorCh)
|
close(errorCh)
|
||||||
if err == nil { // expected exit such as healthcheck
|
if err == nil { // expected exit such as healthcheck
|
||||||
nativeos.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
logger.Error(err)
|
logger.Error(err.Error())
|
||||||
}
|
|
||||||
|
|
||||||
cancel()
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
const shutdownGracePeriod = 5 * time.Second
|
const shutdownGracePeriod = 5 * time.Second
|
||||||
timer := time.NewTimer(shutdownGracePeriod)
|
timer := time.NewTimer(shutdownGracePeriod)
|
||||||
@@ -100,55 +113,99 @@ func main() {
|
|||||||
logger.Warn("Shutdown timed out")
|
logger.Warn("Shutdown timed out")
|
||||||
}
|
}
|
||||||
|
|
||||||
nativeos.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errCommandUnknown = errors.New("command is unknown")
|
||||||
|
)
|
||||||
|
|
||||||
//nolint:gocognit,gocyclo
|
//nolint:gocognit,gocyclo
|
||||||
func _main(ctx context.Context, buildInfo models.BuildInformation,
|
func _main(ctx context.Context, buildInfo models.BuildInformation,
|
||||||
args []string, logger logging.Logger, os os.OS, osUser user.OSUser, unix unix.Unix,
|
args []string, logger logging.ParentLogger, env params.Interface,
|
||||||
cli cli.CLI) error {
|
tun tun.Interface, netLinker netlink.NetLinker, cmder command.RunStarter,
|
||||||
|
cli cli.CLIer) error {
|
||||||
if len(args) > 1 { // cli operation
|
if len(args) > 1 { // cli operation
|
||||||
switch args[1] {
|
switch args[1] {
|
||||||
case "healthcheck":
|
case "healthcheck":
|
||||||
return cli.HealthCheck(ctx)
|
return cli.HealthCheck(ctx, env, logger)
|
||||||
case "clientkey":
|
case "clientkey":
|
||||||
return cli.ClientKey(args[2:], os.OpenFile)
|
return cli.ClientKey(args[2:])
|
||||||
case "openvpnconfig":
|
case "openvpnconfig":
|
||||||
return cli.OpenvpnConfig(os)
|
return cli.OpenvpnConfig(logger, env)
|
||||||
case "update":
|
case "update":
|
||||||
return cli.Update(ctx, args[2:], os)
|
return cli.Update(ctx, args[2:], logger)
|
||||||
|
case "format-servers":
|
||||||
|
return cli.FormatServers(args[2:])
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("command %q is unknown", args[1])
|
return fmt.Errorf("%w: %s", errCommandUnknown, args[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
|
||||||
defer cancel()
|
announcementExp, err := time.Parse(time.RFC3339, "2021-10-02T00: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: "Wireguard is now supported for Mullvad, IVPN and Windscribe!",
|
||||||
|
AnnounceExp: announcementExp,
|
||||||
|
// Sponsor information
|
||||||
|
PaypalUser: "qmcgaw",
|
||||||
|
GithubSponsor: "qdm12",
|
||||||
|
}
|
||||||
|
for _, line := range gosplash.MakeLines(splashSettings) {
|
||||||
|
fmt.Println(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO run this in a loop or in openvpn to reload from file without restarting
|
||||||
|
storageLogger := logger.NewChild(logging.Settings{Prefix: "storage: "})
|
||||||
|
storage, err := storage.New(storageLogger, constants.ServersData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
allServers := storage.GetServers()
|
||||||
|
|
||||||
|
var allSettings configuration.Settings
|
||||||
|
err = allSettings.Read(env, allServers,
|
||||||
|
logger.NewChild(logging.Settings{Prefix: "configuration: "}))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.PatchLevel(allSettings.Log.Level)
|
||||||
|
|
||||||
|
puid, pgid := allSettings.System.PUID, allSettings.System.PGID
|
||||||
|
|
||||||
const clientTimeout = 15 * time.Second
|
const clientTimeout = 15 * time.Second
|
||||||
httpClient := &http.Client{Timeout: clientTimeout}
|
httpClient := &http.Client{Timeout: clientTimeout}
|
||||||
// Create configurators
|
// Create configurators
|
||||||
alpineConf := alpine.NewConfigurator(os.OpenFile, osUser)
|
alpineConf := alpine.New()
|
||||||
ovpnConf := openvpn.NewConfigurator(logger, os, unix)
|
ovpnConf := openvpn.New(
|
||||||
|
logger.NewChild(logging.Settings{Prefix: "openvpn configurator: "}),
|
||||||
|
cmder, puid, pgid)
|
||||||
dnsCrypto := dnscrypto.New(httpClient, "", "")
|
dnsCrypto := dnscrypto.New(httpClient, "", "")
|
||||||
const cacertsPath = "/etc/ssl/certs/ca-certificates.crt"
|
const cacertsPath = "/etc/ssl/certs/ca-certificates.crt"
|
||||||
dnsConf := unbound.NewConfigurator(logger, os.OpenFile, dnsCrypto,
|
dnsConf := unbound.NewConfigurator(nil, cmder, dnsCrypto,
|
||||||
"/etc/unbound", "/usr/sbin/unbound", cacertsPath)
|
"/etc/unbound", "/usr/sbin/unbound", cacertsPath)
|
||||||
routingConf := routing.NewRouting(logger)
|
|
||||||
firewallConf := firewall.NewConfigurator(logger, routingConf, os.OpenFile)
|
|
||||||
|
|
||||||
fmt.Println(gluetunLogging.Splash(buildInfo))
|
err = printVersions(ctx, logger, []printVersionElement{
|
||||||
|
{name: "Alpine", getVersion: alpineConf.Version},
|
||||||
printVersions(ctx, logger, map[string]func(ctx context.Context) (string, error){
|
{name: "OpenVPN 2.4", getVersion: ovpnConf.Version24},
|
||||||
"OpenVPN": ovpnConf.Version,
|
{name: "OpenVPN 2.5", getVersion: ovpnConf.Version25},
|
||||||
"Unbound": dnsConf.Version,
|
{name: "Unbound", getVersion: dnsConf.Version},
|
||||||
"IPtables": firewallConf.Version,
|
{name: "IPtables", getVersion: func(ctx context.Context) (version string, err error) {
|
||||||
|
return firewall.Version(ctx, cmder)
|
||||||
|
}},
|
||||||
})
|
})
|
||||||
|
|
||||||
var allSettings configuration.Settings
|
|
||||||
err := allSettings.Read(params.NewEnv(), os, logger.NewChild(logging.SetPrefix("configuration: ")))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info(allSettings.String())
|
logger.Info(allSettings.String())
|
||||||
|
|
||||||
if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil {
|
if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil {
|
||||||
@@ -158,33 +215,32 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO run this in a loop or in openvpn to reload from file without restarting
|
|
||||||
storage := storage.New(logger, os, 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"
|
const defaultUsername = "nonrootuser"
|
||||||
nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid)
|
nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", errCreateUser, err)
|
||||||
}
|
}
|
||||||
if nonRootUsername != defaultUsername {
|
if nonRootUsername != defaultUsername {
|
||||||
logger.Info("using existing username %s corresponding to user id %d", nonRootUsername, puid)
|
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
|
||||||
|
allSettings.VPN.OpenVPN.ProcUser = nonRootUsername
|
||||||
|
|
||||||
if err := os.Chown("/etc/unbound", puid, pgid); err != nil {
|
if err := os.Chown("/etc/unbound", puid, pgid); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
firewallLogLevel := allSettings.Log.Level
|
||||||
if allSettings.Firewall.Debug {
|
if allSettings.Firewall.Debug {
|
||||||
firewallConf.SetDebug()
|
firewallLogLevel = logging.LevelDebug
|
||||||
routingConf.SetDebug()
|
|
||||||
}
|
}
|
||||||
|
routingLogger := logger.NewChild(logging.Settings{
|
||||||
|
Prefix: "routing: ",
|
||||||
|
Level: firewallLogLevel,
|
||||||
|
})
|
||||||
|
routingConf := routing.New(netLinker, routingLogger)
|
||||||
|
|
||||||
defaultInterface, defaultGateway, err := routingConf.DefaultRoute()
|
defaultInterface, defaultGateway, err := routingConf.DefaultRoute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -201,15 +257,23 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
firewallConf.SetNetworkInformation(defaultInterface, defaultGateway, localNetworks, defaultIP)
|
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 err := routingConf.Setup(); err != nil {
|
||||||
return err
|
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() {
|
defer func() {
|
||||||
routingConf.SetVerbose(false)
|
logger.Info("routing cleanup...")
|
||||||
if err := routingConf.TearDown(); err != nil {
|
if err := routingConf.TearDown(); err != nil {
|
||||||
logger.Error(err)
|
logger.Error("cannot teardown routing: " + err.Error())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -220,17 +284,14 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ovpnConf.CheckTUN(); err != nil {
|
if err := tun.Check(constants.TunnelDevice); err != nil {
|
||||||
logger.Warn(err)
|
logger.Info(err.Error() + "; creating it...")
|
||||||
err = ovpnConf.CreateTUN()
|
err = tun.Create(constants.TunnelDevice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tunnelReadyCh := make(chan struct{})
|
|
||||||
defer close(tunnelReadyCh)
|
|
||||||
|
|
||||||
if allSettings.Firewall.Enabled {
|
if allSettings.Firewall.Enabled {
|
||||||
err := firewallConf.SetEnabled(ctx, true) // disabled by default
|
err := firewallConf.SetEnabled(ctx, true) // disabled by default
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -238,13 +299,6 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
for _, port := range allSettings.Firewall.InputPorts {
|
||||||
err = firewallConf.SetAllowedPort(ctx, port, defaultInterface)
|
err = firewallConf.SetAllowedPort(ctx, port, defaultInterface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -252,162 +306,149 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
}
|
}
|
||||||
} // TODO move inside firewall?
|
} // TODO move inside firewall?
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
// Shutdown settings
|
||||||
healthy := make(chan bool)
|
const totalShutdownTimeout = 3 * time.Second
|
||||||
|
const defaultShutdownTimeout = 400 * time.Millisecond
|
||||||
|
defaultShutdownOnSuccess := func(goRoutineName string) {
|
||||||
|
logger.Info(goRoutineName + ": terminated ✔️")
|
||||||
|
}
|
||||||
|
defaultShutdownOnFailure := func(goRoutineName string, err error) {
|
||||||
|
logger.Warn(goRoutineName + ": " + err.Error() + " ⚠️")
|
||||||
|
}
|
||||||
|
defaultGroupOptions := []group.Option{
|
||||||
|
group.OptionTimeout(defaultShutdownTimeout),
|
||||||
|
group.OptionOnSuccess(defaultShutdownOnSuccess)}
|
||||||
|
|
||||||
openvpnLooper := openvpn.NewLooper(allSettings.OpenVPN, nonRootUsername, puid, pgid, allServers,
|
controlGroupHandler := goshutdown.NewGroupHandler("control", defaultGroupOptions...)
|
||||||
ovpnConf, firewallConf, routingConf, logger, httpClient, os.OpenFile, tunnelReadyCh, healthy, cancel)
|
tickersGroupHandler := goshutdown.NewGroupHandler("tickers", defaultGroupOptions...)
|
||||||
wg.Add(1)
|
otherGroupHandler := goshutdown.NewGroupHandler("other", defaultGroupOptions...)
|
||||||
// wait for restartOpenvpn
|
|
||||||
go openvpnLooper.Run(ctx, wg)
|
portForwardLogger := logger.NewChild(logging.Settings{Prefix: "port forwarding: "})
|
||||||
|
portForwardLooper := portforward.NewLoop(allSettings.VPN.Provider.PortForwarding,
|
||||||
|
httpClient, firewallConf, portForwardLogger)
|
||||||
|
portForwardHandler, portForwardCtx, portForwardDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"port forwarding", goroutine.OptionTimeout(time.Second))
|
||||||
|
go portForwardLooper.Run(portForwardCtx, portForwardDone)
|
||||||
|
|
||||||
|
unboundLogger := logger.NewChild(logging.Settings{Prefix: "dns over tls: "})
|
||||||
|
unboundLooper := dns.NewLoop(dnsConf, allSettings.DNS, httpClient,
|
||||||
|
unboundLogger)
|
||||||
|
dnsHandler, dnsCtx, dnsDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"unbound", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
|
// wait for unboundLooper.Restart or its ticker launched with RunRestartTicker
|
||||||
|
go unboundLooper.Run(dnsCtx, dnsDone)
|
||||||
|
otherGroupHandler.Add(dnsHandler)
|
||||||
|
|
||||||
|
dnsTickerHandler, dnsTickerCtx, dnsTickerDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"dns ticker", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
|
go unboundLooper.RunRestartTicker(dnsTickerCtx, dnsTickerDone)
|
||||||
|
controlGroupHandler.Add(dnsTickerHandler)
|
||||||
|
|
||||||
|
publicIPLooper := publicip.NewLoop(httpClient,
|
||||||
|
logger.NewChild(logging.Settings{Prefix: "ip getter: "}),
|
||||||
|
allSettings.PublicIP, puid, pgid)
|
||||||
|
pubIPHandler, pubIPCtx, pubIPDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"public IP", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
|
go publicIPLooper.Run(pubIPCtx, pubIPDone)
|
||||||
|
otherGroupHandler.Add(pubIPHandler)
|
||||||
|
|
||||||
|
pubIPTickerHandler, pubIPTickerCtx, pubIPTickerDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"public IP", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
|
go publicIPLooper.RunRestartTicker(pubIPTickerCtx, pubIPTickerDone)
|
||||||
|
tickersGroupHandler.Add(pubIPTickerHandler)
|
||||||
|
|
||||||
|
vpnLogger := logger.NewChild(logging.Settings{Prefix: "vpn: "})
|
||||||
|
vpnLooper := vpn.NewLoop(allSettings.VPN, allSettings.Firewall.VPNInputPorts,
|
||||||
|
allServers, ovpnConf, netLinker, firewallConf, routingConf, portForwardLooper,
|
||||||
|
cmder, publicIPLooper, unboundLooper, vpnLogger, httpClient,
|
||||||
|
buildInfo, allSettings.VersionInformation)
|
||||||
|
vpnHandler, vpnCtx, vpnDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"vpn", goroutine.OptionTimeout(time.Second))
|
||||||
|
go vpnLooper.Run(vpnCtx, vpnDone)
|
||||||
|
|
||||||
updaterLooper := updater.NewLooper(allSettings.Updater,
|
updaterLooper := updater.NewLooper(allSettings.Updater,
|
||||||
allServers, storage, openvpnLooper.SetServers, httpClient, logger)
|
allServers, storage, vpnLooper.SetServers, httpClient,
|
||||||
wg.Add(1)
|
logger.NewChild(logging.Settings{Prefix: "updater: "}))
|
||||||
|
updaterHandler, updaterCtx, updaterDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"updater", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
// wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker
|
// wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker
|
||||||
go updaterLooper.Run(ctx, wg)
|
go updaterLooper.Run(updaterCtx, updaterDone)
|
||||||
|
tickersGroupHandler.Add(updaterHandler)
|
||||||
|
|
||||||
unboundLooper := dns.NewLooper(dnsConf, allSettings.DNS, httpClient,
|
updaterTickerHandler, updaterTickerCtx, updaterTickerDone := goshutdown.NewGoRoutineHandler(
|
||||||
logger, nonRootUsername, puid, pgid)
|
"updater ticker", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
wg.Add(1)
|
go updaterLooper.RunRestartTicker(updaterTickerCtx, updaterTickerDone)
|
||||||
// wait for unboundLooper.Restart or its ticker launched with RunRestartTicker
|
controlGroupHandler.Add(updaterTickerHandler)
|
||||||
go unboundLooper.Run(ctx, wg)
|
|
||||||
|
|
||||||
publicIPLooper := publicip.NewLooper(
|
httpProxyLooper := httpproxy.NewLoop(
|
||||||
httpClient, logger, allSettings.PublicIP, puid, pgid, os)
|
logger.NewChild(logging.Settings{Prefix: "http proxy: "}),
|
||||||
wg.Add(1)
|
allSettings.HTTPProxy)
|
||||||
go publicIPLooper.Run(ctx, wg)
|
httpProxyHandler, httpProxyCtx, httpProxyDone := goshutdown.NewGoRoutineHandler(
|
||||||
wg.Add(1)
|
"http proxy", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
go publicIPLooper.RunRestartTicker(ctx, wg)
|
go httpProxyLooper.Run(httpProxyCtx, httpProxyDone)
|
||||||
|
otherGroupHandler.Add(httpProxyHandler)
|
||||||
|
|
||||||
httpProxyLooper := httpproxy.NewLooper(logger, allSettings.HTTPProxy)
|
shadowsocksLooper := shadowsocks.NewLooper(allSettings.ShadowSocks,
|
||||||
wg.Add(1)
|
logger.NewChild(logging.Settings{Prefix: "shadowsocks: "}))
|
||||||
go httpProxyLooper.Run(ctx, wg)
|
shadowsocksHandler, shadowsocksCtx, shadowsocksDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"shadowsocks proxy", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
|
go shadowsocksLooper.Run(shadowsocksCtx, shadowsocksDone)
|
||||||
|
otherGroupHandler.Add(shadowsocksHandler)
|
||||||
|
|
||||||
shadowsocksLooper := shadowsocks.NewLooper(allSettings.ShadowSocks, logger)
|
controlServerAddress := ":" + strconv.Itoa(int(allSettings.ControlServer.Port))
|
||||||
wg.Add(1)
|
|
||||||
go shadowsocksLooper.Run(ctx, wg)
|
|
||||||
|
|
||||||
wg.Add(1)
|
|
||||||
go routeReadyEvents(ctx, wg, buildInfo, tunnelReadyCh,
|
|
||||||
unboundLooper, updaterLooper, publicIPLooper, routingConf, logger, httpClient,
|
|
||||||
allSettings.VersionInformation, allSettings.OpenVPN.Provider.PortForwarding.Enabled, openvpnLooper.PortForward,
|
|
||||||
)
|
|
||||||
controlServerAddress := fmt.Sprintf("0.0.0.0:%d", allSettings.ControlServer.Port)
|
|
||||||
controlServerLogging := allSettings.ControlServer.Log
|
controlServerLogging := allSettings.ControlServer.Log
|
||||||
httpServer := server.New(controlServerAddress, controlServerLogging,
|
httpServerHandler, httpServerCtx, httpServerDone := goshutdown.NewGoRoutineHandler(
|
||||||
logger, buildInfo, openvpnLooper, unboundLooper, updaterLooper, publicIPLooper)
|
"http server", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
wg.Add(1)
|
httpServer := server.New(httpServerCtx, controlServerAddress, controlServerLogging,
|
||||||
go httpServer.Run(ctx, wg)
|
logger.NewChild(logging.Settings{Prefix: "http server: "}),
|
||||||
|
buildInfo, vpnLooper, portForwardLooper, unboundLooper, updaterLooper, publicIPLooper)
|
||||||
|
go httpServer.Run(httpServerCtx, httpServerDone)
|
||||||
|
controlGroupHandler.Add(httpServerHandler)
|
||||||
|
|
||||||
healthcheckServer := healthcheck.NewServer(
|
healthLogger := logger.NewChild(logging.Settings{Prefix: "healthcheck: "})
|
||||||
constants.HealthcheckAddress, logger)
|
healthcheckServer := healthcheck.NewServer(allSettings.Health, healthLogger, vpnLooper)
|
||||||
wg.Add(1)
|
healthServerHandler, healthServerCtx, healthServerDone := goshutdown.NewGoRoutineHandler(
|
||||||
go healthcheckServer.Run(ctx, healthy, wg)
|
"HTTP health server", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
|
go healthcheckServer.Run(healthServerCtx, healthServerDone)
|
||||||
|
|
||||||
// Start openvpn for the first time in a blocking call
|
orderHandler := goshutdown.NewOrderHandler("gluetun",
|
||||||
// until openvpn is launched
|
order.OptionTimeout(totalShutdownTimeout),
|
||||||
_, _ = openvpnLooper.SetStatus(constants.Running) // TODO option to disable with variable
|
order.OptionOnSuccess(defaultShutdownOnSuccess),
|
||||||
|
order.OptionOnFailure(defaultShutdownOnFailure))
|
||||||
|
orderHandler.Append(controlGroupHandler, tickersGroupHandler, healthServerHandler,
|
||||||
|
vpnHandler, portForwardHandler, otherGroupHandler)
|
||||||
|
|
||||||
|
// Start VPN for the first time in a blocking call
|
||||||
|
// until the VPN is launched
|
||||||
|
_, _ = vpnLooper.ApplyStatus(ctx, constants.Running) // TODO option to disable with variable
|
||||||
|
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
|
||||||
if allSettings.OpenVPN.Provider.PortForwarding.Enabled {
|
return orderHandler.Shutdown(context.Background())
|
||||||
logger.Info("Clearing forwarded port status file %s", allSettings.OpenVPN.Provider.PortForwarding.Filepath)
|
|
||||||
if err := os.Remove(allSettings.OpenVPN.Provider.PortForwarding.Filepath); err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if allSettings.Firewall.Enabled {
|
type printVersionElement struct {
|
||||||
const enable = false
|
name string
|
||||||
err := firewallConf.SetEnabled(context.Background(), enable)
|
getVersion func(ctx context.Context) (version string, err error)
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
type infoer interface {
|
||||||
|
Info(s string)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func printVersions(ctx context.Context, logger logging.Logger,
|
func printVersions(ctx context.Context, logger infoer,
|
||||||
versionFunctions map[string]func(ctx context.Context) (string, error)) {
|
elements []printVersionElement) (err error) {
|
||||||
const timeout = 5 * time.Second
|
const timeout = 5 * time.Second
|
||||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
for name, f := range versionFunctions {
|
|
||||||
version, err := f(ctx)
|
for _, element := range elements {
|
||||||
|
version, err := element.getVersion(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
return err
|
||||||
} else {
|
|
||||||
logger.Info("%s version: %s", name, version)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
logger.Info(element.name + " version: " + version)
|
||||||
}
|
}
|
||||||
|
|
||||||
func routeReadyEvents(ctx context.Context, wg *sync.WaitGroup, buildInfo models.BuildInformation,
|
return nil
|
||||||
tunnelReadyCh <-chan struct{},
|
|
||||||
unboundLooper dns.Looper, updaterLooper updater.Looper, publicIPLooper publicip.Looper,
|
|
||||||
routing routing.Routing, logger logging.Logger, httpClient *http.Client,
|
|
||||||
versionInformation, portForwardingEnabled bool, startPortForward func(vpnGateway net.IP)) {
|
|
||||||
defer wg.Done()
|
|
||||||
tickerWg := &sync.WaitGroup{}
|
|
||||||
// for linters only
|
|
||||||
var restartTickerContext context.Context
|
|
||||||
var restartTickerCancel context.CancelFunc = func() {}
|
|
||||||
first := true
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
restartTickerCancel() // for linters only
|
|
||||||
tickerWg.Wait()
|
|
||||||
return
|
|
||||||
case <-tunnelReadyCh: // blocks until openvpn is connected
|
|
||||||
vpnDestination, err := routing.VPNDestinationIP()
|
|
||||||
if err != nil {
|
|
||||||
logger.Warn(err)
|
|
||||||
} else {
|
|
||||||
logger.Info("VPN routing IP address: %s", vpnDestination)
|
|
||||||
}
|
|
||||||
|
|
||||||
if unboundLooper.GetSettings().Enabled {
|
|
||||||
_, _ = unboundLooper.SetStatus(constants.Running)
|
|
||||||
}
|
|
||||||
|
|
||||||
restartTickerCancel() // stop previous restart tickers
|
|
||||||
tickerWg.Wait()
|
|
||||||
restartTickerContext, restartTickerCancel = context.WithCancel(ctx)
|
|
||||||
|
|
||||||
// Runs the Public IP getter job once
|
|
||||||
_, _ = publicIPLooper.SetStatus(constants.Running)
|
|
||||||
if !versionInformation {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
message, err := versionpkg.GetMessage(ctx, buildInfo, httpClient)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
} else {
|
|
||||||
logger.Info(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:gomnd
|
|
||||||
tickerWg.Add(2)
|
|
||||||
go unboundLooper.RunRestartTicker(restartTickerContext, tickerWg)
|
|
||||||
go updaterLooper.RunRestartTicker(restartTickerContext, tickerWg)
|
|
||||||
if portForwardingEnabled {
|
|
||||||
// vpnGateway required only for PIA
|
|
||||||
vpnGateway, err := routing.VPNLocalGatewayIP()
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
}
|
|
||||||
logger.Info("VPN gateway IP address: %s", vpnGateway)
|
|
||||||
startPortForward(vpnGateway)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
version: "3.7"
|
|
||||||
services:
|
|
||||||
gluetun:
|
|
||||||
image: qmcgaw/gluetun
|
|
||||||
container_name: gluetun
|
|
||||||
cap_add:
|
|
||||||
- NET_ADMIN
|
|
||||||
network_mode: bridge
|
|
||||||
ports:
|
|
||||||
- 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
|
|
||||||
secrets:
|
|
||||||
- openvpn_user
|
|
||||||
- openvpn_password
|
|
||||||
environment:
|
|
||||||
# More variables are available, see the readme table
|
|
||||||
- VPNSP=private internet access
|
|
||||||
# Timezone for accurate logs times
|
|
||||||
- TZ=
|
|
||||||
restart: always
|
|
||||||
|
|
||||||
secrets:
|
|
||||||
openvpn_user:
|
|
||||||
file: ./openvpn_user
|
|
||||||
openvpn_password:
|
|
||||||
file: ./openvpn_password
|
|
||||||
46
go.mod
46
go.mod
@@ -1,16 +1,44 @@
|
|||||||
module github.com/qdm12/gluetun
|
module github.com/qdm12/gluetun
|
||||||
|
|
||||||
go 1.16
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fatih/color v1.10.0
|
github.com/breml/rootcerts v0.2.0
|
||||||
github.com/golang/mock v1.5.0
|
github.com/fatih/color v1.13.0
|
||||||
github.com/kyokomi/emoji v2.2.4+incompatible
|
github.com/go-ping/ping v0.0.0-20210911151512-381826476871
|
||||||
github.com/qdm12/dns v1.4.0
|
github.com/golang/mock v1.6.0
|
||||||
github.com/qdm12/golibs v0.0.0-20210215133151-c711ebd3e56a
|
github.com/qdm12/dns v1.11.0
|
||||||
github.com/qdm12/ss-server v0.1.0
|
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6
|
||||||
github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a
|
github.com/qdm12/goshutdown v0.3.0
|
||||||
|
github.com/qdm12/gosplash v0.1.0
|
||||||
|
github.com/qdm12/ss-server v0.3.0
|
||||||
|
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/vishvananda/netlink v1.1.0
|
github.com/vishvananda/netlink v1.1.0
|
||||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20210805125648-3957e9b9dd19
|
||||||
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c
|
||||||
|
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/google/go-cmp v0.5.5 // indirect
|
||||||
|
github.com/google/uuid v1.2.0 // indirect
|
||||||
|
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
|
github.com/mdlayher/genetlink v1.0.0 // indirect
|
||||||
|
github.com/mdlayher/netlink v1.4.0 // indirect
|
||||||
|
github.com/miekg/dns v1.1.40 // indirect
|
||||||
|
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||||
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
|
||||||
|
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d // indirect
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
196
go.sum
196
go.sum
@@ -1,19 +1,21 @@
|
|||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
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/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/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/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/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/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
|
github.com/breml/rootcerts v0.2.0 h1:bBIgVe8bS0Ec+orgWpZ/GRYt3a0O8yoW+g2kSBY2aLE=
|
||||||
|
github.com/breml/rootcerts v0.2.0/go.mod h1:24FDtzYMpqIeYC7QzaE8VPRQaFZU5TIUDlyk8qwjD88=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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/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/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
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/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/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
@@ -30,58 +32,95 @@ github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsd
|
|||||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
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/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/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
github.com/go-ping/ping v0.0.0-20210911151512-381826476871 h1:wtjTfjwAR/BYYMJ+QOLI/3J/qGEI0fgrkZvgsEWK2/Q=
|
||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
github.com/go-ping/ping v0.0.0-20210911151512-381826476871/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
|
||||||
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
|
|
||||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
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/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||||
|
github.com/google/uuid v1.2.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/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/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/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
|
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA=
|
||||||
|
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b h1:c3NTyLNozICy8B4mlMXemD3z/gXgQzVXZS/HqT+i3do=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U=
|
||||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
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.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kyokomi/emoji v2.2.4+incompatible h1:np0woGKwx9LiHAQmwZx79Oc0rHpNw3o+3evou4BEPv4=
|
|
||||||
github.com/kyokomi/emoji v2.2.4+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
|
github.com/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/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
|
||||||
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-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
github.com/mattn/go-colorable v0.1.9/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/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
|
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JAQI/ZLUkCZ7VJS3r74hwFIGXJsgZlY=
|
||||||
|
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
|
||||||
|
github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0=
|
||||||
|
github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=
|
||||||
|
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||||
|
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||||
|
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
|
||||||
|
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
|
||||||
|
github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8=
|
||||||
|
github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
|
||||||
|
github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
|
||||||
|
github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys=
|
||||||
|
github.com/mdlayher/netlink v1.4.0 h1:n3ARR+Fm0dDv37dj5wSWZXDKcy+U0zwcXS3zKMnSiT0=
|
||||||
|
github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8=
|
||||||
|
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
|
||||||
|
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||||
|
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
||||||
|
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
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/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
|
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||||
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
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/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/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/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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/qdm12/dns v1.4.0 h1:P8kVMGo7yIEZSk18fA9XQh9faL1CW20aHosWP064MAA=
|
github.com/qdm12/dns v1.11.0 h1:jpcD5DZXXQSQe5a263PL09ghukiIdptvXFOZvyKEm6Q=
|
||||||
github.com/qdm12/dns v1.4.0/go.mod h1:WUY4/U8Z2O8888DPrahrIBv8GdYeoIcEy4aUDecZ+UM=
|
github.com/qdm12/dns v1.11.0/go.mod h1:FmQsNOUcrrZ4UFzWAiED56AKXeNgaX3ySbmPwEfNjjE=
|
||||||
github.com/qdm12/golibs v0.0.0-20201227203847-2fd99ffdfdba/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
|
github.com/qdm12/golibs v0.0.0-20210603202746-e5494e9c2ebb/go.mod h1:15RBzkun0i8XB7ADIoLJWp9ITRgsz3LroEI2FiOXLRg=
|
||||||
github.com/qdm12/golibs v0.0.0-20210124192933-79a950eaf217/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
|
github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2/go.mod h1:6aRbg4Z/bTbm9JfxsGXfWKHi7zsOvPfUTK1S5HuAFKg=
|
||||||
github.com/qdm12/golibs v0.0.0-20210215133151-c711ebd3e56a h1:DxO9jvcQDtWgKSzxL95828kQxO6WCocP9PPpmIqGMRs=
|
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6 h1:bge5AL7cjHJMPz+5IOz5yF01q/l8No6+lIEBieA8gMg=
|
||||||
github.com/qdm12/golibs v0.0.0-20210215133151-c711ebd3e56a/go.mod h1:y0qNgur9dTkHK2Bb5tK0UCtYyvEiK08flVIglROmnBg=
|
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6/go.mod h1:6aRbg4Z/bTbm9JfxsGXfWKHi7zsOvPfUTK1S5HuAFKg=
|
||||||
github.com/qdm12/ss-server v0.1.0 h1:WV9MkHCDEWRwe4WpnYFeR/zcZAxYoTbfntLDnw9AQ50=
|
github.com/qdm12/goshutdown v0.3.0 h1:pqBpJkdwlZlfTEx4QHtS8u8CXx6pG0fVo6S1N0MpSEM=
|
||||||
github.com/qdm12/ss-server v0.1.0/go.mod h1:ABVUkxubboL3vqBkOwDV9glX1/x7SnYrckBe5d+M/zw=
|
github.com/qdm12/goshutdown v0.3.0/go.mod h1:EqZ46No00kCTZ5qzdd3qIzY6ayhMt24QI8Mh8LVQYmM=
|
||||||
github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a h1:gkyP+gMEeBgMgyRYGrVNcoy6cL1065IvXsyfB6xboIc=
|
github.com/qdm12/gosplash v0.1.0 h1:Sfl+zIjFZFP7b0iqf2l5UkmEY97XBnaKkH3FNY6Gf7g=
|
||||||
github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a/go.mod h1:bbJGxEYCnsA8WU4vBcXYU6mOoHyzdP458FIKP4mfLJM=
|
github.com/qdm12/gosplash v0.1.0/go.mod h1:+A3fWW4/rUeDXhY3ieBzwghKdnIPFJgD8K3qQkenJlw=
|
||||||
|
github.com/qdm12/ss-server v0.3.0 h1:BfKv4OU6dYb2KcDMYpTc7LIuO2jB73g3JCzy988GrLI=
|
||||||
|
github.com/qdm12/ss-server v0.3.0/go.mod h1:ug+nWfuzKw/h5fxL1B6e9/OhkVuWJX4i2V1Pf0pJU1o=
|
||||||
|
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 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
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/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.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
@@ -89,7 +128,6 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
|
|||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
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 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
|
||||||
@@ -98,57 +136,109 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7Zo
|
|||||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
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/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/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
||||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg=
|
||||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
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-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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
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.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-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-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-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-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/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-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d h1:nTDGCTeAu2LhcsHTRzjyIUbZHCJ4QePArsm27Hka0UM=
|
||||||
|
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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-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-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
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-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-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
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-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
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-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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20210427022245-097af6e1351b/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg=
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20210805125648-3957e9b9dd19 h1:ab2jcw2W91Rz07eHAb8Lic7sFQKO0NhBftjv6m/gL/0=
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20210805125648-3957e9b9dd19/go.mod h1:laHzsbfMhGSobUmruXWAyMKKHSqvIcrqZJMyHD+/3O8=
|
||||||
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c h1:ADNrRDI5NR23/TUCnEmlLZLt4u9DnZ2nwRkPrAcFvto=
|
||||||
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c/go.mod h1:+1XihzyZUBJcSc5WO9SwNA7v26puQwOEDwanaxfNXPQ=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
|
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-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/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
|
||||||
@@ -157,6 +247,6 @@ 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.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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
inet.af/netaddr v0.0.0-20210511181906-37180328850c/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722 h1:Qws2rZnQudC58cIagVucPQDLmMi3kAXgxscsgD0v6DU=
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
|
||||||
|
|||||||
@@ -2,22 +2,28 @@
|
|||||||
package alpine
|
package alpine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/qdm12/golibs/os"
|
"os/user"
|
||||||
"github.com/qdm12/golibs/os/user"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Configurator interface {
|
var _ Alpiner = (*Alpine)(nil)
|
||||||
CreateUser(username string, uid int) (createdUsername string, err error)
|
|
||||||
|
type Alpiner interface {
|
||||||
|
UserCreater
|
||||||
|
VersionGetter
|
||||||
}
|
}
|
||||||
|
|
||||||
type configurator struct {
|
type Alpine struct {
|
||||||
openFile os.OpenFileFunc
|
alpineReleasePath string
|
||||||
osUser user.OSUser
|
passwdPath string
|
||||||
|
lookupID func(uid string) (*user.User, error)
|
||||||
|
lookup func(username string) (*user.User, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfigurator(openFile os.OpenFileFunc, osUser user.OSUser) Configurator {
|
func New() *Alpine {
|
||||||
return &configurator{
|
return &Alpine{
|
||||||
openFile: openFile,
|
alpineReleasePath: "/etc/alpine-release",
|
||||||
osUser: osUser,
|
passwdPath: "/etc/passwd",
|
||||||
|
lookupID: user.LookupId,
|
||||||
|
lookup: user.Lookup,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,51 @@
|
|||||||
package alpine
|
package alpine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"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.
|
// CreateUser creates a user in Alpine with the given UID.
|
||||||
func (c *configurator) CreateUser(username string, uid int) (createdUsername string, err error) {
|
func (a *Alpine) CreateUser(username string, uid int) (createdUsername string, err error) {
|
||||||
UIDStr := fmt.Sprintf("%d", uid)
|
UIDStr := strconv.Itoa(uid)
|
||||||
u, err := c.osUser.LookupID(UIDStr)
|
u, err := a.lookupID(UIDStr)
|
||||||
_, unknownUID := err.(user.UnknownUserIdError)
|
_, unknownUID := err.(user.UnknownUserIdError)
|
||||||
if err != nil && !unknownUID {
|
if err != nil && !unknownUID {
|
||||||
return "", fmt.Errorf("cannot create user: %w", err)
|
return "", err
|
||||||
} else if u != nil {
|
}
|
||||||
|
|
||||||
|
if u != nil {
|
||||||
if u.Username == username {
|
if u.Username == username {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
return u.Username, nil
|
return u.Username, nil
|
||||||
}
|
}
|
||||||
u, err = c.osUser.Lookup(username)
|
|
||||||
|
u, err = a.lookup(username)
|
||||||
_, unknownUsername := err.(user.UnknownUserError)
|
_, unknownUsername := err.(user.UnknownUserError)
|
||||||
if err != nil && !unknownUsername {
|
if err != nil && !unknownUsername {
|
||||||
return "", fmt.Errorf("cannot create user: %w", err)
|
return "", err
|
||||||
} else if u != nil {
|
|
||||||
return "", fmt.Errorf("cannot create user: user with name %s already exists for ID %s instead of %d",
|
|
||||||
username, u.Uid, uid)
|
|
||||||
}
|
}
|
||||||
file, err := c.openFile("/etc/passwd", os.O_APPEND|os.O_WRONLY, 0644)
|
|
||||||
|
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 {
|
if err != nil {
|
||||||
return "", fmt.Errorf("cannot create user: %w", err)
|
return "", err
|
||||||
}
|
}
|
||||||
s := fmt.Sprintf("%s:x:%d:::/dev/null:/sbin/nologin\n", username, uid)
|
s := fmt.Sprintf("%s:x:%d:::/dev/null:/sbin/nologin\n", username, uid)
|
||||||
_, err = file.WriteString(s)
|
_, err = file.WriteString(s)
|
||||||
@@ -37,5 +53,6 @@ func (c *configurator) CreateUser(username string, uid int) (createdUsername str
|
|||||||
_ = file.Close()
|
_ = file.Close()
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return username, file.Close()
|
return username, file.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
31
internal/alpine/version.go
Normal file
31
internal/alpine/version.go
Normal 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
|
||||||
|
}
|
||||||
@@ -2,6 +2,6 @@ package cli
|
|||||||
|
|
||||||
import "context"
|
import "context"
|
||||||
|
|
||||||
func (c *cli) CI(context context.Context) error {
|
func (c *CLI) CI(context context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
// Package cli defines an interface CLI to run command line operations.
|
// Package cli defines an interface CLI to run command line operations.
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
var _ CLIer = (*CLI)(nil)
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/qdm12/golibs/os"
|
type CLIer interface {
|
||||||
)
|
ClientKeyFormatter
|
||||||
|
HealthChecker
|
||||||
type CLI interface {
|
OpenvpnConfigMaker
|
||||||
ClientKey(args []string, openFile os.OpenFileFunc) error
|
Updater
|
||||||
HealthCheck(ctx context.Context) error
|
ServersFormatter
|
||||||
OpenvpnConfig(os os.OS) error
|
|
||||||
Update(ctx context.Context, args []string, os os.OS) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type cli struct{}
|
type CLI struct {
|
||||||
|
repoServersPath string
|
||||||
func New() CLI {
|
}
|
||||||
return &cli{}
|
|
||||||
|
func New() *CLI {
|
||||||
|
return &CLI{
|
||||||
|
repoServersPath: "./internal/storage/servers.json",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,24 +3,28 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/golibs/os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *cli) ClientKey(args []string, openFile os.OpenFileFunc) error {
|
type ClientKeyFormatter interface {
|
||||||
|
ClientKey(args []string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CLI) ClientKey(args []string) error {
|
||||||
flagSet := flag.NewFlagSet("clientkey", flag.ExitOnError)
|
flagSet := flag.NewFlagSet("clientkey", flag.ExitOnError)
|
||||||
filepath := flagSet.String("path", constants.ClientKey, "file path to the client.key file")
|
filepath := flagSet.String("path", constants.ClientKey, "file path to the client.key file")
|
||||||
if err := flagSet.Parse(args); err != nil {
|
if err := flagSet.Parse(args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
file, err := openFile(*filepath, os.O_RDONLY, 0)
|
file, err := os.OpenFile(*filepath, os.O_RDONLY, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
data, err := ioutil.ReadAll(file)
|
data, err := io.ReadAll(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = file.Close()
|
_ = file.Close()
|
||||||
return err
|
return err
|
||||||
|
|||||||
133
internal/cli/formatservers.go
Normal file
133
internal/cli/formatservers.go
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/gluetun/internal/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServersFormatter interface {
|
||||||
|
FormatServers(args []string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrFormatNotRecognized = errors.New("format is not recognized")
|
||||||
|
ErrProviderUnspecified = errors.New("VPN provider to format was not specified")
|
||||||
|
ErrOpenOutputFile = errors.New("cannot open output file")
|
||||||
|
ErrWriteOutput = errors.New("cannot write to output file")
|
||||||
|
ErrCloseOutputFile = errors.New("cannot close output file")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *CLI) FormatServers(args []string) error {
|
||||||
|
var format, output string
|
||||||
|
var cyberghost, expressvpn, fastestvpn, hideMyAss, ipvanish, ivpn, mullvad,
|
||||||
|
nordvpn, perfectPrivacy, pia, privado, privatevpn, protonvpn, purevpn, surfshark,
|
||||||
|
torguard, vpnUnlimited, vyprvpn, wevpn, windscribe bool
|
||||||
|
flagSet := flag.NewFlagSet("markdown", flag.ExitOnError)
|
||||||
|
flagSet.StringVar(&format, "format", "markdown", "Format to use which can be: 'markdown'")
|
||||||
|
flagSet.StringVar(&output, "output", "/dev/stdout", "Output file to write the formatted data to")
|
||||||
|
flagSet.BoolVar(&cyberghost, "cyberghost", false, "Format Cyberghost servers")
|
||||||
|
flagSet.BoolVar(&expressvpn, "expressvpn", false, "Format ExpressVPN servers")
|
||||||
|
flagSet.BoolVar(&fastestvpn, "fastestvpn", false, "Format FastestVPN servers")
|
||||||
|
flagSet.BoolVar(&hideMyAss, "hidemyass", false, "Format HideMyAss servers")
|
||||||
|
flagSet.BoolVar(&ipvanish, "ipvanish", false, "Format IpVanish servers")
|
||||||
|
flagSet.BoolVar(&ivpn, "ivpn", false, "Format IVPN servers")
|
||||||
|
flagSet.BoolVar(&mullvad, "mullvad", false, "Format Mullvad servers")
|
||||||
|
flagSet.BoolVar(&nordvpn, "nordvpn", false, "Format Nordvpn servers")
|
||||||
|
flagSet.BoolVar(&perfectPrivacy, "perfectprivacy", false, "Format Perfect Privacy servers")
|
||||||
|
flagSet.BoolVar(&pia, "pia", false, "Format Private Internet Access servers")
|
||||||
|
flagSet.BoolVar(&privado, "privado", false, "Format Privado servers")
|
||||||
|
flagSet.BoolVar(&privatevpn, "privatevpn", false, "Format Private VPN servers")
|
||||||
|
flagSet.BoolVar(&protonvpn, "protonvpn", false, "Format Protonvpn servers")
|
||||||
|
flagSet.BoolVar(&purevpn, "purevpn", false, "Format Purevpn servers")
|
||||||
|
flagSet.BoolVar(&surfshark, "surfshark", false, "Format Surfshark servers")
|
||||||
|
flagSet.BoolVar(&torguard, "torguard", false, "Format Torguard servers")
|
||||||
|
flagSet.BoolVar(&vpnUnlimited, "vpnunlimited", false, "Format VPN Unlimited servers")
|
||||||
|
flagSet.BoolVar(&vyprvpn, "vyprvpn", false, "Format Vyprvpn servers")
|
||||||
|
flagSet.BoolVar(&wevpn, "wevpn", false, "Format WeVPN servers")
|
||||||
|
flagSet.BoolVar(&windscribe, "windscribe", false, "Format Windscribe servers")
|
||||||
|
if err := flagSet.Parse(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if format != "markdown" {
|
||||||
|
return fmt.Errorf("%w: %s", ErrFormatNotRecognized, format)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger := newNoopLogger()
|
||||||
|
storage, err := storage.New(logger, constants.ServersData)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrNewStorage, err)
|
||||||
|
}
|
||||||
|
currentServers := storage.GetServers()
|
||||||
|
|
||||||
|
var formatted string
|
||||||
|
switch {
|
||||||
|
case cyberghost:
|
||||||
|
formatted = currentServers.Cyberghost.ToMarkdown()
|
||||||
|
case expressvpn:
|
||||||
|
formatted = currentServers.Expressvpn.ToMarkdown()
|
||||||
|
case fastestvpn:
|
||||||
|
formatted = currentServers.Fastestvpn.ToMarkdown()
|
||||||
|
case hideMyAss:
|
||||||
|
formatted = currentServers.HideMyAss.ToMarkdown()
|
||||||
|
case ipvanish:
|
||||||
|
formatted = currentServers.Ipvanish.ToMarkdown()
|
||||||
|
case ivpn:
|
||||||
|
formatted = currentServers.Ivpn.ToMarkdown()
|
||||||
|
case mullvad:
|
||||||
|
formatted = currentServers.Mullvad.ToMarkdown()
|
||||||
|
case nordvpn:
|
||||||
|
formatted = currentServers.Nordvpn.ToMarkdown()
|
||||||
|
case perfectPrivacy:
|
||||||
|
formatted = currentServers.Perfectprivacy.ToMarkdown()
|
||||||
|
case pia:
|
||||||
|
formatted = currentServers.Pia.ToMarkdown()
|
||||||
|
case privado:
|
||||||
|
formatted = currentServers.Privado.ToMarkdown()
|
||||||
|
case privatevpn:
|
||||||
|
formatted = currentServers.Privatevpn.ToMarkdown()
|
||||||
|
case protonvpn:
|
||||||
|
formatted = currentServers.Protonvpn.ToMarkdown()
|
||||||
|
case purevpn:
|
||||||
|
formatted = currentServers.Purevpn.ToMarkdown()
|
||||||
|
case surfshark:
|
||||||
|
formatted = currentServers.Surfshark.ToMarkdown()
|
||||||
|
case torguard:
|
||||||
|
formatted = currentServers.Torguard.ToMarkdown()
|
||||||
|
case vpnUnlimited:
|
||||||
|
formatted = currentServers.VPNUnlimited.ToMarkdown()
|
||||||
|
case vyprvpn:
|
||||||
|
formatted = currentServers.Vyprvpn.ToMarkdown()
|
||||||
|
case wevpn:
|
||||||
|
formatted = currentServers.Wevpn.ToMarkdown()
|
||||||
|
case windscribe:
|
||||||
|
formatted = currentServers.Windscribe.ToMarkdown()
|
||||||
|
default:
|
||||||
|
return ErrProviderUnspecified
|
||||||
|
}
|
||||||
|
|
||||||
|
output = filepath.Clean(output)
|
||||||
|
file, err := os.OpenFile(output, os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrOpenOutputFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = fmt.Fprint(file, formatted)
|
||||||
|
if err != nil {
|
||||||
|
_ = file.Close()
|
||||||
|
return fmt.Errorf("%w: %s", ErrWriteOutput, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = file.Close()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrCloseOutputFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -2,19 +2,38 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
"github.com/qdm12/gluetun/internal/healthcheck"
|
"github.com/qdm12/gluetun/internal/healthcheck"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *cli) HealthCheck(ctx context.Context) error {
|
type HealthChecker interface {
|
||||||
|
HealthCheck(ctx context.Context, env params.Interface, warner configuration.Warner) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CLI) HealthCheck(ctx context.Context, env params.Interface,
|
||||||
|
warner configuration.Warner) error {
|
||||||
|
// Extract the health server port from the configuration.
|
||||||
|
config := configuration.Health{}
|
||||||
|
err := config.Read(env, warner)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, port, err := net.SplitHostPort(config.ServerAddress)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const timeout = 10 * time.Second
|
const timeout = 10 * time.Second
|
||||||
httpClient := &http.Client{Timeout: timeout}
|
httpClient := &http.Client{Timeout: timeout}
|
||||||
healthchecker := healthcheck.NewChecker(httpClient)
|
client := healthcheck.NewClient(httpClient)
|
||||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
const url = "http://" + constants.HealthcheckAddress
|
|
||||||
return healthchecker.Check(ctx, url)
|
url := "http://127.0.0.1:" + port
|
||||||
|
return client.Check(ctx, url)
|
||||||
}
|
}
|
||||||
|
|||||||
16
internal/cli/nooplogger.go
Normal file
16
internal/cli/nooplogger.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import "github.com/qdm12/golibs/logging"
|
||||||
|
|
||||||
|
type noopLogger struct{}
|
||||||
|
|
||||||
|
func newNoopLogger() *noopLogger {
|
||||||
|
return new(noopLogger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) Debug(s string) {}
|
||||||
|
func (l *noopLogger) Info(s string) {}
|
||||||
|
func (l *noopLogger) Warn(s string) {}
|
||||||
|
func (l *noopLogger) Error(s string) {}
|
||||||
|
func (l *noopLogger) PatchLevel(level logging.Level) {}
|
||||||
|
func (l *noopLogger) PatchPrefix(prefix string) {}
|
||||||
@@ -9,30 +9,40 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/provider"
|
"github.com/qdm12/gluetun/internal/provider"
|
||||||
"github.com/qdm12/gluetun/internal/storage"
|
"github.com/qdm12/gluetun/internal/storage"
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
"github.com/qdm12/golibs/os"
|
|
||||||
"github.com/qdm12/golibs/params"
|
"github.com/qdm12/golibs/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *cli) OpenvpnConfig(os os.OS) error {
|
type OpenvpnConfigMaker interface {
|
||||||
logger := logging.New(logging.StdLog)
|
OpenvpnConfig(logger OpenvpnConfigLogger, env params.Interface) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpenvpnConfigLogger interface {
|
||||||
|
Info(s string)
|
||||||
|
Warn(s string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CLI) OpenvpnConfig(logger OpenvpnConfigLogger, env params.Interface) error {
|
||||||
|
storage, err := storage.New(logger, constants.ServersData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
allServers := storage.GetServers()
|
||||||
|
|
||||||
var allSettings configuration.Settings
|
var allSettings configuration.Settings
|
||||||
err := allSettings.Read(params.NewEnv(), os, logger)
|
err = allSettings.Read(env, allServers, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
allServers, err := storage.New(logger, os, constants.ServersData).
|
providerConf := provider.New(allSettings.VPN.Provider.Name, allServers, time.Now)
|
||||||
SyncServers(constants.GetAllServers())
|
connection, err := providerConf.GetConnection(allSettings.VPN.Provider.ServerSelection)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
providerConf := provider.New(allSettings.OpenVPN.Provider.Name, allServers, time.Now)
|
lines, err := providerConf.BuildConf(connection, allSettings.VPN.OpenVPN)
|
||||||
connection, err := providerConf.GetOpenVPNConnection(allSettings.OpenVPN.Provider.ServerSelection)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
lines := providerConf.BuildConf(connection, "nonroortuser", allSettings.OpenVPN)
|
|
||||||
fmt.Println(strings.Join(lines, "\n"))
|
fmt.Println(strings.Join(lines, "\n"))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,31 +2,56 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration"
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/gluetun/internal/storage"
|
"github.com/qdm12/gluetun/internal/storage"
|
||||||
"github.com/qdm12/gluetun/internal/updater"
|
"github.com/qdm12/gluetun/internal/updater"
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
"github.com/qdm12/golibs/os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *cli) Update(ctx context.Context, args []string, os os.OS) error {
|
var (
|
||||||
|
ErrModeUnspecified = errors.New("at least one of -enduser or -maintainer must be specified")
|
||||||
|
ErrNewStorage = errors.New("cannot create storage")
|
||||||
|
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 UpdaterLogger) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdaterLogger interface {
|
||||||
|
Info(s string)
|
||||||
|
Warn(s string)
|
||||||
|
Error(s string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) error {
|
||||||
options := configuration.Updater{CLI: true}
|
options := configuration.Updater{CLI: true}
|
||||||
var flushToFile bool
|
var endUserMode, maintainerMode, updateAll bool
|
||||||
flagSet := flag.NewFlagSet("update", flag.ExitOnError)
|
flagSet := flag.NewFlagSet("update", flag.ExitOnError)
|
||||||
flagSet.BoolVar(&flushToFile, "file", false, "Write results to /gluetun/servers.json (for end users)")
|
flagSet.BoolVar(&endUserMode, "enduser", false, "Write results to /gluetun/servers.json (for end users)")
|
||||||
flagSet.BoolVar(&options.Stdout, "stdout", false, "Write results to console to modify the program (for maintainers)")
|
flagSet.BoolVar(&maintainerMode, "maintainer", false,
|
||||||
|
"Write results to ./internal/storage/servers.json to modify the program (for maintainers)")
|
||||||
flagSet.StringVar(&options.DNSAddress, "dns", "8.8.8.8", "DNS resolver address to use")
|
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.Cyberghost, "cyberghost", false, "Update Cyberghost servers")
|
||||||
|
flagSet.BoolVar(&options.Expressvpn, "expressvpn", false, "Update ExpressVPN servers")
|
||||||
flagSet.BoolVar(&options.Fastestvpn, "fastestvpn", false, "Update FastestVPN servers")
|
flagSet.BoolVar(&options.Fastestvpn, "fastestvpn", false, "Update FastestVPN servers")
|
||||||
flagSet.BoolVar(&options.HideMyAss, "hidemyass", false, "Update HideMyAss 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.Mullvad, "mullvad", false, "Update Mullvad servers")
|
||||||
flagSet.BoolVar(&options.Nordvpn, "nordvpn", false, "Update Nordvpn servers")
|
flagSet.BoolVar(&options.Nordvpn, "nordvpn", false, "Update Nordvpn servers")
|
||||||
|
flagSet.BoolVar(&options.Perfectprivacy, "perfectprivacy", false, "Update Perfect Privacy servers")
|
||||||
flagSet.BoolVar(&options.PIA, "pia", false, "Update Private Internet Access post-summer 2020 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.Privado, "privado", false, "Update Privado servers")
|
||||||
flagSet.BoolVar(&options.Privatevpn, "privatevpn", false, "Update Private VPN servers")
|
flagSet.BoolVar(&options.Privatevpn, "privatevpn", false, "Update Private VPN servers")
|
||||||
@@ -34,33 +59,63 @@ func (c *cli) Update(ctx context.Context, args []string, os os.OS) error {
|
|||||||
flagSet.BoolVar(&options.Purevpn, "purevpn", false, "Update Purevpn servers")
|
flagSet.BoolVar(&options.Purevpn, "purevpn", false, "Update Purevpn servers")
|
||||||
flagSet.BoolVar(&options.Surfshark, "surfshark", false, "Update Surfshark servers")
|
flagSet.BoolVar(&options.Surfshark, "surfshark", false, "Update Surfshark servers")
|
||||||
flagSet.BoolVar(&options.Torguard, "torguard", false, "Update Torguard 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.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers")
|
||||||
|
flagSet.BoolVar(&options.Wevpn, "wevpn", false, "Update WeVPN servers")
|
||||||
flagSet.BoolVar(&options.Windscribe, "windscribe", false, "Update Windscribe servers")
|
flagSet.BoolVar(&options.Windscribe, "windscribe", false, "Update Windscribe servers")
|
||||||
if err := flagSet.Parse(args); err != nil {
|
if err := flagSet.Parse(args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger := logging.New(logging.StdLog)
|
if !endUserMode && !maintainerMode {
|
||||||
if !flushToFile && !options.Stdout {
|
return ErrModeUnspecified
|
||||||
return fmt.Errorf("at least one of -file or -stdout must be specified")
|
}
|
||||||
|
|
||||||
|
if updateAll {
|
||||||
|
options.EnableAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientTimeout = 10 * time.Second
|
const clientTimeout = 10 * time.Second
|
||||||
httpClient := &http.Client{Timeout: clientTimeout}
|
httpClient := &http.Client{Timeout: clientTimeout}
|
||||||
storage := storage.New(logger, os, constants.ServersData)
|
|
||||||
currentServers, err := storage.SyncServers(constants.GetAllServers())
|
storage, err := storage.New(logger, constants.ServersData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot update servers: %w", err)
|
return fmt.Errorf("%w: %s", ErrNewStorage, err)
|
||||||
}
|
}
|
||||||
|
currentServers := storage.GetServers()
|
||||||
|
|
||||||
updater := updater.New(options, httpClient, currentServers, logger)
|
updater := updater.New(options, httpClient, currentServers, logger)
|
||||||
allServers, err := updater.UpdateServers(ctx)
|
allServers, err := updater.UpdateServers(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", ErrUpdateServerInformation, err)
|
||||||
}
|
}
|
||||||
if flushToFile {
|
|
||||||
|
if endUserMode {
|
||||||
if err := storage.FlushToFile(allServers); err != nil {
|
if err := storage.FlushToFile(allServers); err != nil {
|
||||||
return fmt.Errorf("cannot update servers: %w", err)
|
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
|
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)
|
||||||
|
}
|
||||||
|
|||||||
95
internal/configuration/custom.go
Normal file
95
internal/configuration/custom.go
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errCustomNotSupported = errors.New("custom provider is not supported")
|
||||||
|
errCustomExtractFromFile = errors.New("cannot extract configuration from file")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) readCustom(r reader, vpnType string) (err error) {
|
||||||
|
settings.Name = constants.Custom
|
||||||
|
|
||||||
|
switch vpnType {
|
||||||
|
case constants.OpenVPN:
|
||||||
|
return settings.ServerSelection.OpenVPN.readCustom(r)
|
||||||
|
case constants.Wireguard:
|
||||||
|
return settings.ServerSelection.Wireguard.readCustom(r)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%w: for VPN type %s", errCustomNotSupported, vpnType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPNSelection) readCustom(r reader) (err error) {
|
||||||
|
configFile, err := r.env.Get("OPENVPN_CUSTOM_CONFIG", params.CaseSensitiveValue(), params.Compulsory())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable OPENVPN_CUSTOM_CONFIG: %w", err)
|
||||||
|
}
|
||||||
|
settings.ConfFile = configFile
|
||||||
|
|
||||||
|
// For display and consistency purposes only,
|
||||||
|
// these values are not actually used since the file is re-read
|
||||||
|
// before each OpenVPN start.
|
||||||
|
_, connection, err := r.ovpnExt.Data(configFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", errCustomExtractFromFile, err)
|
||||||
|
}
|
||||||
|
settings.TCP = connection.Protocol == constants.TCP
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPN) readCustom(r reader) (err error) {
|
||||||
|
settings.ConfFile, err = r.env.Path("OPENVPN_CUSTOM_CONFIG",
|
||||||
|
params.Compulsory(), params.CaseSensitiveValue())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable OPENVPN_CUSTOM_CONFIG: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *WireguardSelection) readCustom(r reader) (err error) {
|
||||||
|
settings.PublicKey, err = r.env.Get("WIREGUARD_PUBLIC_KEY",
|
||||||
|
params.CaseSensitiveValue(), params.Compulsory())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable WIREGUARD_PUBLIC_KEY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.EndpointIP, err = readWireguardEndpointIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.EndpointPort, err = r.env.Port("WIREGUARD_ENDPOINT_PORT", params.Compulsory(),
|
||||||
|
params.RetroKeys([]string{"WIREGUARD_PORT"}, r.onRetroActive))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable WIREGUARD_ENDPOINT_PORT: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readWireguardEndpointIP reads and parses the server endpoint IP
|
||||||
|
// address from the environment variable WIREGUARD_ENDPOINT_IP.
|
||||||
|
func readWireguardEndpointIP(env params.Interface) (endpointIP net.IP, err error) {
|
||||||
|
s, err := env.Get("WIREGUARD_ENDPOINT_IP", params.Compulsory())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("environment variable WIREGUARD_ENDPOINT_IP: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
endpointIP = net.ParseIP(s)
|
||||||
|
if endpointIP == nil {
|
||||||
|
return nil, fmt.Errorf("environment variable WIREGUARD_ENDPOINT_IP: %w: %s",
|
||||||
|
ErrInvalidIP, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpointIP, nil
|
||||||
|
}
|
||||||
@@ -1,107 +1,47 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/golibs/params"
|
"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 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) {
|
func (settings *Provider) readCyberghost(r reader) (err error) {
|
||||||
settings.Name = constants.Cyberghost
|
settings.Name = constants.Cyberghost
|
||||||
|
servers := r.servers.GetCyberghost()
|
||||||
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ExtraConfigOptions.ClientKey, err = readCyberghostClientKey(r)
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY",
|
||||||
|
constants.CyberghostCountryChoices(servers),
|
||||||
|
params.RetroKeys([]string{"REGION"}, r.onRetroActive))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ExtraConfigOptions.ClientCertificate, err = readCyberghostClientCertificate(r)
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
|
||||||
|
constants.CyberghostHostnameChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Group, err = r.env.Inside("CYBERGHOST_GROUP",
|
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r)
|
||||||
constants.CyberghostGroupChoices(), params.Default("Premium UDP Europe"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.CyberghostRegionChoices())
|
func (settings *OpenVPN) readCyberghost(r reader) (err error) {
|
||||||
|
settings.ClientKey, err = readClientKey(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", errClientKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ClientCrt, err = readClientCertificate(r)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", errClientCert, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readCyberghostClientKey(r reader) (clientKey string, err error) {
|
|
||||||
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTKEY", constants.ClientKey)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return extractClientKey(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractClientKey(b []byte) (key string, err error) {
|
|
||||||
pemBlock, _ := pem.Decode(b)
|
|
||||||
if pemBlock == nil {
|
|
||||||
return "", fmt.Errorf("cannot decode PEM block from client key")
|
|
||||||
}
|
|
||||||
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 readCyberghostClientCertificate(r reader) (clientCertificate string, err error) {
|
|
||||||
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTCRT", constants.ClientCertificate)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return extractClientCertificate(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractClientCertificate(b []byte) (certificate string, err error) {
|
|
||||||
pemBlock, _ := pem.Decode(b)
|
|
||||||
if pemBlock == nil {
|
|
||||||
return "", fmt.Errorf("cannot decode PEM block from client certificate")
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,175 +0,0 @@
|
|||||||
package configuration
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_extractClientKey(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
const validPEM = `
|
|
||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCrQDrezCptkWxX
|
|
||||||
ywm3KdXtvti+rPF3vfzOmXRKiKXDMpMxzoiaD5Wspirxxjr4C+B14xTwZjJZfxJL
|
|
||||||
2HpPdOeBmA5tmAoGUESspnzxR/N1T4Uggx0vlAzFo0UZ0sutV6CJK19Kk38REwlG
|
|
||||||
AB8gl6JYeSUuu6qREjrLVwFRH72acvC/p4jBki/MjAfEaeHc0yDJT9jpjpchw+Hx
|
|
||||||
Ymy+1BnfNTAfGDdTVx9qWb+ByQ7xfvzuD9AOeqiWApDzZIuDDsaWn2orv+syoJVo
|
|
||||||
rV52/F+75zks6+fzQ+0sotBlRyvsZKGi80F89RIHwG+5LNSuRDWnVvrwv1oc6V2/
|
|
||||||
lMidwT7yb0kXt0IRW6JzbaHyB2LkPazBlr6IPNupk83x9t2Buw0HI2SQKHMKOChU
|
|
||||||
i2/906yLUOo3QpAi3Wk1c/Xu9DvGR/pOA15WCakiAfG3Fq6hUxNncmpOMeOLF/ez
|
|
||||||
L19jZ3KA4E2Te4+GA0NYlXgkDbsIILWapHwqHXcDukynHisr7RawjrvXoLyasm4L
|
|
||||||
O66aNXK9wtipSMDA7tdlQP6Xe9bHflDHxwreiuEGxnrsvLU7LHBWdD7UT2/u8zdr
|
|
||||||
pimqi4L7W5p5aOBMn8jSVCO9+4CAxiLlc2qx5vb4/EPMsdQfacYP7vY9iVh/qPi3
|
|
||||||
bcUVGUlg8wAJDrTksxU1K3FVR7BEPwIDAQABAoICAAhyrbTJ+5nWH7MhCASqIqyM
|
|
||||||
yqJ1Y6AVlkAW397BaPP9Lbe6SZDYDfkrZVjx/3y3EUafgivtzrQNibiGIFqFGNqS
|
|
||||||
xrtvUadIFGsz91vrwb3aw2V8MldjhVHGoSUJ+hQ+C2RY6GWEazNLbhyu6tovwMl+
|
|
||||||
iHAKv/pSHOZlD2KSH0dcPjYmLJ/n90Wu7r8ovgSnwalMsBWtfBUlVaMTyOuNCQ2y
|
|
||||||
0QHnrusElD8p2EGtynftXMrdqtTcBi8IR2BKaHt5oiBSEum/mPmxZE16p/tUreBW
|
|
||||||
IsLtjE663htimMc2QJtzx2mDeIqSiGYrfxdyd7d1E/SCXPS9a9ObS42k6FSn8NPu
|
|
||||||
K5kN6fPV5EDM2CqKEt9QZPlyrjZIuffOZtJj0xPuTwhRle4SOtyjn2c/vsv9Fkrp
|
|
||||||
B6B1v7T4+SSOIedOYkL+FP/IexMNG/ZTB5Y2hrZ03JW9RGpFAa4//qGG2qUCR3hE
|
|
||||||
rVS6v58qO/3+TCFSn/TI8AfcTcJbes3yTbVyLH6NAjATfYqJDJJFf+PG0qKc8q1N
|
|
||||||
KvXmT+x4JiBBM32cOg11GPflxIZSKi9He50hnPGnC042N06ba/pkUPG49XwE37hn
|
|
||||||
kIGmcFGcDIMDTEZnPBogPFqGpepYdwGWxbadRiUoX2wgurHRRmA0YM32MjVky9C1
|
|
||||||
12Q/Jy9PIk/qdjYdWfAhAoIBAQDcvxfUx7MKMFgYYm4E51X+7B9QoxdhVaxcoVgK
|
|
||||||
VwfvedsLi0Bk1B1JVSXqnNfyDZbpxFz2v5Xd/dSit2rjnfBm+DoJYN9ZNnrbIH+s
|
|
||||||
qsO1DuHZvMZlRDJbpt7PpVH/pcf7rEWRY+avkMMsiGwI/ruDs17eu7jULeG7N4jb
|
|
||||||
kh1mdvF7K56O6Xe8jGJu5qaOPRWOkABK1cEOjQ5hB1iAwO/ua5hehP87SvbYzIhz
|
|
||||||
nQTE3AqTWgWbIyC4R85U7tS9hsXnSQ/ICM9pWcyN0Y667LwR2tX0QKl5M/YoM0sG
|
|
||||||
mw+VQED8O2R45jTzSAcox77dRg55Pp3Xexsp2iVvaTIeAaevAoIBAQDGmZS1gFO4
|
|
||||||
TEgQXHdmibLizDUHLuw662GC+3Hilx+nZBZtWOc6t8yquUyggSKQmBDiKAf0ipMe
|
|
||||||
xFao+5I3StJJ2P4Vel95Vcu8KgqCF736Q1iNgDHuW8ho8e0y+YE371x5co3POGC0
|
|
||||||
SfbcnRTXQx2+wWXzZDh+KtnaDUyDN12/qCIUyAuSVLwEM28ZFM3qadG1aUdCB5oe
|
|
||||||
o8jfgsg6YSukm4uE/tuI3/wAI7FkaCqvt/zkLauRff5FcNa7os4EKtNnGfebxscP
|
|
||||||
yFYpMsW9VI0rfmYz02gho33lnofs4o8x/gxh6t5zfVbsZ7vUiSDJBahWboG9aE99
|
|
||||||
OY2TKb6ibsBxAoIBAQChDBVR2oPnqg+Lcrw7fZ8Cxbeu992F2KBQUDHQEWCruSYy
|
|
||||||
zNwk84+OQb3Q5a6yXHG+iNEd//ZRp+8q60/jUgXiybRlxTQNfS6ykYo0Kb1wabQi
|
|
||||||
S5Qeq1tl/F9P9JfXQFafaTaz9MOHUMDjy3+uLFIXqpRLQX995R9rm/+P2ZDzgVF5
|
|
||||||
///E2dXOTElACax3117TzIE6F6qqeASGi3ppLNmfAwZ95t/inTVsRARE/MhO6w4Y
|
|
||||||
JLQ0U7N6XoDM/BVfVGUr8OS/lpXjkW0oBjvwaehnylUPxuEdmOg8ufdBkX0T8XW3
|
|
||||||
z4jkn2cAGouGl/vKqWLD2AgF/j16Ejn/hyrWM3TnAoIBAA6lSssrwIDJ11KljwSX
|
|
||||||
yQJirtJtymv56cIACwD7xhDRF7pOoRa6cTRx383CWCszm6Mh8pw9D+Zn8kAZ9Ulw
|
|
||||||
khtyDiLFWH8ZLaIds5Kub4siJkihGI2MZTYgCS8GKVpXo4ktQnnynWcOQU85okzR
|
|
||||||
nULw/jS5wlTDkjc7XdYbYiV9H65KplfPOeJRbLL7zsensBhhwCiFaP8zct/QxDVR
|
|
||||||
7yb/dYWESepJIktcVnuiFuvIdLTbDVj4YqT6UkuaEPlLszVaO+FYAlwOmRQGs4Bn
|
|
||||||
2NVJR/4wa/B3HxSs4Tc96fN02bLq4CbCKoPajoZ46lsIuMZO9fBi3eHNObyNiopu
|
|
||||||
AnECggEBAJiJ0tK/PGh+Q9uv57Z4QcmbawoxMQW1qK/rLYwacYsSpzo8VhbZf+Jh
|
|
||||||
8biMg9AIQsLWnqmB3gmndePArGXkSxnilRozNLaeclTZy7rh00BctTEfgee4Kxdi
|
|
||||||
JKkJlVK0CE8I6txVRqkoOMyxsk1kRZ4l2yW2nxzyWlJKwvD75x2PQ6xWvpLAggyn
|
|
||||||
q00I3MzNIgR123jytN1NyC7l+mnGoC23ToXM7B3/PQjGYTq3jawKomrX1cmwzKBT
|
|
||||||
+pzjtJSWvMeUEZQS1PpOhxpPBRHagdKXt+ug2DqDtU6rfpDGtTBh5QNkg5SA7lxZ
|
|
||||||
zZjrL52saevO25cigVl+hxcnY8DTpbk=
|
|
||||||
-----END PRIVATE KEY-----
|
|
||||||
`
|
|
||||||
const validKeyString = "MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCrQDrezCptkWxXywm3KdXtvti+rPF3vfzOmXRKiKXDMpMxzoiaD5Wspirxxjr4C+B14xTwZjJZfxJL2HpPdOeBmA5tmAoGUESspnzxR/N1T4Uggx0vlAzFo0UZ0sutV6CJK19Kk38REwlGAB8gl6JYeSUuu6qREjrLVwFRH72acvC/p4jBki/MjAfEaeHc0yDJT9jpjpchw+HxYmy+1BnfNTAfGDdTVx9qWb+ByQ7xfvzuD9AOeqiWApDzZIuDDsaWn2orv+syoJVorV52/F+75zks6+fzQ+0sotBlRyvsZKGi80F89RIHwG+5LNSuRDWnVvrwv1oc6V2/lMidwT7yb0kXt0IRW6JzbaHyB2LkPazBlr6IPNupk83x9t2Buw0HI2SQKHMKOChUi2/906yLUOo3QpAi3Wk1c/Xu9DvGR/pOA15WCakiAfG3Fq6hUxNncmpOMeOLF/ezL19jZ3KA4E2Te4+GA0NYlXgkDbsIILWapHwqHXcDukynHisr7RawjrvXoLyasm4LO66aNXK9wtipSMDA7tdlQP6Xe9bHflDHxwreiuEGxnrsvLU7LHBWdD7UT2/u8zdrpimqi4L7W5p5aOBMn8jSVCO9+4CAxiLlc2qx5vb4/EPMsdQfacYP7vY9iVh/qPi3bcUVGUlg8wAJDrTksxU1K3FVR7BEPwIDAQABAoICAAhyrbTJ+5nWH7MhCASqIqyMyqJ1Y6AVlkAW397BaPP9Lbe6SZDYDfkrZVjx/3y3EUafgivtzrQNibiGIFqFGNqSxrtvUadIFGsz91vrwb3aw2V8MldjhVHGoSUJ+hQ+C2RY6GWEazNLbhyu6tovwMl+iHAKv/pSHOZlD2KSH0dcPjYmLJ/n90Wu7r8ovgSnwalMsBWtfBUlVaMTyOuNCQ2y0QHnrusElD8p2EGtynftXMrdqtTcBi8IR2BKaHt5oiBSEum/mPmxZE16p/tUreBWIsLtjE663htimMc2QJtzx2mDeIqSiGYrfxdyd7d1E/SCXPS9a9ObS42k6FSn8NPuK5kN6fPV5EDM2CqKEt9QZPlyrjZIuffOZtJj0xPuTwhRle4SOtyjn2c/vsv9FkrpB6B1v7T4+SSOIedOYkL+FP/IexMNG/ZTB5Y2hrZ03JW9RGpFAa4//qGG2qUCR3hErVS6v58qO/3+TCFSn/TI8AfcTcJbes3yTbVyLH6NAjATfYqJDJJFf+PG0qKc8q1NKvXmT+x4JiBBM32cOg11GPflxIZSKi9He50hnPGnC042N06ba/pkUPG49XwE37hnkIGmcFGcDIMDTEZnPBogPFqGpepYdwGWxbadRiUoX2wgurHRRmA0YM32MjVky9C112Q/Jy9PIk/qdjYdWfAhAoIBAQDcvxfUx7MKMFgYYm4E51X+7B9QoxdhVaxcoVgKVwfvedsLi0Bk1B1JVSXqnNfyDZbpxFz2v5Xd/dSit2rjnfBm+DoJYN9ZNnrbIH+sqsO1DuHZvMZlRDJbpt7PpVH/pcf7rEWRY+avkMMsiGwI/ruDs17eu7jULeG7N4jbkh1mdvF7K56O6Xe8jGJu5qaOPRWOkABK1cEOjQ5hB1iAwO/ua5hehP87SvbYzIhznQTE3AqTWgWbIyC4R85U7tS9hsXnSQ/ICM9pWcyN0Y667LwR2tX0QKl5M/YoM0sGmw+VQED8O2R45jTzSAcox77dRg55Pp3Xexsp2iVvaTIeAaevAoIBAQDGmZS1gFO4TEgQXHdmibLizDUHLuw662GC+3Hilx+nZBZtWOc6t8yquUyggSKQmBDiKAf0ipMexFao+5I3StJJ2P4Vel95Vcu8KgqCF736Q1iNgDHuW8ho8e0y+YE371x5co3POGC0SfbcnRTXQx2+wWXzZDh+KtnaDUyDN12/qCIUyAuSVLwEM28ZFM3qadG1aUdCB5oeo8jfgsg6YSukm4uE/tuI3/wAI7FkaCqvt/zkLauRff5FcNa7os4EKtNnGfebxscPyFYpMsW9VI0rfmYz02gho33lnofs4o8x/gxh6t5zfVbsZ7vUiSDJBahWboG9aE99OY2TKb6ibsBxAoIBAQChDBVR2oPnqg+Lcrw7fZ8Cxbeu992F2KBQUDHQEWCruSYyzNwk84+OQb3Q5a6yXHG+iNEd//ZRp+8q60/jUgXiybRlxTQNfS6ykYo0Kb1wabQiS5Qeq1tl/F9P9JfXQFafaTaz9MOHUMDjy3+uLFIXqpRLQX995R9rm/+P2ZDzgVF5///E2dXOTElACax3117TzIE6F6qqeASGi3ppLNmfAwZ95t/inTVsRARE/MhO6w4YJLQ0U7N6XoDM/BVfVGUr8OS/lpXjkW0oBjvwaehnylUPxuEdmOg8ufdBkX0T8XW3z4jkn2cAGouGl/vKqWLD2AgF/j16Ejn/hyrWM3TnAoIBAA6lSssrwIDJ11KljwSXyQJirtJtymv56cIACwD7xhDRF7pOoRa6cTRx383CWCszm6Mh8pw9D+Zn8kAZ9UlwkhtyDiLFWH8ZLaIds5Kub4siJkihGI2MZTYgCS8GKVpXo4ktQnnynWcOQU85okzRnULw/jS5wlTDkjc7XdYbYiV9H65KplfPOeJRbLL7zsensBhhwCiFaP8zct/QxDVR7yb/dYWESepJIktcVnuiFuvIdLTbDVj4YqT6UkuaEPlLszVaO+FYAlwOmRQGs4Bn2NVJR/4wa/B3HxSs4Tc96fN02bLq4CbCKoPajoZ46lsIuMZO9fBi3eHNObyNiopuAnECggEBAJiJ0tK/PGh+Q9uv57Z4QcmbawoxMQW1qK/rLYwacYsSpzo8VhbZf+Jh8biMg9AIQsLWnqmB3gmndePArGXkSxnilRozNLaeclTZy7rh00BctTEfgee4KxdiJKkJlVK0CE8I6txVRqkoOMyxsk1kRZ4l2yW2nxzyWlJKwvD75x2PQ6xWvpLAggynq00I3MzNIgR123jytN1NyC7l+mnGoC23ToXM7B3/PQjGYTq3jawKomrX1cmwzKBT+pzjtJSWvMeUEZQS1PpOhxpPBRHagdKXt+ug2DqDtU6rfpDGtTBh5QNkg5SA7lxZzZjrL52saevO25cigVl+hxcnY8DTpbk=" //nolint:lll
|
|
||||||
testCases := map[string]struct {
|
|
||||||
b []byte
|
|
||||||
key string
|
|
||||||
err error
|
|
||||||
}{
|
|
||||||
"no input": {
|
|
||||||
err: fmt.Errorf("cannot decode PEM block from client key"),
|
|
||||||
},
|
|
||||||
"bad input": {
|
|
||||||
b: []byte{1, 2, 3},
|
|
||||||
err: fmt.Errorf("cannot decode PEM block from client key"),
|
|
||||||
},
|
|
||||||
"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 = "MIIGrDCCBJSgAwIBAgIEAdTnfTANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJSTzESMBAGA1UEBxMJQnVjaGFyZXN0MRgwFgYDVQQKEw9DeWJlckdob3N0IFMuQS4xGzAZBgNVBAMTEkN5YmVyR2hvc3QgUm9vdCBDQTEhMB8GCSqGSIb3DQEJARYSaW5mb0BjeWJlcmdob3N0LnJvMB4XDTIwMDcwNDE1MjkzNloXDTMwMDcwMjE1MjkzNlowfTELMAkGA1UEBhMCUk8xEjAQBgNVBAcMCUJ1Y2hhcmVzdDEYMBYGA1UECgwPQ3liZXJHaG9zdCBTLkEuMR0wGwYDVQQDDBRjLmoua2xhdmVyQGdtYWlsLmNvbTEhMB8GCSqGSIb3DQEJARYSaW5mb0BjeWJlcmdob3N0LnJvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAobp2NlGUHMNBe08YEOnVG3QJjF3ZaXbRhE/II9rmtgJTNZtDohGChvFlNRsExKzVrKxHCeuJkVffwzQ6fYk4/M1RdYLJUh0UVw3e4WdApw8E7TJZxDYm4SHQNXUvt1Rt5TjslcXxIpDZgrMSc/kHROYEL9tdgdzPZErUJehXyJPhEzIrzmAJh501x7WwKPz9ctSVlItyavqEWFF2vyUa6X9DYmD9mQTz5c+VXNO5DkXmPFBIaEVDnvFtcjGJ56yEvFnWVukL+OUX7ezowrIOFOcp9udjgpeiHq+XvsQ6ER0DJt25MiEId3NjkxtZ8BitDftTcLN/kt81hWKT7adMVc3kpIZ80cxrwRCttMd7sHAzKI9u7pMxv10eUOsIEY87ewBe3l6KvEnjA+9uIjim6gLLebDIaEH50Ee9PzNJ8fqQ2u54Ab4bt00/H1sUnJ6Ss/+WsQDOK1BsPRKKcnHZntOlHrs2Tu5+txKNU2cOapI8SjVULUNKrRXASbpfWnLUfri/HO742bJb/TjkOJcOxta3hTPFAhaRWBusVlB41XVHeuH5DAhugYXeSNK6/6Ul8YvKUNH/7QbxuGIGXfth19Xl4QLI1umyEjZopSlt3tOiO2V1soVNSQCCfxXVoCTMESMLjhkjWdmBDhdy2GTW7S4YoJfqVKiS18rYkN7I4ZMCAwEAAaOCATQwggEwMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMDQGCWCGSAGG+EIBDQQnFiVDeWJlckdob3N0IEdlbmVyYXRlZCBVc2VyIENlcnRpZmljYXRlMBEGCWCGSAGG+EIBAQQEAwIHgDAdBgNVHQ4EFgQULwUtU5s6pL2NN9gPeEnKX0dhwiswga0GA1UdIwSBpTCBooAU6tdK1g/He5qzjeAoM5eHt4in9iWhf6R9MHsxCzAJBgNVBAYTAlJPMRIwEAYDVQQHEwlCdWNoYXJlc3QxGDAWBgNVBAoTD0N5YmVyR2hvc3QgUy5BLjEbMBkGA1UEAxMSQ3liZXJHaG9zdCBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJpbmZvQGN5YmVyZ2hvc3Qucm+CCQCcVButZsQ0uzANBgkqhkiG9w0BAQsFAAOCAgEAystGIMYhQWaEdTqlnLCytrr8657t+PuidZMNNIaPB3wN2Fi2xKf14DTg03mqxjmPPb+f+PVNIOV5PdWD4jcQwOP1GEboGV0DFzlRGeAtDcvKwdee4oASJbZq1CETqDaohQTxKEWC+UBk2F36nOaEI6Sab+Mb4cR9//PAwvzOqrXuGF5NuIOX7eFtCMQSgQq6lRRqTQjekm0Dxigx4JA92Jo2qZRwCJ0T3IXBJGL831HCFJbDWv8PV3lsfFb/i2+vr54uywFQVWWp18dYi97gipfuQ4zRg2Ldx5aXSmnhhKpg5ioZvtk043QofF12YORhobElqavRbvvhZvlCouvcuoq9QKi7IPe5SJZkZ1X7ezMesCwBzwFpt6vRUAcslsNFbcYS1iSENlY/PTcDqBhbKuc9yAhq+/aUgaY/8VF5RWVzSRZufbf3BPwOkE4K0UybaobO/YX0JOkCacAD+4tdR6YSXNIMMRAOCBQvxbxFXaHzhwhzBAjdsC56FrJKwXvQrRLU3tF4P0zFMeNTay8uTtUXugDK7EnklLESuYdpUJ8bUMlAUhJBi6UFI9/icMudxXvLRvhnBW9EtKib5JnVFUovcEUt+3EJbyst05nkL4YPjQS4TC9DHdo5SyRAy1TpiOCYTbretAFZRhh6ycUN5hBeN8GMQxiMreMtDV4PEIQ=" //nolint:lll
|
|
||||||
testCases := map[string]struct {
|
|
||||||
b []byte
|
|
||||||
certificate string
|
|
||||||
err error
|
|
||||||
}{
|
|
||||||
"no input": {
|
|
||||||
err: fmt.Errorf("cannot decode PEM block from client certificate"),
|
|
||||||
},
|
|
||||||
"bad input": {
|
|
||||||
b: []byte{1, 2, 3},
|
|
||||||
err: fmt.Errorf("cannot decode PEM block from client certificate"),
|
|
||||||
},
|
|
||||||
"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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
unboundmodels "github.com/qdm12/dns/pkg/models"
|
"github.com/qdm12/dns/pkg/blacklist"
|
||||||
unbound "github.com/qdm12/dns/pkg/unbound"
|
"github.com/qdm12/dns/pkg/unbound"
|
||||||
"github.com/qdm12/golibs/params"
|
"github.com/qdm12/golibs/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,11 +17,9 @@ type DNS struct { //nolint:maligned
|
|||||||
Enabled bool
|
Enabled bool
|
||||||
PlaintextAddress net.IP
|
PlaintextAddress net.IP
|
||||||
KeepNameserver bool
|
KeepNameserver bool
|
||||||
BlockMalicious bool
|
|
||||||
BlockAds bool
|
|
||||||
BlockSurveillance bool
|
|
||||||
UpdatePeriod time.Duration
|
UpdatePeriod time.Duration
|
||||||
Unbound unboundmodels.Settings
|
Unbound unbound.Settings
|
||||||
|
BlacklistBuild blacklist.BuilderSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *DNS) String() string {
|
func (settings *DNS) String() string {
|
||||||
@@ -50,16 +48,9 @@ func (settings *DNS) lines() (lines []string) {
|
|||||||
lines = append(lines, indent+indent+indent+line)
|
lines = append(lines, indent+indent+indent+line)
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.BlockMalicious {
|
lines = append(lines, indent+indent+lastIndent+"Blacklist:")
|
||||||
lines = append(lines, indent+indent+lastIndent+"Block malicious: enabled")
|
for _, line := range settings.BlacklistBuild.Lines(indent, lastIndent) {
|
||||||
}
|
lines = append(lines, indent+indent+indent+line)
|
||||||
|
|
||||||
if settings.BlockAds {
|
|
||||||
lines = append(lines, indent+indent+lastIndent+"Block ads: enabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
if settings.BlockSurveillance {
|
|
||||||
lines = append(lines, indent+indent+lastIndent+"Block surveillance: enabled")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.UpdatePeriod > 0 {
|
if settings.UpdatePeriod > 0 {
|
||||||
@@ -71,15 +62,13 @@ func (settings *DNS) lines() (lines []string) {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
ErrUnboundSettings = errors.New("failed getting Unbound settings")
|
ErrUnboundSettings = errors.New("failed getting Unbound settings")
|
||||||
ErrDNSProviderNoData = errors.New("DNS provider has no associated data")
|
ErrBlacklistSettings = errors.New("failed getting DNS blacklist settings")
|
||||||
ErrDNSProviderNoTLS = errors.New("DNS provider does not support DNS over TLS")
|
|
||||||
ErrDNSNoIPv6Support = errors.New("no DNS provider supports IPv6")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (settings *DNS) read(r reader) (err error) {
|
func (settings *DNS) read(r reader) (err error) {
|
||||||
settings.Enabled, err = r.env.OnOff("DOT", params.Default("on"))
|
settings.Enabled, err = r.env.OnOff("DOT", params.Default("on"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable DOT: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plain DNS settings
|
// Plain DNS settings
|
||||||
@@ -88,50 +77,24 @@ func (settings *DNS) read(r reader) (err error) {
|
|||||||
}
|
}
|
||||||
settings.KeepNameserver, err = r.env.OnOff("DNS_KEEP_NAMESERVER", params.Default("off"))
|
settings.KeepNameserver, err = r.env.OnOff("DNS_KEEP_NAMESERVER", params.Default("off"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable DNS_KEEP_NAMESERVER: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNS over TLS external settings
|
// DNS over TLS external settings
|
||||||
settings.BlockMalicious, err = r.env.OnOff("BLOCK_MALICIOUS", params.Default("on"))
|
if err := settings.readBlacklistBuilding(r); err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("%w: %s", ErrBlacklistSettings, err)
|
||||||
return err
|
|
||||||
}
|
|
||||||
settings.BlockSurveillance, err = r.env.OnOff("BLOCK_SURVEILLANCE", params.Default("on"),
|
|
||||||
params.RetroKeys([]string{"BLOCK_NSA"}, r.onRetroActive))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
settings.BlockAds, err = r.env.OnOff("BLOCK_ADS", params.Default("off"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.UpdatePeriod, err = r.env.Duration("DNS_UPDATE_PERIOD", params.Default("24h"))
|
settings.UpdatePeriod, err = r.env.Duration("DNS_UPDATE_PERIOD", params.Default("24h"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable DNS_UPDATE_PERIOD: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unbound settings
|
||||||
if err := settings.readUnbound(r); err != nil {
|
if err := settings.readUnbound(r); err != nil {
|
||||||
return fmt.Errorf("%w: %s", ErrUnboundSettings, err)
|
return fmt.Errorf("%w: %s", ErrUnboundSettings, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consistency check
|
|
||||||
IPv6Support := false
|
|
||||||
for _, provider := range settings.Unbound.Providers {
|
|
||||||
providerData, ok := unbound.GetProviderData(provider)
|
|
||||||
switch {
|
|
||||||
case !ok:
|
|
||||||
return fmt.Errorf("%w: %s", ErrDNSProviderNoData, provider)
|
|
||||||
case !providerData.SupportsTLS:
|
|
||||||
return fmt.Errorf("%w: %s", ErrDNSProviderNoTLS, provider)
|
|
||||||
case providerData.SupportsIPv6:
|
|
||||||
IPv6Support = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if settings.Unbound.IPv6 && !IPv6Support {
|
|
||||||
return ErrDNSNoIPv6Support
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,10 +102,10 @@ var (
|
|||||||
ErrDNSAddressNotAnIP = errors.New("DNS plaintext address is not an IP address")
|
ErrDNSAddressNotAnIP = errors.New("DNS plaintext address is not an IP address")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (settings *DNS) readDNSPlaintext(env params.Env) error {
|
func (settings *DNS) readDNSPlaintext(env params.Interface) error {
|
||||||
s, err := env.Get("DNS_PLAINTEXT_ADDRESS", params.Default("1.1.1.1"))
|
s, err := env.Get("DNS_PLAINTEXT_ADDRESS", params.Default("1.1.1.1"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable DNS_PLAINTEXT_ADDRESS: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.PlaintextAddress = net.ParseIP(s)
|
settings.PlaintextAddress = net.ParseIP(s)
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/dns/pkg/models"
|
"github.com/qdm12/dns/pkg/blacklist"
|
||||||
|
"github.com/qdm12/dns/pkg/provider"
|
||||||
|
"github.com/qdm12/dns/pkg/unbound"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,12 +30,16 @@ func Test_DNS_Lines(t *testing.T) {
|
|||||||
settings: DNS{
|
settings: DNS{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
KeepNameserver: true,
|
KeepNameserver: true,
|
||||||
Unbound: models.Settings{
|
Unbound: unbound.Settings{
|
||||||
Providers: []string{"cloudflare"},
|
Providers: []provider.Provider{
|
||||||
|
provider.Cloudflare(),
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
BlacklistBuild: blacklist.BuilderSettings{
|
||||||
BlockMalicious: true,
|
BlockMalicious: true,
|
||||||
BlockAds: true,
|
BlockAds: true,
|
||||||
BlockSurveillance: true,
|
BlockSurveillance: true,
|
||||||
|
},
|
||||||
UpdatePeriod: time.Hour,
|
UpdatePeriod: time.Hour,
|
||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
@@ -42,7 +48,7 @@ func Test_DNS_Lines(t *testing.T) {
|
|||||||
" |--DNS over TLS:",
|
" |--DNS over TLS:",
|
||||||
" |--Unbound:",
|
" |--Unbound:",
|
||||||
" |--DNS over TLS providers:",
|
" |--DNS over TLS providers:",
|
||||||
" |--cloudflare",
|
" |--Cloudflare",
|
||||||
" |--Listening port: 0",
|
" |--Listening port: 0",
|
||||||
" |--Access control:",
|
" |--Access control:",
|
||||||
" |--Allowed:",
|
" |--Allowed:",
|
||||||
@@ -52,12 +58,9 @@ func Test_DNS_Lines(t *testing.T) {
|
|||||||
" |--Verbosity level: 0/5",
|
" |--Verbosity level: 0/5",
|
||||||
" |--Verbosity details level: 0/4",
|
" |--Verbosity details level: 0/4",
|
||||||
" |--Validation log level: 0/2",
|
" |--Validation log level: 0/2",
|
||||||
" |--Blocked hostnames:",
|
" |--Username: ",
|
||||||
" |--Blocked IP addresses:",
|
" |--Blacklist:",
|
||||||
" |--Allowed hostnames:",
|
" |--Blocked categories: malicious, surveillance, ads",
|
||||||
" |--Block malicious: enabled",
|
|
||||||
" |--Block ads: enabled",
|
|
||||||
" |--Block surveillance: enabled",
|
|
||||||
" |--Update: every 1h0m0s",
|
" |--Update: every 1h0m0s",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
87
internal/configuration/dnsblacklist.go
Normal file
87
internal/configuration/dnsblacklist.go
Normal 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.Interface) (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
|
||||||
|
}
|
||||||
40
internal/configuration/expressvpn.go
Normal file
40
internal/configuration/expressvpn.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) readExpressvpn(r reader) (err error) {
|
||||||
|
settings.Name = constants.Expressvpn
|
||||||
|
servers := r.servers.GetExpressvpn()
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
|
||||||
|
constants.ExpressvpnHostnameChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.ExpressvpnCountriesChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.ExpressvpnCityChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.OpenVPN.TCP, err = readOpenVPNProtocol(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,43 +1,30 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"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) {
|
func (settings *Provider) readFastestvpn(r reader) (err error) {
|
||||||
settings.Name = constants.Fastestvpn
|
settings.Name = constants.Fastestvpn
|
||||||
|
servers := r.servers.GetFastestvpn()
|
||||||
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.FastestvpnHostnameChoices())
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
|
||||||
|
constants.FastestvpnHostnameChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.FastestvpnCountriesChoices())
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.FastestvpnCountriesChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return settings.ServerSelection.OpenVPN.readProtocolOnly(r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -53,12 +54,12 @@ func (settings *Firewall) lines() (lines []string) {
|
|||||||
func (settings *Firewall) read(r reader) (err error) {
|
func (settings *Firewall) read(r reader) (err error) {
|
||||||
settings.Enabled, err = r.env.OnOff("FIREWALL", params.Default("on"))
|
settings.Enabled, err = r.env.OnOff("FIREWALL", params.Default("on"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable FIREWALL: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Debug, err = r.env.OnOff("FIREWALL_DEBUG", params.Default("off"))
|
settings.Debug, err = r.env.OnOff("FIREWALL_DEBUG", params.Default("off"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable FIREWALL_DEBUG: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := settings.readVPNInputPorts(r.env); err != nil {
|
if err := settings.readVPNInputPorts(r.env); err != nil {
|
||||||
@@ -69,25 +70,30 @@ func (settings *Firewall) read(r reader) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := settings.readOutboundSubnets(r); err != nil {
|
return settings.readOutboundSubnets(r)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (settings *Firewall) readVPNInputPorts(env params.Interface) (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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *Firewall) readVPNInputPorts(env params.Env) (err error) {
|
func (settings *Firewall) readInputPorts(env params.Interface) (err error) {
|
||||||
settings.VPNInputPorts, err = readCSVPorts(env, "FIREWALL_VPN_INPUT_PORTS")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (settings *Firewall) readInputPorts(env params.Env) (err error) {
|
|
||||||
settings.InputPorts, err = readCSVPorts(env, "FIREWALL_INPUT_PORTS")
|
settings.InputPorts, err = readCSVPorts(env, "FIREWALL_INPUT_PORTS")
|
||||||
return err
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable FIREWALL_INPUT_PORTS: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *Firewall) readOutboundSubnets(r reader) (err error) {
|
func (settings *Firewall) readOutboundSubnets(r reader) (err error) {
|
||||||
retroOption := params.RetroKeys([]string{"EXTRA_SUBNETS"}, r.onRetroActive)
|
retroOption := params.RetroKeys([]string{"EXTRA_SUBNETS"}, r.onRetroActive)
|
||||||
settings.OutboundSubnets, err = readCSVIPNets(r.env, "FIREWALL_OUTBOUND_SUBNETS", retroOption)
|
settings.OutboundSubnets, err = readCSVIPNets(r.env, "FIREWALL_OUTBOUND_SUBNETS", retroOption)
|
||||||
return err
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable FIREWALL_OUTBOUND_SUBNETS: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
72
internal/configuration/health.go
Normal file
72
internal/configuration/health.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Health contains settings for the healthcheck and health server.
|
||||||
|
type Health struct {
|
||||||
|
ServerAddress string
|
||||||
|
AddressToPing string
|
||||||
|
VPN 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+"Address to ping: "+settings.AddressToPing)
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"VPN:")
|
||||||
|
for _, line := range settings.VPN.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.Interface, warner Warner) (err error) {
|
||||||
|
reader := newReader(env, models.AllServers{}, warner) // note: no need for servers data
|
||||||
|
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.warner.Warn("environment variable HEALTH_SERVER_ADDRESS: " + warning)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable HEALTH_SERVER_ADDRESS: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.AddressToPing, err = r.env.Get("HEALTH_ADDRESS_TO_PING", params.Default("github.com"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable HEALTH_ADDRESS_TO_PING: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
retroKeyOption := params.RetroKeys([]string{"HEALTH_OPENVPN_DURATION_INITIAL"}, r.onRetroActive)
|
||||||
|
settings.VPN.Initial, err = r.env.Duration("HEALTH_VPN_DURATION_INITIAL", params.Default("6s"), retroKeyOption)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable HEALTH_VPN_DURATION_INITIAL: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
retroKeyOption = params.RetroKeys([]string{"HEALTH_OPENVPN_DURATION_ADDITION"}, r.onRetroActive)
|
||||||
|
settings.VPN.Addition, err = r.env.Duration("HEALTH_VPN_DURATION_ADDITION", params.Default("5s"), retroKeyOption)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable HEALTH_VPN_DURATION_ADDITION: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
272
internal/configuration/health_test.go
Normal file
272
internal/configuration/health_test.go
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"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()
|
||||||
|
|
||||||
|
health := Health{
|
||||||
|
ServerAddress: "a",
|
||||||
|
AddressToPing: "b",
|
||||||
|
}
|
||||||
|
const expected = `|--Health:
|
||||||
|
|--Server address: a
|
||||||
|
|--Address to ping: b
|
||||||
|
|--VPN:
|
||||||
|
|--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: ",
|
||||||
|
" |--Address to ping: ",
|
||||||
|
" |--VPN:",
|
||||||
|
" |--Initial duration: 0s",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"filled settings": {
|
||||||
|
settings: Health{
|
||||||
|
ServerAddress: "address:9999",
|
||||||
|
AddressToPing: "github.com",
|
||||||
|
VPN: HealthyWait{
|
||||||
|
Initial: time.Second,
|
||||||
|
Addition: time.Minute,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Health:",
|
||||||
|
" |--Server address: address:9999",
|
||||||
|
" |--Address to ping: github.com",
|
||||||
|
" |--VPN:",
|
||||||
|
" |--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")
|
||||||
|
|
||||||
|
type stringCall struct {
|
||||||
|
call bool
|
||||||
|
s string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringCallWithWarning struct {
|
||||||
|
call bool
|
||||||
|
s string
|
||||||
|
warning string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type durationCall struct {
|
||||||
|
call bool
|
||||||
|
duration time.Duration
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
serverAddress stringCallWithWarning
|
||||||
|
addressToPing stringCall
|
||||||
|
vpnInitial durationCall
|
||||||
|
vpnAddition durationCall
|
||||||
|
expected Health
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
"success": {
|
||||||
|
serverAddress: stringCallWithWarning{
|
||||||
|
call: true,
|
||||||
|
s: "127.0.0.1:9999",
|
||||||
|
},
|
||||||
|
addressToPing: stringCall{
|
||||||
|
call: true,
|
||||||
|
s: "1.2.3.4",
|
||||||
|
},
|
||||||
|
vpnInitial: durationCall{
|
||||||
|
call: true,
|
||||||
|
duration: time.Second,
|
||||||
|
},
|
||||||
|
vpnAddition: durationCall{
|
||||||
|
call: true,
|
||||||
|
duration: time.Minute,
|
||||||
|
},
|
||||||
|
expected: Health{
|
||||||
|
ServerAddress: "127.0.0.1:9999",
|
||||||
|
AddressToPing: "1.2.3.4",
|
||||||
|
VPN: HealthyWait{
|
||||||
|
Initial: time.Second,
|
||||||
|
Addition: time.Minute,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"listening address error": {
|
||||||
|
serverAddress: stringCallWithWarning{
|
||||||
|
call: true,
|
||||||
|
s: "127.0.0.1:9999",
|
||||||
|
warning: "warning",
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
expected: Health{
|
||||||
|
ServerAddress: "127.0.0.1:9999",
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable HEALTH_SERVER_ADDRESS: dummy"),
|
||||||
|
},
|
||||||
|
"address to ping error": {
|
||||||
|
serverAddress: stringCallWithWarning{
|
||||||
|
call: true,
|
||||||
|
},
|
||||||
|
addressToPing: stringCall{
|
||||||
|
call: true,
|
||||||
|
s: "address",
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
expected: Health{
|
||||||
|
AddressToPing: "address",
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable HEALTH_ADDRESS_TO_PING: dummy"),
|
||||||
|
},
|
||||||
|
"initial error": {
|
||||||
|
serverAddress: stringCallWithWarning{
|
||||||
|
call: true,
|
||||||
|
},
|
||||||
|
addressToPing: stringCall{
|
||||||
|
call: true,
|
||||||
|
},
|
||||||
|
vpnInitial: durationCall{
|
||||||
|
call: true,
|
||||||
|
duration: time.Second,
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
expected: Health{
|
||||||
|
VPN: HealthyWait{
|
||||||
|
Initial: time.Second,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable HEALTH_VPN_DURATION_INITIAL: dummy"),
|
||||||
|
},
|
||||||
|
"addition error": {
|
||||||
|
serverAddress: stringCallWithWarning{
|
||||||
|
call: true,
|
||||||
|
},
|
||||||
|
addressToPing: stringCall{
|
||||||
|
call: true,
|
||||||
|
},
|
||||||
|
vpnInitial: durationCall{
|
||||||
|
call: true,
|
||||||
|
duration: time.Second,
|
||||||
|
},
|
||||||
|
vpnAddition: durationCall{
|
||||||
|
call: true,
|
||||||
|
duration: time.Minute,
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
expected: Health{
|
||||||
|
VPN: HealthyWait{
|
||||||
|
Initial: time.Second,
|
||||||
|
Addition: time.Minute,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable HEALTH_VPN_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.NewMockInterface(ctrl)
|
||||||
|
warner := NewMockWarner(ctrl)
|
||||||
|
|
||||||
|
if testCase.serverAddress.call {
|
||||||
|
value := testCase.serverAddress.s
|
||||||
|
warning := testCase.serverAddress.warning
|
||||||
|
err := testCase.serverAddress.err
|
||||||
|
env.EXPECT().ListeningAddress("HEALTH_SERVER_ADDRESS", gomock.Any()).
|
||||||
|
Return(value, warning, err)
|
||||||
|
if warning != "" {
|
||||||
|
warner.EXPECT().Warn("environment variable HEALTH_SERVER_ADDRESS: " + warning)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if testCase.addressToPing.call {
|
||||||
|
value := testCase.addressToPing.s
|
||||||
|
err := testCase.addressToPing.err
|
||||||
|
env.EXPECT().Get("HEALTH_ADDRESS_TO_PING", gomock.Any()).
|
||||||
|
Return(value, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if testCase.vpnInitial.call {
|
||||||
|
value := testCase.vpnInitial.duration
|
||||||
|
err := testCase.vpnInitial.err
|
||||||
|
env.EXPECT().
|
||||||
|
Duration("HEALTH_VPN_DURATION_INITIAL", gomock.Any()).
|
||||||
|
Return(value, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if testCase.vpnAddition.call {
|
||||||
|
value := testCase.vpnAddition.duration
|
||||||
|
err := testCase.vpnAddition.err
|
||||||
|
env.EXPECT().
|
||||||
|
Duration("HEALTH_VPN_DURATION_ADDITION", gomock.Any()).
|
||||||
|
Return(value, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := reader{
|
||||||
|
env: env,
|
||||||
|
warner: warner,
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
55
internal/configuration/healthwait_test.go
Normal file
55
internal/configuration/healthwait_test.go
Normal 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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
30
internal/configuration/healthywait.go
Normal file
30
internal/configuration/healthywait.go
Normal 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
|
||||||
|
}
|
||||||
@@ -1,61 +1,40 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"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) {
|
func (settings *Provider) readHideMyAss(r reader) (err error) {
|
||||||
settings.Name = constants.HideMyAss
|
settings.Name = constants.HideMyAss
|
||||||
|
servers := r.servers.GetHideMyAss()
|
||||||
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.HideMyAssCountryChoices())
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.HideMyAssCountryChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.HideMyAssCountryChoices())
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.HideMyAssCountryChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable REGION: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.HideMyAssCityChoices())
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.HideMyAssCityChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.TorguardHostnamesChoices())
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
|
||||||
|
constants.HideMyAssHostnameChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -49,24 +50,24 @@ func (settings *HTTPProxy) read(r reader) (err error) {
|
|||||||
settings.Enabled, err = r.env.OnOff("HTTPPROXY", params.Default("off"),
|
settings.Enabled, err = r.env.OnOff("HTTPPROXY", params.Default("off"),
|
||||||
params.RetroKeys([]string{"TINYPROXY", "PROXY"}, r.onRetroActive))
|
params.RetroKeys([]string{"TINYPROXY", "PROXY"}, r.onRetroActive))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable HTTPPROXY (or TINYPROXY, PROXY): %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.User, err = r.getFromEnvOrSecretFile("HTTPPROXY_USER", false, // compulsory
|
settings.User, err = r.getFromEnvOrSecretFile("HTTPPROXY_USER", false, // compulsory
|
||||||
[]string{"TINYPROXY_USER", "PROXY_USER"})
|
[]string{"TINYPROXY_USER", "PROXY_USER"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable HTTPPROXY_USER (or TINYPROXY_USER, PROXY_USER): %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Password, err = r.getFromEnvOrSecretFile("HTTPPROXY_PASSWORD", false,
|
settings.Password, err = r.getFromEnvOrSecretFile("HTTPPROXY_PASSWORD", false,
|
||||||
[]string{"TINYPROXY_PASSWORD", "PROXY_PASSWORD"})
|
[]string{"TINYPROXY_PASSWORD", "PROXY_PASSWORD"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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"))
|
settings.Stealth, err = r.env.OnOff("HTTPPROXY_STEALTH", params.Default("off"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable HTTPPROXY_STEALTH: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := settings.readLog(r); err != nil {
|
if err := settings.readLog(r); err != nil {
|
||||||
@@ -77,10 +78,10 @@ func (settings *HTTPProxy) read(r reader) (err error) {
|
|||||||
settings.Port, warning, err = r.env.ListeningPort("HTTPPROXY_PORT", params.Default("8888"),
|
settings.Port, warning, err = r.env.ListeningPort("HTTPPROXY_PORT", params.Default("8888"),
|
||||||
params.RetroKeys([]string{"TINYPROXY_PORT", "PROXY_PORT"}, r.onRetroActive))
|
params.RetroKeys([]string{"TINYPROXY_PORT", "PROXY_PORT"}, r.onRetroActive))
|
||||||
if len(warning) > 0 {
|
if len(warning) > 0 {
|
||||||
r.logger.Warn(warning)
|
r.warner.Warn(warning)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable HTTPPROXY_PORT (or TINYPROXY_PORT, PROXY_PORT): %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -90,7 +91,7 @@ func (settings *HTTPProxy) readLog(r reader) error {
|
|||||||
s, err := r.env.Get("HTTPPROXY_LOG",
|
s, err := r.env.Get("HTTPPROXY_LOG",
|
||||||
params.RetroKeys([]string{"PROXY_LOG_LEVEL", "TINYPROXY_LOG"}, r.onRetroActive))
|
params.RetroKeys([]string{"PROXY_LOG_LEVEL", "TINYPROXY_LOG"}, r.onRetroActive))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable HTTPPROXY_LOG (or TINYPROXY_LOG, PROXY_LOG_LEVEL): %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch strings.ToLower(s) {
|
switch strings.ToLower(s) {
|
||||||
|
|||||||
35
internal/configuration/ipvanish.go
Normal file
35
internal/configuration/ipvanish.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) readIpvanish(r reader) (err error) {
|
||||||
|
settings.Name = constants.Ipvanish
|
||||||
|
servers := r.servers.GetIpvanish()
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IpvanishCountryChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IpvanishCityChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
|
||||||
|
constants.IpvanishHostnameChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings.ServerSelection.OpenVPN.readProtocolOnly(r)
|
||||||
|
}
|
||||||
170
internal/configuration/ipvanish_test.go
Normal file
170
internal/configuration/ipvanish_test.go
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
|
"github.com/qdm12/golibs/params/mock_params"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 {
|
||||||
|
targetIP singleStringCall
|
||||||
|
countries sliceStringCall
|
||||||
|
cities sliceStringCall
|
||||||
|
hostnames sliceStringCall
|
||||||
|
protocol singleStringCall
|
||||||
|
settings Provider
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
"target IP error": {
|
||||||
|
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": {
|
||||||
|
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": {
|
||||||
|
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": {
|
||||||
|
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"),
|
||||||
|
},
|
||||||
|
"protocol error": {
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true},
|
||||||
|
protocol: singleStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ipvanish,
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable OPENVPN_PROTOCOL: dummy test error"),
|
||||||
|
},
|
||||||
|
"default settings": {
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true},
|
||||||
|
protocol: singleStringCall{call: true},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ipvanish,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"set settings": {
|
||||||
|
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"}},
|
||||||
|
protocol: singleStringCall{call: true, value: constants.TCP},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ipvanish,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
OpenVPN: OpenVPNSelection{
|
||||||
|
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)
|
||||||
|
|
||||||
|
servers := []models.IpvanishServer{{Hostname: "a"}}
|
||||||
|
allServers := models.AllServers{
|
||||||
|
Ipvanish: models.IpvanishServers{
|
||||||
|
Servers: servers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
env := mock_params.NewMockInterface(ctrl)
|
||||||
|
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(servers)).
|
||||||
|
Return(testCase.countries.values, testCase.countries.err)
|
||||||
|
}
|
||||||
|
if testCase.cities.call {
|
||||||
|
env.EXPECT().CSVInside("CITY", constants.IpvanishCityChoices(servers)).
|
||||||
|
Return(testCase.cities.values, testCase.cities.err)
|
||||||
|
}
|
||||||
|
if testCase.hostnames.call {
|
||||||
|
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices(servers)).
|
||||||
|
Return(testCase.hostnames.values, testCase.hostnames.err)
|
||||||
|
}
|
||||||
|
if testCase.protocol.call {
|
||||||
|
env.EXPECT().Inside("OPENVPN_PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
|
||||||
|
Return(testCase.protocol.value, testCase.protocol.err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := reader{
|
||||||
|
servers: allServers,
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
73
internal/configuration/ivpn.go
Normal file
73
internal/configuration/ivpn.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) readIvpn(r reader) (err error) {
|
||||||
|
settings.Name = constants.Ivpn
|
||||||
|
servers := r.servers.GetIvpn()
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IvpnCountryChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IvpnCityChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.IvpnISPChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable ISP: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = settings.ServerSelection.OpenVPN.readIVPN(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings.ServerSelection.Wireguard.readIVPN(r.env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPNSelection) readIVPN(r reader) (err error) {
|
||||||
|
settings.TCP, err = readOpenVPNProtocol(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.CustomPort, err = readOpenVPNCustomPort(r, openvpnPortValidation{
|
||||||
|
tcp: settings.TCP,
|
||||||
|
allowedTCP: []uint16{80, 443, 1443},
|
||||||
|
allowedUDP: []uint16{53, 1194, 2049, 2050},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *WireguardSelection) readIVPN(env params.Interface) (err error) {
|
||||||
|
settings.EndpointPort, err = readWireguardCustomPort(env,
|
||||||
|
[]uint16{2049, 2050, 53, 30587, 41893, 48574, 58237})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
274
internal/configuration/ivpn_test.go
Normal file
274
internal/configuration/ivpn_test.go
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
|
"github.com/qdm12/golibs/params/mock_params"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var errDummy = errors.New("dummy test error")
|
||||||
|
|
||||||
|
type singleStringCall struct {
|
||||||
|
call bool
|
||||||
|
value string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type portCall struct {
|
||||||
|
getCall bool
|
||||||
|
getValue string // "" or "0"
|
||||||
|
getErr error
|
||||||
|
portCall bool
|
||||||
|
portValue uint16
|
||||||
|
portErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliceStringCall struct {
|
||||||
|
call bool
|
||||||
|
values []string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
targetIP singleStringCall
|
||||||
|
countries sliceStringCall
|
||||||
|
cities sliceStringCall
|
||||||
|
isps sliceStringCall
|
||||||
|
hostnames sliceStringCall
|
||||||
|
protocol singleStringCall
|
||||||
|
ovpnPort portCall
|
||||||
|
ovpnOldPort portCall
|
||||||
|
wgPort portCall
|
||||||
|
wgOldPort portCall
|
||||||
|
settings Provider
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
"target IP error": {
|
||||||
|
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": {
|
||||||
|
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": {
|
||||||
|
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"),
|
||||||
|
},
|
||||||
|
"isps error": {
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
isps: sliceStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable ISP: dummy test error"),
|
||||||
|
},
|
||||||
|
"hostnames error": {
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
isps: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable SERVER_HOSTNAME: dummy test error"),
|
||||||
|
},
|
||||||
|
"openvpn protocol error": {
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
isps: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true},
|
||||||
|
protocol: singleStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable OPENVPN_PROTOCOL: dummy test error"),
|
||||||
|
},
|
||||||
|
"openvpn custom port error": {
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
isps: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true},
|
||||||
|
protocol: singleStringCall{call: true},
|
||||||
|
ovpnPort: portCall{getCall: true, getErr: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable OPENVPN_PORT: dummy test error"),
|
||||||
|
},
|
||||||
|
"wireguard custom port error": {
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
isps: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true},
|
||||||
|
protocol: singleStringCall{call: true},
|
||||||
|
ovpnPort: portCall{getCall: true, getValue: "0"},
|
||||||
|
ovpnOldPort: portCall{getCall: true, getValue: "0"},
|
||||||
|
wgPort: portCall{getCall: true, getErr: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable WIREGUARD_ENDPOINT_PORT: dummy test error"),
|
||||||
|
},
|
||||||
|
"default settings": {
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
isps: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true},
|
||||||
|
protocol: singleStringCall{call: true},
|
||||||
|
ovpnPort: portCall{getCall: true, getValue: "0"},
|
||||||
|
ovpnOldPort: portCall{getCall: true, getValue: "0"},
|
||||||
|
wgPort: portCall{getCall: true, getValue: "0"},
|
||||||
|
wgOldPort: portCall{getCall: true, getValue: "0"},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"set settings": {
|
||||||
|
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"}},
|
||||||
|
isps: sliceStringCall{call: true, values: []string{"ISP 1"}},
|
||||||
|
hostnames: sliceStringCall{call: true, values: []string{"E", "F"}},
|
||||||
|
protocol: singleStringCall{call: true, value: constants.TCP},
|
||||||
|
ovpnPort: portCall{getCall: true, portCall: true, portValue: 443},
|
||||||
|
wgPort: portCall{getCall: true, portCall: true, portValue: 2049},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
OpenVPN: OpenVPNSelection{
|
||||||
|
TCP: true,
|
||||||
|
CustomPort: 443,
|
||||||
|
},
|
||||||
|
Wireguard: WireguardSelection{
|
||||||
|
EndpointPort: 2049,
|
||||||
|
},
|
||||||
|
TargetIP: net.IPv4(1, 2, 3, 4),
|
||||||
|
Countries: []string{"A", "B"},
|
||||||
|
Cities: []string{"C", "D"},
|
||||||
|
ISPs: []string{"ISP 1"},
|
||||||
|
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.NewMockInterface(ctrl)
|
||||||
|
|
||||||
|
servers := []models.IvpnServer{{Hostname: "a"}}
|
||||||
|
allServers := models.AllServers{
|
||||||
|
Ivpn: models.IvpnServers{
|
||||||
|
Servers: servers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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(servers)).
|
||||||
|
Return(testCase.countries.values, testCase.countries.err)
|
||||||
|
}
|
||||||
|
if testCase.cities.call {
|
||||||
|
env.EXPECT().CSVInside("CITY", constants.IvpnCityChoices(servers)).
|
||||||
|
Return(testCase.cities.values, testCase.cities.err)
|
||||||
|
}
|
||||||
|
if testCase.isps.call {
|
||||||
|
env.EXPECT().CSVInside("ISP", constants.IvpnISPChoices(servers)).
|
||||||
|
Return(testCase.isps.values, testCase.isps.err)
|
||||||
|
}
|
||||||
|
if testCase.hostnames.call {
|
||||||
|
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices(servers)).
|
||||||
|
Return(testCase.hostnames.values, testCase.hostnames.err)
|
||||||
|
}
|
||||||
|
if testCase.protocol.call {
|
||||||
|
env.EXPECT().Inside("OPENVPN_PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
|
||||||
|
Return(testCase.protocol.value, testCase.protocol.err)
|
||||||
|
}
|
||||||
|
if testCase.ovpnPort.getCall {
|
||||||
|
env.EXPECT().Get("OPENVPN_PORT", gomock.Any()).
|
||||||
|
Return(testCase.ovpnPort.getValue, testCase.ovpnPort.getErr)
|
||||||
|
}
|
||||||
|
if testCase.ovpnPort.portCall {
|
||||||
|
env.EXPECT().Port("OPENVPN_PORT").
|
||||||
|
Return(testCase.ovpnPort.portValue, testCase.ovpnPort.portErr)
|
||||||
|
}
|
||||||
|
if testCase.ovpnOldPort.getCall {
|
||||||
|
env.EXPECT().Get("PORT", gomock.Any()).
|
||||||
|
Return(testCase.ovpnOldPort.getValue, testCase.ovpnOldPort.getErr)
|
||||||
|
}
|
||||||
|
if testCase.ovpnOldPort.portCall {
|
||||||
|
env.EXPECT().Port("PORT").
|
||||||
|
Return(testCase.ovpnOldPort.portValue, testCase.ovpnOldPort.portErr)
|
||||||
|
}
|
||||||
|
if testCase.wgPort.getCall {
|
||||||
|
env.EXPECT().Get("WIREGUARD_ENDPOINT_PORT", gomock.Any()).
|
||||||
|
Return(testCase.wgPort.getValue, testCase.wgPort.getErr)
|
||||||
|
}
|
||||||
|
if testCase.wgPort.portCall {
|
||||||
|
env.EXPECT().Port("WIREGUARD_ENDPOINT_PORT").
|
||||||
|
Return(testCase.wgPort.portValue, testCase.wgPort.portErr)
|
||||||
|
}
|
||||||
|
if testCase.wgOldPort.getCall {
|
||||||
|
env.EXPECT().Get("WIREGUARD_PORT", gomock.Any()).
|
||||||
|
Return(testCase.wgOldPort.getValue, testCase.wgOldPort.getErr)
|
||||||
|
}
|
||||||
|
if testCase.wgOldPort.portCall {
|
||||||
|
env.EXPECT().Port("WIREGUARD_PORT").
|
||||||
|
Return(testCase.wgOldPort.portValue, testCase.wgOldPort.portErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := reader{
|
||||||
|
servers: allServers,
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
29
internal/configuration/keys.go
Normal file
29
internal/configuration/keys.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/gluetun/internal/openvpn/parse"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errClientCert = errors.New("cannot read client certificate")
|
||||||
|
errClientKey = errors.New("cannot read client key")
|
||||||
|
)
|
||||||
|
|
||||||
|
func readClientKey(r reader) (clientKey string, err error) {
|
||||||
|
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTKEY", constants.ClientKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return parse.ExtractPrivateKey(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readClientCertificate(r reader) (clientCertificate string, err error) {
|
||||||
|
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTCRT", constants.ClientCertificate)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return parse.ExtractCert(b)
|
||||||
|
}
|
||||||
30
internal/configuration/log.go
Normal file
30
internal/configuration/log.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/logging"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Log struct {
|
||||||
|
Level logging.Level `json:"level"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Log) lines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+"Log:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Level: "+settings.Level.String())
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Log) read(env params.Interface) (err error) {
|
||||||
|
defaultLevel := logging.LevelInfo.String()
|
||||||
|
settings.Level, err = env.LogLevel("LOG_LEVEL", params.Default(defaultLevel))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable LOG_LEVEL: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,76 +1,74 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/golibs/params"
|
"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.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) {
|
func (settings *Provider) readMullvad(r reader) (err error) {
|
||||||
settings.Name = constants.Mullvad
|
settings.Name = constants.Mullvad
|
||||||
|
servers := r.servers.GetMullvad()
|
||||||
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.MullvadCountryChoices())
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.MullvadCountryChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.MullvadCityChoices())
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.MullvadCityChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.MullvadISPChoices())
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.MullvadHostnameChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.CustomPort, err = readCustomPort(r.env, settings.ServerSelection.Protocol,
|
settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.MullvadISPChoices(servers))
|
||||||
[]uint16{80, 443, 1401}, []uint16{53, 1194, 1195, 1196, 1197, 1300, 1301, 1302, 1303, 1400})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable ISP: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Owned, err = r.env.YesNo("OWNED", params.Default("no"))
|
settings.ServerSelection.Owned, err = r.env.YesNo("OWNED", params.Default("no"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable OWNED: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = settings.ServerSelection.OpenVPN.readMullvad(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ExtraConfigOptions.OpenVPNIPv6, err = r.env.OnOff("OPENVPN_IPV6", params.Default("off"))
|
return settings.ServerSelection.Wireguard.readMullvad(r.env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPNSelection) readMullvad(r reader) (err error) {
|
||||||
|
settings.TCP, err = readOpenVPNProtocol(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.CustomPort, err = readOpenVPNCustomPort(r, openvpnPortValidation{
|
||||||
|
tcp: settings.TCP,
|
||||||
|
allowedTCP: []uint16{80, 443, 1401},
|
||||||
|
allowedUDP: []uint16{53, 1194, 1195, 1196, 1197, 1300, 1301, 1302, 1303, 1400},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *WireguardSelection) readMullvad(env params.Interface) (err error) {
|
||||||
|
settings.EndpointPort, err = readWireguardCustomPort(env, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,38 +8,23 @@ import (
|
|||||||
"github.com/qdm12/golibs/params"
|
"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 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) {
|
func (settings *Provider) readNordvpn(r reader) (err error) {
|
||||||
settings.Name = constants.Nordvpn
|
settings.Name = constants.Nordvpn
|
||||||
|
servers := r.servers.GetNordvpn()
|
||||||
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.NordvpnRegionChoices())
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.NordvpnRegionChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable REGION: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.NordvpnHostnameChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Numbers, err = readNordVPNServerNumbers(r.env)
|
settings.ServerSelection.Numbers, err = readNordVPNServerNumbers(r.env)
|
||||||
@@ -47,11 +32,12 @@ func (settings *Provider) readNordvpn(r reader) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return settings.ServerSelection.OpenVPN.readProtocolOnly(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readNordVPNServerNumbers(env params.Env) (numbers []uint16, err error) {
|
func readNordVPNServerNumbers(env params.Interface) (numbers []uint16, err error) {
|
||||||
possibilities := make([]string, 65537)
|
const possiblePortsCount = 65537
|
||||||
|
possibilities := make([]string, possiblePortsCount)
|
||||||
for i := range possibilities {
|
for i := range possibilities {
|
||||||
possibilities[i] = fmt.Sprintf("%d", i)
|
possibilities[i] = fmt.Sprintf("%d", i)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package configuration
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -15,12 +16,19 @@ type OpenVPN struct {
|
|||||||
User string `json:"user"`
|
User string `json:"user"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Verbosity int `json:"verbosity"`
|
Verbosity int `json:"verbosity"`
|
||||||
|
Flags []string `json:"flags"`
|
||||||
MSSFix uint16 `json:"mssfix"`
|
MSSFix uint16 `json:"mssfix"`
|
||||||
Root bool `json:"run_as_root"`
|
Root bool `json:"run_as_root"`
|
||||||
Cipher string `json:"cipher"`
|
Ciphers []string `json:"ciphers"`
|
||||||
Auth string `json:"auth"`
|
Auth string `json:"auth"`
|
||||||
Provider Provider `json:"provider"`
|
ConfFile string `json:"conf_file"`
|
||||||
Config string `json:"custom_config"`
|
Version string `json:"version"`
|
||||||
|
ClientCrt string `json:"-"` // Cyberghost
|
||||||
|
ClientKey string `json:"-"` // Cyberghost, VPNUnlimited
|
||||||
|
EncPreset string `json:"encryption_preset"` // PIA
|
||||||
|
IPv6 bool `json:"ipv6"` // Mullvad
|
||||||
|
ProcUser string `json:"procuser"` // Process username
|
||||||
|
Interface string `json:"interface"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *OpenVPN) String() string {
|
func (settings *OpenVPN) String() string {
|
||||||
@@ -30,65 +38,67 @@ func (settings *OpenVPN) String() string {
|
|||||||
func (settings *OpenVPN) lines() (lines []string) {
|
func (settings *OpenVPN) lines() (lines []string) {
|
||||||
lines = append(lines, lastIndent+"OpenVPN:")
|
lines = append(lines, lastIndent+"OpenVPN:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Version: "+settings.Version)
|
||||||
|
|
||||||
lines = append(lines, indent+lastIndent+"Verbosity level: "+strconv.Itoa(settings.Verbosity))
|
lines = append(lines, indent+lastIndent+"Verbosity level: "+strconv.Itoa(settings.Verbosity))
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Network interface: "+settings.Interface)
|
||||||
|
|
||||||
|
if len(settings.Flags) > 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Flags: "+strings.Join(settings.Flags, " "))
|
||||||
|
}
|
||||||
|
|
||||||
if settings.Root {
|
if settings.Root {
|
||||||
lines = append(lines, indent+lastIndent+"Run as root: enabled")
|
lines = append(lines, indent+lastIndent+"Run as root: enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(settings.Cipher) > 0 {
|
if len(settings.Ciphers) > 0 {
|
||||||
lines = append(lines, indent+lastIndent+"Custom cipher: "+settings.Cipher)
|
lines = append(lines, indent+lastIndent+"Custom ciphers: "+commaJoin(settings.Ciphers))
|
||||||
}
|
}
|
||||||
if len(settings.Auth) > 0 {
|
if len(settings.Auth) > 0 {
|
||||||
lines = append(lines, indent+lastIndent+"Custom auth algorithm: "+settings.Auth)
|
lines = append(lines, indent+lastIndent+"Custom auth algorithm: "+settings.Auth)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(settings.Config) > 0 {
|
if settings.ConfFile != "" {
|
||||||
lines = append(lines, indent+lastIndent+"Custom configuration: "+settings.Config)
|
lines = append(lines, indent+lastIndent+"Configuration file: "+settings.ConfFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
lines = append(lines, indent+lastIndent+"Provider:")
|
if settings.ClientKey != "" {
|
||||||
for _, line := range settings.Provider.lines() {
|
lines = append(lines, indent+lastIndent+"Client key is set")
|
||||||
lines = append(lines, indent+indent+line)
|
}
|
||||||
|
|
||||||
|
if settings.ClientCrt != "" {
|
||||||
|
lines = append(lines, indent+lastIndent+"Client certificate is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.IPv6 {
|
||||||
|
lines = append(lines, indent+lastIndent+"IPv6: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.EncPreset != "" { // PIA only
|
||||||
|
lines = append(lines, indent+lastIndent+"Encryption preset: "+settings.EncPreset)
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines
|
return lines
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
func (settings *OpenVPN) read(r reader, serviceProvider string) (err error) {
|
||||||
ErrInvalidVPNProvider = errors.New("invalid VPN provider")
|
credentialsRequired := false
|
||||||
)
|
switch serviceProvider {
|
||||||
|
case constants.Custom:
|
||||||
func (settings *OpenVPN) read(r reader) (err error) {
|
case constants.VPNUnlimited:
|
||||||
vpnsp, err := r.env.Inside("VPNSP", []string{
|
default:
|
||||||
"cyberghost", "fastestvpn", "hidemyass", "mullvad", "nordvpn",
|
credentialsRequired = true
|
||||||
"privado", "pia", "private internet access", "privatevpn", "protonvpn",
|
|
||||||
"purevpn", "surfshark", "torguard", "vyprvpn", "windscribe"},
|
|
||||||
params.Default("private internet access"))
|
|
||||||
if err != nil {
|
|
||||||
return 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 err
|
|
||||||
}
|
|
||||||
|
|
||||||
credentialsRequired := len(settings.Config) == 0
|
|
||||||
|
|
||||||
settings.User, err = r.getFromEnvOrSecretFile("OPENVPN_USER", credentialsRequired, []string{"USER"})
|
settings.User, err = r.getFromEnvOrSecretFile("OPENVPN_USER", credentialsRequired, []string{"USER"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable OPENVPN_USER: %w", err)
|
||||||
}
|
}
|
||||||
// Remove spaces in user ID to simplify user's life, thanks @JeordyR
|
// Remove spaces in user ID to simplify user's life, thanks @JeordyR
|
||||||
settings.User = strings.ReplaceAll(settings.User, " ", "")
|
settings.User = strings.ReplaceAll(settings.User, " ", "")
|
||||||
|
|
||||||
if settings.Provider.Name == constants.Mullvad {
|
if serviceProvider == constants.Mullvad {
|
||||||
settings.Password = "m"
|
settings.Password = "m"
|
||||||
} else {
|
} else {
|
||||||
settings.Password, err = r.getFromEnvOrSecretFile("OPENVPN_PASSWORD", credentialsRequired, []string{"PASSWORD"})
|
settings.Password, err = r.getFromEnvOrSecretFile("OPENVPN_PASSWORD", credentialsRequired, []string{"PASSWORD"})
|
||||||
@@ -97,65 +107,101 @@ func (settings *OpenVPN) read(r reader) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Verbosity, err = r.env.IntRange("OPENVPN_VERBOSITY", 0, 6, params.Default("1"))
|
settings.Version, err = r.env.Inside("OPENVPN_VERSION",
|
||||||
|
[]string{constants.Openvpn24, constants.Openvpn25}, params.Default(constants.Openvpn25))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable OPENVPN_VERSION: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Root, err = r.env.YesNo("OPENVPN_ROOT", params.Default("yes"))
|
settings.Verbosity, err = r.env.IntRange("OPENVPN_VERBOSITY", 0, 6, params.Default("1")) //nolint:gomnd
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable OPENVPN_VERBOSITY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Cipher, err = r.env.Get("OPENVPN_CIPHER")
|
settings.Flags = []string{}
|
||||||
|
flagsStr, err := r.env.Get("OPENVPN_FLAGS")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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("no"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable OPENVPN_ROOT: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Ciphers, err = r.env.CSV("OPENVPN_CIPHER")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable OPENVPN_CIPHER: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Auth, err = r.env.Get("OPENVPN_AUTH")
|
settings.Auth, err = r.env.Get("OPENVPN_AUTH")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable OPENVPN_AUTH: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mssFix, err := r.env.IntRange("OPENVPN_MSSFIX", 0, 10000, params.Default("0"))
|
const maxMSSFix = 10000
|
||||||
|
mssFix, err := r.env.IntRange("OPENVPN_MSSFIX", 0, maxMSSFix, params.Default("0"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable OPENVPN_MSSFIX: %w", err)
|
||||||
}
|
}
|
||||||
settings.MSSFix = uint16(mssFix)
|
settings.MSSFix = uint16(mssFix)
|
||||||
|
|
||||||
var readProvider func(r reader) error
|
settings.IPv6, err = r.env.OnOff("OPENVPN_IPV6", params.Default("off"))
|
||||||
switch settings.Provider.Name {
|
if err != nil {
|
||||||
case constants.Cyberghost:
|
return fmt.Errorf("environment variable OPENVPN_IPV6: %w", err)
|
||||||
readProvider = settings.Provider.readCyberghost
|
|
||||||
case constants.Fastestvpn:
|
|
||||||
readProvider = settings.Provider.readFastestvpn
|
|
||||||
case constants.HideMyAss:
|
|
||||||
readProvider = settings.Provider.readHideMyAss
|
|
||||||
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.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)
|
settings.Interface, err = readInterface(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch serviceProvider {
|
||||||
|
case constants.Custom:
|
||||||
|
err = settings.readCustom(r) // read OPENVPN_CUSTOM_CONFIG
|
||||||
|
case constants.Cyberghost:
|
||||||
|
err = settings.readCyberghost(r)
|
||||||
|
case constants.PrivateInternetAccess:
|
||||||
|
settings.EncPreset, err = getPIAEncryptionPreset(r)
|
||||||
|
case constants.VPNUnlimited:
|
||||||
|
err = settings.readVPNUnlimited(r)
|
||||||
|
case constants.Wevpn:
|
||||||
|
err = settings.readWevpn(r)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readOpenVPNProtocol(r reader) (tcp bool, err error) {
|
||||||
|
protocol, err := r.env.Inside("OPENVPN_PROTOCOL", []string{constants.TCP, constants.UDP},
|
||||||
|
params.Default(constants.UDP), params.RetroKeys([]string{"PROTOCOL"}, r.onRetroActive))
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("environment variable OPENVPN_PROTOCOL: %w", err)
|
||||||
|
}
|
||||||
|
return protocol == constants.TCP, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const openvpnIntfRegexString = `^.*[0-9]$`
|
||||||
|
|
||||||
|
var openvpnIntfRegexp = regexp.MustCompile(openvpnIntfRegexString)
|
||||||
|
var errInterfaceNameNotValid = errors.New("interface name is not valid")
|
||||||
|
|
||||||
|
func readInterface(env params.Interface) (intf string, err error) {
|
||||||
|
intf, err = env.Get("OPENVPN_INTERFACE", params.Default("tun0"))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("environment variable OPENVPN_INTERFACE: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !openvpnIntfRegexp.MatchString(intf) {
|
||||||
|
return "", fmt.Errorf("%w: does not match regex %s: %s",
|
||||||
|
errInterfaceNameNotValid, openvpnIntfRegexString, intf)
|
||||||
|
}
|
||||||
|
|
||||||
|
return intf, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ func Test_OpenVPN_JSON(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
in := OpenVPN{
|
in := OpenVPN{
|
||||||
Root: true,
|
Root: true,
|
||||||
Provider: Provider{
|
Flags: []string{},
|
||||||
Name: "name",
|
Ciphers: []string{},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
data, err := json.MarshalIndent(in, "", " ")
|
data, err := json.MarshalIndent(in, "", " ")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -22,36 +21,17 @@ func Test_OpenVPN_JSON(t *testing.T) {
|
|||||||
"user": "",
|
"user": "",
|
||||||
"password": "",
|
"password": "",
|
||||||
"verbosity": 0,
|
"verbosity": 0,
|
||||||
|
"flags": [],
|
||||||
"mssfix": 0,
|
"mssfix": 0,
|
||||||
"run_as_root": true,
|
"run_as_root": true,
|
||||||
"cipher": "",
|
"ciphers": [],
|
||||||
"auth": "",
|
"auth": "",
|
||||||
"provider": {
|
"conf_file": "",
|
||||||
"name": "name",
|
"version": "",
|
||||||
"server_selection": {
|
|
||||||
"network_protocol": "",
|
|
||||||
"regions": null,
|
|
||||||
"group": "",
|
|
||||||
"countries": null,
|
|
||||||
"cities": null,
|
|
||||||
"hostnames": null,
|
|
||||||
"names": null,
|
|
||||||
"isps": null,
|
|
||||||
"owned": false,
|
|
||||||
"custom_port": 0,
|
|
||||||
"numbers": null,
|
|
||||||
"encryption_preset": ""
|
|
||||||
},
|
|
||||||
"extra_config": {
|
|
||||||
"encryption_preset": "",
|
"encryption_preset": "",
|
||||||
"openvpn_ipv6": false
|
"ipv6": false,
|
||||||
},
|
"procuser": "",
|
||||||
"port_forwarding": {
|
"interface": ""
|
||||||
"enabled": false,
|
|
||||||
"filepath": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"custom_config": ""
|
|
||||||
}`, string(data))
|
}`, string(data))
|
||||||
var out OpenVPN
|
var out OpenVPN
|
||||||
err = json.Unmarshal(data, &out)
|
err = json.Unmarshal(data, &out)
|
||||||
|
|||||||
43
internal/configuration/perfectprivacy.go
Normal file
43
internal/configuration/perfectprivacy.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) readPerfectPrivacy(r reader) (err error) {
|
||||||
|
settings.Name = constants.Perfectprivacy
|
||||||
|
servers := r.servers.GetPerfectprivacy()
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PerfectprivacyCityChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings.ServerSelection.OpenVPN.readPerfectPrivacy(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPNSelection) readPerfectPrivacy(r reader) (err error) {
|
||||||
|
settings.TCP, err = readOpenVPNProtocol(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
portValidation := openvpnPortValidation{
|
||||||
|
tcp: settings.TCP,
|
||||||
|
allowedTCP: []uint16{44, 443, 4433},
|
||||||
|
allowedUDP: []uint16{44, 443, 4433},
|
||||||
|
}
|
||||||
|
settings.CustomPort, err = readOpenVPNCustomPort(r, portValidation)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,33 +1,38 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (settings *Provider) privadoLines() (lines []string) {
|
|
||||||
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) {
|
func (settings *Provider) readPrivado(r reader) (err error) {
|
||||||
settings.Name = constants.Privado
|
settings.Name = constants.Privado
|
||||||
|
servers := r.servers.GetPrivado()
|
||||||
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivadoHostnameChoices())
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivadoCountryChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PrivadoRegionChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable REGION: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivadoCityChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivadoHostnameChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,77 +1,75 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/golibs/params"
|
"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))
|
|
||||||
}
|
|
||||||
|
|
||||||
lines = append(lines, lastIndent+"Encryption preset: "+settings.ServerSelection.EncryptionPreset)
|
|
||||||
|
|
||||||
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) {
|
func (settings *Provider) readPrivateInternetAccess(r reader) (err error) {
|
||||||
settings.Name = constants.PrivateInternetAccess
|
settings.Name = constants.PrivateInternetAccess
|
||||||
|
servers := r.servers.GetPia()
|
||||||
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
encryptionPreset, err := r.env.Inside("PIA_ENCRYPTION",
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PIAGeoChoices(servers))
|
||||||
[]string{constants.PIAEncryptionPresetNormal, constants.PIAEncryptionPresetStrong},
|
|
||||||
params.RetroKeys([]string{"ENCRYPTION"}, r.onRetroActive),
|
|
||||||
params.Default(constants.PIACertificateStrong),
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable REGION: %w", err)
|
||||||
}
|
|
||||||
settings.ServerSelection.EncryptionPreset = encryptionPreset
|
|
||||||
settings.ExtraConfigOptions.EncryptionPreset = encryptionPreset
|
|
||||||
|
|
||||||
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PIAGeoChoices())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.CustomPort, err = readPortOrZero(r.env, "PORT")
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PIAHostnameChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.PIANameChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable SERVER_NAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.PortForwarding.Enabled, err = r.env.OnOff("PORT_FORWARDING", params.Default("off"))
|
settings.PortForwarding.Enabled, err = r.env.OnOff("PORT_FORWARDING", params.Default("off"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable PORT_FORWARDING: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.PortForwarding.Enabled {
|
if settings.PortForwarding.Enabled {
|
||||||
settings.PortForwarding.Filepath, err = r.env.Path("PORT_FORWARDING_STATUS_FILE",
|
settings.PortForwarding.Filepath, err = r.env.Path("PORT_FORWARDING_STATUS_FILE",
|
||||||
params.Default("/tmp/gluetun/forwarded_port"), params.CaseSensitiveValue())
|
params.Default("/tmp/gluetun/forwarded_port"), params.CaseSensitiveValue())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable PORT_FORWARDING_STATUS_FILE: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings.ServerSelection.OpenVPN.readPrivateInternetAccess(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPNSelection) readPrivateInternetAccess(r reader) (err error) {
|
||||||
|
settings.EncPreset, err = getPIAEncryptionPreset(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settings.CustomPort, err = readOpenVPNCustomPort(r, openvpnPortValidation{allAllowed: true})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getPIAEncryptionPreset(r reader) (encryptionPreset string, err error) {
|
||||||
|
encryptionPreset, err = r.env.Inside("PIA_ENCRYPTION",
|
||||||
|
[]string{constants.PIAEncryptionPresetNone, constants.PIAEncryptionPresetNormal, constants.PIAEncryptionPresetStrong},
|
||||||
|
params.RetroKeys([]string{"ENCRYPTION"}, r.onRetroActive),
|
||||||
|
params.Default(constants.PIAEncryptionPresetStrong),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("environment variable PIA_ENCRYPTION: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return encryptionPreset, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,52 +1,35 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"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) {
|
func (settings *Provider) readPrivatevpn(r reader) (err error) {
|
||||||
settings.Name = constants.Privatevpn
|
settings.Name = constants.Privatevpn
|
||||||
|
servers := r.servers.GetPrivatevpn()
|
||||||
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivatevpnCountryChoices())
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivatevpnCountryChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivatevpnCityChoices())
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivatevpnCityChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivatevpnHostnameChoices())
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
|
||||||
|
constants.PrivatevpnHostnameChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,75 +1,51 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"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))
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines
|
|
||||||
}
|
|
||||||
|
|
||||||
func (settings *Provider) readProtonvpn(r reader) (err error) {
|
func (settings *Provider) readProtonvpn(r reader) (err error) {
|
||||||
settings.Name = constants.Protonvpn
|
settings.Name = constants.Protonvpn
|
||||||
|
servers := r.servers.GetProtonvpn()
|
||||||
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.CustomPort, err = readPortOrZero(r.env, "PORT")
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.ProtonvpnCountryChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.ProtonvpnCountryChoices())
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.ProtonvpnRegionChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable REGION: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.ProtonvpnRegionChoices())
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.ProtonvpnCityChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.ProtonvpnCityChoices())
|
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.ProtonvpnNameChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable SERVER_NAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_NAME", constants.ProtonvpnNameChoices())
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
|
||||||
|
constants.ProtonvpnHostnameChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.ProtonvpnHostnameChoices())
|
settings.ServerSelection.FreeOnly, err = r.env.YesNo("FREE_ONLY", params.Default("no"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable FREE_ONLY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,102 +14,235 @@ import (
|
|||||||
type Provider struct {
|
type Provider struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
ServerSelection ServerSelection `json:"server_selection"`
|
ServerSelection ServerSelection `json:"server_selection"`
|
||||||
ExtraConfigOptions ExtraConfigOptions `json:"extra_config"`
|
|
||||||
PortForwarding PortForwarding `json:"port_forwarding"`
|
PortForwarding PortForwarding `json:"port_forwarding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *Provider) lines() (lines []string) {
|
func (settings *Provider) lines() (lines []string) {
|
||||||
|
if settings.Name == "" { // custom OpenVPN configuration
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
lines = append(lines, lastIndent+strings.Title(settings.Name)+" settings:")
|
lines = append(lines, lastIndent+strings.Title(settings.Name)+" settings:")
|
||||||
|
|
||||||
lines = append(lines, indent+lastIndent+"Network protocol: "+settings.ServerSelection.Protocol)
|
for _, line := range settings.ServerSelection.toLines() {
|
||||||
|
|
||||||
if settings.ServerSelection.TargetIP != nil {
|
|
||||||
lines = append(lines, indent+lastIndent+"Target IP address: "+settings.ServerSelection.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 "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 "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)
|
lines = append(lines, indent+line)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if settings.PortForwarding.Enabled { // PIA
|
||||||
|
lines = append(lines, indent+lastIndent+"Port forwarding:")
|
||||||
|
for _, line := range settings.PortForwarding.lines() {
|
||||||
|
lines = append(lines, indent+indent+line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return lines
|
return lines
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidVPNProvider = errors.New("invalid VPN provider")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) read(r reader, vpnType string) error {
|
||||||
|
err := settings.readVPNServiceProvider(r, vpnType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch settings.Name {
|
||||||
|
case constants.Custom:
|
||||||
|
err = settings.readCustom(r, vpnType)
|
||||||
|
case constants.Cyberghost:
|
||||||
|
err = settings.readCyberghost(r)
|
||||||
|
case constants.Expressvpn:
|
||||||
|
err = settings.readExpressvpn(r)
|
||||||
|
case constants.Fastestvpn:
|
||||||
|
err = settings.readFastestvpn(r)
|
||||||
|
case constants.HideMyAss:
|
||||||
|
err = settings.readHideMyAss(r)
|
||||||
|
case constants.Ipvanish:
|
||||||
|
err = settings.readIpvanish(r)
|
||||||
|
case constants.Ivpn:
|
||||||
|
err = settings.readIvpn(r)
|
||||||
|
case constants.Mullvad:
|
||||||
|
err = settings.readMullvad(r)
|
||||||
|
case constants.Nordvpn:
|
||||||
|
err = settings.readNordvpn(r)
|
||||||
|
case constants.Perfectprivacy:
|
||||||
|
err = settings.readPerfectPrivacy(r)
|
||||||
|
case constants.Privado:
|
||||||
|
err = settings.readPrivado(r)
|
||||||
|
case constants.PrivateInternetAccess:
|
||||||
|
err = settings.readPrivateInternetAccess(r)
|
||||||
|
case constants.Privatevpn:
|
||||||
|
err = settings.readPrivatevpn(r)
|
||||||
|
case constants.Protonvpn:
|
||||||
|
err = settings.readProtonvpn(r)
|
||||||
|
case constants.Purevpn:
|
||||||
|
err = settings.readPurevpn(r)
|
||||||
|
case constants.Surfshark:
|
||||||
|
err = settings.readSurfshark(r)
|
||||||
|
case constants.Torguard:
|
||||||
|
err = settings.readTorguard(r)
|
||||||
|
case constants.VPNUnlimited:
|
||||||
|
err = settings.readVPNUnlimited(r)
|
||||||
|
case constants.Vyprvpn:
|
||||||
|
err = settings.readVyprvpn(r)
|
||||||
|
case constants.Wevpn:
|
||||||
|
err = settings.readWevpn(r)
|
||||||
|
case constants.Windscribe:
|
||||||
|
err = settings.readWindscribe(r)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%w: %s", ErrInvalidVPNProvider, settings.Name)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.VPN = vpnType
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readVPNServiceProvider(r reader, vpnType string) (err error) {
|
||||||
|
var allowedVPNServiceProviders []string
|
||||||
|
switch vpnType {
|
||||||
|
case constants.OpenVPN:
|
||||||
|
allowedVPNServiceProviders = []string{
|
||||||
|
constants.Custom,
|
||||||
|
"cyberghost", constants.Expressvpn, "fastestvpn", "hidemyass", "ipvanish",
|
||||||
|
"ivpn", "mullvad", "nordvpn",
|
||||||
|
constants.Perfectprivacy, "privado", "pia", "private internet access", "privatevpn", "protonvpn",
|
||||||
|
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn",
|
||||||
|
constants.Wevpn, "windscribe"}
|
||||||
|
case constants.Wireguard:
|
||||||
|
allowedVPNServiceProviders = []string{
|
||||||
|
constants.Custom, constants.Ivpn,
|
||||||
|
constants.Mullvad, constants.Windscribe,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vpnsp, err := r.env.Inside("VPNSP", allowedVPNServiceProviders,
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.isOpenVPNCustomConfig(r.env) { // retro compatibility
|
||||||
|
vpnsp = constants.Custom
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Name = vpnsp
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func commaJoin(slice []string) string {
|
func commaJoin(slice []string) string {
|
||||||
return strings.Join(slice, ", ")
|
return strings.Join(slice, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func readProtocol(env params.Env) (protocol string, err error) {
|
func protoToString(tcp bool) string {
|
||||||
return env.Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, params.Default(constants.UDP))
|
if tcp {
|
||||||
|
return constants.TCP
|
||||||
|
}
|
||||||
|
return constants.UDP
|
||||||
}
|
}
|
||||||
|
|
||||||
func readTargetIP(env params.Env) (targetIP net.IP, err error) {
|
func readTargetIP(env params.Interface) (targetIP net.IP, err error) {
|
||||||
return readIP(env, "OPENVPN_TARGET_IP")
|
targetIP, err = readIP(env, "OPENVPN_TARGET_IP")
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidProtocol = errors.New("invalid network protocol")
|
|
||||||
)
|
|
||||||
|
|
||||||
func readCustomPort(env params.Env, protocol string,
|
|
||||||
allowedTCP, allowedUDP []uint16) (port uint16, err error) {
|
|
||||||
port, err = readPortOrZero(env, "PORT")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return nil, fmt.Errorf("environment variable OPENVPN_TARGET_IP: %w", err)
|
||||||
} else if port == 0 {
|
}
|
||||||
return 0, nil
|
return targetIP, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch protocol {
|
type openvpnPortValidation struct {
|
||||||
case constants.TCP:
|
allAllowed bool
|
||||||
for i := range allowedTCP {
|
tcp bool
|
||||||
if allowedTCP[i] == port {
|
allowedTCP []uint16
|
||||||
|
allowedUDP []uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func readOpenVPNCustomPort(r reader, validation openvpnPortValidation) (
|
||||||
|
port uint16, err error) {
|
||||||
|
port, err = readPortOrZero(r.env, "OPENVPN_PORT")
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("environment variable OPENVPN_PORT: %w", err)
|
||||||
|
} else if port == 0 {
|
||||||
|
// Try using old variable name
|
||||||
|
port, err = readPortOrZero(r.env, "PORT")
|
||||||
|
if err != nil {
|
||||||
|
r.onRetroActive("PORT", "OPENVPN_PORT")
|
||||||
|
return 0, fmt.Errorf("environment variable PORT: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if port == 0 || validation.allAllowed {
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if validation.tcp {
|
||||||
|
for _, allowedPort := range validation.allowedTCP {
|
||||||
|
if port == allowedPort {
|
||||||
return port, nil
|
return port, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0, fmt.Errorf("%w: port %d for TCP protocol", ErrInvalidPort, port)
|
return 0, fmt.Errorf(
|
||||||
case constants.UDP:
|
"environment variable PORT: %w: port %d for TCP protocol, can only be one of %s",
|
||||||
for i := range allowedUDP {
|
ErrInvalidPort, port, portsToString(validation.allowedTCP))
|
||||||
if allowedUDP[i] == port {
|
}
|
||||||
|
for _, allowedPort := range validation.allowedUDP {
|
||||||
|
if port == allowedPort {
|
||||||
return port, nil
|
return port, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0, fmt.Errorf("%w: port %d for UDP protocol", ErrInvalidPort, port)
|
return 0, fmt.Errorf(
|
||||||
default:
|
"environment variable PORT: %w: port %d for UDP protocol, can only be one of %s",
|
||||||
return 0, fmt.Errorf("%w: %s", ErrInvalidProtocol, protocol)
|
ErrInvalidPort, port, portsToString(validation.allowedUDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: set allowed to an empty slice to allow all valid ports
|
||||||
|
func readWireguardCustomPort(env params.Interface, allowed []uint16) (port uint16, err error) {
|
||||||
|
port, err = readPortOrZero(env, "WIREGUARD_ENDPOINT_PORT")
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("environment variable WIREGUARD_ENDPOINT_PORT: %w", err)
|
||||||
|
} else if port == 0 {
|
||||||
|
port, _ = readPortOrZero(env, "WIREGUARD_PORT")
|
||||||
|
if err == nil {
|
||||||
|
return port, nil // 0 or WIREGUARD_PORT value
|
||||||
|
}
|
||||||
|
return 0, nil // default 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(allowed) == 0 {
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range allowed {
|
||||||
|
if allowed[i] == port {
|
||||||
|
return port, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0, fmt.Errorf(
|
||||||
|
"environment variable WIREGUARD_PORT: %w: port %d, can only be one of %s",
|
||||||
|
ErrInvalidPort, port, portsToString(allowed))
|
||||||
|
}
|
||||||
|
|
||||||
|
func portsToString(ports []uint16) string {
|
||||||
|
slice := make([]string, len(ports))
|
||||||
|
for i := range ports {
|
||||||
|
slice[i] = fmt.Sprint(ports[i])
|
||||||
|
}
|
||||||
|
return strings.Join(slice, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isOpenVPNCustomConfig is for retro compatibility to set VPNSP=custom
|
||||||
|
// if OPENVPN_CUSTOM_CONFIG is set.
|
||||||
|
func (settings Provider) isOpenVPNCustomConfig(env params.Interface) (ok bool) {
|
||||||
|
s, _ := env.Get("VPN_TYPE")
|
||||||
|
isOpenVPN := s == constants.OpenVPN
|
||||||
|
s, _ = env.Get("OPENVPN_CUSTOM_CONFIG")
|
||||||
|
return isOpenVPN && s != ""
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,45 +24,58 @@ func Test_Provider_lines(t *testing.T) {
|
|||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Cyberghost,
|
Name: constants.Cyberghost,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
Protocol: constants.UDP,
|
VPN: constants.OpenVPN,
|
||||||
Group: "group",
|
Countries: []string{"a", "El country"},
|
||||||
Regions: []string{"a", "El country"},
|
|
||||||
},
|
|
||||||
ExtraConfigOptions: ExtraConfigOptions{
|
|
||||||
ClientKey: "a",
|
|
||||||
ClientCertificate: "a",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Cyberghost settings:",
|
"|--Cyberghost settings:",
|
||||||
" |--Network protocol: udp",
|
" |--Countries: a, El country",
|
||||||
" |--Server group: group",
|
" |--OpenVPN selection:",
|
||||||
" |--Regions: a, El country",
|
" |--Protocol: udp",
|
||||||
" |--Client key is set",
|
},
|
||||||
" |--Client certificate is set",
|
},
|
||||||
|
"expressvpn": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Expressvpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
VPN: constants.OpenVPN,
|
||||||
|
Hostnames: []string{"a", "b"},
|
||||||
|
Countries: []string{"c", "d"},
|
||||||
|
Cities: []string{"e", "f"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Expressvpn settings:",
|
||||||
|
" |--Countries: c, d",
|
||||||
|
" |--Cities: e, f",
|
||||||
|
" |--Hostnames: a, b",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"fastestvpn": {
|
"fastestvpn": {
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Fastestvpn,
|
Name: constants.Fastestvpn,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
Protocol: constants.UDP,
|
VPN: constants.OpenVPN,
|
||||||
Hostnames: []string{"a", "b"},
|
Hostnames: []string{"a", "b"},
|
||||||
Countries: []string{"c", "d"},
|
Countries: []string{"c", "d"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Fastestvpn settings:",
|
"|--Fastestvpn settings:",
|
||||||
" |--Network protocol: udp",
|
|
||||||
" |--Hostnames: a, b",
|
|
||||||
" |--Countries: c, d",
|
" |--Countries: c, d",
|
||||||
|
" |--Hostnames: a, b",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"hidemyass": {
|
"hidemyass": {
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.HideMyAss,
|
Name: constants.HideMyAss,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
Protocol: constants.UDP,
|
VPN: constants.OpenVPN,
|
||||||
Countries: []string{"a", "b"},
|
Countries: []string{"a", "b"},
|
||||||
Cities: []string{"c", "d"},
|
Cities: []string{"c", "d"},
|
||||||
Hostnames: []string{"e", "f"},
|
Hostnames: []string{"e", "f"},
|
||||||
@@ -70,71 +83,126 @@ func Test_Provider_lines(t *testing.T) {
|
|||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Hidemyass settings:",
|
"|--Hidemyass settings:",
|
||||||
" |--Network protocol: udp",
|
|
||||||
" |--Countries: a, b",
|
" |--Countries: a, b",
|
||||||
" |--Cities: c, d",
|
" |--Cities: c, d",
|
||||||
" |--Hostnames: e, f",
|
" |--Hostnames: e, f",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ipvanish": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ipvanish,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
VPN: constants.OpenVPN,
|
||||||
|
Countries: []string{"a", "b"},
|
||||||
|
Cities: []string{"c", "d"},
|
||||||
|
Hostnames: []string{"e", "f"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Ipvanish settings:",
|
||||||
|
" |--Countries: a, b",
|
||||||
|
" |--Cities: c, d",
|
||||||
|
" |--Hostnames: e, f",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ivpn": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
VPN: constants.OpenVPN,
|
||||||
|
Countries: []string{"a", "b"},
|
||||||
|
Cities: []string{"c", "d"},
|
||||||
|
Hostnames: []string{"e", "f"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Ivpn settings:",
|
||||||
|
" |--Countries: a, b",
|
||||||
|
" |--Cities: c, d",
|
||||||
|
" |--Hostnames: e, f",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"mullvad": {
|
"mullvad": {
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Mullvad,
|
Name: constants.Mullvad,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
Protocol: constants.UDP,
|
VPN: constants.OpenVPN,
|
||||||
Countries: []string{"a", "b"},
|
Countries: []string{"a", "b"},
|
||||||
Cities: []string{"c", "d"},
|
Cities: []string{"c", "d"},
|
||||||
ISPs: []string{"e", "f"},
|
ISPs: []string{"e", "f"},
|
||||||
|
OpenVPN: OpenVPNSelection{
|
||||||
CustomPort: 1,
|
CustomPort: 1,
|
||||||
},
|
},
|
||||||
ExtraConfigOptions: ExtraConfigOptions{
|
|
||||||
OpenVPNIPv6: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Mullvad settings:",
|
"|--Mullvad settings:",
|
||||||
" |--Network protocol: udp",
|
|
||||||
" |--Countries: a, b",
|
" |--Countries: a, b",
|
||||||
" |--Cities: c, d",
|
" |--Cities: c, d",
|
||||||
" |--ISPs: e, f",
|
" |--ISPs: e, f",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
" |--Custom port: 1",
|
" |--Custom port: 1",
|
||||||
" |--IPv6: enabled",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"nordvpn": {
|
"nordvpn": {
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Nordvpn,
|
Name: constants.Nordvpn,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
Protocol: constants.UDP,
|
VPN: constants.OpenVPN,
|
||||||
Regions: []string{"a", "b"},
|
Regions: []string{"a", "b"},
|
||||||
Numbers: []uint16{1, 2},
|
Numbers: []uint16{1, 2},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Nordvpn settings:",
|
"|--Nordvpn settings:",
|
||||||
" |--Network protocol: udp",
|
|
||||||
" |--Regions: a, b",
|
" |--Regions: a, b",
|
||||||
" |--Numbers: 1, 2",
|
" |--Numbers: 1, 2",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"perfectprivacy": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Perfectprivacy,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
VPN: constants.OpenVPN,
|
||||||
|
Cities: []string{"a", "b"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Perfect Privacy settings:",
|
||||||
|
" |--Cities: a, b",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"privado": {
|
"privado": {
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Privado,
|
Name: constants.Privado,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
Protocol: constants.UDP,
|
VPN: constants.OpenVPN,
|
||||||
Hostnames: []string{"a", "b"},
|
Hostnames: []string{"a", "b"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Privado settings:",
|
"|--Privado settings:",
|
||||||
" |--Network protocol: udp",
|
|
||||||
" |--Hostnames: a, b",
|
" |--Hostnames: a, b",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"privatevpn": {
|
"privatevpn": {
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Privatevpn,
|
Name: constants.Privatevpn,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
Protocol: constants.UDP,
|
VPN: constants.OpenVPN,
|
||||||
Hostnames: []string{"a", "b"},
|
Hostnames: []string{"a", "b"},
|
||||||
Countries: []string{"c", "d"},
|
Countries: []string{"c", "d"},
|
||||||
Cities: []string{"e", "f"},
|
Cities: []string{"e", "f"},
|
||||||
@@ -142,17 +210,18 @@ func Test_Provider_lines(t *testing.T) {
|
|||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Privatevpn settings:",
|
"|--Privatevpn settings:",
|
||||||
" |--Network protocol: udp",
|
|
||||||
" |--Countries: c, d",
|
" |--Countries: c, d",
|
||||||
" |--Cities: e, f",
|
" |--Cities: e, f",
|
||||||
" |--Hostnames: a, b",
|
" |--Hostnames: a, b",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"protonvpn": {
|
"protonvpn": {
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Protonvpn,
|
Name: constants.Protonvpn,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
Protocol: constants.UDP,
|
VPN: constants.OpenVPN,
|
||||||
Countries: []string{"a", "b"},
|
Countries: []string{"a", "b"},
|
||||||
Regions: []string{"c", "d"},
|
Regions: []string{"c", "d"},
|
||||||
Cities: []string{"e", "f"},
|
Cities: []string{"e", "f"},
|
||||||
@@ -162,23 +231,25 @@ func Test_Provider_lines(t *testing.T) {
|
|||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Protonvpn settings:",
|
"|--Protonvpn settings:",
|
||||||
" |--Network protocol: udp",
|
|
||||||
" |--Countries: a, b",
|
" |--Countries: a, b",
|
||||||
" |--Regions: c, d",
|
" |--Regions: c, d",
|
||||||
" |--Cities: e, f",
|
" |--Cities: e, f",
|
||||||
" |--Names: g, h",
|
|
||||||
" |--Hostnames: i, j",
|
" |--Hostnames: i, j",
|
||||||
|
" |--Names: g, h",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"private internet access": {
|
"private internet access": {
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.PrivateInternetAccess,
|
Name: constants.PrivateInternetAccess,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
Protocol: constants.UDP,
|
VPN: constants.OpenVPN,
|
||||||
Regions: []string{"a", "b"},
|
Regions: []string{"a", "b"},
|
||||||
EncryptionPreset: constants.PIAEncryptionPresetStrong,
|
OpenVPN: OpenVPNSelection{
|
||||||
CustomPort: 1,
|
CustomPort: 1,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
PortForwarding: PortForwarding{
|
PortForwarding: PortForwarding{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
Filepath: string("/here"),
|
Filepath: string("/here"),
|
||||||
@@ -186,9 +257,9 @@ func Test_Provider_lines(t *testing.T) {
|
|||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Private Internet Access settings:",
|
"|--Private Internet Access settings:",
|
||||||
" |--Network protocol: udp",
|
|
||||||
" |--Regions: a, b",
|
" |--Regions: a, b",
|
||||||
" |--Encryption preset: strong",
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
" |--Custom port: 1",
|
" |--Custom port: 1",
|
||||||
" |--Port forwarding:",
|
" |--Port forwarding:",
|
||||||
" |--File path: /here",
|
" |--File path: /here",
|
||||||
@@ -198,7 +269,7 @@ func Test_Provider_lines(t *testing.T) {
|
|||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Purevpn,
|
Name: constants.Purevpn,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
Protocol: constants.UDP,
|
VPN: constants.OpenVPN,
|
||||||
Regions: []string{"a", "b"},
|
Regions: []string{"a", "b"},
|
||||||
Countries: []string{"c", "d"},
|
Countries: []string{"c", "d"},
|
||||||
Cities: []string{"e", "f"},
|
Cities: []string{"e", "f"},
|
||||||
@@ -206,31 +277,33 @@ func Test_Provider_lines(t *testing.T) {
|
|||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Purevpn settings:",
|
"|--Purevpn settings:",
|
||||||
" |--Network protocol: udp",
|
|
||||||
" |--Regions: a, b",
|
|
||||||
" |--Countries: c, d",
|
" |--Countries: c, d",
|
||||||
|
" |--Regions: a, b",
|
||||||
" |--Cities: e, f",
|
" |--Cities: e, f",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"surfshark": {
|
"surfshark": {
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Surfshark,
|
Name: constants.Surfshark,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
Protocol: constants.UDP,
|
VPN: constants.OpenVPN,
|
||||||
Regions: []string{"a", "b"},
|
Regions: []string{"a", "b"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Surfshark settings:",
|
"|--Surfshark settings:",
|
||||||
" |--Network protocol: udp",
|
|
||||||
" |--Regions: a, b",
|
" |--Regions: a, b",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"torguard": {
|
"torguard": {
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Torguard,
|
Name: constants.Torguard,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
Protocol: constants.UDP,
|
VPN: constants.OpenVPN,
|
||||||
Countries: []string{"a", "b"},
|
Countries: []string{"a", "b"},
|
||||||
Cities: []string{"c", "d"},
|
Cities: []string{"c", "d"},
|
||||||
Hostnames: []string{"e"},
|
Hostnames: []string{"e"},
|
||||||
@@ -238,43 +311,92 @@ func Test_Provider_lines(t *testing.T) {
|
|||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Torguard settings:",
|
"|--Torguard settings:",
|
||||||
" |--Network protocol: udp",
|
|
||||||
" |--Countries: a, b",
|
" |--Countries: a, b",
|
||||||
" |--Cities: c, d",
|
" |--Cities: c, d",
|
||||||
" |--Hostnames: e",
|
" |--Hostnames: e",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
constants.VPNUnlimited: {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.VPNUnlimited,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
VPN: constants.OpenVPN,
|
||||||
|
Countries: []string{"a", "b"},
|
||||||
|
Cities: []string{"c", "d"},
|
||||||
|
Hostnames: []string{"e", "f"},
|
||||||
|
FreeOnly: true,
|
||||||
|
StreamOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Vpn Unlimited settings:",
|
||||||
|
" |--Countries: a, b",
|
||||||
|
" |--Cities: c, d",
|
||||||
|
" |--Free servers only",
|
||||||
|
" |--Stream servers only",
|
||||||
|
" |--Hostnames: e, f",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"vyprvpn": {
|
"vyprvpn": {
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Vyprvpn,
|
Name: constants.Vyprvpn,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
Protocol: constants.UDP,
|
VPN: constants.OpenVPN,
|
||||||
Regions: []string{"a", "b"},
|
Regions: []string{"a", "b"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Vyprvpn settings:",
|
"|--Vyprvpn settings:",
|
||||||
" |--Network protocol: udp",
|
|
||||||
" |--Regions: a, b",
|
" |--Regions: a, b",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"wevpn": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Wevpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
VPN: constants.OpenVPN,
|
||||||
|
Cities: []string{"a", "b"},
|
||||||
|
Hostnames: []string{"c", "d"},
|
||||||
|
OpenVPN: OpenVPNSelection{
|
||||||
|
CustomPort: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Wevpn settings:",
|
||||||
|
" |--Cities: a, b",
|
||||||
|
" |--Hostnames: c, d",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
|
" |--Custom port: 1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"windscribe": {
|
"windscribe": {
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Windscribe,
|
Name: constants.Windscribe,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
Protocol: constants.UDP,
|
VPN: constants.OpenVPN,
|
||||||
Regions: []string{"a", "b"},
|
Regions: []string{"a", "b"},
|
||||||
Cities: []string{"c", "d"},
|
Cities: []string{"c", "d"},
|
||||||
Hostnames: []string{"e", "f"},
|
Hostnames: []string{"e", "f"},
|
||||||
|
OpenVPN: OpenVPNSelection{
|
||||||
CustomPort: 1,
|
CustomPort: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Windscribe settings:",
|
"|--Windscribe settings:",
|
||||||
" |--Network protocol: udp",
|
|
||||||
" |--Regions: a, b",
|
" |--Regions: a, b",
|
||||||
" |--Cities: c, d",
|
" |--Cities: c, d",
|
||||||
" |--Hostnames: e, f",
|
" |--Hostnames: e, f",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
" |--Custom port: 1",
|
" |--Custom port: 1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -298,16 +420,16 @@ func Test_readProtocol(t *testing.T) {
|
|||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
mockStr string
|
mockStr string
|
||||||
mockErr error
|
mockErr error
|
||||||
protocol string
|
tcp bool
|
||||||
err error
|
err error
|
||||||
}{
|
}{
|
||||||
"error": {
|
"error": {
|
||||||
mockErr: errDummy,
|
mockErr: errDummy,
|
||||||
err: errDummy,
|
err: errors.New("environment variable OPENVPN_PROTOCOL: dummy"),
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
mockStr: "tcp",
|
mockStr: "tcp",
|
||||||
protocol: constants.TCP,
|
tcp: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,12 +439,15 @@ func Test_readProtocol(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
|
|
||||||
env := mock_params.NewMockEnv(ctrl)
|
env := mock_params.NewMockInterface(ctrl)
|
||||||
env.EXPECT().
|
env.EXPECT().
|
||||||
Inside("PROTOCOL", []string{"tcp", "udp"}, gomock.Any()).
|
Inside("OPENVPN_PROTOCOL", []string{"tcp", "udp"}, gomock.Any(), gomock.Any()).
|
||||||
Return(testCase.mockStr, testCase.mockErr)
|
Return(testCase.mockStr, testCase.mockErr)
|
||||||
|
reader := reader{
|
||||||
|
env: env,
|
||||||
|
}
|
||||||
|
|
||||||
protocol, err := readProtocol(env)
|
tcp, err := readOpenVPNProtocol(reader)
|
||||||
|
|
||||||
if testCase.err != nil {
|
if testCase.err != nil {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
@@ -331,7 +456,7 @@ func Test_readProtocol(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, testCase.protocol, protocol)
|
assert.Equal(t, testCase.tcp, tcp)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -32,14 +33,14 @@ func (settings *PublicIP) lines() (lines []string) {
|
|||||||
func (settings *PublicIP) read(r reader) (err error) {
|
func (settings *PublicIP) read(r reader) (err error) {
|
||||||
settings.Period, err = r.env.Duration("PUBLICIP_PERIOD", params.Default("12h"))
|
settings.Period, err = r.env.Duration("PUBLICIP_PERIOD", params.Default("12h"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable PUBLICIP_PERIOD: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.IPFilepath, err = r.env.Path("PUBLICIP_FILE", params.CaseSensitiveValue(),
|
settings.IPFilepath, err = r.env.Path("PUBLICIP_FILE", params.CaseSensitiveValue(),
|
||||||
params.Default("/tmp/gluetun/ip"),
|
params.Default("/tmp/gluetun/ip"),
|
||||||
params.RetroKeys([]string{"IP_STATUS_FILE"}, r.onRetroActive))
|
params.RetroKeys([]string{"IP_STATUS_FILE"}, r.onRetroActive))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable PUBLICIP_FILE (or IP_STATUS_FILE): %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,52 +1,39 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"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))
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines
|
|
||||||
}
|
|
||||||
|
|
||||||
func (settings *Provider) readPurevpn(r reader) (err error) {
|
func (settings *Provider) readPurevpn(r reader) (err error) {
|
||||||
settings.Name = constants.Purevpn
|
settings.Name = constants.Purevpn
|
||||||
|
servers := r.servers.GetPurevpn()
|
||||||
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PurevpnRegionChoices())
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PurevpnRegionChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable REGION: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PurevpnCountryChoices())
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PurevpnCountryChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PurevpnCityChoices())
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PurevpnCityChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PurevpnHostnameChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings.ServerSelection.OpenVPN.readProtocolOnly(r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,44 +7,52 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/golibs/logging"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/golibs/os"
|
ovpnextract "github.com/qdm12/gluetun/internal/openvpn/extract"
|
||||||
"github.com/qdm12/golibs/params"
|
"github.com/qdm12/golibs/params"
|
||||||
"github.com/qdm12/golibs/verification"
|
"github.com/qdm12/golibs/verification"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:generate mockgen -destination=warner_mock_test.go -package configuration . Warner
|
||||||
|
|
||||||
type reader struct {
|
type reader struct {
|
||||||
env params.Env
|
servers models.AllServers
|
||||||
logger logging.Logger
|
env params.Interface
|
||||||
|
warner Warner
|
||||||
regex verification.Regex
|
regex verification.Regex
|
||||||
os os.OS
|
ovpnExt ovpnextract.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
func newReader(env params.Env, os os.OS, logger logging.Logger) reader {
|
type Warner interface {
|
||||||
|
Warn(s string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReader(env params.Interface,
|
||||||
|
servers models.AllServers, warner Warner) reader {
|
||||||
return reader{
|
return reader{
|
||||||
|
servers: servers,
|
||||||
env: env,
|
env: env,
|
||||||
logger: logger,
|
warner: warner,
|
||||||
regex: verification.NewRegex(),
|
regex: verification.NewRegex(),
|
||||||
os: os,
|
ovpnExt: ovpnextract.New(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *reader) onRetroActive(oldKey, newKey string) {
|
func (r *reader) onRetroActive(oldKey, newKey string) {
|
||||||
r.logger.Warn(
|
r.warner.Warn(
|
||||||
"You are using the old environment variable %s, please consider changing it to %s",
|
"You are using the old environment variable " + oldKey +
|
||||||
oldKey, newKey,
|
", please consider changing it to " + newKey)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidPort = errors.New("invalid port")
|
ErrInvalidPort = errors.New("invalid port")
|
||||||
)
|
)
|
||||||
|
|
||||||
func readCSVPorts(env params.Env, key string) (ports []uint16, err error) {
|
func readCSVPorts(env params.Interface, key string) (ports []uint16, err error) {
|
||||||
s, err := env.Get(key)
|
s, err := env.Get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if len(s) == 0 {
|
} else if s == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,11 +61,9 @@ func readCSVPorts(env params.Env, key string) (ports []uint16, err error) {
|
|||||||
for i, portStr := range portsStr {
|
for i, portStr := range portsStr {
|
||||||
portInt, err := strconv.Atoi(portStr)
|
portInt, err := strconv.Atoi(portStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%w: %q from environment variable %s: %s",
|
return nil, fmt.Errorf("%w: %s: %s", ErrInvalidPort, portStr, err)
|
||||||
ErrInvalidPort, portStr, key, err)
|
|
||||||
} else if portInt <= 0 || portInt > 65535 {
|
} else if portInt <= 0 || portInt > 65535 {
|
||||||
return nil, fmt.Errorf("%w: %d from environment variable %s: must be between 1 and 65535",
|
return nil, fmt.Errorf("%w: %d: must be between 1 and 65535", ErrInvalidPort, portInt)
|
||||||
ErrInvalidPort, portInt, key)
|
|
||||||
}
|
}
|
||||||
ports[i] = uint16(portInt)
|
ports[i] = uint16(portInt)
|
||||||
}
|
}
|
||||||
@@ -69,7 +75,7 @@ var (
|
|||||||
ErrInvalidIPNet = errors.New("invalid IP network")
|
ErrInvalidIPNet = errors.New("invalid IP network")
|
||||||
)
|
)
|
||||||
|
|
||||||
func readCSVIPNets(env params.Env, key string, options ...params.OptionSetter) (
|
func readCSVIPNets(env params.Interface, key string, options ...params.OptionSetter) (
|
||||||
ipNets []net.IPNet, err error) {
|
ipNets []net.IPNet, err error) {
|
||||||
s, err := env.Get(key, options...)
|
s, err := env.Get(key, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -83,11 +89,10 @@ func readCSVIPNets(env params.Env, key string, options ...params.OptionSetter) (
|
|||||||
for i, ipNetStr := range ipNetsStr {
|
for i, ipNetStr := range ipNetsStr {
|
||||||
_, ipNet, err := net.ParseCIDR(ipNetStr)
|
_, ipNet, err := net.ParseCIDR(ipNetStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%w: %q from environment variable %s: %s",
|
return nil, fmt.Errorf("%w: %s: %s",
|
||||||
ErrInvalidIPNet, ipNetStr, key, err)
|
ErrInvalidIPNet, ipNetStr, err)
|
||||||
} else if ipNet == nil {
|
} else if ipNet == nil {
|
||||||
return nil, fmt.Errorf("%w: %q from environment variable %s: subnet is nil",
|
return nil, fmt.Errorf("%w: %s: subnet is nil", ErrInvalidIPNet, ipNetStr)
|
||||||
ErrInvalidIPNet, ipNetStr, key)
|
|
||||||
}
|
}
|
||||||
ipNets[i] = *ipNet
|
ipNets[i] = *ipNet
|
||||||
}
|
}
|
||||||
@@ -99,9 +104,9 @@ var (
|
|||||||
ErrInvalidIP = errors.New("invalid IP address")
|
ErrInvalidIP = errors.New("invalid IP address")
|
||||||
)
|
)
|
||||||
|
|
||||||
func readIP(env params.Env, key string) (ip net.IP, err error) {
|
func readIP(env params.Interface, key string) (ip net.IP, err error) {
|
||||||
s, err := env.Get(key)
|
s, err := env.Get(key)
|
||||||
if len(s) == 0 {
|
if s == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -115,13 +120,13 @@ func readIP(env params.Env, key string) (ip net.IP, err error) {
|
|||||||
return ip, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readPortOrZero(env params.Env, key string) (port uint16, err error) {
|
func readPortOrZero(env params.Interface, key string) (port uint16, err error) {
|
||||||
s, err := env.Get(key)
|
s, err := env.Get(key, params.Default("0"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s) == 0 || s == "0" {
|
if s == "0" {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package configuration
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/golibs/os"
|
|
||||||
"github.com/qdm12/golibs/params"
|
"github.com/qdm12/golibs/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,6 +18,12 @@ var (
|
|||||||
ErrFilesDoNotExist = errors.New("files do not exist")
|
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) {
|
func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKeys []string) (value string, err error) {
|
||||||
envOptions := []params.OptionSetter{
|
envOptions := []params.OptionSetter{
|
||||||
params.Compulsory(), // to fallback on file reading
|
params.Compulsory(), // to fallback on file reading
|
||||||
@@ -27,37 +33,40 @@ func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKey
|
|||||||
}
|
}
|
||||||
value, envErr := r.env.Get(envKey, envOptions...)
|
value, envErr := r.env.Get(envKey, envOptions...)
|
||||||
if envErr == nil {
|
if envErr == nil {
|
||||||
|
value = cleanSuffix(value)
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
secretFilepathEnvKey := envKey + "_SECRETFILE"
|
||||||
defaultSecretFile := "/run/secrets/" + strings.ToLower(envKey)
|
defaultSecretFile := "/run/secrets/" + strings.ToLower(envKey)
|
||||||
filepath, err := r.env.Get(envKey+"_SECRETFILE",
|
filepath, err := r.env.Get(secretFilepathEnvKey,
|
||||||
params.CaseSensitiveValue(),
|
params.CaseSensitiveValue(),
|
||||||
params.Default(defaultSecretFile),
|
params.Default(defaultSecretFile),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("%w: %s", ErrGetSecretFilepath, err)
|
return "", fmt.Errorf("%w: environment variable %s: %s",
|
||||||
|
ErrGetSecretFilepath, secretFilepathEnvKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
file, fileErr := r.os.OpenFile(filepath, os.O_RDONLY, 0)
|
file, fileErr := os.OpenFile(filepath, os.O_RDONLY, 0)
|
||||||
if os.IsNotExist(fileErr) {
|
if os.IsNotExist(fileErr) {
|
||||||
if compulsory {
|
if compulsory {
|
||||||
return "", envErr
|
return "", fmt.Errorf("environment variable %s: %w", envKey, envErr)
|
||||||
}
|
}
|
||||||
return "", nil
|
return "", nil
|
||||||
} else if fileErr != nil {
|
} else if fileErr != nil {
|
||||||
return "", fmt.Errorf("%w: %s", ErrReadSecretFile, fileErr)
|
return "", fmt.Errorf("%w: %s: %s", ErrReadSecretFile, filepath, fileErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(file)
|
b, err := io.ReadAll(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("%w: %s", ErrReadSecretFile, err)
|
return "", fmt.Errorf("%w: %s: %s", ErrReadSecretFile, filepath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
value = string(b)
|
value = string(b)
|
||||||
value = strings.TrimSuffix(value, "\n")
|
value = cleanSuffix(value)
|
||||||
if compulsory && len(value) == 0 {
|
if compulsory && value == "" {
|
||||||
return "", ErrSecretFileIsEmpty
|
return "", fmt.Errorf("%s: %w", filepath, ErrSecretFileIsEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
return value, nil
|
return value, nil
|
||||||
@@ -67,15 +76,16 @@ func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKey
|
|||||||
func (r *reader) getFromFileOrSecretFile(secretName, filepath string) (
|
func (r *reader) getFromFileOrSecretFile(secretName, filepath string) (
|
||||||
b []byte, err error) {
|
b []byte, err error) {
|
||||||
defaultSecretFile := "/run/secrets/" + strings.ToLower(secretName)
|
defaultSecretFile := "/run/secrets/" + strings.ToLower(secretName)
|
||||||
secretFilepath, err := r.env.Get(strings.ToUpper(secretName)+"_SECRETFILE",
|
key := strings.ToUpper(secretName) + "_SECRETFILE"
|
||||||
|
secretFilepath, err := r.env.Get(key,
|
||||||
params.CaseSensitiveValue(),
|
params.CaseSensitiveValue(),
|
||||||
params.Default(defaultSecretFile),
|
params.Default(defaultSecretFile),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return b, fmt.Errorf("%w: %s", ErrGetSecretFilepath, err)
|
return b, fmt.Errorf("environment variable %s: %w: %s", key, ErrGetSecretFilepath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err = readFromFile(r.os.OpenFile, secretFilepath)
|
b, err = readFromFile(secretFilepath)
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
return b, fmt.Errorf("%w: %s", ErrReadSecretFile, err)
|
return b, fmt.Errorf("%w: %s", ErrReadSecretFile, err)
|
||||||
} else if err == nil {
|
} else if err == nil {
|
||||||
@@ -83,7 +93,7 @@ func (r *reader) getFromFileOrSecretFile(secretName, filepath string) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Secret file does not exist, try the non secret file
|
// Secret file does not exist, try the non secret file
|
||||||
b, err = readFromFile(r.os.OpenFile, filepath)
|
b, err = readFromFile(filepath)
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
return nil, fmt.Errorf("%w: %s", ErrReadSecretFile, err)
|
return nil, fmt.Errorf("%w: %s", ErrReadSecretFile, err)
|
||||||
} else if err == nil {
|
} else if err == nil {
|
||||||
@@ -92,12 +102,12 @@ func (r *reader) getFromFileOrSecretFile(secretName, filepath string) (
|
|||||||
return nil, fmt.Errorf("%w: %s and %s", ErrFilesDoNotExist, secretFilepath, filepath)
|
return nil, fmt.Errorf("%w: %s and %s", ErrFilesDoNotExist, secretFilepath, filepath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readFromFile(openFile os.OpenFileFunc, filepath string) (b []byte, err error) {
|
func readFromFile(filepath string) (b []byte, err error) {
|
||||||
file, err := openFile(filepath, os.O_RDONLY, 0)
|
file, err := os.Open(filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
b, err = ioutil.ReadAll(file)
|
b, err = io.ReadAll(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = file.Close()
|
_ = file.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -1,44 +1,179 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerSelection struct {
|
type ServerSelection struct { //nolint:maligned
|
||||||
// Common
|
// Common
|
||||||
Protocol string `json:"network_protocol"`
|
VPN string `json:"vpn"` // note: this is required
|
||||||
TargetIP net.IP `json:"target_ip,omitempty"`
|
TargetIP net.IP `json:"target_ip,omitempty"`
|
||||||
// TODO comments
|
// TODO comments
|
||||||
// Cyberghost, PIA, Protonvpn, Surfshark, Windscribe, Vyprvpn, NordVPN
|
// Cyberghost, PIA, Protonvpn, Surfshark, Windscribe, Vyprvpn, NordVPN
|
||||||
Regions []string `json:"regions"`
|
Regions []string `json:"regions"`
|
||||||
|
|
||||||
// Cyberghost
|
// Expressvpn, Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
|
||||||
Group string `json:"group"`
|
Countries []string `json:"countries"`
|
||||||
|
// Expressvpn, HideMyAss, IPVanish, IVPN, Mullvad, Perfectprivacy, PrivateVPN, Protonvpn,
|
||||||
Countries []string `json:"countries"` // Fastestvpn, HideMyAss, Mullvad, PrivateVPN, Protonvpn, PureVPN
|
// PureVPN, VPNUnlimited, WeVPN, Windscribe
|
||||||
Cities []string `json:"cities"` // HideMyAss, Mullvad, PrivateVPN, Protonvpn, PureVPN, Windscribe
|
Cities []string `json:"cities"`
|
||||||
Hostnames []string `json:"hostnames"` // Fastestvpn, HideMyAss, PrivateVPN, Windscribe, Privado, Protonvpn
|
// Expressvpn, Fastestvpn, HideMyAss, IPVanish, IVPN, PrivateVPN, Windscribe, Privado, Protonvpn, VPNUnlimited, WeVPN
|
||||||
|
Hostnames []string `json:"hostnames"`
|
||||||
Names []string `json:"names"` // Protonvpn
|
Names []string `json:"names"` // Protonvpn
|
||||||
|
|
||||||
// Mullvad
|
// Mullvad
|
||||||
ISPs []string `json:"isps"`
|
ISPs []string `json:"isps"`
|
||||||
Owned bool `json:"owned"`
|
Owned bool `json:"owned"`
|
||||||
|
|
||||||
// Mullvad, Windscribe, PIA
|
|
||||||
CustomPort uint16 `json:"custom_port"`
|
|
||||||
|
|
||||||
// NordVPN
|
// NordVPN
|
||||||
Numbers []uint16 `json:"numbers"`
|
Numbers []uint16 `json:"numbers"`
|
||||||
|
|
||||||
// PIA
|
// ProtonVPN
|
||||||
EncryptionPreset string `json:"encryption_preset"`
|
FreeOnly bool `json:"free_only"`
|
||||||
|
|
||||||
|
// VPNUnlimited
|
||||||
|
StreamOnly bool `json:"stream_only"`
|
||||||
|
|
||||||
|
// Surfshark
|
||||||
|
MultiHopOnly bool `json:"multihop_only"`
|
||||||
|
|
||||||
|
OpenVPN OpenVPNSelection `json:"openvpn"`
|
||||||
|
Wireguard WireguardSelection `json:"wireguard"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExtraConfigOptions struct {
|
func (selection ServerSelection) toLines() (lines []string) {
|
||||||
ClientCertificate string `json:"-"` // Cyberghost
|
if selection.TargetIP != nil {
|
||||||
ClientKey string `json:"-"` // Cyberghost
|
lines = append(lines, lastIndent+"Target IP address: "+selection.TargetIP.String())
|
||||||
EncryptionPreset string `json:"encryption_preset"` // PIA
|
}
|
||||||
OpenVPNIPv6 bool `json:"openvpn_ipv6"` // Mullvad
|
|
||||||
|
if len(selection.Countries) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(selection.Countries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(selection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(selection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(selection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(selection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(selection.ISPs) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"ISPs: "+commaJoin(selection.ISPs))
|
||||||
|
}
|
||||||
|
|
||||||
|
if selection.FreeOnly {
|
||||||
|
lines = append(lines, lastIndent+"Free servers only")
|
||||||
|
}
|
||||||
|
|
||||||
|
if selection.StreamOnly {
|
||||||
|
lines = append(lines, lastIndent+"Stream servers only")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(selection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(selection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(selection.Names) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Names: "+commaJoin(selection.Names))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(selection.Numbers) > 0 {
|
||||||
|
numbersString := make([]string, len(selection.Numbers))
|
||||||
|
for i, numberUint16 := range selection.Numbers {
|
||||||
|
numbersString[i] = fmt.Sprint(numberUint16)
|
||||||
|
}
|
||||||
|
lines = append(lines, lastIndent+"Numbers: "+commaJoin(numbersString))
|
||||||
|
}
|
||||||
|
|
||||||
|
if selection.VPN == constants.OpenVPN {
|
||||||
|
lines = append(lines, selection.OpenVPN.lines()...)
|
||||||
|
} else { // wireguard
|
||||||
|
lines = append(lines, selection.Wireguard.lines()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpenVPNSelection struct {
|
||||||
|
ConfFile string `json:"conf_file"` // Custom configuration file path
|
||||||
|
TCP bool `json:"tcp"` // UDP if TCP is false
|
||||||
|
CustomPort uint16 `json:"custom_port"` // HideMyAss, Mullvad, PIA, ProtonVPN, WeVPN, Windscribe
|
||||||
|
EncPreset string `json:"encryption_preset"` // PIA - needed to get the port number
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPNSelection) lines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+"OpenVPN selection:")
|
||||||
|
|
||||||
|
if settings.ConfFile != "" {
|
||||||
|
lines = append(lines, indent+lastIndent+"Custom configuration file: "+settings.ConfFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Protocol: "+protoToString(settings.TCP))
|
||||||
|
|
||||||
|
if settings.CustomPort != 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Custom port: "+fmt.Sprint(settings.CustomPort))
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.EncPreset != "" {
|
||||||
|
lines = append(lines, indent+lastIndent+"PIA encryption preset: "+settings.EncPreset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPNSelection) readProtocolOnly(r reader) (err error) {
|
||||||
|
settings.TCP, err = readOpenVPNProtocol(r)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPNSelection) readProtocolAndPort(r reader) (err error) {
|
||||||
|
settings.TCP, err = readOpenVPNProtocol(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.CustomPort, err = readOpenVPNCustomPort(r, openvpnPortValidation{allAllowed: true})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type WireguardSelection struct {
|
||||||
|
// EndpointPort is a the server port to use for the VPN server.
|
||||||
|
// It is optional for Wireguard VPN providers IVPN, Mullvad
|
||||||
|
// and Windscribe, and compulsory for the others
|
||||||
|
EndpointPort uint16 `json:"port,omitempty"`
|
||||||
|
// PublicKey is the server public key.
|
||||||
|
// It is only used with VPN providers generating Wireguard
|
||||||
|
// configurations specific to each server and user.
|
||||||
|
PublicKey string `json:"publickey,omitempty"`
|
||||||
|
// EndpointIP is the server endpoint IP address.
|
||||||
|
// It is only used with VPN providers generating Wireguard
|
||||||
|
// configurations specific to each server and user.
|
||||||
|
EndpointIP net.IP `json:"endpoint_ip,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *WireguardSelection) lines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+"Wireguard selection:")
|
||||||
|
|
||||||
|
if settings.PublicKey != "" {
|
||||||
|
lines = append(lines, indent+lastIndent+"Public key: "+settings.PublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.EndpointIP != nil {
|
||||||
|
endpoint := settings.EndpointIP.String() + ":" + fmt.Sprint(settings.EndpointPort)
|
||||||
|
lines = append(lines, indent+lastIndent+"Server endpoint: "+endpoint)
|
||||||
|
} else if settings.EndpointPort != 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Custom port: "+fmt.Sprint(settings.EndpointPort))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
}
|
}
|
||||||
|
|
||||||
// PortForwarding contains settings for port forwarding.
|
// PortForwarding contains settings for port forwarding.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -32,17 +33,17 @@ func (settings *ControlServer) lines() (lines []string) {
|
|||||||
func (settings *ControlServer) read(r reader) (err error) {
|
func (settings *ControlServer) read(r reader) (err error) {
|
||||||
settings.Log, err = r.env.OnOff("HTTP_CONTROL_SERVER_LOG", params.Default("on"))
|
settings.Log, err = r.env.OnOff("HTTP_CONTROL_SERVER_LOG", params.Default("on"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable HTTP_CONTROL_SERVER_LOG: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var warning string
|
var warning string
|
||||||
settings.Port, warning, err = r.env.ListeningPort(
|
settings.Port, warning, err = r.env.ListeningPort(
|
||||||
"HTTP_CONTROL_SERVER_PORT", params.Default("8000"))
|
"HTTP_CONTROL_SERVER_PORT", params.Default("8000"))
|
||||||
if len(warning) > 0 {
|
if len(warning) > 0 {
|
||||||
r.logger.Warn(warning)
|
r.warner.Warn(warning)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable HTTP_CONTROL_SERVER_PORT: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/golibs/logging"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/golibs/os"
|
|
||||||
"github.com/qdm12/golibs/params"
|
"github.com/qdm12/golibs/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Settings contains all settings for the program to run.
|
// Settings contains all settings for the program to run.
|
||||||
type Settings struct {
|
type Settings struct {
|
||||||
OpenVPN OpenVPN
|
VPN VPN
|
||||||
System System
|
System System
|
||||||
DNS DNS
|
DNS DNS
|
||||||
Firewall Firewall
|
Firewall Firewall
|
||||||
@@ -20,6 +21,8 @@ type Settings struct {
|
|||||||
PublicIP PublicIP
|
PublicIP PublicIP
|
||||||
VersionInformation bool
|
VersionInformation bool
|
||||||
ControlServer ControlServer
|
ControlServer ControlServer
|
||||||
|
Health Health
|
||||||
|
Log Log
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *Settings) String() string {
|
func (settings *Settings) String() string {
|
||||||
@@ -28,12 +31,14 @@ func (settings *Settings) String() string {
|
|||||||
|
|
||||||
func (settings *Settings) lines() (lines []string) {
|
func (settings *Settings) lines() (lines []string) {
|
||||||
lines = append(lines, "Settings summary below:")
|
lines = append(lines, "Settings summary below:")
|
||||||
lines = append(lines, settings.OpenVPN.lines()...)
|
lines = append(lines, settings.VPN.lines()...)
|
||||||
lines = append(lines, settings.DNS.lines()...)
|
lines = append(lines, settings.DNS.lines()...)
|
||||||
lines = append(lines, settings.Firewall.lines()...)
|
lines = append(lines, settings.Firewall.lines()...)
|
||||||
|
lines = append(lines, settings.Log.lines()...)
|
||||||
lines = append(lines, settings.System.lines()...)
|
lines = append(lines, settings.System.lines()...)
|
||||||
lines = append(lines, settings.HTTPProxy.lines()...)
|
lines = append(lines, settings.HTTPProxy.lines()...)
|
||||||
lines = append(lines, settings.ShadowSocks.lines()...)
|
lines = append(lines, settings.ShadowSocks.lines()...)
|
||||||
|
lines = append(lines, settings.Health.lines()...)
|
||||||
lines = append(lines, settings.ControlServer.lines()...)
|
lines = append(lines, settings.ControlServer.lines()...)
|
||||||
lines = append(lines, settings.Updater.lines()...)
|
lines = append(lines, settings.Updater.lines()...)
|
||||||
lines = append(lines, settings.PublicIP.lines()...)
|
lines = append(lines, settings.PublicIP.lines()...)
|
||||||
@@ -43,46 +48,61 @@ func (settings *Settings) lines() (lines []string) {
|
|||||||
return lines
|
return lines
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrVPN = errors.New("cannot read VPN 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")
|
||||||
|
ErrLog = errors.New("cannot read log settings")
|
||||||
|
)
|
||||||
|
|
||||||
// Read obtains all configuration options for the program and returns an error as soon
|
// Read obtains all configuration options for the program and returns an error as soon
|
||||||
// as an error is encountered reading them.
|
// as an error is encountered reading them.
|
||||||
func (settings *Settings) Read(env params.Env, os os.OS, logger logging.Logger) (err error) {
|
func (settings *Settings) Read(env params.Interface, servers models.AllServers,
|
||||||
r := newReader(env, os, logger)
|
warner Warner) (err error) {
|
||||||
|
r := newReader(env, servers, warner)
|
||||||
|
|
||||||
settings.VersionInformation, err = r.env.OnOff("VERSION_INFORMATION", params.Default("on"))
|
settings.VersionInformation, err = r.env.OnOff("VERSION_INFORMATION", params.Default("on"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable VERSION_INFORMATION: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := settings.OpenVPN.read(r); err != nil {
|
if err := settings.VPN.read(r); err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", ErrVPN, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := settings.System.read(r); err != nil {
|
if err := settings.System.read(r); err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", ErrSystem, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := settings.DNS.read(r); err != nil {
|
if err := settings.DNS.read(r); err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", ErrDNS, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := settings.Firewall.read(r); err != nil {
|
if err := settings.Firewall.read(r); err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", ErrFirewall, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := settings.HTTPProxy.read(r); err != nil {
|
if err := settings.HTTPProxy.read(r); err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", ErrHTTPProxy, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := settings.ShadowSocks.read(r); err != nil {
|
if err := settings.ShadowSocks.read(r); err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", ErrShadowsocks, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := settings.ControlServer.read(r); err != nil {
|
if err := settings.ControlServer.read(r); err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", ErrControlServer, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := settings.Updater.read(r); err != nil {
|
if err := settings.Updater.read(r); err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", ErrUpdater, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ip := settings.DNS.PlaintextAddress; ip != nil {
|
if ip := settings.DNS.PlaintextAddress; ip != nil {
|
||||||
@@ -90,7 +110,15 @@ func (settings *Settings) Read(env params.Env, os os.OS, logger logging.Logger)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := settings.PublicIP.read(r); err != nil {
|
if err := settings.PublicIP.read(r); err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", ErrPublicIP, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.Health.read(r); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrHealth, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.Log.read(r.env); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrLog, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -16,25 +16,44 @@ func Test_Settings_lines(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
"default settings": {
|
"default settings": {
|
||||||
settings: Settings{
|
settings: Settings{
|
||||||
OpenVPN: OpenVPN{
|
VPN: VPN{
|
||||||
|
Type: constants.OpenVPN,
|
||||||
Provider: Provider{
|
Provider: Provider{
|
||||||
Name: constants.Mullvad,
|
Name: constants.Mullvad,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
VPN: constants.OpenVPN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OpenVPN: OpenVPN{
|
||||||
|
Version: constants.Openvpn25,
|
||||||
|
Interface: "tun",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"Settings summary below:",
|
"Settings summary below:",
|
||||||
|
"|--VPN:",
|
||||||
|
" |--Type: openvpn",
|
||||||
" |--OpenVPN:",
|
" |--OpenVPN:",
|
||||||
|
" |--Version: 2.5",
|
||||||
" |--Verbosity level: 0",
|
" |--Verbosity level: 0",
|
||||||
" |--Provider:",
|
" |--Network interface: tun",
|
||||||
" |--Mullvad settings:",
|
" |--Mullvad settings:",
|
||||||
" |--Network protocol: ",
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
"|--DNS:",
|
"|--DNS:",
|
||||||
"|--Firewall: disabled ⚠️",
|
"|--Firewall: disabled ⚠️",
|
||||||
|
"|--Log:",
|
||||||
|
" |--Level: DEBUG",
|
||||||
"|--System:",
|
"|--System:",
|
||||||
" |--Process user ID: 0",
|
" |--Process user ID: 0",
|
||||||
" |--Process group ID: 0",
|
" |--Process group ID: 0",
|
||||||
" |--Timezone: NOT SET ⚠️ - it can cause time related issues",
|
" |--Timezone: NOT SET ⚠️ - it can cause time related issues",
|
||||||
|
"|--Health:",
|
||||||
|
" |--Server address: ",
|
||||||
|
" |--Address to ping: ",
|
||||||
|
" |--VPN:",
|
||||||
|
" |--Initial duration: 0s",
|
||||||
"|--HTTP control server:",
|
"|--HTTP control server:",
|
||||||
" |--Listening port: 0",
|
" |--Listening port: 0",
|
||||||
"|--Public IP getter: disabled",
|
"|--Public IP getter: disabled",
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/golibs/params"
|
"github.com/qdm12/golibs/params"
|
||||||
|
"github.com/qdm12/ss-server/pkg/tcpudp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ShadowSocks contains settings to configure the Shadowsocks server.
|
// ShadowSocks contains settings to configure the Shadowsocks server.
|
||||||
type ShadowSocks struct {
|
type ShadowSocks struct {
|
||||||
Method string
|
|
||||||
Password string
|
|
||||||
Port uint16
|
|
||||||
Enabled bool
|
Enabled bool
|
||||||
Log bool
|
tcpudp.Settings
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *ShadowSocks) String() string {
|
func (settings *ShadowSocks) String() string {
|
||||||
@@ -27,12 +25,12 @@ func (settings *ShadowSocks) lines() (lines []string) {
|
|||||||
|
|
||||||
lines = append(lines, lastIndent+"Shadowsocks server:")
|
lines = append(lines, lastIndent+"Shadowsocks server:")
|
||||||
|
|
||||||
lines = append(lines, indent+lastIndent+"Listening port: "+strconv.Itoa(int(settings.Port)))
|
lines = append(lines, indent+lastIndent+"Listening address: "+settings.Address)
|
||||||
|
|
||||||
lines = append(lines, indent+lastIndent+"Method: "+settings.Method)
|
lines = append(lines, indent+lastIndent+"Cipher: "+settings.CipherName)
|
||||||
|
|
||||||
if settings.Log {
|
if settings.LogAddresses {
|
||||||
lines = append(lines, indent+lastIndent+"Logging: enabled")
|
lines = append(lines, indent+lastIndent+"Log addresses: enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines
|
return lines
|
||||||
@@ -40,29 +38,31 @@ func (settings *ShadowSocks) lines() (lines []string) {
|
|||||||
|
|
||||||
func (settings *ShadowSocks) read(r reader) (err error) {
|
func (settings *ShadowSocks) read(r reader) (err error) {
|
||||||
settings.Enabled, err = r.env.OnOff("SHADOWSOCKS", params.Default("off"))
|
settings.Enabled, err = r.env.OnOff("SHADOWSOCKS", params.Default("off"))
|
||||||
if err != nil || !settings.Enabled {
|
if !settings.Enabled {
|
||||||
return err
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("environment variable SHADOWSOCKS: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Password, err = r.getFromEnvOrSecretFile("SHADOWSOCKS_PASSWORD", false, nil)
|
settings.Password, err = r.getFromEnvOrSecretFile("SHADOWSOCKS_PASSWORD", settings.Enabled, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Log, err = r.env.OnOff("SHADOWSOCKS_LOG", params.Default("off"))
|
settings.LogAddresses, err = r.env.OnOff("SHADOWSOCKS_LOG", params.Default("off"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable SHADOWSOCKS_LOG: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Method, err = r.env.Get("SHADOWSOCKS_METHOD", params.Default("chacha20-ietf-poly1305"))
|
settings.CipherName, err = r.env.Get("SHADOWSOCKS_CIPHER", params.Default("chacha20-ietf-poly1305"),
|
||||||
|
params.RetroKeys([]string{"SHADOWSOCKS_METHOD"}, r.onRetroActive))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable SHADOWSOCKS_CIPHER (or SHADOWSOCKS_METHOD): %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var warning string
|
warning, err := settings.getAddress(r.env)
|
||||||
settings.Port, warning, err = r.env.ListeningPort("SHADOWSOCKS_PORT", params.Default("8388"))
|
if warning != "" {
|
||||||
if len(warning) > 0 {
|
r.warner.Warn(warning)
|
||||||
r.logger.Warn(warning)
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -70,3 +70,40 @@ func (settings *ShadowSocks) read(r reader) (err error) {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (settings *ShadowSocks) getAddress(env params.Interface) (
|
||||||
|
warning string, err error) {
|
||||||
|
address, err := env.Get("SHADOWSOCKS_LISTENING_ADDRESS")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("environment variable SHADOWSOCKS_LISTENING_ADDRESS: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if address != "" {
|
||||||
|
address, warning, err := env.ListeningAddress("SHADOWSOCKS_LISTENING_ADDRESS")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("environment variable SHADOWSOCKS_LISTENING_ADDRESS: %w", err)
|
||||||
|
}
|
||||||
|
settings.Address = address
|
||||||
|
return warning, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retro-compatibility
|
||||||
|
const retroWarning = "You are using the old environment variable " +
|
||||||
|
"SHADOWSOCKS_PORT, please consider using " +
|
||||||
|
"SHADOWSOCKS_LISTENING_ADDRESS instead"
|
||||||
|
portStr, err := env.Get("SHADOWSOCKS_PORT")
|
||||||
|
if err != nil {
|
||||||
|
return retroWarning, fmt.Errorf("environment variable SHADOWSOCKS_PORT: %w", err)
|
||||||
|
} else if portStr != "" {
|
||||||
|
port, _, err := env.ListeningPort("SHADOWSOCKS_PORT")
|
||||||
|
if err != nil {
|
||||||
|
return retroWarning, fmt.Errorf("environment variable SHADOWSOCKS_PORT: %w", err)
|
||||||
|
}
|
||||||
|
settings.Address = ":" + fmt.Sprint(port)
|
||||||
|
return retroWarning, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default value
|
||||||
|
settings.Address = ":8388"
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,34 +1,102 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (settings *Provider) surfsharkLines() (lines []string) {
|
|
||||||
if len(settings.ServerSelection.Regions) > 0 {
|
|
||||||
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines
|
|
||||||
}
|
|
||||||
|
|
||||||
func (settings *Provider) readSurfshark(r reader) (err error) {
|
func (settings *Provider) readSurfshark(r reader) (err error) {
|
||||||
settings.Name = constants.Surfshark
|
settings.Name = constants.Surfshark
|
||||||
|
servers := r.servers.GetSurfshark()
|
||||||
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.SurfsharkRegionChoices())
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.SurfsharkCountryChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.SurfsharkCityChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
|
||||||
|
constants.SurfsharkHostnameChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
regionChoices := constants.SurfsharkRegionChoices(servers)
|
||||||
|
regionChoices = append(regionChoices, constants.SurfsharkRetroLocChoices(servers)...)
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", regionChoices)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable REGION: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retro compatibility
|
||||||
|
// TODO remove in v4
|
||||||
|
settings.ServerSelection = surfsharkRetroRegion(settings.ServerSelection)
|
||||||
|
|
||||||
|
settings.ServerSelection.MultiHopOnly, err = r.env.YesNo("MULTIHOP_ONLY", params.Default("no"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable MULTIHOP_ONLY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings.ServerSelection.OpenVPN.readProtocolOnly(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func surfsharkRetroRegion(selection ServerSelection) (
|
||||||
|
updatedSelection ServerSelection) {
|
||||||
|
locationData := constants.SurfsharkLocationData()
|
||||||
|
|
||||||
|
retroToLocation := make(map[string]models.SurfsharkLocationData, len(locationData))
|
||||||
|
for _, data := range locationData {
|
||||||
|
if data.RetroLoc == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
retroToLocation[strings.ToLower(data.RetroLoc)] = data
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, region := range selection.Regions {
|
||||||
|
location, ok := retroToLocation[region]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
selection.Regions[i] = strings.ToLower(location.Region)
|
||||||
|
selection.Countries = append(selection.Countries, strings.ToLower(location.Country))
|
||||||
|
selection.Cities = append(selection.Cities, strings.ToLower(location.City)) // even empty string
|
||||||
|
selection.Hostnames = append(selection.Hostnames, location.Hostname)
|
||||||
|
}
|
||||||
|
|
||||||
|
selection.Regions = dedupSlice(selection.Regions)
|
||||||
|
selection.Countries = dedupSlice(selection.Countries)
|
||||||
|
selection.Cities = dedupSlice(selection.Cities)
|
||||||
|
selection.Hostnames = dedupSlice(selection.Hostnames)
|
||||||
|
|
||||||
|
return selection
|
||||||
|
}
|
||||||
|
|
||||||
|
func dedupSlice(slice []string) (deduped []string) {
|
||||||
|
if slice == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deduped = make([]string, 0, len(slice))
|
||||||
|
seen := make(map[string]struct{}, len(slice))
|
||||||
|
for _, s := range slice {
|
||||||
|
if _, ok := seen[s]; !ok {
|
||||||
|
seen[s] = struct{}{}
|
||||||
|
deduped = append(deduped, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return deduped
|
||||||
|
}
|
||||||
|
|||||||
305
internal/configuration/surfshark_test.go
Normal file
305
internal/configuration/surfshark_test.go
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
|
"github.com/qdm12/golibs/params/mock_params"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Provider_readSurfshark(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var errDummy = errors.New("dummy test error")
|
||||||
|
|
||||||
|
type stringCall struct {
|
||||||
|
call bool
|
||||||
|
value string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type boolCall struct {
|
||||||
|
call bool
|
||||||
|
value bool
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliceStringCall struct {
|
||||||
|
call bool
|
||||||
|
values []string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
targetIP stringCall
|
||||||
|
countries sliceStringCall
|
||||||
|
cities sliceStringCall
|
||||||
|
hostnames sliceStringCall
|
||||||
|
regions sliceStringCall
|
||||||
|
multiHop boolCall
|
||||||
|
protocol stringCall
|
||||||
|
settings Provider
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
"target IP error": {
|
||||||
|
targetIP: stringCall{call: true, value: "something", err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Surfshark,
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable OPENVPN_TARGET_IP: dummy test error"),
|
||||||
|
},
|
||||||
|
"countries error": {
|
||||||
|
targetIP: stringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Surfshark,
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable COUNTRY: dummy test error"),
|
||||||
|
},
|
||||||
|
"cities error": {
|
||||||
|
targetIP: stringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Surfshark,
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable CITY: dummy test error"),
|
||||||
|
},
|
||||||
|
"hostnames error": {
|
||||||
|
targetIP: stringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Surfshark,
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable SERVER_HOSTNAME: dummy test error"),
|
||||||
|
},
|
||||||
|
"regions error": {
|
||||||
|
targetIP: stringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true},
|
||||||
|
regions: sliceStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Surfshark,
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable REGION: dummy test error"),
|
||||||
|
},
|
||||||
|
"multi hop error": {
|
||||||
|
targetIP: stringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true},
|
||||||
|
regions: sliceStringCall{call: true},
|
||||||
|
multiHop: boolCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Surfshark,
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable MULTIHOP_ONLY: dummy test error"),
|
||||||
|
},
|
||||||
|
"openvpn protocol error": {
|
||||||
|
targetIP: stringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true},
|
||||||
|
regions: sliceStringCall{call: true},
|
||||||
|
multiHop: boolCall{call: true},
|
||||||
|
protocol: stringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Surfshark,
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable OPENVPN_PROTOCOL: dummy test error"),
|
||||||
|
},
|
||||||
|
"default settings": {
|
||||||
|
targetIP: stringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true},
|
||||||
|
regions: sliceStringCall{call: true},
|
||||||
|
multiHop: boolCall{call: true},
|
||||||
|
protocol: stringCall{call: true},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Surfshark,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"set settings": {
|
||||||
|
targetIP: stringCall{call: true, value: "1.2.3.4"},
|
||||||
|
countries: sliceStringCall{call: true, values: []string{"A", "B"}},
|
||||||
|
cities: sliceStringCall{call: true, values: []string{"C", "D"}},
|
||||||
|
regions: sliceStringCall{call: true, values: []string{
|
||||||
|
"E", "F", "netherlands amsterdam",
|
||||||
|
}}, // Netherlands Amsterdam is for retro compatibility test
|
||||||
|
multiHop: boolCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true, values: []string{"E", "F"}},
|
||||||
|
protocol: stringCall{call: true, value: constants.TCP},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Surfshark,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
OpenVPN: OpenVPNSelection{
|
||||||
|
TCP: true,
|
||||||
|
},
|
||||||
|
TargetIP: net.IPv4(1, 2, 3, 4),
|
||||||
|
Regions: []string{"E", "F", "europe"},
|
||||||
|
Countries: []string{"A", "B", "netherlands"},
|
||||||
|
Cities: []string{"C", "D", "amsterdam"},
|
||||||
|
Hostnames: []string{"E", "F", "nl-ams.prod.surfshark.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Netherlands Amsterdam": {
|
||||||
|
targetIP: stringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
regions: sliceStringCall{call: true, values: []string{"netherlands amsterdam"}},
|
||||||
|
multiHop: boolCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true},
|
||||||
|
protocol: stringCall{call: true},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Surfshark,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Regions: []string{"europe"},
|
||||||
|
Countries: []string{"netherlands"},
|
||||||
|
Cities: []string{"amsterdam"},
|
||||||
|
Hostnames: []string{"nl-ams.prod.surfshark.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
|
||||||
|
env := mock_params.NewMockInterface(ctrl)
|
||||||
|
|
||||||
|
servers := []models.SurfsharkServer{{Hostname: "a"}}
|
||||||
|
allServers := models.AllServers{
|
||||||
|
Surfshark: models.SurfsharkServers{
|
||||||
|
Servers: servers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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.SurfsharkCountryChoices(servers)).
|
||||||
|
Return(testCase.countries.values, testCase.countries.err)
|
||||||
|
}
|
||||||
|
if testCase.cities.call {
|
||||||
|
env.EXPECT().CSVInside("CITY", constants.SurfsharkCityChoices(servers)).
|
||||||
|
Return(testCase.cities.values, testCase.cities.err)
|
||||||
|
}
|
||||||
|
if testCase.hostnames.call {
|
||||||
|
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.SurfsharkHostnameChoices(servers)).
|
||||||
|
Return(testCase.hostnames.values, testCase.hostnames.err)
|
||||||
|
}
|
||||||
|
if testCase.regions.call {
|
||||||
|
regionChoices := constants.SurfsharkRegionChoices(servers)
|
||||||
|
regionChoices = append(regionChoices, constants.SurfsharkRetroLocChoices(servers)...)
|
||||||
|
env.EXPECT().CSVInside("REGION", regionChoices).
|
||||||
|
Return(testCase.regions.values, testCase.regions.err)
|
||||||
|
}
|
||||||
|
if testCase.multiHop.call {
|
||||||
|
env.EXPECT().YesNo("MULTIHOP_ONLY", gomock.Any()).
|
||||||
|
Return(testCase.multiHop.value, testCase.multiHop.err)
|
||||||
|
}
|
||||||
|
if testCase.protocol.call {
|
||||||
|
env.EXPECT().Inside("OPENVPN_PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
|
||||||
|
Return(testCase.protocol.value, testCase.protocol.err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := reader{
|
||||||
|
servers: allServers,
|
||||||
|
env: env,
|
||||||
|
}
|
||||||
|
|
||||||
|
var settings Provider
|
||||||
|
err := settings.readSurfshark(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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_surfsharkRetroRegion(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
original ServerSelection
|
||||||
|
modified ServerSelection
|
||||||
|
}{
|
||||||
|
"empty": {},
|
||||||
|
"1 retro region": {
|
||||||
|
original: ServerSelection{
|
||||||
|
Regions: []string{"australia adelaide"},
|
||||||
|
},
|
||||||
|
modified: ServerSelection{
|
||||||
|
Regions: []string{"asia pacific"},
|
||||||
|
Countries: []string{"australia"},
|
||||||
|
Cities: []string{"adelaide"},
|
||||||
|
Hostnames: []string{"au-adl.prod.surfshark.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"2 overlapping retro regions": {
|
||||||
|
original: ServerSelection{
|
||||||
|
Regions: []string{"australia adelaide", "australia melbourne"},
|
||||||
|
},
|
||||||
|
modified: ServerSelection{
|
||||||
|
Regions: []string{"asia pacific"},
|
||||||
|
Countries: []string{"australia"},
|
||||||
|
Cities: []string{"adelaide", "melbourne"},
|
||||||
|
Hostnames: []string{"au-adl.prod.surfshark.com", "au-mel.prod.surfshark.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"2 distinct retro regions": {
|
||||||
|
original: ServerSelection{
|
||||||
|
Regions: []string{"australia adelaide", "netherlands amsterdam"},
|
||||||
|
},
|
||||||
|
modified: ServerSelection{
|
||||||
|
Regions: []string{"asia pacific", "europe"},
|
||||||
|
Countries: []string{"australia", "netherlands"},
|
||||||
|
Cities: []string{"adelaide", "amsterdam"},
|
||||||
|
Hostnames: []string{"au-adl.prod.surfshark.com", "nl-ams.prod.surfshark.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"retro region with existing region": {
|
||||||
|
// note TestRegion will be ignored in the filters downstream
|
||||||
|
original: ServerSelection{
|
||||||
|
Regions: []string{"TestRegion", "australia adelaide"},
|
||||||
|
},
|
||||||
|
modified: ServerSelection{
|
||||||
|
Regions: []string{"TestRegion", "asia pacific"},
|
||||||
|
Countries: []string{"australia"},
|
||||||
|
Cities: []string{"adelaide"},
|
||||||
|
Hostnames: []string{"au-adl.prod.surfshark.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
selection := surfsharkRetroRegion(testCase.original)
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.modified, selection)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -32,21 +33,22 @@ func (settings *System) lines() (lines []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (settings *System) read(r reader) (err error) {
|
func (settings *System) read(r reader) (err error) {
|
||||||
settings.PUID, err = r.env.IntRange("PUID", 0, 65535, params.Default("1000"),
|
const maxID = 65535
|
||||||
|
settings.PUID, err = r.env.IntRange("PUID", 0, maxID, params.Default("1000"),
|
||||||
params.RetroKeys([]string{"UID"}, r.onRetroActive))
|
params.RetroKeys([]string{"UID"}, r.onRetroActive))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable PUID (or UID): %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.PGID, err = r.env.IntRange("PGID", 0, 65535, params.Default("1000"),
|
settings.PGID, err = r.env.IntRange("PGID", 0, maxID, params.Default("1000"),
|
||||||
params.RetroKeys([]string{"GID"}, r.onRetroActive))
|
params.RetroKeys([]string{"GID"}, r.onRetroActive))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable PGID (or GID): %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Timezone, err = r.env.Get("TZ")
|
settings.Timezone, err = r.env.Get("TZ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable TZ: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,52 +1,35 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"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) {
|
func (settings *Provider) readTorguard(r reader) (err error) {
|
||||||
settings.Name = constants.Torguard
|
settings.Name = constants.Torguard
|
||||||
|
servers := r.servers.GetTorguard()
|
||||||
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.TorguardCountryChoices())
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.TorguardCountryChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.TorguardCityChoices())
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.TorguardCityChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.TorguardHostnamesChoices())
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
|
||||||
|
constants.TorguardHostnameChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ package configuration
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
unbound "github.com/qdm12/dns/pkg/unbound"
|
"github.com/qdm12/dns/pkg/provider"
|
||||||
"github.com/qdm12/golibs/params"
|
"github.com/qdm12/golibs/params"
|
||||||
|
"inet.af/netaddr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (settings *DNS) readUnbound(r reader) (err error) {
|
func (settings *DNS) readUnbound(r reader) (err error) {
|
||||||
@@ -19,51 +19,37 @@ func (settings *DNS) readUnbound(r reader) (err error) {
|
|||||||
|
|
||||||
settings.Unbound.Caching, err = r.env.OnOff("DOT_CACHING", params.Default("on"))
|
settings.Unbound.Caching, err = r.env.OnOff("DOT_CACHING", params.Default("on"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable DOT_CACHING: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Unbound.IPv4 = true
|
settings.Unbound.IPv4 = true
|
||||||
|
|
||||||
settings.Unbound.IPv6, err = r.env.OnOff("DOT_IPV6", params.Default("off"))
|
settings.Unbound.IPv6, err = r.env.OnOff("DOT_IPV6", params.Default("off"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable DOT_IPV6: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
verbosityLevel, err := r.env.IntRange("DOT_VERBOSITY", 0, 5, params.Default("1"))
|
verbosityLevel, err := r.env.IntRange("DOT_VERBOSITY", 0, 5, params.Default("1")) //nolint:gomnd
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable DOT_VERBOSITY: %w", err)
|
||||||
}
|
}
|
||||||
settings.Unbound.VerbosityLevel = uint8(verbosityLevel)
|
settings.Unbound.VerbosityLevel = uint8(verbosityLevel)
|
||||||
|
|
||||||
verbosityDetailsLevel, err := r.env.IntRange("DOT_VERBOSITY_DETAILS", 0, 4, params.Default("0"))
|
verbosityDetailsLevel, err := r.env.IntRange("DOT_VERBOSITY_DETAILS", 0, 4, params.Default("0")) //nolint:gomnd
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable DOT_VERBOSITY_DETAILS: %w", err)
|
||||||
}
|
}
|
||||||
settings.Unbound.VerbosityDetailsLevel = uint8(verbosityDetailsLevel)
|
settings.Unbound.VerbosityDetailsLevel = uint8(verbosityDetailsLevel)
|
||||||
|
|
||||||
validationLogLevel, err := r.env.IntRange("DOT_VALIDATION_LOGLEVEL", 0, 2, params.Default("0"))
|
validationLogLevel, err := r.env.IntRange("DOT_VALIDATION_LOGLEVEL", 0, 2, params.Default("0")) //nolint:gomnd
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable DOT_VALIDATION_LOGLEVEL: %w", err)
|
||||||
}
|
}
|
||||||
settings.Unbound.ValidationLogLevel = uint8(validationLogLevel)
|
settings.Unbound.ValidationLogLevel = uint8(validationLogLevel)
|
||||||
|
|
||||||
if err := settings.readUnboundPrivateAddresses(r.env); err != nil {
|
settings.Unbound.AccessControl.Allowed = []netaddr.IPPrefix{
|
||||||
return err
|
netaddr.IPPrefixFrom(netaddr.IPv4(0, 0, 0, 0), 0),
|
||||||
}
|
netaddr.IPPrefixFrom(netaddr.IPv6Raw([16]byte{}), 0),
|
||||||
|
|
||||||
if err := settings.readUnboundUnblockedHostnames(r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.Unbound.AccessControl.Allowed = []net.IPNet{
|
|
||||||
{
|
|
||||||
IP: net.IPv4zero,
|
|
||||||
Mask: net.IPv4Mask(0, 0, 0, 0),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
IP: net.IPv6zero,
|
|
||||||
Mask: net.IPMask{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -73,61 +59,21 @@ var (
|
|||||||
ErrInvalidDNSOverTLSProvider = errors.New("invalid DNS over TLS provider")
|
ErrInvalidDNSOverTLSProvider = errors.New("invalid DNS over TLS provider")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (settings *DNS) readUnboundProviders(env params.Env) (err error) {
|
func (settings *DNS) readUnboundProviders(env params.Interface) (err error) {
|
||||||
s, err := env.Get("DOT_PROVIDERS", params.Default("cloudflare"))
|
s, err := env.Get("DOT_PROVIDERS", params.Default("cloudflare"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable DOT_PROVIDERS: %w", err)
|
||||||
}
|
}
|
||||||
for _, provider := range strings.Split(s, ",") {
|
for _, field := range strings.Split(s, ",") {
|
||||||
_, ok := unbound.GetProviderData(provider)
|
dnsProvider, err := provider.Parse(field)
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("%w: %s", ErrInvalidDNSOverTLSProvider, provider)
|
|
||||||
}
|
|
||||||
settings.Unbound.Providers = append(settings.Unbound.Providers, provider)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidPrivateAddress = errors.New("private address is not a valid IP or CIDR range")
|
|
||||||
)
|
|
||||||
|
|
||||||
func (settings *DNS) readUnboundPrivateAddresses(env params.Env) (err error) {
|
|
||||||
privateAddresses, err := env.CSV("DOT_PRIVATE_ADDRESS")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", ErrInvalidDNSOverTLSProvider, err)
|
||||||
} else if len(privateAddresses) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
for _, address := range privateAddresses {
|
settings.Unbound.Providers = append(settings.Unbound.Providers, dnsProvider)
|
||||||
ip := net.ParseIP(address)
|
|
||||||
_, _, err := net.ParseCIDR(address)
|
|
||||||
if ip == nil && err != nil {
|
|
||||||
return fmt.Errorf("%w: %s", ErrInvalidPrivateAddress, address)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
settings.Unbound.BlockedIPs = append(
|
|
||||||
settings.Unbound.BlockedIPs, privateAddresses...)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidHostname = errors.New("invalid hostname")
|
ErrInvalidHostname = errors.New("invalid hostname")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (settings *DNS) readUnboundUnblockedHostnames(r reader) (err error) {
|
|
||||||
hostnames, err := r.env.CSV("UNBLOCK")
|
|
||||||
if err != nil {
|
|
||||||
return 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.Unbound.AllowedHostnames = append(
|
|
||||||
settings.Unbound.AllowedHostnames, hostnames...)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
80
internal/configuration/unbound_test.go
Normal file
80
internal/configuration/unbound_test.go
Normal 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.NewMockInterface(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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -11,10 +12,14 @@ type Updater struct {
|
|||||||
Period time.Duration `json:"period"`
|
Period time.Duration `json:"period"`
|
||||||
DNSAddress string `json:"dns_address"`
|
DNSAddress string `json:"dns_address"`
|
||||||
Cyberghost bool `json:"cyberghost"`
|
Cyberghost bool `json:"cyberghost"`
|
||||||
|
Expressvpn bool `json:"expressvpn"`
|
||||||
Fastestvpn bool `json:"fastestvpn"`
|
Fastestvpn bool `json:"fastestvpn"`
|
||||||
HideMyAss bool `json:"hidemyass"`
|
HideMyAss bool `json:"hidemyass"`
|
||||||
|
Ipvanish bool `json:"ipvanish"`
|
||||||
|
Ivpn bool `json:"ivpn"`
|
||||||
Mullvad bool `json:"mullvad"`
|
Mullvad bool `json:"mullvad"`
|
||||||
Nordvpn bool `json:"nordvpn"`
|
Nordvpn bool `json:"nordvpn"`
|
||||||
|
Perfectprivacy bool `json:"perfectprivacy"`
|
||||||
PIA bool `json:"pia"`
|
PIA bool `json:"pia"`
|
||||||
Privado bool `json:"privado"`
|
Privado bool `json:"privado"`
|
||||||
Privatevpn bool `json:"privatevpn"`
|
Privatevpn bool `json:"privatevpn"`
|
||||||
@@ -22,10 +27,11 @@ type Updater struct {
|
|||||||
Purevpn bool `json:"purevpn"`
|
Purevpn bool `json:"purevpn"`
|
||||||
Surfshark bool `json:"surfshark"`
|
Surfshark bool `json:"surfshark"`
|
||||||
Torguard bool `json:"torguard"`
|
Torguard bool `json:"torguard"`
|
||||||
|
VPNUnlimited bool `json:"vpnunlimited"`
|
||||||
Vyprvpn bool `json:"vyprvpn"`
|
Vyprvpn bool `json:"vyprvpn"`
|
||||||
|
Wevpn bool `json:"wevpn"`
|
||||||
Windscribe bool `json:"windscribe"`
|
Windscribe bool `json:"windscribe"`
|
||||||
// The two below should be used in CLI mode only
|
// The two below should be used in CLI mode only
|
||||||
Stdout bool `json:"-"` // in order to update constants file (maintainer side)
|
|
||||||
CLI bool `json:"-"`
|
CLI bool `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,11 +51,14 @@ func (settings *Updater) lines() (lines []string) {
|
|||||||
return lines
|
return lines
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *Updater) read(r reader) (err error) {
|
func (settings *Updater) EnableAll() {
|
||||||
settings.Cyberghost = true
|
settings.Cyberghost = true
|
||||||
settings.HideMyAss = true
|
settings.HideMyAss = true
|
||||||
|
settings.Ipvanish = true
|
||||||
|
settings.Ivpn = true
|
||||||
settings.Mullvad = true
|
settings.Mullvad = true
|
||||||
settings.Nordvpn = true
|
settings.Nordvpn = true
|
||||||
|
settings.Perfectprivacy = true
|
||||||
settings.Privado = true
|
settings.Privado = true
|
||||||
settings.PIA = true
|
settings.PIA = true
|
||||||
settings.Privado = true
|
settings.Privado = true
|
||||||
@@ -58,10 +67,14 @@ func (settings *Updater) read(r reader) (err error) {
|
|||||||
settings.Purevpn = true
|
settings.Purevpn = true
|
||||||
settings.Surfshark = true
|
settings.Surfshark = true
|
||||||
settings.Torguard = true
|
settings.Torguard = true
|
||||||
|
settings.VPNUnlimited = true
|
||||||
settings.Vyprvpn = true
|
settings.Vyprvpn = true
|
||||||
|
settings.Wevpn = true
|
||||||
settings.Windscribe = true
|
settings.Windscribe = true
|
||||||
settings.Stdout = false
|
}
|
||||||
settings.CLI = false
|
|
||||||
|
func (settings *Updater) read(r reader) (err error) {
|
||||||
|
settings.EnableAll()
|
||||||
// use cloudflare in plaintext to not be blocked by DNS over TLS by default.
|
// 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.
|
// 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
|
// TODO use custom future encrypted DNS written in Go without blocking
|
||||||
@@ -70,7 +83,7 @@ func (settings *Updater) read(r reader) (err error) {
|
|||||||
|
|
||||||
settings.Period, err = r.env.Duration("UPDATER_PERIOD", params.Default("0"))
|
settings.Period, err = r.env.Duration("UPDATER_PERIOD", params.Default("0"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable UPDATER_PERIOD: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
79
internal/configuration/vpn.go
Normal file
79
internal/configuration/vpn.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VPN struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
OpenVPN OpenVPN `json:"openvpn"`
|
||||||
|
Wireguard Wireguard `json:"wireguard"`
|
||||||
|
Provider Provider `json:"provider"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *VPN) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *VPN) lines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+"VPN:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Type: "+settings.Type)
|
||||||
|
|
||||||
|
var vpnLines []string
|
||||||
|
switch settings.Type {
|
||||||
|
case constants.OpenVPN:
|
||||||
|
vpnLines = settings.OpenVPN.lines()
|
||||||
|
case constants.Wireguard:
|
||||||
|
vpnLines = settings.Wireguard.lines()
|
||||||
|
}
|
||||||
|
for _, line := range vpnLines {
|
||||||
|
lines = append(lines, indent+line)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range settings.Provider.lines() {
|
||||||
|
lines = append(lines, indent+line)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errReadProviderSettings = errors.New("cannot read provider settings")
|
||||||
|
errReadOpenVPNSettings = errors.New("cannot read OpenVPN settings")
|
||||||
|
errReadWireguardSettings = errors.New("cannot read Wireguard settings")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *VPN) read(r reader) (err error) {
|
||||||
|
vpnType, err := r.env.Inside("VPN_TYPE",
|
||||||
|
[]string{constants.OpenVPN, constants.Wireguard},
|
||||||
|
params.Default(constants.OpenVPN))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable VPN_TYPE: %w", err)
|
||||||
|
}
|
||||||
|
settings.Type = vpnType
|
||||||
|
|
||||||
|
if err := settings.Provider.read(r, vpnType); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", errReadProviderSettings, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch settings.Type {
|
||||||
|
case constants.OpenVPN:
|
||||||
|
err = settings.OpenVPN.read(r, settings.Provider.Name)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", errReadOpenVPNSettings, err)
|
||||||
|
}
|
||||||
|
case constants.Wireguard:
|
||||||
|
err = settings.Wireguard.read(r)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", errReadWireguardSettings, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
60
internal/configuration/vpnunlimited.go
Normal file
60
internal/configuration/vpnunlimited.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) readVPNUnlimited(r reader) (err error) {
|
||||||
|
settings.Name = constants.VPNUnlimited
|
||||||
|
servers := r.servers.GetVPNUnlimited()
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.VPNUnlimitedCountryChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.VPNUnlimitedCityChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
|
||||||
|
constants.VPNUnlimitedHostnameChoices(servers))
|
||||||
|
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 settings.ServerSelection.OpenVPN.readProtocolOnly(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPN) readVPNUnlimited(r reader) (err error) {
|
||||||
|
settings.ClientKey, err = readClientKey(r)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", errClientKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ClientCrt, err = readClientCertificate(r)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", errClientCert, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,33 +1,23 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"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) {
|
func (settings *Provider) readVyprvpn(r reader) (err error) {
|
||||||
settings.Name = constants.Vyprvpn
|
settings.Name = constants.Vyprvpn
|
||||||
|
servers := r.servers.GetVyprvpn()
|
||||||
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.VyprvpnRegionChoices())
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.VyprvpnRegionChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("environment variable REGION: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
46
internal/configuration/warner_mock_test.go
Normal file
46
internal/configuration/warner_mock_test.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/qdm12/gluetun/internal/configuration (interfaces: Warner)
|
||||||
|
|
||||||
|
// Package configuration is a generated GoMock package.
|
||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockWarner is a mock of Warner interface.
|
||||||
|
type MockWarner struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockWarnerMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockWarnerMockRecorder is the mock recorder for MockWarner.
|
||||||
|
type MockWarnerMockRecorder struct {
|
||||||
|
mock *MockWarner
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockWarner creates a new mock instance.
|
||||||
|
func NewMockWarner(ctrl *gomock.Controller) *MockWarner {
|
||||||
|
mock := &MockWarner{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockWarnerMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockWarner) EXPECT() *MockWarnerMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn mocks base method.
|
||||||
|
func (m *MockWarner) Warn(arg0 string) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "Warn", arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn indicates an expected call of Warn.
|
||||||
|
func (mr *MockWarnerMockRecorder) Warn(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockWarner)(nil).Warn), arg0)
|
||||||
|
}
|
||||||
57
internal/configuration/wevpn.go
Normal file
57
internal/configuration/wevpn.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) readWevpn(r reader) (err error) {
|
||||||
|
settings.Name = constants.Wevpn
|
||||||
|
servers := r.servers.GetWevpn()
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.WevpnCityChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.WevpnHostnameChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings.ServerSelection.OpenVPN.readWevpn(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPNSelection) readWevpn(r reader) (err error) {
|
||||||
|
settings.TCP, err = readOpenVPNProtocol(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
validation := openvpnPortValidation{
|
||||||
|
tcp: settings.TCP,
|
||||||
|
allowedTCP: []uint16{53, 1195, 1199, 2018},
|
||||||
|
allowedUDP: []uint16{80, 1194, 1198},
|
||||||
|
}
|
||||||
|
settings.CustomPort, err = readOpenVPNCustomPort(r, validation)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPN) readWevpn(r reader) (err error) {
|
||||||
|
settings.ClientKey, err = readClientKey(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,60 +1,66 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
|
|
||||||
|
|
||||||
return lines
|
|
||||||
}
|
|
||||||
|
|
||||||
func (settings *Provider) readWindscribe(r reader) (err error) {
|
func (settings *Provider) readWindscribe(r reader) (err error) {
|
||||||
settings.Name = constants.Windscribe
|
settings.Name = constants.Windscribe
|
||||||
|
servers := r.servers.GetWindscribe()
|
||||||
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.WindscribeRegionChoices())
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.WindscribeRegionChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable REGION: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.WindscribeCityChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
|
||||||
|
constants.WindscribeHostnameChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = settings.ServerSelection.OpenVPN.readWindscribe(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.WindscribeCityChoices())
|
return settings.ServerSelection.Wireguard.readWindscribe(r.env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPNSelection) readWindscribe(r reader) (err error) {
|
||||||
|
settings.TCP, err = readOpenVPNProtocol(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.WindscribeHostnameChoices())
|
settings.CustomPort, err = readOpenVPNCustomPort(r, openvpnPortValidation{
|
||||||
if err != nil {
|
tcp: settings.TCP,
|
||||||
return err
|
allowedTCP: []uint16{21, 22, 80, 123, 143, 443, 587, 1194, 3306, 8080, 54783},
|
||||||
}
|
allowedUDP: []uint16{53, 80, 123, 443, 1194, 54783},
|
||||||
|
})
|
||||||
settings.ServerSelection.CustomPort, err = readCustomPort(r.env, settings.ServerSelection.Protocol,
|
if err != nil {
|
||||||
[]uint16{21, 22, 80, 123, 143, 443, 587, 1194, 3306, 8080, 54783},
|
return err
|
||||||
[]uint16{53, 80, 123, 443, 1194, 54783})
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *WireguardSelection) readWindscribe(env params.Interface) (err error) {
|
||||||
|
settings.EndpointPort, err = readWireguardCustomPort(env,
|
||||||
|
[]uint16{53, 80, 123, 443, 1194, 65142})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
88
internal/configuration/wireguard.go
Normal file
88
internal/configuration/wireguard.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Wireguard contains settings to configure the Wireguard client.
|
||||||
|
type Wireguard struct {
|
||||||
|
PrivateKey string `json:"privatekey"`
|
||||||
|
PreSharedKey string `json:"presharedkey"`
|
||||||
|
Addresses []*net.IPNet `json:"addresses"`
|
||||||
|
Interface string `json:"interface"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Wireguard) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Wireguard) lines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+"Wireguard:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Network interface: "+settings.Interface)
|
||||||
|
|
||||||
|
if settings.PrivateKey != "" {
|
||||||
|
lines = append(lines, indent+lastIndent+"Private key is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.PreSharedKey != "" {
|
||||||
|
lines = append(lines, indent+lastIndent+"Pre-shared key is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.Addresses) > 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Addresses: ")
|
||||||
|
for _, address := range settings.Addresses {
|
||||||
|
lines = append(lines, indent+indent+lastIndent+address.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Wireguard) read(r reader) (err error) {
|
||||||
|
settings.PrivateKey, err = r.env.Get("WIREGUARD_PRIVATE_KEY",
|
||||||
|
params.CaseSensitiveValue(), params.Unset(), params.Compulsory())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable WIREGUARD_PRIVATE_KEY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.PreSharedKey, err = r.env.Get("WIREGUARD_PRESHARED_KEY",
|
||||||
|
params.CaseSensitiveValue(), params.Unset())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable WIREGUARD_PRESHARED_KEY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = settings.readAddresses(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Interface, err = r.env.Get("WIREGUARD_INTERFACE", params.Default("wg0"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable WIREGUARD_INTERFACE: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Wireguard) readAddresses(env params.Interface) (err error) {
|
||||||
|
addressStrings, err := env.CSV("WIREGUARD_ADDRESS", params.Compulsory())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable WIREGUARD_ADDRESS: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addressString := range addressStrings {
|
||||||
|
ip, ipNet, err := net.ParseCIDR(addressString)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable WIREGUARD_ADDRESS: address %s: %w", addressString, err)
|
||||||
|
}
|
||||||
|
ipNet.IP = ip
|
||||||
|
settings.Addresses = append(settings.Addresses, ipNet)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package constants
|
|
||||||
|
|
||||||
const (
|
|
||||||
HealthcheckAddress = "127.0.0.1:9999"
|
|
||||||
)
|
|
||||||
@@ -136,6 +136,7 @@ func CountryCodes() map[string]string {
|
|||||||
"mg": "Madagascar",
|
"mg": "Madagascar",
|
||||||
"mw": "Malawi",
|
"mw": "Malawi",
|
||||||
"my": "Malaysia",
|
"my": "Malaysia",
|
||||||
|
"mys": "Kuala Lumpur",
|
||||||
"mv": "Maldives",
|
"mv": "Maldives",
|
||||||
"ml": "Mali",
|
"ml": "Mali",
|
||||||
"mt": "Malta",
|
"mt": "Malta",
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package constants
|
package constants
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -11,184 +9,18 @@ const (
|
|||||||
CyberghostCertificate = "MIIGWjCCBEKgAwIBAgIJAJxUG61mxDS7MA0GCSqGSIb3DQEBDQUAMHsxCzAJBgNVBAYTAlJPMRIwEAYDVQQHEwlCdWNoYXJlc3QxGDAWBgNVBAoTD0N5YmVyR2hvc3QgUy5BLjEbMBkGA1UEAxMSQ3liZXJHaG9zdCBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJpbmZvQGN5YmVyZ2hvc3Qucm8wHhcNMTcwNjE5MDgxNzI1WhcNMzcwNjE0MDgxNzI1WjB7MQswCQYDVQQGEwJSTzESMBAGA1UEBxMJQnVjaGFyZXN0MRgwFgYDVQQKEw9DeWJlckdob3N0IFMuQS4xGzAZBgNVBAMTEkN5YmVyR2hvc3QgUm9vdCBDQTEhMB8GCSqGSIb3DQEJARYSaW5mb0BjeWJlcmdob3N0LnJvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7O8+mji2FlQhJXn/G4VLrKPjGtxgQBAdjo0dZEQzKX08q14dLkslmOLgShStWKrOiLXGAvB1rPvvk613jtA0KjQLpgyLy9lIWohQKYjj5jrJYXMZMkbSHBYI9L8L7iezBEFYrjYKdDo51nq99wRFhKdbyKKjDh3e2L2SVEZLT1ogkK5gWzjvH+mjjtjUUicK+YjGwWOz6I+KKaG4Ve/D/cE6nCLbhHIMMnargZEu7sqA6BFeS4kEP/ZdCZoTSX2n43XV1q63nJt/v0KDetbZDciFVW9h9SVPG4qT44p0550N+Mom7zTX7S/ID5T9dplgU8sRGtIMrG0cIMD9zmpFgUnMusCrR7jJFr0sMAveTbgZg95LmstV6R6WKZkSFdUrE0DHl4dHoZvTFX+1LhwhHgjgDLaosX0vhG/C/7LpoVWimd6RRQT3M9o4Fa1TuhfvBzQ20QHrmRV/yKvGNK0xckZ6EZ/QY7Z55ORU15Tgab4ebnblYPWoEmn0mIYP3LFFeoR5OS1EX7+j4kPv+bwPGsmpHjxmZyq2Y7sJBpbOCJgbkn52WZdPBIRDpPdIHQ8pAJC4T0iMK9xvAwWNl/V6EYYNpR97osyEDXn+BTdAHlhJ5fck9KlwI9mb1Kg1bhbvbmaIAiOLenSULYf3j6rI1ygo3R2cCyybtuAq8M7z0OECAwEAAaOB4DCB3TAdBgNVHQ4EFgQU6tdK1g/He5qzjeAoM5eHt4in9iUwga0GA1UdIwSBpTCBooAU6tdK1g/He5qzjeAoM5eHt4in9iWhf6R9MHsxCzAJBgNVBAYTAlJPMRIwEAYDVQQHEwlCdWNoYXJlc3QxGDAWBgNVBAoTD0N5YmVyR2hvc3QgUy5BLjEbMBkGA1UEAxMSQ3liZXJHaG9zdCBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJpbmZvQGN5YmVyZ2hvc3Qucm+CCQCcVButZsQ0uzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4ICAQDNyQ92kj4qiNjnHk99qvnFw9qGfwB9ofaPL74zh0G5hEe3Wgb2o4fqUGnvUNgOu53gJksz3DcPQ8t40wfmm9I1Z8tiM9qrqvkuQ+nKcLgdooXtEsTybPIYDZ2cWR/5E0TKRvC7RFzKgQ4D77Vbi4TdaHiDV7ZNfU1iLCoBGcYm80hcUHEs5KIVLwUmcSOTmbZBySJxcSD0yUpS7nlZGwLY6VQrU+JFwDSisbXT4DXf3iSzp7FzW0/u/SFvWsPHrjE0hkPoZPalYvouaJEHKAhip0ZwSmitlxbBnmm8+K/3c9mLA5/uXrirfpuhhs8V3lyV2mczVtSiTl6gpi88gc//JY80JeHdupjO25T3XEzY9cpxecmkWaUEjLMx4wVoXQuUiPonfILM6OLwi+zUS8gQErdFeGvcQXbncPa4SdJuHkF8lgiX2i8S8fPGdXvU37E9bdAXwP5nZriYq1s0D59Qfvz+vLXVkmyZp6ztxjKjKolemPMak0Y5c1Q4RjNF6tmQoFuy/ACSkWy14Tzu2dFp7UiVbGg1FOvKhfs48zC2/IUQv1arqmPT/9LVq3B2DVT9UKXRUXX/f/jSSsVjkz4uUe2jUyL+XHX1nSmROTPHSAJ+oKf0BLnfqUxFkEUTwLnayssP2nwGgq35b7wEbTFIXdrjHGFUVQIDeERz8UThew=="
|
CyberghostCertificate = "MIIGWjCCBEKgAwIBAgIJAJxUG61mxDS7MA0GCSqGSIb3DQEBDQUAMHsxCzAJBgNVBAYTAlJPMRIwEAYDVQQHEwlCdWNoYXJlc3QxGDAWBgNVBAoTD0N5YmVyR2hvc3QgUy5BLjEbMBkGA1UEAxMSQ3liZXJHaG9zdCBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJpbmZvQGN5YmVyZ2hvc3Qucm8wHhcNMTcwNjE5MDgxNzI1WhcNMzcwNjE0MDgxNzI1WjB7MQswCQYDVQQGEwJSTzESMBAGA1UEBxMJQnVjaGFyZXN0MRgwFgYDVQQKEw9DeWJlckdob3N0IFMuQS4xGzAZBgNVBAMTEkN5YmVyR2hvc3QgUm9vdCBDQTEhMB8GCSqGSIb3DQEJARYSaW5mb0BjeWJlcmdob3N0LnJvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7O8+mji2FlQhJXn/G4VLrKPjGtxgQBAdjo0dZEQzKX08q14dLkslmOLgShStWKrOiLXGAvB1rPvvk613jtA0KjQLpgyLy9lIWohQKYjj5jrJYXMZMkbSHBYI9L8L7iezBEFYrjYKdDo51nq99wRFhKdbyKKjDh3e2L2SVEZLT1ogkK5gWzjvH+mjjtjUUicK+YjGwWOz6I+KKaG4Ve/D/cE6nCLbhHIMMnargZEu7sqA6BFeS4kEP/ZdCZoTSX2n43XV1q63nJt/v0KDetbZDciFVW9h9SVPG4qT44p0550N+Mom7zTX7S/ID5T9dplgU8sRGtIMrG0cIMD9zmpFgUnMusCrR7jJFr0sMAveTbgZg95LmstV6R6WKZkSFdUrE0DHl4dHoZvTFX+1LhwhHgjgDLaosX0vhG/C/7LpoVWimd6RRQT3M9o4Fa1TuhfvBzQ20QHrmRV/yKvGNK0xckZ6EZ/QY7Z55ORU15Tgab4ebnblYPWoEmn0mIYP3LFFeoR5OS1EX7+j4kPv+bwPGsmpHjxmZyq2Y7sJBpbOCJgbkn52WZdPBIRDpPdIHQ8pAJC4T0iMK9xvAwWNl/V6EYYNpR97osyEDXn+BTdAHlhJ5fck9KlwI9mb1Kg1bhbvbmaIAiOLenSULYf3j6rI1ygo3R2cCyybtuAq8M7z0OECAwEAAaOB4DCB3TAdBgNVHQ4EFgQU6tdK1g/He5qzjeAoM5eHt4in9iUwga0GA1UdIwSBpTCBooAU6tdK1g/He5qzjeAoM5eHt4in9iWhf6R9MHsxCzAJBgNVBAYTAlJPMRIwEAYDVQQHEwlCdWNoYXJlc3QxGDAWBgNVBAoTD0N5YmVyR2hvc3QgUy5BLjEbMBkGA1UEAxMSQ3liZXJHaG9zdCBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJpbmZvQGN5YmVyZ2hvc3Qucm+CCQCcVButZsQ0uzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4ICAQDNyQ92kj4qiNjnHk99qvnFw9qGfwB9ofaPL74zh0G5hEe3Wgb2o4fqUGnvUNgOu53gJksz3DcPQ8t40wfmm9I1Z8tiM9qrqvkuQ+nKcLgdooXtEsTybPIYDZ2cWR/5E0TKRvC7RFzKgQ4D77Vbi4TdaHiDV7ZNfU1iLCoBGcYm80hcUHEs5KIVLwUmcSOTmbZBySJxcSD0yUpS7nlZGwLY6VQrU+JFwDSisbXT4DXf3iSzp7FzW0/u/SFvWsPHrjE0hkPoZPalYvouaJEHKAhip0ZwSmitlxbBnmm8+K/3c9mLA5/uXrirfpuhhs8V3lyV2mczVtSiTl6gpi88gc//JY80JeHdupjO25T3XEzY9cpxecmkWaUEjLMx4wVoXQuUiPonfILM6OLwi+zUS8gQErdFeGvcQXbncPa4SdJuHkF8lgiX2i8S8fPGdXvU37E9bdAXwP5nZriYq1s0D59Qfvz+vLXVkmyZp6ztxjKjKolemPMak0Y5c1Q4RjNF6tmQoFuy/ACSkWy14Tzu2dFp7UiVbGg1FOvKhfs48zC2/IUQv1arqmPT/9LVq3B2DVT9UKXRUXX/f/jSSsVjkz4uUe2jUyL+XHX1nSmROTPHSAJ+oKf0BLnfqUxFkEUTwLnayssP2nwGgq35b7wEbTFIXdrjHGFUVQIDeERz8UThew=="
|
||||||
)
|
)
|
||||||
|
|
||||||
func CyberghostRegionChoices() (choices []string) {
|
func CyberghostCountryChoices(servers []models.CyberghostServer) (choices []string) {
|
||||||
servers := CyberghostServers()
|
|
||||||
choices = make([]string, len(servers))
|
choices = make([]string, len(servers))
|
||||||
for i := range servers {
|
for i := range servers {
|
||||||
choices[i] = servers[i].Region
|
choices[i] = servers[i].Country
|
||||||
}
|
}
|
||||||
return makeUnique(choices)
|
return makeUnique(choices)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CyberghostGroupChoices() (choices []string) {
|
func CyberghostHostnameChoices(servers []models.CyberghostServer) (choices []string) {
|
||||||
servers := CyberghostServers()
|
|
||||||
choices = make([]string, len(servers))
|
choices = make([]string, len(servers))
|
||||||
for i := range servers {
|
for i := range servers {
|
||||||
choices[i] = servers[i].Group
|
choices[i] = servers[i].Hostname
|
||||||
}
|
}
|
||||||
return makeUnique(choices)
|
return makeUnique(choices)
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:lll
|
|
||||||
// CyberghostServers returns a slice with the server information for each
|
|
||||||
// of the Cyberghost server.
|
|
||||||
func CyberghostServers() []models.CyberghostServer {
|
|
||||||
return []models.CyberghostServer{
|
|
||||||
{Region: "Albania", Group: "Premium UDP Europe", IPs: []net.IP{{31, 171, 155, 3}, {31, 171, 155, 4}, {31, 171, 155, 5}, {31, 171, 155, 6}, {31, 171, 155, 7}, {31, 171, 155, 8}, {31, 171, 155, 9}, {31, 171, 155, 11}, {31, 171, 155, 12}, {31, 171, 155, 13}}},
|
|
||||||
{Region: "Albania", Group: "Premium TCP Europe", IPs: []net.IP{{31, 171, 155, 4}, {31, 171, 155, 5}, {31, 171, 155, 6}, {31, 171, 155, 7}, {31, 171, 155, 8}, {31, 171, 155, 9}, {31, 171, 155, 11}, {31, 171, 155, 12}, {31, 171, 155, 13}, {31, 171, 155, 14}}},
|
|
||||||
{Region: "Algeria", Group: "Premium UDP Europe", IPs: []net.IP{{176, 125, 228, 131}, {176, 125, 228, 132}, {176, 125, 228, 133}, {176, 125, 228, 135}, {176, 125, 228, 136}, {176, 125, 228, 138}, {176, 125, 228, 139}, {176, 125, 228, 140}, {176, 125, 228, 141}, {176, 125, 228, 144}}},
|
|
||||||
{Region: "Algeria", Group: "Premium TCP Europe", IPs: []net.IP{{176, 125, 228, 131}, {176, 125, 228, 132}, {176, 125, 228, 134}, {176, 125, 228, 136}, {176, 125, 228, 138}, {176, 125, 228, 139}, {176, 125, 228, 140}, {176, 125, 228, 141}, {176, 125, 228, 142}, {176, 125, 228, 143}}},
|
|
||||||
{Region: "Andorra", Group: "Premium UDP Europe", IPs: []net.IP{{188, 241, 82, 134}, {188, 241, 82, 135}, {188, 241, 82, 143}, {188, 241, 82, 145}, {188, 241, 82, 152}, {188, 241, 82, 155}, {188, 241, 82, 162}, {188, 241, 82, 163}, {188, 241, 82, 166}, {188, 241, 82, 168}}},
|
|
||||||
{Region: "Argentina", Group: "Premium UDP USA", IPs: []net.IP{{190, 106, 130, 15}, {190, 106, 130, 17}, {190, 106, 130, 18}, {190, 106, 130, 19}, {190, 106, 130, 21}, {190, 106, 130, 23}, {190, 106, 130, 26}, {190, 106, 130, 36}, {190, 106, 130, 38}, {190, 106, 130, 45}}},
|
|
||||||
{Region: "Argentina", Group: "Premium TCP USA", IPs: []net.IP{{190, 106, 130, 16}, {190, 106, 130, 18}, {190, 106, 130, 20}, {190, 106, 130, 23}, {190, 106, 130, 26}, {190, 106, 130, 36}, {190, 106, 130, 38}, {190, 106, 130, 39}, {190, 106, 130, 41}, {190, 106, 130, 43}}},
|
|
||||||
{Region: "Armenia", Group: "Premium UDP Europe", IPs: []net.IP{{185, 253, 160, 131}, {185, 253, 160, 133}, {185, 253, 160, 136}, {185, 253, 160, 137}, {185, 253, 160, 138}, {185, 253, 160, 139}, {185, 253, 160, 140}, {185, 253, 160, 142}, {185, 253, 160, 143}, {185, 253, 160, 144}}},
|
|
||||||
{Region: "Australia", Group: "Premium TCP Asia", IPs: []net.IP{{43, 242, 68, 88}, {43, 242, 68, 99}, {202, 60, 91, 202}, {202, 60, 91, 209}, {202, 60, 91, 210}, {202, 60, 91, 214}, {202, 60, 91, 220}, {202, 60, 91, 230}, {202, 60, 91, 245}, {202, 60, 91, 254}}},
|
|
||||||
{Region: "Australia", Group: "Premium UDP Asia", IPs: []net.IP{{43, 242, 68, 75}, {43, 242, 68, 78}, {43, 242, 68, 87}, {43, 242, 68, 93}, {43, 242, 68, 102}, {202, 60, 80, 16}, {202, 60, 91, 197}, {202, 60, 91, 210}, {202, 60, 91, 234}, {202, 60, 91, 240}}},
|
|
||||||
{Region: "Austria", Group: "Premium TCP Europe", IPs: []net.IP{{37, 19, 223, 114}, {37, 19, 223, 119}, {37, 120, 155, 99}, {37, 120, 155, 101}, {89, 187, 168, 161}, {89, 187, 168, 169}, {89, 187, 168, 171}, {89, 187, 168, 174}, {89, 187, 168, 176}, {89, 187, 168, 178}}},
|
|
||||||
{Region: "Bahamas", Group: "Premium TCP USA", IPs: []net.IP{{95, 181, 238, 131}, {95, 181, 238, 132}, {95, 181, 238, 138}, {95, 181, 238, 139}, {95, 181, 238, 141}, {95, 181, 238, 145}, {95, 181, 238, 146}, {95, 181, 238, 147}, {95, 181, 238, 149}, {95, 181, 238, 150}}},
|
|
||||||
{Region: "Bangladesh", Group: "Premium UDP Asia", IPs: []net.IP{{84, 252, 93, 131}, {84, 252, 93, 133}, {84, 252, 93, 134}, {84, 252, 93, 135}, {84, 252, 93, 137}, {84, 252, 93, 139}, {84, 252, 93, 141}, {84, 252, 93, 142}, {84, 252, 93, 144}, {84, 252, 93, 145}}},
|
|
||||||
{Region: "Belarus", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 194, 3}, {45, 132, 194, 10}, {45, 132, 194, 15}, {45, 132, 194, 25}, {45, 132, 194, 29}, {45, 132, 194, 37}, {45, 132, 194, 38}, {45, 132, 194, 39}, {45, 132, 194, 45}, {45, 132, 194, 49}}},
|
|
||||||
{Region: "Belarus", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 194, 3}, {45, 132, 194, 6}, {45, 132, 194, 9}, {45, 132, 194, 14}, {45, 132, 194, 15}, {45, 132, 194, 22}, {45, 132, 194, 33}, {45, 132, 194, 35}, {45, 132, 194, 37}, {45, 132, 194, 49}}},
|
|
||||||
{Region: "Belgium", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 143, 53}, {37, 120, 143, 168}, {185, 210, 217, 10}, {185, 210, 217, 60}, {185, 210, 217, 253}, {193, 9, 114, 220}, {193, 9, 114, 222}, {193, 9, 114, 233}, {193, 9, 114, 245}, {194, 110, 115, 233}}},
|
|
||||||
{Region: "Belgium", Group: "Premium UDP Europe", IPs: []net.IP{{185, 210, 217, 54}, {185, 210, 217, 247}, {185, 210, 217, 250}, {185, 232, 21, 120}, {185, 232, 21, 122}, {193, 9, 114, 212}, {193, 9, 114, 213}, {194, 110, 115, 203}, {194, 110, 115, 213}, {194, 110, 115, 220}}},
|
|
||||||
{Region: "Bosnia and Herzegovina", Group: "Premium UDP Europe", IPs: []net.IP{{185, 99, 3, 57}, {185, 99, 3, 58}, {185, 99, 3, 72}, {185, 99, 3, 73}, {185, 99, 3, 74}, {185, 99, 3, 130}, {185, 99, 3, 131}, {185, 99, 3, 134}, {185, 99, 3, 135}, {185, 99, 3, 136}}},
|
|
||||||
{Region: "Brazil", Group: "Premium UDP USA", IPs: []net.IP{{188, 241, 177, 6}, {188, 241, 177, 14}, {188, 241, 177, 20}, {188, 241, 177, 21}, {188, 241, 177, 37}, {188, 241, 177, 43}, {188, 241, 177, 141}, {188, 241, 177, 148}, {188, 241, 177, 149}, {188, 241, 177, 150}}},
|
|
||||||
{Region: "Brazil", Group: "Premium TCP USA", IPs: []net.IP{{188, 241, 177, 4}, {188, 241, 177, 22}, {188, 241, 177, 23}, {188, 241, 177, 40}, {188, 241, 177, 44}, {188, 241, 177, 46}, {188, 241, 177, 131}, {188, 241, 177, 133}, {188, 241, 177, 149}, {188, 241, 177, 153}}},
|
|
||||||
{Region: "Bulgaria", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 152, 99}, {37, 120, 152, 100}, {37, 120, 152, 101}, {37, 120, 152, 102}, {37, 120, 152, 103}, {37, 120, 152, 104}, {37, 120, 152, 105}, {37, 120, 152, 106}, {37, 120, 152, 107}, {37, 120, 152, 109}}},
|
|
||||||
{Region: "Cambodia", Group: "Premium UDP Asia", IPs: []net.IP{{188, 215, 235, 37}, {188, 215, 235, 41}, {188, 215, 235, 43}, {188, 215, 235, 44}, {188, 215, 235, 48}, {188, 215, 235, 49}, {188, 215, 235, 52}, {188, 215, 235, 53}, {188, 215, 235, 56}, {188, 215, 235, 57}}},
|
|
||||||
{Region: "Cambodia", Group: "Premium TCP Asia", IPs: []net.IP{{188, 215, 235, 35}, {188, 215, 235, 36}, {188, 215, 235, 40}, {188, 215, 235, 43}, {188, 215, 235, 46}, {188, 215, 235, 47}, {188, 215, 235, 50}, {188, 215, 235, 51}, {188, 215, 235, 55}, {188, 215, 235, 57}}},
|
|
||||||
{Region: "Canada", Group: "Premium UDP USA", IPs: []net.IP{{66, 115, 142, 190}, {104, 200, 151, 28}, {104, 200, 151, 37}, {104, 200, 151, 48}, {104, 200, 151, 67}, {104, 200, 151, 72}, {104, 200, 151, 121}, {172, 98, 89, 145}, {172, 98, 89, 152}, {172, 98, 89, 169}}},
|
|
||||||
{Region: "Canada", Group: "Premium TCP USA", IPs: []net.IP{{104, 200, 151, 25}, {104, 200, 151, 33}, {104, 200, 151, 43}, {104, 200, 151, 90}, {104, 200, 151, 118}, {104, 200, 151, 125}, {104, 200, 151, 147}, {172, 98, 89, 147}, {172, 98, 89, 158}, {172, 98, 89, 190}}},
|
|
||||||
{Region: "Chile", Group: "Premium UDP USA", IPs: []net.IP{{146, 70, 11, 3}, {146, 70, 11, 4}, {146, 70, 11, 6}, {146, 70, 11, 7}, {146, 70, 11, 8}, {146, 70, 11, 9}, {146, 70, 11, 11}, {146, 70, 11, 12}, {146, 70, 11, 13}, {146, 70, 11, 14}}},
|
|
||||||
{Region: "Chile", Group: "Premium TCP USA", IPs: []net.IP{{146, 70, 11, 3}, {146, 70, 11, 4}, {146, 70, 11, 5}, {146, 70, 11, 6}, {146, 70, 11, 8}, {146, 70, 11, 9}, {146, 70, 11, 10}, {146, 70, 11, 11}, {146, 70, 11, 13}, {146, 70, 11, 14}}},
|
|
||||||
{Region: "China", Group: "Premium UDP Asia", IPs: []net.IP{{188, 241, 80, 131}, {188, 241, 80, 132}, {188, 241, 80, 133}, {188, 241, 80, 136}, {188, 241, 80, 137}, {188, 241, 80, 138}, {188, 241, 80, 139}, {188, 241, 80, 140}, {188, 241, 80, 141}, {188, 241, 80, 142}}},
|
|
||||||
{Region: "China", Group: "Premium TCP Asia", IPs: []net.IP{{188, 241, 80, 131}, {188, 241, 80, 133}, {188, 241, 80, 134}, {188, 241, 80, 135}, {188, 241, 80, 137}, {188, 241, 80, 138}, {188, 241, 80, 139}, {188, 241, 80, 140}, {188, 241, 80, 141}, {188, 241, 80, 142}}},
|
|
||||||
{Region: "Colombia", Group: "Premium UDP USA", IPs: []net.IP{{146, 70, 9, 3}, {146, 70, 9, 4}, {146, 70, 9, 5}, {146, 70, 9, 6}, {146, 70, 9, 7}, {146, 70, 9, 9}, {146, 70, 9, 10}, {146, 70, 9, 11}, {146, 70, 9, 12}, {146, 70, 9, 14}}},
|
|
||||||
{Region: "Costa Rica", Group: "Premium TCP USA", IPs: []net.IP{{146, 70, 10, 3}, {146, 70, 10, 4}, {146, 70, 10, 6}, {146, 70, 10, 7}, {146, 70, 10, 9}, {146, 70, 10, 10}, {146, 70, 10, 11}, {146, 70, 10, 12}, {146, 70, 10, 13}, {146, 70, 10, 14}}},
|
|
||||||
{Region: "Costa Rica", Group: "Premium UDP USA", IPs: []net.IP{{146, 70, 10, 3}, {146, 70, 10, 4}, {146, 70, 10, 6}, {146, 70, 10, 7}, {146, 70, 10, 8}, {146, 70, 10, 9}, {146, 70, 10, 10}, {146, 70, 10, 12}, {146, 70, 10, 13}, {146, 70, 10, 14}}},
|
|
||||||
{Region: "Croatia", Group: "Premium UDP Europe", IPs: []net.IP{{146, 70, 8, 3}, {146, 70, 8, 4}, {146, 70, 8, 5}, {146, 70, 8, 6}, {146, 70, 8, 7}, {146, 70, 8, 8}, {146, 70, 8, 9}, {146, 70, 8, 11}, {146, 70, 8, 12}, {146, 70, 8, 16}}},
|
|
||||||
{Region: "Croatia", Group: "Premium TCP Europe", IPs: []net.IP{{146, 70, 8, 3}, {146, 70, 8, 4}, {146, 70, 8, 6}, {146, 70, 8, 8}, {146, 70, 8, 10}, {146, 70, 8, 11}, {146, 70, 8, 12}, {146, 70, 8, 14}, {146, 70, 8, 15}, {146, 70, 8, 16}}},
|
|
||||||
{Region: "Cyprus", Group: "Premium TCP Europe", IPs: []net.IP{{185, 253, 162, 131}, {185, 253, 162, 133}, {185, 253, 162, 136}, {185, 253, 162, 137}, {185, 253, 162, 139}, {185, 253, 162, 140}, {185, 253, 162, 141}, {185, 253, 162, 142}, {185, 253, 162, 143}, {185, 253, 162, 144}}},
|
|
||||||
{Region: "Czech Republic", Group: "Premium UDP Europe", IPs: []net.IP{{195, 181, 161, 2}, {195, 181, 161, 3}, {195, 181, 161, 6}, {195, 181, 161, 12}, {195, 181, 161, 13}, {195, 181, 161, 17}, {195, 181, 161, 19}, {195, 181, 161, 21}, {195, 181, 161, 22}, {195, 181, 161, 23}}},
|
|
||||||
{Region: "Denmark", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 194, 37}, {37, 120, 194, 40}, {37, 120, 194, 41}, {37, 120, 194, 62}, {95, 174, 65, 174}, {185, 206, 224, 231}, {185, 206, 224, 235}, {185, 206, 224, 246}, {185, 206, 224, 247}, {185, 206, 224, 254}}},
|
|
||||||
{Region: "Denmark", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 145, 93}, {37, 120, 145, 94}, {37, 120, 194, 40}, {37, 120, 194, 42}, {37, 120, 194, 43}, {37, 120, 194, 44}, {37, 120, 194, 53}, {37, 120, 194, 54}, {185, 206, 224, 232}, {185, 206, 224, 249}}},
|
|
||||||
{Region: "Egypt", Group: "Premium TCP Europe", IPs: []net.IP{{188, 214, 122, 47}, {188, 214, 122, 51}, {188, 214, 122, 55}, {188, 214, 122, 59}, {188, 214, 122, 60}, {188, 214, 122, 62}, {188, 214, 122, 67}, {188, 214, 122, 72}, {188, 214, 122, 73}, {188, 214, 122, 78}}},
|
|
||||||
{Region: "Egypt", Group: "Premium UDP Europe", IPs: []net.IP{{188, 214, 122, 36}, {188, 214, 122, 45}, {188, 214, 122, 53}, {188, 214, 122, 59}, {188, 214, 122, 61}, {188, 214, 122, 68}, {188, 214, 122, 72}, {188, 214, 122, 73}, {188, 214, 122, 74}, {188, 214, 122, 77}}},
|
|
||||||
{Region: "Estonia", Group: "Premium TCP Europe", IPs: []net.IP{{95, 153, 32, 83}, {95, 153, 32, 85}, {95, 153, 32, 86}, {95, 153, 32, 87}, {95, 153, 32, 89}, {95, 153, 32, 90}, {95, 153, 32, 91}, {95, 153, 32, 92}, {95, 153, 32, 93}, {95, 153, 32, 94}}},
|
|
||||||
{Region: "Estonia", Group: "Premium UDP Europe", IPs: []net.IP{{95, 153, 32, 83}, {95, 153, 32, 84}, {95, 153, 32, 87}, {95, 153, 32, 88}, {95, 153, 32, 89}, {95, 153, 32, 90}, {95, 153, 32, 91}, {95, 153, 32, 92}, {95, 153, 32, 93}, {95, 153, 32, 94}}},
|
|
||||||
{Region: "France", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 60, 34}, {84, 17, 60, 41}, {84, 17, 60, 61}, {84, 17, 60, 115}, {84, 17, 60, 136}, {84, 17, 60, 167}, {92, 204, 174, 86}, {191, 101, 31, 84}, {191, 101, 31, 186}, {191, 101, 31, 249}}},
|
|
||||||
{Region: "France", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 43, 167}, {84, 17, 60, 33}, {84, 17, 60, 58}, {84, 17, 60, 68}, {84, 17, 60, 92}, {84, 17, 60, 144}, {191, 101, 31, 170}, {191, 101, 31, 254}, {191, 101, 217, 176}, {191, 101, 217, 193}}},
|
|
||||||
{Region: "Georgia", Group: "Premium UDP Europe", IPs: []net.IP{{95, 181, 236, 131}, {95, 181, 236, 133}, {95, 181, 236, 136}, {95, 181, 236, 137}, {95, 181, 236, 138}, {95, 181, 236, 139}, {95, 181, 236, 140}, {95, 181, 236, 142}, {95, 181, 236, 143}, {95, 181, 236, 144}}},
|
|
||||||
{Region: "Germany", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 48, 35}, {84, 17, 48, 186}, {84, 17, 49, 43}, {84, 17, 49, 48}, {84, 17, 49, 52}, {84, 17, 49, 134}, {84, 17, 49, 231}, {84, 17, 49, 249}, {154, 13, 1, 159}, {154, 28, 188, 47}}},
|
|
||||||
{Region: "Germany", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 48, 7}, {84, 17, 48, 9}, {84, 17, 48, 33}, {84, 17, 48, 143}, {84, 17, 49, 41}, {84, 17, 49, 184}, {138, 199, 36, 134}, {138, 199, 36, 200}, {154, 28, 188, 135}, {154, 28, 188, 162}}},
|
|
||||||
{Region: "Greece", Group: "Premium UDP Europe", IPs: []net.IP{{185, 51, 134, 163}, {185, 51, 134, 170}, {185, 51, 134, 171}, {185, 51, 134, 174}, {185, 51, 134, 243}, {185, 51, 134, 244}, {185, 51, 134, 247}, {185, 51, 134, 248}, {185, 51, 134, 249}, {185, 51, 134, 251}}},
|
|
||||||
{Region: "Greece", Group: "Premium TCP Europe", IPs: []net.IP{{185, 51, 134, 163}, {185, 51, 134, 166}, {185, 51, 134, 168}, {185, 51, 134, 170}, {185, 51, 134, 171}, {185, 51, 134, 174}, {185, 51, 134, 243}, {185, 51, 134, 245}, {185, 51, 134, 246}, {185, 51, 134, 252}}},
|
|
||||||
{Region: "Greenland", Group: "Premium TCP Europe", IPs: []net.IP{{91, 90, 120, 4}, {91, 90, 120, 5}, {91, 90, 120, 6}, {91, 90, 120, 7}, {91, 90, 120, 8}, {91, 90, 120, 10}, {91, 90, 120, 11}, {91, 90, 120, 15}, {91, 90, 120, 16}, {91, 90, 120, 17}}},
|
|
||||||
{Region: "Greenland", Group: "Premium UDP Europe", IPs: []net.IP{{91, 90, 120, 3}, {91, 90, 120, 7}, {91, 90, 120, 8}, {91, 90, 120, 10}, {91, 90, 120, 11}, {91, 90, 120, 12}, {91, 90, 120, 13}, {91, 90, 120, 15}, {91, 90, 120, 16}, {91, 90, 120, 17}}},
|
|
||||||
{Region: "Hong Kong", Group: "Premium UDP Asia", IPs: []net.IP{{84, 17, 56, 130}, {84, 17, 56, 134}, {84, 17, 56, 135}, {84, 17, 56, 151}, {84, 17, 56, 153}, {84, 17, 56, 162}, {84, 17, 56, 172}, {84, 17, 56, 175}, {84, 17, 56, 179}, {84, 17, 56, 180}}},
|
|
||||||
{Region: "Hong Kong", Group: "Premium TCP Asia", IPs: []net.IP{{84, 17, 56, 130}, {84, 17, 56, 132}, {84, 17, 56, 138}, {84, 17, 56, 142}, {84, 17, 56, 161}, {84, 17, 56, 166}, {84, 17, 56, 173}, {84, 17, 56, 175}, {84, 17, 56, 181}, {84, 17, 56, 184}}},
|
|
||||||
{Region: "Hungary", Group: "Premium TCP Europe", IPs: []net.IP{{86, 106, 74, 249}, {86, 106, 74, 250}, {86, 106, 74, 251}, {86, 106, 74, 252}, {86, 106, 74, 253}, {185, 189, 114, 115}, {185, 189, 114, 117}, {185, 189, 114, 121}, {185, 189, 114, 123}, {185, 189, 114, 124}}},
|
|
||||||
{Region: "Iceland", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 193, 3}, {45, 133, 193, 4}, {45, 133, 193, 5}, {45, 133, 193, 7}, {45, 133, 193, 8}, {45, 133, 193, 9}, {45, 133, 193, 11}, {45, 133, 193, 12}, {45, 133, 193, 13}, {45, 133, 193, 14}}},
|
|
||||||
{Region: "India", Group: "Premium UDP Europe", IPs: []net.IP{{103, 13, 112, 50}, {103, 13, 112, 52}, {103, 13, 112, 54}, {103, 13, 112, 58}, {103, 13, 112, 68}, {103, 13, 112, 69}, {103, 13, 112, 70}, {103, 13, 112, 72}, {103, 13, 112, 74}, {103, 13, 112, 75}}},
|
|
||||||
{Region: "India", Group: "Premium TCP Europe", IPs: []net.IP{{103, 13, 112, 52}, {103, 13, 112, 54}, {103, 13, 112, 57}, {103, 13, 112, 59}, {103, 13, 112, 60}, {103, 13, 112, 62}, {103, 13, 112, 73}, {103, 13, 112, 77}, {103, 13, 112, 78}, {103, 13, 112, 79}}},
|
|
||||||
{Region: "Indonesia", Group: "Premium UDP Asia", IPs: []net.IP{{146, 70, 14, 3}, {146, 70, 14, 5}, {146, 70, 14, 6}, {146, 70, 14, 8}, {146, 70, 14, 9}, {146, 70, 14, 10}, {146, 70, 14, 12}, {146, 70, 14, 13}, {146, 70, 14, 14}, {146, 70, 14, 16}}},
|
|
||||||
{Region: "Indonesia", Group: "Premium TCP Asia", IPs: []net.IP{{146, 70, 14, 3}, {146, 70, 14, 5}, {146, 70, 14, 6}, {146, 70, 14, 7}, {146, 70, 14, 8}, {146, 70, 14, 9}, {146, 70, 14, 11}, {146, 70, 14, 12}, {146, 70, 14, 13}, {146, 70, 14, 16}}},
|
|
||||||
{Region: "Iran", Group: "Premium UDP Asia", IPs: []net.IP{{62, 133, 46, 4}, {62, 133, 46, 5}, {62, 133, 46, 6}, {62, 133, 46, 7}, {62, 133, 46, 10}, {62, 133, 46, 11}, {62, 133, 46, 12}, {62, 133, 46, 13}, {62, 133, 46, 14}, {62, 133, 46, 15}}},
|
|
||||||
{Region: "Iran", Group: "Premium TCP Asia", IPs: []net.IP{{62, 133, 46, 4}, {62, 133, 46, 5}, {62, 133, 46, 6}, {62, 133, 46, 8}, {62, 133, 46, 9}, {62, 133, 46, 10}, {62, 133, 46, 11}, {62, 133, 46, 12}, {62, 133, 46, 13}, {62, 133, 46, 14}}},
|
|
||||||
{Region: "Ireland", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 235, 147}, {37, 120, 235, 149}, {37, 120, 235, 158}, {37, 120, 235, 170}, {77, 81, 139, 41}, {77, 81, 139, 42}, {77, 81, 139, 45}, {84, 247, 48, 21}, {84, 247, 48, 27}, {84, 247, 48, 30}}},
|
|
||||||
{Region: "Ireland", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 235, 149}, {37, 120, 235, 150}, {37, 120, 235, 155}, {37, 120, 235, 156}, {37, 120, 235, 157}, {37, 120, 235, 166}, {37, 120, 235, 168}, {37, 120, 235, 171}, {84, 247, 48, 4}, {84, 247, 48, 6}}},
|
|
||||||
{Region: "Isle of Man", Group: "Premium UDP Europe", IPs: []net.IP{{91, 90, 124, 147}, {91, 90, 124, 148}, {91, 90, 124, 149}, {91, 90, 124, 150}, {91, 90, 124, 152}, {91, 90, 124, 154}, {91, 90, 124, 156}, {91, 90, 124, 157}, {91, 90, 124, 158}, {91, 90, 124, 159}}},
|
|
||||||
{Region: "Isle of Man", Group: "Premium TCP Europe", IPs: []net.IP{{91, 90, 124, 148}, {91, 90, 124, 150}, {91, 90, 124, 151}, {91, 90, 124, 153}, {91, 90, 124, 154}, {91, 90, 124, 155}, {91, 90, 124, 156}, {91, 90, 124, 157}, {91, 90, 124, 158}, {91, 90, 124, 159}}},
|
|
||||||
{Region: "Israel", Group: "Premium TCP Europe", IPs: []net.IP{{160, 116, 0, 165}, {160, 116, 0, 170}, {185, 77, 248, 103}, {185, 77, 248, 108}, {185, 77, 248, 110}, {185, 77, 248, 112}, {185, 77, 248, 116}, {185, 77, 248, 123}, {185, 77, 248, 125}, {185, 77, 248, 129}}},
|
|
||||||
{Region: "Italy", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 58, 100}, {84, 17, 58, 117}, {84, 17, 58, 119}, {87, 101, 94, 117}, {87, 101, 94, 124}, {185, 217, 71, 134}, {212, 102, 55, 108}, {212, 102, 55, 110}, {212, 102, 55, 113}, {212, 102, 55, 120}}},
|
|
||||||
{Region: "Italy", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 58, 4}, {84, 17, 58, 14}, {84, 17, 58, 21}, {84, 17, 58, 101}, {84, 17, 58, 102}, {87, 101, 94, 75}, {212, 102, 55, 101}, {212, 102, 55, 102}, {212, 102, 55, 113}, {212, 102, 55, 120}}},
|
|
||||||
{Region: "Japan", Group: "Premium TCP Asia", IPs: []net.IP{{156, 146, 35, 7}, {156, 146, 35, 8}, {156, 146, 35, 11}, {156, 146, 35, 20}, {156, 146, 35, 22}, {156, 146, 35, 32}, {156, 146, 35, 33}, {156, 146, 35, 42}, {156, 146, 35, 43}, {156, 146, 35, 44}}},
|
|
||||||
{Region: "Kazakhstan", Group: "Premium UDP Europe", IPs: []net.IP{{62, 133, 47, 132}, {62, 133, 47, 133}, {62, 133, 47, 134}, {62, 133, 47, 136}, {62, 133, 47, 137}, {62, 133, 47, 139}, {62, 133, 47, 140}, {62, 133, 47, 141}, {62, 133, 47, 142}, {62, 133, 47, 143}}},
|
|
||||||
{Region: "Kazakhstan", Group: "Premium TCP Europe", IPs: []net.IP{{62, 133, 47, 131}, {62, 133, 47, 133}, {62, 133, 47, 134}, {62, 133, 47, 136}, {62, 133, 47, 137}, {62, 133, 47, 138}, {62, 133, 47, 139}, {62, 133, 47, 141}, {62, 133, 47, 143}, {62, 133, 47, 144}}},
|
|
||||||
{Region: "Kenya", Group: "Premium TCP Asia", IPs: []net.IP{{62, 12, 118, 195}, {62, 12, 118, 196}, {62, 12, 118, 197}, {62, 12, 118, 198}, {62, 12, 118, 199}, {62, 12, 118, 200}, {62, 12, 118, 201}, {62, 12, 118, 202}, {62, 12, 118, 203}, {62, 12, 118, 204}}},
|
|
||||||
{Region: "Kenya", Group: "Premium UDP Asia", IPs: []net.IP{{62, 12, 118, 195}, {62, 12, 118, 196}, {62, 12, 118, 197}, {62, 12, 118, 198}, {62, 12, 118, 199}, {62, 12, 118, 200}, {62, 12, 118, 201}, {62, 12, 118, 202}, {62, 12, 118, 203}, {62, 12, 118, 204}}},
|
|
||||||
{Region: "Korea", Group: "Premium UDP Asia", IPs: []net.IP{{79, 110, 55, 132}, {79, 110, 55, 136}, {79, 110, 55, 138}, {79, 110, 55, 141}, {79, 110, 55, 142}, {79, 110, 55, 149}, {79, 110, 55, 153}, {79, 110, 55, 154}, {79, 110, 55, 157}, {79, 110, 55, 158}}},
|
|
||||||
{Region: "Latvia", Group: "Premium UDP Europe", IPs: []net.IP{{109, 248, 148, 243}, {109, 248, 148, 244}, {109, 248, 148, 248}, {109, 248, 148, 249}, {109, 248, 148, 250}, {109, 248, 148, 252}, {109, 248, 149, 19}, {109, 248, 149, 20}, {109, 248, 149, 24}, {109, 248, 149, 29}}},
|
|
||||||
{Region: "Latvia", Group: "Premium TCP Europe", IPs: []net.IP{{109, 248, 148, 243}, {109, 248, 148, 245}, {109, 248, 148, 251}, {109, 248, 148, 252}, {109, 248, 149, 22}, {109, 248, 149, 23}, {109, 248, 149, 24}, {109, 248, 149, 26}, {109, 248, 149, 28}, {109, 248, 149, 29}}},
|
|
||||||
{Region: "Liechtenstein", Group: "Premium UDP Europe", IPs: []net.IP{{91, 90, 122, 131}, {91, 90, 122, 134}, {91, 90, 122, 135}, {91, 90, 122, 136}, {91, 90, 122, 137}, {91, 90, 122, 138}, {91, 90, 122, 141}, {91, 90, 122, 142}, {91, 90, 122, 143}, {91, 90, 122, 144}}},
|
|
||||||
{Region: "Lithuania", Group: "Premium UDP Europe", IPs: []net.IP{{85, 206, 162, 209}, {85, 206, 162, 211}, {85, 206, 162, 216}, {85, 206, 162, 217}, {85, 206, 162, 218}, {85, 206, 162, 220}, {85, 206, 165, 20}, {85, 206, 165, 23}, {85, 206, 165, 25}, {85, 206, 165, 30}}},
|
|
||||||
{Region: "Lithuania", Group: "Premium TCP Europe", IPs: []net.IP{{85, 206, 162, 211}, {85, 206, 162, 214}, {85, 206, 162, 215}, {85, 206, 162, 217}, {85, 206, 162, 221}, {85, 206, 162, 222}, {85, 206, 165, 20}, {85, 206, 165, 23}, {85, 206, 165, 24}, {85, 206, 165, 26}}},
|
|
||||||
{Region: "Luxembourg", Group: "Premium TCP Europe", IPs: []net.IP{{5, 253, 204, 6}, {5, 253, 204, 7}, {5, 253, 204, 13}, {5, 253, 204, 22}, {5, 253, 204, 26}, {5, 253, 204, 29}, {5, 253, 204, 39}, {5, 253, 204, 42}, {5, 253, 204, 43}, {5, 253, 204, 44}}},
|
|
||||||
{Region: "Luxembourg", Group: "Premium UDP Europe", IPs: []net.IP{{5, 253, 204, 3}, {5, 253, 204, 8}, {5, 253, 204, 10}, {5, 253, 204, 13}, {5, 253, 204, 14}, {5, 253, 204, 22}, {5, 253, 204, 25}, {5, 253, 204, 29}, {5, 253, 204, 36}, {5, 253, 204, 39}}},
|
|
||||||
{Region: "Macao", Group: "Premium UDP Asia", IPs: []net.IP{{84, 252, 92, 133}, {84, 252, 92, 135}, {84, 252, 92, 137}, {84, 252, 92, 138}, {84, 252, 92, 139}, {84, 252, 92, 140}, {84, 252, 92, 141}, {84, 252, 92, 142}, {84, 252, 92, 143}, {84, 252, 92, 144}}},
|
|
||||||
{Region: "Macao", Group: "Premium TCP Asia", IPs: []net.IP{{84, 252, 92, 132}, {84, 252, 92, 134}, {84, 252, 92, 137}, {84, 252, 92, 138}, {84, 252, 92, 139}, {84, 252, 92, 140}, {84, 252, 92, 141}, {84, 252, 92, 142}, {84, 252, 92, 143}, {84, 252, 92, 145}}},
|
|
||||||
{Region: "Macedonia", Group: "Premium UDP Europe", IPs: []net.IP{{185, 225, 28, 3}, {185, 225, 28, 4}, {185, 225, 28, 5}, {185, 225, 28, 6}, {185, 225, 28, 7}, {185, 225, 28, 8}, {185, 225, 28, 9}, {185, 225, 28, 10}, {185, 225, 28, 11}, {185, 225, 28, 12}}},
|
|
||||||
{Region: "Macedonia", Group: "Premium TCP Europe", IPs: []net.IP{{185, 225, 28, 3}, {185, 225, 28, 4}, {185, 225, 28, 5}, {185, 225, 28, 6}, {185, 225, 28, 7}, {185, 225, 28, 8}, {185, 225, 28, 9}, {185, 225, 28, 10}, {185, 225, 28, 11}, {185, 225, 28, 12}}},
|
|
||||||
{Region: "Malaysia", Group: "Premium TCP Asia", IPs: []net.IP{{146, 70, 15, 7}, {146, 70, 15, 8}, {146, 70, 15, 9}, {146, 70, 15, 10}, {146, 70, 15, 11}, {146, 70, 15, 12}, {146, 70, 15, 13}, {146, 70, 15, 14}, {146, 70, 15, 15}, {146, 70, 15, 16}}},
|
|
||||||
{Region: "Malaysia", Group: "Premium UDP Asia", IPs: []net.IP{{146, 70, 15, 4}, {146, 70, 15, 6}, {146, 70, 15, 7}, {146, 70, 15, 8}, {146, 70, 15, 9}, {146, 70, 15, 12}, {146, 70, 15, 13}, {146, 70, 15, 14}, {146, 70, 15, 15}, {146, 70, 15, 16}}},
|
|
||||||
{Region: "Malta", Group: "Premium TCP Europe", IPs: []net.IP{{176, 125, 230, 132}, {176, 125, 230, 135}, {176, 125, 230, 136}, {176, 125, 230, 137}, {176, 125, 230, 138}, {176, 125, 230, 139}, {176, 125, 230, 140}, {176, 125, 230, 141}, {176, 125, 230, 142}, {176, 125, 230, 143}}},
|
|
||||||
{Region: "Malta", Group: "Premium UDP Europe", IPs: []net.IP{{176, 125, 230, 131}, {176, 125, 230, 133}, {176, 125, 230, 134}, {176, 125, 230, 135}, {176, 125, 230, 137}, {176, 125, 230, 138}, {176, 125, 230, 139}, {176, 125, 230, 140}, {176, 125, 230, 141}, {176, 125, 230, 145}}},
|
|
||||||
{Region: "Mexico", Group: "Premium TCP USA", IPs: []net.IP{{77, 81, 142, 130}, {77, 81, 142, 131}, {77, 81, 142, 137}, {77, 81, 142, 139}, {77, 81, 142, 144}, {77, 81, 142, 149}, {77, 81, 142, 150}, {77, 81, 142, 154}, {77, 81, 142, 156}, {77, 81, 142, 157}}},
|
|
||||||
{Region: "Mexico", Group: "Premium UDP USA", IPs: []net.IP{{77, 81, 142, 130}, {77, 81, 142, 131}, {77, 81, 142, 135}, {77, 81, 142, 138}, {77, 81, 142, 139}, {77, 81, 142, 140}, {77, 81, 142, 144}, {77, 81, 142, 155}, {77, 81, 142, 156}, {77, 81, 142, 157}}},
|
|
||||||
{Region: "Moldova", Group: "Premium UDP Europe", IPs: []net.IP{{178, 175, 130, 243}, {178, 175, 130, 244}, {178, 175, 130, 245}, {178, 175, 130, 246}, {178, 175, 130, 250}, {178, 175, 130, 252}, {178, 175, 130, 253}, {178, 175, 130, 254}, {178, 175, 142, 133}, {178, 175, 142, 134}}},
|
|
||||||
{Region: "Moldova", Group: "Premium TCP Europe", IPs: []net.IP{{178, 175, 130, 243}, {178, 175, 130, 245}, {178, 175, 130, 246}, {178, 175, 130, 250}, {178, 175, 130, 251}, {178, 175, 130, 252}, {178, 175, 130, 254}, {178, 175, 142, 131}, {178, 175, 142, 132}, {178, 175, 142, 134}}},
|
|
||||||
{Region: "Monaco", Group: "Premium UDP Europe", IPs: []net.IP{{95, 181, 233, 131}, {95, 181, 233, 132}, {95, 181, 233, 135}, {95, 181, 233, 136}, {95, 181, 233, 137}, {95, 181, 233, 139}, {95, 181, 233, 140}, {95, 181, 233, 141}, {95, 181, 233, 143}, {95, 181, 233, 144}}},
|
|
||||||
{Region: "Monaco", Group: "Premium TCP Europe", IPs: []net.IP{{95, 181, 233, 131}, {95, 181, 233, 132}, {95, 181, 233, 133}, {95, 181, 233, 136}, {95, 181, 233, 138}, {95, 181, 233, 139}, {95, 181, 233, 140}, {95, 181, 233, 141}, {95, 181, 233, 142}, {95, 181, 233, 144}}},
|
|
||||||
{Region: "Mongolia", Group: "Premium TCP Asia", IPs: []net.IP{{185, 253, 163, 131}, {185, 253, 163, 132}, {185, 253, 163, 134}, {185, 253, 163, 135}, {185, 253, 163, 137}, {185, 253, 163, 139}, {185, 253, 163, 141}, {185, 253, 163, 142}, {185, 253, 163, 144}, {185, 253, 163, 145}}},
|
|
||||||
{Region: "Mongolia", Group: "Premium UDP Asia", IPs: []net.IP{{185, 253, 163, 131}, {185, 253, 163, 132}, {185, 253, 163, 133}, {185, 253, 163, 134}, {185, 253, 163, 137}, {185, 253, 163, 138}, {185, 253, 163, 140}, {185, 253, 163, 142}, {185, 253, 163, 143}, {185, 253, 163, 145}}},
|
|
||||||
{Region: "Montenegro", Group: "Premium TCP Europe", IPs: []net.IP{{176, 125, 229, 131}, {176, 125, 229, 132}, {176, 125, 229, 133}, {176, 125, 229, 134}, {176, 125, 229, 135}, {176, 125, 229, 138}, {176, 125, 229, 139}, {176, 125, 229, 141}, {176, 125, 229, 142}, {176, 125, 229, 144}}},
|
|
||||||
{Region: "Morocco", Group: "Premium UDP Europe", IPs: []net.IP{{95, 181, 232, 131}, {95, 181, 232, 132}, {95, 181, 232, 133}, {95, 181, 232, 135}, {95, 181, 232, 138}, {95, 181, 232, 139}, {95, 181, 232, 140}, {95, 181, 232, 141}, {95, 181, 232, 143}, {95, 181, 232, 144}}},
|
|
||||||
{Region: "Morocco", Group: "Premium TCP Europe", IPs: []net.IP{{95, 181, 232, 131}, {95, 181, 232, 132}, {95, 181, 232, 133}, {95, 181, 232, 134}, {95, 181, 232, 135}, {95, 181, 232, 136}, {95, 181, 232, 137}, {95, 181, 232, 138}, {95, 181, 232, 140}, {95, 181, 232, 142}}},
|
|
||||||
{Region: "Netherlands", Group: "Premium UDP Europe", IPs: []net.IP{{181, 214, 206, 16}, {181, 214, 206, 18}, {191, 96, 168, 19}, {191, 96, 168, 115}, {191, 96, 168, 128}, {195, 78, 54, 31}, {195, 78, 54, 123}, {195, 78, 54, 125}, {195, 78, 54, 131}, {195, 78, 54, 144}}},
|
|
||||||
{Region: "Netherlands", Group: "Premium TCP Europe", IPs: []net.IP{{191, 96, 168, 18}, {191, 96, 168, 40}, {191, 96, 168, 50}, {191, 96, 168, 112}, {191, 96, 168, 127}, {195, 78, 54, 8}, {195, 78, 54, 50}, {195, 78, 54, 118}, {195, 78, 54, 145}, {195, 181, 172, 78}}},
|
|
||||||
{Region: "New Zealand", Group: "Premium TCP Asia", IPs: []net.IP{{114, 141, 194, 2}, {114, 141, 194, 3}, {114, 141, 194, 5}, {114, 141, 194, 6}, {114, 141, 194, 7}, {114, 141, 194, 9}, {114, 141, 194, 10}, {114, 141, 194, 11}, {114, 141, 194, 13}, {114, 141, 194, 14}}},
|
|
||||||
{Region: "New Zealand", Group: "Premium UDP Asia", IPs: []net.IP{{114, 141, 194, 3}, {114, 141, 194, 4}, {114, 141, 194, 6}, {114, 141, 194, 7}, {114, 141, 194, 8}, {114, 141, 194, 9}, {114, 141, 194, 10}, {114, 141, 194, 11}, {114, 141, 194, 13}, {114, 141, 194, 14}}},
|
|
||||||
{Region: "Nigeria", Group: "Premium UDP Europe", IPs: []net.IP{{102, 165, 25, 68}, {102, 165, 25, 69}, {102, 165, 25, 70}, {102, 165, 25, 71}, {102, 165, 25, 72}, {102, 165, 25, 73}, {102, 165, 25, 74}, {102, 165, 25, 76}, {102, 165, 25, 77}, {102, 165, 25, 78}}},
|
|
||||||
{Region: "Nigeria", Group: "Premium TCP Europe", IPs: []net.IP{{102, 165, 25, 68}, {102, 165, 25, 70}, {102, 165, 25, 71}, {102, 165, 25, 72}, {102, 165, 25, 73}, {102, 165, 25, 74}, {102, 165, 25, 75}, {102, 165, 25, 76}, {102, 165, 25, 77}, {102, 165, 25, 78}}},
|
|
||||||
{Region: "Norway", Group: "Premium TCP Europe", IPs: []net.IP{{45, 12, 223, 131}, {45, 12, 223, 132}, {45, 12, 223, 134}, {45, 12, 223, 138}, {45, 12, 223, 141}, {82, 102, 27, 93}, {185, 206, 225, 235}, {185, 253, 97, 236}, {185, 253, 97, 245}, {185, 253, 97, 247}}},
|
|
||||||
{Region: "Norway", Group: "Premium UDP Europe", IPs: []net.IP{{45, 12, 223, 136}, {45, 12, 223, 141}, {82, 102, 27, 93}, {185, 206, 225, 28}, {185, 206, 225, 29}, {185, 206, 225, 231}, {185, 206, 225, 233}, {185, 253, 97, 245}, {185, 253, 97, 247}, {185, 253, 97, 249}}},
|
|
||||||
{Region: "Pakistan", Group: "Premium TCP Asia", IPs: []net.IP{{146, 70, 12, 3}, {146, 70, 12, 4}, {146, 70, 12, 5}, {146, 70, 12, 6}, {146, 70, 12, 7}, {146, 70, 12, 8}, {146, 70, 12, 9}, {146, 70, 12, 11}, {146, 70, 12, 12}, {146, 70, 12, 14}}},
|
|
||||||
{Region: "Pakistan", Group: "Premium UDP Asia", IPs: []net.IP{{146, 70, 12, 3}, {146, 70, 12, 4}, {146, 70, 12, 5}, {146, 70, 12, 7}, {146, 70, 12, 9}, {146, 70, 12, 10}, {146, 70, 12, 11}, {146, 70, 12, 12}, {146, 70, 12, 13}, {146, 70, 12, 14}}},
|
|
||||||
{Region: "Panama", Group: "Premium UDP Europe", IPs: []net.IP{{91, 90, 126, 131}, {91, 90, 126, 132}, {91, 90, 126, 133}, {91, 90, 126, 135}, {91, 90, 126, 137}, {91, 90, 126, 139}, {91, 90, 126, 140}, {91, 90, 126, 141}, {91, 90, 126, 143}, {91, 90, 126, 145}}},
|
|
||||||
{Region: "Panama", Group: "Premium TCP Europe", IPs: []net.IP{{91, 90, 126, 133}, {91, 90, 126, 134}, {91, 90, 126, 135}, {91, 90, 126, 137}, {91, 90, 126, 138}, {91, 90, 126, 139}, {91, 90, 126, 140}, {91, 90, 126, 141}, {91, 90, 126, 142}, {91, 90, 126, 145}}},
|
|
||||||
{Region: "Philippines", Group: "Premium TCP Asia", IPs: []net.IP{{188, 214, 125, 35}, {188, 214, 125, 36}, {188, 214, 125, 37}, {188, 214, 125, 38}, {188, 214, 125, 44}, {188, 214, 125, 52}, {188, 214, 125, 53}, {188, 214, 125, 54}, {188, 214, 125, 60}, {188, 214, 125, 62}}},
|
|
||||||
{Region: "Philippines", Group: "Premium UDP Asia", IPs: []net.IP{{188, 214, 125, 35}, {188, 214, 125, 37}, {188, 214, 125, 39}, {188, 214, 125, 41}, {188, 214, 125, 42}, {188, 214, 125, 43}, {188, 214, 125, 48}, {188, 214, 125, 52}, {188, 214, 125, 57}, {188, 214, 125, 62}}},
|
|
||||||
{Region: "Poland", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 156, 5}, {37, 120, 156, 14}, {37, 120, 156, 18}, {37, 120, 156, 21}, {37, 120, 156, 23}, {37, 120, 156, 39}, {37, 120, 156, 40}, {51, 75, 56, 34}, {51, 75, 56, 37}, {51, 75, 56, 43}}},
|
|
||||||
{Region: "Poland", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 156, 9}, {37, 120, 156, 10}, {37, 120, 156, 12}, {37, 120, 156, 17}, {37, 120, 156, 20}, {37, 120, 156, 22}, {37, 120, 156, 39}, {51, 75, 56, 41}, {51, 75, 56, 43}, {51, 75, 56, 44}}},
|
|
||||||
{Region: "Portugal", Group: "Premium TCP Europe", IPs: []net.IP{{89, 26, 243, 2}, {89, 26, 243, 100}, {89, 26, 243, 197}, {89, 26, 243, 198}, {89, 26, 243, 216}, {89, 26, 243, 219}, {89, 26, 243, 222}, {89, 26, 243, 224}, {89, 26, 243, 226}, {89, 26, 243, 227}}},
|
|
||||||
{Region: "Qatar", Group: "Premium TCP Europe", IPs: []net.IP{{95, 181, 234, 131}, {95, 181, 234, 133}, {95, 181, 234, 134}, {95, 181, 234, 135}, {95, 181, 234, 136}, {95, 181, 234, 137}, {95, 181, 234, 138}, {95, 181, 234, 142}, {95, 181, 234, 143}, {95, 181, 234, 144}}},
|
|
||||||
{Region: "Qatar", Group: "Premium UDP Europe", IPs: []net.IP{{95, 181, 234, 131}, {95, 181, 234, 132}, {95, 181, 234, 134}, {95, 181, 234, 135}, {95, 181, 234, 136}, {95, 181, 234, 138}, {95, 181, 234, 140}, {95, 181, 234, 142}, {95, 181, 234, 143}, {95, 181, 234, 144}}},
|
|
||||||
{Region: "Russian Federation", Group: "Premium UDP Europe", IPs: []net.IP{{5, 8, 16, 74}, {5, 8, 16, 88}, {5, 8, 16, 89}, {5, 8, 16, 90}, {5, 8, 16, 91}, {5, 8, 16, 94}, {5, 8, 16, 109}, {5, 8, 16, 124}, {5, 8, 16, 125}, {5, 8, 16, 131}}},
|
|
||||||
{Region: "Russian Federation", Group: "Premium TCP Europe", IPs: []net.IP{{5, 8, 16, 75}, {5, 8, 16, 87}, {5, 8, 16, 94}, {5, 8, 16, 116}, {5, 8, 16, 117}, {5, 8, 16, 118}, {5, 8, 16, 126}, {5, 8, 16, 133}, {5, 8, 16, 134}, {5, 8, 16, 142}}},
|
|
||||||
{Region: "Saudi Arabia", Group: "Premium TCP Europe", IPs: []net.IP{{95, 181, 235, 131}, {95, 181, 235, 132}, {95, 181, 235, 133}, {95, 181, 235, 134}, {95, 181, 235, 135}, {95, 181, 235, 136}, {95, 181, 235, 137}, {95, 181, 235, 138}, {95, 181, 235, 140}, {95, 181, 235, 141}}},
|
|
||||||
{Region: "Saudi Arabia", Group: "Premium UDP Europe", IPs: []net.IP{{95, 181, 235, 131}, {95, 181, 235, 132}, {95, 181, 235, 133}, {95, 181, 235, 134}, {95, 181, 235, 137}, {95, 181, 235, 138}, {95, 181, 235, 139}, {95, 181, 235, 140}, {95, 181, 235, 141}, {95, 181, 235, 143}}},
|
|
||||||
{Region: "Serbia", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 193, 180}, {37, 120, 193, 181}, {37, 120, 193, 183}, {37, 120, 193, 184}, {37, 120, 193, 185}, {37, 120, 193, 189}, {141, 98, 103, 35}, {141, 98, 103, 40}, {141, 98, 103, 43}, {141, 98, 103, 46}}},
|
|
||||||
{Region: "Singapore", Group: "Premium TCP Asia", IPs: []net.IP{{84, 17, 39, 162}, {84, 17, 39, 167}, {84, 17, 39, 170}, {84, 17, 39, 175}, {84, 17, 39, 176}, {84, 17, 39, 177}, {84, 17, 39, 179}, {84, 17, 39, 181}, {84, 17, 39, 183}, {84, 17, 39, 185}}},
|
|
||||||
{Region: "Singapore", Group: "Premium UDP Asia", IPs: []net.IP{{84, 17, 39, 162}, {84, 17, 39, 165}, {84, 17, 39, 166}, {84, 17, 39, 167}, {84, 17, 39, 168}, {84, 17, 39, 171}, {84, 17, 39, 172}, {84, 17, 39, 173}, {84, 17, 39, 175}, {84, 17, 39, 185}}},
|
|
||||||
{Region: "Slovakia", Group: "Premium TCP Europe", IPs: []net.IP{{185, 245, 85, 227}, {185, 245, 85, 228}, {185, 245, 85, 229}, {185, 245, 85, 230}, {185, 245, 85, 231}, {185, 245, 85, 232}, {185, 245, 85, 233}, {185, 245, 85, 234}, {185, 245, 85, 235}, {185, 245, 85, 236}}},
|
|
||||||
{Region: "Slovakia", Group: "Premium UDP Europe", IPs: []net.IP{{185, 245, 85, 227}, {185, 245, 85, 228}, {185, 245, 85, 229}, {185, 245, 85, 230}, {185, 245, 85, 231}, {185, 245, 85, 232}, {185, 245, 85, 233}, {185, 245, 85, 234}, {185, 245, 85, 235}, {185, 245, 85, 236}}},
|
|
||||||
{Region: "Slovenia", Group: "Premium TCP Europe", IPs: []net.IP{{195, 80, 150, 211}, {195, 80, 150, 212}, {195, 80, 150, 213}, {195, 80, 150, 214}, {195, 80, 150, 215}, {195, 80, 150, 216}, {195, 80, 150, 217}, {195, 80, 150, 219}, {195, 80, 150, 221}, {195, 80, 150, 222}}},
|
|
||||||
{Region: "Slovenia", Group: "Premium UDP Europe", IPs: []net.IP{{195, 80, 150, 211}, {195, 80, 150, 212}, {195, 80, 150, 213}, {195, 80, 150, 214}, {195, 80, 150, 217}, {195, 80, 150, 218}, {195, 80, 150, 219}, {195, 80, 150, 220}, {195, 80, 150, 221}, {195, 80, 150, 222}}},
|
|
||||||
{Region: "South Africa", Group: "Premium TCP Europe", IPs: []net.IP{{197, 85, 7, 26}, {197, 85, 7, 27}, {197, 85, 7, 28}, {197, 85, 7, 29}, {197, 85, 7, 30}, {197, 85, 7, 31}, {197, 85, 7, 131}, {197, 85, 7, 132}, {197, 85, 7, 133}, {197, 85, 7, 134}}},
|
|
||||||
{Region: "South Africa", Group: "Premium UDP Asia", IPs: []net.IP{{154, 127, 50, 212}, {154, 127, 50, 215}, {154, 127, 50, 218}, {154, 127, 50, 219}, {154, 127, 50, 222}, {154, 127, 60, 195}, {154, 127, 60, 198}, {154, 127, 60, 201}, {154, 127, 60, 204}, {154, 127, 60, 205}}},
|
|
||||||
{Region: "South Africa", Group: "Premium TCP Asia", IPs: []net.IP{{154, 127, 50, 212}, {154, 127, 50, 213}, {154, 127, 50, 216}, {154, 127, 50, 217}, {154, 127, 60, 195}, {154, 127, 60, 199}, {154, 127, 60, 201}, {154, 127, 60, 203}, {154, 127, 60, 205}, {154, 127, 60, 206}}},
|
|
||||||
{Region: "South Africa", Group: "Premium UDP Europe", IPs: []net.IP{{197, 85, 7, 26}, {197, 85, 7, 27}, {197, 85, 7, 28}, {197, 85, 7, 29}, {197, 85, 7, 30}, {197, 85, 7, 31}, {197, 85, 7, 131}, {197, 85, 7, 132}, {197, 85, 7, 133}, {197, 85, 7, 134}}},
|
|
||||||
{Region: "Spain", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 142, 39}, {37, 120, 142, 42}, {37, 120, 142, 51}, {37, 120, 142, 56}, {37, 120, 142, 170}, {82, 102, 26, 211}, {82, 102, 26, 212}, {82, 102, 26, 213}, {82, 102, 26, 214}, {84, 17, 62, 138}}},
|
|
||||||
{Region: "Spain", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 142, 44}, {37, 120, 142, 59}, {37, 120, 142, 60}, {45, 128, 39, 6}, {82, 102, 26, 195}, {82, 102, 26, 198}, {84, 17, 62, 131}, {84, 17, 62, 144}, {84, 17, 62, 152}, {185, 253, 99, 201}}},
|
|
||||||
{Region: "Sri Lanka", Group: "Premium UDP Europe", IPs: []net.IP{{95, 181, 239, 132}, {95, 181, 239, 133}, {95, 181, 239, 135}, {95, 181, 239, 136}, {95, 181, 239, 137}, {95, 181, 239, 138}, {95, 181, 239, 139}, {95, 181, 239, 141}, {95, 181, 239, 143}, {95, 181, 239, 144}}},
|
|
||||||
{Region: "Sri Lanka", Group: "Premium TCP Europe", IPs: []net.IP{{95, 181, 239, 131}, {95, 181, 239, 132}, {95, 181, 239, 133}, {95, 181, 239, 135}, {95, 181, 239, 138}, {95, 181, 239, 139}, {95, 181, 239, 140}, {95, 181, 239, 141}, {95, 181, 239, 142}, {95, 181, 239, 143}}},
|
|
||||||
{Region: "Sweden", Group: "Premium UDP Europe", IPs: []net.IP{{188, 126, 73, 206}, {188, 126, 73, 221}, {188, 126, 73, 222}, {188, 126, 79, 15}, {188, 126, 79, 16}, {188, 126, 79, 26}, {188, 126, 79, 30}, {195, 246, 120, 171}, {195, 246, 120, 175}, {195, 246, 120, 176}}},
|
|
||||||
{Region: "Sweden", Group: "Premium TCP Europe", IPs: []net.IP{{188, 126, 73, 197}, {188, 126, 73, 200}, {188, 126, 73, 203}, {188, 126, 79, 13}, {188, 126, 79, 14}, {188, 126, 79, 16}, {188, 126, 79, 24}, {188, 126, 79, 25}, {188, 126, 79, 28}, {195, 246, 120, 172}}},
|
|
||||||
{Region: "Switzerland", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 52, 18}, {84, 17, 52, 19}, {84, 17, 52, 24}, {84, 17, 52, 39}, {84, 17, 52, 78}, {84, 17, 52, 82}, {185, 32, 222, 5}, {195, 225, 118, 36}, {195, 225, 118, 42}, {195, 225, 118, 60}}},
|
|
||||||
{Region: "Switzerland", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 52, 14}, {84, 17, 52, 39}, {84, 17, 52, 42}, {84, 17, 52, 50}, {84, 17, 52, 55}, {84, 17, 52, 84}, {185, 32, 222, 17}, {185, 32, 222, 106}, {185, 189, 150, 53}, {195, 225, 118, 57}}},
|
|
||||||
{Region: "Taiwan", Group: "Premium UDP Asia", IPs: []net.IP{{45, 133, 181, 106}, {45, 133, 181, 107}, {45, 133, 181, 111}, {45, 133, 181, 112}, {45, 133, 181, 113}, {45, 133, 181, 116}, {45, 133, 181, 119}, {45, 133, 181, 120}, {45, 133, 181, 122}, {45, 133, 181, 126}}},
|
|
||||||
{Region: "Taiwan", Group: "Premium TCP Asia", IPs: []net.IP{{45, 133, 181, 99}, {45, 133, 181, 100}, {45, 133, 181, 105}, {45, 133, 181, 108}, {45, 133, 181, 113}, {45, 133, 181, 116}, {45, 133, 181, 119}, {45, 133, 181, 120}, {45, 133, 181, 121}, {45, 133, 181, 125}}},
|
|
||||||
{Region: "Thailand", Group: "Premium TCP Asia", IPs: []net.IP{{146, 70, 13, 3}, {146, 70, 13, 6}, {146, 70, 13, 7}, {146, 70, 13, 8}, {146, 70, 13, 9}, {146, 70, 13, 10}, {146, 70, 13, 12}, {146, 70, 13, 13}, {146, 70, 13, 14}, {146, 70, 13, 16}}},
|
|
||||||
{Region: "Turkey", Group: "Premium UDP Europe", IPs: []net.IP{{188, 213, 34, 5}, {188, 213, 34, 7}, {188, 213, 34, 10}, {188, 213, 34, 14}, {188, 213, 34, 15}, {188, 213, 34, 23}, {188, 213, 34, 26}, {188, 213, 34, 27}, {188, 213, 34, 104}, {188, 213, 34, 106}}},
|
|
||||||
{Region: "Ukraine", Group: "Premium UDP Europe", IPs: []net.IP{{31, 28, 163, 36}, {31, 28, 163, 50}, {31, 28, 163, 51}, {31, 28, 163, 59}, {62, 149, 7, 162}, {62, 149, 7, 163}, {62, 149, 29, 35}, {62, 149, 29, 37}, {62, 149, 29, 45}, {62, 149, 29, 55}}},
|
|
||||||
{Region: "United Arab Emirates", Group: "Premium TCP Europe", IPs: []net.IP{{217, 138, 193, 179}, {217, 138, 193, 180}, {217, 138, 193, 182}, {217, 138, 193, 183}, {217, 138, 193, 184}, {217, 138, 193, 185}, {217, 138, 193, 187}, {217, 138, 193, 188}, {217, 138, 193, 189}, {217, 138, 193, 190}}},
|
|
||||||
{Region: "United Arab Emirates", Group: "Premium UDP Europe", IPs: []net.IP{{217, 138, 193, 179}, {217, 138, 193, 180}, {217, 138, 193, 181}, {217, 138, 193, 182}, {217, 138, 193, 183}, {217, 138, 193, 184}, {217, 138, 193, 185}, {217, 138, 193, 186}, {217, 138, 193, 188}, {217, 138, 193, 189}}},
|
|
||||||
{Region: "United Kingdom", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 172, 140}, {45, 133, 172, 157}, {95, 154, 200, 161}, {95, 154, 200, 166}, {95, 154, 200, 168}, {181, 215, 176, 138}, {181, 215, 176, 155}, {181, 215, 176, 243}, {191, 101, 209, 147}, {194, 110, 13, 103}}},
|
|
||||||
{Region: "United Kingdom", Group: "Premium TCP Europe", IPs: []net.IP{{45, 133, 172, 112}, {45, 133, 172, 113}, {45, 133, 172, 134}, {45, 133, 172, 140}, {45, 133, 173, 47}, {95, 154, 200, 173}, {181, 215, 176, 157}, {191, 101, 209, 70}, {191, 101, 209, 93}, {194, 110, 13, 119}}},
|
|
||||||
{Region: "United States", Group: "Premium TCP USA", IPs: []net.IP{{23, 82, 9, 164}, {23, 105, 161, 115}, {23, 105, 191, 51}, {45, 89, 173, 214}, {84, 17, 35, 55}, {84, 17, 40, 73}, {84, 17, 40, 99}, {156, 146, 37, 98}, {156, 146, 59, 66}, {212, 102, 41, 28}}},
|
|
||||||
{Region: "Venezuela", Group: "Premium UDP USA", IPs: []net.IP{{95, 181, 237, 131}, {95, 181, 237, 132}, {95, 181, 237, 133}, {95, 181, 237, 134}, {95, 181, 237, 135}, {95, 181, 237, 136}, {95, 181, 237, 137}, {95, 181, 237, 139}, {95, 181, 237, 140}, {95, 181, 237, 143}}},
|
|
||||||
{Region: "Venezuela", Group: "Premium TCP USA", IPs: []net.IP{{95, 181, 237, 132}, {95, 181, 237, 133}, {95, 181, 237, 134}, {95, 181, 237, 135}, {95, 181, 237, 136}, {95, 181, 237, 138}, {95, 181, 237, 139}, {95, 181, 237, 142}, {95, 181, 237, 143}, {95, 181, 237, 144}}},
|
|
||||||
{Region: "Vietnam", Group: "Premium UDP Asia", IPs: []net.IP{{188, 214, 152, 99}, {188, 214, 152, 100}, {188, 214, 152, 101}, {188, 214, 152, 102}, {188, 214, 152, 103}, {188, 214, 152, 104}, {188, 214, 152, 105}, {188, 214, 152, 107}, {188, 214, 152, 108}, {188, 214, 152, 110}}},
|
|
||||||
{Region: "Vietnam", Group: "Premium TCP Asia", IPs: []net.IP{{188, 214, 152, 99}, {188, 214, 152, 100}, {188, 214, 152, 101}, {188, 214, 152, 102}, {188, 214, 152, 103}, {188, 214, 152, 104}, {188, 214, 152, 106}, {188, 214, 152, 107}, {188, 214, 152, 108}, {188, 214, 152, 109}}},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
37
internal/constants/expressvpn.go
Normal file
37
internal/constants/expressvpn.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package constants
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:lll
|
||||||
|
const (
|
||||||
|
ExpressvpnCert = "MIIDTjCCAregAwIBAgIDKzZvMA0GCSqGSIb3DQEBCwUAMIGFMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMGA1UEChMMRm9ydC1GdW5zdG9uMRgwFgYDVQQDEw9Gb3J0LUZ1bnN0b24gQ0ExITAfBgkqhkiG9w0BCQEWEm1lQG15aG9zdC5teWRvbWFpbjAgFw0xNjExMDMwMzA2MThaGA8yMDY2MTEwMzAzMDYxOFowgYoxCzAJBgNVBAYTAlZHMQwwCgYDVQQIDANCVkkxEzARBgNVBAoMCkV4cHJlc3NWUE4xEzARBgNVBAsMCkV4cHJlc3NWUE4xHDAaBgNVBAMME2V4cHJlc3N2cG5fY3VzdG9tZXIxJTAjBgkqhkiG9w0BCQEWFnN1cHBvcnRAZXhwcmVzc3Zwbi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrOYt/KOi2uMDGev3pXg8j1SO4J/4EVWDF7vJcKr2jrZlqD/zuAFx2W1YWvwumPO6PKH4PU9621aNdiumaUkv/RplCfznnnxqobhJuTE2oA+rS1bOq+9OhHwF9jgNXNVk+XX4d0toST5uGE6Z3OdmPBur8o5AlCf78PDSAwpFOw5HrgLqOEU4hTweC1/czX2VsvsHv22HRI6JMZgP8gGQii/p9iukqfaJvGdPciL5p1QRBUQIi8P8pNvEp1pVIpxYj7/LOUqb2DxFvgmp2v1IQ0Yu88SWsFk84+xAYHzfkLyS31Sqj5uLRBnJqx3fIlOihQ50GI72fwPMwo+OippvVAgMBAAGjPzA9MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgSwMB0GA1UdDgQWBBSkBM1TCX9kBgFsv2RmOzudMXa9njANBgkqhkiG9w0BAQsFAAOBgQA+2e4b+33zFmA+1ZQ46kWkfiB+fEeDyMwMLeYYyDS2d8mZhNZKdOw7dy4Ifz9Vqzp4aKuQ6j61c6k1UaQQL0tskqWVzslSFvs9NZyUAJLLdGUc5TT2MiLwiXQwd4UvH6bGeePdhvB4+ZbW7VMD7TE8hZhjhAL4F6yAP1EQvg3LDA=="
|
||||||
|
ExpressvpnRSAKey = "MIIEpAIBAAKCAQEAqzmLfyjotrjAxnr96V4PI9UjuCf+BFVgxe7yXCq9o62Zag/87gBcdltWFr8Lpjzujyh+D1PettWjXYrpmlJL/0aZQn85558aqG4SbkxNqAPq0tWzqvvToR8BfY4DVzVZPl1+HdLaEk+bhhOmdznZjwbq/KOQJQn+/Dw0gMKRTsOR64C6jhFOIU8Hgtf3M19lbL7B79th0SOiTGYD/IBkIov6fYrpKn2ibxnT3Ii+adUEQVECIvD/KTbxKdaVSKcWI+/yzlKm9g8Rb4Jqdr9SENGLvPElrBZPOPsQGB835C8kt9Uqo+bi0QZyasd3yJTooUOdBiO9n8DzMKPjoqab1QIDAQABAoIBAHgsekC0SKi+AOcNOZqJ3pxqophE0V7fQX2KWGXhxZnUZMFxGTc936deMYzjZ1y0lUa6x8cgOUcfqHol3hDmw9oWBckLHGv5Wi9umdb6DOLoZO62+FQATSdfaJ9jheq2Ub2YxsRN0apaXzB6KDKz0oM0+sZ4Udn9Kw6DfuIELRIWwEx4w0v3gKW7YLC4Jkc4AwLkPK03xEA/qImfkCmaMPLhrgVQt+IFfP8bXzL7CCC04rNU/IS8pyjex+iUolnQZlbXntF7Bm4V2mz0827ZVqrgAb/hEQRlsTW3rRkVh+rrdoUE7BCZRTFmRCbLoShjN6XuSf4sAus8ch4UEN12gN0CgYEA4o/tSvij1iPaXLmt4KOEuxqmSGB8MLKhFde8lBbNdrDgxiIH9bH7khKx15XRTX0qLDbs8b2/UJygZG0Aa1kIBqZTXTgeMAuxPRTesALJPdqQ/ROnbJcdFkI7gllrAG8VB0fH4wTRsRd0vWEB6YlCdE107u6LEsLAHxOj9Q5819cCgYEAwXjx9RkQ2qITBx5Ewib8YsltA0n3cmRomPicLlsnKV5DfvyCLpFIsZ1h3f9dUpfxRLwzp8wcoLiq9cCoOGdu1udw/yBTqmhaXWhUK/g77f9Ze2ZB1OEhuyKLYJ1vW/h/Z/a1aPCMxZqsDTPCePsuO8Cez5gqs8LjM3W7EyzRxDMCgYEAvhHrDFt975fSiLoJgo0MPIAGAnBXn+8sLwv3m/FpW+rWF8LTFK/Fku12H5wDpNOdvswxijkauIE+GiJMGMLvdcyx4WHECaC1h73reJRNykOEIZ0Md5BrCZJ1JEzp9Mo8RQhWTEFtvfkkqgApP4g0pSeaMx0StaGG1kt+4IbP+68CgYBrZdQKlquAck/Vt7u7eyDHRcE5/ilaWtqlb/xizz7h++3D5C/v4b5UumTFcyg+3RGVclPKZcfOgDSGzzeSd/hTW46iUTOgeOUQzQVMkzPRXdoyYgVRQtgSpY5xR3O1vjAbahwx8LZ0SvQPMBhYSDbV/Isr+fBacWjl/AipEEwxeQKBgQDdrAEnVlOFoCLw4sUjsPoxkLjhTAgI7CYk5NNxX67Rnj0tp+Y49+sGUhl5sCGfMKkLShiON5P2oxZa+B0aPtQjsdnsFPa1uaZkK4c++SS6AetzYRpVDLmLp7/1CulE0z3O0sBekpwiuaqLJ9ZccC81g4+2j8j6c50rIAct3hxIxw=="
|
||||||
|
ExpressvpnTLSAuthOpenvpnStaticKeyV1 = "48d9999bd71095b10649c7cb471c1051b1afdece597cea06909b99303a18c67401597b12c04a787e98cdb619ee960d90a0165529dc650f3a5c6fbe77c91c137dcf55d863fcbe314df5f0b45dbe974d9bde33ef5b4803c3985531c6c23ca6906d6cd028efc8585d1b9e71003566bd7891b9cc9212bcba510109922eed87f5c8e66d8e59cbd82575261f02777372b2cd4ca5214c4a6513ff26dd568f574fd40d6cd450fc788160ff68434ce2bf6afb00e710a3198538f14c4d45d84ab42637872e778a6b35a124e700920879f1d003ba93dccdb953cdf32bea03f365760b0ed8002098d4ce20d045b45a83a8432cc737677aed27125592a7148d25c87fdbe0a3f6"
|
||||||
|
ExpressvpnCA = "MIIF+DCCA+CgAwIBAgIBATANBgkqhkiG9w0BAQ0FADCBhDELMAkGA1UEBhMCVkcxDDAKBgNVBAgMA0JWSTETMBEGA1UECgwKRXhwcmVzc1ZQTjETMBEGA1UECwwKRXhwcmVzc1ZQTjEWMBQGA1UEAwwNRXhwcmVzc1ZQTiBDQTElMCMGCSqGSIb3DQEJARYWc3VwcG9ydEBleHByZXNzdnBuLmNvbTAeFw0xNTEwMjEwMDAwMDBaFw0yNjA0MDEyMTEyMDBaMIGEMQswCQYDVQQGEwJWRzEMMAoGA1UECAwDQlZJMRMwEQYDVQQKDApFeHByZXNzVlBOMRMwEQYDVQQLDApFeHByZXNzVlBOMRYwFAYDVQQDDA1FeHByZXNzVlBOIENBMSUwIwYJKoZIhvcNAQkBFhZzdXBwb3J0QGV4cHJlc3N2cG4uY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxzXvHZ25OsESKRMQFINHJNqE9kVRLWJS50oVB2jxobudPhCsWvJSApvar8CB2RrqkVMhXu2HT3FBtDL91INg070qAyjjRpzEbDPWqQ1+G0tk0sjiJt2mXPJK2IlNFnhe6rTs09Pkpcp8qRhfZay/dIlmagohQAr4JvYL1Ajg9A3sLb8JkY03H6GhOF8EKYTqhrEppCcg4sQKQhNSytRoQAm8Ta+tnTYIedwWpqjUXP9YXFOvljPaixfYug24eAkpTjeuWTcELSyfnuiBeK+z9+5OYunhqFt2QZMq33kLFZGMN2gHRCzngxxphurypsPRo7jiFgQI1yLt8uZsEZ+otGEK91jjKfOC+g9TBy2RUtxk1neWcQ6syXDuc3rBNrGA8iM0ZoEqQ1BC8xWr3NYlSjqN+1mgpTAX3/Dxze4GzHd7AmYaYJV8xnKBVNphlMlg1giCAu5QXjMxPbfCgZiEFq/uq0SOKQJeT3AI/uVPSvwCMWByjyMbDpKKAK8Hy3UT5m4bCNu8J7bxj+vdnq0A2HPwtF0FwBl/TIM3zNsyFrZZ0j6jLRT50mFsgDBKcD4L/J5rjdCsKPu5rodhxe38rCx2GknP1Zkov4yoVCcR48+CQwg3oBkq0/EflvWUvcYApzs9SomUM/g+8Q/V0WOfJmFWuxN9YntZlnzHRSRjrvMCAwEAAaNzMHEwHQYDVR0OBBYEFIzmQGj8xS+0LLklwqHD45VVOZRJMB8GA1UdIwQYMBaAFIzmQGj8xS+0LLklwqHD45VVOZRJMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIBFjANBgkqhkiG9w0BAQ0FAAOCAgEAbHfuMKtojm1NgX7qSU2Rm2B5L8G0FuFP0L40dj8O5WHt45j2z8coMK90vrUnQEZNQmRzot7v3XjVzVlxBWYSsCEApTsSDNi/4BNFP8H/BUUtJuy2GFTO4wDVJnqNkZOHBmyVD75s1Y+W8a+zB4jkMeDEhOHZdwQ0l1fJDDgXal5f1UT5F5WH6/RwHmWTwX4GxuCiIVtx70CjkXqhM8yZtTp1UtHLRNYcNSIes0vrAPHPgoA5z9B8UvsOjuP+mfcjzi0LGGrY+2pJu0BKO2dRnarIZZABETIisI3FokoTszx5jpRPyxyUTuRDKWHrvi0PPtOmC8nFahfugWFUi6uBsqCaSeuex+ahnTPCq0b1l0Ozpg0YeE8CW1TL9Y92b01up2c+PP6wZOIm3JyTH+L5smDFbh80V42dKyGNdPXMg5IcJhj3YfAy4k8h/qbWY57KFcIzKx40bFsoI7PeydbGtT/dIoFLSZRLW5bleXNgG9mXZp270UeEC6CpATCS6uVl8LVT1I02uulHUpFaRmTEOrmMxsXGt6UAwYTY55K/B8uuID341xKbeC0kzhuN2gsL5UJaocBHyWK/AqwbeBttdhOCLwoaj7+nSViPxICObKrg3qavGNCvtwy/fEegK9X/wlp2e2CFlIhFbadeXOBr9Fn8ypYPP17mTqe98OJYM04="
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExpressvpnCountriesChoices(servers []models.ExpressvpnServer) (choices []string) {
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Country
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExpressvpnCityChoices(servers []models.ExpressvpnServer) (choices []string) {
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].City
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExpressvpnHostnameChoices(servers []models.ExpressvpnServer) (choices []string) {
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Hostname
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
package constants
|
package constants
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -12,110 +10,18 @@ const (
|
|||||||
FastestvpnOpenvpnStaticKeyV1 = "697fe793b32cb5091d30f2326d5d124a9412e93d0a44ef7361395d76528fcbfc82c3859dccea70a93cfa8fae409709bff75f844cf5ff0c237f426d0c20969233db0e706edb6bdf195ec3dc11b3f76bc807a77e74662d9a800c8cd1144ebb67b7f0d3f1281d1baf522bfe03b7c3f963b1364fc0769400e413b61ca7b43ab19fac9e0f77e41efd4bda7fd77b1de2d7d7855cbbe3e620cecceac72c21a825b243e651f44d90e290e09c3ad650de8fca99c858bc7caad584bc69b11e5c9fd9381c69c505ec487a65912c672d83ed0113b5a74ddfbd3ab33b3683cec593557520a72c4d6cce46111f56f3396cc3ce7183edce553c68ea0796cf6c4375fad00aaa2a42"
|
FastestvpnOpenvpnStaticKeyV1 = "697fe793b32cb5091d30f2326d5d124a9412e93d0a44ef7361395d76528fcbfc82c3859dccea70a93cfa8fae409709bff75f844cf5ff0c237f426d0c20969233db0e706edb6bdf195ec3dc11b3f76bc807a77e74662d9a800c8cd1144ebb67b7f0d3f1281d1baf522bfe03b7c3f963b1364fc0769400e413b61ca7b43ab19fac9e0f77e41efd4bda7fd77b1de2d7d7855cbbe3e620cecceac72c21a825b243e651f44d90e290e09c3ad650de8fca99c858bc7caad584bc69b11e5c9fd9381c69c505ec487a65912c672d83ed0113b5a74ddfbd3ab33b3683cec593557520a72c4d6cce46111f56f3396cc3ce7183edce553c68ea0796cf6c4375fad00aaa2a42"
|
||||||
)
|
)
|
||||||
|
|
||||||
func FastestvpnCountriesChoices() (choices []string) {
|
func FastestvpnCountriesChoices(servers []models.FastestvpnServer) (choices []string) {
|
||||||
servers := FastestvpnServers()
|
|
||||||
choices = make([]string, len(servers))
|
choices = make([]string, len(servers))
|
||||||
for i := range servers {
|
for i := range servers {
|
||||||
choices[i] = servers[i].Country
|
choices[i] = servers[i].Country
|
||||||
}
|
}
|
||||||
return choices
|
return makeUnique(choices)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FastestvpnHostnameChoices() (choices []string) {
|
func FastestvpnHostnameChoices(servers []models.FastestvpnServer) (choices []string) {
|
||||||
servers := FastestvpnServers()
|
|
||||||
choices = make([]string, len(servers))
|
choices = make([]string, len(servers))
|
||||||
for i := range servers {
|
for i := range servers {
|
||||||
choices[i] = servers[i].Hostname
|
choices[i] = servers[i].Hostname
|
||||||
}
|
}
|
||||||
return choices
|
return makeUnique(choices)
|
||||||
}
|
|
||||||
|
|
||||||
// FastestvpnServers returns the list of all VPN servers for FastestVPN.
|
|
||||||
//nolint:lll
|
|
||||||
func FastestvpnServers() []models.FastestvpnServer {
|
|
||||||
return []models.FastestvpnServer{
|
|
||||||
{Country: "Australia", Hostname: "au-sd-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{139, 99, 149, 10}}},
|
|
||||||
{Country: "Australia", Hostname: "au-sd-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{139, 99, 149, 10}}},
|
|
||||||
{Country: "Australia", Hostname: "au2-sd-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{139, 99, 131, 126}}},
|
|
||||||
{Country: "Australia", Hostname: "au2-sd-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{139, 99, 131, 126}}},
|
|
||||||
{Country: "Austria", Hostname: "at.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{185, 210, 219, 86}}},
|
|
||||||
{Country: "Belgium", Hostname: "bel1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{217, 138, 211, 67}}},
|
|
||||||
{Country: "Belgium", Hostname: "bel2.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{217, 138, 211, 68}}},
|
|
||||||
{Country: "Belgium", Hostname: "bel3.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{217, 138, 211, 69}}},
|
|
||||||
{Country: "Brazil", Hostname: "br-jp-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{45, 179, 88, 31}}},
|
|
||||||
{Country: "Brazil", Hostname: "br-jp-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{45, 179, 88, 31}}},
|
|
||||||
{Country: "Bulgaria", Hostname: "bg.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{37, 46, 114, 46}}},
|
|
||||||
{Country: "Canada", Hostname: "ca1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{192, 99, 103, 48}}},
|
|
||||||
{Country: "Canada", Hostname: "ca2.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{192, 99, 103, 49}}},
|
|
||||||
{Country: "Czechia", Hostname: "cz-pr-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{185, 216, 35, 218}}},
|
|
||||||
{Country: "Czechia", Hostname: "cz-pr-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{185, 216, 35, 218}}},
|
|
||||||
{Country: "Denmark", Hostname: "dk.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{185, 245, 84, 70}}},
|
|
||||||
{Country: "Finland", Hostname: "fi-hs-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{194, 34, 132, 19}}},
|
|
||||||
{Country: "Finland", Hostname: "fi-hs-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{194, 34, 132, 19}}},
|
|
||||||
{Country: "France", Hostname: "fr-rb-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{37, 59, 172, 213}}},
|
|
||||||
{Country: "France", Hostname: "fr-rb-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{37, 59, 172, 213}}},
|
|
||||||
{Country: "Germany", Hostname: "de1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{83, 143, 245, 254}}},
|
|
||||||
{Country: "Hong.Kong", Hostname: "hk-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{64, 120, 88, 115}}},
|
|
||||||
{Country: "Hong.Kong", Hostname: "hk-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{64, 120, 88, 115}}},
|
|
||||||
{Country: "India", Hostname: "in41.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{101, 53, 128, 39}}},
|
|
||||||
{Country: "India", Hostname: "in45.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{101, 53, 128, 148}}},
|
|
||||||
{Country: "India", Hostname: "in50.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{103, 104, 74, 32}}},
|
|
||||||
{Country: "India-stream", Hostname: "in-stream.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{103, 104, 74, 30}}},
|
|
||||||
{Country: "Italy", Hostname: "it.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{37, 120, 207, 90}}},
|
|
||||||
{Country: "Japan", Hostname: "jp-tk-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{202, 239, 38, 147}}},
|
|
||||||
{Country: "Japan", Hostname: "jp-tk-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{202, 239, 38, 147}}},
|
|
||||||
{Country: "Luxembourg", Hostname: "lux1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{94, 242, 195, 147}}},
|
|
||||||
{Country: "Netherlands", Hostname: "nl.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{213, 5, 64, 22}}},
|
|
||||||
{Country: "Netherlands", Hostname: "nl2.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{89, 46, 223, 251}}},
|
|
||||||
{Country: "Netherlands", Hostname: "nl3.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{89, 46, 223, 252}}},
|
|
||||||
{Country: "Norway", Hostname: "nr-ol-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{185, 90, 61, 20}}},
|
|
||||||
{Country: "Norway", Hostname: "nr-ol-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{185, 90, 61, 20}}},
|
|
||||||
{Country: "Poland", Hostname: "pl2.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{194, 15, 196, 117}}},
|
|
||||||
{Country: "Portugal", Hostname: "pt.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{185, 90, 57, 146}}},
|
|
||||||
{Country: "Romania", Hostname: "ro.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{91, 199, 50, 131}}},
|
|
||||||
{Country: "Russia", Hostname: "russia.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{95, 213, 193, 52}}},
|
|
||||||
{Country: "Serbia", Hostname: "rs.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{37, 46, 115, 246}}},
|
|
||||||
{Country: "Singapore", Hostname: "sg-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{209, 58, 174, 195}}},
|
|
||||||
{Country: "Singapore", Hostname: "sg-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{209, 58, 174, 195}}},
|
|
||||||
{Country: "South.Korea", Hostname: "kr-so-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{103, 249, 31, 36}}},
|
|
||||||
{Country: "South.Korea", Hostname: "kr-so-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{103, 249, 31, 36}}},
|
|
||||||
{Country: "Spain", Hostname: "es-bl-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{193, 148, 19, 155}}},
|
|
||||||
{Country: "Spain", Hostname: "es-bl-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{193, 148, 19, 155}}},
|
|
||||||
{Country: "Sweden", Hostname: "se-st-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{128, 127, 104, 200}}},
|
|
||||||
{Country: "Sweden", Hostname: "se-st-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{128, 127, 104, 201}}},
|
|
||||||
{Country: "Sweden", Hostname: "se2.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{79, 142, 76, 142}}},
|
|
||||||
{Country: "Switzerland", Hostname: "ch-zr-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{82, 102, 24, 254}}},
|
|
||||||
{Country: "Switzerland", Hostname: "ch-zr-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{82, 102, 24, 254}}},
|
|
||||||
{Country: "Turkey", Hostname: "tr-iz-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{185, 123, 102, 57}}},
|
|
||||||
{Country: "Turkey", Hostname: "tr.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{185, 123, 102, 57}}},
|
|
||||||
{Country: "UAE-Dubai", Hostname: "ue-db-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{45, 9, 249, 110}}},
|
|
||||||
{Country: "UAE-Dubai", Hostname: "ue-db-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{45, 9, 249, 110}}},
|
|
||||||
{Country: "UK", Hostname: "uk.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{5, 226, 139, 143}}},
|
|
||||||
{Country: "UK", Hostname: "uk6.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{5, 226, 139, 148}}},
|
|
||||||
{Country: "UK-Stream", Hostname: "uk-stream.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{195, 206, 169, 171}}},
|
|
||||||
{Country: "US-Atlanta", Hostname: "us-at-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{23, 82, 10, 205}}},
|
|
||||||
{Country: "US-Atlanta", Hostname: "us-at-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{23, 82, 10, 205}}},
|
|
||||||
{Country: "US-Charlotte", Hostname: "us-cf-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{192, 154, 253, 6}}},
|
|
||||||
{Country: "US-Charlotte", Hostname: "us-cf-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{192, 154, 253, 6}}},
|
|
||||||
{Country: "US-Chicago", Hostname: "us-ch1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{38, 98, 159, 11}}},
|
|
||||||
{Country: "US-Chicago", Hostname: "us-ch2.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{38, 98, 159, 10}}},
|
|
||||||
{Country: "US-Dallas", Hostname: "us-dl-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{74, 63, 219, 202}}},
|
|
||||||
{Country: "US-Dallas", Hostname: "us-dl-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{74, 63, 219, 202}}},
|
|
||||||
{Country: "US-Denver", Hostname: "us-dv1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{173, 248, 157, 107}}},
|
|
||||||
{Country: "US-Los.Angeles", Hostname: "us-la-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{64, 31, 35, 222}}},
|
|
||||||
{Country: "US-Los.Angeles", Hostname: "us-la-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{64, 31, 35, 222}}},
|
|
||||||
{Country: "US-Miami", Hostname: "usmia.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{162, 255, 138, 226}}},
|
|
||||||
{Country: "US-Netflix", Hostname: "netflix.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{37, 59, 172, 215}}},
|
|
||||||
{Country: "US-New.York", Hostname: "us-ny-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{38, 132, 102, 107}}},
|
|
||||||
{Country: "US-New.York", Hostname: "us-ny-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{38, 132, 102, 107}}},
|
|
||||||
{Country: "US-Phoenix", Hostname: "us-ph1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{23, 83, 184, 71}}},
|
|
||||||
{Country: "US-Phoenix", Hostname: "us-ph2.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{23, 83, 184, 72}}},
|
|
||||||
{Country: "US-Seattle", Hostname: "us-se1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{23, 82, 33, 99}}},
|
|
||||||
{Country: "US-St.Louis", Hostname: "us-st1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{148, 72, 173, 28}}},
|
|
||||||
{Country: "US-St.Louis", Hostname: "us-st3.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{148, 72, 173, 30}}},
|
|
||||||
{Country: "US-St.Louis", Hostname: "us-st4.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{148, 72, 173, 31}}},
|
|
||||||
{Country: "US-St.Louis", Hostname: "us-st5.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{148, 72, 173, 32}}},
|
|
||||||
{Country: "US-Washington", Hostname: "us-wt.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{23, 82, 15, 90}}},
|
|
||||||
{Country: "US-Washington", Hostname: "us-wt2.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{23, 82, 15, 91}}},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package constants
|
package constants
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,8 +11,7 @@ const (
|
|||||||
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=="
|
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) {
|
func HideMyAssCountryChoices(servers []models.HideMyAssServer) (choices []string) {
|
||||||
servers := HideMyAssServers()
|
|
||||||
choices = make([]string, len(servers))
|
choices = make([]string, len(servers))
|
||||||
for i := range servers {
|
for i := range servers {
|
||||||
choices[i] = servers[i].Country
|
choices[i] = servers[i].Country
|
||||||
@@ -22,8 +19,7 @@ func HideMyAssCountryChoices() (choices []string) {
|
|||||||
return makeUnique(choices)
|
return makeUnique(choices)
|
||||||
}
|
}
|
||||||
|
|
||||||
func HideMyAssCityChoices() (choices []string) {
|
func HideMyAssCityChoices(servers []models.HideMyAssServer) (choices []string) {
|
||||||
servers := HideMyAssServers()
|
|
||||||
choices = make([]string, len(servers))
|
choices = make([]string, len(servers))
|
||||||
for i := range servers {
|
for i := range servers {
|
||||||
choices[i] = servers[i].City
|
choices[i] = servers[i].City
|
||||||
@@ -31,283 +27,10 @@ func HideMyAssCityChoices() (choices []string) {
|
|||||||
return makeUnique(choices)
|
return makeUnique(choices)
|
||||||
}
|
}
|
||||||
|
|
||||||
func HideMyAssHostnameChoices() (choices []string) {
|
func HideMyAssHostnameChoices(servers []models.HideMyAssServer) (choices []string) {
|
||||||
servers := HideMyAssServers()
|
|
||||||
choices = make([]string, len(servers))
|
choices = make([]string, len(servers))
|
||||||
for i := range servers {
|
for i := range servers {
|
||||||
choices[i] = servers[i].Hostname
|
choices[i] = servers[i].Hostname
|
||||||
}
|
}
|
||||||
return makeUnique(choices)
|
return makeUnique(choices)
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:lll
|
|
||||||
// HideMyAssServers returns a slice of all the server information for HideMyAss.
|
|
||||||
func HideMyAssServers() []models.HideMyAssServer {
|
|
||||||
return []models.HideMyAssServer{
|
|
||||||
{Country: "Afghanistan", Region: "", City: "Kabul", Hostname: "af.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 236}, {5, 62, 63, 232}}},
|
|
||||||
{Country: "Aland Islands", Region: "", City: "Mariehamn", Hostname: "ax.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 248}, {5, 62, 63, 244}}},
|
|
||||||
{Country: "Albania", Region: "", City: "Tirana", Hostname: "al.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 240}, {5, 62, 63, 236}}},
|
|
||||||
{Country: "Algeria", Region: "", City: "Annaba", Hostname: "dz.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 244}, {5, 62, 63, 240}}},
|
|
||||||
{Country: "American Samoa", Region: "", City: "Pago Pago", Hostname: "as.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 4}, {5, 62, 58, 4}}},
|
|
||||||
{Country: "Andorra", Region: "", City: "Andorrala Vella", Hostname: "ad.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 4}, {5, 62, 62, 4}}},
|
|
||||||
{Country: "Angola", Region: "", City: "Luanda", Hostname: "ao.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 8}, {5, 62, 62, 8}}},
|
|
||||||
{Country: "Anguilla", Region: "", City: "The Valley", Hostname: "ai.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 8}, {5, 62, 58, 8}}},
|
|
||||||
{Country: "Antiguaand Barbuda", Region: "", City: "Saint John's", Hostname: "ag.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 12}, {5, 62, 58, 12}}},
|
|
||||||
{Country: "Argentina", Region: "", City: "Buenos Aires", Hostname: "ar.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 16}, {5, 62, 58, 16}}},
|
|
||||||
{Country: "Armenia", Region: "", City: "Tsaghkadzor", Hostname: "am.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 12}, {5, 62, 62, 12}}},
|
|
||||||
{Country: "Aruba", Region: "", City: "Palm Beach", Hostname: "aw.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 20}, {5, 62, 58, 20}}},
|
|
||||||
{Country: "Australia", Region: "New South Wales", City: "Sydney", Hostname: "au.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 23, 3}, {5, 62, 23, 18}}},
|
|
||||||
{Country: "Austria", Region: "Carinthia", City: "Klagenfurt", Hostname: "at.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 253, 207, 3}, {91, 132, 139, 115}, {94, 198, 41, 94}, {94, 198, 41, 110}, {185, 9, 19, 126}, {185, 183, 107, 163}, {185, 210, 219, 99}, {185, 244, 212, 35}, {185, 244, 212, 190}}},
|
|
||||||
{Country: "Azerbaijan", Region: "", City: "Qusar", Hostname: "az.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 16}, {5, 62, 62, 16}}},
|
|
||||||
{Country: "Bahamas", Region: "", City: "Freeport", Hostname: "bs.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 24}, {5, 62, 58, 24}}},
|
|
||||||
{Country: "Bahrain", Region: "", City: "Manama", Hostname: "bh.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 20}, {5, 62, 62, 20}}},
|
|
||||||
{Country: "Bangladesh", Region: "", City: "Dhaka", Hostname: "bd.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 24}, {5, 62, 62, 24}}},
|
|
||||||
{Country: "Barbados", Region: "", City: "Worthing", Hostname: "bb.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 28}, {5, 62, 58, 28}}},
|
|
||||||
{Country: "Belarus", Region: "", City: "Minsk", Hostname: "by.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 28}, {5, 62, 62, 28}}},
|
|
||||||
{Country: "Belgium", Region: "", City: "Brussels", Hostname: "be.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 20, 24}, {5, 62, 20, 33}, {5, 62, 20, 34}, {5, 62, 20, 44}}},
|
|
||||||
{Country: "Belize", Region: "", City: "Belize City", Hostname: "bz.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 32}, {5, 62, 58, 32}}},
|
|
||||||
{Country: "Benin", Region: "", City: "Cotonou", Hostname: "bj.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 32}, {5, 62, 62, 32}}},
|
|
||||||
{Country: "Bermuda", Region: "", City: "Hamilton", Hostname: "bm.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 36}, {5, 62, 58, 36}}},
|
|
||||||
{Country: "Bhutan", Region: "", City: "Thimphu", Hostname: "bt.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 36}, {5, 62, 62, 36}}},
|
|
||||||
{Country: "Bolivia", Region: "", City: "Santa Cruz", Hostname: "bo.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 40}, {5, 62, 58, 40}}},
|
|
||||||
{Country: "Bosnia", Region: "", City: "Sarajevo", Hostname: "ba.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 40}, {5, 62, 62, 40}}},
|
|
||||||
{Country: "Botswana", Region: "", City: "Gaborone", Hostname: "bw.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 44}, {5, 62, 62, 44}}},
|
|
||||||
{Country: "Brazil", Region: "", City: "Joao Pessoa", Hostname: "br.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 51, 51}, {5, 62, 51, 81}, {5, 62, 51, 111}, {45, 231, 207, 31}, {181, 215, 238, 207}, {191, 96, 13, 175}}},
|
|
||||||
{Country: "British Virgin Islands", Region: "", City: "Tortola", Hostname: "vg.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 44}, {5, 62, 58, 44}}},
|
|
||||||
{Country: "Brunei", Region: "", City: "Jerudong", Hostname: "bn.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 48}, {5, 62, 62, 48}}},
|
|
||||||
{Country: "Bulgaria", Region: "", City: "Sofia", Hostname: "bg.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 52}, {5, 62, 62, 52}}},
|
|
||||||
{Country: "Burkina Faso", Region: "", City: "Ouagadougou", Hostname: "bf.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 56}, {5, 62, 62, 56}}},
|
|
||||||
{Country: "Burundi", Region: "", City: "Bujumbura", Hostname: "bi.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 60}, {5, 62, 62, 60}}},
|
|
||||||
{Country: "Cambodia", Region: "", City: "Phnom Penh", Hostname: "kh.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 64}, {5, 62, 62, 64}}},
|
|
||||||
{Country: "Cameroon", Region: "", City: "Yaounde", Hostname: "cm.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 68}, {5, 62, 62, 68}}},
|
|
||||||
{Country: "Canada", Region: "British Columbia", City: "Vancouver", Hostname: "ca.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{51, 161, 54, 15}, {51, 161, 66, 111}, {54, 39, 219, 127}, {144, 217, 105, 207}, {158, 69, 234, 207}, {192, 99, 89, 207}, {192, 99, 110, 159}, {198, 27, 103, 191}}},
|
|
||||||
{Country: "Cape Verde", Region: "", City: "Cidade Velha", Hostname: "cv.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 72}, {5, 62, 62, 72}}},
|
|
||||||
{Country: "Cayman Islands", Region: "", City: "Spot Bay", Hostname: "ky.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 48}, {5, 62, 58, 48}}},
|
|
||||||
{Country: "Central African Republic", Region: "", City: "Bangassou", Hostname: "cf.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 76}, {5, 62, 62, 76}}},
|
|
||||||
{Country: "Chad", Region: "", City: "N'Djamena", Hostname: "td.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 80}, {5, 62, 62, 80}}},
|
|
||||||
{Country: "Chile", Region: "", City: "Santiago", Hostname: "cl.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 52}, {5, 62, 58, 52}}},
|
|
||||||
{Country: "China", Region: "Sichuan Sheng", City: "Chengdu", Hostname: "cn.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 34, 40}, {5, 62, 34, 44}}},
|
|
||||||
{Country: "Christmas Island", Region: "", City: "Flying Fish Cove", Hostname: "cx.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 88}, {5, 62, 62, 84}}},
|
|
||||||
{Country: "Cocos Islands", Region: "", City: "West Island", Hostname: "cc.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 92}, {5, 62, 62, 88}}},
|
|
||||||
{Country: "Colombia", Region: "", City: "San Andres", Hostname: "co.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 60}, {5, 62, 58, 56}}},
|
|
||||||
{Country: "Comoros", Region: "", City: "Ouani", Hostname: "km.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 96}, {5, 62, 62, 92}}},
|
|
||||||
{Country: "Congo", Region: "", City: "Kinshasa", Hostname: "cd.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 100}, {5, 62, 62, 96}}},
|
|
||||||
{Country: "Cook Islands", Region: "", City: "Avarua", Hostname: "ck.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 64}, {5, 62, 58, 60}}},
|
|
||||||
{Country: "Costa Rica", Region: "", City: "San Jose", Hostname: "cr.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 68}, {5, 62, 58, 64}}},
|
|
||||||
{Country: "Coted`Ivoire", Region: "", City: "Yamoussoukro", Hostname: "ci.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 104}, {5, 62, 62, 100}}},
|
|
||||||
{Country: "Croatia", Region: "", City: "Zagreb", Hostname: "hr.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 228}, {5, 62, 63, 224}}},
|
|
||||||
{Country: "Cuba", Region: "", City: "Havana", Hostname: "cu.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 72}, {5, 62, 58, 68}}},
|
|
||||||
{Country: "Cyprus", Region: "", City: "Limassol", Hostname: "cy.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 108}, {5, 62, 62, 104}}},
|
|
||||||
{Country: "Czech Republic", Region: "", City: "Prague", Hostname: "cz.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{185, 246, 210, 130}, {185, 246, 210, 146}, {185, 246, 210, 162}, {185, 246, 210, 178}, {185, 246, 210, 194}, {212, 102, 38, 173}, {212, 102, 38, 186}}},
|
|
||||||
{Country: "Denmark", Region: "", City: "Copenhagen", Hostname: "dk.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{2, 58, 46, 195}, {2, 58, 46, 211}, {37, 120, 232, 110}, {37, 120, 232, 126}, {37, 120, 232, 142}, {185, 212, 169, 174}, {185, 212, 169, 206}, {185, 212, 169, 222}}},
|
|
||||||
{Country: "Dominica", Region: "", City: "Marigot", Hostname: "dm.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 76}, {5, 62, 58, 72}}},
|
|
||||||
{Country: "Dominican Republic", Region: "", City: "Punta Cana", Hostname: "do.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 80}, {5, 62, 58, 76}}},
|
|
||||||
{Country: "Ecuador", Region: "", City: "Quito", Hostname: "ec.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 84}, {5, 62, 58, 80}}},
|
|
||||||
{Country: "Egypt", Region: "", City: "Cairo", Hostname: "eg.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 112}, {5, 62, 62, 108}}},
|
|
||||||
{Country: "El Salvador", Region: "", City: "San Miguel", Hostname: "sv.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 88}, {5, 62, 58, 84}}},
|
|
||||||
{Country: "Equatorial Guinea", Region: "", City: "Malabo", Hostname: "gq.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 116}, {5, 62, 62, 112}}},
|
|
||||||
{Country: "Eritrea", Region: "", City: "Asmara", Hostname: "er.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 120}, {5, 62, 62, 116}}},
|
|
||||||
{Country: "Estonia", Region: "", City: "Tallinn", Hostname: "ee.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 124}, {5, 62, 62, 120}}},
|
|
||||||
{Country: "Ethiopia", Region: "", City: "Gondar", Hostname: "et.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 128}, {5, 62, 62, 124}}},
|
|
||||||
{Country: "Falkland Islands", Region: "", City: "Stanley", Hostname: "fk.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 92}, {5, 62, 58, 88}}},
|
|
||||||
{Country: "Faroe Islands", Region: "", City: "Torshavn", Hostname: "fo.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 132}, {5, 62, 62, 128}}},
|
|
||||||
{Country: "Fiji", Region: "", City: "Nadi", Hostname: "fj.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 96}, {5, 62, 58, 92}}},
|
|
||||||
{Country: "Finland", Region: "", City: "Helsinki", Hostname: "fi.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{185, 77, 217, 16}, {185, 77, 217, 31}, {185, 77, 217, 46}, {185, 77, 217, 76}, {185, 77, 217, 91}, {185, 77, 217, 106}}},
|
|
||||||
{Country: "France", Region: "", City: "Paris", Hostname: "fr.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{84, 17, 43, 90}, {84, 17, 43, 116}, {185, 93, 2, 50}, {185, 93, 2, 66}, {185, 93, 2, 82}, {185, 93, 2, 98}, {195, 154, 48, 59}, {212, 83, 153, 211}, {212, 83, 164, 190}, {212, 83, 188, 191}, {212, 129, 37, 90}, {212, 129, 55, 206}}},
|
|
||||||
{Country: "Gabon", Region: "", City: "Libreville", Hostname: "ga.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 136}, {5, 62, 62, 132}}},
|
|
||||||
{Country: "Gambia", Region: "", City: "Serekunda", Hostname: "gm.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 140}, {5, 62, 62, 136}}},
|
|
||||||
{Country: "Georgia", Region: "", City: "Tbilisi", Hostname: "ge.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 144}, {5, 62, 62, 140}}},
|
|
||||||
{Country: "Germany", Region: "Hesse", City: "Frankfurt", Hostname: "de.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 41, 121}, {5, 62, 41, 133}, {5, 62, 41, 145}, {5, 62, 41, 157}, {5, 62, 41, 169}, {5, 62, 41, 181}}},
|
|
||||||
{Country: "Ghana", Region: "", City: "Accra", Hostname: "gh.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 148}, {5, 62, 62, 144}}},
|
|
||||||
{Country: "Gibraltar", Region: "", City: "Catalan", Hostname: "gi.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 152}, {5, 62, 62, 148}}},
|
|
||||||
{Country: "Greece", Region: "", City: "Patras", Hostname: "gr.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 156}, {5, 62, 62, 152}}},
|
|
||||||
{Country: "Greenland", Region: "", City: "Ilulissat", Hostname: "gl.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 160}, {5, 62, 62, 156}}},
|
|
||||||
{Country: "Grenada", Region: "", City: "Saint George", Hostname: "gd.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 100}, {5, 62, 58, 96}}},
|
|
||||||
{Country: "Guadeloupe", Region: "", City: "Le Gosier", Hostname: "gp.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 104}, {5, 62, 58, 100}}},
|
|
||||||
{Country: "Guam", Region: "", City: "Tamuning", Hostname: "gu.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 108}, {5, 62, 60, 164}}},
|
|
||||||
{Country: "Guatemala", Region: "", City: "Guatemala City", Hostname: "gt.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 112}, {5, 62, 58, 104}}},
|
|
||||||
{Country: "Guinea-Bissau", Region: "", City: "Bissau", Hostname: "gw.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 168}, {5, 62, 62, 160}}},
|
|
||||||
{Country: "Guinea", Region: "", City: "Conakry", Hostname: "gn.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 172}, {5, 62, 62, 164}}},
|
|
||||||
{Country: "Guyana", Region: "", City: "Barima-Waini", Hostname: "gy.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 116}, {5, 62, 58, 108}}},
|
|
||||||
{Country: "Haiti", Region: "", City: "Cap-Haitien", Hostname: "ht.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 120}, {5, 62, 58, 112}}},
|
|
||||||
{Country: "Honduras", Region: "", City: "Tegucigalpa", Hostname: "hn.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 124}, {5, 62, 58, 116}}},
|
|
||||||
{Country: "Hungary", Region: "", City: "Budapest", Hostname: "hu.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{37, 120, 144, 78}, {37, 120, 144, 94}, {185, 94, 190, 174}, {185, 128, 26, 113}, {185, 128, 26, 126}, {185, 252, 223, 62}, {185, 252, 223, 78}}},
|
|
||||||
{Country: "Iceland", Region: "", City: "Reykjavik", Hostname: "is.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{82, 221, 112, 243}, {82, 221, 112, 244}}},
|
|
||||||
{Country: "India", Region: "Maharashtra", City: "Mumbai", Hostname: "in.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 34, 8}, {5, 62, 34, 12}}},
|
|
||||||
{Country: "Indonesia", Region: "", City: "Jakarta", Hostname: "id.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 34, 16}, {5, 62, 34, 20}}},
|
|
||||||
{Country: "Iran", Region: "", City: "Isfahan", Hostname: "ir.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 176}, {5, 62, 62, 168}}},
|
|
||||||
{Country: "Iraq", Region: "", City: "Baghdad", Hostname: "iq.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 180}, {5, 62, 62, 172}}},
|
|
||||||
{Country: "Ireland", Region: "", City: "Dublin", Hostname: "ie.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{78, 153, 199, 5}, {78, 153, 199, 29}, {78, 153, 199, 243}, {78, 153, 199, 254}}},
|
|
||||||
{Country: "Israel", Region: "", City: "Petah Tikva", Hostname: "il.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{185, 185, 132, 62}, {185, 185, 132, 110}, {185, 185, 133, 190}}},
|
|
||||||
{Country: "Italy", Region: "Pordenone", City: "Porcia", Hostname: "it.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{45, 87, 184, 15}, {45, 87, 184, 47}, {84, 17, 58, 168}, {84, 17, 58, 213}, {84, 17, 59, 45}, {84, 17, 59, 59}}},
|
|
||||||
{Country: "Jamaica", Region: "", City: "Montego Bay", Hostname: "jm.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 128}, {5, 62, 58, 120}}},
|
|
||||||
{Country: "Japan", Region: "", City: "Tokyo", Hostname: "jp.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{156, 146, 35, 162}, {156, 146, 35, 184}, {156, 146, 35, 185}, {212, 102, 51, 235}}},
|
|
||||||
{Country: "Jordan", Region: "", City: "Amman", Hostname: "jo.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 188}, {5, 62, 62, 180}}},
|
|
||||||
{Country: "Kazakhstan", Region: "", City: "Shymkent", Hostname: "kz.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 192}, {5, 62, 62, 184}}},
|
|
||||||
{Country: "Kenya", Region: "", City: "Nairobi", Hostname: "ke.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 196}, {5, 62, 62, 188}}},
|
|
||||||
{Country: "Kiribati", Region: "", City: "Umwa Village", Hostname: "ki.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 132}, {5, 62, 58, 124}}},
|
|
||||||
{Country: "Kuwait", Region: "", City: "Kuwait City", Hostname: "kw.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 200}, {5, 62, 62, 192}}},
|
|
||||||
{Country: "Kyrgyzstan", Region: "", City: "Bishkek", Hostname: "kg.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 204}, {5, 62, 62, 196}}},
|
|
||||||
{Country: "Laos", Region: "", City: "Thakhek", Hostname: "la.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 208}, {5, 62, 62, 200}}},
|
|
||||||
{Country: "Latvia", Region: "", City: "Riga", Hostname: "lv.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 212}, {5, 62, 62, 204}}},
|
|
||||||
{Country: "Lebanon", Region: "", City: "Beirut", Hostname: "lb.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 216}, {5, 62, 62, 208}}},
|
|
||||||
{Country: "Lesotho", Region: "", City: "Peka", Hostname: "ls.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 220}, {5, 62, 62, 212}}},
|
|
||||||
{Country: "Liberia", Region: "", City: "Monrovia", Hostname: "lr.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 224}, {5, 62, 62, 216}}},
|
|
||||||
{Country: "Libya", Region: "", City: "Ghadames", Hostname: "ly.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 228}, {5, 62, 62, 220}}},
|
|
||||||
{Country: "Liechtenstein", Region: "", City: "Vaduz", Hostname: "li.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 232}, {5, 62, 62, 224}}},
|
|
||||||
{Country: "Lithuania", Region: "", City: "Siauliai", Hostname: "lt.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 236}, {5, 62, 62, 228}}},
|
|
||||||
{Country: "Luxembourg", Region: "", City: "", Hostname: "lu.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{92, 38, 162, 123}, {92, 38, 162, 148}, {92, 38, 162, 151}, {92, 38, 172, 25}, {92, 223, 88, 149}}},
|
|
||||||
{Country: "Macau", Region: "", City: "Macau", Hostname: "mo.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 240}, {5, 62, 62, 232}}},
|
|
||||||
{Country: "Macedonia", Region: "", City: "Skopje", Hostname: "mk.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 244}, {5, 62, 62, 236}}},
|
|
||||||
{Country: "Madagascar", Region: "", City: "Antsiranana", Hostname: "mg.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 248}, {5, 62, 62, 240}}},
|
|
||||||
{Country: "Malawi", Region: "", City: "Lilongwe", Hostname: "mw.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 60, 252}, {5, 62, 62, 244}}},
|
|
||||||
{Country: "Malaysia", Region: "", City: "Kuala Lumpur", Hostname: "my.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{185, 54, 231, 26}, {185, 54, 231, 43}, {185, 54, 231, 60}}},
|
|
||||||
{Country: "Maldives", Region: "", City: "Male", Hostname: "mv.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 0}, {5, 62, 62, 248}}},
|
|
||||||
{Country: "Mali", Region: "", City: "Bamako", Hostname: "ml.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 4}, {5, 62, 62, 252}}},
|
|
||||||
{Country: "Malta", Region: "", City: "Cospicua", Hostname: "mt.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 8}, {5, 62, 63, 0}}},
|
|
||||||
{Country: "Mauritius", Region: "", City: "Port Louis", Hostname: "mu.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 16}, {5, 62, 63, 8}}},
|
|
||||||
{Country: "Mexico", Region: "Sinaloa", City: "Mazatlan", Hostname: "mx.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{31, 14, 72, 16}, {31, 14, 72, 23}, {31, 14, 72, 30}, {31, 14, 72, 44}, {31, 14, 72, 51}}},
|
|
||||||
{Country: "Moldova", Region: "", City: "Chisinau", Hostname: "md.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 20}, {5, 62, 63, 12}}},
|
|
||||||
{Country: "Monaco", Region: "", City: "Monaco", Hostname: "mc.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 24}, {5, 62, 63, 16}}},
|
|
||||||
{Country: "Mongolia", Region: "", City: "Suhbaatar", Hostname: "mn.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 28}, {5, 62, 63, 20}}},
|
|
||||||
{Country: "Montenegro", Region: "", City: "Becici", Hostname: "me.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 32}, {5, 62, 63, 24}}},
|
|
||||||
{Country: "Montserrat", Region: "", City: "Plymouth", Hostname: "ms.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 136}, {5, 62, 58, 128}}},
|
|
||||||
{Country: "Morocco", Region: "", City: "Fes", Hostname: "ma.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 36}, {5, 62, 63, 28}}},
|
|
||||||
{Country: "Mozambique", Region: "", City: "Pemba", Hostname: "mz.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 40}, {5, 62, 63, 32}}},
|
|
||||||
{Country: "Myanmar", Region: "", City: "Yangon", Hostname: "mm.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 44}, {5, 62, 63, 36}}},
|
|
||||||
{Country: "Namibia", Region: "", City: "Windhoek", Hostname: "na.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 48}, {5, 62, 63, 40}}},
|
|
||||||
{Country: "Nauru", Region: "", City: "Anabar", Hostname: "nr.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 140}, {5, 62, 58, 132}}},
|
|
||||||
{Country: "Nepal", Region: "", City: "Janakpur", Hostname: "np.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 52}, {5, 62, 63, 44}}},
|
|
||||||
{Country: "Netherlands", Region: "", City: "Amsterdam", Hostname: "nl.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{84, 17, 46, 134}, {84, 17, 46, 158}, {84, 17, 46, 182}, {84, 17, 46, 206}, {84, 17, 46, 251}}},
|
|
||||||
{Country: "New Caledonia", Region: "", City: "Noumea", Hostname: "nc.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 144}, {5, 62, 58, 136}}},
|
|
||||||
{Country: "New Zealand", Region: "", City: "Auckland", Hostname: "nz.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{103, 76, 164, 3}, {103, 76, 164, 19}, {103, 108, 94, 243}, {103, 231, 91, 131}}},
|
|
||||||
{Country: "Nicaragua", Region: "", City: "Managua", Hostname: "ni.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 148}, {5, 62, 58, 140}}},
|
|
||||||
{Country: "Niger", Region: "", City: "Niamey", Hostname: "ne.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 56}, {5, 62, 63, 48}}},
|
|
||||||
{Country: "Nigeria", Region: "", City: "Lagos", Hostname: "ng.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 60}, {5, 62, 63, 52}}},
|
|
||||||
{Country: "Niue", Region: "", City: "Alofi", Hostname: "nu.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 152}, {5, 62, 58, 144}}},
|
|
||||||
{Country: "Norfolk Island", Region: "", City: "Kingston", Hostname: "nf.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 156}, {5, 62, 58, 148}}},
|
|
||||||
{Country: "North Korea", Region: "", City: "Manpo", Hostname: "kp.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 160}, {5, 62, 61, 64}}},
|
|
||||||
{Country: "Norway", Region: "", City: "Oslo", Hostname: "no.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{185, 101, 32, 16}, {185, 101, 32, 48}, {185, 101, 32, 174}, {217, 170, 202, 112}, {217, 170, 204, 160}, {217, 170, 204, 223}, {217, 170, 206, 127}}},
|
|
||||||
{Country: "Oman", Region: "", City: "Salalah", Hostname: "om.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 68}, {5, 62, 63, 56}}},
|
|
||||||
{Country: "Pakistan", Region: "", City: "Karachi", Hostname: "pk.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 72}, {5, 62, 63, 60}}},
|
|
||||||
{Country: "Palau", Region: "", City: "Melekeok", Hostname: "pw.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 164}, {5, 62, 61, 76}}},
|
|
||||||
{Country: "Palestine", Region: "", City: "Bethlehem", Hostname: "ps.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 80}, {5, 62, 63, 64}}},
|
|
||||||
{Country: "Panama", Region: "", City: "Panama City", Hostname: "pa.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 168}, {5, 62, 58, 152}}},
|
|
||||||
{Country: "Papua New Guinea", Region: "", City: "Alotau", Hostname: "pg.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 172}, {5, 62, 61, 84}}},
|
|
||||||
{Country: "Paraguay", Region: "", City: "Boqueron", Hostname: "py.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 176}, {5, 62, 58, 156}}},
|
|
||||||
{Country: "Peru", Region: "", City: "Cusco", Hostname: "pe.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 180}, {5, 62, 58, 160}}},
|
|
||||||
{Country: "Philippines", Region: "", City: "Baguio", Hostname: "ph.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 184}, {5, 62, 61, 88}}},
|
|
||||||
{Country: "Pitcairn Islands", Region: "", City: "Adamstown", Hostname: "pn.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 188}, {5, 62, 58, 164}}},
|
|
||||||
{Country: "Poland", Region: "", City: "Warsaw", Hostname: "pl.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{84, 17, 55, 13}, {84, 17, 55, 26}, {185, 246, 208, 34}, {185, 246, 208, 61}, {185, 246, 208, 130}, {185, 246, 208, 157}}},
|
|
||||||
{Country: "Portugal", Region: "", City: "Leiria", Hostname: "pt.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{91, 205, 230, 239}, {91, 205, 230, 255}, {91, 250, 240, 113}, {91, 250, 240, 193}, {91, 250, 240, 209}, {91, 250, 240, 225}, {195, 158, 248, 46}, {195, 158, 248, 94}}},
|
|
||||||
{Country: "Puerto Rico", Region: "", City: "San Juan", Hostname: "pr.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 192}, {5, 62, 58, 168}}},
|
|
||||||
{Country: "Qatar", Region: "", City: "Doha", Hostname: "qa.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 92}, {5, 62, 63, 68}}},
|
|
||||||
{Country: "Republicof Djibouti", Region: "", City: "Djibouti", Hostname: "dj.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 96}, {5, 62, 63, 72}}},
|
|
||||||
{Country: "Republicof Singapore", Region: "", City: "Singapore", Hostname: "sg.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 35, 135}, {5, 62, 35, 143}, {5, 62, 35, 159}, {5, 62, 35, 167}, {5, 62, 35, 175}, {92, 223, 85, 21}, {92, 223, 85, 22}}},
|
|
||||||
{Country: "Republicofthe Congo", Region: "", City: "Brazzaville", Hostname: "cg.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 100}, {5, 62, 63, 76}}},
|
|
||||||
{Country: "Romania", Region: "", City: "Bucharest", Hostname: "ro.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 104}, {5, 62, 63, 80}}},
|
|
||||||
{Country: "Russia", Region: "", City: "Moscow", Hostname: "ru.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 19, 39}, {5, 62, 19, 47}, {5, 62, 19, 55}, {5, 62, 19, 63}}},
|
|
||||||
{Country: "Rwanda", Region: "", City: "Kigali", Hostname: "rw.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 108}, {5, 62, 63, 84}}},
|
|
||||||
{Country: "Saint Helena", Region: "", City: "Tristan Da Cunha", Hostname: "sh.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 112}, {5, 62, 63, 88}}},
|
|
||||||
{Country: "Saint Kittsand Nevis", Region: "", City: "Basseterre", Hostname: "kn.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 196}, {5, 62, 58, 172}}},
|
|
||||||
{Country: "Saint Lucia", Region: "", City: "Gros Islet", Hostname: "lc.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 200}, {5, 62, 58, 176}}},
|
|
||||||
{Country: "Saint Pierreand Miquelon", Region: "", City: "Saint-Pierre", Hostname: "pm.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 58, 180}, {5, 62, 63, 92}}},
|
|
||||||
{Country: "Saint Vincentandthe Grenadines", Region: "", City: "Kingstown", Hostname: "vc.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 204}, {5, 62, 58, 184}}},
|
|
||||||
{Country: "Samoa", Region: "", City: "Matatufu", Hostname: "ws.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 208}, {5, 62, 58, 188}}},
|
|
||||||
{Country: "San Marino", Region: "", City: "San Marino", Hostname: "sm.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 116}, {5, 62, 63, 96}}},
|
|
||||||
{Country: "Sao Tomeand Principe", Region: "", City: "Sao Tome", Hostname: "st.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 120}, {5, 62, 63, 100}}},
|
|
||||||
{Country: "Saudi Arabia", Region: "", City: "Riyadh", Hostname: "sa.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 124}, {5, 62, 63, 104}}},
|
|
||||||
{Country: "Senegal", Region: "", City: "Dakar", Hostname: "sn.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 128}, {5, 62, 63, 108}}},
|
|
||||||
{Country: "Serbia", Region: "", City: "Belgrade", Hostname: "rs.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 132}, {5, 62, 63, 112}}},
|
|
||||||
{Country: "Slovakia", Region: "", City: "Bratislava", Hostname: "sk.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 140}, {5, 62, 63, 120}}},
|
|
||||||
{Country: "Slovenia", Region: "", City: "Vrhnika", Hostname: "si.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 144}, {5, 62, 63, 124}}},
|
|
||||||
{Country: "Solomon Islands", Region: "", City: "Honiara", Hostname: "sb.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 212}, {5, 62, 58, 192}}},
|
|
||||||
{Country: "Somalia", Region: "", City: "Afgooye", Hostname: "so.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 148}, {5, 62, 63, 128}}},
|
|
||||||
{Country: "South Africa", Region: "", City: "Johannesburg", Hostname: "za.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{102, 165, 47, 160}, {102, 165, 47, 176}, {154, 70, 155, 144}}},
|
|
||||||
{Country: "South Korea", Region: "", City: "Seoul", Hostname: "kr.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{185, 54, 229, 25}, {185, 54, 229, 41}, {185, 54, 229, 57}, {185, 54, 229, 73}}},
|
|
||||||
{Country: "Spain", Region: "", City: "Alicante", Hostname: "es.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{82, 102, 17, 254}, {89, 38, 226, 238}, {217, 138, 218, 238}}},
|
|
||||||
{Country: "Sri Lanka", Region: "", City: "Moratuwa", Hostname: "lk.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 152}, {5, 62, 63, 132}}},
|
|
||||||
{Country: "Sudan", Region: "", City: "Khartoum", Hostname: "sd.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 156}, {5, 62, 63, 136}}},
|
|
||||||
{Country: "Suriname", Region: "", City: "Paramaribo", Hostname: "sr.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 216}, {5, 62, 58, 196}}},
|
|
||||||
{Country: "Svalbardand Jan Mayen", Region: "", City: "Longyearbyen", Hostname: "sj.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 160}, {5, 62, 63, 140}}},
|
|
||||||
{Country: "Swaziland", Region: "", City: "Manzini", Hostname: "sz.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 220}, {5, 62, 58, 200}}},
|
|
||||||
{Country: "Sweden", Region: "Stockholm", City: "Nacka", Hostname: "se.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{31, 3, 152, 98}, {31, 3, 152, 106}, {37, 46, 121, 240}, {128, 127, 105, 164}, {128, 127, 105, 192}}},
|
|
||||||
{Country: "Switzerland", Region: "", City: "Zurich", Hostname: "ch.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{84, 17, 52, 141}, {84, 17, 52, 167}, {84, 17, 52, 180}, {84, 17, 52, 240}, {89, 187, 165, 179}}},
|
|
||||||
{Country: "Syria", Region: "", City: "Ad Darah", Hostname: "sy.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 164}, {5, 62, 63, 144}}},
|
|
||||||
{Country: "Taiwan", Region: "", City: "Taipei", Hostname: "tw.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{172, 107, 246, 62}, {172, 107, 246, 110}, {172, 107, 246, 142}, {172, 107, 246, 174}, {172, 107, 246, 190}, {172, 107, 246, 206}, {172, 107, 246, 222}}},
|
|
||||||
{Country: "Tajikistan", Region: "", City: "Dushanbe", Hostname: "tj.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 168}, {5, 62, 63, 148}}},
|
|
||||||
{Country: "Tanzania", Region: "", City: "Arusha", Hostname: "tz.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 172}, {5, 62, 63, 152}}},
|
|
||||||
{Country: "Thailand", Region: "", City: "Bangkok", Hostname: "th.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 176}, {5, 62, 63, 156}}},
|
|
||||||
{Country: "Togo", Region: "", City: "Lome", Hostname: "tg.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 180}, {5, 62, 63, 160}}},
|
|
||||||
{Country: "Tokelau", Region: "", City: "Atafu", Hostname: "tk.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 224}, {5, 62, 58, 204}}},
|
|
||||||
{Country: "Tonga", Region: "", City: "Nukualofa", Hostname: "to.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 228}, {5, 62, 58, 208}}},
|
|
||||||
{Country: "Trinidadand Tobago", Region: "", City: "San Fernando", Hostname: "tt.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 232}, {5, 62, 58, 212}}},
|
|
||||||
{Country: "Tunisia", Region: "", City: "Mahdia", Hostname: "tn.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 184}, {5, 62, 63, 164}}},
|
|
||||||
{Country: "Turkey", Region: "", City: "Istanbul", Hostname: "tr.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{92, 38, 180, 76}, {92, 38, 180, 86}}},
|
|
||||||
{Country: "Turkmenistan", Region: "", City: "Ashgabat", Hostname: "tm.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 188}, {5, 62, 63, 168}}},
|
|
||||||
{Country: "Turksand Caicos Islands", Region: "", City: "Balfour Town", Hostname: "tc.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 236}, {5, 62, 58, 216}}},
|
|
||||||
{Country: "Tuvalu", Region: "", City: "Vaitupu", Hostname: "tv.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 240}, {5, 62, 58, 220}}},
|
|
||||||
{Country: "UK", Region: "", City: "London", Hostname: "london.gb.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 43, 193}, {5, 62, 43, 202}, {5, 62, 43, 218}, {77, 234, 43, 130}, {77, 234, 43, 166}, {77, 234, 43, 175}, {77, 234, 43, 185}}},
|
|
||||||
{Country: "UK", Region: "Scotland", City: "Glasgow", Hostname: "scotland.gb.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{62, 128, 217, 69}, {62, 128, 217, 85}, {62, 128, 217, 112}, {80, 75, 64, 66}, {109, 169, 34, 23}, {109, 169, 34, 42}, {109, 169, 34, 62}}},
|
|
||||||
{Country: "UK", Region: "", City: "", Hostname: "gb.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{62, 128, 207, 110}, {62, 128, 217, 69}, {62, 128, 217, 85}, {62, 128, 217, 112}, {80, 75, 64, 66}, {109, 169, 34, 23}, {109, 169, 34, 42}, {109, 169, 34, 62}}},
|
|
||||||
{Country: "USA", Region: "Alabama", City: "Montgomery", Hostname: "al.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{95, 142, 127, 16}, {95, 142, 127, 25}}},
|
|
||||||
{Country: "USA", Region: "Alaska", City: "Anchorage", Hostname: "ak.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 252}, {5, 62, 58, 232}}},
|
|
||||||
{Country: "USA", Region: "Arizona", City: "Phoenix", Hostname: "az.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{23, 83, 130, 34}, {23, 83, 130, 37}, {23, 83, 130, 187}, {23, 83, 131, 98}, {23, 83, 131, 215}, {23, 83, 132, 155}, {23, 83, 132, 176}, {23, 83, 185, 50}}},
|
|
||||||
{Country: "USA", Region: "Arkansas", City: "Magnolia", Hostname: "ar.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 0}, {5, 62, 58, 236}}},
|
|
||||||
{Country: "USA", Region: "California", City: "Los Angeles", Hostname: "ca.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{67, 201, 33, 54}, {162, 253, 68, 145}, {162, 253, 68, 161}, {162, 253, 68, 177}, {162, 253, 68, 225}, {192, 252, 220, 60}}},
|
|
||||||
{Country: "USA", Region: "Connecticut", City: "Trumbull", Hostname: "ct.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 16, 16}, {5, 62, 16, 25}}},
|
|
||||||
{Country: "USA", Region: "Delaware", City: "Wilmington", Hostname: "de.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 4}, {5, 62, 58, 240}}},
|
|
||||||
{Country: "USA", Region: "Florida", City: "Miami", Hostname: "fl.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{171, 22, 76, 15}, {171, 22, 76, 31}, {171, 22, 76, 47}, {171, 22, 76, 79}, {171, 22, 76, 95}}},
|
|
||||||
{Country: "USA", Region: "Georgia", City: "Atlanta", Hostname: "ga.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 24, 15}, {5, 62, 24, 31}, {5, 62, 24, 61}, {66, 115, 181, 97}}},
|
|
||||||
{Country: "USA", Region: "Hawaii", City: "Honolulu", Hostname: "hi.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{131, 100, 2, 51}, {131, 100, 2, 99}}},
|
|
||||||
{Country: "USA", Region: "Idaho", City: "Idaho Falls", Hostname: "id.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 12}, {5, 62, 58, 248}}},
|
|
||||||
{Country: "USA", Region: "Illinois", City: "Chicago", Hostname: "il.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{154, 16, 241, 127}, {154, 16, 241, 255}, {181, 214, 61, 64}, {181, 214, 98, 31}, {181, 214, 99, 159}, {181, 214, 102, 159}, {181, 214, 107, 47}, {191, 101, 170, 30}}},
|
|
||||||
{Country: "USA", Region: "Indiana", City: "South Bend", Hostname: "in.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{198, 134, 108, 67}, {198, 134, 109, 131}}},
|
|
||||||
{Country: "USA", Region: "Iowa", City: "Des Moines", Hostname: "ia.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 104}, {5, 62, 59, 84}}},
|
|
||||||
{Country: "USA", Region: "Kansas", City: "Wichita", Hostname: "ks.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 16}, {5, 62, 58, 252}}},
|
|
||||||
{Country: "USA", Region: "Kentucky", City: "Louisville", Hostname: "ky.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 20}, {5, 62, 59, 0}}},
|
|
||||||
{Country: "USA", Region: "Louisiana", City: "New Orleans", Hostname: "la.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 24}, {5, 62, 59, 4}}},
|
|
||||||
{Country: "USA", Region: "Maine", City: "Bath", Hostname: "me.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 28}, {5, 62, 59, 8}}},
|
|
||||||
{Country: "USA", Region: "Maryland", City: "Baltimore", Hostname: "md.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 32}, {5, 62, 59, 12}}},
|
|
||||||
{Country: "USA", Region: "Massachusetts", City: "Boston", Hostname: "ma.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{38, 146, 55, 115}, {38, 146, 57, 253}, {38, 242, 7, 243}, {154, 3, 129, 29}, {154, 3, 129, 77}, {154, 3, 129, 93}}},
|
|
||||||
{Country: "USA", Region: "Michigan", City: "Lansing", Hostname: "mi.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 36}, {5, 62, 59, 16}}},
|
|
||||||
{Country: "USA", Region: "Minnesota", City: "Saint Paul", Hostname: "mn.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 108}, {5, 62, 59, 88}}},
|
|
||||||
{Country: "USA", Region: "Mississippi", City: "Louisville", Hostname: "ms.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 40}, {5, 62, 59, 20}}},
|
|
||||||
{Country: "USA", Region: "Missouri", City: "Kansas City", Hostname: "mo.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{209, 239, 115, 164}, {209, 239, 115, 186}}},
|
|
||||||
{Country: "USA", Region: "Montana", City: "Billings", Hostname: "mt.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 116}, {5, 62, 59, 92}}},
|
|
||||||
{Country: "USA", Region: "Nebraska", City: "Omaha", Hostname: "ne.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 44}, {5, 62, 59, 24}}},
|
|
||||||
{Country: "USA", Region: "Nevada", City: "Las Vegas", Hostname: "nv.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{76, 164, 192, 254}, {76, 164, 193, 82}, {76, 164, 200, 114}, {76, 164, 202, 190}, {76, 164, 205, 194}}},
|
|
||||||
{Country: "USA", Region: "New Hampshire", City: "Bedford", Hostname: "nh.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 48}, {5, 62, 59, 28}}},
|
|
||||||
{Country: "USA", Region: "New York", City: "Manhattan", Hostname: "ny.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{156, 146, 36, 77}, {156, 146, 36, 90}, {156, 146, 36, 103}, {195, 181, 168, 187}, {212, 102, 33, 77}, {212, 102, 33, 90}, {212, 102, 33, 144}, {212, 102, 33, 192}, {212, 102, 33, 224}, {212, 102, 33, 240}}},
|
|
||||||
{Country: "USA", Region: "North Carolina", City: "Asheville", Hostname: "nc.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{104, 247, 208, 78}, {104, 247, 208, 94}, {104, 247, 208, 110}, {104, 247, 208, 126}, {104, 247, 208, 142}, {104, 247, 208, 158}, {104, 247, 208, 174}}},
|
|
||||||
{Country: "USA", Region: "North Dakota", City: "Grand Forks", Hostname: "nd.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 60}, {5, 62, 59, 40}}},
|
|
||||||
{Country: "USA", Region: "Ohio", City: "Columbus", Hostname: "oh.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{199, 114, 218, 99}, {199, 114, 218, 115}}},
|
|
||||||
{Country: "USA", Region: "Oklahoma", City: "Oklahoma City", Hostname: "ok.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{216, 107, 129, 115}, {216, 107, 129, 131}}},
|
|
||||||
{Country: "USA", Region: "Pennsylvania", City: "Wilkes-Barre", Hostname: "pa.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 64}, {5, 62, 59, 44}}},
|
|
||||||
{Country: "USA", Region: "Rhode Island", City: "Providence", Hostname: "ri.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 68}, {5, 62, 59, 48}}},
|
|
||||||
{Country: "USA", Region: "South Carolina", City: "Columbia", Hostname: "sc.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 72}, {5, 62, 59, 52}}},
|
|
||||||
{Country: "USA", Region: "South Dakota", City: "Sioux Falls", Hostname: "sd.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 76}, {5, 62, 59, 56}}},
|
|
||||||
{Country: "USA", Region: "Tennessee", City: "Nashville", Hostname: "tn.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 80}, {5, 62, 59, 60}}},
|
|
||||||
{Country: "USA", Region: "Texas", City: "Dallas", Hostname: "tx.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{89, 187, 164, 86}, {89, 187, 164, 144}, {89, 187, 164, 160}, {89, 187, 164, 176}, {156, 146, 38, 141}, {212, 102, 40, 13}, {212, 102, 40, 141}, {212, 102, 40, 154}}},
|
|
||||||
{Country: "USA", Region: "Utah", City: "Salt Lake City", Hostname: "ut.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{69, 36, 169, 129}, {199, 189, 106, 235}, {199, 189, 106, 239}, {199, 189, 106, 245}, {199, 189, 106, 251}, {209, 95, 34, 73}}},
|
|
||||||
{Country: "USA", Region: "Vermont", City: "Rutland", Hostname: "vt.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 84}, {5, 62, 59, 64}}},
|
|
||||||
{Country: "USA", Region: "Virginia", City: "Ashburn", Hostname: "va.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{198, 98, 183, 37}, {198, 98, 183, 133}, {198, 98, 183, 152}}},
|
|
||||||
{Country: "USA", Region: "Washington", City: "Seattle", Hostname: "wa.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{66, 115, 149, 49}, {172, 98, 86, 150}, {172, 98, 86, 166}, {172, 98, 86, 182}, {199, 187, 211, 46}, {199, 187, 211, 92}, {199, 187, 211, 172}, {199, 187, 211, 187}, {199, 187, 211, 202}, {199, 187, 211, 232}, {199, 187, 211, 247}}},
|
|
||||||
{Country: "USA", Region: "West Virginia", City: "Philippi", Hostname: "wv.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 88}, {5, 62, 59, 68}}},
|
|
||||||
{Country: "USA", Region: "Wisconsin", City: "Madison", Hostname: "wi.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{204, 15, 110, 131}, {204, 15, 110, 163}}},
|
|
||||||
{Country: "USA", Region: "Wyoming", City: "Cheyenne", Hostname: "wy.us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 92}, {5, 62, 59, 72}}},
|
|
||||||
{Country: "USA", Region: "", City: "", Hostname: "us.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 84}, {5, 62, 59, 64}}},
|
|
||||||
{Country: "Uganda", Region: "", City: "Kampala", Hostname: "ug.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 192}, {5, 62, 63, 172}}},
|
|
||||||
{Country: "Ukraine", Region: "", City: "Odessa", Hostname: "ua.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{31, 28, 162, 3}, {31, 28, 162, 35}, {62, 149, 29, 158}, {62, 149, 29, 190}}},
|
|
||||||
{Country: "United Arab Emirates", Region: "", City: "Dubai", Hostname: "ae.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 200}, {5, 62, 63, 188}}},
|
|
||||||
{Country: "Uruguay", Region: "", City: "Montevideo", Hostname: "uy.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 56, 244}, {5, 62, 58, 224}}},
|
|
||||||
{Country: "Uzbekistan", Region: "", City: "Samarkand", Hostname: "uz.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 204}, {5, 62, 63, 192}}},
|
|
||||||
{Country: "Vanuatu", Region: "", City: "Loltong", Hostname: "vu.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 96}, {5, 62, 59, 76}}},
|
|
||||||
{Country: "Vatican", Region: "", City: "Vatican City", Hostname: "va.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 208}, {5, 62, 63, 196}}},
|
|
||||||
{Country: "Venezuela", Region: "", City: "Caracas", Hostname: "ve.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 57, 100}, {5, 62, 59, 80}}},
|
|
||||||
{Country: "Vietnam", Region: "", City: "Ho Chi Minh City", Hostname: "vn.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 212}, {5, 62, 63, 200}}},
|
|
||||||
{Country: "Yemen", Region: "", City: "Sanaa", Hostname: "ye.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 216}, {5, 62, 63, 204}}},
|
|
||||||
{Country: "Zambia", Region: "", City: "Lusaka", Hostname: "zm.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 220}, {5, 62, 63, 208}}},
|
|
||||||
{Country: "Zimbabwe", Region: "", City: "Harare", Hostname: "zw.hma.rocks", TCP: true, UDP: true, IPs: []net.IP{{5, 62, 61, 224}, {5, 62, 63, 212}}},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
34
internal/constants/ipvanish.go
Normal file
34
internal/constants/ipvanish.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
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(servers []models.IpvanishServer) (choices []string) {
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Country
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IpvanishCityChoices(servers []models.IpvanishServer) (choices []string) {
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].City
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IpvanishHostnameChoices(servers []models.IpvanishServer) (choices []string) {
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Hostname
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
43
internal/constants/ivpn.go
Normal file
43
internal/constants/ivpn.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package constants
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:lll
|
||||||
|
const (
|
||||||
|
IvpnCA = "MIIGoDCCBIigAwIBAgIJAJjvUclXmxtnMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJDSDEPMA0GA1UECAwGWnVyaWNoMQ8wDQYDVQQHDAZadXJpY2gxETAPBgNVBAoMCElWUE4ubmV0MQ0wCwYDVQQLDARJVlBOMRgwFgYDVQQDDA9JVlBOIFJvb3QgQ0EgdjIxHzAdBgkqhkiG9w0BCQEWEHN1cHBvcnRAaXZwbi5uZXQwHhcNMjAwMjI2MTA1MjI5WhcNNDAwMjIxMTA1MjI5WjCBjDELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBlp1cmljaDEPMA0GA1UEBwwGWnVyaWNoMREwDwYDVQQKDAhJVlBOLm5ldDENMAsGA1UECwwESVZQTjEYMBYGA1UEAwwPSVZQTiBSb290IENBIHYyMR8wHQYJKoZIhvcNAQkBFhBzdXBwb3J0QGl2cG4ubmV0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxHVeaQN3nYCLnGoEg6cY44AExbQ3W6XGKYwC9vI+HJbb1o0tAv56ryvc6eS6BdG5q9M8fHaHEE/jw9rtznioiXPwIEmqMqFPA9k1oRIQTGX73m+zHGtRpt9P4tGYhkvbqnN0OGI0H+j9R6cwKi7KpWIoTVibtyI7uuwgzC2nvDzVkLi63uvnCKRXcGy3VWC06uWFbqI9+QDrHHgdJA1F0wRfg0Iac7TE75yXItBMvNLbdZpge9SmplYWFQ2rVPG+n75KepJ+KW7PYfTP4Mh3R8A7h3/WRm03o3spf2aYw71t44voZ6agvslvwqGyczDytsLUny0U2zR7/mfEAyVbL8jqcWr2Df0m3TA0WxwdWvA51/RflVk9G96LncUkoxuBT56QSMtdjbMSqRgLfz1iPsglQEaCzUSqHfQExvONhXtNgy+Pr2+wGrEuSlLMee7aUEMTFEX/vHPZanCrUVYf5Vs8vDOirZjQSHJfgZfwj3nL5VLtIq6ekDhSAdrqCTILP3V2HbgdZGWPVQxl4YmQPKo0IJpse5Kb6TF2o0i90KhORcKg7qZA40sEbYLEwqTM7VBs1FahTXsOPAoMa7xZWV1TnigF5pdVS1l51dy5S8L4ErHFEnAp242BDuTClSLVnWDdofW0EZ0OkK7V9zKyVl75dlBgxMIS0y5MsK7IWicCAwEAAaOCAQEwgf4wHQYDVR0OBBYEFHUDcMOMo35yg2A/v0uYfkDE11CXMIHBBgNVHSMEgbkwgbaAFHUDcMOMo35yg2A/v0uYfkDE11CXoYGSpIGPMIGMMQswCQYDVQQGEwJDSDEPMA0GA1UECAwGWnVyaWNoMQ8wDQYDVQQHDAZadXJpY2gxETAPBgNVBAoMCElWUE4ubmV0MQ0wCwYDVQQLDARJVlBOMRgwFgYDVQQDDA9JVlBOIFJvb3QgQ0EgdjIxHzAdBgkqhkiG9w0BCQEWEHN1cHBvcnRAaXZwbi5uZXSCCQCY71HJV5sbZzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAABAjRMJy+mXFLezAZ8iUgxOjNtSqkCv1aU78K1XkYUzbwNNrSIVGKfP9cqOEiComXY6nniws7QEV2IWilcdPKm0x57recrr9TExGGOTVGB/WdmsFfn0g/HgmxNvXypzG3qulBk4qQTymICdsl9vIPb1l9FSjKw1KgUVuCPaYq7xiXbZ/kZdZX49xeKtoDBrXKKhXVYoWus/S+k2IS8iCxvcp599y7LQJg5DOGlbaxFhsW4R+kfGOaegyhPvpaznguv02i7NLd99XqJhpv2jTUF5F3T23Z4KkL/wTo4zxz09DKOlELrE4ai++ilCt/mXWECXNOSNXzgszpe6WAs0h9R++sH+AzJyhBfIGgPUTxHHHvxBVLj3k6VCgF7mRP2Y+rTWa6d8AGI2+RaeyV9DVVH9UeSoU0Hv2JHiZL6dRERnyg8dyzKeTCke8poLIjXF+gyvI+22/xsL8jcNHi9Kji3Vpc3i0Mxzx3gu2N+PL71CwJilgqBgxj0firr3k8sFcWVSGos6RJ3IvFvThxYx0p255WrWM01fR9TktPYEfjDT9qpIJ8OrGlNOhWhYj+a45qibXDpaDdb/uBEmf2sSXNifjSeUyqu6cKfZvMqB7pS3l/AhuAOTT80E4sXLEoDxkFD4C78swZ8wyWRKwsWGIGABGAHwXEAoDiZ/jjFrEZT0="
|
||||||
|
IvpnOpenvpnStaticKeyV1 = "ac470c93ff9f5602a8aab37dee84a52814d10f20490ad23c47d5d82120c1bf859e93d0696b455d4a1b8d55d40c2685c41ca1d0aef29a3efd27274c4ef09020a3978fe45784b335da6df2d12db97bbb838416515f2a96f04715fd28949c6fe296a925cfada3f8b8928ed7fc963c1563272f5cf46e5e1d9c845d7703ca881497b7e6564a9d1dea9358adffd435295479f47d5298fabf5359613ff5992cb57ff081a04dfb81a26513a6b44a9b5490ad265f8a02384832a59cc3e075ad545461060b7bcab49bac815163cb80983dd51d5b1fd76170ffd904d8291071e96efc3fb777856c717b148d08a510f5687b8a8285dcffe737b98916dd15ef6235dee4266d3b"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IvpnCountryChoices(servers []models.IvpnServer) (choices []string) {
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Country
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IvpnCityChoices(servers []models.IvpnServer) (choices []string) {
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].City
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IvpnISPChoices(servers []models.IvpnServer) (choices []string) {
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].ISP
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IvpnHostnameChoices(servers []models.IvpnServer) (choices []string) {
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Hostname
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user