Compare commits
451 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
91b037a335 | ||
|
|
d5ef3de64c | ||
|
|
167a0b0b29 | ||
|
|
954e3c70b2 | ||
|
|
b02a80abbd | ||
|
|
04313d3c3b | ||
|
|
fb8279f8f0 | ||
|
|
e0e56595c6 | ||
|
|
44d8cf9d4e | ||
|
|
282c1e53ec | ||
|
|
7ba98af1cc | ||
|
|
d3df5aaa52 | ||
|
|
1c83dcab5e | ||
|
|
6208081788 | ||
|
|
3795e92a82 | ||
|
|
0636123e7a | ||
|
|
69f9461bcd | ||
|
|
d1558a3472 | ||
|
|
8230596f98 | ||
|
|
cc4117e054 | ||
|
|
a0ddbc037f | ||
|
|
de82d4e616 | ||
|
|
fa220f9e93 | ||
|
|
aca112fa42 | ||
|
|
9f4077d35d | ||
|
|
9509b855f1 | ||
|
|
be72f4a046 | ||
|
|
8b36ce198f | ||
|
|
71de05dc68 | ||
|
|
83b5a9457a | ||
|
|
0b7ada9fd9 | ||
|
|
92bcef0b1c | ||
|
|
a10c4056d0 | ||
|
|
1fd3ee7149 | ||
|
|
e3a157bfe1 | ||
|
|
b446aa6590 | ||
|
|
c54ee71e1d | ||
|
|
1748a2ae12 | ||
|
|
eff46aa97a | ||
|
|
9fb186af75 | ||
|
|
f1b1001863 | ||
|
|
c5af536299 | ||
|
|
b9b2f691a5 | ||
|
|
bdc8817672 | ||
|
|
a55acb2816 | ||
|
|
d686c76db3 | ||
|
|
30c1ae651e | ||
|
|
adaad62fbd | ||
|
|
fe5ec205fc | ||
|
|
576400e0d9 | ||
|
|
f08a03106f | ||
|
|
f852b7789e | ||
|
|
b0bd06bdc5 | ||
|
|
84787f0ea2 | ||
|
|
f69b3dbbe6 | ||
|
|
ec5ec6f02c | ||
|
|
5d681e635b | ||
|
|
3deb65b529 | ||
|
|
3e527fee8b | ||
|
|
b1f1f94a76 | ||
|
|
43e140e6cc | ||
|
|
7ca9d445f1 | ||
|
|
90aaf71270 | ||
|
|
4f2570865c | ||
|
|
81556ec2e1 | ||
|
|
dd5a9c6067 | ||
|
|
982c50c756 | ||
|
|
3c7dc9b9ad | ||
|
|
8f4354936c | ||
|
|
e50941277f | ||
|
|
a72647b925 | ||
|
|
e254849009 | ||
|
|
5757f0e201 | ||
|
|
75fdf7adab | ||
|
|
702eafae4c | ||
|
|
4abb8cd87f | ||
|
|
5194361f3b | ||
|
|
bc83b75634 | ||
|
|
b1ff95affa | ||
|
|
a243d48fb1 | ||
|
|
937d09f1c3 | ||
|
|
3769092888 | ||
|
|
75281dee07 | ||
|
|
26a7c5eaef | ||
|
|
1e8254fabf | ||
|
|
43b6509b43 | ||
|
|
204c5b2446 | ||
|
|
7ab3347604 | ||
|
|
2f24a79d4d | ||
|
|
819b1fe0f6 | ||
|
|
9f04b2d56c | ||
|
|
5eff5fac67 | ||
|
|
d033d08c4d | ||
|
|
25644d061e | ||
|
|
e7372f22cb | ||
|
|
4530dd4fea | ||
|
|
072528af83 | ||
|
|
2c4d577f23 | ||
|
|
edd67e3473 | ||
|
|
f389642dba | ||
|
|
b8690c7f83 | ||
|
|
06b809a492 | ||
|
|
2ceda2faaa | ||
|
|
c7fc3afc21 | ||
|
|
af57043afd | ||
|
|
4a85f3660c | ||
|
|
69713f34b2 | ||
|
|
55801597c6 | ||
|
|
ff3cc98d46 | ||
|
|
79489796ae | ||
|
|
8e495494fd | ||
|
|
1abb716bb6 | ||
|
|
3f012dd7a3 | ||
|
|
bf6bab7963 | ||
|
|
9db10f56ef | ||
|
|
3b91e351b7 | ||
|
|
657937d272 | ||
|
|
d294fbab15 | ||
|
|
cfbf5624e1 | ||
|
|
c833e9a1a8 | ||
|
|
f1b261163b | ||
|
|
4553240601 | ||
|
|
007a4536c7 | ||
|
|
31cf5d4a5a | ||
|
|
3e3bd05c79 | ||
|
|
20deaf2950 | ||
|
|
680aef62ee | ||
|
|
f5eb4887a7 | ||
|
|
dc3452c5b7 | ||
|
|
a67efd1ad1 | ||
|
|
5dcbe79fa8 | ||
|
|
574ac9a603 | ||
|
|
6871444728 | ||
|
|
f4db7e3e53 | ||
|
|
da92b6bfb9 | ||
|
|
d713782fe1 | ||
|
|
02cde5f50b | ||
|
|
c5a7a83d3a | ||
|
|
6655a1a5e6 | ||
|
|
b8cb181070 | ||
|
|
a56471fe73 | ||
|
|
8c769812ae | ||
|
|
f7a842e4ee | ||
|
|
23c0334f68 | ||
|
|
e2ee7a0408 | ||
|
|
8f862b3df7 | ||
|
|
ae1f91a997 | ||
|
|
d4fb76770f | ||
|
|
ea28c791e6 | ||
|
|
251555f859 | ||
|
|
fa7bda7ee4 | ||
|
|
f385c4203a | ||
|
|
1e4243dedb | ||
|
|
5f78ee7b79 | ||
|
|
c6eb5c1785 | ||
|
|
11338b6382 | ||
|
|
6f3a074e00 | ||
|
|
e827079604 | ||
|
|
cf66db8d4b | ||
|
|
25acbf8501 | ||
|
|
e4c7a887d2 | ||
|
|
fb8a615660 | ||
|
|
1d9d49f406 | ||
|
|
0069b59ffe | ||
|
|
d4ba1b1e09 | ||
|
|
3a20b84f3a | ||
|
|
d52fc777ac | ||
|
|
5753a428d8 | ||
|
|
85afef5775 | ||
|
|
b4fc24995c | ||
|
|
5917bb10e4 | ||
|
|
258e150ebf | ||
|
|
96f2b2b617 | ||
|
|
d556db079b | ||
|
|
a811a82329 | ||
|
|
d17a0dae1f | ||
|
|
ef40f2f91b | ||
|
|
a921f9848c | ||
|
|
95ba3261fd | ||
|
|
fe81eb65c2 | ||
|
|
8428714cf5 | ||
|
|
bedf613cff | ||
|
|
e643ce5b99 | ||
|
|
cb64302294 | ||
|
|
8d5f2fec09 | ||
|
|
60e98235ca | ||
|
|
f55fb4055f | ||
|
|
da4e410bb7 | ||
|
|
cdd1f87437 | ||
|
|
7058373916 | ||
|
|
8dd38fd182 | ||
|
|
73479bab26 | ||
|
|
f5366c33bc | ||
|
|
db886163c2 | ||
|
|
91f5338db0 | ||
|
|
82a02287ac | ||
|
|
2dc674559e | ||
|
|
38e713fea2 | ||
|
|
2cbb14c36c | ||
|
|
610e88958e | ||
|
|
bb76477467 | ||
|
|
433a799759 | ||
|
|
22965ccce3 | ||
|
|
4257581f55 | ||
|
|
d60d629105 | ||
|
|
3f721b1717 | ||
|
|
97049bfab4 | ||
|
|
84944a87d3 | ||
|
|
fb62910b17 | ||
|
|
1cc0f5fee9 | ||
|
|
6896cf4258 | ||
|
|
188d63c6b8 | ||
|
|
cbc5d466f6 | ||
|
|
aef14a9f6d | ||
|
|
f48392064e | ||
|
|
994bdd0ca7 | ||
|
|
40ed070f21 | ||
|
|
f1e4b9937b | ||
|
|
0423388b52 | ||
|
|
096a9c5fc0 | ||
|
|
7518f74729 | ||
|
|
854401a150 | ||
|
|
a7a7efe9c3 | ||
|
|
31883f9adb | ||
|
|
3b04677f8f | ||
|
|
b5fb2b849a | ||
|
|
0c9f74ffa4 | ||
|
|
58da55da1e | ||
|
|
db64dea664 | ||
|
|
f7bff247aa | ||
|
|
edc08c46d4 | ||
|
|
78d83145ba | ||
|
|
0c81154f36 | ||
|
|
53fe08ea26 | ||
|
|
a6cb1a7052 | ||
|
|
c64fe7e45d | ||
|
|
a062135148 | ||
|
|
5ae7c15211 | ||
|
|
f29707fa9f | ||
|
|
e97d1e4a9a | ||
|
|
ed4fcc17b3 | ||
|
|
716eb14da1 | ||
|
|
f92489f99b | ||
|
|
ea3b3bc8a3 | ||
|
|
a80cb8f9ba | ||
|
|
d4813ba21c | ||
|
|
bf92008e45 | ||
|
|
9c73faaaeb | ||
|
|
f9bef8ecda | ||
|
|
302adb26d7 | ||
|
|
af606463ea | ||
|
|
c932f48a95 | ||
|
|
84c1f46ae4 | ||
|
|
b27e637894 | ||
|
|
4da9607b4d | ||
|
|
8abc22977c | ||
|
|
6f4be72785 | ||
|
|
0d2ca377df | ||
|
|
98f778c3bb | ||
|
|
9b9ae69404 | ||
|
|
1c747a10c8 | ||
|
|
c4354871f7 | ||
|
|
9f6450502c | ||
|
|
ae7fc5fe96 | ||
|
|
ec157f102b | ||
|
|
fbecbc1c82 | ||
|
|
ecf76896a2 | ||
|
|
ae876b93d7 | ||
|
|
606f2cffce | ||
|
|
564d9cbf90 | ||
|
|
c5b5ae9ca7 | ||
|
|
4e0bd46dd5 | ||
|
|
f9b6e854b1 | ||
|
|
1fc1776dbf | ||
|
|
464c7074d0 | ||
|
|
cb1520cb18 | ||
|
|
e0e450ca1c | ||
|
|
1c012e4c92 | ||
|
|
78ce272bd0 | ||
|
|
a19efbd923 | ||
|
|
ee64cbf1fd | ||
|
|
5b3cbb6906 | ||
|
|
443c7e36d7 | ||
|
|
22b389b6f8 | ||
|
|
797fa33971 | ||
|
|
9dcc00900e | ||
|
|
7c102c0028 | ||
|
|
aac5274eab | ||
|
|
049bc5b226 | ||
|
|
d463e4cb69 | ||
|
|
99ba56f574 | ||
|
|
93aaf1ab02 | ||
|
|
aa9693a84d | ||
|
|
6fc2b3dd21 | ||
|
|
7e3e6f166a | ||
|
|
c614a192a4 | ||
|
|
b10a476622 | ||
|
|
15ddbdefef | ||
|
|
78323f0a33 | ||
|
|
cd60fe4406 | ||
|
|
a2a9410053 | ||
|
|
f95f6201b1 | ||
|
|
90e5742211 | ||
|
|
8f547500d0 | ||
|
|
0811b8b099 | ||
|
|
c5c53a2ff8 | ||
|
|
0ce129b63d | ||
|
|
fec1249293 | ||
|
|
a5c35455d1 | ||
|
|
28e0abc922 | ||
|
|
a13be8f45e | ||
|
|
85bd4f2e8d | ||
|
|
4baf0420d6 | ||
|
|
29f74df450 | ||
|
|
fab9939b26 | ||
|
|
b4a4e441c1 | ||
|
|
e8526141be | ||
|
|
9abb630692 | ||
|
|
9b92ece5a1 | ||
|
|
87a3e54044 | ||
|
|
76b730e2a6 | ||
|
|
51af8d1ab0 | ||
|
|
002ffacd35 | ||
|
|
404cee9371 | ||
|
|
f89e7aa8dc | ||
|
|
a0312ec916 | ||
|
|
83cf59b93e | ||
|
|
ad5de13c25 |
5
.devcontainer/.dockerignore
Normal file
5
.devcontainer/.dockerignore
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.dockerignore
|
||||||
|
devcontainer.json
|
||||||
|
docker-compose.yml
|
||||||
|
Dockerfile
|
||||||
|
README.md
|
||||||
1
.devcontainer/Dockerfile
Normal file
1
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1 @@
|
|||||||
|
FROM qmcgaw/godevcontainer
|
||||||
69
.devcontainer/README.md
Normal file
69
.devcontainer/README.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# Development container
|
||||||
|
|
||||||
|
Development container that can be used with VSCode.
|
||||||
|
|
||||||
|
It works on Linux, Windows and OSX.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- [VS code](https://code.visualstudio.com/download) installed
|
||||||
|
- [VS code remote containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) installed
|
||||||
|
- [Docker](https://www.docker.com/products/docker-desktop) installed and running
|
||||||
|
- If you don't use Linux or WSL 2, share your home directory `~/` and the directory of your project with Docker Desktop
|
||||||
|
- [Docker Compose](https://docs.docker.com/compose/install/) installed
|
||||||
|
- Ensure your host has the following and that they are accessible by Docker:
|
||||||
|
- `~/.ssh` directory
|
||||||
|
- `~/.gitconfig` file (can be empty)
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. Open the command palette in Visual Studio Code (CTRL+SHIFT+P).
|
||||||
|
1. Select `Remote-Containers: Open Folder in Container...` and choose the project directory.
|
||||||
|
1. For Docker running on Windows HyperV, if you want to use SSH keys, bind mount them at `/tmp/.ssh` by changing the `volumes` section in the [docker-compose.yml](docker-compose.yml).
|
||||||
|
|
||||||
|
## Customization
|
||||||
|
|
||||||
|
### Customize the image
|
||||||
|
|
||||||
|
You can make changes to the [Dockerfile](Dockerfile) and then rebuild the image. For example, your Dockerfile could be:
|
||||||
|
|
||||||
|
```Dockerfile
|
||||||
|
FROM qmcgaw/godevcontainer
|
||||||
|
USER root
|
||||||
|
RUN apk add curl
|
||||||
|
USER vscode
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that you may need to use `USER root` to build as root, and then change back to `USER vscode`.
|
||||||
|
|
||||||
|
To rebuild the image, either:
|
||||||
|
|
||||||
|
- With VSCode through the command palette, select `Remote-Containers: Rebuild and reopen in container`
|
||||||
|
- With a terminal, go to this directory and `docker-compose build`
|
||||||
|
|
||||||
|
### Customize VS code settings
|
||||||
|
|
||||||
|
You can customize **settings** and **extensions** in the [devcontainer.json](devcontainer.json) definition file.
|
||||||
|
|
||||||
|
### Entrypoint script
|
||||||
|
|
||||||
|
You can bind mount a shell script to `/home/vscode/.welcome.sh` to replace the [current welcome script](shell/.welcome.sh).
|
||||||
|
|
||||||
|
### Publish a port
|
||||||
|
|
||||||
|
To access a port from your host to your development container, publish a port in [docker-compose.yml](docker-compose.yml).
|
||||||
|
|
||||||
|
### Run other services
|
||||||
|
|
||||||
|
1. Modify [docker-compose.yml](docker-compose.yml) to launch other services at the same time as this development container, such as a test database:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
database:
|
||||||
|
image: postgres
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: password
|
||||||
|
```
|
||||||
|
|
||||||
|
1. In [devcontainer.json](devcontainer.json), change the line `"runServices": ["vscode"],` to `"runServices": ["vscode", "database"],`.
|
||||||
|
1. In the VS code command palette, rebuild the container.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "pia-dev",
|
"name": "gluetun-dev",
|
||||||
"dockerComposeFile": [
|
"dockerComposeFile": [
|
||||||
"docker-compose.yml"
|
"docker-compose.yml"
|
||||||
],
|
],
|
||||||
@@ -8,108 +8,74 @@
|
|||||||
"vscode"
|
"vscode"
|
||||||
],
|
],
|
||||||
"shutdownAction": "stopCompose",
|
"shutdownAction": "stopCompose",
|
||||||
"postCreateCommand": "go mod download",
|
"postCreateCommand": "source ~/.windows.sh && go mod download && go mod tidy",
|
||||||
"workspaceFolder": "/workspace",
|
"workspaceFolder": "/workspace",
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"golang.go",
|
"golang.go",
|
||||||
"IBM.output-colorizer",
|
"eamodio.gitlens", // IDE Git information
|
||||||
"eamodio.gitlens",
|
|
||||||
"mhutchie.git-graph",
|
|
||||||
"davidanson.vscode-markdownlint",
|
"davidanson.vscode-markdownlint",
|
||||||
"shardulm94.trailing-spaces",
|
"ms-azuretools.vscode-docker", // Docker integration and linting
|
||||||
"alefragnani.Bookmarks",
|
"shardulm94.trailing-spaces", // Show trailing spaces
|
||||||
"Gruntfuggly.todo-tree",
|
"Gruntfuggly.todo-tree", // Highlights TODO comments
|
||||||
"mohsen1.prettify-json",
|
"bierner.emojisense", // Emoji sense for markdown
|
||||||
"quicktype.quicktype",
|
"stkb.rewrap", // rewrap comments after n characters on one line
|
||||||
"spikespaz.vscode-smoothtype",
|
"vscode-icons-team.vscode-icons", // Better file extension icons
|
||||||
"stkb.rewrap",
|
"github.vscode-pull-request-github", // Github interaction
|
||||||
"vscode-icons-team.vscode-icons"
|
"redhat.vscode-yaml", // Kubernetes, Drone syntax highlighting
|
||||||
|
"bajdzis.vscode-database", // Supports connections to mysql or postgres, over SSL, socked
|
||||||
|
"IBM.output-colorizer", // Colorize your output/test logs
|
||||||
|
"mohsen1.prettify-json", // Prettify JSON data
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
// General settings
|
|
||||||
"files.eol": "\n",
|
"files.eol": "\n",
|
||||||
// Docker
|
|
||||||
"remote.extensionKind": {
|
"remote.extensionKind": {
|
||||||
"ms-azuretools.vscode-docker": "workspace"
|
"ms-azuretools.vscode-docker": "workspace"
|
||||||
},
|
},
|
||||||
// Golang general settings
|
"editor.codeActionsOnSaveTimeout": 3000,
|
||||||
"go.useLanguageServer": true,
|
"go.useLanguageServer": true,
|
||||||
|
"[go]": {
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.organizeImports": true,
|
||||||
|
},
|
||||||
|
// Optional: Disable snippets, as they conflict with completion ranking.
|
||||||
|
"editor.snippetSuggestions": "none"
|
||||||
|
},
|
||||||
|
"[go.mod]": {
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.organizeImports": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"gopls": {
|
||||||
|
"usePlaceholders": false,
|
||||||
|
"staticcheck": true
|
||||||
|
},
|
||||||
"go.autocompleteUnimportedPackages": true,
|
"go.autocompleteUnimportedPackages": true,
|
||||||
"go.gotoSymbol.includeImports": true,
|
"go.gotoSymbol.includeImports": true,
|
||||||
"go.gotoSymbol.includeGoroot": true,
|
"go.gotoSymbol.includeGoroot": true,
|
||||||
"gopls": {
|
|
||||||
"completeUnimported": true,
|
|
||||||
"deepCompletion": true,
|
|
||||||
"usePlaceholders": false
|
|
||||||
},
|
|
||||||
"go.lintTool": "golangci-lint",
|
"go.lintTool": "golangci-lint",
|
||||||
"go.lintFlags": [
|
|
||||||
"--fast",
|
|
||||||
"--enable",
|
|
||||||
"staticcheck",
|
|
||||||
"--enable",
|
|
||||||
"bodyclose",
|
|
||||||
"--enable",
|
|
||||||
"dogsled",
|
|
||||||
"--enable",
|
|
||||||
"gochecknoglobals",
|
|
||||||
"--enable",
|
|
||||||
"gochecknoinits",
|
|
||||||
"--enable",
|
|
||||||
"gocognit",
|
|
||||||
"--enable",
|
|
||||||
"goconst",
|
|
||||||
"--enable",
|
|
||||||
"gocritic",
|
|
||||||
"--enable",
|
|
||||||
"gocyclo",
|
|
||||||
"--enable",
|
|
||||||
"golint",
|
|
||||||
"--enable",
|
|
||||||
"gosec",
|
|
||||||
"--enable",
|
|
||||||
"interfacer",
|
|
||||||
"--enable",
|
|
||||||
"maligned",
|
|
||||||
"--enable",
|
|
||||||
"misspell",
|
|
||||||
"--enable",
|
|
||||||
"nakedret",
|
|
||||||
"--enable",
|
|
||||||
"prealloc",
|
|
||||||
"--enable",
|
|
||||||
"scopelint",
|
|
||||||
"--enable",
|
|
||||||
"unconvert",
|
|
||||||
"--enable",
|
|
||||||
"unparam",
|
|
||||||
"--enable",
|
|
||||||
"whitespace"
|
|
||||||
],
|
|
||||||
// Golang on save
|
|
||||||
"go.buildOnSave": "workspace",
|
"go.buildOnSave": "workspace",
|
||||||
"go.lintOnSave": "workspace",
|
"go.lintOnSave": "workspace",
|
||||||
"go.vetOnSave": "workspace",
|
"go.vetOnSave": "workspace",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"[go]": {
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.organizeImports": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Golang testing
|
|
||||||
"go.toolsEnvVars": {
|
"go.toolsEnvVars": {
|
||||||
"GOFLAGS": "-tags=integration"
|
"GOFLAGS": "-tags=",
|
||||||
|
// "CGO_ENABLED": 1 // for the race detector
|
||||||
},
|
},
|
||||||
"gopls.env": {
|
"gopls.env": {
|
||||||
"GOFLAGS": "-tags=integration"
|
"GOFLAGS": "-tags="
|
||||||
|
},
|
||||||
|
"go.testEnvVars": {
|
||||||
|
"": ""
|
||||||
},
|
},
|
||||||
"go.testEnvVars": {},
|
|
||||||
"go.testFlags": [
|
"go.testFlags": [
|
||||||
"-v",
|
"-v",
|
||||||
// "-race"
|
// "-race"
|
||||||
],
|
],
|
||||||
"go.testTimeout": "600s",
|
"go.testTimeout": "10s",
|
||||||
|
"go.coverOnSingleTest": true,
|
||||||
"go.coverOnSingleTestFile": true,
|
"go.coverOnSingleTestFile": true,
|
||||||
"go.coverOnSingleTest": true
|
"go.coverOnTestPackage": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,14 +2,29 @@ version: "3.7"
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
vscode:
|
vscode:
|
||||||
image: qmcgaw/godevcontainer
|
build: .
|
||||||
|
image: godevcontainer
|
||||||
volumes:
|
volumes:
|
||||||
- ../:/workspace
|
- ../:/workspace
|
||||||
- ~/.ssh:/home/vscode/.ssh:ro
|
# Docker socket to access Docker server
|
||||||
- ~/.ssh:/root/.ssh:ro
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
# Docker configuration
|
||||||
|
- ~/.docker:/root/.docker:z
|
||||||
|
# SSH directory for Linux, OSX and WSL
|
||||||
|
- ~/.ssh:/root/.ssh:z
|
||||||
|
# For Windows without WSL, a copy will be made
|
||||||
|
# from /tmp/.ssh to ~/.ssh to fix permissions
|
||||||
|
#- ~/.ssh:/tmp/.ssh:ro
|
||||||
|
# Shell history persistence
|
||||||
|
- ~/.zsh_history:/root/.zsh_history:z
|
||||||
|
# Git config
|
||||||
|
- ~/.gitconfig:/root/.gitconfig:z
|
||||||
|
environment:
|
||||||
|
- TZ=
|
||||||
cap_add:
|
cap_add:
|
||||||
|
# For debugging with dlv
|
||||||
- SYS_PTRACE
|
- SYS_PTRACE
|
||||||
security_opt:
|
security_opt:
|
||||||
|
# For debugging with dlv
|
||||||
- seccomp:unconfined
|
- seccomp:unconfined
|
||||||
entrypoint: zsh -c "while sleep 1000; do :; done"
|
entrypoint: zsh -c "while sleep 1000; do :; done"
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
.devcontainer
|
.devcontainer
|
||||||
.git
|
.git
|
||||||
.github
|
.github
|
||||||
.vscode
|
|
||||||
cmd
|
|
||||||
!cmd/gluetun
|
|
||||||
doc
|
doc
|
||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
|
Dockerfile
|
||||||
LICENSE
|
LICENSE
|
||||||
README.md
|
README.md
|
||||||
title.svg
|
title.svg
|
||||||
|
|||||||
19
.github/CONTRIBUTING.md
vendored
19
.github/CONTRIBUTING.md
vendored
@@ -4,26 +4,15 @@ Contributions are [released](https://help.github.com/articles/github-terms-of-se
|
|||||||
|
|
||||||
## Submitting a pull request
|
## Submitting a pull request
|
||||||
|
|
||||||
1. [Fork](https://github.com/qdm12/private-internet-access-docker/fork) and clone the repository
|
1. [Fork](https://github.com/qdm12/gluetun/fork) and clone the repository
|
||||||
1. Create a new branch `git checkout -b my-branch-name`
|
1. Create a new branch `git checkout -b my-branch-name`
|
||||||
1. Modify the code
|
1. Modify the code
|
||||||
1. Ensure the docker build succeeds `docker build .`
|
1. Ensure the docker build succeeds `docker build .` (you might need `export DOCKER_BUILDKIT=1`)
|
||||||
1. Commit your modifications
|
1. Commit your modifications
|
||||||
1. Push to your fork and [submit a pull request](https://github.com/qdm12/private-internet-access-docker/compare)
|
1. Push to your fork and [submit a pull request](https://github.com/qdm12/gluetun/compare)
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
|
- [Gluetun guide on development](https://github.com/qdm12/gluetun/wiki/Development)
|
||||||
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
|
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
|
||||||
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
|
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
|
||||||
|
|
||||||
## Contributors
|
|
||||||
|
|
||||||
Thanks for all the contributions, whether small or not so small!
|
|
||||||
|
|
||||||
- [@JeordyR](https://github.com/JeordyR) for testing the Mullvad version and opening a [PR with a few fixes](https://github.com/qdm12/private-internet-access-docker/pull/84/files) 👍
|
|
||||||
- [@rorph](https://github.com/rorph) for a [PR to pick a random region for PIA](https://github.com/qdm12/private-internet-access-docker/pull/70) and a [PR to make the container work with kubernetes](https://github.com/qdm12/private-internet-access-docker/pull/69)
|
|
||||||
- [@JesterEE](https://github.com/JesterEE) for a [PR to fix silly line endings in block lists back then](https://github.com/qdm12/private-internet-access-docker/pull/55) 📎
|
|
||||||
- [@elmerfdz](https://github.com/elmerfdz) for a [PR to add timezone information to have correct log timestampts](https://github.com/qdm12/private-internet-access-docker/pull/51) 🕙
|
|
||||||
- [@Juggels](https://github.com/Juggels) for a [PR to write the PIA forwarded port to a file](https://github.com/qdm12/private-internet-access-docker/pull/43)
|
|
||||||
- [@gdlx](https://github.com/gdlx) for a [PR to fix and improve PIA port forwarding script](https://github.com/qdm12/private-internet-access-docker/pull/32)
|
|
||||||
- [@janaz](https://github.com/janaz) for keeping an eye on [updating things in the Dockerfile](https://github.com/qdm12/private-internet-access-docker/pull/8)
|
|
||||||
|
|||||||
52
.github/ISSUE_TEMPLATE/bug.md
vendored
52
.github/ISSUE_TEMPLATE/bug.md
vendored
@@ -1,55 +1,45 @@
|
|||||||
---
|
---
|
||||||
name: Bug
|
name: Bug
|
||||||
about: Report a bug
|
about: Report a bug
|
||||||
title: 'Bug: ...'
|
title: 'Bug: FILL THIS TEXT!'
|
||||||
labels: ":bug: bug"
|
labels: ":bug: bug"
|
||||||
assignees: qdm12
|
assignees: qdm12
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**TLDR**: *Describe your issue in a one liner here*
|
<!---
|
||||||
|
⚠️ Answer the following or I'll insta-close your issue
|
||||||
|
-->
|
||||||
|
|
||||||
1. Is this urgent?
|
**Is this urgent?**: No
|
||||||
|
|
||||||
- [ ] Yes
|
**Host OS** (approximate answer is fine too): Ubuntu 18
|
||||||
- [x] No
|
|
||||||
|
|
||||||
2. What VPN service provider are you using?
|
**CPU arch** or **device name**: amd64
|
||||||
|
|
||||||
- [x] PIA
|
**What VPN provider are you using**:
|
||||||
- [ ] Mullvad
|
|
||||||
- [ ] Windscribe
|
|
||||||
- [ ] Surfshark
|
|
||||||
- [ ] Cyberghost
|
|
||||||
|
|
||||||
3. What's the version of the program?
|
**What are you using to run your container?**: Docker Compose
|
||||||
|
|
||||||
**See the line at the top of your logs**
|
**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)`
|
```
|
||||||
|
Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
|
||||||
|
```
|
||||||
|
|
||||||
4. What are you using to run the container?
|
**What's the problem** 🤔
|
||||||
|
|
||||||
- [ ] Docker run
|
That feature doesn't work
|
||||||
- [x] Docker Compose
|
|
||||||
- [ ] Kubernetes
|
|
||||||
- [ ] Docker stack
|
|
||||||
- [ ] Docker swarm
|
|
||||||
- [ ] Podman
|
|
||||||
- [ ] Other:
|
|
||||||
|
|
||||||
5. Extra information
|
**Share your logs... (careful to remove in example tokens)**
|
||||||
|
|
||||||
Logs:
|
|
||||||
|
|
||||||
```log
|
```log
|
||||||
|
|
||||||
```
|
PASTE YOUR LOGS
|
||||||
|
IN THERE
|
||||||
Configuration file:
|
|
||||||
|
|
||||||
```yml
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Host OS:
|
<!---
|
||||||
|
💡 You can highlight your code with https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlight
|
||||||
|
-->
|
||||||
|
|||||||
11
.github/ISSUE_TEMPLATE/feature_request.md
vendored
11
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,14 +1,17 @@
|
|||||||
---
|
---
|
||||||
name: Feature request
|
name: Feature request
|
||||||
about: Suggest a feature to add to this project
|
about: Suggest a feature to add to this project
|
||||||
title: 'Feature request: ...'
|
title: 'Feature request: FILL THIS TEXT!'
|
||||||
labels: ":bulb: feature request"
|
labels: ":bulb: feature request"
|
||||||
assignees: qdm12
|
assignees: qdm12
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
1. What's the feature?
|
**What's the feature?** 🧐
|
||||||
|
|
||||||
2. Why do you need this feature?
|
- Support this new feature because that and that
|
||||||
|
|
||||||
3. Extra information?
|
**Optional extra information** 🚀
|
||||||
|
|
||||||
|
- I tried `docker run something` and it doesn't work
|
||||||
|
- That [url](https://github.com/qdm12/gluetun) is interesting
|
||||||
|
|||||||
68
.github/ISSUE_TEMPLATE/help.md
vendored
68
.github/ISSUE_TEMPLATE/help.md
vendored
@@ -1,55 +1,67 @@
|
|||||||
---
|
---
|
||||||
name: Help
|
name: Help
|
||||||
about: Ask for help
|
about: Ask for help
|
||||||
title: 'Help: ...'
|
title: 'Help: FILL THIS TEXT!'
|
||||||
labels: ":pray: help wanted"
|
labels: ":pray: help wanted"
|
||||||
assignees:
|
assignees:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**TLDR**: *Describe your issue in a one liner here*
|
<!---
|
||||||
|
⚠️ If this about a Docker configuration problem or another service:
|
||||||
|
Start a discussion at https://github.com/qdm12/gluetun/discussions/new
|
||||||
|
OR I WILL INSTA-CLOSE YOUR ISSUE.
|
||||||
|
-->
|
||||||
|
|
||||||
1. Is this urgent?
|
<!---
|
||||||
|
⚠️ Answer the following or I'll insta-close your issue
|
||||||
|
-->
|
||||||
|
|
||||||
- [ ] Yes
|
**Is this urgent?**: No
|
||||||
- [x] No
|
|
||||||
|
|
||||||
2. What VPN service provider are you using?
|
**Host OS** (approximate answer is fine too): Ubuntu 18
|
||||||
|
|
||||||
- [x] PIA
|
**CPU arch** or **device name**: amd64
|
||||||
- [ ] Mullvad
|
|
||||||
- [ ] Windscribe
|
|
||||||
- [ ] Surfshark
|
|
||||||
- [ ] Cyberghost
|
|
||||||
|
|
||||||
3. What's the version of the program?
|
**What VPN provider are you using**:
|
||||||
|
|
||||||
**See the line at the top of your logs**
|
**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)`
|
```
|
||||||
|
Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
|
||||||
|
```
|
||||||
|
|
||||||
4. What are you using to run the container?
|
**What's the problem** 🤔
|
||||||
|
|
||||||
- [ ] Docker run
|
That feature doesn't work
|
||||||
- [x] Docker Compose
|
|
||||||
- [ ] Kubernetes
|
|
||||||
- [ ] Docker stack
|
|
||||||
- [ ] Docker swarm
|
|
||||||
- [ ] Podman
|
|
||||||
- [ ] Other:
|
|
||||||
|
|
||||||
5. Extra information
|
**Share your logs... (careful to remove in example tokens)**
|
||||||
|
|
||||||
Logs:
|
|
||||||
|
|
||||||
```log
|
```log
|
||||||
|
|
||||||
|
PASTE YOUR LOGS
|
||||||
|
IN THERE
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Configuration file:
|
**What are you using to run your container?**: Docker Compose
|
||||||
|
|
||||||
|
<!---
|
||||||
|
💡 You can highlight your code with https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlight
|
||||||
|
-->
|
||||||
|
|
||||||
|
Please also share your configuration file:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
your .yml
|
||||||
|
content
|
||||||
|
in here
|
||||||
```
|
```
|
||||||
|
|
||||||
Host OS:
|
or
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# your docker
|
||||||
|
# run command
|
||||||
|
# in here
|
||||||
|
```
|
||||||
|
|||||||
17
.github/ISSUE_TEMPLATE/provider.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/provider.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
name: Support a VPN provider
|
||||||
|
about: Suggest a VPN provider to be supported
|
||||||
|
title: 'VPN provider support: NAME OF THE PROVIDER'
|
||||||
|
labels: ":bulb: New provider"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
One of the following is required:
|
||||||
|
|
||||||
|
- Publicly accessible URL to a zip file containing the Openvpn configuration files
|
||||||
|
- Publicly accessible URL to a structured (JSON etc.) list of servers **and attach** an example Openvpn configuration file for both TCP and UDP
|
||||||
|
- Publicly accessible URL to the list of servers **and attach** an example Openvpn configuration file for both TCP and UDP
|
||||||
|
|
||||||
|
If the list of servers requires to login **or** is hidden behind an interactive configurator,
|
||||||
|
you can only use a custom Openvpn configuration file.
|
||||||
|
[The Wiki](https://github.com/qdm12/gluetun/wiki/Openvpn-file) describes how to do so.
|
||||||
15
.github/dependabot.yml
vendored
Normal file
15
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
# Maintain dependencies for GitHub Actions
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
- package-ecosystem: docker
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
- package-ecosystem: gomod
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
119
.github/labels.yml
vendored
119
.github/labels.yml
vendored
@@ -1,51 +1,90 @@
|
|||||||
- name: ":robot: bot"
|
- name: "Bug :bug:"
|
||||||
color: "69cde9"
|
|
||||||
description: ""
|
|
||||||
- name: ":bug: bug"
|
|
||||||
color: "b60205"
|
color: "b60205"
|
||||||
description: ""
|
description: ""
|
||||||
- name: ":game_die: dependencies"
|
- name: "Feature request :bulb:"
|
||||||
color: "0366d6"
|
|
||||||
description: ""
|
|
||||||
- name: ":memo: documentation"
|
|
||||||
color: "c5def5"
|
|
||||||
description: ""
|
|
||||||
- name: ":busts_in_silhouette: duplicate"
|
|
||||||
color: "cccccc"
|
|
||||||
description: ""
|
|
||||||
- name: ":sparkles: enhancement"
|
|
||||||
color: "0054ca"
|
|
||||||
description: ""
|
|
||||||
- name: ":bulb: feature request"
|
|
||||||
color: "0e8a16"
|
color: "0e8a16"
|
||||||
description: ""
|
description: ""
|
||||||
- name: ":mega: feedback"
|
- name: "Help wanted :pray:"
|
||||||
color: "03a9f4"
|
|
||||||
description: ""
|
|
||||||
- name: ":rocket: future maybe"
|
|
||||||
color: "fef2c0"
|
|
||||||
description: ""
|
|
||||||
- name: ":hatching_chick: good first issue"
|
|
||||||
color: "7057ff"
|
|
||||||
description: ""
|
|
||||||
- name: ":pray: help wanted"
|
|
||||||
color: "4caf50"
|
color: "4caf50"
|
||||||
description: ""
|
description: ""
|
||||||
- name: ":hand: hold"
|
- name: "Documentation :memo:"
|
||||||
color: "24292f"
|
color: "c5def5"
|
||||||
description: ""
|
description: ""
|
||||||
- name: ":no_entry_sign: invalid"
|
- name: "Needs more info :thinking:"
|
||||||
color: "e6e6e6"
|
|
||||||
description: ""
|
|
||||||
- name: ":interrobang: maybe bug"
|
|
||||||
color: "ff5722"
|
|
||||||
description: ""
|
|
||||||
- name: ":thinking: needs more info"
|
|
||||||
color: "795548"
|
color: "795548"
|
||||||
description: ""
|
description: ""
|
||||||
- name: ":question: question"
|
|
||||||
color: "3f51b5"
|
# VPN providers
|
||||||
|
- name: ":cloud: Cyberghost"
|
||||||
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
- name: ":coffin: wontfix"
|
- name: ":cloud: HideMyAss"
|
||||||
color: "ffffff"
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: IPVanish"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: IVPN"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: FastestVPN"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: Mullvad"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: NordVPN"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: PIA"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: Privado"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: PrivateVPN"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: ProtonVPN"
|
||||||
|
color: "cfe8d4"
|
||||||
|
- name: ":cloud: PureVPN"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: Surfshark"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: Torguard"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: VPNUnlimited"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: Vyprvpn"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
- name: ":cloud: Windscribe"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
|
|
||||||
|
# Problem category
|
||||||
|
- name: "Openvpn"
|
||||||
|
color: "ffc7ea"
|
||||||
|
description: ""
|
||||||
|
- name: "Unbound (DNS over TLS)"
|
||||||
|
color: "ffc7ea"
|
||||||
|
description: ""
|
||||||
|
- name: "Firewall"
|
||||||
|
color: "ffc7ea"
|
||||||
|
description: ""
|
||||||
|
- name: "HTTP proxy"
|
||||||
|
color: "ffc7ea"
|
||||||
|
description: ""
|
||||||
|
- name: "Shadowsocks"
|
||||||
|
color: "ffc7ea"
|
||||||
|
description: ""
|
||||||
|
- name: "Healthcheck server"
|
||||||
|
color: "ffc7ea"
|
||||||
|
description: ""
|
||||||
|
- name: "Control server"
|
||||||
|
color: "ffc7ea"
|
||||||
description: ""
|
description: ""
|
||||||
|
|||||||
131
.github/workflows/build.yml
vendored
131
.github/workflows/build.yml
vendored
@@ -1,34 +1,105 @@
|
|||||||
name: Docker build
|
name: CI
|
||||||
on:
|
on:
|
||||||
pull_request:
|
push:
|
||||||
branches: [master]
|
paths:
|
||||||
paths-ignore:
|
- .github/workflows/build.yml
|
||||||
- .devcontainer
|
- cmd/**
|
||||||
- .github/ISSUE_TEMPLATE
|
- internal/**
|
||||||
- .github/workflows/buildx-release.yml
|
- pkg/**
|
||||||
- .github/workflows/buildx-branch.yml
|
- .dockerignore
|
||||||
- .github/workflows/buildx-latest.yml
|
- .golangci.yml
|
||||||
- .github/workflows/dockerhub-description.yml
|
- Dockerfile
|
||||||
- .github/workflows/labels.yml
|
- go.mod
|
||||||
- .github/workflows/misspell.yml
|
- go.sum
|
||||||
- .github/CODEOWNERS
|
|
||||||
- .github/CONTRIBUTING.md
|
|
||||||
- .github/FUNDING.yml
|
|
||||||
- .github/labels.yml
|
|
||||||
- .vscode
|
|
||||||
- cmd/ovpnparser
|
|
||||||
- cmd/resolver
|
|
||||||
- doc
|
|
||||||
- .gitignore
|
|
||||||
- docker-compose.yml
|
|
||||||
- LICENSE
|
|
||||||
- README.md
|
|
||||||
- title.svg
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
verify:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
DOCKER_BUILDKIT: "1"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2.3.4
|
||||||
|
|
||||||
|
- name: Linting
|
||||||
|
run: docker build --target lint .
|
||||||
|
|
||||||
|
- name: Go mod tidy check
|
||||||
|
run: docker build --target tidy .
|
||||||
|
|
||||||
|
- name: Build test image
|
||||||
|
run: docker build --target test -t test-container .
|
||||||
|
|
||||||
|
- name: Run tests in test container
|
||||||
|
run: |
|
||||||
|
touch coverage.txt
|
||||||
|
docker run --rm \
|
||||||
|
-v "$(pwd)/coverage.txt:/tmp/gobuild/coverage.txt" \
|
||||||
|
test-container
|
||||||
|
|
||||||
|
- name: Code security analysis
|
||||||
|
uses: snyk/actions/golang@master
|
||||||
|
env:
|
||||||
|
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build final image
|
||||||
|
run: docker build -t final-image .
|
||||||
|
|
||||||
|
- name: Image security analysis
|
||||||
|
uses: snyk/actions/docker@master
|
||||||
|
env:
|
||||||
|
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||||
|
with:
|
||||||
|
image: final-image
|
||||||
|
|
||||||
|
publish:
|
||||||
|
needs: [verify]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- uses: actions/checkout@v2.3.4
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Build image
|
- uses: docker/setup-qemu-action@v1
|
||||||
run: docker build .
|
- 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.4.0
|
||||||
|
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 }}
|
||||||
|
ALLTARGETPLATFORMS=${{ steps.vars.outputs.platforms }}
|
||||||
|
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
|
||||||
|
|||||||
50
.github/workflows/buildx-branch.yml
vendored
50
.github/workflows/buildx-branch.yml
vendored
@@ -1,50 +0,0 @@
|
|||||||
name: Buildx branch
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- '*'
|
|
||||||
- '*/*'
|
|
||||||
- '!master'
|
|
||||||
paths-ignore:
|
|
||||||
- .devcontainer
|
|
||||||
- .github/ISSUE_TEMPLATE
|
|
||||||
- .github/workflows/build.yml
|
|
||||||
- .github/workflows/buildx-release.yml
|
|
||||||
- .github/workflows/buildx-latest.yml
|
|
||||||
- .github/workflows/dockerhub-description.yml
|
|
||||||
- .github/workflows/labels.yml
|
|
||||||
- .github/workflows/misspell.yml
|
|
||||||
- .github/CODEOWNERS
|
|
||||||
- .github/CONTRIBUTING.md
|
|
||||||
- .github/FUNDING.yml
|
|
||||||
- .github/labels.yml
|
|
||||||
- .vscode
|
|
||||||
- cmd/ovpnparser
|
|
||||||
- cmd/resolver
|
|
||||||
- doc
|
|
||||||
- .gitignore
|
|
||||||
- docker-compose.yml
|
|
||||||
- LICENSE
|
|
||||||
- README.md
|
|
||||||
- title.svg
|
|
||||||
jobs:
|
|
||||||
buildx:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Buildx setup
|
|
||||||
uses: crazy-max/ghaction-docker-buildx@v1
|
|
||||||
- name: Dockerhub login
|
|
||||||
run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u qmcgaw --password-stdin 2>&1
|
|
||||||
- name: Run Buildx
|
|
||||||
run: |
|
|
||||||
docker buildx build \
|
|
||||||
--progress plain \
|
|
||||||
--platform=linux/amd64 \
|
|
||||||
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
|
|
||||||
--build-arg VCS_REF=`git rev-parse --short HEAD` \
|
|
||||||
--build-arg VERSION=${GITHUB_REF##*/} \
|
|
||||||
-t qmcgaw/private-internet-access:${GITHUB_REF##*/} \
|
|
||||||
--push \
|
|
||||||
.
|
|
||||||
- run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/private-internet-access/tQFy7AxtSUNANPe6aoVChYdsI_I= || exit 0
|
|
||||||
47
.github/workflows/buildx-latest.yml
vendored
47
.github/workflows/buildx-latest.yml
vendored
@@ -1,47 +0,0 @@
|
|||||||
name: Buildx latest
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [master]
|
|
||||||
paths-ignore:
|
|
||||||
- .devcontainer
|
|
||||||
- .github/ISSUE_TEMPLATE
|
|
||||||
- .github/workflows/build.yml
|
|
||||||
- .github/workflows/buildx-branch.yml
|
|
||||||
- .github/workflows/buildx-release.yml
|
|
||||||
- .github/workflows/dockerhub-description.yml
|
|
||||||
- .github/workflows/labels.yml
|
|
||||||
- .github/workflows/misspell.yml
|
|
||||||
- .github/CODEOWNERS
|
|
||||||
- .github/CONTRIBUTING.md
|
|
||||||
- .github/FUNDING.yml
|
|
||||||
- .github/labels.yml
|
|
||||||
- .vscode
|
|
||||||
- cmd/ovpnparser
|
|
||||||
- cmd/resolver
|
|
||||||
- doc
|
|
||||||
- .gitignore
|
|
||||||
- docker-compose.yml
|
|
||||||
- LICENSE
|
|
||||||
- README.md
|
|
||||||
- title.svg
|
|
||||||
jobs:
|
|
||||||
buildx:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Buildx setup
|
|
||||||
uses: crazy-max/ghaction-docker-buildx@v1
|
|
||||||
- name: Dockerhub login
|
|
||||||
run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u qmcgaw --password-stdin 2>&1
|
|
||||||
- name: Run Buildx
|
|
||||||
run: |
|
|
||||||
docker buildx build \
|
|
||||||
--progress plain \
|
|
||||||
--platform=linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6 \
|
|
||||||
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
|
|
||||||
--build-arg VCS_REF=`git rev-parse --short HEAD` \
|
|
||||||
--build-arg VERSION=latest \
|
|
||||||
-t qmcgaw/private-internet-access:latest \
|
|
||||||
--push \
|
|
||||||
.
|
|
||||||
- run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/private-internet-access/tQFy7AxtSUNANPe6aoVChYdsI_I= || exit 0
|
|
||||||
47
.github/workflows/buildx-release.yml
vendored
47
.github/workflows/buildx-release.yml
vendored
@@ -1,47 +0,0 @@
|
|||||||
name: Buildx release
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
paths-ignore:
|
|
||||||
- .devcontainer
|
|
||||||
- .github/ISSUE_TEMPLATE
|
|
||||||
- .github/workflows/build.yml
|
|
||||||
- .github/workflows/buildx-branch.yml
|
|
||||||
- .github/workflows/buildx-latest.yml
|
|
||||||
- .github/workflows/dockerhub-description.yml
|
|
||||||
- .github/workflows/labels.yml
|
|
||||||
- .github/workflows/misspell.yml
|
|
||||||
- .github/CODEOWNERS
|
|
||||||
- .github/CONTRIBUTING.md
|
|
||||||
- .github/FUNDING.yml
|
|
||||||
- .github/labels.yml
|
|
||||||
- .vscode
|
|
||||||
- cmd/ovpnparser
|
|
||||||
- cmd/resolver
|
|
||||||
- doc
|
|
||||||
- .gitignore
|
|
||||||
- docker-compose.yml
|
|
||||||
- LICENSE
|
|
||||||
- README.md
|
|
||||||
- title.svg
|
|
||||||
jobs:
|
|
||||||
buildx:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Buildx setup
|
|
||||||
uses: crazy-max/ghaction-docker-buildx@v1
|
|
||||||
- name: Dockerhub login
|
|
||||||
run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u qmcgaw --password-stdin 2>&1
|
|
||||||
- name: Run Buildx
|
|
||||||
run: |
|
|
||||||
docker buildx build \
|
|
||||||
--progress plain \
|
|
||||||
--platform=linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6 \
|
|
||||||
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
|
|
||||||
--build-arg VCS_REF=`git rev-parse --short HEAD` \
|
|
||||||
--build-arg VERSION=${GITHUB_REF##*/} \
|
|
||||||
-t qmcgaw/private-internet-access:${GITHUB_REF##*/} \
|
|
||||||
--push \
|
|
||||||
.
|
|
||||||
- run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/private-internet-access/tQFy7AxtSUNANPe6aoVChYdsI_I= || exit 0
|
|
||||||
14
.github/workflows/dockerhub-description.yml
vendored
14
.github/workflows/dockerhub-description.yml
vendored
@@ -10,10 +10,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2.3.4
|
||||||
- name: Docker Hub Description
|
- name: Docker Hub Description
|
||||||
uses: peter-evans/dockerhub-description@v2.1.0
|
uses: peter-evans/dockerhub-description@v2
|
||||||
env:
|
with:
|
||||||
DOCKERHUB_USERNAME: qmcgaw
|
username: qmcgaw
|
||||||
DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }}
|
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||||
DOCKERHUB_REPOSITORY: qmcgaw/private-internet-access
|
repository: qmcgaw/gluetun
|
||||||
|
short-description: Lightweight Swiss-knife VPN client to connect to several VPN providers
|
||||||
|
readme-filepath: README.md
|
||||||
|
|||||||
17
.github/workflows/labels.yml
vendored
17
.github/workflows/labels.yml
vendored
@@ -1,18 +1,15 @@
|
|||||||
name: labels
|
name: labels
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["master"]
|
branches: [master]
|
||||||
paths:
|
paths:
|
||||||
- '.github/labels.yml'
|
- .github/labels.yml
|
||||||
- '.github/workflows/labels.yml'
|
- .github/workflows/labels.yml
|
||||||
jobs:
|
jobs:
|
||||||
labeler:
|
labeler:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- uses: actions/checkout@v2.3.4
|
||||||
uses: actions/checkout@v2
|
- uses: crazy-max/ghaction-github-labeler@v3
|
||||||
- name: Labeler
|
with:
|
||||||
if: success()
|
yaml-file: .github/labels.yml
|
||||||
uses: crazy-max/ghaction-github-labeler@v1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|||||||
5
.github/workflows/misspell.yml
vendored
5
.github/workflows/misspell.yml
vendored
@@ -8,9 +8,8 @@ jobs:
|
|||||||
misspell:
|
misspell:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2.3.4
|
||||||
- uses: reviewdog/action-misspell@master
|
- uses: reviewdog/action-misspell@v1
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
locale: "US"
|
locale: "US"
|
||||||
level: error
|
level: error
|
||||||
|
|||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
scratch.txt
|
||||||
@@ -4,35 +4,73 @@ linters-settings:
|
|||||||
misspell:
|
misspell:
|
||||||
locale: US
|
locale: US
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-rules:
|
||||||
|
- path: _test\.go
|
||||||
|
linters:
|
||||||
|
- dupl
|
||||||
|
- maligned
|
||||||
|
- goerr113
|
||||||
|
- path: internal/server/
|
||||||
|
linters:
|
||||||
|
- dupl
|
||||||
|
- path: internal/configuration/
|
||||||
|
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
|
disable-all: true
|
||||||
enable:
|
enable:
|
||||||
|
- asciicheck
|
||||||
- bodyclose
|
- bodyclose
|
||||||
- deadcode
|
- deadcode
|
||||||
- dogsled
|
- dogsled
|
||||||
- dupl
|
- dupl
|
||||||
- errcheck
|
- errcheck
|
||||||
|
- exhaustive
|
||||||
|
- exportloopref
|
||||||
|
- gci
|
||||||
- gochecknoglobals
|
- gochecknoglobals
|
||||||
- gochecknoinits
|
- gochecknoinits
|
||||||
- gocognit
|
- gocognit
|
||||||
- goconst
|
- goconst
|
||||||
- gocritic
|
- gocritic
|
||||||
- gocyclo
|
- gocyclo
|
||||||
|
- godot
|
||||||
|
- goerr113
|
||||||
|
- goheader
|
||||||
- goimports
|
- goimports
|
||||||
- golint
|
- gomnd
|
||||||
|
- goprintffuncname
|
||||||
- gosec
|
- gosec
|
||||||
- gosimple
|
- gosimple
|
||||||
- govet
|
- govet
|
||||||
|
- importas
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- interfacer
|
- lll
|
||||||
- maligned
|
|
||||||
- misspell
|
- misspell
|
||||||
- nakedret
|
- nakedret
|
||||||
|
- nestif
|
||||||
|
- nilerr
|
||||||
|
- noctx
|
||||||
|
- nolintlint
|
||||||
- prealloc
|
- prealloc
|
||||||
|
- predeclared
|
||||||
|
- revive
|
||||||
- rowserrcheck
|
- rowserrcheck
|
||||||
- scopelint
|
- sqlclosecheck
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- structcheck
|
- structcheck
|
||||||
|
- thelper
|
||||||
|
- tparallel
|
||||||
- typecheck
|
- typecheck
|
||||||
- unconvert
|
- unconvert
|
||||||
- unparam
|
- unparam
|
||||||
@@ -44,7 +82,4 @@ run:
|
|||||||
skip-dirs:
|
skip-dirs:
|
||||||
- .devcontainer
|
- .devcontainer
|
||||||
- .github
|
- .github
|
||||||
- postgres
|
- doc
|
||||||
|
|
||||||
service:
|
|
||||||
golangci-lint-version: 1.27.x # use the fixed version to not introduce new linters unexpectedly
|
|
||||||
|
|||||||
9
.vscode/extensions.json
vendored
9
.vscode/extensions.json
vendored
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"recommendations": [
|
|
||||||
"shardulm94.trailing-spaces",
|
|
||||||
"ms-azuretools.vscode-docker",
|
|
||||||
"davidanson.vscode-markdownlint",
|
|
||||||
"IBM.output-colorizer",
|
|
||||||
"golang.go"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
91
.vscode/settings.json
vendored
91
.vscode/settings.json
vendored
@@ -1,91 +0,0 @@
|
|||||||
{
|
|
||||||
// General settings
|
|
||||||
"files.eol": "\n",
|
|
||||||
// Docker
|
|
||||||
"remote.extensionKind": {
|
|
||||||
"ms-azuretools.vscode-docker": "workspace"
|
|
||||||
},
|
|
||||||
// Golang general settings
|
|
||||||
"go.useLanguageServer": true,
|
|
||||||
"go.autocompleteUnimportedPackages": true,
|
|
||||||
"go.gotoSymbol.includeImports": true,
|
|
||||||
"go.gotoSymbol.includeGoroot": true,
|
|
||||||
"gopls": {
|
|
||||||
"completeUnimported": true,
|
|
||||||
"deepCompletion": true,
|
|
||||||
"usePlaceholders": false
|
|
||||||
},
|
|
||||||
"go.lintTool": "golangci-lint",
|
|
||||||
"go.lintFlags": [
|
|
||||||
"--fast",
|
|
||||||
"--enable",
|
|
||||||
"rowserrcheck",
|
|
||||||
"--enable",
|
|
||||||
"bodyclose",
|
|
||||||
"--enable",
|
|
||||||
"dogsled",
|
|
||||||
"--enable",
|
|
||||||
"dupl",
|
|
||||||
"--enable",
|
|
||||||
"gochecknoglobals",
|
|
||||||
"--enable",
|
|
||||||
"gochecknoinits",
|
|
||||||
"--enable",
|
|
||||||
"gocognit",
|
|
||||||
"--enable",
|
|
||||||
"goconst",
|
|
||||||
"--enable",
|
|
||||||
"gocritic",
|
|
||||||
"--enable",
|
|
||||||
"gocyclo",
|
|
||||||
"--enable",
|
|
||||||
"goimports",
|
|
||||||
"--enable",
|
|
||||||
"golint",
|
|
||||||
"--enable",
|
|
||||||
"gosec",
|
|
||||||
"--enable",
|
|
||||||
"interfacer",
|
|
||||||
"--enable",
|
|
||||||
"maligned",
|
|
||||||
"--enable",
|
|
||||||
"misspell",
|
|
||||||
"--enable",
|
|
||||||
"nakedret",
|
|
||||||
"--enable",
|
|
||||||
"prealloc",
|
|
||||||
"--enable",
|
|
||||||
"scopelint",
|
|
||||||
"--enable",
|
|
||||||
"unconvert",
|
|
||||||
"--enable",
|
|
||||||
"unparam",
|
|
||||||
"--enable",
|
|
||||||
"whitespace"
|
|
||||||
],
|
|
||||||
// Golang on save
|
|
||||||
"go.buildOnSave": "workspace",
|
|
||||||
"go.lintOnSave": "workspace",
|
|
||||||
"go.vetOnSave": "workspace",
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"[go]": {
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.organizeImports": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Golang testing
|
|
||||||
"go.toolsEnvVars": {
|
|
||||||
"GOFLAGS": "-tags="
|
|
||||||
},
|
|
||||||
"gopls.env": {
|
|
||||||
"GOFLAGS": "-tags="
|
|
||||||
},
|
|
||||||
"go.testEnvVars": {},
|
|
||||||
"go.testFlags": [
|
|
||||||
"-v",
|
|
||||||
// "-race"
|
|
||||||
],
|
|
||||||
"go.testTimeout": "600s",
|
|
||||||
"go.coverOnSingleTestFile": true,
|
|
||||||
"go.coverOnSingleTest": true
|
|
||||||
}
|
|
||||||
171
Dockerfile
171
Dockerfile
@@ -1,72 +1,120 @@
|
|||||||
ARG ALPINE_VERSION=3.12
|
ARG ALPINE_VERSION=3.14
|
||||||
ARG GO_VERSION=1.14
|
ARG GO_ALPINE_VERSION=3.13
|
||||||
|
ARG GO_VERSION=1.16
|
||||||
|
ARG XCPUTRANSLATE_VERSION=v0.6.0
|
||||||
|
ARG GOLANGCI_LINT_VERSION=v1.41.1
|
||||||
|
ARG BUILDPLATFORM=linux/amd64
|
||||||
|
|
||||||
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS builder
|
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
|
||||||
ARG GOLANGCI_LINT_VERSION=v1.27.0
|
COPY --from=golangci-lint /bin /go/bin/golangci-lint
|
||||||
RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s ${GOLANGCI_LINT_VERSION}
|
|
||||||
WORKDIR /tmp/gobuild
|
WORKDIR /tmp/gobuild
|
||||||
COPY .golangci.yml .
|
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download 2>&1
|
RUN go mod download
|
||||||
COPY cmd/gluetun/main.go .
|
COPY cmd/ ./cmd/
|
||||||
COPY internal/ ./internal/
|
COPY internal/ ./internal/
|
||||||
RUN go test ./...
|
|
||||||
|
FROM --platform=$BUILDPLATFORM base AS test
|
||||||
|
# Note on the go race detector:
|
||||||
|
# - we set CGO_ENABLED=1 to have it enabled
|
||||||
|
# - we installed g++ to support the race detector
|
||||||
|
ENV CGO_ENABLED=1
|
||||||
|
ENTRYPOINT go test -race -coverpkg=./... -coverprofile=coverage.txt -covermode=atomic ./...
|
||||||
|
|
||||||
|
FROM --platform=$BUILDPLATFORM base AS lint
|
||||||
|
COPY .golangci.yml ./
|
||||||
RUN golangci-lint run --timeout=10m
|
RUN golangci-lint run --timeout=10m
|
||||||
RUN go build -ldflags="-s -w" -o entrypoint main.go
|
|
||||||
|
FROM --platform=$BUILDPLATFORM base AS tidy
|
||||||
|
RUN git init && \
|
||||||
|
git config user.email ci@localhost && \
|
||||||
|
git config user.name ci && \
|
||||||
|
git add -A && git commit -m ci && \
|
||||||
|
sed -i '/\/\/ indirect/d' go.mod && \
|
||||||
|
go mod tidy && \
|
||||||
|
git diff --exit-code -- go.mod
|
||||||
|
|
||||||
|
FROM --platform=$BUILDPLATFORM base AS build
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
ARG ALLTARGETPLATFORMS=${TARGETPLATFORM}
|
||||||
|
ARG VERSION=unknown
|
||||||
|
ARG BUILD_DATE="an unknown date"
|
||||||
|
ARG COMMIT=unknown
|
||||||
|
RUN xcputranslate sleep -targetplatform ${TARGETPLATFORM} -buildtime=10s -order=${ALLTARGETPLATFORMS}
|
||||||
|
RUN GOARCH="$(xcputranslate translate -field arch -targetplatform ${TARGETPLATFORM})" \
|
||||||
|
GOARM="$(xcputranslate translate -field arm -targetplatform ${TARGETPLATFORM})" \
|
||||||
|
go build -trimpath -ldflags="-s -w \
|
||||||
|
-X 'main.version=$VERSION' \
|
||||||
|
-X 'main.buildDate=$BUILD_DATE' \
|
||||||
|
-X 'main.commit=$COMMIT' \
|
||||||
|
" -o entrypoint cmd/gluetun/main.go
|
||||||
|
|
||||||
FROM alpine:${ALPINE_VERSION}
|
FROM alpine:${ALPINE_VERSION}
|
||||||
ARG VERSION
|
ARG VERSION=unknown
|
||||||
ARG BUILD_DATE
|
ARG BUILD_DATE="an unknown date"
|
||||||
ARG VCS_REF
|
ARG COMMIT=unknown
|
||||||
ENV VERSION=$VERSION \
|
|
||||||
BUILD_DATE=$BUILD_DATE \
|
|
||||||
VCS_REF=$VCS_REF
|
|
||||||
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=$BUILD_DATE \
|
||||||
org.opencontainers.image.version=$VERSION \
|
org.opencontainers.image.version=$VERSION \
|
||||||
org.opencontainers.image.revision=$VCS_REF \
|
org.opencontainers.image.revision=$COMMIT \
|
||||||
org.opencontainers.image.url="https://github.com/qdm12/private-internet-access-docker" \
|
org.opencontainers.image.url="https://github.com/qdm12/gluetun" \
|
||||||
org.opencontainers.image.documentation="https://github.com/qdm12/private-internet-access-docker" \
|
org.opencontainers.image.documentation="https://github.com/qdm12/gluetun" \
|
||||||
org.opencontainers.image.source="https://github.com/qdm12/private-internet-access-docker" \
|
org.opencontainers.image.source="https://github.com/qdm12/gluetun" \
|
||||||
org.opencontainers.image.title="VPN client for PIA, Mullvad, Windscribe, Surfshark and Cyberghost" \
|
org.opencontainers.image.title="VPN swiss-knife like client for multiple VPN providers" \
|
||||||
org.opencontainers.image.description="VPN client to tunnel to PIA, Mullvad, Windscribe, Surfshark and Cyberghost servers using OpenVPN, IPtables, DNS over TLS and Alpine Linux"
|
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 \
|
||||||
PROTOCOL=udp \
|
PROTOCOL=udp \
|
||||||
|
OPENVPN_VERSION=2.5 \
|
||||||
OPENVPN_VERBOSITY=1 \
|
OPENVPN_VERBOSITY=1 \
|
||||||
OPENVPN_ROOT=no \
|
OPENVPN_ROOT=yes \
|
||||||
OPENVPN_TARGET_IP= \
|
OPENVPN_TARGET_IP= \
|
||||||
|
OPENVPN_IPV6=off \
|
||||||
|
OPENVPN_CUSTOM_CONFIG= \
|
||||||
TZ= \
|
TZ= \
|
||||||
UID=1000 \
|
PUID= \
|
||||||
GID=1000 \
|
PGID= \
|
||||||
IP_STATUS_FILE="/ip" \
|
PUBLICIP_FILE="/tmp/gluetun/ip" \
|
||||||
# PIA, Windscribe, Surfshark, Cyberghost, Vyprvpn, NordVPN only
|
# VPN provider settings
|
||||||
USER= \
|
OPENVPN_USER= \
|
||||||
PASSWORD= \
|
OPENVPN_PASSWORD= \
|
||||||
REGION="Austria" \
|
USER_SECRETFILE=/run/secrets/openvpn_user \
|
||||||
# PIA only
|
PASSWORD_SECRETFILE=/run/secrets/openvpn_password \
|
||||||
|
REGION= \
|
||||||
|
COUNTRY= \
|
||||||
|
CITY= \
|
||||||
|
PORT= \
|
||||||
|
SERVER_HOSTNAME= \
|
||||||
|
# Mullvad only:
|
||||||
|
ISP= \
|
||||||
|
OWNED=no \
|
||||||
|
# Private Internet Access only:
|
||||||
PIA_ENCRYPTION=strong \
|
PIA_ENCRYPTION=strong \
|
||||||
PORT_FORWARDING=off \
|
PORT_FORWARDING=off \
|
||||||
PORT_FORWARDING_STATUS_FILE="/forwarded_port" \
|
PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \
|
||||||
# Mullvad only
|
# Cyberghost only:
|
||||||
COUNTRY=Sweden \
|
|
||||||
CITY= \
|
|
||||||
ISP= \
|
|
||||||
# Mullvad and Windscribe only
|
|
||||||
PORT= \
|
|
||||||
# Cyberghost only
|
|
||||||
CYBERGHOST_GROUP="Premium UDP Europe" \
|
CYBERGHOST_GROUP="Premium UDP Europe" \
|
||||||
# NordVPN only
|
OPENVPN_CLIENTCRT_SECRETFILE=/run/secrets/openvpn_clientcrt \
|
||||||
|
OPENVPN_CLIENTKEY_SECRETFILE=/run/secrets/openvpn_clientkey \
|
||||||
|
# Nordvpn only:
|
||||||
SERVER_NUMBER= \
|
SERVER_NUMBER= \
|
||||||
|
# NordVPN and ProtonVPN only:
|
||||||
|
SERVER_NAME= \
|
||||||
|
# ProtonVPN only:
|
||||||
|
FREE_ONLY= \
|
||||||
# Openvpn
|
# Openvpn
|
||||||
OPENVPN_CIPHER= \
|
OPENVPN_CIPHER= \
|
||||||
OPENVPN_AUTH= \
|
OPENVPN_AUTH= \
|
||||||
# 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 \
|
||||||
@@ -81,28 +129,35 @@ ENV VPNSP=pia \
|
|||||||
DNS_KEEP_NAMESERVER=off \
|
DNS_KEEP_NAMESERVER=off \
|
||||||
# Firewall
|
# Firewall
|
||||||
FIREWALL=on \
|
FIREWALL=on \
|
||||||
EXTRA_SUBNETS= \
|
FIREWALL_VPN_INPUT_PORTS= \
|
||||||
|
FIREWALL_INPUT_PORTS= \
|
||||||
|
FIREWALL_OUTBOUND_SUBNETS= \
|
||||||
FIREWALL_DEBUG=off \
|
FIREWALL_DEBUG=off \
|
||||||
# Tinyproxy
|
# HTTP proxy
|
||||||
TINYPROXY=off \
|
HTTPPROXY= \
|
||||||
TINYPROXY_LOG=Info \
|
HTTPPROXY_LOG=off \
|
||||||
TINYPROXY_PORT=8888 \
|
HTTPPROXY_PORT=8888 \
|
||||||
TINYPROXY_USER= \
|
HTTPPROXY_USER= \
|
||||||
TINYPROXY_PASSWORD= \
|
HTTPPROXY_PASSWORD= \
|
||||||
|
HTTPPROXY_USER_SECRETFILE=/run/secrets/httpproxy_user \
|
||||||
|
HTTPPROXY_PASSWORD_SECRETFILE=/run/secrets/httpproxy_password \
|
||||||
# Shadowsocks
|
# Shadowsocks
|
||||||
SHADOWSOCKS=off \
|
SHADOWSOCKS=off \
|
||||||
SHADOWSOCKS_LOG=off \
|
SHADOWSOCKS_LOG=off \
|
||||||
SHADOWSOCKS_PORT=8388 \
|
SHADOWSOCKS_PORT=8388 \
|
||||||
SHADOWSOCKS_PASSWORD= \
|
SHADOWSOCKS_PASSWORD= \
|
||||||
SHADOWSOCKS_METHOD=chacha20-ietf-poly1305
|
SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password \
|
||||||
|
SHADOWSOCKS_METHOD=chacha20-ietf-poly1305 \
|
||||||
|
UPDATER_PERIOD=0
|
||||||
ENTRYPOINT ["/entrypoint"]
|
ENTRYPOINT ["/entrypoint"]
|
||||||
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
|
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
|
||||||
HEALTHCHECK --interval=10m --timeout=10s --start-period=30s --retries=2 CMD /entrypoint healthcheck
|
HEALTHCHECK --interval=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 tinyproxy tzdata && \
|
RUN apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.12/main" openvpn==2.4.11-r0 && \
|
||||||
echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories && \
|
mv /usr/sbin/openvpn /usr/sbin/openvpn2.4 && \
|
||||||
apk add -q --progress --no-cache --update shadowsocks-libev && \
|
apk del openvpn && \
|
||||||
rm -rf /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-* /etc/tinyproxy/tinyproxy.conf && \
|
apk add --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \
|
||||||
|
rm -rf /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-* /etc/openvpn/*.sh /usr/lib/openvpn/plugins/openvpn-plugin-down-root.so && \
|
||||||
deluser openvpn && \
|
deluser openvpn && \
|
||||||
deluser tinyproxy && \
|
deluser unbound && \
|
||||||
deluser unbound
|
mkdir /gluetun
|
||||||
COPY --from=builder /tmp/gobuild/entrypoint /entrypoint
|
COPY --from=build /tmp/gobuild/entrypoint /entrypoint
|
||||||
|
|||||||
450
README.md
450
README.md
@@ -1,82 +1,73 @@
|
|||||||
# Gluetun VPN client
|
# Gluetun VPN client
|
||||||
|
|
||||||
*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access,
|
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, FastestVPN,
|
||||||
Mullvad, Windscribe, Surfshark Cyberghost and NordVPN VPN servers, using Go, OpenVPN,
|
HideMyAss, IPVanish, IVPN, Mullvad, NordVPN, Privado, Private Internet Access, PrivateVPN,
|
||||||
iptables, DNS over TLS, ShadowSocks and Tinyproxy*
|
ProtonVPN, PureVPN, Surfshark, TorGuard, VPNUnlimited, VyprVPN and Windscribe VPN servers
|
||||||
|
using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
||||||
|
|
||||||
**ANNOUNCEMENT**: *[Video of the Git history of Gluetun](https://youtu.be/khipOYJtGJ0)*
|
**ANNOUNCEMENT**:
|
||||||
|
|
||||||
<img height="250" src="https://raw.githubusercontent.com/qdm12/private-internet-access-docker/master/title.svg?sanitize=true">
|

|
||||||
|
|
||||||
[](https://github.com/qdm12/private-internet-access-docker/actions?query=workflow%3A%22Buildx+latest%22)
|
[](https://github.com/qdm12/gluetun/actions?query=workflow%3ACI)
|
||||||
[](https://hub.docker.com/r/qmcgaw/private-internet-access)
|
|
||||||
[](https://hub.docker.com/r/qmcgaw/private-internet-access)
|
|
||||||
|
|
||||||
[](https://github.com/qdm12/private-internet-access-docker/issues)
|
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||||
[](https://github.com/qdm12/private-internet-access-docker/issues)
|
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||||
[](https://github.com/qdm12/private-internet-access-docker/issues)
|
|
||||||
|
|
||||||
[](https://microbadger.com/images/qmcgaw/private-internet-access)
|
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||||
[](https://microbadger.com/images/qmcgaw/private-internet-access)
|
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||||
[](https://join.slack.com/t/qdm12/shared_invite/enQtOTE0NjcxNTM1ODc5LTYyZmVlOTM3MGI4ZWU0YmJkMjUxNmQ4ODQ2OTAwYzMxMTlhY2Q1MWQyOWUyNjc2ODliNjFjMDUxNWNmNzk5MDk)
|
|
||||||
|
|
||||||
<details><summary>Click to show base components</summary><p>
|

|
||||||
|

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

|
||||||
|

|
||||||
|
|
||||||
- [Alpine 3.12](https://alpinelinux.org) for a tiny image (37MB of packages, 6.7MB of Go binary and 5.6MB for Alpine)
|
[](https://hub.docker.com/r/qmcgaw/gluetun/tags)
|
||||||
- [OpenVPN 2.4.9](https://pkgs.alpinelinux.org/package/v3.11/main/x86_64/openvpn) to tunnel to your VPN provider servers
|
|
||||||
- [IPtables 1.8.4](https://pkgs.alpinelinux.org/package/v3.11/main/x86_64/iptables) enforces the container to communicate only through the VPN or with other containers in its virtual network (acts as a killswitch)
|
|
||||||
- [Unbound 1.10.1](https://pkgs.alpinelinux.org/package/v3.11/main/x86_64/unbound) configured with Cloudflare's [1.1.1.1](https://1.1.1.1) DNS over TLS (configurable with 5 different providers)
|
|
||||||
- [Files and blocking lists built periodically](https://github.com/qdm12/updated/tree/master/files) used with Unbound (see `BLOCK_MALICIOUS`, `BLOCK_SURVEILLANCE` and `BLOCK_ADS` environment variables)
|
|
||||||
- [TinyProxy 1.10.0](https://pkgs.alpinelinux.org/package/v3.11/main/x86_64/tinyproxy)
|
|
||||||
- [Shadowsocks 3.3.4](https://pkgs.alpinelinux.org/package/edge/testing/x86/shadowsocks-libev)
|
|
||||||
|
|
||||||
</p></details>
|
[](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)
|
||||||
|

|
||||||
|

|
||||||
|

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

|
||||||
|
|
||||||
|
## Quick links
|
||||||
|
|
||||||
|
- Problem or suggestion?
|
||||||
|
- [Start a discussion](https://github.com/qdm12/gluetun/discussions)
|
||||||
|
- [Create an issue](https://github.com/qdm12/gluetun/issues)
|
||||||
|
- [Check the Wiki](https://github.com/qdm12/gluetun/wiki)
|
||||||
|
- [Join the Slack channel](https://join.slack.com/t/qdm12/shared_invite/enQtOTE0NjcxNTM1ODc5LTYyZmVlOTM3MGI4ZWU0YmJkMjUxNmQ4ODQ2OTAwYzMxMTlhY2Q1MWQyOWUyNjc2ODliNjFjMDUxNWNmNzk5MDk)
|
||||||
|
- Happy?
|
||||||
|
- Sponsor me on [github.com/sponsors/qdm12](https://github.com/sponsors/qdm12)
|
||||||
|
- Donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw)
|
||||||
|
- Drop me [an email](mailto:quentin.mcgaw@gmail.com)
|
||||||
|
- Video:
|
||||||
|
|
||||||
|
[](https://youtu.be/0F6I03LQcI4)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Based on Alpine 3.12 for a small Docker image of 52MB
|
- Based on Alpine 3.13 for a small Docker image of 54MB
|
||||||
- Supports **Private Internet Access**, **Mullvad**, **Windscribe**,
|
- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** servers
|
||||||
**Surfshark**, **Cyberghost** and **NordVPN** servers
|
- Supports Openvpn only for now
|
||||||
- DNS over TLS baked in with service provider(s) of your choice
|
- DNS over TLS baked in with service provider(s) of your choice
|
||||||
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
|
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
|
||||||
- Choose the vpn network protocol, `udp` or `tcp`
|
- Choose the vpn network protocol, `udp` or `tcp`
|
||||||
- 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 SOCKS5 proxy (Shadowsocks, tunnels TCP+UDP)
|
- Built in Shadowsocks proxy (protocol based on SOCKS5 with an encryption layer, tunnels TCP+UDP)
|
||||||
- Built in HTTP proxy (Tinyproxy, tunnels TCP)
|
- Built in HTTP proxy (tunnels HTTP and HTTPS through TCP)
|
||||||
- [Connect other containers to it](https://github.com/qdm12/private-internet-access-docker#connect-to-it)
|
- [Connect other containers to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
|
||||||
- [Connect LAN devices to it](https://github.com/qdm12/private-internet-access-docker#connect-to-it)
|
- [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
|
||||||
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7 🎆
|
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even ppc64le 🎆
|
||||||
|
- VPN server side port forwarding for Private Internet Access and Vyprvpn
|
||||||
### Private Internet Access
|
|
||||||
|
|
||||||
- Pick the [region](https://www.privateinternetaccess.com/pages/network/)
|
|
||||||
- Pick the level of encryption
|
|
||||||
- Enable port forwarding
|
|
||||||
|
|
||||||
### Mullvad
|
|
||||||
|
|
||||||
- Pick the [country, city and ISP](https://mullvad.net/en/servers/#openvpn)
|
|
||||||
- Pick the port to use (i.e. `53` (udp) or `80` (tcp))
|
|
||||||
|
|
||||||
### Windscribe
|
|
||||||
|
|
||||||
- Pick the [region](https://windscribe.com/status)
|
|
||||||
- Pick the port to use
|
|
||||||
|
|
||||||
### Surfshark
|
|
||||||
|
|
||||||
- Pick the [region](https://github.com/qdm12/private-internet-access-docker/wiki/Surfshark) or a multi hop region name
|
|
||||||
|
|
||||||
### Cyberghost
|
|
||||||
|
|
||||||
- Pick the [region](https://github.com/qdm12/private-internet-access-docker/wiki/Cyberghost)
|
|
||||||
|
|
||||||
### Vyprvpn
|
|
||||||
|
|
||||||
- Pick the [region](https://www.vyprvpn.com/server-locations)
|
|
||||||
|
|
||||||
### Extra niche features
|
|
||||||
|
|
||||||
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
|
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
|
||||||
- Subprograms all drop root privileges once launched
|
- Subprograms all drop root privileges once launched
|
||||||
- Subprograms output streams are all merged together
|
- Subprograms output streams are all merged together
|
||||||
@@ -84,333 +75,46 @@ iptables, DNS over TLS, ShadowSocks and Tinyproxy*
|
|||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. Requirements
|
1. Ensure your `tun` kernel module is setup:
|
||||||
- A VPN account with one of the service providers:
|
|
||||||
- Private Internet Access: **username** and **password** ([sign up](https://www.privateinternetaccess.com/pages/buy-vpn/))
|
|
||||||
- Mullvad: user ID ([sign up](https://mullvad.net/en/account/))
|
|
||||||
- Windscribe: **username** and **password** | Signup up using my affiliate link below
|
|
||||||
|
|
||||||
[](https://windscribe.com/?affid=mh7nyafu)
|
```sh
|
||||||
|
sudo modprobe tun
|
||||||
- Surfshark: **username** and **password** ([sign up](https://order.surfshark.com/))
|
# or, if you don't have modprobe, with
|
||||||
- Cyberghost: **username**, **password** and **device client key file** ([sign up](https://www.cyberghostvpn.com/en_US/buy/cyberghost-vpn-4))
|
sudo insmod /lib/modules/tun.ko
|
||||||
- Vyprvpn: **username** and **password**
|
```
|
||||||
- NordVPN: **username** and **password**
|
|
||||||
- If you have a host or router firewall, please refer [to the firewall documentation](https://github.com/qdm12/private-internet-access-docker/blob/master/doc/firewall.md)
|
|
||||||
|
|
||||||
1. On some devices you may need to setup your tunnel kernel module on your host with `insmod /lib/modules/tun.ko` or `modprobe tun`
|
|
||||||
- *Synology users*: please read [this part of the Wiki](https://github.com/qdm12/private-internet-access-docker/wiki/Common-issues#synology)
|
|
||||||
|
|
||||||
|
1. Extra steps:
|
||||||
|
- [For Synology users](https://github.com/qdm12/gluetun/wiki/Synology-setup)
|
||||||
|
- [For 32 bit Operating systems (**Rasberry Pis**)](https://github.com/qdm12/gluetun/wiki/32-bit-setup)
|
||||||
1. Launch the container with:
|
1. Launch the container with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d --name gluetun --cap-add=NET_ADMIN \
|
docker run -d --name gluetun --cap-add=NET_ADMIN \
|
||||||
-e REGION="CA Montreal" -e USER=js89ds7 -e PASSWORD=8fd9s239G \
|
-e VPNSP="private internet access" -e REGION="CA Montreal" \
|
||||||
qmcgaw/private-internet-access
|
-e OPENVPN_USER=js89ds7 -e OPENVPN_PASSWORD=8fd9s239G \
|
||||||
|
-v /yourpath:/gluetun \
|
||||||
|
qmcgaw/gluetun
|
||||||
```
|
```
|
||||||
|
|
||||||
or use [docker-compose.yml](https://github.com/qdm12/private-internet-access-docker/blob/master/docker-compose.yml) with:
|
or use [docker-compose.yml](https://github.com/qdm12/gluetun/blob/master/docker-compose.yml) with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that you can:
|
You should probably check the many [environment variables](https://github.com/qdm12/gluetun/wiki/Environment-variables) available to adapt the container to your needs.
|
||||||
|
|
||||||
- Change the many [environment variables](#environment-variables) available
|
## Further setup
|
||||||
- Use `-p 8888:8888/tcp` to access the HTTP web proxy (and put your LAN in `EXTRA_SUBNETS` environment variable, in example `192.168.1.0/24`)
|
|
||||||
- Use `-p 8388:8388/tcp -p 8388:8388/udp` to access the SOCKS5 proxy (and put your LAN in `EXTRA_SUBNETS` environment variable, in example `192.168.1.0/24`)
|
|
||||||
- Use `-p 8000:8000/tcp` to access the [HTTP control server](#HTTP-control-server) built-in
|
|
||||||
|
|
||||||
**If you encounter an issue with the tun device not being available, see [the FAQ](https://github.com/qdm12/private-internet-access-docker/blob/master/doc/faq.md#how-to-fix-openvpn-failing-to-start)**
|
The following points are all optional but should give you insights on all the possibilities with this container.
|
||||||
|
|
||||||
1. You can update the image with `docker pull qmcgaw/private-internet-access:latest`. See the [wiki](https://github.com/qdm12/private-internet-access-docker/wiki/Common-issues#use-a-release-tag) for more information on other tags available.
|
- [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)
|
||||||
## Testing
|
- [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.
|
||||||
Check the VPN IP address matches your expectations
|
- Update the image with `docker pull qmcgaw/gluetun:latest`. See this [Wiki document](https://github.com/qdm12/gluetun/wiki/Docker-image-tags) for Docker tags available.
|
||||||
|
- Use [Docker secrets](https://github.com/qdm12/gluetun/wiki/Docker-secrets) to read your credentials instead of environment variables
|
||||||
```sh
|
|
||||||
docker run --rm --network=container:gluetun alpine:3.12 wget -qO- https://ipinfo.io
|
|
||||||
```
|
|
||||||
|
|
||||||
Want more testing? ▶ [see the Wiki](https://github.com/qdm12/private-internet-access-docker/wiki/Testing)
|
|
||||||
|
|
||||||
## Environment variables
|
|
||||||
|
|
||||||
**TLDR**; only set the 🏁 marked environment variables to get started.
|
|
||||||
|
|
||||||
### VPN
|
|
||||||
|
|
||||||
| Variable | Default | Choices | Description |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| 🏁 `VPNSP` | `private internet access` | `private internet access`, `mullvad`, `windscribe`, `surfshark`, `vyprvpn`, `nordvpn` | VPN Service Provider |
|
|
||||||
| `IP_STATUS_FILE` | `/ip` | Any filepath | Filepath to store the public IP address assigned |
|
|
||||||
| `PROTOCOL` | `udp` | `udp` or `tcp` | Network protocol to use |
|
|
||||||
| `OPENVPN_VERBOSITY` | `1` | `0` to `6` | Openvpn verbosity level |
|
|
||||||
| `OPENVPN_ROOT` | `no` | `yes` or `no` | Run OpenVPN as root |
|
|
||||||
| `OPENVPN_TARGET_IP` | | Valid IP address | Specify a target VPN server (or gateway) IP address to use |
|
|
||||||
| `OPENVPN_CIPHER` | | i.e. `aes-256-gcm` | Specify a custom cipher to use. It will also set `ncp-disable` if using AES GCM for PIA |
|
|
||||||
| `OPENVPN_AUTH` | | i.e. `sha256` | Specify a custom auth algorithm to use |
|
|
||||||
|
|
||||||
- Private Internet Access
|
|
||||||
|
|
||||||
| Variable | Default | Choices | Description |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| 🏁 `USER` | | | Your username |
|
|
||||||
| 🏁 `PASSWORD` | | | Your password |
|
|
||||||
| `REGION` | `Austria` | One of the [PIA regions](https://www.privateinternetaccess.com/pages/network/) | VPN server region |
|
|
||||||
| `PIA_ENCRYPTION` | `strong` | `normal`, `strong` | Encryption preset |
|
|
||||||
| `PORT_FORWARDING` | `off` | `on`, `off` | Enable port forwarding on the VPN server |
|
|
||||||
| `PORT_FORWARDING_STATUS_FILE` | `/forwarded_port` | Any filepath | Filepath to store the forwarded port number |
|
|
||||||
|
|
||||||
- Mullvad
|
|
||||||
|
|
||||||
| Variable | Default | Choices | Description |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| 🏁 `USER` | | | Your user ID |
|
|
||||||
| `COUNTRY` | `Sweden` | One of the [Mullvad countries](https://mullvad.net/en/servers/#openvpn) | VPN server country |
|
|
||||||
| `CITY` | | One of the [Mullvad cities](https://mullvad.net/en/servers/#openvpn) | VPN server city |
|
|
||||||
| `ISP` | | One of the [Mullvad ISP](https://mullvad.net/en/servers/#openvpn) | VPN server ISP |
|
|
||||||
| `PORT` | | `80` or `443` for TCP; or `53` for UDP. Leave blank for default Mullvad server port | Custom VPN port to use |
|
|
||||||
|
|
||||||
- Windscribe
|
|
||||||
|
|
||||||
| Variable | Default | Choices | Description |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| 🏁 `USER` | | | Your username |
|
|
||||||
| 🏁 `PASSWORD` | | | Your password |
|
|
||||||
| `REGION` | `Austria` | One of the [Windscribe regions](https://windscribe.com/status) | VPN server region |
|
|
||||||
| `PORT` | | One from the [this list of ports](https://windscribe.com/getconfig/openvpn) | Custom VPN port to use |
|
|
||||||
|
|
||||||
- Surfshark
|
|
||||||
|
|
||||||
| Variable | Default | Choices | Description |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| 🏁 `USER` | | | Your **service** username, found at the bottom of the [manual setup page](https://account.surfshark.com/setup/manual) |
|
|
||||||
| 🏁 `PASSWORD` | | | Your **service** password |
|
|
||||||
| `REGION` | `Austria` | One of the [Surfshark regions (subdomains)](https://github.com/qdm12/private-internet-access-docker/wiki/surfshark) | VPN server region |
|
|
||||||
|
|
||||||
- Cyberghost
|
|
||||||
|
|
||||||
| Variable | Default | Choices | Description |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| 🏁 `USER` | | | Your username |
|
|
||||||
| 🏁 `PASSWORD` | | | Your password |
|
|
||||||
| 🏁 `CLIENT_KEY` | | | Your device client key content, **see below** |
|
|
||||||
| `REGION` | `Austria` | One of the [Cyberghost countries](https://github.com/qdm12/private-internet-access-docker/wiki/Cyberghost#regions) | VPN server country |
|
|
||||||
| `CYBERGHOST_GROUP` | `Premium UDP Europe` | One of the [server groups](https://github.com/qdm12/private-internet-access-docker/wiki/Cyberghost#server-groups) | Server group |
|
|
||||||
|
|
||||||
To specify your client key, you can either:
|
|
||||||
|
|
||||||
- Bind mount it at `/files/client.key`, for example with `-v /yourpath/client.key:/files/client.key:ro`
|
|
||||||
- Convert it to a single line value using:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
docker run -it --rm -v /yourpath/client.key:/files/client.key:ro qmcgaw/private-internet-access clientkey
|
|
||||||
```
|
|
||||||
|
|
||||||
And use the line produced as the value for the environment variable `CLIENT_KEY`.
|
|
||||||
|
|
||||||
- NordVPN
|
|
||||||
|
|
||||||
| Variable | Default | Choices | Description |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| 🏁 `USER` | | | Your username |
|
|
||||||
| 🏁 `PASSWORD` | | | Your password |
|
|
||||||
| `REGION` | `Austria` | One of the [VyprVPN regions](https://www.vyprvpn.com/server-locations) | VPN server region |
|
|
||||||
| `SERVER_NUMBER` | | Server integer number | Optional server number. For example `251` for `Italy #251` |
|
|
||||||
|
|
||||||
- NordVPN
|
|
||||||
|
|
||||||
| Variable | Default | Choices | Description |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| 🏁 `USER` | | | Your username |
|
|
||||||
| 🏁 `PASSWORD` | | | Your password |
|
|
||||||
| 🏁 `REGION` | `Austria` (wrong) | One of the NordVPN server name, i.e. `Cyprus #12` | VPN server name |
|
|
||||||
|
|
||||||
### DNS over TLS
|
|
||||||
|
|
||||||
None of the following values are required.
|
|
||||||
|
|
||||||
| Variable | Default | Choices | Description |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `DOT` | `on` | `on`, `off` | Activate DNS over TLS with Unbound |
|
|
||||||
| `DOT_PROVIDERS` | `cloudflare` | `cloudflare`, `google`, `quad9`, `quadrant`, `cleanbrowsing`, `securedns`, `libredns` | Comma delimited list of DNS over TLS providers |
|
|
||||||
| `DOT_CACHING` | `on` | `on`, `off` | Unbound caching |
|
|
||||||
| `DOT_IPV6` | `off` | `on`, `off` | DNS IPv6 resolution |
|
|
||||||
| `DOT_PRIVATE_ADDRESS` | All private CIDRs ranges | | Comma separated list of CIDRs or single IP addresses Unbound won't resolve to. Note that the default setting prevents DNS rebinding |
|
|
||||||
| `DOT_VERBOSITY` | `1` | `0` to `5` | Unbound verbosity level |
|
|
||||||
| `DOT_VERBOSITY_DETAILS` | `0` | `0` to `4` | Unbound details verbosity level |
|
|
||||||
| `DOT_VALIDATION_LOGLEVEL` | `0` | `0` to `2` | Unbound validation log level |
|
|
||||||
| `DNS_UPDATE_PERIOD` | `24h` | i.e. `0`, `30s`, `5m`, `24h` | Period to update block lists and cryptographic files and restart Unbound. Set to `0` to deactivate updates |
|
|
||||||
| `BLOCK_MALICIOUS` | `on` | `on`, `off` | Block malicious hostnames and IPs with Unbound |
|
|
||||||
| `BLOCK_SURVEILLANCE` | `off` | `on`, `off` | Block surveillance hostnames and IPs with Unbound |
|
|
||||||
| `BLOCK_ADS` | `off` | `on`, `off` | Block ads hostnames and IPs with Unbound |
|
|
||||||
| `UNBLOCK` | |i.e. `domain1.com,x.domain2.co.uk` | Comma separated list of domain names to leave unblocked with Unbound |
|
|
||||||
| `DNS_PLAINTEXT_ADDRESS` | `1.1.1.1` | Any IP address | IP address to use as DNS resolver if `DOT` is `off` |
|
|
||||||
| `DNS_KEEP_NAMESERVER` | `off` | `on` or `off` | Keep the nameservers in /etc/resolv.conf untouched, but disabled DNS blocking features |
|
|
||||||
|
|
||||||
### Firewall
|
|
||||||
|
|
||||||
That one is important if you want to connect to the container from your LAN for example, using Shadowsocks or Tinyproxy.
|
|
||||||
|
|
||||||
| Variable | Default | Choices | Description |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `FIREWALL` | `on` | `on` or `off` | Turn on or off the container built-in firewall. You should use it for **debugging purposes** only. |
|
|
||||||
| `EXTRA_SUBNETS` | | i.e. `192.168.1.0/24,192.168.10.121,10.0.0.5/28` | Comma separated subnets allowed in the container firewall |
|
|
||||||
| `FIREWALL_DEBUG` | `off` | `on` or `off` | Prints every firewall related command. You should use it for **debugging purposes** only. |
|
|
||||||
|
|
||||||
### Shadowsocks
|
|
||||||
|
|
||||||
| Variable | Default | Choices | Description |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `SHADOWSOCKS` | `off` | `on`, `off` | Enable the internal SOCKS5 proxy Shadowsocks |
|
|
||||||
| `SHADOWSOCKS_LOG` | `off` | `on`, `off` | Enable logging |
|
|
||||||
| `SHADOWSOCKS_PORT` | `8388` | `1024` to `65535` | Internal port number for Shadowsocks to listen on |
|
|
||||||
| `SHADOWSOCKS_PASSWORD` | | | Password to use to connect to Shadowsocks |
|
|
||||||
| `SHADOWSOCKS_METHOD` | `chacha20-ietf-poly1305` | One of [these ciphers](https://shadowsocks.org/en/config/quick-guide.html) | Method to use for Shadowsocks |
|
|
||||||
|
|
||||||
### Tinyproxy
|
|
||||||
|
|
||||||
| Variable | Default | Choices | Description |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `TINYPROXY` | `off` | `on`, `off` | Enable the internal HTTP proxy tinyproxy |
|
|
||||||
| `TINYPROXY_LOG` | `Info` | `Info`, `Connect`, `Notice`, `Warning`, `Error`, `Critical` | Tinyproxy log level |
|
|
||||||
| `TINYPROXY_PORT` | `8888` | `1024` to `65535` | Internal port number for Tinyproxy to listen on |
|
|
||||||
| `TINYPROXY_USER` | | | Username to use to connect to Tinyproxy |
|
|
||||||
| `TINYPROXY_PASSWORD` | | | Password to use to connect to Tinyproxy |
|
|
||||||
|
|
||||||
### System
|
|
||||||
|
|
||||||
| Variable | Default | Choices | Description |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `TZ` | | i.e. `Europe/London` | Specify a timezone to use to have correct log times |
|
|
||||||
| `UID` | `1000` | | User ID to run as non root and for ownership of files written |
|
|
||||||
| `GID` | `1000` | | Group ID to run as non root and for ownership of files written |
|
|
||||||
|
|
||||||
## Connect to it
|
|
||||||
|
|
||||||
There are various ways to achieve this, depending on your use case.
|
|
||||||
|
|
||||||
- <details><summary>Connect containers in the same docker-compose.yml as Gluetun</summary><p>
|
|
||||||
|
|
||||||
Add `network_mode: "service:gluetun"` to your *docker-compose.yml* (no need for `depends_on`)
|
|
||||||
|
|
||||||
</p></details>
|
|
||||||
- <details><summary>Connect other containers to Gluetun</summary><p>
|
|
||||||
|
|
||||||
Add `--network=container:gluetun` when launching the container, provided Gluetun is already running
|
|
||||||
|
|
||||||
</p></details>
|
|
||||||
- <details><summary>Connect containers from another docker-compose.yml</summary><p>
|
|
||||||
|
|
||||||
Add `network_mode: "container:gluetun"` to your *docker-compose.yml*, provided Gluetun is already running
|
|
||||||
|
|
||||||
</p></details>
|
|
||||||
- <details><summary>Connect LAN devices through the built-in HTTP proxy *Tinyproxy* (i.e. with Chrome, Kodi, etc.)</summary><p>
|
|
||||||
|
|
||||||
You might want to use Shadowsocks instead which tunnels UDP as well as TCP, whereas Tinyproxy only tunnels TCP.
|
|
||||||
|
|
||||||
1. Setup a HTTP proxy client, such as [SwitchyOmega for Chrome](https://chrome.google.com/webstore/detail/proxy-switchyomega/padekgcemlokbadohgkifijomclgjgif?hl=en)
|
|
||||||
1. Ensure the Gluetun container is launched with:
|
|
||||||
- port `8888` published `-p 8888:8888/tcp`
|
|
||||||
- your LAN subnet, i.e. `192.168.1.0/24`, set as `-e EXTRA_SUBNETS=192.168.1.0/24`
|
|
||||||
1. With your HTTP proxy client, connect to the Docker host (i.e. `192.168.1.10`) on port `8888`. You need to enter your credentials if you set them with `TINYPROXY_USER` and `TINYPROXY_PASSWORD`.
|
|
||||||
1. If you set `TINYPROXY_LOG` to `Info`, more information will be logged in the Docker logs
|
|
||||||
|
|
||||||
</p></details>
|
|
||||||
- <details><summary>Connect LAN devices through the built-in SOCKS5 proxy *Shadowsocks* (per app, system wide, etc.)</summary><p>
|
|
||||||
|
|
||||||
1. Setup a SOCKS5 proxy client, there is a list of [ShadowSocks clients for **all platforms**](https://shadowsocks.org/en/download/clients.html)
|
|
||||||
- **note** some clients do not tunnel UDP so your DNS queries will be done locally and not through Gluetun and its built in DNS over TLS
|
|
||||||
- Clients that support such UDP tunneling are, as far as I know:
|
|
||||||
- iOS: Potatso Lite
|
|
||||||
- OSX: ShadowsocksX
|
|
||||||
- Android: Shadowsocks by Max Lv
|
|
||||||
1. Ensure the Gluetun container is launched with:
|
|
||||||
- port `8388` published `-p 8388:8388/tcp -p 8388:8388/udp`
|
|
||||||
- your LAN subnet, i.e. `192.168.1.0/24`, set as `-e EXTRA_SUBNETS=192.168.1.0/24`
|
|
||||||
1. With your SOCKS5 proxy client
|
|
||||||
- Enter the Docker host (i.e. `192.168.1.10`) as the server IP
|
|
||||||
- Enter port TCP (and UDP, if available) `8388` as the server port
|
|
||||||
- Use the password you have set with `SHADOWSOCKS_PASSWORD`
|
|
||||||
- Choose the encryption method/algorithm to the method you specified in `SHADOWSOCKS_METHOD`
|
|
||||||
1. If you set `SHADOWSOCKS_LOG` to `on`, (a lot) more information will be logged in the Docker logs
|
|
||||||
|
|
||||||
</p></details>
|
|
||||||
- <details><summary>Access ports of containers connected to Gluetun</summary><p>
|
|
||||||
|
|
||||||
In example, to access port `8000` of container `xyz` and `9000` of container `abc` connected to Gluetun,
|
|
||||||
publish ports `8000` and `9000` for the Gluetun container and access them as you would with any other container
|
|
||||||
|
|
||||||
</p></details>
|
|
||||||
- <details><summary>Access ports of containers connected to Gluetun, all in the same docker-compose.yml</summary><p>
|
|
||||||
|
|
||||||
In example, to access port `8000` of container `xyz` and `9000` of container `abc` connected to Gluetun, publish port `8000` and `9000` for the Gluetun container.
|
|
||||||
The docker-compose.yml file would look like:
|
|
||||||
|
|
||||||
```yml
|
|
||||||
version: '3.7'
|
|
||||||
services:
|
|
||||||
gluetun:
|
|
||||||
image: qmcgaw/private-internet-access
|
|
||||||
container_name: gluetun
|
|
||||||
cap_add:
|
|
||||||
- NET_ADMIN
|
|
||||||
environment:
|
|
||||||
- USER=js89ds7
|
|
||||||
- PASSWORD=8fd9s239G
|
|
||||||
ports:
|
|
||||||
- 8000:8000/tcp
|
|
||||||
- 9000:9000/tcp
|
|
||||||
abc:
|
|
||||||
image: abc
|
|
||||||
container_name: abc
|
|
||||||
network_mode: "service:gluetun"
|
|
||||||
xyz:
|
|
||||||
image: xyz
|
|
||||||
container_name: xyz
|
|
||||||
network_mode: "service:gluetun"
|
|
||||||
```
|
|
||||||
|
|
||||||
</p></details>
|
|
||||||
|
|
||||||
## Private Internet Access port forwarding
|
|
||||||
|
|
||||||
Note that [not all regions support port forwarding](https://www.privateinternetaccess.com/helpdesk/kb/articles/how-do-i-enable-port-forwarding-on-my-vpn).
|
|
||||||
|
|
||||||
When `PORT_FORWARDING=on`, a port will be forwarded on the VPN server side and written to the file specified by `PORT_FORWARDING_STATUS_FILE=/forwarded_port`.
|
|
||||||
|
|
||||||
It can be useful to mount this file as a volume to read it from other containers, for example to configure a torrenting client.
|
|
||||||
|
|
||||||
## HTTP control server
|
|
||||||
|
|
||||||
A built-in HTTP server listens on port `8000` to modify the state of the container. You have the following routes available:
|
|
||||||
|
|
||||||
- `http://<your-docker-host-ip>:8000/openvpn/actions/restart` restarts the openvpn process
|
|
||||||
- `http://<your-docker-host-ip>:8000/unbound/actions/restart` re-downloads the DNS files (crypto and block lists) and restarts the unbound process
|
|
||||||
|
|
||||||
## Development and contributing
|
|
||||||
|
|
||||||
- Contribute with code: see [the Wiki](https://github.com/qdm12/private-internet-access-docker/wiki/Contributing).
|
|
||||||
- [The list of existing contributors 👍](https://github.com/qdm12/private-internet-access-docker/blob/master/.github/CONTRIBUTING.md#Contributors)
|
|
||||||
- [Github workflows](https://github.com/qdm12/private-internet-access-docker/actions) to know what's building
|
|
||||||
- [List of issues and feature requests](https://github.com/qdm12/private-internet-access-docker/issues)
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This repository is under an [MIT license](https://github.com/qdm12/private-internet-access-docker/master/license)
|
[](https://github.com/qdm12/gluetun/master/LICENSE)
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
Sponsor me on [Github](https://github.com/sponsors/qdm12), donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw) or subscribe to a VPN provider through one of my affiliate links:
|
|
||||||
|
|
||||||
[](https://github.com/sponsors/qdm12)
|
|
||||||
[](https://www.paypal.me/qmcgaw)
|
|
||||||
|
|
||||||
[](https://windscribe.com/?affid=mh7nyafu)
|
|
||||||
|
|
||||||
Feel also free to have a look at [the Kanban board](https://github.com/qdm12/private-internet-access-docker/projects/1) and [contribute](#Development-and-contributing) to the code or the issues discussion.
|
|
||||||
|
|
||||||
Many thanks to @Frepke, @Ralph521, G. Mendez, M. Otmar Weber, J. Perez and A. Cooper for supporting me financially 🥇👍
|
|
||||||
|
|||||||
@@ -2,309 +2,489 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"net"
|
||||||
|
"net/http"
|
||||||
|
nativeos "os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/golibs/command"
|
"github.com/qdm12/dns/pkg/unbound"
|
||||||
"github.com/qdm12/golibs/files"
|
"github.com/qdm12/gluetun/internal/alpine"
|
||||||
|
"github.com/qdm12/gluetun/internal/cli"
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/gluetun/internal/dns"
|
||||||
|
"github.com/qdm12/gluetun/internal/firewall"
|
||||||
|
"github.com/qdm12/gluetun/internal/healthcheck"
|
||||||
|
"github.com/qdm12/gluetun/internal/httpproxy"
|
||||||
|
gluetunLogging "github.com/qdm12/gluetun/internal/logging"
|
||||||
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
|
"github.com/qdm12/gluetun/internal/openvpn"
|
||||||
|
"github.com/qdm12/gluetun/internal/publicip"
|
||||||
|
"github.com/qdm12/gluetun/internal/routing"
|
||||||
|
"github.com/qdm12/gluetun/internal/server"
|
||||||
|
"github.com/qdm12/gluetun/internal/shadowsocks"
|
||||||
|
"github.com/qdm12/gluetun/internal/storage"
|
||||||
|
"github.com/qdm12/gluetun/internal/unix"
|
||||||
|
"github.com/qdm12/gluetun/internal/updater"
|
||||||
|
versionpkg "github.com/qdm12/gluetun/internal/version"
|
||||||
"github.com/qdm12/golibs/logging"
|
"github.com/qdm12/golibs/logging"
|
||||||
"github.com/qdm12/golibs/network"
|
"github.com/qdm12/golibs/os"
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/alpine"
|
"github.com/qdm12/golibs/os/user"
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/cli"
|
"github.com/qdm12/golibs/params"
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/dns"
|
"github.com/qdm12/goshutdown"
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/firewall"
|
"github.com/qdm12/updated/pkg/dnscrypto"
|
||||||
gluetunLogging "github.com/qdm12/private-internet-access-docker/internal/logging"
|
)
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/openvpn"
|
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/params"
|
//nolint:gochecknoglobals
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/publicip"
|
var (
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/routing"
|
version = "unknown"
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/server"
|
commit = "unknown"
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/settings"
|
buildDate = "an unknown date"
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/shadowsocks"
|
)
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/tinyproxy"
|
|
||||||
|
var (
|
||||||
|
errSetupRouting = errors.New("cannot setup routing")
|
||||||
|
errCreateUser = errors.New("cannot create user")
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
buildInfo := models.BuildInformation{
|
||||||
|
Version: version,
|
||||||
|
Commit: commit,
|
||||||
|
BuildDate: buildDate,
|
||||||
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
os.Exit(_main(ctx, os.Args))
|
ctx, stop := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM, nativeos.Interrupt)
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
|
logger := logging.NewParent(logging.Settings{})
|
||||||
|
|
||||||
|
args := nativeos.Args
|
||||||
|
os := os.New()
|
||||||
|
osUser := user.New()
|
||||||
|
unix := unix.New()
|
||||||
|
cli := cli.New()
|
||||||
|
|
||||||
|
errorCh := make(chan error)
|
||||||
|
go func() {
|
||||||
|
errorCh <- _main(ctx, buildInfo, args, logger, os, osUser, unix, cli)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
stop()
|
||||||
|
logger.Warn("Caught OS signal, shutting down")
|
||||||
|
case err := <-errorCh:
|
||||||
|
stop()
|
||||||
|
close(errorCh)
|
||||||
|
if err == nil { // expected exit such as healthcheck
|
||||||
|
nativeos.Exit(0)
|
||||||
|
}
|
||||||
|
logger.Error(err)
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
const shutdownGracePeriod = 5 * time.Second
|
||||||
|
timer := time.NewTimer(shutdownGracePeriod)
|
||||||
|
select {
|
||||||
|
case <-errorCh:
|
||||||
|
if !timer.Stop() {
|
||||||
|
<-timer.C
|
||||||
|
}
|
||||||
|
logger.Info("Shutdown successful")
|
||||||
|
case <-timer.C:
|
||||||
|
logger.Warn("Shutdown timed out")
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeos.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _main(background context.Context, args []string) int {
|
var (
|
||||||
|
errCommandUnknown = errors.New("command is unknown")
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:gocognit,gocyclo
|
||||||
|
func _main(ctx context.Context, buildInfo models.BuildInformation,
|
||||||
|
args []string, logger logging.ParentLogger, os os.OS,
|
||||||
|
osUser user.OSUser, unix unix.Unix, cli cli.CLI) error {
|
||||||
if len(args) > 1 { // cli operation
|
if len(args) > 1 { // cli operation
|
||||||
var err error
|
|
||||||
switch args[1] {
|
switch args[1] {
|
||||||
case "healthcheck":
|
case "healthcheck":
|
||||||
err = cli.HealthCheck()
|
return cli.HealthCheck(ctx)
|
||||||
case "clientkey":
|
case "clientkey":
|
||||||
err = cli.ClientKey(args[2:])
|
return cli.ClientKey(args[2:], os.OpenFile)
|
||||||
case "openvpnconfig":
|
case "openvpnconfig":
|
||||||
err = cli.OpenvpnConfig()
|
return cli.OpenvpnConfig(os, logger)
|
||||||
|
case "update":
|
||||||
|
return cli.Update(ctx, args[2:], os, logger)
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("command %q is unknown", args[1])
|
return fmt.Errorf("%w: %s", errCommandUnknown, args[1])
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
return 0
|
|
||||||
}
|
|
||||||
ctx, cancel := context.WithCancel(background)
|
|
||||||
defer cancel()
|
|
||||||
logger := createLogger()
|
|
||||||
wg := &sync.WaitGroup{}
|
|
||||||
fatalOnError := makeFatalOnError(logger, cancel, wg)
|
|
||||||
|
|
||||||
client := network.NewClient(15 * time.Second)
|
const clientTimeout = 15 * time.Second
|
||||||
|
httpClient := &http.Client{Timeout: clientTimeout}
|
||||||
// Create configurators
|
// Create configurators
|
||||||
fileManager := files.NewFileManager()
|
alpineConf := alpine.NewConfigurator(os.OpenFile, osUser)
|
||||||
alpineConf := alpine.NewConfigurator(fileManager)
|
ovpnConf := openvpn.NewConfigurator(
|
||||||
ovpnConf := openvpn.NewConfigurator(logger, fileManager)
|
logger.NewChild(logging.Settings{Prefix: "openvpn configurator: "}),
|
||||||
dnsConf := dns.NewConfigurator(logger, client, fileManager)
|
os, unix)
|
||||||
routingConf := routing.NewRouting(logger, fileManager)
|
dnsCrypto := dnscrypto.New(httpClient, "", "")
|
||||||
firewallConf := firewall.NewConfigurator(logger, routingConf, fileManager)
|
const cacertsPath = "/etc/ssl/certs/ca-certificates.crt"
|
||||||
tinyProxyConf := tinyproxy.NewConfigurator(fileManager, logger)
|
dnsConf := unbound.NewConfigurator(nil, os.OpenFile, dnsCrypto,
|
||||||
shadowsocksConf := shadowsocks.NewConfigurator(fileManager, logger)
|
"/etc/unbound", "/usr/sbin/unbound", cacertsPath)
|
||||||
streamMerger := command.NewStreamMerger()
|
routingConf := routing.NewRouting(
|
||||||
|
logger.NewChild(logging.Settings{Prefix: "routing: "}))
|
||||||
|
firewallConf := firewall.NewConfigurator(
|
||||||
|
logger.NewChild(logging.Settings{Prefix: "firewall: "}),
|
||||||
|
routingConf, os.OpenFile)
|
||||||
|
|
||||||
paramsReader := params.NewReader(logger, fileManager)
|
fmt.Println(gluetunLogging.Splash(buildInfo))
|
||||||
fmt.Println(gluetunLogging.Splash(
|
|
||||||
paramsReader.GetVersion(),
|
|
||||||
paramsReader.GetVcsRef(),
|
|
||||||
paramsReader.GetBuildDate()))
|
|
||||||
|
|
||||||
printVersions(ctx, logger, map[string]func(ctx context.Context) (string, error){
|
if err := printVersions(ctx, logger, []printVersionElement{
|
||||||
"OpenVPN": ovpnConf.Version,
|
{name: "Alpine", getVersion: alpineConf.Version},
|
||||||
"Unbound": dnsConf.Version,
|
{name: "OpenVPN 2.4", getVersion: ovpnConf.Version24},
|
||||||
"IPtables": firewallConf.Version,
|
{name: "OpenVPN 2.5", getVersion: ovpnConf.Version25},
|
||||||
"TinyProxy": tinyProxyConf.Version,
|
{name: "Unbound", getVersion: dnsConf.Version},
|
||||||
"ShadowSocks": shadowsocksConf.Version,
|
{name: "IPtables", getVersion: firewallConf.Version},
|
||||||
})
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
allSettings, err := settings.GetAllSettings(paramsReader)
|
var allSettings configuration.Settings
|
||||||
fatalOnError(err)
|
err := allSettings.Read(params.NewEnv(), os,
|
||||||
|
logger.NewChild(logging.Settings{Prefix: "configuration: "}))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
logger.Info(allSettings.String())
|
logger.Info(allSettings.String())
|
||||||
|
|
||||||
// Should never change
|
if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil {
|
||||||
uid, gid := allSettings.System.UID, allSettings.System.GID
|
return err
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll("/gluetun", 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
err = alpineConf.CreateUser("nonrootuser", uid)
|
// TODO run this in a loop or in openvpn to reload from file without restarting
|
||||||
fatalOnError(err)
|
storage := storage.New(
|
||||||
err = fileManager.SetOwnership("/etc/unbound", uid, gid)
|
logger.NewChild(logging.Settings{Prefix: "storage: "}),
|
||||||
fatalOnError(err)
|
os, constants.ServersData)
|
||||||
err = fileManager.SetOwnership("/etc/tinyproxy", uid, gid)
|
allServers, err := storage.SyncServers(constants.GetAllServers())
|
||||||
fatalOnError(err)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should never change
|
||||||
|
puid, pgid := allSettings.System.PUID, allSettings.System.PGID
|
||||||
|
|
||||||
|
const defaultUsername = "nonrootuser"
|
||||||
|
nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", errCreateUser, err)
|
||||||
|
}
|
||||||
|
if nonRootUsername != defaultUsername {
|
||||||
|
logger.Info("using existing username %s corresponding to user id %d", nonRootUsername, puid)
|
||||||
|
}
|
||||||
|
// set it for Unbound
|
||||||
|
// TODO remove this when migrating to qdm12/dns v2
|
||||||
|
allSettings.DNS.Unbound.Username = nonRootUsername
|
||||||
|
|
||||||
|
if err := os.Chown("/etc/unbound", puid, pgid); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if allSettings.Firewall.Debug {
|
if allSettings.Firewall.Debug {
|
||||||
firewallConf.SetDebug()
|
firewallConf.SetDebug()
|
||||||
routingConf.SetDebug()
|
routingConf.SetDebug()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultInterface, defaultGateway, err := routingConf.DefaultRoute()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
localNetworks, err := routingConf.LocalNetworks()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultIP, err := routingConf.DefaultIP()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
firewallConf.SetNetworkInformation(defaultInterface, defaultGateway, localNetworks, defaultIP)
|
||||||
|
|
||||||
|
if err := routingConf.Setup(); err != nil {
|
||||||
|
if strings.Contains(err.Error(), "operation not permitted") {
|
||||||
|
logger.Warn("💡 Tip: Are you passing NET_ADMIN capability to gluetun?")
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%w: %s", errSetupRouting, err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
routingConf.SetVerbose(false)
|
||||||
|
if err := routingConf.TearDown(); err != nil {
|
||||||
|
logger.Error("cannot teardown routing: " + err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := firewallConf.SetOutboundSubnets(ctx, allSettings.Firewall.OutboundSubnets); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := routingConf.SetOutboundRoutes(allSettings.Firewall.OutboundSubnets); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := ovpnConf.CheckTUN(); err != nil {
|
if err := ovpnConf.CheckTUN(); err != nil {
|
||||||
logger.Warn(err)
|
logger.Warn(err)
|
||||||
err = ovpnConf.CreateTUN()
|
err = ovpnConf.CreateTUN()
|
||||||
fatalOnError(err)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCh := make(chan struct{})
|
tunnelReadyCh := make(chan struct{})
|
||||||
signalConnected := func() {
|
defer close(tunnelReadyCh)
|
||||||
connectedCh <- struct{}{}
|
|
||||||
}
|
|
||||||
defer close(connectedCh)
|
|
||||||
go collectStreamLines(ctx, streamMerger, logger, signalConnected)
|
|
||||||
|
|
||||||
if allSettings.Firewall.Enabled {
|
if allSettings.Firewall.Enabled {
|
||||||
err := firewallConf.SetEnabled(ctx, true) // disabled by default
|
err := firewallConf.SetEnabled(ctx, true) // disabled by default
|
||||||
fatalOnError(err)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = firewallConf.SetAllowedSubnets(ctx, allSettings.Firewall.AllowedSubnets)
|
for _, vpnPort := range allSettings.Firewall.VPNInputPorts {
|
||||||
fatalOnError(err)
|
err = firewallConf.SetAllowedPort(ctx, vpnPort, string(constants.TUN))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
openvpnLooper := openvpn.NewLooper(allSettings.VPNSP, allSettings.OpenVPN, uid, gid,
|
for _, port := range allSettings.Firewall.InputPorts {
|
||||||
ovpnConf, firewallConf, logger, client, fileManager, streamMerger, fatalOnError)
|
err = firewallConf.SetAllowedPort(ctx, port, defaultInterface)
|
||||||
restartOpenvpn := openvpnLooper.Restart
|
if err != nil {
|
||||||
portForward := openvpnLooper.PortForward
|
return err
|
||||||
|
}
|
||||||
|
} // TODO move inside firewall?
|
||||||
|
|
||||||
|
// Shutdown settings
|
||||||
|
const defaultShutdownTimeout = 400 * time.Millisecond
|
||||||
|
defaultShutdownOnSuccess := func(goRoutineName string) {
|
||||||
|
logger.Info(goRoutineName + ": terminated ✔️")
|
||||||
|
}
|
||||||
|
defaultShutdownOnFailure := func(goRoutineName string, err error) {
|
||||||
|
logger.Warn(goRoutineName + ": " + err.Error() + " ⚠️")
|
||||||
|
}
|
||||||
|
defaultGoRoutineSettings := goshutdown.GoRoutineSettings{Timeout: defaultShutdownTimeout}
|
||||||
|
defaultGroupSettings := goshutdown.GroupSettings{
|
||||||
|
Timeout: defaultShutdownTimeout,
|
||||||
|
OnSuccess: defaultShutdownOnSuccess,
|
||||||
|
}
|
||||||
|
|
||||||
|
controlGroupHandler := goshutdown.NewGroupHandler("control", defaultGroupSettings)
|
||||||
|
tickersGroupHandler := goshutdown.NewGroupHandler("tickers", defaultGroupSettings)
|
||||||
|
otherGroupHandler := goshutdown.NewGroupHandler("other", defaultGroupSettings)
|
||||||
|
|
||||||
|
openvpnLooper := openvpn.NewLooper(allSettings.OpenVPN, nonRootUsername, puid, pgid, allServers,
|
||||||
|
ovpnConf, firewallConf, routingConf, logger, httpClient, os.OpenFile, tunnelReadyCh)
|
||||||
|
openvpnHandler, openvpnCtx, openvpnDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"openvpn", goshutdown.GoRoutineSettings{Timeout: time.Second})
|
||||||
// wait for restartOpenvpn
|
// wait for restartOpenvpn
|
||||||
go openvpnLooper.Run(ctx, wg)
|
go openvpnLooper.Run(openvpnCtx, openvpnDone)
|
||||||
|
|
||||||
unboundLooper := dns.NewLooper(dnsConf, allSettings.DNS, logger, streamMerger, uid, gid)
|
updaterLooper := updater.NewLooper(allSettings.Updater,
|
||||||
restartUnbound := unboundLooper.Restart
|
allServers, storage, openvpnLooper.SetServers, httpClient,
|
||||||
// wait for restartUnbound
|
logger.NewChild(logging.Settings{Prefix: "updater: "}))
|
||||||
go unboundLooper.Run(ctx, wg)
|
updaterHandler, updaterCtx, updaterDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"updater", defaultGoRoutineSettings)
|
||||||
|
// wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker
|
||||||
|
go updaterLooper.Run(updaterCtx, updaterDone)
|
||||||
|
tickersGroupHandler.Add(updaterHandler)
|
||||||
|
|
||||||
publicIPLooper := publicip.NewLooper(client, logger, fileManager, allSettings.System.IPStatusFilepath, uid, gid)
|
unboundLogger := logger.NewChild(logging.Settings{Prefix: "dns over tls: "})
|
||||||
restartPublicIP := publicIPLooper.Restart
|
unboundLooper := dns.NewLooper(dnsConf, allSettings.DNS, httpClient,
|
||||||
go publicIPLooper.Run(ctx)
|
unboundLogger, os.OpenFile)
|
||||||
go publicIPLooper.RunRestartTicker(ctx)
|
dnsHandler, dnsCtx, dnsDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"unbound", defaultGoRoutineSettings)
|
||||||
|
// wait for unboundLooper.Restart or its ticker launched with RunRestartTicker
|
||||||
|
go unboundLooper.Run(dnsCtx, dnsDone)
|
||||||
|
otherGroupHandler.Add(dnsHandler)
|
||||||
|
|
||||||
tinyproxyLooper := tinyproxy.NewLooper(tinyProxyConf, firewallConf, allSettings.TinyProxy, logger, streamMerger, uid, gid)
|
publicIPLooper := publicip.NewLooper(httpClient,
|
||||||
restartTinyproxy := tinyproxyLooper.Restart
|
logger.NewChild(logging.Settings{Prefix: "ip getter: "}),
|
||||||
go tinyproxyLooper.Run(ctx, wg)
|
allSettings.PublicIP, puid, pgid, os)
|
||||||
|
pubIPHandler, pubIPCtx, pubIPDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"public IP", defaultGoRoutineSettings)
|
||||||
|
go publicIPLooper.Run(pubIPCtx, pubIPDone)
|
||||||
|
otherGroupHandler.Add(pubIPHandler)
|
||||||
|
|
||||||
shadowsocksLooper := shadowsocks.NewLooper(shadowsocksConf, firewallConf, allSettings.ShadowSocks, allSettings.DNS, logger, streamMerger, uid, gid)
|
pubIPTickerHandler, pubIPTickerCtx, pubIPTickerDone := goshutdown.NewGoRoutineHandler(
|
||||||
restartShadowsocks := shadowsocksLooper.Restart
|
"public IP", defaultGoRoutineSettings)
|
||||||
go shadowsocksLooper.Run(ctx, wg)
|
go publicIPLooper.RunRestartTicker(pubIPTickerCtx, pubIPTickerDone)
|
||||||
|
tickersGroupHandler.Add(pubIPTickerHandler)
|
||||||
|
|
||||||
if allSettings.TinyProxy.Enabled {
|
httpProxyLooper := httpproxy.NewLooper(
|
||||||
restartTinyproxy()
|
logger.NewChild(logging.Settings{Prefix: "http proxy: "}),
|
||||||
|
allSettings.HTTPProxy)
|
||||||
|
httpProxyHandler, httpProxyCtx, httpProxyDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"http proxy", defaultGoRoutineSettings)
|
||||||
|
go httpProxyLooper.Run(httpProxyCtx, httpProxyDone)
|
||||||
|
otherGroupHandler.Add(httpProxyHandler)
|
||||||
|
|
||||||
|
shadowsocksLooper := shadowsocks.NewLooper(allSettings.ShadowSocks,
|
||||||
|
logger.NewChild(logging.Settings{Prefix: "shadowsocks: "}))
|
||||||
|
shadowsocksHandler, shadowsocksCtx, shadowsocksDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"shadowsocks proxy", defaultGoRoutineSettings)
|
||||||
|
go shadowsocksLooper.Run(shadowsocksCtx, shadowsocksDone)
|
||||||
|
otherGroupHandler.Add(shadowsocksHandler)
|
||||||
|
|
||||||
|
eventsRoutingHandler, eventsRoutingCtx, eventsRoutingDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"events routing", defaultGoRoutineSettings)
|
||||||
|
go routeReadyEvents(eventsRoutingCtx, eventsRoutingDone, buildInfo, tunnelReadyCh,
|
||||||
|
unboundLooper, updaterLooper, publicIPLooper, routingConf, logger, httpClient,
|
||||||
|
allSettings.VersionInformation, allSettings.OpenVPN.Provider.PortForwarding.Enabled, openvpnLooper.PortForward,
|
||||||
|
)
|
||||||
|
controlGroupHandler.Add(eventsRoutingHandler)
|
||||||
|
|
||||||
|
controlServerAddress := ":" + strconv.Itoa(int(allSettings.ControlServer.Port))
|
||||||
|
controlServerLogging := allSettings.ControlServer.Log
|
||||||
|
httpServerHandler, httpServerCtx, httpServerDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"http server", defaultGoRoutineSettings)
|
||||||
|
httpServer := server.New(httpServerCtx, controlServerAddress, controlServerLogging,
|
||||||
|
logger.NewChild(logging.Settings{Prefix: "http server: "}),
|
||||||
|
buildInfo, openvpnLooper, unboundLooper, updaterLooper, publicIPLooper)
|
||||||
|
go httpServer.Run(httpServerCtx, httpServerDone)
|
||||||
|
controlGroupHandler.Add(httpServerHandler)
|
||||||
|
|
||||||
|
healthLogger := logger.NewChild(logging.Settings{Prefix: "healthcheck: "})
|
||||||
|
healthcheckServer := healthcheck.NewServer(
|
||||||
|
constants.HealthcheckAddress, healthLogger, openvpnLooper)
|
||||||
|
healthServerHandler, healthServerCtx, healthServerDone := goshutdown.NewGoRoutineHandler(
|
||||||
|
"HTTP health server", defaultGoRoutineSettings)
|
||||||
|
go healthcheckServer.Run(healthServerCtx, healthServerDone)
|
||||||
|
|
||||||
|
const orderShutdownTimeout = 3 * time.Second
|
||||||
|
orderSettings := goshutdown.OrderSettings{
|
||||||
|
Timeout: orderShutdownTimeout,
|
||||||
|
OnFailure: defaultShutdownOnFailure,
|
||||||
|
OnSuccess: defaultShutdownOnSuccess,
|
||||||
|
}
|
||||||
|
orderHandler := goshutdown.NewOrder("gluetun", orderSettings)
|
||||||
|
orderHandler.Append(controlGroupHandler, tickersGroupHandler, healthServerHandler,
|
||||||
|
openvpnHandler, otherGroupHandler)
|
||||||
|
|
||||||
|
// Start openvpn for the first time in a blocking call
|
||||||
|
// until openvpn is launched
|
||||||
|
_, _ = openvpnLooper.ApplyStatus(ctx, constants.Running) // TODO option to disable with variable
|
||||||
|
|
||||||
|
<-ctx.Done()
|
||||||
|
|
||||||
|
if allSettings.OpenVPN.Provider.PortForwarding.Enabled {
|
||||||
|
logger.Info("Clearing forwarded port status file %s", allSettings.OpenVPN.Provider.PortForwarding.Filepath)
|
||||||
|
if err := os.Remove(allSettings.OpenVPN.Provider.PortForwarding.Filepath); err != nil {
|
||||||
|
logger.Error(err)
|
||||||
}
|
}
|
||||||
if allSettings.ShadowSocks.Enabled {
|
|
||||||
restartShadowsocks()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
return orderHandler.Shutdown(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
type printVersionElement struct {
|
||||||
|
name string
|
||||||
|
getVersion func(ctx context.Context) (version string, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printVersions(ctx context.Context, logger logging.Logger,
|
||||||
|
elements []printVersionElement) (err error) {
|
||||||
|
const timeout = 5 * time.Second
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
for _, element := range elements {
|
||||||
|
version, err := element.getVersion(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Info(element.name + " version: " + version)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func routeReadyEvents(ctx context.Context, done chan<- struct{}, buildInfo models.BuildInformation,
|
||||||
|
tunnelReadyCh <-chan struct{},
|
||||||
|
unboundLooper dns.Looper, updaterLooper updater.Looper, publicIPLooper publicip.Looper,
|
||||||
|
routing routing.Routing, logger logging.Logger, httpClient *http.Client,
|
||||||
|
versionInformation, portForwardingEnabled bool, startPortForward func(vpnGateway net.IP)) {
|
||||||
|
defer close(done)
|
||||||
|
|
||||||
|
// for linters only
|
||||||
var restartTickerContext context.Context
|
var restartTickerContext context.Context
|
||||||
var restartTickerCancel context.CancelFunc = func() {}
|
var restartTickerCancel context.CancelFunc = func() {}
|
||||||
|
|
||||||
|
unboundTickerDone := make(chan struct{})
|
||||||
|
close(unboundTickerDone)
|
||||||
|
updaterTickerDone := make(chan struct{})
|
||||||
|
close(updaterTickerDone)
|
||||||
|
|
||||||
|
first := true
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
restartTickerCancel()
|
restartTickerCancel() // for linters only
|
||||||
|
<-unboundTickerDone
|
||||||
|
<-updaterTickerDone
|
||||||
return
|
return
|
||||||
case <-connectedCh: // blocks until openvpn is connected
|
case <-tunnelReadyCh: // blocks until openvpn is connected
|
||||||
restartTickerCancel()
|
vpnDestination, err := routing.VPNDestinationIP()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn(err)
|
||||||
|
} else {
|
||||||
|
logger.Info("VPN routing IP address: %s", vpnDestination)
|
||||||
|
}
|
||||||
|
|
||||||
|
if unboundLooper.GetSettings().Enabled {
|
||||||
|
_, _ = unboundLooper.ApplyStatus(ctx, constants.Running)
|
||||||
|
}
|
||||||
|
|
||||||
|
restartTickerCancel() // stop previous restart tickers
|
||||||
|
<-unboundTickerDone
|
||||||
|
<-updaterTickerDone
|
||||||
restartTickerContext, restartTickerCancel = context.WithCancel(ctx)
|
restartTickerContext, restartTickerCancel = context.WithCancel(ctx)
|
||||||
go unboundLooper.RunRestartTicker(restartTickerContext)
|
|
||||||
onConnected(allSettings, logger, routingConf, portForward, restartUnbound, restartPublicIP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
httpServer := server.New("0.0.0.0:8000", logger, restartOpenvpn, restartUnbound)
|
// Runs the Public IP getter job once
|
||||||
go httpServer.Run(ctx, wg)
|
_, _ = publicIPLooper.SetStatus(ctx, constants.Running)
|
||||||
|
if versionInformation && first {
|
||||||
// Start openvpn for the first time
|
first = false
|
||||||
restartOpenvpn()
|
message, err := versionpkg.GetMessage(ctx, buildInfo, httpClient)
|
||||||
|
|
||||||
signalsCh := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(signalsCh,
|
|
||||||
syscall.SIGINT,
|
|
||||||
syscall.SIGTERM,
|
|
||||||
os.Interrupt,
|
|
||||||
)
|
|
||||||
shutdownErrorsCount := 0
|
|
||||||
select {
|
|
||||||
case signal := <-signalsCh:
|
|
||||||
logger.Warn("Caught OS signal %s, shutting down", signal)
|
|
||||||
cancel()
|
|
||||||
case <-ctx.Done():
|
|
||||||
logger.Warn("context canceled, shutting down")
|
|
||||||
}
|
|
||||||
logger.Info("Clearing ip status file %s", allSettings.System.IPStatusFilepath)
|
|
||||||
if err := fileManager.Remove(string(allSettings.System.IPStatusFilepath)); err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
shutdownErrorsCount++
|
|
||||||
}
|
|
||||||
if allSettings.OpenVPN.Provider.PortForwarding.Enabled {
|
|
||||||
logger.Info("Clearing forwarded port status file %s", allSettings.OpenVPN.Provider.PortForwarding.Filepath)
|
|
||||||
if err := fileManager.Remove(string(allSettings.OpenVPN.Provider.PortForwarding.Filepath)); err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
shutdownErrorsCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
waiting, waited := context.WithTimeout(context.Background(), time.Second)
|
|
||||||
go func() {
|
|
||||||
defer waited()
|
|
||||||
wg.Wait()
|
|
||||||
}()
|
|
||||||
<-waiting.Done()
|
|
||||||
if waiting.Err() == context.DeadlineExceeded {
|
|
||||||
if shutdownErrorsCount > 0 {
|
|
||||||
logger.Warn("Shutdown had %d errors", shutdownErrorsCount)
|
|
||||||
}
|
|
||||||
logger.Warn("Shutdown timed out")
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if shutdownErrorsCount > 0 {
|
|
||||||
logger.Warn("Shutdown had %d errors")
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
logger.Info("Shutdown successful")
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeFatalOnError(logger logging.Logger, cancel context.CancelFunc, wg *sync.WaitGroup) func(err error) {
|
|
||||||
return func(err error) {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Error("cannot get version information: " + err.Error())
|
||||||
cancel()
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
cancel()
|
|
||||||
}()
|
|
||||||
<-ctx.Done() // either timeout or wait group completed
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createLogger() logging.Logger {
|
|
||||||
logger, err := logging.NewLogger(logging.ConsoleEncoding, logging.InfoLevel, -1)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func printVersions(ctx context.Context, logger logging.Logger, versionFunctions map[string]func(ctx context.Context) (string, error)) {
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
for name, f := range versionFunctions {
|
|
||||||
version, err := f(ctx)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
} else {
|
} else {
|
||||||
logger.Info("%s version: %s", name, version)
|
logger.Info(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func collectStreamLines(ctx context.Context, streamMerger command.StreamMerger, logger logging.Logger, signalConnected func()) {
|
unboundTickerDone = make(chan struct{})
|
||||||
// Blocking line merging paramsReader for all programs: openvpn, tinyproxy, unbound and shadowsocks
|
updaterTickerDone = make(chan struct{})
|
||||||
logger.Info("Launching standard output merger")
|
go unboundLooper.RunRestartTicker(restartTickerContext, unboundTickerDone)
|
||||||
streamMerger.CollectLines(ctx, func(line string) {
|
go updaterLooper.RunRestartTicker(restartTickerContext, updaterTickerDone)
|
||||||
line, level := gluetunLogging.PostProcessLine(line)
|
if portForwardingEnabled {
|
||||||
if line == "" {
|
// vpnGateway required only for PIA
|
||||||
return
|
vpnGateway, err := routing.VPNLocalGatewayIP()
|
||||||
}
|
|
||||||
switch level {
|
|
||||||
case logging.InfoLevel:
|
|
||||||
logger.Info(line)
|
|
||||||
case logging.WarnLevel:
|
|
||||||
logger.Warn(line)
|
|
||||||
case logging.ErrorLevel:
|
|
||||||
logger.Error(line)
|
|
||||||
}
|
|
||||||
if strings.Contains(line, "Initialization Sequence Completed") {
|
|
||||||
signalConnected()
|
|
||||||
}
|
|
||||||
}, func(err error) {
|
|
||||||
logger.Warn(err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func onConnected(allSettings settings.Settings, logger logging.Logger, routingConf routing.Routing,
|
|
||||||
portForward, restartUnbound, restartPublicIP func(),
|
|
||||||
) {
|
|
||||||
restartUnbound()
|
|
||||||
restartPublicIP()
|
|
||||||
if allSettings.OpenVPN.Provider.PortForwarding.Enabled {
|
|
||||||
time.AfterFunc(5*time.Second, portForward)
|
|
||||||
}
|
|
||||||
defaultInterface, _, err := routingConf.DefaultRoute()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warn(err)
|
logger.Error(err)
|
||||||
} else {
|
}
|
||||||
vpnGatewayIP, err := routingConf.VPNGatewayIP(defaultInterface)
|
logger.Info("VPN gateway IP address: %s", vpnGateway)
|
||||||
if err != nil {
|
startPortForward(vpnGateway)
|
||||||
logger.Warn(err)
|
}
|
||||||
} else {
|
|
||||||
logger.Info("Gateway VPN IP address: %s", vpnGatewayIP)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,365 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
ctx := context.Background()
|
|
||||||
os.Exit(_main(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func _main(ctx context.Context) int {
|
|
||||||
fmt.Println("Host finder for Cyberghost")
|
|
||||||
resolverAddress := flag.String("resolver", "1.1.1.1", "DNS Resolver IP address to use")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
resolver := newResolver(*resolverAddress)
|
|
||||||
lookupIP := newLookupIP(resolver)
|
|
||||||
|
|
||||||
const domain = "cg-dialup.net"
|
|
||||||
groups := getCyberghostGroups()
|
|
||||||
countryCodes := getCountryCodes()
|
|
||||||
type result struct {
|
|
||||||
groupName string
|
|
||||||
region string
|
|
||||||
subdomain string
|
|
||||||
exists bool
|
|
||||||
}
|
|
||||||
resultsChannel := make(chan result)
|
|
||||||
const maxGoroutines = 10
|
|
||||||
guard := make(chan struct{}, maxGoroutines)
|
|
||||||
fmt.Print("Subdomains found: ")
|
|
||||||
for groupName, groupID := range groups {
|
|
||||||
for country, countryCode := range countryCodes {
|
|
||||||
go func(groupName, groupID, country, countryCode string) {
|
|
||||||
r := result{
|
|
||||||
region: country,
|
|
||||||
groupName: groupName,
|
|
||||||
subdomain: fmt.Sprintf("%s-%s", groupID, countryCode),
|
|
||||||
}
|
|
||||||
fqdn := fmt.Sprintf("%s.%s", r.subdomain, domain)
|
|
||||||
guard <- struct{}{}
|
|
||||||
ips, err := lookupIP(ctx, fqdn)
|
|
||||||
<-guard
|
|
||||||
if err == nil && len(ips) > 0 {
|
|
||||||
r.exists = true
|
|
||||||
}
|
|
||||||
resultsChannel <- r
|
|
||||||
}(groupName, groupID, country, countryCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
results := make([]result, len(groups)*len(countryCodes))
|
|
||||||
for i := range results {
|
|
||||||
results[i] = <-resultsChannel
|
|
||||||
fmt.Printf("%s ", results[i].subdomain)
|
|
||||||
}
|
|
||||||
fmt.Print("\n\n")
|
|
||||||
sort.Slice(results, func(i, j int) bool {
|
|
||||||
return results[i].region < results[j].region
|
|
||||||
})
|
|
||||||
for _, r := range results {
|
|
||||||
if r.exists {
|
|
||||||
// Use in resolver program
|
|
||||||
fmt.Printf("{subdomain: %q, region: %q, group: %q},\n", r.subdomain, r.region, r.groupName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func newResolver(ip string) *net.Resolver {
|
|
||||||
return &net.Resolver{
|
|
||||||
PreferGo: true,
|
|
||||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
|
||||||
d := net.Dialer{}
|
|
||||||
return d.DialContext(ctx, "udp", net.JoinHostPort(ip, "53"))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type lookupIPFunc func(ctx context.Context, host string) (ips []net.IP, err error)
|
|
||||||
|
|
||||||
func newLookupIP(r *net.Resolver) lookupIPFunc {
|
|
||||||
return func(ctx context.Context, host string) (ips []net.IP, err error) {
|
|
||||||
addresses, err := r.LookupIPAddr(ctx, host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ips = make([]net.IP, len(addresses))
|
|
||||||
for i := range addresses {
|
|
||||||
ips[i] = addresses[i].IP
|
|
||||||
}
|
|
||||||
return ips, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCyberghostGroups() map[string]string {
|
|
||||||
return map[string]string{
|
|
||||||
"Premium UDP Europe": "87-1",
|
|
||||||
"Premium UDP USA": "94-1",
|
|
||||||
"Premium UDP Asia": "95-1",
|
|
||||||
"NoSpy UDP Europe": "87-8",
|
|
||||||
"Premium TCP Europe": "97-1",
|
|
||||||
"Premium TCP USA": "93-1",
|
|
||||||
"Premium TCP Asia": "96-1",
|
|
||||||
"NoSpy TCP Europe": "97-8",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCountryCodes() map[string]string {
|
|
||||||
return map[string]string{
|
|
||||||
"Afghanistan": "af",
|
|
||||||
"Aland Islands": "ax",
|
|
||||||
"Albania": "al",
|
|
||||||
"Algeria": "dz",
|
|
||||||
"American Samoa": "as",
|
|
||||||
"Andorra": "ad",
|
|
||||||
"Angola": "ao",
|
|
||||||
"Anguilla": "ai",
|
|
||||||
"Antarctica": "aq",
|
|
||||||
"Antigua and Barbuda": "ag",
|
|
||||||
"Argentina": "ar",
|
|
||||||
"Armenia": "am",
|
|
||||||
"Aruba": "aw",
|
|
||||||
"Australia": "au",
|
|
||||||
"Austria": "at",
|
|
||||||
"Azerbaijan": "az",
|
|
||||||
"Bahamas": "bs",
|
|
||||||
"Bahrain": "bh",
|
|
||||||
"Bangladesh": "bd",
|
|
||||||
"Barbados": "bb",
|
|
||||||
"Belarus": "by",
|
|
||||||
"Belgium": "be",
|
|
||||||
"Belize": "bz",
|
|
||||||
"Benin": "bj",
|
|
||||||
"Bermuda": "bm",
|
|
||||||
"Bhutan": "bt",
|
|
||||||
"Bolivia": "bo",
|
|
||||||
"Bonaire": "bq",
|
|
||||||
"Bosnia and Herzegovina": "ba",
|
|
||||||
"Botswana": "bw",
|
|
||||||
"Bouvet Island": "bv",
|
|
||||||
"Brazil": "br",
|
|
||||||
"British Indian Ocean Territory": "io",
|
|
||||||
"British Virgin Islands": "vg",
|
|
||||||
"Brunei Darussalam": "bn",
|
|
||||||
"Bulgaria": "bg",
|
|
||||||
"Burkina Faso": "bf",
|
|
||||||
"Burundi": "bi",
|
|
||||||
"Cambodia": "kh",
|
|
||||||
"Cameroon": "cm",
|
|
||||||
"Canada": "ca",
|
|
||||||
"Cape Verde": "cv",
|
|
||||||
"Cayman Islands": "ky",
|
|
||||||
"Central African Republic": "cf",
|
|
||||||
"Chad": "td",
|
|
||||||
"Chile": "cl",
|
|
||||||
"China": "cn",
|
|
||||||
"Christmas Island": "cx",
|
|
||||||
"Cocos Islands": "cc",
|
|
||||||
"Colombia": "co",
|
|
||||||
"Comoros": "km",
|
|
||||||
"Congo": "cg",
|
|
||||||
"Cook Islands": "ck",
|
|
||||||
"Costa Rica": "cr",
|
|
||||||
"Cote d'Ivoire": "ci",
|
|
||||||
"Croatia": "hr",
|
|
||||||
"Cuba": "cu",
|
|
||||||
"Curacao": "cw",
|
|
||||||
"Cyprus": "cy",
|
|
||||||
"Czech Republic": "cz",
|
|
||||||
"Democratic Republic of the Congo": "cd",
|
|
||||||
"Denmark": "dk",
|
|
||||||
"Djibouti": "dj",
|
|
||||||
"Dominica": "dm",
|
|
||||||
"Dominican Republic": "do",
|
|
||||||
"Ecuador": "ec",
|
|
||||||
"Egypt": "eg",
|
|
||||||
"El Salvador": "sv",
|
|
||||||
"Equatorial Guinea": "gq",
|
|
||||||
"Eritrea": "er",
|
|
||||||
"Estonia": "ee",
|
|
||||||
"Ethiopia": "et",
|
|
||||||
"Falkland Islands": "fk",
|
|
||||||
"Faroe Islands": "fo",
|
|
||||||
"Fiji": "fj",
|
|
||||||
"Finland": "fi",
|
|
||||||
"France": "fr",
|
|
||||||
"French Guiana": "gf",
|
|
||||||
"French Polynesia": "pf",
|
|
||||||
"French Southern Territories": "tf",
|
|
||||||
"Gabon": "ga",
|
|
||||||
"Gambia": "gm",
|
|
||||||
"Georgia": "ge",
|
|
||||||
"Germany": "de",
|
|
||||||
"Ghana": "gh",
|
|
||||||
"Gibraltar": "gi",
|
|
||||||
"Greece": "gr",
|
|
||||||
"Greenland": "gl",
|
|
||||||
"Grenada": "gd",
|
|
||||||
"Guadeloupe": "gp",
|
|
||||||
"Guam": "gu",
|
|
||||||
"Guatemala": "gt",
|
|
||||||
"Guernsey": "gg",
|
|
||||||
"Guinea-Bissau": "gw",
|
|
||||||
"Guinea": "gn",
|
|
||||||
"Guyana": "gy",
|
|
||||||
"Haiti": "ht",
|
|
||||||
"Heard Island and McDonald Islands": "hm",
|
|
||||||
"Honduras": "hn",
|
|
||||||
"Hong Kong": "hk",
|
|
||||||
"Hungary": "hu",
|
|
||||||
"Iceland": "is",
|
|
||||||
"India": "in",
|
|
||||||
"Indonesia": "id",
|
|
||||||
"Iran": "ir",
|
|
||||||
"Iraq": "iq",
|
|
||||||
"Ireland": "ie",
|
|
||||||
"Isle of Man": "im",
|
|
||||||
"Israel": "il",
|
|
||||||
"Italy": "it",
|
|
||||||
"Jamaica": "jm",
|
|
||||||
"Japan": "jp",
|
|
||||||
"Jersey": "je",
|
|
||||||
"Jordan": "jo",
|
|
||||||
"Kazakhstan": "kz",
|
|
||||||
"Kenya": "ke",
|
|
||||||
"Kiribati": "ki",
|
|
||||||
"Korea": "kr",
|
|
||||||
"Kuwait": "kw",
|
|
||||||
"Kyrgyzstan": "kg",
|
|
||||||
"Lao People's Democratic Republic": "la",
|
|
||||||
"Latvia": "lv",
|
|
||||||
"Lebanon": "lb",
|
|
||||||
"Lesotho": "ls",
|
|
||||||
"Liberia": "lr",
|
|
||||||
"Libya": "ly",
|
|
||||||
"Liechtenstein": "li",
|
|
||||||
"Lithuania": "lt",
|
|
||||||
"Luxembourg": "lu",
|
|
||||||
"Macao": "mo",
|
|
||||||
"Macedonia": "mk",
|
|
||||||
"Madagascar": "mg",
|
|
||||||
"Malawi": "mw",
|
|
||||||
"Malaysia": "my",
|
|
||||||
"Maldives": "mv",
|
|
||||||
"Mali": "ml",
|
|
||||||
"Malta": "mt",
|
|
||||||
"Marshall Islands": "mh",
|
|
||||||
"Martinique": "mq",
|
|
||||||
"Mauritania": "mr",
|
|
||||||
"Mauritius": "mu",
|
|
||||||
"Mayotte": "yt",
|
|
||||||
"Mexico": "mx",
|
|
||||||
"Micronesia": "fm",
|
|
||||||
"Moldova": "md",
|
|
||||||
"Monaco": "mc",
|
|
||||||
"Mongolia": "mn",
|
|
||||||
"Montenegro": "me",
|
|
||||||
"Montserrat": "ms",
|
|
||||||
"Morocco": "ma",
|
|
||||||
"Mozambique": "mz",
|
|
||||||
"Myanmar": "mm",
|
|
||||||
"Namibia": "na",
|
|
||||||
"Nauru": "nr",
|
|
||||||
"Nepal": "np",
|
|
||||||
"Netherlands": "nl",
|
|
||||||
"New Caledonia": "nc",
|
|
||||||
"New Zealand": "nz",
|
|
||||||
"Nicaragua": "ni",
|
|
||||||
"Niger": "ne",
|
|
||||||
"Nigeria": "ng",
|
|
||||||
"Niue": "nu",
|
|
||||||
"Norfolk Island": "nf",
|
|
||||||
"Northern Mariana Islands": "mp",
|
|
||||||
"Norway": "no",
|
|
||||||
"Oman": "om",
|
|
||||||
"Pakistan": "pk",
|
|
||||||
"Palau": "pw",
|
|
||||||
"Palestine, State of": "ps",
|
|
||||||
"Panama": "pa",
|
|
||||||
"Papua New Guinea": "pg",
|
|
||||||
"Paraguay": "py",
|
|
||||||
"Peru": "pe",
|
|
||||||
"Philippines": "ph",
|
|
||||||
"Pitcairn": "pn",
|
|
||||||
"Poland": "pl",
|
|
||||||
"Portugal": "pt",
|
|
||||||
"Puerto Rico": "pr",
|
|
||||||
"Qatar": "qa",
|
|
||||||
"Reunion": "re",
|
|
||||||
"Romania": "ro",
|
|
||||||
"Russian Federation": "ru",
|
|
||||||
"Rwanda": "rw",
|
|
||||||
"Saint Barthelemy": "bl",
|
|
||||||
"Saint Helena": "sh",
|
|
||||||
"Saint Kitts and Nevis": "kn",
|
|
||||||
"Saint Lucia": "lc",
|
|
||||||
"Saint Martin": "mf",
|
|
||||||
"Saint Pierre and Miquelon": "pm",
|
|
||||||
"Saint Vincent and the Grenadines": "vc",
|
|
||||||
"Samoa": "ws",
|
|
||||||
"San Marino": "sm",
|
|
||||||
"Sao Tome and Principe": "st",
|
|
||||||
"Saudi Arabia": "sa",
|
|
||||||
"Senegal": "sn",
|
|
||||||
"Serbia": "rs",
|
|
||||||
"Seychelles": "sc",
|
|
||||||
"Sierra Leone": "sl",
|
|
||||||
"Singapore": "sg",
|
|
||||||
"Sint Maarten": "sx",
|
|
||||||
"Slovakia": "sk",
|
|
||||||
"Slovenia": "si",
|
|
||||||
"Solomon Islands": "sb",
|
|
||||||
"Somalia": "so",
|
|
||||||
"South Africa": "za",
|
|
||||||
"South Georgia and the South Sandwich Islands": "gs",
|
|
||||||
"South Sudan": "ss",
|
|
||||||
"Spain": "es",
|
|
||||||
"Sri Lanka": "lk",
|
|
||||||
"Sudan": "sd",
|
|
||||||
"Suriname": "sr",
|
|
||||||
"Svalbard and Jan Mayen": "sj",
|
|
||||||
"Swaziland": "sz",
|
|
||||||
"Sweden": "se",
|
|
||||||
"Switzerland": "ch",
|
|
||||||
"Syrian Arab Republic": "sy",
|
|
||||||
"Taiwan": "tw",
|
|
||||||
"Tajikistan": "tj",
|
|
||||||
"Tanzania": "tz",
|
|
||||||
"Thailand": "th",
|
|
||||||
"Timor-Leste": "tl",
|
|
||||||
"Togo": "tg",
|
|
||||||
"Tokelau": "tk",
|
|
||||||
"Tonga": "to",
|
|
||||||
"Trinidad and Tobago": "tt",
|
|
||||||
"Tunisia": "tn",
|
|
||||||
"Turkey": "tr",
|
|
||||||
"Turkmenistan": "tm",
|
|
||||||
"Turks and Caicos Islands": "tc",
|
|
||||||
"Tuvalu": "tv",
|
|
||||||
"Uganda": "ug",
|
|
||||||
"Ukraine": "ua",
|
|
||||||
"United Arab Emirates": "ae",
|
|
||||||
"United Kingdom": "gb",
|
|
||||||
"United States Minor Outlying Islands": "um",
|
|
||||||
"United States": "us",
|
|
||||||
"Uruguay": "uy",
|
|
||||||
"US Virgin Islands": "vi",
|
|
||||||
"Uzbekistan": "uz",
|
|
||||||
"Vanuatu": "vu",
|
|
||||||
"Vatican City State": "va",
|
|
||||||
"Venezuela": "ve",
|
|
||||||
"Vietnam": "vn",
|
|
||||||
"Wallis and Futuna": "wf",
|
|
||||||
"Western Sahara": "eh",
|
|
||||||
"Yemen": "ye",
|
|
||||||
"Zambia": "zm",
|
|
||||||
"Zimbabwe": "zw",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/qdm12/golibs/network"
|
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
os.Exit(_main())
|
|
||||||
}
|
|
||||||
|
|
||||||
func _main() int {
|
|
||||||
provider := flag.String("provider", "nordvpn", "VPN provider to map region to IP addresses using their API, can be 'nordvpn'")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
client := network.NewClient(30 * time.Second) // big file so 30 seconds
|
|
||||||
switch *provider {
|
|
||||||
case "nordvpn":
|
|
||||||
servers, ignoredServers, err := nordvpn(client)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
for _, server := range servers {
|
|
||||||
fmt.Printf(
|
|
||||||
"{Region: %q, Number: %d, TCP: %t, UDP: %t, IP: net.IP{%s}},\n",
|
|
||||||
server.Region, server.Number, server.TCP, server.UDP, strings.ReplaceAll(server.IP.String(), ".", ", "),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
fmt.Print("\n\n")
|
|
||||||
for _, serverName := range ignoredServers {
|
|
||||||
fmt.Printf("ignored server %q because it does not support both UDP and TCP\n", serverName)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
fmt.Printf("Provider %q is not supported\n", *provider)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func nordvpn(client network.Client) (servers []models.NordvpnServer, ignoredServers []string, err error) {
|
|
||||||
content, status, err := client.GetContent("https://nordvpn.com/api/server")
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
} else if status != http.StatusOK {
|
|
||||||
return nil, nil, fmt.Errorf("HTTP status %d from NordVPN API", status)
|
|
||||||
}
|
|
||||||
response := []struct {
|
|
||||||
IPAddress string `json:"ip_address"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Country string `json:"country"`
|
|
||||||
Features struct {
|
|
||||||
UDP bool `json:"openvpn_udp"`
|
|
||||||
TCP bool `json:"openvpn_tcp"`
|
|
||||||
} `json:"features"`
|
|
||||||
}{}
|
|
||||||
if err := json.Unmarshal(content, &response); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, element := range response {
|
|
||||||
if !element.Features.TCP && !element.Features.UDP {
|
|
||||||
ignoredServers = append(ignoredServers, element.Name)
|
|
||||||
}
|
|
||||||
ip := net.ParseIP(element.IPAddress)
|
|
||||||
if ip == nil {
|
|
||||||
return nil, nil, fmt.Errorf("IP address %q is not valid for server %q", element.IPAddress, element.Name)
|
|
||||||
}
|
|
||||||
i := strings.IndexRune(element.Name, '#')
|
|
||||||
if i < 0 {
|
|
||||||
return nil, nil, fmt.Errorf("No ID in server name %q", element.Name)
|
|
||||||
}
|
|
||||||
idString := element.Name[i+1:]
|
|
||||||
idUint64, err := strconv.ParseUint(idString, 10, 16)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("Bad ID in server name %q", element.Name)
|
|
||||||
}
|
|
||||||
id := uint16(idUint64)
|
|
||||||
server := models.NordvpnServer{
|
|
||||||
Region: element.Country,
|
|
||||||
Number: id,
|
|
||||||
IP: ip,
|
|
||||||
TCP: element.Features.TCP,
|
|
||||||
UDP: element.Features.UDP,
|
|
||||||
}
|
|
||||||
servers = append(servers, server)
|
|
||||||
}
|
|
||||||
sort.Slice(servers, func(i, j int) bool {
|
|
||||||
if servers[i].Region == servers[j].Region {
|
|
||||||
return servers[i].Number < servers[j].Number
|
|
||||||
}
|
|
||||||
return servers[i].Region < servers[j].Region
|
|
||||||
})
|
|
||||||
return servers, ignoredServers, nil
|
|
||||||
}
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"archive/zip"
|
|
||||||
"bytes"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/qdm12/golibs/network"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
os.Exit(_main())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find subdomains from .ovpn files contained in a .zip file
|
|
||||||
func _main() int {
|
|
||||||
provider := flag.String("provider", "surfshark", "VPN provider to parse openvpn files for, can be 'surfshark' or 'vyprvpn")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
var urls []string
|
|
||||||
var suffix string
|
|
||||||
switch *provider {
|
|
||||||
case "surfshark":
|
|
||||||
urls = []string{
|
|
||||||
"https://account.surfshark.com/api/v1/server/configurations",
|
|
||||||
"https://v2uploads.zopim.io/p/2/L/p2LbwLkvfQoSdzOl6VEltzQA6StiZqrs/12500634259669c77012765139bcfe4f4c90db1e.zip",
|
|
||||||
}
|
|
||||||
suffix = ".prod.surfshark.com"
|
|
||||||
case "vyprvpn":
|
|
||||||
urls = []string{
|
|
||||||
"https://support.vyprvpn.com/hc/article_attachments/360052617332/Vypr_OpenVPN_20200320.zip",
|
|
||||||
}
|
|
||||||
suffix = ".vyprvpn.com"
|
|
||||||
default:
|
|
||||||
fmt.Printf("Provider %q is not supported\n", *provider)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
contents, err := fetchAndExtractFiles(urls...)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
uniqueSubdomainsToFilename := make(map[string]string)
|
|
||||||
for fileName, content := range contents {
|
|
||||||
subdomain, err := extractInformation(content, suffix)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return 1
|
|
||||||
} else if len(subdomain) > 0 {
|
|
||||||
fileName = strings.TrimSuffix(fileName, ".ovpn")
|
|
||||||
fileName = strings.ReplaceAll(fileName, " - ", " ")
|
|
||||||
uniqueSubdomainsToFilename[subdomain] = fileName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type subdomainFilename struct {
|
|
||||||
subdomain string
|
|
||||||
fileName string
|
|
||||||
}
|
|
||||||
subdomains := make([]subdomainFilename, len(uniqueSubdomainsToFilename))
|
|
||||||
i := 0
|
|
||||||
for subdomain, fileName := range uniqueSubdomainsToFilename {
|
|
||||||
subdomains[i] = subdomainFilename{
|
|
||||||
subdomain: subdomain,
|
|
||||||
fileName: fileName,
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
sort.Slice(subdomains, func(i, j int) bool {
|
|
||||||
return subdomains[i].subdomain < subdomains[j].subdomain
|
|
||||||
})
|
|
||||||
fmt.Println("Subdomain Filename")
|
|
||||||
for i := range subdomains {
|
|
||||||
fmt.Printf("%s %s\n", subdomains[i].subdomain, subdomains[i].fileName)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchAndExtractFiles(urls ...string) (contents map[string][]byte, err error) {
|
|
||||||
client := network.NewClient(10 * time.Second)
|
|
||||||
contents = make(map[string][]byte)
|
|
||||||
for _, url := range urls {
|
|
||||||
zipBytes, status, err := client.GetContent(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if status != http.StatusOK {
|
|
||||||
return nil, fmt.Errorf("Getting %s results in HTTP status code %d", url, status)
|
|
||||||
}
|
|
||||||
newContents, err := zipExtractAll(zipBytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for fileName, content := range newContents {
|
|
||||||
contents[fileName] = content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return contents, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func zipExtractAll(zipBytes []byte) (contents map[string][]byte, err error) {
|
|
||||||
r, err := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
contents = map[string][]byte{}
|
|
||||||
for _, zf := range r.File {
|
|
||||||
fileName := filepath.Base(zf.Name)
|
|
||||||
if !strings.HasSuffix(fileName, ".ovpn") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
f, err := zf.Open()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
contents[fileName], err = ioutil.ReadAll(f)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := f.Close(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return contents, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractInformation(content []byte, suffix string) (subdomain string, err error) {
|
|
||||||
lines := strings.Split(string(content), "\n")
|
|
||||||
for _, line := range lines {
|
|
||||||
if strings.HasPrefix(line, "remote ") {
|
|
||||||
words := strings.Fields(line)
|
|
||||||
if len(words) < 2 {
|
|
||||||
return "", fmt.Errorf("not enough words on line %q", line)
|
|
||||||
}
|
|
||||||
host := words[1]
|
|
||||||
if net.ParseIP(host) != nil {
|
|
||||||
return "", nil // ignore IP addresses
|
|
||||||
}
|
|
||||||
return strings.TrimSuffix(host, suffix), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("could not find remote line in: %s", string(content))
|
|
||||||
}
|
|
||||||
@@ -1,749 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
ctx := context.Background()
|
|
||||||
os.Exit(_main(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func _main(ctx context.Context) int {
|
|
||||||
resolverAddress := flag.String("resolver", "1.1.1.1", "DNS Resolver IP address to use")
|
|
||||||
provider := flag.String("provider", "pia", "VPN provider to resolve for, 'pia', 'windscribe', 'cyberghost' or 'vyprvpn'")
|
|
||||||
region := flag.String("region", "all", "Comma separated list of VPN provider region names to resolve for, use 'all' to resolve all")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
resolver := newResolver(*resolverAddress)
|
|
||||||
lookupIP := newLookupIP(resolver)
|
|
||||||
|
|
||||||
var domain string
|
|
||||||
var servers []server
|
|
||||||
switch *provider {
|
|
||||||
case "pia":
|
|
||||||
domain = "privateinternetaccess.com"
|
|
||||||
servers = piaServers()
|
|
||||||
case "windscribe":
|
|
||||||
domain = "windscribe.com"
|
|
||||||
servers = windscribeServers()
|
|
||||||
case "surfshark":
|
|
||||||
domain = "prod.surfshark.com"
|
|
||||||
servers = surfsharkServers()
|
|
||||||
case "cyberghost":
|
|
||||||
domain = "cg-dialup.net"
|
|
||||||
servers = cyberghostServers()
|
|
||||||
case "vyprvpn":
|
|
||||||
domain = "vyprvpn.com"
|
|
||||||
servers = vyprvpnServers()
|
|
||||||
default:
|
|
||||||
fmt.Printf("Provider %q is not supported\n", *provider)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if *region != "all" {
|
|
||||||
regions := strings.Split(*region, ",")
|
|
||||||
uniqueRegions := make(map[string]struct{})
|
|
||||||
for _, r := range regions {
|
|
||||||
uniqueRegions[r] = struct{}{}
|
|
||||||
}
|
|
||||||
for i := range servers {
|
|
||||||
if _, ok := uniqueRegions[servers[i].region]; !ok {
|
|
||||||
servers[i] = servers[len(servers)-1]
|
|
||||||
servers = servers[:len(servers)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stringChannel := make(chan string)
|
|
||||||
errorChannel := make(chan error)
|
|
||||||
const maxGoroutines = 10
|
|
||||||
guard := make(chan struct{}, maxGoroutines)
|
|
||||||
for _, s := range servers {
|
|
||||||
go func(s server) {
|
|
||||||
guard <- struct{}{}
|
|
||||||
ips, err := resolveRepeat(ctx, lookupIP, s.subdomain+"."+domain, 3)
|
|
||||||
<-guard
|
|
||||||
if err != nil {
|
|
||||||
errorChannel <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stringChannel <- formatLine(*provider, s, ips)
|
|
||||||
}(s)
|
|
||||||
}
|
|
||||||
var lines []string
|
|
||||||
var errors []error
|
|
||||||
for range servers {
|
|
||||||
select {
|
|
||||||
case err := <-errorChannel:
|
|
||||||
errors = append(errors, err)
|
|
||||||
case s := <-stringChannel:
|
|
||||||
lines = append(lines, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Slice(lines, func(i, j int) bool {
|
|
||||||
return lines[i] < lines[j]
|
|
||||||
})
|
|
||||||
for _, s := range lines {
|
|
||||||
fmt.Println(s)
|
|
||||||
}
|
|
||||||
if len(errors) > 0 {
|
|
||||||
fmt.Printf("\n%d errors occurred, described below\n\n", len(errors))
|
|
||||||
for _, err := range errors {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatLine(provider string, s server, ips []net.IP) string {
|
|
||||||
ipStrings := make([]string, len(ips))
|
|
||||||
for i := range ips {
|
|
||||||
ipStrings[i] = fmt.Sprintf("{%s}", strings.ReplaceAll(ips[i].String(), ".", ", "))
|
|
||||||
}
|
|
||||||
ipString := strings.Join(ipStrings, ", ")
|
|
||||||
switch provider {
|
|
||||||
case "pia":
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"{Region: %q, IPs: []net.IP{%s}},",
|
|
||||||
s.region, ipString,
|
|
||||||
)
|
|
||||||
case "windscribe":
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"{Region: %q, IPs: []net.IP{%s}},",
|
|
||||||
s.region, ipString,
|
|
||||||
)
|
|
||||||
case "surfshark":
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"{Region: %q, IPs: []net.IP{%s}},",
|
|
||||||
s.region, ipString,
|
|
||||||
)
|
|
||||||
case "cyberghost":
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"{Region: %q, Group: %q, IPs: []net.IP{%s}},",
|
|
||||||
s.region, s.group, ipString,
|
|
||||||
)
|
|
||||||
case "vyprvpn":
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"{Region: %q, IPs: []net.IP{%s}},",
|
|
||||||
s.region, ipString,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type lookupIPFunc func(ctx context.Context, host string) (ips []net.IP, err error)
|
|
||||||
|
|
||||||
func newLookupIP(r *net.Resolver) lookupIPFunc {
|
|
||||||
return func(ctx context.Context, host string) (ips []net.IP, err error) {
|
|
||||||
addresses, err := r.LookupIPAddr(ctx, host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ips = make([]net.IP, len(addresses))
|
|
||||||
for i := range addresses {
|
|
||||||
ips[i] = addresses[i].IP
|
|
||||||
}
|
|
||||||
return ips, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newResolver(ip string) *net.Resolver {
|
|
||||||
return &net.Resolver{
|
|
||||||
PreferGo: true,
|
|
||||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
|
||||||
d := net.Dialer{}
|
|
||||||
return d.DialContext(ctx, "udp", net.JoinHostPort(ip, "53"))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveRepeat(ctx context.Context, lookupIP lookupIPFunc, host string, n int) (ips []net.IP, err error) {
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
newIPs, err := lookupIP(ctx, host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ips = append(ips, newIPs...)
|
|
||||||
}
|
|
||||||
return uniqueSortedIPs(ips), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func uniqueSortedIPs(ips []net.IP) []net.IP {
|
|
||||||
uniqueIPs := make(map[string]struct{})
|
|
||||||
for _, ip := range ips {
|
|
||||||
uniqueIPs[ip.String()] = struct{}{}
|
|
||||||
}
|
|
||||||
ips = make([]net.IP, len(uniqueIPs))
|
|
||||||
i := 0
|
|
||||||
for ip := range uniqueIPs {
|
|
||||||
ips[i] = net.ParseIP(ip)
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
sort.Slice(ips, func(i, j int) bool {
|
|
||||||
return bytes.Compare(ips[i], ips[j]) < 0
|
|
||||||
})
|
|
||||||
return ips
|
|
||||||
}
|
|
||||||
|
|
||||||
type server struct {
|
|
||||||
subdomain string
|
|
||||||
region string
|
|
||||||
group string // only for cyberghost
|
|
||||||
}
|
|
||||||
|
|
||||||
func piaServers() []server {
|
|
||||||
return []server{
|
|
||||||
{subdomain: "au-melbourne", region: "AU Melbourne"},
|
|
||||||
{subdomain: "au-perth", region: "AU Perth"},
|
|
||||||
{subdomain: "au-sydney", region: "AU Sydney"},
|
|
||||||
{subdomain: "austria", region: "Austria"},
|
|
||||||
{subdomain: "belgium", region: "Belgium"},
|
|
||||||
{subdomain: "ca-montreal", region: "CA Montreal"},
|
|
||||||
{subdomain: "ca-toronto", region: "CA Toronto"},
|
|
||||||
{subdomain: "ca-vancouver", region: "CA Vancouver"},
|
|
||||||
{subdomain: "czech", region: "Czech Republic"},
|
|
||||||
{subdomain: "de-berlin", region: "DE Berlin"},
|
|
||||||
{subdomain: "de-frankfurt", region: "DE Frankfurt"},
|
|
||||||
{subdomain: "denmark", region: "Denmark"},
|
|
||||||
{subdomain: "fi", region: "Finlan"},
|
|
||||||
{subdomain: "france", region: "France"},
|
|
||||||
{subdomain: "hk", region: "Hong Kong"},
|
|
||||||
{subdomain: "hungary", region: "Hungary"},
|
|
||||||
{subdomain: "in", region: "India"},
|
|
||||||
{subdomain: "ireland", region: "Ireland"},
|
|
||||||
{subdomain: "israel", region: "Israel"},
|
|
||||||
{subdomain: "italy", region: "Italy"},
|
|
||||||
{subdomain: "japan", region: "Japan"},
|
|
||||||
{subdomain: "lu", region: "Luxembourg"},
|
|
||||||
{subdomain: "mexico", region: "Mexico"},
|
|
||||||
{subdomain: "nl", region: "Netherlands"},
|
|
||||||
{subdomain: "nz", region: "New Zealand"},
|
|
||||||
{subdomain: "no", region: "Norway"},
|
|
||||||
{subdomain: "poland", region: "Poland"},
|
|
||||||
{subdomain: "ro", region: "Romania"},
|
|
||||||
{subdomain: "sg", region: "Singapore"},
|
|
||||||
{subdomain: "spain", region: "Spain"},
|
|
||||||
{subdomain: "sweden", region: "Sweden"},
|
|
||||||
{subdomain: "swiss", region: "Switzerland"},
|
|
||||||
{subdomain: "ae", region: "UAE"},
|
|
||||||
{subdomain: "uk-london", region: "UK London"},
|
|
||||||
{subdomain: "uk-manchester", region: "UK Manchester"},
|
|
||||||
{subdomain: "uk-southampton", region: "UK Southampton"},
|
|
||||||
{subdomain: "us-atlanta", region: "US Atlanta"},
|
|
||||||
{subdomain: "us-california", region: "US California"},
|
|
||||||
{subdomain: "us-chicago", region: "US Chicago"},
|
|
||||||
{subdomain: "us-denver", region: "US Denver"},
|
|
||||||
{subdomain: "us-east", region: "US East"},
|
|
||||||
{subdomain: "us-florida", region: "US Florida"},
|
|
||||||
{subdomain: "us-houston", region: "US Houston"},
|
|
||||||
{subdomain: "us-lasvegas", region: "US Las Vegas"},
|
|
||||||
{subdomain: "us-newyorkcity", region: "US New York City"},
|
|
||||||
{subdomain: "us-seattle", region: "US Seattle"},
|
|
||||||
{subdomain: "us-siliconvalley", region: "US Silicon Valley"},
|
|
||||||
{subdomain: "us-texas", region: "US Texas"},
|
|
||||||
{subdomain: "us-washingtondc", region: "US Washington DC"},
|
|
||||||
{subdomain: "us-west", region: "US West"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func windscribeServers() []server {
|
|
||||||
return []server{
|
|
||||||
{subdomain: "al", region: "Albania"},
|
|
||||||
{subdomain: "ar", region: "Argentina"},
|
|
||||||
{subdomain: "au", region: "Australia"},
|
|
||||||
{subdomain: "at", region: "Austria"},
|
|
||||||
{subdomain: "az", region: "Azerbaijan"},
|
|
||||||
{subdomain: "be", region: "Belgium"},
|
|
||||||
{subdomain: "ba", region: "Bosnia"},
|
|
||||||
{subdomain: "br", region: "Brazil"},
|
|
||||||
{subdomain: "bg", region: "Bulgaria"},
|
|
||||||
{subdomain: "ca", region: "Canada East"},
|
|
||||||
{subdomain: "ca-west", region: "Canada West"},
|
|
||||||
{subdomain: "co", region: "Colombia"},
|
|
||||||
{subdomain: "hr", region: "Croatia"},
|
|
||||||
{subdomain: "cy", region: "Cyprus"},
|
|
||||||
{subdomain: "cz", region: "Czech republic"},
|
|
||||||
{subdomain: "dk", region: "Denmark"},
|
|
||||||
{subdomain: "ee", region: "Estonia"},
|
|
||||||
{subdomain: "aq", region: "Fake antarctica"},
|
|
||||||
{subdomain: "fi", region: "Finland"},
|
|
||||||
{subdomain: "fr", region: "France"},
|
|
||||||
{subdomain: "ge", region: "Georgia"},
|
|
||||||
{subdomain: "de", region: "Germany"},
|
|
||||||
{subdomain: "gr", region: "Greece"},
|
|
||||||
{subdomain: "hk", region: "Hong kong"},
|
|
||||||
{subdomain: "hu", region: "Hungary"},
|
|
||||||
{subdomain: "is", region: "Iceland"},
|
|
||||||
{subdomain: "in", region: "India"},
|
|
||||||
{subdomain: "id", region: "Indonesia"},
|
|
||||||
{subdomain: "ie", region: "Ireland"},
|
|
||||||
{subdomain: "il", region: "Israel"},
|
|
||||||
{subdomain: "it", region: "Italy"},
|
|
||||||
{subdomain: "jp", region: "Japan"},
|
|
||||||
{subdomain: "lv", region: "Latvia"},
|
|
||||||
{subdomain: "lt", region: "Lithuania"},
|
|
||||||
{subdomain: "mk", region: "Macedonia"},
|
|
||||||
{subdomain: "my", region: "Malaysia"},
|
|
||||||
{subdomain: "mx", region: "Mexico"},
|
|
||||||
{subdomain: "md", region: "Moldova"},
|
|
||||||
{subdomain: "nl", region: "Netherlands"},
|
|
||||||
{subdomain: "nz", region: "New zealand"},
|
|
||||||
{subdomain: "no", region: "Norway"},
|
|
||||||
{subdomain: "ph", region: "Philippines"},
|
|
||||||
{subdomain: "pl", region: "Poland"},
|
|
||||||
{subdomain: "pt", region: "Portugal"},
|
|
||||||
{subdomain: "ro", region: "Romania"},
|
|
||||||
{subdomain: "ru", region: "Russia"},
|
|
||||||
{subdomain: "rs", region: "Serbia"},
|
|
||||||
{subdomain: "sg", region: "Singapore"},
|
|
||||||
{subdomain: "sk", region: "Slovakia"},
|
|
||||||
{subdomain: "si", region: "Slovenia"},
|
|
||||||
{subdomain: "za", region: "South Africa"},
|
|
||||||
{subdomain: "kr", region: "South Korea"},
|
|
||||||
{subdomain: "es", region: "Spain"},
|
|
||||||
{subdomain: "se", region: "Sweden"},
|
|
||||||
{subdomain: "ch", region: "Switzerland"},
|
|
||||||
{subdomain: "th", region: "Thailand"},
|
|
||||||
{subdomain: "tn", region: "Tunisia"},
|
|
||||||
{subdomain: "tr", region: "Turkey"},
|
|
||||||
{subdomain: "ua", region: "Ukraine"},
|
|
||||||
{subdomain: "ae", region: "United Arab Emirates"},
|
|
||||||
{subdomain: "uk", region: "United Kingdom"},
|
|
||||||
{subdomain: "us-central", region: "US Central"},
|
|
||||||
{subdomain: "us-east", region: "US East"},
|
|
||||||
{subdomain: "us-west", region: "US West"},
|
|
||||||
{subdomain: "vn", region: "Vietnam"},
|
|
||||||
{subdomain: "wf-ca", region: "Windflix CA"},
|
|
||||||
{subdomain: "wf-jp", region: "Windflix JP"},
|
|
||||||
{subdomain: "wf-uk", region: "Windflix UK"},
|
|
||||||
{subdomain: "wf-us", region: "Windflix US"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func surfsharkServers() []server {
|
|
||||||
return []server{
|
|
||||||
{subdomain: "ae-dub", region: "United Arab Emirates"},
|
|
||||||
{subdomain: "al-tia", region: "Albania"},
|
|
||||||
{subdomain: "at-vie", region: "Austria"},
|
|
||||||
{subdomain: "au-adl", region: "Australia Adelaide"},
|
|
||||||
{subdomain: "au-bne", region: "Australia Brisbane"},
|
|
||||||
{subdomain: "au-mel", region: "Australia Melbourne"},
|
|
||||||
{subdomain: "au-per", region: "Australia Perth"},
|
|
||||||
{subdomain: "au-syd", region: "Australia Sydney"},
|
|
||||||
{subdomain: "au-us", region: "Australia US"},
|
|
||||||
{subdomain: "az-bak", region: "Azerbaijan"},
|
|
||||||
{subdomain: "ba-sjj", region: "Bosnia and Herzegovina"},
|
|
||||||
{subdomain: "be-bru", region: "Belgium"},
|
|
||||||
{subdomain: "bg-sof", region: "Bulgaria"},
|
|
||||||
{subdomain: "br-sao", region: "Brazil"},
|
|
||||||
{subdomain: "ca-mon", region: "Canada Montreal"},
|
|
||||||
{subdomain: "ca-tor", region: "Canada Toronto"},
|
|
||||||
{subdomain: "ca-us", region: "Canada US"},
|
|
||||||
{subdomain: "ca-van", region: "Canada Vancouver"},
|
|
||||||
{subdomain: "ch-zur", region: "Switzerland"},
|
|
||||||
{subdomain: "cl-san", region: "Chile"},
|
|
||||||
{subdomain: "co-bog", region: "Colombia"},
|
|
||||||
{subdomain: "cr-sjn", region: "Costa Rica"},
|
|
||||||
{subdomain: "cy-nic", region: "Cyprus"},
|
|
||||||
{subdomain: "cz-prg", region: "Czech Republic"},
|
|
||||||
{subdomain: "de-ber", region: "Germany Berlin"},
|
|
||||||
{subdomain: "de-fra", region: "Germany Frankfurt am Main"},
|
|
||||||
{subdomain: "de-fra-st001", region: "Germany Frankfurt am Main st001"},
|
|
||||||
{subdomain: "de-fra-st002", region: "Germany Frankfurt am Main st002"},
|
|
||||||
{subdomain: "de-fra-st003", region: "Germany Frankfurt am Main st003"},
|
|
||||||
{subdomain: "de-muc", region: "Germany Munich"},
|
|
||||||
{subdomain: "de-nue", region: "Germany Nuremberg"},
|
|
||||||
{subdomain: "de-sg", region: "Germany Singapour"},
|
|
||||||
{subdomain: "de-uk", region: "Germany UK"},
|
|
||||||
{subdomain: "dk-cph", region: "Denmark"},
|
|
||||||
{subdomain: "ee-tll", region: "Estonia"},
|
|
||||||
{subdomain: "es-bcn", region: "Spain Barcelona"},
|
|
||||||
{subdomain: "es-mad", region: "Spain Madrid"},
|
|
||||||
{subdomain: "es-vlc", region: "Spain Valencia"},
|
|
||||||
{subdomain: "fi-hel", region: "Finland"},
|
|
||||||
{subdomain: "fr-bod", region: "France Bordeaux"},
|
|
||||||
{subdomain: "fr-mrs", region: "France Marseilles"},
|
|
||||||
{subdomain: "fr-par", region: "France Paris"},
|
|
||||||
{subdomain: "fr-se", region: "France Sweden"},
|
|
||||||
{subdomain: "gr-ath", region: "Greece"},
|
|
||||||
{subdomain: "hk-hkg", region: "Hong Kong"},
|
|
||||||
{subdomain: "hr-zag", region: "Croatia"},
|
|
||||||
{subdomain: "hu-bud", region: "Hungary"},
|
|
||||||
{subdomain: "id-jak", region: "Indonesia"},
|
|
||||||
{subdomain: "ie-dub", region: "Ireland"},
|
|
||||||
{subdomain: "il-tlv", region: "Israel"},
|
|
||||||
{subdomain: "in-chn", region: "India Chennai"},
|
|
||||||
{subdomain: "in-idr", region: "India Indore"},
|
|
||||||
{subdomain: "in-mum", region: "India Mumbai"},
|
|
||||||
{subdomain: "in-uk", region: "India UK"},
|
|
||||||
{subdomain: "is-rkv", region: "Iceland"},
|
|
||||||
{subdomain: "it-mil", region: "Italy Milan"},
|
|
||||||
{subdomain: "it-rom", region: "Italy Rome"},
|
|
||||||
{subdomain: "jp-tok", region: "Japan Tokyo"},
|
|
||||||
{subdomain: "jp-tok-st001", region: "Japan Tokyo st001"},
|
|
||||||
{subdomain: "jp-tok-st002", region: "Japan Tokyo st002"},
|
|
||||||
{subdomain: "jp-tok-st003", region: "Japan Tokyo st003"},
|
|
||||||
{subdomain: "jp-tok-st004", region: "Japan Tokyo st004"},
|
|
||||||
{subdomain: "jp-tok-st005", region: "Japan Tokyo st005"},
|
|
||||||
{subdomain: "jp-tok-st006", region: "Japan Tokyo st006"},
|
|
||||||
{subdomain: "jp-tok-st007", region: "Japan Tokyo st007"},
|
|
||||||
{subdomain: "kr-seo", region: "Korea"},
|
|
||||||
{subdomain: "kz-ura", region: "Kazakhstan"},
|
|
||||||
{subdomain: "lu-ste", region: "Luxembourg"},
|
|
||||||
{subdomain: "lv-rig", region: "Latvia"},
|
|
||||||
{subdomain: "ly-tip", region: "Libya"},
|
|
||||||
{subdomain: "md-chi", region: "Moldova"},
|
|
||||||
{subdomain: "mk-skp", region: "North Macedonia"},
|
|
||||||
{subdomain: "my-kul", region: "Malaysia"},
|
|
||||||
{subdomain: "ng-lag", region: "Nigeria"},
|
|
||||||
{subdomain: "nl-ams", region: "Netherlands Amsterdam"},
|
|
||||||
{subdomain: "nl-ams-st001", region: "Netherlands Amsterdam st001"},
|
|
||||||
{subdomain: "nl-us", region: "Netherlands US"},
|
|
||||||
{subdomain: "no-osl", region: "Norway"},
|
|
||||||
{subdomain: "nz-akl", region: "New Zealand"},
|
|
||||||
{subdomain: "ph-mnl", region: "Philippines"},
|
|
||||||
{subdomain: "pl-gdn", region: "Poland Gdansk"},
|
|
||||||
{subdomain: "pl-waw", region: "Poland Warsaw"},
|
|
||||||
{subdomain: "pt-lis", region: "Portugal Lisbon"},
|
|
||||||
{subdomain: "pt-lou", region: "Portugal Loule"},
|
|
||||||
{subdomain: "pt-opo", region: "Portugal Porto"},
|
|
||||||
{subdomain: "py-asu", region: "Paraguay"},
|
|
||||||
{subdomain: "ro-buc", region: "Romania"},
|
|
||||||
{subdomain: "rs-beg", region: "Serbia"},
|
|
||||||
{subdomain: "ru-mos", region: "Russia Moscow"},
|
|
||||||
{subdomain: "ru-spt", region: "Russia St. Petersburg"},
|
|
||||||
{subdomain: "se-sto", region: "Sweden"},
|
|
||||||
{subdomain: "sg-hk", region: "Singapore Hong Kong"},
|
|
||||||
{subdomain: "sg-nl", region: "Singapore Netherlands"},
|
|
||||||
{subdomain: "sg-sng", region: "Singapore"},
|
|
||||||
{subdomain: "sg-sng-st001", region: "Singapore st001"},
|
|
||||||
{subdomain: "sg-sng-st002", region: "Singapore st002"},
|
|
||||||
{subdomain: "sg-sng-st003", region: "Singapore st003"},
|
|
||||||
{subdomain: "sg-sng-st004", region: "Singapore st004"},
|
|
||||||
{subdomain: "si-lju", region: "Slovenia"},
|
|
||||||
{subdomain: "sk-bts", region: "Slovekia"},
|
|
||||||
{subdomain: "th-bkk", region: "Thailand"},
|
|
||||||
{subdomain: "tr-bur", region: "Turkey"},
|
|
||||||
{subdomain: "tw-tai", region: "Taiwan"},
|
|
||||||
{subdomain: "ua-iev", region: "Ukraine"},
|
|
||||||
{subdomain: "uk-de", region: "UK Germany"},
|
|
||||||
{subdomain: "uk-fr", region: "UK France"},
|
|
||||||
{subdomain: "uk-gla", region: "UK Glasgow"},
|
|
||||||
{subdomain: "uk-lon", region: "UK London"},
|
|
||||||
{subdomain: "uk-lon-st001", region: "UK London st001"},
|
|
||||||
{subdomain: "uk-lon-st002", region: "UK London st002"},
|
|
||||||
{subdomain: "uk-lon-st003", region: "UK London st003"},
|
|
||||||
{subdomain: "uk-lon-st004", region: "UK London st004"},
|
|
||||||
{subdomain: "uk-lon-st005", region: "UK London st005"},
|
|
||||||
{subdomain: "uk-man", region: "UK Manchester"},
|
|
||||||
{subdomain: "us-atl", region: "US Atlanta"},
|
|
||||||
{subdomain: "us-bdn", region: "US Bend"},
|
|
||||||
{subdomain: "us-bos", region: "US Boston"},
|
|
||||||
{subdomain: "us-buf", region: "US Buffalo"},
|
|
||||||
{subdomain: "us-chi", region: "US Chicago"},
|
|
||||||
{subdomain: "us-clt", region: "US Charlotte"},
|
|
||||||
{subdomain: "us-dal", region: "US Dallas"},
|
|
||||||
{subdomain: "us-den", region: "US Denver"},
|
|
||||||
{subdomain: "us-dtw", region: "US Gahanna"},
|
|
||||||
{subdomain: "us-hou", region: "US Houston"},
|
|
||||||
{subdomain: "us-kan", region: "US Kansas City"},
|
|
||||||
{subdomain: "us-las", region: "US Las Vegas"},
|
|
||||||
{subdomain: "us-lax", region: "US Los Angeles"},
|
|
||||||
{subdomain: "us-ltm", region: "US Latham"},
|
|
||||||
{subdomain: "us-mia", region: "US Miami"},
|
|
||||||
{subdomain: "us-mnz", region: "US Maryland"},
|
|
||||||
{subdomain: "us-nl", region: "US Netherlands"},
|
|
||||||
{subdomain: "us-nyc", region: "US New York City"},
|
|
||||||
{subdomain: "us-nyc-mp001", region: "US New York City mp001"},
|
|
||||||
{subdomain: "us-nyc-st001", region: "US New York City st001"},
|
|
||||||
{subdomain: "us-nyc-st002", region: "US New York City st002"},
|
|
||||||
{subdomain: "us-nyc-st003", region: "US New York City st003"},
|
|
||||||
{subdomain: "us-nyc-st004", region: "US New York City st004"},
|
|
||||||
{subdomain: "us-nyc-st005", region: "US New York City st005"},
|
|
||||||
{subdomain: "us-orl", region: "US Orlando"},
|
|
||||||
{subdomain: "us-phx", region: "US Phoenix"},
|
|
||||||
{subdomain: "us-pt", region: "US Portugal"},
|
|
||||||
{subdomain: "us-sea", region: "US Seatle"},
|
|
||||||
{subdomain: "us-sfo", region: "US San Francisco"},
|
|
||||||
{subdomain: "us-slc", region: "US Salt Lake City"},
|
|
||||||
{subdomain: "us-stl", region: "US Saint Louis"},
|
|
||||||
{subdomain: "us-tpa", region: "US Tampa"},
|
|
||||||
{subdomain: "vn-hcm", region: "Vietnam"},
|
|
||||||
{subdomain: "za-jnb", region: "South Africa"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cyberghostServers() []server {
|
|
||||||
return []server{
|
|
||||||
{subdomain: "97-1-al", region: "Albania", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-al", region: "Albania", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "87-1-dz", region: "Algeria", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-dz", region: "Algeria", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "97-1-ad", region: "Andorra", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-ad", region: "Andorra", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "94-1-ar", region: "Argentina", group: "Premium UDP USA"},
|
|
||||||
{subdomain: "93-1-ar", region: "Argentina", group: "Premium TCP USA"},
|
|
||||||
{subdomain: "87-1-am", region: "Armenia", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-am", region: "Armenia", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "95-1-au", region: "Australia", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "96-1-au", region: "Australia", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "97-1-at", region: "Austria", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-at", region: "Austria", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "93-1-bs", region: "Bahamas", group: "Premium TCP USA"},
|
|
||||||
{subdomain: "94-1-bs", region: "Bahamas", group: "Premium UDP USA"},
|
|
||||||
{subdomain: "95-1-bd", region: "Bangladesh", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "96-1-bd", region: "Bangladesh", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "97-1-by", region: "Belarus", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-by", region: "Belarus", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-be", region: "Belgium", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-be", region: "Belgium", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "87-1-ba", region: "Bosnia and Herzegovina", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-ba", region: "Bosnia and Herzegovina", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "94-1-br", region: "Brazil", group: "Premium UDP USA"},
|
|
||||||
{subdomain: "93-1-br", region: "Brazil", group: "Premium TCP USA"},
|
|
||||||
{subdomain: "87-1-bg", region: "Bulgaria", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-bg", region: "Bulgaria", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "96-1-kh", region: "Cambodia", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "95-1-kh", region: "Cambodia", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "93-1-ca", region: "Canada", group: "Premium TCP USA"},
|
|
||||||
{subdomain: "94-1-ca", region: "Canada", group: "Premium UDP USA"},
|
|
||||||
{subdomain: "93-1-cl", region: "Chile", group: "Premium TCP USA"},
|
|
||||||
{subdomain: "94-1-cl", region: "Chile", group: "Premium UDP USA"},
|
|
||||||
{subdomain: "96-1-cn", region: "China", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "95-1-cn", region: "China", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "94-1-co", region: "Colombia", group: "Premium UDP USA"},
|
|
||||||
{subdomain: "93-1-co", region: "Colombia", group: "Premium TCP USA"},
|
|
||||||
{subdomain: "93-1-cr", region: "Costa Rica", group: "Premium TCP USA"},
|
|
||||||
{subdomain: "94-1-cr", region: "Costa Rica", group: "Premium UDP USA"},
|
|
||||||
{subdomain: "87-1-cy", region: "Cyprus", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-cy", region: "Cyprus", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "97-1-cz", region: "Czech Republic", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-cz", region: "Czech Republic", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "87-1-dk", region: "Denmark", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-dk", region: "Denmark", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-eg", region: "Egypt", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-eg", region: "Egypt", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-ee", region: "Estonia", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-ee", region: "Estonia", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "97-1-fi", region: "Finland", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-fi", region: "Finland", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "87-1-fr", region: "France", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-fr", region: "France", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-ge", region: "Georgia", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-ge", region: "Georgia", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "97-1-de", region: "Germany", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-de", region: "Germany", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "87-1-gr", region: "Greece", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-gr", region: "Greece", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "97-1-gl", region: "Greenland", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-gl", region: "Greenland", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "96-1-hk", region: "Hong Kong", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "95-1-hk", region: "Hong Kong", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "87-1-hu", region: "Hungary", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-hu", region: "Hungary", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "97-1-is", region: "Iceland", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-is", region: "Iceland", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "87-1-in", region: "India", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-in", region: "India", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "95-1-id", region: "Indonesia", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "96-1-id", region: "Indonesia", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "87-1-ir", region: "Iran", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-ir", region: "Iran", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-ie", region: "Ireland", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-ie", region: "Ireland", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-im", region: "Isle of Man", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-im", region: "Isle of Man", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-il", region: "Israel", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-il", region: "Israel", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "97-1-it", region: "Italy", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-it", region: "Italy", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "95-1-jp", region: "Japan", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "96-1-jp", region: "Japan", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "97-1-kz", region: "Kazakhstan", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-kz", region: "Kazakhstan", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "95-1-ke", region: "Kenya", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "96-1-ke", region: "Kenya", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "95-1-kr", region: "Korea", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "96-1-kr", region: "Korea", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "97-1-lv", region: "Latvia", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-lv", region: "Latvia", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-li", region: "Liechtenstein", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-li", region: "Liechtenstein", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-lt", region: "Lithuania", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-lt", region: "Lithuania", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "87-1-lu", region: "Luxembourg", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-lu", region: "Luxembourg", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "96-1-mo", region: "Macao", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "95-1-mo", region: "Macao", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "97-1-mk", region: "Macedonia", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-mk", region: "Macedonia", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "95-1-my", region: "Malaysia", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "96-1-my", region: "Malaysia", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "87-1-mt", region: "Malta", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-mt", region: "Malta", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "93-1-mx", region: "Mexico", group: "Premium TCP USA"},
|
|
||||||
{subdomain: "94-1-mx", region: "Mexico", group: "Premium UDP USA"},
|
|
||||||
{subdomain: "87-1-md", region: "Moldova", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-md", region: "Moldova", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-mc", region: "Monaco", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-mc", region: "Monaco", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "96-1-mn", region: "Mongolia", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "95-1-mn", region: "Mongolia", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "87-1-me", region: "Montenegro", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-me", region: "Montenegro", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "97-1-ma", region: "Morocco", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-ma", region: "Morocco", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-nl", region: "Netherlands", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-nl", region: "Netherlands", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "95-1-nz", region: "New Zealand", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "96-1-nz", region: "New Zealand", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "87-1-ng", region: "Nigeria", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-ng", region: "Nigeria", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "97-1-no", region: "Norway", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-no", region: "Norway", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-pk", region: "Pakistan", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-pk", region: "Pakistan", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-pa", region: "Panama", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-pa", region: "Panama", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "95-1-ph", region: "Philippines", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "96-1-ph", region: "Philippines", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "97-1-pl", region: "Poland", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-pl", region: "Poland", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-pt", region: "Portugal", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-pt", region: "Portugal", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-qa", region: "Qatar", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-qa", region: "Qatar", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "87-1-ro", region: "Romania", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "87-8-ro", region: "Romania", group: "NoSpy UDP Europe"},
|
|
||||||
{subdomain: "97-8-ro", region: "Romania", group: "NoSpy TCP Europe"},
|
|
||||||
{subdomain: "97-1-ro", region: "Romania", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-ru", region: "Russian Federation", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-ru", region: "Russian Federation", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "97-1-sa", region: "Saudi Arabia", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-sa", region: "Saudi Arabia", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-rs", region: "Serbia", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-rs", region: "Serbia", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "95-1-sg", region: "Singapore", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "96-1-sg", region: "Singapore", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "87-1-sk", region: "Slovakia", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-sk", region: "Slovakia", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-si", region: "Slovenia", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-si", region: "Slovenia", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-za", region: "South Africa", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "95-1-za", region: "South Africa", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "97-1-za", region: "South Africa", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "96-1-za", region: "South Africa", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "97-1-es", region: "Spain", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-es", region: "Spain", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-lk", region: "Sri Lanka", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-lk", region: "Sri Lanka", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-se", region: "Sweden", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-se", region: "Sweden", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "87-1-ch", region: "Switzerland", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-ch", region: "Switzerland", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "96-1-tw", region: "Taiwan", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "95-1-tw", region: "Taiwan", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "96-1-th", region: "Thailand", group: "Premium TCP Asia"},
|
|
||||||
{subdomain: "95-1-th", region: "Thailand", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "87-1-tr", region: "Turkey", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-tr", region: "Turkey", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "97-1-ua", region: "Ukraine", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-ua", region: "Ukraine", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "87-1-ae", region: "United Arab Emirates", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-ae", region: "United Arab Emirates", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "97-1-gb", region: "United Kingdom", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "87-1-gb", region: "United Kingdom", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "94-1-us", region: "United States", group: "Premium UDP USA"},
|
|
||||||
{subdomain: "93-1-us", region: "United States", group: "Premium TCP USA"},
|
|
||||||
{subdomain: "87-1-ve", region: "Venezuela", group: "Premium UDP Europe"},
|
|
||||||
{subdomain: "97-1-ve", region: "Venezuela", group: "Premium TCP Europe"},
|
|
||||||
{subdomain: "95-1-vn", region: "Vietnam", group: "Premium UDP Asia"},
|
|
||||||
{subdomain: "96-1-vn", region: "Vietnam", group: "Premium TCP Asia"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func vyprvpnServers() []server {
|
|
||||||
return []server{
|
|
||||||
{subdomain: "ae1", region: "Dubai"},
|
|
||||||
{subdomain: "ar1", region: "Argentina"},
|
|
||||||
{subdomain: "at1", region: "Austria"},
|
|
||||||
{subdomain: "au1", region: "Australia Sydney"},
|
|
||||||
{subdomain: "au2", region: "Australia Melbourne"},
|
|
||||||
{subdomain: "au3", region: "Australia Perth"},
|
|
||||||
{subdomain: "be1", region: "Belgium"},
|
|
||||||
{subdomain: "bg1", region: "Bulgaria"},
|
|
||||||
{subdomain: "bh1", region: "Bahrain"},
|
|
||||||
{subdomain: "br1", region: "Brazil"},
|
|
||||||
{subdomain: "ca1", region: "Canada"},
|
|
||||||
{subdomain: "ch1", region: "Switzerland"},
|
|
||||||
{subdomain: "co1", region: "Columbia"},
|
|
||||||
{subdomain: "cr1", region: "Costa Rica"},
|
|
||||||
{subdomain: "cz1", region: "Czech Republic"},
|
|
||||||
{subdomain: "de1", region: "Germany"},
|
|
||||||
{subdomain: "dk1", region: "Denmark"},
|
|
||||||
{subdomain: "dz1", region: "Algeria"},
|
|
||||||
{subdomain: "eg1", region: "Egypt"},
|
|
||||||
{subdomain: "es1", region: "Spain"},
|
|
||||||
{subdomain: "eu1", region: "Netherlands"},
|
|
||||||
{subdomain: "fi1", region: "Finland"},
|
|
||||||
{subdomain: "fr1", region: "France"},
|
|
||||||
{subdomain: "gr1", region: "Greece"},
|
|
||||||
{subdomain: "hk1", region: "Hong Kong"},
|
|
||||||
{subdomain: "id1", region: "Indonesia"},
|
|
||||||
{subdomain: "ie1", region: "Ireland"},
|
|
||||||
{subdomain: "il1", region: "Israel"},
|
|
||||||
{subdomain: "in1", region: "India"},
|
|
||||||
{subdomain: "is1", region: "Iceland"},
|
|
||||||
{subdomain: "it1", region: "Italy"},
|
|
||||||
{subdomain: "jp1", region: "Japan"},
|
|
||||||
{subdomain: "kr1", region: "South Korea"},
|
|
||||||
{subdomain: "li1", region: "Liechtenstein"},
|
|
||||||
{subdomain: "lt1", region: "Lithuania"},
|
|
||||||
{subdomain: "lu1", region: "Luxembourg"},
|
|
||||||
{subdomain: "lv1", region: "Latvia"},
|
|
||||||
{subdomain: "mh1", region: "Marshall Islands"},
|
|
||||||
{subdomain: "mo1", region: "Macao"},
|
|
||||||
{subdomain: "mv1", region: "Maldives"},
|
|
||||||
{subdomain: "mx1", region: "Mexico"},
|
|
||||||
{subdomain: "my1", region: "Malaysia"},
|
|
||||||
{subdomain: "no1", region: "Norway"},
|
|
||||||
{subdomain: "nz1", region: "New Zealand"},
|
|
||||||
{subdomain: "pa1", region: "Panama"},
|
|
||||||
{subdomain: "ph1", region: "Philippines"},
|
|
||||||
{subdomain: "pk1", region: "Pakistan"},
|
|
||||||
{subdomain: "pl1", region: "Poland"},
|
|
||||||
{subdomain: "pt1", region: "Portugal"},
|
|
||||||
{subdomain: "qa1", region: "Qatar"},
|
|
||||||
{subdomain: "ro1", region: "Romania"},
|
|
||||||
{subdomain: "ru1", region: "Russia"},
|
|
||||||
{subdomain: "sa1", region: "Saudi Arabia"},
|
|
||||||
{subdomain: "se1", region: "Sweden"},
|
|
||||||
{subdomain: "sg1", region: "Singapore"},
|
|
||||||
{subdomain: "si1", region: "Slovenia"},
|
|
||||||
{subdomain: "sk1", region: "Slovakia"},
|
|
||||||
{subdomain: "sv1", region: "El Salvador"},
|
|
||||||
{subdomain: "th1", region: "Thailand"},
|
|
||||||
{subdomain: "tr1", region: "Turkey"},
|
|
||||||
{subdomain: "tw1", region: "Taiwan"},
|
|
||||||
{subdomain: "ua1", region: "Ukraine"},
|
|
||||||
{subdomain: "uk1", region: "United Kingdom"},
|
|
||||||
{subdomain: "us1", region: "USA Los Angeles"},
|
|
||||||
{subdomain: "us2", region: "USA Washington DC"},
|
|
||||||
{subdomain: "us3", region: "USA Austin"},
|
|
||||||
{subdomain: "us4", region: "USA Miami"},
|
|
||||||
{subdomain: "us5", region: "USA New York"},
|
|
||||||
{subdomain: "us6", region: "USA Chicago"},
|
|
||||||
{subdomain: "us7", region: "USA San Francisco"},
|
|
||||||
{subdomain: "us8", region: "USA Seattle"},
|
|
||||||
{subdomain: "uy1", region: "Uruguay"},
|
|
||||||
{subdomain: "vn1", region: "Vietnam"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1720
doc/logo.svg
Normal file
1720
doc/logo.svg
Normal file
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 62 KiB |
BIN
doc/logo_256.png
Normal file
BIN
doc/logo_256.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
doc/paypal.jpg
BIN
doc/paypal.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 5.6 KiB |
BIN
doc/sponsors.jpg
BIN
doc/sponsors.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
@@ -1,39 +1,24 @@
|
|||||||
version: "3.7"
|
version: "3.7"
|
||||||
services:
|
services:
|
||||||
gluetun:
|
gluetun:
|
||||||
image: qmcgaw/private-internet-access
|
image: qmcgaw/gluetun
|
||||||
container_name: gluetun
|
container_name: gluetun
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
network_mode: bridge
|
network_mode: bridge
|
||||||
ports:
|
ports:
|
||||||
- 8888:8888/tcp # Tinyproxy
|
- 8888:8888/tcp # HTTP proxy
|
||||||
- 8388:8388/tcp # Shadowsocks
|
- 8388:8388/tcp # Shadowsocks
|
||||||
- 8388:8388/udp # Shadowsocks
|
- 8388:8388/udp # Shadowsocks
|
||||||
- 8000:8000/tcp # Built-in HTTP control server
|
- 8000:8000/tcp # Built-in HTTP control server
|
||||||
# command:
|
# command:
|
||||||
|
volumes:
|
||||||
|
- /yourpath:/gluetun
|
||||||
environment:
|
environment:
|
||||||
# More variables are available, see the readme table
|
# More variables are available, see the readme table
|
||||||
|
- OPENVPN_USER=
|
||||||
|
- OPENVPN_PASSWORD=
|
||||||
- VPNSP=private internet access
|
- VPNSP=private internet access
|
||||||
|
|
||||||
# Timezone for accurate logs times
|
# Timezone for accurate logs times
|
||||||
- TZ=
|
- TZ=
|
||||||
|
|
||||||
# All VPN providers
|
|
||||||
- USER=js89ds7
|
|
||||||
|
|
||||||
# All VPN providers but Mullvad
|
|
||||||
- PASSWORD=8fd9s239G
|
|
||||||
|
|
||||||
# Cyberghost only
|
|
||||||
- CLIENT_KEY=
|
|
||||||
|
|
||||||
# All VPN providers but Mullvad
|
|
||||||
- REGION=Austria
|
|
||||||
|
|
||||||
# Mullvad only
|
|
||||||
- COUNTRY=Sweden
|
|
||||||
|
|
||||||
# Allow for example your LAN, set to: 192.168.1.0/24
|
|
||||||
- EXTRA_SUBNETS=
|
|
||||||
restart: always
|
restart: always
|
||||||
|
|||||||
21
go.mod
21
go.mod
@@ -1,12 +1,17 @@
|
|||||||
module github.com/qdm12/private-internet-access-docker
|
module github.com/qdm12/gluetun
|
||||||
|
|
||||||
go 1.14
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fatih/color v1.9.0
|
github.com/fatih/color v1.12.0
|
||||||
github.com/golang/mock v1.4.3
|
github.com/golang/mock v1.6.0
|
||||||
github.com/kyokomi/emoji v2.2.4+incompatible
|
github.com/qdm12/dns v1.9.0
|
||||||
github.com/qdm12/golibs v0.0.0-20200712151944-a0325873bf5a
|
github.com/qdm12/golibs v0.0.0-20210716185557-66793f4ddd80
|
||||||
github.com/stretchr/testify v1.6.1
|
github.com/qdm12/goshutdown v0.1.0
|
||||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae
|
github.com/qdm12/ss-server v0.2.0
|
||||||
|
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
|
||||||
|
github.com/stretchr/testify v1.7.0
|
||||||
|
github.com/vishvananda/netlink v1.1.0
|
||||||
|
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015
|
||||||
|
inet.af/netaddr v0.0.0-20210511181906-37180328850c
|
||||||
)
|
)
|
||||||
|
|||||||
184
go.sum
184
go.sum
@@ -1,134 +1,164 @@
|
|||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
|
|
||||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco=
|
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||||
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.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/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb h1:D4uzjWwKYQ5XnAvUbuvHW93esHg7F8N/OYeBBcJoTr0=
|
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
||||||
|
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
|
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
||||||
github.com/go-openapi/analysis v0.17.0 h1:8JV+dzJJiK46XqGLqqLav8ZfEiJECp8jlOFhpiCdZ+0=
|
|
||||||
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||||
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||||
github.com/go-openapi/errors v0.17.2 h1:azEQ8Fnx0jmtFF2fxsnmd6I0x6rsweUF63qqSO1NmKk=
|
|
||||||
github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||||
github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=
|
|
||||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||||
github.com/go-openapi/jsonreference v0.17.0 h1:yJW3HCkTHg7NOA+gZ83IPHzUSnUzGXhGmsdiCcMexbA=
|
|
||||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||||
github.com/go-openapi/loads v0.17.0 h1:H22nMs3GDQk4SwAaFQ+jLNw+0xoFeCueawhZlv8MBYs=
|
|
||||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
|
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
|
||||||
github.com/go-openapi/runtime v0.17.2 h1:/ZK67ikFhQAMFFH/aPu2MaGH7QjP4wHBvHYOVIzDAw0=
|
|
||||||
github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q=
|
github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q=
|
||||||
github.com/go-openapi/spec v0.17.0 h1:XNvrt8FlSVP8T1WuhbAFF6QDhJc0zsoWzX4wXARhhpE=
|
|
||||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||||
github.com/go-openapi/strfmt v0.17.0 h1:1isAxYf//QDTnVzbLAMrUK++0k1EjeLJU/gTOR0o3Mc=
|
|
||||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||||
github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=
|
|
||||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||||
github.com/go-openapi/validate v0.17.0 h1:pqoViQz3YLOGIhAmD0N4Lt6pa/3Gnj3ymKqQwq8iS6U=
|
|
||||||
github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||||
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
|
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gotify/go-api-client/v2 v2.0.4 h1:0w8skCr8aLBDKaQDg31LKKHUGF7rt7zdRpR+6cqIAlE=
|
|
||||||
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
|
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
|
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 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/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 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
|
|
||||||
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 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
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-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
|
||||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/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/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee h1:P6U24L02WMfj9ymZTxl7CxS73JC99x3ukk+DBkgQGQs=
|
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 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
|
||||||
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/golibs v0.0.0-20200712151944-a0325873bf5a h1:IyS72qFm+iXipadmUKXmpJScKXXK2GrD8yYfxXsnIYs=
|
github.com/qdm12/dns v1.9.0 h1:p4g/BfbpQ+gJRpQdklDAnybkjds+OuenF0wEGoZ8/AI=
|
||||||
github.com/qdm12/golibs v0.0.0-20200712151944-a0325873bf5a/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
|
github.com/qdm12/dns v1.9.0/go.mod h1:fqZoDf3VzddnKBMNI/OzZUp5H4dO0VBw1fp4qPkolOg=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/qdm12/golibs v0.0.0-20210603202746-e5494e9c2ebb/go.mod h1:15RBzkun0i8XB7ADIoLJWp9ITRgsz3LroEI2FiOXLRg=
|
||||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
github.com/qdm12/golibs v0.0.0-20210716185557-66793f4ddd80 h1:rvH2MSs8RXEfuXivzoYCim6tRNPzdqjBzqJq8w4Tc0k=
|
||||||
|
github.com/qdm12/golibs v0.0.0-20210716185557-66793f4ddd80/go.mod h1:15RBzkun0i8XB7ADIoLJWp9ITRgsz3LroEI2FiOXLRg=
|
||||||
|
github.com/qdm12/goshutdown v0.1.0 h1:lmwnygdXtnr2pa6VqfR/bm8077/BnBef1+7CP96B7Sw=
|
||||||
|
github.com/qdm12/goshutdown v0.1.0/go.mod h1:/LP3MWLqI+wGH/ijfaUG+RHzBbKXIiVKnrg5vXOCf6Q=
|
||||||
|
github.com/qdm12/ss-server v0.2.0 h1:+togLzeeLAJ68MD1JqOWvYi9rl9t/fx1Qh7wKzZhY1g=
|
||||||
|
github.com/qdm12/ss-server v0.2.0/go.mod h1:+1bWO1EfWNvsGM5Cuep6vneChK2OHniqtAsED9Fh1y0=
|
||||||
|
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e h1:4q+uFLawkaQRq3yARYLsjJPZd2wYwxn4g6G/5v0xW1g=
|
||||||
|
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e/go.mod h1:UvJRGkZ9XL3/D7e7JiTTVLm1F3Cymd3/gFpD6frEpBo=
|
||||||
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||||
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||||
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
|
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
|
||||||
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 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
|
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
|
||||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||||
go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
|
||||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
||||||
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg=
|
||||||
|
go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 h1:1tk03FUNpulq2cuWpXZWj649rwJpk0d20rxWiopKRmc=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
|
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-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-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
|
||||||
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-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo=
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E=
|
||||||
|
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/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-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=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 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-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||||
|
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
|
||||||
|
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.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 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
inet.af/netaddr v0.0.0-20210511181906-37180328850c h1:rzDy/tC8LjEdN94+i0Bu22tTo/qE9cvhKyfD0HMU0NU=
|
||||||
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=
|
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
|
||||||
|
|||||||
@@ -1,25 +1,26 @@
|
|||||||
|
// Package alpine defines a configurator to interact with the Alpine operating system.
|
||||||
package alpine
|
package alpine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os/user"
|
"context"
|
||||||
|
|
||||||
"github.com/qdm12/golibs/files"
|
"github.com/qdm12/golibs/os"
|
||||||
|
"github.com/qdm12/golibs/os/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Configurator interface {
|
type Configurator interface {
|
||||||
CreateUser(username string, uid int) error
|
CreateUser(username string, uid int) (createdUsername string, err error)
|
||||||
|
Version(ctx context.Context) (version string, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type configurator struct {
|
type configurator struct {
|
||||||
fileManager files.FileManager
|
openFile os.OpenFileFunc
|
||||||
lookupUID func(uid string) (*user.User, error)
|
osUser user.OSUser
|
||||||
lookupUser func(username string) (*user.User, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfigurator(fileManager files.FileManager) Configurator {
|
func NewConfigurator(openFile os.OpenFileFunc, osUser user.OSUser) Configurator {
|
||||||
return &configurator{
|
return &configurator{
|
||||||
fileManager: fileManager,
|
openFile: openFile,
|
||||||
lookupUID: user.LookupId,
|
osUser: osUser,
|
||||||
lookupUser: user.Lookup,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,54 @@
|
|||||||
package alpine
|
package alpine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateUser creates a user in Alpine with the given UID
|
var (
|
||||||
func (c *configurator) CreateUser(username string, uid int) error {
|
ErrUserAlreadyExists = errors.New("user already exists")
|
||||||
UIDStr := fmt.Sprintf("%d", uid)
|
)
|
||||||
u, err := c.lookupUID(UIDStr)
|
|
||||||
|
// CreateUser creates a user in Alpine with the given UID.
|
||||||
|
func (c *configurator) CreateUser(username string, uid int) (createdUsername string, err error) {
|
||||||
|
UIDStr := strconv.Itoa(uid)
|
||||||
|
u, err := c.osUser.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 fmt.Errorf("user with ID %d exists with username %q instead of %q", uid, u.Username, username)
|
return u.Username, nil
|
||||||
}
|
}
|
||||||
u, err = c.lookupUser(username)
|
|
||||||
|
u, err = c.osUser.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)
|
|
||||||
}
|
}
|
||||||
passwd, err := c.fileManager.ReadFile("/etc/passwd")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot create user: %w", err)
|
|
||||||
}
|
|
||||||
passwd = append(passwd, []byte(fmt.Sprintf("%s:x:%d:::/dev/null:/sbin/nologin\n", username, uid))...)
|
|
||||||
|
|
||||||
if err := c.fileManager.WriteToFile("/etc/passwd", passwd); err != nil {
|
if u != nil {
|
||||||
return fmt.Errorf("cannot create user: %w", err)
|
return "", fmt.Errorf("%w: with name %s for ID %s instead of %d",
|
||||||
|
ErrUserAlreadyExists, username, u.Uid, uid)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
file, err := c.openFile("/etc/passwd", os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
s := fmt.Sprintf("%s:x:%d:::/dev/null:/sbin/nologin\n", username, uid)
|
||||||
|
_, err = file.WriteString(s)
|
||||||
|
if err != nil {
|
||||||
|
_ = file.Close()
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return username, file.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
27
internal/alpine/version.go
Normal file
27
internal/alpine/version.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package alpine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *configurator) Version(ctx context.Context) (version string, err error) {
|
||||||
|
file, err := c.openFile("/etc/alpine-release", os.O_RDONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := file.Close(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
version = strings.ReplaceAll(string(b), "\n", "")
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
7
internal/cli/ci.go
Normal file
7
internal/cli/ci.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
func (c *cli) CI(context context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,74 +1,22 @@
|
|||||||
|
// Package cli defines an interface CLI to run command line operations.
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"context"
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/qdm12/golibs/files"
|
|
||||||
"github.com/qdm12/golibs/logging"
|
"github.com/qdm12/golibs/logging"
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/params"
|
"github.com/qdm12/golibs/os"
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/provider"
|
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/settings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ClientKey(args []string) error {
|
type CLI interface {
|
||||||
flagSet := flag.NewFlagSet("clientkey", flag.ExitOnError)
|
ClientKey(args []string, openFile os.OpenFileFunc) error
|
||||||
filepath := flagSet.String("path", "/files/client.key", "file path to the client.key file")
|
HealthCheck(ctx context.Context) error
|
||||||
if err := flagSet.Parse(args); err != nil {
|
OpenvpnConfig(os os.OS, logger logging.Logger) error
|
||||||
return err
|
Update(ctx context.Context, args []string, os os.OS, logger logging.Logger) error
|
||||||
}
|
|
||||||
fileManager := files.NewFileManager()
|
|
||||||
data, err := fileManager.ReadFile(*filepath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s := string(data)
|
|
||||||
s = strings.ReplaceAll(s, "\n", "")
|
|
||||||
s = strings.ReplaceAll(s, "\r", "")
|
|
||||||
s = strings.TrimPrefix(s, "-----BEGIN PRIVATE KEY-----")
|
|
||||||
s = strings.TrimSuffix(s, "-----END PRIVATE KEY-----")
|
|
||||||
fmt.Println(s)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func HealthCheck() error {
|
type cli struct{}
|
||||||
ips, err := net.LookupIP("github.com")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot resolve github.com (%s)", err)
|
|
||||||
} else if len(ips) == 0 {
|
|
||||||
return fmt.Errorf("resolved no IP addresses for github.com")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func OpenvpnConfig() error {
|
func New() CLI {
|
||||||
logger, err := logging.NewLogger(logging.ConsoleEncoding, logging.InfoLevel, -1)
|
return &cli{}
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
paramsReader := params.NewReader(logger, files.NewFileManager())
|
|
||||||
allSettings, err := settings.GetAllSettings(paramsReader)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
providerConf := provider.New(allSettings.OpenVPN.Provider.Name)
|
|
||||||
connections, err := providerConf.GetOpenVPNConnections(allSettings.OpenVPN.Provider.ServerSelection)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
lines := providerConf.BuildConf(
|
|
||||||
connections,
|
|
||||||
allSettings.OpenVPN.Verbosity,
|
|
||||||
allSettings.System.UID,
|
|
||||||
allSettings.System.GID,
|
|
||||||
allSettings.OpenVPN.Root,
|
|
||||||
allSettings.OpenVPN.Cipher,
|
|
||||||
allSettings.OpenVPN.Auth,
|
|
||||||
allSettings.OpenVPN.Provider.ExtraConfigOptions,
|
|
||||||
)
|
|
||||||
fmt.Println(strings.Join(lines, "\n"))
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
41
internal/cli/clientkey.go
Normal file
41
internal/cli/clientkey.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *cli) ClientKey(args []string, openFile os.OpenFileFunc) error {
|
||||||
|
flagSet := flag.NewFlagSet("clientkey", flag.ExitOnError)
|
||||||
|
filepath := flagSet.String("path", constants.ClientKey, "file path to the client.key file")
|
||||||
|
if err := flagSet.Parse(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
file, err := openFile(*filepath, os.O_RDONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
_ = file.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s := string(data)
|
||||||
|
s = strings.ReplaceAll(s, "\n", "")
|
||||||
|
s = strings.ReplaceAll(s, "\r", "")
|
||||||
|
s = strings.TrimPrefix(s, "-----BEGIN PRIVATE KEY-----")
|
||||||
|
s = strings.TrimSuffix(s, "-----END PRIVATE KEY-----")
|
||||||
|
fmt.Println(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
20
internal/cli/healthcheck.go
Normal file
20
internal/cli/healthcheck.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/gluetun/internal/healthcheck"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *cli) HealthCheck(ctx context.Context) error {
|
||||||
|
const timeout = 10 * time.Second
|
||||||
|
httpClient := &http.Client{Timeout: timeout}
|
||||||
|
healthchecker := healthcheck.NewChecker(httpClient)
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||||
|
defer cancel()
|
||||||
|
const url = "http://" + constants.HealthcheckAddress
|
||||||
|
return healthchecker.Check(ctx, url)
|
||||||
|
}
|
||||||
36
internal/cli/openvpnconfig.go
Normal file
36
internal/cli/openvpnconfig.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/gluetun/internal/provider"
|
||||||
|
"github.com/qdm12/gluetun/internal/storage"
|
||||||
|
"github.com/qdm12/golibs/logging"
|
||||||
|
"github.com/qdm12/golibs/os"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *cli) OpenvpnConfig(os os.OS, logger logging.Logger) error {
|
||||||
|
var allSettings configuration.Settings
|
||||||
|
err := allSettings.Read(params.NewEnv(), os, logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
allServers, err := storage.New(logger, os, constants.ServersData).
|
||||||
|
SyncServers(constants.GetAllServers())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
providerConf := provider.New(allSettings.OpenVPN.Provider.Name, allServers, time.Now)
|
||||||
|
connection, err := providerConf.GetOpenVPNConnection(allSettings.OpenVPN.Provider.ServerSelection)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lines := providerConf.BuildConf(connection, "nonroortuser", allSettings.OpenVPN)
|
||||||
|
fmt.Println(strings.Join(lines, "\n"))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
76
internal/cli/update.go
Normal file
76
internal/cli/update.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/gluetun/internal/storage"
|
||||||
|
"github.com/qdm12/gluetun/internal/updater"
|
||||||
|
"github.com/qdm12/golibs/logging"
|
||||||
|
"github.com/qdm12/golibs/os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNoFileOrStdoutFlag = errors.New("at least one of -file or -stdout must be specified")
|
||||||
|
ErrSyncServers = errors.New("cannot sync hardcoded and persisted servers")
|
||||||
|
ErrUpdateServerInformation = errors.New("cannot update server information")
|
||||||
|
ErrWriteToFile = errors.New("cannot write updated information to file")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *cli) Update(ctx context.Context, args []string, os os.OS, logger logging.Logger) error {
|
||||||
|
options := configuration.Updater{CLI: true}
|
||||||
|
var flushToFile bool
|
||||||
|
flagSet := flag.NewFlagSet("update", flag.ExitOnError)
|
||||||
|
flagSet.BoolVar(&flushToFile, "file", 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.StringVar(&options.DNSAddress, "dns", "8.8.8.8", "DNS resolver address to use")
|
||||||
|
flagSet.BoolVar(&options.Cyberghost, "cyberghost", false, "Update Cyberghost servers")
|
||||||
|
flagSet.BoolVar(&options.Fastestvpn, "fastestvpn", false, "Update FastestVPN servers")
|
||||||
|
flagSet.BoolVar(&options.HideMyAss, "hidemyass", false, "Update HideMyAss servers")
|
||||||
|
flagSet.BoolVar(&options.Ipvanish, "ipvanish", false, "Update IpVanish servers")
|
||||||
|
flagSet.BoolVar(&options.Ivpn, "ivpn", false, "Update IVPN servers")
|
||||||
|
flagSet.BoolVar(&options.Mullvad, "mullvad", false, "Update Mullvad servers")
|
||||||
|
flagSet.BoolVar(&options.Nordvpn, "nordvpn", false, "Update Nordvpn servers")
|
||||||
|
flagSet.BoolVar(&options.PIA, "pia", false, "Update Private Internet Access post-summer 2020 servers")
|
||||||
|
flagSet.BoolVar(&options.Privado, "privado", false, "Update Privado servers")
|
||||||
|
flagSet.BoolVar(&options.Privatevpn, "privatevpn", false, "Update Private VPN servers")
|
||||||
|
flagSet.BoolVar(&options.Protonvpn, "protonvpn", false, "Update Protonvpn servers")
|
||||||
|
flagSet.BoolVar(&options.Purevpn, "purevpn", false, "Update Purevpn servers")
|
||||||
|
flagSet.BoolVar(&options.Surfshark, "surfshark", false, "Update Surfshark servers")
|
||||||
|
flagSet.BoolVar(&options.Torguard, "torguard", false, "Update Torguard servers")
|
||||||
|
flagSet.BoolVar(&options.VPNUnlimited, "vpnunlimited", false, "Update VPN Unlimited servers")
|
||||||
|
flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers")
|
||||||
|
flagSet.BoolVar(&options.Windscribe, "windscribe", false, "Update Windscribe servers")
|
||||||
|
if err := flagSet.Parse(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !flushToFile && !options.Stdout {
|
||||||
|
return ErrNoFileOrStdoutFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientTimeout = 10 * time.Second
|
||||||
|
httpClient := &http.Client{Timeout: clientTimeout}
|
||||||
|
storage := storage.New(logger, os, constants.ServersData)
|
||||||
|
currentServers, err := storage.SyncServers(constants.GetAllServers())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrSyncServers, err)
|
||||||
|
}
|
||||||
|
updater := updater.New(options, httpClient, currentServers, logger)
|
||||||
|
allServers, err := updater.UpdateServers(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrUpdateServerInformation, err)
|
||||||
|
}
|
||||||
|
if flushToFile {
|
||||||
|
if err := storage.FlushToFile(allServers); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrWriteToFile, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
3
internal/configuration/configuration.go
Normal file
3
internal/configuration/configuration.go
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// Package configuration reads initial settings from environment variables
|
||||||
|
// and secret files.
|
||||||
|
package configuration
|
||||||
6
internal/configuration/constants.go
Normal file
6
internal/configuration/constants.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
const (
|
||||||
|
lastIndent = "|--"
|
||||||
|
indent = " "
|
||||||
|
)
|
||||||
70
internal/configuration/cyberghost.go
Normal file
70
internal/configuration/cyberghost.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) cyberghostLines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+"Server group: "+settings.ServerSelection.Group)
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.ExtraConfigOptions.ClientKey != "" {
|
||||||
|
lines = append(lines, lastIndent+"Client key is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.ExtraConfigOptions.ClientCertificate != "" {
|
||||||
|
lines = append(lines, lastIndent+"Client certificate is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readCyberghost(r reader) (err error) {
|
||||||
|
settings.Name = constants.Cyberghost
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ExtraConfigOptions.ClientKey, err = readClientKey(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ExtraConfigOptions.ClientCertificate, err = readClientCertificate(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Group, err = r.env.Inside("CYBERGHOST_GROUP",
|
||||||
|
constants.CyberghostGroupChoices(), params.Default("Premium UDP Europe"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.CyberghostRegionChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.CyberghostHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
117
internal/configuration/dns.go
Normal file
117
internal/configuration/dns.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/dns/pkg/blacklist"
|
||||||
|
"github.com/qdm12/dns/pkg/unbound"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DNS contains settings to configure Unbound for DNS over TLS operation.
|
||||||
|
type DNS struct { //nolint:maligned
|
||||||
|
Enabled bool
|
||||||
|
PlaintextAddress net.IP
|
||||||
|
KeepNameserver bool
|
||||||
|
UpdatePeriod time.Duration
|
||||||
|
Unbound unbound.Settings
|
||||||
|
BlacklistBuild blacklist.BuilderSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *DNS) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *DNS) lines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+"DNS:")
|
||||||
|
|
||||||
|
if settings.PlaintextAddress != nil {
|
||||||
|
lines = append(lines, indent+lastIndent+"Plaintext address: "+settings.PlaintextAddress.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.KeepNameserver {
|
||||||
|
lines = append(lines, indent+lastIndent+"Keep nameserver (disabled blocking): yes")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !settings.Enabled {
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"DNS over TLS:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+indent+lastIndent+"Unbound:")
|
||||||
|
for _, line := range settings.Unbound.Lines() {
|
||||||
|
lines = append(lines, indent+indent+indent+line)
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, indent+indent+lastIndent+"Blacklist:")
|
||||||
|
for _, line := range settings.BlacklistBuild.Lines(indent, lastIndent) {
|
||||||
|
lines = append(lines, indent+indent+indent+line)
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.UpdatePeriod > 0 {
|
||||||
|
lines = append(lines, indent+indent+lastIndent+"Update: every "+settings.UpdatePeriod.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnboundSettings = errors.New("failed getting Unbound settings")
|
||||||
|
ErrBlacklistSettings = errors.New("failed getting DNS blacklist settings")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *DNS) read(r reader) (err error) {
|
||||||
|
settings.Enabled, err = r.env.OnOff("DOT", params.Default("on"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plain DNS settings
|
||||||
|
if err := settings.readDNSPlaintext(r.env); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settings.KeepNameserver, err = r.env.OnOff("DNS_KEEP_NAMESERVER", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNS over TLS external settings
|
||||||
|
if err := settings.readBlacklistBuilding(r); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrBlacklistSettings, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.UpdatePeriod, err = r.env.Duration("DNS_UPDATE_PERIOD", params.Default("24h"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unbound settings
|
||||||
|
if err := settings.readUnbound(r); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrUnboundSettings, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrDNSAddressNotAnIP = errors.New("DNS plaintext address is not an IP address")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *DNS) readDNSPlaintext(env params.Env) error {
|
||||||
|
s, err := env.Get("DNS_PLAINTEXT_ADDRESS", params.Default("1.1.1.1"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.PlaintextAddress = net.ParseIP(s)
|
||||||
|
if settings.PlaintextAddress == nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrDNSAddressNotAnIP, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
76
internal/configuration/dns_test.go
Normal file
76
internal/configuration/dns_test.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/dns/pkg/blacklist"
|
||||||
|
"github.com/qdm12/dns/pkg/provider"
|
||||||
|
"github.com/qdm12/dns/pkg/unbound"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_DNS_Lines(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
testCases := map[string]struct {
|
||||||
|
settings DNS
|
||||||
|
lines []string
|
||||||
|
}{
|
||||||
|
"disabled DOT": {
|
||||||
|
settings: DNS{
|
||||||
|
PlaintextAddress: net.IP{1, 1, 1, 1},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--DNS:",
|
||||||
|
" |--Plaintext address: 1.1.1.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"enabled DOT": {
|
||||||
|
settings: DNS{
|
||||||
|
Enabled: true,
|
||||||
|
KeepNameserver: true,
|
||||||
|
Unbound: unbound.Settings{
|
||||||
|
Providers: []provider.Provider{
|
||||||
|
provider.Cloudflare(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BlacklistBuild: blacklist.BuilderSettings{
|
||||||
|
BlockMalicious: true,
|
||||||
|
BlockAds: true,
|
||||||
|
BlockSurveillance: true,
|
||||||
|
},
|
||||||
|
UpdatePeriod: time.Hour,
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--DNS:",
|
||||||
|
" |--Keep nameserver (disabled blocking): yes",
|
||||||
|
" |--DNS over TLS:",
|
||||||
|
" |--Unbound:",
|
||||||
|
" |--DNS over TLS providers:",
|
||||||
|
" |--Cloudflare",
|
||||||
|
" |--Listening port: 0",
|
||||||
|
" |--Access control:",
|
||||||
|
" |--Allowed:",
|
||||||
|
" |--Caching: disabled",
|
||||||
|
" |--IPv4 resolution: disabled",
|
||||||
|
" |--IPv6 resolution: disabled",
|
||||||
|
" |--Verbosity level: 0/5",
|
||||||
|
" |--Verbosity details level: 0/4",
|
||||||
|
" |--Validation log level: 0/2",
|
||||||
|
" |--Username: ",
|
||||||
|
" |--Blacklist:",
|
||||||
|
" |--Blocked categories: malicious, surveillance, ads",
|
||||||
|
" |--Update: every 1h0m0s",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
lines := testCase.settings.lines()
|
||||||
|
assert.Equal(t, testCase.lines, lines)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
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 err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.BlacklistBuild.BlockSurveillance, err = r.env.OnOff("BLOCK_SURVEILLANCE", params.Default("on"),
|
||||||
|
params.RetroKeys([]string{"BLOCK_NSA"}, r.onRetroActive))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.BlacklistBuild.BlockAds, err = r.env.OnOff("BLOCK_ADS", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.readPrivateAddresses(r.env); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings.readBlacklistUnblockedHostnames(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidPrivateAddress = errors.New("private address is not a valid IP or CIDR range")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *DNS) readPrivateAddresses(env params.Env) (err error) {
|
||||||
|
privateAddresses, err := env.CSV("DOT_PRIVATE_ADDRESS")
|
||||||
|
if err != nil {
|
||||||
|
return 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 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
|
||||||
|
}
|
||||||
43
internal/configuration/fastestvpn.go
Normal file
43
internal/configuration/fastestvpn.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) fastestvpnLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Countries) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readFastestvpn(r reader) (err error) {
|
||||||
|
settings.Name = constants.Fastestvpn
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.FastestvpnHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.FastestvpnCountriesChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
89
internal/configuration/firewall.go
Normal file
89
internal/configuration/firewall.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Firewall contains settings to customize the firewall operation.
|
||||||
|
type Firewall struct {
|
||||||
|
VPNInputPorts []uint16
|
||||||
|
InputPorts []uint16
|
||||||
|
OutboundSubnets []net.IPNet
|
||||||
|
Enabled bool
|
||||||
|
Debug bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Firewall) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Firewall) lines() (lines []string) {
|
||||||
|
if !settings.Enabled {
|
||||||
|
lines = append(lines, lastIndent+"Firewall: disabled ⚠️")
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, lastIndent+"Firewall:")
|
||||||
|
|
||||||
|
if settings.Debug {
|
||||||
|
lines = append(lines, indent+lastIndent+"Debug: on")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.VPNInputPorts) > 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"VPN input ports: "+
|
||||||
|
strings.Join(uint16sToStrings(settings.VPNInputPorts), ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.InputPorts) > 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Input ports: "+
|
||||||
|
strings.Join(uint16sToStrings(settings.InputPorts), ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.OutboundSubnets) > 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Outbound subnets: "+
|
||||||
|
strings.Join(ipNetsToStrings(settings.OutboundSubnets), ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Firewall) read(r reader) (err error) {
|
||||||
|
settings.Enabled, err = r.env.OnOff("FIREWALL", params.Default("on"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Debug, err = r.env.OnOff("FIREWALL_DEBUG", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.readVPNInputPorts(r.env); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.readInputPorts(r.env); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings.readOutboundSubnets(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Firewall) readVPNInputPorts(env params.Env) (err error) {
|
||||||
|
settings.VPNInputPorts, err = readCSVPorts(env, "FIREWALL_VPN_INPUT_PORTS")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Firewall) readInputPorts(env params.Env) (err error) {
|
||||||
|
settings.InputPorts, err = readCSVPorts(env, "FIREWALL_INPUT_PORTS")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Firewall) readOutboundSubnets(r reader) (err error) {
|
||||||
|
retroOption := params.RetroKeys([]string{"EXTRA_SUBNETS"}, r.onRetroActive)
|
||||||
|
settings.OutboundSubnets, err = readCSVIPNets(r.env, "FIREWALL_OUTBOUND_SUBNETS", retroOption)
|
||||||
|
return err
|
||||||
|
}
|
||||||
61
internal/configuration/hidemyass.go
Normal file
61
internal/configuration/hidemyass.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) hideMyAssLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Countries) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readHideMyAss(r reader) (err error) {
|
||||||
|
settings.Name = constants.HideMyAss
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.HideMyAssCountryChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.HideMyAssCountryChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.HideMyAssCityChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.HideMyAssHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
105
internal/configuration/httpproxy.go
Normal file
105
internal/configuration/httpproxy.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPProxy contains settings to configure the HTTP proxy.
|
||||||
|
type HTTPProxy struct {
|
||||||
|
User string
|
||||||
|
Password string
|
||||||
|
Port uint16
|
||||||
|
Enabled bool
|
||||||
|
Stealth bool
|
||||||
|
Log bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *HTTPProxy) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *HTTPProxy) lines() (lines []string) {
|
||||||
|
if !settings.Enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, lastIndent+"HTTP proxy:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Port: "+strconv.Itoa(int(settings.Port)))
|
||||||
|
|
||||||
|
if settings.User != "" {
|
||||||
|
lines = append(lines, indent+lastIndent+"Authentication: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Log {
|
||||||
|
lines = append(lines, indent+lastIndent+"Log: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Stealth {
|
||||||
|
lines = append(lines, indent+lastIndent+"Stealth: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *HTTPProxy) read(r reader) (err error) {
|
||||||
|
settings.Enabled, err = r.env.OnOff("HTTPPROXY", params.Default("off"),
|
||||||
|
params.RetroKeys([]string{"TINYPROXY", "PROXY"}, r.onRetroActive))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.User, err = r.getFromEnvOrSecretFile("HTTPPROXY_USER", false, // compulsory
|
||||||
|
[]string{"TINYPROXY_USER", "PROXY_USER"})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Password, err = r.getFromEnvOrSecretFile("HTTPPROXY_PASSWORD", false,
|
||||||
|
[]string{"TINYPROXY_PASSWORD", "PROXY_PASSWORD"})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Stealth, err = r.env.OnOff("HTTPPROXY_STEALTH", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.readLog(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var warning string
|
||||||
|
settings.Port, warning, err = r.env.ListeningPort("HTTPPROXY_PORT", params.Default("8888"),
|
||||||
|
params.RetroKeys([]string{"TINYPROXY_PORT", "PROXY_PORT"}, r.onRetroActive))
|
||||||
|
if len(warning) > 0 {
|
||||||
|
r.logger.Warn(warning)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *HTTPProxy) readLog(r reader) error {
|
||||||
|
s, err := r.env.Get("HTTPPROXY_LOG",
|
||||||
|
params.RetroKeys([]string{"PROXY_LOG_LEVEL", "TINYPROXY_LOG"}, r.onRetroActive))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch strings.ToLower(s) {
|
||||||
|
case "on":
|
||||||
|
settings.Log = true
|
||||||
|
// Retro compatibility
|
||||||
|
case "info", "connect", "notice":
|
||||||
|
settings.Log = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
52
internal/configuration/ipvanish.go
Normal file
52
internal/configuration/ipvanish.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) ipvanishLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Countries) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readIpvanish(r reader) (err error) {
|
||||||
|
settings.Name = constants.Ipvanish
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IpvanishCountryChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IpvanishCityChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
192
internal/configuration/ipvanish_test.go
Normal file
192
internal/configuration/ipvanish_test.go
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params/mock_params"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Provider_ipvanishLines(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
settings Provider
|
||||||
|
lines []string
|
||||||
|
}{
|
||||||
|
"empty settings": {},
|
||||||
|
"full settings": {
|
||||||
|
settings: Provider{
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Countries: []string{"A", "B"},
|
||||||
|
Cities: []string{"C", "D"},
|
||||||
|
Hostnames: []string{"E", "F"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Countries: A, B",
|
||||||
|
"|--Cities: C, D",
|
||||||
|
"|--Hostnames: E, F",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
lines := testCase.settings.ipvanishLines()
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.lines, lines)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Provider_readIpvanish(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var errDummy = errors.New("dummy test error")
|
||||||
|
|
||||||
|
type singleStringCall struct {
|
||||||
|
call bool
|
||||||
|
value string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliceStringCall struct {
|
||||||
|
call bool
|
||||||
|
values []string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
protocol singleStringCall
|
||||||
|
targetIP singleStringCall
|
||||||
|
countries sliceStringCall
|
||||||
|
cities sliceStringCall
|
||||||
|
hostnames sliceStringCall
|
||||||
|
settings Provider
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
"protocol error": {
|
||||||
|
protocol: singleStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ipvanish,
|
||||||
|
},
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
"target IP error": {
|
||||||
|
protocol: singleStringCall{call: true},
|
||||||
|
targetIP: singleStringCall{call: true, value: "something", err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ipvanish,
|
||||||
|
},
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
"countries error": {
|
||||||
|
protocol: singleStringCall{call: true},
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ipvanish,
|
||||||
|
},
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
"cities error": {
|
||||||
|
protocol: singleStringCall{call: true},
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ipvanish,
|
||||||
|
},
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
"hostnames error": {
|
||||||
|
protocol: singleStringCall{call: true},
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ipvanish,
|
||||||
|
},
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
"default settings": {
|
||||||
|
protocol: singleStringCall{call: true},
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ipvanish,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"set settings": {
|
||||||
|
protocol: singleStringCall{call: true, value: constants.TCP},
|
||||||
|
targetIP: singleStringCall{call: true, value: "1.2.3.4"},
|
||||||
|
countries: sliceStringCall{call: true, values: []string{"A", "B"}},
|
||||||
|
cities: sliceStringCall{call: true, values: []string{"C", "D"}},
|
||||||
|
hostnames: sliceStringCall{call: true, values: []string{"E", "F"}},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ipvanish,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
TCP: true,
|
||||||
|
TargetIP: net.IPv4(1, 2, 3, 4),
|
||||||
|
Countries: []string{"A", "B"},
|
||||||
|
Cities: []string{"C", "D"},
|
||||||
|
Hostnames: []string{"E", "F"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
|
||||||
|
env := mock_params.NewMockEnv(ctrl)
|
||||||
|
if testCase.protocol.call {
|
||||||
|
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
|
||||||
|
Return(testCase.protocol.value, testCase.protocol.err)
|
||||||
|
}
|
||||||
|
if testCase.targetIP.call {
|
||||||
|
env.EXPECT().Get("OPENVPN_TARGET_IP").
|
||||||
|
Return(testCase.targetIP.value, testCase.targetIP.err)
|
||||||
|
}
|
||||||
|
if testCase.countries.call {
|
||||||
|
env.EXPECT().CSVInside("COUNTRY", constants.IpvanishCountryChoices()).
|
||||||
|
Return(testCase.countries.values, testCase.countries.err)
|
||||||
|
}
|
||||||
|
if testCase.cities.call {
|
||||||
|
env.EXPECT().CSVInside("CITY", constants.IpvanishCityChoices()).
|
||||||
|
Return(testCase.cities.values, testCase.cities.err)
|
||||||
|
}
|
||||||
|
if testCase.hostnames.call {
|
||||||
|
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices()).
|
||||||
|
Return(testCase.hostnames.values, testCase.hostnames.err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := reader{env: env}
|
||||||
|
|
||||||
|
var settings Provider
|
||||||
|
err := settings.readIpvanish(r)
|
||||||
|
|
||||||
|
if testCase.err != nil {
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Equal(t, testCase.err.Error(), err.Error())
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.settings, settings)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
52
internal/configuration/ivpn.go
Normal file
52
internal/configuration/ivpn.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) ivpnLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Countries) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readIvpn(r reader) (err error) {
|
||||||
|
settings.Name = constants.Ivpn
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IvpnCountryChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IvpnCityChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
192
internal/configuration/ivpn_test.go
Normal file
192
internal/configuration/ivpn_test.go
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params/mock_params"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Provider_ivpnLines(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
settings Provider
|
||||||
|
lines []string
|
||||||
|
}{
|
||||||
|
"empty settings": {},
|
||||||
|
"full settings": {
|
||||||
|
settings: Provider{
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Countries: []string{"A", "B"},
|
||||||
|
Cities: []string{"C", "D"},
|
||||||
|
Hostnames: []string{"E", "F"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Countries: A, B",
|
||||||
|
"|--Cities: C, D",
|
||||||
|
"|--Hostnames: E, F",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
lines := testCase.settings.ivpnLines()
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.lines, lines)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Provider_readIvpn(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var errDummy = errors.New("dummy test error")
|
||||||
|
|
||||||
|
type singleStringCall struct {
|
||||||
|
call bool
|
||||||
|
value string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliceStringCall struct {
|
||||||
|
call bool
|
||||||
|
values []string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
protocol singleStringCall
|
||||||
|
targetIP singleStringCall
|
||||||
|
countries sliceStringCall
|
||||||
|
cities sliceStringCall
|
||||||
|
hostnames sliceStringCall
|
||||||
|
settings Provider
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
"protocol error": {
|
||||||
|
protocol: singleStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
},
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
"target IP error": {
|
||||||
|
protocol: singleStringCall{call: true},
|
||||||
|
targetIP: singleStringCall{call: true, value: "something", err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
},
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
"countries error": {
|
||||||
|
protocol: singleStringCall{call: true},
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
},
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
"cities error": {
|
||||||
|
protocol: singleStringCall{call: true},
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
},
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
"hostnames error": {
|
||||||
|
protocol: singleStringCall{call: true},
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true, err: errDummy},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
},
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
"default settings": {
|
||||||
|
protocol: singleStringCall{call: true},
|
||||||
|
targetIP: singleStringCall{call: true},
|
||||||
|
countries: sliceStringCall{call: true},
|
||||||
|
cities: sliceStringCall{call: true},
|
||||||
|
hostnames: sliceStringCall{call: true},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"set settings": {
|
||||||
|
protocol: singleStringCall{call: true, value: constants.TCP},
|
||||||
|
targetIP: singleStringCall{call: true, value: "1.2.3.4"},
|
||||||
|
countries: sliceStringCall{call: true, values: []string{"A", "B"}},
|
||||||
|
cities: sliceStringCall{call: true, values: []string{"C", "D"}},
|
||||||
|
hostnames: sliceStringCall{call: true, values: []string{"E", "F"}},
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
TCP: true,
|
||||||
|
TargetIP: net.IPv4(1, 2, 3, 4),
|
||||||
|
Countries: []string{"A", "B"},
|
||||||
|
Cities: []string{"C", "D"},
|
||||||
|
Hostnames: []string{"E", "F"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
|
||||||
|
env := mock_params.NewMockEnv(ctrl)
|
||||||
|
if testCase.protocol.call {
|
||||||
|
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
|
||||||
|
Return(testCase.protocol.value, testCase.protocol.err)
|
||||||
|
}
|
||||||
|
if testCase.targetIP.call {
|
||||||
|
env.EXPECT().Get("OPENVPN_TARGET_IP").
|
||||||
|
Return(testCase.targetIP.value, testCase.targetIP.err)
|
||||||
|
}
|
||||||
|
if testCase.countries.call {
|
||||||
|
env.EXPECT().CSVInside("COUNTRY", constants.IvpnCountryChoices()).
|
||||||
|
Return(testCase.countries.values, testCase.countries.err)
|
||||||
|
}
|
||||||
|
if testCase.cities.call {
|
||||||
|
env.EXPECT().CSVInside("CITY", constants.IvpnCityChoices()).
|
||||||
|
Return(testCase.cities.values, testCase.cities.err)
|
||||||
|
}
|
||||||
|
if testCase.hostnames.call {
|
||||||
|
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices()).
|
||||||
|
Return(testCase.hostnames.values, testCase.hostnames.err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := reader{env: env}
|
||||||
|
|
||||||
|
var settings Provider
|
||||||
|
err := settings.readIvpn(r)
|
||||||
|
|
||||||
|
if testCase.err != nil {
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Equal(t, testCase.err.Error(), err.Error())
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.settings, settings)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
55
internal/configuration/keys.go
Normal file
55
internal/configuration/keys.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func readClientKey(r reader) (clientKey string, err error) {
|
||||||
|
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTKEY", constants.ClientKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return extractClientKey(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
var errDecodePEMBlockClientKey = errors.New("cannot decode PEM block from client key")
|
||||||
|
|
||||||
|
func extractClientKey(b []byte) (key string, err error) {
|
||||||
|
pemBlock, _ := pem.Decode(b)
|
||||||
|
if pemBlock == nil {
|
||||||
|
return "", errDecodePEMBlockClientKey
|
||||||
|
}
|
||||||
|
parsedBytes := pem.EncodeToMemory(pemBlock)
|
||||||
|
s := string(parsedBytes)
|
||||||
|
s = strings.ReplaceAll(s, "\n", "")
|
||||||
|
s = strings.TrimPrefix(s, "-----BEGIN PRIVATE KEY-----")
|
||||||
|
s = strings.TrimSuffix(s, "-----END PRIVATE KEY-----")
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readClientCertificate(r reader) (clientCertificate string, err error) {
|
||||||
|
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTCRT", constants.ClientCertificate)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return extractClientCertificate(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
var errDecodePEMBlockClientCert = errors.New("cannot decode PEM block from client certificate")
|
||||||
|
|
||||||
|
func extractClientCertificate(b []byte) (certificate string, err error) {
|
||||||
|
pemBlock, _ := pem.Decode(b)
|
||||||
|
if pemBlock == nil {
|
||||||
|
return "", errDecodePEMBlockClientCert
|
||||||
|
}
|
||||||
|
parsedBytes := pem.EncodeToMemory(pemBlock)
|
||||||
|
s := string(parsedBytes)
|
||||||
|
s = strings.ReplaceAll(s, "\n", "")
|
||||||
|
s = strings.TrimPrefix(s, "-----BEGIN CERTIFICATE-----")
|
||||||
|
s = strings.TrimSuffix(s, "-----END CERTIFICATE-----")
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
174
internal/configuration/keys_test.go
Normal file
174
internal/configuration/keys_test.go
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_extractClientKey(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
const validPEM = `
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
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: errDecodePEMBlockClientKey,
|
||||||
|
},
|
||||||
|
"bad input": {
|
||||||
|
b: []byte{1, 2, 3},
|
||||||
|
err: errDecodePEMBlockClientKey,
|
||||||
|
},
|
||||||
|
"valid key": {
|
||||||
|
b: []byte(validPEM),
|
||||||
|
key: validKeyString,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
key, err := extractClientKey(testCase.b)
|
||||||
|
if testCase.err != nil {
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Equal(t, testCase.err.Error(), err.Error())
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, testCase.key, key)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_extractClientCertificate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
const validPEM = `
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIGrDCCBJSgAwIBAgIEAdTnfTANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJS
|
||||||
|
TzESMBAGA1UEBxMJQnVjaGFyZXN0MRgwFgYDVQQKEw9DeWJlckdob3N0IFMuQS4x
|
||||||
|
GzAZBgNVBAMTEkN5YmVyR2hvc3QgUm9vdCBDQTEhMB8GCSqGSIb3DQEJARYSaW5m
|
||||||
|
b0BjeWJlcmdob3N0LnJvMB4XDTIwMDcwNDE1MjkzNloXDTMwMDcwMjE1MjkzNlow
|
||||||
|
fTELMAkGA1UEBhMCUk8xEjAQBgNVBAcMCUJ1Y2hhcmVzdDEYMBYGA1UECgwPQ3li
|
||||||
|
ZXJHaG9zdCBTLkEuMR0wGwYDVQQDDBRjLmoua2xhdmVyQGdtYWlsLmNvbTEhMB8G
|
||||||
|
CSqGSIb3DQEJARYSaW5mb0BjeWJlcmdob3N0LnJvMIICIjANBgkqhkiG9w0BAQEF
|
||||||
|
AAOCAg8AMIICCgKCAgEAobp2NlGUHMNBe08YEOnVG3QJjF3ZaXbRhE/II9rmtgJT
|
||||||
|
NZtDohGChvFlNRsExKzVrKxHCeuJkVffwzQ6fYk4/M1RdYLJUh0UVw3e4WdApw8E
|
||||||
|
7TJZxDYm4SHQNXUvt1Rt5TjslcXxIpDZgrMSc/kHROYEL9tdgdzPZErUJehXyJPh
|
||||||
|
EzIrzmAJh501x7WwKPz9ctSVlItyavqEWFF2vyUa6X9DYmD9mQTz5c+VXNO5DkXm
|
||||||
|
PFBIaEVDnvFtcjGJ56yEvFnWVukL+OUX7ezowrIOFOcp9udjgpeiHq+XvsQ6ER0D
|
||||||
|
Jt25MiEId3NjkxtZ8BitDftTcLN/kt81hWKT7adMVc3kpIZ80cxrwRCttMd7sHAz
|
||||||
|
KI9u7pMxv10eUOsIEY87ewBe3l6KvEnjA+9uIjim6gLLebDIaEH50Ee9PzNJ8fqQ
|
||||||
|
2u54Ab4bt00/H1sUnJ6Ss/+WsQDOK1BsPRKKcnHZntOlHrs2Tu5+txKNU2cOapI8
|
||||||
|
SjVULUNKrRXASbpfWnLUfri/HO742bJb/TjkOJcOxta3hTPFAhaRWBusVlB41XVH
|
||||||
|
euH5DAhugYXeSNK6/6Ul8YvKUNH/7QbxuGIGXfth19Xl4QLI1umyEjZopSlt3tOi
|
||||||
|
O2V1soVNSQCCfxXVoCTMESMLjhkjWdmBDhdy2GTW7S4YoJfqVKiS18rYkN7I4ZMC
|
||||||
|
AwEAAaOCATQwggEwMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMDQGCWCGSAGG+EIB
|
||||||
|
DQQnFiVDeWJlckdob3N0IEdlbmVyYXRlZCBVc2VyIENlcnRpZmljYXRlMBEGCWCG
|
||||||
|
SAGG+EIBAQQEAwIHgDAdBgNVHQ4EFgQULwUtU5s6pL2NN9gPeEnKX0dhwiswga0G
|
||||||
|
A1UdIwSBpTCBooAU6tdK1g/He5qzjeAoM5eHt4in9iWhf6R9MHsxCzAJBgNVBAYT
|
||||||
|
AlJPMRIwEAYDVQQHEwlCdWNoYXJlc3QxGDAWBgNVBAoTD0N5YmVyR2hvc3QgUy5B
|
||||||
|
LjEbMBkGA1UEAxMSQ3liZXJHaG9zdCBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJp
|
||||||
|
bmZvQGN5YmVyZ2hvc3Qucm+CCQCcVButZsQ0uzANBgkqhkiG9w0BAQsFAAOCAgEA
|
||||||
|
ystGIMYhQWaEdTqlnLCytrr8657t+PuidZMNNIaPB3wN2Fi2xKf14DTg03mqxjmP
|
||||||
|
Pb+f+PVNIOV5PdWD4jcQwOP1GEboGV0DFzlRGeAtDcvKwdee4oASJbZq1CETqDao
|
||||||
|
hQTxKEWC+UBk2F36nOaEI6Sab+Mb4cR9//PAwvzOqrXuGF5NuIOX7eFtCMQSgQq6
|
||||||
|
lRRqTQjekm0Dxigx4JA92Jo2qZRwCJ0T3IXBJGL831HCFJbDWv8PV3lsfFb/i2+v
|
||||||
|
r54uywFQVWWp18dYi97gipfuQ4zRg2Ldx5aXSmnhhKpg5ioZvtk043QofF12YORh
|
||||||
|
obElqavRbvvhZvlCouvcuoq9QKi7IPe5SJZkZ1X7ezMesCwBzwFpt6vRUAcslsNF
|
||||||
|
bcYS1iSENlY/PTcDqBhbKuc9yAhq+/aUgaY/8VF5RWVzSRZufbf3BPwOkE4K0Uyb
|
||||||
|
aobO/YX0JOkCacAD+4tdR6YSXNIMMRAOCBQvxbxFXaHzhwhzBAjdsC56FrJKwXvQ
|
||||||
|
rRLU3tF4P0zFMeNTay8uTtUXugDK7EnklLESuYdpUJ8bUMlAUhJBi6UFI9/icMud
|
||||||
|
xXvLRvhnBW9EtKib5JnVFUovcEUt+3EJbyst05nkL4YPjQS4TC9DHdo5SyRAy1Tp
|
||||||
|
iOCYTbretAFZRhh6ycUN5hBeN8GMQxiMreMtDV4PEIQ=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
`
|
||||||
|
const validCertificateString = "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: errDecodePEMBlockClientCert,
|
||||||
|
},
|
||||||
|
"bad input": {
|
||||||
|
b: []byte{1, 2, 3},
|
||||||
|
err: errDecodePEMBlockClientCert,
|
||||||
|
},
|
||||||
|
"valid key": {
|
||||||
|
b: []byte(validPEM),
|
||||||
|
certificate: validCertificateString,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
certificate, err := extractClientCertificate(testCase.b)
|
||||||
|
if testCase.err != nil {
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Equal(t, testCase.err.Error(), err.Error())
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, testCase.certificate, certificate)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
22
internal/configuration/lines.go
Normal file
22
internal/configuration/lines.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func uint16sToStrings(uint16s []uint16) (strings []string) {
|
||||||
|
strings = make([]string, len(uint16s))
|
||||||
|
for i := range uint16s {
|
||||||
|
strings[i] = strconv.Itoa(int(uint16s[i]))
|
||||||
|
}
|
||||||
|
return strings
|
||||||
|
}
|
||||||
|
|
||||||
|
func ipNetsToStrings(ipNets []net.IPNet) (strings []string) {
|
||||||
|
strings = make([]string, len(ipNets))
|
||||||
|
for i := range ipNets {
|
||||||
|
strings[i] = ipNets[i].String()
|
||||||
|
}
|
||||||
|
return strings
|
||||||
|
}
|
||||||
88
internal/configuration/mullvad.go
Normal file
88
internal/configuration/mullvad.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) mullvadLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Countries) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.ISPs) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"ISPs: "+commaJoin(settings.ServerSelection.ISPs))
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.ServerSelection.CustomPort > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.ExtraConfigOptions.OpenVPNIPv6 {
|
||||||
|
lines = append(lines, lastIndent+"IPv6: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readMullvad(r reader) (err error) {
|
||||||
|
settings.Name = constants.Mullvad
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.MullvadCountryChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.MullvadCityChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.MullvadHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.MullvadISPChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.CustomPort, err = readCustomPort(r.env, settings.ServerSelection.TCP,
|
||||||
|
[]uint16{80, 443, 1401}, []uint16{53, 1194, 1195, 1196, 1197, 1300, 1301, 1302, 1303, 1400})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Owned, err = r.env.YesNo("OWNED", params.Default("no"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ExtraConfigOptions.OpenVPNIPv6, err = r.env.OnOff("OPENVPN_IPV6", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
91
internal/configuration/nordvpn.go
Normal file
91
internal/configuration/nordvpn.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) nordvpnLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Names) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Names: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
if numbersUint16 := settings.ServerSelection.Numbers; len(numbersUint16) > 0 {
|
||||||
|
numbersString := make([]string, len(numbersUint16))
|
||||||
|
for i, numberUint16 := range numbersUint16 {
|
||||||
|
numbersString[i] = strconv.Itoa(int(numberUint16))
|
||||||
|
}
|
||||||
|
lines = append(lines, lastIndent+"Numbers: "+commaJoin(numbersString))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readNordvpn(r reader) (err error) {
|
||||||
|
settings.Name = constants.Nordvpn
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.NordvpnRegionChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.NordvpnHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.NordvpnHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Numbers, err = readNordVPNServerNumbers(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readNordVPNServerNumbers(env params.Env) (numbers []uint16, err error) {
|
||||||
|
const possiblePortsCount = 65537
|
||||||
|
possibilities := make([]string, possiblePortsCount)
|
||||||
|
for i := range possibilities {
|
||||||
|
possibilities[i] = fmt.Sprintf("%d", i)
|
||||||
|
}
|
||||||
|
possibilities[65536] = ""
|
||||||
|
values, err := env.CSVInside("SERVER_NUMBER", possibilities)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
numbers = make([]uint16, len(values))
|
||||||
|
for i := range values {
|
||||||
|
n, err := strconv.Atoi(values[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
numbers[i] = uint16(n)
|
||||||
|
}
|
||||||
|
return numbers, nil
|
||||||
|
}
|
||||||
190
internal/configuration/openvpn.go
Normal file
190
internal/configuration/openvpn.go
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OpenVPN contains settings to configure the OpenVPN client.
|
||||||
|
type OpenVPN struct {
|
||||||
|
User string `json:"user"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Verbosity int `json:"verbosity"`
|
||||||
|
MSSFix uint16 `json:"mssfix"`
|
||||||
|
Root bool `json:"run_as_root"`
|
||||||
|
Cipher string `json:"cipher"`
|
||||||
|
Auth string `json:"auth"`
|
||||||
|
Provider Provider `json:"provider"`
|
||||||
|
Config string `json:"custom_config"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPN) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPN) lines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+"OpenVPN:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Version: "+settings.Version)
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Verbosity level: "+strconv.Itoa(settings.Verbosity))
|
||||||
|
|
||||||
|
if settings.Root {
|
||||||
|
lines = append(lines, indent+lastIndent+"Run as root: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.Cipher) > 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Custom cipher: "+settings.Cipher)
|
||||||
|
}
|
||||||
|
if len(settings.Auth) > 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Custom auth algorithm: "+settings.Auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.Config) > 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Custom configuration: "+settings.Config)
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Provider.Name == "" {
|
||||||
|
lines = append(lines, indent+lastIndent+"Provider: custom configuration")
|
||||||
|
} else {
|
||||||
|
lines = append(lines, indent+lastIndent+"Provider:")
|
||||||
|
for _, line := range settings.Provider.lines() {
|
||||||
|
lines = append(lines, indent+indent+line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidVPNProvider = errors.New("invalid VPN provider")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *OpenVPN) read(r reader) (err error) {
|
||||||
|
vpnsp, err := r.env.Inside("VPNSP", []string{
|
||||||
|
"cyberghost", "fastestvpn", "hidemyass", "ipvanish", "ivpn", "mullvad", "nordvpn",
|
||||||
|
"privado", "pia", "private internet access", "privatevpn", "protonvpn",
|
||||||
|
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn", "windscribe"},
|
||||||
|
params.Default("private internet access"))
|
||||||
|
if err != nil {
|
||||||
|
return 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
|
||||||
|
}
|
||||||
|
customConfig := settings.Config != ""
|
||||||
|
|
||||||
|
if customConfig {
|
||||||
|
settings.Provider.Name = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
credentialsRequired := !customConfig && settings.Provider.Name != constants.VPNUnlimited
|
||||||
|
|
||||||
|
settings.User, err = r.getFromEnvOrSecretFile("OPENVPN_USER", credentialsRequired, []string{"USER"})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Remove spaces in user ID to simplify user's life, thanks @JeordyR
|
||||||
|
settings.User = strings.ReplaceAll(settings.User, " ", "")
|
||||||
|
|
||||||
|
if settings.Provider.Name == constants.Mullvad {
|
||||||
|
settings.Password = "m"
|
||||||
|
} else {
|
||||||
|
settings.Password, err = r.getFromEnvOrSecretFile("OPENVPN_PASSWORD", credentialsRequired, []string{"PASSWORD"})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Version, err = r.env.Inside("OPENVPN_VERSION",
|
||||||
|
[]string{constants.Openvpn24, constants.Openvpn25}, params.Default(constants.Openvpn25))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Verbosity, err = r.env.IntRange("OPENVPN_VERBOSITY", 0, 6, params.Default("1")) //nolint:gomnd
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Root, err = r.env.YesNo("OPENVPN_ROOT", params.Default("yes"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Cipher, err = r.env.Get("OPENVPN_CIPHER")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Auth, err = r.env.Get("OPENVPN_AUTH")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxMSSFix = 10000
|
||||||
|
mssFix, err := r.env.IntRange("OPENVPN_MSSFIX", 0, maxMSSFix, params.Default("0"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settings.MSSFix = uint16(mssFix)
|
||||||
|
return settings.readProvider(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPN) readProvider(r reader) error {
|
||||||
|
var readProvider func(r reader) error
|
||||||
|
switch settings.Provider.Name {
|
||||||
|
case "": // custom config
|
||||||
|
readProvider = func(r reader) error { return nil }
|
||||||
|
case constants.Cyberghost:
|
||||||
|
readProvider = settings.Provider.readCyberghost
|
||||||
|
case constants.Fastestvpn:
|
||||||
|
readProvider = settings.Provider.readFastestvpn
|
||||||
|
case constants.HideMyAss:
|
||||||
|
readProvider = settings.Provider.readHideMyAss
|
||||||
|
case constants.Ipvanish:
|
||||||
|
readProvider = settings.Provider.readIpvanish
|
||||||
|
case constants.Ivpn:
|
||||||
|
readProvider = settings.Provider.readIvpn
|
||||||
|
case constants.Mullvad:
|
||||||
|
readProvider = settings.Provider.readMullvad
|
||||||
|
case constants.Nordvpn:
|
||||||
|
readProvider = settings.Provider.readNordvpn
|
||||||
|
case constants.Privado:
|
||||||
|
readProvider = settings.Provider.readPrivado
|
||||||
|
case constants.PrivateInternetAccess:
|
||||||
|
readProvider = settings.Provider.readPrivateInternetAccess
|
||||||
|
case constants.Privatevpn:
|
||||||
|
readProvider = settings.Provider.readPrivatevpn
|
||||||
|
case constants.Protonvpn:
|
||||||
|
readProvider = settings.Provider.readProtonvpn
|
||||||
|
case constants.Purevpn:
|
||||||
|
readProvider = settings.Provider.readPurevpn
|
||||||
|
case constants.Surfshark:
|
||||||
|
readProvider = settings.Provider.readSurfshark
|
||||||
|
case constants.Torguard:
|
||||||
|
readProvider = settings.Provider.readTorguard
|
||||||
|
case constants.VPNUnlimited:
|
||||||
|
readProvider = settings.Provider.readVPNUnlimited
|
||||||
|
case constants.Vyprvpn:
|
||||||
|
readProvider = settings.Provider.readVyprvpn
|
||||||
|
case constants.Windscribe:
|
||||||
|
readProvider = settings.Provider.readWindscribe
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%w: %s", ErrInvalidVPNProvider, settings.Provider.Name)
|
||||||
|
}
|
||||||
|
return readProvider(r)
|
||||||
|
}
|
||||||
63
internal/configuration/openvpn_test.go
Normal file
63
internal/configuration/openvpn_test.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_OpenVPN_JSON(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
in := OpenVPN{
|
||||||
|
Root: true,
|
||||||
|
Provider: Provider{
|
||||||
|
Name: "name",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
data, err := json.MarshalIndent(in, "", " ")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, `{
|
||||||
|
"user": "",
|
||||||
|
"password": "",
|
||||||
|
"verbosity": 0,
|
||||||
|
"mssfix": 0,
|
||||||
|
"run_as_root": true,
|
||||||
|
"cipher": "",
|
||||||
|
"auth": "",
|
||||||
|
"provider": {
|
||||||
|
"name": "name",
|
||||||
|
"server_selection": {
|
||||||
|
"tcp": false,
|
||||||
|
"regions": null,
|
||||||
|
"group": "",
|
||||||
|
"countries": null,
|
||||||
|
"cities": null,
|
||||||
|
"hostnames": null,
|
||||||
|
"names": null,
|
||||||
|
"isps": null,
|
||||||
|
"owned": false,
|
||||||
|
"custom_port": 0,
|
||||||
|
"numbers": null,
|
||||||
|
"encryption_preset": "",
|
||||||
|
"free_only": false,
|
||||||
|
"stream_only": false
|
||||||
|
},
|
||||||
|
"extra_config": {
|
||||||
|
"encryption_preset": "",
|
||||||
|
"openvpn_ipv6": false
|
||||||
|
},
|
||||||
|
"port_forwarding": {
|
||||||
|
"enabled": false,
|
||||||
|
"filepath": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"custom_config": "",
|
||||||
|
"version": ""
|
||||||
|
}`, string(data))
|
||||||
|
var out OpenVPN
|
||||||
|
err = json.Unmarshal(data, &out)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, in, out)
|
||||||
|
}
|
||||||
61
internal/configuration/privado.go
Normal file
61
internal/configuration/privado.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) privadoLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Countries) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readPrivado(r reader) (err error) {
|
||||||
|
settings.Name = constants.Privado
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivadoCountryChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PrivadoRegionChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivadoCityChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivadoHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
97
internal/configuration/privateinternetaccess.go
Normal file
97
internal/configuration/privateinternetaccess.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) privateinternetaccessLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Names) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Names: "+commaJoin(settings.ServerSelection.Names))
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, lastIndent+"Encryption preset: "+settings.ServerSelection.EncryptionPreset)
|
||||||
|
|
||||||
|
if settings.ServerSelection.CustomPort > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.PortForwarding.Enabled {
|
||||||
|
lines = append(lines, lastIndent+"Port forwarding:")
|
||||||
|
for _, line := range settings.PortForwarding.lines() {
|
||||||
|
lines = append(lines, indent+line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readPrivateInternetAccess(r reader) (err error) {
|
||||||
|
settings.Name = constants.PrivateInternetAccess
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptionPreset, err := r.env.Inside("PIA_ENCRYPTION",
|
||||||
|
[]string{constants.PIAEncryptionPresetNone, constants.PIAEncryptionPresetNormal, constants.PIAEncryptionPresetStrong},
|
||||||
|
params.RetroKeys([]string{"ENCRYPTION"}, r.onRetroActive),
|
||||||
|
params.Default(constants.PIACertificateStrong),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return 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.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PIAHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_NAME", constants.PIANameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.CustomPort, err = readPortOrZero(r.env, "PORT")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.PortForwarding.Enabled, err = r.env.OnOff("PORT_FORWARDING", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.PortForwarding.Enabled {
|
||||||
|
settings.PortForwarding.Filepath, err = r.env.Path("PORT_FORWARDING_STATUS_FILE",
|
||||||
|
params.Default("/tmp/gluetun/forwarded_port"), params.CaseSensitiveValue())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
52
internal/configuration/privatevpn.go
Normal file
52
internal/configuration/privatevpn.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) privatevpnLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Countries) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readPrivatevpn(r reader) (err error) {
|
||||||
|
settings.Name = constants.Privatevpn
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivatevpnCountryChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivatevpnCityChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivatevpnHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
85
internal/configuration/protonvpn.go
Normal file
85
internal/configuration/protonvpn.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) protonvpnLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Countries) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Names) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Names: "+commaJoin(settings.ServerSelection.Names))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.ServerSelection.FreeOnly {
|
||||||
|
lines = append(lines, lastIndent+"Free only: yes")
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readProtonvpn(r reader) (err error) {
|
||||||
|
settings.Name = constants.Protonvpn
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.CustomPort, err = readPortOrZero(r.env, "PORT")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.ProtonvpnCountryChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.ProtonvpnRegionChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.ProtonvpnCityChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.ProtonvpnNameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.ProtonvpnHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.FreeOnly, err = r.env.YesNo("FREE_ONLY", params.Default("no"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
125
internal/configuration/provider.go
Normal file
125
internal/configuration/provider.go
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Provider contains settings specific to a VPN provider.
|
||||||
|
type Provider struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ServerSelection ServerSelection `json:"server_selection"`
|
||||||
|
ExtraConfigOptions ExtraConfigOptions `json:"extra_config"`
|
||||||
|
PortForwarding PortForwarding `json:"port_forwarding"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) lines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+strings.Title(settings.Name)+" settings:")
|
||||||
|
|
||||||
|
selection := settings.ServerSelection
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Network protocol: "+protoToString(selection.TCP))
|
||||||
|
|
||||||
|
if selection.TargetIP != nil {
|
||||||
|
lines = append(lines, indent+lastIndent+"Target IP address: "+selection.TargetIP.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
var providerLines []string
|
||||||
|
switch strings.ToLower(settings.Name) {
|
||||||
|
case "cyberghost":
|
||||||
|
providerLines = settings.cyberghostLines()
|
||||||
|
case "fastestvpn":
|
||||||
|
providerLines = settings.fastestvpnLines()
|
||||||
|
case "hidemyass":
|
||||||
|
providerLines = settings.hideMyAssLines()
|
||||||
|
case "ipvanish":
|
||||||
|
providerLines = settings.ipvanishLines()
|
||||||
|
case "ivpn":
|
||||||
|
providerLines = settings.ivpnLines()
|
||||||
|
case "mullvad":
|
||||||
|
providerLines = settings.mullvadLines()
|
||||||
|
case "nordvpn":
|
||||||
|
providerLines = settings.nordvpnLines()
|
||||||
|
case "privado":
|
||||||
|
providerLines = settings.privadoLines()
|
||||||
|
case "privatevpn":
|
||||||
|
providerLines = settings.privatevpnLines()
|
||||||
|
case "private internet access":
|
||||||
|
providerLines = settings.privateinternetaccessLines()
|
||||||
|
case "protonvpn":
|
||||||
|
providerLines = settings.protonvpnLines()
|
||||||
|
case "purevpn":
|
||||||
|
providerLines = settings.purevpnLines()
|
||||||
|
case "surfshark":
|
||||||
|
providerLines = settings.surfsharkLines()
|
||||||
|
case "torguard":
|
||||||
|
providerLines = settings.torguardLines()
|
||||||
|
case strings.ToLower(constants.VPNUnlimited):
|
||||||
|
providerLines = settings.vpnUnlimitedLines()
|
||||||
|
case "vyprvpn":
|
||||||
|
providerLines = settings.vyprvpnLines()
|
||||||
|
case "windscribe":
|
||||||
|
providerLines = settings.windscribeLines()
|
||||||
|
default:
|
||||||
|
panic(`Missing lines method for provider "` +
|
||||||
|
settings.Name + `"! Please create a Github issue.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range providerLines {
|
||||||
|
lines = append(lines, indent+line)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func commaJoin(slice []string) string {
|
||||||
|
return strings.Join(slice, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func readProtocol(env params.Env) (tcp bool, err error) {
|
||||||
|
protocol, err := env.Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, params.Default(constants.UDP))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return protocol == constants.TCP, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func protoToString(tcp bool) string {
|
||||||
|
if tcp {
|
||||||
|
return constants.TCP
|
||||||
|
}
|
||||||
|
return constants.UDP
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTargetIP(env params.Env) (targetIP net.IP, err error) {
|
||||||
|
return readIP(env, "OPENVPN_TARGET_IP")
|
||||||
|
}
|
||||||
|
|
||||||
|
func readCustomPort(env params.Env, tcp bool,
|
||||||
|
allowedTCP, allowedUDP []uint16) (port uint16, err error) {
|
||||||
|
port, err = readPortOrZero(env, "PORT")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else if port == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if tcp {
|
||||||
|
for i := range allowedTCP {
|
||||||
|
if allowedTCP[i] == port {
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("%w: port %d for TCP protocol", ErrInvalidPort, port)
|
||||||
|
}
|
||||||
|
for i := range allowedUDP {
|
||||||
|
if allowedUDP[i] == port {
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("%w: port %d for UDP protocol", ErrInvalidPort, port)
|
||||||
|
}
|
||||||
382
internal/configuration/provider_test.go
Normal file
382
internal/configuration/provider_test.go
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params/mock_params"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errDummy = errors.New("dummy")
|
||||||
|
|
||||||
|
func Test_Provider_lines(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
settings Provider
|
||||||
|
lines []string
|
||||||
|
}{
|
||||||
|
"cyberghost": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Cyberghost,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Group: "group",
|
||||||
|
Regions: []string{"a", "El country"},
|
||||||
|
},
|
||||||
|
ExtraConfigOptions: ExtraConfigOptions{
|
||||||
|
ClientKey: "a",
|
||||||
|
ClientCertificate: "a",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Cyberghost settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Server group: group",
|
||||||
|
" |--Regions: a, El country",
|
||||||
|
" |--Client key is set",
|
||||||
|
" |--Client certificate is set",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"fastestvpn": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Fastestvpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Hostnames: []string{"a", "b"},
|
||||||
|
Countries: []string{"c", "d"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Fastestvpn settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Hostnames: a, b",
|
||||||
|
" |--Countries: c, d",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"hidemyass": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.HideMyAss,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Countries: []string{"a", "b"},
|
||||||
|
Cities: []string{"c", "d"},
|
||||||
|
Hostnames: []string{"e", "f"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Hidemyass settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Countries: a, b",
|
||||||
|
" |--Cities: c, d",
|
||||||
|
" |--Hostnames: e, f",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ipvanish": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ipvanish,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Countries: []string{"a", "b"},
|
||||||
|
Cities: []string{"c", "d"},
|
||||||
|
Hostnames: []string{"e", "f"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Ipvanish settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Countries: a, b",
|
||||||
|
" |--Cities: c, d",
|
||||||
|
" |--Hostnames: e, f",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ivpn": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Ivpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Countries: []string{"a", "b"},
|
||||||
|
Cities: []string{"c", "d"},
|
||||||
|
Hostnames: []string{"e", "f"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Ivpn settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Countries: a, b",
|
||||||
|
" |--Cities: c, d",
|
||||||
|
" |--Hostnames: e, f",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"mullvad": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Mullvad,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Countries: []string{"a", "b"},
|
||||||
|
Cities: []string{"c", "d"},
|
||||||
|
ISPs: []string{"e", "f"},
|
||||||
|
CustomPort: 1,
|
||||||
|
},
|
||||||
|
ExtraConfigOptions: ExtraConfigOptions{
|
||||||
|
OpenVPNIPv6: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Mullvad settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Countries: a, b",
|
||||||
|
" |--Cities: c, d",
|
||||||
|
" |--ISPs: e, f",
|
||||||
|
" |--Custom port: 1",
|
||||||
|
" |--IPv6: enabled",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"nordvpn": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Nordvpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Regions: []string{"a", "b"},
|
||||||
|
Numbers: []uint16{1, 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Nordvpn settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Regions: a, b",
|
||||||
|
" |--Numbers: 1, 2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"privado": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Privado,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Hostnames: []string{"a", "b"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Privado settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Hostnames: a, b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"privatevpn": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Privatevpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Hostnames: []string{"a", "b"},
|
||||||
|
Countries: []string{"c", "d"},
|
||||||
|
Cities: []string{"e", "f"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Privatevpn settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Countries: c, d",
|
||||||
|
" |--Cities: e, f",
|
||||||
|
" |--Hostnames: a, b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"protonvpn": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Protonvpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Countries: []string{"a", "b"},
|
||||||
|
Regions: []string{"c", "d"},
|
||||||
|
Cities: []string{"e", "f"},
|
||||||
|
Names: []string{"g", "h"},
|
||||||
|
Hostnames: []string{"i", "j"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Protonvpn settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Countries: a, b",
|
||||||
|
" |--Regions: c, d",
|
||||||
|
" |--Cities: e, f",
|
||||||
|
" |--Names: g, h",
|
||||||
|
" |--Hostnames: i, j",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"private internet access": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.PrivateInternetAccess,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Regions: []string{"a", "b"},
|
||||||
|
EncryptionPreset: constants.PIAEncryptionPresetStrong,
|
||||||
|
CustomPort: 1,
|
||||||
|
},
|
||||||
|
PortForwarding: PortForwarding{
|
||||||
|
Enabled: true,
|
||||||
|
Filepath: string("/here"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Private Internet Access settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Regions: a, b",
|
||||||
|
" |--Encryption preset: strong",
|
||||||
|
" |--Custom port: 1",
|
||||||
|
" |--Port forwarding:",
|
||||||
|
" |--File path: /here",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"purevpn": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Purevpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Regions: []string{"a", "b"},
|
||||||
|
Countries: []string{"c", "d"},
|
||||||
|
Cities: []string{"e", "f"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Purevpn settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Regions: a, b",
|
||||||
|
" |--Countries: c, d",
|
||||||
|
" |--Cities: e, f",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"surfshark": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Surfshark,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Regions: []string{"a", "b"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Surfshark settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Regions: a, b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"torguard": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Torguard,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Countries: []string{"a", "b"},
|
||||||
|
Cities: []string{"c", "d"},
|
||||||
|
Hostnames: []string{"e"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Torguard settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Countries: a, b",
|
||||||
|
" |--Cities: c, d",
|
||||||
|
" |--Hostnames: e",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
constants.VPNUnlimited: {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.VPNUnlimited,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Countries: []string{"a", "b"},
|
||||||
|
Cities: []string{"c", "d"},
|
||||||
|
Hostnames: []string{"e", "f"},
|
||||||
|
FreeOnly: true,
|
||||||
|
StreamOnly: true,
|
||||||
|
},
|
||||||
|
ExtraConfigOptions: ExtraConfigOptions{
|
||||||
|
ClientKey: "a",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Vpn Unlimited settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Countries: a, b",
|
||||||
|
" |--Cities: c, d",
|
||||||
|
" |--Hostnames: e, f",
|
||||||
|
" |--Free servers only",
|
||||||
|
" |--Stream servers only",
|
||||||
|
" |--Client key is set",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"vyprvpn": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Vyprvpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Regions: []string{"a", "b"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Vyprvpn settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Regions: a, b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"windscribe": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Windscribe,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Regions: []string{"a", "b"},
|
||||||
|
Cities: []string{"c", "d"},
|
||||||
|
Hostnames: []string{"e", "f"},
|
||||||
|
CustomPort: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Windscribe settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Regions: a, b",
|
||||||
|
" |--Cities: c, d",
|
||||||
|
" |--Hostnames: e, f",
|
||||||
|
" |--Custom port: 1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
lines := testCase.settings.lines()
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.lines, lines)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_readProtocol(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
mockStr string
|
||||||
|
mockErr error
|
||||||
|
tcp bool
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
"error": {
|
||||||
|
mockErr: errDummy,
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
mockStr: "tcp",
|
||||||
|
tcp: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
|
||||||
|
env := mock_params.NewMockEnv(ctrl)
|
||||||
|
env.EXPECT().
|
||||||
|
Inside("PROTOCOL", []string{"tcp", "udp"}, gomock.Any()).
|
||||||
|
Return(testCase.mockStr, testCase.mockErr)
|
||||||
|
|
||||||
|
tcp, err := readProtocol(env)
|
||||||
|
|
||||||
|
if testCase.err != nil {
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Equal(t, testCase.err.Error(), err.Error())
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.tcp, tcp)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
46
internal/configuration/publicip.go
Normal file
46
internal/configuration/publicip.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PublicIP struct {
|
||||||
|
Period time.Duration `json:"period"`
|
||||||
|
IPFilepath string `json:"ip_filepath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *PublicIP) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *PublicIP) lines() (lines []string) {
|
||||||
|
if settings.Period == 0 {
|
||||||
|
lines = append(lines, lastIndent+"Public IP getter: disabled")
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, lastIndent+"Public IP getter:")
|
||||||
|
lines = append(lines, indent+lastIndent+"Fetch period: "+settings.Period.String())
|
||||||
|
lines = append(lines, indent+lastIndent+"IP file: "+settings.IPFilepath)
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *PublicIP) read(r reader) (err error) {
|
||||||
|
settings.Period, err = r.env.Duration("PUBLICIP_PERIOD", params.Default("12h"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.IPFilepath, err = r.env.Path("PUBLICIP_FILE", params.CaseSensitiveValue(),
|
||||||
|
params.Default("/tmp/gluetun/ip"),
|
||||||
|
params.RetroKeys([]string{"IP_STATUS_FILE"}, r.onRetroActive))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
61
internal/configuration/purevpn.go
Normal file
61
internal/configuration/purevpn.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) purevpnLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Countries) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readPurevpn(r reader) (err error) {
|
||||||
|
settings.Name = constants.Purevpn
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PurevpnRegionChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PurevpnCountryChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PurevpnCityChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PurevpnHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
129
internal/configuration/reader.go
Normal file
129
internal/configuration/reader.go
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/logging"
|
||||||
|
"github.com/qdm12/golibs/os"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
"github.com/qdm12/golibs/verification"
|
||||||
|
)
|
||||||
|
|
||||||
|
type reader struct {
|
||||||
|
env params.Env
|
||||||
|
logger logging.Logger
|
||||||
|
regex verification.Regex
|
||||||
|
os os.OS
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReader(env params.Env, os os.OS, logger logging.Logger) reader {
|
||||||
|
return reader{
|
||||||
|
env: env,
|
||||||
|
logger: logger,
|
||||||
|
regex: verification.NewRegex(),
|
||||||
|
os: os,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reader) onRetroActive(oldKey, newKey string) {
|
||||||
|
r.logger.Warn(
|
||||||
|
"You are using the old environment variable %s, please consider changing it to %s",
|
||||||
|
oldKey, newKey,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidPort = errors.New("invalid port")
|
||||||
|
)
|
||||||
|
|
||||||
|
func readCSVPorts(env params.Env, key string) (ports []uint16, err error) {
|
||||||
|
s, err := env.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if len(s) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
portsStr := strings.Split(s, ",")
|
||||||
|
ports = make([]uint16, len(portsStr))
|
||||||
|
for i, portStr := range portsStr {
|
||||||
|
portInt, err := strconv.Atoi(portStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: %q from environment variable %s: %s",
|
||||||
|
ErrInvalidPort, portStr, key, err)
|
||||||
|
} else if portInt <= 0 || portInt > 65535 {
|
||||||
|
return nil, fmt.Errorf("%w: %d from environment variable %s: must be between 1 and 65535",
|
||||||
|
ErrInvalidPort, portInt, key)
|
||||||
|
}
|
||||||
|
ports[i] = uint16(portInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ports, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidIPNet = errors.New("invalid IP network")
|
||||||
|
)
|
||||||
|
|
||||||
|
func readCSVIPNets(env params.Env, key string, options ...params.OptionSetter) (
|
||||||
|
ipNets []net.IPNet, err error) {
|
||||||
|
s, err := env.Get(key, options...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if s == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ipNetsStr := strings.Split(s, ",")
|
||||||
|
ipNets = make([]net.IPNet, len(ipNetsStr))
|
||||||
|
for i, ipNetStr := range ipNetsStr {
|
||||||
|
_, ipNet, err := net.ParseCIDR(ipNetStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: %q from environment variable %s: %s",
|
||||||
|
ErrInvalidIPNet, ipNetStr, key, err)
|
||||||
|
} else if ipNet == nil {
|
||||||
|
return nil, fmt.Errorf("%w: %q from environment variable %s: subnet is nil",
|
||||||
|
ErrInvalidIPNet, ipNetStr, key)
|
||||||
|
}
|
||||||
|
ipNets[i] = *ipNet
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipNets, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidIP = errors.New("invalid IP address")
|
||||||
|
)
|
||||||
|
|
||||||
|
func readIP(env params.Env, key string) (ip net.IP, err error) {
|
||||||
|
s, err := env.Get(key)
|
||||||
|
if len(s) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = net.ParseIP(s)
|
||||||
|
if ip == nil {
|
||||||
|
return nil, fmt.Errorf("%w: %s", ErrInvalidIP, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readPortOrZero(env params.Env, key string) (port uint16, err error) {
|
||||||
|
s, err := env.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s) == 0 || s == "0" {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.Port(key)
|
||||||
|
}
|
||||||
116
internal/configuration/secrets.go
Normal file
116
internal/configuration/secrets.go
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/os"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrGetSecretFilepath = errors.New("cannot get secret file path from env")
|
||||||
|
ErrReadSecretFile = errors.New("cannot read secret file")
|
||||||
|
ErrSecretFileIsEmpty = errors.New("secret file is empty")
|
||||||
|
ErrReadNonSecretFile = errors.New("cannot read non secret file")
|
||||||
|
ErrFilesDoNotExist = errors.New("files do not exist")
|
||||||
|
)
|
||||||
|
|
||||||
|
func cleanSuffix(value string) string {
|
||||||
|
value = strings.TrimSuffix(value, "\n")
|
||||||
|
value = strings.TrimSuffix(value, "\r")
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKeys []string) (value string, err error) {
|
||||||
|
envOptions := []params.OptionSetter{
|
||||||
|
params.Compulsory(), // to fallback on file reading
|
||||||
|
params.CaseSensitiveValue(),
|
||||||
|
params.Unset(),
|
||||||
|
params.RetroKeys(retroKeys, r.onRetroActive),
|
||||||
|
}
|
||||||
|
value, envErr := r.env.Get(envKey, envOptions...)
|
||||||
|
if envErr == nil {
|
||||||
|
value = cleanSuffix(value)
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultSecretFile := "/run/secrets/" + strings.ToLower(envKey)
|
||||||
|
filepath, err := r.env.Get(envKey+"_SECRETFILE",
|
||||||
|
params.CaseSensitiveValue(),
|
||||||
|
params.Default(defaultSecretFile),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("%w: %s", ErrGetSecretFilepath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, fileErr := r.os.OpenFile(filepath, os.O_RDONLY, 0)
|
||||||
|
if os.IsNotExist(fileErr) {
|
||||||
|
if compulsory {
|
||||||
|
return "", envErr
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
} else if fileErr != nil {
|
||||||
|
return "", fmt.Errorf("%w: %s", ErrReadSecretFile, fileErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("%w: %s", ErrReadSecretFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
value = string(b)
|
||||||
|
value = cleanSuffix(value)
|
||||||
|
if compulsory && len(value) == 0 {
|
||||||
|
return "", ErrSecretFileIsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tries to read from the secret file then the non secret file.
|
||||||
|
func (r *reader) getFromFileOrSecretFile(secretName, filepath string) (
|
||||||
|
b []byte, err error) {
|
||||||
|
defaultSecretFile := "/run/secrets/" + strings.ToLower(secretName)
|
||||||
|
secretFilepath, err := r.env.Get(strings.ToUpper(secretName)+"_SECRETFILE",
|
||||||
|
params.CaseSensitiveValue(),
|
||||||
|
params.Default(defaultSecretFile),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return b, fmt.Errorf("%w: %s", ErrGetSecretFilepath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err = readFromFile(r.os.OpenFile, secretFilepath)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return b, fmt.Errorf("%w: %s", ErrReadSecretFile, err)
|
||||||
|
} else if err == nil {
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secret file does not exist, try the non secret file
|
||||||
|
b, err = readFromFile(r.os.OpenFile, filepath)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return nil, fmt.Errorf("%w: %s", ErrReadSecretFile, err)
|
||||||
|
} else if err == nil {
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%w: %s and %s", ErrFilesDoNotExist, secretFilepath, filepath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readFromFile(openFile os.OpenFileFunc, filepath string) (b []byte, err error) {
|
||||||
|
file, err := openFile(filepath, os.O_RDONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b, err = io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
_ = file.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := file.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
63
internal/configuration/selection.go
Normal file
63
internal/configuration/selection.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerSelection struct { //nolint:maligned
|
||||||
|
// Common
|
||||||
|
TCP bool `json:"tcp"` // UDP if TCP is false
|
||||||
|
TargetIP net.IP `json:"target_ip,omitempty"`
|
||||||
|
// TODO comments
|
||||||
|
// Cyberghost, PIA, Protonvpn, Surfshark, Windscribe, Vyprvpn, NordVPN
|
||||||
|
Regions []string `json:"regions"`
|
||||||
|
|
||||||
|
// Cyberghost
|
||||||
|
Group string `json:"group"`
|
||||||
|
|
||||||
|
// Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
|
||||||
|
Countries []string `json:"countries"`
|
||||||
|
// HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited, Windscribe
|
||||||
|
Cities []string `json:"cities"`
|
||||||
|
// Fastestvpn, HideMyAss, IPVanish, IVPN, PrivateVPN, Windscribe, Privado, Protonvpn, VPNUnlimited
|
||||||
|
Hostnames []string `json:"hostnames"`
|
||||||
|
Names []string `json:"names"` // Protonvpn
|
||||||
|
|
||||||
|
// Mullvad
|
||||||
|
ISPs []string `json:"isps"`
|
||||||
|
Owned bool `json:"owned"`
|
||||||
|
|
||||||
|
// Mullvad, Windscribe, PIA
|
||||||
|
CustomPort uint16 `json:"custom_port"`
|
||||||
|
|
||||||
|
// NordVPN
|
||||||
|
Numbers []uint16 `json:"numbers"`
|
||||||
|
|
||||||
|
// PIA
|
||||||
|
EncryptionPreset string `json:"encryption_preset"`
|
||||||
|
|
||||||
|
// ProtonVPN
|
||||||
|
FreeOnly bool `json:"free_only"`
|
||||||
|
|
||||||
|
// VPNUnlimited
|
||||||
|
StreamOnly bool `json:"stream_only"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExtraConfigOptions struct {
|
||||||
|
ClientCertificate string `json:"-"` // Cyberghost
|
||||||
|
ClientKey string `json:"-"` // Cyberghost, VPNUnlimited
|
||||||
|
EncryptionPreset string `json:"encryption_preset"` // PIA
|
||||||
|
OpenVPNIPv6 bool `json:"openvpn_ipv6"` // Mullvad
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortForwarding contains settings for port forwarding.
|
||||||
|
type PortForwarding struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Filepath string `json:"filepath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PortForwarding) lines() (lines []string) {
|
||||||
|
return []string{
|
||||||
|
lastIndent + "File path: " + p.Filepath,
|
||||||
|
}
|
||||||
|
}
|
||||||
49
internal/configuration/server.go
Normal file
49
internal/configuration/server.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ControlServer contains settings to customize the control server operation.
|
||||||
|
type ControlServer struct {
|
||||||
|
Port uint16
|
||||||
|
Log bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *ControlServer) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *ControlServer) lines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+"HTTP control server:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Listening port: "+strconv.Itoa(int(settings.Port)))
|
||||||
|
|
||||||
|
if settings.Log {
|
||||||
|
lines = append(lines, indent+lastIndent+"Logging: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *ControlServer) read(r reader) (err error) {
|
||||||
|
settings.Log, err = r.env.OnOff("HTTP_CONTROL_SERVER_LOG", params.Default("on"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var warning string
|
||||||
|
settings.Port, warning, err = r.env.ListeningPort(
|
||||||
|
"HTTP_CONTROL_SERVER_PORT", params.Default("8000"))
|
||||||
|
if len(warning) > 0 {
|
||||||
|
r.logger.Warn(warning)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
111
internal/configuration/settings.go
Normal file
111
internal/configuration/settings.go
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/logging"
|
||||||
|
"github.com/qdm12/golibs/os"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Settings contains all settings for the program to run.
|
||||||
|
type Settings struct {
|
||||||
|
OpenVPN OpenVPN
|
||||||
|
System System
|
||||||
|
DNS DNS
|
||||||
|
Firewall Firewall
|
||||||
|
HTTPProxy HTTPProxy
|
||||||
|
ShadowSocks ShadowSocks
|
||||||
|
Updater Updater
|
||||||
|
PublicIP PublicIP
|
||||||
|
VersionInformation bool
|
||||||
|
ControlServer ControlServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Settings) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Settings) lines() (lines []string) {
|
||||||
|
lines = append(lines, "Settings summary below:")
|
||||||
|
lines = append(lines, settings.OpenVPN.lines()...)
|
||||||
|
lines = append(lines, settings.DNS.lines()...)
|
||||||
|
lines = append(lines, settings.Firewall.lines()...)
|
||||||
|
lines = append(lines, settings.System.lines()...)
|
||||||
|
lines = append(lines, settings.HTTPProxy.lines()...)
|
||||||
|
lines = append(lines, settings.ShadowSocks.lines()...)
|
||||||
|
lines = append(lines, settings.ControlServer.lines()...)
|
||||||
|
lines = append(lines, settings.Updater.lines()...)
|
||||||
|
lines = append(lines, settings.PublicIP.lines()...)
|
||||||
|
if settings.VersionInformation {
|
||||||
|
lines = append(lines, lastIndent+"Github version information: enabled")
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrOpenvpn = errors.New("cannot read Openvpn settings")
|
||||||
|
ErrSystem = errors.New("cannot read System settings")
|
||||||
|
ErrDNS = errors.New("cannot read DNS settings")
|
||||||
|
ErrFirewall = errors.New("cannot read firewall settings")
|
||||||
|
ErrHTTPProxy = errors.New("cannot read HTTP proxy settings")
|
||||||
|
ErrShadowsocks = errors.New("cannot read Shadowsocks settings")
|
||||||
|
ErrControlServer = errors.New("cannot read control server settings")
|
||||||
|
ErrUpdater = errors.New("cannot read Updater settings")
|
||||||
|
ErrPublicIP = errors.New("cannot read Public IP getter settings")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Read obtains all configuration options for the program and returns an error as soon
|
||||||
|
// as an error is encountered reading them.
|
||||||
|
func (settings *Settings) Read(env params.Env, os os.OS, logger logging.Logger) (err error) {
|
||||||
|
r := newReader(env, os, logger)
|
||||||
|
|
||||||
|
settings.VersionInformation, err = r.env.OnOff("VERSION_INFORMATION", params.Default("on"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.OpenVPN.read(r); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrOpenvpn, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.System.read(r); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrSystem, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.DNS.read(r); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrDNS, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.Firewall.read(r); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrFirewall, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.HTTPProxy.read(r); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrHTTPProxy, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.ShadowSocks.read(r); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrShadowsocks, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.ControlServer.read(r); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrControlServer, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.Updater.read(r); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrUpdater, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip := settings.DNS.PlaintextAddress; ip != nil {
|
||||||
|
settings.Updater.DNSAddress = ip.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.PublicIP.read(r); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrPublicIP, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
57
internal/configuration/settings_test.go
Normal file
57
internal/configuration/settings_test.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Settings_lines(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
settings Settings
|
||||||
|
lines []string
|
||||||
|
}{
|
||||||
|
"default settings": {
|
||||||
|
settings: Settings{
|
||||||
|
OpenVPN: OpenVPN{
|
||||||
|
Version: constants.Openvpn25,
|
||||||
|
Provider: Provider{
|
||||||
|
Name: constants.Mullvad,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"Settings summary below:",
|
||||||
|
"|--OpenVPN:",
|
||||||
|
" |--Version: 2.5",
|
||||||
|
" |--Verbosity level: 0",
|
||||||
|
" |--Provider:",
|
||||||
|
" |--Mullvad settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
"|--DNS:",
|
||||||
|
"|--Firewall: disabled ⚠️",
|
||||||
|
"|--System:",
|
||||||
|
" |--Process user ID: 0",
|
||||||
|
" |--Process group ID: 0",
|
||||||
|
" |--Timezone: NOT SET ⚠️ - it can cause time related issues",
|
||||||
|
"|--HTTP control server:",
|
||||||
|
" |--Listening port: 0",
|
||||||
|
"|--Public IP getter: disabled",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
lines := testCase.settings.lines()
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.lines, lines)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
72
internal/configuration/shadowsocks.go
Normal file
72
internal/configuration/shadowsocks.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ShadowSocks contains settings to configure the Shadowsocks server.
|
||||||
|
type ShadowSocks struct {
|
||||||
|
Method string
|
||||||
|
Password string
|
||||||
|
Port uint16
|
||||||
|
Enabled bool
|
||||||
|
Log bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *ShadowSocks) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *ShadowSocks) lines() (lines []string) {
|
||||||
|
if !settings.Enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, lastIndent+"Shadowsocks server:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Listening port: "+strconv.Itoa(int(settings.Port)))
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Method: "+settings.Method)
|
||||||
|
|
||||||
|
if settings.Log {
|
||||||
|
lines = append(lines, indent+lastIndent+"Logging: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *ShadowSocks) read(r reader) (err error) {
|
||||||
|
settings.Enabled, err = r.env.OnOff("SHADOWSOCKS", params.Default("off"))
|
||||||
|
if err != nil || !settings.Enabled {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Password, err = r.getFromEnvOrSecretFile("SHADOWSOCKS_PASSWORD", settings.Enabled, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Log, err = r.env.OnOff("SHADOWSOCKS_LOG", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Method, err = r.env.Get("SHADOWSOCKS_METHOD", params.Default("chacha20-ietf-poly1305"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var warning string
|
||||||
|
settings.Port, warning, err = r.env.ListeningPort("SHADOWSOCKS_PORT", params.Default("8388"))
|
||||||
|
if len(warning) > 0 {
|
||||||
|
r.logger.Warn(warning)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
43
internal/configuration/surfshark.go
Normal file
43
internal/configuration/surfshark.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) surfsharkLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readSurfshark(r reader) (err error) {
|
||||||
|
settings.Name = constants.Surfshark
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.SurfsharkRegionChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.SurfsharkHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
54
internal/configuration/system.go
Normal file
54
internal/configuration/system.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// System contains settings to configure system related elements.
|
||||||
|
type System struct {
|
||||||
|
PUID int
|
||||||
|
PGID int
|
||||||
|
Timezone string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *System) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *System) lines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+"System:")
|
||||||
|
lines = append(lines, indent+lastIndent+"Process user ID: "+strconv.Itoa(settings.PUID))
|
||||||
|
lines = append(lines, indent+lastIndent+"Process group ID: "+strconv.Itoa(settings.PGID))
|
||||||
|
|
||||||
|
if len(settings.Timezone) > 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Timezone: "+settings.Timezone)
|
||||||
|
} else {
|
||||||
|
lines = append(lines, indent+lastIndent+"Timezone: NOT SET ⚠️ - it can cause time related issues")
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *System) read(r reader) (err error) {
|
||||||
|
const maxID = 65535
|
||||||
|
settings.PUID, err = r.env.IntRange("PUID", 0, maxID, params.Default("1000"),
|
||||||
|
params.RetroKeys([]string{"UID"}, r.onRetroActive))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.PGID, err = r.env.IntRange("PGID", 0, maxID, params.Default("1000"),
|
||||||
|
params.RetroKeys([]string{"GID"}, r.onRetroActive))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Timezone, err = r.env.Get("TZ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
52
internal/configuration/torguard.go
Normal file
52
internal/configuration/torguard.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) torguardLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Countries) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readTorguard(r reader) (err error) {
|
||||||
|
settings.Name = constants.Torguard
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.TorguardCountryChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.TorguardCityChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.TorguardHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
79
internal/configuration/unbound.go
Normal file
79
internal/configuration/unbound.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/dns/pkg/provider"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
"inet.af/netaddr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *DNS) readUnbound(r reader) (err error) {
|
||||||
|
if err := settings.readUnboundProviders(r.env); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Unbound.ListeningPort = 53
|
||||||
|
|
||||||
|
settings.Unbound.Caching, err = r.env.OnOff("DOT_CACHING", params.Default("on"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Unbound.IPv4 = true
|
||||||
|
|
||||||
|
settings.Unbound.IPv6, err = r.env.OnOff("DOT_IPV6", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
verbosityLevel, err := r.env.IntRange("DOT_VERBOSITY", 0, 5, params.Default("1")) //nolint:gomnd
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settings.Unbound.VerbosityLevel = uint8(verbosityLevel)
|
||||||
|
|
||||||
|
verbosityDetailsLevel, err := r.env.IntRange("DOT_VERBOSITY_DETAILS", 0, 4, params.Default("0")) //nolint:gomnd
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settings.Unbound.VerbosityDetailsLevel = uint8(verbosityDetailsLevel)
|
||||||
|
|
||||||
|
validationLogLevel, err := r.env.IntRange("DOT_VALIDATION_LOGLEVEL", 0, 2, params.Default("0")) //nolint:gomnd
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settings.Unbound.ValidationLogLevel = uint8(validationLogLevel)
|
||||||
|
|
||||||
|
settings.Unbound.AccessControl.Allowed = []netaddr.IPPrefix{
|
||||||
|
{IP: netaddr.IPv4(0, 0, 0, 0)},
|
||||||
|
{IP: netaddr.IPv6Raw([16]byte{})},
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidDNSOverTLSProvider = errors.New("invalid DNS over TLS provider")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *DNS) readUnboundProviders(env params.Env) (err error) {
|
||||||
|
s, err := env.Get("DOT_PROVIDERS", params.Default("cloudflare"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, field := range strings.Split(s, ",") {
|
||||||
|
dnsProvider, err := provider.Parse(field)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrInvalidDNSOverTLSProvider, err)
|
||||||
|
}
|
||||||
|
settings.Unbound.Providers = append(settings.Unbound.Providers, dnsProvider)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidHostname = errors.New("invalid hostname")
|
||||||
|
)
|
||||||
83
internal/configuration/updater.go
Normal file
83
internal/configuration/updater.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Updater struct {
|
||||||
|
Period time.Duration `json:"period"`
|
||||||
|
DNSAddress string `json:"dns_address"`
|
||||||
|
Cyberghost bool `json:"cyberghost"`
|
||||||
|
Fastestvpn bool `json:"fastestvpn"`
|
||||||
|
HideMyAss bool `json:"hidemyass"`
|
||||||
|
Ipvanish bool `json:"ipvanish"`
|
||||||
|
Ivpn bool `json:"ivpn"`
|
||||||
|
Mullvad bool `json:"mullvad"`
|
||||||
|
Nordvpn bool `json:"nordvpn"`
|
||||||
|
PIA bool `json:"pia"`
|
||||||
|
Privado bool `json:"privado"`
|
||||||
|
Privatevpn bool `json:"privatevpn"`
|
||||||
|
Protonvpn bool `json:"protonvpn"`
|
||||||
|
Purevpn bool `json:"purevpn"`
|
||||||
|
Surfshark bool `json:"surfshark"`
|
||||||
|
Torguard bool `json:"torguard"`
|
||||||
|
VPNUnlimited bool `json:"vpnunlimited"`
|
||||||
|
Vyprvpn bool `json:"vyprvpn"`
|
||||||
|
Windscribe bool `json:"windscribe"`
|
||||||
|
// The two below should be used in CLI mode only
|
||||||
|
Stdout bool `json:"-"` // in order to update constants file (maintainer side)
|
||||||
|
CLI bool `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Updater) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Updater) lines() (lines []string) {
|
||||||
|
if settings.Period == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, lastIndent+"Updater:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Period: every "+settings.Period.String())
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Updater) read(r reader) (err error) {
|
||||||
|
settings.Cyberghost = true
|
||||||
|
settings.HideMyAss = true
|
||||||
|
settings.Ipvanish = true
|
||||||
|
settings.Ivpn = true
|
||||||
|
settings.Mullvad = true
|
||||||
|
settings.Nordvpn = true
|
||||||
|
settings.Privado = true
|
||||||
|
settings.PIA = true
|
||||||
|
settings.Privado = true
|
||||||
|
settings.Privatevpn = true
|
||||||
|
settings.Protonvpn = true
|
||||||
|
settings.Purevpn = true
|
||||||
|
settings.Surfshark = true
|
||||||
|
settings.Torguard = true
|
||||||
|
settings.VPNUnlimited = true
|
||||||
|
settings.Vyprvpn = true
|
||||||
|
settings.Windscribe = true
|
||||||
|
settings.Stdout = false
|
||||||
|
settings.CLI = false
|
||||||
|
// use cloudflare in plaintext to not be blocked by DNS over TLS by default.
|
||||||
|
// If a plaintext address is set in the DNS settings, this one will be used.
|
||||||
|
// TODO use custom future encrypted DNS written in Go without blocking
|
||||||
|
// as it's too much trouble to start another parallel unbound instance for now.
|
||||||
|
settings.DNSAddress = "1.1.1.1"
|
||||||
|
|
||||||
|
settings.Period, err = r.env.Duration("UPDATER_PERIOD", params.Default("0"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
85
internal/configuration/vpnunlimited.go
Normal file
85
internal/configuration/vpnunlimited.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) vpnUnlimitedLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Countries) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.ServerSelection.FreeOnly {
|
||||||
|
lines = append(lines, lastIndent+"Free servers only")
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.ServerSelection.StreamOnly {
|
||||||
|
lines = append(lines, lastIndent+"Stream servers only")
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.ExtraConfigOptions.ClientKey != "" {
|
||||||
|
lines = append(lines, lastIndent+"Client key is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readVPNUnlimited(r reader) (err error) {
|
||||||
|
settings.Name = constants.VPNUnlimited
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ExtraConfigOptions.ClientKey, err = readClientKey(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ExtraConfigOptions.ClientCertificate, err = readClientCertificate(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.VPNUnlimitedCountryChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.VPNUnlimitedCityChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.VPNUnlimitedHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.FreeOnly, err = r.env.YesNo("FREE_ONLY", params.Default("no"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.StreamOnly, err = r.env.YesNo("STREAM_ONLY", params.Default("no"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
42
internal/configuration/vpnunlimited_test.go
Normal file
42
internal/configuration/vpnunlimited_test.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Provider_vpnUnlimitedLines(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
settings Provider
|
||||||
|
lines []string
|
||||||
|
}{
|
||||||
|
"empty settings": {},
|
||||||
|
"full settings": {
|
||||||
|
settings: Provider{
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Countries: []string{"A", "B"},
|
||||||
|
Cities: []string{"C", "D"},
|
||||||
|
Hostnames: []string{"E", "F"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Countries: A, B",
|
||||||
|
"|--Cities: C, D",
|
||||||
|
"|--Hostnames: E, F",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
lines := testCase.settings.vpnUnlimitedLines()
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.lines, lines)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
34
internal/configuration/vyprvpn.go
Normal file
34
internal/configuration/vyprvpn.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) vyprvpnLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readVyprvpn(r reader) (err error) {
|
||||||
|
settings.Name = constants.Vyprvpn
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.VyprvpnRegionChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
65
internal/configuration/windscribe.go
Normal file
65
internal/configuration/windscribe.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) windscribeLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.ServerSelection.CustomPort > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readWindscribe(r reader) (err error) {
|
||||||
|
settings.Name = constants.Windscribe
|
||||||
|
|
||||||
|
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.WindscribeRegionChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.WindscribeCityChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.WindscribeHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.CustomPort, err = readCustomPort(r.env, settings.ServerSelection.TCP,
|
||||||
|
[]uint16{21, 22, 80, 123, 143, 443, 587, 1194, 3306, 8080, 54783},
|
||||||
|
[]uint16{53, 80, 123, 443, 1194, 54783})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
6
internal/constants/addresses.go
Normal file
6
internal/constants/addresses.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package constants
|
||||||
|
|
||||||
|
const (
|
||||||
|
// HealthcheckAddress is the default listening address for the healthcheck server.
|
||||||
|
HealthcheckAddress = "127.0.0.1:9999"
|
||||||
|
)
|
||||||
@@ -6,18 +6,6 @@ func ColorUnbound() *color.Color {
|
|||||||
return color.New(color.FgCyan)
|
return color.New(color.FgCyan)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ColorTinyproxy() *color.Color {
|
|
||||||
return color.New(color.FgHiGreen)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ColorShadowsocks() *color.Color {
|
|
||||||
return color.New(color.FgHiYellow)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ColorShadowsocksError() *color.Color {
|
|
||||||
return color.New(color.FgHiRed)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ColorOpenvpn() *color.Color {
|
func ColorOpenvpn() *color.Color {
|
||||||
return color.New(color.FgHiMagenta)
|
return color.New(color.FgHiMagenta)
|
||||||
}
|
}
|
||||||
|
|||||||
25
internal/constants/constants.go
Normal file
25
internal/constants/constants.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Package constants defines constants shared throughout the program.
|
||||||
|
// It also defines constant maps and slices using functions.
|
||||||
|
package constants
|
||||||
|
|
||||||
|
import "sort"
|
||||||
|
|
||||||
|
func makeChoicesUnique(choices []string) []string {
|
||||||
|
uniqueChoices := map[string]struct{}{}
|
||||||
|
for _, choice := range choices {
|
||||||
|
uniqueChoices[choice] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueChoicesSlice := make([]string, len(uniqueChoices))
|
||||||
|
i := 0
|
||||||
|
for choice := range uniqueChoices {
|
||||||
|
uniqueChoicesSlice[i] = choice
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(uniqueChoicesSlice, func(i, j int) bool {
|
||||||
|
return uniqueChoicesSlice[i] < uniqueChoicesSlice[j]
|
||||||
|
})
|
||||||
|
|
||||||
|
return uniqueChoicesSlice
|
||||||
|
}
|
||||||
256
internal/constants/countries.go
Normal file
256
internal/constants/countries.go
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
package constants
|
||||||
|
|
||||||
|
func CountryCodes() map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"af": "Afghanistan",
|
||||||
|
"ax": "Aland Islands",
|
||||||
|
"al": "Albania",
|
||||||
|
"dz": "Algeria",
|
||||||
|
"as": "American Samoa",
|
||||||
|
"ad": "Andorra",
|
||||||
|
"ao": "Angola",
|
||||||
|
"ai": "Anguilla",
|
||||||
|
"aq": "Antarctica",
|
||||||
|
"ag": "Antigua and Barbuda",
|
||||||
|
"ar": "Argentina",
|
||||||
|
"am": "Armenia",
|
||||||
|
"aw": "Aruba",
|
||||||
|
"au": "Australia",
|
||||||
|
"at": "Austria",
|
||||||
|
"az": "Azerbaijan",
|
||||||
|
"bs": "Bahamas",
|
||||||
|
"bh": "Bahrain",
|
||||||
|
"bd": "Bangladesh",
|
||||||
|
"bb": "Barbados",
|
||||||
|
"by": "Belarus",
|
||||||
|
"be": "Belgium",
|
||||||
|
"bz": "Belize",
|
||||||
|
"bj": "Benin",
|
||||||
|
"bm": "Bermuda",
|
||||||
|
"bt": "Bhutan",
|
||||||
|
"bo": "Bolivia",
|
||||||
|
"bq": "Bonaire",
|
||||||
|
"ba": "Bosnia and Herzegovina",
|
||||||
|
"bw": "Botswana",
|
||||||
|
"bv": "Bouvet Island",
|
||||||
|
"br": "Brazil",
|
||||||
|
"io": "British Indian Ocean Territory",
|
||||||
|
"vg": "British Virgin Islands",
|
||||||
|
"bn": "Brunei Darussalam",
|
||||||
|
"bg": "Bulgaria",
|
||||||
|
"bf": "Burkina Faso",
|
||||||
|
"bi": "Burundi",
|
||||||
|
"kh": "Cambodia",
|
||||||
|
"cm": "Cameroon",
|
||||||
|
"ca": "Canada",
|
||||||
|
"cv": "Cape Verde",
|
||||||
|
"ky": "Cayman Islands",
|
||||||
|
"cf": "Central African Republic",
|
||||||
|
"td": "Chad",
|
||||||
|
"cl": "Chile",
|
||||||
|
"cn": "China",
|
||||||
|
"cx": "Christmas Island",
|
||||||
|
"cc": "Cocos Islands",
|
||||||
|
"co": "Colombia",
|
||||||
|
"km": "Comoros",
|
||||||
|
"cg": "Congo",
|
||||||
|
"ck": "Cook Islands",
|
||||||
|
"cr": "Costa Rica",
|
||||||
|
"ci": "Cote d'Ivoire",
|
||||||
|
"hr": "Croatia",
|
||||||
|
"cu": "Cuba",
|
||||||
|
"cw": "Curacao",
|
||||||
|
"cy": "Cyprus",
|
||||||
|
"cz": "Czech Republic",
|
||||||
|
"cd": "Democratic Republic of the Congo",
|
||||||
|
"dk": "Denmark",
|
||||||
|
"dj": "Djibouti",
|
||||||
|
"dm": "Dominica",
|
||||||
|
"do": "Dominican Republic",
|
||||||
|
"ec": "Ecuador",
|
||||||
|
"eg": "Egypt",
|
||||||
|
"sv": "El Salvador",
|
||||||
|
"gq": "Equatorial Guinea",
|
||||||
|
"er": "Eritrea",
|
||||||
|
"ee": "Estonia",
|
||||||
|
"et": "Ethiopia",
|
||||||
|
"fk": "Falkland Islands",
|
||||||
|
"fo": "Faroe Islands",
|
||||||
|
"fj": "Fiji",
|
||||||
|
"fi": "Finland",
|
||||||
|
"fr": "France",
|
||||||
|
"gf": "French Guiana",
|
||||||
|
"pf": "French Polynesia",
|
||||||
|
"tf": "French Southern Territories",
|
||||||
|
"ga": "Gabon",
|
||||||
|
"gm": "Gambia",
|
||||||
|
"ge": "Georgia",
|
||||||
|
"de": "Germany",
|
||||||
|
"gh": "Ghana",
|
||||||
|
"gi": "Gibraltar",
|
||||||
|
"gr": "Greece",
|
||||||
|
"gl": "Greenland",
|
||||||
|
"gd": "Grenada",
|
||||||
|
"gp": "Guadeloupe",
|
||||||
|
"gu": "Guam",
|
||||||
|
"gt": "Guatemala",
|
||||||
|
"gg": "Guernsey",
|
||||||
|
"gw": "Guinea-Bissau",
|
||||||
|
"gn": "Guinea",
|
||||||
|
"gy": "Guyana",
|
||||||
|
"ht": "Haiti",
|
||||||
|
"hm": "Heard Island and McDonald Islands",
|
||||||
|
"hn": "Honduras",
|
||||||
|
"hk": "Hong Kong",
|
||||||
|
"hu": "Hungary",
|
||||||
|
"is": "Iceland",
|
||||||
|
"in": "India",
|
||||||
|
"id": "Indonesia",
|
||||||
|
"ir": "Iran",
|
||||||
|
"iq": "Iraq",
|
||||||
|
"ie": "Ireland",
|
||||||
|
"im": "Isle of Man",
|
||||||
|
"il": "Israel",
|
||||||
|
"it": "Italy",
|
||||||
|
"jm": "Jamaica",
|
||||||
|
"jp": "Japan",
|
||||||
|
"je": "Jersey",
|
||||||
|
"jo": "Jordan",
|
||||||
|
"kz": "Kazakhstan",
|
||||||
|
"ke": "Kenya",
|
||||||
|
"ki": "Kiribati",
|
||||||
|
"kr": "Korea",
|
||||||
|
"kw": "Kuwait",
|
||||||
|
"kg": "Kyrgyzstan",
|
||||||
|
"la": "Lao People's Democratic Republic",
|
||||||
|
"lv": "Latvia",
|
||||||
|
"lb": "Lebanon",
|
||||||
|
"ls": "Lesotho",
|
||||||
|
"lr": "Liberia",
|
||||||
|
"ly": "Libya",
|
||||||
|
"li": "Liechtenstein",
|
||||||
|
"lt": "Lithuania",
|
||||||
|
"lu": "Luxembourg",
|
||||||
|
"mo": "Macao",
|
||||||
|
"mk": "Macedonia",
|
||||||
|
"mg": "Madagascar",
|
||||||
|
"mw": "Malawi",
|
||||||
|
"my": "Malaysia",
|
||||||
|
"mys": "Kuala Lumpur",
|
||||||
|
"mv": "Maldives",
|
||||||
|
"ml": "Mali",
|
||||||
|
"mt": "Malta",
|
||||||
|
"mh": "Marshall Islands",
|
||||||
|
"mq": "Martinique",
|
||||||
|
"mr": "Mauritania",
|
||||||
|
"mu": "Mauritius",
|
||||||
|
"yt": "Mayotte",
|
||||||
|
"mx": "Mexico",
|
||||||
|
"fm": "Micronesia",
|
||||||
|
"md": "Moldova",
|
||||||
|
"mc": "Monaco",
|
||||||
|
"mn": "Mongolia",
|
||||||
|
"me": "Montenegro",
|
||||||
|
"ms": "Montserrat",
|
||||||
|
"ma": "Morocco",
|
||||||
|
"mz": "Mozambique",
|
||||||
|
"mm": "Myanmar",
|
||||||
|
"na": "Namibia",
|
||||||
|
"nr": "Nauru",
|
||||||
|
"np": "Nepal",
|
||||||
|
"nl": "Netherlands",
|
||||||
|
"nc": "New Caledonia",
|
||||||
|
"nz": "New Zealand",
|
||||||
|
"ni": "Nicaragua",
|
||||||
|
"ne": "Niger",
|
||||||
|
"ng": "Nigeria",
|
||||||
|
"nu": "Niue",
|
||||||
|
"nf": "Norfolk Island",
|
||||||
|
"mp": "Northern Mariana Islands",
|
||||||
|
"no": "Norway",
|
||||||
|
"om": "Oman",
|
||||||
|
"pk": "Pakistan",
|
||||||
|
"pw": "Palau",
|
||||||
|
"ps": "Palestine, State of",
|
||||||
|
"pa": "Panama",
|
||||||
|
"pg": "Papua New Guinea",
|
||||||
|
"py": "Paraguay",
|
||||||
|
"pe": "Peru",
|
||||||
|
"ph": "Philippines",
|
||||||
|
"pn": "Pitcairn",
|
||||||
|
"pl": "Poland",
|
||||||
|
"pt": "Portugal",
|
||||||
|
"pr": "Puerto Rico",
|
||||||
|
"qa": "Qatar",
|
||||||
|
"re": "Reunion",
|
||||||
|
"ro": "Romania",
|
||||||
|
"ru": "Russian Federation",
|
||||||
|
"rw": "Rwanda",
|
||||||
|
"bl": "Saint Barthelemy",
|
||||||
|
"sh": "Saint Helena",
|
||||||
|
"kn": "Saint Kitts and Nevis",
|
||||||
|
"lc": "Saint Lucia",
|
||||||
|
"mf": "Saint Martin",
|
||||||
|
"pm": "Saint Pierre and Miquelon",
|
||||||
|
"vc": "Saint Vincent and the Grenadines",
|
||||||
|
"ws": "Samoa",
|
||||||
|
"sm": "San Marino",
|
||||||
|
"st": "Sao Tome and Principe",
|
||||||
|
"sa": "Saudi Arabia",
|
||||||
|
"sn": "Senegal",
|
||||||
|
"rs": "Serbia",
|
||||||
|
"sc": "Seychelles",
|
||||||
|
"sl": "Sierra Leone",
|
||||||
|
"sg": "Singapore",
|
||||||
|
"sx": "Sint Maarten",
|
||||||
|
"sk": "Slovakia",
|
||||||
|
"si": "Slovenia",
|
||||||
|
"sb": "Solomon Islands",
|
||||||
|
"so": "Somalia",
|
||||||
|
"za": "South Africa",
|
||||||
|
"gs": "South Georgia and the South Sandwich Islands",
|
||||||
|
"ss": "South Sudan",
|
||||||
|
"es": "Spain",
|
||||||
|
"lk": "Sri Lanka",
|
||||||
|
"sd": "Sudan",
|
||||||
|
"sr": "Suriname",
|
||||||
|
"sj": "Svalbard and Jan Mayen",
|
||||||
|
"sz": "Swaziland",
|
||||||
|
"se": "Sweden",
|
||||||
|
"ch": "Switzerland",
|
||||||
|
"sy": "Syrian Arab Republic",
|
||||||
|
"tw": "Taiwan",
|
||||||
|
"tj": "Tajikistan",
|
||||||
|
"tz": "Tanzania",
|
||||||
|
"th": "Thailand",
|
||||||
|
"tl": "Timor-Leste",
|
||||||
|
"tg": "Togo",
|
||||||
|
"tk": "Tokelau",
|
||||||
|
"to": "Tonga",
|
||||||
|
"tt": "Trinidad and Tobago",
|
||||||
|
"tn": "Tunisia",
|
||||||
|
"tr": "Turkey",
|
||||||
|
"tm": "Turkmenistan",
|
||||||
|
"tc": "Turks and Caicos Islands",
|
||||||
|
"tv": "Tuvalu",
|
||||||
|
"ug": "Uganda",
|
||||||
|
"ua": "Ukraine",
|
||||||
|
"ae": "United Arab Emirates",
|
||||||
|
"gb": "United Kingdom",
|
||||||
|
"uk": "United Kingdom",
|
||||||
|
"um": "United States Minor Outlying Islands",
|
||||||
|
"us": "United States",
|
||||||
|
"uy": "Uruguay",
|
||||||
|
"vi": "US Virgin Islands",
|
||||||
|
"uz": "Uzbekistan",
|
||||||
|
"vu": "Vanuatu",
|
||||||
|
"va": "Vatican City State",
|
||||||
|
"ve": "Venezuela",
|
||||||
|
"vn": "Vietnam",
|
||||||
|
"wf": "Wallis and Futuna",
|
||||||
|
"eh": "Western Sahara",
|
||||||
|
"ye": "Yemen",
|
||||||
|
"zm": "Zambia",
|
||||||
|
"zw": "Zimbabwe",
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,229 +2,225 @@ package constants
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//nolint:lll
|
||||||
const (
|
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=="
|
||||||
CyberghostClientCertificate = "MIIGrDCCBJSgAwIBAgIEAdTnfTANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJSTzESMBAGA1UEBxMJQnVjaGFyZXN0MRgwFgYDVQQKEw9DeWJlckdob3N0IFMuQS4xGzAZBgNVBAMTEkN5YmVyR2hvc3QgUm9vdCBDQTEhMB8GCSqGSIb3DQEJARYSaW5mb0BjeWJlcmdob3N0LnJvMB4XDTIwMDcwNDE1MjkzNloXDTMwMDcwMjE1MjkzNlowfTELMAkGA1UEBhMCUk8xEjAQBgNVBAcMCUJ1Y2hhcmVzdDEYMBYGA1UECgwPQ3liZXJHaG9zdCBTLkEuMR0wGwYDVQQDDBRjLmoua2xhdmVyQGdtYWlsLmNvbTEhMB8GCSqGSIb3DQEJARYSaW5mb0BjeWJlcmdob3N0LnJvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAobp2NlGUHMNBe08YEOnVG3QJjF3ZaXbRhE/II9rmtgJTNZtDohGChvFlNRsExKzVrKxHCeuJkVffwzQ6fYk4/M1RdYLJUh0UVw3e4WdApw8E7TJZxDYm4SHQNXUvt1Rt5TjslcXxIpDZgrMSc/kHROYEL9tdgdzPZErUJehXyJPhEzIrzmAJh501x7WwKPz9ctSVlItyavqEWFF2vyUa6X9DYmD9mQTz5c+VXNO5DkXmPFBIaEVDnvFtcjGJ56yEvFnWVukL+OUX7ezowrIOFOcp9udjgpeiHq+XvsQ6ER0DJt25MiEId3NjkxtZ8BitDftTcLN/kt81hWKT7adMVc3kpIZ80cxrwRCttMd7sHAzKI9u7pMxv10eUOsIEY87ewBe3l6KvEnjA+9uIjim6gLLebDIaEH50Ee9PzNJ8fqQ2u54Ab4bt00/H1sUnJ6Ss/+WsQDOK1BsPRKKcnHZntOlHrs2Tu5+txKNU2cOapI8SjVULUNKrRXASbpfWnLUfri/HO742bJb/TjkOJcOxta3hTPFAhaRWBusVlB41XVHeuH5DAhugYXeSNK6/6Ul8YvKUNH/7QbxuGIGXfth19Xl4QLI1umyEjZopSlt3tOiO2V1soVNSQCCfxXVoCTMESMLjhkjWdmBDhdy2GTW7S4YoJfqVKiS18rYkN7I4ZMCAwEAAaOCATQwggEwMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMDQGCWCGSAGG+EIBDQQnFiVDeWJlckdob3N0IEdlbmVyYXRlZCBVc2VyIENlcnRpZmljYXRlMBEGCWCGSAGG+EIBAQQEAwIHgDAdBgNVHQ4EFgQULwUtU5s6pL2NN9gPeEnKX0dhwiswga0GA1UdIwSBpTCBooAU6tdK1g/He5qzjeAoM5eHt4in9iWhf6R9MHsxCzAJBgNVBAYTAlJPMRIwEAYDVQQHEwlCdWNoYXJlc3QxGDAWBgNVBAoTD0N5YmVyR2hvc3QgUy5BLjEbMBkGA1UEAxMSQ3liZXJHaG9zdCBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJpbmZvQGN5YmVyZ2hvc3Qucm+CCQCcVButZsQ0uzANBgkqhkiG9w0BAQsFAAOCAgEAystGIMYhQWaEdTqlnLCytrr8657t+PuidZMNNIaPB3wN2Fi2xKf14DTg03mqxjmPPb+f+PVNIOV5PdWD4jcQwOP1GEboGV0DFzlRGeAtDcvKwdee4oASJbZq1CETqDaohQTxKEWC+UBk2F36nOaEI6Sab+Mb4cR9//PAwvzOqrXuGF5NuIOX7eFtCMQSgQq6lRRqTQjekm0Dxigx4JA92Jo2qZRwCJ0T3IXBJGL831HCFJbDWv8PV3lsfFb/i2+vr54uywFQVWWp18dYi97gipfuQ4zRg2Ldx5aXSmnhhKpg5ioZvtk043QofF12YORhobElqavRbvvhZvlCouvcuoq9QKi7IPe5SJZkZ1X7ezMesCwBzwFpt6vRUAcslsNFbcYS1iSENlY/PTcDqBhbKuc9yAhq+/aUgaY/8VF5RWVzSRZufbf3BPwOkE4K0UybaobO/YX0JOkCacAD+4tdR6YSXNIMMRAOCBQvxbxFXaHzhwhzBAjdsC56FrJKwXvQrRLU3tF4P0zFMeNTay8uTtUXugDK7EnklLESuYdpUJ8bUMlAUhJBi6UFI9/icMudxXvLRvhnBW9EtKib5JnVFUovcEUt+3EJbyst05nkL4YPjQS4TC9DHdo5SyRAy1TpiOCYTbretAFZRhh6ycUN5hBeN8GMQxiMreMtDV4PEIQ="
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func CyberghostRegionChoices() (choices []string) {
|
func CyberghostRegionChoices() (choices []string) {
|
||||||
uniqueChoices := map[string]struct{}{}
|
servers := CyberghostServers()
|
||||||
for _, server := range CyberghostServers() {
|
choices = make([]string, len(servers))
|
||||||
uniqueChoices[server.Region] = struct{}{}
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Region
|
||||||
}
|
}
|
||||||
for choice := range uniqueChoices {
|
return makeUnique(choices)
|
||||||
choices = append(choices, choice)
|
|
||||||
}
|
|
||||||
sort.Slice(choices, func(i, j int) bool {
|
|
||||||
return choices[i] < choices[j]
|
|
||||||
})
|
|
||||||
return choices
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CyberghostGroupChoices() (choices []string) {
|
func CyberghostGroupChoices() (choices []string) {
|
||||||
uniqueChoices := map[string]struct{}{}
|
servers := CyberghostServers()
|
||||||
for _, server := range CyberghostServers() {
|
choices = make([]string, len(servers))
|
||||||
uniqueChoices[server.Group] = struct{}{}
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Group
|
||||||
}
|
}
|
||||||
for choice := range uniqueChoices {
|
return makeUnique(choices)
|
||||||
choices = append(choices, choice)
|
|
||||||
}
|
|
||||||
sort.Slice(choices, func(i, j int) bool {
|
|
||||||
return choices[i] < choices[j]
|
|
||||||
})
|
|
||||||
return choices
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CyberghostHostnameChoices() (choices []string) {
|
||||||
|
servers := CyberghostServers()
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Hostname
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:lll
|
||||||
|
// CyberghostServers returns a slice with the server information for each
|
||||||
|
// of the Cyberghost server.
|
||||||
func CyberghostServers() []models.CyberghostServer {
|
func CyberghostServers() []models.CyberghostServer {
|
||||||
return []models.CyberghostServer{
|
return []models.CyberghostServer{
|
||||||
{Region: "Albania", Group: "Premium TCP Europe", IPs: []net.IP{{31, 171, 152, 99}, {31, 171, 152, 100}, {31, 171, 152, 102}, {31, 171, 152, 105}, {31, 171, 152, 107}, {31, 171, 152, 109}, {31, 171, 152, 133}, {31, 171, 152, 135}, {31, 171, 152, 136}, {31, 171, 152, 139}}},
|
{Region: "Albania", Group: "Premium TCP Europe", Hostname: "97-1-al.cg-dialup.net", IPs: []net.IP{{31, 171, 155, 3}, {31, 171, 155, 4}, {31, 171, 155, 7}, {31, 171, 155, 8}, {31, 171, 155, 9}, {31, 171, 155, 10}, {31, 171, 155, 11}, {31, 171, 155, 12}, {31, 171, 155, 13}, {31, 171, 155, 14}}},
|
||||||
{Region: "Albania", Group: "Premium UDP Europe", IPs: []net.IP{{31, 171, 152, 99}, {31, 171, 152, 101}, {31, 171, 152, 103}, {31, 171, 152, 105}, {31, 171, 152, 106}, {31, 171, 152, 133}, {31, 171, 152, 135}, {31, 171, 152, 137}, {31, 171, 152, 138}, {31, 171, 152, 139}}},
|
{Region: "Albania", Group: "Premium UDP Europe", Hostname: "87-1-al.cg-dialup.net", 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, 10}, {31, 171, 155, 11}, {31, 171, 155, 13}, {31, 171, 155, 14}}},
|
||||||
{Region: "Algeria", Group: "Premium TCP Europe", IPs: []net.IP{{45, 133, 91, 7}, {45, 133, 91, 9}, {45, 133, 91, 12}, {45, 133, 91, 15}, {45, 133, 91, 17}, {45, 133, 91, 18}, {45, 133, 91, 20}, {45, 133, 91, 23}, {45, 133, 91, 24}, {45, 133, 91, 26}}},
|
{Region: "Algeria", Group: "Premium TCP Europe", Hostname: "97-1-dz.cg-dialup.net", IPs: []net.IP{{176, 125, 228, 132}, {176, 125, 228, 134}, {176, 125, 228, 135}, {176, 125, 228, 136}, {176, 125, 228, 137}, {176, 125, 228, 138}, {176, 125, 228, 139}, {176, 125, 228, 140}, {176, 125, 228, 141}, {176, 125, 228, 142}}},
|
||||||
{Region: "Algeria", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 91, 7}, {45, 133, 91, 10}, {45, 133, 91, 11}, {45, 133, 91, 15}, {45, 133, 91, 17}, {45, 133, 91, 18}, {45, 133, 91, 21}, {45, 133, 91, 25}, {45, 133, 91, 26}, {45, 133, 91, 29}}},
|
{Region: "Algeria", Group: "Premium UDP Europe", Hostname: "87-1-dz.cg-dialup.net", IPs: []net.IP{{176, 125, 228, 131}, {176, 125, 228, 133}, {176, 125, 228, 134}, {176, 125, 228, 136}, {176, 125, 228, 137}, {176, 125, 228, 139}, {176, 125, 228, 140}, {176, 125, 228, 141}, {176, 125, 228, 142}, {176, 125, 228, 143}}},
|
||||||
{Region: "Andorra", Group: "Premium TCP Europe", IPs: []net.IP{{45, 139, 49, 10}, {45, 139, 49, 14}, {45, 139, 49, 15}, {45, 139, 49, 16}, {45, 139, 49, 17}, {45, 139, 49, 18}, {45, 139, 49, 19}, {45, 139, 49, 23}, {45, 139, 49, 25}, {45, 139, 49, 28}}},
|
{Region: "Andorra", Group: "Premium TCP Europe", Hostname: "97-1-ad.cg-dialup.net", IPs: []net.IP{{188, 241, 82, 137}, {188, 241, 82, 138}, {188, 241, 82, 140}, {188, 241, 82, 142}, {188, 241, 82, 147}, {188, 241, 82, 155}, {188, 241, 82, 159}, {188, 241, 82, 160}, {188, 241, 82, 161}, {188, 241, 82, 166}}},
|
||||||
{Region: "Andorra", Group: "Premium UDP Europe", IPs: []net.IP{{45, 139, 49, 7}, {45, 139, 49, 9}, {45, 139, 49, 10}, {45, 139, 49, 12}, {45, 139, 49, 15}, {45, 139, 49, 16}, {45, 139, 49, 17}, {45, 139, 49, 25}, {45, 139, 49, 27}, {45, 139, 49, 28}}},
|
{Region: "Andorra", Group: "Premium UDP Europe", Hostname: "87-1-ad.cg-dialup.net", IPs: []net.IP{{188, 241, 82, 133}, {188, 241, 82, 134}, {188, 241, 82, 136}, {188, 241, 82, 137}, {188, 241, 82, 146}, {188, 241, 82, 153}, {188, 241, 82, 155}, {188, 241, 82, 160}, {188, 241, 82, 164}, {188, 241, 82, 168}}},
|
||||||
{Region: "Argentina", Group: "Premium TCP USA", IPs: []net.IP{{190, 106, 130, 16}, {190, 106, 130, 17}, {190, 106, 130, 20}, {190, 106, 130, 22}, {190, 106, 130, 23}, {190, 106, 130, 34}, {190, 106, 130, 37}, {190, 106, 130, 38}, {190, 106, 130, 44}, {190, 106, 130, 45}}},
|
{Region: "Argentina", Group: "Premium TCP USA", Hostname: "93-1-ar.cg-dialup.net", IPs: []net.IP{{146, 70, 39, 4}, {146, 70, 39, 9}, {146, 70, 39, 15}, {146, 70, 39, 19}, {146, 70, 39, 135}, {146, 70, 39, 136}, {146, 70, 39, 139}, {146, 70, 39, 142}, {146, 70, 39, 143}, {146, 70, 39, 145}}},
|
||||||
{Region: "Argentina", Group: "Premium UDP USA", IPs: []net.IP{{190, 106, 130, 15}, {190, 106, 130, 16}, {190, 106, 130, 18}, {190, 106, 130, 19}, {190, 106, 130, 20}, {190, 106, 130, 34}, {190, 106, 130, 36}, {190, 106, 130, 37}, {190, 106, 130, 43}, {190, 106, 130, 52}}},
|
{Region: "Argentina", Group: "Premium UDP USA", Hostname: "94-1-ar.cg-dialup.net", IPs: []net.IP{{146, 70, 39, 3}, {146, 70, 39, 5}, {146, 70, 39, 6}, {146, 70, 39, 8}, {146, 70, 39, 11}, {146, 70, 39, 12}, {146, 70, 39, 131}, {146, 70, 39, 134}, {146, 70, 39, 142}, {146, 70, 39, 143}}},
|
||||||
{Region: "Armenia", Group: "Premium TCP Europe", IPs: []net.IP{{45, 139, 50, 10}, {45, 139, 50, 12}, {45, 139, 50, 14}, {45, 139, 50, 18}, {45, 139, 50, 19}, {45, 139, 50, 20}, {45, 139, 50, 21}, {45, 139, 50, 26}, {45, 139, 50, 27}, {45, 139, 50, 29}}},
|
{Region: "Armenia", Group: "Premium TCP Europe", Hostname: "97-1-am.cg-dialup.net", IPs: []net.IP{{185, 253, 160, 131}, {185, 253, 160, 134}, {185, 253, 160, 136}, {185, 253, 160, 137}, {185, 253, 160, 138}, {185, 253, 160, 139}, {185, 253, 160, 140}, {185, 253, 160, 141}, {185, 253, 160, 142}, {185, 253, 160, 143}}},
|
||||||
{Region: "Armenia", Group: "Premium UDP Europe", IPs: []net.IP{{45, 139, 50, 8}, {45, 139, 50, 10}, {45, 139, 50, 11}, {45, 139, 50, 14}, {45, 139, 50, 18}, {45, 139, 50, 20}, {45, 139, 50, 24}, {45, 139, 50, 26}, {45, 139, 50, 27}, {45, 139, 50, 29}}},
|
{Region: "Armenia", Group: "Premium UDP Europe", Hostname: "87-1-am.cg-dialup.net", IPs: []net.IP{{185, 253, 160, 131}, {185, 253, 160, 132}, {185, 253, 160, 133}, {185, 253, 160, 134}, {185, 253, 160, 135}, {185, 253, 160, 136}, {185, 253, 160, 137}, {185, 253, 160, 141}, {185, 253, 160, 142}, {185, 253, 160, 144}}},
|
||||||
{Region: "Australia", Group: "Premium TCP Asia", IPs: []net.IP{{27, 50, 79, 3}, {27, 50, 79, 4}, {27, 50, 79, 5}, {27, 50, 79, 6}, {27, 50, 79, 9}, {27, 50, 79, 12}, {27, 50, 79, 14}, {103, 13, 101, 171}, {202, 130, 33, 114}, {202, 130, 33, 118}}},
|
{Region: "Australia", Group: "Premium TCP Asia", Hostname: "96-1-au.cg-dialup.net", IPs: []net.IP{{154, 16, 81, 22}, {181, 214, 215, 7}, {181, 214, 215, 15}, {181, 214, 215, 18}, {191, 101, 210, 15}, {191, 101, 210, 50}, {191, 101, 210, 60}, {202, 60, 80, 78}, {202, 60, 80, 82}, {202, 60, 80, 102}}},
|
||||||
{Region: "Australia", Group: "Premium UDP Asia", IPs: []net.IP{{27, 50, 79, 3}, {27, 50, 79, 6}, {27, 50, 79, 9}, {27, 50, 79, 10}, {27, 50, 79, 11}, {27, 50, 79, 13}, {103, 13, 101, 174}, {202, 130, 33, 114}, {202, 130, 33, 117}, {202, 130, 33, 118}}},
|
{Region: "Australia", Group: "Premium UDP Asia", Hostname: "95-1-au.cg-dialup.net", IPs: []net.IP{{181, 214, 215, 4}, {181, 214, 215, 16}, {191, 101, 210, 18}, {191, 101, 210, 21}, {191, 101, 210, 36}, {191, 101, 210, 58}, {191, 101, 210, 60}, {202, 60, 80, 74}, {202, 60, 80, 106}, {202, 60, 80, 124}}},
|
||||||
{Region: "Austria", Group: "Premium TCP Europe", IPs: []net.IP{{89, 187, 168, 133}, {89, 187, 168, 144}, {89, 187, 168, 150}, {89, 187, 168, 151}, {89, 187, 168, 162}, {89, 187, 168, 163}, {89, 187, 168, 164}, {89, 187, 168, 167}, {89, 187, 168, 178}, {89, 187, 168, 182}}},
|
{Region: "Austria", Group: "Premium TCP Europe", Hostname: "97-1-at.cg-dialup.net", IPs: []net.IP{{37, 19, 223, 9}, {37, 19, 223, 16}, {37, 19, 223, 113}, {37, 19, 223, 205}, {37, 19, 223, 211}, {37, 19, 223, 218}, {37, 19, 223, 223}, {37, 19, 223, 245}, {37, 120, 155, 104}, {89, 187, 168, 174}}},
|
||||||
{Region: "Austria", Group: "Premium UDP Europe", IPs: []net.IP{{89, 187, 168, 138}, {89, 187, 168, 139}, {89, 187, 168, 149}, {89, 187, 168, 150}, {89, 187, 168, 161}, {89, 187, 168, 165}, {89, 187, 168, 167}, {89, 187, 168, 168}, {89, 187, 168, 174}, {89, 187, 168, 182}}},
|
{Region: "Austria", Group: "Premium UDP Europe", Hostname: "87-1-at.cg-dialup.net", IPs: []net.IP{{37, 19, 223, 202}, {37, 19, 223, 205}, {37, 19, 223, 229}, {37, 19, 223, 239}, {37, 19, 223, 241}, {37, 19, 223, 243}, {37, 120, 155, 103}, {89, 187, 168, 160}, {89, 187, 168, 174}, {89, 187, 168, 181}}},
|
||||||
{Region: "Bahamas", Group: "Premium TCP USA", IPs: []net.IP{{45, 132, 143, 8}, {45, 132, 143, 10}, {45, 132, 143, 11}, {45, 132, 143, 19}, {45, 132, 143, 24}, {45, 132, 143, 28}, {45, 132, 143, 31}, {45, 132, 143, 42}, {45, 132, 143, 43}, {45, 132, 143, 44}}},
|
{Region: "Bahamas", Group: "Premium TCP USA", Hostname: "93-1-bs.cg-dialup.net", IPs: []net.IP{{95, 181, 238, 131}, {95, 181, 238, 136}, {95, 181, 238, 142}, {95, 181, 238, 144}, {95, 181, 238, 146}, {95, 181, 238, 147}, {95, 181, 238, 148}, {95, 181, 238, 152}, {95, 181, 238, 153}, {95, 181, 238, 155}}},
|
||||||
{Region: "Bahamas", Group: "Premium UDP USA", IPs: []net.IP{{45, 132, 143, 1}, {45, 132, 143, 2}, {45, 132, 143, 3}, {45, 132, 143, 5}, {45, 132, 143, 7}, {45, 132, 143, 18}, {45, 132, 143, 23}, {45, 132, 143, 30}, {45, 132, 143, 32}, {45, 132, 143, 48}}},
|
{Region: "Bahamas", Group: "Premium UDP USA", Hostname: "94-1-bs.cg-dialup.net", IPs: []net.IP{{95, 181, 238, 131}, {95, 181, 238, 138}, {95, 181, 238, 140}, {95, 181, 238, 141}, {95, 181, 238, 146}, {95, 181, 238, 147}, {95, 181, 238, 148}, {95, 181, 238, 151}, {95, 181, 238, 153}, {95, 181, 238, 155}}},
|
||||||
{Region: "Bangladesh", Group: "Premium TCP Asia", IPs: []net.IP{{45, 132, 142, 3}, {45, 132, 142, 8}, {45, 132, 142, 12}, {45, 132, 142, 20}, {45, 132, 142, 22}, {45, 132, 142, 26}, {45, 132, 142, 27}, {45, 132, 142, 37}, {45, 132, 142, 39}, {45, 132, 142, 42}}},
|
{Region: "Bangladesh", Group: "Premium TCP Asia", Hostname: "96-1-bd.cg-dialup.net", IPs: []net.IP{{84, 252, 93, 132}, {84, 252, 93, 133}, {84, 252, 93, 135}, {84, 252, 93, 138}, {84, 252, 93, 139}, {84, 252, 93, 141}, {84, 252, 93, 142}, {84, 252, 93, 143}, {84, 252, 93, 144}, {84, 252, 93, 145}}},
|
||||||
{Region: "Bangladesh", Group: "Premium UDP Asia", IPs: []net.IP{{45, 132, 142, 6}, {45, 132, 142, 8}, {45, 132, 142, 13}, {45, 132, 142, 18}, {45, 132, 142, 33}, {45, 132, 142, 35}, {45, 132, 142, 38}, {45, 132, 142, 41}, {45, 132, 142, 42}, {45, 132, 142, 45}}},
|
{Region: "Bangladesh", Group: "Premium UDP Asia", Hostname: "95-1-bd.cg-dialup.net", IPs: []net.IP{{84, 252, 93, 131}, {84, 252, 93, 133}, {84, 252, 93, 134}, {84, 252, 93, 135}, {84, 252, 93, 136}, {84, 252, 93, 139}, {84, 252, 93, 140}, {84, 252, 93, 141}, {84, 252, 93, 143}, {84, 252, 93, 145}}},
|
||||||
{Region: "Belarus", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 194, 3}, {45, 132, 194, 4}, {45, 132, 194, 26}, {45, 132, 194, 28}, {45, 132, 194, 34}, {45, 132, 194, 38}, {45, 132, 194, 39}, {45, 132, 194, 42}, {45, 132, 194, 44}, {45, 132, 194, 48}}},
|
{Region: "Belarus", Group: "Premium TCP Europe", Hostname: "97-1-by.cg-dialup.net", IPs: []net.IP{{45, 132, 194, 5}, {45, 132, 194, 6}, {45, 132, 194, 23}, {45, 132, 194, 24}, {45, 132, 194, 25}, {45, 132, 194, 27}, {45, 132, 194, 30}, {45, 132, 194, 35}, {45, 132, 194, 44}, {45, 132, 194, 49}}},
|
||||||
{Region: "Belarus", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 194, 4}, {45, 132, 194, 5}, {45, 132, 194, 9}, {45, 132, 194, 10}, {45, 132, 194, 20}, {45, 132, 194, 25}, {45, 132, 194, 29}, {45, 132, 194, 31}, {45, 132, 194, 40}, {45, 132, 194, 45}}},
|
{Region: "Belarus", Group: "Premium UDP Europe", Hostname: "87-1-by.cg-dialup.net", IPs: []net.IP{{45, 132, 194, 6}, {45, 132, 194, 8}, {45, 132, 194, 9}, {45, 132, 194, 11}, {45, 132, 194, 15}, {45, 132, 194, 19}, {45, 132, 194, 20}, {45, 132, 194, 23}, {45, 132, 194, 24}, {45, 132, 194, 26}}},
|
||||||
{Region: "Belgium", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 143, 52}, {37, 120, 143, 55}, {37, 120, 143, 58}, {185, 210, 217, 10}, {185, 210, 217, 54}, {185, 210, 217, 244}, {185, 210, 217, 251}, {185, 232, 21, 119}, {193, 9, 114, 228}, {193, 9, 114, 230}}},
|
{Region: "Belgium", Group: "Premium TCP Europe", Hostname: "97-1-be.cg-dialup.net", IPs: []net.IP{{37, 120, 143, 165}, {37, 120, 143, 166}, {185, 210, 217, 10}, {185, 210, 217, 248}, {193, 9, 114, 211}, {193, 9, 114, 220}, {194, 110, 115, 195}, {194, 110, 115, 199}, {194, 110, 115, 205}, {194, 110, 115, 238}}},
|
||||||
{Region: "Belgium", Group: "Premium UDP Europe", IPs: []net.IP{{5, 253, 205, 22}, {5, 253, 205, 23}, {37, 120, 143, 165}, {185, 210, 217, 14}, {185, 210, 217, 248}, {185, 210, 217, 254}, {193, 9, 114, 212}, {193, 9, 114, 213}, {193, 9, 114, 219}, {193, 9, 114, 228}}},
|
{Region: "Belgium", Group: "Premium UDP Europe", Hostname: "87-1-be.cg-dialup.net", IPs: []net.IP{{37, 120, 143, 163}, {37, 120, 143, 167}, {185, 210, 217, 9}, {185, 210, 217, 13}, {185, 210, 217, 55}, {185, 210, 217, 251}, {185, 232, 21, 120}, {194, 110, 115, 214}, {194, 110, 115, 218}, {194, 110, 115, 236}}},
|
||||||
{Region: "Bosnia and Herzegovina", Group: "Premium TCP Europe", IPs: []net.IP{{185, 99, 3, 57}, {185, 99, 3, 58}, {185, 99, 3, 72}, {185, 99, 3, 73}, {185, 99, 3, 74}, {185, 99, 3, 130}, {185, 99, 3, 131}, {185, 99, 3, 134}, {185, 99, 3, 135}, {185, 99, 3, 136}}},
|
{Region: "Bosnia and Herzegovina", Group: "Premium TCP Europe", Hostname: "97-1-ba.cg-dialup.net", IPs: []net.IP{{185, 99, 3, 57}, {185, 99, 3, 58}, {185, 99, 3, 72}, {185, 99, 3, 73}, {185, 99, 3, 74}, {185, 99, 3, 130}, {185, 99, 3, 131}, {185, 99, 3, 134}, {185, 99, 3, 135}, {185, 99, 3, 136}}},
|
||||||
{Region: "Bosnia and Herzegovina", Group: "Premium UDP Europe", IPs: []net.IP{{185, 99, 3, 57}, {185, 99, 3, 58}, {185, 99, 3, 72}, {185, 99, 3, 73}, {185, 99, 3, 74}, {185, 99, 3, 130}, {185, 99, 3, 131}, {185, 99, 3, 134}, {185, 99, 3, 135}, {185, 99, 3, 136}}},
|
{Region: "Bosnia and Herzegovina", Group: "Premium UDP Europe", Hostname: "87-1-ba.cg-dialup.net", IPs: []net.IP{{185, 99, 3, 57}, {185, 99, 3, 58}, {185, 99, 3, 72}, {185, 99, 3, 73}, {185, 99, 3, 74}, {185, 99, 3, 130}, {185, 99, 3, 131}, {185, 99, 3, 134}, {185, 99, 3, 135}, {185, 99, 3, 136}}},
|
||||||
{Region: "Brazil", Group: "Premium TCP USA", IPs: []net.IP{{45, 231, 207, 65}, {45, 231, 207, 67}, {45, 231, 207, 68}, {45, 231, 207, 69}, {45, 231, 207, 75}, {177, 67, 81, 170}, {181, 41, 203, 98}, {181, 41, 203, 100}, {181, 41, 203, 102}, {181, 41, 203, 110}}},
|
{Region: "Brazil", Group: "Premium TCP USA", Hostname: "93-1-br.cg-dialup.net", IPs: []net.IP{{188, 241, 177, 5}, {188, 241, 177, 11}, {188, 241, 177, 38}, {188, 241, 177, 45}, {188, 241, 177, 132}, {188, 241, 177, 135}, {188, 241, 177, 136}, {188, 241, 177, 152}, {188, 241, 177, 153}, {188, 241, 177, 156}}},
|
||||||
{Region: "Brazil", Group: "Premium UDP USA", IPs: []net.IP{{45, 231, 207, 77}, {177, 67, 81, 163}, {177, 67, 81, 164}, {177, 67, 81, 165}, {177, 67, 81, 167}, {177, 67, 81, 170}, {177, 67, 81, 173}, {177, 67, 81, 174}, {181, 41, 203, 103}, {181, 41, 203, 104}}},
|
{Region: "Brazil", Group: "Premium UDP USA", Hostname: "94-1-br.cg-dialup.net", IPs: []net.IP{{188, 241, 177, 8}, {188, 241, 177, 37}, {188, 241, 177, 40}, {188, 241, 177, 42}, {188, 241, 177, 45}, {188, 241, 177, 135}, {188, 241, 177, 139}, {188, 241, 177, 149}, {188, 241, 177, 152}, {188, 241, 177, 154}}},
|
||||||
{Region: "Bulgaria", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 152, 99}, {37, 120, 152, 102}, {37, 120, 152, 103}, {37, 120, 152, 104}, {37, 120, 152, 105}, {37, 120, 152, 106}, {37, 120, 152, 107}, {37, 120, 152, 108}, {37, 120, 152, 109}, {37, 120, 152, 110}}},
|
{Region: "Bulgaria", Group: "Premium TCP Europe", Hostname: "97-1-bg.cg-dialup.net", IPs: []net.IP{{37, 120, 152, 99}, {37, 120, 152, 101}, {37, 120, 152, 103}, {37, 120, 152, 104}, {37, 120, 152, 105}, {37, 120, 152, 106}, {37, 120, 152, 107}, {37, 120, 152, 108}, {37, 120, 152, 109}, {37, 120, 152, 110}}},
|
||||||
{Region: "Bulgaria", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 152, 99}, {37, 120, 152, 100}, {37, 120, 152, 101}, {37, 120, 152, 103}, {37, 120, 152, 104}, {37, 120, 152, 105}, {37, 120, 152, 107}, {37, 120, 152, 108}, {37, 120, 152, 109}, {37, 120, 152, 110}}},
|
{Region: "Bulgaria", Group: "Premium UDP Europe", Hostname: "87-1-bg.cg-dialup.net", 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, 105}, {37, 120, 152, 106}, {37, 120, 152, 107}, {37, 120, 152, 108}, {37, 120, 152, 109}}},
|
||||||
{Region: "Cambodia", Group: "Premium TCP Asia", IPs: []net.IP{{188, 215, 235, 36}, {188, 215, 235, 38}, {188, 215, 235, 39}, {188, 215, 235, 40}, {188, 215, 235, 43}, {188, 215, 235, 46}, {188, 215, 235, 47}, {188, 215, 235, 48}, {188, 215, 235, 51}, {188, 215, 235, 54}}},
|
{Region: "Cambodia", Group: "Premium TCP Asia", Hostname: "96-1-kh.cg-dialup.net", IPs: []net.IP{{188, 215, 235, 35}, {188, 215, 235, 36}, {188, 215, 235, 38}, {188, 215, 235, 39}, {188, 215, 235, 45}, {188, 215, 235, 49}, {188, 215, 235, 51}, {188, 215, 235, 53}, {188, 215, 235, 54}, {188, 215, 235, 57}}},
|
||||||
{Region: "Cambodia", Group: "Premium UDP Asia", IPs: []net.IP{{188, 215, 235, 35}, {188, 215, 235, 36}, {188, 215, 235, 37}, {188, 215, 235, 38}, {188, 215, 235, 45}, {188, 215, 235, 46}, {188, 215, 235, 52}, {188, 215, 235, 53}, {188, 215, 235, 55}, {188, 215, 235, 58}}},
|
{Region: "Cambodia", Group: "Premium UDP Asia", Hostname: "95-1-kh.cg-dialup.net", IPs: []net.IP{{188, 215, 235, 36}, {188, 215, 235, 40}, {188, 215, 235, 42}, {188, 215, 235, 44}, {188, 215, 235, 46}, {188, 215, 235, 47}, {188, 215, 235, 48}, {188, 215, 235, 50}, {188, 215, 235, 55}, {188, 215, 235, 57}}},
|
||||||
{Region: "Canada", Group: "Premium TCP USA", IPs: []net.IP{{37, 120, 205, 8}, {37, 120, 205, 28}, {104, 245, 145, 163}, {104, 245, 146, 38}, {104, 245, 146, 101}, {176, 113, 74, 44}, {176, 113, 74, 52}, {176, 113, 74, 67}, {176, 113, 74, 126}, {176, 113, 74, 217}}},
|
{Region: "Canada", Group: "Premium TCP USA", Hostname: "93-1-ca.cg-dialup.net", IPs: []net.IP{{66, 115, 142, 136}, {66, 115, 142, 139}, {66, 115, 142, 156}, {66, 115, 142, 162}, {66, 115, 142, 172}, {104, 200, 151, 99}, {104, 200, 151, 111}, {104, 200, 151, 153}, {104, 200, 151, 164}, {172, 98, 89, 137}}},
|
||||||
{Region: "Canada", Group: "Premium UDP USA", IPs: []net.IP{{37, 120, 205, 40}, {54, 39, 11, 194}, {104, 245, 145, 164}, {104, 245, 146, 41}, {139, 28, 218, 86}, {139, 28, 218, 87}, {176, 113, 74, 19}, {176, 113, 74, 25}, {176, 113, 74, 30}, {176, 113, 74, 195}}},
|
{Region: "Canada", Group: "Premium UDP USA", Hostname: "94-1-ca.cg-dialup.net", IPs: []net.IP{{66, 115, 142, 135}, {66, 115, 142, 154}, {66, 115, 142, 165}, {104, 200, 151, 32}, {104, 200, 151, 57}, {104, 200, 151, 85}, {104, 200, 151, 86}, {104, 200, 151, 147}, {172, 98, 89, 144}, {172, 98, 89, 173}}},
|
||||||
{Region: "Chile", Group: "Premium TCP USA", IPs: []net.IP{{190, 105, 239, 129}, {190, 105, 239, 130}, {190, 105, 239, 131}, {190, 105, 239, 132}, {190, 105, 239, 133}, {190, 105, 239, 134}, {190, 105, 239, 135}, {190, 105, 239, 136}, {190, 105, 239, 137}, {190, 105, 239, 138}}},
|
{Region: "Chile", Group: "Premium TCP USA", Hostname: "93-1-cl.cg-dialup.net", IPs: []net.IP{{146, 70, 11, 3}, {146, 70, 11, 6}, {146, 70, 11, 7}, {146, 70, 11, 8}, {146, 70, 11, 9}, {146, 70, 11, 10}, {146, 70, 11, 11}, {146, 70, 11, 12}, {146, 70, 11, 13}, {146, 70, 11, 14}}},
|
||||||
{Region: "Chile", Group: "Premium UDP USA", IPs: []net.IP{{190, 105, 239, 129}, {190, 105, 239, 130}, {190, 105, 239, 131}, {190, 105, 239, 132}, {190, 105, 239, 133}, {190, 105, 239, 134}, {190, 105, 239, 135}, {190, 105, 239, 136}, {190, 105, 239, 137}, {190, 105, 239, 138}}},
|
{Region: "Chile", Group: "Premium UDP USA", Hostname: "94-1-cl.cg-dialup.net", 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, 10}, {146, 70, 11, 11}, {146, 70, 11, 13}, {146, 70, 11, 14}}},
|
||||||
{Region: "China", Group: "Premium TCP Asia", IPs: []net.IP{{45, 132, 193, 2}, {45, 132, 193, 3}, {45, 132, 193, 9}, {45, 132, 193, 10}, {45, 132, 193, 12}, {45, 132, 193, 13}, {45, 132, 193, 32}, {45, 132, 193, 36}, {45, 132, 193, 43}, {45, 132, 193, 45}}},
|
{Region: "China", Group: "Premium TCP Asia", Hostname: "96-1-cn.cg-dialup.net", IPs: []net.IP{{188, 241, 80, 131}, {188, 241, 80, 132}, {188, 241, 80, 133}, {188, 241, 80, 134}, {188, 241, 80, 135}, {188, 241, 80, 137}, {188, 241, 80, 139}, {188, 241, 80, 140}, {188, 241, 80, 141}, {188, 241, 80, 142}}},
|
||||||
{Region: "China", Group: "Premium UDP Asia", IPs: []net.IP{{45, 132, 193, 2}, {45, 132, 193, 3}, {45, 132, 193, 4}, {45, 132, 193, 14}, {45, 132, 193, 19}, {45, 132, 193, 22}, {45, 132, 193, 30}, {45, 132, 193, 35}, {45, 132, 193, 36}, {45, 132, 193, 42}}},
|
{Region: "China", Group: "Premium UDP Asia", Hostname: "95-1-cn.cg-dialup.net", IPs: []net.IP{{188, 241, 80, 131}, {188, 241, 80, 132}, {188, 241, 80, 133}, {188, 241, 80, 134}, {188, 241, 80, 135}, {188, 241, 80, 136}, {188, 241, 80, 137}, {188, 241, 80, 138}, {188, 241, 80, 139}, {188, 241, 80, 142}}},
|
||||||
{Region: "Colombia", Group: "Premium TCP USA", IPs: []net.IP{{190, 105, 229, 19}, {190, 105, 229, 20}, {190, 105, 229, 21}, {190, 105, 229, 22}}},
|
{Region: "Colombia", Group: "Premium TCP USA", Hostname: "93-1-co.cg-dialup.net", IPs: []net.IP{{146, 70, 9, 3}, {146, 70, 9, 4}, {146, 70, 9, 5}, {146, 70, 9, 7}, {146, 70, 9, 9}, {146, 70, 9, 10}, {146, 70, 9, 11}, {146, 70, 9, 12}, {146, 70, 9, 13}, {146, 70, 9, 14}}},
|
||||||
{Region: "Colombia", Group: "Premium UDP USA", IPs: []net.IP{{190, 105, 229, 19}, {190, 105, 229, 20}, {190, 105, 229, 21}, {190, 105, 229, 22}}},
|
{Region: "Colombia", Group: "Premium UDP USA", Hostname: "94-1-co.cg-dialup.net", 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, 8}, {146, 70, 9, 9}, {146, 70, 9, 10}, {146, 70, 9, 11}, {146, 70, 9, 12}}},
|
||||||
{Region: "Costa Rica", Group: "Premium TCP USA", IPs: []net.IP{{143, 202, 160, 67}, {143, 202, 160, 69}, {143, 202, 160, 70}, {143, 202, 160, 72}, {143, 202, 160, 73}, {143, 202, 160, 74}, {143, 202, 160, 75}, {143, 202, 160, 76}, {143, 202, 160, 77}, {143, 202, 160, 78}}},
|
{Region: "Costa Rica", Group: "Premium TCP USA", Hostname: "93-1-cr.cg-dialup.net", IPs: []net.IP{{146, 70, 10, 3}, {146, 70, 10, 4}, {146, 70, 10, 5}, {146, 70, 10, 6}, {146, 70, 10, 7}, {146, 70, 10, 8}, {146, 70, 10, 10}, {146, 70, 10, 11}, {146, 70, 10, 12}, {146, 70, 10, 13}}},
|
||||||
{Region: "Costa Rica", Group: "Premium UDP USA", IPs: []net.IP{{143, 202, 160, 67}, {143, 202, 160, 68}, {143, 202, 160, 69}, {143, 202, 160, 70}, {143, 202, 160, 71}, {143, 202, 160, 73}, {143, 202, 160, 74}, {143, 202, 160, 75}, {143, 202, 160, 76}, {143, 202, 160, 78}}},
|
{Region: "Costa Rica", Group: "Premium UDP USA", Hostname: "94-1-cr.cg-dialup.net", IPs: []net.IP{{146, 70, 10, 3}, {146, 70, 10, 4}, {146, 70, 10, 5}, {146, 70, 10, 6}, {146, 70, 10, 7}, {146, 70, 10, 8}, {146, 70, 10, 9}, {146, 70, 10, 11}, {146, 70, 10, 12}, {146, 70, 10, 14}}},
|
||||||
{Region: "Cyprus", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 137, 8}, {45, 132, 137, 11}, {45, 132, 137, 12}, {45, 132, 137, 15}, {45, 132, 137, 17}, {45, 132, 137, 18}, {45, 132, 137, 19}, {45, 132, 137, 23}, {45, 132, 137, 26}, {45, 132, 137, 28}}},
|
{Region: "Croatia", Group: "Premium TCP Europe", Hostname: "97-1-hr.cg-dialup.net", IPs: []net.IP{{146, 70, 8, 5}, {146, 70, 8, 8}, {146, 70, 8, 9}, {146, 70, 8, 10}, {146, 70, 8, 11}, {146, 70, 8, 12}, {146, 70, 8, 13}, {146, 70, 8, 14}, {146, 70, 8, 15}, {146, 70, 8, 16}}},
|
||||||
{Region: "Cyprus", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 137, 8}, {45, 132, 137, 10}, {45, 132, 137, 16}, {45, 132, 137, 19}, {45, 132, 137, 20}, {45, 132, 137, 21}, {45, 132, 137, 22}, {45, 132, 137, 23}, {45, 132, 137, 25}, {45, 132, 137, 28}}},
|
{Region: "Croatia", Group: "Premium UDP Europe", Hostname: "87-1-hr.cg-dialup.net", 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, 9}, {146, 70, 8, 11}, {146, 70, 8, 13}, {146, 70, 8, 14}, {146, 70, 8, 16}}},
|
||||||
{Region: "Czech Republic", Group: "Premium TCP Europe", IPs: []net.IP{{195, 181, 160, 66}, {195, 181, 160, 72}, {195, 181, 161, 7}, {195, 181, 161, 9}, {195, 181, 161, 10}, {195, 181, 161, 11}, {195, 181, 161, 14}, {195, 181, 161, 17}, {195, 181, 161, 23}, {195, 181, 161, 25}}},
|
{Region: "Cyprus", Group: "Premium TCP Europe", Hostname: "97-1-cy.cg-dialup.net", IPs: []net.IP{{185, 253, 162, 131}, {185, 253, 162, 133}, {185, 253, 162, 135}, {185, 253, 162, 136}, {185, 253, 162, 137}, {185, 253, 162, 139}, {185, 253, 162, 140}, {185, 253, 162, 142}, {185, 253, 162, 143}, {185, 253, 162, 144}}},
|
||||||
{Region: "Czech Republic", Group: "Premium UDP Europe", IPs: []net.IP{{185, 216, 35, 231}, {185, 216, 35, 232}, {185, 216, 35, 235}, {195, 181, 160, 75}, {195, 181, 161, 2}, {195, 181, 161, 8}, {195, 181, 161, 15}, {195, 181, 161, 16}, {195, 181, 161, 23}, {195, 181, 161, 25}}},
|
{Region: "Cyprus", Group: "Premium UDP Europe", Hostname: "87-1-cy.cg-dialup.net", IPs: []net.IP{{185, 253, 162, 131}, {185, 253, 162, 132}, {185, 253, 162, 134}, {185, 253, 162, 135}, {185, 253, 162, 137}, {185, 253, 162, 138}, {185, 253, 162, 140}, {185, 253, 162, 142}, {185, 253, 162, 143}, {185, 253, 162, 144}}},
|
||||||
{Region: "Denmark", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 145, 93}, {37, 120, 194, 39}, {37, 120, 194, 41}, {37, 120, 194, 53}, {95, 174, 65, 166}, {95, 174, 65, 167}, {95, 174, 65, 174}, {185, 206, 224, 230}, {185, 206, 224, 235}, {185, 206, 224, 253}}},
|
{Region: "Czech Republic", Group: "Premium TCP Europe", Hostname: "97-1-cz.cg-dialup.net", IPs: []net.IP{{138, 199, 56, 235}, {138, 199, 56, 236}, {138, 199, 56, 237}, {138, 199, 56, 245}, {138, 199, 56, 246}, {138, 199, 56, 249}, {195, 181, 161, 12}, {195, 181, 161, 16}, {195, 181, 161, 20}, {195, 181, 161, 23}}},
|
||||||
{Region: "Denmark", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 145, 84}, {37, 120, 145, 88}, {37, 120, 194, 38}, {37, 120, 194, 58}, {95, 174, 65, 163}, {95, 174, 65, 165}, {185, 206, 224, 227}, {185, 206, 224, 243}, {185, 206, 224, 245}, {185, 206, 224, 253}}},
|
{Region: "Czech Republic", Group: "Premium UDP Europe", Hostname: "87-1-cz.cg-dialup.net", IPs: []net.IP{{138, 199, 56, 227}, {138, 199, 56, 229}, {138, 199, 56, 231}, {138, 199, 56, 235}, {138, 199, 56, 241}, {138, 199, 56, 247}, {195, 181, 161, 10}, {195, 181, 161, 16}, {195, 181, 161, 18}, {195, 181, 161, 22}}},
|
||||||
{Region: "Egypt", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 139, 7}, {45, 132, 139, 22}, {45, 132, 139, 24}, {45, 132, 139, 27}, {45, 132, 139, 28}, {45, 132, 139, 29}, {188, 214, 122, 36}, {188, 214, 122, 41}, {188, 214, 122, 52}, {188, 214, 122, 56}}},
|
{Region: "Denmark", Group: "Premium TCP Europe", Hostname: "97-1-dk.cg-dialup.net", IPs: []net.IP{{37, 120, 145, 83}, {37, 120, 145, 88}, {37, 120, 145, 93}, {37, 120, 194, 36}, {37, 120, 194, 56}, {37, 120, 194, 57}, {95, 174, 65, 163}, {95, 174, 65, 174}, {185, 206, 224, 238}, {185, 206, 224, 243}}},
|
||||||
{Region: "Egypt", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 139, 7}, {45, 132, 139, 17}, {45, 132, 139, 18}, {45, 132, 139, 22}, {45, 132, 139, 23}, {188, 214, 122, 41}, {188, 214, 122, 48}, {188, 214, 122, 49}, {188, 214, 122, 51}, {188, 214, 122, 61}}},
|
{Region: "Denmark", Group: "Premium UDP Europe", Hostname: "87-1-dk.cg-dialup.net", IPs: []net.IP{{37, 120, 194, 39}, {95, 174, 65, 167}, {95, 174, 65, 170}, {185, 206, 224, 227}, {185, 206, 224, 230}, {185, 206, 224, 236}, {185, 206, 224, 238}, {185, 206, 224, 245}, {185, 206, 224, 250}, {185, 206, 224, 254}}},
|
||||||
{Region: "Estonia", Group: "Premium TCP Europe", IPs: []net.IP{{77, 247, 111, 6}, {77, 247, 111, 11}, {77, 247, 111, 52}, {77, 247, 111, 53}, {77, 247, 111, 55}, {77, 247, 111, 56}, {77, 247, 111, 57}, {77, 247, 111, 60}, {77, 247, 111, 61}, {77, 247, 111, 62}}},
|
{Region: "Egypt", Group: "Premium TCP Europe", Hostname: "97-1-eg.cg-dialup.net", IPs: []net.IP{{188, 214, 122, 40}, {188, 214, 122, 42}, {188, 214, 122, 43}, {188, 214, 122, 45}, {188, 214, 122, 48}, {188, 214, 122, 50}, {188, 214, 122, 52}, {188, 214, 122, 60}, {188, 214, 122, 70}, {188, 214, 122, 73}}},
|
||||||
{Region: "Estonia", Group: "Premium UDP Europe", IPs: []net.IP{{77, 247, 111, 3}, {77, 247, 111, 4}, {77, 247, 111, 5}, {77, 247, 111, 7}, {77, 247, 111, 11}, {77, 247, 111, 12}, {77, 247, 111, 52}, {77, 247, 111, 53}, {77, 247, 111, 55}, {77, 247, 111, 59}}},
|
{Region: "Egypt", Group: "Premium UDP Europe", Hostname: "87-1-eg.cg-dialup.net", IPs: []net.IP{{188, 214, 122, 37}, {188, 214, 122, 38}, {188, 214, 122, 44}, {188, 214, 122, 54}, {188, 214, 122, 57}, {188, 214, 122, 59}, {188, 214, 122, 60}, {188, 214, 122, 61}, {188, 214, 122, 67}, {188, 214, 122, 69}}},
|
||||||
{Region: "Finland", Group: "Premium TCP Europe", IPs: []net.IP{{194, 34, 133, 171}, {194, 34, 133, 172}, {194, 34, 133, 176}, {194, 34, 133, 179}, {194, 34, 133, 180}, {194, 34, 133, 195}, {194, 34, 133, 196}, {194, 34, 133, 204}, {194, 34, 133, 207}, {194, 34, 133, 208}}},
|
{Region: "Estonia", Group: "Premium TCP Europe", Hostname: "97-1-ee.cg-dialup.net", IPs: []net.IP{{95, 153, 32, 83}, {95, 153, 32, 84}, {95, 153, 32, 86}, {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: "Finland", Group: "Premium UDP Europe", IPs: []net.IP{{194, 34, 133, 163}, {194, 34, 133, 164}, {194, 34, 133, 167}, {194, 34, 133, 178}, {194, 34, 133, 192}, {194, 34, 133, 201}, {194, 34, 133, 205}, {194, 34, 133, 206}, {194, 34, 133, 208}, {194, 34, 133, 214}}},
|
{Region: "Estonia", Group: "Premium UDP Europe", Hostname: "87-1-ee.cg-dialup.net", IPs: []net.IP{{95, 153, 32, 83}, {95, 153, 32, 84}, {95, 153, 32, 85}, {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, 94}}},
|
||||||
{Region: "France", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 60, 21}, {84, 17, 60, 33}, {84, 17, 60, 89}, {84, 17, 60, 92}, {84, 17, 60, 114}, {84, 17, 61, 23}, {84, 17, 61, 43}, {84, 17, 61, 111}, {84, 17, 61, 235}, {151, 106, 8, 36}}},
|
{Region: "Finland", Group: "Premium TCP Europe", Hostname: "97-1-fi.cg-dialup.net", IPs: []net.IP{{188, 126, 89, 99}, {188, 126, 89, 102}, {188, 126, 89, 105}, {188, 126, 89, 107}, {188, 126, 89, 108}, {188, 126, 89, 110}, {188, 126, 89, 112}, {188, 126, 89, 115}, {188, 126, 89, 116}, {188, 126, 89, 119}}},
|
||||||
{Region: "France", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 60, 8}, {84, 17, 60, 54}, {84, 17, 60, 161}, {84, 17, 60, 188}, {84, 17, 61, 32}, {84, 17, 61, 101}, {84, 17, 61, 163}, {84, 17, 61, 187}, {84, 17, 61, 213}, {151, 106, 8, 46}}},
|
{Region: "Finland", Group: "Premium UDP Europe", Hostname: "87-1-fi.cg-dialup.net", IPs: []net.IP{{188, 126, 89, 101}, {188, 126, 89, 104}, {188, 126, 89, 109}, {188, 126, 89, 110}, {188, 126, 89, 111}, {188, 126, 89, 113}, {188, 126, 89, 114}, {188, 126, 89, 115}, {188, 126, 89, 122}, {188, 126, 89, 124}}},
|
||||||
{Region: "Georgia", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 138, 7}, {45, 132, 138, 8}, {45, 132, 138, 9}, {45, 132, 138, 12}, {45, 132, 138, 14}, {45, 132, 138, 18}, {45, 132, 138, 19}, {45, 132, 138, 20}, {45, 132, 138, 23}, {45, 132, 138, 27}}},
|
{Region: "France", Group: "Premium TCP Europe", Hostname: "97-1-fr.cg-dialup.net", IPs: []net.IP{{84, 17, 43, 167}, {84, 17, 60, 147}, {84, 17, 60, 155}, {151, 106, 8, 108}, {191, 101, 31, 202}, {191, 101, 31, 254}, {191, 101, 217, 45}, {191, 101, 217, 159}, {191, 101, 217, 211}, {191, 101, 217, 240}}},
|
||||||
{Region: "Georgia", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 138, 7}, {45, 132, 138, 8}, {45, 132, 138, 9}, {45, 132, 138, 10}, {45, 132, 138, 17}, {45, 132, 138, 18}, {45, 132, 138, 25}, {45, 132, 138, 26}, {45, 132, 138, 27}, {45, 132, 138, 28}}},
|
{Region: "France", Group: "Premium UDP Europe", Hostname: "87-1-fr.cg-dialup.net", IPs: []net.IP{{84, 17, 60, 59}, {84, 17, 60, 121}, {191, 101, 31, 81}, {191, 101, 31, 84}, {191, 101, 31, 126}, {191, 101, 31, 127}, {191, 101, 217, 140}, {191, 101, 217, 201}, {191, 101, 217, 206}, {191, 101, 217, 211}}},
|
||||||
{Region: "Germany", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 217, 110}, {84, 17, 48, 75}, {84, 17, 48, 100}, {84, 17, 48, 182}, {84, 17, 49, 129}, {154, 28, 188, 50}, {154, 28, 188, 128}, {178, 162, 208, 155}, {178, 162, 209, 72}, {178, 162, 216, 49}}},
|
{Region: "Georgia", Group: "Premium TCP Europe", Hostname: "97-1-ge.cg-dialup.net", IPs: []net.IP{{95, 181, 236, 131}, {95, 181, 236, 132}, {95, 181, 236, 133}, {95, 181, 236, 134}, {95, 181, 236, 135}, {95, 181, 236, 136}, {95, 181, 236, 138}, {95, 181, 236, 139}, {95, 181, 236, 142}, {95, 181, 236, 144}}},
|
||||||
{Region: "Germany", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 217, 5}, {37, 120, 217, 27}, {84, 17, 48, 20}, {84, 17, 48, 64}, {84, 17, 48, 68}, {84, 17, 48, 182}, {84, 17, 49, 141}, {84, 17, 49, 144}, {154, 28, 188, 90}, {154, 28, 188, 143}}},
|
{Region: "Georgia", Group: "Premium UDP Europe", Hostname: "87-1-ge.cg-dialup.net", IPs: []net.IP{{95, 181, 236, 132}, {95, 181, 236, 133}, {95, 181, 236, 134}, {95, 181, 236, 136}, {95, 181, 236, 137}, {95, 181, 236, 139}, {95, 181, 236, 141}, {95, 181, 236, 142}, {95, 181, 236, 143}, {95, 181, 236, 144}}},
|
||||||
{Region: "Greece", Group: "Premium TCP Europe", IPs: []net.IP{{154, 57, 3, 130}, {154, 57, 3, 132}, {154, 57, 3, 135}, {154, 57, 3, 138}, {154, 57, 3, 140}, {188, 123, 126, 168}, {188, 123, 126, 170}, {188, 123, 126, 176}, {188, 123, 126, 177}, {188, 123, 126, 178}}},
|
{Region: "Germany", Group: "Premium TCP Europe", Hostname: "97-1-de.cg-dialup.net", IPs: []net.IP{{84, 17, 48, 39}, {84, 17, 48, 234}, {84, 17, 49, 106}, {84, 17, 49, 112}, {84, 17, 49, 218}, {154, 28, 188, 35}, {154, 28, 188, 66}, {154, 28, 188, 133}, {154, 28, 188, 144}, {154, 28, 188, 145}}},
|
||||||
{Region: "Greece", Group: "Premium UDP Europe", IPs: []net.IP{{154, 57, 3, 130}, {154, 57, 3, 132}, {154, 57, 3, 133}, {154, 57, 3, 137}, {188, 123, 126, 167}, {188, 123, 126, 170}, {188, 123, 126, 172}, {188, 123, 126, 173}, {188, 123, 126, 174}, {188, 123, 126, 177}}},
|
{Region: "Germany", Group: "Premium UDP Europe", Hostname: "87-1-de.cg-dialup.net", IPs: []net.IP{{84, 17, 48, 41}, {84, 17, 48, 224}, {84, 17, 49, 95}, {84, 17, 49, 236}, {84, 17, 49, 241}, {138, 199, 36, 151}, {154, 13, 1, 177}, {154, 28, 188, 73}, {154, 28, 188, 76}, {154, 28, 188, 93}}},
|
||||||
{Region: "Greenland", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 209, 8}, {45, 131, 209, 9}, {45, 131, 209, 12}, {45, 131, 209, 16}, {45, 131, 209, 18}, {45, 131, 209, 20}, {45, 131, 209, 22}, {45, 131, 209, 23}, {45, 131, 209, 26}, {45, 131, 209, 27}}},
|
{Region: "Greece", Group: "Premium TCP Europe", Hostname: "97-1-gr.cg-dialup.net", IPs: []net.IP{{185, 51, 134, 163}, {185, 51, 134, 165}, {185, 51, 134, 171}, {185, 51, 134, 172}, {185, 51, 134, 245}, {185, 51, 134, 246}, {185, 51, 134, 247}, {185, 51, 134, 249}, {185, 51, 134, 251}, {185, 51, 134, 254}}},
|
||||||
{Region: "Greenland", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 209, 6}, {45, 131, 209, 11}, {45, 131, 209, 19}, {45, 131, 209, 20}, {45, 131, 209, 21}, {45, 131, 209, 23}, {45, 131, 209, 26}, {45, 131, 209, 27}, {45, 131, 209, 28}, {45, 131, 209, 29}}},
|
{Region: "Greece", Group: "Premium UDP Europe", Hostname: "87-1-gr.cg-dialup.net", IPs: []net.IP{{185, 51, 134, 163}, {185, 51, 134, 166}, {185, 51, 134, 173}, {185, 51, 134, 174}, {185, 51, 134, 244}, {185, 51, 134, 246}, {185, 51, 134, 247}, {185, 51, 134, 251}, {185, 51, 134, 252}, {185, 51, 134, 253}}},
|
||||||
{Region: "Hong Kong", Group: "Premium TCP Asia", IPs: []net.IP{{84, 17, 56, 130}, {84, 17, 56, 136}, {84, 17, 56, 148}, {84, 17, 56, 149}, {84, 17, 56, 152}, {84, 17, 56, 172}, {84, 17, 56, 173}, {84, 17, 56, 175}, {84, 17, 56, 176}, {84, 17, 56, 181}}},
|
{Region: "Greenland", Group: "Premium TCP Europe", Hostname: "97-1-gl.cg-dialup.net", IPs: []net.IP{{91, 90, 120, 3}, {91, 90, 120, 4}, {91, 90, 120, 5}, {91, 90, 120, 7}, {91, 90, 120, 8}, {91, 90, 120, 10}, {91, 90, 120, 12}, {91, 90, 120, 13}, {91, 90, 120, 14}, {91, 90, 120, 17}}},
|
||||||
{Region: "Hong Kong", Group: "Premium UDP Asia", IPs: []net.IP{{84, 17, 56, 133}, {84, 17, 56, 140}, {84, 17, 56, 146}, {84, 17, 56, 151}, {84, 17, 56, 162}, {84, 17, 56, 166}, {84, 17, 56, 172}, {84, 17, 56, 174}, {84, 17, 56, 176}, {84, 17, 56, 182}}},
|
{Region: "Greenland", Group: "Premium UDP Europe", Hostname: "87-1-gl.cg-dialup.net", IPs: []net.IP{{91, 90, 120, 3}, {91, 90, 120, 4}, {91, 90, 120, 5}, {91, 90, 120, 7}, {91, 90, 120, 9}, {91, 90, 120, 10}, {91, 90, 120, 12}, {91, 90, 120, 14}, {91, 90, 120, 15}, {91, 90, 120, 16}}},
|
||||||
{Region: "Hungary", Group: "Premium TCP Europe", IPs: []net.IP{{185, 104, 187, 83}, {185, 104, 187, 85}, {185, 104, 187, 86}, {185, 104, 187, 92}, {185, 189, 114, 116}, {185, 189, 114, 119}, {185, 189, 114, 120}, {185, 189, 114, 121}, {185, 189, 114, 124}, {185, 189, 114, 125}}},
|
{Region: "Hong Kong", Group: "Premium TCP Asia", Hostname: "96-1-hk.cg-dialup.net", IPs: []net.IP{{84, 17, 56, 144}, {84, 17, 56, 148}, {84, 17, 56, 153}, {84, 17, 56, 162}, {84, 17, 56, 163}, {84, 17, 56, 169}, {84, 17, 56, 170}, {84, 17, 56, 179}, {84, 17, 56, 180}, {84, 17, 56, 181}}},
|
||||||
{Region: "Hungary", Group: "Premium UDP Europe", IPs: []net.IP{{185, 104, 187, 83}, {185, 104, 187, 85}, {185, 104, 187, 93}, {185, 189, 114, 115}, {185, 189, 114, 118}, {185, 189, 114, 119}, {185, 189, 114, 120}, {185, 189, 114, 121}, {185, 189, 114, 123}, {185, 189, 114, 125}}},
|
{Region: "Hong Kong", Group: "Premium UDP Asia", Hostname: "95-1-hk.cg-dialup.net", IPs: []net.IP{{84, 17, 56, 143}, {84, 17, 56, 147}, {84, 17, 56, 150}, {84, 17, 56, 152}, {84, 17, 56, 161}, {84, 17, 56, 164}, {84, 17, 56, 168}, {84, 17, 56, 179}, {84, 17, 56, 180}, {84, 17, 56, 183}}},
|
||||||
{Region: "Iceland", Group: "Premium TCP Europe", IPs: []net.IP{{213, 167, 139, 19}, {213, 167, 139, 20}, {213, 167, 139, 21}, {213, 167, 139, 22}, {213, 167, 139, 23}, {213, 167, 139, 24}, {213, 167, 139, 25}, {213, 167, 139, 26}, {213, 167, 139, 28}, {213, 167, 139, 30}}},
|
{Region: "Hungary", Group: "Premium TCP Europe", Hostname: "97-1-hu.cg-dialup.net", IPs: []net.IP{{86, 106, 74, 247}, {86, 106, 74, 251}, {86, 106, 74, 253}, {185, 189, 114, 117}, {185, 189, 114, 118}, {185, 189, 114, 119}, {185, 189, 114, 121}, {185, 189, 114, 123}, {185, 189, 114, 125}, {185, 189, 114, 126}}},
|
||||||
{Region: "Iceland", Group: "Premium UDP Europe", IPs: []net.IP{{213, 167, 139, 19}, {213, 167, 139, 21}, {213, 167, 139, 22}, {213, 167, 139, 24}, {213, 167, 139, 25}, {213, 167, 139, 26}, {213, 167, 139, 27}, {213, 167, 139, 28}, {213, 167, 139, 29}, {213, 167, 139, 30}}},
|
{Region: "Hungary", Group: "Premium UDP Europe", Hostname: "87-1-hu.cg-dialup.net", IPs: []net.IP{{86, 106, 74, 245}, {86, 106, 74, 247}, {86, 106, 74, 248}, {86, 106, 74, 249}, {86, 106, 74, 250}, {86, 106, 74, 252}, {86, 106, 74, 253}, {185, 189, 114, 120}, {185, 189, 114, 121}, {185, 189, 114, 122}}},
|
||||||
{Region: "India", Group: "Premium TCP Europe", IPs: []net.IP{{43, 241, 71, 115}, {43, 241, 71, 118}, {43, 241, 71, 120}, {43, 241, 71, 122}, {43, 241, 71, 125}, {43, 241, 71, 147}, {43, 241, 71, 148}, {43, 241, 71, 149}, {43, 241, 71, 154}, {43, 241, 71, 156}}},
|
{Region: "Iceland", Group: "Premium TCP Europe", Hostname: "97-1-is.cg-dialup.net", IPs: []net.IP{{45, 133, 193, 3}, {45, 133, 193, 4}, {45, 133, 193, 6}, {45, 133, 193, 7}, {45, 133, 193, 8}, {45, 133, 193, 10}, {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{{43, 241, 71, 116}, {43, 241, 71, 117}, {43, 241, 71, 118}, {43, 241, 71, 122}, {43, 241, 71, 125}, {43, 241, 71, 147}, {43, 241, 71, 148}, {43, 241, 71, 153}, {43, 241, 71, 155}, {43, 241, 71, 156}}},
|
{Region: "Iceland", Group: "Premium UDP Europe", Hostname: "87-1-is.cg-dialup.net", IPs: []net.IP{{45, 133, 193, 3}, {45, 133, 193, 5}, {45, 133, 193, 6}, {45, 133, 193, 7}, {45, 133, 193, 8}, {45, 133, 193, 9}, {45, 133, 193, 10}, {45, 133, 193, 11}, {45, 133, 193, 13}, {45, 133, 193, 14}}},
|
||||||
{Region: "Indonesia", Group: "Premium TCP Asia", IPs: []net.IP{{113, 20, 29, 243}, {113, 20, 29, 244}, {113, 20, 29, 246}, {113, 20, 29, 247}, {113, 20, 29, 248}, {113, 20, 29, 249}, {113, 20, 29, 250}, {113, 20, 29, 251}, {113, 20, 29, 252}, {113, 20, 29, 254}}},
|
{Region: "India", Group: "Premium TCP Europe", Hostname: "97-1-in.cg-dialup.net", IPs: []net.IP{{103, 13, 112, 68}, {103, 13, 112, 70}, {103, 13, 112, 72}, {103, 13, 112, 74}, {103, 13, 112, 75}, {103, 13, 113, 74}, {103, 13, 113, 79}, {103, 13, 113, 82}, {103, 13, 113, 83}, {103, 13, 113, 84}}},
|
||||||
{Region: "Indonesia", Group: "Premium UDP Asia", IPs: []net.IP{{113, 20, 29, 243}, {113, 20, 29, 245}, {113, 20, 29, 246}, {113, 20, 29, 247}, {113, 20, 29, 248}, {113, 20, 29, 249}, {113, 20, 29, 250}, {113, 20, 29, 251}, {113, 20, 29, 252}, {113, 20, 29, 253}}},
|
{Region: "India", Group: "Premium UDP Europe", Hostname: "87-1-in.cg-dialup.net", IPs: []net.IP{{103, 13, 112, 67}, {103, 13, 112, 70}, {103, 13, 112, 71}, {103, 13, 112, 77}, {103, 13, 112, 80}, {103, 13, 113, 72}, {103, 13, 113, 74}, {103, 13, 113, 75}, {103, 13, 113, 77}, {103, 13, 113, 85}}},
|
||||||
{Region: "Iran", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 4, 10}, {45, 131, 4, 16}, {45, 131, 4, 17}, {45, 131, 4, 18}, {45, 131, 4, 20}, {45, 131, 4, 21}, {45, 131, 4, 22}, {45, 131, 4, 24}, {45, 131, 4, 25}, {45, 131, 4, 28}}},
|
{Region: "Indonesia", Group: "Premium TCP Asia", Hostname: "96-1-id.cg-dialup.net", IPs: []net.IP{{146, 70, 14, 3}, {146, 70, 14, 4}, {146, 70, 14, 5}, {146, 70, 14, 6}, {146, 70, 14, 7}, {146, 70, 14, 10}, {146, 70, 14, 12}, {146, 70, 14, 13}, {146, 70, 14, 15}, {146, 70, 14, 16}}},
|
||||||
{Region: "Iran", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 4, 6}, {45, 131, 4, 8}, {45, 131, 4, 12}, {45, 131, 4, 14}, {45, 131, 4, 15}, {45, 131, 4, 20}, {45, 131, 4, 23}, {45, 131, 4, 24}, {45, 131, 4, 27}, {45, 131, 4, 28}}},
|
{Region: "Indonesia", Group: "Premium UDP Asia", Hostname: "95-1-id.cg-dialup.net", IPs: []net.IP{{146, 70, 14, 3}, {146, 70, 14, 5}, {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, 15}, {146, 70, 14, 16}}},
|
||||||
{Region: "Ireland", Group: "Premium TCP Europe", IPs: []net.IP{{84, 247, 48, 3}, {84, 247, 48, 4}, {84, 247, 48, 5}, {84, 247, 48, 8}, {84, 247, 48, 9}, {84, 247, 48, 10}, {84, 247, 48, 11}, {84, 247, 48, 12}, {84, 247, 48, 19}, {84, 247, 48, 21}}},
|
{Region: "Iran", Group: "Premium TCP Asia", Hostname: "96-1-ir.cg-dialup.net", IPs: []net.IP{{62, 133, 46, 3}, {62, 133, 46, 4}, {62, 133, 46, 5}, {62, 133, 46, 6}, {62, 133, 46, 7}, {62, 133, 46, 8}, {62, 133, 46, 9}, {62, 133, 46, 10}, {62, 133, 46, 14}, {62, 133, 46, 15}}},
|
||||||
{Region: "Ireland", Group: "Premium UDP Europe", IPs: []net.IP{{84, 247, 48, 3}, {84, 247, 48, 13}, {84, 247, 48, 14}, {84, 247, 48, 20}, {84, 247, 48, 21}, {84, 247, 48, 23}, {84, 247, 48, 24}, {84, 247, 48, 25}, {84, 247, 48, 26}, {84, 247, 48, 27}}},
|
{Region: "Iran", Group: "Premium UDP Asia", Hostname: "95-1-ir.cg-dialup.net", IPs: []net.IP{{62, 133, 46, 3}, {62, 133, 46, 4}, {62, 133, 46, 7}, {62, 133, 46, 8}, {62, 133, 46, 11}, {62, 133, 46, 12}, {62, 133, 46, 13}, {62, 133, 46, 14}, {62, 133, 46, 15}, {62, 133, 46, 16}}},
|
||||||
{Region: "Isle of Man", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 140, 7}, {45, 132, 140, 8}, {45, 132, 140, 10}, {45, 132, 140, 11}, {45, 132, 140, 15}, {45, 132, 140, 17}, {45, 132, 140, 18}, {45, 132, 140, 19}, {45, 132, 140, 27}, {45, 132, 140, 28}}},
|
{Region: "Ireland", Group: "Premium TCP Europe", Hostname: "97-1-ie.cg-dialup.net", IPs: []net.IP{{37, 120, 235, 154}, {37, 120, 235, 166}, {37, 120, 235, 174}, {77, 81, 139, 35}, {84, 247, 48, 6}, {84, 247, 48, 19}, {84, 247, 48, 22}, {84, 247, 48, 23}, {84, 247, 48, 25}, {84, 247, 48, 26}}},
|
||||||
{Region: "Isle of Man", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 140, 7}, {45, 132, 140, 9}, {45, 132, 140, 10}, {45, 132, 140, 16}, {45, 132, 140, 19}, {45, 132, 140, 20}, {45, 132, 140, 21}, {45, 132, 140, 25}, {45, 132, 140, 26}, {45, 132, 140, 28}}},
|
{Region: "Ireland", Group: "Premium UDP Europe", Hostname: "87-1-ie.cg-dialup.net", IPs: []net.IP{{37, 120, 235, 147}, {37, 120, 235, 148}, {37, 120, 235, 153}, {37, 120, 235, 158}, {37, 120, 235, 169}, {37, 120, 235, 174}, {84, 247, 48, 8}, {84, 247, 48, 11}, {84, 247, 48, 20}, {84, 247, 48, 23}}},
|
||||||
{Region: "Israel", Group: "Premium TCP Europe", IPs: []net.IP{{160, 116, 0, 163}, {160, 116, 0, 164}, {160, 116, 0, 166}, {160, 116, 0, 167}, {160, 116, 0, 169}, {160, 116, 0, 170}, {160, 116, 0, 171}, {160, 116, 0, 172}, {160, 116, 0, 173}, {160, 116, 0, 174}}},
|
{Region: "Isle of Man", Group: "Premium TCP Europe", Hostname: "97-1-im.cg-dialup.net", IPs: []net.IP{{91, 90, 124, 147}, {91, 90, 124, 149}, {91, 90, 124, 150}, {91, 90, 124, 151}, {91, 90, 124, 152}, {91, 90, 124, 153}, {91, 90, 124, 154}, {91, 90, 124, 156}, {91, 90, 124, 157}, {91, 90, 124, 158}}},
|
||||||
{Region: "Israel", Group: "Premium UDP Europe", IPs: []net.IP{{160, 116, 0, 163}, {160, 116, 0, 165}, {160, 116, 0, 166}, {160, 116, 0, 167}, {160, 116, 0, 168}, {160, 116, 0, 169}, {160, 116, 0, 170}, {160, 116, 0, 172}, {160, 116, 0, 173}, {160, 116, 0, 174}}},
|
{Region: "Isle of Man", Group: "Premium UDP Europe", Hostname: "87-1-im.cg-dialup.net", IPs: []net.IP{{91, 90, 124, 147}, {91, 90, 124, 149}, {91, 90, 124, 150}, {91, 90, 124, 151}, {91, 90, 124, 152}, {91, 90, 124, 153}, {91, 90, 124, 154}, {91, 90, 124, 155}, {91, 90, 124, 156}, {91, 90, 124, 157}}},
|
||||||
{Region: "Italy", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 58, 11}, {84, 17, 58, 19}, {87, 101, 94, 70}, {87, 101, 94, 116}, {185, 217, 71, 133}, {185, 217, 71, 137}, {212, 102, 55, 100}, {212, 102, 55, 123}, {212, 102, 55, 139}, {212, 102, 55, 184}}},
|
{Region: "Israel", Group: "Premium TCP Europe", Hostname: "97-1-il.cg-dialup.net", IPs: []net.IP{{160, 116, 0, 174}, {185, 77, 248, 103}, {185, 77, 248, 111}, {185, 77, 248, 113}, {185, 77, 248, 114}, {185, 77, 248, 124}, {185, 77, 248, 125}, {185, 77, 248, 127}, {185, 77, 248, 128}, {185, 77, 248, 129}}},
|
||||||
{Region: "Italy", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 58, 7}, {84, 17, 58, 100}, {84, 17, 58, 103}, {84, 17, 58, 104}, {87, 101, 94, 116}, {87, 101, 94, 124}, {185, 217, 71, 132}, {185, 217, 71, 134}, {212, 102, 55, 156}, {212, 102, 55, 167}}},
|
{Region: "Israel", Group: "Premium UDP Europe", Hostname: "87-1-il.cg-dialup.net", IPs: []net.IP{{160, 116, 0, 163}, {160, 116, 0, 165}, {160, 116, 0, 172}, {185, 77, 248, 103}, {185, 77, 248, 106}, {185, 77, 248, 114}, {185, 77, 248, 117}, {185, 77, 248, 118}, {185, 77, 248, 126}, {185, 77, 248, 129}}},
|
||||||
{Region: "Japan", Group: "Premium TCP Asia", IPs: []net.IP{{156, 146, 35, 5}, {156, 146, 35, 8}, {156, 146, 35, 9}, {156, 146, 35, 17}, {156, 146, 35, 20}, {156, 146, 35, 33}, {156, 146, 35, 36}, {156, 146, 35, 42}, {156, 146, 35, 45}, {156, 146, 35, 50}}},
|
{Region: "Italy", Group: "Premium TCP Europe", Hostname: "97-1-it.cg-dialup.net", IPs: []net.IP{{84, 17, 58, 21}, {84, 17, 58, 100}, {84, 17, 58, 106}, {84, 17, 58, 111}, {84, 17, 58, 117}, {87, 101, 94, 122}, {212, 102, 55, 100}, {212, 102, 55, 106}, {212, 102, 55, 110}, {212, 102, 55, 122}}},
|
||||||
{Region: "Japan", Group: "Premium UDP Asia", IPs: []net.IP{{156, 146, 35, 6}, {156, 146, 35, 8}, {156, 146, 35, 19}, {156, 146, 35, 22}, {156, 146, 35, 27}, {156, 146, 35, 32}, {156, 146, 35, 35}, {156, 146, 35, 37}, {156, 146, 35, 41}, {156, 146, 35, 47}}},
|
{Region: "Italy", Group: "Premium UDP Europe", Hostname: "87-1-it.cg-dialup.net", IPs: []net.IP{{84, 17, 58, 19}, {84, 17, 58, 95}, {84, 17, 58, 105}, {84, 17, 58, 119}, {84, 17, 58, 120}, {87, 101, 94, 116}, {185, 217, 71, 137}, {185, 217, 71, 138}, {185, 217, 71, 153}, {212, 102, 55, 108}}},
|
||||||
{Region: "Kazakhstan", Group: "Premium TCP Europe", IPs: []net.IP{{45, 133, 88, 7}, {45, 133, 88, 11}, {45, 133, 88, 12}, {45, 133, 88, 13}, {45, 133, 88, 14}, {45, 133, 88, 20}, {45, 133, 88, 23}, {45, 133, 88, 24}, {45, 133, 88, 26}, {45, 133, 88, 28}}},
|
{Region: "Japan", Group: "Premium TCP Asia", Hostname: "96-1-jp.cg-dialup.net", IPs: []net.IP{{156, 146, 35, 6}, {156, 146, 35, 10}, {156, 146, 35, 15}, {156, 146, 35, 22}, {156, 146, 35, 37}, {156, 146, 35, 39}, {156, 146, 35, 40}, {156, 146, 35, 41}, {156, 146, 35, 44}, {156, 146, 35, 50}}},
|
||||||
{Region: "Kazakhstan", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 88, 8}, {45, 133, 88, 12}, {45, 133, 88, 13}, {45, 133, 88, 15}, {45, 133, 88, 19}, {45, 133, 88, 20}, {45, 133, 88, 24}, {45, 133, 88, 25}, {45, 133, 88, 26}, {45, 133, 88, 28}}},
|
{Region: "Japan", Group: "Premium UDP Asia", Hostname: "95-1-jp.cg-dialup.net", IPs: []net.IP{{156, 146, 35, 4}, {156, 146, 35, 14}, {156, 146, 35, 15}, {156, 146, 35, 18}, {156, 146, 35, 25}, {156, 146, 35, 34}, {156, 146, 35, 36}, {156, 146, 35, 46}, {156, 146, 35, 49}, {156, 146, 35, 50}}},
|
||||||
{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: "Kazakhstan", Group: "Premium TCP Europe", Hostname: "97-1-kz.cg-dialup.net", IPs: []net.IP{{62, 133, 47, 131}, {62, 133, 47, 132}, {62, 133, 47, 134}, {62, 133, 47, 136}, {62, 133, 47, 138}, {62, 133, 47, 139}, {62, 133, 47, 140}, {62, 133, 47, 142}, {62, 133, 47, 143}, {62, 133, 47, 144}}},
|
||||||
{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: "Kazakhstan", Group: "Premium UDP Europe", Hostname: "87-1-kz.cg-dialup.net", IPs: []net.IP{{62, 133, 47, 131}, {62, 133, 47, 132}, {62, 133, 47, 133}, {62, 133, 47, 134}, {62, 133, 47, 135}, {62, 133, 47, 138}, {62, 133, 47, 139}, {62, 133, 47, 140}, {62, 133, 47, 142}, {62, 133, 47, 143}}},
|
||||||
{Region: "Korea", Group: "Premium TCP Asia", IPs: []net.IP{{27, 255, 75, 227}, {27, 255, 75, 229}, {27, 255, 75, 233}, {27, 255, 75, 234}, {27, 255, 75, 235}, {27, 255, 75, 236}, {27, 255, 75, 248}, {27, 255, 75, 249}, {27, 255, 75, 251}, {27, 255, 75, 254}}},
|
{Region: "Kenya", Group: "Premium TCP Asia", Hostname: "96-1-ke.cg-dialup.net", 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{{27, 255, 75, 228}, {27, 255, 75, 229}, {27, 255, 75, 230}, {27, 255, 75, 231}, {27, 255, 75, 234}, {27, 255, 75, 235}, {27, 255, 75, 244}, {27, 255, 75, 245}, {27, 255, 75, 246}, {27, 255, 75, 247}}},
|
{Region: "Kenya", Group: "Premium UDP Asia", Hostname: "95-1-ke.cg-dialup.net", 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: "Latvia", Group: "Premium TCP Europe", IPs: []net.IP{{109, 248, 148, 244}, {109, 248, 148, 251}, {109, 248, 148, 252}, {109, 248, 148, 253}, {109, 248, 149, 19}, {109, 248, 149, 20}, {109, 248, 149, 21}, {109, 248, 149, 22}, {109, 248, 149, 24}, {109, 248, 149, 27}}},
|
{Region: "Korea", Group: "Premium TCP Asia", Hostname: "96-1-kr.cg-dialup.net", IPs: []net.IP{{79, 110, 55, 131}, {79, 110, 55, 134}, {79, 110, 55, 141}, {79, 110, 55, 147}, {79, 110, 55, 148}, {79, 110, 55, 151}, {79, 110, 55, 152}, {79, 110, 55, 153}, {79, 110, 55, 155}, {79, 110, 55, 157}}},
|
||||||
{Region: "Latvia", Group: "Premium UDP Europe", IPs: []net.IP{{109, 248, 148, 248}, {109, 248, 148, 252}, {109, 248, 148, 253}, {109, 248, 148, 254}, {109, 248, 149, 19}, {109, 248, 149, 20}, {109, 248, 149, 21}, {109, 248, 149, 24}, {109, 248, 149, 28}, {109, 248, 149, 29}}},
|
{Region: "Korea", Group: "Premium UDP Asia", Hostname: "95-1-kr.cg-dialup.net", IPs: []net.IP{{79, 110, 55, 131}, {79, 110, 55, 133}, {79, 110, 55, 134}, {79, 110, 55, 136}, {79, 110, 55, 138}, {79, 110, 55, 140}, {79, 110, 55, 149}, {79, 110, 55, 151}, {79, 110, 55, 152}, {79, 110, 55, 157}}},
|
||||||
{Region: "Liechtenstein", Group: "Premium TCP Europe", IPs: []net.IP{{45, 139, 48, 6}, {45, 139, 48, 7}, {45, 139, 48, 9}, {45, 139, 48, 16}, {45, 139, 48, 17}, {45, 139, 48, 19}, {45, 139, 48, 20}, {45, 139, 48, 22}, {45, 139, 48, 23}, {45, 139, 48, 27}}},
|
{Region: "Latvia", Group: "Premium TCP Europe", Hostname: "97-1-lv.cg-dialup.net", IPs: []net.IP{{109, 248, 148, 244}, {109, 248, 148, 245}, {109, 248, 148, 246}, {109, 248, 148, 247}, {109, 248, 148, 249}, {109, 248, 148, 250}, {109, 248, 148, 253}, {109, 248, 149, 22}, {109, 248, 149, 24}, {109, 248, 149, 25}}},
|
||||||
{Region: "Liechtenstein", Group: "Premium UDP Europe", IPs: []net.IP{{45, 139, 48, 9}, {45, 139, 48, 12}, {45, 139, 48, 15}, {45, 139, 48, 16}, {45, 139, 48, 17}, {45, 139, 48, 18}, {45, 139, 48, 24}, {45, 139, 48, 26}, {45, 139, 48, 28}, {45, 139, 48, 29}}},
|
{Region: "Latvia", Group: "Premium UDP Europe", Hostname: "87-1-lv.cg-dialup.net", IPs: []net.IP{{109, 248, 148, 248}, {109, 248, 148, 250}, {109, 248, 148, 254}, {109, 248, 149, 19}, {109, 248, 149, 20}, {109, 248, 149, 22}, {109, 248, 149, 24}, {109, 248, 149, 26}, {109, 248, 149, 28}, {109, 248, 149, 30}}},
|
||||||
{Region: "Lithuania", Group: "Premium TCP Europe", IPs: []net.IP{{85, 206, 162, 211}, {85, 206, 162, 213}, {85, 206, 162, 214}, {85, 206, 162, 216}, {85, 206, 162, 219}, {85, 206, 162, 220}, {85, 206, 165, 18}, {85, 206, 165, 20}, {85, 206, 165, 24}, {85, 206, 165, 25}}},
|
{Region: "Liechtenstein", Group: "Premium UDP Europe", Hostname: "87-1-li.cg-dialup.net", IPs: []net.IP{{91, 90, 122, 131}, {91, 90, 122, 134}, {91, 90, 122, 137}, {91, 90, 122, 138}, {91, 90, 122, 139}, {91, 90, 122, 140}, {91, 90, 122, 141}, {91, 90, 122, 142}, {91, 90, 122, 144}, {91, 90, 122, 145}}},
|
||||||
{Region: "Lithuania", Group: "Premium UDP Europe", IPs: []net.IP{{85, 206, 162, 210}, {85, 206, 162, 211}, {85, 206, 162, 214}, {85, 206, 162, 215}, {85, 206, 162, 220}, {85, 206, 162, 221}, {85, 206, 162, 222}, {85, 206, 165, 18}, {85, 206, 165, 25}, {85, 206, 165, 26}}},
|
{Region: "Lithuania", Group: "Premium TCP Europe", Hostname: "97-1-lt.cg-dialup.net", IPs: []net.IP{{85, 206, 162, 212}, {85, 206, 162, 215}, {85, 206, 162, 219}, {85, 206, 162, 222}, {85, 206, 165, 17}, {85, 206, 165, 23}, {85, 206, 165, 25}, {85, 206, 165, 26}, {85, 206, 165, 30}, {85, 206, 165, 31}}},
|
||||||
{Region: "Luxembourg", Group: "Premium TCP Europe", IPs: []net.IP{{5, 253, 204, 5}, {5, 253, 204, 6}, {5, 253, 204, 9}, {5, 253, 204, 10}, {5, 253, 204, 12}, {5, 253, 204, 14}, {5, 253, 204, 20}, {5, 253, 204, 23}, {5, 253, 204, 29}, {5, 253, 204, 30}}},
|
{Region: "Lithuania", Group: "Premium UDP Europe", Hostname: "87-1-lt.cg-dialup.net", IPs: []net.IP{{85, 206, 162, 209}, {85, 206, 162, 210}, {85, 206, 162, 211}, {85, 206, 162, 213}, {85, 206, 162, 214}, {85, 206, 162, 217}, {85, 206, 162, 218}, {85, 206, 162, 220}, {85, 206, 165, 26}, {85, 206, 165, 30}}},
|
||||||
{Region: "Luxembourg", Group: "Premium UDP Europe", IPs: []net.IP{{5, 253, 204, 7}, {5, 253, 204, 11}, {5, 253, 204, 20}, {5, 253, 204, 22}, {5, 253, 204, 23}, {5, 253, 204, 26}, {5, 253, 204, 27}, {5, 253, 204, 28}, {5, 253, 204, 29}, {5, 253, 204, 30}}},
|
{Region: "Luxembourg", Group: "Premium UDP Europe", Hostname: "87-1-lu.cg-dialup.net", IPs: []net.IP{{5, 253, 204, 7}, {5, 253, 204, 10}, {5, 253, 204, 12}, {5, 253, 204, 23}, {5, 253, 204, 26}, {5, 253, 204, 30}, {5, 253, 204, 37}, {5, 253, 204, 39}, {5, 253, 204, 44}, {5, 253, 204, 45}}},
|
||||||
{Region: "Macao", Group: "Premium TCP Asia", IPs: []net.IP{{45, 137, 197, 8}, {45, 137, 197, 9}, {45, 137, 197, 12}, {45, 137, 197, 14}, {45, 137, 197, 17}, {45, 137, 197, 33}, {45, 137, 197, 35}, {45, 137, 197, 42}, {45, 137, 197, 45}, {45, 137, 197, 47}}},
|
{Region: "Macao", Group: "Premium TCP Asia", Hostname: "96-1-mo.cg-dialup.net", IPs: []net.IP{{84, 252, 92, 131}, {84, 252, 92, 133}, {84, 252, 92, 135}, {84, 252, 92, 137}, {84, 252, 92, 138}, {84, 252, 92, 139}, {84, 252, 92, 141}, {84, 252, 92, 142}, {84, 252, 92, 144}, {84, 252, 92, 145}}},
|
||||||
{Region: "Macao", Group: "Premium UDP Asia", IPs: []net.IP{{45, 137, 197, 2}, {45, 137, 197, 7}, {45, 137, 197, 18}, {45, 137, 197, 19}, {45, 137, 197, 28}, {45, 137, 197, 30}, {45, 137, 197, 33}, {45, 137, 197, 35}, {45, 137, 197, 44}, {45, 137, 197, 47}}},
|
{Region: "Macao", Group: "Premium UDP Asia", Hostname: "95-1-mo.cg-dialup.net", IPs: []net.IP{{84, 252, 92, 132}, {84, 252, 92, 134}, {84, 252, 92, 135}, {84, 252, 92, 136}, {84, 252, 92, 137}, {84, 252, 92, 139}, {84, 252, 92, 141}, {84, 252, 92, 143}, {84, 252, 92, 144}, {84, 252, 92, 145}}},
|
||||||
{Region: "Macedonia", Group: "Premium TCP Europe", IPs: []net.IP{{185, 225, 28, 3}, {185, 225, 28, 4}, {185, 225, 28, 5}, {185, 225, 28, 6}, {185, 225, 28, 7}, {185, 225, 28, 8}, {185, 225, 28, 9}, {185, 225, 28, 10}, {185, 225, 28, 11}, {185, 225, 28, 12}}},
|
{Region: "Macedonia", Group: "Premium TCP Europe", Hostname: "97-1-mk.cg-dialup.net", IPs: []net.IP{{185, 225, 28, 3}, {185, 225, 28, 4}, {185, 225, 28, 5}, {185, 225, 28, 6}, {185, 225, 28, 7}, {185, 225, 28, 8}, {185, 225, 28, 9}, {185, 225, 28, 10}, {185, 225, 28, 11}, {185, 225, 28, 12}}},
|
||||||
{Region: "Macedonia", Group: "Premium UDP Europe", IPs: []net.IP{{185, 225, 28, 3}, {185, 225, 28, 4}, {185, 225, 28, 5}, {185, 225, 28, 6}, {185, 225, 28, 7}, {185, 225, 28, 8}, {185, 225, 28, 9}, {185, 225, 28, 10}, {185, 225, 28, 11}, {185, 225, 28, 12}}},
|
{Region: "Macedonia", Group: "Premium UDP Europe", Hostname: "87-1-mk.cg-dialup.net", IPs: []net.IP{{185, 225, 28, 3}, {185, 225, 28, 4}, {185, 225, 28, 5}, {185, 225, 28, 6}, {185, 225, 28, 7}, {185, 225, 28, 8}, {185, 225, 28, 9}, {185, 225, 28, 10}, {185, 225, 28, 11}, {185, 225, 28, 12}}},
|
||||||
{Region: "Malaysia", Group: "Premium TCP Asia", IPs: []net.IP{{139, 5, 177, 69}, {139, 5, 177, 70}, {139, 5, 177, 71}, {139, 5, 177, 72}, {139, 5, 177, 73}, {139, 5, 177, 74}, {139, 5, 177, 75}, {139, 5, 177, 76}, {139, 5, 177, 77}, {139, 5, 177, 78}}},
|
{Region: "Malaysia", Group: "Premium TCP Asia", Hostname: "96-1-my.cg-dialup.net", IPs: []net.IP{{146, 70, 15, 4}, {146, 70, 15, 6}, {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, 15}, {146, 70, 15, 16}}},
|
||||||
{Region: "Malaysia", Group: "Premium UDP Asia", IPs: []net.IP{{139, 5, 177, 69}, {139, 5, 177, 70}, {139, 5, 177, 71}, {139, 5, 177, 72}, {139, 5, 177, 73}, {139, 5, 177, 74}, {139, 5, 177, 75}, {139, 5, 177, 76}, {139, 5, 177, 77}, {139, 5, 177, 78}}},
|
{Region: "Malaysia", Group: "Premium UDP Asia", Hostname: "95-1-my.cg-dialup.net", IPs: []net.IP{{146, 70, 15, 3}, {146, 70, 15, 4}, {146, 70, 15, 5}, {146, 70, 15, 6}, {146, 70, 15, 7}, {146, 70, 15, 8}, {146, 70, 15, 10}, {146, 70, 15, 12}, {146, 70, 15, 15}, {146, 70, 15, 16}}},
|
||||||
{Region: "Malta", Group: "Premium TCP Europe", IPs: []net.IP{{45, 137, 198, 6}, {45, 137, 198, 8}, {45, 137, 198, 9}, {45, 137, 198, 11}, {45, 137, 198, 16}, {45, 137, 198, 18}, {45, 137, 198, 22}, {45, 137, 198, 23}, {45, 137, 198, 25}, {45, 137, 198, 27}}},
|
{Region: "Malta", Group: "Premium TCP Europe", Hostname: "97-1-mt.cg-dialup.net", IPs: []net.IP{{176, 125, 230, 133}, {176, 125, 230, 135}, {176, 125, 230, 136}, {176, 125, 230, 137}, {176, 125, 230, 138}, {176, 125, 230, 140}, {176, 125, 230, 142}, {176, 125, 230, 143}, {176, 125, 230, 144}, {176, 125, 230, 145}}},
|
||||||
{Region: "Malta", Group: "Premium UDP Europe", IPs: []net.IP{{45, 137, 198, 8}, {45, 137, 198, 10}, {45, 137, 198, 12}, {45, 137, 198, 13}, {45, 137, 198, 16}, {45, 137, 198, 17}, {45, 137, 198, 21}, {45, 137, 198, 23}, {45, 137, 198, 26}, {45, 137, 198, 28}}},
|
{Region: "Malta", Group: "Premium UDP Europe", Hostname: "87-1-mt.cg-dialup.net", IPs: []net.IP{{176, 125, 230, 131}, {176, 125, 230, 133}, {176, 125, 230, 134}, {176, 125, 230, 136}, {176, 125, 230, 137}, {176, 125, 230, 138}, {176, 125, 230, 139}, {176, 125, 230, 140}, {176, 125, 230, 144}, {176, 125, 230, 145}}},
|
||||||
{Region: "Mexico", Group: "Premium TCP USA", IPs: []net.IP{{45, 133, 180, 99}, {45, 133, 180, 106}, {45, 133, 180, 107}, {45, 133, 180, 108}, {45, 133, 180, 115}, {45, 133, 180, 118}, {45, 133, 180, 119}, {45, 133, 180, 121}, {45, 133, 180, 122}, {45, 133, 180, 123}}},
|
{Region: "Mexico", Group: "Premium TCP USA", Hostname: "93-1-mx.cg-dialup.net", IPs: []net.IP{{77, 81, 142, 132}, {77, 81, 142, 134}, {77, 81, 142, 136}, {77, 81, 142, 139}, {77, 81, 142, 142}, {77, 81, 142, 154}, {77, 81, 142, 155}, {77, 81, 142, 157}, {77, 81, 142, 158}, {77, 81, 142, 159}}},
|
||||||
{Region: "Mexico", Group: "Premium UDP USA", IPs: []net.IP{{45, 133, 180, 99}, {45, 133, 180, 101}, {45, 133, 180, 104}, {45, 133, 180, 105}, {45, 133, 180, 106}, {45, 133, 180, 115}, {45, 133, 180, 117}, {45, 133, 180, 118}, {45, 133, 180, 120}, {45, 133, 180, 121}}},
|
{Region: "Mexico", Group: "Premium UDP USA", Hostname: "94-1-mx.cg-dialup.net", IPs: []net.IP{{77, 81, 142, 130}, {77, 81, 142, 131}, {77, 81, 142, 132}, {77, 81, 142, 139}, {77, 81, 142, 141}, {77, 81, 142, 142}, {77, 81, 142, 146}, {77, 81, 142, 147}, {77, 81, 142, 154}, {77, 81, 142, 159}}},
|
||||||
{Region: "Moldova", Group: "Premium TCP Europe", IPs: []net.IP{{178, 175, 130, 243}, {178, 175, 130, 245}, {178, 175, 130, 250}, {178, 175, 130, 251}, {178, 175, 130, 253}, {178, 175, 130, 254}, {178, 175, 142, 131}, {178, 175, 142, 132}, {178, 175, 142, 133}, {178, 175, 142, 134}}},
|
{Region: "Moldova", Group: "Premium TCP Europe", Hostname: "97-1-md.cg-dialup.net", IPs: []net.IP{{178, 175, 130, 243}, {178, 175, 130, 244}, {178, 175, 130, 245}, {178, 175, 130, 246}, {178, 175, 130, 251}, {178, 175, 130, 254}, {178, 175, 142, 131}, {178, 175, 142, 132}, {178, 175, 142, 133}, {178, 175, 142, 134}}},
|
||||||
{Region: "Moldova", Group: "Premium UDP Europe", IPs: []net.IP{{178, 175, 130, 243}, {178, 175, 130, 244}, {178, 175, 130, 245}, {178, 175, 130, 246}, {178, 175, 130, 250}, {178, 175, 130, 251}, {178, 175, 130, 253}, {178, 175, 130, 254}, {178, 175, 142, 132}, {178, 175, 142, 133}}},
|
{Region: "Moldova", Group: "Premium UDP Europe", Hostname: "87-1-md.cg-dialup.net", IPs: []net.IP{{178, 175, 130, 243}, {178, 175, 130, 244}, {178, 175, 130, 246}, {178, 175, 130, 250}, {178, 175, 130, 251}, {178, 175, 130, 253}, {178, 175, 130, 254}, {178, 175, 142, 132}, {178, 175, 142, 133}, {178, 175, 142, 134}}},
|
||||||
{Region: "Monaco", Group: "Premium TCP Europe", IPs: []net.IP{{45, 137, 199, 7}, {45, 137, 199, 9}, {45, 137, 199, 11}, {45, 137, 199, 12}, {45, 137, 199, 14}, {45, 137, 199, 16}, {45, 137, 199, 20}, {45, 137, 199, 24}, {45, 137, 199, 25}, {45, 137, 199, 29}}},
|
{Region: "Monaco", Group: "Premium TCP Europe", Hostname: "97-1-mc.cg-dialup.net", IPs: []net.IP{{95, 181, 233, 131}, {95, 181, 233, 132}, {95, 181, 233, 133}, {95, 181, 233, 137}, {95, 181, 233, 138}, {95, 181, 233, 139}, {95, 181, 233, 140}, {95, 181, 233, 141}, {95, 181, 233, 143}, {95, 181, 233, 144}}},
|
||||||
{Region: "Monaco", Group: "Premium UDP Europe", IPs: []net.IP{{45, 137, 199, 6}, {45, 137, 199, 7}, {45, 137, 199, 10}, {45, 137, 199, 16}, {45, 137, 199, 18}, {45, 137, 199, 22}, {45, 137, 199, 23}, {45, 137, 199, 24}, {45, 137, 199, 25}, {45, 137, 199, 27}}},
|
{Region: "Monaco", Group: "Premium UDP Europe", Hostname: "87-1-mc.cg-dialup.net", IPs: []net.IP{{95, 181, 233, 132}, {95, 181, 233, 135}, {95, 181, 233, 136}, {95, 181, 233, 137}, {95, 181, 233, 138}, {95, 181, 233, 139}, {95, 181, 233, 141}, {95, 181, 233, 142}, {95, 181, 233, 143}, {95, 181, 233, 144}}},
|
||||||
{Region: "Mongolia", Group: "Premium TCP Asia", IPs: []net.IP{{45, 139, 51, 4}, {45, 139, 51, 13}, {45, 139, 51, 14}, {45, 139, 51, 17}, {45, 139, 51, 18}, {45, 139, 51, 22}, {45, 139, 51, 23}, {45, 139, 51, 32}, {45, 139, 51, 44}, {45, 139, 51, 48}}},
|
{Region: "Mongolia", Group: "Premium TCP Asia", Hostname: "96-1-mn.cg-dialup.net", IPs: []net.IP{{185, 253, 163, 132}, {185, 253, 163, 133}, {185, 253, 163, 135}, {185, 253, 163, 136}, {185, 253, 163, 139}, {185, 253, 163, 140}, {185, 253, 163, 141}, {185, 253, 163, 142}, {185, 253, 163, 143}, {185, 253, 163, 144}}},
|
||||||
{Region: "Mongolia", Group: "Premium UDP Asia", IPs: []net.IP{{45, 139, 51, 3}, {45, 139, 51, 6}, {45, 139, 51, 11}, {45, 139, 51, 12}, {45, 139, 51, 17}, {45, 139, 51, 20}, {45, 139, 51, 28}, {45, 139, 51, 30}, {45, 139, 51, 37}, {45, 139, 51, 41}}},
|
{Region: "Mongolia", Group: "Premium UDP Asia", Hostname: "95-1-mn.cg-dialup.net", IPs: []net.IP{{185, 253, 163, 131}, {185, 253, 163, 133}, {185, 253, 163, 134}, {185, 253, 163, 137}, {185, 253, 163, 138}, {185, 253, 163, 139}, {185, 253, 163, 140}, {185, 253, 163, 141}, {185, 253, 163, 142}, {185, 253, 163, 144}}},
|
||||||
{Region: "Montenegro", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 208, 6}, {45, 131, 208, 7}, {45, 131, 208, 8}, {45, 131, 208, 9}, {45, 131, 208, 10}, {45, 131, 208, 13}, {45, 131, 208, 14}, {45, 131, 208, 20}, {45, 131, 208, 24}, {45, 131, 208, 28}}},
|
{Region: "Montenegro", Group: "Premium TCP Europe", Hostname: "97-1-me.cg-dialup.net", IPs: []net.IP{{176, 125, 229, 131}, {176, 125, 229, 135}, {176, 125, 229, 137}, {176, 125, 229, 138}, {176, 125, 229, 140}, {176, 125, 229, 141}, {176, 125, 229, 142}, {176, 125, 229, 143}, {176, 125, 229, 144}, {176, 125, 229, 145}}},
|
||||||
{Region: "Montenegro", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 208, 6}, {45, 131, 208, 8}, {45, 131, 208, 9}, {45, 131, 208, 12}, {45, 131, 208, 16}, {45, 131, 208, 20}, {45, 131, 208, 22}, {45, 131, 208, 24}, {45, 131, 208, 26}, {45, 131, 208, 28}}},
|
{Region: "Montenegro", Group: "Premium UDP Europe", Hostname: "87-1-me.cg-dialup.net", IPs: []net.IP{{176, 125, 229, 131}, {176, 125, 229, 134}, {176, 125, 229, 136}, {176, 125, 229, 137}, {176, 125, 229, 138}, {176, 125, 229, 139}, {176, 125, 229, 140}, {176, 125, 229, 141}, {176, 125, 229, 143}, {176, 125, 229, 144}}},
|
||||||
{Region: "Morocco", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 211, 6}, {45, 131, 211, 9}, {45, 131, 211, 11}, {45, 131, 211, 13}, {45, 131, 211, 18}, {45, 131, 211, 21}, {45, 131, 211, 22}, {45, 131, 211, 23}, {45, 131, 211, 28}, {45, 131, 211, 29}}},
|
{Region: "Morocco", Group: "Premium TCP Europe", Hostname: "97-1-ma.cg-dialup.net", IPs: []net.IP{{95, 181, 232, 132}, {95, 181, 232, 133}, {95, 181, 232, 134}, {95, 181, 232, 136}, {95, 181, 232, 137}, {95, 181, 232, 138}, {95, 181, 232, 139}, {95, 181, 232, 140}, {95, 181, 232, 141}, {95, 181, 232, 144}}},
|
||||||
{Region: "Morocco", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 211, 9}, {45, 131, 211, 10}, {45, 131, 211, 13}, {45, 131, 211, 18}, {45, 131, 211, 20}, {45, 131, 211, 21}, {45, 131, 211, 25}, {45, 131, 211, 26}, {45, 131, 211, 28}, {45, 131, 211, 29}}},
|
{Region: "Morocco", Group: "Premium UDP Europe", Hostname: "87-1-ma.cg-dialup.net", IPs: []net.IP{{95, 181, 232, 131}, {95, 181, 232, 132}, {95, 181, 232, 133}, {95, 181, 232, 135}, {95, 181, 232, 137}, {95, 181, 232, 139}, {95, 181, 232, 140}, {95, 181, 232, 141}, {95, 181, 232, 142}, {95, 181, 232, 143}}},
|
||||||
{Region: "Netherlands", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 47, 3}, {84, 17, 47, 20}, {84, 17, 47, 39}, {84, 17, 47, 42}, {84, 17, 47, 74}, {84, 17, 47, 111}, {139, 28, 217, 199}, {185, 132, 177, 136}, {190, 2, 149, 196}, {195, 181, 172, 69}}},
|
{Region: "Netherlands", Group: "Premium TCP Europe", Hostname: "97-1-nl.cg-dialup.net", IPs: []net.IP{{84, 17, 47, 98}, {181, 214, 206, 22}, {181, 214, 206, 27}, {181, 214, 206, 36}, {195, 78, 54, 10}, {195, 78, 54, 20}, {195, 78, 54, 43}, {195, 78, 54, 50}, {195, 78, 54, 119}, {195, 181, 172, 78}}},
|
||||||
{Region: "Netherlands", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 47, 4}, {84, 17, 47, 8}, {84, 17, 47, 12}, {84, 17, 47, 38}, {84, 17, 47, 68}, {185, 132, 177, 235}, {190, 2, 149, 28}, {190, 2, 149, 30}, {190, 2, 149, 208}, {195, 181, 172, 79}}},
|
{Region: "Netherlands", Group: "Premium UDP Europe", Hostname: "87-1-nl.cg-dialup.net", IPs: []net.IP{{84, 17, 47, 110}, {181, 214, 206, 29}, {181, 214, 206, 42}, {195, 78, 54, 8}, {195, 78, 54, 19}, {195, 78, 54, 47}, {195, 78, 54, 110}, {195, 78, 54, 141}, {195, 78, 54, 143}, {195, 78, 54, 157}}},
|
||||||
{Region: "New Zealand", Group: "Premium TCP Asia", IPs: []net.IP{{103, 231, 91, 131}, {103, 231, 91, 132}, {103, 231, 91, 133}, {103, 231, 91, 134}, {103, 231, 91, 135}, {103, 231, 91, 136}, {103, 231, 91, 137}, {103, 231, 91, 138}, {103, 231, 91, 139}, {103, 231, 91, 140}}},
|
{Region: "New Zealand", Group: "Premium TCP Asia", Hostname: "96-1-nz.cg-dialup.net", IPs: []net.IP{{43, 250, 207, 98}, {43, 250, 207, 99}, {43, 250, 207, 100}, {43, 250, 207, 101}, {43, 250, 207, 102}, {43, 250, 207, 103}, {43, 250, 207, 105}, {43, 250, 207, 106}, {43, 250, 207, 108}, {43, 250, 207, 109}}},
|
||||||
{Region: "New Zealand", Group: "Premium UDP Asia", IPs: []net.IP{{103, 231, 91, 131}, {103, 231, 91, 132}, {103, 231, 91, 133}, {103, 231, 91, 134}, {103, 231, 91, 135}, {103, 231, 91, 136}, {103, 231, 91, 137}, {103, 231, 91, 138}, {103, 231, 91, 139}, {103, 231, 91, 140}}},
|
{Region: "New Zealand", Group: "Premium UDP Asia", Hostname: "95-1-nz.cg-dialup.net", IPs: []net.IP{{43, 250, 207, 98}, {43, 250, 207, 99}, {43, 250, 207, 102}, {43, 250, 207, 104}, {43, 250, 207, 105}, {43, 250, 207, 106}, {43, 250, 207, 107}, {43, 250, 207, 108}, {43, 250, 207, 109}, {43, 250, 207, 110}}},
|
||||||
{Region: "Nigeria", Group: "Premium TCP Europe", IPs: []net.IP{{45, 137, 196, 6}, {45, 137, 196, 10}, {45, 137, 196, 12}, {45, 137, 196, 13}, {45, 137, 196, 15}, {45, 137, 196, 16}, {45, 137, 196, 17}, {45, 137, 196, 20}, {45, 137, 196, 24}, {45, 137, 196, 29}}},
|
{Region: "Nigeria", Group: "Premium TCP Europe", Hostname: "97-1-ng.cg-dialup.net", 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, 75}, {102, 165, 25, 76}, {102, 165, 25, 77}, {102, 165, 25, 78}}},
|
||||||
{Region: "Nigeria", Group: "Premium UDP Europe", IPs: []net.IP{{45, 137, 196, 6}, {45, 137, 196, 7}, {45, 137, 196, 8}, {45, 137, 196, 9}, {45, 137, 196, 12}, {45, 137, 196, 13}, {45, 137, 196, 14}, {45, 137, 196, 20}, {45, 137, 196, 28}, {45, 137, 196, 29}}},
|
{Region: "Nigeria", Group: "Premium UDP Europe", Hostname: "87-1-ng.cg-dialup.net", 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, 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, 135}, {45, 12, 223, 136}, {45, 12, 223, 138}, {185, 206, 225, 231}, {185, 206, 225, 233}, {185, 253, 97, 235}, {185, 253, 97, 236}, {185, 253, 97, 247}, {185, 253, 97, 253}}},
|
{Region: "Norway", Group: "Premium TCP Europe", Hostname: "97-1-no.cg-dialup.net", IPs: []net.IP{{45, 12, 223, 137}, {45, 12, 223, 140}, {185, 206, 225, 29}, {185, 206, 225, 231}, {185, 253, 97, 234}, {185, 253, 97, 236}, {185, 253, 97, 238}, {185, 253, 97, 244}, {185, 253, 97, 250}, {185, 253, 97, 254}}},
|
||||||
{Region: "Norway", Group: "Premium UDP Europe", IPs: []net.IP{{45, 12, 223, 132}, {45, 12, 223, 137}, {45, 12, 223, 138}, {45, 12, 223, 141}, {185, 206, 225, 27}, {185, 206, 225, 30}, {185, 253, 97, 245}, {185, 253, 97, 246}, {185, 253, 97, 248}, {185, 253, 97, 249}}},
|
{Region: "Norway", Group: "Premium UDP Europe", Hostname: "87-1-no.cg-dialup.net", IPs: []net.IP{{45, 12, 223, 133}, {45, 12, 223, 134}, {45, 12, 223, 142}, {185, 206, 225, 227}, {185, 206, 225, 228}, {185, 206, 225, 231}, {185, 206, 225, 235}, {185, 253, 97, 237}, {185, 253, 97, 246}, {185, 253, 97, 254}}},
|
||||||
{Region: "Pakistan", Group: "Premium TCP Europe", IPs: []net.IP{{103, 76, 3, 244}, {103, 76, 3, 245}, {103, 76, 3, 246}, {103, 76, 3, 247}, {103, 76, 3, 248}, {103, 76, 3, 249}, {103, 76, 3, 250}, {103, 76, 3, 251}, {103, 76, 3, 252}, {103, 76, 3, 253}}},
|
{Region: "Pakistan", Group: "Premium TCP Asia", Hostname: "96-1-pk.cg-dialup.net", IPs: []net.IP{{146, 70, 12, 3}, {146, 70, 12, 4}, {146, 70, 12, 6}, {146, 70, 12, 8}, {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: "Pakistan", Group: "Premium UDP Europe", IPs: []net.IP{{103, 76, 3, 244}, {103, 76, 3, 245}, {103, 76, 3, 246}, {103, 76, 3, 247}, {103, 76, 3, 248}, {103, 76, 3, 249}, {103, 76, 3, 250}, {103, 76, 3, 251}, {103, 76, 3, 252}, {103, 76, 3, 253}}},
|
{Region: "Pakistan", Group: "Premium UDP Asia", Hostname: "95-1-pk.cg-dialup.net", IPs: []net.IP{{146, 70, 12, 4}, {146, 70, 12, 5}, {146, 70, 12, 6}, {146, 70, 12, 7}, {146, 70, 12, 8}, {146, 70, 12, 10}, {146, 70, 12, 11}, {146, 70, 12, 12}, {146, 70, 12, 13}, {146, 70, 12, 14}}},
|
||||||
{Region: "Panama", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 210, 9}, {45, 131, 210, 10}, {45, 131, 210, 14}, {45, 131, 210, 15}, {45, 131, 210, 16}, {45, 131, 210, 21}, {45, 131, 210, 22}, {45, 131, 210, 23}, {45, 131, 210, 24}, {45, 131, 210, 26}}},
|
{Region: "Panama", Group: "Premium TCP Europe", Hostname: "97-1-pa.cg-dialup.net", IPs: []net.IP{{91, 90, 126, 131}, {91, 90, 126, 132}, {91, 90, 126, 133}, {91, 90, 126, 134}, {91, 90, 126, 136}, {91, 90, 126, 138}, {91, 90, 126, 139}, {91, 90, 126, 141}, {91, 90, 126, 142}, {91, 90, 126, 145}}},
|
||||||
{Region: "Panama", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 210, 6}, {45, 131, 210, 8}, {45, 131, 210, 9}, {45, 131, 210, 12}, {45, 131, 210, 14}, {45, 131, 210, 18}, {45, 131, 210, 19}, {45, 131, 210, 25}, {45, 131, 210, 28}, {45, 131, 210, 29}}},
|
{Region: "Panama", Group: "Premium UDP Europe", Hostname: "87-1-pa.cg-dialup.net", IPs: []net.IP{{91, 90, 126, 131}, {91, 90, 126, 133}, {91, 90, 126, 134}, {91, 90, 126, 135}, {91, 90, 126, 136}, {91, 90, 126, 138}, {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, 39}, {188, 214, 125, 45}, {188, 214, 125, 47}, {188, 214, 125, 49}, {188, 214, 125, 50}, {188, 214, 125, 51}, {188, 214, 125, 53}, {188, 214, 125, 57}, {188, 214, 125, 58}, {188, 214, 125, 61}}},
|
{Region: "Philippines", Group: "Premium TCP Asia", Hostname: "96-1-ph.cg-dialup.net", IPs: []net.IP{{188, 214, 125, 37}, {188, 214, 125, 38}, {188, 214, 125, 40}, {188, 214, 125, 43}, {188, 214, 125, 44}, {188, 214, 125, 45}, {188, 214, 125, 52}, {188, 214, 125, 55}, {188, 214, 125, 61}, {188, 214, 125, 62}}},
|
||||||
{Region: "Philippines", Group: "Premium UDP Asia", IPs: []net.IP{{188, 214, 125, 35}, {188, 214, 125, 36}, {188, 214, 125, 37}, {188, 214, 125, 48}, {188, 214, 125, 55}, {188, 214, 125, 56}, {188, 214, 125, 57}, {188, 214, 125, 58}, {188, 214, 125, 59}, {188, 214, 125, 62}}},
|
{Region: "Philippines", Group: "Premium UDP Asia", Hostname: "95-1-ph.cg-dialup.net", IPs: []net.IP{{188, 214, 125, 37}, {188, 214, 125, 40}, {188, 214, 125, 46}, {188, 214, 125, 49}, {188, 214, 125, 52}, {188, 214, 125, 54}, {188, 214, 125, 57}, {188, 214, 125, 58}, {188, 214, 125, 61}, {188, 214, 125, 62}}},
|
||||||
{Region: "Poland", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 156, 11}, {37, 120, 156, 17}, {37, 120, 156, 23}, {37, 120, 156, 25}, {37, 120, 156, 26}, {51, 75, 56, 35}, {51, 75, 56, 37}, {51, 75, 56, 41}, {54, 37, 238, 36}, {54, 37, 238, 43}}},
|
{Region: "Poland", Group: "Premium TCP Europe", Hostname: "97-1-pl.cg-dialup.net", IPs: []net.IP{{138, 199, 59, 132}, {138, 199, 59, 136}, {138, 199, 59, 137}, {138, 199, 59, 143}, {138, 199, 59, 144}, {138, 199, 59, 152}, {138, 199, 59, 153}, {138, 199, 59, 166}, {138, 199, 59, 174}, {138, 199, 59, 175}}},
|
||||||
{Region: "Poland", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 156, 8}, {37, 120, 156, 10}, {37, 120, 156, 12}, {37, 120, 156, 18}, {37, 120, 156, 19}, {37, 120, 156, 24}, {51, 75, 56, 33}, {51, 75, 56, 40}, {51, 75, 56, 41}, {54, 37, 238, 33}}},
|
{Region: "Poland", Group: "Premium UDP Europe", Hostname: "87-1-pl.cg-dialup.net", IPs: []net.IP{{138, 199, 59, 130}, {138, 199, 59, 136}, {138, 199, 59, 148}, {138, 199, 59, 149}, {138, 199, 59, 153}, {138, 199, 59, 156}, {138, 199, 59, 157}, {138, 199, 59, 164}, {138, 199, 59, 171}, {138, 199, 59, 173}}},
|
||||||
{Region: "Portugal", Group: "Premium TCP Europe", IPs: []net.IP{{89, 26, 243, 47}, {89, 26, 243, 48}, {89, 26, 243, 50}, {89, 26, 243, 51}, {89, 26, 243, 54}, {89, 26, 243, 58}, {89, 26, 243, 60}, {185, 90, 57, 172}, {185, 90, 57, 176}, {185, 90, 57, 179}}},
|
{Region: "Portugal", Group: "Premium TCP Europe", Hostname: "97-1-pt.cg-dialup.net", IPs: []net.IP{{89, 26, 243, 112}, {89, 26, 243, 115}, {89, 26, 243, 195}, {89, 26, 243, 216}, {89, 26, 243, 218}, {89, 26, 243, 220}, {89, 26, 243, 222}, {89, 26, 243, 223}, {89, 26, 243, 225}, {89, 26, 243, 228}}},
|
||||||
{Region: "Portugal", Group: "Premium UDP Europe", IPs: []net.IP{{89, 26, 243, 47}, {89, 26, 243, 50}, {89, 26, 243, 51}, {89, 26, 243, 53}, {89, 26, 243, 58}, {185, 90, 57, 173}, {185, 90, 57, 175}, {185, 90, 57, 176}, {185, 90, 57, 178}, {185, 90, 57, 179}}},
|
{Region: "Portugal", Group: "Premium UDP Europe", Hostname: "87-1-pt.cg-dialup.net", IPs: []net.IP{{89, 26, 243, 99}, {89, 26, 243, 113}, {89, 26, 243, 115}, {89, 26, 243, 195}, {89, 26, 243, 199}, {89, 26, 243, 216}, {89, 26, 243, 219}, {89, 26, 243, 225}, {89, 26, 243, 226}, {89, 26, 243, 227}}},
|
||||||
{Region: "Qatar", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 7, 6}, {45, 131, 7, 9}, {45, 131, 7, 10}, {45, 131, 7, 13}, {45, 131, 7, 16}, {45, 131, 7, 17}, {45, 131, 7, 20}, {45, 131, 7, 22}, {45, 131, 7, 23}, {45, 131, 7, 28}}},
|
{Region: "Qatar", Group: "Premium TCP Europe", Hostname: "97-1-qa.cg-dialup.net", IPs: []net.IP{{95, 181, 234, 133}, {95, 181, 234, 135}, {95, 181, 234, 136}, {95, 181, 234, 137}, {95, 181, 234, 138}, {95, 181, 234, 139}, {95, 181, 234, 140}, {95, 181, 234, 141}, {95, 181, 234, 142}, {95, 181, 234, 143}}},
|
||||||
{Region: "Qatar", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 7, 8}, {45, 131, 7, 9}, {45, 131, 7, 14}, {45, 131, 7, 15}, {45, 131, 7, 16}, {45, 131, 7, 17}, {45, 131, 7, 23}, {45, 131, 7, 27}, {45, 131, 7, 28}, {45, 131, 7, 29}}},
|
{Region: "Qatar", Group: "Premium UDP Europe", Hostname: "87-1-qa.cg-dialup.net", IPs: []net.IP{{95, 181, 234, 131}, {95, 181, 234, 132}, {95, 181, 234, 133}, {95, 181, 234, 134}, {95, 181, 234, 135}, {95, 181, 234, 137}, {95, 181, 234, 138}, {95, 181, 234, 139}, {95, 181, 234, 142}, {95, 181, 234, 143}}},
|
||||||
{Region: "Romania", Group: "NoSpy TCP Europe", IPs: []net.IP{{85, 9, 20, 134}, {85, 9, 20, 135}, {85, 9, 20, 137}, {85, 9, 20, 138}, {85, 9, 20, 139}, {85, 9, 20, 147}, {85, 9, 20, 149}, {85, 9, 20, 154}, {85, 9, 20, 248}, {85, 9, 20, 249}}},
|
{Region: "Russian Federation", Group: "Premium TCP Europe", Hostname: "97-1-ru.cg-dialup.net", IPs: []net.IP{{5, 8, 16, 72}, {5, 8, 16, 74}, {5, 8, 16, 84}, {5, 8, 16, 85}, {5, 8, 16, 123}, {5, 8, 16, 124}, {5, 8, 16, 132}, {146, 70, 52, 35}, {146, 70, 52, 44}, {146, 70, 52, 54}}},
|
||||||
{Region: "Romania", Group: "NoSpy UDP Europe", IPs: []net.IP{{85, 9, 20, 134}, {85, 9, 20, 138}, {85, 9, 20, 139}, {85, 9, 20, 144}, {85, 9, 20, 145}, {85, 9, 20, 147}, {85, 9, 20, 148}, {85, 9, 20, 151}, {85, 9, 20, 248}, {85, 9, 20, 249}}},
|
{Region: "Russian Federation", Group: "Premium UDP Europe", Hostname: "87-1-ru.cg-dialup.net", IPs: []net.IP{{5, 8, 16, 75}, {5, 8, 16, 87}, {5, 8, 16, 99}, {5, 8, 16, 110}, {5, 8, 16, 138}, {146, 70, 52, 29}, {146, 70, 52, 52}, {146, 70, 52, 58}, {146, 70, 52, 59}, {146, 70, 52, 67}}},
|
||||||
{Region: "Romania", Group: "Premium TCP Europe", IPs: []net.IP{{193, 176, 84, 44}, {193, 176, 84, 49}, {193, 176, 84, 51}, {193, 176, 84, 52}, {193, 176, 84, 88}, {193, 176, 84, 121}, {193, 176, 84, 126}, {193, 176, 85, 72}, {193, 176, 85, 74}, {193, 176, 85, 101}}},
|
{Region: "Saudi Arabia", Group: "Premium TCP Europe", Hostname: "97-1-sa.cg-dialup.net", IPs: []net.IP{{95, 181, 235, 131}, {95, 181, 235, 133}, {95, 181, 235, 134}, {95, 181, 235, 135}, {95, 181, 235, 137}, {95, 181, 235, 138}, {95, 181, 235, 139}, {95, 181, 235, 140}, {95, 181, 235, 141}, {95, 181, 235, 142}}},
|
||||||
{Region: "Romania", Group: "Premium UDP Europe", IPs: []net.IP{{193, 176, 84, 46}, {193, 176, 84, 47}, {193, 176, 84, 48}, {193, 176, 84, 49}, {193, 176, 84, 90}, {193, 176, 84, 125}, {193, 176, 85, 68}, {193, 176, 85, 72}, {193, 176, 85, 78}, {193, 176, 85, 91}}},
|
{Region: "Saudi Arabia", Group: "Premium UDP Europe", Hostname: "87-1-sa.cg-dialup.net", IPs: []net.IP{{95, 181, 235, 131}, {95, 181, 235, 132}, {95, 181, 235, 134}, {95, 181, 235, 135}, {95, 181, 235, 136}, {95, 181, 235, 137}, {95, 181, 235, 138}, {95, 181, 235, 139}, {95, 181, 235, 141}, {95, 181, 235, 144}}},
|
||||||
{Region: "Russian Federation", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 192, 42}, {45, 132, 192, 43}, {45, 132, 192, 49}, {45, 132, 192, 60}, {45, 132, 192, 63}, {45, 132, 192, 66}, {45, 132, 192, 78}, {45, 132, 192, 80}, {45, 132, 192, 85}, {45, 132, 192, 96}}},
|
{Region: "Serbia", Group: "Premium TCP Europe", Hostname: "97-1-rs.cg-dialup.net", IPs: []net.IP{{37, 120, 193, 179}, {37, 120, 193, 186}, {37, 120, 193, 188}, {37, 120, 193, 190}, {141, 98, 103, 36}, {141, 98, 103, 38}, {141, 98, 103, 39}, {141, 98, 103, 43}, {141, 98, 103, 44}, {141, 98, 103, 46}}},
|
||||||
{Region: "Russian Federation", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 192, 11}, {45, 132, 192, 15}, {45, 132, 192, 24}, {45, 132, 192, 32}, {45, 132, 192, 43}, {45, 132, 192, 49}, {45, 132, 192, 55}, {45, 132, 192, 63}, {45, 132, 192, 65}, {45, 132, 192, 95}}},
|
{Region: "Serbia", Group: "Premium UDP Europe", Hostname: "87-1-rs.cg-dialup.net", IPs: []net.IP{{37, 120, 193, 180}, {37, 120, 193, 186}, {37, 120, 193, 187}, {37, 120, 193, 188}, {37, 120, 193, 189}, {37, 120, 193, 190}, {141, 98, 103, 35}, {141, 98, 103, 36}, {141, 98, 103, 39}, {141, 98, 103, 41}}},
|
||||||
{Region: "Saudi Arabia", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 6, 7}, {45, 131, 6, 10}, {45, 131, 6, 12}, {45, 131, 6, 13}, {45, 131, 6, 21}, {45, 131, 6, 23}, {45, 131, 6, 24}, {45, 131, 6, 25}, {45, 131, 6, 26}, {45, 131, 6, 28}}},
|
{Region: "Singapore", Group: "Premium TCP Asia", Hostname: "96-1-sg.cg-dialup.net", IPs: []net.IP{{84, 17, 39, 162}, {84, 17, 39, 165}, {84, 17, 39, 168}, {84, 17, 39, 171}, {84, 17, 39, 175}, {84, 17, 39, 177}, {84, 17, 39, 178}, {84, 17, 39, 181}, {84, 17, 39, 183}, {84, 17, 39, 185}}},
|
||||||
{Region: "Saudi Arabia", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 6, 7}, {45, 131, 6, 9}, {45, 131, 6, 14}, {45, 131, 6, 15}, {45, 131, 6, 18}, {45, 131, 6, 19}, {45, 131, 6, 22}, {45, 131, 6, 24}, {45, 131, 6, 28}, {45, 131, 6, 29}}},
|
{Region: "Singapore", Group: "Premium UDP Asia", Hostname: "95-1-sg.cg-dialup.net", IPs: []net.IP{{84, 17, 39, 162}, {84, 17, 39, 165}, {84, 17, 39, 166}, {84, 17, 39, 167}, {84, 17, 39, 171}, {84, 17, 39, 174}, {84, 17, 39, 175}, {84, 17, 39, 178}, {84, 17, 39, 180}, {84, 17, 39, 185}}},
|
||||||
{Region: "Serbia", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 193, 179}, {37, 120, 193, 181}, {37, 120, 193, 182}, {37, 120, 193, 183}, {37, 120, 193, 184}, {37, 120, 193, 186}, {37, 120, 193, 190}, {141, 98, 103, 37}, {141, 98, 103, 43}, {141, 98, 103, 46}}},
|
{Region: "Slovakia", Group: "Premium TCP Europe", Hostname: "97-1-sk.cg-dialup.net", 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: "Serbia", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 193, 179}, {37, 120, 193, 182}, {37, 120, 193, 183}, {37, 120, 193, 188}, {37, 120, 193, 189}, {37, 120, 193, 190}, {141, 98, 103, 35}, {141, 98, 103, 40}, {141, 98, 103, 42}, {141, 98, 103, 46}}},
|
{Region: "Slovakia", Group: "Premium UDP Europe", Hostname: "87-1-sk.cg-dialup.net", 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: "Singapore", Group: "Premium TCP Asia", IPs: []net.IP{{37, 120, 151, 55}, {37, 120, 151, 60}, {37, 120, 151, 131}, {37, 120, 151, 139}, {84, 17, 39, 168}, {84, 17, 39, 175}, {84, 17, 39, 176}, {84, 17, 39, 179}, {84, 17, 39, 182}, {84, 17, 39, 185}}},
|
{Region: "Slovenia", Group: "Premium TCP Europe", Hostname: "97-1-si.cg-dialup.net", IPs: []net.IP{{195, 80, 150, 211}, {195, 80, 150, 212}, {195, 80, 150, 214}, {195, 80, 150, 215}, {195, 80, 150, 216}, {195, 80, 150, 217}, {195, 80, 150, 218}, {195, 80, 150, 219}, {195, 80, 150, 221}, {195, 80, 150, 222}}},
|
||||||
{Region: "Singapore", Group: "Premium UDP Asia", IPs: []net.IP{{37, 120, 151, 51}, {37, 120, 151, 57}, {37, 120, 151, 131}, {37, 120, 151, 132}, {37, 120, 151, 140}, {37, 120, 151, 141}, {84, 17, 39, 165}, {84, 17, 39, 169}, {84, 17, 39, 171}, {84, 17, 39, 180}}},
|
{Region: "Slovenia", Group: "Premium UDP Europe", Hostname: "87-1-si.cg-dialup.net", IPs: []net.IP{{195, 80, 150, 211}, {195, 80, 150, 212}, {195, 80, 150, 214}, {195, 80, 150, 215}, {195, 80, 150, 216}, {195, 80, 150, 217}, {195, 80, 150, 219}, {195, 80, 150, 220}, {195, 80, 150, 221}, {195, 80, 150, 222}}},
|
||||||
{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: "South Africa", Group: "Premium TCP Asia", Hostname: "96-1-za.cg-dialup.net", IPs: []net.IP{{154, 127, 50, 212}, {154, 127, 50, 215}, {154, 127, 50, 217}, {154, 127, 50, 219}, {154, 127, 50, 220}, {154, 127, 50, 222}, {154, 127, 60, 196}, {154, 127, 60, 198}, {154, 127, 60, 199}, {154, 127, 60, 200}}},
|
||||||
{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: "South Africa", Group: "Premium TCP Europe", Hostname: "97-1-za.cg-dialup.net", 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: "Slovenia", Group: "Premium TCP Europe", IPs: []net.IP{{146, 247, 25, 79}, {146, 247, 25, 80}, {146, 247, 25, 81}, {146, 247, 25, 83}, {146, 247, 25, 84}, {146, 247, 25, 85}, {146, 247, 25, 86}, {146, 247, 25, 87}, {146, 247, 25, 88}, {146, 247, 25, 90}}},
|
{Region: "South Africa", Group: "Premium UDP Asia", Hostname: "95-1-za.cg-dialup.net", IPs: []net.IP{{154, 127, 50, 210}, {154, 127, 50, 214}, {154, 127, 50, 218}, {154, 127, 50, 219}, {154, 127, 50, 220}, {154, 127, 50, 221}, {154, 127, 50, 222}, {154, 127, 60, 195}, {154, 127, 60, 199}, {154, 127, 60, 206}}},
|
||||||
{Region: "Slovenia", Group: "Premium UDP Europe", IPs: []net.IP{{146, 247, 25, 80}, {146, 247, 25, 81}, {146, 247, 25, 82}, {146, 247, 25, 83}, {146, 247, 25, 84}, {146, 247, 25, 85}, {146, 247, 25, 86}, {146, 247, 25, 87}, {146, 247, 25, 88}, {146, 247, 25, 89}}},
|
{Region: "South Africa", Group: "Premium UDP Europe", Hostname: "87-1-za.cg-dialup.net", 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 TCP Asia", IPs: []net.IP{{165, 73, 248, 211}, {165, 73, 248, 214}, {165, 73, 248, 222}, {165, 73, 248, 227}, {165, 73, 248, 229}, {165, 73, 248, 230}, {165, 73, 248, 231}, {165, 73, 248, 234}, {165, 73, 248, 236}, {165, 73, 248, 237}}},
|
{Region: "Spain", Group: "Premium TCP Europe", Hostname: "97-1-es.cg-dialup.net", IPs: []net.IP{{37, 120, 142, 41}, {37, 120, 142, 52}, {37, 120, 142, 55}, {37, 120, 142, 61}, {37, 120, 142, 173}, {84, 17, 62, 131}, {84, 17, 62, 142}, {84, 17, 62, 144}, {185, 93, 3, 108}, {185, 93, 3, 114}}},
|
||||||
{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: "Sri Lanka", Group: "Premium TCP Europe", Hostname: "97-1-lk.cg-dialup.net", IPs: []net.IP{{95, 181, 239, 131}, {95, 181, 239, 132}, {95, 181, 239, 133}, {95, 181, 239, 134}, {95, 181, 239, 135}, {95, 181, 239, 136}, {95, 181, 239, 137}, {95, 181, 239, 138}, {95, 181, 239, 140}, {95, 181, 239, 144}}},
|
||||||
{Region: "South Africa", Group: "Premium UDP Asia", IPs: []net.IP{{165, 73, 248, 212}, {165, 73, 248, 215}, {165, 73, 248, 217}, {165, 73, 248, 218}, {165, 73, 248, 219}, {165, 73, 248, 222}, {165, 73, 248, 227}, {165, 73, 248, 230}, {165, 73, 248, 234}, {165, 73, 248, 237}}},
|
{Region: "Sri Lanka", Group: "Premium UDP Europe", Hostname: "87-1-lk.cg-dialup.net", IPs: []net.IP{{95, 181, 239, 131}, {95, 181, 239, 132}, {95, 181, 239, 133}, {95, 181, 239, 134}, {95, 181, 239, 135}, {95, 181, 239, 136}, {95, 181, 239, 140}, {95, 181, 239, 141}, {95, 181, 239, 142}, {95, 181, 239, 144}}},
|
||||||
{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: "Sweden", Group: "Premium TCP Europe", Hostname: "97-1-se.cg-dialup.net", IPs: []net.IP{{188, 126, 73, 207}, {188, 126, 73, 209}, {188, 126, 73, 214}, {188, 126, 73, 219}, {188, 126, 79, 6}, {188, 126, 79, 11}, {188, 126, 79, 19}, {188, 126, 79, 25}, {195, 246, 120, 148}, {195, 246, 120, 161}}},
|
||||||
{Region: "Spain", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 142, 163}, {37, 120, 142, 166}, {37, 120, 142, 167}, {84, 17, 62, 138}, {84, 17, 62, 141}, {84, 17, 62, 145}, {84, 17, 62, 147}, {84, 17, 62, 152}, {185, 93, 3, 113}, {185, 93, 182, 139}}},
|
{Region: "Sweden", Group: "Premium UDP Europe", Hostname: "87-1-se.cg-dialup.net", IPs: []net.IP{{188, 126, 73, 201}, {188, 126, 73, 211}, {188, 126, 73, 213}, {188, 126, 73, 218}, {188, 126, 79, 6}, {188, 126, 79, 8}, {188, 126, 79, 19}, {195, 246, 120, 142}, {195, 246, 120, 144}, {195, 246, 120, 168}}},
|
||||||
{Region: "Spain", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 142, 150}, {37, 120, 142, 157}, {37, 120, 142, 165}, {84, 17, 62, 139}, {84, 17, 62, 142}, {185, 93, 3, 109}, {185, 93, 3, 111}, {185, 93, 3, 113}, {185, 93, 182, 136}, {185, 93, 182, 138}}},
|
{Region: "Switzerland", Group: "Premium TCP Europe", Hostname: "97-1-ch.cg-dialup.net", IPs: []net.IP{{84, 17, 52, 4}, {84, 17, 52, 20}, {84, 17, 52, 44}, {84, 17, 52, 65}, {84, 17, 52, 72}, {84, 17, 52, 80}, {84, 17, 52, 83}, {84, 17, 52, 85}, {185, 32, 222, 112}, {185, 189, 150, 73}}},
|
||||||
{Region: "Sri Lanka", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 136, 6}, {45, 132, 136, 7}, {45, 132, 136, 8}, {45, 132, 136, 12}, {45, 132, 136, 13}, {45, 132, 136, 17}, {45, 132, 136, 21}, {45, 132, 136, 22}, {45, 132, 136, 26}, {45, 132, 136, 27}}},
|
{Region: "Switzerland", Group: "Premium UDP Europe", Hostname: "87-1-ch.cg-dialup.net", IPs: []net.IP{{84, 17, 52, 5}, {84, 17, 52, 14}, {84, 17, 52, 24}, {84, 17, 52, 64}, {84, 17, 52, 73}, {84, 17, 52, 85}, {185, 32, 222, 114}, {185, 189, 150, 52}, {185, 189, 150, 57}, {195, 225, 118, 43}}},
|
||||||
{Region: "Sri Lanka", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 136, 7}, {45, 132, 136, 9}, {45, 132, 136, 10}, {45, 132, 136, 11}, {45, 132, 136, 12}, {45, 132, 136, 15}, {45, 132, 136, 20}, {45, 132, 136, 21}, {45, 132, 136, 27}, {45, 132, 136, 29}}},
|
{Region: "Taiwan", Group: "Premium TCP Asia", Hostname: "96-1-tw.cg-dialup.net", IPs: []net.IP{{45, 133, 181, 100}, {45, 133, 181, 102}, {45, 133, 181, 103}, {45, 133, 181, 106}, {45, 133, 181, 109}, {45, 133, 181, 113}, {45, 133, 181, 115}, {45, 133, 181, 116}, {45, 133, 181, 123}, {45, 133, 181, 125}}},
|
||||||
{Region: "Sweden", Group: "Premium TCP Europe", IPs: []net.IP{{46, 246, 65, 151}, {46, 246, 65, 156}, {46, 246, 65, 157}, {46, 246, 65, 167}, {46, 246, 65, 203}, {46, 246, 65, 221}, {91, 132, 138, 51}, {188, 126, 64, 101}, {188, 126, 66, 3}, {188, 126, 66, 8}}},
|
{Region: "Taiwan", Group: "Premium UDP Asia", Hostname: "95-1-tw.cg-dialup.net", IPs: []net.IP{{45, 133, 181, 99}, {45, 133, 181, 102}, {45, 133, 181, 107}, {45, 133, 181, 108}, {45, 133, 181, 109}, {45, 133, 181, 114}, {45, 133, 181, 116}, {45, 133, 181, 117}, {45, 133, 181, 123}, {45, 133, 181, 124}}},
|
||||||
{Region: "Sweden", Group: "Premium UDP Europe", IPs: []net.IP{{46, 246, 65, 167}, {46, 246, 65, 170}, {46, 246, 65, 172}, {46, 246, 65, 181}, {46, 246, 65, 214}, {46, 246, 65, 215}, {91, 132, 138, 57}, {188, 126, 66, 8}, {188, 126, 66, 24}, {188, 126, 66, 29}}},
|
{Region: "Thailand", Group: "Premium TCP Asia", Hostname: "96-1-th.cg-dialup.net", IPs: []net.IP{{146, 70, 13, 3}, {146, 70, 13, 4}, {146, 70, 13, 6}, {146, 70, 13, 7}, {146, 70, 13, 8}, {146, 70, 13, 9}, {146, 70, 13, 11}, {146, 70, 13, 13}, {146, 70, 13, 15}, {146, 70, 13, 16}}},
|
||||||
{Region: "Switzerland", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 52, 14}, {84, 17, 52, 39}, {84, 17, 52, 68}, {84, 17, 52, 73}, {84, 17, 52, 84}, {89, 187, 165, 131}, {185, 32, 222, 8}, {185, 32, 222, 105}, {195, 225, 118, 38}, {195, 225, 118, 56}}},
|
{Region: "Thailand", Group: "Premium UDP Asia", Hostname: "95-1-th.cg-dialup.net", IPs: []net.IP{{146, 70, 13, 3}, {146, 70, 13, 4}, {146, 70, 13, 8}, {146, 70, 13, 9}, {146, 70, 13, 10}, {146, 70, 13, 11}, {146, 70, 13, 12}, {146, 70, 13, 13}, {146, 70, 13, 15}, {146, 70, 13, 16}}},
|
||||||
{Region: "Switzerland", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 52, 15}, {84, 17, 52, 20}, {84, 17, 52, 36}, {84, 17, 52, 52}, {84, 17, 52, 62}, {84, 17, 52, 75}, {91, 132, 136, 174}, {91, 132, 136, 205}, {91, 132, 136, 206}, {185, 32, 222, 113}}},
|
{Region: "Turkey", Group: "Premium TCP Europe", Hostname: "97-1-tr.cg-dialup.net", IPs: []net.IP{{188, 213, 34, 9}, {188, 213, 34, 11}, {188, 213, 34, 15}, {188, 213, 34, 16}, {188, 213, 34, 23}, {188, 213, 34, 25}, {188, 213, 34, 28}, {188, 213, 34, 41}, {188, 213, 34, 108}, {188, 213, 34, 110}}},
|
||||||
{Region: "Taiwan", Group: "Premium TCP Asia", IPs: []net.IP{{45, 133, 181, 102}, {45, 133, 181, 105}, {45, 133, 181, 106}, {45, 133, 181, 108}, {45, 133, 181, 110}, {45, 133, 181, 112}, {45, 133, 181, 114}, {45, 133, 181, 117}, {45, 133, 181, 120}, {45, 133, 181, 124}}},
|
{Region: "Turkey", Group: "Premium UDP Europe", Hostname: "87-1-tr.cg-dialup.net", IPs: []net.IP{{188, 213, 34, 8}, {188, 213, 34, 11}, {188, 213, 34, 14}, {188, 213, 34, 28}, {188, 213, 34, 35}, {188, 213, 34, 42}, {188, 213, 34, 43}, {188, 213, 34, 100}, {188, 213, 34, 103}, {188, 213, 34, 107}}},
|
||||||
{Region: "Taiwan", Group: "Premium UDP Asia", IPs: []net.IP{{45, 133, 181, 102}, {45, 133, 181, 103}, {45, 133, 181, 110}, {45, 133, 181, 111}, {45, 133, 181, 112}, {45, 133, 181, 114}, {45, 133, 181, 118}, {45, 133, 181, 120}, {45, 133, 181, 125}, {45, 133, 181, 126}}},
|
{Region: "Ukraine", Group: "Premium TCP Europe", Hostname: "97-1-ua.cg-dialup.net", IPs: []net.IP{{31, 28, 161, 18}, {31, 28, 161, 20}, {31, 28, 161, 27}, {31, 28, 163, 34}, {31, 28, 163, 37}, {31, 28, 163, 44}, {62, 149, 7, 167}, {62, 149, 7, 172}, {62, 149, 29, 45}, {62, 149, 29, 57}}},
|
||||||
{Region: "Thailand", Group: "Premium TCP Asia", IPs: []net.IP{{119, 59, 98, 238}, {119, 59, 98, 239}, {119, 59, 98, 243}, {119, 59, 98, 244}, {119, 59, 98, 248}, {119, 59, 98, 249}, {119, 59, 121, 165}, {119, 59, 121, 166}, {119, 59, 121, 171}, {119, 59, 121, 173}}},
|
{Region: "Ukraine", Group: "Premium UDP Europe", Hostname: "87-1-ua.cg-dialup.net", IPs: []net.IP{{31, 28, 161, 27}, {31, 28, 163, 38}, {31, 28, 163, 42}, {31, 28, 163, 54}, {31, 28, 163, 61}, {62, 149, 7, 162}, {62, 149, 7, 163}, {62, 149, 29, 35}, {62, 149, 29, 38}, {62, 149, 29, 41}}},
|
||||||
{Region: "Thailand", Group: "Premium UDP Asia", IPs: []net.IP{{119, 59, 98, 214}, {119, 59, 98, 238}, {119, 59, 98, 244}, {119, 59, 98, 247}, {119, 59, 121, 163}, {119, 59, 121, 165}, {119, 59, 121, 166}, {119, 59, 121, 167}, {119, 59, 121, 173}, {119, 59, 121, 175}}},
|
{Region: "United Arab Emirates", Group: "Premium TCP Europe", Hostname: "97-1-ae.cg-dialup.net", 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, 190}}},
|
||||||
{Region: "Turkey", Group: "Premium TCP Europe", IPs: []net.IP{{188, 213, 34, 9}, {188, 213, 34, 22}, {188, 213, 34, 24}, {188, 213, 34, 27}, {188, 213, 34, 28}, {188, 213, 34, 35}, {188, 213, 34, 41}, {188, 213, 34, 44}, {188, 213, 34, 45}, {188, 213, 34, 46}}},
|
{Region: "United Arab Emirates", Group: "Premium UDP Europe", Hostname: "87-1-ae.cg-dialup.net", 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, 186}, {217, 138, 193, 187}, {217, 138, 193, 188}, {217, 138, 193, 189}, {217, 138, 193, 190}}},
|
||||||
{Region: "Turkey", Group: "Premium UDP Europe", IPs: []net.IP{{188, 213, 34, 3}, {188, 213, 34, 13}, {188, 213, 34, 18}, {188, 213, 34, 25}, {188, 213, 34, 27}, {188, 213, 34, 30}, {188, 213, 34, 35}, {188, 213, 34, 37}, {188, 213, 34, 38}, {188, 213, 34, 44}}},
|
{Region: "United Kingdom", Group: "Premium TCP Europe", Hostname: "97-1-gb.cg-dialup.net", IPs: []net.IP{{45, 133, 173, 49}, {45, 133, 173, 56}, {45, 133, 173, 82}, {45, 133, 173, 86}, {95, 154, 200, 153}, {95, 154, 200, 156}, {181, 215, 176, 103}, {181, 215, 176, 246}, {181, 215, 176, 251}, {194, 110, 13, 141}}},
|
||||||
{Region: "Ukraine", Group: "Premium TCP Europe", IPs: []net.IP{{31, 28, 161, 21}, {31, 28, 163, 34}, {31, 28, 163, 35}, {31, 28, 163, 38}, {31, 28, 163, 50}, {62, 149, 7, 164}, {62, 149, 7, 167}, {62, 149, 29, 50}, {62, 149, 29, 53}, {62, 149, 29, 56}}},
|
{Region: "United Kingdom", Group: "Premium UDP Europe", Hostname: "87-1-gb.cg-dialup.net", IPs: []net.IP{{45, 133, 172, 100}, {45, 133, 172, 126}, {45, 133, 173, 84}, {95, 154, 200, 174}, {181, 215, 176, 110}, {181, 215, 176, 151}, {181, 215, 176, 158}, {191, 101, 209, 142}, {194, 110, 13, 107}, {194, 110, 13, 128}}},
|
||||||
{Region: "Ukraine", Group: "Premium UDP Europe", IPs: []net.IP{{31, 28, 161, 20}, {31, 28, 161, 28}, {31, 28, 161, 30}, {31, 28, 163, 37}, {31, 28, 163, 45}, {31, 28, 163, 53}, {31, 28, 163, 56}, {31, 28, 163, 59}, {62, 149, 7, 168}, {62, 149, 29, 56}}},
|
{Region: "United States", Group: "Premium TCP USA", Hostname: "93-1-us.cg-dialup.net", IPs: []net.IP{{102, 129, 145, 15}, {102, 129, 152, 195}, {102, 129, 152, 248}, {154, 21, 208, 159}, {185, 242, 5, 117}, {185, 242, 5, 123}, {185, 242, 5, 229}, {191, 96, 227, 173}, {191, 96, 227, 196}, {199, 115, 119, 248}}},
|
||||||
{Region: "United Arab Emirates", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 5, 6}, {45, 131, 5, 9}, {45, 131, 5, 12}, {45, 131, 5, 14}, {45, 131, 5, 16}, {45, 131, 5, 17}, {45, 131, 5, 18}, {45, 131, 5, 27}, {45, 131, 5, 28}, {45, 131, 5, 29}}},
|
{Region: "United States", Group: "Premium UDP USA", Hostname: "94-1-us.cg-dialup.net", IPs: []net.IP{{23, 82, 14, 113}, {23, 105, 177, 122}, {45, 89, 173, 222}, {84, 17, 35, 4}, {89, 187, 171, 132}, {156, 146, 37, 45}, {156, 146, 59, 86}, {184, 170, 240, 231}, {191, 96, 150, 248}, {199, 115, 119, 248}}},
|
||||||
{Region: "United Arab Emirates", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 5, 8}, {45, 131, 5, 14}, {45, 131, 5, 15}, {45, 131, 5, 20}, {45, 131, 5, 22}, {45, 131, 5, 23}, {45, 131, 5, 24}, {45, 131, 5, 26}, {45, 131, 5, 28}, {45, 131, 5, 29}}},
|
{Region: "Venezuela", Group: "Premium TCP USA", Hostname: "93-1-ve.cg-dialup.net", 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, 140}, {95, 181, 237, 141}, {95, 181, 237, 143}}},
|
||||||
{Region: "United Kingdom", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 133, 134}, {37, 120, 133, 140}, {84, 17, 51, 32}, {84, 17, 51, 75}, {89, 238, 138, 243}, {89, 238, 183, 230}, {95, 154, 200, 144}, {95, 154, 200, 186}, {141, 98, 100, 56}, {141, 98, 100, 61}}},
|
{Region: "Venezuela", Group: "Premium UDP USA", Hostname: "94-1-ve.cg-dialup.net", IPs: []net.IP{{95, 181, 237, 131}, {95, 181, 237, 132}, {95, 181, 237, 134}, {95, 181, 237, 135}, {95, 181, 237, 136}, {95, 181, 237, 140}, {95, 181, 237, 141}, {95, 181, 237, 142}, {95, 181, 237, 143}, {95, 181, 237, 144}}},
|
||||||
{Region: "United Kingdom", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 133, 134}, {37, 120, 159, 35}, {81, 92, 206, 172}, {84, 17, 51, 34}, {84, 17, 51, 110}, {84, 17, 51, 115}, {89, 238, 135, 59}, {95, 154, 200, 147}, {95, 154, 200, 149}, {141, 98, 100, 58}}},
|
{Region: "Vietnam", Group: "Premium TCP Asia", Hostname: "96-1-vn.cg-dialup.net", IPs: []net.IP{{188, 214, 152, 99}, {188, 214, 152, 101}, {188, 214, 152, 103}, {188, 214, 152, 104}, {188, 214, 152, 105}, {188, 214, 152, 106}, {188, 214, 152, 107}, {188, 214, 152, 108}, {188, 214, 152, 109}, {188, 214, 152, 110}}},
|
||||||
{Region: "United States", Group: "Premium TCP USA", IPs: []net.IP{{23, 105, 160, 184}, {38, 131, 126, 150}, {84, 17, 35, 32}, {89, 187, 182, 7}, {89, 187, 182, 31}, {176, 113, 72, 153}, {176, 113, 72, 167}, {192, 96, 203, 150}, {199, 115, 119, 238}, {212, 102, 41, 4}}},
|
{Region: "Vietnam", Group: "Premium UDP Asia", Hostname: "95-1-vn.cg-dialup.net", 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, 106}, {188, 214, 152, 107}, {188, 214, 152, 109}}},
|
||||||
{Region: "United States", Group: "Premium UDP USA", IPs: []net.IP{{23, 19, 68, 56}, {23, 82, 78, 29}, {23, 105, 191, 36}, {23, 105, 191, 51}, {84, 17, 35, 8}, {89, 187, 171, 138}, {108, 62, 96, 33}, {176, 113, 72, 151}, {192, 96, 203, 154}, {199, 115, 117, 70}}},
|
|
||||||
{Region: "Venezuela", Group: "Premium TCP Europe", IPs: []net.IP{{45, 133, 89, 6}, {45, 133, 89, 9}, {45, 133, 89, 11}, {45, 133, 89, 15}, {45, 133, 89, 17}, {45, 133, 89, 18}, {45, 133, 89, 22}, {45, 133, 89, 24}, {45, 133, 89, 26}, {45, 133, 89, 29}}},
|
|
||||||
{Region: "Venezuela", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 89, 8}, {45, 133, 89, 9}, {45, 133, 89, 15}, {45, 133, 89, 17}, {45, 133, 89, 21}, {45, 133, 89, 22}, {45, 133, 89, 25}, {45, 133, 89, 26}, {45, 133, 89, 27}, {45, 133, 89, 29}}},
|
|
||||||
{Region: "Vietnam", Group: "Premium TCP Asia", IPs: []net.IP{{45, 117, 79, 118}, {45, 117, 79, 123}, {45, 117, 79, 124}, {45, 117, 79, 125}, {103, 238, 214, 131}, {103, 238, 214, 134}, {103, 238, 214, 135}, {103, 238, 214, 136}, {103, 238, 214, 137}, {103, 238, 214, 140}}},
|
|
||||||
{Region: "Vietnam", Group: "Premium UDP Asia", IPs: []net.IP{{45, 117, 79, 114}, {45, 117, 79, 116}, {45, 117, 79, 117}, {45, 117, 79, 118}, {45, 117, 79, 123}, {103, 238, 214, 134}, {103, 238, 214, 135}, {103, 238, 214, 136}, {103, 238, 214, 137}, {103, 238, 214, 140}}},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
package constants
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/qdm12/private-internet-access-docker/internal/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Cloudflare is a DNS over TLS provider
|
|
||||||
Cloudflare models.DNSProvider = "cloudflare"
|
|
||||||
// Google is a DNS over TLS provider
|
|
||||||
Google models.DNSProvider = "google"
|
|
||||||
// Quad9 is a DNS over TLS provider
|
|
||||||
Quad9 models.DNSProvider = "quad9"
|
|
||||||
// Quadrant is a DNS over TLS provider
|
|
||||||
Quadrant models.DNSProvider = "quadrant"
|
|
||||||
// CleanBrowsing is a DNS over TLS provider
|
|
||||||
CleanBrowsing models.DNSProvider = "cleanbrowsing"
|
|
||||||
// SecureDNS is a DNS over TLS provider
|
|
||||||
SecureDNS models.DNSProvider = "securedns"
|
|
||||||
// LibreDNS is a DNS over TLS provider
|
|
||||||
LibreDNS models.DNSProvider = "libredns"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DNSProviderMapping returns a constant mapping of dns provider name
|
|
||||||
// to their data such as IP addresses or TLS host name.
|
|
||||||
func DNSProviderMapping() map[models.DNSProvider]models.DNSProviderData {
|
|
||||||
return map[models.DNSProvider]models.DNSProviderData{
|
|
||||||
Cloudflare: {
|
|
||||||
IPs: []net.IP{{1, 1, 1, 1}, {1, 0, 0, 1}, {0x26, 0x6, 0x47, 0x0, 0x47, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11}, {0x26, 0x6, 0x47, 0x0, 0x47, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x01}},
|
|
||||||
SupportsTLS: true,
|
|
||||||
SupportsIPv6: true,
|
|
||||||
Host: models.DNSHost("cloudflare-dns.com"),
|
|
||||||
},
|
|
||||||
Google: {
|
|
||||||
IPs: []net.IP{{8, 8, 8, 8}, {8, 8, 4, 4}, {0x20, 0x1, 0x48, 0x60, 0x48, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88, 0x88}, {0x20, 0x1, 0x48, 0x60, 0x48, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88, 0x44}},
|
|
||||||
SupportsTLS: true,
|
|
||||||
SupportsIPv6: true,
|
|
||||||
Host: models.DNSHost("dns.google"),
|
|
||||||
},
|
|
||||||
Quad9: {
|
|
||||||
IPs: []net.IP{{9, 9, 9, 9}, {149, 112, 112, 112}, {0x26, 0x20, 0x0, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe}, {0x26, 0x20, 0x0, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9}},
|
|
||||||
SupportsTLS: true,
|
|
||||||
SupportsIPv6: true,
|
|
||||||
Host: models.DNSHost("dns.quad9.net"),
|
|
||||||
},
|
|
||||||
Quadrant: {
|
|
||||||
IPs: []net.IP{{12, 159, 2, 159}, {0x20, 0x1, 0x18, 0x90, 0x14, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x59}},
|
|
||||||
SupportsTLS: true,
|
|
||||||
SupportsIPv6: true,
|
|
||||||
Host: models.DNSHost("dns-tls.qis.io"),
|
|
||||||
},
|
|
||||||
CleanBrowsing: {
|
|
||||||
IPs: []net.IP{{185, 228, 168, 9}, {185, 228, 169, 9}, {0x2a, 0xd, 0x2a, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}, {0x2a, 0xd, 0x2a, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}},
|
|
||||||
SupportsTLS: true,
|
|
||||||
SupportsIPv6: true,
|
|
||||||
Host: models.DNSHost("security-filter-dns.cleanbrowsing.org"),
|
|
||||||
},
|
|
||||||
SecureDNS: {
|
|
||||||
IPs: []net.IP{{146, 185, 167, 43}, {0x2a, 0x3, 0xb0, 0xc0, 0x0, 0x0, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0xe, 0x9a, 0x30, 0x1}},
|
|
||||||
SupportsTLS: true,
|
|
||||||
SupportsIPv6: true,
|
|
||||||
Host: models.DNSHost("dot.securedns.eu"),
|
|
||||||
},
|
|
||||||
LibreDNS: {
|
|
||||||
IPs: []net.IP{{116, 203, 115, 192}},
|
|
||||||
SupportsTLS: true,
|
|
||||||
Host: models.DNSHost("dot.libredns.gr"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block lists URLs
|
|
||||||
const (
|
|
||||||
AdsBlockListHostnamesURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/ads-hostnames.updated"
|
|
||||||
AdsBlockListIPsURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/ads-ips.updated"
|
|
||||||
MaliciousBlockListHostnamesURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/malicious-hostnames.updated"
|
|
||||||
MaliciousBlockListIPsURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/malicious-ips.updated"
|
|
||||||
SurveillanceBlockListHostnamesURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/surveillance-hostnames.updated"
|
|
||||||
SurveillanceBlockListIPsURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/surveillance-ips.updated"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DNS certificates to fetch
|
|
||||||
// TODO obtain from source directly, see qdm12/updated)
|
|
||||||
const (
|
|
||||||
NamedRootURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/named.root.updated"
|
|
||||||
RootKeyURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/root.key.updated"
|
|
||||||
)
|
|
||||||
120
internal/constants/fastestvpn.go
Normal file
120
internal/constants/fastestvpn.go
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package constants
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:lll
|
||||||
|
const (
|
||||||
|
FastestvpnCertificate = "MIIFQjCCAyqgAwIBAgIIUfxepT+rr8owDQYJKoZIhvcNAQEMBQAwPzELMAkGA1UEBhMCS1kxEzARBgNVBAoTCkZhc3Rlc3RWUE4xGzAZBgNVBAMTEkZhc3Rlc3RWUE4gUm9vdCBDQTAeFw0xNzA5MTYwMDAxNDZaFw0yNzA5MTQwMDAxNDZaMD8xCzAJBgNVBAYTAktZMRMwEQYDVQQKEwpGYXN0ZXN0VlBOMRswGQYDVQQDExJGYXN0ZXN0VlBOIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC1Xj+WfPTozFynFqc+c3CVrggIllaXEl5bY5VgFynXkqCTM6lSrfC4pNjGXUbqWe6RnGJbM4/6kUn+lQDjFSQV1rzP2eDS8+r5+X2WXh4AoeNRUWhvSG+HiHD/B2EFK+Nd5BRSdUjpKWAtsCmT2bBt7nT0jN1OdeNrLJeyF8siAqv/oQzKznF9aIe/N01b2M8ZOFTzoXi2fZAckgGWui8NB/lzkVIJqSkAPRL8qiJLuRCPVOX1PFD8vV//R8/QumtfbcYBMo6vCk2HmWdrh5OQHPxb3KJtbtG+Z1j8x6HGEAe17djYepBiRMyCEQvYgfD6tvFylc4IquhqE9yaP60PJod5TxpWnRQ6HIGSeBm+S+rYSMalTZ8+pUqOOA+IQCYpfpx6EKIJL/VsW2C7cXdvudxDhXPI5lR/QidCb9Ohq3WkfxXaYwzrngdg2avmNqId9R4KESuM9GoHW0dszfyBCh5wYfeaffMElfDam3B92NUwyhZwtIiv623WVXY9PPz+EDjSJsIAu2Vi1vdJyA4nD4k9Lwmx/1zTc/UaYVLsiBqL2WdfvFTeoWoV+dNxQXSEPhB8gwi8x4O4lZW0cwVy/6fa8KMY8gZbcbSTr7U5bRERfW8l+jY+mYKQ/M/ccgpxaHiw1/+4LWfbJQ7VhJJrTyN0C36FQzY1URkSXg+53wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmVEL4x6xdCqiqu2OBLs27EA8xGYwDQYJKoZIhvcNAQEMBQADggIBABCpITvO1+R4T9v2+onHiFxU5JjtCZ0zkXqRCMp/Z0UIYbeo1p07pZCPAUjBfGPCkAaR++OiG9sysALdJf8Y6HQKcyuAcWUqQnaIhoZ2JcAP7EKq7uCqsMhcYZD/j3O/3RPtSW5UOx6ItDU+Ua0t9Edho9whNw0VQXmo1JjYoP3FzPjuKoDWTSO1q5eYlZfwcTcs55O2shNkFafPg/6cCm5j6v9nyHrM3sk4LjkrBPUXVx2m/aoz219t8O9Ha9/CdMKXsPO/8gTUzpgnzSgPnGnBmi5xr1nspVN8X4E2f3D+DKqBim3YgslD68NcuFQvJ0/BxZzWVbrr+QXoyzaiCgXuogpIDc2bB6oRXqFnHNz36d4QJmJdWdSaijiS/peQ6EOPgOZ1GuObLWlDCBZLNeQ+N6QaiJxVO4XUj/s22i1IRtwdz84TRHrbWiIpEymsqmb/Ep5r4xV5d6+791axclfOTH7tQrY/SbPtTJI4OEgNekI8YfadQifpelF82MsFFEZuaQn0lj+fvLGtE/zKh3OdLTxRc5TAgBB+0T81+JQosygNr2aFFG0hxar1eyw/gLeG8H+7Ie50pyPvXO4OgB6Key8rSExpilQXlvAT1qX0qS3/K1i/9QkSE9ftIPT6vtwLV2sVQzfyanI4IZgWC6ryhvNLsRn0NFnQclor0+aq"
|
||||||
|
FastestvpnOpenvpnStaticKeyV1 = "697fe793b32cb5091d30f2326d5d124a9412e93d0a44ef7361395d76528fcbfc82c3859dccea70a93cfa8fae409709bff75f844cf5ff0c237f426d0c20969233db0e706edb6bdf195ec3dc11b3f76bc807a77e74662d9a800c8cd1144ebb67b7f0d3f1281d1baf522bfe03b7c3f963b1364fc0769400e413b61ca7b43ab19fac9e0f77e41efd4bda7fd77b1de2d7d7855cbbe3e620cecceac72c21a825b243e651f44d90e290e09c3ad650de8fca99c858bc7caad584bc69b11e5c9fd9381c69c505ec487a65912c672d83ed0113b5a74ddfbd3ab33b3683cec593557520a72c4d6cce46111f56f3396cc3ce7183edce553c68ea0796cf6c4375fad00aaa2a42"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FastestvpnCountriesChoices() (choices []string) {
|
||||||
|
servers := FastestvpnServers()
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Country
|
||||||
|
}
|
||||||
|
return choices
|
||||||
|
}
|
||||||
|
|
||||||
|
func FastestvpnHostnameChoices() (choices []string) {
|
||||||
|
servers := FastestvpnServers()
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Hostname
|
||||||
|
}
|
||||||
|
return choices
|
||||||
|
}
|
||||||
|
|
||||||
|
// FastestvpnServers returns the list of all VPN servers for FastestVPN.
|
||||||
|
//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-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}}},
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user