Compare commits
522 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c74ec9a38b | ||
|
|
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 | ||
|
|
1281026850 | ||
|
|
616ba0c538 | ||
|
|
8c7c8f7d5a | ||
|
|
78877483e9 | ||
|
|
de7f12d958 | ||
|
|
7e7312459d | ||
|
|
e3a677c22b | ||
|
|
2f955e0190 | ||
|
|
618441b008 | ||
|
|
4a7d341c57 | ||
|
|
95ad58687d | ||
|
|
0fc69e068e | ||
|
|
7252ac722c | ||
|
|
4cd6b33044 | ||
|
|
0731b1cb82 | ||
|
|
07efea612b | ||
|
|
6afa4f69a0 | ||
|
|
2acf627918 | ||
|
|
4eb7c4ac36 | ||
|
|
b4c838e6ab | ||
|
|
8b096af04e | ||
|
|
78b63174ce | ||
|
|
11fca08028 | ||
|
|
515e72a0ed | ||
|
|
2f9d1f09d3 | ||
|
|
b1596bc7e4 | ||
|
|
ccf11990f1 | ||
|
|
1ac06ee4a8 | ||
|
|
dc1c7eab81 | ||
|
|
5bf471767d | ||
|
|
3d25db1bed | ||
|
|
99e386abc8 | ||
|
|
8669748289 | ||
|
|
a39d885e34 | ||
|
|
7d36993450 | ||
|
|
0d53461706 | ||
|
|
758f316816 | ||
|
|
ad73a027f3 | ||
|
|
2c96f91043 | ||
|
|
53b7fafc49 | ||
|
|
7450ffce2b | ||
|
|
765f06e5a8 | ||
|
|
e304b4a829 | ||
|
|
3ae4523280 | ||
|
|
7a136db085 | ||
|
|
e809e178b9 | ||
|
|
dd529a48fa | ||
|
|
2c6eae4e90 | ||
|
|
18e99d07d0 | ||
|
|
a4b0e0ff86 | ||
|
|
7e36fbbd00 | ||
|
|
d228216d1c | ||
|
|
c9368e352c | ||
|
|
d947d9fe30 | ||
|
|
613ded51ab | ||
|
|
3b43b7c2f6 | ||
|
|
cdbb7bf771 | ||
|
|
5a6cf0fe3a | ||
|
|
082a5bdf51 | ||
|
|
7369808b84 | ||
|
|
4f502abcf8 | ||
|
|
bdcadf09ec | ||
|
|
8cae369186 | ||
|
|
a3d75f3d8b | ||
|
|
1a06d01ae2 | ||
|
|
634cef2bb2 | ||
|
|
6107f5c4ab | ||
|
|
6ae9dc5c2c | ||
|
|
ea3a173e3b | ||
|
|
69217f61a1 | ||
|
|
e33a6a8503 | ||
|
|
0fb065eb61 | ||
|
|
f6a2aac475 | ||
|
|
900fa261d8 | ||
|
|
cfb4dd84bc | ||
|
|
4f72f60a3e | ||
|
|
f262ee6454 | ||
|
|
20a3327815 | ||
|
|
3ab1298b1f | ||
|
|
a7739b6f5d | ||
|
|
263368af89 | ||
|
|
96e57d2c32 | ||
|
|
85a93bdd34 | ||
|
|
cc80d224c2 | ||
|
|
c85cca7fdc | ||
|
|
3f6d3d7c2a | ||
|
|
09a0ba1228 | ||
|
|
6b81ed6bde | ||
|
|
64e447b262 | ||
|
|
d0926111e0 | ||
|
|
aac4298f69 | ||
|
|
f4018d3411 | ||
|
|
0710199409 | ||
|
|
43c15b3e68 | ||
|
|
ab223a5e06 | ||
|
|
fd5e7af3ff | ||
|
|
886d4ad1a9 | ||
|
|
40a72b6189 | ||
|
|
5eb1859f41 | ||
|
|
b45fa026dd | ||
|
|
da739a0c3d | ||
|
|
0dc400b540 | ||
|
|
d12668d57f | ||
|
|
c39affeb12 | ||
|
|
d73765a5f5 | ||
|
|
37282c014b | ||
|
|
adeccf8548 | ||
|
|
a97cbcc4e4 | ||
|
|
89187b6b86 | ||
|
|
754bab9763 | ||
|
|
0d7f6dab1a | ||
|
|
507374ca4e | ||
|
|
318c3c9032 | ||
|
|
c068484fa0 | ||
|
|
7cd35737ba | ||
|
|
0247a1ff01 | ||
|
|
363fabc810 | ||
|
|
6049b10209 | ||
|
|
bc05ff34fd | ||
|
|
8e77842f1e | ||
|
|
41168f88cd | ||
|
|
88ad10d429 | ||
|
|
f4cd1896c9 | ||
|
|
944e6a107b | ||
|
|
b6135d2476 | ||
|
|
c9b6e79792 | ||
|
|
94255aaa38 | ||
|
|
ac706bd156 | ||
|
|
d864a9f580 | ||
|
|
a32318d246 | ||
|
|
45a7a5b9e2 | ||
|
|
9af2a7a640 | ||
|
|
eb62ad06db | ||
|
|
a033637e85 | ||
|
|
b0ea739c20 | ||
|
|
352af84977 | ||
|
|
eb149ee040 | ||
|
|
9b3166a2e2 | ||
|
|
e94f4283e1 | ||
|
|
ef0959a15e | ||
|
|
36424c08ac | ||
|
|
97ea5f63b8 | ||
|
|
88c9d3d687 | ||
|
|
f1569dac05 | ||
|
|
4cb32ef9dc | ||
|
|
e805d42197 | ||
|
|
cbd11bfdf2 | ||
|
|
422bd8d428 | ||
|
|
58459f0336 | ||
|
|
6f6e227b94 | ||
|
|
e015cd4a27 | ||
|
|
768147095f | ||
|
|
8f6b6306d6 | ||
|
|
fb4c9b8a58 | ||
|
|
3d7cfb125a | ||
|
|
d42de99879 | ||
|
|
68203c221d | ||
|
|
3ac3e5022c | ||
|
|
da8391e9ae | ||
|
|
ebdf241888 | ||
|
|
60cec716b2 | ||
|
|
e7a475a303 | ||
|
|
67588e0072 | ||
|
|
bfa3d749ac | ||
|
|
7e79d9696f | ||
|
|
f251c6aa4d | ||
|
|
d2117cd043 | ||
|
|
0235df74a0 | ||
|
|
e5adccd9c5 | ||
|
|
76cea56864 | ||
|
|
643745d33e | ||
|
|
3d6a580102 | ||
|
|
d4a1828c1d | ||
|
|
bdf96d864e | ||
|
|
15a549be11 | ||
|
|
d534f92432 | ||
|
|
d0c61662b5 | ||
|
|
98b076e2cb | ||
|
|
0b997fe6c8 | ||
|
|
b0c0bd6364 | ||
|
|
c61a418430 | ||
|
|
e6bbaa2ba6 | ||
|
|
17ccf98c75 | ||
|
|
4db67c70b8 | ||
|
|
3250a20ffc | ||
|
|
6c12fdff2b | ||
|
|
f033204844 | ||
|
|
e334cf6c5f | ||
|
|
9435db8e1e | ||
|
|
d2b361b998 | ||
|
|
9d786bf338 | ||
|
|
3339455a97 | ||
|
|
0eb2e5a120 | ||
|
|
d0f678c315 | ||
|
|
0c48d2d5a0 | ||
|
|
47a197be48 | ||
|
|
28edae383b | ||
|
|
939b58c457 | ||
|
|
fa0272d5ad | ||
|
|
839c6f05dd | ||
|
|
9ada201b82 | ||
|
|
dd0170afb1 | ||
|
|
9239e840c4 | ||
|
|
96713b26cb | ||
|
|
3ad60349db | ||
|
|
5ee4e2fde0 | ||
|
|
ce4fd8bc68 | ||
|
|
90fc12a941 | ||
|
|
16995e1d93 | ||
|
|
9669938703 | ||
|
|
ac60cf8ab8 | ||
|
|
f5a32e690f | ||
|
|
4e622a92a5 | ||
|
|
d1412f43fd | ||
|
|
1b3a135920 | ||
|
|
53db4813fa | ||
|
|
2f09ed9069 | ||
|
|
9202d6c15f | ||
|
|
023f1c7e8e | ||
|
|
1aebe1a4c1 | ||
|
|
f45f40eee1 | ||
|
|
ab5d60754f | ||
|
|
83e8bb780a | ||
|
|
095623925a | ||
|
|
888d8bbf87 | ||
|
|
fbf04677f1 | ||
|
|
2051aa1b04 | ||
|
|
fc88ee135d | ||
|
|
a6f9a1a3d1 | ||
|
|
f181ff0005 | ||
|
|
71dcf23013 | ||
|
|
95ee3b4276 | ||
|
|
c42d13f14f | ||
|
|
ce11745f6f | ||
|
|
f6b91bd74f | ||
|
|
5c69ddc05f | ||
|
|
ded635bd56 | ||
|
|
66667f94e1 | ||
|
|
77c6eeb765 | ||
|
|
040b5afca6 | ||
|
|
321579333d | ||
|
|
a76aa5276d | ||
|
|
0264f8726a | ||
|
|
247dc01f8a | ||
|
|
6734779e90 | ||
|
|
e527f14bd2 | ||
|
|
a40f68f1df | ||
|
|
84f49c5827 | ||
|
|
792f70ffa7 | ||
|
|
7f35daa418 | ||
|
|
86ed6736a5 | ||
|
|
6620ba52d2 | ||
|
|
1f873e7d66 | ||
|
|
fc9ebd561c | ||
|
|
63fd72524e | ||
|
|
ed5a90ef25 | ||
|
|
7f103b2749 | ||
|
|
69796e1ff9 | ||
|
|
6a9cd7ed9c | ||
|
|
64649039d9 | ||
|
|
3de4ffcf66 | ||
|
|
60a69f316b | ||
|
|
9b26a39690 | ||
|
|
73cef63e73 | ||
|
|
90f506d2b7 | ||
|
|
07cb909061 | ||
|
|
af5c7c648d | ||
|
|
fd248098a6 | ||
|
|
a21bb009e5 | ||
|
|
8b313cf211 | ||
|
|
adf82d844a | ||
|
|
0af0632304 | ||
|
|
9a2d0ec3ef |
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
|
||||
68
.devcontainer/README.md
Normal file
68
.devcontainer/README.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# 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.
|
||||
|
||||
## 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.
|
||||
81
.devcontainer/devcontainer.json
Normal file
81
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"name": "gluetun-dev",
|
||||
"dockerComposeFile": [
|
||||
"docker-compose.yml"
|
||||
],
|
||||
"service": "vscode",
|
||||
"runServices": [
|
||||
"vscode"
|
||||
],
|
||||
"shutdownAction": "stopCompose",
|
||||
"postCreateCommand": "go mod download",
|
||||
"workspaceFolder": "/workspace",
|
||||
"extensions": [
|
||||
"golang.go",
|
||||
"eamodio.gitlens", // IDE Git information
|
||||
"davidanson.vscode-markdownlint",
|
||||
"ms-azuretools.vscode-docker", // Docker integration and linting
|
||||
"shardulm94.trailing-spaces", // Show trailing spaces
|
||||
"Gruntfuggly.todo-tree", // Highlights TODO comments
|
||||
"bierner.emojisense", // Emoji sense for markdown
|
||||
"stkb.rewrap", // rewrap comments after n characters on one line
|
||||
"vscode-icons-team.vscode-icons", // Better file extension icons
|
||||
"github.vscode-pull-request-github", // Github interaction
|
||||
"redhat.vscode-yaml", // Kubernetes, Drone syntax highlighting
|
||||
"bajdzis.vscode-database", // Supports connections to mysql or postgres, over SSL, socked
|
||||
"IBM.output-colorizer", // Colorize your output/test logs
|
||||
"mohsen1.prettify-json", // Prettify JSON data
|
||||
],
|
||||
"settings": {
|
||||
"files.eol": "\n",
|
||||
"remote.extensionKind": {
|
||||
"ms-azuretools.vscode-docker": "workspace"
|
||||
},
|
||||
"editor.codeActionsOnSaveTimeout": 3000,
|
||||
"go.useLanguageServer": true,
|
||||
"[go]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": true,
|
||||
},
|
||||
// Optional: Disable snippets, as they conflict with completion ranking.
|
||||
"editor.snippetSuggestions": "none"
|
||||
},
|
||||
"[go.mod]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": true,
|
||||
},
|
||||
},
|
||||
"gopls": {
|
||||
"usePlaceholders": false,
|
||||
"staticcheck": true
|
||||
},
|
||||
"go.autocompleteUnimportedPackages": true,
|
||||
"go.gotoSymbol.includeImports": true,
|
||||
"go.gotoSymbol.includeGoroot": true,
|
||||
"go.lintTool": "golangci-lint",
|
||||
"go.buildOnSave": "workspace",
|
||||
"go.lintOnSave": "workspace",
|
||||
"go.vetOnSave": "workspace",
|
||||
"editor.formatOnSave": true,
|
||||
"go.toolsEnvVars": {
|
||||
"GOFLAGS": "-tags=",
|
||||
// "CGO_ENABLED": 1 // for the race detector
|
||||
},
|
||||
"gopls.env": {
|
||||
"GOFLAGS": "-tags="
|
||||
},
|
||||
"go.testEnvVars": {
|
||||
"": ""
|
||||
},
|
||||
"go.testFlags": [
|
||||
"-v",
|
||||
// "-race"
|
||||
],
|
||||
"go.testTimeout": "10s",
|
||||
"go.coverOnSingleTest": true,
|
||||
"go.coverOnSingleTestFile": true,
|
||||
"go.coverOnTestPackage": true
|
||||
}
|
||||
}
|
||||
25
.devcontainer/docker-compose.yml
Normal file
25
.devcontainer/docker-compose.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
vscode:
|
||||
build: .
|
||||
image: godevcontainer
|
||||
volumes:
|
||||
- ../:/workspace
|
||||
# Docker socket to access Docker server
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
# SSH directory
|
||||
- ~/.ssh:/home/vscode/.ssh
|
||||
- ~/.ssh:/root/.ssh
|
||||
# Git config
|
||||
- ~/.gitconfig:/home/districter/.gitconfig
|
||||
- ~/.gitconfig:/root/.gitconfig
|
||||
environment:
|
||||
- TZ=
|
||||
cap_add:
|
||||
# For debugging with dlv
|
||||
- SYS_PTRACE
|
||||
security_opt:
|
||||
# For debugging with dlv
|
||||
- seccomp:unconfined
|
||||
entrypoint: zsh -c "while sleep 1000; do :; done"
|
||||
@@ -1,4 +1,9 @@
|
||||
.devcontainer
|
||||
.git
|
||||
readme
|
||||
*.yml
|
||||
*.md
|
||||
.github
|
||||
doc
|
||||
docker-compose.yml
|
||||
Dockerfile
|
||||
LICENSE
|
||||
README.md
|
||||
title.svg
|
||||
|
||||
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1 @@
|
||||
@qdm12
|
||||
18
.github/CONTRIBUTING.md
vendored
Normal file
18
.github/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# Contributing
|
||||
|
||||
Contributions are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [open source license of this project](../LICENSE).
|
||||
|
||||
## Submitting a pull request
|
||||
|
||||
1. [Fork](https://github.com/qdm12/gluetun/fork) and clone the repository
|
||||
1. Create a new branch `git checkout -b my-branch-name`
|
||||
1. Modify the code
|
||||
1. Ensure the docker build succeeds `docker build .` (you might need `export DOCKER_BUILDKIT=1`)
|
||||
1. Commit your modifications
|
||||
1. Push to your fork and [submit a pull request](https://github.com/qdm12/gluetun/compare)
|
||||
|
||||
## Resources
|
||||
|
||||
- [Gluetun guide on development](https://github.com/qdm12/gluetun/wiki/Development)
|
||||
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
|
||||
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
|
||||
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
github: [qdm12]
|
||||
37
.github/ISSUE_TEMPLATE/bug.md
vendored
Normal file
37
.github/ISSUE_TEMPLATE/bug.md
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: Bug
|
||||
about: Report a bug
|
||||
title: 'Bug: FILL THIS TEXT!'
|
||||
labels: ":bug: bug"
|
||||
assignees: qdm12
|
||||
|
||||
---
|
||||
|
||||
**Host OS** (approximate answer is fine too): Ubuntu 18
|
||||
|
||||
**Is this urgent?**: No
|
||||
|
||||
**What VPN provider are you using**:
|
||||
|
||||
**What are you using to run your container?**: Docker Compose
|
||||
|
||||
**What is the version of the program** (See the line at the top of your logs)
|
||||
|
||||
```
|
||||
Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
|
||||
```
|
||||
|
||||
**What's the problem** 🤔
|
||||
|
||||
That feature doesn't work
|
||||
|
||||
**Share your logs...**
|
||||
|
||||
...*careful to remove i.e. token information with PIA port forwarding*
|
||||
|
||||
```log
|
||||
|
||||
PASTE YOUR LOGS
|
||||
IN THERE
|
||||
|
||||
```
|
||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest a feature to add to this project
|
||||
title: 'Feature request: FILL THIS TEXT!'
|
||||
labels: ":bulb: feature request"
|
||||
assignees: qdm12
|
||||
|
||||
---
|
||||
|
||||
**What's the feature?** 🧐
|
||||
|
||||
- Support this new feature because that and that
|
||||
|
||||
**Optional extra information** 🚀
|
||||
|
||||
- I tried `docker run something` and it doesn't work
|
||||
- That [url](https://github.com/qdm12/gluetun) is interesting
|
||||
53
.github/ISSUE_TEMPLATE/help.md
vendored
Normal file
53
.github/ISSUE_TEMPLATE/help.md
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: Help
|
||||
about: Ask for help
|
||||
title: 'Help: FILL THIS TEXT!'
|
||||
labels: ":pray: help wanted"
|
||||
assignees:
|
||||
|
||||
---
|
||||
|
||||
**Host OS** (approximate answer is fine too): Ubuntu 18
|
||||
|
||||
**Is this urgent?**: No
|
||||
|
||||
**What VPN provider are you using**:
|
||||
|
||||
**What is the version of the program** (See the line at the top of your logs)
|
||||
|
||||
```
|
||||
Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
|
||||
```
|
||||
|
||||
**What's the problem** 🤔
|
||||
|
||||
That feature doesn't work
|
||||
|
||||
**Share your logs...**
|
||||
|
||||
...*careful to remove i.e. token information with PIA port forwarding*
|
||||
|
||||
```log
|
||||
|
||||
PASTE YOUR LOGS
|
||||
IN THERE
|
||||
|
||||
```
|
||||
|
||||
**What are you using to run your container?**: Docker Compose
|
||||
|
||||
Please also share your configuration file:
|
||||
|
||||
```yml
|
||||
your .yml
|
||||
content
|
||||
in here
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```sh
|
||||
# your docker
|
||||
# run command
|
||||
# in here
|
||||
```
|
||||
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"
|
||||
67
.github/labels.yml
vendored
Normal file
67
.github/labels.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
- name: "Bug :bug:"
|
||||
color: "b60205"
|
||||
description: ""
|
||||
- name: "Feature request :bulb:"
|
||||
color: "0e8a16"
|
||||
description: ""
|
||||
- name: "Help wanted :pray:"
|
||||
color: "4caf50"
|
||||
description: ""
|
||||
- name: "Documentation :memo:"
|
||||
color: "c5def5"
|
||||
description: ""
|
||||
- name: "Needs more info :thinking:"
|
||||
color: "795548"
|
||||
description: ""
|
||||
|
||||
# VPN providers
|
||||
- name: ":cloud: Cyberghost"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: Mullvad"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: NordVPN"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: PIA"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: Privado"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: PureVPN"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: Surfshark"
|
||||
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: ""
|
||||
100
.github/workflows/build.yml
vendored
Normal file
100
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- .github/workflows/build.yml
|
||||
- cmd/**
|
||||
- internal/**
|
||||
- pkg/**
|
||||
- .dockerignore
|
||||
- .golangci.yml
|
||||
- Dockerfile
|
||||
- go.mod
|
||||
- go.sum
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_BUILDKIT: "1"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Linting
|
||||
run: docker build --target lint .
|
||||
|
||||
- name: Go mod tidy check
|
||||
run: docker build --target tidy .
|
||||
|
||||
- name: Build test image
|
||||
run: docker build --target test -t test-container .
|
||||
|
||||
- name: Run tests in test container
|
||||
run: |
|
||||
touch coverage.txt
|
||||
docker run --rm \
|
||||
-v "$(pwd)/coverage.txt:/tmp/gobuild/coverage.txt" \
|
||||
test-container \
|
||||
go test \
|
||||
-race \
|
||||
-coverpkg=./... \
|
||||
-coverprofile=coverage.txt \
|
||||
-covermode=atomic \
|
||||
./...
|
||||
|
||||
# We run this here to use the caching of the previous steps
|
||||
- if: github.event_name == 'push'
|
||||
name: Build final image
|
||||
run: docker build .
|
||||
|
||||
publish:
|
||||
needs: [verify]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: docker/setup-qemu-action@v1
|
||||
- uses: docker/setup-buildx-action@v1
|
||||
|
||||
- uses: docker/login-action@v1
|
||||
with:
|
||||
username: qmcgaw
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Set variables
|
||||
id: vars
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
run: |
|
||||
BRANCH=${GITHUB_REF#refs/heads/}
|
||||
TAG=${GITHUB_REF#refs/tags/}
|
||||
echo ::set-output name=commit::$(git rev-parse --short HEAD)
|
||||
echo ::set-output name=build_date::$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
if [ "$TAG" != "$GITHUB_REF" ]; then
|
||||
echo ::set-output name=version::$TAG
|
||||
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/s390x,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/s390x,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
|
||||
fi
|
||||
|
||||
- name: Build and push final image
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
platforms: ${{ steps.vars.outputs.platforms }}
|
||||
build-args: |
|
||||
BUILD_DATE=${{ steps.vars.outputs.build_date }}
|
||||
COMMIT=${{ steps.vars.outputs.commit }}
|
||||
VERSION=${{ steps.vars.outputs.version }}
|
||||
tags: |
|
||||
qmcgaw/gluetun:${{ steps.vars.outputs.version }}
|
||||
qmcgaw/private-internet-access:${{ steps.vars.outputs.version }}
|
||||
push: true
|
||||
|
||||
- if: github.event_name == 'push' && github.event.ref == 'refs/heads/master'
|
||||
name: Microbadger hook
|
||||
run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/gluetun/l-keGI7p4IhX4QuIDMFYKhsZ1L0=
|
||||
continue-on-error: true
|
||||
21
.github/workflows/dockerhub-description.yml
vendored
Normal file
21
.github/workflows/dockerhub-description.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Docker Hub description
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- README.md
|
||||
- .github/workflows/dockerhub-description.yml
|
||||
jobs:
|
||||
dockerHubDescription:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Docker Hub Description
|
||||
uses: peter-evans/dockerhub-description@v2
|
||||
with:
|
||||
username: qmcgaw
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
repository: qmcgaw/gluetun
|
||||
short-description: Lightweight Swiss-knife VPN client to connect to several VPN providers
|
||||
readme-filepath: README.md
|
||||
15
.github/workflows/labels.yml
vendored
Normal file
15
.github/workflows/labels.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: labels
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- .github/labels.yml
|
||||
- .github/workflows/labels.yml
|
||||
jobs:
|
||||
labeler:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: crazy-max/ghaction-github-labeler@v3
|
||||
with:
|
||||
yaml-file: .github/labels.yml
|
||||
15
.github/workflows/misspell.yml
vendored
Normal file
15
.github/workflows/misspell.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: Misspells
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master]
|
||||
push:
|
||||
branches: [master]
|
||||
jobs:
|
||||
misspell:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: reviewdog/action-misspell@v1
|
||||
with:
|
||||
locale: "US"
|
||||
level: error
|
||||
70
.golangci.yml
Normal file
70
.golangci.yml
Normal file
@@ -0,0 +1,70 @@
|
||||
linters-settings:
|
||||
maligned:
|
||||
suggest-new: true
|
||||
misspell:
|
||||
locale: US
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- dupl
|
||||
- maligned
|
||||
- path: internal/unix/constants\.go
|
||||
linters:
|
||||
- golint
|
||||
text: don't use ALL_CAPS in Go names; use CamelCase
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- asciicheck
|
||||
- bodyclose
|
||||
- deadcode
|
||||
- dogsled
|
||||
- dupl
|
||||
- errcheck
|
||||
- exhaustive
|
||||
- exportloopref
|
||||
- gci
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- gocognit
|
||||
- goconst
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- godot
|
||||
- goheader
|
||||
- goimports
|
||||
- golint
|
||||
- gomnd
|
||||
- goprintffuncname
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- interfacer
|
||||
- lll
|
||||
- maligned
|
||||
- misspell
|
||||
- nakedret
|
||||
- nestif
|
||||
- noctx
|
||||
- nolintlint
|
||||
- prealloc
|
||||
- rowserrcheck
|
||||
- scopelint
|
||||
- sqlclosecheck
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- varcheck
|
||||
- whitespace
|
||||
|
||||
run:
|
||||
skip-dirs:
|
||||
- .devcontainer
|
||||
- .github
|
||||
- doc
|
||||
8
.vscode/extensions.json
vendored
8
.vscode/extensions.json
vendored
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"shardulm94.trailing-spaces",
|
||||
"ms-azuretools.vscode-docker",
|
||||
"davidanson.vscode-markdownlint",
|
||||
"IBM.output-colorizer"
|
||||
]
|
||||
}
|
||||
217
Dockerfile
217
Dockerfile
@@ -1,81 +1,154 @@
|
||||
ARG BASE_IMAGE=alpine
|
||||
ARG ALPINE_VERSION=3.10
|
||||
ARG ALPINE_VERSION=3.12
|
||||
ARG GO_VERSION=1.15
|
||||
ARG BUILDPLATFORM=linux/amd64
|
||||
|
||||
FROM ${BASE_IMAGE}:${ALPINE_VERSION}
|
||||
ARG BUILD_DATE
|
||||
ARG VCS_REF
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
|
||||
RUN apk --update add git
|
||||
ENV CGO_ENABLED=0
|
||||
WORKDIR /tmp/gobuild
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY cmd/ ./cmd/
|
||||
COPY internal/ ./internal/
|
||||
|
||||
FROM --platform=$BUILDPLATFORM base AS test
|
||||
# Note on the go race detector:
|
||||
# - we set CGO_ENABLED=1 to have it enabled
|
||||
# - we install g++ to support the race detector
|
||||
ENV CGO_ENABLED=1
|
||||
RUN apk --update --no-cache add g++
|
||||
|
||||
FROM --platform=$BUILDPLATFORM base AS lint
|
||||
ARG GOLANGCI_LINT_VERSION=v1.35.2
|
||||
RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | \
|
||||
sh -s -- -b /usr/local/bin ${GOLANGCI_LINT_VERSION}
|
||||
COPY .golangci.yml ./
|
||||
RUN golangci-lint run --timeout=10m
|
||||
|
||||
FROM --platform=$BUILDPLATFORM base AS tidy
|
||||
RUN git init && \
|
||||
git config user.email ci@localhost && \
|
||||
git config user.name ci && \
|
||||
git add -A && git commit -m ci && \
|
||||
sed -i '/\/\/ indirect/d' go.mod && \
|
||||
go mod tidy && \
|
||||
git diff --exit-code -- go.mod
|
||||
|
||||
FROM --platform=$BUILDPLATFORM base AS build
|
||||
COPY --from=qmcgaw/xcputranslate:v0.4.0 /xcputranslate /usr/local/bin/xcputranslate
|
||||
ARG TARGETPLATFORM
|
||||
ARG VERSION=unknown
|
||||
ARG BUILD_DATE="an unknown date"
|
||||
ARG COMMIT=unknown
|
||||
RUN GOARCH="$(xcputranslate -field arch -targetplatform ${TARGETPLATFORM})" \
|
||||
GOARM="$(xcputranslate -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}
|
||||
ARG VERSION=unknown
|
||||
ARG BUILD_DATE="an unknown date"
|
||||
ARG COMMIT=unknown
|
||||
LABEL \
|
||||
org.opencontainers.image.authors="quentin.mcgaw@gmail.com" \
|
||||
org.opencontainers.image.created=$BUILD_DATE \
|
||||
org.opencontainers.image.version="" \
|
||||
org.opencontainers.image.revision=$VCS_REF \
|
||||
org.opencontainers.image.url="https://github.com/qdm12/private-internet-access-docker" \
|
||||
org.opencontainers.image.documentation="https://github.com/qdm12/private-internet-access-docker" \
|
||||
org.opencontainers.image.source="https://github.com/qdm12/private-internet-access-docker" \
|
||||
org.opencontainers.image.title="PIA client" \
|
||||
org.opencontainers.image.description="VPN client to tunnel to private internet access servers using OpenVPN, IPtables, DNS over TLS and Alpine Linux" \
|
||||
image-size="23.3MB" \
|
||||
ram-usage="13MB to 80MB" \
|
||||
cpu-usage="Low to Medium"
|
||||
ENV USER= \
|
||||
PASSWORD= \
|
||||
ENCRYPTION=strong \
|
||||
org.opencontainers.image.version=$VERSION \
|
||||
org.opencontainers.image.revision=$COMMIT \
|
||||
org.opencontainers.image.url="https://github.com/qdm12/gluetun" \
|
||||
org.opencontainers.image.documentation="https://github.com/qdm12/gluetun" \
|
||||
org.opencontainers.image.source="https://github.com/qdm12/gluetun" \
|
||||
org.opencontainers.image.title="VPN swiss-knife like client for multiple VPN providers" \
|
||||
org.opencontainers.image.description="VPN swiss-knife like client to tunnel to multiple VPN servers using OpenVPN, IPtables, DNS over TLS, Shadowsocks, an HTTP proxy and Alpine Linux"
|
||||
ENV VPNSP=pia \
|
||||
VERSION_INFORMATION=on \
|
||||
PROTOCOL=udp \
|
||||
REGION="CA Montreal" \
|
||||
NONROOT=no \
|
||||
DOT=on \
|
||||
BLOCK_MALICIOUS=off \
|
||||
BLOCK_NSA=off \
|
||||
UNBLOCK= \
|
||||
EXTRA_SUBNETS= \
|
||||
OPENVPN_VERBOSITY=1 \
|
||||
OPENVPN_ROOT=no \
|
||||
OPENVPN_TARGET_IP= \
|
||||
OPENVPN_IPV6=off \
|
||||
TZ= \
|
||||
PUID= \
|
||||
PGID= \
|
||||
PUBLICIP_FILE="/tmp/gluetun/ip" \
|
||||
# PIA, Windscribe, Surfshark, Cyberghost, Vyprvpn, NordVPN, PureVPN only
|
||||
OPENVPN_USER= \
|
||||
OPENVPN_PASSWORD= \
|
||||
USER_SECRETFILE=/run/secrets/openvpn_user \
|
||||
PASSWORD_SECRETFILE=/run/secrets/openvpn_password \
|
||||
REGION= \
|
||||
# PIA only
|
||||
PIA_ENCRYPTION=strong \
|
||||
PORT_FORWARDING=off \
|
||||
PORT_FORWARDING_STATUS_FILE="/forwarded_port" \
|
||||
TINYPROXY=off \
|
||||
TINYPROXY_LOG=Critical \
|
||||
TINYPROXY_PORT=8888 \
|
||||
TINYPROXY_USER= \
|
||||
TINYPROXY_PASSWORD= \
|
||||
PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \
|
||||
# Mullvad and PureVPN only
|
||||
COUNTRY= \
|
||||
# Mullvad, PureVPN, Windscribe only
|
||||
CITY= \
|
||||
# Windscribe only
|
||||
SERVER_HOSTNAME= \
|
||||
# Mullvad only
|
||||
ISP= \
|
||||
OWNED=no \
|
||||
# Mullvad and Windscribe only
|
||||
PORT= \
|
||||
# Cyberghost only
|
||||
CYBERGHOST_GROUP="Premium UDP Europe" \
|
||||
OPENVPN_CLIENTCRT_SECRETFILE=/run/secrets/openvpn_clientcrt \
|
||||
OPENVPN_CLIENTKEY_SECRETFILE=/run/secrets/openvpn_clientkey \
|
||||
# NordVPN only
|
||||
SERVER_NUMBER= \
|
||||
# Openvpn
|
||||
OPENVPN_CIPHER= \
|
||||
OPENVPN_AUTH= \
|
||||
# DNS over TLS
|
||||
DOT=on \
|
||||
DOT_PROVIDERS=cloudflare \
|
||||
DOT_PRIVATE_ADDRESS=127.0.0.1/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,::1/128,fc00::/7,fe80::/10,::ffff:0:0/96 \
|
||||
DOT_VERBOSITY=1 \
|
||||
DOT_VERBOSITY_DETAILS=0 \
|
||||
DOT_VALIDATION_LOGLEVEL=0 \
|
||||
DOT_CACHING=on \
|
||||
DOT_IPV6=off \
|
||||
BLOCK_MALICIOUS=on \
|
||||
BLOCK_SURVEILLANCE=off \
|
||||
BLOCK_ADS=off \
|
||||
UNBLOCK= \
|
||||
DNS_UPDATE_PERIOD=24h \
|
||||
DNS_PLAINTEXT_ADDRESS=1.1.1.1 \
|
||||
DNS_KEEP_NAMESERVER=off \
|
||||
# Firewall
|
||||
FIREWALL=on \
|
||||
FIREWALL_VPN_INPUT_PORTS= \
|
||||
FIREWALL_INPUT_PORTS= \
|
||||
FIREWALL_OUTBOUND_SUBNETS= \
|
||||
FIREWALL_DEBUG=off \
|
||||
# HTTP proxy
|
||||
HTTPPROXY= \
|
||||
HTTPPROXY_LOG=off \
|
||||
HTTPPROXY_PORT=8888 \
|
||||
HTTPPROXY_USER= \
|
||||
HTTPPROXY_PASSWORD= \
|
||||
HTTPPROXY_USER_SECRETFILE=/run/secrets/httpproxy_user \
|
||||
HTTPPROXY_PASSWORD_SECRETFILE=/run/secrets/httpproxy_password \
|
||||
# Shadowsocks
|
||||
SHADOWSOCKS=off \
|
||||
SHADOWSOCKS_LOG=on \
|
||||
SHADOWSOCKS_LOG=off \
|
||||
SHADOWSOCKS_PORT=8388 \
|
||||
SHADOWSOCKS_PASSWORD= \
|
||||
TZ=
|
||||
ENTRYPOINT /entrypoint.sh
|
||||
EXPOSE 8888/tcp 8388/tcp 8388/udp
|
||||
HEALTHCHECK --interval=3m --timeout=3s --start-period=20s --retries=1 CMD /healthcheck.sh
|
||||
RUN apk add -q --progress --no-cache --update openvpn wget ca-certificates iptables unbound unzip tinyproxy jq tzdata && \
|
||||
echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories && \
|
||||
apk add -q --progress --no-cache --update shadowsocks-libev && \
|
||||
wget -q https://www.privateinternetaccess.com/openvpn/openvpn.zip \
|
||||
https://www.privateinternetaccess.com/openvpn/openvpn-strong.zip \
|
||||
https://www.privateinternetaccess.com/openvpn/openvpn-tcp.zip \
|
||||
https://www.privateinternetaccess.com/openvpn/openvpn-strong-tcp.zip && \
|
||||
mkdir -p /openvpn/target && \
|
||||
unzip -q openvpn.zip -d /openvpn/udp-normal && \
|
||||
unzip -q openvpn-strong.zip -d /openvpn/udp-strong && \
|
||||
unzip -q openvpn-tcp.zip -d /openvpn/tcp-normal && \
|
||||
unzip -q openvpn-strong-tcp.zip -d /openvpn/tcp-strong && \
|
||||
apk del -q --progress --purge unzip && \
|
||||
rm -rf /*.zip /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-anchor /usr/sbin/unbound-checkconf /usr/sbin/unbound-control /usr/sbin/unbound-control-setup /usr/sbin/unbound-host /etc/tinyproxy/tinyproxy.conf && \
|
||||
adduser nonrootuser -D -H --uid 1000 && \
|
||||
wget -q https://raw.githubusercontent.com/qdm12/files/master/named.root.updated -O /etc/unbound/root.hints && \
|
||||
wget -q https://raw.githubusercontent.com/qdm12/files/master/root.key.updated -O /etc/unbound/root.key && \
|
||||
cd /tmp && \
|
||||
wget -q https://raw.githubusercontent.com/qdm12/files/master/malicious-hostnames.updated -O malicious-hostnames && \
|
||||
wget -q https://raw.githubusercontent.com/qdm12/files/master/surveillance-hostnames.updated -O nsa-hostnames && \
|
||||
wget -q https://raw.githubusercontent.com/qdm12/files/master/malicious-ips.updated -O malicious-ips && \
|
||||
while read hostname; do echo "local-zone: \""$hostname"\" static" >> blocks-malicious.conf; done < malicious-hostnames && \
|
||||
while read ip; do echo "private-address: $ip" >> blocks-malicious.conf; done < malicious-ips && \
|
||||
tar -cjf /etc/unbound/blocks-malicious.bz2 blocks-malicious.conf && \
|
||||
while read hostname; do echo "local-zone: \""$hostname"\" static" >> blocks-nsa.conf; done < nsa-hostnames && \
|
||||
tar -cjf /etc/unbound/blocks-nsa.bz2 blocks-nsa.conf && \
|
||||
rm -f /tmp/*
|
||||
COPY unbound.conf /etc/unbound/unbound.conf
|
||||
COPY tinyproxy.conf /etc/tinyproxy/tinyproxy.conf
|
||||
COPY shadowsocks.json /etc/shadowsocks.json
|
||||
COPY entrypoint.sh healthcheck.sh portforward.sh /
|
||||
RUN chown nonrootuser -R /etc/unbound /etc/tinyproxy && \
|
||||
chmod 700 /etc/unbound /etc/tinyproxy && \
|
||||
chmod 600 /etc/unbound/unbound.conf /etc/tinyproxy/tinyproxy.conf /etc/shadowsocks.json && \
|
||||
chmod 500 /entrypoint.sh /healthcheck.sh /portforward.sh && \
|
||||
chmod 400 /etc/unbound/root.hints /etc/unbound/root.key /etc/unbound/*.bz2
|
||||
SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password \
|
||||
SHADOWSOCKS_METHOD=chacha20-ietf-poly1305 \
|
||||
UPDATER_PERIOD=0
|
||||
ENTRYPOINT ["/entrypoint"]
|
||||
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
|
||||
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /entrypoint healthcheck
|
||||
RUN apk add -q --progress --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \
|
||||
rm -rf /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-* && \
|
||||
deluser openvpn && \
|
||||
deluser unbound && \
|
||||
mkdir /gluetun
|
||||
# TODO remove once SAN is added to PIA servers certificates, see https://github.com/pia-foss/manual-connections/issues/10
|
||||
ENV GODEBUG=x509ignoreCN=0
|
||||
COPY --from=build /tmp/gobuild/entrypoint /entrypoint
|
||||
|
||||
440
README.md
440
README.md
@@ -1,324 +1,116 @@
|
||||
# Private Internet Access Client (OpenVPN+Iptables+DNS over TLS on Alpine Linux)
|
||||
|
||||
*Lightweight VPN client to tunnel to private internet access servers*
|
||||
|
||||
[](https://hub.docker.com/r/qmcgaw/private-internet-access/)
|
||||
|
||||
[](https://join.slack.com/t/qdm12/shared_invite/enQtODMwMDQyMTAxMjY1LTU1YjE1MTVhNTBmNTViNzJiZmQwZWRmMDhhZjEyNjVhZGM4YmIxOTMxOTYzN2U0N2U2YjQ2MDk3YmYxN2NiNTc)
|
||||
[](https://travis-ci.org/qdm12/private-internet-access-docker)
|
||||
[](https://hub.docker.com/r/qmcgaw/private-internet-access)
|
||||
|
||||
[](https://github.com/qdm12/private-internet-access-docker/issues)
|
||||
[](https://github.com/qdm12/private-internet-access-docker/issues)
|
||||
[](https://github.com/qdm12/private-internet-access-docker/issues)
|
||||
|
||||
[](https://hub.docker.com/r/qmcgaw/private-internet-access)
|
||||
[](https://hub.docker.com/r/qmcgaw/private-internet-access)
|
||||
[](https://hub.docker.com/r/qmcgaw/private-internet-access)
|
||||
|
||||
[](https://microbadger.com/images/qmcgaw/private-internet-access)
|
||||
[](https://microbadger.com/images/qmcgaw/private-internet-access)
|
||||
|
||||
| Image size | RAM usage | CPU usage |
|
||||
| --- | --- | --- |
|
||||
| 23.3MB | 14MB to 80MB | Low to Medium |
|
||||
|
||||
<details><summary>Click to show base components</summary><p>
|
||||
|
||||
- [Alpine 3.10](https://alpinelinux.org) for a tiny image
|
||||
- [OpenVPN 2.4.7](https://pkgs.alpinelinux.org/package/v3.10/main/x86_64/openvpn) to tunnel to PIA servers
|
||||
- [IPtables 1.8.3](https://pkgs.alpinelinux.org/package/v3.10/main/x86_64/iptables) enforces the container to communicate only through the VPN or with other containers in its virtual network (acts as a killswitch)
|
||||
- [Unbound 1.9.1](https://pkgs.alpinelinux.org/package/v3.10/main/x86_64/unbound) configured with Cloudflare's [1.1.1.1](https://1.1.1.1) DNS over TLS
|
||||
- [Files and blocking lists built periodically](https://github.com/qdm12/updated/tree/master/files) used with Unbound (see `BLOCK_MALICIOUS` and `BLOCK_NSA` environment variables)
|
||||
- [TinyProxy 1.10.0](https://pkgs.alpinelinux.org/package/v3.10/main/x86_64/tinyproxy)
|
||||
|
||||
</p></details>
|
||||
|
||||
## Features
|
||||
|
||||
- <details><summary>Configure everything with environment variables</summary><p>
|
||||
|
||||
- [Destination region](https://www.privateinternetaccess.com/pages/network)
|
||||
- Internet protocol
|
||||
- Level of encryption
|
||||
- PIA Username and password
|
||||
- DNS over TLS
|
||||
- Malicious DNS blocking
|
||||
- Internal firewall
|
||||
- Web HTTP proxy
|
||||
- Run openvpn without root
|
||||
|
||||
</p></details>
|
||||
- Connect other containers to it, [see this](https://github.com/qdm12/private-internet-access-docker#connect-to-it)
|
||||
- **ARM** compatible
|
||||
- Port forwarding
|
||||
- The *iptables* firewall allows traffic only with needed PIA servers (IP addresses, port, protocol) combinations
|
||||
- OpenVPN reconnects automatically on failure
|
||||
- Docker healthcheck pings the DNS 1.1.1.1 to verify the connection is up
|
||||
- Unbound DNS runs *without root*
|
||||
- OpenVPN can run *without root* but this disallows OpenVPN reconnecting, it can be set with `NONROOT=yes`
|
||||
- Connect your LAN devices
|
||||
- HTTP Web proxy *tinyproxy*
|
||||
- SOCKS5 proxy *shadowsocks*
|
||||
|
||||
## Setup
|
||||
|
||||
1. <details><summary>Requirements</summary><p>
|
||||
|
||||
- A Private Internet Access **username** and **password** - [Sign up](https://www.privateinternetaccess.com/pages/buy-vpn/)
|
||||
- External firewall requirements, if you have one
|
||||
- Allow outbound TCP 853 to 1.1.1.1 to allow Unbound to resolve the PIA domain name at start. You can then block it once the container is started.
|
||||
- For UDP strong encryption, allow outbound UDP 1197
|
||||
- For UDP normal encryption, allow outbound UDP 1198
|
||||
- For TCP strong encryption, allow outbound TCP 501
|
||||
- For TCP normal encryption, allow outbound TCP 502
|
||||
- For the built-in web HTTP proxy, allow inbound TCP 8888
|
||||
- For the built-in SOCKS5 proxy, allow inbound TCP 8388 and UDP 8388
|
||||
- Docker API 1.25 to support `init`
|
||||
- If you use Docker Compose, docker-compose >= 1.22.0, to support `init: true`
|
||||
|
||||
</p></details>
|
||||
|
||||
1. Ensure `/dev/net/tun` is setup on your host with either:
|
||||
|
||||
```sh
|
||||
insmod /lib/modules/tun.ko
|
||||
# or...
|
||||
modprobe tun
|
||||
```
|
||||
|
||||
1. <details><summary>CLICK IF YOU HAVE AN ARM DEVICE</summary><p>
|
||||
|
||||
- If you have a ARM 32 bit v6 architecture
|
||||
|
||||
```sh
|
||||
docker build -t qmcgaw/private-internet-access \
|
||||
--build-arg BASE_IMAGE=arm32v6/alpine \
|
||||
https://github.com/qdm12/private-internet-access-docker.git
|
||||
```
|
||||
|
||||
- If you have a ARM 32 bit v7 architecture
|
||||
|
||||
```sh
|
||||
docker build -t qmcgaw/private-internet-access \
|
||||
--build-arg BASE_IMAGE=arm32v7/alpine \
|
||||
https://github.com/qdm12/private-internet-access-docker.git
|
||||
```
|
||||
|
||||
- If you have a ARM 64 bit v8 architecture
|
||||
|
||||
```sh
|
||||
docker build -t qmcgaw/private-internet-access \
|
||||
--build-arg BASE_IMAGE=arm64v8/alpine \
|
||||
https://github.com/qdm12/private-internet-access-docker.git
|
||||
```
|
||||
|
||||
</p></details>
|
||||
|
||||
1. Launch the container with:
|
||||
|
||||
```bash
|
||||
docker run -d --init --name=pia --cap-add=NET_ADMIN --device=/dev/net/tun \
|
||||
-e REGION="CA Montreal" -e USER=js89ds7 -e PASSWORD=8fd9s239G \
|
||||
qmcgaw/private-internet-access
|
||||
```
|
||||
|
||||
or use [docker-compose.yml](https://github.com/qdm12/private-internet-access-docker/blob/master/docker-compose.yml) with:
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
Note that you can:
|
||||
- Change the many [environment variables](#environment-variables) available
|
||||
- Use `-p 8888:8888/tcp` to access the HTTP web proxy (and put your LAN in `EXTRA_SUBNETS` environment variable)
|
||||
- Use `-p 8388:8388/tcp -p 8388:8388/udp` to access the SOCKS5 proxy (and put your LAN in `EXTRA_SUBNETS` environment variable)
|
||||
- Pass additional arguments to *openvpn* using Docker's command function (commands after the image name)
|
||||
|
||||
## Testing
|
||||
|
||||
Check the PIA IP address matches your expectations
|
||||
|
||||
```sh
|
||||
docker run --rm --network=container:pia alpine:3.10 wget -qO- https://ipinfo.io
|
||||
```
|
||||
|
||||
## Environment variables
|
||||
|
||||
| Environment variable | Default | Description |
|
||||
| --- | --- | --- |
|
||||
| `REGION` | `CA Montreal` | One of the [PIA regions](https://www.privateinternetaccess.com/pages/network/) |
|
||||
| `PROTOCOL` | `udp` | `tcp` or `udp` |
|
||||
| `ENCRYPTION` | `strong` | `normal` or `strong` |
|
||||
| `USER` | | Your PIA username |
|
||||
| `PASSWORD` | | Your PIA password |
|
||||
| `NONROOT` | `no` | Run OpenVPN without root, `yes` or `no` |
|
||||
| `DOT` | `on` | `on` or `off`, to activate DNS over TLS to 1.1.1.1 |
|
||||
| `BLOCK_MALICIOUS` | `off` | `on` or `off`, blocks malicious hostnames and IPs |
|
||||
| `BLOCK_NSA` | `off` | `on` or `off`, blocks NSA hostnames |
|
||||
| `UNBLOCK` | | comma separated string (i.e. `web.com,web2.ca`) to unblock hostnames |
|
||||
| `EXTRA_SUBNETS` | | comma separated subnets allowed in the container firewall (i.e. `192.168.1.0/24,192.168.10.121,10.0.0.5/28`) |
|
||||
| `PORT_FORWARDING` | `off` | Set to `on` to forward a port on PIA server |
|
||||
| `PORT_FORWARDING_STATUS_FILE` | `/forwarded_port` | File path to store the forwarded port number |
|
||||
| `TINYPROXY` | `on` | `on` or `off`, to enable the internal HTTP proxy tinyproxy |
|
||||
| `TINYPROXY_LOG` | `Critical` | `Info`, `Warning`, `Error` or `Critical` |
|
||||
| `TINYPROXY_PORT` | `8888` | `1024` to `65535` internal port for HTTP proxy |
|
||||
| `TINYPROXY_USER` | | Username to use to connect to the HTTP proxy |
|
||||
| `TINYPROXY_PASSWORD` | | Passsword to use to connect to the HTTP proxy |
|
||||
| `SHADOWSOCKS` | `on` | `on` or `off`, to enable the internal SOCKS5 proxy Shadowsocks |
|
||||
| `SHADOWSOCKS_LOG` | `on` | `on` or `off` to enable logging for Shadowsocks |
|
||||
| `SHADOWSOCKS_PORT` | `8388` | `1024` to `65535` internal port for SOCKS5 proxy |
|
||||
| `SHADOWSOCKS_PASSWORD` | | Passsword to use to connect to the SOCKS5 proxy |
|
||||
| `TZ` | | Specify a timezone to use e.g. `Europe/London` |
|
||||
|
||||
## Connect to it
|
||||
|
||||
There are various ways to achieve this, depending on your use case.
|
||||
|
||||
- <details><summary>Connect containers in the same docker-compose.yml as PIA</summary><p>
|
||||
|
||||
Add `network_mode: "service:pia"` to your *docker-compose.yml* (no need for `depends_on`)
|
||||
|
||||
</p></details>
|
||||
- <details><summary>Connect other containers to PIA</summary><p>
|
||||
|
||||
Add `--network=container:pia` when launching the container, provided PIA is already running
|
||||
|
||||
</p></details>
|
||||
- <details><summary>Connect containers from another docker-compose.yml</summary><p>
|
||||
|
||||
Add `network_mode: "container:pia"` to your *docker-compose.yml*, provided PIA is already running
|
||||
|
||||
</p></details>
|
||||
- <details><summary>Connect LAN devices through the built-in HTTP proxy *Tinyproxy* (i.e. with Chrome, Kodi, etc.)</summary><p>
|
||||
|
||||
1. Setup a HTTP proxy client, such as [SwitchyOmega for Chrome](https://chrome.google.com/webstore/detail/proxy-switchyomega/padekgcemlokbadohgkifijomclgjgif?hl=en)
|
||||
1. Ensure the PIA container is launched with:
|
||||
- port `8888` published `-p 8888:8888/tcp`
|
||||
- your LAN subnet, i.e. `192.168.1.0/24`, set as `-e EXTRA_SUBNETS=192.168.1.0/24`
|
||||
1. With your HTTP proxy client, connect to the Docker host (i.e. `192.168.1.10`) on port `8888`. You need to enter your credentials if you set them with `TINYPROXY_USER` and `TINYPROXY_PASSWORD`.
|
||||
1. If you set `TINYPROXY_LOG` to `Info`, more information will be logged in the Docker logs, merged with the OpenVPN logs.
|
||||
`TINYPROXY_LOG` defaults to `Critical` to avoid logging everything, for privacy purposes.
|
||||
|
||||
</p></details>
|
||||
- <details><summary>Connect LAN devices through the built-in SOCKS5 proxy *Shadowsocks* (per app, system wide, etc.)</summary><p>
|
||||
|
||||
1. Setup a SOCKS5 proxy client, there is a list of [ShadowSocks clients for **all platforms**](https://shadowsocks.org/en/download/clients.html)
|
||||
- **note** some clients do not tunnel UDP so your DNS queries will be done locally and not through PIA and its built in DNS over TLS
|
||||
- Clients that support such UDP tunneling are, as far as I know:
|
||||
- iOS: Potatso Lite
|
||||
- OSX: ShadowsocksX
|
||||
- Android: Shadowsocks by Max Lv
|
||||
1. Ensure the PIA container is launched with:
|
||||
- port `8388` published `-p 8388:8388/tcp -p 8388:8388/udp`
|
||||
- your LAN subnet, i.e. `192.168.1.0/24`, set as `-e EXTRA_SUBNETS=192.168.1.0/24`
|
||||
1. With your SOCKS5 proxy client
|
||||
- Enter the Docker host (i.e. `192.168.1.10`) as the server IP
|
||||
- Enter port TCP (and UDP, if available) `8388` as the server port
|
||||
- Use the password you have set with `SHADOWSOCKS_PASSWORD`
|
||||
- Choose the encryption method/algorithm `chacha20-ietf-poly1305`
|
||||
1. If you set `SHADOWSOCKS_LOG` to `on`, more information will be logged in the Docker logs, merged with the OpenVPN logs.
|
||||
|
||||
</p></details>
|
||||
- <details><summary>Access ports of containers connected to PIA</summary><p>
|
||||
|
||||
In example, to access port `8000` of container `xyz` and `9000` of container `abc` connected to PIA,
|
||||
publish ports `8000` and `9000` for the PIA container and access them as you would with any other container
|
||||
|
||||
</p></details>
|
||||
- <details><summary>Access ports of containers connected to PIA, all in the same docker-compose.yml</summary><p>
|
||||
|
||||
In example, to access port `8000` of container `xyz` and `9000` of container `abc` connected to PIA, publish port `8000` and `9000` for the PIA container.
|
||||
The docker-compose.yml file would look like:
|
||||
|
||||
```yml
|
||||
version: '3.7'
|
||||
services:
|
||||
pia:
|
||||
image: qmcgaw/private-internet-access
|
||||
container_name: pia
|
||||
init: true
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
devices:
|
||||
- /dev/net/tun
|
||||
environment:
|
||||
- USER=js89ds7
|
||||
- PASSWORD=8fd9s239G
|
||||
ports:
|
||||
- 8000:8000/tcp
|
||||
- 9000:9000/tcp
|
||||
abc:
|
||||
image: abc
|
||||
container_name: abc
|
||||
network_mode: "service:pia"
|
||||
xyz:
|
||||
image: xyz
|
||||
container_name: xyz
|
||||
network_mode: "service:pia"
|
||||
```
|
||||
|
||||
</p></details>
|
||||
|
||||
## Port forwarding
|
||||
|
||||
By setting `PORT_FORWARDING` environment variable to `on`, the forwarded port will be read and written to the file specified in `PORT_FORWARDING_STATUS_FILE` (by default, this is set to `/forwarded_port`). If the location for this file does not exist, it will be created automatically.
|
||||
|
||||
You can mount this file as a volume to read it from other containers.
|
||||
|
||||
Note that not all regions support port forwarding.
|
||||
|
||||
## For the paranoids
|
||||
|
||||
- You can review the code which essential consists in the [Dockerfile](https://github.com/qdm12/private-internet-access-docker/blob/master/Dockerfile) and [entrypoint.sh](https://github.com/qdm12/private-internet-access-docker/blob/master/entrypoint.sh)
|
||||
- Build the images yourself:
|
||||
|
||||
```bash
|
||||
docker build -t qmcgaw/private-internet-access https://github.com/qdm12/private-internet-access-docker.git
|
||||
```
|
||||
|
||||
- The download and unziping of PIA openvpn files is done at build for the ones not able to download the zip files
|
||||
- Checksums for PIA openvpn zip files are not used as these files change often (but HTTPS is used)
|
||||
- Use `-e ENCRYPTION=strong -e BLOCK_MALICIOUS=on`
|
||||
- You can test DNSSEC using [internet.nl/connection](https://www.internet.nl/connection/)
|
||||
- Check DNS leak tests with [https://www.dnsleaktest.com](https://www.dnsleaktest.com)
|
||||
- DNS Leaks tests might not work because of [this](https://github.com/qdm12/cloudflare-dns-server#verify-dns-connection) (*TLDR*: DNS server is a local caching intermediary)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Password problems `AUTH: Received control message: AUTH_FAILED`
|
||||
- Your password may contain a special character such as `$`.
|
||||
You need to escape it with `\` in your run command or docker-compose.yml.
|
||||
For example you would set `-e PASSWORD=mypa\$\$word`.
|
||||
- Fallback to a previous version
|
||||
1. Clone the repository on your machine
|
||||
|
||||
```sh
|
||||
git clone https://github.com/qdm12/private-internet-access-docker.git pia
|
||||
cd pia
|
||||
```
|
||||
|
||||
1. Look up which commit you want to go back to [here](https://github.com/qdm12/private-internet-access-docker/commits/master), i.e. `942cc7d4d10545b6f5f89c907b7dd1dbc39368e0`
|
||||
1. Revert to this commit locally
|
||||
|
||||
```sh
|
||||
git reset --hard 942cc7d4d10545b6f5f89c907b7dd1dbc39368e0
|
||||
```
|
||||
|
||||
1. Build the Docker image
|
||||
|
||||
```sh
|
||||
docker build -t qmcgaw/private-internet-access .
|
||||
```
|
||||
|
||||
## TODOs
|
||||
|
||||
- Shadowsocks
|
||||
- Get logs from file and merge with docker stdout
|
||||
- Mix Logs of Unbound
|
||||
- Maybe use `--inactive 3600 --ping 10 --ping-exit 60` as default behavior
|
||||
- Try without tun
|
||||
|
||||
## License
|
||||
|
||||
This repository is under an [MIT license](https://github.com/qdm12/private-internet-access-docker/master/license)
|
||||
# Gluetun VPN client
|
||||
|
||||
*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access,
|
||||
Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN and Privado VPN servers, using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
||||
|
||||
**ANNOUNCEMENT**: *New Docker image name `qmcgaw/gluetun`*
|
||||
|
||||
<img height="250" src="https://raw.githubusercontent.com/qdm12/gluetun/master/title.svg?sanitize=true">
|
||||
|
||||
[](https://hub.docker.com/r/qmcgaw/gluetun/tags?page=1&ordering=last_updated)
|
||||
[](https://hub.docker.com/r/qmcgaw/gluetun/tags)
|
||||
|
||||
[](https://hub.docker.com/r/qmcgaw/private-internet-access)
|
||||
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||

|
||||
[](https://github.com/qdm12/gluetun/commits)
|
||||
|
||||
[](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)
|
||||
|
||||
## Features
|
||||
|
||||
- Based on Alpine 3.12 for a small Docker image of 52MB
|
||||
- Supports **Private Internet Access**, **Mullvad**, **Windscribe**, **Surfshark**, **Cyberghost**, **Vyprvpn**, **NordVPN**, **PureVPN** and **Privado** servers
|
||||
- Supports Openvpn only for now
|
||||
- DNS over TLS baked in with service provider(s) of your choice
|
||||
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
|
||||
- Choose the vpn network protocol, `udp` or `tcp`
|
||||
- Built in firewall kill switch to allow traffic only with needed the VPN servers and LAN devices
|
||||
- Built in Shadowsocks proxy (protocol based on SOCKS5 with an encryption layer, tunnels TCP+UDP)
|
||||
- Built in HTTP proxy (tunnels HTTP and HTTPS through TCP)
|
||||
- [Connect other containers to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
|
||||
- [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
|
||||
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even s390x as well as ppc64le 🎆
|
||||
- VPN server side port forwarding for Private Internet Access and Vyprvpn
|
||||
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
|
||||
- Subprograms all drop root privileges once launched
|
||||
- Subprograms output streams are all merged together
|
||||
- Can work as a Kubernetes sidecar container, thanks @rorph
|
||||
|
||||
## Setup
|
||||
|
||||
1. 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 Wiki page](https://github.com/qdm12/gluetun/wiki/Synology-setup)
|
||||
1. Launch the container with:
|
||||
|
||||
```bash
|
||||
docker run -d --name gluetun --cap-add=NET_ADMIN \
|
||||
-e VPNSP="private internet access" -e REGION="CA Montreal" \
|
||||
-e OPENVPN_USER=js89ds7 -e OPENVPN_PASSWORD=8fd9s239G \
|
||||
-v /yourpath:/gluetun \
|
||||
qmcgaw/gluetun
|
||||
```
|
||||
|
||||
or use [docker-compose.yml](https://github.com/qdm12/gluetun/blob/master/docker-compose.yml) with:
|
||||
|
||||
```bash
|
||||
echo "your openvpn username" > openvpn_user
|
||||
echo "your openvpn password" > openvpn_password
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
You should probably check the many [environment variables](https://github.com/qdm12/gluetun/wiki/Environment-variables) available to adapt the container to your needs.
|
||||
|
||||
## Further setup
|
||||
|
||||
The following points are all optional but should give you insights on all the possibilities with this container.
|
||||
|
||||
- Use [Docker secrets](https://github.com/qdm12/gluetun/wiki/Docker-secrets) to read your credentials instead of environment variables
|
||||
- [Test your setup](https://github.com/qdm12/gluetun/wiki/Test-your-setup)
|
||||
- [How to connect other containers and devices to Gluetun](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
|
||||
- [VPN server side port forwarding](https://github.com/qdm12/gluetun/wiki/Port-forwarding)
|
||||
- [HTTP control server](https://github.com/qdm12/gluetun/wiki/HTTP-Control-server) to automate things, restart Openvpn etc.
|
||||
- Update the image with `docker pull qmcgaw/gluetun:latest`. See this [Wiki document](https://github.com/qdm12/gluetun/wiki/Docker-image-tags) for Docker tags available.
|
||||
|
||||
## License
|
||||
|
||||
[](https://github.com/qdm12/gluetun/master/LICENSE)
|
||||
|
||||
## Metadata
|
||||
|
||||
[](https://github.com/qdm12/gluetun/commits)
|
||||
[](https://github.com/qdm12/gluetun/pulls?q=is%3Apr+is%3Aclosed)
|
||||
|
||||
[](https://github.com/qdm12/gluetun/issues)
|
||||
[](https://github.com/qdm12/gluetun/issues?q=is%3Aissue+is%3Aclosed)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

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

|
||||

|
||||
|
||||
447
cmd/gluetun/main.go
Normal file
447
cmd/gluetun/main.go
Normal file
@@ -0,0 +1,447 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
nativeos "os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/dns/pkg/unbound"
|
||||
"github.com/qdm12/gluetun/internal/alpine"
|
||||
"github.com/qdm12/gluetun/internal/cli"
|
||||
"github.com/qdm12/gluetun/internal/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/params"
|
||||
"github.com/qdm12/gluetun/internal/publicip"
|
||||
"github.com/qdm12/gluetun/internal/routing"
|
||||
"github.com/qdm12/gluetun/internal/server"
|
||||
"github.com/qdm12/gluetun/internal/settings"
|
||||
"github.com/qdm12/gluetun/internal/shadowsocks"
|
||||
"github.com/qdm12/gluetun/internal/storage"
|
||||
"github.com/qdm12/gluetun/internal/unix"
|
||||
"github.com/qdm12/gluetun/internal/updater"
|
||||
versionpkg "github.com/qdm12/gluetun/internal/version"
|
||||
"github.com/qdm12/golibs/command"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
"github.com/qdm12/golibs/os"
|
||||
"github.com/qdm12/golibs/os/user"
|
||||
"github.com/qdm12/updated/pkg/dnscrypto"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
version = "unknown"
|
||||
commit = "unknown"
|
||||
buildDate = "an unknown date"
|
||||
)
|
||||
|
||||
func main() {
|
||||
buildInfo := models.BuildInformation{
|
||||
Version: version,
|
||||
Commit: commit,
|
||||
BuildDate: buildDate,
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
logger, err := logging.NewLogger(logging.ConsoleEncoding, logging.InfoLevel)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
nativeos.Exit(1)
|
||||
}
|
||||
|
||||
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)
|
||||
}()
|
||||
|
||||
signalsCh := make(chan nativeos.Signal, 1)
|
||||
signal.Notify(signalsCh,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM,
|
||||
nativeos.Interrupt,
|
||||
)
|
||||
|
||||
select {
|
||||
case signal := <-signalsCh:
|
||||
logger.Warn("Caught OS signal %s, shutting down", signal)
|
||||
case err := <-errorCh:
|
||||
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)
|
||||
}
|
||||
|
||||
//nolint:gocognit,gocyclo
|
||||
func _main(background context.Context, buildInfo models.BuildInformation,
|
||||
args []string, logger logging.Logger, os os.OS, osUser user.OSUser, unix unix.Unix,
|
||||
cli cli.CLI) error {
|
||||
if len(args) > 1 { // cli operation
|
||||
switch args[1] {
|
||||
case "healthcheck":
|
||||
return cli.HealthCheck(background)
|
||||
case "clientkey":
|
||||
return cli.ClientKey(args[2:], os.OpenFile)
|
||||
case "openvpnconfig":
|
||||
return cli.OpenvpnConfig(os)
|
||||
case "update":
|
||||
return cli.Update(args[2:], os)
|
||||
default:
|
||||
return fmt.Errorf("command %q is unknown", args[1])
|
||||
}
|
||||
}
|
||||
ctx, cancel := context.WithCancel(background)
|
||||
defer cancel()
|
||||
|
||||
const clientTimeout = 15 * time.Second
|
||||
httpClient := &http.Client{Timeout: clientTimeout}
|
||||
// Create configurators
|
||||
alpineConf := alpine.NewConfigurator(os.OpenFile, osUser)
|
||||
ovpnConf := openvpn.NewConfigurator(logger, os, unix)
|
||||
dnsCrypto := dnscrypto.New(httpClient, "", "")
|
||||
const cacertsPath = "/etc/ssl/certs/ca-certificates.crt"
|
||||
dnsConf := unbound.NewConfigurator(logger, os.OpenFile, dnsCrypto,
|
||||
"/etc/unbound", "/usr/sbin/unbound", cacertsPath)
|
||||
routingConf := routing.NewRouting(logger)
|
||||
firewallConf := firewall.NewConfigurator(logger, routingConf, os.OpenFile)
|
||||
streamMerger := command.NewStreamMerger()
|
||||
|
||||
paramsReader := params.NewReader(logger, os)
|
||||
fmt.Println(gluetunLogging.Splash(buildInfo))
|
||||
|
||||
printVersions(ctx, logger, map[string]func(ctx context.Context) (string, error){
|
||||
"OpenVPN": ovpnConf.Version,
|
||||
"Unbound": dnsConf.Version,
|
||||
"IPtables": firewallConf.Version,
|
||||
})
|
||||
|
||||
allSettings, warnings, err := settings.GetAllSettings(paramsReader)
|
||||
for _, warning := range warnings {
|
||||
logger.Warn(warning)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Info(allSettings.String())
|
||||
|
||||
if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll("/gluetun", 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO run this in a loop or in openvpn to reload from file without restarting
|
||||
storage := storage.New(logger, os, constants.ServersData)
|
||||
allServers, err := storage.SyncServers(constants.GetAllServers())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Should never change
|
||||
puid, pgid := allSettings.System.PUID, allSettings.System.PGID
|
||||
|
||||
const defaultUsername = "nonrootuser"
|
||||
nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if nonRootUsername != defaultUsername {
|
||||
logger.Info("using existing username %s corresponding to user id %d", nonRootUsername, puid)
|
||||
}
|
||||
|
||||
if err := os.Chown("/etc/unbound", puid, pgid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if allSettings.Firewall.Debug {
|
||||
firewallConf.SetDebug()
|
||||
routingConf.SetDebug()
|
||||
}
|
||||
|
||||
defaultInterface, defaultGateway, err := routingConf.DefaultRoute()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
localSubnet, err := routingConf.LocalSubnet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defaultIP, err := routingConf.DefaultIP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
firewallConf.SetNetworkInformation(defaultInterface, defaultGateway, localSubnet, defaultIP)
|
||||
|
||||
if err := routingConf.Setup(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
routingConf.SetVerbose(false)
|
||||
if err := routingConf.TearDown(); err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := firewallConf.SetOutboundSubnets(ctx, allSettings.Firewall.OutboundSubnets); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := routingConf.SetOutboundRoutes(allSettings.Firewall.OutboundSubnets); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ovpnConf.CheckTUN(); err != nil {
|
||||
logger.Warn(err)
|
||||
err = ovpnConf.CreateTUN()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
tunnelReadyCh := make(chan struct{})
|
||||
dnsReadyCh := make(chan struct{})
|
||||
defer close(tunnelReadyCh)
|
||||
defer close(dnsReadyCh)
|
||||
|
||||
if allSettings.Firewall.Enabled {
|
||||
err := firewallConf.SetEnabled(ctx, true) // disabled by default
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, vpnPort := range allSettings.Firewall.VPNInputPorts {
|
||||
err = firewallConf.SetAllowedPort(ctx, vpnPort, string(constants.TUN))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, port := range allSettings.Firewall.InputPorts {
|
||||
err = firewallConf.SetAllowedPort(ctx, port, defaultInterface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} // TODO move inside firewall?
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
wg.Add(1)
|
||||
go collectStreamLines(ctx, wg, streamMerger, logger, tunnelReadyCh)
|
||||
|
||||
openvpnLooper := openvpn.NewLooper(allSettings.OpenVPN, nonRootUsername, puid, pgid, allServers,
|
||||
ovpnConf, firewallConf, routingConf, logger, httpClient, os.OpenFile, streamMerger, cancel)
|
||||
wg.Add(1)
|
||||
// wait for restartOpenvpn
|
||||
go openvpnLooper.Run(ctx, wg)
|
||||
|
||||
updaterLooper := updater.NewLooper(allSettings.Updater,
|
||||
allServers, storage, openvpnLooper.SetServers, httpClient, logger)
|
||||
wg.Add(1)
|
||||
// wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker
|
||||
go updaterLooper.Run(ctx, wg)
|
||||
|
||||
unboundLooper := dns.NewLooper(dnsConf, allSettings.DNS, httpClient,
|
||||
logger, streamMerger, nonRootUsername, puid, pgid)
|
||||
wg.Add(1)
|
||||
// wait for unboundLooper.Restart or its ticker launched with RunRestartTicker
|
||||
go unboundLooper.Run(ctx, wg, dnsReadyCh)
|
||||
|
||||
publicIPLooper := publicip.NewLooper(
|
||||
httpClient, logger, allSettings.PublicIP, puid, pgid, os)
|
||||
wg.Add(1)
|
||||
go publicIPLooper.Run(ctx, wg)
|
||||
wg.Add(1)
|
||||
go publicIPLooper.RunRestartTicker(ctx, wg)
|
||||
|
||||
httpProxyLooper := httpproxy.NewLooper(logger, allSettings.HTTPProxy)
|
||||
wg.Add(1)
|
||||
go httpProxyLooper.Run(ctx, wg)
|
||||
|
||||
shadowsocksLooper := shadowsocks.NewLooper(allSettings.ShadowSocks, logger)
|
||||
wg.Add(1)
|
||||
go shadowsocksLooper.Run(ctx, wg)
|
||||
|
||||
wg.Add(1)
|
||||
go routeReadyEvents(ctx, wg, buildInfo, tunnelReadyCh, dnsReadyCh,
|
||||
unboundLooper, updaterLooper, publicIPLooper, routingConf, logger, httpClient,
|
||||
allSettings.VersionInformation, allSettings.OpenVPN.Provider.PortForwarding.Enabled, openvpnLooper.PortForward,
|
||||
)
|
||||
controlServerAddress := fmt.Sprintf("0.0.0.0:%d", allSettings.ControlServer.Port)
|
||||
controlServerLogging := allSettings.ControlServer.Log
|
||||
httpServer := server.New(controlServerAddress, controlServerLogging,
|
||||
logger, buildInfo, openvpnLooper, unboundLooper, updaterLooper, publicIPLooper)
|
||||
wg.Add(1)
|
||||
go httpServer.Run(ctx, wg)
|
||||
|
||||
healthcheckServer := healthcheck.NewServer(
|
||||
constants.HealthcheckAddress, logger)
|
||||
wg.Add(1)
|
||||
go healthcheckServer.Run(ctx, wg)
|
||||
|
||||
// Start openvpn for the first time in a blocking call
|
||||
// until openvpn is launched
|
||||
_, _ = openvpnLooper.SetStatus(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(string(allSettings.OpenVPN.Provider.PortForwarding.Filepath)); err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func printVersions(ctx context.Context, logger logging.Logger,
|
||||
versionFunctions map[string]func(ctx context.Context) (string, error)) {
|
||||
const timeout = 5 * time.Second
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
for name, f := range versionFunctions {
|
||||
version, err := f(ctx)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
} else {
|
||||
logger.Info("%s version: %s", name, version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func collectStreamLines(ctx context.Context, wg *sync.WaitGroup,
|
||||
streamMerger command.StreamMerger,
|
||||
logger logging.Logger, tunnelReadyCh chan<- struct{}) {
|
||||
defer wg.Done()
|
||||
// Blocking line merging paramsReader for openvpn and unbound
|
||||
logger.Info("Launching standard output merger")
|
||||
streamMerger.CollectLines(ctx, func(line string) {
|
||||
line, level := gluetunLogging.PostProcessLine(line)
|
||||
if line == "" {
|
||||
return
|
||||
}
|
||||
switch level {
|
||||
case logging.DebugLevel:
|
||||
logger.Debug(line)
|
||||
case logging.InfoLevel:
|
||||
logger.Info(line)
|
||||
case logging.WarnLevel:
|
||||
logger.Warn(line)
|
||||
case logging.ErrorLevel:
|
||||
logger.Error(line)
|
||||
}
|
||||
switch {
|
||||
case strings.Contains(line, "Initialization Sequence Completed"):
|
||||
tunnelReadyCh <- struct{}{}
|
||||
case strings.Contains(line, "TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity)"): //nolint:lll
|
||||
logger.Warn("This means that either...")
|
||||
logger.Warn("1. The VPN server IP address you are trying to connect to is no longer valid, see https://github.com/qdm12/gluetun/wiki/Update-servers-information") //nolint:lll
|
||||
logger.Warn("2. The VPN server crashed, try changing region")
|
||||
logger.Warn("3. Your Internet connection is not working, ensure it works")
|
||||
logger.Warn("Feel free to create an issue at https://github.com/qdm12/gluetun/issues/new/choose")
|
||||
}
|
||||
}, func(err error) {
|
||||
logger.Warn(err)
|
||||
})
|
||||
}
|
||||
|
||||
func routeReadyEvents(ctx context.Context, wg *sync.WaitGroup, buildInfo models.BuildInformation,
|
||||
tunnelReadyCh, dnsReadyCh <-chan struct{},
|
||||
unboundLooper dns.Looper, updaterLooper updater.Looper, publicIPLooper publicip.Looper,
|
||||
routing routing.Routing, logger logging.Logger, httpClient *http.Client,
|
||||
versionInformation, portForwardingEnabled bool, startPortForward func(vpnGateway net.IP)) {
|
||||
defer wg.Done()
|
||||
tickerWg := &sync.WaitGroup{}
|
||||
// for linters only
|
||||
var restartTickerContext context.Context
|
||||
var restartTickerCancel context.CancelFunc = func() {}
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
restartTickerCancel() // for linters only
|
||||
tickerWg.Wait()
|
||||
return
|
||||
case <-tunnelReadyCh: // blocks until openvpn is connected
|
||||
if unboundLooper.GetSettings().Enabled {
|
||||
_, _ = unboundLooper.SetStatus(constants.Running)
|
||||
}
|
||||
restartTickerCancel() // stop previous restart tickers
|
||||
tickerWg.Wait()
|
||||
restartTickerContext, restartTickerCancel = context.WithCancel(ctx)
|
||||
//nolint:gomnd
|
||||
tickerWg.Add(2)
|
||||
go unboundLooper.RunRestartTicker(restartTickerContext, tickerWg)
|
||||
go updaterLooper.RunRestartTicker(restartTickerContext, tickerWg)
|
||||
vpnDestination, err := routing.VPNDestinationIP()
|
||||
if err != nil {
|
||||
logger.Warn(err)
|
||||
} else {
|
||||
logger.Info("VPN routing IP address: %s", vpnDestination)
|
||||
}
|
||||
if portForwardingEnabled {
|
||||
// vpnGateway required only for PIA
|
||||
vpnGateway, err := routing.VPNLocalGatewayIP()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
logger.Info("VPN gateway IP address: %s", vpnGateway)
|
||||
startPortForward(vpnGateway)
|
||||
}
|
||||
case <-dnsReadyCh:
|
||||
// Runs the Public IP getter job once
|
||||
_, _ = publicIPLooper.SetStatus(constants.Running)
|
||||
if !versionInformation {
|
||||
break
|
||||
}
|
||||
message, err := versionpkg.GetMessage(ctx, buildInfo, httpClient)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
break
|
||||
}
|
||||
logger.Info(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
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 |
@@ -1,38 +1,31 @@
|
||||
version: "3.7"
|
||||
services:
|
||||
pia:
|
||||
build: https://github.com/qdm12/private-internet-access-docker.git
|
||||
image: qmcgaw/private-internet-access
|
||||
container_name: pia
|
||||
gluetun:
|
||||
image: qmcgaw/gluetun
|
||||
container_name: gluetun
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
devices:
|
||||
- /dev/net/tun
|
||||
network_mode: bridge
|
||||
init: true
|
||||
ports:
|
||||
- 8888:8888/tcp
|
||||
- 8388:8388/tcp
|
||||
- 8388:8388/udp
|
||||
- 8888:8888/tcp # HTTP proxy
|
||||
- 8388:8388/tcp # Shadowsocks
|
||||
- 8388:8388/udp # Shadowsocks
|
||||
- 8000:8000/tcp # Built-in HTTP control server
|
||||
# command:
|
||||
volumes:
|
||||
- /yourpath:/gluetun
|
||||
secrets:
|
||||
- openvpn_user
|
||||
- openvpn_password
|
||||
environment:
|
||||
- USER=js89ds7
|
||||
- PASSWORD=8fd9s239G
|
||||
- ENCRYPTION=strong
|
||||
- PROTOCOL=udp
|
||||
- REGION=CA Montreal
|
||||
- NONROOT=no
|
||||
- DOT=on
|
||||
- BLOCK_MALICIOUS=on
|
||||
- BLOCK_NSA=off
|
||||
- UNBLOCK=
|
||||
- FIREWALL=on
|
||||
- EXTRA_SUBNETS=
|
||||
- TINYPROXY=on
|
||||
- TINYPROXY_LOG=Critical
|
||||
- TINYPROXY_USER=
|
||||
- TINYPROXY_PASSWORD=
|
||||
- SHADOWSOCKS=on
|
||||
- SHADOWSOCKS_LOG=on
|
||||
- SHADOWSOCKS_PASSWORD=
|
||||
# More variables are available, see the readme table
|
||||
- VPNSP=private internet access
|
||||
# Timezone for accurate logs times
|
||||
- TZ=
|
||||
restart: always
|
||||
|
||||
secrets:
|
||||
openvpn_user:
|
||||
file: ./openvpn_user
|
||||
openvpn_password:
|
||||
file: ./openvpn_password
|
||||
|
||||
492
entrypoint.sh
492
entrypoint.sh
@@ -1,492 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
exitOnError(){
|
||||
# $1 must be set to $?
|
||||
status=$1
|
||||
message=$2
|
||||
[ "$message" != "" ] || message="Undefined error"
|
||||
if [ $status != 0 ]; then
|
||||
printf "[ERROR] $message, with status $status\n"
|
||||
exit $status
|
||||
fi
|
||||
}
|
||||
|
||||
exitIfUnset(){
|
||||
# $1 is the name of the variable to check - not the variable itself
|
||||
var="$(eval echo "\$$1")"
|
||||
if [ -z "$var" ]; then
|
||||
printf "[ERROR] Environment variable $1 is not set\n"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
exitIfNotIn(){
|
||||
# $1 is the name of the variable to check - not the variable itself
|
||||
# $2 is a string of comma separated possible values
|
||||
var="$(eval echo "\$$1")"
|
||||
for value in ${2//,/ }
|
||||
do
|
||||
if [ "$var" = "$value" ]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
printf "[ERROR] Environment variable $1 cannot be '$var' and must be one of the following: "
|
||||
for value in ${2//,/ }
|
||||
do
|
||||
printf "$value "
|
||||
done
|
||||
printf "\n"
|
||||
exit 1
|
||||
}
|
||||
|
||||
printf " =========================================\n"
|
||||
printf " =========================================\n"
|
||||
printf " ============= PIA CONTAINER =============\n"
|
||||
printf " =========================================\n"
|
||||
printf " =========================================\n"
|
||||
printf " == by github.com/qdm12 - Quentin McGaw ==\n\n"
|
||||
|
||||
printf "OpenVPN version: $(openvpn --version | head -n 1 | grep -oE "OpenVPN [0-9\.]* " | cut -d" " -f2)\n"
|
||||
printf "Unbound version: $(unbound -h | grep "Version" | cut -d" " -f2)\n"
|
||||
printf "Iptables version: $(iptables --version | cut -d" " -f2)\n"
|
||||
printf "TinyProxy version: $(tinyproxy -v | cut -d" " -f2)\n"
|
||||
printf "ShadowSocks version: $(ss-server --help | head -n 2 | tail -n 1 | cut -d" " -f 2)\n"
|
||||
|
||||
############################################
|
||||
# BACKWARD COMPATIBILITY PARAMETERS
|
||||
############################################
|
||||
[ "$PORT_FORWARDING" == "false" ] && PORT_FORWARDING=on
|
||||
[ "$PORT_FORWARDING" == "true" ] && PORT_FORWARDING=off
|
||||
if [ -z $TINYPROXY ] && [ ! -z $PROXY ]; then
|
||||
TINYPROXY=$PROXY
|
||||
fi
|
||||
if [ -z $TINYPROXY_LOG ] && [ ! -z $PROXY_LOG_LEVEL ]; then
|
||||
TINYPROXY_LOG=$PROXY_LOG_LEVEL
|
||||
fi
|
||||
if [ -z $TINYPROXY_PORT ] && [ ! -z $PROXY_PORT ]; then
|
||||
TINYPROXY_PORT=$PROXY_PORT
|
||||
fi
|
||||
if [ -z $TINYPROXY_USER ] && [ ! -z $PROXY_USER ]; then
|
||||
TINYPROXY_USER=$PROXY_USER
|
||||
fi
|
||||
if [ -z $TINYPROXY_PASSWORD ] && [ ! -z $PROXY_PASSWORD ]; then
|
||||
TINYPROXY_PASSWORD=$PROXY_PASSWORD
|
||||
fi
|
||||
|
||||
############################################
|
||||
# CHECK PARAMETERS
|
||||
############################################
|
||||
exitIfUnset USER
|
||||
exitIfUnset PASSWORD
|
||||
exitIfNotIn ENCRYPTION "normal,strong"
|
||||
exitIfNotIn PROTOCOL "tcp,udp"
|
||||
exitIfNotIn NONROOT "yes,no"
|
||||
cat "/openvpn/$PROTOCOL-$ENCRYPTION/$REGION.ovpn" &> /dev/null
|
||||
exitOnError $? "/openvpn/$PROTOCOL-$ENCRYPTION/$REGION.ovpn is not accessible"
|
||||
for EXTRA_SUBNET in ${EXTRA_SUBNETS//,/ }; do
|
||||
if [ $(echo "$EXTRA_SUBNET" | grep -Eo '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(/([0-2]?[0-9])|([3]?[0-1]))?$') = "" ]; then
|
||||
printf "Extra subnet $EXTRA_SUBNET is not a valid IPv4 subnet of the form 255.255.255.255/31 or 255.255.255.255\n"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
exitIfNotIn DOT "on,off"
|
||||
exitIfNotIn BLOCK_MALICIOUS "on,off"
|
||||
exitIfNotIn BLOCK_NSA "on,off"
|
||||
if [ "$DOT" == "off" ]; then
|
||||
if [ "$BLOCK_MALICIOUS" == "on" ]; then
|
||||
printf "DOT is off so BLOCK_MALICIOUS cannot be on\n"
|
||||
exit 1
|
||||
elif [ "$BLOCK_NSA" == "on" ]; then
|
||||
printf "DOT is off so BLOCK_NSA cannot be on\n"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
exitIfNotIn PORT_FORWARDING "on,off"
|
||||
if [ "$PORT_FORWARDING" == "on" ] && [ -z "$PORT_FORWARDING_STATUS_FILE" ]; then
|
||||
printf "PORT_FORWARDING is on but PORT_FORWARDING_STATUS_FILE is not set\n"
|
||||
exit 1
|
||||
fi
|
||||
exitIfNotIn TINYPROXY "on,off"
|
||||
if [ "$TINYPROXY" == "on" ]; then
|
||||
exitIfNotIn TINYPROXY_LOG "Info,Warning,Error,Critical"
|
||||
if [ -z $TINYPROXY_PORT ]; then
|
||||
TINYPROXY_PORT=8888
|
||||
fi
|
||||
if [ `echo $TINYPROXY_PORT | grep -E "^[0-9]+$"` != $TINYPROXY_PORT ]; then
|
||||
printf "TINYPROXY_PORT is not a valid number\n"
|
||||
exit 1
|
||||
elif [ $TINYPROXY_PORT -lt 1024 ]; then
|
||||
printf "TINYPROXY_PORT cannot be a privileged port under port 1024\n"
|
||||
exit 1
|
||||
elif [ $TINYPROXY_PORT -gt 65535 ]; then
|
||||
printf "TINYPROXY_PORT cannot be a port higher than the maximum port 65535\n"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -z "$TINYPROXY_USER" ] && [ -z "$TINYPROXY_PASSWORD" ]; then
|
||||
printf "TINYPROXY_USER is set but TINYPROXY_PASSWORD is not set\n"
|
||||
exit 1
|
||||
elif [ -z "$TINYPROXY_USER" ] && [ ! -z "$TINYPROXY_PASSWORD" ]; then
|
||||
printf "TINYPROXY_USER is not set but TINYPROXY_PASSWORD is set\n"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
exitIfNotIn SHADOWSOCKS "on,off"
|
||||
if [ "$SHADOWSOCKS" == "on" ]; then
|
||||
exitIfNotIn SHADOWSOCKS_LOG "on,off"
|
||||
if [ -z $SHADOWSOCKS_PORT ]; then
|
||||
SHADOWSOCKS_PORT=8388
|
||||
fi
|
||||
if [ `echo $SHADOWSOCKS_PORT | grep -E "^[0-9]+$"` != $SHADOWSOCKS_PORT ]; then
|
||||
printf "SHADOWSOCKS_PORT is not a valid number\n"
|
||||
exit 1
|
||||
elif [ $SHADOWSOCKS_PORT -lt 1024 ]; then
|
||||
printf "SHADOWSOCKS_PORT cannot be a privileged port under port 1024\n"
|
||||
exit 1
|
||||
elif [ $SHADOWSOCKS_PORT -gt 65535 ]; then
|
||||
printf "SHADOWSOCKS_PORT cannot be a port higher than the maximum port 65535\n"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z $SHADOWSOCKS_PASSWORD ]; then
|
||||
printf "SHADOWSOCKS_PASSWORD is not set\n"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
############################################
|
||||
# SHOW PARAMETERS
|
||||
############################################
|
||||
printf "\n"
|
||||
printf "OpenVPN parameters:\n"
|
||||
printf " * Region: $REGION\n"
|
||||
printf " * Encryption: $ENCRYPTION\n"
|
||||
printf " * Protocol: $PROTOCOL\n"
|
||||
printf " * Running without root: $NONROOT\n"
|
||||
printf "DNS over TLS:\n"
|
||||
printf " * Activated: $DOT\n"
|
||||
if [ "$DOT" = "on" ]; then
|
||||
printf " * Malicious hostnames DNS blocking: $BLOCK_MALICIOUS\n"
|
||||
printf " * NSA related DNS blocking: $BLOCK_NSA\n"
|
||||
printf " * Unblocked hostnames: $UNBLOCK\n"
|
||||
fi
|
||||
printf "Local network parameters:\n"
|
||||
printf " * Extra subnets: $EXTRA_SUBNETS\n"
|
||||
printf " * Tinyproxy HTTP proxy: $TINYPROXY\n"
|
||||
if [ "$TINYPROXY" == "on" ]; then
|
||||
printf " * Tinyproxy port: $TINYPROXY_PORT\n"
|
||||
tinyproxy_auth=yes
|
||||
if [ -z $TINYPROXY_USER ]; then
|
||||
tinyproxy_auth=no
|
||||
fi
|
||||
printf " * Tinyproxy has authentication: $tinyproxy_auth\n"
|
||||
unset -v tinyproxy_auth
|
||||
fi
|
||||
printf " * ShadowSocks SOCKS5 proxy: $SHADOWSOCKS\n"
|
||||
printf "PIA parameters:\n"
|
||||
printf " * Remote port forwarding: $PORT_FORWARDING\n"
|
||||
[ "$PORT_FORWARDING" == "on" ] && printf " * Remote port forwarding status file: $PORT_FORWARDING_STATUS_FILE\n"
|
||||
printf "\n"
|
||||
|
||||
#####################################################
|
||||
# Writes to protected file and remove USER, PASSWORD
|
||||
#####################################################
|
||||
if [ -f /auth.conf ]; then
|
||||
printf "[INFO] /auth.conf already exists\n"
|
||||
else
|
||||
printf "[INFO] Writing USER and PASSWORD to protected file /auth.conf..."
|
||||
echo "$USER" > /auth.conf
|
||||
exitOnError $?
|
||||
echo "$PASSWORD" >> /auth.conf
|
||||
exitOnError $?
|
||||
chown nonrootuser /auth.conf
|
||||
exitOnError $?
|
||||
chmod 400 /auth.conf
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
printf "[INFO] Clearing environment variables USER and PASSWORD..."
|
||||
unset -v USER
|
||||
unset -v PASSWORD
|
||||
printf "DONE\n"
|
||||
fi
|
||||
|
||||
############################################
|
||||
# CHECK FOR TUN DEVICE
|
||||
############################################
|
||||
if [ "$(cat /dev/net/tun 2>&1 /dev/null)" != "cat: read error: File descriptor in bad state" ]; then
|
||||
printf "[WARNING] TUN device is not available, creating it..."
|
||||
mkdir -p /dev/net
|
||||
mknod /dev/net/tun c 10 200
|
||||
exitOnError $?
|
||||
chmod 0666 /dev/net/tun
|
||||
printf "DONE\n"
|
||||
fi
|
||||
|
||||
############################################
|
||||
# BLOCKING MALICIOUS HOSTNAMES AND IPs WITH UNBOUND
|
||||
############################################
|
||||
if [ "$DOT" == "on" ]; then
|
||||
rm -f /etc/unbound/blocks-malicious.conf
|
||||
if [ "$BLOCK_MALICIOUS" = "on" ]; then
|
||||
tar -xjf /etc/unbound/blocks-malicious.bz2 -C /etc/unbound/
|
||||
printf "[INFO] $(cat /etc/unbound/blocks-malicious.conf | grep "local-zone" | wc -l ) malicious hostnames and $(cat /etc/unbound/blocks-malicious.conf | grep "private-address" | wc -l) malicious IP addresses blacklisted\n"
|
||||
else
|
||||
echo "" > /etc/unbound/blocks-malicious.conf
|
||||
fi
|
||||
if [ "$BLOCK_NSA" = "on" ]; then
|
||||
tar -xjf /etc/unbound/blocks-nsa.bz2 -C /etc/unbound/
|
||||
printf "[INFO] $(cat /etc/unbound/blocks-nsa.conf | grep "local-zone" | wc -l ) NSA hostnames blacklisted\n"
|
||||
cat /etc/unbound/blocks-nsa.conf >> /etc/unbound/blocks-malicious.conf
|
||||
rm /etc/unbound/blocks-nsa.conf
|
||||
sort -u -o /etc/unbound/blocks-malicious.conf /etc/unbound/blocks-malicious.conf
|
||||
fi
|
||||
for hostname in ${UNBLOCK//,/ }
|
||||
do
|
||||
printf "[INFO] Unblocking hostname $hostname\n"
|
||||
sed -i "/$hostname/d" /etc/unbound/blocks-malicious.conf
|
||||
done
|
||||
fi
|
||||
|
||||
############################################
|
||||
# SETTING DNS OVER TLS TO 1.1.1.1 / 1.0.0.1
|
||||
############################################
|
||||
if [ "$DOT" == "on" ]; then
|
||||
printf "[INFO] Launching Unbound to connect to Cloudflare DNS 1.1.1.1 over TLS..."
|
||||
unbound
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
printf "[INFO] Changing DNS to localhost..."
|
||||
printf "`sed '/^nameserver /d' /etc/resolv.conf`\nnameserver 127.0.0.1\n" > /etc/resolv.conf
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
fi
|
||||
|
||||
############################################
|
||||
# Reading chosen OpenVPN configuration
|
||||
############################################
|
||||
printf "[INFO] Reading OpenVPN configuration...\n"
|
||||
CONNECTIONSTRING=$(grep -i "/openvpn/$PROTOCOL-$ENCRYPTION/$REGION.ovpn" -e 'privateinternetaccess.com')
|
||||
exitOnError $?
|
||||
PORT=$(echo $CONNECTIONSTRING | cut -d' ' -f3)
|
||||
if [ "$PORT" = "" ]; then
|
||||
printf "[ERROR] Port not found in /openvpn/$PROTOCOL-$ENCRYPTION/$REGION.ovpn\n"
|
||||
exit 1
|
||||
fi
|
||||
PIADOMAIN=$(echo $CONNECTIONSTRING | cut -d' ' -f2)
|
||||
if [ "$PIADOMAIN" = "" ]; then
|
||||
printf "[ERROR] Domain not found in /openvpn/$PROTOCOL-$ENCRYPTION/$REGION.ovpn\n"
|
||||
exit 1
|
||||
fi
|
||||
printf " * Port: $PORT\n"
|
||||
printf " * Domain: $PIADOMAIN\n"
|
||||
printf "[INFO] Detecting IP addresses corresponding to $PIADOMAIN...\n"
|
||||
VPNIPS=$(nslookup $PIADOMAIN localhost | tail -n +3 | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}')
|
||||
exitOnError $?
|
||||
for ip in $VPNIPS; do
|
||||
printf " $ip\n";
|
||||
done
|
||||
|
||||
############################################
|
||||
# Writing target OpenVPN files
|
||||
############################################
|
||||
TARGET_PATH="/openvpn/target"
|
||||
printf "[INFO] Creating target OpenVPN files in $TARGET_PATH..."
|
||||
rm -rf $TARGET_PATH/*
|
||||
cd "/openvpn/$PROTOCOL-$ENCRYPTION"
|
||||
cp -f *.crt "$TARGET_PATH"
|
||||
exitOnError $? "Cannot copy crt file to $TARGET_PATH"
|
||||
cp -f *.pem "$TARGET_PATH"
|
||||
exitOnError $? "Cannot copy pem file to $TARGET_PATH"
|
||||
cp -f "$REGION.ovpn" "$TARGET_PATH/config.ovpn"
|
||||
exitOnError $? "Cannot copy $REGION.ovpn file to $TARGET_PATH"
|
||||
sed -i "/$CONNECTIONSTRING/d" "$TARGET_PATH/config.ovpn"
|
||||
exitOnError $? "Cannot delete '$CONNECTIONSTRING' from $TARGET_PATH/config.ovpn"
|
||||
sed -i '/resolv-retry/d' "$TARGET_PATH/config.ovpn"
|
||||
exitOnError $? "Cannot delete 'resolv-retry' from $TARGET_PATH/config.ovpn"
|
||||
for ip in $VPNIPS; do
|
||||
echo "remote $ip $PORT" >> "$TARGET_PATH/config.ovpn"
|
||||
exitOnError $? "Cannot add 'remote $ip $PORT' to $TARGET_PATH/config.ovpn"
|
||||
done
|
||||
# Uses the username/password from this file to get the token from PIA
|
||||
echo "auth-user-pass /auth.conf" >> "$TARGET_PATH/config.ovpn"
|
||||
exitOnError $? "Cannot add 'auth-user-pass /auth.conf' to $TARGET_PATH/config.ovpn"
|
||||
# Reconnects automatically on failure
|
||||
echo "auth-retry nointeract" >> "$TARGET_PATH/config.ovpn"
|
||||
exitOnError $? "Cannot add 'auth-retry nointeract' to $TARGET_PATH/config.ovpn"
|
||||
# Prevents auth_failed infinite loops - make it interact? Remove persist-tun? nobind?
|
||||
echo "pull-filter ignore \"auth-token\"" >> "$TARGET_PATH/config.ovpn"
|
||||
exitOnError $? "Cannot add 'pull-filter ignore \"auth-token\"' to $TARGET_PATH/config.ovpn"
|
||||
# Runs openvpn without root, as nonrootuser if specified
|
||||
if [ "$NONROOT" = "yes" ]; then
|
||||
echo "user nonrootuser" >> "$TARGET_PATH/config.ovpn"
|
||||
exitOnError $? "Cannot add 'user nonrootuser' to $TARGET_PATH/config.ovpn"
|
||||
fi
|
||||
echo "mute-replay-warnings" >> "$TARGET_PATH/config.ovpn"
|
||||
exitOnError $? "Cannot add 'mute-replay-warnings' to $TARGET_PATH/config.ovpn"
|
||||
# Note: TUN device re-opening will restart the container due to permissions
|
||||
printf "DONE\n"
|
||||
|
||||
############################################
|
||||
# NETWORKING
|
||||
############################################
|
||||
printf "[INFO] Finding network properties...\n"
|
||||
printf " * Detecting default gateway..."
|
||||
DEFAULT_GATEWAY=$(ip r | grep 'default via' | cut -d" " -f 3)
|
||||
exitOnError $?
|
||||
printf "$DEFAULT_GATEWAY\n"
|
||||
printf " * Detecting local interface..."
|
||||
INTERFACE=$(ip r | grep 'default via' | cut -d" " -f 5)
|
||||
exitOnError $?
|
||||
printf "$INTERFACE\n"
|
||||
printf " * Detecting local subnet..."
|
||||
SUBNET=$(ip r | grep -v 'default via' | grep $INTERFACE | tail -n 1 | cut -d" " -f 1)
|
||||
exitOnError $?
|
||||
printf "$SUBNET\n"
|
||||
for EXTRASUBNET in ${EXTRA_SUBNETS//,/ }
|
||||
do
|
||||
printf " * Adding $EXTRASUBNET as route via $INTERFACE..."
|
||||
ip route add $EXTRASUBNET via $DEFAULT_GATEWAY dev $INTERFACE
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
done
|
||||
printf " * Detecting target VPN interface..."
|
||||
VPN_DEVICE=$(cat $TARGET_PATH/config.ovpn | grep 'dev ' | cut -d" " -f 2)0
|
||||
exitOnError $?
|
||||
printf "$VPN_DEVICE\n"
|
||||
|
||||
|
||||
############################################
|
||||
# FIREWALL
|
||||
############################################
|
||||
printf "[INFO] Setting firewall\n"
|
||||
printf " * Blocking everyting\n"
|
||||
printf " * Deleting all iptables rules..."
|
||||
iptables --flush
|
||||
exitOnError $?
|
||||
iptables --delete-chain
|
||||
exitOnError $?
|
||||
iptables -t nat --flush
|
||||
exitOnError $?
|
||||
iptables -t nat --delete-chain
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
printf " * Block input traffic..."
|
||||
iptables -P INPUT DROP
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
printf " * Block output traffic..."
|
||||
iptables -F OUTPUT
|
||||
exitOnError $?
|
||||
iptables -P OUTPUT DROP
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
printf " * Block forward traffic..."
|
||||
iptables -P FORWARD DROP
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
|
||||
printf " * Creating general rules\n"
|
||||
printf " * Accept established and related input and output traffic..."
|
||||
iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
|
||||
exitOnError $?
|
||||
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
printf " * Accept local loopback input and output traffic..."
|
||||
iptables -A OUTPUT -o lo -j ACCEPT
|
||||
exitOnError $?
|
||||
iptables -A INPUT -i lo -j ACCEPT
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
|
||||
printf " * Creating VPN rules\n"
|
||||
for ip in $VPNIPS; do
|
||||
printf " * Accept output traffic to VPN server $ip through $INTERFACE, port $PROTOCOL $PORT..."
|
||||
iptables -A OUTPUT -d $ip -o $INTERFACE -p $PROTOCOL -m $PROTOCOL --dport $PORT -j ACCEPT
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
done
|
||||
printf " * Accept all output traffic through $VPN_DEVICE..."
|
||||
iptables -A OUTPUT -o $VPN_DEVICE -j ACCEPT
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
|
||||
printf " * Creating local subnet rules\n"
|
||||
printf " * Accept input and output traffic to and from $SUBNET..."
|
||||
iptables -A INPUT -s $SUBNET -d $SUBNET -j ACCEPT
|
||||
iptables -A OUTPUT -s $SUBNET -d $SUBNET -j ACCEPT
|
||||
printf "DONE\n"
|
||||
for EXTRASUBNET in ${EXTRA_SUBNETS//,/ }
|
||||
do
|
||||
printf " * Accept input traffic through $INTERFACE from $EXTRASUBNET to $SUBNET..."
|
||||
iptables -A INPUT -i $INTERFACE -s $EXTRASUBNET -d $SUBNET -j ACCEPT
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
# iptables -A OUTPUT -d $EXTRASUBNET -j ACCEPT
|
||||
# iptables -A OUTPUT -o $INTERFACE -s $SUBNET -d $EXTRASUBNET -j ACCEPT
|
||||
done
|
||||
|
||||
############################################
|
||||
# TINYPROXY LAUNCH
|
||||
############################################
|
||||
if [ "$TINYPROXY" == "on" ]; then
|
||||
printf "[INFO] Setting TinyProxy log level to $TINYPROXY_LOG..."
|
||||
sed -i "/LogLevel /c\LogLevel $TINYPROXY_LOG" /etc/tinyproxy/tinyproxy.conf
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
printf "[INFO] Setting TinyProxy port to $TINYPROXY_PORT..."
|
||||
sed -i "/Port /c\Port $TINYPROXY_PORT" /etc/tinyproxy/tinyproxy.conf
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
if [ ! -z "$TINYPROXY_USER" ]; then
|
||||
printf "[INFO] Setting TinyProxy credentials..."
|
||||
echo "BasicAuth $TINYPROXY_USER $TINYPROXY_PASSWORD" >> /etc/tinyproxy/tinyproxy.conf
|
||||
unset -v TINYPROXY_USER
|
||||
unset -v TINYPROXY_PASSWORD
|
||||
printf "DONE\n"
|
||||
fi
|
||||
tinyproxy -d &
|
||||
fi
|
||||
|
||||
############################################
|
||||
# SHADOWSOCKS
|
||||
############################################
|
||||
if [ "$SHADOWSOCKS" == "on" ]; then
|
||||
ARGS="-c /etc/shadowsocks.json"
|
||||
if [ "$SHADOWSOCKS_LOG" == " on" ]; then
|
||||
printf "[INFO] Setting ShadowSocks logging..."
|
||||
ARGS="$ARGS -v"
|
||||
printf "DONE\n"
|
||||
fi
|
||||
printf "[INFO] Setting ShadowSocks port to $SHADOWSOCKS_PORT..."
|
||||
jq ".port_password = {\"$SHADOWSOCKS_PORT\":\"\"}" /etc/shadowsocks.json > /tmp/shadowsocks.json && mv /tmp/shadowsocks.json /etc/shadowsocks.json
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
printf "[INFO] Setting ShadowSocks password..."
|
||||
jq ".port_password[\"$SHADOWSOCKS_PORT\"] = \"$SHADOWSOCKS_PASSWORD\"" /etc/shadowsocks.json > /tmp/shadowsocks.json && mv /tmp/shadowsocks.json /etc/shadowsocks.json
|
||||
exitOnError $?
|
||||
printf "DONE\n"
|
||||
ARGS="$ARGS -s `jq --raw-output '.server' /etc/shadowsocks.json`"
|
||||
unset -v SERVER
|
||||
ARGS="$ARGS -p $SHADOWSOCKS_PORT"
|
||||
ARGS="$ARGS -k $SHADOWSOCKS_PASSWORD"
|
||||
ss-server $ARGS &
|
||||
unset -v ARGS
|
||||
fi
|
||||
|
||||
############################################
|
||||
# READ FORWARDED PORT
|
||||
############################################
|
||||
|
||||
if [ "$PORT_FORWARDING" == "on" ]; then
|
||||
sleep 10 && /portforward.sh &
|
||||
fi
|
||||
|
||||
############################################
|
||||
# OPENVPN LAUNCH
|
||||
############################################
|
||||
printf "[INFO] Launching OpenVPN\n"
|
||||
cd "$TARGET_PATH"
|
||||
openvpn --config config.ovpn "$@"
|
||||
status=$?
|
||||
printf "\n =========================================\n"
|
||||
printf " OpenVPN exit with status $status\n"
|
||||
printf " =========================================\n\n"
|
||||
16
go.mod
Normal file
16
go.mod
Normal file
@@ -0,0 +1,16 @@
|
||||
module github.com/qdm12/gluetun
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.10.0
|
||||
github.com/golang/mock v1.4.4
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible
|
||||
github.com/qdm12/dns v1.4.0-rc4
|
||||
github.com/qdm12/golibs v0.0.0-20210110211000-0a3a4541ae09
|
||||
github.com/qdm12/ss-server v0.1.0
|
||||
github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930
|
||||
)
|
||||
192
go.sum
Normal file
192
go.sum
Normal file
@@ -0,0 +1,192 @@
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/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 h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||
github.com/fatih/color v1.10.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 h1:D4uzjWwKYQ5XnAvUbuvHW93esHg7F8N/OYeBBcJoTr0=
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
||||
github.com/go-openapi/analysis v0.17.0 h1:8JV+dzJJiK46XqGLqqLav8ZfEiJECp8jlOFhpiCdZ+0=
|
||||
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.17.2 h1:azEQ8Fnx0jmtFF2fxsnmd6I0x6rsweUF63qqSO1NmKk=
|
||||
github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonreference v0.17.0 h1:yJW3HCkTHg7NOA+gZ83IPHzUSnUzGXhGmsdiCcMexbA=
|
||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/loads v0.17.0 h1:H22nMs3GDQk4SwAaFQ+jLNw+0xoFeCueawhZlv8MBYs=
|
||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
|
||||
github.com/go-openapi/runtime v0.17.2 h1:/ZK67ikFhQAMFFH/aPu2MaGH7QjP4wHBvHYOVIzDAw0=
|
||||
github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q=
|
||||
github.com/go-openapi/spec v0.17.0 h1:XNvrt8FlSVP8T1WuhbAFF6QDhJc0zsoWzX4wXARhhpE=
|
||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/strfmt v0.17.0 h1:1isAxYf//QDTnVzbLAMrUK++0k1EjeLJU/gTOR0o3Mc=
|
||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/validate v0.17.0 h1:pqoViQz3YLOGIhAmD0N4Lt6pa/3Gnj3ymKqQwq8iS6U=
|
||||
github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gotify/go-api-client/v2 v2.0.4 h1:0w8skCr8aLBDKaQDg31LKKHUGF7rt7zdRpR+6cqIAlE=
|
||||
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
|
||||
github.com/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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible h1:np0woGKwx9LiHAQmwZx79Oc0rHpNw3o+3evou4BEPv4=
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
|
||||
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee h1:P6U24L02WMfj9ymZTxl7CxS73JC99x3ukk+DBkgQGQs=
|
||||
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee/go.mod h1:3uODdxMgOaPYeWU7RzZLxVtJHZ/x1f/iHkBZuKJDzuY=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/qdm12/dns v1.4.0-rc4 h1:pCcFMqismbktPQX7yrtdmJZq30Y05JBfWRTXY1ZLVFw=
|
||||
github.com/qdm12/dns v1.4.0-rc4/go.mod h1:JhUKBhuDRYBUQ2XwW/jbeWx/qS0sSJjIFjGTCFGP5I8=
|
||||
github.com/qdm12/golibs v0.0.0-20201227203847-2fd99ffdfdba/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
|
||||
github.com/qdm12/golibs v0.0.0-20210102020307-17bc97def973 h1:5YeJALmDjvg2wSi6XB8MpQQekbT/eBnwGahJrh01HHQ=
|
||||
github.com/qdm12/golibs v0.0.0-20210102020307-17bc97def973/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
|
||||
github.com/qdm12/golibs v0.0.0-20210110211000-0a3a4541ae09 h1:zP+ZRwV3GldgTWFgKNBQ2zoFA8mIczb+fvTvrX8LZRo=
|
||||
github.com/qdm12/golibs v0.0.0-20210110211000-0a3a4541ae09/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
|
||||
github.com/qdm12/ss-server v0.1.0 h1:WV9MkHCDEWRwe4WpnYFeR/zcZAxYoTbfntLDnw9AQ50=
|
||||
github.com/qdm12/ss-server v0.1.0/go.mod h1:ABVUkxubboL3vqBkOwDV9glX1/x7SnYrckBe5d+M/zw=
|
||||
github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a h1:gkyP+gMEeBgMgyRYGrVNcoy6cL1065IvXsyfB6xboIc=
|
||||
github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a/go.mod h1:bbJGxEYCnsA8WU4vBcXYU6mOoHyzdP458FIKP4mfLJM=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/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 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
||||
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
golang.org/x/crypto v0.0.0-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-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-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
out="$(ping -W 3 -c 1 -q -s 8 1.1.1.1)"
|
||||
[ $? != 0 ] || exit 0
|
||||
printf "$out"
|
||||
exit 1
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker build --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
|
||||
--build-arg VCS_REF=`git rev-parse --short HEAD` \
|
||||
-t $IMAGE_NAME .
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
curl -X POST https://hooks.microbadger.com/images/qmcgaw/${DOCKER_REPO}/tQFy7AxtSUNANPe6aoVChYdsI_I= || exit 0
|
||||
22
internal/alpine/alpine.go
Normal file
22
internal/alpine/alpine.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package alpine
|
||||
|
||||
import (
|
||||
"github.com/qdm12/golibs/os"
|
||||
"github.com/qdm12/golibs/os/user"
|
||||
)
|
||||
|
||||
type Configurator interface {
|
||||
CreateUser(username string, uid int) (createdUsername string, err error)
|
||||
}
|
||||
|
||||
type configurator struct {
|
||||
openFile os.OpenFileFunc
|
||||
osUser user.OSUser
|
||||
}
|
||||
|
||||
func NewConfigurator(openFile os.OpenFileFunc, osUser user.OSUser) Configurator {
|
||||
return &configurator{
|
||||
openFile: openFile,
|
||||
osUser: osUser,
|
||||
}
|
||||
}
|
||||
41
internal/alpine/users.go
Normal file
41
internal/alpine/users.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package alpine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
)
|
||||
|
||||
// CreateUser creates a user in Alpine with the given UID.
|
||||
func (c *configurator) CreateUser(username string, uid int) (createdUsername string, err error) {
|
||||
UIDStr := fmt.Sprintf("%d", uid)
|
||||
u, err := c.osUser.LookupID(UIDStr)
|
||||
_, unknownUID := err.(user.UnknownUserIdError)
|
||||
if err != nil && !unknownUID {
|
||||
return "", fmt.Errorf("cannot create user: %w", err)
|
||||
} else if u != nil {
|
||||
if u.Username == username {
|
||||
return "", nil
|
||||
}
|
||||
return u.Username, nil
|
||||
}
|
||||
u, err = c.osUser.Lookup(username)
|
||||
_, unknownUsername := err.(user.UnknownUserError)
|
||||
if err != nil && !unknownUsername {
|
||||
return "", fmt.Errorf("cannot create user: %w", err)
|
||||
} else if u != nil {
|
||||
return "", fmt.Errorf("cannot create user: user with name %s already exists for ID %s instead of %d",
|
||||
username, u.Uid, uid)
|
||||
}
|
||||
file, err := c.openFile("/etc/passwd", os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot create user: %w", 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()
|
||||
}
|
||||
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
|
||||
}
|
||||
20
internal/cli/cli.go
Normal file
20
internal/cli/cli.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdm12/golibs/os"
|
||||
)
|
||||
|
||||
type CLI interface {
|
||||
ClientKey(args []string, openFile os.OpenFileFunc) error
|
||||
HealthCheck(ctx context.Context) error
|
||||
OpenvpnConfig(os os.OS) error
|
||||
Update(args []string, os os.OS) error
|
||||
}
|
||||
|
||||
type cli struct{}
|
||||
|
||||
func New() CLI {
|
||||
return &cli{}
|
||||
}
|
||||
41
internal/cli/clientkey.go
Normal file
41
internal/cli/clientkey.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"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", string(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 := ioutil.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)
|
||||
}
|
||||
40
internal/cli/openvpnconfig.go
Normal file
40
internal/cli/openvpnconfig.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/params"
|
||||
"github.com/qdm12/gluetun/internal/provider"
|
||||
"github.com/qdm12/gluetun/internal/settings"
|
||||
"github.com/qdm12/gluetun/internal/storage"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
"github.com/qdm12/golibs/os"
|
||||
)
|
||||
|
||||
func (c *cli) OpenvpnConfig(os os.OS) error {
|
||||
logger, err := logging.NewLogger(logging.ConsoleEncoding, logging.InfoLevel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
paramsReader := params.NewReader(logger, os)
|
||||
allSettings, _, err := settings.GetAllSettings(paramsReader)
|
||||
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
|
||||
}
|
||||
64
internal/cli/update.go
Normal file
64
internal/cli/update.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/settings"
|
||||
"github.com/qdm12/gluetun/internal/storage"
|
||||
"github.com/qdm12/gluetun/internal/updater"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
"github.com/qdm12/golibs/os"
|
||||
)
|
||||
|
||||
func (c *cli) Update(args []string, os os.OS) error {
|
||||
options := settings.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", "1.1.1.1", "DNS resolver address to use")
|
||||
flagSet.BoolVar(&options.Cyberghost, "cyberghost", false, "Update Cyberghost 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.Purevpn, "purevpn", false, "Update Purevpn servers")
|
||||
flagSet.BoolVar(&options.Surfshark, "surfshark", false, "Update Surfshark 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
|
||||
}
|
||||
logger, err := logging.NewLogger(logging.ConsoleEncoding, logging.InfoLevel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !flushToFile && !options.Stdout {
|
||||
return fmt.Errorf("at least one of -file or -stdout must be specified")
|
||||
}
|
||||
ctx := context.Background()
|
||||
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("cannot update servers: %w", err)
|
||||
}
|
||||
updater := updater.New(options, httpClient, currentServers, logger)
|
||||
allServers, err := updater.UpdateServers(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if flushToFile {
|
||||
if err := storage.FlushToFile(allServers); err != nil {
|
||||
return fmt.Errorf("cannot update servers: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
5
internal/constants/addresses.go
Normal file
5
internal/constants/addresses.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
HealthcheckAddress = "127.0.0.1:9999"
|
||||
)
|
||||
11
internal/constants/colors.go
Normal file
11
internal/constants/colors.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package constants
|
||||
|
||||
import "github.com/fatih/color"
|
||||
|
||||
func ColorUnbound() *color.Color {
|
||||
return color.New(color.FgCyan)
|
||||
}
|
||||
|
||||
func ColorOpenvpn() *color.Color {
|
||||
return color.New(color.FgHiMagenta)
|
||||
}
|
||||
231
internal/constants/cyberghost.go
Normal file
231
internal/constants/cyberghost.go
Normal file
@@ -0,0 +1,231 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sort"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
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=="
|
||||
)
|
||||
|
||||
func CyberghostRegionChoices() (choices []string) {
|
||||
uniqueChoices := map[string]struct{}{}
|
||||
for _, server := range CyberghostServers() {
|
||||
uniqueChoices[server.Region] = struct{}{}
|
||||
}
|
||||
for choice := range uniqueChoices {
|
||||
choices = append(choices, choice)
|
||||
}
|
||||
sort.Slice(choices, func(i, j int) bool {
|
||||
return choices[i] < choices[j]
|
||||
})
|
||||
return choices
|
||||
}
|
||||
|
||||
func CyberghostGroupChoices() (choices []string) {
|
||||
uniqueChoices := map[string]struct{}{}
|
||||
for _, server := range CyberghostServers() {
|
||||
uniqueChoices[server.Group] = struct{}{}
|
||||
}
|
||||
for choice := range uniqueChoices {
|
||||
choices = append(choices, choice)
|
||||
}
|
||||
sort.Slice(choices, func(i, j int) bool {
|
||||
return choices[i] < choices[j]
|
||||
})
|
||||
return choices
|
||||
}
|
||||
|
||||
//nolint:lll
|
||||
func CyberghostServers() []models.CyberghostServer {
|
||||
return []models.CyberghostServer{
|
||||
{Region: "Albania", Group: "Premium TCP Europe", IPs: []net.IP{{31, 171, 152, 99}, {31, 171, 152, 102}, {31, 171, 152, 104}, {31, 171, 152, 106}, {31, 171, 152, 107}, {31, 171, 152, 108}, {31, 171, 152, 109}, {31, 171, 152, 133}, {31, 171, 152, 139}, {31, 171, 152, 140}}},
|
||||
{Region: "Albania", Group: "Premium UDP Europe", IPs: []net.IP{{31, 171, 152, 102}, {31, 171, 152, 103}, {31, 171, 152, 105}, {31, 171, 152, 106}, {31, 171, 152, 107}, {31, 171, 152, 109}, {31, 171, 152, 133}, {31, 171, 152, 136}, {31, 171, 152, 138}, {31, 171, 152, 139}}},
|
||||
{Region: "Algeria", Group: "Premium TCP Europe", IPs: []net.IP{{45, 133, 91, 9}, {45, 133, 91, 11}, {45, 133, 91, 12}, {45, 133, 91, 16}, {45, 133, 91, 18}, {45, 133, 91, 19}, {45, 133, 91, 21}, {45, 133, 91, 26}, {45, 133, 91, 27}, {45, 133, 91, 28}}},
|
||||
{Region: "Algeria", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 91, 7}, {45, 133, 91, 9}, {45, 133, 91, 12}, {45, 133, 91, 16}, {45, 133, 91, 19}, {45, 133, 91, 20}, {45, 133, 91, 25}, {45, 133, 91, 26}, {45, 133, 91, 27}, {45, 133, 91, 29}}},
|
||||
{Region: "Andorra", Group: "Premium TCP Europe", IPs: []net.IP{{45, 139, 49, 7}, {45, 139, 49, 11}, {45, 139, 49, 27}, {45, 139, 49, 151}, {45, 139, 49, 154}, {45, 139, 49, 158}, {45, 139, 49, 161}, {45, 139, 49, 162}, {45, 139, 49, 165}, {45, 139, 49, 166}}},
|
||||
{Region: "Andorra", Group: "Premium UDP Europe", IPs: []net.IP{{45, 139, 49, 7}, {45, 139, 49, 21}, {45, 139, 49, 28}, {45, 139, 49, 128}, {45, 139, 49, 136}, {45, 139, 49, 146}, {45, 139, 49, 156}, {45, 139, 49, 163}, {45, 139, 49, 165}, {45, 139, 49, 171}}},
|
||||
{Region: "Argentina", Group: "Premium UDP USA", IPs: []net.IP{{190, 106, 130, 19}, {190, 106, 130, 20}, {190, 106, 130, 26}, {190, 106, 130, 34}, {190, 106, 130, 36}, {190, 106, 130, 37}, {190, 106, 130, 38}, {190, 106, 130, 40}, {190, 106, 130, 42}, {190, 106, 130, 43}}},
|
||||
{Region: "Argentina", Group: "Premium TCP USA", IPs: []net.IP{{190, 106, 130, 17}, {190, 106, 130, 18}, {190, 106, 130, 20}, {190, 106, 130, 37}, {190, 106, 130, 38}, {190, 106, 130, 40}, {190, 106, 130, 41}, {190, 106, 130, 42}, {190, 106, 130, 43}, {190, 106, 130, 45}}},
|
||||
{Region: "Armenia", Group: "Premium UDP Europe", IPs: []net.IP{{45, 139, 50, 7}, {45, 139, 50, 9}, {45, 139, 50, 11}, {45, 139, 50, 12}, {45, 139, 50, 17}, {45, 139, 50, 22}, {45, 139, 50, 25}, {45, 139, 50, 27}, {45, 139, 50, 28}, {45, 139, 50, 29}}},
|
||||
{Region: "Armenia", Group: "Premium TCP Europe", IPs: []net.IP{{45, 139, 50, 6}, {45, 139, 50, 11}, {45, 139, 50, 16}, {45, 139, 50, 17}, {45, 139, 50, 18}, {45, 139, 50, 21}, {45, 139, 50, 22}, {45, 139, 50, 24}, {45, 139, 50, 25}, {45, 139, 50, 28}}},
|
||||
{Region: "Australia", Group: "Premium UDP Asia", IPs: []net.IP{{27, 50, 79, 9}, {27, 50, 79, 22}, {27, 50, 79, 23}, {27, 50, 79, 30}, {203, 26, 199, 68}, {203, 26, 199, 73}, {221, 121, 146, 40}, {221, 121, 146, 41}, {221, 121, 146, 55}, {221, 121, 146, 58}}},
|
||||
{Region: "Australia", Group: "Premium TCP Asia", IPs: []net.IP{{27, 50, 79, 19}, {43, 242, 68, 3}, {43, 242, 68, 8}, {43, 242, 68, 11}, {103, 13, 101, 173}, {203, 26, 199, 66}, {221, 121, 146, 34}, {221, 121, 146, 42}, {221, 121, 146, 50}, {221, 121, 146, 52}}},
|
||||
{Region: "Austria", Group: "Premium TCP Europe", IPs: []net.IP{{89, 187, 168, 147}, {89, 187, 168, 148}, {89, 187, 168, 150}, {89, 187, 168, 162}, {89, 187, 168, 165}, {89, 187, 168, 166}, {89, 187, 168, 169}, {89, 187, 168, 172}, {89, 187, 168, 174}, {89, 187, 168, 183}}},
|
||||
{Region: "Austria", Group: "Premium UDP Europe", IPs: []net.IP{{89, 187, 168, 132}, {89, 187, 168, 136}, {89, 187, 168, 138}, {89, 187, 168, 146}, {89, 187, 168, 148}, {89, 187, 168, 163}, {89, 187, 168, 171}, {89, 187, 168, 172}, {89, 187, 168, 179}, {89, 187, 168, 180}}},
|
||||
{Region: "Bahamas", Group: "Premium TCP USA", IPs: []net.IP{{45, 132, 143, 2}, {45, 132, 143, 4}, {45, 132, 143, 6}, {45, 132, 143, 20}, {45, 132, 143, 22}, {45, 132, 143, 23}, {45, 132, 143, 36}, {45, 132, 143, 39}, {45, 132, 143, 46}, {45, 132, 143, 48}}},
|
||||
{Region: "Bahamas", Group: "Premium UDP USA", IPs: []net.IP{{45, 132, 143, 1}, {45, 132, 143, 4}, {45, 132, 143, 5}, {45, 132, 143, 13}, {45, 132, 143, 15}, {45, 132, 143, 17}, {45, 132, 143, 36}, {45, 132, 143, 41}, {45, 132, 143, 42}, {45, 132, 143, 44}}},
|
||||
{Region: "Bangladesh", Group: "Premium UDP Asia", IPs: []net.IP{{45, 132, 142, 1}, {45, 132, 142, 6}, {45, 132, 142, 10}, {45, 132, 142, 11}, {45, 132, 142, 15}, {45, 132, 142, 16}, {45, 132, 142, 19}, {45, 132, 142, 26}, {45, 132, 142, 36}, {45, 132, 142, 47}}},
|
||||
{Region: "Bangladesh", Group: "Premium TCP Asia", IPs: []net.IP{{45, 132, 142, 3}, {45, 132, 142, 4}, {45, 132, 142, 8}, {45, 132, 142, 16}, {45, 132, 142, 21}, {45, 132, 142, 32}, {45, 132, 142, 36}, {45, 132, 142, 37}, {45, 132, 142, 43}, {45, 132, 142, 48}}},
|
||||
{Region: "Belarus", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 194, 7}, {45, 132, 194, 10}, {45, 132, 194, 20}, {45, 132, 194, 22}, {45, 132, 194, 27}, {45, 132, 194, 30}, {45, 132, 194, 38}, {45, 132, 194, 43}, {45, 132, 194, 45}, {45, 132, 194, 50}}},
|
||||
{Region: "Belarus", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 194, 9}, {45, 132, 194, 14}, {45, 132, 194, 20}, {45, 132, 194, 24}, {45, 132, 194, 29}, {45, 132, 194, 32}, {45, 132, 194, 37}, {45, 132, 194, 45}, {45, 132, 194, 46}, {45, 132, 194, 48}}},
|
||||
{Region: "Belgium", Group: "Premium UDP Europe", IPs: []net.IP{{5, 253, 205, 20}, {5, 253, 205, 25}, {5, 253, 205, 29}, {37, 120, 143, 168}, {185, 210, 217, 58}, {185, 232, 21, 122}, {185, 232, 21, 124}, {193, 9, 114, 211}, {193, 9, 114, 213}, {193, 9, 114, 216}}},
|
||||
{Region: "Belgium", Group: "Premium TCP Europe", IPs: []net.IP{{5, 253, 205, 23}, {5, 253, 205, 27}, {37, 120, 143, 54}, {37, 120, 143, 60}, {37, 120, 143, 166}, {37, 120, 143, 173}, {185, 210, 217, 51}, {185, 210, 217, 252}, {185, 210, 217, 254}, {193, 9, 114, 227}}},
|
||||
{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 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: "Brazil", Group: "Premium UDP USA", IPs: []net.IP{{45, 231, 207, 71}, {45, 231, 207, 76}, {45, 231, 207, 77}, {177, 67, 81, 165}, {177, 67, 81, 172}, {181, 41, 203, 97}, {181, 41, 203, 99}, {181, 41, 203, 101}, {181, 41, 203, 106}, {181, 41, 203, 109}}},
|
||||
{Region: "Brazil", Group: "Premium TCP USA", IPs: []net.IP{{45, 231, 207, 72}, {45, 231, 207, 74}, {45, 231, 207, 79}, {177, 67, 81, 166}, {177, 67, 81, 169}, {177, 67, 81, 171}, {181, 41, 203, 99}, {181, 41, 203, 106}, {181, 41, 203, 108}, {181, 41, 203, 110}}},
|
||||
{Region: "Bulgaria", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 152, 100}, {37, 120, 152, 102}, {37, 120, 152, 103}, {37, 120, 152, 104}, {37, 120, 152, 105}, {37, 120, 152, 106}, {37, 120, 152, 107}, {37, 120, 152, 108}, {37, 120, 152, 109}, {37, 120, 152, 110}}},
|
||||
{Region: "Bulgaria", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 152, 99}, {37, 120, 152, 100}, {37, 120, 152, 101}, {37, 120, 152, 103}, {37, 120, 152, 104}, {37, 120, 152, 105}, {37, 120, 152, 107}, {37, 120, 152, 108}, {37, 120, 152, 109}, {37, 120, 152, 110}}},
|
||||
{Region: "Cambodia", Group: "Premium UDP Asia", IPs: []net.IP{{188, 215, 235, 35}, {188, 215, 235, 36}, {188, 215, 235, 37}, {188, 215, 235, 45}, {188, 215, 235, 47}, {188, 215, 235, 48}, {188, 215, 235, 49}, {188, 215, 235, 53}, {188, 215, 235, 55}, {188, 215, 235, 56}}},
|
||||
{Region: "Cambodia", Group: "Premium TCP Asia", IPs: []net.IP{{188, 215, 235, 36}, {188, 215, 235, 37}, {188, 215, 235, 38}, {188, 215, 235, 40}, {188, 215, 235, 41}, {188, 215, 235, 43}, {188, 215, 235, 47}, {188, 215, 235, 49}, {188, 215, 235, 55}, {188, 215, 235, 56}}},
|
||||
{Region: "Canada", Group: "Premium UDP USA", IPs: []net.IP{{37, 120, 130, 145}, {37, 120, 130, 180}, {37, 120, 130, 203}, {37, 120, 205, 27}, {37, 120, 205, 30}, {89, 47, 234, 117}, {139, 28, 218, 94}, {176, 113, 74, 199}, {176, 113, 74, 205}, {176, 113, 74, 212}}},
|
||||
{Region: "Canada", Group: "Premium TCP USA", IPs: []net.IP{{37, 120, 130, 173}, {37, 120, 130, 203}, {37, 120, 130, 210}, {37, 120, 205, 8}, {89, 47, 234, 88}, {104, 245, 145, 169}, {139, 28, 218, 88}, {176, 113, 74, 70}, {176, 113, 74, 89}, {176, 113, 74, 195}}},
|
||||
{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 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: "China", Group: "Premium TCP Asia", IPs: []net.IP{{45, 132, 193, 2}, {45, 132, 193, 3}, {45, 132, 193, 7}, {45, 132, 193, 10}, {45, 132, 193, 13}, {45, 132, 193, 15}, {45, 132, 193, 20}, {45, 132, 193, 31}, {45, 132, 193, 41}, {45, 132, 193, 47}}},
|
||||
{Region: "China", Group: "Premium UDP Asia", IPs: []net.IP{{45, 132, 193, 7}, {45, 132, 193, 14}, {45, 132, 193, 23}, {45, 132, 193, 26}, {45, 132, 193, 30}, {45, 132, 193, 36}, {45, 132, 193, 44}, {45, 132, 193, 45}, {45, 132, 193, 46}, {45, 132, 193, 48}}},
|
||||
{Region: "Colombia", Group: "Premium TCP USA", IPs: []net.IP{{190, 105, 229, 19}, {190, 105, 229, 20}, {190, 105, 229, 21}, {190, 105, 229, 22}}},
|
||||
{Region: "Colombia", Group: "Premium UDP USA", IPs: []net.IP{{190, 105, 229, 19}, {190, 105, 229, 20}, {190, 105, 229, 21}, {190, 105, 229, 22}}},
|
||||
{Region: "Costa Rica", Group: "Premium TCP USA", IPs: []net.IP{{143, 202, 160, 67}, {143, 202, 160, 68}, {143, 202, 160, 70}, {143, 202, 160, 71}, {143, 202, 160, 72}, {143, 202, 160, 73}, {143, 202, 160, 74}, {143, 202, 160, 75}, {143, 202, 160, 77}, {143, 202, 160, 78}}},
|
||||
{Region: "Costa Rica", Group: "Premium UDP USA", IPs: []net.IP{{143, 202, 160, 67}, {143, 202, 160, 68}, {143, 202, 160, 69}, {143, 202, 160, 70}, {143, 202, 160, 71}, {143, 202, 160, 72}, {143, 202, 160, 73}, {143, 202, 160, 74}, {143, 202, 160, 75}, {143, 202, 160, 76}}},
|
||||
{Region: "Cyprus", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 137, 7}, {45, 132, 137, 9}, {45, 132, 137, 11}, {45, 132, 137, 13}, {45, 132, 137, 20}, {45, 132, 137, 23}, {45, 132, 137, 26}, {45, 132, 137, 27}, {45, 132, 137, 28}, {45, 132, 137, 29}}},
|
||||
{Region: "Cyprus", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 137, 7}, {45, 132, 137, 10}, {45, 132, 137, 14}, {45, 132, 137, 15}, {45, 132, 137, 17}, {45, 132, 137, 18}, {45, 132, 137, 21}, {45, 132, 137, 24}, {45, 132, 137, 27}, {45, 132, 137, 29}}},
|
||||
{Region: "Czech Republic", Group: "Premium UDP Europe", IPs: []net.IP{{185, 216, 35, 227}, {185, 216, 35, 232}, {185, 216, 35, 236}, {195, 181, 161, 5}, {195, 181, 161, 6}, {195, 181, 161, 7}, {195, 181, 161, 10}, {195, 181, 161, 12}, {195, 181, 161, 15}, {195, 181, 161, 19}}},
|
||||
{Region: "Czech Republic", Group: "Premium TCP Europe", IPs: []net.IP{{185, 216, 35, 230}, {185, 216, 35, 232}, {185, 216, 35, 236}, {195, 181, 161, 4}, {195, 181, 161, 8}, {195, 181, 161, 15}, {195, 181, 161, 18}, {195, 181, 161, 20}, {195, 181, 161, 24}, {195, 181, 161, 25}}},
|
||||
{Region: "Denmark", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 145, 85}, {37, 120, 145, 90}, {37, 120, 194, 37}, {37, 120, 194, 41}, {95, 174, 65, 164}, {95, 174, 65, 174}, {185, 206, 224, 231}, {185, 206, 224, 235}, {185, 206, 224, 236}, {185, 206, 224, 238}}},
|
||||
{Region: "Denmark", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 145, 86}, {37, 120, 145, 88}, {37, 120, 194, 40}, {37, 120, 194, 42}, {37, 120, 194, 60}, {37, 120, 194, 62}, {95, 174, 65, 170}, {185, 206, 224, 231}, {185, 206, 224, 248}, {185, 206, 224, 250}}},
|
||||
{Region: "Egypt", Group: "Premium TCP Europe", IPs: []net.IP{{188, 214, 122, 40}, {188, 214, 122, 44}, {188, 214, 122, 46}, {188, 214, 122, 47}, {188, 214, 122, 48}, {188, 214, 122, 51}, {188, 214, 122, 52}, {188, 214, 122, 56}, {188, 214, 122, 59}, {188, 214, 122, 60}}},
|
||||
{Region: "Egypt", Group: "Premium UDP Europe", IPs: []net.IP{{188, 214, 122, 40}, {188, 214, 122, 41}, {188, 214, 122, 43}, {188, 214, 122, 46}, {188, 214, 122, 48}, {188, 214, 122, 51}, {188, 214, 122, 52}, {188, 214, 122, 55}, {188, 214, 122, 57}, {188, 214, 122, 60}}},
|
||||
{Region: "Estonia", Group: "Premium UDP Europe", IPs: []net.IP{{77, 247, 111, 3}, {77, 247, 111, 4}, {77, 247, 111, 6}, {77, 247, 111, 9}, {77, 247, 111, 11}, {77, 247, 111, 51}, {77, 247, 111, 53}, {77, 247, 111, 57}, {77, 247, 111, 58}, {77, 247, 111, 62}}},
|
||||
{Region: "Estonia", Group: "Premium TCP Europe", IPs: []net.IP{{77, 247, 111, 4}, {77, 247, 111, 7}, {77, 247, 111, 9}, {77, 247, 111, 10}, {77, 247, 111, 11}, {77, 247, 111, 52}, {77, 247, 111, 53}, {77, 247, 111, 55}, {77, 247, 111, 56}, {77, 247, 111, 57}}},
|
||||
{Region: "Finland", Group: "Premium UDP Europe", IPs: []net.IP{{188, 126, 89, 102}, {188, 126, 89, 103}, {188, 126, 89, 104}, {188, 126, 89, 105}, {188, 126, 89, 112}, {188, 126, 89, 116}, {188, 126, 89, 124}, {188, 126, 89, 131}, {188, 126, 89, 137}, {188, 126, 89, 138}}},
|
||||
{Region: "Finland", Group: "Premium TCP Europe", IPs: []net.IP{{188, 126, 89, 99}, {188, 126, 89, 106}, {188, 126, 89, 113}, {188, 126, 89, 121}, {188, 126, 89, 125}, {188, 126, 89, 131}, {188, 126, 89, 132}, {188, 126, 89, 142}, {188, 126, 89, 146}, {188, 126, 89, 155}}},
|
||||
{Region: "France", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 60, 9}, {84, 17, 60, 23}, {84, 17, 60, 28}, {84, 17, 60, 95}, {84, 17, 61, 110}, {84, 17, 61, 162}, {84, 17, 61, 171}, {84, 17, 61, 187}, {151, 106, 12, 248}, {194, 59, 249, 150}}},
|
||||
{Region: "France", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 60, 53}, {84, 17, 60, 59}, {84, 17, 60, 99}, {84, 17, 60, 150}, {84, 17, 61, 53}, {84, 17, 61, 104}, {84, 17, 61, 158}, {84, 17, 61, 203}, {151, 106, 8, 45}, {151, 106, 12, 243}}},
|
||||
{Region: "Georgia", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 138, 7}, {45, 132, 138, 11}, {45, 132, 138, 12}, {45, 132, 138, 13}, {45, 132, 138, 16}, {45, 132, 138, 19}, {45, 132, 138, 20}, {45, 132, 138, 21}, {45, 132, 138, 28}, {45, 132, 138, 29}}},
|
||||
{Region: "Georgia", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 138, 6}, {45, 132, 138, 7}, {45, 132, 138, 8}, {45, 132, 138, 10}, {45, 132, 138, 14}, {45, 132, 138, 15}, {45, 132, 138, 17}, {45, 132, 138, 19}, {45, 132, 138, 22}, {45, 132, 138, 29}}},
|
||||
{Region: "Germany", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 217, 40}, {84, 17, 48, 203}, {84, 17, 49, 94}, {84, 17, 49, 102}, {84, 17, 49, 188}, {154, 28, 188, 30}, {154, 28, 188, 99}, {154, 28, 188, 117}, {154, 28, 188, 131}, {193, 176, 86, 218}}},
|
||||
{Region: "Germany", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 48, 13}, {84, 17, 48, 66}, {84, 17, 48, 72}, {84, 17, 48, 193}, {84, 17, 49, 129}, {154, 28, 188, 45}, {154, 28, 188, 58}, {154, 28, 188, 109}, {193, 176, 86, 215}, {212, 103, 50, 69}}},
|
||||
{Region: "Greece", Group: "Premium UDP Europe", IPs: []net.IP{{154, 57, 3, 130}, {154, 57, 3, 131}, {154, 57, 3, 133}, {154, 57, 3, 134}, {154, 57, 3, 137}, {154, 57, 3, 138}, {188, 123, 126, 170}, {188, 123, 126, 174}, {188, 123, 126, 175}, {188, 123, 126, 176}}},
|
||||
{Region: "Greece", Group: "Premium TCP Europe", IPs: []net.IP{{154, 57, 3, 130}, {154, 57, 3, 131}, {154, 57, 3, 133}, {154, 57, 3, 135}, {154, 57, 3, 136}, {154, 57, 3, 140}, {154, 57, 3, 141}, {188, 123, 126, 168}, {188, 123, 126, 174}, {188, 123, 126, 176}}},
|
||||
{Region: "Greenland", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 209, 6}, {45, 131, 209, 8}, {45, 131, 209, 9}, {45, 131, 209, 15}, {45, 131, 209, 19}, {45, 131, 209, 20}, {45, 131, 209, 21}, {45, 131, 209, 25}, {45, 131, 209, 26}, {45, 131, 209, 29}}},
|
||||
{Region: "Greenland", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 209, 7}, {45, 131, 209, 14}, {45, 131, 209, 16}, {45, 131, 209, 17}, {45, 131, 209, 18}, {45, 131, 209, 19}, {45, 131, 209, 20}, {45, 131, 209, 23}, {45, 131, 209, 26}, {45, 131, 209, 27}}},
|
||||
{Region: "Hong Kong", Group: "Premium TCP Asia", IPs: []net.IP{{84, 17, 56, 131}, {84, 17, 56, 136}, {84, 17, 56, 152}, {84, 17, 56, 168}, {84, 17, 56, 170}, {84, 17, 56, 171}, {84, 17, 56, 174}, {84, 17, 56, 179}, {84, 17, 56, 180}, {84, 17, 56, 182}}},
|
||||
{Region: "Hong Kong", Group: "Premium UDP Asia", IPs: []net.IP{{84, 17, 56, 135}, {84, 17, 56, 139}, {84, 17, 56, 140}, {84, 17, 56, 147}, {84, 17, 56, 148}, {84, 17, 56, 153}, {84, 17, 56, 164}, {84, 17, 56, 168}, {84, 17, 56, 179}, {84, 17, 56, 181}}},
|
||||
{Region: "Hungary", Group: "Premium TCP Europe", IPs: []net.IP{{185, 104, 187, 83}, {185, 104, 187, 85}, {185, 104, 187, 89}, {185, 189, 114, 115}, {185, 189, 114, 116}, {185, 189, 114, 117}, {185, 189, 114, 118}, {185, 189, 114, 121}, {185, 189, 114, 124}, {185, 189, 114, 126}}},
|
||||
{Region: "Hungary", Group: "Premium UDP Europe", IPs: []net.IP{{185, 104, 187, 85}, {185, 104, 187, 88}, {185, 104, 187, 91}, {185, 104, 187, 94}, {185, 189, 114, 116}, {185, 189, 114, 119}, {185, 189, 114, 120}, {185, 189, 114, 123}, {185, 189, 114, 124}, {185, 189, 114, 125}}},
|
||||
{Region: "Iceland", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 193, 3}, {45, 133, 193, 4}, {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, 12}, {45, 133, 193, 13}}},
|
||||
{Region: "Iceland", Group: "Premium TCP Europe", IPs: []net.IP{{45, 133, 193, 3}, {45, 133, 193, 4}, {45, 133, 193, 5}, {45, 133, 193, 8}, {45, 133, 193, 9}, {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, 118}, {43, 241, 71, 119}, {43, 241, 71, 120}, {43, 241, 71, 124}, {43, 241, 71, 125}, {43, 241, 71, 148}, {43, 241, 71, 150}, {43, 241, 71, 154}, {43, 241, 71, 155}}},
|
||||
{Region: "India", Group: "Premium TCP Europe", IPs: []net.IP{{43, 241, 71, 116}, {43, 241, 71, 120}, {43, 241, 71, 121}, {43, 241, 71, 122}, {43, 241, 71, 125}, {43, 241, 71, 148}, {43, 241, 71, 151}, {43, 241, 71, 153}, {43, 241, 71, 155}, {43, 241, 71, 157}}},
|
||||
{Region: "Indonesia", Group: "Premium UDP Asia", IPs: []net.IP{{113, 20, 29, 243}, {113, 20, 29, 244}, {113, 20, 29, 245}, {113, 20, 29, 246}, {113, 20, 29, 247}, {113, 20, 29, 249}, {113, 20, 29, 251}, {113, 20, 29, 252}, {113, 20, 29, 253}, {113, 20, 29, 254}}},
|
||||
{Region: "Indonesia", Group: "Premium TCP Asia", IPs: []net.IP{{113, 20, 29, 243}, {113, 20, 29, 245}, {113, 20, 29, 246}, {113, 20, 29, 248}, {113, 20, 29, 249}, {113, 20, 29, 250}, {113, 20, 29, 251}, {113, 20, 29, 252}, {113, 20, 29, 253}, {113, 20, 29, 254}}},
|
||||
{Region: "Iran", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 4, 10}, {45, 131, 4, 13}, {45, 131, 4, 14}, {45, 131, 4, 15}, {45, 131, 4, 17}, {45, 131, 4, 20}, {45, 131, 4, 21}, {45, 131, 4, 22}, {45, 131, 4, 24}, {45, 131, 4, 28}}},
|
||||
{Region: "Iran", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 4, 7}, {45, 131, 4, 11}, {45, 131, 4, 12}, {45, 131, 4, 13}, {45, 131, 4, 19}, {45, 131, 4, 21}, {45, 131, 4, 26}, {45, 131, 4, 27}, {45, 131, 4, 28}, {45, 131, 4, 29}}},
|
||||
{Region: "Ireland", Group: "Premium TCP Europe", IPs: []net.IP{{77, 81, 139, 36}, {77, 81, 139, 38}, {77, 81, 139, 39}, {77, 81, 139, 40}, {84, 247, 48, 3}, {84, 247, 48, 5}, {84, 247, 48, 11}, {84, 247, 48, 12}, {84, 247, 48, 27}, {84, 247, 48, 28}}},
|
||||
{Region: "Ireland", Group: "Premium UDP Europe", IPs: []net.IP{{77, 81, 139, 42}, {77, 81, 139, 45}, {84, 247, 48, 3}, {84, 247, 48, 6}, {84, 247, 48, 7}, {84, 247, 48, 8}, {84, 247, 48, 20}, {84, 247, 48, 22}, {84, 247, 48, 24}, {84, 247, 48, 29}}},
|
||||
{Region: "Isle of Man", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 140, 7}, {45, 132, 140, 8}, {45, 132, 140, 9}, {45, 132, 140, 11}, {45, 132, 140, 13}, {45, 132, 140, 15}, {45, 132, 140, 18}, {45, 132, 140, 20}, {45, 132, 140, 24}, {45, 132, 140, 25}}},
|
||||
{Region: "Isle of Man", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 140, 7}, {45, 132, 140, 8}, {45, 132, 140, 15}, {45, 132, 140, 20}, {45, 132, 140, 21}, {45, 132, 140, 22}, {45, 132, 140, 23}, {45, 132, 140, 27}, {45, 132, 140, 28}, {45, 132, 140, 29}}},
|
||||
{Region: "Israel", Group: "Premium TCP Europe", IPs: []net.IP{{160, 116, 0, 163}, {160, 116, 0, 165}, {160, 116, 0, 166}, {160, 116, 0, 167}, {160, 116, 0, 169}, {160, 116, 0, 170}, {160, 116, 0, 171}, {160, 116, 0, 172}, {160, 116, 0, 173}, {160, 116, 0, 174}}},
|
||||
{Region: "Israel", Group: "Premium UDP Europe", IPs: []net.IP{{160, 116, 0, 163}, {160, 116, 0, 164}, {160, 116, 0, 165}, {160, 116, 0, 166}, {160, 116, 0, 167}, {160, 116, 0, 168}, {160, 116, 0, 169}, {160, 116, 0, 171}, {160, 116, 0, 172}, {160, 116, 0, 173}}},
|
||||
{Region: "Italy", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 58, 5}, {84, 17, 58, 8}, {84, 17, 58, 22}, {84, 17, 58, 97}, {84, 17, 58, 121}, {87, 101, 94, 119}, {185, 217, 71, 135}, {185, 217, 71, 142}, {185, 217, 71, 147}, {212, 102, 55, 115}}},
|
||||
{Region: "Italy", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 58, 9}, {84, 17, 58, 96}, {84, 17, 58, 97}, {84, 17, 58, 105}, {87, 101, 94, 115}, {87, 101, 94, 124}, {185, 217, 71, 135}, {185, 217, 71, 149}, {212, 102, 55, 98}, {212, 102, 55, 122}}},
|
||||
{Region: "Japan", Group: "Premium UDP Asia", IPs: []net.IP{{156, 146, 35, 3}, {156, 146, 35, 6}, {156, 146, 35, 12}, {156, 146, 35, 21}, {156, 146, 35, 25}, {156, 146, 35, 30}, {156, 146, 35, 35}, {156, 146, 35, 39}, {156, 146, 35, 46}, {156, 146, 35, 50}}},
|
||||
{Region: "Japan", Group: "Premium TCP Asia", IPs: []net.IP{{156, 146, 35, 3}, {156, 146, 35, 5}, {156, 146, 35, 17}, {156, 146, 35, 29}, {156, 146, 35, 32}, {156, 146, 35, 35}, {156, 146, 35, 36}, {156, 146, 35, 41}, {156, 146, 35, 47}, {156, 146, 35, 48}}},
|
||||
{Region: "Kazakhstan", Group: "Premium TCP Europe", IPs: []net.IP{{45, 133, 88, 10}, {45, 133, 88, 11}, {45, 133, 88, 13}, {45, 133, 88, 14}, {45, 133, 88, 15}, {45, 133, 88, 17}, {45, 133, 88, 20}, {45, 133, 88, 21}, {45, 133, 88, 27}, {45, 133, 88, 28}}},
|
||||
{Region: "Kazakhstan", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 88, 6}, {45, 133, 88, 9}, {45, 133, 88, 11}, {45, 133, 88, 13}, {45, 133, 88, 14}, {45, 133, 88, 17}, {45, 133, 88, 20}, {45, 133, 88, 22}, {45, 133, 88, 25}, {45, 133, 88, 28}}},
|
||||
{Region: "Kenya", Group: "Premium TCP Asia", IPs: []net.IP{{62, 12, 118, 195}, {62, 12, 118, 196}, {62, 12, 118, 197}, {62, 12, 118, 198}, {62, 12, 118, 199}, {62, 12, 118, 200}, {62, 12, 118, 201}, {62, 12, 118, 202}, {62, 12, 118, 203}, {62, 12, 118, 204}}},
|
||||
{Region: "Kenya", Group: "Premium UDP Asia", IPs: []net.IP{{62, 12, 118, 195}, {62, 12, 118, 196}, {62, 12, 118, 197}, {62, 12, 118, 198}, {62, 12, 118, 199}, {62, 12, 118, 200}, {62, 12, 118, 201}, {62, 12, 118, 202}, {62, 12, 118, 203}, {62, 12, 118, 204}}},
|
||||
{Region: "Korea", Group: "Premium UDP Asia", IPs: []net.IP{{27, 255, 75, 233}, {27, 255, 75, 236}, {27, 255, 75, 237}, {27, 255, 75, 244}, {27, 255, 75, 247}, {27, 255, 75, 249}, {27, 255, 75, 250}, {27, 255, 75, 252}, {27, 255, 75, 253}, {27, 255, 75, 254}}},
|
||||
{Region: "Korea", Group: "Premium TCP Asia", IPs: []net.IP{{27, 255, 75, 228}, {27, 255, 75, 231}, {27, 255, 75, 233}, {27, 255, 75, 237}, {27, 255, 75, 243}, {27, 255, 75, 246}, {27, 255, 75, 248}, {27, 255, 75, 250}, {27, 255, 75, 252}, {27, 255, 75, 254}}},
|
||||
{Region: "Latvia", Group: "Premium UDP Europe", IPs: []net.IP{{109, 248, 148, 244}, {109, 248, 148, 246}, {109, 248, 148, 252}, {109, 248, 148, 253}, {109, 248, 149, 19}, {109, 248, 149, 20}, {109, 248, 149, 23}, {109, 248, 149, 24}, {109, 248, 149, 27}, {109, 248, 149, 29}}},
|
||||
{Region: "Latvia", Group: "Premium TCP Europe", IPs: []net.IP{{109, 248, 148, 243}, {109, 248, 148, 244}, {109, 248, 148, 251}, {109, 248, 148, 252}, {109, 248, 148, 254}, {109, 248, 149, 21}, {109, 248, 149, 24}, {109, 248, 149, 25}, {109, 248, 149, 28}, {109, 248, 149, 29}}},
|
||||
{Region: "Liechtenstein", Group: "Premium TCP Europe", IPs: []net.IP{{45, 139, 48, 7}, {45, 139, 48, 8}, {45, 139, 48, 10}, {45, 139, 48, 11}, {45, 139, 48, 13}, {45, 139, 48, 19}, {45, 139, 48, 22}, {45, 139, 48, 24}, {45, 139, 48, 25}, {45, 139, 48, 29}}},
|
||||
{Region: "Liechtenstein", Group: "Premium UDP Europe", IPs: []net.IP{{45, 139, 48, 7}, {45, 139, 48, 12}, {45, 139, 48, 13}, {45, 139, 48, 14}, {45, 139, 48, 16}, {45, 139, 48, 21}, {45, 139, 48, 22}, {45, 139, 48, 23}, {45, 139, 48, 25}, {45, 139, 48, 27}}},
|
||||
{Region: "Lithuania", Group: "Premium UDP Europe", IPs: []net.IP{{85, 206, 162, 209}, {85, 206, 162, 211}, {85, 206, 162, 215}, {85, 206, 162, 218}, {85, 206, 162, 220}, {85, 206, 162, 222}, {85, 206, 165, 18}, {85, 206, 165, 20}, {85, 206, 165, 23}, {85, 206, 165, 26}}},
|
||||
{Region: "Lithuania", Group: "Premium TCP Europe", IPs: []net.IP{{85, 206, 162, 210}, {85, 206, 162, 214}, {85, 206, 162, 215}, {85, 206, 162, 218}, {85, 206, 162, 219}, {85, 206, 162, 220}, {85, 206, 162, 221}, {85, 206, 165, 17}, {85, 206, 165, 18}, {85, 206, 165, 19}}},
|
||||
{Region: "Luxembourg", Group: "Premium UDP Europe", IPs: []net.IP{{5, 253, 204, 8}, {5, 253, 204, 10}, {5, 253, 204, 11}, {5, 253, 204, 14}, {5, 253, 204, 19}, {5, 253, 204, 21}, {5, 253, 204, 23}, {5, 253, 204, 27}, {5, 253, 204, 29}, {5, 253, 204, 30}}},
|
||||
{Region: "Luxembourg", Group: "Premium TCP Europe", IPs: []net.IP{{5, 253, 204, 5}, {5, 253, 204, 9}, {5, 253, 204, 10}, {5, 253, 204, 12}, {5, 253, 204, 13}, {5, 253, 204, 19}, {5, 253, 204, 22}, {5, 253, 204, 26}, {5, 253, 204, 27}, {5, 253, 204, 28}}},
|
||||
{Region: "Macao", Group: "Premium UDP Asia", IPs: []net.IP{{45, 137, 197, 9}, {45, 137, 197, 10}, {45, 137, 197, 12}, {45, 137, 197, 14}, {45, 137, 197, 18}, {45, 137, 197, 25}, {45, 137, 197, 29}, {45, 137, 197, 30}, {45, 137, 197, 33}, {45, 137, 197, 45}}},
|
||||
{Region: "Macao", Group: "Premium TCP Asia", IPs: []net.IP{{45, 137, 197, 1}, {45, 137, 197, 14}, {45, 137, 197, 16}, {45, 137, 197, 17}, {45, 137, 197, 26}, {45, 137, 197, 28}, {45, 137, 197, 30}, {45, 137, 197, 40}, {45, 137, 197, 42}, {45, 137, 197, 48}}},
|
||||
{Region: "Macedonia", Group: "Premium UDP Europe", IPs: []net.IP{{185, 225, 28, 3}, {185, 225, 28, 4}, {185, 225, 28, 5}, {185, 225, 28, 6}, {185, 225, 28, 7}, {185, 225, 28, 8}, {185, 225, 28, 9}, {185, 225, 28, 10}, {185, 225, 28, 11}, {185, 225, 28, 12}}},
|
||||
{Region: "Macedonia", Group: "Premium TCP Europe", IPs: []net.IP{{185, 225, 28, 3}, {185, 225, 28, 4}, {185, 225, 28, 5}, {185, 225, 28, 6}, {185, 225, 28, 7}, {185, 225, 28, 8}, {185, 225, 28, 9}, {185, 225, 28, 10}, {185, 225, 28, 11}, {185, 225, 28, 12}}},
|
||||
{Region: "Malaysia", Group: "Premium 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 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: "Malta", Group: "Premium UDP Europe", IPs: []net.IP{{45, 137, 198, 9}, {45, 137, 198, 11}, {45, 137, 198, 18}, {45, 137, 198, 20}, {45, 137, 198, 24}, {45, 137, 198, 25}, {45, 137, 198, 26}, {45, 137, 198, 27}, {45, 137, 198, 28}, {45, 137, 198, 29}}},
|
||||
{Region: "Malta", Group: "Premium TCP Europe", IPs: []net.IP{{45, 137, 198, 8}, {45, 137, 198, 10}, {45, 137, 198, 12}, {45, 137, 198, 14}, {45, 137, 198, 17}, {45, 137, 198, 19}, {45, 137, 198, 20}, {45, 137, 198, 22}, {45, 137, 198, 27}, {45, 137, 198, 28}}},
|
||||
{Region: "Mexico", Group: "Premium TCP USA", IPs: []net.IP{{45, 133, 180, 99}, {45, 133, 180, 100}, {45, 133, 180, 103}, {45, 133, 180, 104}, {45, 133, 180, 109}, {45, 133, 180, 115}, {45, 133, 180, 118}, {45, 133, 180, 119}, {45, 133, 180, 120}, {45, 133, 180, 123}}},
|
||||
{Region: "Mexico", Group: "Premium UDP USA", IPs: []net.IP{{45, 133, 180, 101}, {45, 133, 180, 103}, {45, 133, 180, 106}, {45, 133, 180, 107}, {45, 133, 180, 109}, {45, 133, 180, 110}, {45, 133, 180, 119}, {45, 133, 180, 121}, {45, 133, 180, 122}, {45, 133, 180, 123}}},
|
||||
{Region: "Moldova", Group: "Premium TCP Europe", IPs: []net.IP{{178, 175, 130, 243}, {178, 175, 130, 245}, {178, 175, 130, 246}, {178, 175, 130, 250}, {178, 175, 130, 251}, {178, 175, 130, 252}, {178, 175, 130, 254}, {178, 175, 142, 131}, {178, 175, 142, 133}, {178, 175, 142, 134}}},
|
||||
{Region: "Moldova", Group: "Premium UDP Europe", IPs: []net.IP{{178, 175, 130, 243}, {178, 175, 130, 246}, {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: "Monaco", Group: "Premium TCP Europe", IPs: []net.IP{{45, 137, 199, 6}, {45, 137, 199, 8}, {45, 137, 199, 11}, {45, 137, 199, 12}, {45, 137, 199, 13}, {45, 137, 199, 15}, {45, 137, 199, 16}, {45, 137, 199, 18}, {45, 137, 199, 23}, {45, 137, 199, 26}}},
|
||||
{Region: "Monaco", Group: "Premium UDP Europe", IPs: []net.IP{{45, 137, 199, 6}, {45, 137, 199, 7}, {45, 137, 199, 10}, {45, 137, 199, 12}, {45, 137, 199, 13}, {45, 137, 199, 19}, {45, 137, 199, 20}, {45, 137, 199, 23}, {45, 137, 199, 25}, {45, 137, 199, 29}}},
|
||||
{Region: "Mongolia", Group: "Premium UDP Asia", IPs: []net.IP{{45, 139, 51, 4}, {45, 139, 51, 11}, {45, 139, 51, 12}, {45, 139, 51, 15}, {45, 139, 51, 16}, {45, 139, 51, 27}, {45, 139, 51, 29}, {45, 139, 51, 32}, {45, 139, 51, 46}, {45, 139, 51, 48}}},
|
||||
{Region: "Mongolia", Group: "Premium TCP Asia", IPs: []net.IP{{45, 139, 51, 5}, {45, 139, 51, 7}, {45, 139, 51, 15}, {45, 139, 51, 17}, {45, 139, 51, 18}, {45, 139, 51, 20}, {45, 139, 51, 21}, {45, 139, 51, 39}, {45, 139, 51, 41}, {45, 139, 51, 45}}},
|
||||
{Region: "Montenegro", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 208, 8}, {45, 131, 208, 9}, {45, 131, 208, 10}, {45, 131, 208, 17}, {45, 131, 208, 19}, {45, 131, 208, 20}, {45, 131, 208, 23}, {45, 131, 208, 25}, {45, 131, 208, 26}, {45, 131, 208, 28}}},
|
||||
{Region: "Montenegro", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 208, 6}, {45, 131, 208, 7}, {45, 131, 208, 8}, {45, 131, 208, 9}, {45, 131, 208, 13}, {45, 131, 208, 18}, {45, 131, 208, 21}, {45, 131, 208, 23}, {45, 131, 208, 27}, {45, 131, 208, 29}}},
|
||||
{Region: "Morocco", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 211, 9}, {45, 131, 211, 11}, {45, 131, 211, 12}, {45, 131, 211, 16}, {45, 131, 211, 18}, {45, 131, 211, 19}, {45, 131, 211, 21}, {45, 131, 211, 24}, {45, 131, 211, 27}, {45, 131, 211, 28}}},
|
||||
{Region: "Morocco", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 211, 8}, {45, 131, 211, 14}, {45, 131, 211, 15}, {45, 131, 211, 17}, {45, 131, 211, 19}, {45, 131, 211, 21}, {45, 131, 211, 22}, {45, 131, 211, 23}, {45, 131, 211, 25}, {45, 131, 211, 26}}},
|
||||
{Region: "Netherlands", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 47, 53}, {84, 17, 47, 55}, {84, 17, 47, 64}, {84, 17, 47, 73}, {84, 17, 47, 102}, {84, 17, 47, 107}, {84, 17, 47, 110}, {84, 17, 47, 112}, {139, 28, 217, 200}, {195, 181, 172, 80}}},
|
||||
{Region: "Netherlands", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 47, 45}, {84, 17, 47, 68}, {84, 17, 47, 75}, {84, 17, 47, 92}, {84, 17, 47, 105}, {195, 181, 172, 69}, {195, 181, 172, 70}, {195, 181, 172, 77}, {195, 181, 172, 78}, {195, 181, 172, 79}}},
|
||||
{Region: "New Zealand", Group: "Premium TCP Asia", IPs: []net.IP{{114, 141, 194, 2}, {114, 141, 194, 4}, {114, 141, 194, 5}, {114, 141, 194, 7}, {114, 141, 194, 8}, {114, 141, 194, 9}, {114, 141, 194, 10}, {114, 141, 194, 11}, {114, 141, 194, 13}, {114, 141, 194, 14}}},
|
||||
{Region: "New Zealand", Group: "Premium UDP Asia", IPs: []net.IP{{114, 141, 194, 2}, {114, 141, 194, 3}, {114, 141, 194, 4}, {114, 141, 194, 6}, {114, 141, 194, 7}, {114, 141, 194, 8}, {114, 141, 194, 9}, {114, 141, 194, 10}, {114, 141, 194, 12}, {114, 141, 194, 13}}},
|
||||
{Region: "Nigeria", Group: "Premium UDP Europe", IPs: []net.IP{{45, 137, 196, 6}, {45, 137, 196, 10}, {45, 137, 196, 14}, {45, 137, 196, 15}, {45, 137, 196, 17}, {45, 137, 196, 20}, {45, 137, 196, 24}, {45, 137, 196, 26}, {45, 137, 196, 28}, {45, 137, 196, 29}}},
|
||||
{Region: "Nigeria", Group: "Premium TCP Europe", IPs: []net.IP{{45, 137, 196, 6}, {45, 137, 196, 7}, {45, 137, 196, 8}, {45, 137, 196, 15}, {45, 137, 196, 16}, {45, 137, 196, 19}, {45, 137, 196, 23}, {45, 137, 196, 24}, {45, 137, 196, 27}, {45, 137, 196, 28}}},
|
||||
{Region: "Norway", Group: "Premium TCP Europe", IPs: []net.IP{{45, 12, 223, 136}, {45, 12, 223, 139}, {45, 12, 223, 141}, {82, 102, 27, 93}, {185, 206, 225, 230}, {185, 206, 225, 232}, {185, 206, 225, 235}, {185, 253, 97, 235}, {185, 253, 97, 251}, {185, 253, 97, 253}}},
|
||||
{Region: "Norway", Group: "Premium UDP Europe", IPs: []net.IP{{45, 12, 223, 134}, {82, 102, 27, 92}, {185, 206, 225, 29}, {185, 206, 225, 30}, {185, 206, 225, 231}, {185, 206, 225, 233}, {185, 206, 225, 234}, {185, 253, 97, 236}, {185, 253, 97, 243}, {185, 253, 97, 245}}},
|
||||
{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 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: "Panama", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 210, 6}, {45, 131, 210, 7}, {45, 131, 210, 10}, {45, 131, 210, 13}, {45, 131, 210, 14}, {45, 131, 210, 19}, {45, 131, 210, 24}, {45, 131, 210, 25}, {45, 131, 210, 28}, {45, 131, 210, 29}}},
|
||||
{Region: "Panama", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 210, 7}, {45, 131, 210, 8}, {45, 131, 210, 9}, {45, 131, 210, 10}, {45, 131, 210, 12}, {45, 131, 210, 13}, {45, 131, 210, 16}, {45, 131, 210, 17}, {45, 131, 210, 20}, {45, 131, 210, 24}}},
|
||||
{Region: "Philippines", Group: "Premium UDP Asia", IPs: []net.IP{{188, 214, 125, 35}, {188, 214, 125, 37}, {188, 214, 125, 40}, {188, 214, 125, 41}, {188, 214, 125, 44}, {188, 214, 125, 47}, {188, 214, 125, 54}, {188, 214, 125, 57}, {188, 214, 125, 58}, {188, 214, 125, 59}}},
|
||||
{Region: "Philippines", Group: "Premium TCP Asia", IPs: []net.IP{{188, 214, 125, 35}, {188, 214, 125, 39}, {188, 214, 125, 40}, {188, 214, 125, 43}, {188, 214, 125, 44}, {188, 214, 125, 50}, {188, 214, 125, 57}, {188, 214, 125, 58}, {188, 214, 125, 60}, {188, 214, 125, 62}}},
|
||||
{Region: "Poland", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 156, 8}, {37, 120, 156, 10}, {37, 120, 156, 12}, {37, 120, 156, 13}, {37, 120, 156, 19}, {37, 120, 156, 22}, {37, 120, 156, 23}, {37, 120, 156, 24}, {51, 75, 56, 37}, {51, 75, 56, 44}}},
|
||||
{Region: "Poland", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 156, 6}, {37, 120, 156, 8}, {37, 120, 156, 10}, {37, 120, 156, 12}, {37, 120, 156, 13}, {37, 120, 156, 20}, {37, 120, 156, 21}, {37, 120, 156, 27}, {51, 75, 56, 34}, {51, 75, 56, 43}}},
|
||||
{Region: "Portugal", Group: "Premium UDP Europe", IPs: []net.IP{{89, 26, 243, 2}, {89, 26, 243, 98}, {89, 26, 243, 100}, {89, 26, 243, 112}, {89, 26, 243, 113}, {89, 26, 243, 115}, {89, 26, 243, 194}, {89, 26, 243, 195}, {89, 26, 243, 198}, {89, 26, 243, 199}}},
|
||||
{Region: "Portugal", Group: "Premium TCP Europe", IPs: []net.IP{{89, 26, 243, 1}, {89, 26, 243, 98}, {89, 26, 243, 100}, {89, 26, 243, 112}, {89, 26, 243, 113}, {89, 26, 243, 114}, {89, 26, 243, 194}, {89, 26, 243, 195}, {89, 26, 243, 197}, {89, 26, 243, 198}}},
|
||||
{Region: "Qatar", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 7, 6}, {45, 131, 7, 10}, {45, 131, 7, 13}, {45, 131, 7, 15}, {45, 131, 7, 16}, {45, 131, 7, 22}, {45, 131, 7, 24}, {45, 131, 7, 26}, {45, 131, 7, 27}, {45, 131, 7, 28}}},
|
||||
{Region: "Qatar", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 7, 7}, {45, 131, 7, 9}, {45, 131, 7, 13}, {45, 131, 7, 16}, {45, 131, 7, 17}, {45, 131, 7, 18}, {45, 131, 7, 19}, {45, 131, 7, 20}, {45, 131, 7, 26}, {45, 131, 7, 27}}},
|
||||
{Region: "Romania", Group: "NoSpy UDP Europe", IPs: []net.IP{{85, 9, 20, 132}, {85, 9, 20, 133}, {85, 9, 20, 134}, {85, 9, 20, 139}, {85, 9, 20, 144}, {85, 9, 20, 145}, {85, 9, 20, 147}, {85, 9, 20, 148}, {85, 9, 20, 154}, {85, 9, 20, 249}}},
|
||||
{Region: "Romania", Group: "Premium TCP Europe", IPs: []net.IP{{193, 176, 84, 43}, {193, 176, 84, 45}, {193, 176, 84, 47}, {193, 176, 84, 52}, {193, 176, 84, 120}, {193, 176, 85, 79}, {193, 176, 85, 91}, {193, 176, 85, 99}, {193, 176, 85, 105}, {193, 176, 85, 116}}},
|
||||
{Region: "Romania", Group: "NoSpy TCP Europe", IPs: []net.IP{{85, 9, 20, 132}, {85, 9, 20, 134}, {85, 9, 20, 137}, {85, 9, 20, 148}, {85, 9, 20, 149}, {85, 9, 20, 150}, {85, 9, 20, 151}, {85, 9, 20, 155}, {85, 9, 20, 248}, {85, 9, 20, 249}}},
|
||||
{Region: "Romania", Group: "Premium UDP Europe", IPs: []net.IP{{193, 176, 84, 84}, {193, 176, 84, 124}, {193, 176, 84, 126}, {193, 176, 85, 68}, {193, 176, 85, 72}, {193, 176, 85, 81}, {193, 176, 85, 85}, {193, 176, 85, 104}, {193, 176, 85, 108}, {193, 176, 85, 116}}},
|
||||
{Region: "Russian Federation", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 192, 6}, {45, 132, 192, 16}, {45, 132, 192, 28}, {45, 132, 192, 46}, {45, 132, 192, 52}, {45, 132, 192, 57}, {45, 132, 192, 70}, {45, 132, 192, 71}, {45, 132, 192, 76}, {45, 132, 192, 92}}},
|
||||
{Region: "Russian Federation", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 192, 9}, {45, 132, 192, 11}, {45, 132, 192, 22}, {45, 132, 192, 36}, {45, 132, 192, 39}, {45, 132, 192, 44}, {45, 132, 192, 51}, {45, 132, 192, 74}, {45, 132, 192, 79}, {45, 132, 192, 92}}},
|
||||
{Region: "Saudi Arabia", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 6, 6}, {45, 131, 6, 7}, {45, 131, 6, 8}, {45, 131, 6, 15}, {45, 131, 6, 16}, {45, 131, 6, 18}, {45, 131, 6, 19}, {45, 131, 6, 22}, {45, 131, 6, 28}, {45, 131, 6, 29}}},
|
||||
{Region: "Saudi Arabia", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 6, 10}, {45, 131, 6, 11}, {45, 131, 6, 13}, {45, 131, 6, 14}, {45, 131, 6, 16}, {45, 131, 6, 17}, {45, 131, 6, 24}, {45, 131, 6, 25}, {45, 131, 6, 27}, {45, 131, 6, 28}}},
|
||||
{Region: "Serbia", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 193, 180}, {37, 120, 193, 181}, {37, 120, 193, 183}, {37, 120, 193, 184}, {37, 120, 193, 186}, {37, 120, 193, 187}, {141, 98, 103, 36}, {141, 98, 103, 38}, {141, 98, 103, 40}, {141, 98, 103, 46}}},
|
||||
{Region: "Serbia", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 193, 181}, {37, 120, 193, 182}, {37, 120, 193, 183}, {37, 120, 193, 187}, {37, 120, 193, 189}, {141, 98, 103, 35}, {141, 98, 103, 36}, {141, 98, 103, 38}, {141, 98, 103, 44}, {141, 98, 103, 45}}},
|
||||
{Region: "Singapore", Group: "Premium UDP Asia", IPs: []net.IP{{84, 17, 39, 162}, {84, 17, 39, 164}, {84, 17, 39, 166}, {84, 17, 39, 167}, {84, 17, 39, 169}, {84, 17, 39, 170}, {84, 17, 39, 174}, {84, 17, 39, 177}, {84, 17, 39, 179}, {84, 17, 39, 182}}},
|
||||
{Region: "Singapore", Group: "Premium TCP Asia", IPs: []net.IP{{84, 17, 39, 163}, {84, 17, 39, 164}, {84, 17, 39, 165}, {84, 17, 39, 168}, {84, 17, 39, 170}, {84, 17, 39, 173}, {84, 17, 39, 177}, {84, 17, 39, 179}, {84, 17, 39, 181}, {84, 17, 39, 184}}},
|
||||
{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: "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: "Slovenia", Group: "Premium UDP Europe", IPs: []net.IP{{146, 247, 25, 79}, {146, 247, 25, 80}, {146, 247, 25, 82}, {146, 247, 25, 83}, {146, 247, 25, 85}, {146, 247, 25, 86}, {146, 247, 25, 87}, {146, 247, 25, 88}, {146, 247, 25, 89}, {146, 247, 25, 90}}},
|
||||
{Region: "Slovenia", Group: "Premium TCP Europe", IPs: []net.IP{{146, 247, 25, 79}, {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}}},
|
||||
{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: "South Africa", Group: "Premium UDP Asia", IPs: []net.IP{{165, 73, 248, 214}, {165, 73, 248, 215}, {165, 73, 248, 216}, {165, 73, 248, 220}, {165, 73, 248, 221}, {165, 73, 248, 222}, {165, 73, 248, 229}, {165, 73, 248, 230}, {165, 73, 248, 232}, {165, 73, 248, 234}}},
|
||||
{Region: "South Africa", Group: "Premium TCP Asia", IPs: []net.IP{{165, 73, 248, 211}, {165, 73, 248, 212}, {165, 73, 248, 216}, {165, 73, 248, 219}, {165, 73, 248, 227}, {165, 73, 248, 230}, {165, 73, 248, 232}, {165, 73, 248, 233}, {165, 73, 248, 234}, {165, 73, 248, 235}}},
|
||||
{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: "Spain", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 142, 147}, {37, 120, 142, 155}, {37, 120, 142, 167}, {37, 120, 142, 169}, {37, 120, 142, 170}, {84, 17, 62, 131}, {84, 17, 62, 142}, {84, 17, 62, 145}, {84, 17, 62, 147}, {185, 93, 3, 113}}},
|
||||
{Region: "Spain", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 62, 133}, {84, 17, 62, 141}, {84, 17, 62, 142}, {185, 93, 3, 105}, {185, 93, 3, 109}, {185, 93, 3, 111}, {185, 93, 182, 133}, {185, 93, 182, 136}, {185, 93, 182, 140}, {185, 93, 182, 141}}},
|
||||
{Region: "Sri Lanka", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 136, 7}, {45, 132, 136, 9}, {45, 132, 136, 11}, {45, 132, 136, 13}, {45, 132, 136, 16}, {45, 132, 136, 17}, {45, 132, 136, 20}, {45, 132, 136, 23}, {45, 132, 136, 26}, {45, 132, 136, 29}}},
|
||||
{Region: "Sri Lanka", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 136, 7}, {45, 132, 136, 9}, {45, 132, 136, 10}, {45, 132, 136, 11}, {45, 132, 136, 13}, {45, 132, 136, 17}, {45, 132, 136, 22}, {45, 132, 136, 25}, {45, 132, 136, 26}, {45, 132, 136, 29}}},
|
||||
{Region: "Sweden", Group: "Premium TCP Europe", IPs: []net.IP{{46, 246, 65, 137}, {46, 246, 65, 139}, {46, 246, 65, 218}, {91, 132, 138, 60}, {188, 126, 64, 105}, {188, 126, 66, 10}, {188, 126, 66, 14}, {188, 126, 66, 29}, {188, 126, 73, 207}, {188, 126, 73, 209}}},
|
||||
{Region: "Sweden", Group: "Premium UDP Europe", IPs: []net.IP{{46, 246, 65, 131}, {46, 246, 65, 140}, {46, 246, 65, 170}, {46, 246, 65, 189}, {46, 246, 65, 200}, {46, 246, 65, 203}, {46, 246, 65, 212}, {91, 132, 138, 52}, {188, 126, 73, 199}, {188, 126, 73, 220}}},
|
||||
{Region: "Switzerland", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 52, 10}, {84, 17, 52, 14}, {84, 17, 52, 17}, {84, 17, 52, 21}, {185, 32, 222, 17}, {185, 32, 222, 18}, {185, 32, 222, 111}, {195, 225, 118, 45}, {195, 225, 118, 58}, {195, 225, 118, 61}}},
|
||||
{Region: "Switzerland", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 52, 5}, {84, 17, 52, 33}, {84, 17, 52, 45}, {84, 17, 52, 62}, {84, 17, 52, 69}, {84, 17, 52, 80}, {91, 132, 136, 171}, {185, 32, 222, 13}, {195, 225, 118, 44}, {195, 225, 118, 52}}},
|
||||
{Region: "Taiwan", Group: "Premium UDP Asia", IPs: []net.IP{{45, 133, 181, 100}, {45, 133, 181, 104}, {45, 133, 181, 106}, {45, 133, 181, 109}, {45, 133, 181, 110}, {45, 133, 181, 114}, {45, 133, 181, 117}, {45, 133, 181, 119}, {45, 133, 181, 124}, {45, 133, 181, 125}}},
|
||||
{Region: "Taiwan", Group: "Premium TCP Asia", IPs: []net.IP{{45, 133, 181, 99}, {45, 133, 181, 103}, {45, 133, 181, 104}, {45, 133, 181, 105}, {45, 133, 181, 107}, {45, 133, 181, 110}, {45, 133, 181, 112}, {45, 133, 181, 113}, {45, 133, 181, 116}, {45, 133, 181, 123}}},
|
||||
{Region: "Thailand", Group: "Premium TCP Asia", IPs: []net.IP{{119, 59, 98, 214}, {119, 59, 98, 239}, {119, 59, 98, 240}, {119, 59, 98, 244}, {119, 59, 121, 162}, {119, 59, 121, 168}, {119, 59, 121, 169}, {119, 59, 121, 170}, {119, 59, 121, 171}, {119, 59, 121, 173}}},
|
||||
{Region: "Thailand", Group: "Premium UDP Asia", IPs: []net.IP{{119, 59, 98, 238}, {119, 59, 98, 240}, {119, 59, 98, 244}, {119, 59, 98, 249}, {119, 59, 121, 166}, {119, 59, 121, 167}, {119, 59, 121, 168}, {119, 59, 121, 170}, {119, 59, 121, 172}, {119, 59, 121, 175}}},
|
||||
{Region: "Turkey", Group: "Premium UDP Europe", IPs: []net.IP{{188, 213, 34, 5}, {188, 213, 34, 9}, {188, 213, 34, 14}, {188, 213, 34, 30}, {188, 213, 34, 35}, {188, 213, 34, 39}, {188, 213, 34, 42}, {188, 213, 34, 45}, {188, 213, 34, 103}, {188, 213, 34, 110}}},
|
||||
{Region: "Turkey", Group: "Premium TCP Europe", IPs: []net.IP{{188, 213, 34, 10}, {188, 213, 34, 12}, {188, 213, 34, 25}, {188, 213, 34, 30}, {188, 213, 34, 36}, {188, 213, 34, 42}, {188, 213, 34, 102}, {188, 213, 34, 103}, {188, 213, 34, 104}, {188, 213, 34, 110}}},
|
||||
{Region: "Ukraine", Group: "Premium TCP Europe", IPs: []net.IP{{31, 28, 161, 20}, {31, 28, 163, 40}, {31, 28, 163, 51}, {62, 149, 7, 168}, {62, 149, 29, 35}, {62, 149, 29, 40}, {62, 149, 29, 41}, {62, 149, 29, 48}, {62, 149, 29, 52}, {62, 149, 29, 56}}},
|
||||
{Region: "Ukraine", Group: "Premium UDP Europe", IPs: []net.IP{{31, 28, 163, 35}, {31, 28, 163, 45}, {31, 28, 163, 51}, {31, 28, 163, 55}, {62, 149, 7, 167}, {62, 149, 29, 38}, {62, 149, 29, 40}, {62, 149, 29, 46}, {62, 149, 29, 47}, {62, 149, 29, 50}}},
|
||||
{Region: "United Arab Emirates", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 5, 6}, {45, 131, 5, 12}, {45, 131, 5, 13}, {45, 131, 5, 15}, {45, 131, 5, 17}, {45, 131, 5, 21}, {45, 131, 5, 22}, {45, 131, 5, 24}, {45, 131, 5, 28}, {45, 131, 5, 29}}},
|
||||
{Region: "United Arab Emirates", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 5, 6}, {45, 131, 5, 7}, {45, 131, 5, 10}, {45, 131, 5, 11}, {45, 131, 5, 12}, {45, 131, 5, 14}, {45, 131, 5, 17}, {45, 131, 5, 24}, {45, 131, 5, 26}, {45, 131, 5, 27}}},
|
||||
{Region: "United Kingdom", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 51, 18}, {84, 17, 51, 62}, {89, 238, 138, 245}, {89, 238, 167, 46}, {89, 238, 167, 56}, {95, 154, 200, 153}, {95, 154, 200, 155}, {95, 154, 200, 187}, {95, 154, 200, 188}, {141, 98, 100, 73}}},
|
||||
{Region: "United Kingdom", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 133, 165}, {84, 17, 51, 32}, {84, 17, 51, 106}, {84, 17, 51, 124}, {89, 238, 167, 45}, {95, 154, 200, 147}, {95, 154, 200, 165}, {95, 154, 200, 172}, {95, 154, 200, 179}, {141, 98, 100, 59}}},
|
||||
{Region: "United States", Group: "Premium TCP USA", IPs: []net.IP{{23, 105, 191, 33}, {23, 106, 83, 26}, {37, 120, 157, 131}, {45, 89, 173, 221}, {84, 17, 40, 70}, {89, 187, 182, 6}, {91, 132, 137, 86}, {173, 234, 158, 179}, {173, 234, 158, 184}, {185, 250, 220, 39}}},
|
||||
{Region: "United States", Group: "Premium UDP USA", IPs: []net.IP{{89, 187, 171, 143}, {108, 62, 235, 183}, {143, 244, 51, 169}, {156, 146, 37, 29}, {156, 146, 37, 106}, {156, 146, 37, 120}, {172, 255, 125, 138}, {173, 208, 44, 90}, {185, 242, 5, 120}, {185, 242, 5, 249}}},
|
||||
{Region: "Venezuela", Group: "Premium TCP Europe", IPs: []net.IP{{45, 133, 89, 8}, {45, 133, 89, 10}, {45, 133, 89, 12}, {45, 133, 89, 16}, {45, 133, 89, 17}, {45, 133, 89, 18}, {45, 133, 89, 20}, {45, 133, 89, 22}, {45, 133, 89, 27}, {45, 133, 89, 28}}},
|
||||
{Region: "Venezuela", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 89, 6}, {45, 133, 89, 7}, {45, 133, 89, 15}, {45, 133, 89, 16}, {45, 133, 89, 17}, {45, 133, 89, 20}, {45, 133, 89, 22}, {45, 133, 89, 26}, {45, 133, 89, 28}, {45, 133, 89, 29}}},
|
||||
{Region: "Vietnam", Group: "Premium UDP Asia", IPs: []net.IP{{45, 117, 79, 114}, {45, 117, 79, 118}, {45, 117, 79, 124}, {45, 117, 79, 125}, {103, 238, 214, 131}, {103, 238, 214, 132}, {103, 238, 214, 133}, {103, 238, 214, 134}, {103, 238, 214, 135}, {103, 238, 214, 137}}},
|
||||
{Region: "Vietnam", Group: "Premium TCP Asia", IPs: []net.IP{{45, 117, 79, 114}, {45, 117, 79, 116}, {45, 117, 79, 124}, {45, 117, 79, 125}, {103, 238, 214, 131}, {103, 238, 214, 132}, {103, 238, 214, 133}, {103, 238, 214, 135}, {103, 238, 214, 136}, {103, 238, 214, 140}}},
|
||||
}
|
||||
}
|
||||
142
internal/constants/mullvad.go
Normal file
142
internal/constants/mullvad.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sort"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
const (
|
||||
MullvadCertificate = "MIIGIzCCBAugAwIBAgIJAK6BqXN9GHI0MA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJTRTERMA8GA1UECAwIR290YWxhbmQxEzARBgNVBAcMCkdvdGhlbmJ1cmcxFDASBgNVBAoMC0FtYWdpY29tIEFCMRAwDgYDVQQLDAdNdWxsdmFkMRswGQYDVQQDDBJNdWxsdmFkIFJvb3QgQ0EgdjIxIzAhBgkqhkiG9w0BCQEWFHNlY3VyaXR5QG11bGx2YWQubmV0MB4XDTE4MTEwMjExMTYxMVoXDTI4MTAzMDExMTYxMVowgZ8xCzAJBgNVBAYTAlNFMREwDwYDVQQIDAhHb3RhbGFuZDETMBEGA1UEBwwKR290aGVuYnVyZzEUMBIGA1UECgwLQW1hZ2ljb20gQUIxEDAOBgNVBAsMB011bGx2YWQxGzAZBgNVBAMMEk11bGx2YWQgUm9vdCBDQSB2MjEjMCEGCSqGSIb3DQEJARYUc2VjdXJpdHlAbXVsbHZhZC5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCifDn75E/Zdx1qsy31rMEzuvbTXqZVZp4bjWbmcyyXqvnayRUHHoovG+lzc+HDL3HJV+kjxKpCMkEVWwjY159lJbQbm8kkYntBBREdzRRjjJpTb6haf/NXeOtQJ9aVlCc4dM66bEmyAoXkzXVZTQJ8h2FE55KVxHi5Sdy4XC5zm0wPa4DPDokNp1qm3A9Xicq3HsflLbMZRCAGuI+Jek6caHqiKjTHtujn6Gfxv2WsZ7SjerUAk+mvBo2sfKmB7octxG7yAOFFg7YsWL0AxddBWqgq5R/1WDJ9d1Cwun9WGRRQ1TLvzF1yABUerjjKrk89RCzYISwsKcgJPscaDqZgO6RIruY/xjuTtrnZSv+FXs+Woxf87P+QgQd76LC0MstTnys+AfTMuMPOLy9fMfEzs3LP0Nz6v5yjhX8ff7+3UUI3IcMxCvyxdTPClY5IvFdW7CCmmLNzakmx5GCItBWg/EIg1K1SG0jU9F8vlNZUqLKz42hWy/xB5C4QYQQ9ILdu4araPnrXnmd1D1QKVwKQ1DpWhNbpBDfE776/4xXD/tGM5O0TImp1NXul8wYsDi8g+e0pxNgY3Pahnj1yfG75Yw82spZanUH0QSNoMVMWnmV2hXGsWqypRq0pH8mPeLzeKa82gzsAZsouRD1k8wFlYA4z9HQFxqfcntTqXuwQcQIDAQABo2AwXjAdBgNVHQ4EFgQUfaEyaBpGNzsqttiSMETq+X/GJ0YwHwYDVR0jBBgwFoAUfaEyaBpGNzsqttiSMETq+X/GJ0YwCwYDVR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBADH5izxu4V8Javal8EA4DxZxIHUsWCg5cuopB28PsyJYpyKipsBoI8+RXqbtrLLue4WQfNPZHLXlKi+A3GTrLdlnenYzXVipPd+n3vRZyofaB3Jtb03nirVWGa8FG21Xy/f4rPqwcW54lxrnnh0SA0hwuZ+b2yAWESBXPxrzVQdTWCqoFI6/aRnN8RyZn0LqRYoW7WDtKpLmfyvshBmmu4PCYSh/SYiFHgR9fsWzVcxdySDsmX8wXowuFfp8V9sFhD4TsebAaplaICOuLUgj+Yin5QzgB0F9Ci3Zh6oWwl64SL/OxxQLpzMWzr0lrWsQrS3PgC4+6JC4IpTXX5eUqfSvHPtbRKK0yLnd9hYgvZUBvvZvUFR/3/fW+mpBHbZJBu9+/1uux46M4rJ2FeaJUf9PhYCPuUj63yu0Grn0DreVKK1SkD5V6qXN0TmoxYyguhfsIPCpI1VsdaSWuNjJ+a/HIlKIU8vKp5iN/+6ZTPAg9Q7s3Ji+vfx/AhFtQyTpIYNszVzNZyobvkiMUlK+eUKGlHVQp73y6MmGIlbBbyzpEoedNU4uFu57mw4fYGHqYZmYqFaiNQv4tVrGkg6p+Ypyu1zOfIHF7eqlAOu/SyRTvZkt9VtSVEOVH7nDIGdrCC9U/g1Lqk8Td00Oj8xesyKzsG214Xd8m7/7GmJ7nXe5"
|
||||
)
|
||||
|
||||
func MullvadCountryChoices() (choices []string) {
|
||||
uniqueChoices := map[string]struct{}{}
|
||||
for _, server := range MullvadServers() {
|
||||
uniqueChoices[server.Country] = struct{}{}
|
||||
}
|
||||
for choice := range uniqueChoices {
|
||||
choices = append(choices, choice)
|
||||
}
|
||||
sort.Slice(choices, func(i, j int) bool {
|
||||
return choices[i] < choices[j]
|
||||
})
|
||||
return choices
|
||||
}
|
||||
|
||||
func MullvadCityChoices() (choices []string) {
|
||||
uniqueChoices := map[string]struct{}{}
|
||||
for _, server := range MullvadServers() {
|
||||
uniqueChoices[server.City] = struct{}{}
|
||||
}
|
||||
for choice := range uniqueChoices {
|
||||
choices = append(choices, choice)
|
||||
}
|
||||
sort.Slice(choices, func(i, j int) bool {
|
||||
return choices[i] < choices[j]
|
||||
})
|
||||
return choices
|
||||
}
|
||||
|
||||
func MullvadISPChoices() (choices []string) {
|
||||
uniqueChoices := map[string]struct{}{}
|
||||
for _, server := range MullvadServers() {
|
||||
uniqueChoices[server.ISP] = struct{}{}
|
||||
}
|
||||
for choice := range uniqueChoices {
|
||||
choices = append(choices, choice)
|
||||
}
|
||||
sort.Slice(choices, func(i, j int) bool {
|
||||
return choices[i] < choices[j]
|
||||
})
|
||||
return choices
|
||||
}
|
||||
|
||||
//nolint:dupl,lll
|
||||
func MullvadServers() []models.MullvadServer {
|
||||
return []models.MullvadServer{
|
||||
{Country: "Albania", City: "Tirana", ISP: "iRegister", Owned: false, IPs: []net.IP{{31, 171, 154, 210}}, IPsV6: []net.IP{{0x2a, 0x4, 0x27, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Australia", City: "Adelaide", ISP: "Intergrid", Owned: false, IPs: []net.IP{{116, 206, 231, 58}}, IPsV6: []net.IP{{0x24, 0x7, 0xa0, 0x80, 0x50, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Australia", City: "Brisbane", ISP: "Intergrid", Owned: false, IPs: []net.IP{{43, 245, 160, 162}}, IPsV6: []net.IP{{0x24, 0x7, 0xa0, 0x80, 0x20, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Australia", City: "Canberra", ISP: "Intergrid", Owned: false, IPs: []net.IP{{116, 206, 229, 98}}, IPsV6: []net.IP{{0x24, 0x7, 0xa0, 0x80, 0x40, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Australia", City: "Melbourne", ISP: "Intergrid", Owned: false, IPs: []net.IP{{116, 206, 228, 202}, {116, 206, 228, 242}, {116, 206, 230, 98}}, IPsV6: []net.IP{{0x24, 0x7, 0xa0, 0x80, 0x30, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x24, 0x7, 0xa0, 0x80, 0x30, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x24, 0x7, 0xa0, 0x80, 0x30, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}}},
|
||||
{Country: "Australia", City: "Perth", ISP: "Intergrid", Owned: false, IPs: []net.IP{{103, 77, 235, 66}}, IPsV6: []net.IP{{0x24, 0x0, 0xfa, 0x80, 0x0, 0x5, 0x0, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Australia", City: "Sydney", ISP: "Intergrid", Owned: false, IPs: []net.IP{{43, 245, 162, 130}, {103, 77, 232, 130}, {103, 77, 232, 146}}, IPsV6: []net.IP{{0x24, 0x0, 0xfa, 0x80, 0x0, 0x1, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x24, 0x0, 0xfa, 0x80, 0x0, 0x1, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x24, 0x0, 0xfa, 0x80, 0x0, 0x1, 0x0, 0x15, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
|
||||
{Country: "Australia", City: "Sydney", ISP: "M247", Owned: false, IPs: []net.IP{{89, 44, 10, 18}, {89, 44, 10, 34}, {89, 44, 10, 50}, {89, 44, 10, 194}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x84, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x84, 0x0, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x84, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x84, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}}},
|
||||
{Country: "Austria", City: "Vienna", ISP: "M247", Owned: false, IPs: []net.IP{{5, 253, 207, 34}, {86, 107, 21, 210}, {86, 107, 21, 226}, {86, 107, 21, 242}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x29, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x29, 0x0, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x29, 0x0, 0x5b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x29, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x4f}}},
|
||||
{Country: "Belgium", City: "Brussels", ISP: "M247", Owned: false, IPs: []net.IP{{37, 120, 143, 138}, {37, 120, 218, 138}, {37, 120, 218, 146}, {91, 207, 57, 50}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x27, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x27, 0x0, 0x32, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x27, 0x0, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x27, 0x0, 0x55, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}}},
|
||||
{Country: "Brazil", City: "Sao Paulo", ISP: "Heficed", Owned: false, IPs: []net.IP{{191, 101, 62, 178}}, IPsV6: []net.IP{{0x28, 0x3, 0x0, 0x80, 0x80, 0x3, 0x80, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
|
||||
{Country: "Brazil", City: "Sao Paulo", ISP: "Qnax", Owned: false, IPs: []net.IP{{177, 67, 80, 186}}, IPsV6: []net.IP{{0x28, 0x4, 0x53, 0x64, 0x21, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Bulgaria", City: "Sofia", ISP: "M247", Owned: false, IPs: []net.IP{{37, 120, 152, 114}, {37, 120, 152, 146}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x30, 0x0, 0x19, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x30, 0x0, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Canada", City: "Montreal", ISP: "M247", Owned: false, IPs: []net.IP{{89, 36, 78, 18}, {89, 36, 78, 34}, {89, 36, 78, 50}, {89, 36, 78, 66}, {89, 36, 78, 82}, {89, 36, 78, 98}, {89, 36, 78, 114}, {89, 36, 78, 130}}, IPsV6: []net.IP{{0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x0, 0xb6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x0, 0xb7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x0, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x0, 0xba, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x0, 0xc8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x0, 0xc9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x1, 0x61, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}}},
|
||||
{Country: "Canada", City: "Toronto", ISP: "Amanah", Owned: false, IPs: []net.IP{{162, 219, 176, 250}}, IPsV6: []net.IP{{0x26, 0x6, 0x60, 0x80, 0x10, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
|
||||
{Country: "Canada", City: "Toronto", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 132, 34}, {198, 54, 132, 50}, {198, 54, 132, 66}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x60, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x7, 0x90, 0x0, 0x60, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x60, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}}},
|
||||
{Country: "Canada", City: "Vancouver", ISP: "100TB", Owned: false, IPs: []net.IP{{172, 83, 40, 34}, {172, 83, 40, 38}}, IPsV6: []net.IP{{0x26, 0x7, 0xf7, 0xa0, 0x0, 0xd, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0xd, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Canada", City: "Vancouver", ISP: "Esecuredata", Owned: false, IPs: []net.IP{{71, 19, 248, 240}, {71, 19, 249, 81}}, IPsV6: []net.IP{{0x26, 0x5, 0x0, 0x80, 0x0, 0x18, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4}, {0x26, 0x5, 0x0, 0x80, 0x0, 0x19, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5}}},
|
||||
{Country: "Czech Republic", City: "Prague", ISP: "M247", Owned: false, IPs: []net.IP{{185, 156, 174, 146}, {185, 156, 174, 170}, {185, 216, 35, 242}, {217, 138, 199, 74}, {217, 138, 199, 82}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x33, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x33, 0x0, 0xb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x33, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x33, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x33, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}}},
|
||||
{Country: "Denmark", City: "Copenhagen", ISP: "31173", Owned: true, IPs: []net.IP{{45, 129, 56, 81}, {141, 98, 254, 71}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x8, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x8, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
|
||||
{Country: "Denmark", City: "Copenhagen", ISP: "Asergo", Owned: false, IPs: []net.IP{{82, 103, 140, 213}}, IPsV6: []net.IP{{0x2a, 0x0, 0x90, 0x80, 0x0, 0x1, 0x9, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Denmark", City: "Copenhagen", ISP: "Blix", Owned: false, IPs: []net.IP{{134, 90, 149, 138}}, IPsV6: []net.IP{{0x2a, 0x2, 0xed, 0x1, 0x41, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Denmark", City: "Copenhagen", ISP: "M247", Owned: false, IPs: []net.IP{{89, 45, 7, 130}, {89, 45, 7, 146}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x37, 0x0, 0x5b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x37, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x4f}}},
|
||||
{Country: "Finland", City: "Helsinki", ISP: "Creanova", Owned: true, IPs: []net.IP{{185, 204, 1, 171}, {185, 204, 1, 172}, {185, 204, 1, 173}, {185, 204, 1, 174}, {185, 204, 1, 175}, {185, 204, 1, 176}, {185, 212, 149, 201}}, IPsV6: []net.IP{{0x2a, 0xc, 0xf0, 0x40, 0x0, 0x0, 0x27, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0xc, 0xf0, 0x40, 0x0, 0x0, 0x27, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xc, 0xf0, 0x40, 0x0, 0x0, 0x27, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0xc, 0xf0, 0x40, 0x0, 0x0, 0x27, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0xc, 0xf0, 0x40, 0x0, 0x0, 0x27, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0xc, 0xf0, 0x40, 0x0, 0x0, 0x27, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0xc, 0xf0, 0x40, 0x0, 0x0, 0x27, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}}},
|
||||
{Country: "France", City: "Paris", ISP: "31173", Owned: true, IPs: []net.IP{{193, 32, 126, 81}, {193, 32, 126, 82}, {193, 32, 126, 83}, {193, 32, 126, 84}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x9, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x9, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x9, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x9, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}}},
|
||||
{Country: "France", City: "Paris", ISP: "M247", Owned: false, IPs: []net.IP{{89, 44, 9, 19}, {89, 44, 9, 35}, {194, 110, 113, 3}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x25, 0x0, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x25, 0x0, 0xd1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x25, 0x0, 0xd2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}}},
|
||||
{Country: "Germany", City: "Frankfurt", ISP: "31173", Owned: true, IPs: []net.IP{{185, 213, 155, 131}, {185, 213, 155, 132}, {185, 213, 155, 133}, {185, 213, 155, 134}, {185, 213, 155, 135}, {185, 213, 155, 136}, {185, 213, 155, 137}, {185, 213, 155, 138}, {185, 213, 155, 139}, {185, 213, 155, 140}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x6, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x6, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x6, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x6, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x6, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x6, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x6, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x6, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x6, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x6, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}}},
|
||||
{Country: "Germany", City: "Frankfurt", ISP: "M247", Owned: false, IPs: []net.IP{{193, 27, 14, 2}, {193, 27, 14, 18}, {193, 27, 14, 34}, {193, 27, 14, 50}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x20, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x20, 0x3, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x20, 0x3, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x20, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x4f}}},
|
||||
{Country: "Greece", City: "Athens", ISP: "aweb", Owned: false, IPs: []net.IP{{185, 226, 67, 168}}, IPsV6: []net.IP{{0x2a, 0xc, 0x5e, 0x0, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Hong Kong", City: "Hong Kong", ISP: "Leaseweb", Owned: false, IPs: []net.IP{{209, 58, 184, 146}, {209, 58, 185, 53}, {209, 58, 185, 186}}, IPsV6: []net.IP{{0x20, 0x1, 0xd, 0xf1, 0x8, 0x1, 0xa0, 0x3, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xd, 0xf1, 0x8, 0x1, 0xa0, 0x3, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xd, 0xf1, 0x8, 0x1, 0xa0, 0x5, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Hong Kong", City: "Hong Kong", ISP: "M247", Owned: false, IPs: []net.IP{{89, 45, 6, 50}, {89, 45, 6, 66}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x92, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x92, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Hungary", City: "Budapest", ISP: "M247", Owned: false, IPs: []net.IP{{86, 106, 74, 34}, {86, 106, 74, 50}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x26, 0x0, 0xab, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x26, 0x0, 0xac, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}}},
|
||||
{Country: "Ireland", City: "Dublin", ISP: "M247", Owned: false, IPs: []net.IP{{217, 138, 222, 82}, {217, 138, 222, 90}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x88, 0x0, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x88, 0x0, 0x5b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
|
||||
{Country: "Israel", City: "Tel Aviv", ISP: "HQServ", Owned: false, IPs: []net.IP{{185, 191, 207, 210}}, IPsV6: []net.IP{{0x2a, 0xa, 0x1d, 0xc4, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Italy", City: "Milan", ISP: "M247", Owned: false, IPs: []net.IP{{89, 40, 182, 146}, {89, 40, 182, 210}, {192, 145, 127, 98}, {192, 145, 127, 114}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x24, 0x0, 0x76, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x24, 0x0, 0x77, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x24, 0x0, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x24, 0x0, 0x79, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}}},
|
||||
{Country: "Japan", City: "Tokyo", ISP: "M247", Owned: false, IPs: []net.IP{{217, 138, 252, 50}, {217, 138, 252, 162}, {217, 138, 252, 178}, {217, 138, 252, 194}, {217, 138, 252, 210}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x40, 0x0, 0xb1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x40, 0x0, 0xb2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x40, 0x0, 0xb3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x40, 0x0, 0xb4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x40, 0x0, 0xb5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}}},
|
||||
{Country: "Latvia", City: "Riga", ISP: "Makonix", Owned: false, IPs: []net.IP{{31, 170, 22, 2}}, IPsV6: []net.IP{{0x2a, 0x0, 0xc, 0x68, 0x0, 0x0, 0xcb, 0xcf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Luxembourg", City: "Luxembourg", ISP: "Evoluso", Owned: false, IPs: []net.IP{{92, 223, 89, 182}}, IPsV6: []net.IP{{0x2a, 0x3, 0x90, 0xc0, 0x0, 0x83, 0x29, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
|
||||
{Country: "Moldova", City: "Chisinau", ISP: "Trabia", Owned: false, IPs: []net.IP{{178, 175, 142, 194}}, IPsV6: []net.IP{{0x2a, 0x0, 0x1d, 0xc0, 0x29, 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Netherlands", City: "Amsterdam", ISP: "31173", Owned: true, IPs: []net.IP{{185, 65, 134, 131}, {185, 65, 134, 132}, {185, 65, 134, 133}, {185, 65, 134, 134}, {185, 65, 134, 135}, {185, 65, 134, 136}, {185, 65, 134, 139}, {185, 65, 134, 140}, {185, 65, 134, 141}, {185, 65, 134, 142}, {185, 65, 134, 143}, {185, 65, 134, 144}, {185, 65, 134, 145}, {185, 65, 134, 146}, {185, 65, 134, 147}, {185, 65, 134, 148}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x8f}}},
|
||||
{Country: "New Zealand", City: "Auckland", ISP: "Intergrid", Owned: false, IPs: []net.IP{{103, 231, 91, 114}}, IPsV6: []net.IP{{0x24, 0x0, 0xfa, 0x80, 0x0, 0x4, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Norway", City: "Oslo", ISP: "Blix", Owned: true, IPs: []net.IP{{91, 90, 44, 11}, {91, 90, 44, 12}, {91, 90, 44, 13}, {91, 90, 44, 14}, {91, 90, 44, 15}, {91, 90, 44, 16}, {91, 90, 44, 17}, {91, 90, 44, 18}}, IPsV6: []net.IP{{0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}}},
|
||||
{Country: "Poland", City: "Warsaw", ISP: "M247", Owned: false, IPs: []net.IP{{37, 120, 156, 162}, {37, 120, 211, 186}, {37, 120, 211, 194}, {37, 120, 211, 202}, {185, 244, 214, 210}, {185, 244, 214, 215}}, IPsV6: []net.IP{{0x2a, 0xd, 0x56, 0x0, 0x0, 0x13, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x13, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x13, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x13, 0x0, 0x3a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x13, 0x0, 0x3b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x13, 0xb, 0xb1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Romania", City: "Bucharest", ISP: "M247", Owned: false, IPs: []net.IP{{185, 163, 110, 66}, {185, 163, 110, 82}, {185, 163, 110, 98}, {185, 163, 110, 114}}, IPsV6: []net.IP{{0x2a, 0x4, 0x9d, 0xc0, 0x0, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1f}, {0x2a, 0x4, 0x9d, 0xc0, 0x0, 0x0, 0x0, 0x91, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x2f}, {0x2a, 0x4, 0x9d, 0xc0, 0x0, 0x0, 0x0, 0x92, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x3f}, {0x2a, 0x4, 0x9d, 0xc0, 0x0, 0x0, 0x0, 0x93, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x4f}}},
|
||||
{Country: "Serbia", City: "Belgrade", ISP: "M247", Owned: false, IPs: []net.IP{{89, 38, 224, 98}, {89, 38, 224, 114}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x7d, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x7d, 0x0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}}},
|
||||
{Country: "Serbia", City: "Nis", ISP: "ninet", Owned: false, IPs: []net.IP{{176, 104, 107, 118}}, IPsV6: []net.IP{{0x2a, 0x6, 0x1, 0x85, 0x0, 0x1, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Singapore", City: "Singapore", ISP: "M247", Owned: false, IPs: []net.IP{{89, 38, 225, 34}, {94, 198, 43, 2}, {94, 198, 43, 18}}, IPsV6: []net.IP{{0x2a, 0xa, 0xb6, 0x40, 0x0, 0x1, 0x0, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0xa, 0xb6, 0x40, 0x0, 0x1, 0x0, 0x55, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xa, 0xb6, 0x40, 0x0, 0x1, 0x0, 0x56, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
|
||||
{Country: "Spain", City: "Madrid", ISP: "M247", Owned: false, IPs: []net.IP{{45, 152, 183, 26}, {45, 152, 183, 42}, {89, 238, 178, 34}, {89, 238, 178, 74}, {195, 206, 107, 146}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x23, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x23, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf2}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x23, 0x0, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x23, 0x0, 0x58, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x23, 0x0, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}}},
|
||||
{Country: "Sweden", City: "Gothenburg", ISP: "31173", Owned: true, IPs: []net.IP{{185, 213, 154, 131}, {185, 213, 154, 132}, {185, 213, 154, 133}, {185, 213, 154, 134}, {185, 213, 154, 135}, {185, 213, 154, 136}, {185, 213, 154, 137}, {185, 213, 154, 138}, {185, 213, 154, 139}, {185, 213, 154, 140}, {185, 213, 154, 141}, {185, 213, 154, 142}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}}},
|
||||
{Country: "Sweden", City: "Helsingborg", ISP: "31173", Owned: true, IPs: []net.IP{{185, 213, 152, 131}, {185, 213, 152, 132}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x2, 0xf7, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x2, 0xf7, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
|
||||
{Country: "Sweden", City: "Malmö", ISP: "31173", Owned: true, IPs: []net.IP{{45, 83, 220, 87}, {45, 83, 220, 88}, {45, 83, 220, 89}, {45, 83, 220, 90}, {45, 83, 220, 91}, {45, 83, 220, 92}, {45, 83, 220, 93}, {141, 98, 255, 83}, {141, 98, 255, 84}, {141, 98, 255, 85}, {141, 98, 255, 86}, {141, 98, 255, 87}, {141, 98, 255, 88}, {141, 98, 255, 89}, {141, 98, 255, 90}, {141, 98, 255, 91}, {141, 98, 255, 92}, {141, 98, 255, 93}, {141, 98, 255, 94}, {193, 138, 218, 131}, {193, 138, 218, 132}, {193, 138, 218, 133}, {193, 138, 218, 134}, {193, 138, 218, 135}, {193, 138, 218, 136}, {193, 138, 218, 137}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}}},
|
||||
{Country: "Sweden", City: "Stockholm", ISP: "31173", Owned: true, IPs: []net.IP{{185, 65, 135, 136}, {185, 65, 135, 137}, {185, 65, 135, 138}, {185, 65, 135, 139}, {185, 65, 135, 140}, {185, 65, 135, 141}, {185, 65, 135, 142}, {185, 65, 135, 143}, {185, 65, 135, 144}, {185, 65, 135, 145}, {185, 65, 135, 146}, {185, 65, 135, 147}, {185, 65, 135, 148}, {185, 65, 135, 149}, {185, 65, 135, 150}, {185, 65, 135, 151}, {185, 65, 135, 152}, {185, 65, 135, 153}, {185, 65, 135, 154}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4e}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x4f}}},
|
||||
{Country: "Switzerland", City: "Zurich", ISP: "31173", Owned: true, IPs: []net.IP{{193, 32, 127, 81}, {193, 32, 127, 82}, {193, 32, 127, 83}, {193, 32, 127, 84}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0xa, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0xa, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0xa, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0xa, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}}},
|
||||
{Country: "Switzerland", City: "Zurich", ISP: "M247", Owned: false, IPs: []net.IP{{91, 193, 4, 2}, {91, 193, 4, 18}, {91, 193, 4, 34}, {91, 193, 4, 50}, {91, 193, 4, 66}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x28, 0x0, 0x84, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x28, 0x0, 0x85, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x28, 0x0, 0x86, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x28, 0x0, 0x87, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x28, 0x0, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "Switzerland", City: "Zurich", ISP: "PrivateLayer", Owned: false, IPs: []net.IP{{179, 43, 128, 170}}, IPsV6: []net.IP{{0x2a, 0x2, 0x29, 0xb8, 0xdc, 0x1, 0x5, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "UK", City: "London", ISP: "31173", Owned: true, IPs: []net.IP{{141, 98, 252, 131}, {141, 98, 252, 132}, {141, 98, 252, 133}, {141, 98, 252, 138}, {141, 98, 252, 139}, {141, 98, 252, 140}, {185, 195, 232, 84}, {185, 195, 232, 85}, {185, 195, 232, 86}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}}},
|
||||
{Country: "UK", City: "London", ISP: "M247", Owned: false, IPs: []net.IP{{45, 87, 215, 50}, {185, 200, 118, 178}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x31, 0x2, 0x35, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x31, 0x2, 0x36, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
|
||||
{Country: "UK", City: "Manchester", ISP: "M247", Owned: false, IPs: []net.IP{{37, 120, 159, 164}, {89, 238, 132, 36}, {194, 37, 96, 180}, {217, 151, 98, 68}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x21, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x21, 0x0, 0x1b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x21, 0x0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x21, 0x0, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "USA", City: "Atlanta GA", ISP: "100TB", Owned: false, IPs: []net.IP{{66, 115, 180, 227}, {66, 115, 180, 228}, {66, 115, 180, 229}, {66, 115, 180, 230}, {107, 152, 108, 62}}, IPsV6: []net.IP{{0x26, 0x7, 0xf7, 0xa0, 0x0, 0x1, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0x1, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0x1, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0x1, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0x6, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}}},
|
||||
{Country: "USA", City: "Atlanta GA", ISP: "Quadranet", Owned: false, IPs: []net.IP{{104, 129, 24, 242}}, IPsV6: []net.IP{{0x26, 0x7, 0xfc, 0xd0, 0xaa, 0x80, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
|
||||
{Country: "USA", City: "Chicago IL", ISP: "Quadranet", Owned: false, IPs: []net.IP{{104, 129, 31, 26}}, IPsV6: []net.IP{{0x26, 0x7, 0xfc, 0xd0, 0xbb, 0x80, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "USA", City: "Chicago IL", ISP: "Tzulo", Owned: false, IPs: []net.IP{{68, 235, 43, 10}, {68, 235, 43, 18}, {68, 235, 43, 26}, {68, 235, 43, 34}, {68, 235, 43, 42}, {68, 235, 43, 50}, {68, 235, 43, 58}, {68, 235, 43, 66}, {68, 235, 43, 74}, {68, 235, 43, 122}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x51, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x52, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x55, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x56, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x57, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x58, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "USA", City: "Dallas TX", ISP: "100TB", Owned: false, IPs: []net.IP{{174, 127, 113, 3}, {174, 127, 113, 4}, {174, 127, 113, 5}, {174, 127, 113, 6}, {174, 127, 113, 7}}, IPsV6: []net.IP{{0x26, 0x6, 0x2e, 0x0, 0x80, 0x7, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x6, 0x2e, 0x0, 0x80, 0x7, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x6, 0x2e, 0x0, 0x80, 0x7, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x26, 0x6, 0x2e, 0x0, 0x80, 0x7, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x26, 0x6, 0x2e, 0x0, 0x80, 0x7, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}}},
|
||||
{Country: "USA", City: "Dallas TX", ISP: "M247", Owned: false, IPs: []net.IP{{193, 27, 13, 34}, {193, 27, 13, 50}, {193, 27, 13, 66}, {193, 27, 13, 82}, {193, 27, 13, 178}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x9a, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x9a, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x9a, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x9a, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x9a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "USA", City: "Dallas TX", ISP: "Quadranet", Owned: false, IPs: []net.IP{{96, 44, 145, 18}, {96, 44, 147, 130}}, IPsV6: []net.IP{{0x26, 0x7, 0xfc, 0xd0, 0xda, 0x80, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8}, {0x26, 0x7, 0xfc, 0xd0, 0xda, 0x80, 0x18, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9}}},
|
||||
{Country: "USA", City: "Denver CO", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 128, 66}, {198, 54, 128, 74}, {198, 54, 128, 106}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x20, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x20, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x7, 0x90, 0x0, 0x20, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}}},
|
||||
{Country: "USA", City: "Los Angeles CA", ISP: "100TB", Owned: false, IPs: []net.IP{{104, 200, 152, 66}, {107, 181, 168, 130}}, IPsV6: []net.IP{{0x26, 0x7, 0xf7, 0xa0, 0x0, 0x3, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0x3, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}}},
|
||||
{Country: "USA", City: "Los Angeles CA", ISP: "M247", Owned: false, IPs: []net.IP{{89, 46, 114, 15}, {89, 46, 114, 28}, {89, 46, 114, 41}, {89, 46, 114, 54}, {89, 46, 114, 67}, {89, 46, 114, 80}, {89, 46, 114, 93}, {89, 46, 114, 106}, {89, 46, 114, 119}, {89, 46, 114, 132}, {89, 46, 114, 145}, {89, 46, 114, 158}, {89, 46, 114, 171}, {89, 46, 114, 184}, {89, 46, 114, 197}, {89, 46, 114, 210}, {89, 46, 114, 223}}, IPsV6: []net.IP{{0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7f}}},
|
||||
{Country: "USA", City: "Los Angeles CA", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 129, 74}, {198, 54, 129, 82}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x30, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x30, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "USA", City: "Miami FL", ISP: "M247", Owned: false, IPs: []net.IP{{94, 198, 42, 50}, {94, 198, 42, 66}, {94, 198, 42, 82}, {94, 198, 42, 98}, {193, 27, 12, 2}, {193, 27, 12, 18}}, IPsV6: []net.IP{{0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0x0, 0x33, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0x0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0x0, 0x35, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0x0, 0x36, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0xa, 0xd6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0xa, 0xd7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
{Country: "USA", City: "New York NY", ISP: "100TB", Owned: false, IPs: []net.IP{{107, 182, 226, 206}, {107, 182, 226, 218}}, IPsV6: []net.IP{{0x26, 0x6, 0x2e, 0x0, 0x80, 0x3, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x26, 0x6, 0x2e, 0x0, 0x80, 0x3, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}}},
|
||||
{Country: "USA", City: "New York NY", ISP: "M247", Owned: false, IPs: []net.IP{{86, 106, 121, 15}, {86, 106, 121, 28}, {86, 106, 121, 41}, {86, 106, 121, 54}, {86, 106, 121, 67}, {86, 106, 121, 80}, {86, 106, 121, 93}, {86, 106, 121, 106}, {86, 106, 121, 119}, {86, 106, 121, 132}, {89, 46, 62, 15}, {89, 46, 62, 28}, {89, 46, 62, 41}, {89, 46, 62, 54}, {89, 46, 62, 67}, {89, 46, 62, 80}, {89, 46, 62, 93}, {89, 46, 62, 106}, {89, 46, 62, 119}, {89, 46, 62, 132}}, IPsV6: []net.IP{{0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x71, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x72, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x73, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x75, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x76, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x77, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x79, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x7a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x99, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0xa1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x8f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0xa2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x9f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0xa3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xf}}},
|
||||
{Country: "USA", City: "Phoenix AZ", ISP: "100TB", Owned: false, IPs: []net.IP{{107, 152, 99, 86}}, IPsV6: []net.IP{{0x26, 0x7, 0xf7, 0xa0, 0x0, 0x5, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}}},
|
||||
{Country: "USA", City: "Phoenix AZ", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 133, 34}, {198, 54, 133, 50}, {198, 54, 133, 66}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x70, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1f}, {0x26, 0x7, 0x90, 0x0, 0x70, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x70, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x3f}}},
|
||||
{Country: "USA", City: "Raleigh NC", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 130, 34}, {198, 54, 130, 50}, {198, 54, 130, 66}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x40, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1f}, {0x26, 0x7, 0x90, 0x0, 0x40, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x40, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x3f}}},
|
||||
{Country: "USA", City: "Salt Lake City UT", ISP: "100TB", Owned: false, IPs: []net.IP{{69, 4, 234, 132}, {69, 4, 234, 133}, {69, 4, 234, 134}, {69, 4, 234, 135}, {69, 4, 234, 136}, {69, 4, 234, 137}}, IPsV6: []net.IP{{0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}}},
|
||||
{Country: "USA", City: "San Jose CA", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 134, 34}, {198, 54, 134, 50}, {198, 54, 134, 66}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x80, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x7, 0x90, 0x0, 0x80, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x80, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}}},
|
||||
{Country: "USA", City: "Seattle WA", ISP: "100TB", Owned: false, IPs: []net.IP{{104, 200, 129, 42}, {104, 200, 129, 110}, {104, 200, 129, 150}}, IPsV6: []net.IP{{0x26, 0x7, 0xf7, 0xa0, 0x0, 0xc, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0xc, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0xc, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
|
||||
{Country: "USA", City: "Seattle WA", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 131, 34}, {198, 54, 131, 50}, {198, 54, 131, 66}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x50, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1f}, {0x26, 0x7, 0x90, 0x0, 0x50, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x50, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x3f}}},
|
||||
{Country: "USA", City: "Secaucus NJ", ISP: "Quadranet", Owned: false, IPs: []net.IP{{23, 226, 131, 130}, {23, 226, 131, 154}}, IPsV6: []net.IP{{0x26, 0x7, 0xfc, 0xd0, 0xcc, 0xc0, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x7, 0xfc, 0xd0, 0xcc, 0xc0, 0x1d, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
|
||||
{Country: "United Arab Emirates", City: "Dubai", ISP: "M247", Owned: false, IPs: []net.IP{{45, 9, 249, 34}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||
}
|
||||
}
|
||||
5367
internal/constants/nordvpn.go
Normal file
5367
internal/constants/nordvpn.go
Normal file
File diff suppressed because it is too large
Load Diff
10
internal/constants/openvpn.go
Normal file
10
internal/constants/openvpn.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
const (
|
||||
TUN models.VPNDevice = "tun0"
|
||||
TAP models.VPNDevice = "tap0"
|
||||
)
|
||||
34
internal/constants/paths.go
Normal file
34
internal/constants/paths.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
const (
|
||||
// UnboundConf is the file path to the Unbound configuration file.
|
||||
UnboundConf models.Filepath = "/etc/unbound/unbound.conf"
|
||||
// ResolvConf is the file path to the system resolv.conf file.
|
||||
ResolvConf models.Filepath = "/etc/resolv.conf"
|
||||
// CACertificates is the file path to the CA certificates file.
|
||||
CACertificates models.Filepath = "/etc/ssl/certs/ca-certificates.crt"
|
||||
// OpenVPNAuthConf is the file path to the OpenVPN auth file.
|
||||
OpenVPNAuthConf models.Filepath = "/etc/openvpn/auth.conf"
|
||||
// OpenVPNConf is the file path to the OpenVPN client configuration file.
|
||||
OpenVPNConf models.Filepath = "/etc/openvpn/target.ovpn"
|
||||
// PIAPortForward is the file path to the port forwarding JSON information for PIA servers.
|
||||
PIAPortForward models.Filepath = "/gluetun/piaportforward.json"
|
||||
// TunnelDevice is the file path to tun device.
|
||||
TunnelDevice models.Filepath = "/dev/net/tun"
|
||||
// NetRoute is the path to the file containing information on the network route.
|
||||
NetRoute models.Filepath = "/proc/net/route"
|
||||
// RootHints is the filepath to the root.hints file used by Unbound.
|
||||
RootHints models.Filepath = "/etc/unbound/root.hints"
|
||||
// RootKey is the filepath to the root.key file used by Unbound.
|
||||
RootKey models.Filepath = "/etc/unbound/root.key"
|
||||
// Client key filepath, used by Cyberghost.
|
||||
ClientKey models.Filepath = "/gluetun/client.key"
|
||||
// Client certificate filepath, used by Cyberghost.
|
||||
ClientCertificate models.Filepath = "/gluetun/client.crt"
|
||||
// Servers information filepath.
|
||||
ServersData = "/gluetun/servers.json"
|
||||
)
|
||||
131
internal/constants/pia.go
Normal file
131
internal/constants/pia.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
const (
|
||||
PIAEncryptionPresetNormal = "normal"
|
||||
PIAEncryptionPresetStrong = "strong"
|
||||
PiaX509CRLNormal = "MIICWDCCAUAwDQYJKoZIhvcNAQENBQAwgegxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRlaW50ZXJuZXRhY2Nlc3MuY29tFw0xNjA3MDgxOTAwNDZaFw0zNjA3MDMxOTAwNDZaMCYwEQIBARcMMTYwNzA4MTkwMDQ2MBECAQYXDDE2MDcwODE5MDA0NjANBgkqhkiG9w0BAQ0FAAOCAQEAQZo9X97ci8EcPYu/uK2HB152OZbeZCINmYyluLDOdcSvg6B5jI+ffKN3laDvczsG6CxmY3jNyc79XVpEYUnq4rT3FfveW1+Ralf+Vf38HdpwB8EWB4hZlQ205+21CALLvZvR8HcPxC9KEnev1mU46wkTiov0EKc+EdRxkj5yMgv0V2Reze7AP+NQ9ykvDScH4eYCsmufNpIjBLhpLE2cuZZXBLcPhuRzVoU3l7A9lvzG9mjA5YijHJGHNjlWFqyrn1CfYS6koa4TGEPngBoAziWRbDGdhEgJABHrpoaFYaL61zqyMR6jC0K2ps9qyZAN74LEBedEfK7tBOzWMwr58A=="
|
||||
PiaX509CRLStrong = "MIIDWDCCAUAwDQYJKoZIhvcNAQENBQAwgegxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRlaW50ZXJuZXRhY2Nlc3MuY29tFw0xNjA3MDgxOTAwNDZaFw0zNjA3MDMxOTAwNDZaMCYwEQIBARcMMTYwNzA4MTkwMDQ2MBECAQYXDDE2MDcwODE5MDA0NjANBgkqhkiG9w0BAQ0FAAOCAgEAppFfEpGsasjB1QgJcosGpzbf2kfRhM84o2TlqY1ua+Gi5TMdKydA3LJcNTjlI9a0TYAJfeRX5IkpoglSUuHuJgXhP3nEvX10mjXDpcu/YvM8TdE5JV2+EGqZ80kFtBeOq94WcpiVKFTR4fO+VkOK9zwspFfb1cNs9rHvgJ1QMkRUF8PpLN6AkntHY0+6DnigtSaKqldqjKTDTv2OeH3nPoh80SGrt0oCOmYKfWTJGpggMGKvIdvU3vH9+EuILZKKIskt+1dwdfA5Bkz1GLmiQG7+9ZZBQUjBG9Dos4hfX/rwJ3eU8oUIm4WoTz9rb71SOEuUUjP5NPy9HNx2vx+cVvLsTF4ZDZaUztW9o9JmIURDtbeyqxuHN3prlPWB6aj73IIm2dsDQvs3XXwRIxs8NwLbJ6CyEuvEOVCskdM8rdADWx1J0lRNlOJ0Z8ieLLEmYAA834VN1SboB6wJIAPxQU3rcBhXqO9y8aa2oRMg8NxZ5gr+PnKVMqag1x0IxbIgLxtkXQvxXxQHEMSODzvcOfK/nBRBsqTj30P+R87sU8titOoxNeRnBDRNhdEy/QGAqGh62ShPpQUCJdnKRiRTjnil9hMQHevoSuFKeEMO30FQL7BZyo37GFU+q1WPCplVZgCP9hC8Rn5K2+f6KLFo5bhtowSmu+GY1yZtg+RTtsA="
|
||||
PIACertificateNormal = "MIIFqzCCBJOgAwIBAgIJAKZ7D5Yv87qDMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzM1MThaFw0zNDA0MTIxNzM1MThaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPXDL1L9tX6DGf36liA7UBTy5I869z0UVo3lImfOs/GSiFKPtInlesP65577nd7UNzzXlH/P/CnFPdBWlLp5ze3HRBCc/Avgr5CdMRkEsySL5GHBZsx6w2cayQ2EcRhVTwWpcdldeNO+pPr9rIgPrtXqT4SWViTQRBeGM8CDxAyTopTsobjSiYZCF9Ta1gunl0G/8Vfp+SXfYCC+ZzWvP+L1pFhPRqzQQ8k+wMZIovObK1s+nlwPaLyayzw9a8sUnvWB/5rGPdIYnQWPgoNlLN9HpSmsAcw2z8DXI9pIxbr74cb3/HSfuYGOLkRqrOk6h4RCOfuWoTrZup1uEOn+fw8CAwEAAaOCAVQwggFQMB0GA1UdDgQWBBQv63nQ/pJAt5tLy8VJcbHe22ZOsjCCAR8GA1UdIwSCARYwggESgBQv63nQ/pJAt5tLy8VJcbHe22ZOsqGB7qSB6zCB6DELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRMwEQYDVQQHEwpMb3NBbmdlbGVzMSAwHgYDVQQKExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UECxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAMTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQpExdQcml2YXRlIEludGVybmV0IEFjY2VzczEvMC0GCSqGSIb3DQEJARYgc2VjdXJlQHByaXZhdGVpbnRlcm5ldGFjY2Vzcy5jb22CCQCmew+WL/O6gzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQAna5PgrtxfwTumD4+3/SYvwoD66cB8IcK//h1mCzAduU8KgUXocLx7QgJWo9lnZ8xUryXvWab2usg4fqk7FPi00bED4f4qVQFVfGfPZIH9QQ7/48bPM9RyfzImZWUCenK37pdw4Bvgoys2rHLHbGen7f28knT2j/cbMxd78tQc20TIObGjo8+ISTRclSTRBtyCGohseKYpTS9himFERpUgNtefvYHbn70mIOzfOJFTVqfrptf9jXa9N8Mpy3ayfodz1wiqdteqFXkTYoSDctgKMiZ6GdocK9nMroQipIQtpnwd4yBDWIyC6Bvlkrq5TQUtYDQ8z9v+DMO6iwyIDRiU"
|
||||
PIACertificateStrong = "MIIHqzCCBZOgAwIBAgIJAJ0u+vODZJntMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzQwMzNaFw0zNDA0MTIxNzQwMzNaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALVkhjumaqBbL8aSgj6xbX1QPTfTd1qHsAZd2B97m8Vw31c/2yQgZNf5qZY0+jOIHULNDe4R9TIvyBEbvnAg/OkPw8n/+ScgYOeH876VUXzjLDBnDb8DLr/+w9oVsuDeFJ9KV2UFM1OYX0SnkHnrYAN2QLF98ESK4NCSU01h5zkcgmQ+qKSfA9Ny0/UpsKPBFqsQ25NvjDWFhCpeqCHKUJ4Be27CDbSl7lAkBuHMPHJs8f8xPgAbHRXZOxVCpayZ2SNDfCwsnGWpWFoMGvdMbygngCn6jA/W1VSFOlRlfLuuGe7QFfDwA0jaLCxuWt/BgZylp7tAzYKR8lnWmtUCPm4+BtjyVDYtDCiGBD9Z4P13RFWvJHw5aapx/5W/CuvVyI7pKwvc2IT+KPxCUhH1XI8ca5RN3C9NoPJJf6qpg4g0rJH3aaWkoMRrYvQ+5PXXYUzjtRHImghRGd/ydERYoAZXuGSbPkm9Y/p2X8unLcW+F0xpJD98+ZI+tzSsI99Zs5wijSUGYr9/j18KHFTMQ8n+1jauc5bCCegN27dPeKXNSZ5riXFL2XX6BkY68y58UaNzmeGMiUL9BOV1iV+PMb7B7PYs7oFLjAhh0EdyvfHkrh/ZV9BEhtFa7yXp8XR0J6vz1YV9R6DYJmLjOEbhU8N0gc3tZm4Qz39lIIG6w3FDAgMBAAGjggFUMIIBUDAdBgNVHQ4EFgQUrsRtyWJftjpdRM0+925Y6Cl08SUwggEfBgNVHSMEggEWMIIBEoAUrsRtyWJftjpdRM0+925Y6Cl08SWhge6kgeswgegxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRlaW50ZXJuZXRhY2Nlc3MuY29tggkAnS7684Nkme0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOCAgEAJsfhsPk3r8kLXLxY+v+vHzbr4ufNtqnL9/1Uuf8NrsCtpXAoyZ0YqfbkWx3NHTZ7OE9ZRhdMP/RqHQE1p4N4Sa1nZKhTKasV6KhHDqSCt/dvEm89xWm2MVA7nyzQxVlHa9AkcBaemcXEiyT19XdpiXOP4Vhs+J1R5m8zQOxZlV1GtF9vsXmJqWZpOVPmZ8f35BCsYPvv4yMewnrtAC8PFEK/bOPeYcKN50bol22QYaZuLfpkHfNiFTnfMh8sl/ablPyNY7DUNiP5DRcMdIwmfGQxR5WEQoHL3yPJ42LkB5zs6jIm26DGNXfwura/mi105+ENH1CaROtRYwkiHb08U6qLXXJz80mWJkT90nr8Asj35xN2cUppg74nG3YVav/38P48T56hG1NHbYF5uOCske19F6wi9maUoto/3vEr0rnXJUp2KODmKdvBI7co245lHBABWikk8VfejQSlCtDBXn644ZMtAdoxKNfR2WTFVEwJiyd1Fzx0yujuiXDROLhISLQDRjVVAvawrAtLZWYK31bY7KlezPlQnl/D9Asxe85l8jO5+0LdJ6VyOs/Hd4w52alDW/MFySDZSfQHMTIc30hLBJ8OnCEIvluVQQ2UQvoW+no177N9L2Y+M9TcTA62ZyMXShHQGeh20rb4kK8f+iFX8NxtdHVSkxMEFSfDDyQ="
|
||||
)
|
||||
|
||||
func PIAGeoChoices() (choices []string) {
|
||||
servers := PIAServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Region
|
||||
}
|
||||
return choices
|
||||
}
|
||||
|
||||
//nolint:lll
|
||||
func PIAServers() []models.PIAServer {
|
||||
return []models.PIAServer{
|
||||
{Region: "AU Melbourne", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "melbourne404", IPs: []net.IP{{103, 2, 198, 93}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "melbourne404", IPs: []net.IP{{103, 2, 198, 85}}}},
|
||||
{Region: "AU Perth", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "perth403", IPs: []net.IP{{43, 250, 205, 156}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "perth403", IPs: []net.IP{{43, 250, 205, 153}}}},
|
||||
{Region: "AU Sydney", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "sydney408", IPs: []net.IP{{117, 120, 10, 56}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "sydney408", IPs: []net.IP{{117, 120, 10, 48}}}},
|
||||
{Region: "Albania", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "tirana402", IPs: []net.IP{{31, 171, 154, 115}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "tirana402", IPs: []net.IP{{31, 171, 154, 118}}}},
|
||||
{Region: "Algeria", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "algiers404", IPs: []net.IP{{176, 125, 228, 19}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "algiers404", IPs: []net.IP{{176, 125, 228, 26}}}},
|
||||
{Region: "Andorra", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "andorra405", IPs: []net.IP{{188, 241, 82, 40}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "andorra405", IPs: []net.IP{{188, 241, 82, 37}}}},
|
||||
{Region: "Argentina", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "buenosaires401", IPs: []net.IP{{190, 106, 134, 95}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "buenosaires401", IPs: []net.IP{{190, 106, 134, 85}}}},
|
||||
{Region: "Armenia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "armenia403", IPs: []net.IP{{185, 253, 160, 3}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "armenia403", IPs: []net.IP{{185, 253, 160, 9}}}},
|
||||
{Region: "Austria", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "vienna403", IPs: []net.IP{{156, 146, 60, 108}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "vienna403", IPs: []net.IP{{156, 146, 60, 110}}}},
|
||||
{Region: "Bahamas", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "bahamas404", IPs: []net.IP{{95, 181, 238, 9}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "bahamas404", IPs: []net.IP{{95, 181, 238, 9}}}},
|
||||
{Region: "Bangladesh", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "bangladesh403", IPs: []net.IP{{84, 252, 93, 7}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "bangladesh403", IPs: []net.IP{{84, 252, 93, 7}}}},
|
||||
{Region: "Belgium", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "brussels404", IPs: []net.IP{{37, 120, 143, 156}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "brussels404", IPs: []net.IP{{37, 120, 143, 156}}}},
|
||||
{Region: "Bosnia and Herzegovina", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "sarajevo402", IPs: []net.IP{{185, 212, 111, 4}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "sarajevo402", IPs: []net.IP{{185, 212, 111, 71}}}},
|
||||
{Region: "Brazil", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "saopaolo401", IPs: []net.IP{{45, 133, 180, 236}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "saopaolo401", IPs: []net.IP{{45, 133, 180, 227}}}},
|
||||
{Region: "Bulgaria", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "sofia402", IPs: []net.IP{{217, 138, 221, 89}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "sofia402", IPs: []net.IP{{217, 138, 221, 89}}}},
|
||||
{Region: "CA Montreal", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "montreal410", IPs: []net.IP{{199, 36, 223, 210}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "montreal410", IPs: []net.IP{{199, 36, 223, 210}}}},
|
||||
{Region: "CA Ontario", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "ontario408", IPs: []net.IP{{172, 98, 92, 87}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "ontario408", IPs: []net.IP{{172, 98, 92, 80}}}},
|
||||
{Region: "CA Toronto", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "toronto402", IPs: []net.IP{{66, 115, 142, 58}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "toronto402", IPs: []net.IP{{66, 115, 142, 58}}}},
|
||||
{Region: "CA Vancouver", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "vancouver411", IPs: []net.IP{{208, 78, 42, 168}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "vancouver411", IPs: []net.IP{{208, 78, 42, 161}}}},
|
||||
{Region: "Cambodia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "cambodia401", IPs: []net.IP{{188, 215, 235, 109}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "cambodia401", IPs: []net.IP{{188, 215, 235, 110}}}},
|
||||
{Region: "China", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "china404", IPs: []net.IP{{188, 241, 80, 9}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "china404", IPs: []net.IP{{188, 241, 80, 4}}}},
|
||||
{Region: "Cyprus", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "cyprus403", IPs: []net.IP{{185, 253, 162, 8}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "cyprus403", IPs: []net.IP{{185, 253, 162, 14}}}},
|
||||
{Region: "Czech Republic", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "prague405", IPs: []net.IP{{143, 244, 59, 168}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "prague405", IPs: []net.IP{{143, 244, 59, 154}}}},
|
||||
{Region: "DE Berlin", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "berlin425", IPs: []net.IP{{154, 13, 1, 148}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "berlin425", IPs: []net.IP{{154, 13, 1, 146}}}},
|
||||
{Region: "DE Frankfurt", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "frankfurt440", IPs: []net.IP{{185, 216, 33, 165}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "frankfurt440", IPs: []net.IP{{185, 216, 33, 166}}}},
|
||||
{Region: "Denmark", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "copenhagen404", IPs: []net.IP{{188, 126, 94, 190}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "copenhagen404", IPs: []net.IP{{188, 126, 94, 165}}}},
|
||||
{Region: "Egypt", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "cairo402", IPs: []net.IP{{188, 214, 122, 126}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "cairo402", IPs: []net.IP{{188, 214, 122, 123}}}},
|
||||
{Region: "Estonia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "talinn402", IPs: []net.IP{{95, 153, 31, 68}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "talinn402", IPs: []net.IP{{95, 153, 31, 78}}}},
|
||||
{Region: "Finland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "helsinki402", IPs: []net.IP{{188, 126, 89, 45}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "helsinki402", IPs: []net.IP{{188, 126, 89, 35}}}},
|
||||
{Region: "France", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "paris406", IPs: []net.IP{{143, 244, 57, 169}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "paris406", IPs: []net.IP{{143, 244, 57, 169}}}},
|
||||
{Region: "Georgia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "georgia403", IPs: []net.IP{{95, 181, 236, 8}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "georgia403", IPs: []net.IP{{95, 181, 236, 10}}}},
|
||||
{Region: "Greece", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "athens401", IPs: []net.IP{{154, 57, 3, 87}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "athens401", IPs: []net.IP{{154, 57, 3, 85}}}},
|
||||
{Region: "Greenland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "greenland404", IPs: []net.IP{{91, 90, 120, 149}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "greenland404", IPs: []net.IP{{91, 90, 120, 147}}}},
|
||||
{Region: "Hong Kong", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "china403", IPs: []net.IP{{86, 107, 104, 213}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "china403", IPs: []net.IP{{86, 107, 104, 219}}}},
|
||||
{Region: "Hungary", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "budapest402", IPs: []net.IP{{86, 106, 74, 115}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "budapest402", IPs: []net.IP{{86, 106, 74, 117}}}},
|
||||
{Region: "Iceland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "reykjavik402", IPs: []net.IP{{45, 133, 193, 83}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "reykjavik402", IPs: []net.IP{{45, 133, 193, 88}}}},
|
||||
{Region: "India", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "mumbai402", IPs: []net.IP{{45, 120, 139, 138}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "mumbai402", IPs: []net.IP{{45, 120, 139, 127}}}},
|
||||
{Region: "Ireland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "dublin411", IPs: []net.IP{{188, 241, 178, 23}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "dublin411", IPs: []net.IP{{188, 241, 178, 30}}}},
|
||||
{Region: "Isle of Man", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "douglas403", IPs: []net.IP{{91, 90, 124, 7}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "douglas403", IPs: []net.IP{{91, 90, 124, 18}}}},
|
||||
{Region: "Israel", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "jerusalem407", IPs: []net.IP{{185, 77, 248, 91}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "jerusalem407", IPs: []net.IP{{185, 77, 248, 91}}}},
|
||||
{Region: "Italy", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "milano403", IPs: []net.IP{{156, 146, 41, 74}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "milano403", IPs: []net.IP{{156, 146, 41, 84}}}},
|
||||
{Region: "Japan", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "tokyo401", IPs: []net.IP{{156, 146, 34, 130}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "tokyo401", IPs: []net.IP{{156, 146, 34, 130}}}},
|
||||
{Region: "Kazakhstan", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "kazakhstan403", IPs: []net.IP{{62, 133, 47, 13}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "kazakhstan403", IPs: []net.IP{{62, 133, 47, 5}}}},
|
||||
{Region: "Latvia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "riga401", IPs: []net.IP{{109, 248, 149, 5}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "riga401", IPs: []net.IP{{109, 248, 149, 8}}}},
|
||||
{Region: "Liechtenstein", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "liechtenstein403", IPs: []net.IP{{91, 90, 122, 7}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "liechtenstein403", IPs: []net.IP{{91, 90, 122, 18}}}},
|
||||
{Region: "Lithuania", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "vilnius403", IPs: []net.IP{{85, 206, 165, 118}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "vilnius403", IPs: []net.IP{{85, 206, 165, 116}}}},
|
||||
{Region: "Luxembourg", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "luxembourg407", IPs: []net.IP{{5, 253, 204, 150}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "luxembourg407", IPs: []net.IP{{5, 253, 204, 155}}}},
|
||||
{Region: "Macao", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "macau403", IPs: []net.IP{{84, 252, 92, 6}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "macau403", IPs: []net.IP{{84, 252, 92, 15}}}},
|
||||
{Region: "Macedonia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "macedonia401", IPs: []net.IP{{185, 225, 28, 120}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "macedonia401", IPs: []net.IP{{185, 225, 28, 120}}}},
|
||||
{Region: "Malta", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "malta403", IPs: []net.IP{{176, 125, 230, 13}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "malta403", IPs: []net.IP{{176, 125, 230, 7}}}},
|
||||
{Region: "Mexico", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "mexico403", IPs: []net.IP{{77, 81, 142, 14}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "mexico403", IPs: []net.IP{{77, 81, 142, 13}}}},
|
||||
{Region: "Moldova", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "chisinau401", IPs: []net.IP{{178, 175, 129, 44}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "chisinau401", IPs: []net.IP{{178, 175, 129, 46}}}},
|
||||
{Region: "Monaco", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "monaco403", IPs: []net.IP{{95, 181, 233, 8}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "monaco403", IPs: []net.IP{{95, 181, 233, 5}}}},
|
||||
{Region: "Mongolia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "mongolia403", IPs: []net.IP{{185, 253, 163, 15}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "mongolia403", IPs: []net.IP{{185, 253, 163, 5}}}},
|
||||
{Region: "Montenegro", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "montenegro403", IPs: []net.IP{{176, 125, 229, 14}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "montenegro403", IPs: []net.IP{{176, 125, 229, 4}}}},
|
||||
{Region: "Morocco", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "morocco403", IPs: []net.IP{{95, 181, 232, 4}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "morocco403", IPs: []net.IP{{95, 181, 232, 8}}}},
|
||||
{Region: "Netherlands", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "amsterdam412", IPs: []net.IP{{143, 244, 41, 196}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "amsterdam412", IPs: []net.IP{{143, 244, 41, 196}}}},
|
||||
{Region: "New Zealand", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "newzealand403", IPs: []net.IP{{43, 250, 207, 90}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "newzealand403", IPs: []net.IP{{43, 250, 207, 84}}}},
|
||||
{Region: "Nigeria", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "nigeria403", IPs: []net.IP{{102, 165, 25, 86}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "nigeria403", IPs: []net.IP{{102, 165, 25, 85}}}},
|
||||
{Region: "Norway", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "oslo401", IPs: []net.IP{{46, 246, 122, 37}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "oslo401", IPs: []net.IP{{46, 246, 122, 60}}}},
|
||||
{Region: "Panama", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "panama404", IPs: []net.IP{{91, 90, 126, 25}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "panama404", IPs: []net.IP{{91, 90, 126, 28}}}},
|
||||
{Region: "Philippines", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "philippines401", IPs: []net.IP{{188, 214, 125, 140}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "philippines401", IPs: []net.IP{{188, 214, 125, 137}}}},
|
||||
{Region: "Poland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "warsaw409", IPs: []net.IP{{194, 110, 114, 119}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "warsaw409", IPs: []net.IP{{194, 110, 114, 118}}}},
|
||||
{Region: "Portugal", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "lisbon402", IPs: []net.IP{{89, 26, 241, 87}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "lisbon402", IPs: []net.IP{{89, 26, 241, 88}}}},
|
||||
{Region: "Qatar", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "qatar403", IPs: []net.IP{{95, 181, 234, 9}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "qatar403", IPs: []net.IP{{95, 181, 234, 8}}}},
|
||||
{Region: "Romania", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "romania408", IPs: []net.IP{{143, 244, 54, 117}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "romania408", IPs: []net.IP{{143, 244, 54, 116}}}},
|
||||
{Region: "Saudi Arabia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "saudiarabia403", IPs: []net.IP{{95, 181, 235, 8}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "saudiarabia403", IPs: []net.IP{{95, 181, 235, 4}}}},
|
||||
{Region: "Serbia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "belgrade401", IPs: []net.IP{{37, 120, 193, 250}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "belgrade401", IPs: []net.IP{{37, 120, 193, 249}}}},
|
||||
{Region: "Singapore", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "singapore401", IPs: []net.IP{{156, 146, 57, 213}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "singapore401", IPs: []net.IP{{156, 146, 57, 197}}}},
|
||||
{Region: "Slovakia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "bratislava402", IPs: []net.IP{{37, 120, 221, 213}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "bratislava402", IPs: []net.IP{{37, 120, 221, 218}}}},
|
||||
{Region: "South Africa", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "johannesburg401", IPs: []net.IP{{154, 16, 93, 33}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "johannesburg401", IPs: []net.IP{{154, 16, 93, 46}}}},
|
||||
{Region: "Spain", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "madrid401", IPs: []net.IP{{212, 102, 49, 68}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "madrid401", IPs: []net.IP{{212, 102, 49, 68}}}},
|
||||
{Region: "Sri Lanka", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "srilanka403", IPs: []net.IP{{95, 181, 239, 8}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "srilanka403", IPs: []net.IP{{95, 181, 239, 13}}}},
|
||||
{Region: "Sweden", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "stockholm401", IPs: []net.IP{{195, 246, 120, 4}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "stockholm401", IPs: []net.IP{{195, 246, 120, 39}}}},
|
||||
{Region: "Switzerland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "zurich407", IPs: []net.IP{{156, 146, 62, 194}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "zurich407", IPs: []net.IP{{156, 146, 62, 194}}}},
|
||||
{Region: "Taiwan", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "taiwan401", IPs: []net.IP{{188, 214, 106, 74}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "taiwan401", IPs: []net.IP{{188, 214, 106, 69}}}},
|
||||
{Region: "Turkey", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "istanbul402", IPs: []net.IP{{188, 213, 34, 88}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "istanbul402", IPs: []net.IP{{188, 213, 34, 83}}}},
|
||||
{Region: "UK London", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "london405", IPs: []net.IP{{212, 102, 53, 15}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "london405", IPs: []net.IP{{212, 102, 53, 60}}}},
|
||||
{Region: "UK London-2", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "london420", IPs: []net.IP{{37, 235, 96, 200}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "london420", IPs: []net.IP{{37, 235, 96, 206}}}},
|
||||
{Region: "UK Manchester", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "manchester414", IPs: []net.IP{{194, 37, 96, 194}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "manchester414", IPs: []net.IP{{194, 37, 96, 197}}}},
|
||||
{Region: "UK Southampton", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "southampton401", IPs: []net.IP{{143, 244, 37, 244}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "southampton401", IPs: []net.IP{{143, 244, 37, 194}}}},
|
||||
{Region: "US Atlanta", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "atlanta417", IPs: []net.IP{{154, 21, 22, 216}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "atlanta417", IPs: []net.IP{{154, 21, 22, 216}}}},
|
||||
{Region: "US California", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "losangeles401", IPs: []net.IP{{143, 244, 48, 15}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "losangeles401", IPs: []net.IP{{143, 244, 48, 17}}}},
|
||||
{Region: "US Chicago", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "chicago413", IPs: []net.IP{{154, 21, 23, 125}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "chicago413", IPs: []net.IP{{154, 21, 23, 137}}}},
|
||||
{Region: "US Denver", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "denver410", IPs: []net.IP{{174, 128, 227, 24}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "denver410", IPs: []net.IP{{174, 128, 227, 24}}}},
|
||||
{Region: "US East", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "newjersey405", IPs: []net.IP{{143, 244, 46, 65}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "newjersey405", IPs: []net.IP{{143, 244, 46, 115}}}},
|
||||
{Region: "US Florida", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "miami407", IPs: []net.IP{{156, 146, 42, 78}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "miami407", IPs: []net.IP{{156, 146, 42, 113}}}},
|
||||
{Region: "US Houston", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "houston413", IPs: []net.IP{{205, 251, 142, 20}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "houston413", IPs: []net.IP{{205, 251, 142, 20}}}},
|
||||
{Region: "US Las Vegas", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "lasvegas426", IPs: []net.IP{{196, 53, 64, 175}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "lasvegas426", IPs: []net.IP{{196, 53, 64, 189}}}},
|
||||
{Region: "US New York", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "newyork404", IPs: []net.IP{{143, 244, 44, 227}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "newyork404", IPs: []net.IP{{143, 244, 44, 208}}}},
|
||||
{Region: "US Seattle", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "seattle422", IPs: []net.IP{{156, 146, 48, 204}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "seattle422", IPs: []net.IP{{156, 146, 48, 239}}}},
|
||||
{Region: "US Silicon Valley", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "siliconvalley420", IPs: []net.IP{{66, 115, 165, 93}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "siliconvalley420", IPs: []net.IP{{66, 115, 165, 80}}}},
|
||||
{Region: "US Texas", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "dallas415", IPs: []net.IP{{154, 3, 250, 16}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "dallas415", IPs: []net.IP{{154, 3, 250, 24}}}},
|
||||
{Region: "US Washington DC", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "washington452", IPs: []net.IP{{91, 149, 244, 110}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "washington452", IPs: []net.IP{{91, 149, 244, 114}}}},
|
||||
{Region: "US West", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "phoenix413", IPs: []net.IP{{184, 170, 241, 169}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "phoenix413", IPs: []net.IP{{184, 170, 241, 169}}}},
|
||||
{Region: "Ukraine", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "kiev403", IPs: []net.IP{{62, 149, 20, 7}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "kiev403", IPs: []net.IP{{62, 149, 20, 4}}}},
|
||||
{Region: "United Arab Emirates", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "dubai403", IPs: []net.IP{{217, 138, 193, 153}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "dubai403", IPs: []net.IP{{217, 138, 193, 150}}}},
|
||||
{Region: "Venezuela", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "venezuela403", IPs: []net.IP{{95, 181, 237, 3}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "venezuela403", IPs: []net.IP{{95, 181, 237, 9}}}},
|
||||
{Region: "Vietnam", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "vietnam401", IPs: []net.IP{{188, 214, 152, 67}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "vietnam401", IPs: []net.IP{{188, 214, 152, 67}}}},
|
||||
}
|
||||
}
|
||||
205
internal/constants/privado.go
Normal file
205
internal/constants/privado.go
Normal file
@@ -0,0 +1,205 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
const (
|
||||
PrivadoCertificate = "MIIFKDCCAxCgAwIBAgIJAMtrmqZxIV/OMA0GCSqGSIb3DQEBDQUAMBIxEDAOBgNVBAMMB1ByaXZhZG8wHhcNMjAwMTA4MjEyODQ1WhcNMzUwMTA5MjEyODQ1WjASMRAwDgYDVQQDDAdQcml2YWRvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxPwOgiwNJzZTnKIXwAB0TSu/Lu2qt2U2I8obtQjwhi/7OrfmbmYykSdro70al2XPhnwAGGdCxW6LDnp0UN/IOhD11mgBPo14f5CLkBQjSJ6VN5miPbvK746LsNZl9H8rQGvDuPo4CG9BfPZMiDRGlsMxij/jztzgT1gmuxQ7WHfFRcNzBas1dHa9hV/d3TU6/t47x4SE/ljdcCtJiu7Zn6ODKQoys3mB7Luz2ngqUJWvkqsg+E4+3eJ0M8Hlbn5TPaRJBID7DAdYo6Vs6xGCYr981ThFcmoIQ10js10yANrrfGAzd03b3TnLAgko0uQMHjliMZL6L8sWOPHxyxJI0us88SFh4UgcFyRHKHPKux7w24SxAlZUYoUcTHp9VjG5XvDKYxzgV2RdM4ulBGbQRQ3y3/CyddsyQYMvA55Ets0LfPaBvDIcct70iXijGsdvlX1du3ArGpG7Vaje/RU4nbbGT6HYRdt5YyZfof288ukMOSj20nVcmS+c/4tqsxSerRb1aq5LOi1IemSkTMeC5gCbexk+L1vl7NT/58sxjGmu5bXwnvev/lIItfi2AlITrfUSEv19iDMKkeshwn/+sFJBMWYyluP+yJ56yR+MWoXvLlSWphLDTqq19yx3BZn0P1tgbXoR0g8PTdJFcz8z3RIb7myVLYulV1oGG/3rka0CAwEAAaOBgDB+MB0GA1UdDgQWBBTFtJkZCVDuDAD6k5bJzefjJdO3DTBCBgNVHSMEOzA5gBTFtJkZCVDuDAD6k5bJzefjJdO3DaEWpBQwEjEQMA4GA1UEAwwHUHJpdmFkb4IJAMtrmqZxIV/OMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBDQUAA4ICAQB7MUSXMeBb9wlSv4sUaT1JHEwE26nlBw+TKmezfuPU5pBlY0LYr6qQZY95DHqsRJ7ByUzGUrGo17dNGXlcuNc6TAaQQEDRPo6y+LVh2TWMk15TUMI+MkqryJtCret7xGvDigKYMJgBy58HN3RAVr1B7cL9youwzLgc2Y/NcFKvnQJKeiIYAJ7g0CcnJiQvgZTS7xdwkEBXfsngmUCIG320DLPEL+Ze0HiUrxwWljMRya6i40AeH3Zu2i532xX1wV5+cjA4RJWIKg6ri/Q54iFGtZrA9/nc6y9uoQHkmz8cGyVUmJxFzMrrIICVqUtVRxLhkTMe4UzwRWTBeGgtW4tS0yq1QonAKfOyjgRw/CeY55D2UGvnAFZdTadtYXS4Alu2P9zdwoEk3fzHiVmDjqfJVr5wz9383aABUFrPI3nz6ed/Z6LZflKh1k+DUDEp8NxU4klUULWsSOKoa5zGX51G8cdHxwQLImXvtGuN5eSR8jCTgxFZhdps/xes4KkyfIz9FMYG748M+uOTgKITf4zdJ9BAyiQaOufVQZ8WjhWzWk9YHec9VqPkzpWNGkVjiRI5ewuXwZzZ164tMv2hikBXSuUCnFz37/ZNwGlDi0oBdDszCk2GxccdFHHaCSmpjU5MrdJ+5IhtTKGeTx+US2hTIVHQFIO99DmacxSYvLNcSQ=="
|
||||
)
|
||||
|
||||
func PrivadoHostnameChoices() (choices []string) {
|
||||
servers := PrivadoServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Hostname
|
||||
}
|
||||
return choices
|
||||
}
|
||||
|
||||
func PrivadoServers() []models.PrivadoServer {
|
||||
return []models.PrivadoServer{
|
||||
{Hostname: "akl-001.vpn.privado.io", IP: net.IP{23, 254, 104, 114}},
|
||||
{Hostname: "akl-002.vpn.privado.io", IP: net.IP{23, 254, 104, 120}},
|
||||
{Hostname: "akl-003.vpn.privado.io", IP: net.IP{23, 254, 104, 51}},
|
||||
{Hostname: "ams-001.vpn.privado.io", IP: net.IP{91, 148, 224, 10}},
|
||||
{Hostname: "ams-002.vpn.privado.io", IP: net.IP{91, 148, 224, 20}},
|
||||
{Hostname: "ams-003.vpn.privado.io", IP: net.IP{91, 148, 224, 30}},
|
||||
{Hostname: "ams-004.vpn.privado.io", IP: net.IP{91, 148, 224, 40}},
|
||||
{Hostname: "ams-005.vpn.privado.io", IP: net.IP{91, 148, 224, 50}},
|
||||
{Hostname: "ams-006.vpn.privado.io", IP: net.IP{91, 148, 224, 60}},
|
||||
{Hostname: "ams-007.vpn.privado.io", IP: net.IP{91, 148, 224, 70}},
|
||||
{Hostname: "ams-008.vpn.privado.io", IP: net.IP{91, 148, 224, 80}},
|
||||
{Hostname: "ams-009.vpn.privado.io", IP: net.IP{91, 148, 228, 10}},
|
||||
{Hostname: "ams-010.vpn.privado.io", IP: net.IP{91, 148, 228, 20}},
|
||||
{Hostname: "ams-011.vpn.privado.io", IP: net.IP{91, 148, 228, 30}},
|
||||
{Hostname: "ams-012.vpn.privado.io", IP: net.IP{91, 148, 228, 40}},
|
||||
{Hostname: "ams-013.vpn.privado.io", IP: net.IP{91, 148, 228, 50}},
|
||||
{Hostname: "ams-014.vpn.privado.io", IP: net.IP{91, 148, 228, 60}},
|
||||
{Hostname: "ams-015.vpn.privado.io", IP: net.IP{91, 148, 228, 70}},
|
||||
{Hostname: "ams-016.vpn.privado.io", IP: net.IP{91, 148, 228, 80}},
|
||||
{Hostname: "arn-001.vpn.privado.io", IP: net.IP{86, 106, 103, 67}},
|
||||
{Hostname: "arn-002.vpn.privado.io", IP: net.IP{86, 106, 103, 74}},
|
||||
{Hostname: "arn-003.vpn.privado.io", IP: net.IP{86, 106, 103, 81}},
|
||||
{Hostname: "ath-001.vpn.privado.io", IP: net.IP{188, 123, 126, 61}},
|
||||
{Hostname: "ath-002.vpn.privado.io", IP: net.IP{188, 123, 126, 64}},
|
||||
{Hostname: "ath-003.vpn.privado.io", IP: net.IP{188, 123, 126, 68}},
|
||||
{Hostname: "ath-004.vpn.privado.io", IP: net.IP{188, 123, 126, 72}},
|
||||
{Hostname: "beg-001.vpn.privado.io", IP: net.IP{89, 38, 224, 19}},
|
||||
{Hostname: "beg-002.vpn.privado.io", IP: net.IP{89, 38, 224, 25}},
|
||||
{Hostname: "bkk-001.vpn.privado.io", IP: net.IP{119, 59, 111, 3}},
|
||||
{Hostname: "bkk-002.vpn.privado.io", IP: net.IP{119, 59, 111, 11}},
|
||||
{Hostname: "bom-001.vpn.privado.io", IP: net.IP{103, 26, 204, 61}},
|
||||
{Hostname: "bom-002.vpn.privado.io", IP: net.IP{103, 26, 204, 70}},
|
||||
{Hostname: "bru-001.vpn.privado.io", IP: net.IP{217, 138, 211, 163}},
|
||||
{Hostname: "bru-002.vpn.privado.io", IP: net.IP{217, 138, 211, 170}},
|
||||
{Hostname: "bru-003.vpn.privado.io", IP: net.IP{217, 138, 211, 177}},
|
||||
{Hostname: "bru-004.vpn.privado.io", IP: net.IP{217, 138, 211, 184}},
|
||||
{Hostname: "bts-001.vpn.privado.io", IP: net.IP{37, 120, 221, 227}},
|
||||
{Hostname: "bts-002.vpn.privado.io", IP: net.IP{37, 120, 221, 233}},
|
||||
{Hostname: "bud-001.vpn.privado.io", IP: net.IP{185, 128, 26, 194}},
|
||||
{Hostname: "bud-002.vpn.privado.io", IP: net.IP{185, 128, 26, 200}},
|
||||
{Hostname: "cdg-001.vpn.privado.io", IP: net.IP{89, 40, 183, 99}},
|
||||
{Hostname: "cdg-002.vpn.privado.io", IP: net.IP{89, 40, 183, 106}},
|
||||
{Hostname: "cdg-003.vpn.privado.io", IP: net.IP{89, 40, 183, 113}},
|
||||
{Hostname: "cdg-004.vpn.privado.io", IP: net.IP{89, 40, 183, 120}},
|
||||
{Hostname: "cph-001.vpn.privado.io", IP: net.IP{2, 58, 46, 35}},
|
||||
{Hostname: "cph-002.vpn.privado.io", IP: net.IP{2, 58, 46, 42}},
|
||||
{Hostname: "cph-003.vpn.privado.io", IP: net.IP{2, 58, 46, 49}},
|
||||
{Hostname: "cph-004.vpn.privado.io", IP: net.IP{2, 58, 46, 56}},
|
||||
{Hostname: "dca-001.vpn.privado.io", IP: net.IP{85, 12, 61, 10}},
|
||||
{Hostname: "dca-002.vpn.privado.io", IP: net.IP{85, 12, 61, 20}},
|
||||
{Hostname: "dca-003.vpn.privado.io", IP: net.IP{85, 12, 61, 30}},
|
||||
{Hostname: "dca-004.vpn.privado.io", IP: net.IP{85, 12, 61, 40}},
|
||||
{Hostname: "dca-005.vpn.privado.io", IP: net.IP{85, 12, 61, 50}},
|
||||
{Hostname: "dca-006.vpn.privado.io", IP: net.IP{85, 12, 61, 60}},
|
||||
{Hostname: "dca-007.vpn.privado.io", IP: net.IP{85, 12, 61, 70}},
|
||||
{Hostname: "dca-008.vpn.privado.io", IP: net.IP{85, 12, 61, 80}},
|
||||
{Hostname: "dca-013.vpn.privado.io", IP: net.IP{185, 247, 68, 3}},
|
||||
{Hostname: "dca-014.vpn.privado.io", IP: net.IP{185, 247, 68, 10}},
|
||||
{Hostname: "dca-015.vpn.privado.io", IP: net.IP{185, 247, 68, 17}},
|
||||
{Hostname: "dca-016.vpn.privado.io", IP: net.IP{185, 247, 68, 24}},
|
||||
{Hostname: "dfw-001.vpn.privado.io", IP: net.IP{23, 105, 32, 243}},
|
||||
{Hostname: "dfw-002.vpn.privado.io", IP: net.IP{23, 105, 32, 244}},
|
||||
{Hostname: "dub-001.vpn.privado.io", IP: net.IP{84, 247, 48, 227}},
|
||||
{Hostname: "dub-002.vpn.privado.io", IP: net.IP{84, 247, 48, 234}},
|
||||
{Hostname: "dub-003.vpn.privado.io", IP: net.IP{84, 247, 48, 241}},
|
||||
{Hostname: "dub-004.vpn.privado.io", IP: net.IP{84, 247, 48, 248}},
|
||||
{Hostname: "eze-001.vpn.privado.io", IP: net.IP{168, 205, 93, 211}},
|
||||
{Hostname: "eze-002.vpn.privado.io", IP: net.IP{168, 205, 93, 217}},
|
||||
{Hostname: "fra-001.vpn.privado.io", IP: net.IP{91, 148, 232, 10}},
|
||||
{Hostname: "fra-002.vpn.privado.io", IP: net.IP{91, 148, 232, 20}},
|
||||
{Hostname: "fra-003.vpn.privado.io", IP: net.IP{91, 148, 232, 30}},
|
||||
{Hostname: "fra-004.vpn.privado.io", IP: net.IP{91, 148, 232, 40}},
|
||||
{Hostname: "fra-005.vpn.privado.io", IP: net.IP{91, 148, 233, 7}},
|
||||
{Hostname: "fra-006.vpn.privado.io", IP: net.IP{91, 148, 233, 8}},
|
||||
{Hostname: "fra-007.vpn.privado.io", IP: net.IP{91, 148, 233, 9}},
|
||||
{Hostname: "fra-008.vpn.privado.io", IP: net.IP{91, 148, 233, 10}},
|
||||
{Hostname: "gru-001.vpn.privado.io", IP: net.IP{177, 54, 145, 193}},
|
||||
{Hostname: "gru-002.vpn.privado.io", IP: net.IP{177, 54, 145, 197}},
|
||||
{Hostname: "hel-001.vpn.privado.io", IP: net.IP{194, 34, 134, 219}},
|
||||
{Hostname: "hel-002.vpn.privado.io", IP: net.IP{194, 34, 134, 227}},
|
||||
{Hostname: "hkg-001.vpn.privado.io", IP: net.IP{209, 58, 185, 88}},
|
||||
{Hostname: "hkg-002.vpn.privado.io", IP: net.IP{209, 58, 185, 97}},
|
||||
{Hostname: "hkg-003.vpn.privado.io", IP: net.IP{209, 58, 185, 108}},
|
||||
{Hostname: "hkg-004.vpn.privado.io", IP: net.IP{209, 58, 185, 120}},
|
||||
{Hostname: "icn-001.vpn.privado.io", IP: net.IP{169, 56, 73, 146}},
|
||||
{Hostname: "icn-002.vpn.privado.io", IP: net.IP{169, 56, 73, 153}},
|
||||
{Hostname: "iev-001.vpn.privado.io", IP: net.IP{176, 103, 52, 40}},
|
||||
{Hostname: "iev-002.vpn.privado.io", IP: net.IP{176, 103, 53, 40}},
|
||||
{Hostname: "ist-001.vpn.privado.io", IP: net.IP{185, 84, 183, 3}},
|
||||
{Hostname: "ist-002.vpn.privado.io", IP: net.IP{185, 84, 183, 4}},
|
||||
{Hostname: "jfk-001.vpn.privado.io", IP: net.IP{217, 138, 208, 99}},
|
||||
{Hostname: "jfk-002.vpn.privado.io", IP: net.IP{217, 138, 208, 106}},
|
||||
{Hostname: "jfk-003.vpn.privado.io", IP: net.IP{217, 138, 208, 113}},
|
||||
{Hostname: "jfk-004.vpn.privado.io", IP: net.IP{217, 138, 208, 120}},
|
||||
{Hostname: "jnb-001.vpn.privado.io", IP: net.IP{172, 107, 93, 131}},
|
||||
{Hostname: "jnb-002.vpn.privado.io", IP: net.IP{172, 107, 93, 137}},
|
||||
{Hostname: "lax-009.vpn.privado.io", IP: net.IP{45, 152, 182, 227}},
|
||||
{Hostname: "lax-010.vpn.privado.io", IP: net.IP{45, 152, 182, 234}},
|
||||
{Hostname: "lax-011.vpn.privado.io", IP: net.IP{45, 152, 182, 241}},
|
||||
{Hostname: "lax-012.vpn.privado.io", IP: net.IP{45, 152, 182, 248}},
|
||||
{Hostname: "lis-001.vpn.privado.io", IP: net.IP{89, 26, 243, 153}},
|
||||
{Hostname: "lis-002.vpn.privado.io", IP: net.IP{89, 26, 243, 154}},
|
||||
{Hostname: "lon-001.vpn.privado.io", IP: net.IP{217, 138, 195, 163}},
|
||||
{Hostname: "lon-002.vpn.privado.io", IP: net.IP{217, 138, 195, 170}},
|
||||
{Hostname: "lon-003.vpn.privado.io", IP: net.IP{217, 138, 195, 177}},
|
||||
{Hostname: "lon-004.vpn.privado.io", IP: net.IP{217, 138, 195, 184}},
|
||||
{Hostname: "mad-001.vpn.privado.io", IP: net.IP{217, 138, 218, 131}},
|
||||
{Hostname: "man-001.vpn.privado.io", IP: net.IP{217, 138, 196, 131}},
|
||||
{Hostname: "man-002.vpn.privado.io", IP: net.IP{217, 138, 196, 138}},
|
||||
{Hostname: "man-003.vpn.privado.io", IP: net.IP{217, 138, 196, 145}},
|
||||
{Hostname: "man-004.vpn.privado.io", IP: net.IP{217, 138, 196, 152}},
|
||||
{Hostname: "mex-001.vpn.privado.io", IP: net.IP{169, 57, 96, 52}},
|
||||
{Hostname: "mex-002.vpn.privado.io", IP: net.IP{169, 57, 96, 57}},
|
||||
{Hostname: "mia-001.vpn.privado.io", IP: net.IP{86, 106, 87, 131}},
|
||||
{Hostname: "mia-002.vpn.privado.io", IP: net.IP{86, 106, 87, 138}},
|
||||
{Hostname: "mia-003.vpn.privado.io", IP: net.IP{86, 106, 87, 145}},
|
||||
{Hostname: "mia-004.vpn.privado.io", IP: net.IP{86, 106, 87, 152}},
|
||||
{Hostname: "mxp-001.vpn.privado.io", IP: net.IP{89, 40, 182, 195}},
|
||||
{Hostname: "mxp-002.vpn.privado.io", IP: net.IP{89, 40, 182, 201}},
|
||||
{Hostname: "nrt-001.vpn.privado.io", IP: net.IP{217, 138, 252, 3}},
|
||||
{Hostname: "nrt-002.vpn.privado.io", IP: net.IP{217, 138, 252, 10}},
|
||||
{Hostname: "nrt-003.vpn.privado.io", IP: net.IP{217, 138, 252, 17}},
|
||||
{Hostname: "nrt-004.vpn.privado.io", IP: net.IP{217, 138, 252, 24}},
|
||||
{Hostname: "ord-001.vpn.privado.io", IP: net.IP{23, 108, 95, 129}},
|
||||
{Hostname: "ord-002.vpn.privado.io", IP: net.IP{23, 108, 95, 167}},
|
||||
{Hostname: "osl-001.vpn.privado.io", IP: net.IP{84, 247, 50, 115}},
|
||||
{Hostname: "osl-002.vpn.privado.io", IP: net.IP{84, 247, 50, 119}},
|
||||
{Hostname: "osl-003.vpn.privado.io", IP: net.IP{84, 247, 50, 123}},
|
||||
{Hostname: "otp-001.vpn.privado.io", IP: net.IP{89, 46, 102, 179}},
|
||||
{Hostname: "otp-002.vpn.privado.io", IP: net.IP{89, 46, 102, 185}},
|
||||
{Hostname: "phx-001.vpn.privado.io", IP: net.IP{91, 148, 236, 10}},
|
||||
{Hostname: "phx-002.vpn.privado.io", IP: net.IP{91, 148, 236, 20}},
|
||||
{Hostname: "phx-003.vpn.privado.io", IP: net.IP{91, 148, 236, 30}},
|
||||
{Hostname: "phx-004.vpn.privado.io", IP: net.IP{91, 148, 236, 40}},
|
||||
{Hostname: "phx-005.vpn.privado.io", IP: net.IP{91, 148, 236, 50}},
|
||||
{Hostname: "phx-006.vpn.privado.io", IP: net.IP{91, 148, 236, 60}},
|
||||
{Hostname: "phx-007.vpn.privado.io", IP: net.IP{91, 148, 236, 70}},
|
||||
{Hostname: "phx-008.vpn.privado.io", IP: net.IP{91, 148, 236, 80}},
|
||||
{Hostname: "prg-001.vpn.privado.io", IP: net.IP{185, 216, 35, 99}},
|
||||
{Hostname: "prg-002.vpn.privado.io", IP: net.IP{185, 216, 35, 105}},
|
||||
{Hostname: "rix-001.vpn.privado.io", IP: net.IP{109, 248, 149, 35}},
|
||||
{Hostname: "rix-002.vpn.privado.io", IP: net.IP{109, 248, 149, 40}},
|
||||
{Hostname: "rkv-001.vpn.privado.io", IP: net.IP{82, 221, 131, 78}},
|
||||
{Hostname: "rkv-002.vpn.privado.io", IP: net.IP{82, 221, 131, 127}},
|
||||
{Hostname: "sea-001.vpn.privado.io", IP: net.IP{23, 81, 208, 96}},
|
||||
{Hostname: "sea-002.vpn.privado.io", IP: net.IP{23, 81, 208, 104}},
|
||||
{Hostname: "sin-001.vpn.privado.io", IP: net.IP{92, 119, 178, 131}},
|
||||
{Hostname: "sin-002.vpn.privado.io", IP: net.IP{92, 119, 178, 138}},
|
||||
{Hostname: "sin-003.vpn.privado.io", IP: net.IP{92, 119, 178, 145}},
|
||||
{Hostname: "sin-004.vpn.privado.io", IP: net.IP{92, 119, 178, 152}},
|
||||
{Hostname: "sof-001.vpn.privado.io", IP: net.IP{217, 138, 221, 163}},
|
||||
{Hostname: "sof-002.vpn.privado.io", IP: net.IP{217, 138, 221, 169}},
|
||||
{Hostname: "stl-001.vpn.privado.io", IP: net.IP{148, 72, 170, 145}},
|
||||
{Hostname: "stl-002.vpn.privado.io", IP: net.IP{148, 72, 172, 82}},
|
||||
{Hostname: "syd-001.vpn.privado.io", IP: net.IP{93, 115, 35, 35}},
|
||||
{Hostname: "syd-002.vpn.privado.io", IP: net.IP{93, 115, 35, 42}},
|
||||
{Hostname: "syd-003.vpn.privado.io", IP: net.IP{93, 115, 35, 49}},
|
||||
{Hostname: "syd-004.vpn.privado.io", IP: net.IP{93, 115, 35, 56}},
|
||||
{Hostname: "vie-001.vpn.privado.io", IP: net.IP{5, 253, 207, 227}},
|
||||
{Hostname: "vie-002.vpn.privado.io", IP: net.IP{5, 253, 207, 234}},
|
||||
{Hostname: "vie-003.vpn.privado.io", IP: net.IP{5, 253, 207, 241}},
|
||||
{Hostname: "vie-004.vpn.privado.io", IP: net.IP{5, 253, 207, 248}},
|
||||
{Hostname: "vno-001.vpn.privado.io", IP: net.IP{185, 64, 104, 176}},
|
||||
{Hostname: "vno-002.vpn.privado.io", IP: net.IP{185, 64, 104, 180}},
|
||||
{Hostname: "waw-001.vpn.privado.io", IP: net.IP{217, 138, 209, 163}},
|
||||
{Hostname: "waw-002.vpn.privado.io", IP: net.IP{217, 138, 209, 164}},
|
||||
{Hostname: "waw-003.vpn.privado.io", IP: net.IP{217, 138, 209, 165}},
|
||||
{Hostname: "waw-004.vpn.privado.io", IP: net.IP{217, 138, 209, 166}},
|
||||
{Hostname: "yul-001.vpn.privado.io", IP: net.IP{217, 138, 213, 67}},
|
||||
{Hostname: "yul-002.vpn.privado.io", IP: net.IP{217, 138, 213, 74}},
|
||||
{Hostname: "yul-003.vpn.privado.io", IP: net.IP{217, 138, 213, 81}},
|
||||
{Hostname: "yul-004.vpn.privado.io", IP: net.IP{217, 138, 213, 88}},
|
||||
{Hostname: "yvr-001.vpn.privado.io", IP: net.IP{71, 19, 248, 57}},
|
||||
{Hostname: "yvr-002.vpn.privado.io", IP: net.IP{71, 19, 248, 113}},
|
||||
{Hostname: "yyz-003.vpn.privado.io", IP: net.IP{199, 189, 27, 19}},
|
||||
{Hostname: "zrh-001.vpn.privado.io", IP: net.IP{185, 156, 175, 195}},
|
||||
{Hostname: "zrh-002.vpn.privado.io", IP: net.IP{185, 156, 175, 202}},
|
||||
{Hostname: "zrh-003.vpn.privado.io", IP: net.IP{185, 156, 175, 209}},
|
||||
{Hostname: "zrh-004.vpn.privado.io", IP: net.IP{185, 156, 175, 216}},
|
||||
}
|
||||
}
|
||||
89
internal/constants/purevpn.go
Normal file
89
internal/constants/purevpn.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
const (
|
||||
PurevpnCertificateAuthority = "MIIE6DCCA9CgAwIBAgIJAMjXFoeo5uSlMA0GCSqGSIb3DQEBCwUAMIGoMQswCQYDVQQGEwJISzEQMA4GA1UECBMHQ2VudHJhbDELMAkGA1UEBxMCSEsxGDAWBgNVBAoTD1NlY3VyZS1TZXJ2ZXJDQTELMAkGA1UECxMCSVQxGDAWBgNVBAMTD1NlY3VyZS1TZXJ2ZXJDQTEYMBYGA1UEKRMPU2VjdXJlLVNlcnZlckNBMR8wHQYJKoZIhvcNAQkBFhBtYWlsQGhvc3QuZG9tYWluMB4XDTE2MDExNTE1MzQwOVoXDTI2MDExMjE1MzQwOVowgagxCzAJBgNVBAYTAkhLMRAwDgYDVQQIEwdDZW50cmFsMQswCQYDVQQHEwJISzEYMBYGA1UEChMPU2VjdXJlLVNlcnZlckNBMQswCQYDVQQLEwJJVDEYMBYGA1UEAxMPU2VjdXJlLVNlcnZlckNBMRgwFgYDVQQpEw9TZWN1cmUtU2VydmVyQ0ExHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDluufhyLlyvXzPUL16kAWAdivl1roQv3QHbuRshyKacf/1Er1JqEbtW3Mx9Fvr/u27qU2W8lQI6DaJhU2BfijPe/KHkib55mvHzIVvoexxya26nk79F2c+d9PnuuMdThWQO3El5a/i2AASnM7T7piIBT2WRZW2i8RbfJaTT7G7LP7OpMKIV1qyBg/cWoO7cIWQW4jmzqrNryIkF0AzStLN1DxvnQZwgXBGv0CwuAkfQuNSLu0PQgPp0PhdukNZFllv5D29IhPr0Z+kwPtrAgPQo+lHlOBHBMUpDT4XChTPeAvMaUSBsqmonAE8UUHEabWrqYN/kWNHCNkYXMkiVmK1AgMBAAGjggERMIIBDTAdBgNVHQ4EFgQU456ijsFrYnzHBShLAPpOUqQ+Z2cwgd0GA1UdIwSB1TCB0oAU456ijsFrYnzHBShLAPpOUqQ+Z2ehga6kgaswgagxCzAJBgNVBAYTAkhLMRAwDgYDVQQIEwdDZW50cmFsMQswCQYDVQQHEwJISzEYMBYGA1UEChMPU2VjdXJlLVNlcnZlckNBMQswCQYDVQQLEwJJVDEYMBYGA1UEAxMPU2VjdXJlLVNlcnZlckNBMRgwFgYDVQQpEw9TZWN1cmUtU2VydmVyQ0ExHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQDI1xaHqObkpTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCvga2HMwOtUxWH/inL2qk24KX2pxLg939JNhqoyNrUpbDHag5xPQYXUmUpKrNJZ0z+o/ZnNUPHydTSXE7Z7E45J0GDN5E7g4pakndKnDLSjp03NgGsCGW+cXnz6UBPM5FStFvGdDeModeSUyoS9fjk+mYROvmiy5EiVDP91sKGcPLR7Ym0M7zl2aaqV7bb98HmMoBOxpeZQinof67nKrCsgz/xjktWFgcmPl4/PQSsmqQD0fTtWxGuRX+FzwvF2OCMCAJgp1RqJNlk2g50/kBIoJVPPCfjDFeDU5zGaWGSQ9+z1L6/z7VXdjUiHL0ouOcHwbiS4ZjTr9nMn6WdAHU2"
|
||||
PurevpnCertificate = "MIIEnzCCA4egAwIBAgIBAzANBgkqhkiG9w0BAQsFADCBqDELMAkGA1UEBhMCSEsxEDAOBgNVBAgTB0NlbnRyYWwxCzAJBgNVBAcTAkhLMRgwFgYDVQQKEw9TZWN1cmUtU2VydmVyQ0ExCzAJBgNVBAsTAklUMRgwFgYDVQQDEw9TZWN1cmUtU2VydmVyQ0ExGDAWBgNVBCkTD1NlY3VyZS1TZXJ2ZXJDQTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRvbWFpbjAeFw0xNjAxMTUxNjE1MzhaFw0yNjAxMTIxNjE1MzhaMIGdMQswCQYDVQQGEwJISzEQMA4GA1UECBMHQ2VudHJhbDELMAkGA1UEBxMCSEsxFjAUBgNVBAoTDVNlY3VyZS1DbGllbnQxCzAJBgNVBAsTAklUMRYwFAYDVQQDEw1TZWN1cmUtQ2xpZW50MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRvbWFpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxsnyn4v6xxDPnuDaYS0b9M1N8nxgg7OBPBlK+FWRxdTQ8yxt5U5CZGm7riVp7fya2J2iPZIgmHQEv/KbxztsHAVlYSfYYlalrnhEL3bDP2tY+N43AwB1k5BrPq2s1pPLT2XG951drDKG4PUuFHUP1sHzW5oQlfVCmxgIMAP8OYkCAwEAAaOCAV8wggFbMAkGA1UdEwQCMAAwLQYJYIZIAYb4QgENBCAWHkVhc3ktUlNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU9MwUnUDbQKKZKjoeieD2OD5NlAEwgd0GA1UdIwSB1TCB0oAU456ijsFrYnzHBShLAPpOUqQ+Z2ehga6kgaswgagxCzAJBgNVBAYTAkhLMRAwDgYDVQQIEwdDZW50cmFsMQswCQYDVQQHEwJISzEYMBYGA1UEChMPU2VjdXJlLVNlcnZlckNBMQswCQYDVQQLEwJJVDEYMBYGA1UEAxMPU2VjdXJlLVNlcnZlckNBMRgwFgYDVQQpEw9TZWN1cmUtU2VydmVyQ0ExHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQDI1xaHqObkpTATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADggEBAFyFo2VUX/UFixsdPdK9/Yt6mkCWc+XS1xbapGXXb9U1d+h1iBCIV9odUHgNCXWpz1hR5Uu/OCzaZ0asLE4IFMZlQmJs8sMT0c1tfPPGW45vxbL0lhqnQ8PNcBH7huNK7VFjUh4szXRKmaQPaM4S91R3L4CaNfVeHfAg7mN2m9Zn5Gto1Q1/CFMGKu2hxwGEw5p+X1czBWEvg/O09ckx/ggkkI1NcZsNiYQ+6Pz8DdGGX3+05YwLZu94+O6iIMrzxl/il0eK83g3YPbsOrASARvw6w/8sOnJCK5eOacl21oww875KisnYdWjHB1FiI+VzQ1/gyoDsL5kPTJVuu2CoG8="
|
||||
PurevpnKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMbJ8p+L+scQz57g2mEtG/TNTfJ8YIOzgTwZSvhVkcXU0PMsbeVOQmRpu64lae38mtidoj2SIJh0BL/ym8c7bBwFZWEn2GJWpa54RC92wz9rWPjeNwMAdZOQaz6trNaTy09lxvedXawyhuD1LhR1D9bB81uaEJX1QpsYCDAD/DmJAgMBAAECgYEAvTHbDupE5U0krUvHzBEIuHblptGlcfNYHoDcD3oxYR3pOGeiuElBexv+mgHVzcFLBrsQfJUlHLPfCWi3xmjRvDQcr7N7U1u7NIzazy/PpRBaKolMRiM1KMYi2DG0i4ZONwFT8bvNHOIrZzCLY54KDrqOn55OzC70WYjWh4t5evkCQQDkkzZUAeskBC9+JP/zLps8jhwfoLBWGw/zbC9ePDmX0N8MTZdcUpg6KUTf1wbkLUyVtIRjS2ao6qu1jWG6K0x3AkEA3qPWyaWQWCynhNDqu2U1cPb2kh5AJip+gqxO3emikAdajsSxeoyEC2AfyBITbeB1tvCUZH17J4i/0+OFTEQp/wJAb/zEOGJ8PzghwK8GC7JA8mk51DEZVAaMSRovFv9wxDXcoh191AjPdmdzzCuAv9iF1i8MUc3GbWoUWK39PIYsPwJAWh63sqfx5b8tj/WBDpnJKBDPfhYAoXJSA1L8GZeY1fQkE+ZKcPCwAmrGcpXeh3t0Krj3WDXyw+32uC5Apr5wwQJAPZwOOReaC4YNfBPZN9BdHvVjOYGGUffpI+X+hWpLRnQFJteAi+eqwyk0Oi0SkJB+a7jcerK2d7q7xhec5WHlng=="
|
||||
PurevpnOpenvpnStaticKeyV1 = "e30af995f56d07426d9ba1f824730521d4283db4b4d0cdda9c6e8759a3799dcb7939b6a5989160c9660de0f6125cbb1f585b41c074b2fe88ecfcf17eab9a33be1352379cdf74952b588fb161a93e13df9135b2b29038231e02d657a6225705e6868ccb0c384ed11614690a1894bfbeb274cebf1fe9c2329bdd5c8a40fe8820624d2ea7540cd79ab76892db51fc371a3ac5fc9573afecb3fffe3281e61d72e91579d9b03d8cbf7909b3aebf4d90850321ee6b7d0a7846d15c27d8290e031e951e19438a4654663cad975e138f5bc5af89c737ad822f27e19057731f41e1e254cc9c95b7175c622422cde9f1f2cfd3510add94498b4d7133d3729dd214a16b27fb"
|
||||
)
|
||||
|
||||
func PurevpnRegionChoices() (choices []string) {
|
||||
servers := PurevpnServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Region
|
||||
}
|
||||
return choices
|
||||
}
|
||||
|
||||
func PurevpnCountryChoices() (choices []string) {
|
||||
servers := PurevpnServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Country
|
||||
}
|
||||
return choices
|
||||
}
|
||||
|
||||
func PurevpnCityChoices() (choices []string) {
|
||||
servers := PurevpnServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].City
|
||||
}
|
||||
return choices
|
||||
}
|
||||
|
||||
//nolint:lll
|
||||
func PurevpnServers() []models.PurevpnServer {
|
||||
return []models.PurevpnServer{
|
||||
{Country: "Australia", Region: "New South Wales", City: "Sydney", IPs: []net.IP{{192, 253, 241, 4}, {43, 245, 161, 85}}},
|
||||
{Country: "Australia", Region: "Western Australia", City: "Perth", IPs: []net.IP{{172, 94, 123, 4}}},
|
||||
{Country: "Austria", Region: "Lower Austria", City: "Langenzersdorf", IPs: []net.IP{{172, 94, 109, 4}}},
|
||||
{Country: "Austria", Region: "Vienna", City: "Vienna", IPs: []net.IP{{217, 64, 127, 252}}},
|
||||
{Country: "Belgium", Region: "Flanders", City: "Zaventem", IPs: []net.IP{{172, 111, 223, 4}}},
|
||||
{Country: "Bulgaria", Region: "Sofia-Capital", City: "Sofia", IPs: []net.IP{{217, 138, 221, 120}}},
|
||||
{Country: "Canada", Region: "Alberta", City: "Calgary", IPs: []net.IP{{172, 94, 34, 4}}},
|
||||
{Country: "Canada", Region: "Ontario", City: "Toronto", IPs: []net.IP{{104, 200, 138, 196}}},
|
||||
{Country: "France", Region: "Île-de-France", City: "Paris", IPs: []net.IP{{89, 40, 183, 178}}},
|
||||
{Country: "Germany", Region: "Hesse", City: "Frankfurt am Main", IPs: []net.IP{{188, 72, 84, 4}}},
|
||||
{Country: "Greece", Region: "Central Macedonia", City: "Thessaloníki", IPs: []net.IP{{178, 21, 169, 244}}},
|
||||
{Country: "Hong Kong", Region: "Central and Western", City: "Central", IPs: []net.IP{{141, 101, 168, 4}, {141, 101, 168, 4}}},
|
||||
{Country: "Hong Kong", Region: "Central and Western", City: "Hong Kong", IPs: []net.IP{{43, 226, 231, 6}, {172, 111, 168, 4}}},
|
||||
{Country: "India", Region: "Tamil Nadu", City: "Chennai", IPs: []net.IP{{129, 227, 107, 242}}},
|
||||
{Country: "Italy", Region: "Trentino-Alto Adige", City: "Trento", IPs: []net.IP{{172, 111, 173, 3}}},
|
||||
{Country: "Japan", Region: "Ōsaka", City: "Osaka", IPs: []net.IP{{172, 94, 56, 4}}},
|
||||
{Country: "Malaysia", Region: "Kuala Lumpur", City: "Kuala Lumpur", IPs: []net.IP{{103, 55, 10, 133}}},
|
||||
{Country: "Netherlands", Region: "North Holland", City: "Amsterdam", IPs: []net.IP{{5, 254, 73, 172}}},
|
||||
{Country: "Norway", Region: "Oslo", City: "Oslo", IPs: []net.IP{{82, 102, 22, 212}}},
|
||||
{Country: "Panama", Region: "Panamá", City: "Panamá", IPs: []net.IP{{104, 243, 243, 131}}},
|
||||
{Country: "Philippines", Region: "Metro Manila", City: "Quezon City", IPs: []net.IP{{129, 227, 119, 84}}},
|
||||
{Country: "Poland", Region: "Mazovia", City: "Warsaw", IPs: []net.IP{{5, 253, 206, 251}}},
|
||||
{Country: "Portugal", Region: "Lisbon", City: "Lisbon", IPs: []net.IP{{5, 154, 174, 3}}},
|
||||
{Country: "Russian Federation", Region: "Moscow", City: "Moscow", IPs: []net.IP{{46, 243, 220, 2}, {206, 123, 139, 4}}},
|
||||
{Country: "Singapore", Region: "Singapore", City: "Singapore", IPs: []net.IP{{37, 120, 208, 147}}},
|
||||
{Country: "South Africa", Region: "Gauteng", City: "Johannesburg", IPs: []net.IP{{102, 165, 3, 33}}},
|
||||
{Country: "Spain", Region: "Madrid", City: "Madrid", IPs: []net.IP{{217, 138, 218, 210}}},
|
||||
{Country: "Sweden", Region: "Stockholm", City: "Stockholm", IPs: []net.IP{{86, 106, 103, 139}}},
|
||||
{Country: "Switzerland", Region: "Zurich", City: "Zürich", IPs: []net.IP{{45, 12, 222, 103}}},
|
||||
{Country: "Taiwan", Region: "Taiwan", City: "Taipei", IPs: []net.IP{{128, 1, 155, 178}}},
|
||||
{Country: "United Arab Emirates", Region: "Dubai", City: "Dubai", IPs: []net.IP{{104, 37, 6, 4}}},
|
||||
{Country: "United Kingdom", Region: "England", City: "Birmingham", IPs: []net.IP{{188, 72, 89, 4}}},
|
||||
{Country: "United Kingdom", Region: "England", City: "London", IPs: []net.IP{{45, 141, 154, 71}, {45, 141, 154, 71}}},
|
||||
{Country: "United States", Region: "California", City: "Los Angeles", IPs: []net.IP{{172, 111, 147, 4}}},
|
||||
{Country: "United States", Region: "California", City: "South San Francisco", IPs: []net.IP{{141, 101, 166, 4}, {141, 101, 166, 4}, {141, 101, 166, 4}}},
|
||||
{Country: "United States", Region: "Florida", City: "Miami", IPs: []net.IP{{5, 254, 79, 115}}},
|
||||
{Country: "United States", Region: "Massachusetts", City: "Newton", IPs: []net.IP{{104, 243, 244, 2}}},
|
||||
{Country: "United States", Region: "Michigan", City: "Ypsilanti", IPs: []net.IP{{172, 111, 149, 4}}},
|
||||
{Country: "United States", Region: "Texas", City: "Dallas", IPs: []net.IP{{208, 84, 155, 100}}},
|
||||
{Country: "United States", Region: "Utah", City: "Salt Lake City", IPs: []net.IP{{45, 74, 52, 4}, {45, 74, 52, 4}, {45, 74, 52, 4}, {45, 74, 52, 4}, {45, 74, 52, 4}, {45, 74, 52, 4}}},
|
||||
{Country: "Vietnam", Region: "Hanoi", City: "Cầu Giấy", IPs: []net.IP{{192, 253, 249, 132}}},
|
||||
}
|
||||
}
|
||||
55
internal/constants/servers.go
Normal file
55
internal/constants/servers.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package constants
|
||||
|
||||
import "github.com/qdm12/gluetun/internal/models"
|
||||
|
||||
func GetAllServers() (allServers models.AllServers) {
|
||||
//nolint:gomnd
|
||||
return models.AllServers{
|
||||
Version: 1, // used for migration of the top level scheme
|
||||
Cyberghost: models.CyberghostServers{
|
||||
Version: 1, // model version
|
||||
Timestamp: 1599323261, // latest takes precedence
|
||||
Servers: CyberghostServers(),
|
||||
},
|
||||
Mullvad: models.MullvadServers{
|
||||
Version: 1,
|
||||
Timestamp: 1603660367,
|
||||
Servers: MullvadServers(),
|
||||
},
|
||||
Nordvpn: models.NordvpnServers{
|
||||
Version: 1,
|
||||
Timestamp: 1611096594,
|
||||
Servers: NordvpnServers(),
|
||||
},
|
||||
Pia: models.PiaServers{
|
||||
Version: 2,
|
||||
Timestamp: 1609343591,
|
||||
Servers: PIAServers(),
|
||||
},
|
||||
Purevpn: models.PurevpnServers{
|
||||
Version: 1,
|
||||
Timestamp: 1609448478,
|
||||
Servers: PurevpnServers(),
|
||||
},
|
||||
Privado: models.PrivadoServers{
|
||||
Version: 2,
|
||||
Timestamp: 1604963273,
|
||||
Servers: PrivadoServers(),
|
||||
},
|
||||
Surfshark: models.SurfsharkServers{
|
||||
Version: 1,
|
||||
Timestamp: 1599957644,
|
||||
Servers: SurfsharkServers(),
|
||||
},
|
||||
Vyprvpn: models.VyprvpnServers{
|
||||
Version: 1,
|
||||
Timestamp: 1599323261,
|
||||
Servers: VyprvpnServers(),
|
||||
},
|
||||
Windscribe: models.WindscribeServers{
|
||||
Version: 2,
|
||||
Timestamp: 1604019438,
|
||||
Servers: WindscribeServers(),
|
||||
},
|
||||
}
|
||||
}
|
||||
174
internal/constants/servers_test.go
Normal file
174
internal/constants/servers_test.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func digestServerModelVersion(t *testing.T, server interface{}, version uint16) string {
|
||||
bytes, err := json.Marshal(server)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bytes = append(bytes, []byte(fmt.Sprintf("%d", version))...)
|
||||
arr := sha256.Sum256(bytes)
|
||||
hexString := hex.EncodeToString(arr[:])
|
||||
if len(hexString) > 8 {
|
||||
hexString = hexString[:8]
|
||||
}
|
||||
return hexString
|
||||
}
|
||||
|
||||
func Test_versions(t *testing.T) {
|
||||
t.Parallel()
|
||||
allServers := GetAllServers()
|
||||
const format = "you forgot to update the version for %s"
|
||||
testCases := map[string]struct {
|
||||
model interface{}
|
||||
version uint16
|
||||
digest string
|
||||
}{
|
||||
"Cyberghost": {
|
||||
model: models.CyberghostServer{},
|
||||
version: allServers.Cyberghost.Version,
|
||||
digest: "fd6242bb",
|
||||
},
|
||||
"Mullvad": {
|
||||
model: models.MullvadServer{},
|
||||
version: allServers.Mullvad.Version,
|
||||
digest: "665e9dc1",
|
||||
},
|
||||
"Nordvpn": {
|
||||
model: models.NordvpnServer{},
|
||||
version: allServers.Nordvpn.Version,
|
||||
digest: "040de8d0",
|
||||
},
|
||||
"Private Internet Access": {
|
||||
model: models.PIAServer{},
|
||||
version: allServers.Pia.Version,
|
||||
digest: "f1e01afe",
|
||||
},
|
||||
"Privado": {
|
||||
model: models.PrivadoServer{},
|
||||
version: allServers.Privado.Version,
|
||||
digest: "1d5aeb23",
|
||||
},
|
||||
"Purevpn": {
|
||||
model: models.PurevpnServer{},
|
||||
version: allServers.Purevpn.Version,
|
||||
digest: "ada45379",
|
||||
},
|
||||
"Surfshark": {
|
||||
model: models.SurfsharkServer{},
|
||||
version: allServers.Surfshark.Version,
|
||||
digest: "042bef64",
|
||||
},
|
||||
"Vyprvpn": {
|
||||
model: models.VyprvpnServer{},
|
||||
version: allServers.Vyprvpn.Version,
|
||||
digest: "042bef64",
|
||||
},
|
||||
"Windscribe": {
|
||||
model: models.WindscribeServer{},
|
||||
version: allServers.Windscribe.Version,
|
||||
digest: "6e3ca639",
|
||||
},
|
||||
}
|
||||
for name, testCase := range testCases {
|
||||
name := name
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
digest := digestServerModelVersion(t, testCase.model, testCase.version)
|
||||
failureMessage := fmt.Sprintf(format, name)
|
||||
assert.Equal(t, testCase.digest, digest, failureMessage)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func digestServersTimestamp(t *testing.T, servers interface{}, timestamp int64) string {
|
||||
bytes, err := json.Marshal(servers)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bytes = append(bytes, []byte(fmt.Sprintf("%d", timestamp))...)
|
||||
arr := sha256.Sum256(bytes)
|
||||
hexString := hex.EncodeToString(arr[:])
|
||||
if len(hexString) > 8 {
|
||||
hexString = hexString[:8]
|
||||
}
|
||||
return hexString
|
||||
}
|
||||
|
||||
func Test_timestamps(t *testing.T) {
|
||||
t.Parallel()
|
||||
allServers := GetAllServers()
|
||||
const format = "you forgot to update the timestamp for %s"
|
||||
testCases := map[string]struct {
|
||||
servers interface{}
|
||||
timestamp int64
|
||||
digest string
|
||||
}{
|
||||
"Cyberghost": {
|
||||
servers: allServers.Cyberghost.Servers,
|
||||
timestamp: allServers.Cyberghost.Timestamp,
|
||||
digest: "160631de",
|
||||
},
|
||||
"Mullvad": {
|
||||
servers: allServers.Mullvad.Servers,
|
||||
timestamp: allServers.Mullvad.Timestamp,
|
||||
digest: "93859652",
|
||||
},
|
||||
"Nordvpn": {
|
||||
servers: allServers.Nordvpn.Servers,
|
||||
timestamp: allServers.Nordvpn.Timestamp,
|
||||
digest: "5f70b19b",
|
||||
},
|
||||
"Private Internet Access": {
|
||||
servers: allServers.Pia.Servers,
|
||||
timestamp: allServers.Pia.Timestamp,
|
||||
digest: "d797f112",
|
||||
},
|
||||
"Purevpn": {
|
||||
servers: allServers.Purevpn.Servers,
|
||||
timestamp: allServers.Purevpn.Timestamp,
|
||||
digest: "8abe18d4",
|
||||
},
|
||||
"Privado": {
|
||||
servers: allServers.Privado.Servers,
|
||||
timestamp: allServers.Privado.Timestamp,
|
||||
digest: "df2a046d",
|
||||
},
|
||||
"Surfshark": {
|
||||
servers: allServers.Surfshark.Servers,
|
||||
timestamp: allServers.Surfshark.Timestamp,
|
||||
digest: "79484811",
|
||||
},
|
||||
"Vyprvpn": {
|
||||
servers: allServers.Vyprvpn.Servers,
|
||||
timestamp: allServers.Vyprvpn.Timestamp,
|
||||
digest: "1992457c",
|
||||
},
|
||||
"Windscribe": {
|
||||
servers: allServers.Windscribe.Servers,
|
||||
timestamp: allServers.Windscribe.Timestamp,
|
||||
digest: "fd87502e",
|
||||
},
|
||||
}
|
||||
for name, testCase := range testCases {
|
||||
name := name
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
digest := digestServersTimestamp(t, testCase.servers, testCase.timestamp)
|
||||
failureMessage := fmt.Sprintf(format, name)
|
||||
assert.Equal(t, testCase.digest, digest, failureMessage)
|
||||
})
|
||||
}
|
||||
}
|
||||
13
internal/constants/splash.go
Normal file
13
internal/constants/splash.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
// Announcement is a message announcement.
|
||||
Announcement = "New Docker image qmcgaw/gluetun"
|
||||
// AnnouncementExpiration is the expiration date of the announcement in format yyyy-mm-dd.
|
||||
AnnouncementExpiration = "2021-01-20"
|
||||
)
|
||||
|
||||
const (
|
||||
// IssueLink is the link for users to use to create issues.
|
||||
IssueLink = "https://github.com/qdm12/gluetun/issues/new"
|
||||
)
|
||||
17
internal/constants/splash_test.go
Normal file
17
internal/constants/splash_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_AnnouncementExpiration(t *testing.T) {
|
||||
t.Parallel()
|
||||
if len(AnnouncementExpiration) == 0 {
|
||||
return
|
||||
}
|
||||
_, err := time.Parse("2006-01-02", AnnouncementExpiration)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
14
internal/constants/status.go
Normal file
14
internal/constants/status.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
const (
|
||||
Starting models.LoopStatus = "starting"
|
||||
Running models.LoopStatus = "running"
|
||||
Stopping models.LoopStatus = "stopping"
|
||||
Stopped models.LoopStatus = "stopped"
|
||||
Crashed models.LoopStatus = "crashed"
|
||||
Completed models.LoopStatus = "completed"
|
||||
)
|
||||
185
internal/constants/surfshark.go
Normal file
185
internal/constants/surfshark.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
const (
|
||||
SurfsharkCertificate = "MIIFTTCCAzWgAwIBAgIJAMs9S3fqwv+mMA0GCSqGSIb3DQEBCwUAMD0xCzAJBgNVBAYTAlZHMRIwEAYDVQQKDAlTdXJmc2hhcmsxGjAYBgNVBAMMEVN1cmZzaGFyayBSb290IENBMB4XDTE4MDMxNDA4NTkyM1oXDTI4MDMxMTA4NTkyM1owPTELMAkGA1UEBhMCVkcxEjAQBgNVBAoMCVN1cmZzaGFyazEaMBgGA1UEAwwRU3VyZnNoYXJrIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDEGMNj0aisM63oSkmVJyZPaYX7aPsZtzsxo6m6p5Wta3MGASoryRsBuRaH6VVa0fwbI1nw5ubyxkuaNa4v3zHVwuSq6F1p8S811+1YP1av+jqDcMyojH0ujZSHIcb/i5LtaHNXBQ3qN48Cc7sqBnTIIFpmb5HthQ/4pW+a82b1guM5dZHsh7q+LKQDIGmvtMtO1+NEnmj81BApFayiaD1ggvwDI4x7o/Y3ksfWSCHnqXGyqzSFLh8QuQrTmWUm84YHGFxoI1/8AKdIyVoB6BjcaMKtKs/pbctk6vkzmYf0XmGovDKPQF6MwUekchLjB5gSBNnptSQ9kNgnTLqi0OpSwI6ixX52Ksva6UM8P01ZIhWZ6ua/T/tArgODy5JZMW+pQ1A6L0b7egIeghpwKnPRG+5CzgO0J5UE6gv000mqbmC3CbiS8xi2xuNgruAyY2hUOoV9/BuBev8ttE5ZCsJH3YlG6NtbZ9hPc61GiBSx8NJnX5QHyCnfic/X87eST/amZsZCAOJ5v4EPSaKrItt+HrEFWZQIq4fJmHJNNbYvWzCE08AL+5/6Z+lxb/Bm3dapx2zdit3x2e+miGHekuiE8lQWD0rXD4+T+nDRi3X+kyt8Ex/8qRiUfrisrSHFzVMRungIMGdO9O/zCINFrb7wahm4PqU2f12Z9TRCOTXciQIDAQABo1AwTjAdBgNVHQ4EFgQUYRpbQwyDahLMN3F2ony3+UqOYOgwHwYDVR0jBBgwFoAUYRpbQwyDahLMN3F2ony3+UqOYOgwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAn9zV7F/XVnFNZhHFrt0ZS1Yqz+qM9CojLmiyblMFh0p7t+Hh+VKVgMwrz0LwDH4UsOosXA28eJPmech6/bjfymkoXISy/NUSTFpUChGO9RabGGxJsT4dugOw9MPaIVZffny4qYOc/rXDXDSfF2b+303lLPI43y9qoe0oyZ1vtk/UKG75FkWfFUogGNbpOkuz+et5Y0aIEiyg0yh6/l5Q5h8+yom0HZnREHhqieGbkaGKLkyu7zQ4D4tRK/mBhd8nv+09GtPEG+D5LPbabFVxKjBMP4Vp24WuSUOqcGSsURHevawPVBfgmsxf1UCjelaIwngdh6WfNCRXa5QQPQTKubQvkvXONCDdhmdXQccnRX1nJWhPYi0onffvjsWUfztRypsKzX4dvM9k7xnIcGSGEnCC4RCgt1UiZIj7frcCMssbA6vJ9naM0s7JF7N3VKeHJtqe1OCRHMYnWUZt9vrqX6IoIHlZCoLlv39wFW9QNxelcAOCVbD+19MZ0ZXt7LitjIqe7yF5WxDQN4xru087FzQ4Hfj7eH1SNLLyKZkA1eecjmRoi/OoqAt7afSnwtQLtMUc2bQDg6rHt5C0e4dCLqP/9PGZTSJiwmtRHJ/N5qYWIh9ju83APvLm/AGBTR2pXmj9G3KdVOkpIC7L35dI623cSEC3Q3UZutsEm/UplsM="
|
||||
SurfsharkOpenvpnStaticKeyV1 = "b02cb1d7c6fee5d4f89b8de72b51a8d0c7b282631d6fc19be1df6ebae9e2779e6d9f097058a31c97f57f0c35526a44ae09a01d1284b50b954d9246725a1ead1ff224a102ed9ab3da0152a15525643b2eee226c37041dc55539d475183b889a10e18bb94f079a4a49888da566b99783460ece01daaf93548beea6c827d9674897e7279ff1a19cb092659e8c1860fbad0db4ad0ad5732f1af4655dbd66214e552f04ed8fd0104e1d4bf99c249ac229ce169d9ba22068c6c0ab742424760911d4636aafb4b85f0c952a9ce4275bc821391aa65fcd0d2394f006e3fba0fd34c4bc4ab260f4b45dec3285875589c97d3087c9134d3a3aa2f904512e85aa2dc2202498"
|
||||
)
|
||||
|
||||
func SurfsharkRegionChoices() (choices []string) {
|
||||
servers := SurfsharkServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Region
|
||||
}
|
||||
return choices
|
||||
}
|
||||
|
||||
func SurfsharkServers() []models.SurfsharkServer {
|
||||
return []models.SurfsharkServer{
|
||||
{Region: "Albania", IPs: []net.IP{{31, 171, 152, 197}, {31, 171, 154, 147}}},
|
||||
{Region: "Argentina Buenos Aires", IPs: []net.IP{{91, 206, 168, 13}, {91, 206, 168, 24}}},
|
||||
{Region: "Australia Adelaide", IPs: []net.IP{{45, 248, 79, 67}, {45, 248, 79, 69}}},
|
||||
{Region: "Australia Brisbane", IPs: []net.IP{{144, 48, 39, 107}, {144, 48, 39, 131}}},
|
||||
{Region: "Australia Melbourne", IPs: []net.IP{{103, 192, 80, 141}, {144, 48, 38, 141}}},
|
||||
{Region: "Australia Perth", IPs: []net.IP{{45, 248, 78, 45}, {124, 150, 139, 27}}},
|
||||
{Region: "Australia Sydney", IPs: []net.IP{{45, 125, 247, 195}, {180, 149, 228, 117}}},
|
||||
{Region: "Australia US", IPs: []net.IP{{45, 76, 117, 108}}},
|
||||
{Region: "Austria", IPs: []net.IP{{5, 253, 207, 83}, {37, 120, 212, 131}}},
|
||||
{Region: "Azerbaijan", IPs: []net.IP{{94, 20, 21, 85}, {94, 20, 21, 87}}},
|
||||
{Region: "Belgium", IPs: []net.IP{{5, 253, 205, 181}, {5, 253, 205, 213}}},
|
||||
{Region: "Bosnia and Herzegovina", IPs: []net.IP{{185, 99, 3, 7}, {185, 212, 111, 41}}},
|
||||
{Region: "Brazil", IPs: []net.IP{{191, 96, 73, 214}, {191, 96, 73, 216}}},
|
||||
{Region: "Bulgaria", IPs: []net.IP{{37, 120, 152, 37}}},
|
||||
{Region: "Canada Montreal", IPs: []net.IP{{172, 98, 82, 85}, {198, 8, 85, 19}}},
|
||||
{Region: "Canada Toronto", IPs: []net.IP{{68, 71, 244, 200}, {104, 200, 138, 163}}},
|
||||
{Region: "Canada Toronto mp001", IPs: []net.IP{{138, 197, 151, 26}}},
|
||||
{Region: "Canada US", IPs: []net.IP{{159, 203, 57, 80}}},
|
||||
{Region: "Canada Vancouver", IPs: []net.IP{{66, 115, 147, 79}, {66, 115, 147, 87}}},
|
||||
{Region: "Chile", IPs: []net.IP{{31, 169, 121, 16}}},
|
||||
{Region: "Colombia", IPs: []net.IP{{45, 129, 32, 8}}},
|
||||
{Region: "Costa Rica", IPs: []net.IP{{176, 227, 241, 19}, {176, 227, 241, 21}}},
|
||||
{Region: "Croatia", IPs: []net.IP{{89, 164, 99, 109}, {89, 164, 99, 111}}},
|
||||
{Region: "Cyprus", IPs: []net.IP{{195, 47, 194, 34}}},
|
||||
{Region: "Czech Republic", IPs: []net.IP{{185, 152, 64, 151}, {185, 152, 64, 178}}},
|
||||
{Region: "Denmark", IPs: []net.IP{{37, 120, 194, 115}, {95, 174, 65, 71}}},
|
||||
{Region: "Estonia", IPs: []net.IP{{165, 231, 163, 23}, {185, 174, 159, 69}}},
|
||||
{Region: "Finland", IPs: []net.IP{{196, 244, 191, 179}, {196, 244, 191, 181}}},
|
||||
{Region: "France Bordeaux", IPs: []net.IP{{185, 108, 106, 67}, {185, 108, 106, 150}}},
|
||||
{Region: "France Marseilles", IPs: []net.IP{{185, 166, 84, 53}, {185, 166, 84, 75}}},
|
||||
{Region: "France Paris", IPs: []net.IP{{45, 83, 90, 181}, {45, 89, 174, 103}}},
|
||||
{Region: "France Sweden", IPs: []net.IP{{199, 247, 8, 20}}},
|
||||
{Region: "Germany Berlin", IPs: []net.IP{{152, 89, 163, 19}, {217, 138, 216, 243}}},
|
||||
{Region: "Germany Frankfurt am Main", IPs: []net.IP{{185, 158, 135, 36}, {185, 220, 70, 83}}},
|
||||
{Region: "Germany Frankfurt am Main st001", IPs: []net.IP{{45, 87, 212, 179}}},
|
||||
{Region: "Germany Frankfurt am Main st002", IPs: []net.IP{{45, 87, 212, 181}}},
|
||||
{Region: "Germany Frankfurt am Main st003", IPs: []net.IP{{45, 87, 212, 183}}},
|
||||
{Region: "Germany Frankfurt mp001", IPs: []net.IP{{46, 101, 189, 14}}},
|
||||
{Region: "Germany Munich", IPs: []net.IP{{178, 238, 231, 51}, {178, 238, 231, 55}}},
|
||||
{Region: "Germany Nuremberg", IPs: []net.IP{{62, 171, 149, 162}, {62, 171, 151, 182}}},
|
||||
{Region: "Germany Singapour", IPs: []net.IP{{159, 89, 14, 157}}},
|
||||
{Region: "Germany UK", IPs: []net.IP{{46, 101, 250, 73}}},
|
||||
{Region: "Greece", IPs: []net.IP{{194, 150, 167, 34}, {194, 150, 167, 40}}},
|
||||
{Region: "Hong Kong", IPs: []net.IP{{84, 17, 57, 73}, {212, 102, 42, 201}}},
|
||||
{Region: "Hungary", IPs: []net.IP{{37, 120, 144, 151}, {37, 120, 144, 213}}},
|
||||
{Region: "Iceland", IPs: []net.IP{{82, 221, 128, 166}, {82, 221, 143, 243}}},
|
||||
{Region: "India Chennai", IPs: []net.IP{{103, 108, 117, 118}, {103, 108, 117, 151}}},
|
||||
{Region: "India Indore", IPs: []net.IP{{103, 39, 132, 189}, {137, 59, 52, 109}}},
|
||||
{Region: "India Mumbai", IPs: []net.IP{{103, 221, 233, 88}, {165, 231, 253, 147}}},
|
||||
{Region: "India UK", IPs: []net.IP{{134, 209, 148, 122}}},
|
||||
{Region: "Indonesia", IPs: []net.IP{{103, 120, 66, 214}, {103, 120, 66, 216}}},
|
||||
{Region: "Ireland", IPs: []net.IP{{185, 108, 128, 159}, {185, 108, 128, 181}}},
|
||||
{Region: "Israel", IPs: []net.IP{{87, 239, 255, 109}}},
|
||||
{Region: "Italy Milan", IPs: []net.IP{{84, 17, 58, 148}, {185, 128, 27, 37}}},
|
||||
{Region: "Italy Rome", IPs: []net.IP{{82, 102, 26, 61}, {82, 102, 26, 115}}},
|
||||
{Region: "Japan Tokyo", IPs: []net.IP{{45, 87, 213, 5}, {103, 208, 221, 227}}},
|
||||
{Region: "Japan Tokyo st001", IPs: []net.IP{{45, 87, 213, 19}}},
|
||||
{Region: "Japan Tokyo st002", IPs: []net.IP{{45, 87, 213, 21}}},
|
||||
{Region: "Japan Tokyo st003", IPs: []net.IP{{45, 87, 213, 23}}},
|
||||
{Region: "Japan Tokyo st004", IPs: []net.IP{{217, 138, 212, 19}}},
|
||||
{Region: "Japan Tokyo st005", IPs: []net.IP{{217, 138, 212, 21}}},
|
||||
{Region: "Japan Tokyo st006", IPs: []net.IP{{82, 102, 28, 123}}},
|
||||
{Region: "Japan Tokyo st007", IPs: []net.IP{{82, 102, 28, 125}}},
|
||||
{Region: "Kazakhstan", IPs: []net.IP{{95, 57, 207, 200}}},
|
||||
{Region: "Korea", IPs: []net.IP{{61, 14, 210, 239}, {61, 97, 243, 112}}},
|
||||
{Region: "Latvia", IPs: []net.IP{{188, 92, 78, 140}, {188, 92, 78, 142}}},
|
||||
{Region: "Libya", IPs: []net.IP{{41, 208, 72, 157}, {41, 208, 72, 204}}},
|
||||
{Region: "Luxembourg", IPs: []net.IP{{185, 153, 151, 60}, {185, 153, 151, 83}}},
|
||||
{Region: "Malaysia", IPs: []net.IP{{42, 0, 30, 152}, {42, 0, 30, 209}}},
|
||||
{Region: "Mexico City Mexico", IPs: []net.IP{{194, 41, 112, 9}, {194, 41, 112, 19}}},
|
||||
{Region: "Moldova", IPs: []net.IP{{178, 175, 128, 235}, {178, 175, 128, 237}}},
|
||||
{Region: "Netherlands Amsterdam", IPs: []net.IP{{89, 46, 223, 74}, {89, 46, 223, 217}}},
|
||||
{Region: "Netherlands Amsterdam mp001", IPs: []net.IP{{188, 166, 43, 117}}},
|
||||
{Region: "Netherlands Amsterdam st001", IPs: []net.IP{{81, 19, 209, 51}}},
|
||||
{Region: "Netherlands US", IPs: []net.IP{{188, 166, 98, 91}}},
|
||||
{Region: "New Zealand", IPs: []net.IP{{180, 149, 231, 67}}},
|
||||
{Region: "Nigeria", IPs: []net.IP{{102, 165, 23, 6}, {102, 165, 23, 42}}},
|
||||
{Region: "North Macedonia", IPs: []net.IP{{185, 225, 28, 93}, {185, 225, 28, 101}}},
|
||||
{Region: "Norway", IPs: []net.IP{{45, 12, 223, 213}, {84, 247, 50, 69}}},
|
||||
{Region: "Paraguay", IPs: []net.IP{{181, 40, 18, 56}, {186, 16, 32, 163}}},
|
||||
{Region: "Philippines", IPs: []net.IP{{45, 134, 224, 10}, {45, 134, 224, 20}}},
|
||||
{Region: "Poland Gdansk", IPs: []net.IP{{5, 187, 53, 51}}},
|
||||
{Region: "Poland Warsaw", IPs: []net.IP{{185, 246, 208, 77}, {185, 246, 208, 105}}},
|
||||
{Region: "Portugal Lisbon", IPs: []net.IP{{5, 154, 174, 26}, {5, 154, 174, 173}}},
|
||||
{Region: "Portugal Loule", IPs: []net.IP{{176, 61, 146, 111}, {176, 61, 146, 123}}},
|
||||
{Region: "Portugal Porto", IPs: []net.IP{{194, 39, 127, 242}}},
|
||||
{Region: "Romania", IPs: []net.IP{{86, 106, 137, 147}, {86, 106, 137, 149}}},
|
||||
{Region: "Russia Moscow", IPs: []net.IP{{213, 183, 56, 145}, {213, 183, 56, 160}}},
|
||||
{Region: "Russia St. Petersburg", IPs: []net.IP{{213, 183, 54, 23}, {213, 183, 54, 165}}},
|
||||
{Region: "Serbia", IPs: []net.IP{{37, 120, 193, 53}, {152, 89, 160, 211}}},
|
||||
{Region: "Singapore", IPs: []net.IP{{89, 187, 162, 186}, {89, 187, 163, 210}}},
|
||||
{Region: "Singapore Hong Kong", IPs: []net.IP{{206, 189, 83, 129}}},
|
||||
{Region: "Singapore Netherlands", IPs: []net.IP{{104, 248, 148, 18}}},
|
||||
{Region: "Singapore in", IPs: []net.IP{{128, 199, 193, 35}}},
|
||||
{Region: "Singapore mp001", IPs: []net.IP{{206, 189, 94, 229}}},
|
||||
{Region: "Singapore st001", IPs: []net.IP{{217, 138, 201, 91}}},
|
||||
{Region: "Singapore st002", IPs: []net.IP{{217, 138, 201, 93}}},
|
||||
{Region: "Singapore st003", IPs: []net.IP{{84, 247, 49, 19}}},
|
||||
{Region: "Singapore st004", IPs: []net.IP{{84, 247, 49, 21}}},
|
||||
{Region: "Slovekia", IPs: []net.IP{{193, 37, 255, 39}, {193, 37, 255, 41}}},
|
||||
{Region: "Slovenia", IPs: []net.IP{{195, 158, 249, 38}, {195, 158, 249, 42}}},
|
||||
{Region: "South Africa", IPs: []net.IP{{154, 127, 49, 226}, {154, 127, 49, 232}}},
|
||||
{Region: "Spain Barcelona", IPs: []net.IP{{185, 188, 61, 15}, {185, 188, 61, 25}}},
|
||||
{Region: "Spain Madrid", IPs: []net.IP{{188, 208, 141, 18}, {188, 208, 141, 20}}},
|
||||
{Region: "Spain Valencia", IPs: []net.IP{{185, 153, 150, 48}, {196, 196, 150, 71}}},
|
||||
{Region: "Sweden", IPs: []net.IP{{45, 83, 91, 149}, {185, 76, 9, 41}}},
|
||||
{Region: "Switzerland", IPs: []net.IP{{84, 17, 53, 219}, {84, 17, 53, 221}}},
|
||||
{Region: "Taiwan", IPs: []net.IP{{2, 58, 241, 5}, {2, 58, 241, 43}}},
|
||||
{Region: "Thailand", IPs: []net.IP{{45, 64, 186, 132}, {45, 64, 186, 163}}},
|
||||
{Region: "Turkey", IPs: []net.IP{{185, 195, 79, 5}, {185, 195, 79, 19}}},
|
||||
{Region: "Turkey Istanbul", IPs: []net.IP{{107, 150, 95, 155}, {107, 150, 95, 157}}},
|
||||
{Region: "UK France", IPs: []net.IP{{188, 166, 168, 247}}},
|
||||
{Region: "UK Germany", IPs: []net.IP{{45, 77, 58, 16}}},
|
||||
{Region: "UK Glasgow", IPs: []net.IP{{185, 108, 105, 157}}},
|
||||
{Region: "UK London", IPs: []net.IP{{178, 239, 166, 218}, {185, 44, 78, 164}}},
|
||||
{Region: "UK London mp001", IPs: []net.IP{{206, 189, 119, 92}}},
|
||||
{Region: "UK London st001", IPs: []net.IP{{217, 146, 82, 83}}},
|
||||
{Region: "UK London st002", IPs: []net.IP{{185, 134, 22, 80}}},
|
||||
{Region: "UK London st003", IPs: []net.IP{{185, 134, 22, 92}}},
|
||||
{Region: "UK London st004", IPs: []net.IP{{185, 44, 76, 186}}},
|
||||
{Region: "UK London st005", IPs: []net.IP{{185, 44, 76, 188}}},
|
||||
{Region: "UK Manchester", IPs: []net.IP{{193, 148, 17, 83}, {217, 138, 196, 91}}},
|
||||
{Region: "US Atlanta", IPs: []net.IP{{66, 115, 166, 147}, {66, 115, 166, 151}}},
|
||||
{Region: "US Bend", IPs: []net.IP{{45, 43, 14, 95}}},
|
||||
{Region: "US Boston", IPs: []net.IP{{173, 237, 207, 13}, {199, 217, 107, 20}}},
|
||||
{Region: "US Buffalo", IPs: []net.IP{{107, 174, 20, 130}}},
|
||||
{Region: "US Charlotte", IPs: []net.IP{{66, 11, 124, 136}, {192, 154, 254, 135}}},
|
||||
{Region: "US Chicago", IPs: []net.IP{{74, 119, 146, 197}, {89, 187, 182, 173}}},
|
||||
{Region: "US Dallas", IPs: []net.IP{{66, 115, 177, 133}, {66, 115, 177, 158}}},
|
||||
{Region: "US Denver", IPs: []net.IP{{212, 102, 44, 76}, {212, 102, 44, 98}}},
|
||||
{Region: "US Gahanna", IPs: []net.IP{{104, 244, 209, 99}, {104, 244, 211, 171}}},
|
||||
{Region: "US Houston", IPs: []net.IP{{104, 148, 30, 53}, {199, 10, 64, 115}}},
|
||||
{Region: "US Kansas City", IPs: []net.IP{{173, 208, 202, 59}, {173, 208, 202, 61}}},
|
||||
{Region: "US Las Vegas", IPs: []net.IP{{89, 187, 187, 149}, {185, 242, 5, 215}}},
|
||||
{Region: "US Latham", IPs: []net.IP{{45, 43, 19, 74}, {45, 43, 19, 92}}},
|
||||
{Region: "US Los Angeles", IPs: []net.IP{{38, 95, 110, 73}, {192, 111, 134, 202}}},
|
||||
{Region: "US Maryland", IPs: []net.IP{{23, 82, 8, 173}, {23, 105, 163, 94}}},
|
||||
{Region: "US Miami", IPs: []net.IP{{89, 187, 173, 250}, {172, 83, 42, 143}}},
|
||||
{Region: "US Netherlands", IPs: []net.IP{{142, 93, 58, 71}}},
|
||||
{Region: "US New York City", IPs: []net.IP{{84, 17, 35, 78}, {89, 187, 178, 92}}},
|
||||
{Region: "US New York City mp001", IPs: []net.IP{{45, 55, 60, 159}}},
|
||||
{Region: "US New York City st001", IPs: []net.IP{{92, 119, 177, 19}}},
|
||||
{Region: "US New York City st002", IPs: []net.IP{{92, 119, 177, 21}}},
|
||||
{Region: "US New York City st003", IPs: []net.IP{{92, 119, 177, 23}}},
|
||||
{Region: "US New York City st004", IPs: []net.IP{{193, 148, 18, 51}}},
|
||||
{Region: "US New York City st005", IPs: []net.IP{{193, 148, 18, 53}}},
|
||||
{Region: "US Orlando", IPs: []net.IP{{198, 147, 22, 135}, {198, 147, 22, 213}}},
|
||||
{Region: "US Phoenix", IPs: []net.IP{{184, 170, 240, 179}, {199, 58, 187, 3}}},
|
||||
{Region: "US Portugal", IPs: []net.IP{{142, 93, 81, 242}}},
|
||||
{Region: "US Saint Louis", IPs: []net.IP{{148, 72, 174, 43}, {148, 72, 174, 51}}},
|
||||
{Region: "US Salt Lake City", IPs: []net.IP{{104, 200, 131, 165}, {104, 200, 131, 249}}},
|
||||
{Region: "US San Francisco", IPs: []net.IP{{107, 181, 166, 39}, {107, 181, 166, 83}}},
|
||||
{Region: "US San Francisco mp001", IPs: []net.IP{{165, 232, 53, 25}}},
|
||||
{Region: "US Seatle", IPs: []net.IP{{199, 229, 250, 163}}},
|
||||
{Region: "US Tampa", IPs: []net.IP{{209, 216, 92, 197}, {209, 216, 92, 205}}},
|
||||
{Region: "Ukraine", IPs: []net.IP{{45, 9, 238, 23}, {45, 9, 238, 30}}},
|
||||
{Region: "United Arab Emirates", IPs: []net.IP{{45, 9, 250, 99}, {45, 9, 250, 103}}},
|
||||
{Region: "Vietnam", IPs: []net.IP{{202, 143, 110, 29}, {202, 143, 110, 32}}},
|
||||
}
|
||||
}
|
||||
33
internal/constants/vpn.go
Normal file
33
internal/constants/vpn.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
const (
|
||||
// PrivateInternetAccess is a VPN provider.
|
||||
PrivateInternetAccess models.VPNProvider = "private internet access"
|
||||
// Mullvad is a VPN provider.
|
||||
Mullvad models.VPNProvider = "mullvad"
|
||||
// Windscribe is a VPN provider.
|
||||
Windscribe models.VPNProvider = "windscribe"
|
||||
// Surfshark is a VPN provider.
|
||||
Surfshark models.VPNProvider = "surfshark"
|
||||
// Cyberghost is a VPN provider.
|
||||
Cyberghost models.VPNProvider = "cyberghost"
|
||||
// Vyprvpn is a VPN provider.
|
||||
Vyprvpn models.VPNProvider = "vyprvpn"
|
||||
// NordVPN is a VPN provider.
|
||||
Nordvpn models.VPNProvider = "nordvpn"
|
||||
// PureVPN is a VPN provider.
|
||||
Purevpn models.VPNProvider = "purevpn"
|
||||
// Privado is a VPN provider.
|
||||
Privado models.VPNProvider = "privado"
|
||||
)
|
||||
|
||||
const (
|
||||
// TCP is a network protocol (reliable and slower than UDP).
|
||||
TCP models.NetworkProtocol = "tcp"
|
||||
// UDP is a network protocol (unreliable and faster than TCP).
|
||||
UDP models.NetworkProtocol = "udp"
|
||||
)
|
||||
100
internal/constants/vyprvpn.go
Normal file
100
internal/constants/vyprvpn.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
const (
|
||||
VyprvpnCertificate = "MIIGDjCCA/agAwIBAgIJAL2ON5xbane/MA0GCSqGSIb3DQEBDQUAMIGTMQswCQYDVQQGEwJDSDEQMA4GA1UECAwHTHVjZXJuZTEPMA0GA1UEBwwGTWVnZ2VuMRkwFwYDVQQKDBBHb2xkZW4gRnJvZyBHbWJIMSEwHwYDVQQDDBhHb2xkZW4gRnJvZyBHbWJIIFJvb3QgQ0ExIzAhBgkqhkiG9w0BCQEWFGFkbWluQGdvbGRlbmZyb2cuY29tMB4XDTE5MTAxNzIwMTQxMFoXDTM5MTAxMjIwMTQxMFowgZMxCzAJBgNVBAYTAkNIMRAwDgYDVQQIDAdMdWNlcm5lMQ8wDQYDVQQHDAZNZWdnZW4xGTAXBgNVBAoMEEdvbGRlbiBGcm9nIEdtYkgxITAfBgNVBAMMGEdvbGRlbiBGcm9nIEdtYkggUm9vdCBDQTEjMCEGCSqGSIb3DQEJARYUYWRtaW5AZ29sZGVuZnJvZy5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCtuddaZrpWZ+nUuJpG+ohTquO3XZtq6d4U0E2oiPeIiwm+WWLY49G+GNJb5aVrlrBojaykCAc2sU6NeUlpg3zuqrDqLcz7PAE4OdNiOdrLBF1o9ZHrcITDZN304eAY5nbyHx5V6x/QoDVCi4g+5OVTA+tZjpcl4wRIpgknWznO73IKCJ6YckpLn1BsFrVCb2ehHYZLg7Js58FzMySIxBmtkuPeHQXL61DFHh3cTFcMxqJjzh7EGsWRyXfbAaBGYnT+TZwzpLXXt8oBGpNXG8YBDrPdK0A+lzMnJ4nS0rgHDSRF0brx+QYk/6CgM510uFzB7zytw9UTD3/5TvKlCUmTGGgI84DbJ3DEvjxbgiQnJXCUZKKYSHwrK79Y4Qn+lXu4Bu0ZTCJBje0GUVMTPAvBCeDvzSe0iRcVSNMJVM68d4kD1PpSY/zWfCz5hiOjHWuXinaoZ0JJqRF8kGbJsbDlDYDtVvh/Cd4aWN6Q/2XLpszBsG5i8sdkS37nzkdlRwNEIZwsKfcXwdTOlDinR1LUG68LmzJAwfNE47xbrZUsdGGfG+HSPsrqFFiLGe7Y4e2+a7vGdSY9qR9PAzyx0ijCCrYzZDIsb2dwjLctUx6a3LNV8cpfhKX+s6tfMldGufPI7byHT1Ybf0NtMS1d1RjD6IbqedXQdCKtaw68kTX//wIDAQABo2MwYTAdBgNVHQ4EFgQU2EbQvBd1r/EADr2jCPMXsH7zEXEwHwYDVR0jBBgwFoAU2EbQvBd1r/EADr2jCPMXsH7zEXEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQENBQADggIBAAViCPieIronV+9asjZyo5oSZSNWUkWRYdezjezsf49+fwT12iRgnkSEQeoj5caqcOfNm/eRpN4G7jhhCcxy9RGF+GurIlZ4v0mChZbx1jcxqr9/3/Z2TqvHALyWngBYDv6pv1iWcd9a4+QL9kj1Tlp8vUDIcHMtDQkEHnkhC+MnjyrdsdNE5wjlLljjFR2Qy5a6/kWwZ1JQVYof1J1EzY6mU7YLMHOdjfmeci5i0vg8+9kGMsc/7Wm69L1BeqpDB3ZEAgmOtda2jwOevJ4sABmRoSThFp4DeMcxb62HW1zZCCpgzWv/33+pZdPvnZHSz7RGoxH4Ln7eBf3oo2PMlu7wCsid3HUdgkRf2Og1RJIrFfEjb7jga1JbKX2Qo/FH3txzdUimKiDRv3ccFmEOqjndUG6hP+7/EsI43oCPYOvZR+u5GdOkhYrDGZlvjXeJ1CpQxTR/EX+Vt7F8YG+i2LkO7lhPLb+LzgPAxVPCcEMHruuUlE1BYxxzRMOW4X4kjHvJjZGISxa9lgTY3e0mnoQNQVBHKfzI2vGLwvcrFcCIrVxeEbj2dryfByyhZlrNPFbXyf7P4OSfk+fVh6Is1IF1wksfLY/6gWvcmXB8JwmKFDa9s5NfzXnzP3VMrNUWXN3G8Eee6qzKKTDsJ70OrgAx9j9a+dMLfe1vP5t6GQj5"
|
||||
)
|
||||
|
||||
func VyprvpnRegionChoices() (choices []string) {
|
||||
servers := VyprvpnServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Region
|
||||
}
|
||||
return choices
|
||||
}
|
||||
|
||||
func VyprvpnServers() []models.VyprvpnServer {
|
||||
return []models.VyprvpnServer{
|
||||
{Region: "Algeria", IPs: []net.IP{{209, 99, 75, 20}}},
|
||||
{Region: "Argentina", IPs: []net.IP{{209, 99, 109, 19}}},
|
||||
{Region: "Australia Melbourne", IPs: []net.IP{{209, 99, 117, 19}}},
|
||||
{Region: "Australia Perth", IPs: []net.IP{{209, 99, 1, 19}}},
|
||||
{Region: "Australia Sydney", IPs: []net.IP{{209, 99, 117, 18}}},
|
||||
{Region: "Austria", IPs: []net.IP{{128, 90, 96, 18}}},
|
||||
{Region: "Bahrain", IPs: []net.IP{{209, 99, 115, 19}}},
|
||||
{Region: "Belgium", IPs: []net.IP{{128, 90, 96, 20}}},
|
||||
{Region: "Brazil", IPs: []net.IP{{209, 99, 109, 20}}},
|
||||
{Region: "Bulgaria", IPs: []net.IP{{128, 90, 96, 22}}},
|
||||
{Region: "Canada", IPs: []net.IP{{209, 99, 21, 18}}},
|
||||
{Region: "Columbia", IPs: []net.IP{{209, 99, 109, 21}}},
|
||||
{Region: "Costa Rica", IPs: []net.IP{{209, 99, 109, 22}}},
|
||||
{Region: "Czech Republic", IPs: []net.IP{{128, 90, 96, 24}}},
|
||||
{Region: "Denmark", IPs: []net.IP{{128, 90, 96, 28}}},
|
||||
{Region: "Dubai", IPs: []net.IP{{128, 90, 45, 104}}},
|
||||
{Region: "Egypt", IPs: []net.IP{{128, 90, 228, 43}}},
|
||||
{Region: "El Salvador", IPs: []net.IP{{209, 99, 61, 20}}},
|
||||
{Region: "Finland", IPs: []net.IP{{128, 90, 96, 32}}},
|
||||
{Region: "France", IPs: []net.IP{{128, 90, 96, 34}}},
|
||||
{Region: "Germany", IPs: []net.IP{{128, 90, 96, 26}}},
|
||||
{Region: "Greece", IPs: []net.IP{{128, 90, 228, 59}}},
|
||||
{Region: "Hong Kong", IPs: []net.IP{{128, 90, 227, 18}}},
|
||||
{Region: "Iceland", IPs: []net.IP{{209, 99, 22, 20}}},
|
||||
{Region: "India", IPs: []net.IP{{209, 99, 115, 20}}},
|
||||
{Region: "Indonesia", IPs: []net.IP{{209, 99, 1, 20}}},
|
||||
{Region: "Ireland", IPs: []net.IP{{209, 99, 22, 19}}},
|
||||
{Region: "Israel", IPs: []net.IP{{128, 90, 228, 20}}},
|
||||
{Region: "Italy", IPs: []net.IP{{128, 90, 96, 36}}},
|
||||
{Region: "Japan", IPs: []net.IP{{209, 99, 113, 18}}},
|
||||
{Region: "Latvia", IPs: []net.IP{{128, 90, 96, 44}}},
|
||||
{Region: "Liechtenstein", IPs: []net.IP{{128, 90, 96, 38}}},
|
||||
{Region: "Lithuania", IPs: []net.IP{{128, 90, 96, 40}}},
|
||||
{Region: "Luxembourg", IPs: []net.IP{{128, 90, 96, 42}}},
|
||||
{Region: "Macao", IPs: []net.IP{{128, 90, 227, 36}}},
|
||||
{Region: "Malaysia", IPs: []net.IP{{209, 99, 1, 21}}},
|
||||
{Region: "Maldives", IPs: []net.IP{{209, 99, 1, 26}}},
|
||||
{Region: "Marshall Islands", IPs: []net.IP{{209, 99, 1, 25}}},
|
||||
{Region: "Mexico", IPs: []net.IP{{209, 99, 61, 19}}},
|
||||
{Region: "Netherlands", IPs: []net.IP{{128, 90, 96, 16}}},
|
||||
{Region: "New Zealand", IPs: []net.IP{{209, 99, 117, 20}}},
|
||||
{Region: "Norway", IPs: []net.IP{{128, 90, 96, 46}}},
|
||||
{Region: "Pakistan", IPs: []net.IP{{128, 90, 228, 67}}},
|
||||
{Region: "Panama", IPs: []net.IP{{209, 99, 109, 23}}},
|
||||
{Region: "Philippines", IPs: []net.IP{{209, 99, 1, 22}}},
|
||||
{Region: "Poland", IPs: []net.IP{{128, 90, 96, 48}}},
|
||||
{Region: "Portugal", IPs: []net.IP{{128, 90, 96, 50}}},
|
||||
{Region: "Qatar", IPs: []net.IP{{209, 99, 115, 21}}},
|
||||
{Region: "Romania", IPs: []net.IP{{128, 90, 96, 52}}},
|
||||
{Region: "Russia", IPs: []net.IP{{128, 90, 96, 54}}},
|
||||
{Region: "Saudi Arabia", IPs: []net.IP{{209, 99, 115, 22}}},
|
||||
{Region: "Singapore", IPs: []net.IP{{209, 99, 1, 18}}},
|
||||
{Region: "Slovakia", IPs: []net.IP{{128, 90, 96, 60}}},
|
||||
{Region: "Slovenia", IPs: []net.IP{{128, 90, 96, 58}}},
|
||||
{Region: "South Korea", IPs: []net.IP{{209, 99, 113, 19}}},
|
||||
{Region: "Spain", IPs: []net.IP{{128, 90, 96, 30}}},
|
||||
{Region: "Sweden", IPs: []net.IP{{128, 90, 96, 56}}},
|
||||
{Region: "Switzerland", IPs: []net.IP{{209, 99, 60, 18}}},
|
||||
{Region: "Taiwan", IPs: []net.IP{{128, 90, 227, 27}}},
|
||||
{Region: "Thailand", IPs: []net.IP{{209, 99, 1, 23}}},
|
||||
{Region: "Turkey", IPs: []net.IP{{128, 90, 96, 62}}},
|
||||
{Region: "USA Austin", IPs: []net.IP{{209, 99, 61, 18}}},
|
||||
{Region: "USA Chicago", IPs: []net.IP{{209, 99, 93, 18}}},
|
||||
{Region: "USA Los Angeles", IPs: []net.IP{{209, 99, 67, 18}}},
|
||||
{Region: "USA Miami", IPs: []net.IP{{209, 99, 109, 18}}},
|
||||
{Region: "USA New York", IPs: []net.IP{{209, 99, 63, 18}}},
|
||||
{Region: "USA San Francisco", IPs: []net.IP{{209, 99, 95, 18}}},
|
||||
{Region: "USA Seattle", IPs: []net.IP{{209, 99, 94, 18}}},
|
||||
{Region: "USA Washington", IPs: []net.IP{{209, 99, 62, 18}}},
|
||||
{Region: "USA Washington DC", IPs: []net.IP{{209, 99, 62, 18}}},
|
||||
{Region: "Ukraine", IPs: []net.IP{{128, 90, 96, 64}}},
|
||||
{Region: "United Kingdom", IPs: []net.IP{{209, 99, 22, 18}}},
|
||||
{Region: "Uruguay", IPs: []net.IP{{209, 99, 61, 21}}},
|
||||
{Region: "Vietnam", IPs: []net.IP{{209, 99, 1, 24}}},
|
||||
}
|
||||
}
|
||||
339
internal/constants/windscribe.go
Normal file
339
internal/constants/windscribe.go
Normal file
@@ -0,0 +1,339 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
const (
|
||||
WindscribeCertificate = "MIIF3DCCA8SgAwIBAgIJAMsOivWTmu9fMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEQMA4GA1UEBwwHVG9yb250bzEbMBkGA1UECgwSV2luZHNjcmliZSBMaW1pdGVkMRMwEQYDVQQLDApPcGVyYXRpb25zMRswGQYDVQQDDBJXaW5kc2NyaWJlIE5vZGUgQ0EwHhcNMTYwMzA5MDMyNjIwWhcNNDAxMDI5MDMyNjIwWjB7MQswCQYDVQQGEwJDQTELMAkGA1UECAwCT04xEDAOBgNVBAcMB1Rvcm9udG8xGzAZBgNVBAoMEldpbmRzY3JpYmUgTGltaXRlZDETMBEGA1UECwwKT3BlcmF0aW9uczEbMBkGA1UEAwwSV2luZHNjcmliZSBOb2RlIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAruBtLR1Vufd71LeQEqChgHS4AQJ0fSRner0gmZPEr2TL5uWboOEWXFFoEUTthF+P/N8yy3xRZ8HhG/zKlmJ1xw+7KZRbTADD6shJPj3/uvTIO80sU+9LmsyKSWuPhQ1NkgNA7rrMTfz9eHJ2MVDs4XCpYWyX9iuAQrHSY6aPq+4TpCbUgprkM3Gwjh9RSt9IoDoc4CF2bWSaVepUcL9yz/SXLPzFx2OT9rFrDhL3ryHRzJQ/tA+VD8A7lo8bhOcDqiXgEFmVOZNMLw+r167Qq1Ck7X86yr2mnW/6HK2gJOvY0/SPKukfGJAiYZKdG+fe4ekyYcAVhDfPJg7rF9wUqPwUzejJyAs1K18JwX94Y8fnD6vQobjpC3qfHtwQP7Uj2AcI6QC8ytWDegV6UIkHXAMXBQSX5suSQoE11deG32cy7nyp5vhgy31rTyNoopqlcCAhPm6k0jVVQbvXhLcpTSL8iCCoMdrP28i/xsfvktBAkl5giHMdK6hxqWgPI+Bx9uPIhRp3fJ2z8AgFm8g1ARB2ZzQ+OZZ2RUIkJuUKhi2kUhgKSAQ+eF89aoqDjp/J1miZqGRzt4DovSZfQOeL01RkKHEibAPYCfgHG2ZSwoLoeaxE2vNZiX4dpXiOQYTOIXOwEPZzPvfTQf9T4Kxvx3jzQnt3PzjlMCqKk3Aipm8CAwEAAaNjMGEwHQYDVR0OBBYEFEH2v9F2z938Ebngsj9RkVSSgs45MB8GA1UdIwQYMBaAFEH2v9F2z938Ebngsj9RkVSSgs45MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQAgI6NgYkVo5rB6yKStgHjjZsINsgEvoMuHwkM0YaV22XtKNiHdsiOmY/PGCRemFobTEHk5XHcvcOTWv/D1qVf8fI21WAoNQVH7h8KEsr4uMGKCB6Lu8l6xALXRMjo1xb6JKBWXwIAzUu691rUD2exT1E+A5t+xw+gzqV8rWTMIoUaH7O1EKjN6ryGW71Khiik8/ETrP3YT32ZbS2P902iMKw9rpmuS0wWhnO5k/iO/6YNA1ZMV5JG5oZvZQYEDk7enLD9HvqazofMuy/Sz/n62ZCDdQsnabzxl04wwv5Y3JZbV/6bOM520GgdJEoDxviY05ax2Mz05otyBzrAVjFw9RZt/Ls8ATifu9BusZ2ootvscdIuE3x+ZCl5lvANcFEnvgGw0qpCeASLpsfxwq1dRgIn7BOiTauFv4eoeFAQvCD+l+EKGWKu3M2y19DgYX94N2+Xs2bwChroaO5e4iFemMLMuWKZvYgnqS9OAtRSYWbNX/wliiPz7u13yj+qSWgMfu8WPYNQlMZJXuGWUvKLEXCUExlu7/o8D4HpsVs30E0pUdaqN0vExB1KegxPWWrmLcYnPG3knXpkC3ZBZ5P/el/2eyhZRy9ydiITF8gM3L08E8aeqvzZMw2FDSmousydIzlXgeS5VuEf+lUFA2h8oZYGQgrLt+ot8MbLhJlkp4Q=="
|
||||
WindscribeOpenvpnStaticKeyV1 = "5801926a57ac2ce27e3dfd1dd6ef82042d82bd4f3f0021296f57734f6f1ea714a6623845541c4b0c3dea0a050fe6746cb66dfab14cda27e5ae09d7c155aa554f399fa4a863f0e8c1af787e5c602a801d3a2ec41e395a978d56729457fe6102d7d9e9119aa83643210b33c678f9d4109e3154ac9c759e490cb309b319cf708cae83ddadc3060a7a26564d1a24411cd552fe6620ea16b755697a4fc5e6e9d0cfc0c5c4a1874685429046a424c026db672e4c2c492898052ba59128d46200b40f880027a8b6610a4d559bdc9346d33a0a6b08e75c7fd43192b162bfd0aef0c716b31584827693f676f9a5047123466f0654eade34972586b31c6ce7e395f4b478cb"
|
||||
)
|
||||
|
||||
func WindscribeRegionChoices() (choices []string) {
|
||||
servers := WindscribeServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Region
|
||||
}
|
||||
return choices
|
||||
}
|
||||
|
||||
func WindscribeCityChoices() (choices []string) {
|
||||
servers := WindscribeServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].City
|
||||
}
|
||||
return choices
|
||||
}
|
||||
|
||||
func WindscribeHostnameChoices() (choices []string) {
|
||||
servers := WindscribeServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Hostname
|
||||
}
|
||||
return choices
|
||||
}
|
||||
|
||||
//nolint:lll
|
||||
func WindscribeServers() []models.WindscribeServer {
|
||||
return []models.WindscribeServer{
|
||||
{Region: "Albania", City: "Tirana", Hostname: "al-002.whiskergalaxy.com", IP: net.IP{31, 171, 152, 179}},
|
||||
{Region: "Argentina", City: "Buenos Aires", Hostname: "ar-001.whiskergalaxy.com", IP: net.IP{200, 85, 152, 110}},
|
||||
{Region: "Argentina", City: "Buenos Aires", Hostname: "ar-003.whiskergalaxy.com", IP: net.IP{167, 250, 6, 121}},
|
||||
{Region: "Argentina", City: "Buenos Aires", Hostname: "ar-004.whiskergalaxy.com", IP: net.IP{190, 105, 236, 50}},
|
||||
{Region: "Argentina", City: "Buenos Aires", Hostname: "ar-005.whiskergalaxy.com", IP: net.IP{190, 105, 236, 32}},
|
||||
{Region: "Argentina", City: "Buenos Aires", Hostname: "ar-006.whiskergalaxy.com", IP: net.IP{190, 105, 236, 19}},
|
||||
{Region: "Australia", City: "Adelaide ", Hostname: "au-011.whiskergalaxy.com", IP: net.IP{103, 108, 92, 83}},
|
||||
{Region: "Australia", City: "Adelaide", Hostname: "au-008.whiskergalaxy.com", IP: net.IP{116, 90, 72, 243}},
|
||||
{Region: "Australia", City: "Brisbane", Hostname: "au-007.whiskergalaxy.com", IP: net.IP{103, 62, 50, 208}},
|
||||
{Region: "Australia", City: "Brisbane", Hostname: "au-014.whiskergalaxy.com", IP: net.IP{43, 245, 160, 35}},
|
||||
{Region: "Australia", City: "Canberra", Hostname: "au-010.whiskergalaxy.com", IP: net.IP{116, 206, 229, 131}},
|
||||
{Region: "Australia", City: "Melbourne ", Hostname: "au-005.whiskergalaxy.com", IP: net.IP{45, 121, 209, 160}},
|
||||
{Region: "Australia", City: "Melbourne ", Hostname: "au-013.whiskergalaxy.com", IP: net.IP{116, 206, 228, 67}},
|
||||
{Region: "Australia", City: "Perth", Hostname: "au-004.whiskergalaxy.com", IP: net.IP{45, 121, 208, 160}},
|
||||
{Region: "Australia", City: "Perth", Hostname: "au-012.whiskergalaxy.com", IP: net.IP{103, 77, 234, 211}},
|
||||
{Region: "Australia", City: "Sydney", Hostname: "au-009.whiskergalaxy.com", IP: net.IP{103, 77, 233, 67}},
|
||||
{Region: "Australia", City: "Sydney", Hostname: "au-015.whiskergalaxy.com", IP: net.IP{103, 1, 213, 211}},
|
||||
{Region: "Austria", City: "Vienna", Hostname: "at-001.whiskergalaxy.com", IP: net.IP{217, 64, 127, 11}},
|
||||
{Region: "Austria", City: "Vienna", Hostname: "at-002.whiskergalaxy.com", IP: net.IP{89, 187, 168, 66}},
|
||||
{Region: "Belgium", City: "Brussels", Hostname: "be-001.whiskergalaxy.com", IP: net.IP{194, 187, 251, 147}},
|
||||
{Region: "Belgium", City: "Brussels", Hostname: "be-002.whiskergalaxy.com", IP: net.IP{185, 232, 21, 131}},
|
||||
{Region: "Brazil", City: "Sao Paulo", Hostname: "br-004.whiskergalaxy.com", IP: net.IP{177, 67, 80, 59}},
|
||||
{Region: "Brazil", City: "Sao Paulo", Hostname: "br-005.whiskergalaxy.com", IP: net.IP{177, 54, 157, 178}},
|
||||
{Region: "Brazil", City: "Sao Paulo", Hostname: "br-006.whiskergalaxy.com", IP: net.IP{177, 54, 148, 247}},
|
||||
{Region: "Bulgaria", City: "Sofia", Hostname: "bg-001.whiskergalaxy.com", IP: net.IP{185, 94, 192, 35}},
|
||||
{Region: "Canada East", City: "Halifax", Hostname: "ca-029.whiskergalaxy.com", IP: net.IP{199, 204, 208, 158}},
|
||||
{Region: "Canada East", City: "Montreal", Hostname: "ca-004.whiskergalaxy.com", IP: net.IP{66, 70, 148, 80}},
|
||||
{Region: "Canada East", City: "Montreal", Hostname: "ca-027.whiskergalaxy.com", IP: net.IP{144, 168, 163, 160}},
|
||||
{Region: "Canada East", City: "Montreal", Hostname: "ca-028.whiskergalaxy.com", IP: net.IP{144, 168, 163, 193}},
|
||||
{Region: "Canada East", City: "Montreal", Hostname: "ca-032.whiskergalaxy.com", IP: net.IP{104, 227, 235, 129}},
|
||||
{Region: "Canada East", City: "Montreal", Hostname: "ca-033.whiskergalaxy.com", IP: net.IP{198, 8, 85, 195}},
|
||||
{Region: "Canada East", City: "Montreal", Hostname: "ca-034.whiskergalaxy.com", IP: net.IP{198, 8, 85, 210}},
|
||||
{Region: "Canada East", City: "Toronto", Hostname: "ca-002.whiskergalaxy.com", IP: net.IP{104, 254, 92, 11}},
|
||||
{Region: "Canada East", City: "Toronto", Hostname: "ca-009.whiskergalaxy.com", IP: net.IP{104, 254, 92, 91}},
|
||||
{Region: "Canada East", City: "Toronto", Hostname: "ca-017.whiskergalaxy.com", IP: net.IP{184, 75, 212, 91}},
|
||||
{Region: "Canada East", City: "Toronto", Hostname: "ca-025.whiskergalaxy.com", IP: net.IP{192, 190, 19, 65}},
|
||||
{Region: "Canada East", City: "Toronto", Hostname: "ca-026.whiskergalaxy.com", IP: net.IP{192, 190, 19, 97}},
|
||||
{Region: "Canada East", City: "Toronto", Hostname: "ca-030.whiskergalaxy.com", IP: net.IP{23, 154, 160, 177}},
|
||||
{Region: "Canada West", City: "Vancouver", Hostname: "ca-west-005.whiskergalaxy.com", IP: net.IP{162, 221, 207, 95}},
|
||||
{Region: "Canada West", City: "Vancouver", Hostname: "ca-west-011.whiskergalaxy.com", IP: net.IP{104, 218, 61, 1}},
|
||||
{Region: "Canada West", City: "Vancouver", Hostname: "ca-west-012.whiskergalaxy.com", IP: net.IP{104, 218, 61, 33}},
|
||||
{Region: "Canada West", City: "Vancouver", Hostname: "ca-west-016.whiskergalaxy.com", IP: net.IP{208, 78, 41, 1}},
|
||||
{Region: "Canada West", City: "Vancouver", Hostname: "ca-west-017.whiskergalaxy.com", IP: net.IP{208, 78, 41, 131}},
|
||||
{Region: "Canada West", City: "Vancouver", Hostname: "ca-west-019.whiskergalaxy.com", IP: net.IP{208, 78, 41, 163}},
|
||||
{Region: "Colombia", City: "Bogota", Hostname: "co-001.whiskergalaxy.com", IP: net.IP{138, 121, 203, 203}},
|
||||
{Region: "Colombia", City: "Bogota", Hostname: "co-002.whiskergalaxy.com", IP: net.IP{138, 186, 141, 155}},
|
||||
{Region: "Croatia", City: "Zagreb", Hostname: "hr-002.whiskergalaxy.com", IP: net.IP{85, 10, 56, 129}},
|
||||
{Region: "Cyprus", City: "Nicosia", Hostname: "cy-001.whiskergalaxy.com", IP: net.IP{157, 97, 132, 43}},
|
||||
{Region: "Czech Republic", City: "Prague ", Hostname: "cz-002.whiskergalaxy.com", IP: net.IP{185, 246, 210, 2}},
|
||||
{Region: "Czech Republic", City: "Prague", Hostname: "cz-001.whiskergalaxy.com", IP: net.IP{185, 156, 174, 11}},
|
||||
{Region: "Denmark", City: "Copenhagen", Hostname: "dk-001.whiskergalaxy.com", IP: net.IP{185, 206, 224, 195}},
|
||||
{Region: "Denmark", City: "Copenhagen", Hostname: "dk-002.whiskergalaxy.com", IP: net.IP{134, 90, 149, 147}},
|
||||
{Region: "Estonia", City: "Tallinn", Hostname: "ee-001.whiskergalaxy.com", IP: net.IP{46, 22, 211, 251}},
|
||||
{Region: "Estonia", City: "Tallinn", Hostname: "ee-002.whiskergalaxy.com", IP: net.IP{196, 196, 216, 131}},
|
||||
{Region: "Fake Antarctica", City: "Troll", Hostname: "aq-001.whiskergalaxy.com", IP: net.IP{23, 154, 160, 212}},
|
||||
{Region: "Fake Antarctica", City: "Troll", Hostname: "aq-002.whiskergalaxy.com", IP: net.IP{23, 154, 160, 222}},
|
||||
{Region: "Finland", City: "Helsinki", Hostname: "fi-002.whiskergalaxy.com", IP: net.IP{185, 112, 82, 227}},
|
||||
{Region: "Finland", City: "Helsinki", Hostname: "fi-003.whiskergalaxy.com", IP: net.IP{194, 34, 133, 82}},
|
||||
{Region: "Finland", City: "Helsinki", Hostname: "fi-004.whiskergalaxy.com", IP: net.IP{196, 244, 192, 51}},
|
||||
{Region: "France", City: "Paris", Hostname: "fr-004.whiskergalaxy.com", IP: net.IP{185, 156, 173, 187}},
|
||||
{Region: "France", City: "Paris", Hostname: "fr-005.whiskergalaxy.com", IP: net.IP{82, 102, 18, 35}},
|
||||
{Region: "France", City: "Paris", Hostname: "fr-008.whiskergalaxy.com", IP: net.IP{84, 17, 42, 34}},
|
||||
{Region: "France", City: "Paris", Hostname: "fr-009.whiskergalaxy.com", IP: net.IP{84, 17, 42, 2}},
|
||||
{Region: "France", City: "Paris", Hostname: "fr-011.whiskergalaxy.com", IP: net.IP{45, 89, 174, 35}},
|
||||
{Region: "Germany", City: "Frankfurt", Hostname: "de-003.whiskergalaxy.com", IP: net.IP{89, 249, 65, 19}},
|
||||
{Region: "Germany", City: "Frankfurt", Hostname: "de-006.whiskergalaxy.com", IP: net.IP{185, 130, 184, 195}},
|
||||
{Region: "Germany", City: "Frankfurt", Hostname: "de-009.whiskergalaxy.com", IP: net.IP{195, 181, 170, 66}},
|
||||
{Region: "Germany", City: "Frankfurt", Hostname: "de-010.whiskergalaxy.com", IP: net.IP{195, 181, 175, 98}},
|
||||
{Region: "Germany", City: "Frankfurt", Hostname: "de-011.whiskergalaxy.com", IP: net.IP{217, 138, 194, 115}},
|
||||
{Region: "Germany", City: "Frankfurt", Hostname: "de-012.whiskergalaxy.com", IP: net.IP{45, 87, 212, 51}},
|
||||
{Region: "Greece", City: "Athens", Hostname: "gr-002.whiskergalaxy.com", IP: net.IP{78, 108, 38, 155}},
|
||||
{Region: "Greece", City: "Athens", Hostname: "gr-004.whiskergalaxy.com", IP: net.IP{185, 226, 64, 111}},
|
||||
{Region: "Greece", City: "Athens", Hostname: "gr-005.whiskergalaxy.com", IP: net.IP{188, 123, 126, 146}},
|
||||
{Region: "Hong Kong", City: "Hong Kong", Hostname: "hk-005.whiskergalaxy.com", IP: net.IP{103, 10, 197, 99}},
|
||||
{Region: "Hong Kong", City: "Hong Kong", Hostname: "hk-006.whiskergalaxy.com", IP: net.IP{84, 17, 57, 114}},
|
||||
{Region: "Hungary", City: "Budapest", Hostname: "hu-001.whiskergalaxy.com", IP: net.IP{185, 104, 187, 43}},
|
||||
{Region: "Iceland", City: "Reykjavik", Hostname: "is-001.whiskergalaxy.com", IP: net.IP{82, 221, 139, 38}},
|
||||
{Region: "Iceland", City: "Reykjavik", Hostname: "is-002.whiskergalaxy.com", IP: net.IP{185, 165, 170, 2}},
|
||||
{Region: "India", City: "Chennai", Hostname: "in-005.whiskergalaxy.com", IP: net.IP{169, 38, 68, 188}},
|
||||
{Region: "India", City: "Chennai", Hostname: "in-006.whiskergalaxy.com", IP: net.IP{169, 38, 72, 14}},
|
||||
{Region: "India", City: "Chennai", Hostname: "in-007.whiskergalaxy.com", IP: net.IP{169, 38, 72, 12}},
|
||||
{Region: "India", City: "Mumbai", Hostname: "in-009.whiskergalaxy.com", IP: net.IP{165, 231, 253, 211}},
|
||||
{Region: "India", City: "New Delhi", Hostname: "in-008.whiskergalaxy.com", IP: net.IP{103, 205, 140, 227}},
|
||||
{Region: "Indonesia", City: "Jakarta", Hostname: "id-002.whiskergalaxy.com", IP: net.IP{45, 127, 134, 91}},
|
||||
{Region: "Ireland", City: "Dublin", Hostname: "ie-001.whiskergalaxy.com", IP: net.IP{185, 24, 232, 146}},
|
||||
{Region: "Ireland", City: "Dublin", Hostname: "ie-002.whiskergalaxy.com", IP: net.IP{185, 104, 219, 2}},
|
||||
{Region: "Ireland", City: "Dublin", Hostname: "ie-003.whiskergalaxy.com", IP: net.IP{23, 92, 127, 35}},
|
||||
{Region: "Israel", City: "Ashdod", Hostname: "il-002.whiskergalaxy.com", IP: net.IP{185, 191, 205, 139}},
|
||||
{Region: "Israel", City: "Jerusalem", Hostname: "il-001.whiskergalaxy.com", IP: net.IP{160, 116, 0, 27}},
|
||||
{Region: "Italy", City: "Milan", Hostname: "it-001.whiskergalaxy.com", IP: net.IP{37, 120, 135, 83}},
|
||||
{Region: "Italy", City: "Milan", Hostname: "it-004.whiskergalaxy.com", IP: net.IP{84, 17, 59, 66}},
|
||||
{Region: "Italy", City: "Milan", Hostname: "it-005.whiskergalaxy.com", IP: net.IP{89, 40, 182, 3}},
|
||||
{Region: "Italy", City: "Rome", Hostname: "it-003.whiskergalaxy.com", IP: net.IP{87, 101, 94, 195}},
|
||||
{Region: "Italy", City: "Rome", Hostname: "it-006.whiskergalaxy.com", IP: net.IP{37, 120, 207, 19}},
|
||||
{Region: "Japan", City: "Tokyo", Hostname: "jp-004.whiskergalaxy.com", IP: net.IP{193, 148, 16, 243}},
|
||||
{Region: "Japan", City: "Tokyo", Hostname: "jp-005.whiskergalaxy.com", IP: net.IP{89, 187, 161, 114}},
|
||||
{Region: "Latvia", City: "Riga", Hostname: "lv-003.whiskergalaxy.com", IP: net.IP{85, 254, 72, 23}},
|
||||
{Region: "Latvia", City: "Riga", Hostname: "lv-004.whiskergalaxy.com", IP: net.IP{89, 111, 33, 220}},
|
||||
{Region: "Lithuania", City: "Siauliai", Hostname: "lt-003.whiskergalaxy.com", IP: net.IP{85, 206, 163, 225}},
|
||||
{Region: "Malaysia", City: "Kuala Lumpur", Hostname: "my-001.whiskergalaxy.com", IP: net.IP{103, 106, 250, 31}},
|
||||
{Region: "Malaysia", City: "Kuala Lumpur", Hostname: "my-003.whiskergalaxy.com", IP: net.IP{103, 212, 69, 232}},
|
||||
{Region: "Mexico", City: "Guadalajara", Hostname: "mx-007.whiskergalaxy.com", IP: net.IP{201, 131, 125, 107}},
|
||||
{Region: "Mexico", City: "Guadalajara", Hostname: "mx-008.whiskergalaxy.com", IP: net.IP{143, 255, 57, 67}},
|
||||
{Region: "Mexico", City: "Mexico City", Hostname: "mx-009.whiskergalaxy.com", IP: net.IP{190, 103, 179, 211}},
|
||||
{Region: "Mexico", City: "Mexico City", Hostname: "mx-010.whiskergalaxy.com", IP: net.IP{190, 103, 179, 217}},
|
||||
{Region: "Moldova", City: "Chisinau", Hostname: "md-002.whiskergalaxy.com", IP: net.IP{178, 175, 144, 123}},
|
||||
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-001.whiskergalaxy.com", IP: net.IP{46, 166, 143, 98}},
|
||||
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-005.whiskergalaxy.com", IP: net.IP{185, 212, 171, 131}},
|
||||
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-008.whiskergalaxy.com", IP: net.IP{185, 253, 96, 3}},
|
||||
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-011.whiskergalaxy.com", IP: net.IP{84, 17, 46, 2}},
|
||||
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-012.whiskergalaxy.com", IP: net.IP{37, 120, 192, 19}},
|
||||
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-013.whiskergalaxy.com", IP: net.IP{72, 11, 157, 67}},
|
||||
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-014.whiskergalaxy.com", IP: net.IP{72, 11, 157, 35}},
|
||||
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-015.whiskergalaxy.com", IP: net.IP{109, 201, 130, 2}},
|
||||
{Region: "New Zealand", City: "Auckland ", Hostname: "nz-003.whiskergalaxy.com", IP: net.IP{103, 108, 94, 163}},
|
||||
{Region: "New Zealand", City: "Auckland", Hostname: "nz-002.whiskergalaxy.com", IP: net.IP{103, 62, 49, 113}},
|
||||
{Region: "North Macedonia", City: "Skopje", Hostname: "mk-001.whiskergalaxy.com", IP: net.IP{185, 225, 28, 51}},
|
||||
{Region: "Norway", City: "Oslo", Hostname: "no-003.whiskergalaxy.com", IP: net.IP{185, 206, 225, 131}},
|
||||
{Region: "Norway", City: "Oslo", Hostname: "no-006.whiskergalaxy.com", IP: net.IP{37, 120, 203, 67}},
|
||||
{Region: "Panama", City: "Panama City", Hostname: "pa-001.whiskergalaxy.com", IP: net.IP{138, 186, 142, 203}},
|
||||
{Region: "Peru", City: "Lima", Hostname: "pe-002.whiskergalaxy.com", IP: net.IP{190, 120, 229, 139}},
|
||||
{Region: "Philippines", City: "Manila", Hostname: "ph-003.whiskergalaxy.com", IP: net.IP{141, 98, 215, 211}},
|
||||
{Region: "Philippines", City: "San Antonio", Hostname: "ph-002.whiskergalaxy.com", IP: net.IP{103, 103, 0, 118}},
|
||||
{Region: "Poland", City: "Warsaw", Hostname: "pl-002.whiskergalaxy.com", IP: net.IP{185, 244, 214, 35}},
|
||||
{Region: "Poland", City: "Warsaw", Hostname: "pl-004.whiskergalaxy.com", IP: net.IP{84, 17, 55, 98}},
|
||||
{Region: "Poland", City: "Warsaw", Hostname: "pl-005.whiskergalaxy.com", IP: net.IP{5, 133, 11, 116}},
|
||||
{Region: "Portugal", City: "Lisbon", Hostname: "pt-002.whiskergalaxy.com", IP: net.IP{94, 46, 13, 215}},
|
||||
{Region: "Portugal", City: "Lisbon", Hostname: "pt-003.whiskergalaxy.com", IP: net.IP{185, 15, 21, 66}},
|
||||
{Region: "Romania", City: "Bucharest", Hostname: "ro-006.whiskergalaxy.com", IP: net.IP{89, 46, 103, 147}},
|
||||
{Region: "Romania", City: "Bucharest", Hostname: "ro-008.whiskergalaxy.com", IP: net.IP{91, 207, 102, 147}},
|
||||
{Region: "Russia", City: "Moscow", Hostname: "ru-010.whiskergalaxy.com", IP: net.IP{95, 213, 193, 227}},
|
||||
{Region: "Russia", City: "Moscow", Hostname: "ru-011.whiskergalaxy.com", IP: net.IP{95, 213, 193, 195}},
|
||||
{Region: "Russia", City: "Saint Petersburg", Hostname: "ru-008.whiskergalaxy.com", IP: net.IP{94, 242, 62, 19}},
|
||||
{Region: "Russia", City: "Saint Petersburg", Hostname: "ru-009.whiskergalaxy.com", IP: net.IP{94, 242, 62, 67}},
|
||||
{Region: "Russia", City: "Saint Petersburg", Hostname: "ru-012.whiskergalaxy.com", IP: net.IP{188, 124, 42, 115}},
|
||||
{Region: "Russia", City: "Saint Petersburg", Hostname: "ru-013.whiskergalaxy.com", IP: net.IP{188, 124, 42, 99}},
|
||||
{Region: "Serbia", City: "Belgrade", Hostname: "rs-003.whiskergalaxy.com", IP: net.IP{141, 98, 103, 19}},
|
||||
{Region: "Singapore", City: "Singapore", Hostname: "sg-003.whiskergalaxy.com", IP: net.IP{185, 200, 117, 163}},
|
||||
{Region: "Singapore", City: "Singapore", Hostname: "sg-004.whiskergalaxy.com", IP: net.IP{82, 102, 25, 131}},
|
||||
{Region: "Singapore", City: "Singapore", Hostname: "sg-005.whiskergalaxy.com", IP: net.IP{103, 62, 48, 224}},
|
||||
{Region: "Singapore", City: "Singapore", Hostname: "sg-006.whiskergalaxy.com", IP: net.IP{156, 146, 56, 98}},
|
||||
{Region: "Singapore", City: "Singapore", Hostname: "sg-007.whiskergalaxy.com", IP: net.IP{156, 146, 56, 111}},
|
||||
{Region: "Slovakia", City: "Bratislava", Hostname: "sk-001.whiskergalaxy.com", IP: net.IP{185, 245, 85, 3}},
|
||||
{Region: "South Africa", City: "Johannesburg", Hostname: "za-001.whiskergalaxy.com", IP: net.IP{197, 242, 157, 235}},
|
||||
{Region: "South Africa", City: "Johannesburg", Hostname: "za-002.whiskergalaxy.com", IP: net.IP{129, 232, 167, 211}},
|
||||
{Region: "South Africa", City: "Johannesburg", Hostname: "za-003.whiskergalaxy.com", IP: net.IP{197, 242, 156, 53}},
|
||||
{Region: "South Africa", City: "Johannesburg", Hostname: "za-004.whiskergalaxy.com", IP: net.IP{165, 73, 248, 91}},
|
||||
{Region: "South Korea", City: "Seoul", Hostname: "kr-001.whiskergalaxy.com", IP: net.IP{103, 212, 223, 3}},
|
||||
{Region: "South Korea", City: "Seoul", Hostname: "kr-002.whiskergalaxy.com", IP: net.IP{218, 232, 76, 179}},
|
||||
{Region: "South Korea", City: "Seoul", Hostname: "kr-005.whiskergalaxy.com", IP: net.IP{45, 133, 194, 235}},
|
||||
{Region: "Spain", City: "Barcelona", Hostname: "es-004.whiskergalaxy.com", IP: net.IP{37, 120, 142, 227}},
|
||||
{Region: "Spain", City: "Madrid", Hostname: "es-002.whiskergalaxy.com", IP: net.IP{89, 238, 178, 43}},
|
||||
{Region: "Spain", City: "Madrid", Hostname: "es-003.whiskergalaxy.com", IP: net.IP{217, 138, 218, 99}},
|
||||
{Region: "Sweden", City: "Stockholm", Hostname: "se-001.whiskergalaxy.com", IP: net.IP{31, 13, 191, 67}},
|
||||
{Region: "Sweden", City: "Stockholm", Hostname: "se-002.whiskergalaxy.com", IP: net.IP{79, 142, 76, 198}},
|
||||
{Region: "Sweden", City: "Stockholm", Hostname: "se-003.whiskergalaxy.com", IP: net.IP{195, 181, 166, 129}},
|
||||
{Region: "Switzerland", City: "Zurich", Hostname: "ch-001.whiskergalaxy.com", IP: net.IP{31, 7, 57, 242}},
|
||||
{Region: "Switzerland", City: "Zurich", Hostname: "ch-003.whiskergalaxy.com", IP: net.IP{185, 156, 175, 179}},
|
||||
{Region: "Switzerland", City: "Zurich", Hostname: "ch-005.whiskergalaxy.com", IP: net.IP{89, 187, 165, 98}},
|
||||
{Region: "Switzerland", City: "Zurich", Hostname: "ch-006.whiskergalaxy.com", IP: net.IP{84, 17, 53, 2}},
|
||||
{Region: "Switzerland", City: "Zurich", Hostname: "ch-008.whiskergalaxy.com", IP: net.IP{37, 120, 213, 163}},
|
||||
{Region: "Taiwan", City: "Taipei", Hostname: "tw-008.whiskergalaxy.com", IP: net.IP{103, 4, 29, 77}},
|
||||
{Region: "Taiwan", City: "Taipei", Hostname: "tw-009.whiskergalaxy.com", IP: net.IP{185, 189, 160, 12}},
|
||||
{Region: "Taiwan", City: "Taipei", Hostname: "tw-010.whiskergalaxy.com", IP: net.IP{185, 189, 160, 27}},
|
||||
{Region: "Taiwan", City: "Taipei", Hostname: "tw-011.whiskergalaxy.com", IP: net.IP{185, 189, 160, 32}},
|
||||
{Region: "Thailand", City: "Bangkok", Hostname: "th-003.whiskergalaxy.com", IP: net.IP{27, 254, 130, 221}},
|
||||
{Region: "Thailand", City: "Bangkok", Hostname: "th-005.whiskergalaxy.com", IP: net.IP{202, 129, 16, 147}},
|
||||
{Region: "Thailand", City: "Bangkok", Hostname: "th-006.whiskergalaxy.com", IP: net.IP{202, 129, 16, 155}},
|
||||
{Region: "Tunisia", City: "Tunis", Hostname: "tn-001.whiskergalaxy.com", IP: net.IP{41, 231, 5, 23}},
|
||||
{Region: "Turkey", City: "Bursa", Hostname: "tr-001.whiskergalaxy.com", IP: net.IP{45, 123, 118, 156}},
|
||||
{Region: "Turkey", City: "Istanbul", Hostname: "tr-004.whiskergalaxy.com", IP: net.IP{45, 123, 119, 11}},
|
||||
{Region: "Turkey", City: "Istanbul", Hostname: "tr-006.whiskergalaxy.com", IP: net.IP{185, 125, 33, 227}},
|
||||
{Region: "Turkey", City: "Istanbul", Hostname: "tr-009.whiskergalaxy.com", IP: net.IP{79, 98, 131, 43}},
|
||||
{Region: "Turkey", City: "Istanbul", Hostname: "tr-011.whiskergalaxy.com", IP: net.IP{176, 53, 113, 163}},
|
||||
{Region: "US Central", City: "Atlanta", Hostname: "us-central-016.whiskergalaxy.com", IP: net.IP{104, 129, 18, 3}},
|
||||
{Region: "US Central", City: "Atlanta", Hostname: "us-central-020.whiskergalaxy.com", IP: net.IP{104, 129, 18, 131}},
|
||||
{Region: "US Central", City: "Atlanta", Hostname: "us-central-034.whiskergalaxy.com", IP: net.IP{161, 129, 70, 195}},
|
||||
{Region: "US Central", City: "Atlanta", Hostname: "us-central-046.whiskergalaxy.com", IP: net.IP{198, 12, 76, 211}},
|
||||
{Region: "US Central", City: "Atlanta", Hostname: "us-central-049.whiskergalaxy.com", IP: net.IP{107, 150, 31, 3}},
|
||||
{Region: "US Central", City: "Atlanta", Hostname: "us-central-050.whiskergalaxy.com", IP: net.IP{107, 150, 31, 67}},
|
||||
{Region: "US Central", City: "Atlanta", Hostname: "us-central-051.whiskergalaxy.com", IP: net.IP{162, 222, 198, 67}},
|
||||
{Region: "US Central", City: "Atlanta", Hostname: "us-central-054.whiskergalaxy.com", IP: net.IP{104, 223, 92, 163}},
|
||||
{Region: "US Central", City: "Atlanta", Hostname: "us-central-056.whiskergalaxy.com", IP: net.IP{206, 217, 143, 131}},
|
||||
{Region: "US Central", City: "Dallas", Hostname: "us-central-014.whiskergalaxy.com", IP: net.IP{69, 12, 94, 67}},
|
||||
{Region: "US Central", City: "Dallas", Hostname: "us-central-029.whiskergalaxy.com", IP: net.IP{198, 55, 125, 195}},
|
||||
{Region: "US Central", City: "Dallas", Hostname: "us-central-036.whiskergalaxy.com", IP: net.IP{204, 44, 112, 67}},
|
||||
{Region: "US Central", City: "Dallas", Hostname: "us-central-037.whiskergalaxy.com", IP: net.IP{204, 44, 112, 131}},
|
||||
{Region: "US Central", City: "Dallas", Hostname: "us-central-044.whiskergalaxy.com", IP: net.IP{206, 217, 139, 195}},
|
||||
{Region: "US Central", City: "Dallas", Hostname: "us-central-045.whiskergalaxy.com", IP: net.IP{172, 241, 131, 129}},
|
||||
{Region: "US Central", City: "Dallas", Hostname: "us-central-055.whiskergalaxy.com", IP: net.IP{206, 217, 139, 19}},
|
||||
{Region: "US Central", City: "Dallas", Hostname: "us-central-057.whiskergalaxy.com", IP: net.IP{172, 241, 26, 78}},
|
||||
{Region: "US Central", City: "Dallas", Hostname: "us-central-060.whiskergalaxy.com", IP: net.IP{198, 55, 126, 131}},
|
||||
{Region: "US Central", City: "Denver", Hostname: "us-central-043.whiskergalaxy.com", IP: net.IP{199, 115, 96, 83}},
|
||||
{Region: "US Central", City: "Denver", Hostname: "us-central-058.whiskergalaxy.com", IP: net.IP{198, 54, 128, 116}},
|
||||
{Region: "US Central", City: "Denver", Hostname: "us-central-062.whiskergalaxy.com", IP: net.IP{174, 128, 251, 147}},
|
||||
{Region: "US Central", City: "Kansas City", Hostname: "us-central-063.whiskergalaxy.com", IP: net.IP{38, 146, 5, 51}},
|
||||
{Region: "US Central", City: "Salt Lake City", Hostname: "us-central-047.whiskergalaxy.com", IP: net.IP{107, 182, 234, 240}},
|
||||
{Region: "US Central", City: "Salt Lake City", Hostname: "us-central-052.whiskergalaxy.com", IP: net.IP{67, 212, 238, 196}},
|
||||
{Region: "US East", City: "Boston", Hostname: "us-east-039.whiskergalaxy.com", IP: net.IP{199, 217, 104, 227}},
|
||||
{Region: "US East", City: "Boston", Hostname: "us-east-051.whiskergalaxy.com", IP: net.IP{199, 217, 105, 227}},
|
||||
{Region: "US East", City: "Buffalo", Hostname: "us-east-045.whiskergalaxy.com", IP: net.IP{104, 168, 34, 147}},
|
||||
{Region: "US East", City: "Buffalo", Hostname: "us-east-065.whiskergalaxy.com", IP: net.IP{198, 12, 64, 35}},
|
||||
{Region: "US East", City: "Charlotte", Hostname: "us-east-040.whiskergalaxy.com", IP: net.IP{67, 21, 32, 145}},
|
||||
{Region: "US East", City: "Chicago", Hostname: "us-east-015.whiskergalaxy.com", IP: net.IP{68, 235, 50, 227}},
|
||||
{Region: "US East", City: "Chicago", Hostname: "us-east-019.whiskergalaxy.com", IP: net.IP{23, 226, 141, 195}},
|
||||
{Region: "US East", City: "Chicago", Hostname: "us-east-022.whiskergalaxy.com", IP: net.IP{167, 160, 172, 3}},
|
||||
{Region: "US East", City: "Chicago", Hostname: "us-east-047.whiskergalaxy.com", IP: net.IP{23, 83, 91, 170}},
|
||||
{Region: "US East", City: "Chicago", Hostname: "us-east-053.whiskergalaxy.com", IP: net.IP{107, 150, 29, 131}},
|
||||
{Region: "US East", City: "Chicago", Hostname: "us-east-069.whiskergalaxy.com", IP: net.IP{68, 235, 35, 172}},
|
||||
{Region: "US East", City: "Chicago", Hostname: "us-east-071.whiskergalaxy.com", IP: net.IP{68, 235, 35, 12}},
|
||||
{Region: "US East", City: "Chicago", Hostname: "us-east-077.whiskergalaxy.com", IP: net.IP{68, 235, 43, 204}},
|
||||
{Region: "US East", City: "Cleveland", Hostname: "us-east-078.whiskergalaxy.com", IP: net.IP{38, 101, 74, 19}},
|
||||
{Region: "US East", City: "Columbus", Hostname: "us-east-059.whiskergalaxy.com", IP: net.IP{67, 219, 146, 67}},
|
||||
{Region: "US East", City: "Detroit", Hostname: "us-east-079.whiskergalaxy.com", IP: net.IP{104, 244, 210, 51}},
|
||||
{Region: "US East", City: "Miami", Hostname: "us-east-006.whiskergalaxy.com", IP: net.IP{173, 44, 36, 67}},
|
||||
{Region: "US East", City: "Miami", Hostname: "us-east-012.whiskergalaxy.com", IP: net.IP{45, 87, 214, 35}},
|
||||
{Region: "US East", City: "Miami", Hostname: "us-east-028.whiskergalaxy.com", IP: net.IP{104, 223, 127, 195}},
|
||||
{Region: "US East", City: "Miami", Hostname: "us-east-049.whiskergalaxy.com", IP: net.IP{23, 82, 136, 93}},
|
||||
{Region: "US East", City: "Miami", Hostname: "us-east-067.whiskergalaxy.com", IP: net.IP{86, 106, 87, 83}},
|
||||
{Region: "US East", City: "New Jersey", Hostname: "us-east-020.whiskergalaxy.com", IP: net.IP{162, 222, 195, 67}},
|
||||
{Region: "US East", City: "New Jersey", Hostname: "us-east-054.whiskergalaxy.com", IP: net.IP{167, 160, 167, 195}},
|
||||
{Region: "US East", City: "New York", Hostname: "us-east-013.whiskergalaxy.com", IP: net.IP{185, 232, 22, 195}},
|
||||
{Region: "US East", City: "New York", Hostname: "us-east-046.whiskergalaxy.com", IP: net.IP{206, 217, 129, 227}},
|
||||
{Region: "US East", City: "New York", Hostname: "us-east-050.whiskergalaxy.com", IP: net.IP{173, 208, 45, 33}},
|
||||
{Region: "US East", City: "New York", Hostname: "us-east-064.whiskergalaxy.com", IP: net.IP{206, 217, 128, 3}},
|
||||
{Region: "US East", City: "New York", Hostname: "us-east-068.whiskergalaxy.com", IP: net.IP{142, 234, 200, 176}},
|
||||
{Region: "US East", City: "New York", Hostname: "us-east-073.whiskergalaxy.com", IP: net.IP{217, 138, 255, 163}},
|
||||
{Region: "US East", City: "New York", Hostname: "us-east-074.whiskergalaxy.com", IP: net.IP{217, 138, 255, 179}},
|
||||
{Region: "US East", City: "Orlando", Hostname: "us-east-052.whiskergalaxy.com", IP: net.IP{198, 147, 22, 225}},
|
||||
{Region: "US East", City: "Philadelphia", Hostname: "us-east-060.whiskergalaxy.com", IP: net.IP{76, 72, 175, 99}},
|
||||
{Region: "US East", City: "Philadelphia", Hostname: "us-east-061.whiskergalaxy.com", IP: net.IP{156, 96, 59, 102}},
|
||||
{Region: "US East", City: "Washington DC", Hostname: "us-east-048.whiskergalaxy.com", IP: net.IP{23, 82, 8, 143}},
|
||||
{Region: "US East", City: "Washington DC", Hostname: "us-east-055.whiskergalaxy.com", IP: net.IP{23, 105, 170, 139}},
|
||||
{Region: "US East", City: "Washington DC", Hostname: "us-east-057.whiskergalaxy.com", IP: net.IP{23, 105, 170, 130}},
|
||||
{Region: "US East", City: "Washington DC", Hostname: "us-east-058.whiskergalaxy.com", IP: net.IP{23, 105, 170, 151}},
|
||||
{Region: "US West", City: "Bend", Hostname: "us-west-038.whiskergalaxy.com", IP: net.IP{104, 152, 222, 33}},
|
||||
{Region: "US West", City: "Las Vegas", Hostname: "us-west-018.whiskergalaxy.com", IP: net.IP{82, 102, 30, 67}},
|
||||
{Region: "US West", City: "Las Vegas", Hostname: "us-west-030.whiskergalaxy.com", IP: net.IP{37, 120, 147, 163}},
|
||||
{Region: "US West", City: "Los Angeles", Hostname: "us-west-004.whiskergalaxy.com", IP: net.IP{185, 236, 200, 35}},
|
||||
{Region: "US West", City: "Los Angeles", Hostname: "us-west-015.whiskergalaxy.com", IP: net.IP{216, 45, 53, 131}},
|
||||
{Region: "US West", City: "Los Angeles", Hostname: "us-west-027.whiskergalaxy.com", IP: net.IP{212, 103, 49, 67}},
|
||||
{Region: "US West", City: "Los Angeles", Hostname: "us-west-040.whiskergalaxy.com", IP: net.IP{89, 187, 185, 34}},
|
||||
{Region: "US West", City: "Los Angeles", Hostname: "us-west-044.whiskergalaxy.com", IP: net.IP{192, 3, 20, 51}},
|
||||
{Region: "US West", City: "Los Angeles", Hostname: "us-west-047.whiskergalaxy.com", IP: net.IP{172, 241, 214, 202}},
|
||||
{Region: "US West", City: "Los Angeles", Hostname: "us-west-055.whiskergalaxy.com", IP: net.IP{104, 129, 3, 67}},
|
||||
{Region: "US West", City: "Los Angeles", Hostname: "us-west-059.whiskergalaxy.com", IP: net.IP{104, 129, 3, 163}},
|
||||
{Region: "US West", City: "Los Angeles", Hostname: "us-west-060.whiskergalaxy.com", IP: net.IP{217, 138, 217, 51}},
|
||||
{Region: "US West", City: "Los Angeles", Hostname: "us-west-063.whiskergalaxy.com", IP: net.IP{198, 23, 242, 147}},
|
||||
{Region: "US West", City: "Los Angeles", Hostname: "us-west-065.whiskergalaxy.com", IP: net.IP{217, 138, 217, 211}},
|
||||
{Region: "US West", City: "Los Angeles", Hostname: "us-west-066.whiskergalaxy.com", IP: net.IP{89, 187, 187, 98}},
|
||||
{Region: "US West", City: "Phoenix", Hostname: "us-west-046.whiskergalaxy.com", IP: net.IP{23, 83, 130, 166}},
|
||||
{Region: "US West", City: "Phoenix", Hostname: "us-west-061.whiskergalaxy.com", IP: net.IP{23, 83, 131, 187}},
|
||||
{Region: "US West", City: "San Francisco", Hostname: "us-west-048.whiskergalaxy.com", IP: net.IP{172, 241, 250, 131}},
|
||||
{Region: "US West", City: "San Francisco", Hostname: "us-west-053.whiskergalaxy.com", IP: net.IP{209, 58, 129, 121}},
|
||||
{Region: "US West", City: "San Francisco", Hostname: "us-west-054.whiskergalaxy.com", IP: net.IP{172, 255, 125, 141}},
|
||||
{Region: "US West", City: "San Jose", Hostname: "us-west-052.whiskergalaxy.com", IP: net.IP{66, 115, 176, 3}},
|
||||
{Region: "US West", City: "Santa Clara", Hostname: "us-west-050.whiskergalaxy.com", IP: net.IP{167, 88, 60, 227}},
|
||||
{Region: "US West", City: "Santa Clara", Hostname: "us-west-051.whiskergalaxy.com", IP: net.IP{167, 88, 60, 243}},
|
||||
{Region: "US West", City: "Seattle", Hostname: "us-west-043.whiskergalaxy.com", IP: net.IP{23, 94, 74, 99}},
|
||||
{Region: "US West", City: "Seattle", Hostname: "us-west-045.whiskergalaxy.com", IP: net.IP{64, 120, 2, 174}},
|
||||
{Region: "US West", City: "Seattle", Hostname: "us-west-056.whiskergalaxy.com", IP: net.IP{104, 129, 56, 67}},
|
||||
{Region: "US West", City: "Seattle", Hostname: "us-west-057.whiskergalaxy.com", IP: net.IP{104, 129, 56, 131}},
|
||||
{Region: "US West", City: "Seattle", Hostname: "us-west-062.whiskergalaxy.com", IP: net.IP{198, 12, 116, 195}},
|
||||
{Region: "Ukraine", City: "Kyiv", Hostname: "ua-006.whiskergalaxy.com", IP: net.IP{45, 141, 156, 11}},
|
||||
{Region: "Ukraine", City: "Kyiv", Hostname: "ua-007.whiskergalaxy.com", IP: net.IP{45, 141, 156, 50}},
|
||||
{Region: "United Arab Emirates", City: "Dubai", Hostname: "ae-001.whiskergalaxy.com", IP: net.IP{45, 9, 249, 43}},
|
||||
{Region: "United Kingdom", City: "Edinburgh", Hostname: "uk-026.whiskergalaxy.com", IP: net.IP{193, 36, 118, 243}},
|
||||
{Region: "United Kingdom", City: "London", Hostname: "uk-007.whiskergalaxy.com", IP: net.IP{185, 212, 168, 133}},
|
||||
{Region: "United Kingdom", City: "London", Hostname: "uk-013.whiskergalaxy.com", IP: net.IP{89, 238, 150, 229}},
|
||||
{Region: "United Kingdom", City: "London", Hostname: "uk-014.whiskergalaxy.com", IP: net.IP{2, 58, 29, 145}},
|
||||
{Region: "United Kingdom", City: "London", Hostname: "uk-015.whiskergalaxy.com", IP: net.IP{2, 58, 29, 17}},
|
||||
{Region: "United Kingdom", City: "London", Hostname: "uk-017.whiskergalaxy.com", IP: net.IP{84, 17, 50, 130}},
|
||||
{Region: "United Kingdom", City: "London", Hostname: "uk-021.whiskergalaxy.com", IP: net.IP{212, 102, 63, 32}},
|
||||
{Region: "United Kingdom", City: "London", Hostname: "uk-022.whiskergalaxy.com", IP: net.IP{212, 102, 63, 62}},
|
||||
{Region: "United Kingdom", City: "London", Hostname: "uk-024.whiskergalaxy.com", IP: net.IP{217, 138, 254, 51}},
|
||||
{Region: "United Kingdom", City: "Manchester", Hostname: "uk-008.whiskergalaxy.com", IP: net.IP{81, 92, 207, 69}},
|
||||
{Region: "United Kingdom", City: "Manchester", Hostname: "uk-010.whiskergalaxy.com", IP: net.IP{89, 238, 135, 133}},
|
||||
{Region: "United Kingdom", City: "Manchester", Hostname: "uk-025.whiskergalaxy.com", IP: net.IP{89, 44, 201, 99}},
|
||||
{Region: "Vietnam", City: "Hanoi", Hostname: "vn-001.whiskergalaxy.com", IP: net.IP{103, 9, 76, 197}},
|
||||
{Region: "Vietnam", City: "Hanoi", Hostname: "vn-002.whiskergalaxy.com", IP: net.IP{103, 9, 79, 186}},
|
||||
{Region: "Vietnam", City: "Hanoi", Hostname: "vn-003.whiskergalaxy.com", IP: net.IP{103, 9, 79, 219}},
|
||||
{Region: "WINDFLIX CA", City: "Toronto", Hostname: "wf-ca-003.whiskergalaxy.com", IP: net.IP{104, 218, 60, 111}},
|
||||
{Region: "WINDFLIX CA", City: "Toronto", Hostname: "wf-ca-004.whiskergalaxy.com", IP: net.IP{104, 254, 92, 99}},
|
||||
{Region: "WINDFLIX JP", City: "Tokyo", Hostname: "wf-jp-002.whiskergalaxy.com", IP: net.IP{5, 181, 235, 67}},
|
||||
{Region: "WINDFLIX UK", City: "London", Hostname: "wf-uk-001.whiskergalaxy.com", IP: net.IP{45, 9, 248, 3}},
|
||||
{Region: "WINDFLIX UK", City: "London", Hostname: "wf-uk-006.whiskergalaxy.com", IP: net.IP{81, 92, 200, 85}},
|
||||
{Region: "WINDFLIX UK", City: "London", Hostname: "wf-uk-007.whiskergalaxy.com", IP: net.IP{89, 47, 62, 83}},
|
||||
{Region: "WINDFLIX US", City: "New York", Hostname: "wf-us-010.whiskergalaxy.com", IP: net.IP{38, 132, 122, 195}},
|
||||
{Region: "WINDFLIX US", City: "New York", Hostname: "wf-us-011.whiskergalaxy.com", IP: net.IP{38, 132, 122, 131}},
|
||||
{Region: "WINDFLIX US", City: "New York", Hostname: "wf-us-012.whiskergalaxy.com", IP: net.IP{185, 232, 22, 131}},
|
||||
{Region: "WINDFLIX US", City: "New York", Hostname: "wf-us-013.whiskergalaxy.com", IP: net.IP{217, 138, 206, 211}},
|
||||
{Region: "WINDFLIX US", City: "New York", Hostname: "wf-us-014.whiskergalaxy.com", IP: net.IP{77, 81, 136, 99}},
|
||||
{Region: "WINDFLIX US", City: "New York", Hostname: "wf-us-015.whiskergalaxy.com", IP: net.IP{38, 132, 101, 211}},
|
||||
}
|
||||
}
|
||||
347
internal/dns/loop.go
Normal file
347
internal/dns/loop.go
Normal file
@@ -0,0 +1,347 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/dns/pkg/unbound"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/settings"
|
||||
"github.com/qdm12/golibs/command"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
)
|
||||
|
||||
type Looper interface {
|
||||
Run(ctx context.Context, wg *sync.WaitGroup, dnsReadyCh chan<- struct{})
|
||||
RunRestartTicker(ctx context.Context, wg *sync.WaitGroup)
|
||||
GetStatus() (status models.LoopStatus)
|
||||
SetStatus(status models.LoopStatus) (outcome string, err error)
|
||||
GetSettings() (settings settings.DNS)
|
||||
SetSettings(settings settings.DNS) (outcome string)
|
||||
}
|
||||
|
||||
type looper struct {
|
||||
state state
|
||||
conf unbound.Configurator
|
||||
client *http.Client
|
||||
logger logging.Logger
|
||||
streamMerger command.StreamMerger
|
||||
username string
|
||||
puid int
|
||||
pgid int
|
||||
loopLock sync.Mutex
|
||||
start chan struct{}
|
||||
running chan models.LoopStatus
|
||||
stop chan struct{}
|
||||
stopped chan struct{}
|
||||
updateTicker chan struct{}
|
||||
backoffTime time.Duration
|
||||
timeNow func() time.Time
|
||||
timeSince func(time.Time) time.Duration
|
||||
}
|
||||
|
||||
const defaultBackoffTime = 10 * time.Second
|
||||
|
||||
func NewLooper(conf unbound.Configurator, settings settings.DNS, client *http.Client,
|
||||
logger logging.Logger, streamMerger command.StreamMerger,
|
||||
username string, puid, pgid int) Looper {
|
||||
return &looper{
|
||||
state: state{
|
||||
status: constants.Stopped,
|
||||
settings: settings,
|
||||
},
|
||||
conf: conf,
|
||||
client: client,
|
||||
logger: logger.WithPrefix("dns over tls: "),
|
||||
username: username,
|
||||
puid: puid,
|
||||
pgid: pgid,
|
||||
streamMerger: streamMerger,
|
||||
start: make(chan struct{}),
|
||||
running: make(chan models.LoopStatus),
|
||||
stop: make(chan struct{}),
|
||||
stopped: make(chan struct{}),
|
||||
updateTicker: make(chan struct{}),
|
||||
backoffTime: defaultBackoffTime,
|
||||
timeNow: time.Now,
|
||||
timeSince: time.Since,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *looper) logAndWait(ctx context.Context, err error) {
|
||||
l.logger.Warn(err)
|
||||
l.logger.Info("attempting restart in %s", l.backoffTime)
|
||||
timer := time.NewTimer(l.backoffTime)
|
||||
l.backoffTime *= 2
|
||||
select {
|
||||
case <-timer.C:
|
||||
case <-ctx.Done():
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup, dnsReadyCh chan<- struct{}) {
|
||||
defer wg.Done()
|
||||
|
||||
const fallback = false
|
||||
l.useUnencryptedDNS(fallback) // TODO remove? Use default DNS by default for Docker resolution?
|
||||
// TODO this one is kept if DNS_KEEP_NAMESERVER=on and should be replaced
|
||||
|
||||
select {
|
||||
case <-l.start:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
|
||||
defer l.logger.Warn("loop exited")
|
||||
|
||||
crashed := false
|
||||
l.backoffTime = defaultBackoffTime
|
||||
|
||||
for ctx.Err() == nil {
|
||||
// Upper scope variables for Unbound only
|
||||
var unboundCancel context.CancelFunc = func() {}
|
||||
waitError := make(chan error)
|
||||
|
||||
for l.GetSettings().Enabled {
|
||||
if ctx.Err() != nil {
|
||||
l.logger.Warn("context canceled: exiting loop")
|
||||
return
|
||||
}
|
||||
var err error
|
||||
unboundCancel, err = l.setupUnbound(ctx, crashed, waitError)
|
||||
if err != nil {
|
||||
if !errors.Is(err, errUpdateFiles) {
|
||||
const fallback = true
|
||||
l.useUnencryptedDNS(fallback)
|
||||
}
|
||||
l.logAndWait(ctx, err)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if !l.GetSettings().Enabled {
|
||||
const fallback = false
|
||||
l.useUnencryptedDNS(fallback)
|
||||
}
|
||||
|
||||
dnsReadyCh <- struct{}{}
|
||||
|
||||
stayHere := true
|
||||
for stayHere {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
l.logger.Warn("context canceled: exiting loop")
|
||||
unboundCancel()
|
||||
<-waitError
|
||||
close(waitError)
|
||||
return
|
||||
case <-l.stop:
|
||||
l.logger.Info("stopping")
|
||||
const fallback = false
|
||||
l.useUnencryptedDNS(fallback)
|
||||
unboundCancel()
|
||||
<-waitError
|
||||
l.stopped <- struct{}{}
|
||||
case <-l.start:
|
||||
l.logger.Info("starting")
|
||||
stayHere = false
|
||||
case err := <-waitError: // unexpected error
|
||||
unboundCancel()
|
||||
l.state.setStatusWithLock(constants.Crashed)
|
||||
const fallback = true
|
||||
l.useUnencryptedDNS(fallback)
|
||||
l.logAndWait(ctx, err)
|
||||
stayHere = false
|
||||
}
|
||||
}
|
||||
close(waitError)
|
||||
unboundCancel()
|
||||
}
|
||||
}
|
||||
|
||||
var errUpdateFiles = errors.New("cannot update files")
|
||||
|
||||
// Returning cancel == nil signals we want to re-run setupUnbound
|
||||
// Returning err == errUpdateFiles signals we should not fall back
|
||||
// on the plaintext DNS as DOT is still up and running.
|
||||
func (l *looper) setupUnbound(ctx context.Context,
|
||||
previousCrashed bool, waitError chan<- error) (cancel context.CancelFunc, err error) {
|
||||
err = l.updateFiles(ctx)
|
||||
if err != nil {
|
||||
l.state.setStatusWithLock(constants.Crashed)
|
||||
return nil, errUpdateFiles
|
||||
}
|
||||
|
||||
settings := l.GetSettings()
|
||||
|
||||
unboundCtx, cancel := context.WithCancel(context.Background())
|
||||
stream, waitFn, err := l.conf.Start(unboundCtx, settings.Unbound.VerbosityDetailsLevel)
|
||||
if err != nil {
|
||||
cancel()
|
||||
if !previousCrashed {
|
||||
l.running <- constants.Crashed
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Started successfully
|
||||
go l.streamMerger.Merge(unboundCtx, stream, command.MergeName("unbound"))
|
||||
|
||||
l.conf.UseDNSInternally(net.IP{127, 0, 0, 1}) // use Unbound
|
||||
if err := l.conf.UseDNSSystemWide(net.IP{127, 0, 0, 1}, settings.KeepNameserver); err != nil { // use Unbound
|
||||
l.logger.Error(err)
|
||||
}
|
||||
|
||||
if err := l.conf.WaitForUnbound(ctx); err != nil {
|
||||
if !previousCrashed {
|
||||
l.running <- constants.Crashed
|
||||
}
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := waitFn() // blocking
|
||||
waitError <- err
|
||||
}()
|
||||
|
||||
l.logger.Info("ready")
|
||||
if !previousCrashed {
|
||||
l.running <- constants.Running
|
||||
} else {
|
||||
l.backoffTime = defaultBackoffTime
|
||||
l.state.setStatusWithLock(constants.Running)
|
||||
}
|
||||
return cancel, nil
|
||||
}
|
||||
|
||||
func (l *looper) useUnencryptedDNS(fallback bool) {
|
||||
settings := l.GetSettings()
|
||||
|
||||
// Try with user provided plaintext ip address
|
||||
targetIP := settings.PlaintextAddress
|
||||
if targetIP != nil {
|
||||
if fallback {
|
||||
l.logger.Info("falling back on plaintext DNS at address %s", targetIP)
|
||||
} else {
|
||||
l.logger.Info("using plaintext DNS at address %s", targetIP)
|
||||
}
|
||||
l.conf.UseDNSInternally(targetIP)
|
||||
if err := l.conf.UseDNSSystemWide(targetIP, settings.KeepNameserver); err != nil {
|
||||
l.logger.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Try with any IPv4 address from the providers chosen
|
||||
for _, provider := range settings.Unbound.Providers {
|
||||
data, _ := unbound.GetProviderData(provider)
|
||||
for _, targetIP = range data.IPs {
|
||||
if targetIP.To4() != nil {
|
||||
if fallback {
|
||||
l.logger.Info("falling back on plaintext DNS at address %s", targetIP)
|
||||
} else {
|
||||
l.logger.Info("using plaintext DNS at address %s", targetIP)
|
||||
}
|
||||
l.conf.UseDNSInternally(targetIP)
|
||||
if err := l.conf.UseDNSSystemWide(targetIP, settings.KeepNameserver); err != nil {
|
||||
l.logger.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No IPv4 address found
|
||||
l.logger.Error("no ipv4 DNS address found for providers %s", settings.Unbound.Providers)
|
||||
}
|
||||
|
||||
func (l *looper) RunRestartTicker(ctx context.Context, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
// Timer that acts as a ticker
|
||||
timer := time.NewTimer(time.Hour)
|
||||
timer.Stop()
|
||||
timerIsStopped := true
|
||||
settings := l.GetSettings()
|
||||
if settings.UpdatePeriod > 0 {
|
||||
timer.Reset(settings.UpdatePeriod)
|
||||
timerIsStopped = false
|
||||
}
|
||||
lastTick := time.Unix(0, 0)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if !timerIsStopped && !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
return
|
||||
case <-timer.C:
|
||||
lastTick = l.timeNow()
|
||||
|
||||
status := l.GetStatus()
|
||||
if status == constants.Running {
|
||||
if err := l.updateFiles(ctx); err != nil {
|
||||
l.state.setStatusWithLock(constants.Crashed)
|
||||
l.logger.Error(err)
|
||||
l.logger.Warn("skipping Unbound restart due to failed files update")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
_, _ = l.SetStatus(constants.Stopped)
|
||||
_, _ = l.SetStatus(constants.Running)
|
||||
|
||||
settings := l.GetSettings()
|
||||
timer.Reset(settings.UpdatePeriod)
|
||||
case <-l.updateTicker:
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
timerIsStopped = true
|
||||
settings := l.GetSettings()
|
||||
newUpdatePeriod := settings.UpdatePeriod
|
||||
if newUpdatePeriod == 0 {
|
||||
continue
|
||||
}
|
||||
var waited time.Duration
|
||||
if lastTick.UnixNano() != 0 {
|
||||
waited = l.timeSince(lastTick)
|
||||
}
|
||||
leftToWait := newUpdatePeriod - waited
|
||||
timer.Reset(leftToWait)
|
||||
timerIsStopped = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *looper) updateFiles(ctx context.Context) (err error) {
|
||||
l.logger.Info("downloading DNS over TLS cryptographic files")
|
||||
if err := l.conf.SetupFiles(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
settings := l.GetSettings()
|
||||
|
||||
l.logger.Info("downloading hostnames and IP block lists")
|
||||
hostnameLines, ipLines, errs := l.conf.BuildBlocked(ctx, l.client,
|
||||
settings.BlockMalicious, settings.BlockAds, settings.BlockSurveillance,
|
||||
settings.Unbound.BlockedHostnames, settings.Unbound.BlockedIPs,
|
||||
settings.Unbound.AllowedHostnames)
|
||||
for _, err := range errs {
|
||||
l.logger.Warn(err)
|
||||
}
|
||||
|
||||
if err := l.conf.MakeUnboundConf(
|
||||
settings.Unbound, hostnameLines, ipLines,
|
||||
l.username, l.puid, l.pgid); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
99
internal/dns/state.go
Normal file
99
internal/dns/state.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/settings"
|
||||
)
|
||||
|
||||
type state struct {
|
||||
status models.LoopStatus
|
||||
settings settings.DNS
|
||||
statusMu sync.RWMutex
|
||||
settingsMu sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *state) setStatusWithLock(status models.LoopStatus) {
|
||||
s.statusMu.Lock()
|
||||
defer s.statusMu.Unlock()
|
||||
s.status = status
|
||||
}
|
||||
|
||||
func (l *looper) GetStatus() (status models.LoopStatus) {
|
||||
l.state.statusMu.RLock()
|
||||
defer l.state.statusMu.RUnlock()
|
||||
return l.state.status
|
||||
}
|
||||
|
||||
func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) {
|
||||
l.state.statusMu.Lock()
|
||||
defer l.state.statusMu.Unlock()
|
||||
existingStatus := l.state.status
|
||||
|
||||
switch status {
|
||||
case constants.Running:
|
||||
switch existingStatus {
|
||||
case constants.Starting, constants.Running, constants.Stopping, constants.Crashed:
|
||||
return fmt.Sprintf("already %s", existingStatus), nil
|
||||
}
|
||||
l.loopLock.Lock()
|
||||
defer l.loopLock.Unlock()
|
||||
l.state.status = constants.Starting
|
||||
l.state.statusMu.Unlock()
|
||||
l.start <- struct{}{}
|
||||
newStatus := <-l.running
|
||||
l.state.statusMu.Lock()
|
||||
l.state.status = newStatus
|
||||
return newStatus.String(), nil
|
||||
case constants.Stopped:
|
||||
switch existingStatus {
|
||||
case constants.Starting, constants.Stopping, constants.Stopped, constants.Crashed:
|
||||
return fmt.Sprintf("already %s", existingStatus), nil
|
||||
}
|
||||
l.loopLock.Lock()
|
||||
defer l.loopLock.Unlock()
|
||||
l.state.status = constants.Stopping
|
||||
l.state.statusMu.Unlock()
|
||||
l.stop <- struct{}{}
|
||||
<-l.stopped
|
||||
l.state.statusMu.Lock()
|
||||
l.state.status = constants.Stopped
|
||||
return status.String(), nil
|
||||
default:
|
||||
return "", fmt.Errorf("status %q can only be %q or %q",
|
||||
status, constants.Running, constants.Stopped)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *looper) GetSettings() (settings settings.DNS) {
|
||||
l.state.settingsMu.RLock()
|
||||
defer l.state.settingsMu.RUnlock()
|
||||
return l.state.settings
|
||||
}
|
||||
|
||||
func (l *looper) SetSettings(settings settings.DNS) (outcome string) {
|
||||
l.state.settingsMu.Lock()
|
||||
settingsUnchanged := reflect.DeepEqual(l.state.settings, settings)
|
||||
if settingsUnchanged {
|
||||
l.state.settingsMu.Unlock()
|
||||
return "settings left unchanged"
|
||||
}
|
||||
tempSettings := l.state.settings
|
||||
tempSettings.UpdatePeriod = settings.UpdatePeriod
|
||||
onlyUpdatePeriodChanged := reflect.DeepEqual(tempSettings, settings)
|
||||
l.state.settings = settings
|
||||
l.state.settingsMu.Unlock()
|
||||
if onlyUpdatePeriodChanged {
|
||||
l.updateTicker <- struct{}{}
|
||||
return "update period changed"
|
||||
}
|
||||
_, _ = l.SetStatus(constants.Stopped)
|
||||
if settings.Enabled {
|
||||
outcome, _ = l.SetStatus(constants.Running)
|
||||
}
|
||||
return outcome
|
||||
}
|
||||
124
internal/firewall/enable.go
Normal file
124
internal/firewall/enable.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
func (c *configurator) SetEnabled(ctx context.Context, enabled bool) (err error) {
|
||||
c.stateMutex.Lock()
|
||||
defer c.stateMutex.Unlock()
|
||||
|
||||
if enabled == c.enabled {
|
||||
if enabled {
|
||||
c.logger.Info("already enabled")
|
||||
} else {
|
||||
c.logger.Info("already disabled")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if !enabled {
|
||||
c.logger.Info("disabling...")
|
||||
if err = c.disable(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
c.enabled = false
|
||||
c.logger.Info("disabled successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
c.logger.Info("enabling...")
|
||||
|
||||
if err := c.enable(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
c.enabled = true
|
||||
c.logger.Info("enabled successfully")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *configurator) disable(ctx context.Context) (err error) {
|
||||
if err = c.clearAllRules(ctx); err != nil {
|
||||
return fmt.Errorf("cannot disable firewall: %w", err)
|
||||
}
|
||||
if err = c.setAllPolicies(ctx, "ACCEPT"); err != nil {
|
||||
return fmt.Errorf("cannot disable firewall: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// To use in defered call when enabling the firewall.
|
||||
func (c *configurator) fallbackToDisabled(ctx context.Context) {
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
if err := c.SetEnabled(ctx, false); err != nil {
|
||||
c.logger.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *configurator) enable(ctx context.Context) (err error) {
|
||||
if err = c.setAllPolicies(ctx, "DROP"); err != nil {
|
||||
return fmt.Errorf("cannot enable firewall: %w", err)
|
||||
}
|
||||
|
||||
const remove = false
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
c.fallbackToDisabled(ctx)
|
||||
}
|
||||
}()
|
||||
|
||||
// Loopback traffic
|
||||
if err = c.acceptInputThroughInterface(ctx, "lo", remove); err != nil {
|
||||
return fmt.Errorf("cannot enable firewall: %w", err)
|
||||
}
|
||||
if err = c.acceptOutputThroughInterface(ctx, "lo", remove); err != nil {
|
||||
return fmt.Errorf("cannot enable firewall: %w", err)
|
||||
}
|
||||
|
||||
if err = c.acceptEstablishedRelatedTraffic(ctx, remove); err != nil {
|
||||
return fmt.Errorf("cannot enable firewall: %w", err)
|
||||
}
|
||||
if c.vpnConnection.IP != nil {
|
||||
if err = c.acceptOutputTrafficToVPN(ctx, c.defaultInterface, c.vpnConnection, remove); err != nil {
|
||||
return fmt.Errorf("cannot enable firewall: %w", err)
|
||||
}
|
||||
}
|
||||
if err = c.acceptOutputThroughInterface(ctx, string(constants.TUN), remove); err != nil {
|
||||
return fmt.Errorf("cannot enable firewall: %w", err)
|
||||
}
|
||||
|
||||
if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, c.localSubnet, remove); err != nil {
|
||||
return fmt.Errorf("cannot enable firewall: %w", err)
|
||||
}
|
||||
|
||||
for _, subnet := range c.outboundSubnets {
|
||||
if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, subnet, remove); err != nil {
|
||||
return fmt.Errorf("cannot enable firewall: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Allows packets from any IP address to go through eth0 / local network
|
||||
// to reach Gluetun.
|
||||
if err := c.acceptInputToSubnet(ctx, c.defaultInterface, c.localSubnet, remove); err != nil {
|
||||
return fmt.Errorf("cannot enable firewall: %w", err)
|
||||
}
|
||||
|
||||
for port, intf := range c.allowedInputPorts {
|
||||
if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
|
||||
return fmt.Errorf("cannot enable firewall: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.runUserPostRules(ctx, "/iptables/post-rules.txt", remove); err != nil {
|
||||
return fmt.Errorf("cannot enable firewall: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
72
internal/firewall/firewall.go
Normal file
72
internal/firewall/firewall.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/routing"
|
||||
"github.com/qdm12/golibs/command"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
"github.com/qdm12/golibs/os"
|
||||
)
|
||||
|
||||
// Configurator allows to change firewall rules and modify network routes.
|
||||
type Configurator interface {
|
||||
Version(ctx context.Context) (string, error)
|
||||
SetEnabled(ctx context.Context, enabled bool) (err error)
|
||||
SetVPNConnection(ctx context.Context, connection models.OpenVPNConnection) (err error)
|
||||
SetAllowedPort(ctx context.Context, port uint16, intf string) (err error)
|
||||
SetOutboundSubnets(ctx context.Context, subnets []net.IPNet) (err error)
|
||||
RemoveAllowedPort(ctx context.Context, port uint16) (err error)
|
||||
SetDebug()
|
||||
// SetNetworkInformation is meant to be called only once
|
||||
SetNetworkInformation(defaultInterface string, defaultGateway net.IP, localSubnet net.IPNet, localIP net.IP)
|
||||
}
|
||||
|
||||
type configurator struct { //nolint:maligned
|
||||
commander command.Commander
|
||||
logger logging.Logger
|
||||
routing routing.Routing
|
||||
openFile os.OpenFileFunc // for custom iptables rules
|
||||
iptablesMutex sync.Mutex
|
||||
debug bool
|
||||
defaultInterface string
|
||||
defaultGateway net.IP
|
||||
localSubnet net.IPNet
|
||||
localIP net.IP
|
||||
networkInfoMutex sync.Mutex
|
||||
|
||||
// State
|
||||
enabled bool
|
||||
vpnConnection models.OpenVPNConnection
|
||||
outboundSubnets []net.IPNet
|
||||
allowedInputPorts map[uint16]string // port to interface mapping
|
||||
stateMutex sync.Mutex
|
||||
}
|
||||
|
||||
// NewConfigurator creates a new Configurator instance.
|
||||
func NewConfigurator(logger logging.Logger, routing routing.Routing, openFile os.OpenFileFunc) Configurator {
|
||||
return &configurator{
|
||||
commander: command.NewCommander(),
|
||||
logger: logger.WithPrefix("firewall: "),
|
||||
routing: routing,
|
||||
openFile: openFile,
|
||||
allowedInputPorts: make(map[uint16]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *configurator) SetDebug() {
|
||||
c.debug = true
|
||||
}
|
||||
|
||||
func (c *configurator) SetNetworkInformation(
|
||||
defaultInterface string, defaultGateway net.IP, localSubnet net.IPNet, localIP net.IP) {
|
||||
c.networkInfoMutex.Lock()
|
||||
defer c.networkInfoMutex.Unlock()
|
||||
c.defaultInterface = defaultInterface
|
||||
c.defaultGateway = defaultGateway
|
||||
c.localSubnet = localSubnet
|
||||
c.localIP = localIP
|
||||
}
|
||||
194
internal/firewall/iptables.go
Normal file
194
internal/firewall/iptables.go
Normal file
@@ -0,0 +1,194 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
func appendOrDelete(remove bool) string {
|
||||
if remove {
|
||||
return "--delete"
|
||||
}
|
||||
return "--append"
|
||||
}
|
||||
|
||||
// flipRule changes an append rule in a delete rule or a delete rule into an
|
||||
// append rule.
|
||||
func flipRule(rule string) string {
|
||||
switch {
|
||||
case strings.HasPrefix(rule, "-A"):
|
||||
return strings.Replace(rule, "-A", "-D", 1)
|
||||
case strings.HasPrefix(rule, "--append"):
|
||||
return strings.Replace(rule, "--append", "-D", 1)
|
||||
case strings.HasPrefix(rule, "-D"):
|
||||
return strings.Replace(rule, "-D", "-A", 1)
|
||||
case strings.HasPrefix(rule, "--delete"):
|
||||
return strings.Replace(rule, "--delete", "-A", 1)
|
||||
}
|
||||
return rule
|
||||
}
|
||||
|
||||
// Version obtains the version of the installed iptables.
|
||||
func (c *configurator) Version(ctx context.Context) (string, error) {
|
||||
output, err := c.commander.Run(ctx, "iptables", "--version")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
words := strings.Fields(output)
|
||||
const minWords = 2
|
||||
if len(words) < minWords {
|
||||
return "", fmt.Errorf("iptables --version: output is too short: %q", output)
|
||||
}
|
||||
return words[1], nil
|
||||
}
|
||||
|
||||
func (c *configurator) runIptablesInstructions(ctx context.Context, instructions []string) error {
|
||||
for _, instruction := range instructions {
|
||||
if err := c.runIptablesInstruction(ctx, instruction); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *configurator) runIptablesInstruction(ctx context.Context, instruction string) error {
|
||||
c.iptablesMutex.Lock() // only one iptables command at once
|
||||
defer c.iptablesMutex.Unlock()
|
||||
if c.debug {
|
||||
fmt.Printf("iptables %s\n", instruction)
|
||||
}
|
||||
flags := strings.Fields(instruction)
|
||||
if output, err := c.commander.Run(ctx, "iptables", flags...); err != nil {
|
||||
return fmt.Errorf("failed executing \"iptables %s\": %s: %w", instruction, output, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *configurator) clearAllRules(ctx context.Context) error {
|
||||
return c.runIptablesInstructions(ctx, []string{
|
||||
"--flush", // flush all chains
|
||||
"--delete-chain", // delete all chains
|
||||
})
|
||||
}
|
||||
|
||||
func (c *configurator) setAllPolicies(ctx context.Context, policy string) error {
|
||||
switch policy {
|
||||
case "ACCEPT", "DROP":
|
||||
default:
|
||||
return fmt.Errorf("policy %q not recognized", policy)
|
||||
}
|
||||
return c.runIptablesInstructions(ctx, []string{
|
||||
fmt.Sprintf("--policy INPUT %s", policy),
|
||||
fmt.Sprintf("--policy OUTPUT %s", policy),
|
||||
fmt.Sprintf("--policy FORWARD %s", policy),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *configurator) acceptInputThroughInterface(ctx context.Context, intf string, remove bool) error {
|
||||
return c.runIptablesInstruction(ctx, fmt.Sprintf(
|
||||
"%s INPUT -i %s -j ACCEPT", appendOrDelete(remove), intf,
|
||||
))
|
||||
}
|
||||
|
||||
func (c *configurator) acceptInputToSubnet(ctx context.Context, intf string, destination net.IPNet, remove bool) error {
|
||||
interfaceFlag := "-i " + intf
|
||||
if intf == "*" { // all interfaces
|
||||
interfaceFlag = ""
|
||||
}
|
||||
return c.runIptablesInstruction(ctx, fmt.Sprintf(
|
||||
"%s INPUT %s -d %s -j ACCEPT", appendOrDelete(remove), interfaceFlag, destination.String(),
|
||||
))
|
||||
}
|
||||
|
||||
func (c *configurator) acceptOutputThroughInterface(ctx context.Context, intf string, remove bool) error {
|
||||
return c.runIptablesInstruction(ctx, fmt.Sprintf(
|
||||
"%s OUTPUT -o %s -j ACCEPT", appendOrDelete(remove), intf,
|
||||
))
|
||||
}
|
||||
|
||||
func (c *configurator) acceptEstablishedRelatedTraffic(ctx context.Context, remove bool) error {
|
||||
return c.runIptablesInstructions(ctx, []string{
|
||||
fmt.Sprintf("%s OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT", appendOrDelete(remove)),
|
||||
fmt.Sprintf("%s INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT", appendOrDelete(remove)),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *configurator) acceptOutputTrafficToVPN(ctx context.Context,
|
||||
defaultInterface string, connection models.OpenVPNConnection, remove bool) error {
|
||||
return c.runIptablesInstruction(ctx,
|
||||
fmt.Sprintf("%s OUTPUT -d %s -o %s -p %s -m %s --dport %d -j ACCEPT",
|
||||
appendOrDelete(remove), connection.IP, defaultInterface, connection.Protocol, connection.Protocol, connection.Port))
|
||||
}
|
||||
|
||||
// Thanks to @npawelek.
|
||||
func (c *configurator) acceptOutputFromIPToSubnet(ctx context.Context,
|
||||
intf string, sourceIP net.IP, destinationSubnet net.IPNet, remove bool) error {
|
||||
interfaceFlag := "-o " + intf
|
||||
if intf == "*" { // all interfaces
|
||||
interfaceFlag = ""
|
||||
}
|
||||
return c.runIptablesInstruction(ctx, fmt.Sprintf(
|
||||
"%s OUTPUT %s -s %s -d %s -j ACCEPT",
|
||||
appendOrDelete(remove), interfaceFlag, sourceIP.String(), destinationSubnet.String(),
|
||||
))
|
||||
}
|
||||
|
||||
// Used for port forwarding, with intf set to tun.
|
||||
func (c *configurator) acceptInputToPort(ctx context.Context, intf string, port uint16, remove bool) error {
|
||||
interfaceFlag := "-i " + intf
|
||||
if intf == "*" { // all interfaces
|
||||
interfaceFlag = ""
|
||||
}
|
||||
return c.runIptablesInstructions(ctx, []string{
|
||||
fmt.Sprintf("%s INPUT %s -p tcp --dport %d -j ACCEPT", appendOrDelete(remove), interfaceFlag, port),
|
||||
fmt.Sprintf("%s INPUT %s -p udp --dport %d -j ACCEPT", appendOrDelete(remove), interfaceFlag, port),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *configurator) runUserPostRules(ctx context.Context, filepath string, remove bool) error {
|
||||
file, err := c.openFile(filepath, os.O_RDONLY, 0)
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
_ = file.Close()
|
||||
return err
|
||||
}
|
||||
if err := file.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
lines := strings.Split(string(b), "\n")
|
||||
successfulRules := []string{}
|
||||
defer func() {
|
||||
// transaction-like rollback
|
||||
if err == nil || ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
for _, rule := range successfulRules {
|
||||
_ = c.runIptablesInstruction(ctx, flipRule(rule))
|
||||
}
|
||||
}()
|
||||
for _, line := range lines {
|
||||
if !strings.HasPrefix(line, "iptables ") {
|
||||
continue
|
||||
}
|
||||
rule := strings.TrimPrefix(line, "iptables ")
|
||||
if remove {
|
||||
rule = flipRule(rule)
|
||||
}
|
||||
if err = c.runIptablesInstruction(ctx, rule); err != nil {
|
||||
return fmt.Errorf("cannot run custom rule: %w", err)
|
||||
}
|
||||
successfulRules = append(successfulRules, rule)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
56
internal/firewall/outboundsubnets.go
Normal file
56
internal/firewall/outboundsubnets.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
func (c *configurator) SetOutboundSubnets(ctx context.Context, subnets []net.IPNet) (err error) {
|
||||
c.stateMutex.Lock()
|
||||
defer c.stateMutex.Unlock()
|
||||
|
||||
if !c.enabled {
|
||||
c.logger.Info("firewall disabled, only updating allowed subnets internal list")
|
||||
c.outboundSubnets = make([]net.IPNet, len(subnets))
|
||||
copy(c.outboundSubnets, subnets)
|
||||
return nil
|
||||
}
|
||||
|
||||
c.logger.Info("setting allowed subnets through firewall...")
|
||||
|
||||
subnetsToAdd := findSubnetsToAdd(c.outboundSubnets, subnets)
|
||||
subnetsToRemove := findSubnetsToRemove(c.outboundSubnets, subnets)
|
||||
if len(subnetsToAdd) == 0 && len(subnetsToRemove) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.removeOutboundSubnets(ctx, subnetsToRemove)
|
||||
if err := c.addOutboundSubnets(ctx, subnetsToAdd); err != nil {
|
||||
return fmt.Errorf("cannot set allowed subnets through firewall: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *configurator) removeOutboundSubnets(ctx context.Context, subnets []net.IPNet) {
|
||||
const remove = true
|
||||
for _, subnet := range subnets {
|
||||
if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, subnet, remove); err != nil {
|
||||
c.logger.Error("cannot remove outdated outbound subnet through firewall: %s", err)
|
||||
continue
|
||||
}
|
||||
c.outboundSubnets = removeSubnetFromSubnets(c.outboundSubnets, subnet)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *configurator) addOutboundSubnets(ctx context.Context, subnets []net.IPNet) error {
|
||||
const remove = false
|
||||
for _, subnet := range subnets {
|
||||
if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, subnet, remove); err != nil {
|
||||
return fmt.Errorf("cannot add allowed subnet through firewall: %w", err)
|
||||
}
|
||||
c.outboundSubnets = append(c.outboundSubnets, subnet)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
71
internal/firewall/ports.go
Normal file
71
internal/firewall/ports.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (c *configurator) SetAllowedPort(ctx context.Context, port uint16, intf string) (err error) {
|
||||
c.stateMutex.Lock()
|
||||
defer c.stateMutex.Unlock()
|
||||
|
||||
if port == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !c.enabled {
|
||||
c.logger.Info("firewall disabled, only updating allowed ports internal state")
|
||||
c.allowedInputPorts[port] = intf
|
||||
return nil
|
||||
}
|
||||
|
||||
c.logger.Info("setting allowed input port %d through interface %s...", port, intf)
|
||||
|
||||
if existingIntf, ok := c.allowedInputPorts[port]; ok {
|
||||
if intf == existingIntf {
|
||||
return nil
|
||||
}
|
||||
const remove = true
|
||||
if err := c.acceptInputToPort(ctx, existingIntf, port, remove); err != nil {
|
||||
return fmt.Errorf("cannot remove old allowed port %d through interface %s: %w", port, existingIntf, err)
|
||||
}
|
||||
}
|
||||
|
||||
const remove = false
|
||||
if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
|
||||
return fmt.Errorf("cannot set allowed port %d through interface %s: %w", port, intf, err)
|
||||
}
|
||||
c.allowedInputPorts[port] = intf
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *configurator) RemoveAllowedPort(ctx context.Context, port uint16) (err error) {
|
||||
c.stateMutex.Lock()
|
||||
defer c.stateMutex.Unlock()
|
||||
|
||||
if port == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !c.enabled {
|
||||
c.logger.Info("firewall disabled, only updating allowed ports internal list")
|
||||
delete(c.allowedInputPorts, port)
|
||||
return nil
|
||||
}
|
||||
|
||||
c.logger.Info("removing allowed port %d through firewall...", port)
|
||||
|
||||
intf, ok := c.allowedInputPorts[port]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
const remove = true
|
||||
if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
|
||||
return fmt.Errorf("cannot remove allowed port %d through interface %s: %w", port, intf, err)
|
||||
}
|
||||
delete(c.allowedInputPorts, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
53
internal/firewall/subnets.go
Normal file
53
internal/firewall/subnets.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func findSubnetsToAdd(oldSubnets, newSubnets []net.IPNet) (subnetsToAdd []net.IPNet) {
|
||||
for _, newSubnet := range newSubnets {
|
||||
found := false
|
||||
for _, oldSubnet := range oldSubnets {
|
||||
if subnetsAreEqual(oldSubnet, newSubnet) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
subnetsToAdd = append(subnetsToAdd, newSubnet)
|
||||
}
|
||||
}
|
||||
return subnetsToAdd
|
||||
}
|
||||
|
||||
func findSubnetsToRemove(oldSubnets, newSubnets []net.IPNet) (subnetsToRemove []net.IPNet) {
|
||||
for _, oldSubnet := range oldSubnets {
|
||||
found := false
|
||||
for _, newSubnet := range newSubnets {
|
||||
if subnetsAreEqual(oldSubnet, newSubnet) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
subnetsToRemove = append(subnetsToRemove, oldSubnet)
|
||||
}
|
||||
}
|
||||
return subnetsToRemove
|
||||
}
|
||||
|
||||
func subnetsAreEqual(a, b net.IPNet) bool {
|
||||
return a.IP.Equal(b.IP) && a.Mask.String() == b.Mask.String()
|
||||
}
|
||||
|
||||
func removeSubnetFromSubnets(subnets []net.IPNet, subnet net.IPNet) []net.IPNet {
|
||||
L := len(subnets)
|
||||
for i := range subnets {
|
||||
if subnetsAreEqual(subnet, subnets[i]) {
|
||||
subnets[i] = subnets[L-1]
|
||||
subnets = subnets[:L-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
return subnets
|
||||
}
|
||||
39
internal/firewall/vpn.go
Normal file
39
internal/firewall/vpn.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
func (c *configurator) SetVPNConnection(ctx context.Context, connection models.OpenVPNConnection) (err error) {
|
||||
c.stateMutex.Lock()
|
||||
defer c.stateMutex.Unlock()
|
||||
|
||||
if !c.enabled {
|
||||
c.logger.Info("firewall disabled, only updating internal VPN connection")
|
||||
c.vpnConnection = connection
|
||||
return nil
|
||||
}
|
||||
|
||||
c.logger.Info("setting VPN connection through firewall...")
|
||||
|
||||
if c.vpnConnection.Equal(connection) {
|
||||
return nil
|
||||
}
|
||||
|
||||
remove := true
|
||||
if c.vpnConnection.IP != nil {
|
||||
if err := c.acceptOutputTrafficToVPN(ctx, c.defaultInterface, c.vpnConnection, remove); err != nil {
|
||||
c.logger.Error("cannot remove outdated VPN connection through firewall: %s", err)
|
||||
}
|
||||
}
|
||||
c.vpnConnection = models.OpenVPNConnection{}
|
||||
remove = false
|
||||
if err := c.acceptOutputTrafficToVPN(ctx, c.defaultInterface, connection, remove); err != nil {
|
||||
return fmt.Errorf("cannot set VPN connection through firewall: %w", err)
|
||||
}
|
||||
c.vpnConnection = connection
|
||||
return nil
|
||||
}
|
||||
42
internal/healthcheck/client.go
Normal file
42
internal/healthcheck/client.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package healthcheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Checker interface {
|
||||
Check(ctx context.Context, url string) error
|
||||
}
|
||||
|
||||
type checker struct {
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
func NewChecker(httpClient *http.Client) Checker {
|
||||
return &checker{
|
||||
httpClient: httpClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *checker) Check(ctx context.Context, url string) error {
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response, err := h.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode == http.StatusOK {
|
||||
return nil
|
||||
}
|
||||
b, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("%s: %s", response.Status, string(b))
|
||||
}
|
||||
49
internal/healthcheck/handler.go
Normal file
49
internal/healthcheck/handler.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package healthcheck
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/qdm12/golibs/logging"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
logger logging.Logger
|
||||
healthErr error
|
||||
healthErrMu sync.RWMutex
|
||||
}
|
||||
|
||||
var errHealthcheckNotRunYet = errors.New("healthcheck did not run yet")
|
||||
|
||||
func newHandler(logger logging.Logger) *handler {
|
||||
return &handler{
|
||||
logger: logger,
|
||||
healthErr: errHealthcheckNotRunYet,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
|
||||
if request.Method != http.MethodGet {
|
||||
http.Error(responseWriter, "method not supported for healthcheck", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := h.getErr(); err != nil {
|
||||
h.logger.Error(err)
|
||||
http.Error(responseWriter, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
responseWriter.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (h *handler) setErr(err error) {
|
||||
h.healthErrMu.Lock()
|
||||
defer h.healthErrMu.Unlock()
|
||||
h.healthErr = err
|
||||
}
|
||||
|
||||
func (h *handler) getErr() (err error) {
|
||||
h.healthErrMu.RLock()
|
||||
defer h.healthErrMu.RUnlock()
|
||||
return h.healthErr
|
||||
}
|
||||
66
internal/healthcheck/health.go
Normal file
66
internal/healthcheck/health.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package healthcheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (s *server) runHealthcheckLoop(ctx context.Context, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
for {
|
||||
previousErr := s.handler.getErr()
|
||||
|
||||
err := healthCheck(ctx, s.resolver)
|
||||
s.handler.setErr(err)
|
||||
|
||||
if previousErr != nil && err == nil {
|
||||
s.logger.Info("passed")
|
||||
}
|
||||
|
||||
if err != nil { // try again after 1 second
|
||||
timer := time.NewTimer(time.Second)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
return
|
||||
case <-timer.C:
|
||||
}
|
||||
continue
|
||||
}
|
||||
// Success, check again in 10 minutes
|
||||
const period = 10 * time.Minute
|
||||
timer := time.NewTimer(period)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
return
|
||||
case <-timer.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
errNoIPResolved = errors.New("no IP address resolved")
|
||||
)
|
||||
|
||||
func healthCheck(ctx context.Context, resolver *net.Resolver) (err error) {
|
||||
// TODO use mullvad API if current provider is Mullvad
|
||||
const domainToResolve = "github.com"
|
||||
ips, err := resolver.LookupIP(ctx, "ip", domainToResolve)
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
case len(ips) == 0:
|
||||
return fmt.Errorf("%w for %s", errNoIPResolved, domainToResolve)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
67
internal/healthcheck/server.go
Normal file
67
internal/healthcheck/server.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package healthcheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/golibs/logging"
|
||||
)
|
||||
|
||||
type Server interface {
|
||||
Run(ctx context.Context, wg *sync.WaitGroup)
|
||||
}
|
||||
|
||||
type server struct {
|
||||
address string
|
||||
logger logging.Logger
|
||||
handler *handler
|
||||
resolver *net.Resolver
|
||||
}
|
||||
|
||||
func NewServer(address string, logger logging.Logger) Server {
|
||||
healthcheckLogger := logger.WithPrefix("healthcheck: ")
|
||||
return &server{
|
||||
address: address,
|
||||
logger: healthcheckLogger,
|
||||
handler: newHandler(healthcheckLogger),
|
||||
resolver: net.DefaultResolver,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) Run(ctx context.Context, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
internalWg := &sync.WaitGroup{}
|
||||
internalWg.Add(1)
|
||||
go s.runHealthcheckLoop(ctx, internalWg)
|
||||
|
||||
server := http.Server{
|
||||
Addr: s.address,
|
||||
Handler: s.handler,
|
||||
}
|
||||
internalWg.Add(1)
|
||||
go func() {
|
||||
defer internalWg.Done()
|
||||
<-ctx.Done()
|
||||
s.logger.Warn("context canceled: shutting down server")
|
||||
defer s.logger.Warn("server shut down")
|
||||
const shutdownGraceDuration = 2 * time.Second
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownGraceDuration)
|
||||
defer cancel()
|
||||
if err := server.Shutdown(shutdownCtx); err != nil {
|
||||
s.logger.Error("failed shutting down: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
s.logger.Info("listening on %s", s.address)
|
||||
err := server.ListenAndServe()
|
||||
if err != nil && !errors.Is(ctx.Err(), context.Canceled) {
|
||||
s.logger.Error(err)
|
||||
}
|
||||
|
||||
internalWg.Wait()
|
||||
}
|
||||
24
internal/httpproxy/accept.go
Normal file
24
internal/httpproxy/accept.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package httpproxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (h *handler) isAccepted(responseWriter http.ResponseWriter, request *http.Request) bool {
|
||||
// Not compatible with HTTP < 1.0 or HTTP >= 2.0 (see https://github.com/golang/go/issues/14797#issuecomment-196103814)
|
||||
const (
|
||||
minimalMajorVersion = 1
|
||||
minimalMinorVersion = 0
|
||||
maximumMajorVersion = 2
|
||||
maximumMinorVersion = 0
|
||||
)
|
||||
if !request.ProtoAtLeast(minimalMajorVersion, minimalMinorVersion) ||
|
||||
request.ProtoAtLeast(maximumMajorVersion, maximumMinorVersion) {
|
||||
message := fmt.Sprintf("http version not supported: %s", request.Proto)
|
||||
h.logger.Info("%s, from %s", message, request.RemoteAddr)
|
||||
http.Error(responseWriter, message, http.StatusBadRequest)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
41
internal/httpproxy/auth.go
Normal file
41
internal/httpproxy/auth.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package httpproxy
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (h *handler) isAuthorized(responseWriter http.ResponseWriter, request *http.Request) (authorized bool) {
|
||||
if len(h.username) == 0 || (request.Method != "CONNECT" && !request.URL.IsAbs()) {
|
||||
return true
|
||||
}
|
||||
basicAuth := request.Header.Get("Proxy-Authorization")
|
||||
if len(basicAuth) == 0 {
|
||||
h.logger.Info("Proxy-Authorization header not found from %s", request.RemoteAddr)
|
||||
responseWriter.Header().Set("Proxy-Authenticate", `Basic realm="Access to Gluetun over HTTP"`)
|
||||
responseWriter.WriteHeader(http.StatusProxyAuthRequired)
|
||||
return false
|
||||
}
|
||||
b64UsernamePassword := strings.TrimPrefix(basicAuth, "Basic ")
|
||||
b, err := base64.StdEncoding.DecodeString(b64UsernamePassword)
|
||||
if err != nil {
|
||||
h.logger.Info("Cannot decode Proxy-Authorization header value from %s: %s",
|
||||
request.RemoteAddr, err.Error())
|
||||
responseWriter.WriteHeader(http.StatusUnauthorized)
|
||||
return false
|
||||
}
|
||||
usernamePassword := strings.Split(string(b), ":")
|
||||
const expectedFields = 2
|
||||
if len(usernamePassword) != expectedFields {
|
||||
responseWriter.WriteHeader(http.StatusBadRequest)
|
||||
return false
|
||||
}
|
||||
if h.username != usernamePassword[0] || h.password != usernamePassword[1] {
|
||||
h.logger.Info("Username or password mismatch from %s", request.RemoteAddr)
|
||||
h.logger.Debug("username provided %q and password provided %q", usernamePassword[0], usernamePassword[1])
|
||||
responseWriter.WriteHeader(http.StatusUnauthorized)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
64
internal/httpproxy/handler.go
Normal file
64
internal/httpproxy/handler.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package httpproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/golibs/logging"
|
||||
)
|
||||
|
||||
func newHandler(ctx context.Context, wg *sync.WaitGroup, logger logging.Logger,
|
||||
stealth, verbose bool, username, password string) http.Handler {
|
||||
const httpTimeout = 24 * time.Hour
|
||||
return &handler{
|
||||
ctx: ctx,
|
||||
wg: wg,
|
||||
client: &http.Client{Timeout: httpTimeout},
|
||||
logger: logger,
|
||||
verbose: verbose,
|
||||
stealth: stealth,
|
||||
username: username,
|
||||
password: password,
|
||||
}
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
ctx context.Context
|
||||
wg *sync.WaitGroup
|
||||
client *http.Client
|
||||
logger logging.Logger
|
||||
verbose, stealth bool
|
||||
username, password string
|
||||
}
|
||||
|
||||
func (h *handler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
|
||||
if !h.isAccepted(responseWriter, request) {
|
||||
return
|
||||
}
|
||||
if !h.isAuthorized(responseWriter, request) {
|
||||
return
|
||||
}
|
||||
request.Header.Del("Proxy-Connection")
|
||||
request.Header.Del("Proxy-Authenticate")
|
||||
request.Header.Del("Proxy-Authorization")
|
||||
switch request.Method {
|
||||
case http.MethodConnect:
|
||||
h.handleHTTPS(responseWriter, request)
|
||||
default:
|
||||
h.handleHTTP(responseWriter, request)
|
||||
}
|
||||
}
|
||||
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
|
||||
var hopHeaders = [...]string{ //nolint:gochecknoglobals
|
||||
"Connection",
|
||||
"Keep-Alive",
|
||||
"Proxy-Authenticate",
|
||||
"Proxy-Authorization",
|
||||
"Te", // canonicalized version of "TE"
|
||||
"Trailers",
|
||||
"Transfer-Encoding",
|
||||
"Upgrade",
|
||||
}
|
||||
71
internal/httpproxy/http.go
Normal file
71
internal/httpproxy/http.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package httpproxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (h *handler) handleHTTP(responseWriter http.ResponseWriter, request *http.Request) {
|
||||
switch request.URL.Scheme {
|
||||
case "http", "https":
|
||||
default:
|
||||
h.logger.Warn("Unsupported scheme %q", request.URL.Scheme)
|
||||
http.Error(responseWriter, "unsupported scheme", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
request = request.WithContext(h.ctx)
|
||||
|
||||
request.RequestURI = ""
|
||||
|
||||
for _, key := range hopHeaders {
|
||||
request.Header.Del(key)
|
||||
}
|
||||
|
||||
if !h.stealth {
|
||||
setForwardedHeaders(request)
|
||||
}
|
||||
|
||||
response, err := h.client.Do(request)
|
||||
if err != nil {
|
||||
http.Error(responseWriter, "server error", http.StatusInternalServerError)
|
||||
h.logger.Warn("cannot request %s for client %q: %s",
|
||||
request.URL, request.RemoteAddr, err)
|
||||
return
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if h.verbose {
|
||||
h.logger.Info("%s %s %s %s", request.RemoteAddr, response.Status, request.Method, request.URL)
|
||||
}
|
||||
|
||||
for _, key := range hopHeaders {
|
||||
response.Header.Del(key)
|
||||
}
|
||||
|
||||
targetHeaderPtr := responseWriter.Header()
|
||||
for key, values := range response.Header {
|
||||
for _, value := range values {
|
||||
targetHeaderPtr.Add(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
responseWriter.WriteHeader(response.StatusCode)
|
||||
if _, err := io.Copy(responseWriter, response.Body); err != nil {
|
||||
h.logger.Error("%s %s: body copy error: %s", request.RemoteAddr, request.URL, err)
|
||||
}
|
||||
}
|
||||
|
||||
func setForwardedHeaders(request *http.Request) {
|
||||
clientIP, _, err := net.SplitHostPort(request.RemoteAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// keep existing proxy headers
|
||||
if prior, ok := request.Header["X-Forwarded-For"]; ok {
|
||||
clientIP = fmt.Sprintf("%s,%s", strings.Join(prior, ", "), clientIP)
|
||||
}
|
||||
request.Header.Set("X-Forwarded-For", clientIP)
|
||||
}
|
||||
64
internal/httpproxy/https.go
Normal file
64
internal/httpproxy/https.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package httpproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func (h *handler) handleHTTPS(responseWriter http.ResponseWriter, request *http.Request) {
|
||||
dialer := net.Dialer{}
|
||||
destinationConn, err := dialer.DialContext(h.ctx, "tcp", request.Host)
|
||||
if err != nil {
|
||||
http.Error(responseWriter, err.Error(), http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
responseWriter.WriteHeader(http.StatusOK)
|
||||
|
||||
hijacker, ok := responseWriter.(http.Hijacker)
|
||||
if !ok {
|
||||
http.Error(responseWriter, "Hijacking not supported", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
clientConnection, _, err := hijacker.Hijack()
|
||||
if err != nil {
|
||||
h.logger.Warn(err)
|
||||
http.Error(responseWriter, err.Error(), http.StatusServiceUnavailable)
|
||||
if err := destinationConn.Close(); err != nil {
|
||||
h.logger.Error("closing destination connection: %s", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if h.verbose {
|
||||
h.logger.Info("%s <-> %s", request.RemoteAddr, request.Host)
|
||||
}
|
||||
|
||||
h.wg.Add(1)
|
||||
ctx, cancel := context.WithCancel(h.ctx)
|
||||
const transferGoroutines = 2
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(transferGoroutines)
|
||||
go func() { // trigger cleanup when done
|
||||
wg.Wait()
|
||||
cancel()
|
||||
}()
|
||||
go func() { // cleanup
|
||||
<-ctx.Done()
|
||||
destinationConn.Close()
|
||||
clientConnection.Close()
|
||||
h.wg.Done()
|
||||
}()
|
||||
go transfer(destinationConn, clientConnection, wg)
|
||||
go transfer(clientConnection, destinationConn, wg)
|
||||
}
|
||||
|
||||
func transfer(destination io.WriteCloser, source io.ReadCloser, wg *sync.WaitGroup) {
|
||||
_, _ = io.Copy(destination, source)
|
||||
_ = source.Close()
|
||||
_ = destination.Close()
|
||||
wg.Done()
|
||||
}
|
||||
134
internal/httpproxy/loop.go
Normal file
134
internal/httpproxy/loop.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package httpproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/settings"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
)
|
||||
|
||||
type Looper interface {
|
||||
Run(ctx context.Context, wg *sync.WaitGroup)
|
||||
SetStatus(status models.LoopStatus) (outcome string, err error)
|
||||
GetStatus() (status models.LoopStatus)
|
||||
GetSettings() (settings settings.HTTPProxy)
|
||||
SetSettings(settings settings.HTTPProxy) (outcome string)
|
||||
}
|
||||
|
||||
type looper struct {
|
||||
state state
|
||||
// Other objects
|
||||
logger logging.Logger
|
||||
// Internal channels and locks
|
||||
loopLock sync.Mutex
|
||||
running chan models.LoopStatus
|
||||
stop, stopped chan struct{}
|
||||
start chan struct{}
|
||||
backoffTime time.Duration
|
||||
}
|
||||
|
||||
const defaultBackoffTime = 10 * time.Second
|
||||
|
||||
func NewLooper(logger logging.Logger, settings settings.HTTPProxy) Looper {
|
||||
return &looper{
|
||||
state: state{
|
||||
status: constants.Stopped,
|
||||
settings: settings,
|
||||
},
|
||||
logger: logger.WithPrefix("http proxy: "),
|
||||
start: make(chan struct{}),
|
||||
running: make(chan models.LoopStatus),
|
||||
stop: make(chan struct{}),
|
||||
stopped: make(chan struct{}),
|
||||
backoffTime: defaultBackoffTime,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
crashed := false
|
||||
|
||||
if l.GetSettings().Enabled {
|
||||
go func() {
|
||||
_, _ = l.SetStatus(constants.Running)
|
||||
}()
|
||||
}
|
||||
|
||||
select {
|
||||
case <-l.start:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
|
||||
defer l.logger.Warn("loop exited")
|
||||
|
||||
for ctx.Err() == nil {
|
||||
runCtx, runCancel := context.WithCancel(ctx)
|
||||
|
||||
settings := l.GetSettings()
|
||||
address := fmt.Sprintf(":%d", settings.Port)
|
||||
server := New(runCtx, address, l.logger, settings.Stealth, settings.Log, settings.User, settings.Password)
|
||||
|
||||
runWg := &sync.WaitGroup{}
|
||||
runWg.Add(1)
|
||||
errorCh := make(chan error)
|
||||
go server.Run(runCtx, runWg, errorCh)
|
||||
|
||||
// TODO stable timer, check Shadowsocks
|
||||
if !crashed {
|
||||
l.running <- constants.Running
|
||||
crashed = false
|
||||
} else {
|
||||
l.backoffTime = defaultBackoffTime
|
||||
l.state.setStatusWithLock(constants.Running)
|
||||
}
|
||||
|
||||
stayHere := true
|
||||
for stayHere {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
l.logger.Warn("context canceled: exiting loop")
|
||||
runCancel()
|
||||
runWg.Wait()
|
||||
return
|
||||
case <-l.start:
|
||||
l.logger.Info("starting")
|
||||
runCancel()
|
||||
runWg.Wait()
|
||||
stayHere = false
|
||||
case <-l.stop:
|
||||
l.logger.Info("stopping")
|
||||
runCancel()
|
||||
runWg.Wait()
|
||||
l.stopped <- struct{}{}
|
||||
case err := <-errorCh:
|
||||
runWg.Wait()
|
||||
l.state.setStatusWithLock(constants.Crashed)
|
||||
l.logAndWait(ctx, err)
|
||||
crashed = true
|
||||
stayHere = false
|
||||
}
|
||||
}
|
||||
runCancel() // repetition for linter only
|
||||
}
|
||||
}
|
||||
|
||||
func (l *looper) logAndWait(ctx context.Context, err error) {
|
||||
l.logger.Error(err)
|
||||
l.logger.Info("retrying in %s", l.backoffTime)
|
||||
timer := time.NewTimer(l.backoffTime)
|
||||
l.backoffTime *= 2
|
||||
select {
|
||||
case <-timer.C:
|
||||
case <-ctx.Done():
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
}
|
||||
}
|
||||
54
internal/httpproxy/server.go
Normal file
54
internal/httpproxy/server.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package httpproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/golibs/logging"
|
||||
)
|
||||
|
||||
type Server interface {
|
||||
Run(ctx context.Context, wg *sync.WaitGroup, errorCh chan<- error)
|
||||
}
|
||||
|
||||
type server struct {
|
||||
address string
|
||||
handler http.Handler
|
||||
logger logging.Logger
|
||||
internalWG *sync.WaitGroup
|
||||
}
|
||||
|
||||
func New(ctx context.Context, address string, logger logging.Logger,
|
||||
stealth, verbose bool, username, password string) Server {
|
||||
wg := &sync.WaitGroup{}
|
||||
return &server{
|
||||
address: address,
|
||||
handler: newHandler(ctx, wg, logger, stealth, verbose, username, password),
|
||||
logger: logger,
|
||||
internalWG: wg,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) Run(ctx context.Context, wg *sync.WaitGroup, errorCh chan<- error) {
|
||||
defer wg.Done()
|
||||
server := http.Server{Addr: s.address, Handler: s.handler}
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
s.logger.Warn("shutting down server")
|
||||
defer s.logger.Warn("server shut down")
|
||||
const shutdownGraceDuration = 2 * time.Second
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownGraceDuration)
|
||||
defer cancel()
|
||||
if err := server.Shutdown(shutdownCtx); err != nil {
|
||||
s.logger.Error("failed shutting down: %s", err)
|
||||
}
|
||||
}()
|
||||
s.logger.Info("listening on %s", s.address)
|
||||
err := server.ListenAndServe()
|
||||
if err != nil && ctx.Err() == nil {
|
||||
errorCh <- err
|
||||
}
|
||||
s.internalWG.Wait()
|
||||
}
|
||||
101
internal/httpproxy/state.go
Normal file
101
internal/httpproxy/state.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package httpproxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/settings"
|
||||
)
|
||||
|
||||
type state struct {
|
||||
status models.LoopStatus
|
||||
settings settings.HTTPProxy
|
||||
statusMu sync.RWMutex
|
||||
settingsMu sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *state) setStatusWithLock(status models.LoopStatus) {
|
||||
s.statusMu.Lock()
|
||||
defer s.statusMu.Unlock()
|
||||
s.status = status
|
||||
}
|
||||
|
||||
func (l *looper) GetStatus() (status models.LoopStatus) {
|
||||
l.state.statusMu.RLock()
|
||||
defer l.state.statusMu.RUnlock()
|
||||
return l.state.status
|
||||
}
|
||||
|
||||
func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) {
|
||||
l.state.statusMu.Lock()
|
||||
defer l.state.statusMu.Unlock()
|
||||
existingStatus := l.state.status
|
||||
|
||||
switch status {
|
||||
case constants.Running:
|
||||
switch existingStatus {
|
||||
case constants.Starting, constants.Running, constants.Stopping, constants.Crashed:
|
||||
return fmt.Sprintf("already %s", existingStatus), nil
|
||||
}
|
||||
l.loopLock.Lock()
|
||||
defer l.loopLock.Unlock()
|
||||
l.state.status = constants.Starting
|
||||
l.state.statusMu.Unlock()
|
||||
l.start <- struct{}{}
|
||||
newStatus := <-l.running
|
||||
l.state.statusMu.Lock()
|
||||
l.state.status = newStatus
|
||||
return newStatus.String(), nil
|
||||
case constants.Stopped:
|
||||
switch existingStatus {
|
||||
case constants.Stopped, constants.Stopping, constants.Starting, constants.Crashed:
|
||||
return fmt.Sprintf("already %s", existingStatus), nil
|
||||
}
|
||||
l.loopLock.Lock()
|
||||
defer l.loopLock.Unlock()
|
||||
l.state.status = constants.Stopping
|
||||
l.state.statusMu.Unlock()
|
||||
l.stop <- struct{}{}
|
||||
<-l.stopped
|
||||
l.state.statusMu.Lock()
|
||||
l.state.status = status
|
||||
return status.String(), nil
|
||||
default:
|
||||
return "", fmt.Errorf("status %q can only be %q or %q",
|
||||
status, constants.Running, constants.Stopped)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *looper) GetSettings() (settings settings.HTTPProxy) {
|
||||
l.state.settingsMu.RLock()
|
||||
defer l.state.settingsMu.RUnlock()
|
||||
return l.state.settings
|
||||
}
|
||||
|
||||
func (l *looper) SetSettings(settings settings.HTTPProxy) (outcome string) {
|
||||
l.state.settingsMu.Lock()
|
||||
settingsUnchanged := reflect.DeepEqual(settings, l.state.settings)
|
||||
if settingsUnchanged {
|
||||
l.state.settingsMu.Unlock()
|
||||
return "settings left unchanged"
|
||||
}
|
||||
newEnabled := settings.Enabled
|
||||
previousEnabled := l.state.settings.Enabled
|
||||
l.state.settings = settings
|
||||
l.state.settingsMu.Unlock()
|
||||
// Either restart or set changed status
|
||||
switch {
|
||||
case !newEnabled && !previousEnabled:
|
||||
case newEnabled && previousEnabled:
|
||||
_, _ = l.SetStatus(constants.Stopped)
|
||||
_, _ = l.SetStatus(constants.Running)
|
||||
case newEnabled && !previousEnabled:
|
||||
_, _ = l.SetStatus(constants.Running)
|
||||
case !newEnabled && previousEnabled:
|
||||
_, _ = l.SetStatus(constants.Stopped)
|
||||
}
|
||||
return "settings updated"
|
||||
}
|
||||
31
internal/logging/duration.go
Normal file
31
internal/logging/duration.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func FormatDuration(duration time.Duration) string {
|
||||
switch {
|
||||
case duration < time.Minute:
|
||||
seconds := int(duration.Round(time.Second).Seconds())
|
||||
const two = 2
|
||||
if seconds < two {
|
||||
return fmt.Sprintf("%d second", seconds)
|
||||
}
|
||||
return fmt.Sprintf("%d seconds", seconds)
|
||||
case duration <= time.Hour:
|
||||
minutes := int(duration.Round(time.Minute).Minutes())
|
||||
if minutes == 1 {
|
||||
return "1 minute"
|
||||
}
|
||||
return fmt.Sprintf("%d minutes", minutes)
|
||||
case duration < 48*time.Hour:
|
||||
hours := int(duration.Truncate(time.Hour).Hours())
|
||||
return fmt.Sprintf("%d hours", hours)
|
||||
default:
|
||||
const hoursInDay = 24
|
||||
days := int(duration.Truncate(time.Hour).Hours() / hoursInDay)
|
||||
return fmt.Sprintf("%d days", days)
|
||||
}
|
||||
}
|
||||
64
internal/logging/duration_test.go
Normal file
64
internal/logging/duration_test.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_FormatDuration(t *testing.T) {
|
||||
t.Parallel()
|
||||
testCases := map[string]struct {
|
||||
duration time.Duration
|
||||
s string
|
||||
}{
|
||||
"zero": {
|
||||
s: "0 second",
|
||||
},
|
||||
"one second": {
|
||||
duration: time.Second,
|
||||
s: "1 second",
|
||||
},
|
||||
"59 seconds": {
|
||||
duration: 59 * time.Second,
|
||||
s: "59 seconds",
|
||||
},
|
||||
"1 minute": {
|
||||
duration: time.Minute,
|
||||
s: "1 minute",
|
||||
},
|
||||
"2 minutes": {
|
||||
duration: 2 * time.Minute,
|
||||
s: "2 minutes",
|
||||
},
|
||||
"1 hour": {
|
||||
duration: time.Hour,
|
||||
s: "60 minutes",
|
||||
},
|
||||
"2 hours": {
|
||||
duration: 2 * time.Hour,
|
||||
s: "2 hours",
|
||||
},
|
||||
"26 hours": {
|
||||
duration: 26 * time.Hour,
|
||||
s: "26 hours",
|
||||
},
|
||||
"28 hours": {
|
||||
duration: 28 * time.Hour,
|
||||
s: "28 hours",
|
||||
},
|
||||
"55 hours": {
|
||||
duration: 55 * time.Hour,
|
||||
s: "2 days",
|
||||
},
|
||||
}
|
||||
for name, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := FormatDuration(testCase.duration)
|
||||
assert.Equal(t, testCase.s, s)
|
||||
})
|
||||
}
|
||||
}
|
||||
78
internal/logging/line.go
Normal file
78
internal/logging/line.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
)
|
||||
|
||||
var regularExpressions = struct { //nolint:gochecknoglobals
|
||||
unboundPrefix *regexp.Regexp
|
||||
}{
|
||||
unboundPrefix: regexp.MustCompile(`unbound: \[[0-9]{10}\] unbound\[[0-9]+:[0|1]\] `),
|
||||
}
|
||||
|
||||
func PostProcessLine(s string) (filtered string, level logging.Level) {
|
||||
switch {
|
||||
case strings.HasPrefix(s, "openvpn: "):
|
||||
for _, ignored := range []string{
|
||||
"openvpn: WARNING: you are using user/group/chroot/setcon without persist-tun -- this may cause restarts to fail",
|
||||
"openvpn: NOTE: UID/GID downgrade will be delayed because of --client, --pull, or --up-delay",
|
||||
} {
|
||||
if s == ignored {
|
||||
return "", ""
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case strings.HasPrefix(s, "openvpn: NOTE: "):
|
||||
filtered = strings.TrimPrefix(s, "openvpn: NOTE: ")
|
||||
filtered = "openvpn: " + filtered
|
||||
level = logging.InfoLevel
|
||||
case strings.HasPrefix(s, "openvpn: WARNING: "):
|
||||
filtered = strings.TrimPrefix(s, "openvpn: WARNING: ")
|
||||
filtered = "openvpn: " + filtered
|
||||
level = logging.WarnLevel
|
||||
case strings.HasPrefix(s, "openvpn: Options error: "):
|
||||
filtered = strings.TrimPrefix(s, "openvpn: Options error: ")
|
||||
filtered = "openvpn: " + filtered
|
||||
level = logging.ErrorLevel
|
||||
case s == "openvpn: Initialization Sequence Completed":
|
||||
return color.HiGreenString(s), logging.InfoLevel
|
||||
case s == "openvpn: AUTH: Received control message: AUTH_FAILED":
|
||||
filtered = s + "\n\n (IF YOU ARE USING PIA servers, MAYBE CHECK OUT https://github.com/qdm12/gluetun/issues/265)\n" //nolint:lll
|
||||
level = logging.ErrorLevel
|
||||
default:
|
||||
filtered = s
|
||||
level = logging.InfoLevel
|
||||
}
|
||||
filtered = constants.ColorOpenvpn().Sprintf(filtered)
|
||||
return filtered, level
|
||||
case strings.HasPrefix(s, "unbound: "):
|
||||
prefix := regularExpressions.unboundPrefix.FindString(s)
|
||||
filtered = s[len(prefix):]
|
||||
switch {
|
||||
case strings.HasPrefix(filtered, "notice: "):
|
||||
filtered = strings.TrimPrefix(filtered, "notice: ")
|
||||
level = logging.InfoLevel
|
||||
case strings.HasPrefix(filtered, "info: "):
|
||||
filtered = strings.TrimPrefix(filtered, "info: ")
|
||||
level = logging.InfoLevel
|
||||
case strings.HasPrefix(filtered, "warn: "):
|
||||
filtered = strings.TrimPrefix(filtered, "warn: ")
|
||||
level = logging.WarnLevel
|
||||
case strings.HasPrefix(filtered, "error: "):
|
||||
filtered = strings.TrimPrefix(filtered, "error: ")
|
||||
level = logging.ErrorLevel
|
||||
default:
|
||||
level = logging.ErrorLevel
|
||||
}
|
||||
filtered = fmt.Sprintf("unbound: %s", filtered)
|
||||
filtered = constants.ColorUnbound().Sprintf(filtered)
|
||||
return filtered, level
|
||||
}
|
||||
return s, logging.InfoLevel
|
||||
}
|
||||
77
internal/logging/line_test.go
Normal file
77
internal/logging/line_test.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/qdm12/golibs/logging"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_PostProcessLine(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := map[string]struct {
|
||||
s string
|
||||
filtered string
|
||||
level logging.Level
|
||||
}{
|
||||
"empty string": {"", "", logging.InfoLevel},
|
||||
"random string": {"asdasqdb", "asdasqdb", logging.InfoLevel},
|
||||
"unbound notice": {
|
||||
"unbound: [1594595249] unbound[75:0] notice: init module 0: validator",
|
||||
"unbound: init module 0: validator",
|
||||
logging.InfoLevel},
|
||||
"unbound info": {
|
||||
"unbound: [1594595249] unbound[75:0] info: init module 0: validator",
|
||||
"unbound: init module 0: validator",
|
||||
logging.InfoLevel},
|
||||
"unbound warn": {
|
||||
"unbound: [1594595249] unbound[75:0] warn: init module 0: validator",
|
||||
"unbound: init module 0: validator",
|
||||
logging.WarnLevel},
|
||||
"unbound error": {
|
||||
"unbound: [1594595249] unbound[75:0] error: init module 0: validator",
|
||||
"unbound: init module 0: validator",
|
||||
logging.ErrorLevel},
|
||||
"unbound unknown": {
|
||||
"unbound: [1594595249] unbound[75:0] BLA: init module 0: validator",
|
||||
"unbound: BLA: init module 0: validator",
|
||||
logging.ErrorLevel},
|
||||
"openvpn unknown": {
|
||||
"openvpn: message",
|
||||
"openvpn: message",
|
||||
logging.InfoLevel},
|
||||
"openvpn note": {
|
||||
"openvpn: NOTE: message",
|
||||
"openvpn: message",
|
||||
logging.InfoLevel},
|
||||
"openvpn warning": {
|
||||
"openvpn: WARNING: message",
|
||||
"openvpn: message",
|
||||
logging.WarnLevel},
|
||||
"openvpn options error": {
|
||||
"openvpn: Options error: message",
|
||||
"openvpn: message",
|
||||
logging.ErrorLevel},
|
||||
"openvpn ignored message": {
|
||||
"openvpn: NOTE: UID/GID downgrade will be delayed because of --client, --pull, or --up-delay",
|
||||
"",
|
||||
""},
|
||||
"openvpn success": {
|
||||
"openvpn: Initialization Sequence Completed",
|
||||
"openvpn: Initialization Sequence Completed",
|
||||
logging.InfoLevel},
|
||||
"openvpn auth failed": {
|
||||
"openvpn: AUTH: Received control message: AUTH_FAILED",
|
||||
"openvpn: AUTH: Received control message: AUTH_FAILED\n\n (IF YOU ARE USING PIA servers, MAYBE CHECK OUT https://github.com/qdm12/gluetun/issues/265)\n", //nolint:lll
|
||||
logging.ErrorLevel},
|
||||
}
|
||||
for name, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
filtered, level := PostProcessLine(tc.s)
|
||||
assert.Equal(t, tc.filtered, filtered)
|
||||
assert.Equal(t, tc.level, level)
|
||||
})
|
||||
}
|
||||
}
|
||||
61
internal/logging/splash.go
Normal file
61
internal/logging/splash.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kyokomi/emoji"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
// Splash returns the welcome spash message.
|
||||
func Splash(buildInfo models.BuildInformation) string {
|
||||
lines := title()
|
||||
lines = append(lines, "")
|
||||
lines = append(lines, fmt.Sprintf("Running version %s built on %s (commit %s)",
|
||||
buildInfo.Version, buildInfo.BuildDate, buildInfo.Commit))
|
||||
lines = append(lines, "")
|
||||
lines = append(lines, announcement()...)
|
||||
lines = append(lines, "")
|
||||
lines = append(lines, links()...)
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func title() []string {
|
||||
return []string{
|
||||
"=========================================",
|
||||
"================ Gluetun ================",
|
||||
"=========================================",
|
||||
"==== A mix of OpenVPN, DNS over TLS, ====",
|
||||
"======= Shadowsocks and HTTP proxy ======",
|
||||
"========= all glued up with Go ==========",
|
||||
"=========================================",
|
||||
"=========== For tunneling to ============",
|
||||
"======== your favorite VPN server =======",
|
||||
"=========================================",
|
||||
"=== Made with " + emoji.Sprint(":heart:") + " by github.com/qdm12 ====",
|
||||
"=========================================",
|
||||
}
|
||||
}
|
||||
|
||||
func announcement() []string {
|
||||
if len(constants.Announcement) == 0 {
|
||||
return nil
|
||||
}
|
||||
expirationDate, _ := time.Parse("2006-01-02", constants.AnnouncementExpiration) // error covered by a unit test
|
||||
if time.Now().After(expirationDate) {
|
||||
return nil
|
||||
}
|
||||
return []string{emoji.Sprint(":mega: ") + constants.Announcement}
|
||||
}
|
||||
|
||||
func links() []string {
|
||||
return []string{
|
||||
emoji.Sprint(":wrench: ") + "Need help? " + constants.IssueLink,
|
||||
emoji.Sprint(":computer: ") + "Email? quentin.mcgaw@gmail.com",
|
||||
emoji.Sprint(":coffee: ") + "Slack? Join from the Slack button on Github",
|
||||
emoji.Sprint(":money_with_wings: ") + "Help me? https://github.com/sponsors/qdm12",
|
||||
}
|
||||
}
|
||||
65
internal/models/alias.go
Normal file
65
internal/models/alias.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
// VPNDevice is the device name used to tunnel using Openvpn.
|
||||
VPNDevice string
|
||||
// DNSHost is the DNS host to use for TLS validation.
|
||||
DNSHost string
|
||||
// URL is an HTTP(s) URL address.
|
||||
URL string
|
||||
// Filepath is a local filesytem file path.
|
||||
Filepath string
|
||||
// VPNProvider is the name of the VPN provider to be used.
|
||||
VPNProvider string
|
||||
// NetworkProtocol contains the network protocol to be used to communicate with the VPN servers.
|
||||
NetworkProtocol string
|
||||
// Loop status such as stopped or running.
|
||||
LoopStatus string
|
||||
)
|
||||
|
||||
func (ls LoopStatus) String() string {
|
||||
return string(ls)
|
||||
}
|
||||
|
||||
func marshalJSONString(s string) (data []byte, err error) {
|
||||
return []byte(fmt.Sprintf("%q", s)), nil
|
||||
}
|
||||
|
||||
func unmarshalJSONString(data []byte) (s string) {
|
||||
s = string(data)
|
||||
s = strings.TrimPrefix(s, "\"")
|
||||
s = strings.TrimSuffix(s, "\"")
|
||||
return s
|
||||
}
|
||||
|
||||
func (v *VPNProvider) MarshalJSON() ([]byte, error) {
|
||||
return marshalJSONString(string(*v))
|
||||
}
|
||||
|
||||
func (v *VPNProvider) UnmarshalJSON(data []byte) error {
|
||||
*v = VPNProvider(unmarshalJSONString(data))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NetworkProtocol) MarshalJSON() ([]byte, error) {
|
||||
return marshalJSONString(string(*n))
|
||||
}
|
||||
|
||||
func (n *NetworkProtocol) UnmarshalJSON(data []byte) error {
|
||||
*n = NetworkProtocol(unmarshalJSONString(data))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Filepath) MarshalJSON() ([]byte, error) {
|
||||
return marshalJSONString(string(*f))
|
||||
}
|
||||
|
||||
func (f *Filepath) UnmarshalJSON(data []byte) error {
|
||||
*f = Filepath(unmarshalJSONString(data))
|
||||
return nil
|
||||
}
|
||||
41
internal/models/alias_test.go
Normal file
41
internal/models/alias_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_VPNProvider_JSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
v := VPNProvider("name")
|
||||
data, err := v.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte{0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22}, data)
|
||||
err = v.UnmarshalJSON(data)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, VPNProvider("name"), v)
|
||||
}
|
||||
|
||||
func Test_NetworkProtocol_JSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
v := NetworkProtocol("name")
|
||||
data, err := v.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte{0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22}, data)
|
||||
err = v.UnmarshalJSON(data)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, NetworkProtocol("name"), v)
|
||||
}
|
||||
|
||||
func Test_Filepath_JSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
v := Filepath("name")
|
||||
data, err := v.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte{0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22}, data)
|
||||
err = v.UnmarshalJSON(data)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, Filepath("name"), v)
|
||||
}
|
||||
7
internal/models/build.go
Normal file
7
internal/models/build.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package models
|
||||
|
||||
type BuildInformation struct {
|
||||
Version string `json:"version"`
|
||||
Commit string `json:"commit"`
|
||||
BuildDate string `json:"build_date"`
|
||||
}
|
||||
12
internal/models/dns.go
Normal file
12
internal/models/dns.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package models
|
||||
|
||||
import "net"
|
||||
|
||||
// DNSProviderData contains information for a DNS provider.
|
||||
type DNSProviderData struct {
|
||||
IPs []net.IP
|
||||
SupportsTLS bool
|
||||
SupportsIPv6 bool
|
||||
SupportsDNSSec bool
|
||||
Host DNSHost
|
||||
}
|
||||
17
internal/models/openvpn.go
Normal file
17
internal/models/openvpn.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
type OpenVPNConnection struct {
|
||||
IP net.IP
|
||||
Port uint16
|
||||
Protocol NetworkProtocol
|
||||
Hostname string // Privado for tls verification
|
||||
}
|
||||
|
||||
func (o *OpenVPNConnection) Equal(other OpenVPNConnection) bool {
|
||||
return o.IP.Equal(other.IP) && o.Port == other.Port && o.Protocol == other.Protocol &&
|
||||
o.Hostname == other.Hostname
|
||||
}
|
||||
154
internal/models/selection.go
Normal file
154
internal/models/selection.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ProviderSettings contains settings specific to a VPN provider.
|
||||
type ProviderSettings struct {
|
||||
Name VPNProvider `json:"name"`
|
||||
ServerSelection ServerSelection `json:"server_selection"`
|
||||
ExtraConfigOptions ExtraConfigOptions `json:"extra_config"`
|
||||
PortForwarding PortForwarding `json:"port_forwarding"`
|
||||
}
|
||||
|
||||
type ServerSelection struct {
|
||||
// Common
|
||||
Protocol NetworkProtocol `json:"network_protocol"`
|
||||
TargetIP net.IP `json:"target_ip,omitempty"`
|
||||
|
||||
// Cyberghost, PIA, Surfshark, Windscribe, Vyprvpn, NordVPN
|
||||
Regions []string `json:"regions"`
|
||||
|
||||
// Cyberghost
|
||||
Group string `json:"group"`
|
||||
|
||||
Countries []string `json:"countries"` // Mullvad, PureVPN
|
||||
Cities []string `json:"cities"` // Mullvad, PureVPN, Windscribe
|
||||
Hostnames []string `json:"hostnames"` // Windscribe, Privado
|
||||
|
||||
// Mullvad
|
||||
ISPs []string `json:"isps"`
|
||||
Owned bool `json:"owned"`
|
||||
|
||||
// Mullvad, Windscribe
|
||||
CustomPort uint16 `json:"custom_port"`
|
||||
|
||||
// NordVPN
|
||||
Numbers []uint16 `json:"numbers"`
|
||||
|
||||
// PIA
|
||||
EncryptionPreset string `json:"encryption_preset"`
|
||||
}
|
||||
|
||||
type ExtraConfigOptions struct {
|
||||
ClientCertificate string `json:"-"` // Cyberghost
|
||||
ClientKey string `json:"-"` // Cyberghost
|
||||
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 Filepath `json:"filepath"`
|
||||
}
|
||||
|
||||
func (p *PortForwarding) String() string {
|
||||
if p.Enabled {
|
||||
return fmt.Sprintf("on, saved in %s", p.Filepath)
|
||||
}
|
||||
return "off"
|
||||
}
|
||||
|
||||
func (p *ProviderSettings) String() string {
|
||||
settingsList := []string{
|
||||
fmt.Sprintf("%s settings:", strings.Title(string(p.Name))),
|
||||
"Network protocol: " + string(p.ServerSelection.Protocol),
|
||||
}
|
||||
customPort := ""
|
||||
if p.ServerSelection.CustomPort > 0 {
|
||||
customPort = fmt.Sprintf("%d", p.ServerSelection.CustomPort)
|
||||
}
|
||||
numbers := make([]string, len(p.ServerSelection.Numbers))
|
||||
for i, number := range p.ServerSelection.Numbers {
|
||||
numbers[i] = fmt.Sprintf("%d", number)
|
||||
}
|
||||
ipv6 := "off"
|
||||
if p.ExtraConfigOptions.OpenVPNIPv6 {
|
||||
ipv6 = "on"
|
||||
}
|
||||
switch strings.ToLower(string(p.Name)) {
|
||||
case "private internet access old":
|
||||
settingsList = append(settingsList,
|
||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
||||
"Encryption preset: "+p.ExtraConfigOptions.EncryptionPreset,
|
||||
"Port forwarding: "+p.PortForwarding.String(),
|
||||
)
|
||||
case "private internet access":
|
||||
settingsList = append(settingsList,
|
||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
||||
"Encryption preset: "+p.ExtraConfigOptions.EncryptionPreset,
|
||||
"Port forwarding: "+p.PortForwarding.String(),
|
||||
)
|
||||
case "mullvad":
|
||||
settingsList = append(settingsList,
|
||||
"Countries: "+commaJoin(p.ServerSelection.Countries),
|
||||
"Cities: "+commaJoin(p.ServerSelection.Cities),
|
||||
"ISPs: "+commaJoin(p.ServerSelection.ISPs),
|
||||
"Custom port: "+customPort,
|
||||
"IPv6: "+ipv6,
|
||||
)
|
||||
case "windscribe":
|
||||
settingsList = append(settingsList,
|
||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
||||
"Custom port: "+customPort,
|
||||
)
|
||||
case "surfshark":
|
||||
settingsList = append(settingsList,
|
||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
||||
)
|
||||
case "cyberghost":
|
||||
settingsList = append(settingsList,
|
||||
"Client key: [redacted]",
|
||||
"Client certificate: [redacted]",
|
||||
"Group: "+p.ServerSelection.Group,
|
||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
||||
)
|
||||
case "vyprvpn":
|
||||
settingsList = append(settingsList,
|
||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
||||
)
|
||||
case "nordvpn":
|
||||
settingsList = append(settingsList,
|
||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
||||
"Numbers: "+commaJoin(numbers),
|
||||
)
|
||||
case "purevpn":
|
||||
settingsList = append(settingsList,
|
||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
||||
"Countries: "+commaJoin(p.ServerSelection.Countries),
|
||||
"Cities: "+commaJoin(p.ServerSelection.Cities),
|
||||
)
|
||||
case "privado":
|
||||
settingsList = append(settingsList,
|
||||
"Hostnames: "+commaJoin(p.ServerSelection.Hostnames),
|
||||
)
|
||||
default:
|
||||
settingsList = append(settingsList,
|
||||
"<Missing String method, please implement me!>",
|
||||
)
|
||||
}
|
||||
if p.ServerSelection.TargetIP != nil {
|
||||
settingsList = append(settingsList,
|
||||
"Target IP address: "+string(p.ServerSelection.TargetIP),
|
||||
)
|
||||
}
|
||||
return strings.Join(settingsList, "\n |--")
|
||||
}
|
||||
|
||||
func commaJoin(slice []string) string {
|
||||
return strings.Join(slice, ", ")
|
||||
}
|
||||
164
internal/models/server.go
Normal file
164
internal/models/server.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PIAServer struct {
|
||||
Region string `json:"region"`
|
||||
PortForward bool `json:"port_forward"`
|
||||
OpenvpnUDP PIAServerOpenvpn `json:"openvpn_udp"`
|
||||
OpenvpnTCP PIAServerOpenvpn `json:"openvpn_tcp"`
|
||||
}
|
||||
|
||||
type PIAServerOpenvpn struct {
|
||||
IPs []net.IP `json:"ips"`
|
||||
CN string `json:"cn"`
|
||||
}
|
||||
|
||||
func (p *PIAServerOpenvpn) String() string {
|
||||
return fmt.Sprintf("models.PIAServerOpenvpn{CN: %q, IPs: %s}", p.CN, goStringifyIPs(p.IPs))
|
||||
}
|
||||
|
||||
func (p *PIAServer) String() string {
|
||||
return fmt.Sprintf("{Region: %q, PortForward: %t, OpenvpnUDP: %s, OpenvpnTCP: %s}",
|
||||
p.Region, p.PortForward, p.OpenvpnUDP.String(), p.OpenvpnTCP.String())
|
||||
}
|
||||
|
||||
type MullvadServer struct {
|
||||
IPs []net.IP `json:"ips"`
|
||||
IPsV6 []net.IP `json:"ipsv6"`
|
||||
Country string `json:"country"`
|
||||
City string `json:"city"`
|
||||
ISP string `json:"isp"`
|
||||
Owned bool `json:"owned"`
|
||||
}
|
||||
|
||||
func (s *MullvadServer) String() string {
|
||||
return fmt.Sprintf("{Country: %q, City: %q, ISP: %q, Owned: %t, IPs: %s, IPsV6: %s}",
|
||||
s.Country, s.City, s.ISP, s.Owned, goStringifyIPs(s.IPs), goStringifyIPs(s.IPsV6))
|
||||
}
|
||||
|
||||
type WindscribeServer struct {
|
||||
Region string `json:"region"`
|
||||
City string `json:"city"`
|
||||
Hostname string `json:"hostname"`
|
||||
IP net.IP `json:"ip"`
|
||||
}
|
||||
|
||||
func (s *WindscribeServer) String() string {
|
||||
return fmt.Sprintf("{Region: %q, City: %q, Hostname: %q, IP: %s}",
|
||||
s.Region, s.City, s.Hostname, goStringifyIP(s.IP))
|
||||
}
|
||||
|
||||
type SurfsharkServer struct {
|
||||
Region string `json:"region"`
|
||||
IPs []net.IP `json:"ips"`
|
||||
}
|
||||
|
||||
func (s *SurfsharkServer) String() string {
|
||||
return fmt.Sprintf("{Region: %q, IPs: %s}", s.Region, goStringifyIPs(s.IPs))
|
||||
}
|
||||
|
||||
type CyberghostServer struct {
|
||||
Region string `json:"region"`
|
||||
Group string `json:"group"`
|
||||
IPs []net.IP `json:"ips"`
|
||||
}
|
||||
|
||||
func (s *CyberghostServer) String() string {
|
||||
return fmt.Sprintf("{Region: %q, Group: %q, IPs: %s}", s.Region, s.Group, goStringifyIPs(s.IPs))
|
||||
}
|
||||
|
||||
type VyprvpnServer struct {
|
||||
Region string `json:"region"`
|
||||
IPs []net.IP `json:"ips"`
|
||||
}
|
||||
|
||||
func (s *VyprvpnServer) String() string {
|
||||
return fmt.Sprintf("{Region: %q, IPs: %s}", s.Region, goStringifyIPs(s.IPs))
|
||||
}
|
||||
|
||||
type NordvpnServer struct { //nolint:maligned
|
||||
Region string `json:"region"`
|
||||
Number uint16 `json:"number"`
|
||||
IP net.IP `json:"ip"`
|
||||
TCP bool `json:"tcp"`
|
||||
UDP bool `json:"udp"`
|
||||
}
|
||||
|
||||
func (s *NordvpnServer) String() string {
|
||||
return fmt.Sprintf("{Region: %q, Number: %d, TCP: %t, UDP: %t, IP: %s}",
|
||||
s.Region, s.Number, s.TCP, s.UDP, goStringifyIP(s.IP))
|
||||
}
|
||||
|
||||
type PurevpnServer struct {
|
||||
Country string `json:"country"`
|
||||
Region string `json:"region"`
|
||||
City string `json:"city"`
|
||||
IPs []net.IP `json:"ips"`
|
||||
}
|
||||
|
||||
func (s *PurevpnServer) String() string {
|
||||
return fmt.Sprintf("{Country: %q, Region: %q, City: %q, IPs: %s}",
|
||||
s.Country, s.Region, s.City, goStringifyIPs(s.IPs))
|
||||
}
|
||||
|
||||
type PrivadoServer struct {
|
||||
IP net.IP `json:"ip"`
|
||||
Hostname string `json:"hostname"`
|
||||
}
|
||||
|
||||
func (s *PrivadoServer) String() string {
|
||||
return fmt.Sprintf("{Hostname: %q, IP: %s}",
|
||||
s.Hostname, goStringifyIP(s.IP))
|
||||
}
|
||||
|
||||
func goStringifyIP(ip net.IP) string {
|
||||
s := fmt.Sprintf("%#v", ip)
|
||||
s = strings.TrimSuffix(strings.TrimPrefix(s, "net.IP{"), "}")
|
||||
fields := strings.Split(s, ", ")
|
||||
isIPv4 := ip.To4() != nil
|
||||
if isIPv4 {
|
||||
fields = fields[len(fields)-4:]
|
||||
}
|
||||
|
||||
// Count leading zeros
|
||||
leadingZeros := 0
|
||||
for i := range fields {
|
||||
if fields[i] == "0x0" {
|
||||
leadingZeros++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Remove leading zeros
|
||||
fields = fields[leadingZeros:]
|
||||
|
||||
for i := range fields {
|
||||
// IPv4 is better understood in integer notation, whereas IPv6 is written in hex notation
|
||||
if isIPv4 {
|
||||
hexString := strings.Replace(fields[i], "0x", "", 1)
|
||||
if len(hexString) == 1 {
|
||||
hexString = "0" + hexString
|
||||
}
|
||||
b, _ := hex.DecodeString(hexString)
|
||||
fields[i] = fmt.Sprintf("%d", b[0])
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("net.IP{%s}", strings.Join(fields, ", "))
|
||||
}
|
||||
|
||||
func goStringifyIPs(ips []net.IP) string {
|
||||
ipStrings := make([]string, len(ips))
|
||||
for i := range ips {
|
||||
ipStrings[i] = goStringifyIP(ips[i])
|
||||
ipStrings[i] = strings.TrimPrefix(ipStrings[i], "net.IP")
|
||||
}
|
||||
return "[]net.IP{" + strings.Join(ipStrings, ", ") + "}"
|
||||
}
|
||||
118
internal/models/server_test.go
Normal file
118
internal/models/server_test.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_MullvadServer_String(t *testing.T) {
|
||||
t.Parallel()
|
||||
testCases := map[string]struct {
|
||||
server MullvadServer
|
||||
s string
|
||||
}{
|
||||
"example": {
|
||||
server: MullvadServer{
|
||||
IPs: []net.IP{{1, 1, 1, 1}},
|
||||
IPsV6: []net.IP{{0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1}},
|
||||
Country: "That Country",
|
||||
City: "That City",
|
||||
ISP: "not spying on you",
|
||||
Owned: true,
|
||||
},
|
||||
//nolint:lll
|
||||
s: `{Country: "That Country", City: "That City", ISP: "not spying on you", Owned: true, IPs: []net.IP{{1, 1, 1, 1}}, IPsV6: []net.IP{{0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1}}}`,
|
||||
},
|
||||
}
|
||||
for name, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := testCase.server.String()
|
||||
assert.Equal(t, testCase.s, s)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_goStringifyIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
testCases := map[string]struct {
|
||||
ip net.IP
|
||||
s string
|
||||
}{
|
||||
"nil ip": {
|
||||
s: "net.IP{net.IP(nil)}",
|
||||
},
|
||||
"empty ip": {
|
||||
ip: net.IP{},
|
||||
s: "net.IP{}",
|
||||
},
|
||||
"ipv4": {
|
||||
ip: net.IP{10, 16, 54, 25},
|
||||
s: "net.IP{10, 16, 54, 25}",
|
||||
},
|
||||
"ipv6": {
|
||||
ip: net.IP{0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1},
|
||||
s: "net.IP{0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1}",
|
||||
},
|
||||
"zeros ipv4": {
|
||||
ip: net.IP{0, 0, 0, 0},
|
||||
s: "net.IP{}",
|
||||
},
|
||||
"zeros ipv46": {
|
||||
ip: net.ParseIP("::"),
|
||||
s: "net.IP{}",
|
||||
},
|
||||
}
|
||||
for name, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := goStringifyIP(testCase.ip)
|
||||
assert.Equal(t, testCase.s, s)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_stringifyIPs(t *testing.T) {
|
||||
t.Parallel()
|
||||
testCases := map[string]struct {
|
||||
ips []net.IP
|
||||
s string
|
||||
}{
|
||||
"nil ips": {
|
||||
s: "[]net.IP{}",
|
||||
},
|
||||
"empty ips": {
|
||||
ips: []net.IP{},
|
||||
s: "[]net.IP{}",
|
||||
},
|
||||
"single ipv4": {
|
||||
ips: []net.IP{{10, 16, 54, 25}},
|
||||
s: "[]net.IP{{10, 16, 54, 25}}",
|
||||
},
|
||||
"single ipv6": {
|
||||
ips: []net.IP{{0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1}},
|
||||
s: "[]net.IP{{0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1}}",
|
||||
},
|
||||
"mix of ips": {
|
||||
ips: []net.IP{
|
||||
{10, 16, 54, 25},
|
||||
{0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1},
|
||||
{0, 0, 0, 0},
|
||||
},
|
||||
//nolint:lll
|
||||
s: "[]net.IP{{10, 16, 54, 25}, {0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1}, {}}",
|
||||
},
|
||||
}
|
||||
for name, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := goStringifyIPs(testCase.ips)
|
||||
assert.Equal(t, testCase.s, s)
|
||||
})
|
||||
}
|
||||
}
|
||||
60
internal/models/servers.go
Normal file
60
internal/models/servers.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package models
|
||||
|
||||
type AllServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Cyberghost CyberghostServers `json:"cyberghost"`
|
||||
Mullvad MullvadServers `json:"mullvad"`
|
||||
Nordvpn NordvpnServers `json:"nordvpn"`
|
||||
Pia PiaServers `json:"pia"`
|
||||
Privado PrivadoServers `json:"privado"`
|
||||
Purevpn PurevpnServers `json:"purevpn"`
|
||||
Surfshark SurfsharkServers `json:"surfshark"`
|
||||
Vyprvpn VyprvpnServers `json:"vyprvpn"`
|
||||
Windscribe WindscribeServers `json:"windscribe"`
|
||||
}
|
||||
|
||||
type CyberghostServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []CyberghostServer `json:"servers"`
|
||||
}
|
||||
type MullvadServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []MullvadServer `json:"servers"`
|
||||
}
|
||||
type NordvpnServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []NordvpnServer `json:"servers"`
|
||||
}
|
||||
type PiaServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []PIAServer `json:"servers"`
|
||||
}
|
||||
type PrivadoServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []PrivadoServer `json:"servers"`
|
||||
}
|
||||
type PurevpnServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []PurevpnServer `json:"servers"`
|
||||
}
|
||||
type SurfsharkServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []SurfsharkServer `json:"servers"`
|
||||
}
|
||||
type VyprvpnServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []VyprvpnServer `json:"servers"`
|
||||
}
|
||||
type WindscribeServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []WindscribeServer `json:"servers"`
|
||||
}
|
||||
68
internal/openvpn/auth.go
Normal file
68
internal/openvpn/auth.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package openvpn
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
// WriteAuthFile writes the OpenVPN auth file to disk with the right permissions.
|
||||
func (c *configurator) WriteAuthFile(user, password string, puid, pgid int) error {
|
||||
const filepath = string(constants.OpenVPNAuthConf)
|
||||
file, err := c.os.OpenFile(filepath, os.O_RDONLY, 0)
|
||||
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
file, err = c.os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE, 0400)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = file.WriteString(user + "\n" + password)
|
||||
if err != nil {
|
||||
_ = file.Close()
|
||||
return err
|
||||
}
|
||||
err = file.Chown(puid, pgid)
|
||||
if err != nil {
|
||||
_ = file.Close()
|
||||
return err
|
||||
}
|
||||
return file.Close()
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
_ = file.Close()
|
||||
return err
|
||||
}
|
||||
if err := file.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lines := strings.Split(string(data), "\n")
|
||||
if len(lines) > 1 && lines[0] == user && lines[1] == password {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.logger.Info("username and password changed in %s", constants.OpenVPNAuthConf)
|
||||
file, err = c.os.OpenFile(filepath, os.O_TRUNC|os.O_WRONLY, 0400)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = file.WriteString(user + "\n" + password)
|
||||
if err != nil {
|
||||
_ = file.Close()
|
||||
return err
|
||||
}
|
||||
err = file.Chown(puid, pgid)
|
||||
if err != nil {
|
||||
_ = file.Close()
|
||||
return err
|
||||
}
|
||||
return file.Close()
|
||||
}
|
||||
30
internal/openvpn/command.go
Normal file
30
internal/openvpn/command.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package openvpn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
func (c *configurator) Start(ctx context.Context) (stdout io.ReadCloser, waitFn func() error, err error) {
|
||||
c.logger.Info("starting openvpn")
|
||||
stdout, _, waitFn, err = c.commander.Start(ctx, "openvpn", "--config", string(constants.OpenVPNConf))
|
||||
return stdout, waitFn, err
|
||||
}
|
||||
|
||||
func (c *configurator) Version(ctx context.Context) (string, error) {
|
||||
output, err := c.commander.Run(ctx, "openvpn", "--version")
|
||||
if err != nil && err.Error() != "exit status 1" {
|
||||
return "", err
|
||||
}
|
||||
firstLine := strings.Split(output, "\n")[0]
|
||||
words := strings.Fields(firstLine)
|
||||
const minWords = 2
|
||||
if len(words) < minWords {
|
||||
return "", fmt.Errorf("openvpn --version: first line is too short: %q", firstLine)
|
||||
}
|
||||
return words[1], nil
|
||||
}
|
||||
266
internal/openvpn/loop.go
Normal file
266
internal/openvpn/loop.go
Normal file
@@ -0,0 +1,266 @@
|
||||
package openvpn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/firewall"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/provider"
|
||||
"github.com/qdm12/gluetun/internal/routing"
|
||||
"github.com/qdm12/gluetun/internal/settings"
|
||||
"github.com/qdm12/golibs/command"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
"github.com/qdm12/golibs/os"
|
||||
)
|
||||
|
||||
type Looper interface {
|
||||
Run(ctx context.Context, wg *sync.WaitGroup)
|
||||
GetStatus() (status models.LoopStatus)
|
||||
SetStatus(status models.LoopStatus) (outcome string, err error)
|
||||
GetSettings() (settings settings.OpenVPN)
|
||||
SetSettings(settings settings.OpenVPN) (outcome string)
|
||||
GetServers() (servers models.AllServers)
|
||||
SetServers(servers models.AllServers)
|
||||
GetPortForwarded() (port uint16)
|
||||
PortForward(vpnGatewayIP net.IP)
|
||||
}
|
||||
|
||||
type looper struct {
|
||||
state state
|
||||
// Fixed parameters
|
||||
username string
|
||||
puid int
|
||||
pgid int
|
||||
// Configurators
|
||||
conf Configurator
|
||||
fw firewall.Configurator
|
||||
routing routing.Routing
|
||||
// Other objects
|
||||
logger, pfLogger logging.Logger
|
||||
client *http.Client
|
||||
openFile os.OpenFileFunc
|
||||
streamMerger command.StreamMerger
|
||||
cancel context.CancelFunc
|
||||
// Internal channels and locks
|
||||
loopLock sync.Mutex
|
||||
running chan models.LoopStatus
|
||||
stop, stopped chan struct{}
|
||||
start chan struct{}
|
||||
portForwardSignals chan net.IP
|
||||
crashed bool
|
||||
backoffTime time.Duration
|
||||
}
|
||||
|
||||
const defaultBackoffTime = 15 * time.Second
|
||||
|
||||
func NewLooper(settings settings.OpenVPN,
|
||||
username string, puid, pgid int, allServers models.AllServers,
|
||||
conf Configurator, fw firewall.Configurator, routing routing.Routing,
|
||||
logger logging.Logger, client *http.Client, openFile os.OpenFileFunc,
|
||||
streamMerger command.StreamMerger, cancel context.CancelFunc) Looper {
|
||||
return &looper{
|
||||
state: state{
|
||||
status: constants.Stopped,
|
||||
settings: settings,
|
||||
allServers: allServers,
|
||||
},
|
||||
username: username,
|
||||
puid: puid,
|
||||
pgid: pgid,
|
||||
conf: conf,
|
||||
fw: fw,
|
||||
routing: routing,
|
||||
logger: logger.WithPrefix("openvpn: "),
|
||||
pfLogger: logger.WithPrefix("port forwarding: "),
|
||||
client: client,
|
||||
openFile: openFile,
|
||||
streamMerger: streamMerger,
|
||||
cancel: cancel,
|
||||
start: make(chan struct{}),
|
||||
running: make(chan models.LoopStatus),
|
||||
stop: make(chan struct{}),
|
||||
stopped: make(chan struct{}),
|
||||
portForwardSignals: make(chan net.IP),
|
||||
backoffTime: defaultBackoffTime,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *looper) PortForward(vpnGateway net.IP) { l.portForwardSignals <- vpnGateway }
|
||||
|
||||
func (l *looper) signalCrashedStatus() {
|
||||
if !l.crashed {
|
||||
l.crashed = true
|
||||
l.running <- constants.Crashed
|
||||
}
|
||||
}
|
||||
|
||||
func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
select {
|
||||
case <-l.start:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
defer l.logger.Warn("loop exited")
|
||||
|
||||
for ctx.Err() == nil {
|
||||
settings, allServers := l.state.getSettingsAndServers()
|
||||
providerConf := provider.New(settings.Provider.Name, allServers, time.Now)
|
||||
connection, err := providerConf.GetOpenVPNConnection(settings.Provider.ServerSelection)
|
||||
if err != nil {
|
||||
l.logger.Error(err)
|
||||
l.signalCrashedStatus()
|
||||
l.cancel()
|
||||
return
|
||||
}
|
||||
lines := providerConf.BuildConf(connection, l.username, settings)
|
||||
|
||||
if err := writeOpenvpnConf(lines, l.openFile); err != nil {
|
||||
l.logger.Error(err)
|
||||
l.signalCrashedStatus()
|
||||
l.cancel()
|
||||
return
|
||||
}
|
||||
|
||||
if err := l.conf.WriteAuthFile(settings.User, settings.Password, l.puid, l.pgid); err != nil {
|
||||
l.logger.Error(err)
|
||||
l.signalCrashedStatus()
|
||||
l.cancel()
|
||||
return
|
||||
}
|
||||
|
||||
if err := l.fw.SetVPNConnection(ctx, connection); err != nil {
|
||||
l.logger.Error(err)
|
||||
l.signalCrashedStatus()
|
||||
l.cancel()
|
||||
return
|
||||
}
|
||||
|
||||
openvpnCtx, openvpnCancel := context.WithCancel(context.Background())
|
||||
|
||||
stream, waitFn, err := l.conf.Start(openvpnCtx)
|
||||
if err != nil {
|
||||
openvpnCancel()
|
||||
l.signalCrashedStatus()
|
||||
l.logAndWait(ctx, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Needs the stream line from main.go to know when the tunnel is up
|
||||
go func(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
// TODO have a way to disable pf with a context
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case gateway := <-l.portForwardSignals:
|
||||
wg.Add(1)
|
||||
go l.portForward(ctx, wg, providerConf, l.client, gateway)
|
||||
}
|
||||
}
|
||||
}(openvpnCtx)
|
||||
|
||||
go l.streamMerger.Merge(openvpnCtx, stream, command.MergeName("openvpn"))
|
||||
waitError := make(chan error)
|
||||
go func() {
|
||||
err := waitFn() // blocking
|
||||
waitError <- err
|
||||
}()
|
||||
|
||||
if l.crashed {
|
||||
l.crashed = false
|
||||
l.backoffTime = defaultBackoffTime
|
||||
l.state.setStatusWithLock(constants.Running)
|
||||
} else {
|
||||
l.running <- constants.Running
|
||||
}
|
||||
|
||||
stayHere := true
|
||||
for stayHere {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
l.logger.Warn("context canceled: exiting loop")
|
||||
openvpnCancel()
|
||||
<-waitError
|
||||
close(waitError)
|
||||
return
|
||||
case <-l.stop:
|
||||
l.logger.Info("stopping")
|
||||
openvpnCancel()
|
||||
<-waitError
|
||||
l.stopped <- struct{}{}
|
||||
case <-l.start:
|
||||
l.logger.Info("starting")
|
||||
stayHere = false
|
||||
case err := <-waitError: // unexpected error
|
||||
openvpnCancel()
|
||||
l.state.setStatusWithLock(constants.Crashed)
|
||||
l.logAndWait(ctx, err)
|
||||
l.crashed = true
|
||||
stayHere = false
|
||||
}
|
||||
}
|
||||
close(waitError)
|
||||
openvpnCancel() // just for the linter
|
||||
}
|
||||
}
|
||||
|
||||
func (l *looper) logAndWait(ctx context.Context, err error) {
|
||||
l.logger.Error(err)
|
||||
l.logger.Info("retrying in %s", l.backoffTime)
|
||||
timer := time.NewTimer(l.backoffTime)
|
||||
l.backoffTime *= 2
|
||||
select {
|
||||
case <-timer.C:
|
||||
case <-ctx.Done():
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// portForward is a blocking operation which may or may not be infinite.
|
||||
// You should therefore always call it in a goroutine.
|
||||
func (l *looper) portForward(ctx context.Context, wg *sync.WaitGroup,
|
||||
providerConf provider.Provider, client *http.Client, gateway net.IP) {
|
||||
defer wg.Done()
|
||||
l.state.portForwardedMu.RLock()
|
||||
settings := l.state.settings
|
||||
l.state.portForwardedMu.RUnlock()
|
||||
if !settings.Provider.PortForwarding.Enabled {
|
||||
return
|
||||
}
|
||||
syncState := func(port uint16) (pfFilepath models.Filepath) {
|
||||
l.state.portForwardedMu.Lock()
|
||||
defer l.state.portForwardedMu.Unlock()
|
||||
l.state.portForwarded = port
|
||||
l.state.settingsMu.RLock()
|
||||
defer l.state.settingsMu.RUnlock()
|
||||
return settings.Provider.PortForwarding.Filepath
|
||||
}
|
||||
providerConf.PortForward(ctx,
|
||||
client, l.openFile, l.pfLogger,
|
||||
gateway, l.fw, syncState)
|
||||
}
|
||||
|
||||
func writeOpenvpnConf(lines []string, openFile os.OpenFileFunc) error {
|
||||
const filepath = string(constants.OpenVPNConf)
|
||||
file, err := openFile(filepath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = file.WriteString(strings.Join(lines, "\n"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := file.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
35
internal/openvpn/openvpn.go
Normal file
35
internal/openvpn/openvpn.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package openvpn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/unix"
|
||||
"github.com/qdm12/golibs/command"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
"github.com/qdm12/golibs/os"
|
||||
)
|
||||
|
||||
type Configurator interface {
|
||||
Version(ctx context.Context) (string, error)
|
||||
WriteAuthFile(user, password string, puid, pgid int) error
|
||||
CheckTUN() error
|
||||
CreateTUN() error
|
||||
Start(ctx context.Context) (stdout io.ReadCloser, waitFn func() error, err error)
|
||||
}
|
||||
|
||||
type configurator struct {
|
||||
logger logging.Logger
|
||||
commander command.Commander
|
||||
os os.OS
|
||||
unix unix.Unix
|
||||
}
|
||||
|
||||
func NewConfigurator(logger logging.Logger, os os.OS, unix unix.Unix) Configurator {
|
||||
return &configurator{
|
||||
logger: logger.WithPrefix("openvpn configurator: "),
|
||||
commander: command.NewCommander(),
|
||||
os: os,
|
||||
unix: unix,
|
||||
}
|
||||
}
|
||||
121
internal/openvpn/state.go
Normal file
121
internal/openvpn/state.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package openvpn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/settings"
|
||||
)
|
||||
|
||||
type state struct {
|
||||
status models.LoopStatus
|
||||
settings settings.OpenVPN
|
||||
allServers models.AllServers
|
||||
portForwarded uint16
|
||||
statusMu sync.RWMutex
|
||||
settingsMu sync.RWMutex
|
||||
allServersMu sync.RWMutex
|
||||
portForwardedMu sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *state) setStatusWithLock(status models.LoopStatus) {
|
||||
s.statusMu.Lock()
|
||||
defer s.statusMu.Unlock()
|
||||
s.status = status
|
||||
}
|
||||
|
||||
func (s *state) getSettingsAndServers() (settings settings.OpenVPN, allServers models.AllServers) {
|
||||
s.settingsMu.RLock()
|
||||
s.allServersMu.RLock()
|
||||
settings = s.settings
|
||||
allServers = s.allServers
|
||||
s.settingsMu.RLock()
|
||||
s.allServersMu.RLock()
|
||||
return settings, allServers
|
||||
}
|
||||
|
||||
func (l *looper) GetStatus() (status models.LoopStatus) {
|
||||
l.state.statusMu.RLock()
|
||||
defer l.state.statusMu.RUnlock()
|
||||
return l.state.status
|
||||
}
|
||||
|
||||
func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) {
|
||||
l.state.statusMu.Lock()
|
||||
defer l.state.statusMu.Unlock()
|
||||
existingStatus := l.state.status
|
||||
|
||||
switch status {
|
||||
case constants.Running:
|
||||
switch existingStatus {
|
||||
case constants.Starting, constants.Running, constants.Stopping, constants.Crashed:
|
||||
return fmt.Sprintf("already %s", existingStatus), nil
|
||||
}
|
||||
l.loopLock.Lock()
|
||||
defer l.loopLock.Unlock()
|
||||
l.state.status = constants.Starting
|
||||
l.state.statusMu.Unlock()
|
||||
l.start <- struct{}{}
|
||||
newStatus := <-l.running
|
||||
l.state.statusMu.Lock()
|
||||
l.state.status = newStatus
|
||||
return newStatus.String(), nil
|
||||
case constants.Stopped:
|
||||
switch existingStatus {
|
||||
case constants.Starting, constants.Stopping, constants.Stopped, constants.Crashed:
|
||||
return fmt.Sprintf("already %s", existingStatus), nil
|
||||
}
|
||||
l.loopLock.Lock()
|
||||
defer l.loopLock.Unlock()
|
||||
l.state.status = constants.Stopping
|
||||
l.state.statusMu.Unlock()
|
||||
l.stop <- struct{}{}
|
||||
<-l.stopped
|
||||
l.state.statusMu.Lock()
|
||||
l.state.status = constants.Stopped
|
||||
return status.String(), nil
|
||||
default:
|
||||
return "", fmt.Errorf("status %q can only be %q or %q",
|
||||
status, constants.Running, constants.Stopped)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *looper) GetSettings() (settings settings.OpenVPN) {
|
||||
l.state.settingsMu.RLock()
|
||||
defer l.state.settingsMu.RUnlock()
|
||||
return l.state.settings
|
||||
}
|
||||
|
||||
func (l *looper) SetSettings(settings settings.OpenVPN) (outcome string) {
|
||||
l.state.settingsMu.Lock()
|
||||
settingsUnchanged := reflect.DeepEqual(l.state.settings, settings)
|
||||
if settingsUnchanged {
|
||||
l.state.settingsMu.Unlock()
|
||||
return "settings left unchanged"
|
||||
}
|
||||
l.state.settings = settings
|
||||
_, _ = l.SetStatus(constants.Stopped)
|
||||
outcome, _ = l.SetStatus(constants.Running)
|
||||
return outcome
|
||||
}
|
||||
|
||||
func (l *looper) GetServers() (servers models.AllServers) {
|
||||
l.state.allServersMu.RLock()
|
||||
defer l.state.allServersMu.RUnlock()
|
||||
return l.state.allServers
|
||||
}
|
||||
|
||||
func (l *looper) SetServers(servers models.AllServers) {
|
||||
l.state.allServersMu.Lock()
|
||||
defer l.state.allServersMu.Unlock()
|
||||
l.state.allServers = servers
|
||||
}
|
||||
|
||||
func (l *looper) GetPortForwarded() (port uint16) {
|
||||
l.state.portForwardedMu.RLock()
|
||||
defer l.state.portForwardedMu.RUnlock()
|
||||
return l.state.portForwarded
|
||||
}
|
||||
51
internal/openvpn/tun.go
Normal file
51
internal/openvpn/tun.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package openvpn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/unix"
|
||||
)
|
||||
|
||||
// CheckTUN checks the tunnel device is present and accessible.
|
||||
func (c *configurator) CheckTUN() error {
|
||||
c.logger.Info("checking for device %s", constants.TunnelDevice)
|
||||
f, err := c.os.OpenFile(string(constants.TunnelDevice), os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("TUN device is not available: %w", err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
c.logger.Warn("Could not close TUN device file: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *configurator) CreateTUN() error {
|
||||
c.logger.Info("creating %s", constants.TunnelDevice)
|
||||
if err := c.os.MkdirAll("/dev/net", 0751); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
const (
|
||||
major = 10
|
||||
minor = 200
|
||||
)
|
||||
dev := c.unix.Mkdev(major, minor)
|
||||
if err := c.unix.Mknod(string(constants.TunnelDevice), unix.S_IFCHR, int(dev)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
const filepath = string(constants.TunnelDevice)
|
||||
file, err := c.os.OpenFile(filepath, os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
const readWriteAllPerms os.FileMode = 0666
|
||||
if err := file.Chmod(readWriteAllPerms); err != nil {
|
||||
_ = file.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
return file.Close()
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user