mirror of
https://github.com/qdm12/gluetun.git
synced 2026-08-01 09:53:20 +02:00
Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 62a6016882 | |||
| 18a4a79763 | |||
| 56ea722f93 | |||
| d2ab974933 | |||
| 37d7a8b5fe | |||
| e4dcadd825 | |||
| fee99e9fe3 | |||
| 8ac4826126 | |||
| 7deb12e06d | |||
| d6e218141b | |||
| f44121b044 | |||
| 5d8d92462d | |||
| 985cf7b7dd | |||
| dcbc10fd57 | |||
| 79f243e98d | |||
| cf95692b93 | |||
| d8e008606f | |||
| 3cd26a9f61 | |||
| 5d74320ee7 | |||
| f9aadeef1c | |||
| 625de1c834 | |||
| 1c0a3ed1a4 | |||
| 03ba9169f4 | |||
| c22e0e9db7 | |||
| 6bcbaf085d | |||
| 9a1d9c5d74 | |||
| 59a3a072e0 | |||
| 9f001bbc06 | |||
| b8356b60a6 | |||
| e2e218c74b | |||
| 3bf23cbae5 | |||
| da562d8206 | |||
| 81bf83db13 | |||
| 7a25dcd130 | |||
| 877c7e1a9f | |||
| 77b2512745 | |||
| 749b73ef15 | |||
| e499eca12c | |||
| 80f25c34e5 | |||
| 61677fbce2 | |||
| dc6171185e | |||
| f7e4331e93 | |||
| 1340511b64 | |||
| c3078f84e8 | |||
| 9f65157a0d | |||
| 89166cdabf | |||
| b872973e8b | |||
| 2000e72357 | |||
| 836e53642d | |||
| af3f882bb8 | |||
| 2ab05b9350 | |||
| 1022eb8a6e | |||
| 15fe62de32 | |||
| 83d87f83f9 | |||
| 76a0c1f6c4 | |||
| a1588302a7 | |||
| 91ce790b6b | |||
| 5d3982c2d2 | |||
| 2cf7f7b268 | |||
| 8645d978ba | |||
| cc18b158f4 | |||
| 0730b6db6e | |||
| 3d2a360401 | |||
| 0c60dab384 | |||
| f5f0ad7f28 | |||
| f807f756eb | |||
| 11af6c10f1 | |||
| 40342619e7 | |||
| 19bf62c21f | |||
| 2ea00d149f | |||
| cc677bde93 | |||
| 6627cda96c | |||
| cade2732b0 | |||
| 541a4a3271 | |||
| 0eccd068e5 | |||
| 87f4b9e422 | |||
| bcaf2e42fd | |||
| d39201f9b3 | |||
| 8ac2a816c3 | |||
| 344f1bf9ee | |||
| f0a006fc43 | |||
| 145da0b21d | |||
| 094de89a3e | |||
| 65ace12def | |||
| 9afe455635 | |||
| 45ce422a89 | |||
| 4a0738cd49 |
@@ -1,49 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug
|
|
||||||
about: Report a bug
|
|
||||||
title: 'Bug: FILL THIS TEXT!'
|
|
||||||
labels: ":bug: bug"
|
|
||||||
assignees: qdm12
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!---
|
|
||||||
⚠️ Answer the following or I'll insta-close your issue
|
|
||||||
-->
|
|
||||||
|
|
||||||
**Is this urgent?**: No
|
|
||||||
|
|
||||||
**Host OS** (approximate answer is fine too): Ubuntu 18
|
|
||||||
|
|
||||||
<!---
|
|
||||||
🚧 If this is about the Unraid template see https://github.com/qdm12/gluetun/discussions/550
|
|
||||||
-->
|
|
||||||
|
|
||||||
**CPU arch** or **device name**: amd64
|
|
||||||
|
|
||||||
**What VPN provider are you using**:
|
|
||||||
|
|
||||||
**What are you using to run your container?**: Docker Compose
|
|
||||||
|
|
||||||
**What is the version of the program** (See the line at the top of your logs)
|
|
||||||
|
|
||||||
```
|
|
||||||
Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
|
|
||||||
```
|
|
||||||
|
|
||||||
**What's the problem** 🤔
|
|
||||||
|
|
||||||
That feature doesn't work
|
|
||||||
|
|
||||||
**Share your logs... (careful to remove in example tokens)**
|
|
||||||
|
|
||||||
```log
|
|
||||||
|
|
||||||
PASTE YOUR LOGS
|
|
||||||
IN THERE
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
<!---
|
|
||||||
💡 You can highlight your code with https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlight
|
|
||||||
-->
|
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
name: Bug
|
||||||
|
description: Report a bug
|
||||||
|
title: "Bug: "
|
||||||
|
labels: [":bug: bug"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
- type: dropdown
|
||||||
|
id: urgent
|
||||||
|
attributes:
|
||||||
|
label: Is this urgent?
|
||||||
|
description: Is this a critical bug, or do you need this fixed urgently?
|
||||||
|
options:
|
||||||
|
- "No"
|
||||||
|
- "Yes"
|
||||||
|
- type: input
|
||||||
|
id: host-os
|
||||||
|
attributes:
|
||||||
|
label: Host OS
|
||||||
|
description: What is your host OS?
|
||||||
|
placeholder: "Debian Buster"
|
||||||
|
- type: dropdown
|
||||||
|
id: cpu-arch
|
||||||
|
attributes:
|
||||||
|
label: CPU arch
|
||||||
|
description: You can find it on Linux with `uname -m`.
|
||||||
|
options:
|
||||||
|
- x86_64
|
||||||
|
- aarch64
|
||||||
|
- armv7l
|
||||||
|
- "386"
|
||||||
|
- s390x
|
||||||
|
- ppc64le
|
||||||
|
- type: dropdown
|
||||||
|
id: vpn-service-provider
|
||||||
|
attributes:
|
||||||
|
label: VPN service provider
|
||||||
|
options:
|
||||||
|
- Cyberghost
|
||||||
|
- ExpressVPN
|
||||||
|
- FastestVPN
|
||||||
|
- HideMyAss
|
||||||
|
- IPVanish
|
||||||
|
- IVPN
|
||||||
|
- Mullvad
|
||||||
|
- NordVPN
|
||||||
|
- Privado
|
||||||
|
- Private Internet Access
|
||||||
|
- PrivateVPN
|
||||||
|
- ProtonVPN
|
||||||
|
- PureVPN
|
||||||
|
- Surfshark
|
||||||
|
- TorGuard
|
||||||
|
- VPNUnlimited
|
||||||
|
- VyprVPN
|
||||||
|
- WeVPN
|
||||||
|
- Windscribe
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: docker
|
||||||
|
attributes:
|
||||||
|
label: What are you using to run the container
|
||||||
|
options:
|
||||||
|
- docker run
|
||||||
|
- docker-compose
|
||||||
|
- Portainer
|
||||||
|
- Kubernetes
|
||||||
|
- Podman
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: What is the version of Gluetun
|
||||||
|
description: |
|
||||||
|
Copy paste the version line at the top of your logs.
|
||||||
|
It should be in the form `Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)`.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: problem
|
||||||
|
attributes:
|
||||||
|
label: "What's the problem 🤔"
|
||||||
|
placeholder: "That feature does not work..."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Share your logs
|
||||||
|
description: No sensitive information is logged out except when running with `LOG_LEVEL=debug`.
|
||||||
|
render: log
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: config
|
||||||
|
attributes:
|
||||||
|
label: Share your configuration
|
||||||
|
description: Share your configuration such as `docker-compose.yml`. Ensure to remove credentials.
|
||||||
|
render: yml
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
contact_links:
|
||||||
|
- name: Configuration help?
|
||||||
|
url: https://github.com/qdm12/gluetun/discussions/new
|
||||||
|
about: Please create a Github discussion.
|
||||||
|
- name: Unraid template issue
|
||||||
|
url: https://github.com/qdm12/gluetun/discussions/550
|
||||||
|
about: Please read the relevant Github discussion.
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest a feature to add to this project
|
|
||||||
title: 'Feature request: FILL THIS TEXT!'
|
|
||||||
labels: ":bulb: feature request"
|
|
||||||
assignees: qdm12
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**What's the feature?** 🧐
|
|
||||||
|
|
||||||
- Support this new feature because that and that
|
|
||||||
|
|
||||||
**Optional extra information** 🚀
|
|
||||||
|
|
||||||
- I tried `docker run something` and it doesn't work
|
|
||||||
- That [url](https://github.com/qdm12/gluetun) is interesting
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
name: Feature request
|
||||||
|
description: Suggest a feature to add to Gluetun
|
||||||
|
title: "Feature request: "
|
||||||
|
labels: [":bulb: feature request"]
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: "What's the feature 🧐"
|
||||||
|
placeholder: "Make the tunnel resistant to earth quakes"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: extra
|
||||||
|
attributes:
|
||||||
|
label: "Extra information and references"
|
||||||
|
placeholder: |
|
||||||
|
- I tried `docker run something` and it doesn't work
|
||||||
|
- That [url](https://github.com/qdm12/gluetun) is interesting
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
---
|
|
||||||
name: Help
|
|
||||||
about: Ask for help
|
|
||||||
title: 'Help: FILL THIS TEXT!'
|
|
||||||
labels: ":pray: help wanted"
|
|
||||||
assignees:
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!---
|
|
||||||
⚠️ If this about a Docker configuration problem or another service:
|
|
||||||
Start a discussion at https://github.com/qdm12/gluetun/discussions/new
|
|
||||||
OR I WILL INSTA-CLOSE YOUR ISSUE.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!---
|
|
||||||
⚠️ Answer the following or I'll insta-close your issue
|
|
||||||
-->
|
|
||||||
|
|
||||||
**Is this urgent?**: No
|
|
||||||
|
|
||||||
**Host OS** (approximate answer is fine too): Ubuntu 18
|
|
||||||
|
|
||||||
**CPU arch** or **device name**: amd64
|
|
||||||
|
|
||||||
**What VPN provider are you using**:
|
|
||||||
|
|
||||||
**What is the version of the program** (See the line at the top of your logs)
|
|
||||||
|
|
||||||
```
|
|
||||||
Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
|
|
||||||
```
|
|
||||||
|
|
||||||
**What's the problem** 🤔
|
|
||||||
|
|
||||||
That feature doesn't work
|
|
||||||
|
|
||||||
**Share your logs... (careful to remove in example tokens)**
|
|
||||||
|
|
||||||
```log
|
|
||||||
|
|
||||||
PASTE YOUR LOGS
|
|
||||||
IN THERE
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
**What are you using to run your container?**: Docker Compose
|
|
||||||
|
|
||||||
<!---
|
|
||||||
💡 You can highlight your code with https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlight
|
|
||||||
-->
|
|
||||||
|
|
||||||
Please also share your configuration file:
|
|
||||||
|
|
||||||
```yml
|
|
||||||
your .yml
|
|
||||||
content
|
|
||||||
in here
|
|
||||||
```
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# your docker
|
|
||||||
# run command
|
|
||||||
# in here
|
|
||||||
```
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
name: Wiki issue
|
||||||
|
description: Report a Wiki issue
|
||||||
|
title: "Wiki issue: "
|
||||||
|
labels: ["📄 Wiki issue"]
|
||||||
|
body:
|
||||||
|
- type: input
|
||||||
|
id: url
|
||||||
|
attributes:
|
||||||
|
label: "URL to the Wiki page"
|
||||||
|
placeholder: "https://github.com/qdm12/gluetun/wiki/OpenVPN-options"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: "What's the issue?"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
@@ -27,6 +27,9 @@
|
|||||||
- name: ":cloud: IVPN"
|
- name: ":cloud: IVPN"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
|
- name: ":cloud: ExpressVPN"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
- name: ":cloud: FastestVPN"
|
- name: ":cloud: FastestVPN"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
@@ -62,6 +65,9 @@
|
|||||||
- name: ":cloud: Vyprvpn"
|
- name: ":cloud: Vyprvpn"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
|
- name: ":cloud: WeVPN"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
- name: ":cloud: Windscribe"
|
- name: ":cloud: Windscribe"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
|
|||||||
+12
-13
@@ -1,6 +1,4 @@
|
|||||||
linters-settings:
|
linters-settings:
|
||||||
maligned:
|
|
||||||
suggest-new: true
|
|
||||||
misspell:
|
misspell:
|
||||||
locale: US
|
locale: US
|
||||||
|
|
||||||
@@ -30,16 +28,19 @@ issues:
|
|||||||
linters:
|
linters:
|
||||||
- gomnd
|
- gomnd
|
||||||
linters:
|
linters:
|
||||||
disable-all: true
|
|
||||||
enable:
|
enable:
|
||||||
|
# - cyclop
|
||||||
|
# - errorlint
|
||||||
|
# - wrapcheck
|
||||||
- asciicheck
|
- asciicheck
|
||||||
- bodyclose
|
- bodyclose
|
||||||
- deadcode
|
|
||||||
- dogsled
|
- dogsled
|
||||||
- dupl
|
- dupl
|
||||||
- errcheck
|
- durationcheck
|
||||||
|
- errname
|
||||||
- exhaustive
|
- exhaustive
|
||||||
- exportloopref
|
- exportloopref
|
||||||
|
- forcetypeassert
|
||||||
- gci
|
- gci
|
||||||
- gochecknoglobals
|
- gochecknoglobals
|
||||||
- gochecknoinits
|
- gochecknoinits
|
||||||
@@ -52,13 +53,13 @@ linters:
|
|||||||
- goheader
|
- goheader
|
||||||
- goimports
|
- goimports
|
||||||
- gomnd
|
- gomnd
|
||||||
|
- gomoddirectives
|
||||||
- goprintffuncname
|
- goprintffuncname
|
||||||
- gosec
|
- gosec
|
||||||
- gosimple
|
|
||||||
- govet
|
|
||||||
- importas
|
- importas
|
||||||
- ineffassign
|
- importas
|
||||||
- lll
|
- lll
|
||||||
|
- makezero
|
||||||
- misspell
|
- misspell
|
||||||
- nakedret
|
- nakedret
|
||||||
- nestif
|
- nestif
|
||||||
@@ -67,18 +68,16 @@ linters:
|
|||||||
- nolintlint
|
- nolintlint
|
||||||
- prealloc
|
- prealloc
|
||||||
- predeclared
|
- predeclared
|
||||||
|
- predeclared
|
||||||
|
- promlinter
|
||||||
- revive
|
- revive
|
||||||
- rowserrcheck
|
- rowserrcheck
|
||||||
- sqlclosecheck
|
- sqlclosecheck
|
||||||
- staticcheck
|
|
||||||
- structcheck
|
|
||||||
- thelper
|
- thelper
|
||||||
- tparallel
|
- tparallel
|
||||||
- typecheck
|
|
||||||
- unconvert
|
- unconvert
|
||||||
- unparam
|
- unparam
|
||||||
- unused
|
- wastedassign
|
||||||
- varcheck
|
|
||||||
- whitespace
|
- whitespace
|
||||||
|
|
||||||
run:
|
run:
|
||||||
|
|||||||
+44
-34
@@ -2,7 +2,7 @@ ARG ALPINE_VERSION=3.14
|
|||||||
ARG GO_ALPINE_VERSION=3.14
|
ARG GO_ALPINE_VERSION=3.14
|
||||||
ARG GO_VERSION=1.17
|
ARG GO_VERSION=1.17
|
||||||
ARG XCPUTRANSLATE_VERSION=v0.6.0
|
ARG XCPUTRANSLATE_VERSION=v0.6.0
|
||||||
ARG GOLANGCI_LINT_VERSION=v1.41.1
|
ARG GOLANGCI_LINT_VERSION=v1.42.1
|
||||||
ARG BUILDPLATFORM=linux/amd64
|
ARG BUILDPLATFORM=linux/amd64
|
||||||
|
|
||||||
FROM --platform=${BUILDPLATFORM} qmcgaw/xcputranslate:${XCPUTRANSLATE_VERSION} AS xcputranslate
|
FROM --platform=${BUILDPLATFORM} qmcgaw/xcputranslate:${XCPUTRANSLATE_VERSION} AS xcputranslate
|
||||||
@@ -67,61 +67,68 @@ LABEL \
|
|||||||
org.opencontainers.image.title="VPN swiss-knife like client for multiple VPN providers" \
|
org.opencontainers.image.title="VPN swiss-knife like client for multiple VPN providers" \
|
||||||
org.opencontainers.image.description="VPN swiss-knife like client to tunnel to multiple VPN servers using OpenVPN, IPtables, DNS over TLS, Shadowsocks, an HTTP proxy and Alpine Linux"
|
org.opencontainers.image.description="VPN swiss-knife like client to tunnel to multiple VPN servers using OpenVPN, IPtables, DNS over TLS, Shadowsocks, an HTTP proxy and Alpine Linux"
|
||||||
ENV VPNSP=pia \
|
ENV VPNSP=pia \
|
||||||
VERSION_INFORMATION=on \
|
|
||||||
LOG_LEVEL=info \
|
|
||||||
VPN_TYPE=openvpn \
|
VPN_TYPE=openvpn \
|
||||||
PROTOCOL=udp \
|
# OpenVPN
|
||||||
|
OPENVPN_PROTOCOL=udp \
|
||||||
|
OPENVPN_USER= \
|
||||||
|
OPENVPN_PASSWORD= \
|
||||||
|
OPENVPN_USER_SECRETFILE=/run/secrets/openvpn_user \
|
||||||
|
OPENVPN_PASSWORD_SECRETFILE=/run/secrets/openvpn_password \
|
||||||
OPENVPN_VERSION=2.5 \
|
OPENVPN_VERSION=2.5 \
|
||||||
OPENVPN_VERBOSITY=1 \
|
OPENVPN_VERBOSITY=1 \
|
||||||
OPENVPN_FLAGS= \
|
OPENVPN_FLAGS= \
|
||||||
|
OPENVPN_CIPHER= \
|
||||||
|
OPENVPN_AUTH= \
|
||||||
OPENVPN_ROOT=yes \
|
OPENVPN_ROOT=yes \
|
||||||
OPENVPN_TARGET_IP= \
|
OPENVPN_TARGET_IP= \
|
||||||
OPENVPN_IPV6=off \
|
OPENVPN_IPV6=off \
|
||||||
OPENVPN_CUSTOM_CONFIG= \
|
OPENVPN_CUSTOM_CONFIG= \
|
||||||
OPENVPN_INTERFACE=tun0 \
|
OPENVPN_INTERFACE=tun0 \
|
||||||
|
OPENVPN_PORT= \
|
||||||
|
# Wireguard
|
||||||
WIREGUARD_PRIVATE_KEY= \
|
WIREGUARD_PRIVATE_KEY= \
|
||||||
WIREGUARD_PRESHARED_KEY= \
|
WIREGUARD_PRESHARED_KEY= \
|
||||||
|
WIREGUARD_PUBLIC_KEY= \
|
||||||
WIREGUARD_ADDRESS= \
|
WIREGUARD_ADDRESS= \
|
||||||
WIREGUARD_PORT= \
|
WIREGUARD_ENDPOINT_IP= \
|
||||||
|
WIREGUARD_ENDPOINT_PORT= \
|
||||||
WIREGUARD_INTERFACE=wg0 \
|
WIREGUARD_INTERFACE=wg0 \
|
||||||
TZ= \
|
# VPN server filtering
|
||||||
PUID= \
|
|
||||||
PGID= \
|
|
||||||
PUBLICIP_FILE="/tmp/gluetun/ip" \
|
|
||||||
# VPN provider settings
|
|
||||||
OPENVPN_USER= \
|
|
||||||
OPENVPN_PASSWORD= \
|
|
||||||
USER_SECRETFILE=/run/secrets/openvpn_user \
|
|
||||||
PASSWORD_SECRETFILE=/run/secrets/openvpn_password \
|
|
||||||
REGION= \
|
REGION= \
|
||||||
COUNTRY= \
|
COUNTRY= \
|
||||||
CITY= \
|
CITY= \
|
||||||
PORT= \
|
|
||||||
SERVER_HOSTNAME= \
|
SERVER_HOSTNAME= \
|
||||||
# Mullvad only:
|
# # Mullvad only:
|
||||||
ISP= \
|
ISP= \
|
||||||
OWNED=no \
|
OWNED=no \
|
||||||
# Private Internet Access only:
|
# # Private Internet Access only:
|
||||||
PIA_ENCRYPTION=strong \
|
PIA_ENCRYPTION=strong \
|
||||||
PORT_FORWARDING=off \
|
PORT_FORWARDING=off \
|
||||||
PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \
|
PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \
|
||||||
# Cyberghost only:
|
# # Cyberghost only:
|
||||||
CYBERGHOST_GROUP="Premium UDP Europe" \
|
|
||||||
OPENVPN_CLIENTCRT_SECRETFILE=/run/secrets/openvpn_clientcrt \
|
OPENVPN_CLIENTCRT_SECRETFILE=/run/secrets/openvpn_clientcrt \
|
||||||
OPENVPN_CLIENTKEY_SECRETFILE=/run/secrets/openvpn_clientkey \
|
OPENVPN_CLIENTKEY_SECRETFILE=/run/secrets/openvpn_clientkey \
|
||||||
# Nordvpn only:
|
# # Nordvpn only:
|
||||||
SERVER_NUMBER= \
|
SERVER_NUMBER= \
|
||||||
# NordVPN and ProtonVPN only:
|
# # PIA and ProtonVPN only:
|
||||||
SERVER_NAME= \
|
SERVER_NAME= \
|
||||||
# ProtonVPN only:
|
# # ProtonVPN only:
|
||||||
FREE_ONLY= \
|
FREE_ONLY= \
|
||||||
# Openvpn
|
# # Surfshark only:
|
||||||
OPENVPN_CIPHER= \
|
MULTIHOP_ONLY= \
|
||||||
OPENVPN_AUTH= \
|
# Firewall
|
||||||
|
FIREWALL=on \
|
||||||
|
FIREWALL_VPN_INPUT_PORTS= \
|
||||||
|
FIREWALL_INPUT_PORTS= \
|
||||||
|
FIREWALL_OUTBOUND_SUBNETS= \
|
||||||
|
FIREWALL_DEBUG=off \
|
||||||
|
# Logging
|
||||||
|
LOG_LEVEL=info \
|
||||||
# Health
|
# Health
|
||||||
HEALTH_OPENVPN_DURATION_INITIAL=6s \
|
|
||||||
HEALTH_OPENVPN_DURATION_ADDITION=5s \
|
|
||||||
HEALTH_SERVER_ADDRESS=127.0.0.1:9999 \
|
HEALTH_SERVER_ADDRESS=127.0.0.1:9999 \
|
||||||
|
HEALTH_ADDRESS_TO_PING=github.com \
|
||||||
|
HEALTH_VPN_DURATION_INITIAL=6s \
|
||||||
|
HEALTH_VPN_DURATION_ADDITION=5s \
|
||||||
# DNS over TLS
|
# DNS over TLS
|
||||||
DOT=on \
|
DOT=on \
|
||||||
DOT_PROVIDERS=cloudflare \
|
DOT_PROVIDERS=cloudflare \
|
||||||
@@ -138,12 +145,6 @@ ENV VPNSP=pia \
|
|||||||
DNS_UPDATE_PERIOD=24h \
|
DNS_UPDATE_PERIOD=24h \
|
||||||
DNS_PLAINTEXT_ADDRESS=1.1.1.1 \
|
DNS_PLAINTEXT_ADDRESS=1.1.1.1 \
|
||||||
DNS_KEEP_NAMESERVER=off \
|
DNS_KEEP_NAMESERVER=off \
|
||||||
# Firewall
|
|
||||||
FIREWALL=on \
|
|
||||||
FIREWALL_VPN_INPUT_PORTS= \
|
|
||||||
FIREWALL_INPUT_PORTS= \
|
|
||||||
FIREWALL_OUTBOUND_SUBNETS= \
|
|
||||||
FIREWALL_DEBUG=off \
|
|
||||||
# HTTP proxy
|
# HTTP proxy
|
||||||
HTTPPROXY= \
|
HTTPPROXY= \
|
||||||
HTTPPROXY_LOG=off \
|
HTTPPROXY_LOG=off \
|
||||||
@@ -159,7 +160,16 @@ ENV VPNSP=pia \
|
|||||||
SHADOWSOCKS_PASSWORD= \
|
SHADOWSOCKS_PASSWORD= \
|
||||||
SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password \
|
SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password \
|
||||||
SHADOWSOCKS_CIPHER=chacha20-ietf-poly1305 \
|
SHADOWSOCKS_CIPHER=chacha20-ietf-poly1305 \
|
||||||
UPDATER_PERIOD=0
|
# Server data updater
|
||||||
|
UPDATER_PERIOD=0 \
|
||||||
|
# Public IP
|
||||||
|
PUBLICIP_FILE="/tmp/gluetun/ip" \
|
||||||
|
PUBLICIP_PERIOD=12h \
|
||||||
|
# Extras
|
||||||
|
VERSION_INFORMATION=on \
|
||||||
|
TZ= \
|
||||||
|
PUID= \
|
||||||
|
PGID=
|
||||||
ENTRYPOINT ["/entrypoint"]
|
ENTRYPOINT ["/entrypoint"]
|
||||||
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
|
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
|
||||||
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /entrypoint healthcheck
|
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /entrypoint healthcheck
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# Gluetun VPN client
|
# Gluetun VPN client
|
||||||
|
|
||||||
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, FastestVPN,
|
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, ExpressVPN, FastestVPN,
|
||||||
HideMyAss, IPVanish, IVPN, Mullvad, NordVPN, Privado, Private Internet Access, PrivateVPN,
|
HideMyAss, IPVanish, IVPN, Mullvad, NordVPN, Privado, Private Internet Access, PrivateVPN,
|
||||||
ProtonVPN, PureVPN, Surfshark, TorGuard, VPNUnlimited, VyprVPN and Windscribe VPN servers
|
ProtonVPN, PureVPN, Surfshark, TorGuard, VPNUnlimited, VyprVPN, WeVPN and Windscribe VPN servers
|
||||||
using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
using Go, OpenVPN or Wireguard, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
||||||
|
|
||||||
**ANNOUNCEMENT**: Wireguard is now supported for all providers supporting it!
|
**ANNOUNCEMENT**: Wireguard is now supported for all providers supporting it!
|
||||||
|
|
||||||
@@ -40,10 +40,14 @@ using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
|||||||
|
|
||||||
## Quick links
|
## Quick links
|
||||||
|
|
||||||
- Problem or suggestion?
|
- [Setup](#Setup)
|
||||||
- [Start a discussion](https://github.com/qdm12/gluetun/discussions)
|
- [Features](#Features)
|
||||||
- [Create an issue](https://github.com/qdm12/gluetun/issues)
|
- Problem?
|
||||||
- [Check the Wiki](https://github.com/qdm12/gluetun/wiki)
|
- [Check the Wiki](https://github.com/qdm12/gluetun/wiki)
|
||||||
|
- [Start a discussion](https://github.com/qdm12/gluetun/discussions)
|
||||||
|
- [Fix the Unraid template](https://github.com/qdm12/gluetun/discussions/550)
|
||||||
|
- Suggestion?
|
||||||
|
- [Create an issue](https://github.com/qdm12/gluetun/issues)
|
||||||
- [Join the Slack channel](https://join.slack.com/t/qdm12/shared_invite/enQtOTE0NjcxNTM1ODc5LTYyZmVlOTM3MGI4ZWU0YmJkMjUxNmQ4ODQ2OTAwYzMxMTlhY2Q1MWQyOWUyNjc2ODliNjFjMDUxNWNmNzk5MDk)
|
- [Join the Slack channel](https://join.slack.com/t/qdm12/shared_invite/enQtOTE0NjcxNTM1ODc5LTYyZmVlOTM3MGI4ZWU0YmJkMjUxNmQ4ODQ2OTAwYzMxMTlhY2Q1MWQyOWUyNjc2ODliNjFjMDUxNWNmNzk5MDk)
|
||||||
- Happy?
|
- Happy?
|
||||||
- Sponsor me on [github.com/sponsors/qdm12](https://github.com/sponsors/qdm12)
|
- Sponsor me on [github.com/sponsors/qdm12](https://github.com/sponsors/qdm12)
|
||||||
@@ -53,69 +57,68 @@ using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
|||||||
|
|
||||||
[](https://youtu.be/0F6I03LQcI4)
|
[](https://youtu.be/0F6I03LQcI4)
|
||||||
|
|
||||||
|
- [Substack Console interview](https://console.substack.com/p/console-72)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Based on Alpine 3.14 for a small Docker image of 31MB
|
- Based on Alpine 3.14 for a small Docker image of 31MB
|
||||||
- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** servers
|
- Supports: **Cyberghost**, **ExpressVPN**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **WeVPN**, **Windscribe** servers
|
||||||
- Supports OpenVPN
|
- Supports OpenVPN for all providers listed
|
||||||
- Supports Wireguard for **Mullvad**, **Ivpn** and **Windscribe** (more in progress, see #134)
|
- Supports Wireguard
|
||||||
|
- For **Mullvad**, **Ivpn** and **Windscribe**
|
||||||
|
- For **Torguard**, **VPN Unlimited** and **WeVPN** using [the custom provider](https://github.com/qdm12/gluetun/wiki/Environment-variables#custom)
|
||||||
|
- For custom Wireguard configurations using [the custom provider](https://github.com/qdm12/gluetun/wiki/Environment-variables#custom)
|
||||||
|
- More in progress, see [#134](https://github.com/qdm12/gluetun/issues/134)
|
||||||
- DNS over TLS baked in with service provider(s) of your choice
|
- DNS over TLS baked in with service provider(s) of your choice
|
||||||
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
|
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
|
||||||
- Choose the vpn network protocol, `udp` or `tcp`
|
- Choose the vpn network protocol, `udp` or `tcp`
|
||||||
- Built in firewall kill switch to allow traffic only with needed the VPN servers and LAN devices
|
- Built in firewall kill switch to allow traffic only with needed the VPN servers and LAN devices
|
||||||
- Built in Shadowsocks proxy (protocol based on SOCKS5 with an encryption layer, tunnels TCP+UDP)
|
- Built in Shadowsocks proxy (protocol based on SOCKS5 with an encryption layer, tunnels TCP+UDP)
|
||||||
- Built in HTTP proxy (tunnels HTTP and HTTPS through TCP)
|
- Built in HTTP proxy (tunnels HTTP and HTTPS through TCP)
|
||||||
- [Connect other containers to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
|
- [Connect other containers to it](https://github.com/qdm12/gluetun/wiki/Connect-a-container-to-gluetun)
|
||||||
- [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
|
- [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-a-LAN-device-to-gluetun)
|
||||||
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even ppc64le 🎆
|
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even ppc64le 🎆
|
||||||
- VPN server side port forwarding for Private Internet Access and Vyprvpn
|
- [Custom VPN server side port forwarding for Private Internet Access](https://github.com/qdm12/gluetun/wiki/Private-internet-access#vpn-server-port-forwarding)
|
||||||
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
|
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
|
||||||
- Subprograms all drop root privileges once launched
|
- Unbound subprogram drops root privileges once launched
|
||||||
- Subprograms output streams are all merged together
|
|
||||||
- Can work as a Kubernetes sidecar container, thanks @rorph
|
- Can work as a Kubernetes sidecar container, thanks @rorph
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. Ensure your `tun` kernel module is setup:
|
🎉 There are now instructions specific to each VPN provider with examples to help you get started as quickly as possible!
|
||||||
|
|
||||||
```sh
|
Go to the [Wiki](https://github.com/qdm12/gluetun/wiki)!
|
||||||
sudo modprobe tun
|
|
||||||
# or, if you don't have modprobe, with
|
|
||||||
sudo insmod /lib/modules/tun.ko
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Extra steps:
|
[🐛 Found a bug in the Wiki?!](https://github.com/qdm12/gluetun/issues/new?assignees=&labels=%F0%9F%93%84+Wiki+issue&template=wiki+issue.md&title=Wiki+issue%3A+)
|
||||||
- [For Synology users](https://github.com/qdm12/gluetun/wiki/Synology-setup)
|
|
||||||
- [For 32 bit Operating systems (**Rasberry Pis**)](https://github.com/qdm12/gluetun/wiki/32-bit-setup)
|
|
||||||
1. Launch the container with:
|
|
||||||
|
|
||||||
```bash
|
Here's a docker-compose.yml for the laziest:
|
||||||
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:
|
```yml
|
||||||
|
version: "3"
|
||||||
```bash
|
services:
|
||||||
docker-compose up -d
|
gluetun:
|
||||||
```
|
image: qmcgaw/gluetun
|
||||||
|
cap_add:
|
||||||
You should probably check the many [environment variables](https://github.com/qdm12/gluetun/wiki/Environment-variables) available to adapt the container to your needs.
|
- NET_ADMIN
|
||||||
|
ports:
|
||||||
## Further setup
|
- 8888:8888/tcp # HTTP proxy
|
||||||
|
- 8388:8388/tcp # Shadowsocks
|
||||||
The following points are all optional but should give you insights on all the possibilities with this container.
|
- 8388:8388/udp # Shadowsocks
|
||||||
|
volumes:
|
||||||
- [Test your setup](https://github.com/qdm12/gluetun/wiki/Test-your-setup)
|
- /yourpath:/gluetun
|
||||||
- [How to connect other containers and devices to Gluetun](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
|
environment:
|
||||||
- [How to use Wireguard](https://github.com/qdm12/gluetun/wiki/Wireguard)
|
# See https://github.com/qdm12/gluetun/wiki
|
||||||
- [VPN server side port forwarding](https://github.com/qdm12/gluetun/wiki/Port-forwarding)
|
- VPNSP=ivpn
|
||||||
- [HTTP control server](https://github.com/qdm12/gluetun/wiki/HTTP-Control-server) to automate things, restart Openvpn etc.
|
- VPN_TYPE=openvpn
|
||||||
- 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.
|
# OpenVPN:
|
||||||
- Use [Docker secrets](https://github.com/qdm12/gluetun/wiki/Docker-secrets) to read your credentials instead of environment variables
|
- OPENVPN_USER=
|
||||||
|
- OPENVPN_PASSWORD=
|
||||||
|
# Wireguard:
|
||||||
|
# - WIREGUARD_PRIVATE_KEY=wOEI9rqqbDwnN8/Bpp22sVz48T71vJ4fYmFWujulwUU=
|
||||||
|
# - WIREGUARD_ADDRESS=10.64.222.21/32
|
||||||
|
# Timezone for accurate log times
|
||||||
|
- TZ=
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
+36
-37
@@ -11,7 +11,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
_ "time/tzdata"
|
||||||
|
|
||||||
|
_ "github.com/breml/rootcerts"
|
||||||
"github.com/qdm12/dns/pkg/unbound"
|
"github.com/qdm12/dns/pkg/unbound"
|
||||||
"github.com/qdm12/gluetun/internal/alpine"
|
"github.com/qdm12/gluetun/internal/alpine"
|
||||||
"github.com/qdm12/gluetun/internal/cli"
|
"github.com/qdm12/gluetun/internal/cli"
|
||||||
@@ -37,6 +39,9 @@ import (
|
|||||||
"github.com/qdm12/golibs/logging"
|
"github.com/qdm12/golibs/logging"
|
||||||
"github.com/qdm12/golibs/params"
|
"github.com/qdm12/golibs/params"
|
||||||
"github.com/qdm12/goshutdown"
|
"github.com/qdm12/goshutdown"
|
||||||
|
"github.com/qdm12/goshutdown/goroutine"
|
||||||
|
"github.com/qdm12/goshutdown/group"
|
||||||
|
"github.com/qdm12/goshutdown/order"
|
||||||
"github.com/qdm12/gosplash"
|
"github.com/qdm12/gosplash"
|
||||||
"github.com/qdm12/updated/pkg/dnscrypto"
|
"github.com/qdm12/updated/pkg/dnscrypto"
|
||||||
)
|
)
|
||||||
@@ -130,6 +135,8 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
return cli.OpenvpnConfig(logger, env)
|
return cli.OpenvpnConfig(logger, env)
|
||||||
case "update":
|
case "update":
|
||||||
return cli.Update(ctx, args[2:], logger)
|
return cli.Update(ctx, args[2:], logger)
|
||||||
|
case "format-servers":
|
||||||
|
return cli.FormatServers(args[2:])
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("%w: %s", errCommandUnknown, args[1])
|
return fmt.Errorf("%w: %s", errCommandUnknown, args[1])
|
||||||
}
|
}
|
||||||
@@ -292,14 +299,6 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, vpnPort := range allSettings.Firewall.VPNInputPorts {
|
|
||||||
vpnIntf := allSettings.VPN.VPNInterface()
|
|
||||||
err = firewallConf.SetAllowedPort(ctx, vpnPort, vpnIntf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, port := range allSettings.Firewall.InputPorts {
|
for _, port := range allSettings.Firewall.InputPorts {
|
||||||
err = firewallConf.SetAllowedPort(ctx, port, defaultInterface)
|
err = firewallConf.SetAllowedPort(ctx, port, defaultInterface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -308,6 +307,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
} // TODO move inside firewall?
|
} // TODO move inside firewall?
|
||||||
|
|
||||||
// Shutdown settings
|
// Shutdown settings
|
||||||
|
const totalShutdownTimeout = 3 * time.Second
|
||||||
const defaultShutdownTimeout = 400 * time.Millisecond
|
const defaultShutdownTimeout = 400 * time.Millisecond
|
||||||
defaultShutdownOnSuccess := func(goRoutineName string) {
|
defaultShutdownOnSuccess := func(goRoutineName string) {
|
||||||
logger.Info(goRoutineName + ": terminated ✔️")
|
logger.Info(goRoutineName + ": terminated ✔️")
|
||||||
@@ -315,34 +315,32 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
defaultShutdownOnFailure := func(goRoutineName string, err error) {
|
defaultShutdownOnFailure := func(goRoutineName string, err error) {
|
||||||
logger.Warn(goRoutineName + ": " + err.Error() + " ⚠️")
|
logger.Warn(goRoutineName + ": " + err.Error() + " ⚠️")
|
||||||
}
|
}
|
||||||
defaultGoRoutineSettings := goshutdown.GoRoutineSettings{Timeout: defaultShutdownTimeout}
|
defaultGroupOptions := []group.Option{
|
||||||
defaultGroupSettings := goshutdown.GroupSettings{
|
group.OptionTimeout(defaultShutdownTimeout),
|
||||||
Timeout: defaultShutdownTimeout,
|
group.OptionOnSuccess(defaultShutdownOnSuccess)}
|
||||||
OnSuccess: defaultShutdownOnSuccess,
|
|
||||||
}
|
|
||||||
|
|
||||||
controlGroupHandler := goshutdown.NewGroupHandler("control", defaultGroupSettings)
|
controlGroupHandler := goshutdown.NewGroupHandler("control", defaultGroupOptions...)
|
||||||
tickersGroupHandler := goshutdown.NewGroupHandler("tickers", defaultGroupSettings)
|
tickersGroupHandler := goshutdown.NewGroupHandler("tickers", defaultGroupOptions...)
|
||||||
otherGroupHandler := goshutdown.NewGroupHandler("other", defaultGroupSettings)
|
otherGroupHandler := goshutdown.NewGroupHandler("other", defaultGroupOptions...)
|
||||||
|
|
||||||
portForwardLogger := logger.NewChild(logging.Settings{Prefix: "port forwarding: "})
|
portForwardLogger := logger.NewChild(logging.Settings{Prefix: "port forwarding: "})
|
||||||
portForwardLooper := portforward.NewLoop(allSettings.VPN.Provider.PortForwarding,
|
portForwardLooper := portforward.NewLoop(allSettings.VPN.Provider.PortForwarding,
|
||||||
httpClient, firewallConf, portForwardLogger)
|
httpClient, firewallConf, portForwardLogger)
|
||||||
portForwardHandler, portForwardCtx, portForwardDone := goshutdown.NewGoRoutineHandler(
|
portForwardHandler, portForwardCtx, portForwardDone := goshutdown.NewGoRoutineHandler(
|
||||||
"port forwarding", goshutdown.GoRoutineSettings{Timeout: time.Second})
|
"port forwarding", goroutine.OptionTimeout(time.Second))
|
||||||
go portForwardLooper.Run(portForwardCtx, portForwardDone)
|
go portForwardLooper.Run(portForwardCtx, portForwardDone)
|
||||||
|
|
||||||
unboundLogger := logger.NewChild(logging.Settings{Prefix: "dns over tls: "})
|
unboundLogger := logger.NewChild(logging.Settings{Prefix: "dns over tls: "})
|
||||||
unboundLooper := dns.NewLoop(dnsConf, allSettings.DNS, httpClient,
|
unboundLooper := dns.NewLoop(dnsConf, allSettings.DNS, httpClient,
|
||||||
unboundLogger)
|
unboundLogger)
|
||||||
dnsHandler, dnsCtx, dnsDone := goshutdown.NewGoRoutineHandler(
|
dnsHandler, dnsCtx, dnsDone := goshutdown.NewGoRoutineHandler(
|
||||||
"unbound", defaultGoRoutineSettings)
|
"unbound", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
// wait for unboundLooper.Restart or its ticker launched with RunRestartTicker
|
// wait for unboundLooper.Restart or its ticker launched with RunRestartTicker
|
||||||
go unboundLooper.Run(dnsCtx, dnsDone)
|
go unboundLooper.Run(dnsCtx, dnsDone)
|
||||||
otherGroupHandler.Add(dnsHandler)
|
otherGroupHandler.Add(dnsHandler)
|
||||||
|
|
||||||
dnsTickerHandler, dnsTickerCtx, dnsTickerDone := goshutdown.NewGoRoutineHandler(
|
dnsTickerHandler, dnsTickerCtx, dnsTickerDone := goshutdown.NewGoRoutineHandler(
|
||||||
"dns ticker", defaultGoRoutineSettings)
|
"dns ticker", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
go unboundLooper.RunRestartTicker(dnsTickerCtx, dnsTickerDone)
|
go unboundLooper.RunRestartTicker(dnsTickerCtx, dnsTickerDone)
|
||||||
controlGroupHandler.Add(dnsTickerHandler)
|
controlGroupHandler.Add(dnsTickerHandler)
|
||||||
|
|
||||||
@@ -350,35 +348,35 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
logger.NewChild(logging.Settings{Prefix: "ip getter: "}),
|
logger.NewChild(logging.Settings{Prefix: "ip getter: "}),
|
||||||
allSettings.PublicIP, puid, pgid)
|
allSettings.PublicIP, puid, pgid)
|
||||||
pubIPHandler, pubIPCtx, pubIPDone := goshutdown.NewGoRoutineHandler(
|
pubIPHandler, pubIPCtx, pubIPDone := goshutdown.NewGoRoutineHandler(
|
||||||
"public IP", defaultGoRoutineSettings)
|
"public IP", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
go publicIPLooper.Run(pubIPCtx, pubIPDone)
|
go publicIPLooper.Run(pubIPCtx, pubIPDone)
|
||||||
otherGroupHandler.Add(pubIPHandler)
|
otherGroupHandler.Add(pubIPHandler)
|
||||||
|
|
||||||
pubIPTickerHandler, pubIPTickerCtx, pubIPTickerDone := goshutdown.NewGoRoutineHandler(
|
pubIPTickerHandler, pubIPTickerCtx, pubIPTickerDone := goshutdown.NewGoRoutineHandler(
|
||||||
"public IP", defaultGoRoutineSettings)
|
"public IP", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
go publicIPLooper.RunRestartTicker(pubIPTickerCtx, pubIPTickerDone)
|
go publicIPLooper.RunRestartTicker(pubIPTickerCtx, pubIPTickerDone)
|
||||||
tickersGroupHandler.Add(pubIPTickerHandler)
|
tickersGroupHandler.Add(pubIPTickerHandler)
|
||||||
|
|
||||||
vpnLogger := logger.NewChild(logging.Settings{Prefix: "vpn: "})
|
vpnLogger := logger.NewChild(logging.Settings{Prefix: "vpn: "})
|
||||||
vpnLooper := vpn.NewLoop(allSettings.VPN,
|
vpnLooper := vpn.NewLoop(allSettings.VPN, allSettings.Firewall.VPNInputPorts,
|
||||||
allServers, ovpnConf, netLinker, firewallConf, routingConf, portForwardLooper,
|
allServers, ovpnConf, netLinker, firewallConf, routingConf, portForwardLooper,
|
||||||
cmder, publicIPLooper, unboundLooper, vpnLogger, httpClient,
|
cmder, publicIPLooper, unboundLooper, vpnLogger, httpClient,
|
||||||
buildInfo, allSettings.VersionInformation)
|
buildInfo, allSettings.VersionInformation)
|
||||||
vpnHandler, vpnCtx, vpnDone := goshutdown.NewGoRoutineHandler(
|
vpnHandler, vpnCtx, vpnDone := goshutdown.NewGoRoutineHandler(
|
||||||
"vpn", goshutdown.GoRoutineSettings{Timeout: time.Second})
|
"vpn", goroutine.OptionTimeout(time.Second))
|
||||||
go vpnLooper.Run(vpnCtx, vpnDone)
|
go vpnLooper.Run(vpnCtx, vpnDone)
|
||||||
|
|
||||||
updaterLooper := updater.NewLooper(allSettings.Updater,
|
updaterLooper := updater.NewLooper(allSettings.Updater,
|
||||||
allServers, storage, vpnLooper.SetServers, httpClient,
|
allServers, storage, vpnLooper.SetServers, httpClient,
|
||||||
logger.NewChild(logging.Settings{Prefix: "updater: "}))
|
logger.NewChild(logging.Settings{Prefix: "updater: "}))
|
||||||
updaterHandler, updaterCtx, updaterDone := goshutdown.NewGoRoutineHandler(
|
updaterHandler, updaterCtx, updaterDone := goshutdown.NewGoRoutineHandler(
|
||||||
"updater", defaultGoRoutineSettings)
|
"updater", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
// wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker
|
// wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker
|
||||||
go updaterLooper.Run(updaterCtx, updaterDone)
|
go updaterLooper.Run(updaterCtx, updaterDone)
|
||||||
tickersGroupHandler.Add(updaterHandler)
|
tickersGroupHandler.Add(updaterHandler)
|
||||||
|
|
||||||
updaterTickerHandler, updaterTickerCtx, updaterTickerDone := goshutdown.NewGoRoutineHandler(
|
updaterTickerHandler, updaterTickerCtx, updaterTickerDone := goshutdown.NewGoRoutineHandler(
|
||||||
"updater ticker", defaultGoRoutineSettings)
|
"updater ticker", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
go updaterLooper.RunRestartTicker(updaterTickerCtx, updaterTickerDone)
|
go updaterLooper.RunRestartTicker(updaterTickerCtx, updaterTickerDone)
|
||||||
controlGroupHandler.Add(updaterTickerHandler)
|
controlGroupHandler.Add(updaterTickerHandler)
|
||||||
|
|
||||||
@@ -386,21 +384,21 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
logger.NewChild(logging.Settings{Prefix: "http proxy: "}),
|
logger.NewChild(logging.Settings{Prefix: "http proxy: "}),
|
||||||
allSettings.HTTPProxy)
|
allSettings.HTTPProxy)
|
||||||
httpProxyHandler, httpProxyCtx, httpProxyDone := goshutdown.NewGoRoutineHandler(
|
httpProxyHandler, httpProxyCtx, httpProxyDone := goshutdown.NewGoRoutineHandler(
|
||||||
"http proxy", defaultGoRoutineSettings)
|
"http proxy", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
go httpProxyLooper.Run(httpProxyCtx, httpProxyDone)
|
go httpProxyLooper.Run(httpProxyCtx, httpProxyDone)
|
||||||
otherGroupHandler.Add(httpProxyHandler)
|
otherGroupHandler.Add(httpProxyHandler)
|
||||||
|
|
||||||
shadowsocksLooper := shadowsocks.NewLooper(allSettings.ShadowSocks,
|
shadowsocksLooper := shadowsocks.NewLooper(allSettings.ShadowSocks,
|
||||||
logger.NewChild(logging.Settings{Prefix: "shadowsocks: "}))
|
logger.NewChild(logging.Settings{Prefix: "shadowsocks: "}))
|
||||||
shadowsocksHandler, shadowsocksCtx, shadowsocksDone := goshutdown.NewGoRoutineHandler(
|
shadowsocksHandler, shadowsocksCtx, shadowsocksDone := goshutdown.NewGoRoutineHandler(
|
||||||
"shadowsocks proxy", defaultGoRoutineSettings)
|
"shadowsocks proxy", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
go shadowsocksLooper.Run(shadowsocksCtx, shadowsocksDone)
|
go shadowsocksLooper.Run(shadowsocksCtx, shadowsocksDone)
|
||||||
otherGroupHandler.Add(shadowsocksHandler)
|
otherGroupHandler.Add(shadowsocksHandler)
|
||||||
|
|
||||||
controlServerAddress := ":" + strconv.Itoa(int(allSettings.ControlServer.Port))
|
controlServerAddress := ":" + strconv.Itoa(int(allSettings.ControlServer.Port))
|
||||||
controlServerLogging := allSettings.ControlServer.Log
|
controlServerLogging := allSettings.ControlServer.Log
|
||||||
httpServerHandler, httpServerCtx, httpServerDone := goshutdown.NewGoRoutineHandler(
|
httpServerHandler, httpServerCtx, httpServerDone := goshutdown.NewGoRoutineHandler(
|
||||||
"http server", defaultGoRoutineSettings)
|
"http server", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
httpServer := server.New(httpServerCtx, controlServerAddress, controlServerLogging,
|
httpServer := server.New(httpServerCtx, controlServerAddress, controlServerLogging,
|
||||||
logger.NewChild(logging.Settings{Prefix: "http server: "}),
|
logger.NewChild(logging.Settings{Prefix: "http server: "}),
|
||||||
buildInfo, vpnLooper, portForwardLooper, unboundLooper, updaterLooper, publicIPLooper)
|
buildInfo, vpnLooper, portForwardLooper, unboundLooper, updaterLooper, publicIPLooper)
|
||||||
@@ -410,16 +408,13 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
healthLogger := logger.NewChild(logging.Settings{Prefix: "healthcheck: "})
|
healthLogger := logger.NewChild(logging.Settings{Prefix: "healthcheck: "})
|
||||||
healthcheckServer := healthcheck.NewServer(allSettings.Health, healthLogger, vpnLooper)
|
healthcheckServer := healthcheck.NewServer(allSettings.Health, healthLogger, vpnLooper)
|
||||||
healthServerHandler, healthServerCtx, healthServerDone := goshutdown.NewGoRoutineHandler(
|
healthServerHandler, healthServerCtx, healthServerDone := goshutdown.NewGoRoutineHandler(
|
||||||
"HTTP health server", defaultGoRoutineSettings)
|
"HTTP health server", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
go healthcheckServer.Run(healthServerCtx, healthServerDone)
|
go healthcheckServer.Run(healthServerCtx, healthServerDone)
|
||||||
|
|
||||||
const orderShutdownTimeout = 3 * time.Second
|
orderHandler := goshutdown.NewOrderHandler("gluetun",
|
||||||
orderSettings := goshutdown.OrderSettings{
|
order.OptionTimeout(totalShutdownTimeout),
|
||||||
Timeout: orderShutdownTimeout,
|
order.OptionOnSuccess(defaultShutdownOnSuccess),
|
||||||
OnFailure: defaultShutdownOnFailure,
|
order.OptionOnFailure(defaultShutdownOnFailure))
|
||||||
OnSuccess: defaultShutdownOnSuccess,
|
|
||||||
}
|
|
||||||
orderHandler := goshutdown.NewOrder("gluetun", orderSettings)
|
|
||||||
orderHandler.Append(controlGroupHandler, tickersGroupHandler, healthServerHandler,
|
orderHandler.Append(controlGroupHandler, tickersGroupHandler, healthServerHandler,
|
||||||
vpnHandler, portForwardHandler, otherGroupHandler)
|
vpnHandler, portForwardHandler, otherGroupHandler)
|
||||||
|
|
||||||
@@ -437,7 +432,11 @@ type printVersionElement struct {
|
|||||||
getVersion func(ctx context.Context) (version string, err error)
|
getVersion func(ctx context.Context) (version string, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func printVersions(ctx context.Context, logger logging.Logger,
|
type infoer interface {
|
||||||
|
Info(s string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printVersions(ctx context.Context, logger infoer,
|
||||||
elements []printVersionElement) (err error) {
|
elements []printVersionElement) (err error) {
|
||||||
const timeout = 5 * time.Second
|
const timeout = 5 * time.Second
|
||||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
version: "3.7"
|
|
||||||
services:
|
|
||||||
gluetun:
|
|
||||||
image: qmcgaw/gluetun
|
|
||||||
container_name: gluetun
|
|
||||||
cap_add:
|
|
||||||
- NET_ADMIN
|
|
||||||
network_mode: bridge
|
|
||||||
ports:
|
|
||||||
- 8888:8888/tcp # HTTP proxy
|
|
||||||
- 8388:8388/tcp # Shadowsocks
|
|
||||||
- 8388:8388/udp # Shadowsocks
|
|
||||||
- 8000:8000/tcp # Built-in HTTP control server
|
|
||||||
# command:
|
|
||||||
volumes:
|
|
||||||
- /yourpath:/gluetun
|
|
||||||
environment:
|
|
||||||
# More variables are available, see the Wiki table
|
|
||||||
- OPENVPN_USER=
|
|
||||||
- OPENVPN_PASSWORD=
|
|
||||||
- VPNSP=private internet access
|
|
||||||
- VPN_TYPE=openvpn
|
|
||||||
# Timezone for accurate logs times
|
|
||||||
- TZ=
|
|
||||||
restart: always
|
|
||||||
@@ -3,11 +3,13 @@ module github.com/qdm12/gluetun
|
|||||||
go 1.17
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fatih/color v1.12.0
|
github.com/breml/rootcerts v0.1.0
|
||||||
|
github.com/fatih/color v1.13.0
|
||||||
|
github.com/go-ping/ping v0.0.0-20210911151512-381826476871
|
||||||
github.com/golang/mock v1.6.0
|
github.com/golang/mock v1.6.0
|
||||||
github.com/qdm12/dns v1.11.0
|
github.com/qdm12/dns v1.11.0
|
||||||
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6
|
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6
|
||||||
github.com/qdm12/goshutdown v0.1.0
|
github.com/qdm12/goshutdown v0.3.0
|
||||||
github.com/qdm12/gosplash v0.1.0
|
github.com/qdm12/gosplash v0.1.0
|
||||||
github.com/qdm12/ss-server v0.3.0
|
github.com/qdm12/ss-server v0.3.0
|
||||||
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
|
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
|
||||||
@@ -22,9 +24,10 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/google/go-cmp v0.5.5 // indirect
|
github.com/google/go-cmp v0.5.5 // indirect
|
||||||
|
github.com/google/uuid v1.2.0 // indirect
|
||||||
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect
|
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
github.com/mdlayher/genetlink v1.0.0 // indirect
|
github.com/mdlayher/genetlink v1.0.0 // indirect
|
||||||
github.com/mdlayher/netlink v1.4.0 // indirect
|
github.com/mdlayher/netlink v1.4.0 // indirect
|
||||||
github.com/miekg/dns v1.1.40 // indirect
|
github.com/miekg/dns v1.1.40 // indirect
|
||||||
@@ -36,5 +39,6 @@ require (
|
|||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 // indirect
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
|
||||||
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d // indirect
|
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d // indirect
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/g
|
|||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
|
github.com/breml/rootcerts v0.1.0 h1:jf/F4MHeXBi7FVAHrPq+/MBmPlGXTbUAyR977bZ0j4E=
|
||||||
|
github.com/breml/rootcerts v0.1.0/go.mod h1:24FDtzYMpqIeYC7QzaE8VPRQaFZU5TIUDlyk8qwjD88=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
@@ -11,8 +13,9 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
|
||||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
@@ -29,6 +32,8 @@ github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsd
|
|||||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||||
github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||||
|
github.com/go-ping/ping v0.0.0-20210911151512-381826476871 h1:wtjTfjwAR/BYYMJ+QOLI/3J/qGEI0fgrkZvgsEWK2/Q=
|
||||||
|
github.com/go-ping/ping v0.0.0-20210911151512-381826476871/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
|
||||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
@@ -42,6 +47,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||||
|
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
|
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
@@ -64,10 +71,12 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kyokomi/emoji v2.2.4+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
|
github.com/kyokomi/emoji v2.2.4+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
|
||||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||||
|
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JAQI/ZLUkCZ7VJS3r74hwFIGXJsgZlY=
|
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JAQI/ZLUkCZ7VJS3r74hwFIGXJsgZlY=
|
||||||
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
|
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
|
||||||
github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0=
|
github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0=
|
||||||
@@ -102,8 +111,8 @@ github.com/qdm12/golibs v0.0.0-20210603202746-e5494e9c2ebb/go.mod h1:15RBzkun0i8
|
|||||||
github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2/go.mod h1:6aRbg4Z/bTbm9JfxsGXfWKHi7zsOvPfUTK1S5HuAFKg=
|
github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2/go.mod h1:6aRbg4Z/bTbm9JfxsGXfWKHi7zsOvPfUTK1S5HuAFKg=
|
||||||
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6 h1:bge5AL7cjHJMPz+5IOz5yF01q/l8No6+lIEBieA8gMg=
|
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6 h1:bge5AL7cjHJMPz+5IOz5yF01q/l8No6+lIEBieA8gMg=
|
||||||
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6/go.mod h1:6aRbg4Z/bTbm9JfxsGXfWKHi7zsOvPfUTK1S5HuAFKg=
|
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6/go.mod h1:6aRbg4Z/bTbm9JfxsGXfWKHi7zsOvPfUTK1S5HuAFKg=
|
||||||
github.com/qdm12/goshutdown v0.1.0 h1:lmwnygdXtnr2pa6VqfR/bm8077/BnBef1+7CP96B7Sw=
|
github.com/qdm12/goshutdown v0.3.0 h1:pqBpJkdwlZlfTEx4QHtS8u8CXx6pG0fVo6S1N0MpSEM=
|
||||||
github.com/qdm12/goshutdown v0.1.0/go.mod h1:/LP3MWLqI+wGH/ijfaUG+RHzBbKXIiVKnrg5vXOCf6Q=
|
github.com/qdm12/goshutdown v0.3.0/go.mod h1:EqZ46No00kCTZ5qzdd3qIzY6ayhMt24QI8Mh8LVQYmM=
|
||||||
github.com/qdm12/gosplash v0.1.0 h1:Sfl+zIjFZFP7b0iqf2l5UkmEY97XBnaKkH3FNY6Gf7g=
|
github.com/qdm12/gosplash v0.1.0 h1:Sfl+zIjFZFP7b0iqf2l5UkmEY97XBnaKkH3FNY6Gf7g=
|
||||||
github.com/qdm12/gosplash v0.1.0/go.mod h1:+A3fWW4/rUeDXhY3ieBzwghKdnIPFJgD8K3qQkenJlw=
|
github.com/qdm12/gosplash v0.1.0/go.mod h1:+A3fWW4/rUeDXhY3ieBzwghKdnIPFJgD8K3qQkenJlw=
|
||||||
github.com/qdm12/ss-server v0.3.0 h1:BfKv4OU6dYb2KcDMYpTc7LIuO2jB73g3JCzy988GrLI=
|
github.com/qdm12/ss-server v0.3.0 h1:BfKv4OU6dYb2KcDMYpTc7LIuO2jB73g3JCzy988GrLI=
|
||||||
@@ -163,6 +172,7 @@ golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d h1:nTDGCTeAu2LhcsHTRzjyIUbZHCJ4QePArsm27Hka0UM=
|
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d h1:nTDGCTeAu2LhcsHTRzjyIUbZHCJ4QePArsm27Hka0UM=
|
||||||
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
@@ -195,6 +205,7 @@ golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
|||||||
+2
-1
@@ -8,6 +8,7 @@ type CLIer interface {
|
|||||||
HealthChecker
|
HealthChecker
|
||||||
OpenvpnConfigMaker
|
OpenvpnConfigMaker
|
||||||
Updater
|
Updater
|
||||||
|
ServersFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
type CLI struct {
|
type CLI struct {
|
||||||
@@ -16,6 +17,6 @@ type CLI struct {
|
|||||||
|
|
||||||
func New() *CLI {
|
func New() *CLI {
|
||||||
return &CLI{
|
return &CLI{
|
||||||
repoServersPath: "./internal/constants/servers.json",
|
repoServersPath: "./internal/storage/servers.json",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/gluetun/internal/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServersFormatter interface {
|
||||||
|
FormatServers(args []string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrFormatNotRecognized = errors.New("format is not recognized")
|
||||||
|
ErrProviderUnspecified = errors.New("VPN provider to format was not specified")
|
||||||
|
ErrOpenOutputFile = errors.New("cannot open output file")
|
||||||
|
ErrWriteOutput = errors.New("cannot write to output file")
|
||||||
|
ErrCloseOutputFile = errors.New("cannot close output file")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *CLI) FormatServers(args []string) error {
|
||||||
|
var format, output string
|
||||||
|
var cyberghost, expressvpn, fastestvpn, hideMyAss, ipvanish, ivpn, mullvad,
|
||||||
|
nordvpn, pia, privado, privatevpn, protonvpn, purevpn, surfshark,
|
||||||
|
torguard, vpnUnlimited, vyprvpn, wevpn, windscribe bool
|
||||||
|
flagSet := flag.NewFlagSet("markdown", flag.ExitOnError)
|
||||||
|
flagSet.StringVar(&format, "format", "markdown", "Format to use which can be: 'markdown'")
|
||||||
|
flagSet.StringVar(&output, "output", "/dev/stdout", "Output file to write the formatted data to")
|
||||||
|
flagSet.BoolVar(&cyberghost, "cyberghost", false, "Format Cyberghost servers")
|
||||||
|
flagSet.BoolVar(&expressvpn, "expressvpn", false, "Format ExpressVPN servers")
|
||||||
|
flagSet.BoolVar(&fastestvpn, "fastestvpn", false, "Format FastestVPN servers")
|
||||||
|
flagSet.BoolVar(&hideMyAss, "hidemyass", false, "Format HideMyAss servers")
|
||||||
|
flagSet.BoolVar(&ipvanish, "ipvanish", false, "Format IpVanish servers")
|
||||||
|
flagSet.BoolVar(&ivpn, "ivpn", false, "Format IVPN servers")
|
||||||
|
flagSet.BoolVar(&mullvad, "mullvad", false, "Format Mullvad servers")
|
||||||
|
flagSet.BoolVar(&nordvpn, "nordvpn", false, "Format Nordvpn servers")
|
||||||
|
flagSet.BoolVar(&pia, "pia", false, "Format Private Internet Access servers")
|
||||||
|
flagSet.BoolVar(&privado, "privado", false, "Format Privado servers")
|
||||||
|
flagSet.BoolVar(&privatevpn, "privatevpn", false, "Format Private VPN servers")
|
||||||
|
flagSet.BoolVar(&protonvpn, "protonvpn", false, "Format Protonvpn servers")
|
||||||
|
flagSet.BoolVar(&purevpn, "purevpn", false, "Format Purevpn servers")
|
||||||
|
flagSet.BoolVar(&surfshark, "surfshark", false, "Format Surfshark servers")
|
||||||
|
flagSet.BoolVar(&torguard, "torguard", false, "Format Torguard servers")
|
||||||
|
flagSet.BoolVar(&vpnUnlimited, "vpnunlimited", false, "Format VPN Unlimited servers")
|
||||||
|
flagSet.BoolVar(&vyprvpn, "vyprvpn", false, "Format Vyprvpn servers")
|
||||||
|
flagSet.BoolVar(&wevpn, "wevpn", false, "Format WeVPN servers")
|
||||||
|
flagSet.BoolVar(&windscribe, "windscribe", false, "Format Windscribe servers")
|
||||||
|
if err := flagSet.Parse(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if format != "markdown" {
|
||||||
|
return fmt.Errorf("%w: %s", ErrFormatNotRecognized, format)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger := newNoopLogger()
|
||||||
|
storage, err := storage.New(logger, constants.ServersData)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrNewStorage, err)
|
||||||
|
}
|
||||||
|
currentServers := storage.GetServers()
|
||||||
|
|
||||||
|
var formatted string
|
||||||
|
switch {
|
||||||
|
case cyberghost:
|
||||||
|
formatted = currentServers.Cyberghost.ToMarkdown()
|
||||||
|
case expressvpn:
|
||||||
|
formatted = currentServers.Expressvpn.ToMarkdown()
|
||||||
|
case fastestvpn:
|
||||||
|
formatted = currentServers.Fastestvpn.ToMarkdown()
|
||||||
|
case hideMyAss:
|
||||||
|
formatted = currentServers.HideMyAss.ToMarkdown()
|
||||||
|
case ipvanish:
|
||||||
|
formatted = currentServers.Ipvanish.ToMarkdown()
|
||||||
|
case ivpn:
|
||||||
|
formatted = currentServers.Ivpn.ToMarkdown()
|
||||||
|
case mullvad:
|
||||||
|
formatted = currentServers.Mullvad.ToMarkdown()
|
||||||
|
case nordvpn:
|
||||||
|
formatted = currentServers.Nordvpn.ToMarkdown()
|
||||||
|
case pia:
|
||||||
|
formatted = currentServers.Pia.ToMarkdown()
|
||||||
|
case privado:
|
||||||
|
formatted = currentServers.Privado.ToMarkdown()
|
||||||
|
case privatevpn:
|
||||||
|
formatted = currentServers.Privatevpn.ToMarkdown()
|
||||||
|
case protonvpn:
|
||||||
|
formatted = currentServers.Protonvpn.ToMarkdown()
|
||||||
|
case purevpn:
|
||||||
|
formatted = currentServers.Purevpn.ToMarkdown()
|
||||||
|
case surfshark:
|
||||||
|
formatted = currentServers.Surfshark.ToMarkdown()
|
||||||
|
case torguard:
|
||||||
|
formatted = currentServers.Torguard.ToMarkdown()
|
||||||
|
case vpnUnlimited:
|
||||||
|
formatted = currentServers.VPNUnlimited.ToMarkdown()
|
||||||
|
case vyprvpn:
|
||||||
|
formatted = currentServers.Vyprvpn.ToMarkdown()
|
||||||
|
case wevpn:
|
||||||
|
formatted = currentServers.Wevpn.ToMarkdown()
|
||||||
|
case windscribe:
|
||||||
|
formatted = currentServers.Windscribe.ToMarkdown()
|
||||||
|
default:
|
||||||
|
return ErrProviderUnspecified
|
||||||
|
}
|
||||||
|
|
||||||
|
output = filepath.Clean(output)
|
||||||
|
file, err := os.OpenFile(output, os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrOpenOutputFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = fmt.Fprint(file, formatted)
|
||||||
|
if err != nil {
|
||||||
|
_ = file.Close()
|
||||||
|
return fmt.Errorf("%w: %s", ErrWriteOutput, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = file.Close()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrCloseOutputFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -8,19 +8,18 @@ import (
|
|||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration"
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
"github.com/qdm12/gluetun/internal/healthcheck"
|
"github.com/qdm12/gluetun/internal/healthcheck"
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
"github.com/qdm12/golibs/params"
|
"github.com/qdm12/golibs/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HealthChecker interface {
|
type HealthChecker interface {
|
||||||
HealthCheck(ctx context.Context, env params.Interface, logger logging.Logger) error
|
HealthCheck(ctx context.Context, env params.Interface, warner configuration.Warner) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CLI) HealthCheck(ctx context.Context, env params.Interface,
|
func (c *CLI) HealthCheck(ctx context.Context, env params.Interface,
|
||||||
logger logging.Logger) error {
|
warner configuration.Warner) error {
|
||||||
// Extract the health server port from the configuration.
|
// Extract the health server port from the configuration.
|
||||||
config := configuration.Health{}
|
config := configuration.Health{}
|
||||||
err := config.Read(env, logger)
|
err := config.Read(env, warner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import "github.com/qdm12/golibs/logging"
|
||||||
|
|
||||||
|
type noopLogger struct{}
|
||||||
|
|
||||||
|
func newNoopLogger() *noopLogger {
|
||||||
|
return new(noopLogger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) Debug(s string) {}
|
||||||
|
func (l *noopLogger) Info(s string) {}
|
||||||
|
func (l *noopLogger) Warn(s string) {}
|
||||||
|
func (l *noopLogger) Error(s string) {}
|
||||||
|
func (l *noopLogger) PatchLevel(level logging.Level) {}
|
||||||
|
func (l *noopLogger) PatchPrefix(prefix string) {}
|
||||||
@@ -9,15 +9,19 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/provider"
|
"github.com/qdm12/gluetun/internal/provider"
|
||||||
"github.com/qdm12/gluetun/internal/storage"
|
"github.com/qdm12/gluetun/internal/storage"
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
"github.com/qdm12/golibs/params"
|
"github.com/qdm12/golibs/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OpenvpnConfigMaker interface {
|
type OpenvpnConfigMaker interface {
|
||||||
OpenvpnConfig(logger logging.Logger, env params.Interface) error
|
OpenvpnConfig(logger OpenvpnConfigLogger, env params.Interface) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CLI) OpenvpnConfig(logger logging.Logger, env params.Interface) error {
|
type OpenvpnConfigLogger interface {
|
||||||
|
Info(s string)
|
||||||
|
Warn(s string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CLI) OpenvpnConfig(logger OpenvpnConfigLogger, env params.Interface) error {
|
||||||
storage, err := storage.New(logger, constants.ServersData)
|
storage, err := storage.New(logger, constants.ServersData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -34,7 +38,11 @@ func (c *CLI) OpenvpnConfig(logger logging.Logger, env params.Interface) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
lines := providerConf.BuildConf(connection, allSettings.VPN.OpenVPN)
|
lines, err := providerConf.BuildConf(connection, allSettings.VPN.OpenVPN)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println(strings.Join(lines, "\n"))
|
fmt.Println(strings.Join(lines, "\n"))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-4
@@ -15,7 +15,6 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/gluetun/internal/storage"
|
"github.com/qdm12/gluetun/internal/storage"
|
||||||
"github.com/qdm12/gluetun/internal/updater"
|
"github.com/qdm12/gluetun/internal/updater"
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -26,19 +25,26 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Updater interface {
|
type Updater interface {
|
||||||
Update(ctx context.Context, args []string, logger logging.Logger) error
|
Update(ctx context.Context, args []string, logger UpdaterLogger) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CLI) Update(ctx context.Context, args []string, logger logging.Logger) error {
|
type UpdaterLogger interface {
|
||||||
|
Info(s string)
|
||||||
|
Warn(s string)
|
||||||
|
Error(s string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) error {
|
||||||
options := configuration.Updater{CLI: true}
|
options := configuration.Updater{CLI: true}
|
||||||
var endUserMode, maintainerMode, updateAll bool
|
var endUserMode, maintainerMode, updateAll bool
|
||||||
flagSet := flag.NewFlagSet("update", flag.ExitOnError)
|
flagSet := flag.NewFlagSet("update", flag.ExitOnError)
|
||||||
flagSet.BoolVar(&endUserMode, "enduser", false, "Write results to /gluetun/servers.json (for end users)")
|
flagSet.BoolVar(&endUserMode, "enduser", false, "Write results to /gluetun/servers.json (for end users)")
|
||||||
flagSet.BoolVar(&maintainerMode, "maintainer", false,
|
flagSet.BoolVar(&maintainerMode, "maintainer", false,
|
||||||
"Write results to ./internal/constants/servers.json to modify the program (for maintainers)")
|
"Write results to ./internal/storage/servers.json to modify the program (for maintainers)")
|
||||||
flagSet.StringVar(&options.DNSAddress, "dns", "8.8.8.8", "DNS resolver address to use")
|
flagSet.StringVar(&options.DNSAddress, "dns", "8.8.8.8", "DNS resolver address to use")
|
||||||
flagSet.BoolVar(&updateAll, "all", false, "Update servers for all VPN providers")
|
flagSet.BoolVar(&updateAll, "all", false, "Update servers for all VPN providers")
|
||||||
flagSet.BoolVar(&options.Cyberghost, "cyberghost", false, "Update Cyberghost servers")
|
flagSet.BoolVar(&options.Cyberghost, "cyberghost", false, "Update Cyberghost servers")
|
||||||
|
flagSet.BoolVar(&options.Expressvpn, "expressvpn", false, "Update ExpressVPN servers")
|
||||||
flagSet.BoolVar(&options.Fastestvpn, "fastestvpn", false, "Update FastestVPN servers")
|
flagSet.BoolVar(&options.Fastestvpn, "fastestvpn", false, "Update FastestVPN servers")
|
||||||
flagSet.BoolVar(&options.HideMyAss, "hidemyass", false, "Update HideMyAss servers")
|
flagSet.BoolVar(&options.HideMyAss, "hidemyass", false, "Update HideMyAss servers")
|
||||||
flagSet.BoolVar(&options.Ipvanish, "ipvanish", false, "Update IpVanish servers")
|
flagSet.BoolVar(&options.Ipvanish, "ipvanish", false, "Update IpVanish servers")
|
||||||
@@ -54,6 +60,7 @@ func (c *CLI) Update(ctx context.Context, args []string, logger logging.Logger)
|
|||||||
flagSet.BoolVar(&options.Torguard, "torguard", false, "Update Torguard servers")
|
flagSet.BoolVar(&options.Torguard, "torguard", false, "Update Torguard servers")
|
||||||
flagSet.BoolVar(&options.VPNUnlimited, "vpnunlimited", false, "Update VPN Unlimited servers")
|
flagSet.BoolVar(&options.VPNUnlimited, "vpnunlimited", false, "Update VPN Unlimited servers")
|
||||||
flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers")
|
flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers")
|
||||||
|
flagSet.BoolVar(&options.Wevpn, "wevpn", false, "Update WeVPN servers")
|
||||||
flagSet.BoolVar(&options.Windscribe, "windscribe", false, "Update Windscribe servers")
|
flagSet.BoolVar(&options.Windscribe, "windscribe", false, "Update Windscribe servers")
|
||||||
if err := flagSet.Parse(args); err != nil {
|
if err := flagSet.Parse(args); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errCustomNotSupported = errors.New("custom provider is not supported")
|
||||||
|
errCustomExtractFromFile = errors.New("cannot extract configuration from file")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) readCustom(r reader, vpnType string) (err error) {
|
||||||
|
settings.Name = constants.Custom
|
||||||
|
|
||||||
|
switch vpnType {
|
||||||
|
case constants.OpenVPN:
|
||||||
|
return settings.ServerSelection.OpenVPN.readCustom(r)
|
||||||
|
case constants.Wireguard:
|
||||||
|
return settings.ServerSelection.Wireguard.readCustom(r)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%w: for VPN type %s", errCustomNotSupported, vpnType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPNSelection) readCustom(r reader) (err error) {
|
||||||
|
configFile, err := r.env.Get("OPENVPN_CUSTOM_CONFIG", params.CaseSensitiveValue(), params.Compulsory())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable OPENVPN_CUSTOM_CONFIG: %w", err)
|
||||||
|
}
|
||||||
|
settings.ConfFile = configFile
|
||||||
|
|
||||||
|
// For display and consistency purposes only,
|
||||||
|
// these values are not actually used since the file is re-read
|
||||||
|
// before each OpenVPN start.
|
||||||
|
_, connection, err := r.ovpnExt.Data(configFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", errCustomExtractFromFile, err)
|
||||||
|
}
|
||||||
|
settings.TCP = connection.Protocol == constants.TCP
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPN) readCustom(r reader) (err error) {
|
||||||
|
settings.ConfFile, err = r.env.Path("OPENVPN_CUSTOM_CONFIG",
|
||||||
|
params.Compulsory(), params.CaseSensitiveValue())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable OPENVPN_CUSTOM_CONFIG: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *WireguardSelection) readCustom(r reader) (err error) {
|
||||||
|
settings.PublicKey, err = r.env.Get("WIREGUARD_PUBLIC_KEY",
|
||||||
|
params.CaseSensitiveValue(), params.Compulsory())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable WIREGUARD_PUBLIC_KEY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.EndpointIP, err = readWireguardEndpointIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.EndpointPort, err = r.env.Port("WIREGUARD_ENDPOINT_PORT", params.Compulsory(),
|
||||||
|
params.RetroKeys([]string{"WIREGUARD_PORT"}, r.onRetroActive))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable WIREGUARD_ENDPOINT_PORT: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readWireguardEndpointIP reads and parses the server endpoint IP
|
||||||
|
// address from the environment variable WIREGUARD_ENDPOINT_IP.
|
||||||
|
func readWireguardEndpointIP(env params.Interface) (endpointIP net.IP, err error) {
|
||||||
|
s, err := env.Get("WIREGUARD_ENDPOINT_IP", params.Compulsory())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("environment variable WIREGUARD_ENDPOINT_IP: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
endpointIP = net.ParseIP(s)
|
||||||
|
if endpointIP == nil {
|
||||||
|
return nil, fmt.Errorf("environment variable WIREGUARD_ENDPOINT_IP: %w: %s",
|
||||||
|
ErrInvalidIP, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpointIP, nil
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (settings *Provider) readCyberghost(r reader) (err error) {
|
func (settings *Provider) readCyberghost(r reader) (err error) {
|
||||||
@@ -15,15 +16,11 @@ func (settings *Provider) readCyberghost(r reader) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Groups, err = r.env.CSVInside("CYBERGHOST_GROUP",
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY",
|
||||||
constants.CyberghostGroupChoices(servers))
|
constants.CyberghostCountryChoices(servers),
|
||||||
|
params.RetroKeys([]string{"REGION"}, r.onRetroActive))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("environment variable CYBERGHOST_GROUP: %w", err)
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.CyberghostRegionChoices(servers))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("environment variable REGION: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
|
||||||
@@ -32,18 +29,18 @@ func (settings *Provider) readCyberghost(r reader) (err error) {
|
|||||||
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r.env)
|
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *OpenVPN) readCyberghost(r reader) (err error) {
|
func (settings *OpenVPN) readCyberghost(r reader) (err error) {
|
||||||
settings.ClientKey, err = readClientKey(r)
|
settings.ClientKey, err = readClientKey(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", errClientKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ClientCrt, err = readClientCertificate(r)
|
settings.ClientCrt, err = readClientCertificate(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", errClientCert, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) readExpressvpn(r reader) (err error) {
|
||||||
|
settings.Name = constants.Expressvpn
|
||||||
|
servers := r.servers.GetExpressvpn()
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
|
||||||
|
constants.ExpressvpnHostnameChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.ExpressvpnCountriesChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.ExpressvpnCityChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.OpenVPN.TCP, err = readOpenVPNProtocol(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -26,5 +26,5 @@ func (settings *Provider) readFastestvpn(r reader) (err error) {
|
|||||||
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env)
|
return settings.ServerSelection.OpenVPN.readProtocolOnly(r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
"github.com/qdm12/golibs/params"
|
"github.com/qdm12/golibs/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Health contains settings for the healthcheck and health server.
|
// Health contains settings for the healthcheck and health server.
|
||||||
type Health struct {
|
type Health struct {
|
||||||
ServerAddress string
|
ServerAddress string
|
||||||
OpenVPN HealthyWait
|
AddressToPing string
|
||||||
|
VPN HealthyWait
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *Health) String() string {
|
func (settings *Health) String() string {
|
||||||
@@ -24,8 +24,10 @@ func (settings *Health) lines() (lines []string) {
|
|||||||
|
|
||||||
lines = append(lines, indent+lastIndent+"Server address: "+settings.ServerAddress)
|
lines = append(lines, indent+lastIndent+"Server address: "+settings.ServerAddress)
|
||||||
|
|
||||||
lines = append(lines, indent+lastIndent+"OpenVPN:")
|
lines = append(lines, indent+lastIndent+"Address to ping: "+settings.AddressToPing)
|
||||||
for _, line := range settings.OpenVPN.lines() {
|
|
||||||
|
lines = append(lines, indent+lastIndent+"VPN:")
|
||||||
|
for _, line := range settings.VPN.lines() {
|
||||||
lines = append(lines, indent+indent+line)
|
lines = append(lines, indent+indent+line)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,8 +35,8 @@ func (settings *Health) lines() (lines []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read is to be used for the healthcheck query mode.
|
// Read is to be used for the healthcheck query mode.
|
||||||
func (settings *Health) Read(env params.Interface, logger logging.Logger) (err error) {
|
func (settings *Health) Read(env params.Interface, warner Warner) (err error) {
|
||||||
reader := newReader(env, models.AllServers{}, logger) // note: no need for servers data
|
reader := newReader(env, models.AllServers{}, warner) // note: no need for servers data
|
||||||
return settings.read(reader)
|
return settings.read(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,20 +45,27 @@ func (settings *Health) read(r reader) (err error) {
|
|||||||
settings.ServerAddress, warning, err = r.env.ListeningAddress(
|
settings.ServerAddress, warning, err = r.env.ListeningAddress(
|
||||||
"HEALTH_SERVER_ADDRESS", params.Default("127.0.0.1:9999"))
|
"HEALTH_SERVER_ADDRESS", params.Default("127.0.0.1:9999"))
|
||||||
if warning != "" {
|
if warning != "" {
|
||||||
r.logger.Warn("environment variable HEALTH_SERVER_ADDRESS: " + warning)
|
r.warner.Warn("environment variable HEALTH_SERVER_ADDRESS: " + warning)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("environment variable HEALTH_SERVER_ADDRESS: %w", err)
|
return fmt.Errorf("environment variable HEALTH_SERVER_ADDRESS: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.OpenVPN.Initial, err = r.env.Duration("HEALTH_OPENVPN_DURATION_INITIAL", params.Default("6s"))
|
settings.AddressToPing, err = r.env.Get("HEALTH_ADDRESS_TO_PING", params.Default("github.com"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("environment variable HEALTH_OPENVPN_DURATION_INITIAL: %w", err)
|
return fmt.Errorf("environment variable HEALTH_ADDRESS_TO_PING: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.OpenVPN.Addition, err = r.env.Duration("HEALTH_OPENVPN_DURATION_ADDITION", params.Default("5s"))
|
retroKeyOption := params.RetroKeys([]string{"HEALTH_OPENVPN_DURATION_INITIAL"}, r.onRetroActive)
|
||||||
|
settings.VPN.Initial, err = r.env.Duration("HEALTH_VPN_DURATION_INITIAL", params.Default("6s"), retroKeyOption)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("environment variable HEALTH_OPENVPN_DURATION_ADDITION: %w", err)
|
return fmt.Errorf("environment variable HEALTH_VPN_DURATION_INITIAL: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
retroKeyOption = params.RetroKeys([]string{"HEALTH_OPENVPN_DURATION_ADDITION"}, r.onRetroActive)
|
||||||
|
settings.VPN.Addition, err = r.env.Duration("HEALTH_VPN_DURATION_ADDITION", params.Default("5s"), retroKeyOption)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable HEALTH_VPN_DURATION_ADDITION: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/qdm12/golibs/logging/mock_logging"
|
|
||||||
"github.com/qdm12/golibs/params/mock_params"
|
"github.com/qdm12/golibs/params/mock_params"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -15,8 +14,15 @@ import (
|
|||||||
func Test_Health_String(t *testing.T) {
|
func Test_Health_String(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
var health Health
|
health := Health{
|
||||||
const expected = "|--Health:\n |--Server address: \n |--OpenVPN:\n |--Initial duration: 0s"
|
ServerAddress: "a",
|
||||||
|
AddressToPing: "b",
|
||||||
|
}
|
||||||
|
const expected = `|--Health:
|
||||||
|
|--Server address: a
|
||||||
|
|--Address to ping: b
|
||||||
|
|--VPN:
|
||||||
|
|--Initial duration: 0s`
|
||||||
|
|
||||||
s := health.String()
|
s := health.String()
|
||||||
|
|
||||||
@@ -34,14 +40,16 @@ func Test_Health_lines(t *testing.T) {
|
|||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Health:",
|
"|--Health:",
|
||||||
" |--Server address: ",
|
" |--Server address: ",
|
||||||
" |--OpenVPN:",
|
" |--Address to ping: ",
|
||||||
|
" |--VPN:",
|
||||||
" |--Initial duration: 0s",
|
" |--Initial duration: 0s",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"filled settings": {
|
"filled settings": {
|
||||||
settings: Health{
|
settings: Health{
|
||||||
ServerAddress: "address:9999",
|
ServerAddress: "address:9999",
|
||||||
OpenVPN: HealthyWait{
|
AddressToPing: "github.com",
|
||||||
|
VPN: HealthyWait{
|
||||||
Initial: time.Second,
|
Initial: time.Second,
|
||||||
Addition: time.Minute,
|
Addition: time.Minute,
|
||||||
},
|
},
|
||||||
@@ -49,7 +57,8 @@ func Test_Health_lines(t *testing.T) {
|
|||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Health:",
|
"|--Health:",
|
||||||
" |--Server address: address:9999",
|
" |--Server address: address:9999",
|
||||||
" |--OpenVPN:",
|
" |--Address to ping: github.com",
|
||||||
|
" |--VPN:",
|
||||||
" |--Initial duration: 1s",
|
" |--Initial duration: 1s",
|
||||||
" |--Addition duration: 1m0s",
|
" |--Addition duration: 1m0s",
|
||||||
},
|
},
|
||||||
@@ -73,62 +82,127 @@ func Test_Health_read(t *testing.T) {
|
|||||||
|
|
||||||
errDummy := errors.New("dummy")
|
errDummy := errors.New("dummy")
|
||||||
|
|
||||||
|
type stringCall struct {
|
||||||
|
call bool
|
||||||
|
s string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringCallWithWarning struct {
|
||||||
|
call bool
|
||||||
|
s string
|
||||||
|
warning string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type durationCall struct {
|
||||||
|
call bool
|
||||||
|
duration time.Duration
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
openvpnInitialDuration time.Duration
|
serverAddress stringCallWithWarning
|
||||||
openvpnInitialErr error
|
addressToPing stringCall
|
||||||
openvpnAdditionDuration time.Duration
|
vpnInitial durationCall
|
||||||
openvpnAdditionErr error
|
vpnAddition durationCall
|
||||||
serverAddress string
|
|
||||||
serverAddressWarning string
|
|
||||||
serverAddressErr error
|
|
||||||
expected Health
|
expected Health
|
||||||
err error
|
err error
|
||||||
}{
|
}{
|
||||||
"success": {
|
"success": {
|
||||||
openvpnInitialDuration: time.Second,
|
serverAddress: stringCallWithWarning{
|
||||||
openvpnAdditionDuration: time.Minute,
|
call: true,
|
||||||
serverAddress: "127.0.0.1:9999",
|
s: "127.0.0.1:9999",
|
||||||
|
},
|
||||||
|
addressToPing: stringCall{
|
||||||
|
call: true,
|
||||||
|
s: "1.2.3.4",
|
||||||
|
},
|
||||||
|
vpnInitial: durationCall{
|
||||||
|
call: true,
|
||||||
|
duration: time.Second,
|
||||||
|
},
|
||||||
|
vpnAddition: durationCall{
|
||||||
|
call: true,
|
||||||
|
duration: time.Minute,
|
||||||
|
},
|
||||||
expected: Health{
|
expected: Health{
|
||||||
ServerAddress: "127.0.0.1:9999",
|
ServerAddress: "127.0.0.1:9999",
|
||||||
OpenVPN: HealthyWait{
|
AddressToPing: "1.2.3.4",
|
||||||
|
VPN: HealthyWait{
|
||||||
Initial: time.Second,
|
Initial: time.Second,
|
||||||
Addition: time.Minute,
|
Addition: time.Minute,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"listening address error": {
|
"listening address error": {
|
||||||
openvpnInitialDuration: time.Second,
|
serverAddress: stringCallWithWarning{
|
||||||
openvpnAdditionDuration: time.Minute,
|
call: true,
|
||||||
serverAddress: "127.0.0.1:9999",
|
s: "127.0.0.1:9999",
|
||||||
serverAddressWarning: "warning",
|
warning: "warning",
|
||||||
serverAddressErr: errDummy,
|
err: errDummy,
|
||||||
|
},
|
||||||
expected: Health{
|
expected: Health{
|
||||||
ServerAddress: "127.0.0.1:9999",
|
ServerAddress: "127.0.0.1:9999",
|
||||||
},
|
},
|
||||||
err: errors.New("environment variable HEALTH_SERVER_ADDRESS: dummy"),
|
err: errors.New("environment variable HEALTH_SERVER_ADDRESS: dummy"),
|
||||||
},
|
},
|
||||||
"initial error": {
|
"address to ping error": {
|
||||||
openvpnInitialDuration: time.Second,
|
serverAddress: stringCallWithWarning{
|
||||||
openvpnInitialErr: errDummy,
|
call: true,
|
||||||
openvpnAdditionDuration: time.Minute,
|
},
|
||||||
|
addressToPing: stringCall{
|
||||||
|
call: true,
|
||||||
|
s: "address",
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
expected: Health{
|
expected: Health{
|
||||||
OpenVPN: HealthyWait{
|
AddressToPing: "address",
|
||||||
|
},
|
||||||
|
err: errors.New("environment variable HEALTH_ADDRESS_TO_PING: dummy"),
|
||||||
|
},
|
||||||
|
"initial error": {
|
||||||
|
serverAddress: stringCallWithWarning{
|
||||||
|
call: true,
|
||||||
|
},
|
||||||
|
addressToPing: stringCall{
|
||||||
|
call: true,
|
||||||
|
},
|
||||||
|
vpnInitial: durationCall{
|
||||||
|
call: true,
|
||||||
|
duration: time.Second,
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
expected: Health{
|
||||||
|
VPN: HealthyWait{
|
||||||
Initial: time.Second,
|
Initial: time.Second,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
err: errors.New("environment variable HEALTH_OPENVPN_DURATION_INITIAL: dummy"),
|
err: errors.New("environment variable HEALTH_VPN_DURATION_INITIAL: dummy"),
|
||||||
},
|
},
|
||||||
"addition error": {
|
"addition error": {
|
||||||
openvpnInitialDuration: time.Second,
|
serverAddress: stringCallWithWarning{
|
||||||
openvpnAdditionDuration: time.Minute,
|
call: true,
|
||||||
openvpnAdditionErr: errDummy,
|
},
|
||||||
|
addressToPing: stringCall{
|
||||||
|
call: true,
|
||||||
|
},
|
||||||
|
vpnInitial: durationCall{
|
||||||
|
call: true,
|
||||||
|
duration: time.Second,
|
||||||
|
},
|
||||||
|
vpnAddition: durationCall{
|
||||||
|
call: true,
|
||||||
|
duration: time.Minute,
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
expected: Health{
|
expected: Health{
|
||||||
OpenVPN: HealthyWait{
|
VPN: HealthyWait{
|
||||||
Initial: time.Second,
|
Initial: time.Second,
|
||||||
Addition: time.Minute,
|
Addition: time.Minute,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
err: errors.New("environment variable HEALTH_OPENVPN_DURATION_ADDITION: dummy"),
|
err: errors.New("environment variable HEALTH_VPN_DURATION_ADDITION: dummy"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,29 +214,45 @@ func Test_Health_read(t *testing.T) {
|
|||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
|
|
||||||
env := mock_params.NewMockInterface(ctrl)
|
env := mock_params.NewMockInterface(ctrl)
|
||||||
logger := mock_logging.NewMockLogger(ctrl)
|
warner := NewMockWarner(ctrl)
|
||||||
|
|
||||||
|
if testCase.serverAddress.call {
|
||||||
|
value := testCase.serverAddress.s
|
||||||
|
warning := testCase.serverAddress.warning
|
||||||
|
err := testCase.serverAddress.err
|
||||||
env.EXPECT().ListeningAddress("HEALTH_SERVER_ADDRESS", gomock.Any()).
|
env.EXPECT().ListeningAddress("HEALTH_SERVER_ADDRESS", gomock.Any()).
|
||||||
Return(testCase.serverAddress, testCase.serverAddressWarning,
|
Return(value, warning, err)
|
||||||
testCase.serverAddressErr)
|
if warning != "" {
|
||||||
if testCase.serverAddressWarning != "" {
|
warner.EXPECT().Warn("environment variable HEALTH_SERVER_ADDRESS: " + warning)
|
||||||
logger.EXPECT().Warn("environment variable HEALTH_SERVER_ADDRESS: " + testCase.serverAddressWarning)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if testCase.serverAddressErr == nil {
|
if testCase.addressToPing.call {
|
||||||
env.EXPECT().
|
value := testCase.addressToPing.s
|
||||||
Duration("HEALTH_OPENVPN_DURATION_INITIAL", gomock.Any()).
|
err := testCase.addressToPing.err
|
||||||
Return(testCase.openvpnInitialDuration, testCase.openvpnInitialErr)
|
env.EXPECT().Get("HEALTH_ADDRESS_TO_PING", gomock.Any()).
|
||||||
if testCase.openvpnInitialErr == nil {
|
Return(value, err)
|
||||||
env.EXPECT().
|
|
||||||
Duration("HEALTH_OPENVPN_DURATION_ADDITION", gomock.Any()).
|
|
||||||
Return(testCase.openvpnAdditionDuration, testCase.openvpnAdditionErr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if testCase.vpnInitial.call {
|
||||||
|
value := testCase.vpnInitial.duration
|
||||||
|
err := testCase.vpnInitial.err
|
||||||
|
env.EXPECT().
|
||||||
|
Duration("HEALTH_VPN_DURATION_INITIAL", gomock.Any()).
|
||||||
|
Return(value, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if testCase.vpnAddition.call {
|
||||||
|
value := testCase.vpnAddition.duration
|
||||||
|
err := testCase.vpnAddition.err
|
||||||
|
env.EXPECT().
|
||||||
|
Duration("HEALTH_VPN_DURATION_ADDITION", gomock.Any()).
|
||||||
|
Return(value, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r := reader{
|
r := reader{
|
||||||
env: env,
|
env: env,
|
||||||
logger: logger,
|
warner: warner,
|
||||||
}
|
}
|
||||||
|
|
||||||
var health Health
|
var health Health
|
||||||
|
|||||||
@@ -36,5 +36,5 @@ func (settings *Provider) readHideMyAss(r reader) (err error) {
|
|||||||
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r.env)
|
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ func (settings *HTTPProxy) read(r reader) (err error) {
|
|||||||
settings.Port, warning, err = r.env.ListeningPort("HTTPPROXY_PORT", params.Default("8888"),
|
settings.Port, warning, err = r.env.ListeningPort("HTTPPROXY_PORT", params.Default("8888"),
|
||||||
params.RetroKeys([]string{"TINYPROXY_PORT", "PROXY_PORT"}, r.onRetroActive))
|
params.RetroKeys([]string{"TINYPROXY_PORT", "PROXY_PORT"}, r.onRetroActive))
|
||||||
if len(warning) > 0 {
|
if len(warning) > 0 {
|
||||||
r.logger.Warn(warning)
|
r.warner.Warn(warning)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("environment variable HTTPPROXY_PORT (or TINYPROXY_PORT, PROXY_PORT): %w", err)
|
return fmt.Errorf("environment variable HTTPPROXY_PORT (or TINYPROXY_PORT, PROXY_PORT): %w", err)
|
||||||
|
|||||||
@@ -31,5 +31,5 @@ func (settings *Provider) readIpvanish(r reader) (err error) {
|
|||||||
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env)
|
return settings.ServerSelection.OpenVPN.readProtocolOnly(r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ func Test_Provider_readIpvanish(t *testing.T) {
|
|||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Ipvanish,
|
Name: constants.Ipvanish,
|
||||||
},
|
},
|
||||||
err: errors.New("environment variable PROTOCOL: dummy test error"),
|
err: errors.New("environment variable OPENVPN_PROTOCOL: dummy test error"),
|
||||||
},
|
},
|
||||||
"default settings": {
|
"default settings": {
|
||||||
targetIP: singleStringCall{call: true},
|
targetIP: singleStringCall{call: true},
|
||||||
@@ -145,7 +145,7 @@ func Test_Provider_readIpvanish(t *testing.T) {
|
|||||||
Return(testCase.hostnames.values, testCase.hostnames.err)
|
Return(testCase.hostnames.values, testCase.hostnames.err)
|
||||||
}
|
}
|
||||||
if testCase.protocol.call {
|
if testCase.protocol.call {
|
||||||
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
|
env.EXPECT().Inside("OPENVPN_PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
|
||||||
Return(testCase.protocol.value, testCase.protocol.err)
|
Return(testCase.protocol.value, testCase.protocol.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func (settings *Provider) readIvpn(r reader) (err error) {
|
|||||||
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = settings.ServerSelection.OpenVPN.readIVPN(r.env)
|
err = settings.ServerSelection.OpenVPN.readIVPN(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -44,14 +44,17 @@ func (settings *Provider) readIvpn(r reader) (err error) {
|
|||||||
return settings.ServerSelection.Wireguard.readIVPN(r.env)
|
return settings.ServerSelection.Wireguard.readIVPN(r.env)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *OpenVPNSelection) readIVPN(env params.Interface) (err error) {
|
func (settings *OpenVPNSelection) readIVPN(r reader) (err error) {
|
||||||
settings.TCP, err = readProtocol(env)
|
settings.TCP, err = readOpenVPNProtocol(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.CustomPort, err = readOpenVPNCustomPort(env, settings.TCP,
|
settings.CustomPort, err = readOpenVPNCustomPort(r, openvpnPortValidation{
|
||||||
[]uint16{80, 443, 1443}, []uint16{53, 1194, 2049, 2050})
|
tcp: settings.TCP,
|
||||||
|
allowedTCP: []uint16{80, 443, 1443},
|
||||||
|
allowedUDP: []uint16{53, 1194, 2049, 2050},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -60,7 +63,7 @@ func (settings *OpenVPNSelection) readIVPN(env params.Interface) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (settings *WireguardSelection) readIVPN(env params.Interface) (err error) {
|
func (settings *WireguardSelection) readIVPN(env params.Interface) (err error) {
|
||||||
settings.CustomPort, err = readWireguardCustomPort(env,
|
settings.EndpointPort, err = readWireguardCustomPort(env,
|
||||||
[]uint16{2049, 2050, 53, 30587, 41893, 48574, 58237})
|
[]uint16{2049, 2050, 53, 30587, 41893, 48574, 58237})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -47,7 +47,9 @@ func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
|
|||||||
hostnames sliceStringCall
|
hostnames sliceStringCall
|
||||||
protocol singleStringCall
|
protocol singleStringCall
|
||||||
ovpnPort portCall
|
ovpnPort portCall
|
||||||
|
ovpnOldPort portCall
|
||||||
wgPort portCall
|
wgPort portCall
|
||||||
|
wgOldPort portCall
|
||||||
settings Provider
|
settings Provider
|
||||||
err error
|
err error
|
||||||
}{
|
}{
|
||||||
@@ -106,7 +108,7 @@ func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
|
|||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Ivpn,
|
Name: constants.Ivpn,
|
||||||
},
|
},
|
||||||
err: errors.New("environment variable PROTOCOL: dummy test error"),
|
err: errors.New("environment variable OPENVPN_PROTOCOL: dummy test error"),
|
||||||
},
|
},
|
||||||
"openvpn custom port error": {
|
"openvpn custom port error": {
|
||||||
targetIP: singleStringCall{call: true},
|
targetIP: singleStringCall{call: true},
|
||||||
@@ -119,7 +121,7 @@ func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
|
|||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Ivpn,
|
Name: constants.Ivpn,
|
||||||
},
|
},
|
||||||
err: errors.New("environment variable PORT: dummy test error"),
|
err: errors.New("environment variable OPENVPN_PORT: dummy test error"),
|
||||||
},
|
},
|
||||||
"wireguard custom port error": {
|
"wireguard custom port error": {
|
||||||
targetIP: singleStringCall{call: true},
|
targetIP: singleStringCall{call: true},
|
||||||
@@ -129,11 +131,12 @@ func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
|
|||||||
hostnames: sliceStringCall{call: true},
|
hostnames: sliceStringCall{call: true},
|
||||||
protocol: singleStringCall{call: true},
|
protocol: singleStringCall{call: true},
|
||||||
ovpnPort: portCall{getCall: true, getValue: "0"},
|
ovpnPort: portCall{getCall: true, getValue: "0"},
|
||||||
|
ovpnOldPort: portCall{getCall: true, getValue: "0"},
|
||||||
wgPort: portCall{getCall: true, getErr: errDummy},
|
wgPort: portCall{getCall: true, getErr: errDummy},
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Ivpn,
|
Name: constants.Ivpn,
|
||||||
},
|
},
|
||||||
err: errors.New("environment variable WIREGUARD_PORT: dummy test error"),
|
err: errors.New("environment variable WIREGUARD_ENDPOINT_PORT: dummy test error"),
|
||||||
},
|
},
|
||||||
"default settings": {
|
"default settings": {
|
||||||
targetIP: singleStringCall{call: true},
|
targetIP: singleStringCall{call: true},
|
||||||
@@ -143,7 +146,9 @@ func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
|
|||||||
hostnames: sliceStringCall{call: true},
|
hostnames: sliceStringCall{call: true},
|
||||||
protocol: singleStringCall{call: true},
|
protocol: singleStringCall{call: true},
|
||||||
ovpnPort: portCall{getCall: true, getValue: "0"},
|
ovpnPort: portCall{getCall: true, getValue: "0"},
|
||||||
|
ovpnOldPort: portCall{getCall: true, getValue: "0"},
|
||||||
wgPort: portCall{getCall: true, getValue: "0"},
|
wgPort: portCall{getCall: true, getValue: "0"},
|
||||||
|
wgOldPort: portCall{getCall: true, getValue: "0"},
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Ivpn,
|
Name: constants.Ivpn,
|
||||||
},
|
},
|
||||||
@@ -165,7 +170,7 @@ func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
|
|||||||
CustomPort: 443,
|
CustomPort: 443,
|
||||||
},
|
},
|
||||||
Wireguard: WireguardSelection{
|
Wireguard: WireguardSelection{
|
||||||
CustomPort: 2049,
|
EndpointPort: 2049,
|
||||||
},
|
},
|
||||||
TargetIP: net.IPv4(1, 2, 3, 4),
|
TargetIP: net.IPv4(1, 2, 3, 4),
|
||||||
Countries: []string{"A", "B"},
|
Countries: []string{"A", "B"},
|
||||||
@@ -212,25 +217,41 @@ func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
|
|||||||
Return(testCase.hostnames.values, testCase.hostnames.err)
|
Return(testCase.hostnames.values, testCase.hostnames.err)
|
||||||
}
|
}
|
||||||
if testCase.protocol.call {
|
if testCase.protocol.call {
|
||||||
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
|
env.EXPECT().Inside("OPENVPN_PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
|
||||||
Return(testCase.protocol.value, testCase.protocol.err)
|
Return(testCase.protocol.value, testCase.protocol.err)
|
||||||
}
|
}
|
||||||
if testCase.ovpnPort.getCall {
|
if testCase.ovpnPort.getCall {
|
||||||
env.EXPECT().Get("PORT", gomock.Any()).
|
env.EXPECT().Get("OPENVPN_PORT", gomock.Any()).
|
||||||
Return(testCase.ovpnPort.getValue, testCase.ovpnPort.getErr)
|
Return(testCase.ovpnPort.getValue, testCase.ovpnPort.getErr)
|
||||||
}
|
}
|
||||||
if testCase.ovpnPort.portCall {
|
if testCase.ovpnPort.portCall {
|
||||||
env.EXPECT().Port("PORT").
|
env.EXPECT().Port("OPENVPN_PORT").
|
||||||
Return(testCase.ovpnPort.portValue, testCase.ovpnPort.portErr)
|
Return(testCase.ovpnPort.portValue, testCase.ovpnPort.portErr)
|
||||||
}
|
}
|
||||||
|
if testCase.ovpnOldPort.getCall {
|
||||||
|
env.EXPECT().Get("PORT", gomock.Any()).
|
||||||
|
Return(testCase.ovpnOldPort.getValue, testCase.ovpnOldPort.getErr)
|
||||||
|
}
|
||||||
|
if testCase.ovpnOldPort.portCall {
|
||||||
|
env.EXPECT().Port("PORT").
|
||||||
|
Return(testCase.ovpnOldPort.portValue, testCase.ovpnOldPort.portErr)
|
||||||
|
}
|
||||||
if testCase.wgPort.getCall {
|
if testCase.wgPort.getCall {
|
||||||
env.EXPECT().Get("WIREGUARD_PORT", gomock.Any()).
|
env.EXPECT().Get("WIREGUARD_ENDPOINT_PORT", gomock.Any()).
|
||||||
Return(testCase.wgPort.getValue, testCase.wgPort.getErr)
|
Return(testCase.wgPort.getValue, testCase.wgPort.getErr)
|
||||||
}
|
}
|
||||||
if testCase.wgPort.portCall {
|
if testCase.wgPort.portCall {
|
||||||
env.EXPECT().Port("WIREGUARD_PORT").
|
env.EXPECT().Port("WIREGUARD_ENDPOINT_PORT").
|
||||||
Return(testCase.wgPort.portValue, testCase.wgPort.portErr)
|
Return(testCase.wgPort.portValue, testCase.wgPort.portErr)
|
||||||
}
|
}
|
||||||
|
if testCase.wgOldPort.getCall {
|
||||||
|
env.EXPECT().Get("WIREGUARD_PORT", gomock.Any()).
|
||||||
|
Return(testCase.wgOldPort.getValue, testCase.wgOldPort.getErr)
|
||||||
|
}
|
||||||
|
if testCase.wgOldPort.portCall {
|
||||||
|
env.EXPECT().Port("WIREGUARD_PORT").
|
||||||
|
Return(testCase.wgOldPort.portValue, testCase.wgOldPort.portErr)
|
||||||
|
}
|
||||||
|
|
||||||
r := reader{
|
r := reader{
|
||||||
servers: allServers,
|
servers: allServers,
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/pem"
|
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/gluetun/internal/openvpn/parse"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errClientCert = errors.New("cannot read client certificate")
|
||||||
|
errClientKey = errors.New("cannot read client key")
|
||||||
)
|
)
|
||||||
|
|
||||||
func readClientKey(r reader) (clientKey string, err error) {
|
func readClientKey(r reader) (clientKey string, err error) {
|
||||||
@@ -13,22 +17,7 @@ func readClientKey(r reader) (clientKey string, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return extractClientKey(b)
|
return parse.ExtractPrivateKey(b)
|
||||||
}
|
|
||||||
|
|
||||||
var errDecodePEMBlockClientKey = errors.New("cannot decode PEM block from client key")
|
|
||||||
|
|
||||||
func extractClientKey(b []byte) (key string, err error) {
|
|
||||||
pemBlock, _ := pem.Decode(b)
|
|
||||||
if pemBlock == nil {
|
|
||||||
return "", errDecodePEMBlockClientKey
|
|
||||||
}
|
|
||||||
parsedBytes := pem.EncodeToMemory(pemBlock)
|
|
||||||
s := string(parsedBytes)
|
|
||||||
s = strings.ReplaceAll(s, "\n", "")
|
|
||||||
s = strings.TrimPrefix(s, "-----BEGIN PRIVATE KEY-----")
|
|
||||||
s = strings.TrimSuffix(s, "-----END PRIVATE KEY-----")
|
|
||||||
return s, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func readClientCertificate(r reader) (clientCertificate string, err error) {
|
func readClientCertificate(r reader) (clientCertificate string, err error) {
|
||||||
@@ -36,20 +25,5 @@ func readClientCertificate(r reader) (clientCertificate string, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return extractClientCertificate(b)
|
return parse.ExtractCert(b)
|
||||||
}
|
|
||||||
|
|
||||||
var errDecodePEMBlockClientCert = errors.New("cannot decode PEM block from client certificate")
|
|
||||||
|
|
||||||
func extractClientCertificate(b []byte) (certificate string, err error) {
|
|
||||||
pemBlock, _ := pem.Decode(b)
|
|
||||||
if pemBlock == nil {
|
|
||||||
return "", errDecodePEMBlockClientCert
|
|
||||||
}
|
|
||||||
parsedBytes := pem.EncodeToMemory(pemBlock)
|
|
||||||
s := string(parsedBytes)
|
|
||||||
s = strings.ReplaceAll(s, "\n", "")
|
|
||||||
s = strings.TrimPrefix(s, "-----BEGIN CERTIFICATE-----")
|
|
||||||
s = strings.TrimSuffix(s, "-----END CERTIFICATE-----")
|
|
||||||
return s, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,174 +0,0 @@
|
|||||||
package configuration
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_extractClientKey(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
const validPEM = `
|
|
||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
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
|
|
||||||
zZjrL52saevO25cigVl+hxcnY8DTpbk=
|
|
||||||
-----END PRIVATE KEY-----
|
|
||||||
`
|
|
||||||
const validKeyString = "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" //nolint:lll
|
|
||||||
testCases := map[string]struct {
|
|
||||||
b []byte
|
|
||||||
key string
|
|
||||||
err error
|
|
||||||
}{
|
|
||||||
"no input": {
|
|
||||||
err: errDecodePEMBlockClientKey,
|
|
||||||
},
|
|
||||||
"bad input": {
|
|
||||||
b: []byte{1, 2, 3},
|
|
||||||
err: errDecodePEMBlockClientKey,
|
|
||||||
},
|
|
||||||
"valid key": {
|
|
||||||
b: []byte(validPEM),
|
|
||||||
key: validKeyString,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for name, testCase := range testCases {
|
|
||||||
testCase := testCase
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
key, err := extractClientKey(testCase.b)
|
|
||||||
if testCase.err != nil {
|
|
||||||
require.Error(t, err)
|
|
||||||
assert.Equal(t, testCase.err.Error(), err.Error())
|
|
||||||
} else {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
assert.Equal(t, testCase.key, key)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_extractClientCertificate(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
const validPEM = `
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIGrDCCBJSgAwIBAgIEAdTnfTANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJS
|
|
||||||
TzESMBAGA1UEBxMJQnVjaGFyZXN0MRgwFgYDVQQKEw9DeWJlckdob3N0IFMuQS4x
|
|
||||||
GzAZBgNVBAMTEkN5YmVyR2hvc3QgUm9vdCBDQTEhMB8GCSqGSIb3DQEJARYSaW5m
|
|
||||||
b0BjeWJlcmdob3N0LnJvMB4XDTIwMDcwNDE1MjkzNloXDTMwMDcwMjE1MjkzNlow
|
|
||||||
fTELMAkGA1UEBhMCUk8xEjAQBgNVBAcMCUJ1Y2hhcmVzdDEYMBYGA1UECgwPQ3li
|
|
||||||
ZXJHaG9zdCBTLkEuMR0wGwYDVQQDDBRjLmoua2xhdmVyQGdtYWlsLmNvbTEhMB8G
|
|
||||||
CSqGSIb3DQEJARYSaW5mb0BjeWJlcmdob3N0LnJvMIICIjANBgkqhkiG9w0BAQEF
|
|
||||||
AAOCAg8AMIICCgKCAgEAobp2NlGUHMNBe08YEOnVG3QJjF3ZaXbRhE/II9rmtgJT
|
|
||||||
NZtDohGChvFlNRsExKzVrKxHCeuJkVffwzQ6fYk4/M1RdYLJUh0UVw3e4WdApw8E
|
|
||||||
7TJZxDYm4SHQNXUvt1Rt5TjslcXxIpDZgrMSc/kHROYEL9tdgdzPZErUJehXyJPh
|
|
||||||
EzIrzmAJh501x7WwKPz9ctSVlItyavqEWFF2vyUa6X9DYmD9mQTz5c+VXNO5DkXm
|
|
||||||
PFBIaEVDnvFtcjGJ56yEvFnWVukL+OUX7ezowrIOFOcp9udjgpeiHq+XvsQ6ER0D
|
|
||||||
Jt25MiEId3NjkxtZ8BitDftTcLN/kt81hWKT7adMVc3kpIZ80cxrwRCttMd7sHAz
|
|
||||||
KI9u7pMxv10eUOsIEY87ewBe3l6KvEnjA+9uIjim6gLLebDIaEH50Ee9PzNJ8fqQ
|
|
||||||
2u54Ab4bt00/H1sUnJ6Ss/+WsQDOK1BsPRKKcnHZntOlHrs2Tu5+txKNU2cOapI8
|
|
||||||
SjVULUNKrRXASbpfWnLUfri/HO742bJb/TjkOJcOxta3hTPFAhaRWBusVlB41XVH
|
|
||||||
euH5DAhugYXeSNK6/6Ul8YvKUNH/7QbxuGIGXfth19Xl4QLI1umyEjZopSlt3tOi
|
|
||||||
O2V1soVNSQCCfxXVoCTMESMLjhkjWdmBDhdy2GTW7S4YoJfqVKiS18rYkN7I4ZMC
|
|
||||||
AwEAAaOCATQwggEwMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMDQGCWCGSAGG+EIB
|
|
||||||
DQQnFiVDeWJlckdob3N0IEdlbmVyYXRlZCBVc2VyIENlcnRpZmljYXRlMBEGCWCG
|
|
||||||
SAGG+EIBAQQEAwIHgDAdBgNVHQ4EFgQULwUtU5s6pL2NN9gPeEnKX0dhwiswga0G
|
|
||||||
A1UdIwSBpTCBooAU6tdK1g/He5qzjeAoM5eHt4in9iWhf6R9MHsxCzAJBgNVBAYT
|
|
||||||
AlJPMRIwEAYDVQQHEwlCdWNoYXJlc3QxGDAWBgNVBAoTD0N5YmVyR2hvc3QgUy5B
|
|
||||||
LjEbMBkGA1UEAxMSQ3liZXJHaG9zdCBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJp
|
|
||||||
bmZvQGN5YmVyZ2hvc3Qucm+CCQCcVButZsQ0uzANBgkqhkiG9w0BAQsFAAOCAgEA
|
|
||||||
ystGIMYhQWaEdTqlnLCytrr8657t+PuidZMNNIaPB3wN2Fi2xKf14DTg03mqxjmP
|
|
||||||
Pb+f+PVNIOV5PdWD4jcQwOP1GEboGV0DFzlRGeAtDcvKwdee4oASJbZq1CETqDao
|
|
||||||
hQTxKEWC+UBk2F36nOaEI6Sab+Mb4cR9//PAwvzOqrXuGF5NuIOX7eFtCMQSgQq6
|
|
||||||
lRRqTQjekm0Dxigx4JA92Jo2qZRwCJ0T3IXBJGL831HCFJbDWv8PV3lsfFb/i2+v
|
|
||||||
r54uywFQVWWp18dYi97gipfuQ4zRg2Ldx5aXSmnhhKpg5ioZvtk043QofF12YORh
|
|
||||||
obElqavRbvvhZvlCouvcuoq9QKi7IPe5SJZkZ1X7ezMesCwBzwFpt6vRUAcslsNF
|
|
||||||
bcYS1iSENlY/PTcDqBhbKuc9yAhq+/aUgaY/8VF5RWVzSRZufbf3BPwOkE4K0Uyb
|
|
||||||
aobO/YX0JOkCacAD+4tdR6YSXNIMMRAOCBQvxbxFXaHzhwhzBAjdsC56FrJKwXvQ
|
|
||||||
rRLU3tF4P0zFMeNTay8uTtUXugDK7EnklLESuYdpUJ8bUMlAUhJBi6UFI9/icMud
|
|
||||||
xXvLRvhnBW9EtKib5JnVFUovcEUt+3EJbyst05nkL4YPjQS4TC9DHdo5SyRAy1Tp
|
|
||||||
iOCYTbretAFZRhh6ycUN5hBeN8GMQxiMreMtDV4PEIQ=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
`
|
|
||||||
const validCertificateString = "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" //nolint:lll
|
|
||||||
testCases := map[string]struct {
|
|
||||||
b []byte
|
|
||||||
certificate string
|
|
||||||
err error
|
|
||||||
}{
|
|
||||||
"no input": {
|
|
||||||
err: errDecodePEMBlockClientCert,
|
|
||||||
},
|
|
||||||
"bad input": {
|
|
||||||
b: []byte{1, 2, 3},
|
|
||||||
err: errDecodePEMBlockClientCert,
|
|
||||||
},
|
|
||||||
"valid key": {
|
|
||||||
b: []byte(validPEM),
|
|
||||||
certificate: validCertificateString,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for name, testCase := range testCases {
|
|
||||||
testCase := testCase
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
certificate, err := extractClientCertificate(testCase.b)
|
|
||||||
if testCase.err != nil {
|
|
||||||
require.Error(t, err)
|
|
||||||
assert.Equal(t, testCase.err.Error(), err.Error())
|
|
||||||
} else {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
assert.Equal(t, testCase.certificate, certificate)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -41,7 +41,7 @@ func (settings *Provider) readMullvad(r reader) (err error) {
|
|||||||
return fmt.Errorf("environment variable OWNED: %w", err)
|
return fmt.Errorf("environment variable OWNED: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = settings.ServerSelection.OpenVPN.readMullvad(r.env)
|
err = settings.ServerSelection.OpenVPN.readMullvad(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -49,14 +49,17 @@ func (settings *Provider) readMullvad(r reader) (err error) {
|
|||||||
return settings.ServerSelection.Wireguard.readMullvad(r.env)
|
return settings.ServerSelection.Wireguard.readMullvad(r.env)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *OpenVPNSelection) readMullvad(env params.Interface) (err error) {
|
func (settings *OpenVPNSelection) readMullvad(r reader) (err error) {
|
||||||
settings.TCP, err = readProtocol(env)
|
settings.TCP, err = readOpenVPNProtocol(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.CustomPort, err = readOpenVPNCustomPort(env, settings.TCP,
|
settings.CustomPort, err = readOpenVPNCustomPort(r, openvpnPortValidation{
|
||||||
[]uint16{80, 443, 1401}, []uint16{53, 1194, 1195, 1196, 1197, 1300, 1301, 1302, 1303, 1400})
|
tcp: settings.TCP,
|
||||||
|
allowedTCP: []uint16{80, 443, 1401},
|
||||||
|
allowedUDP: []uint16{53, 1194, 1195, 1196, 1197, 1300, 1301, 1302, 1303, 1400},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -65,7 +68,7 @@ func (settings *OpenVPNSelection) readMullvad(env params.Interface) (err error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (settings *WireguardSelection) readMullvad(env params.Interface) (err error) {
|
func (settings *WireguardSelection) readMullvad(env params.Interface) (err error) {
|
||||||
settings.CustomPort, err = readWireguardCustomPort(env, nil)
|
settings.EndpointPort, err = readWireguardCustomPort(env, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,17 +27,12 @@ func (settings *Provider) readNordvpn(r reader) (err error) {
|
|||||||
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.NordvpnHostnameChoices(servers))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("environment variable SERVER_NAME: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ServerSelection.Numbers, err = readNordVPNServerNumbers(r.env)
|
settings.ServerSelection.Numbers, err = readNordVPNServerNumbers(r.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env)
|
return settings.ServerSelection.OpenVPN.readProtocolOnly(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readNordVPNServerNumbers(env params.Interface) (numbers []uint16, err error) {
|
func readNordVPNServerNumbers(env params.Interface) (numbers []uint16, err error) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ type OpenVPN struct {
|
|||||||
Root bool `json:"run_as_root"`
|
Root bool `json:"run_as_root"`
|
||||||
Cipher string `json:"cipher"`
|
Cipher string `json:"cipher"`
|
||||||
Auth string `json:"auth"`
|
Auth string `json:"auth"`
|
||||||
Config string `json:"custom_config"`
|
ConfFile string `json:"conf_file"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
ClientCrt string `json:"-"` // Cyberghost
|
ClientCrt string `json:"-"` // Cyberghost
|
||||||
ClientKey string `json:"-"` // Cyberghost, VPNUnlimited
|
ClientKey string `json:"-"` // Cyberghost, VPNUnlimited
|
||||||
@@ -59,8 +59,8 @@ func (settings *OpenVPN) lines() (lines []string) {
|
|||||||
lines = append(lines, indent+lastIndent+"Custom auth algorithm: "+settings.Auth)
|
lines = append(lines, indent+lastIndent+"Custom auth algorithm: "+settings.Auth)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(settings.Config) > 0 {
|
if settings.ConfFile != "" {
|
||||||
lines = append(lines, indent+lastIndent+"Custom configuration: "+settings.Config)
|
lines = append(lines, indent+lastIndent+"Configuration file: "+settings.ConfFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.ClientKey != "" {
|
if settings.ClientKey != "" {
|
||||||
@@ -83,13 +83,14 @@ func (settings *OpenVPN) lines() (lines []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (settings *OpenVPN) read(r reader, serviceProvider string) (err error) {
|
func (settings *OpenVPN) read(r reader, serviceProvider string) (err error) {
|
||||||
settings.Config, err = r.env.Get("OPENVPN_CUSTOM_CONFIG", params.CaseSensitiveValue())
|
credentialsRequired := false
|
||||||
if err != nil {
|
switch serviceProvider {
|
||||||
return fmt.Errorf("environment variable OPENVPN_CUSTOM_CONFIG: %w", err)
|
case constants.Custom:
|
||||||
|
case constants.VPNUnlimited:
|
||||||
|
default:
|
||||||
|
credentialsRequired = true
|
||||||
}
|
}
|
||||||
|
|
||||||
credentialsRequired := settings.Config == "" && serviceProvider != constants.VPNUnlimited
|
|
||||||
|
|
||||||
settings.User, err = r.getFromEnvOrSecretFile("OPENVPN_USER", credentialsRequired, []string{"USER"})
|
settings.User, err = r.getFromEnvOrSecretFile("OPENVPN_USER", credentialsRequired, []string{"USER"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("environment variable OPENVPN_USER: %w", err)
|
return fmt.Errorf("environment variable OPENVPN_USER: %w", err)
|
||||||
@@ -158,16 +159,17 @@ func (settings *OpenVPN) read(r reader, serviceProvider string) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.EncPreset, err = getPIAEncryptionPreset(r)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch serviceProvider {
|
switch serviceProvider {
|
||||||
|
case constants.Custom:
|
||||||
|
err = settings.readCustom(r) // read OPENVPN_CUSTOM_CONFIG
|
||||||
case constants.Cyberghost:
|
case constants.Cyberghost:
|
||||||
err = settings.readCyberghost(r)
|
err = settings.readCyberghost(r)
|
||||||
|
case constants.PrivateInternetAccess:
|
||||||
|
settings.EncPreset, err = getPIAEncryptionPreset(r)
|
||||||
case constants.VPNUnlimited:
|
case constants.VPNUnlimited:
|
||||||
err = settings.readVPNUnlimited(r)
|
err = settings.readVPNUnlimited(r)
|
||||||
|
case constants.Wevpn:
|
||||||
|
err = settings.readWevpn(r)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -176,10 +178,11 @@ func (settings *OpenVPN) read(r reader, serviceProvider string) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readProtocol(env params.Interface) (tcp bool, err error) {
|
func readOpenVPNProtocol(r reader) (tcp bool, err error) {
|
||||||
protocol, err := env.Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, params.Default(constants.UDP))
|
protocol, err := r.env.Inside("OPENVPN_PROTOCOL", []string{constants.TCP, constants.UDP},
|
||||||
|
params.Default(constants.UDP), params.RetroKeys([]string{"PROTOCOL"}, r.onRetroActive))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("environment variable PROTOCOL: %w", err)
|
return false, fmt.Errorf("environment variable OPENVPN_PROTOCOL: %w", err)
|
||||||
}
|
}
|
||||||
return protocol == constants.TCP, nil
|
return protocol == constants.TCP, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func Test_OpenVPN_JSON(t *testing.T) {
|
|||||||
"run_as_root": true,
|
"run_as_root": true,
|
||||||
"cipher": "",
|
"cipher": "",
|
||||||
"auth": "",
|
"auth": "",
|
||||||
"custom_config": "",
|
"conf_file": "",
|
||||||
"version": "",
|
"version": "",
|
||||||
"encryption_preset": "",
|
"encryption_preset": "",
|
||||||
"ipv6": false,
|
"ipv6": false,
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func (settings *Provider) readPrivateInternetAccess(r reader) (err error) {
|
|||||||
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_NAME", constants.PIANameChoices(servers))
|
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.PIANameChoices(servers))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("environment variable SERVER_NAME: %w", err)
|
return fmt.Errorf("environment variable SERVER_NAME: %w", err)
|
||||||
}
|
}
|
||||||
@@ -53,9 +53,9 @@ func (settings *OpenVPNSelection) readPrivateInternetAccess(r reader) (err error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.CustomPort, err = readPortOrZero(r.env, "PORT")
|
settings.CustomPort, err = readOpenVPNCustomPort(r, openvpnPortValidation{allAllowed: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("environment variable PORT: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -65,7 +65,7 @@ func getPIAEncryptionPreset(r reader) (encryptionPreset string, err error) {
|
|||||||
encryptionPreset, err = r.env.Inside("PIA_ENCRYPTION",
|
encryptionPreset, err = r.env.Inside("PIA_ENCRYPTION",
|
||||||
[]string{constants.PIAEncryptionPresetNone, constants.PIAEncryptionPresetNormal, constants.PIAEncryptionPresetStrong},
|
[]string{constants.PIAEncryptionPresetNone, constants.PIAEncryptionPresetNormal, constants.PIAEncryptionPresetStrong},
|
||||||
params.RetroKeys([]string{"ENCRYPTION"}, r.onRetroActive),
|
params.RetroKeys([]string{"ENCRYPTION"}, r.onRetroActive),
|
||||||
params.Default(constants.PIACertificateStrong),
|
params.Default(constants.PIAEncryptionPresetStrong),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("environment variable PIA_ENCRYPTION: %w", err)
|
return "", fmt.Errorf("environment variable PIA_ENCRYPTION: %w", err)
|
||||||
|
|||||||
@@ -31,5 +31,5 @@ func (settings *Provider) readPrivatevpn(r reader) (err error) {
|
|||||||
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r.env)
|
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,5 +47,5 @@ func (settings *Provider) readProtonvpn(r reader) (err error) {
|
|||||||
return fmt.Errorf("environment variable FREE_ONLY: %w", err)
|
return fmt.Errorf("environment variable FREE_ONLY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r.env)
|
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,8 +49,12 @@ func (settings *Provider) read(r reader, vpnType string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch settings.Name {
|
switch settings.Name {
|
||||||
|
case constants.Custom:
|
||||||
|
err = settings.readCustom(r, vpnType)
|
||||||
case constants.Cyberghost:
|
case constants.Cyberghost:
|
||||||
err = settings.readCyberghost(r)
|
err = settings.readCyberghost(r)
|
||||||
|
case constants.Expressvpn:
|
||||||
|
err = settings.readExpressvpn(r)
|
||||||
case constants.Fastestvpn:
|
case constants.Fastestvpn:
|
||||||
err = settings.readFastestvpn(r)
|
err = settings.readFastestvpn(r)
|
||||||
case constants.HideMyAss:
|
case constants.HideMyAss:
|
||||||
@@ -81,6 +85,8 @@ func (settings *Provider) read(r reader, vpnType string) error {
|
|||||||
err = settings.readVPNUnlimited(r)
|
err = settings.readVPNUnlimited(r)
|
||||||
case constants.Vyprvpn:
|
case constants.Vyprvpn:
|
||||||
err = settings.readVyprvpn(r)
|
err = settings.readVyprvpn(r)
|
||||||
|
case constants.Wevpn:
|
||||||
|
err = settings.readWevpn(r)
|
||||||
case constants.Windscribe:
|
case constants.Windscribe:
|
||||||
err = settings.readWindscribe(r)
|
err = settings.readWindscribe(r)
|
||||||
default:
|
default:
|
||||||
@@ -99,12 +105,17 @@ func (settings *Provider) readVPNServiceProvider(r reader, vpnType string) (err
|
|||||||
switch vpnType {
|
switch vpnType {
|
||||||
case constants.OpenVPN:
|
case constants.OpenVPN:
|
||||||
allowedVPNServiceProviders = []string{
|
allowedVPNServiceProviders = []string{
|
||||||
"cyberghost", "fastestvpn", "hidemyass", "ipvanish", "ivpn", "mullvad", "nordvpn",
|
constants.Custom,
|
||||||
|
"cyberghost", constants.Expressvpn, "fastestvpn", "hidemyass", "ipvanish",
|
||||||
|
"ivpn", "mullvad", "nordvpn",
|
||||||
"privado", "pia", "private internet access", "privatevpn", "protonvpn",
|
"privado", "pia", "private internet access", "privatevpn", "protonvpn",
|
||||||
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn", "windscribe"}
|
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn",
|
||||||
|
constants.Wevpn, "windscribe"}
|
||||||
case constants.Wireguard:
|
case constants.Wireguard:
|
||||||
allowedVPNServiceProviders = []string{constants.Mullvad, constants.Windscribe,
|
allowedVPNServiceProviders = []string{
|
||||||
constants.Ivpn}
|
constants.Custom, constants.Ivpn,
|
||||||
|
constants.Mullvad, constants.Windscribe,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vpnsp, err := r.env.Inside("VPNSP", allowedVPNServiceProviders,
|
vpnsp, err := r.env.Inside("VPNSP", allowedVPNServiceProviders,
|
||||||
@@ -115,6 +126,11 @@ func (settings *Provider) readVPNServiceProvider(r reader, vpnType string) (err
|
|||||||
if vpnsp == "pia" { // retro compatibility
|
if vpnsp == "pia" { // retro compatibility
|
||||||
vpnsp = "private internet access"
|
vpnsp = "private internet access"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if settings.isOpenVPNCustomConfig(r.env) { // retro compatibility
|
||||||
|
vpnsp = constants.Custom
|
||||||
|
}
|
||||||
|
|
||||||
settings.Name = vpnsp
|
settings.Name = vpnsp
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -139,42 +155,62 @@ func readTargetIP(env params.Interface) (targetIP net.IP, err error) {
|
|||||||
return targetIP, nil
|
return targetIP, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readOpenVPNCustomPort(env params.Interface, tcp bool,
|
type openvpnPortValidation struct {
|
||||||
allowedTCP, allowedUDP []uint16) (port uint16, err error) {
|
allAllowed bool
|
||||||
port, err = readPortOrZero(env, "PORT")
|
tcp bool
|
||||||
|
allowedTCP []uint16
|
||||||
|
allowedUDP []uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func readOpenVPNCustomPort(r reader, validation openvpnPortValidation) (
|
||||||
|
port uint16, err error) {
|
||||||
|
port, err = readPortOrZero(r.env, "OPENVPN_PORT")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("environment variable PORT: %w", err)
|
return 0, fmt.Errorf("environment variable OPENVPN_PORT: %w", err)
|
||||||
} else if port == 0 {
|
} else if port == 0 {
|
||||||
return 0, nil
|
// Try using old variable name
|
||||||
|
port, err = readPortOrZero(r.env, "PORT")
|
||||||
|
if err != nil {
|
||||||
|
r.onRetroActive("PORT", "OPENVPN_PORT")
|
||||||
|
return 0, fmt.Errorf("environment variable PORT: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if tcp {
|
if port == 0 || validation.allAllowed {
|
||||||
for i := range allowedTCP {
|
return port, nil
|
||||||
if allowedTCP[i] == port {
|
}
|
||||||
|
|
||||||
|
if validation.tcp {
|
||||||
|
for _, allowedPort := range validation.allowedTCP {
|
||||||
|
if port == allowedPort {
|
||||||
return port, nil
|
return port, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0, fmt.Errorf(
|
return 0, fmt.Errorf(
|
||||||
"environment variable PORT: %w: port %d for TCP protocol, can only be one of %s",
|
"environment variable PORT: %w: port %d for TCP protocol, can only be one of %s",
|
||||||
ErrInvalidPort, port, portsToString(allowedTCP))
|
ErrInvalidPort, port, portsToString(validation.allowedTCP))
|
||||||
}
|
}
|
||||||
for i := range allowedUDP {
|
for _, allowedPort := range validation.allowedUDP {
|
||||||
if allowedUDP[i] == port {
|
if port == allowedPort {
|
||||||
return port, nil
|
return port, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0, fmt.Errorf(
|
return 0, fmt.Errorf(
|
||||||
"environment variable PORT: %w: port %d for UDP protocol, can only be one of %s",
|
"environment variable PORT: %w: port %d for UDP protocol, can only be one of %s",
|
||||||
ErrInvalidPort, port, portsToString(allowedUDP))
|
ErrInvalidPort, port, portsToString(validation.allowedUDP))
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: set allowed to an empty slice to allow all valid ports
|
// note: set allowed to an empty slice to allow all valid ports
|
||||||
func readWireguardCustomPort(env params.Interface, allowed []uint16) (port uint16, err error) {
|
func readWireguardCustomPort(env params.Interface, allowed []uint16) (port uint16, err error) {
|
||||||
port, err = readPortOrZero(env, "WIREGUARD_PORT")
|
port, err = readPortOrZero(env, "WIREGUARD_ENDPOINT_PORT")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("environment variable WIREGUARD_PORT: %w", err)
|
return 0, fmt.Errorf("environment variable WIREGUARD_ENDPOINT_PORT: %w", err)
|
||||||
} else if port == 0 {
|
} else if port == 0 {
|
||||||
return 0, nil
|
port, _ = readPortOrZero(env, "WIREGUARD_PORT")
|
||||||
|
if err == nil {
|
||||||
|
return port, nil // 0 or WIREGUARD_PORT value
|
||||||
|
}
|
||||||
|
return 0, nil // default 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(allowed) == 0 {
|
if len(allowed) == 0 {
|
||||||
@@ -199,3 +235,12 @@ func portsToString(ports []uint16) string {
|
|||||||
}
|
}
|
||||||
return strings.Join(slice, ", ")
|
return strings.Join(slice, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isOpenVPNCustomConfig is for retro compatibility to set VPNSP=custom
|
||||||
|
// if OPENVPN_CUSTOM_CONFIG is set.
|
||||||
|
func (settings Provider) isOpenVPNCustomConfig(env params.Interface) (ok bool) {
|
||||||
|
s, _ := env.Get("VPN_TYPE")
|
||||||
|
isOpenVPN := s == constants.OpenVPN
|
||||||
|
s, _ = env.Get("OPENVPN_CUSTOM_CONFIG")
|
||||||
|
return isOpenVPN && s != ""
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,14 +25,31 @@ func Test_Provider_lines(t *testing.T) {
|
|||||||
Name: constants.Cyberghost,
|
Name: constants.Cyberghost,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
VPN: constants.OpenVPN,
|
VPN: constants.OpenVPN,
|
||||||
Groups: []string{"group"},
|
Countries: []string{"a", "El country"},
|
||||||
Regions: []string{"a", "El country"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Cyberghost settings:",
|
"|--Cyberghost settings:",
|
||||||
" |--Server groups: group",
|
" |--Countries: a, El country",
|
||||||
" |--Regions: a, El country",
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"expressvpn": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Expressvpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
VPN: constants.OpenVPN,
|
||||||
|
Hostnames: []string{"a", "b"},
|
||||||
|
Countries: []string{"c", "d"},
|
||||||
|
Cities: []string{"e", "f"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Expressvpn settings:",
|
||||||
|
" |--Countries: c, d",
|
||||||
|
" |--Cities: e, f",
|
||||||
|
" |--Hostnames: a, b",
|
||||||
" |--OpenVPN selection:",
|
" |--OpenVPN selection:",
|
||||||
" |--Protocol: udp",
|
" |--Protocol: udp",
|
||||||
},
|
},
|
||||||
@@ -324,6 +341,27 @@ func Test_Provider_lines(t *testing.T) {
|
|||||||
" |--Protocol: udp",
|
" |--Protocol: udp",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"wevpn": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Wevpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
VPN: constants.OpenVPN,
|
||||||
|
Cities: []string{"a", "b"},
|
||||||
|
Hostnames: []string{"c", "d"},
|
||||||
|
OpenVPN: OpenVPNSelection{
|
||||||
|
CustomPort: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Wevpn settings:",
|
||||||
|
" |--Cities: a, b",
|
||||||
|
" |--Hostnames: c, d",
|
||||||
|
" |--OpenVPN selection:",
|
||||||
|
" |--Protocol: udp",
|
||||||
|
" |--Custom port: 1",
|
||||||
|
},
|
||||||
|
},
|
||||||
"windscribe": {
|
"windscribe": {
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Windscribe,
|
Name: constants.Windscribe,
|
||||||
@@ -372,7 +410,7 @@ func Test_readProtocol(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
"error": {
|
"error": {
|
||||||
mockErr: errDummy,
|
mockErr: errDummy,
|
||||||
err: errors.New("environment variable PROTOCOL: dummy"),
|
err: errors.New("environment variable OPENVPN_PROTOCOL: dummy"),
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
mockStr: "tcp",
|
mockStr: "tcp",
|
||||||
@@ -388,10 +426,13 @@ func Test_readProtocol(t *testing.T) {
|
|||||||
|
|
||||||
env := mock_params.NewMockInterface(ctrl)
|
env := mock_params.NewMockInterface(ctrl)
|
||||||
env.EXPECT().
|
env.EXPECT().
|
||||||
Inside("PROTOCOL", []string{"tcp", "udp"}, gomock.Any()).
|
Inside("OPENVPN_PROTOCOL", []string{"tcp", "udp"}, gomock.Any(), gomock.Any()).
|
||||||
Return(testCase.mockStr, testCase.mockErr)
|
Return(testCase.mockStr, testCase.mockErr)
|
||||||
|
reader := reader{
|
||||||
|
env: env,
|
||||||
|
}
|
||||||
|
|
||||||
tcp, err := readProtocol(env)
|
tcp, err := readOpenVPNProtocol(reader)
|
||||||
|
|
||||||
if testCase.err != nil {
|
if testCase.err != nil {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|||||||
@@ -35,5 +35,5 @@ func (settings *Provider) readPurevpn(r reader) (err error) {
|
|||||||
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env)
|
return settings.ServerSelection.OpenVPN.readProtocolOnly(r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,30 +8,38 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/golibs/logging"
|
ovpnextract "github.com/qdm12/gluetun/internal/openvpn/extract"
|
||||||
"github.com/qdm12/golibs/params"
|
"github.com/qdm12/golibs/params"
|
||||||
"github.com/qdm12/golibs/verification"
|
"github.com/qdm12/golibs/verification"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:generate mockgen -destination=warner_mock_test.go -package configuration . Warner
|
||||||
|
|
||||||
type reader struct {
|
type reader struct {
|
||||||
servers models.AllServers
|
servers models.AllServers
|
||||||
env params.Interface
|
env params.Interface
|
||||||
logger logging.Logger
|
warner Warner
|
||||||
regex verification.Regex
|
regex verification.Regex
|
||||||
|
ovpnExt ovpnextract.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
type Warner interface {
|
||||||
|
Warn(s string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newReader(env params.Interface,
|
func newReader(env params.Interface,
|
||||||
servers models.AllServers, logger logging.Logger) reader {
|
servers models.AllServers, warner Warner) reader {
|
||||||
return reader{
|
return reader{
|
||||||
servers: servers,
|
servers: servers,
|
||||||
env: env,
|
env: env,
|
||||||
logger: logger,
|
warner: warner,
|
||||||
regex: verification.NewRegex(),
|
regex: verification.NewRegex(),
|
||||||
|
ovpnExt: ovpnextract.New(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *reader) onRetroActive(oldKey, newKey string) {
|
func (r *reader) onRetroActive(oldKey, newKey string) {
|
||||||
r.logger.Warn(
|
r.warner.Warn(
|
||||||
"You are using the old environment variable " + oldKey +
|
"You are using the old environment variable " + oldKey +
|
||||||
", please consider changing it to " + newKey)
|
", please consider changing it to " + newKey)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,22 +51,22 @@ func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKey
|
|||||||
file, fileErr := os.OpenFile(filepath, os.O_RDONLY, 0)
|
file, fileErr := os.OpenFile(filepath, os.O_RDONLY, 0)
|
||||||
if os.IsNotExist(fileErr) {
|
if os.IsNotExist(fileErr) {
|
||||||
if compulsory {
|
if compulsory {
|
||||||
return "", envErr
|
return "", fmt.Errorf("environment variable %s: %w", envKey, envErr)
|
||||||
}
|
}
|
||||||
return "", nil
|
return "", nil
|
||||||
} else if fileErr != nil {
|
} else if fileErr != nil {
|
||||||
return "", fmt.Errorf("%w: %s", ErrReadSecretFile, fileErr)
|
return "", fmt.Errorf("%w: %s: %s", ErrReadSecretFile, filepath, fileErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := io.ReadAll(file)
|
b, err := io.ReadAll(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("%w: %s", ErrReadSecretFile, err)
|
return "", fmt.Errorf("%w: %s: %s", ErrReadSecretFile, filepath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
value = string(b)
|
value = string(b)
|
||||||
value = cleanSuffix(value)
|
value = cleanSuffix(value)
|
||||||
if compulsory && value == "" {
|
if compulsory && value == "" {
|
||||||
return "", ErrSecretFileIsEmpty
|
return "", fmt.Errorf("%s: %w", filepath, ErrSecretFileIsEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
return value, nil
|
return value, nil
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/golibs/params"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerSelection struct { //nolint:maligned
|
type ServerSelection struct { //nolint:maligned
|
||||||
@@ -16,14 +15,11 @@ type ServerSelection struct { //nolint:maligned
|
|||||||
// Cyberghost, PIA, Protonvpn, Surfshark, Windscribe, Vyprvpn, NordVPN
|
// Cyberghost, PIA, Protonvpn, Surfshark, Windscribe, Vyprvpn, NordVPN
|
||||||
Regions []string `json:"regions"`
|
Regions []string `json:"regions"`
|
||||||
|
|
||||||
// Cyberghost
|
// Expressvpn, Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
|
||||||
Groups []string `json:"groups"`
|
|
||||||
|
|
||||||
// Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
|
|
||||||
Countries []string `json:"countries"`
|
Countries []string `json:"countries"`
|
||||||
// HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited, Windscribe
|
// Expressvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited, WeVPN, Windscribe
|
||||||
Cities []string `json:"cities"`
|
Cities []string `json:"cities"`
|
||||||
// Fastestvpn, HideMyAss, IPVanish, IVPN, PrivateVPN, Windscribe, Privado, Protonvpn, VPNUnlimited
|
// Expressvpn, Fastestvpn, HideMyAss, IPVanish, IVPN, PrivateVPN, Windscribe, Privado, Protonvpn, VPNUnlimited, WeVPN
|
||||||
Hostnames []string `json:"hostnames"`
|
Hostnames []string `json:"hostnames"`
|
||||||
Names []string `json:"names"` // Protonvpn
|
Names []string `json:"names"` // Protonvpn
|
||||||
|
|
||||||
@@ -52,10 +48,6 @@ func (selection ServerSelection) toLines() (lines []string) {
|
|||||||
lines = append(lines, lastIndent+"Target IP address: "+selection.TargetIP.String())
|
lines = append(lines, lastIndent+"Target IP address: "+selection.TargetIP.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(selection.Groups) > 0 {
|
|
||||||
lines = append(lines, lastIndent+"Server groups: "+commaJoin(selection.Groups))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(selection.Countries) > 0 {
|
if len(selection.Countries) > 0 {
|
||||||
lines = append(lines, lastIndent+"Countries: "+commaJoin(selection.Countries))
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(selection.Countries))
|
||||||
}
|
}
|
||||||
@@ -106,14 +98,19 @@ func (selection ServerSelection) toLines() (lines []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type OpenVPNSelection struct {
|
type OpenVPNSelection struct {
|
||||||
|
ConfFile string `json:"conf_file"` // Custom configuration file path
|
||||||
TCP bool `json:"tcp"` // UDP if TCP is false
|
TCP bool `json:"tcp"` // UDP if TCP is false
|
||||||
CustomPort uint16 `json:"custom_port"` // HideMyAss, Mullvad, PIA, ProtonVPN, Windscribe
|
CustomPort uint16 `json:"custom_port"` // HideMyAss, Mullvad, PIA, ProtonVPN, WeVPN, Windscribe
|
||||||
EncPreset string `json:"encryption_preset"` // PIA - needed to get the port number
|
EncPreset string `json:"encryption_preset"` // PIA - needed to get the port number
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *OpenVPNSelection) lines() (lines []string) {
|
func (settings *OpenVPNSelection) lines() (lines []string) {
|
||||||
lines = append(lines, lastIndent+"OpenVPN selection:")
|
lines = append(lines, lastIndent+"OpenVPN selection:")
|
||||||
|
|
||||||
|
if settings.ConfFile != "" {
|
||||||
|
lines = append(lines, indent+lastIndent+"Custom configuration file: "+settings.ConfFile)
|
||||||
|
}
|
||||||
|
|
||||||
lines = append(lines, indent+lastIndent+"Protocol: "+protoToString(settings.TCP))
|
lines = append(lines, indent+lastIndent+"Protocol: "+protoToString(settings.TCP))
|
||||||
|
|
||||||
if settings.CustomPort != 0 {
|
if settings.CustomPort != 0 {
|
||||||
@@ -127,18 +124,18 @@ func (settings *OpenVPNSelection) lines() (lines []string) {
|
|||||||
return lines
|
return lines
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *OpenVPNSelection) readProtocolOnly(env params.Interface) (err error) {
|
func (settings *OpenVPNSelection) readProtocolOnly(r reader) (err error) {
|
||||||
settings.TCP, err = readProtocol(env)
|
settings.TCP, err = readOpenVPNProtocol(r)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *OpenVPNSelection) readProtocolAndPort(env params.Interface) (err error) {
|
func (settings *OpenVPNSelection) readProtocolAndPort(r reader) (err error) {
|
||||||
settings.TCP, err = readProtocol(env)
|
settings.TCP, err = readOpenVPNProtocol(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.CustomPort, err = readPortOrZero(env, "PORT")
|
settings.CustomPort, err = readOpenVPNCustomPort(r, openvpnPortValidation{allAllowed: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("environment variable PORT: %w", err)
|
return fmt.Errorf("environment variable PORT: %w", err)
|
||||||
}
|
}
|
||||||
@@ -147,14 +144,32 @@ func (settings *OpenVPNSelection) readProtocolAndPort(env params.Interface) (err
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WireguardSelection struct {
|
type WireguardSelection struct {
|
||||||
CustomPort uint16 `json:"custom_port"` // Mullvad
|
// EndpointPort is a the server port to use for the VPN server.
|
||||||
|
// It is optional for Wireguard VPN providers IVPN, Mullvad
|
||||||
|
// and Windscribe, and compulsory for the others
|
||||||
|
EndpointPort uint16 `json:"port,omitempty"`
|
||||||
|
// PublicKey is the server public key.
|
||||||
|
// It is only used with VPN providers generating Wireguard
|
||||||
|
// configurations specific to each server and user.
|
||||||
|
PublicKey string `json:"publickey,omitempty"`
|
||||||
|
// EndpointIP is the server endpoint IP address.
|
||||||
|
// It is only used with VPN providers generating Wireguard
|
||||||
|
// configurations specific to each server and user.
|
||||||
|
EndpointIP net.IP `json:"endpoint_ip,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *WireguardSelection) lines() (lines []string) {
|
func (settings *WireguardSelection) lines() (lines []string) {
|
||||||
lines = append(lines, lastIndent+"Wireguard selection:")
|
lines = append(lines, lastIndent+"Wireguard selection:")
|
||||||
|
|
||||||
if settings.CustomPort != 0 {
|
if settings.PublicKey != "" {
|
||||||
lines = append(lines, indent+lastIndent+"Custom port: "+fmt.Sprint(settings.CustomPort))
|
lines = append(lines, indent+lastIndent+"Public key: "+settings.PublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.EndpointIP != nil {
|
||||||
|
endpoint := settings.EndpointIP.String() + ":" + fmt.Sprint(settings.EndpointPort)
|
||||||
|
lines = append(lines, indent+lastIndent+"Server endpoint: "+endpoint)
|
||||||
|
} else if settings.EndpointPort != 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Custom port: "+fmt.Sprint(settings.EndpointPort))
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines
|
return lines
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func (settings *ControlServer) read(r reader) (err error) {
|
|||||||
settings.Port, warning, err = r.env.ListeningPort(
|
settings.Port, warning, err = r.env.ListeningPort(
|
||||||
"HTTP_CONTROL_SERVER_PORT", params.Default("8000"))
|
"HTTP_CONTROL_SERVER_PORT", params.Default("8000"))
|
||||||
if len(warning) > 0 {
|
if len(warning) > 0 {
|
||||||
r.logger.Warn(warning)
|
r.warner.Warn(warning)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("environment variable HTTP_CONTROL_SERVER_PORT: %w", err)
|
return fmt.Errorf("environment variable HTTP_CONTROL_SERVER_PORT: %w", err)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
"github.com/qdm12/golibs/params"
|
"github.com/qdm12/golibs/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -66,8 +65,8 @@ var (
|
|||||||
// Read obtains all configuration options for the program and returns an error as soon
|
// Read obtains all configuration options for the program and returns an error as soon
|
||||||
// as an error is encountered reading them.
|
// as an error is encountered reading them.
|
||||||
func (settings *Settings) Read(env params.Interface, servers models.AllServers,
|
func (settings *Settings) Read(env params.Interface, servers models.AllServers,
|
||||||
logger logging.Logger) (err error) {
|
warner Warner) (err error) {
|
||||||
r := newReader(env, servers, logger)
|
r := newReader(env, servers, warner)
|
||||||
|
|
||||||
settings.VersionInformation, err = r.env.OnOff("VERSION_INFORMATION", params.Default("on"))
|
settings.VersionInformation, err = r.env.OnOff("VERSION_INFORMATION", params.Default("on"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ func Test_Settings_lines(t *testing.T) {
|
|||||||
" |--Timezone: NOT SET ⚠️ - it can cause time related issues",
|
" |--Timezone: NOT SET ⚠️ - it can cause time related issues",
|
||||||
"|--Health:",
|
"|--Health:",
|
||||||
" |--Server address: ",
|
" |--Server address: ",
|
||||||
" |--OpenVPN:",
|
" |--Address to ping: ",
|
||||||
|
" |--VPN:",
|
||||||
" |--Initial duration: 0s",
|
" |--Initial duration: 0s",
|
||||||
"|--HTTP control server:",
|
"|--HTTP control server:",
|
||||||
" |--Listening port: 0",
|
" |--Listening port: 0",
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func (settings *ShadowSocks) read(r reader) (err error) {
|
|||||||
|
|
||||||
warning, err := settings.getAddress(r.env)
|
warning, err := settings.getAddress(r.env)
|
||||||
if warning != "" {
|
if warning != "" {
|
||||||
r.logger.Warn(warning)
|
r.warner.Warn(warning)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ func (settings *Provider) readSurfshark(r reader) (err error) {
|
|||||||
return fmt.Errorf("environment variable MULTIHOP_ONLY: %w", err)
|
return fmt.Errorf("environment variable MULTIHOP_ONLY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env)
|
return settings.ServerSelection.OpenVPN.readProtocolOnly(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func surfsharkRetroRegion(selection ServerSelection) (
|
func surfsharkRetroRegion(selection ServerSelection) (
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ func Test_Provider_readSurfshark(t *testing.T) {
|
|||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Surfshark,
|
Name: constants.Surfshark,
|
||||||
},
|
},
|
||||||
err: errors.New("environment variable PROTOCOL: dummy test error"),
|
err: errors.New("environment variable OPENVPN_PROTOCOL: dummy test error"),
|
||||||
},
|
},
|
||||||
"default settings": {
|
"default settings": {
|
||||||
targetIP: stringCall{call: true},
|
targetIP: stringCall{call: true},
|
||||||
@@ -214,7 +214,7 @@ func Test_Provider_readSurfshark(t *testing.T) {
|
|||||||
Return(testCase.multiHop.value, testCase.multiHop.err)
|
Return(testCase.multiHop.value, testCase.multiHop.err)
|
||||||
}
|
}
|
||||||
if testCase.protocol.call {
|
if testCase.protocol.call {
|
||||||
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
|
env.EXPECT().Inside("OPENVPN_PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
|
||||||
Return(testCase.protocol.value, testCase.protocol.err)
|
Return(testCase.protocol.value, testCase.protocol.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,5 +31,5 @@ func (settings *Provider) readTorguard(r reader) (err error) {
|
|||||||
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r.env)
|
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ type Updater struct {
|
|||||||
Period time.Duration `json:"period"`
|
Period time.Duration `json:"period"`
|
||||||
DNSAddress string `json:"dns_address"`
|
DNSAddress string `json:"dns_address"`
|
||||||
Cyberghost bool `json:"cyberghost"`
|
Cyberghost bool `json:"cyberghost"`
|
||||||
|
Expressvpn bool `json:"expressvpn"`
|
||||||
Fastestvpn bool `json:"fastestvpn"`
|
Fastestvpn bool `json:"fastestvpn"`
|
||||||
HideMyAss bool `json:"hidemyass"`
|
HideMyAss bool `json:"hidemyass"`
|
||||||
Ipvanish bool `json:"ipvanish"`
|
Ipvanish bool `json:"ipvanish"`
|
||||||
@@ -27,6 +28,7 @@ type Updater struct {
|
|||||||
Torguard bool `json:"torguard"`
|
Torguard bool `json:"torguard"`
|
||||||
VPNUnlimited bool `json:"vpnunlimited"`
|
VPNUnlimited bool `json:"vpnunlimited"`
|
||||||
Vyprvpn bool `json:"vyprvpn"`
|
Vyprvpn bool `json:"vyprvpn"`
|
||||||
|
Wevpn bool `json:"wevpn"`
|
||||||
Windscribe bool `json:"windscribe"`
|
Windscribe bool `json:"windscribe"`
|
||||||
// The two below should be used in CLI mode only
|
// The two below should be used in CLI mode only
|
||||||
CLI bool `json:"-"`
|
CLI bool `json:"-"`
|
||||||
@@ -65,6 +67,7 @@ func (settings *Updater) EnableAll() {
|
|||||||
settings.Torguard = true
|
settings.Torguard = true
|
||||||
settings.VPNUnlimited = true
|
settings.VPNUnlimited = true
|
||||||
settings.Vyprvpn = true
|
settings.Vyprvpn = true
|
||||||
|
settings.Wevpn = true
|
||||||
settings.Windscribe = true
|
settings.Windscribe = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,11 +58,9 @@ func (settings *VPN) read(r reader) (err error) {
|
|||||||
}
|
}
|
||||||
settings.Type = vpnType
|
settings.Type = vpnType
|
||||||
|
|
||||||
if !settings.isOpenVPNCustomConfig(r.env) {
|
|
||||||
if err := settings.Provider.read(r, vpnType); err != nil {
|
if err := settings.Provider.read(r, vpnType); err != nil {
|
||||||
return fmt.Errorf("%w: %s", errReadProviderSettings, err)
|
return fmt.Errorf("%w: %s", errReadProviderSettings, err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
switch settings.Type {
|
switch settings.Type {
|
||||||
case constants.OpenVPN:
|
case constants.OpenVPN:
|
||||||
@@ -79,19 +77,3 @@ func (settings *VPN) read(r reader) (err error) {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings VPN) isOpenVPNCustomConfig(env params.Interface) (ok bool) {
|
|
||||||
if settings.Type != constants.OpenVPN {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
s, err := env.Get("OPENVPN_CUSTOM_CONFIG")
|
|
||||||
return err == nil && s != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (settings VPN) VPNInterface() (intf string) {
|
|
||||||
if settings.Type == constants.Wireguard {
|
|
||||||
return settings.Wireguard.Interface
|
|
||||||
}
|
|
||||||
// OpenVPN
|
|
||||||
return settings.OpenVPN.Interface
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -42,18 +42,18 @@ func (settings *Provider) readVPNUnlimited(r reader) (err error) {
|
|||||||
return fmt.Errorf("environment variable STREAM_ONLY: %w", err)
|
return fmt.Errorf("environment variable STREAM_ONLY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env)
|
return settings.ServerSelection.OpenVPN.readProtocolOnly(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *OpenVPN) readVPNUnlimited(r reader) (err error) {
|
func (settings *OpenVPN) readVPNUnlimited(r reader) (err error) {
|
||||||
settings.ClientKey, err = readClientKey(r)
|
settings.ClientKey, err = readClientKey(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", errClientKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ClientCrt, err = readClientCertificate(r)
|
settings.ClientCrt, err = readClientCertificate(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", errClientCert, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/qdm12/gluetun/internal/configuration (interfaces: Warner)
|
||||||
|
|
||||||
|
// Package configuration is a generated GoMock package.
|
||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockWarner is a mock of Warner interface.
|
||||||
|
type MockWarner struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockWarnerMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockWarnerMockRecorder is the mock recorder for MockWarner.
|
||||||
|
type MockWarnerMockRecorder struct {
|
||||||
|
mock *MockWarner
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockWarner creates a new mock instance.
|
||||||
|
func NewMockWarner(ctrl *gomock.Controller) *MockWarner {
|
||||||
|
mock := &MockWarner{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockWarnerMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockWarner) EXPECT() *MockWarnerMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn mocks base method.
|
||||||
|
func (m *MockWarner) Warn(arg0 string) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "Warn", arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn indicates an expected call of Warn.
|
||||||
|
func (mr *MockWarnerMockRecorder) Warn(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockWarner)(nil).Warn), arg0)
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) readWevpn(r reader) (err error) {
|
||||||
|
settings.Name = constants.Wevpn
|
||||||
|
servers := r.servers.GetWevpn()
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.WevpnCityChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable CITY: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.WevpnHostnameChoices(servers))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings.ServerSelection.OpenVPN.readWevpn(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPNSelection) readWevpn(r reader) (err error) {
|
||||||
|
settings.TCP, err = readOpenVPNProtocol(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
validation := openvpnPortValidation{
|
||||||
|
tcp: settings.TCP,
|
||||||
|
allowedTCP: []uint16{53, 1195, 1199, 2018},
|
||||||
|
allowedUDP: []uint16{80, 1194, 1198},
|
||||||
|
}
|
||||||
|
settings.CustomPort, err = readOpenVPNCustomPort(r, validation)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPN) readWevpn(r reader) (err error) {
|
||||||
|
settings.ClientKey, err = readClientKey(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -32,7 +32,7 @@ func (settings *Provider) readWindscribe(r reader) (err error) {
|
|||||||
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = settings.ServerSelection.OpenVPN.readWindscribe(r.env)
|
err = settings.ServerSelection.OpenVPN.readWindscribe(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -40,15 +40,17 @@ func (settings *Provider) readWindscribe(r reader) (err error) {
|
|||||||
return settings.ServerSelection.Wireguard.readWindscribe(r.env)
|
return settings.ServerSelection.Wireguard.readWindscribe(r.env)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *OpenVPNSelection) readWindscribe(env params.Interface) (err error) {
|
func (settings *OpenVPNSelection) readWindscribe(r reader) (err error) {
|
||||||
settings.TCP, err = readProtocol(env)
|
settings.TCP, err = readOpenVPNProtocol(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.CustomPort, err = readOpenVPNCustomPort(env, settings.TCP,
|
settings.CustomPort, err = readOpenVPNCustomPort(r, openvpnPortValidation{
|
||||||
[]uint16{21, 22, 80, 123, 143, 443, 587, 1194, 3306, 8080, 54783},
|
tcp: settings.TCP,
|
||||||
[]uint16{53, 80, 123, 443, 1194, 54783})
|
allowedTCP: []uint16{21, 22, 80, 123, 143, 443, 587, 1194, 3306, 8080, 54783},
|
||||||
|
allowedUDP: []uint16{53, 80, 123, 443, 1194, 54783},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -57,7 +59,7 @@ func (settings *OpenVPNSelection) readWindscribe(env params.Interface) (err erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (settings *WireguardSelection) readWindscribe(env params.Interface) (err error) {
|
func (settings *WireguardSelection) readWindscribe(env params.Interface) (err error) {
|
||||||
settings.CustomPort, err = readWireguardCustomPort(env,
|
settings.EndpointPort, err = readWireguardCustomPort(env,
|
||||||
[]uint16{53, 80, 123, 443, 1194, 65142})
|
[]uint16{53, 80, 123, 443, 1194, 65142})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package constants
|
package constants
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -11,31 +9,14 @@ const (
|
|||||||
CyberghostCertificate = "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"
|
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(servers []models.CyberghostServer) (choices []string) {
|
func CyberghostCountryChoices(servers []models.CyberghostServer) (choices []string) {
|
||||||
choices = make([]string, len(servers))
|
choices = make([]string, len(servers))
|
||||||
for i := range servers {
|
for i := range servers {
|
||||||
choices[i] = servers[i].Region
|
choices[i] = servers[i].Country
|
||||||
}
|
}
|
||||||
return makeUnique(choices)
|
return makeUnique(choices)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CyberghostGroupChoices(servers []models.CyberghostServer) (choices []string) {
|
|
||||||
uniqueChoices := map[string]struct{}{}
|
|
||||||
for _, server := range servers {
|
|
||||||
uniqueChoices[server.Group] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
choices = make([]string, 0, len(uniqueChoices))
|
|
||||||
for choice := range uniqueChoices {
|
|
||||||
choices = append(choices, choice)
|
|
||||||
}
|
|
||||||
|
|
||||||
sortable := sort.StringSlice(choices)
|
|
||||||
sortable.Sort()
|
|
||||||
|
|
||||||
return sortable
|
|
||||||
}
|
|
||||||
|
|
||||||
func CyberghostHostnameChoices(servers []models.CyberghostServer) (choices []string) {
|
func CyberghostHostnameChoices(servers []models.CyberghostServer) (choices []string) {
|
||||||
choices = make([]string, len(servers))
|
choices = make([]string, len(servers))
|
||||||
for i := range servers {
|
for i := range servers {
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
package constants
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
"github.com/qdm12/gluetun/internal/storage"
|
|
||||||
"github.com/qdm12/golibs/logging/mock_logging"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_CyberghostGroupChoices(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
|
|
||||||
logger := mock_logging.NewMockLogger(ctrl)
|
|
||||||
logger.EXPECT().Info(gomock.Any())
|
|
||||||
|
|
||||||
storage, err := storage.New(logger, "")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
servers := storage.GetServers()
|
|
||||||
|
|
||||||
expected := []string{"Premium TCP Asia", "Premium TCP Europe",
|
|
||||||
"Premium TCP USA", "Premium UDP Asia", "Premium UDP Europe",
|
|
||||||
"Premium UDP USA"}
|
|
||||||
choices := CyberghostGroupChoices(servers.GetCyberghost())
|
|
||||||
|
|
||||||
assert.Equal(t, expected, choices)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package constants
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:lll
|
||||||
|
const (
|
||||||
|
ExpressvpnCert = "MIIDTjCCAregAwIBAgIDKzZvMA0GCSqGSIb3DQEBCwUAMIGFMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMGA1UEChMMRm9ydC1GdW5zdG9uMRgwFgYDVQQDEw9Gb3J0LUZ1bnN0b24gQ0ExITAfBgkqhkiG9w0BCQEWEm1lQG15aG9zdC5teWRvbWFpbjAgFw0xNjExMDMwMzA2MThaGA8yMDY2MTEwMzAzMDYxOFowgYoxCzAJBgNVBAYTAlZHMQwwCgYDVQQIDANCVkkxEzARBgNVBAoMCkV4cHJlc3NWUE4xEzARBgNVBAsMCkV4cHJlc3NWUE4xHDAaBgNVBAMME2V4cHJlc3N2cG5fY3VzdG9tZXIxJTAjBgkqhkiG9w0BCQEWFnN1cHBvcnRAZXhwcmVzc3Zwbi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrOYt/KOi2uMDGev3pXg8j1SO4J/4EVWDF7vJcKr2jrZlqD/zuAFx2W1YWvwumPO6PKH4PU9621aNdiumaUkv/RplCfznnnxqobhJuTE2oA+rS1bOq+9OhHwF9jgNXNVk+XX4d0toST5uGE6Z3OdmPBur8o5AlCf78PDSAwpFOw5HrgLqOEU4hTweC1/czX2VsvsHv22HRI6JMZgP8gGQii/p9iukqfaJvGdPciL5p1QRBUQIi8P8pNvEp1pVIpxYj7/LOUqb2DxFvgmp2v1IQ0Yu88SWsFk84+xAYHzfkLyS31Sqj5uLRBnJqx3fIlOihQ50GI72fwPMwo+OippvVAgMBAAGjPzA9MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgSwMB0GA1UdDgQWBBSkBM1TCX9kBgFsv2RmOzudMXa9njANBgkqhkiG9w0BAQsFAAOBgQA+2e4b+33zFmA+1ZQ46kWkfiB+fEeDyMwMLeYYyDS2d8mZhNZKdOw7dy4Ifz9Vqzp4aKuQ6j61c6k1UaQQL0tskqWVzslSFvs9NZyUAJLLdGUc5TT2MiLwiXQwd4UvH6bGeePdhvB4+ZbW7VMD7TE8hZhjhAL4F6yAP1EQvg3LDA=="
|
||||||
|
ExpressvpnRSAKey = "MIIEpAIBAAKCAQEAqzmLfyjotrjAxnr96V4PI9UjuCf+BFVgxe7yXCq9o62Zag/87gBcdltWFr8Lpjzujyh+D1PettWjXYrpmlJL/0aZQn85558aqG4SbkxNqAPq0tWzqvvToR8BfY4DVzVZPl1+HdLaEk+bhhOmdznZjwbq/KOQJQn+/Dw0gMKRTsOR64C6jhFOIU8Hgtf3M19lbL7B79th0SOiTGYD/IBkIov6fYrpKn2ibxnT3Ii+adUEQVECIvD/KTbxKdaVSKcWI+/yzlKm9g8Rb4Jqdr9SENGLvPElrBZPOPsQGB835C8kt9Uqo+bi0QZyasd3yJTooUOdBiO9n8DzMKPjoqab1QIDAQABAoIBAHgsekC0SKi+AOcNOZqJ3pxqophE0V7fQX2KWGXhxZnUZMFxGTc936deMYzjZ1y0lUa6x8cgOUcfqHol3hDmw9oWBckLHGv5Wi9umdb6DOLoZO62+FQATSdfaJ9jheq2Ub2YxsRN0apaXzB6KDKz0oM0+sZ4Udn9Kw6DfuIELRIWwEx4w0v3gKW7YLC4Jkc4AwLkPK03xEA/qImfkCmaMPLhrgVQt+IFfP8bXzL7CCC04rNU/IS8pyjex+iUolnQZlbXntF7Bm4V2mz0827ZVqrgAb/hEQRlsTW3rRkVh+rrdoUE7BCZRTFmRCbLoShjN6XuSf4sAus8ch4UEN12gN0CgYEA4o/tSvij1iPaXLmt4KOEuxqmSGB8MLKhFde8lBbNdrDgxiIH9bH7khKx15XRTX0qLDbs8b2/UJygZG0Aa1kIBqZTXTgeMAuxPRTesALJPdqQ/ROnbJcdFkI7gllrAG8VB0fH4wTRsRd0vWEB6YlCdE107u6LEsLAHxOj9Q5819cCgYEAwXjx9RkQ2qITBx5Ewib8YsltA0n3cmRomPicLlsnKV5DfvyCLpFIsZ1h3f9dUpfxRLwzp8wcoLiq9cCoOGdu1udw/yBTqmhaXWhUK/g77f9Ze2ZB1OEhuyKLYJ1vW/h/Z/a1aPCMxZqsDTPCePsuO8Cez5gqs8LjM3W7EyzRxDMCgYEAvhHrDFt975fSiLoJgo0MPIAGAnBXn+8sLwv3m/FpW+rWF8LTFK/Fku12H5wDpNOdvswxijkauIE+GiJMGMLvdcyx4WHECaC1h73reJRNykOEIZ0Md5BrCZJ1JEzp9Mo8RQhWTEFtvfkkqgApP4g0pSeaMx0StaGG1kt+4IbP+68CgYBrZdQKlquAck/Vt7u7eyDHRcE5/ilaWtqlb/xizz7h++3D5C/v4b5UumTFcyg+3RGVclPKZcfOgDSGzzeSd/hTW46iUTOgeOUQzQVMkzPRXdoyYgVRQtgSpY5xR3O1vjAbahwx8LZ0SvQPMBhYSDbV/Isr+fBacWjl/AipEEwxeQKBgQDdrAEnVlOFoCLw4sUjsPoxkLjhTAgI7CYk5NNxX67Rnj0tp+Y49+sGUhl5sCGfMKkLShiON5P2oxZa+B0aPtQjsdnsFPa1uaZkK4c++SS6AetzYRpVDLmLp7/1CulE0z3O0sBekpwiuaqLJ9ZccC81g4+2j8j6c50rIAct3hxIxw=="
|
||||||
|
ExpressvpnTLSAuthOpenvpnStaticKeyV1 = "48d9999bd71095b10649c7cb471c1051b1afdece597cea06909b99303a18c67401597b12c04a787e98cdb619ee960d90a0165529dc650f3a5c6fbe77c91c137dcf55d863fcbe314df5f0b45dbe974d9bde33ef5b4803c3985531c6c23ca6906d6cd028efc8585d1b9e71003566bd7891b9cc9212bcba510109922eed87f5c8e66d8e59cbd82575261f02777372b2cd4ca5214c4a6513ff26dd568f574fd40d6cd450fc788160ff68434ce2bf6afb00e710a3198538f14c4d45d84ab42637872e778a6b35a124e700920879f1d003ba93dccdb953cdf32bea03f365760b0ed8002098d4ce20d045b45a83a8432cc737677aed27125592a7148d25c87fdbe0a3f6"
|
||||||
|
ExpressvpnCA = "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"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExpressvpnCountriesChoices(servers []models.ExpressvpnServer) (choices []string) {
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Country
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExpressvpnCityChoices(servers []models.ExpressvpnServer) (choices []string) {
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].City
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExpressvpnHostnameChoices(servers []models.ExpressvpnServer) (choices []string) {
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Hostname
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ func FastestvpnCountriesChoices(servers []models.FastestvpnServer) (choices []st
|
|||||||
for i := range servers {
|
for i := range servers {
|
||||||
choices[i] = servers[i].Country
|
choices[i] = servers[i].Country
|
||||||
}
|
}
|
||||||
return choices
|
return makeUnique(choices)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FastestvpnHostnameChoices(servers []models.FastestvpnServer) (choices []string) {
|
func FastestvpnHostnameChoices(servers []models.FastestvpnServer) (choices []string) {
|
||||||
@@ -23,5 +23,5 @@ func FastestvpnHostnameChoices(servers []models.FastestvpnServer) (choices []str
|
|||||||
for i := range servers {
|
for i := range servers {
|
||||||
choices[i] = servers[i].Hostname
|
choices[i] = servers[i].Hostname
|
||||||
}
|
}
|
||||||
return choices
|
return makeUnique(choices)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,11 +25,3 @@ func NordvpnHostnameChoices(servers []models.NordvpnServer) (choices []string) {
|
|||||||
}
|
}
|
||||||
return makeUnique(choices)
|
return makeUnique(choices)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NordvpnNameChoices(servers []models.NordvpnServer) (choices []string) {
|
|
||||||
choices = make([]string, len(servers))
|
|
||||||
for i := range servers {
|
|
||||||
choices[i] = servers[i].Name
|
|
||||||
}
|
|
||||||
return makeUnique(choices)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,8 +6,13 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// Custom is the VPN provider name for custom
|
||||||
|
// VPN configurations.
|
||||||
|
Custom = "custom"
|
||||||
// Cyberghost is a VPN provider.
|
// Cyberghost is a VPN provider.
|
||||||
Cyberghost = "cyberghost"
|
Cyberghost = "cyberghost"
|
||||||
|
// Expressvpn is a VPN provider.
|
||||||
|
Expressvpn = "expressvpn"
|
||||||
// Fastestvpn is a VPN provider.
|
// Fastestvpn is a VPN provider.
|
||||||
Fastestvpn = "fastestvpn"
|
Fastestvpn = "fastestvpn"
|
||||||
// HideMyAss is a VPN provider.
|
// HideMyAss is a VPN provider.
|
||||||
@@ -38,6 +43,8 @@ const (
|
|||||||
VPNUnlimited = "vpn unlimited"
|
VPNUnlimited = "vpn unlimited"
|
||||||
// Vyprvpn is a VPN provider.
|
// Vyprvpn is a VPN provider.
|
||||||
Vyprvpn = "vyprvpn"
|
Vyprvpn = "vyprvpn"
|
||||||
|
// WeVPN is a VPN provider.
|
||||||
|
Wevpn = "wevpn"
|
||||||
// Windscribe is a VPN provider.
|
// Windscribe is a VPN provider.
|
||||||
Windscribe = "windscribe"
|
Windscribe = "windscribe"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package constants
|
||||||
|
|
||||||
|
import "github.com/qdm12/gluetun/internal/models"
|
||||||
|
|
||||||
|
//nolint:lll
|
||||||
|
const (
|
||||||
|
WevpnCA = "MIIDQjCCAiqgAwIBAgIUPppqnRZfvGGrT4GjXFE4Q29QzgowDQYJKoZIhvcNAQELBQAwEzERMA8GA1UEAwwIQ2hhbmdlTWUwHhcNMTkxMTA1MjMzMzIzWhcNMjkxMTAyMjMzMzIzWjATMREwDwYDVQQDDAhDaGFuZ2VNZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL5DFBJlTqhXukJFWlI8TNW9+HEQCZXhyVFvQhJFF2xIGVNx51XzqxiRANjVJZJrA68kV8az0v2Dxj0SFnRWDR6pOjjdp2CyHFcgHyfv+4MrsreAtkue86bB/1ECPWaoIwtaLnwI6SEmFZl98RlI9v4M/8IE4chOnMrM/F22+2OXI//TduvTcbyOMUiiouIP8UG1FB3J5FyuaW6qPZz2G0efDoaOI+E9LSxE87OoFrII7UqdHlWxRb3nUuPU1Ee4rN/d4tFyP4AvPKfsGhVOwyGG21IdRnbXIuDi0xytkCGOZ4j2bq5zqudnp4Izt6yJgdzZpQQWK3kSHB3qTT/Yzl8CAwEAAaOBjTCBijAdBgNVHQ4EFgQUXYkoo4WbkkvbgLVdGob9RScRf3AwTgYDVR0jBEcwRYAUXYkoo4WbkkvbgLVdGob9RScRf3ChF6QVMBMxETAPBgNVBAMMCENoYW5nZU1lghQ+mmqdFl+8YatPgaNcUThDb1DOCjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAOr1XmyWBRYfTQPNvZZ+DjCfiRYzLOi2AGefZt/jETqPDF8deVbyL1fLhXZzuX+5Etlsil3PflJjpzc/FSeZRuYRaShtwF3j6I08Eww9rBkaCnsukMUcLtMOvhdAU8dUakcRA2wkQ7Z+TWdMBv5+/6MnX10An1fIz7bAy3btMEOPTEFLo8Bst1SxJtUMaqhUteSOJ1VorpK3CWfOFaXxbJAb4E0+3zt6Vsc8mY5tt6wAi8IqiN4WD79ZdvKxENK4FMkR1kNpBY97mvdf82rzpwiBuJgN5ywmH78Ghj+9T8nI6/UIqJ1y22IRYGv6dMif8fHo5WWhCv3qmCqqY8vwuxw=="
|
||||||
|
WevpnCertificate = "MIIDTDCCAjSgAwIBAgIRAKxt8SMIXezjmHm2KDCAQdIwDQYJKoZIhvcNAQELBQAwEzERMA8GA1UEAwwIQ2hhbmdlTWUwHhcNMTkxMTA1MjMzMzI0WhcNMjkxMTAyMjMzMzI0WjAOMQwwCgYDVQQDDAN0Y3AwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvEwY2erLhMm3Mpsnybm3G6zvGyeblUAaehQVEUs+KM2/5np0Ovx0y8Iz9pIC9ITaWM0B3dM6uBsNEtylZIe4Dd9aFujunSeCFsLRf8i9AbrUombpQ6P4jzYFBxwcEw//UShwa4HZI6JuSYikdpx/dyXdBH2skahwDVc8VUFdBLLSglfKGbuzP9GsdSwQCeBRWgA3dvIzIkQkBwfnt9WQKUfRAe8e5NybaAn8Yuu9sjLkQe6eyV7toxkZTcEXdABG2vtdTEzlAsQilZzIxg3jcdeEgMgRKngng+YNP0rR5nofZ1iDlp+vBj0nuqTTJLHMrRWPIc7bdYFD/f2J49WORAgMBAAGjgZ8wgZwwCQYDVR0TBAIwADAdBgNVHQ4EFgQUmSAFmCo1FAKVq8RQF7jMxMxcMtUwTgYDVR0jBEcwRYAUXYkoo4WbkkvbgLVdGob9RScRf3ChF6QVMBMxETAPBgNVBAMMCENoYW5nZU1lghQ+mmqdFl+8YatPgaNcUThDb1DOCjATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADggEBADPqdEgL+0kou8P974QEaNg1XOAXpwP0NNqbkZ/Oj9+Lp96YAhAHOAJig+RWbBktK8zu8oUUGR1qLXAWCmirlXErVuBRnadTEh3A7SOuY02BcsYAtpQ2EU9j5K/LV7nTfagkVdWy7x/av361UD4t9fv1j4YYTh4XLRp7KVXs6AGZ7T1hqPYFMUIoPpFhPzFxH4euJjfazr4SkTR6k6Vhw3pyFd6HP65vcqpzHGxFytSa8HtltBk2DpzIf8yV9TEy+gOXFaaGss0YKQ5OU1ieqZRuLVEGiu17lByYiQGyemIETJbdkyiSg93dDJRxjaTk7c8CEdpipt07ndSIPldMtXA="
|
||||||
|
WevpnOpenvpnStaticKeyV1 = "7be66c0df0b8855e076d9e37b19f9ff3c1735ed537dee6dc786e51bdb8502f878077eeba0420a25e2b04814d22bbdcc0191a4fc396fdba1af6eb090a9d8664f18e70012ee98a2e32c28620a771d13cf3a619c417480c2c312562fffaebfd7ba73f57a28edde6c287365e6ce28291a29728da211cb53e01aa46b92f5f276c61fb46bd810b41219022c8f3d9e699fe9ade6bfcbb937fbbf6f49d741740e71c7c008a9a13c2432608038c6310b4f33588d8d234b3dffcf0823395267d73140d0e9a40e323ca92866c37073bfb072ab9de518bb9f2c65df7e219c2f114afbcf7c6e3c401cb08c3ed2901725b0601d2b5de89245719dd32506d52f149d14156215c1e"
|
||||||
|
)
|
||||||
|
|
||||||
|
func WevpnCityChoices(servers []models.WevpnServer) (choices []string) {
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].City
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WevpnHostnameChoices(servers []models.WevpnServer) (choices []string) {
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Hostname
|
||||||
|
}
|
||||||
|
return makeUnique(choices)
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
type Logger interface {
|
||||||
|
Debug(s string)
|
||||||
|
Info(s string)
|
||||||
|
Warn(s string)
|
||||||
|
Error(s string)
|
||||||
|
}
|
||||||
+19
-11
@@ -6,7 +6,15 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/golibs/logging"
|
)
|
||||||
|
|
||||||
|
type logLevel uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
levelDebug logLevel = iota
|
||||||
|
levelInfo
|
||||||
|
levelWarn
|
||||||
|
levelError
|
||||||
)
|
)
|
||||||
|
|
||||||
func (l *Loop) collectLines(ctx context.Context, done chan<- struct{},
|
func (l *Loop) collectLines(ctx context.Context, done chan<- struct{},
|
||||||
@@ -29,13 +37,13 @@ func (l *Loop) collectLines(ctx context.Context, done chan<- struct{},
|
|||||||
|
|
||||||
line, level := processLogLine(line)
|
line, level := processLogLine(line)
|
||||||
switch level {
|
switch level {
|
||||||
case logging.LevelDebug:
|
case levelDebug:
|
||||||
l.logger.Debug(line)
|
l.logger.Debug(line)
|
||||||
case logging.LevelInfo:
|
case levelInfo:
|
||||||
l.logger.Info(line)
|
l.logger.Info(line)
|
||||||
case logging.LevelWarn:
|
case levelWarn:
|
||||||
l.logger.Warn(line)
|
l.logger.Warn(line)
|
||||||
case logging.LevelError:
|
case levelError:
|
||||||
l.logger.Error(line)
|
l.logger.Error(line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,24 +51,24 @@ func (l *Loop) collectLines(ctx context.Context, done chan<- struct{},
|
|||||||
|
|
||||||
var unboundPrefix = regexp.MustCompile(`\[[0-9]{10}\] unbound\[[0-9]+:[0|1]\] `)
|
var unboundPrefix = regexp.MustCompile(`\[[0-9]{10}\] unbound\[[0-9]+:[0|1]\] `)
|
||||||
|
|
||||||
func processLogLine(s string) (filtered string, level logging.Level) {
|
func processLogLine(s string) (filtered string, level logLevel) {
|
||||||
prefix := unboundPrefix.FindString(s)
|
prefix := unboundPrefix.FindString(s)
|
||||||
filtered = s[len(prefix):]
|
filtered = s[len(prefix):]
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(filtered, "notice: "):
|
case strings.HasPrefix(filtered, "notice: "):
|
||||||
filtered = strings.TrimPrefix(filtered, "notice: ")
|
filtered = strings.TrimPrefix(filtered, "notice: ")
|
||||||
level = logging.LevelInfo
|
level = levelInfo
|
||||||
case strings.HasPrefix(filtered, "info: "):
|
case strings.HasPrefix(filtered, "info: "):
|
||||||
filtered = strings.TrimPrefix(filtered, "info: ")
|
filtered = strings.TrimPrefix(filtered, "info: ")
|
||||||
level = logging.LevelInfo
|
level = levelInfo
|
||||||
case strings.HasPrefix(filtered, "warn: "):
|
case strings.HasPrefix(filtered, "warn: "):
|
||||||
filtered = strings.TrimPrefix(filtered, "warn: ")
|
filtered = strings.TrimPrefix(filtered, "warn: ")
|
||||||
level = logging.LevelWarn
|
level = levelWarn
|
||||||
case strings.HasPrefix(filtered, "error: "):
|
case strings.HasPrefix(filtered, "error: "):
|
||||||
filtered = strings.TrimPrefix(filtered, "error: ")
|
filtered = strings.TrimPrefix(filtered, "error: ")
|
||||||
level = logging.LevelError
|
level = levelError
|
||||||
default:
|
default:
|
||||||
level = logging.LevelInfo
|
level = levelInfo
|
||||||
}
|
}
|
||||||
filtered = constants.ColorUnbound().Sprintf(filtered)
|
filtered = constants.ColorUnbound().Sprintf(filtered)
|
||||||
return filtered, level
|
return filtered, level
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package dns
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -12,30 +11,30 @@ func Test_processLogLine(t *testing.T) {
|
|||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
s string
|
s string
|
||||||
filtered string
|
filtered string
|
||||||
level logging.Level
|
level logLevel
|
||||||
}{
|
}{
|
||||||
"empty string": {"", "", logging.LevelInfo},
|
"empty string": {"", "", levelInfo},
|
||||||
"random string": {"asdasqdb", "asdasqdb", logging.LevelInfo},
|
"random string": {"asdasqdb", "asdasqdb", levelInfo},
|
||||||
"unbound notice": {
|
"unbound notice": {
|
||||||
"[1594595249] unbound[75:0] notice: init module 0: validator",
|
"[1594595249] unbound[75:0] notice: init module 0: validator",
|
||||||
"init module 0: validator",
|
"init module 0: validator",
|
||||||
logging.LevelInfo},
|
levelInfo},
|
||||||
"unbound info": {
|
"unbound info": {
|
||||||
"[1594595249] unbound[75:0] info: init module 0: validator",
|
"[1594595249] unbound[75:0] info: init module 0: validator",
|
||||||
"init module 0: validator",
|
"init module 0: validator",
|
||||||
logging.LevelInfo},
|
levelInfo},
|
||||||
"unbound warn": {
|
"unbound warn": {
|
||||||
"[1594595249] unbound[75:0] warn: init module 0: validator",
|
"[1594595249] unbound[75:0] warn: init module 0: validator",
|
||||||
"init module 0: validator",
|
"init module 0: validator",
|
||||||
logging.LevelWarn},
|
levelWarn},
|
||||||
"unbound error": {
|
"unbound error": {
|
||||||
"[1594595249] unbound[75:0] error: init module 0: validator",
|
"[1594595249] unbound[75:0] error: init module 0: validator",
|
||||||
"init module 0: validator",
|
"init module 0: validator",
|
||||||
logging.LevelError},
|
levelError},
|
||||||
"unbound unknown": {
|
"unbound unknown": {
|
||||||
"[1594595249] unbound[75:0] BLA: init module 0: validator",
|
"[1594595249] unbound[75:0] BLA: init module 0: validator",
|
||||||
"BLA: init module 0: validator",
|
"BLA: init module 0: validator",
|
||||||
logging.LevelInfo},
|
levelInfo},
|
||||||
}
|
}
|
||||||
for name, tc := range tests {
|
for name, tc := range tests {
|
||||||
tc := tc
|
tc := tc
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/dns/state"
|
"github.com/qdm12/gluetun/internal/dns/state"
|
||||||
"github.com/qdm12/gluetun/internal/loopstate"
|
"github.com/qdm12/gluetun/internal/loopstate"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Looper = (*Loop)(nil)
|
var _ Looper = (*Loop)(nil)
|
||||||
@@ -33,7 +32,7 @@ type Loop struct {
|
|||||||
resolvConf string
|
resolvConf string
|
||||||
blockBuilder blacklist.Builder
|
blockBuilder blacklist.Builder
|
||||||
client *http.Client
|
client *http.Client
|
||||||
logger logging.Logger
|
logger Logger
|
||||||
userTrigger bool
|
userTrigger bool
|
||||||
start <-chan struct{}
|
start <-chan struct{}
|
||||||
running chan<- models.LoopStatus
|
running chan<- models.LoopStatus
|
||||||
@@ -48,7 +47,7 @@ type Loop struct {
|
|||||||
const defaultBackoffTime = 10 * time.Second
|
const defaultBackoffTime = 10 * time.Second
|
||||||
|
|
||||||
func NewLoop(conf unbound.Configurator, settings configuration.DNS, client *http.Client,
|
func NewLoop(conf unbound.Configurator, settings configuration.DNS, client *http.Client,
|
||||||
logger logging.Logger) *Loop {
|
logger Logger) *Loop {
|
||||||
start := make(chan struct{})
|
start := make(chan struct{})
|
||||||
running := make(chan models.LoopStatus)
|
running := make(chan models.LoopStatus)
|
||||||
stop := make(chan struct{})
|
stop := make(chan struct{})
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/gluetun/internal/routing"
|
"github.com/qdm12/gluetun/internal/routing"
|
||||||
"github.com/qdm12/golibs/command"
|
"github.com/qdm12/golibs/command"
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Configurator = (*Config)(nil)
|
var _ Configurator = (*Config)(nil)
|
||||||
@@ -25,7 +24,7 @@ type Configurator interface {
|
|||||||
|
|
||||||
type Config struct { //nolint:maligned
|
type Config struct { //nolint:maligned
|
||||||
runner command.Runner
|
runner command.Runner
|
||||||
logger logging.Logger
|
logger Logger
|
||||||
iptablesMutex sync.Mutex
|
iptablesMutex sync.Mutex
|
||||||
ip6tablesMutex sync.Mutex
|
ip6tablesMutex sync.Mutex
|
||||||
defaultInterface string
|
defaultInterface string
|
||||||
@@ -47,7 +46,7 @@ type Config struct { //nolint:maligned
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewConfig creates a new Config instance.
|
// NewConfig creates a new Config instance.
|
||||||
func NewConfig(logger logging.Logger, runner command.Runner,
|
func NewConfig(logger Logger, runner command.Runner,
|
||||||
defaultInterface string, defaultGateway net.IP,
|
defaultInterface string, defaultGateway net.IP,
|
||||||
localNetworks []routing.LocalNetwork, localIP net.IP) *Config {
|
localNetworks []routing.LocalNetwork, localIP net.IP) *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package firewall
|
||||||
|
|
||||||
|
type Logger interface {
|
||||||
|
Debug(s string)
|
||||||
|
Info(s string)
|
||||||
|
Error(s string)
|
||||||
|
}
|
||||||
@@ -4,21 +4,17 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type handler struct {
|
type handler struct {
|
||||||
logger logging.Logger
|
|
||||||
healthErr error
|
healthErr error
|
||||||
healthErrMu sync.RWMutex
|
healthErrMu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
var errHealthcheckNotRunYet = errors.New("healthcheck did not run yet")
|
var errHealthcheckNotRunYet = errors.New("healthcheck did not run yet")
|
||||||
|
|
||||||
func newHandler(logger logging.Logger) *handler {
|
func newHandler() *handler {
|
||||||
return &handler{
|
return &handler{
|
||||||
logger: logger,
|
|
||||||
healthErr: errHealthcheckNotRunYet,
|
healthErr: errHealthcheckNotRunYet,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,6 @@ package healthcheck
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,13 +14,13 @@ func (s *Server) runHealthcheckLoop(ctx context.Context, done chan<- struct{}) {
|
|||||||
for {
|
for {
|
||||||
previousErr := s.handler.getErr()
|
previousErr := s.handler.getErr()
|
||||||
|
|
||||||
err := healthCheck(ctx, s.resolver)
|
err := healthCheck(ctx, s.pinger)
|
||||||
s.handler.setErr(err)
|
s.handler.setErr(err)
|
||||||
|
|
||||||
if previousErr != nil && err == nil {
|
if previousErr != nil && err == nil {
|
||||||
s.logger.Info("healthy!")
|
s.logger.Info("healthy!")
|
||||||
s.vpn.healthyTimer.Stop()
|
s.vpn.healthyTimer.Stop()
|
||||||
s.vpn.healthyWait = s.config.OpenVPN.Initial
|
s.vpn.healthyWait = s.config.VPN.Initial
|
||||||
} else if previousErr == nil && err != nil {
|
} else if previousErr == nil && err != nil {
|
||||||
s.logger.Info("unhealthy: " + err.Error())
|
s.logger.Info("unhealthy: " + err.Error())
|
||||||
s.vpn.healthyTimer.Stop()
|
s.vpn.healthyTimer.Stop()
|
||||||
@@ -40,7 +37,7 @@ func (s *Server) runHealthcheckLoop(ctx context.Context, done chan<- struct{}) {
|
|||||||
return
|
return
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
case <-s.vpn.healthyTimer.C:
|
case <-s.vpn.healthyTimer.C:
|
||||||
s.onUnhealthyOpenvpn(ctx)
|
s.onUnhealthyVPN(ctx)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -59,20 +56,23 @@ func (s *Server) runHealthcheckLoop(ctx context.Context, done chan<- struct{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
func healthCheck(ctx context.Context, pinger Pinger) (err error) {
|
||||||
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
|
// TODO use mullvad API if current provider is Mullvad
|
||||||
const domainToResolve = "github.com"
|
// If we run without root, you need to run this on the gluetun binary:
|
||||||
ips, err := resolver.LookupIP(ctx, "ip", domainToResolve)
|
// setcap cap_net_raw=+ep /path/to/your/compiled/binary
|
||||||
switch {
|
// Alternatively, we could have a separate binary just for healthcheck to
|
||||||
case err != nil:
|
// reduce attack surface.
|
||||||
|
errCh := make(chan error)
|
||||||
|
go func() {
|
||||||
|
errCh <- pinger.Run()
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
pinger.Stop()
|
||||||
|
<-errCh
|
||||||
|
return ctx.Err()
|
||||||
|
case err = <-errCh:
|
||||||
return err
|
return err
|
||||||
case len(ips) == 0:
|
|
||||||
return fmt.Errorf("%w for %s", errNoIPResolved, domainToResolve)
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
//go:build integration
|
||||||
|
|
||||||
|
package healthcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_healthCheck_ping(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const timeout = time.Second
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
address string
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
"github.com": {
|
||||||
|
address: "github.com",
|
||||||
|
},
|
||||||
|
"99.99.99.99": {
|
||||||
|
address: "99.99.99.99",
|
||||||
|
err: context.DeadlineExceeded,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
pinger := newPinger(testCase.address)
|
||||||
|
|
||||||
|
err := healthCheck(ctx, pinger)
|
||||||
|
|
||||||
|
assert.ErrorIs(t, testCase.err, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package healthcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_healthCheck(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
canceledCtx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
someErr := errors.New("error")
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
ctx context.Context
|
||||||
|
runErr error
|
||||||
|
stopCall bool
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
"success": {
|
||||||
|
ctx: context.Background(),
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
ctx: context.Background(),
|
||||||
|
runErr: someErr,
|
||||||
|
err: someErr,
|
||||||
|
},
|
||||||
|
"context canceled": {
|
||||||
|
ctx: canceledCtx,
|
||||||
|
stopCall: true,
|
||||||
|
err: context.Canceled,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
|
||||||
|
stopped := make(chan struct{})
|
||||||
|
|
||||||
|
pinger := NewMockPinger(ctrl)
|
||||||
|
pinger.EXPECT().Run().DoAndReturn(func() error {
|
||||||
|
if testCase.stopCall {
|
||||||
|
<-stopped
|
||||||
|
}
|
||||||
|
return testCase.runErr
|
||||||
|
})
|
||||||
|
|
||||||
|
if testCase.stopCall {
|
||||||
|
pinger.EXPECT().Stop().DoAndReturn(func() {
|
||||||
|
close(stopped)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
err := healthCheck(testCase.ctx, pinger)
|
||||||
|
|
||||||
|
assert.ErrorIs(t, testCase.err, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("canceled real pinger", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
pinger := newPinger("github.com")
|
||||||
|
|
||||||
|
canceledCtx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
err := healthCheck(canceledCtx, pinger)
|
||||||
|
|
||||||
|
assert.ErrorIs(t, context.Canceled, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ping 127.0.0.1", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
pinger := newPinger("127.0.0.1")
|
||||||
|
|
||||||
|
const timeout = 100 * time.Millisecond
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
err := healthCheck(ctx, pinger)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package healthcheck
|
||||||
|
|
||||||
|
type Logger interface {
|
||||||
|
Info(s string)
|
||||||
|
Error(s string)
|
||||||
|
}
|
||||||
@@ -14,11 +14,11 @@ type vpnHealth struct {
|
|||||||
healthyTimer *time.Timer
|
healthyTimer *time.Timer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) onUnhealthyOpenvpn(ctx context.Context) {
|
func (s *Server) onUnhealthyVPN(ctx context.Context) {
|
||||||
s.logger.Info("program has been unhealthy for " +
|
s.logger.Info("program has been unhealthy for " +
|
||||||
s.vpn.healthyWait.String() + ": restarting OpenVPN")
|
s.vpn.healthyWait.String() + ": restarting VPN")
|
||||||
_, _ = s.vpn.looper.ApplyStatus(ctx, constants.Stopped)
|
_, _ = s.vpn.looper.ApplyStatus(ctx, constants.Stopped)
|
||||||
_, _ = s.vpn.looper.ApplyStatus(ctx, constants.Running)
|
_, _ = s.vpn.looper.ApplyStatus(ctx, constants.Running)
|
||||||
s.vpn.healthyWait += s.config.OpenVPN.Addition
|
s.vpn.healthyWait += s.config.VPN.Addition
|
||||||
s.vpn.healthyTimer = time.NewTimer(s.vpn.healthyWait)
|
s.vpn.healthyTimer = time.NewTimer(s.vpn.healthyWait)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package healthcheck
|
||||||
|
|
||||||
|
import "github.com/go-ping/ping"
|
||||||
|
|
||||||
|
//go:generate mockgen -destination=pinger_mock_test.go -package healthcheck . Pinger
|
||||||
|
|
||||||
|
type Pinger interface {
|
||||||
|
Run() error
|
||||||
|
Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPinger(addrToPing string) (pinger *ping.Pinger) {
|
||||||
|
const count = 1
|
||||||
|
pinger = ping.New(addrToPing)
|
||||||
|
pinger.Count = count
|
||||||
|
pinger.SetPrivileged(true) // see https://github.com/go-ping/ping#linux
|
||||||
|
return pinger
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/qdm12/gluetun/internal/healthcheck (interfaces: Pinger)
|
||||||
|
|
||||||
|
// Package healthcheck is a generated GoMock package.
|
||||||
|
package healthcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockPinger is a mock of Pinger interface.
|
||||||
|
type MockPinger struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockPingerMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockPingerMockRecorder is the mock recorder for MockPinger.
|
||||||
|
type MockPingerMockRecorder struct {
|
||||||
|
mock *MockPinger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockPinger creates a new mock instance.
|
||||||
|
func NewMockPinger(ctrl *gomock.Controller) *MockPinger {
|
||||||
|
mock := &MockPinger{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockPingerMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockPinger) EXPECT() *MockPingerMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run mocks base method.
|
||||||
|
func (m *MockPinger) Run() error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Run")
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run indicates an expected call of Run.
|
||||||
|
func (mr *MockPingerMockRecorder) Run() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockPinger)(nil).Run))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop mocks base method.
|
||||||
|
func (m *MockPinger) Stop() {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "Stop")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop indicates an expected call of Stop.
|
||||||
|
func (mr *MockPingerMockRecorder) Stop() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockPinger)(nil).Stop))
|
||||||
|
}
|
||||||
@@ -2,11 +2,9 @@ package healthcheck
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration"
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
"github.com/qdm12/gluetun/internal/vpn"
|
"github.com/qdm12/gluetun/internal/vpn"
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ ServerRunner = (*Server)(nil)
|
var _ ServerRunner = (*Server)(nil)
|
||||||
@@ -16,23 +14,23 @@ type ServerRunner interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
logger logging.Logger
|
logger Logger
|
||||||
handler *handler
|
handler *handler
|
||||||
resolver *net.Resolver
|
pinger Pinger
|
||||||
config configuration.Health
|
config configuration.Health
|
||||||
vpn vpnHealth
|
vpn vpnHealth
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(config configuration.Health,
|
func NewServer(config configuration.Health,
|
||||||
logger logging.Logger, vpnLooper vpn.Looper) *Server {
|
logger Logger, vpnLooper vpn.Looper) *Server {
|
||||||
return &Server{
|
return &Server{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
handler: newHandler(logger),
|
handler: newHandler(),
|
||||||
resolver: net.DefaultResolver,
|
pinger: newPinger(config.AddressToPing),
|
||||||
config: config,
|
config: config,
|
||||||
vpn: vpnHealth{
|
vpn: vpnHealth{
|
||||||
looper: vpnLooper,
|
looper: vpnLooper,
|
||||||
healthyWait: config.OpenVPN.Initial,
|
healthyWait: config.VPN.Initial,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newHandler(ctx context.Context, wg *sync.WaitGroup, logger logging.Logger,
|
func newHandler(ctx context.Context, wg *sync.WaitGroup, logger Logger,
|
||||||
stealth, verbose bool, username, password string) http.Handler {
|
stealth, verbose bool, username, password string) http.Handler {
|
||||||
const httpTimeout = 24 * time.Hour
|
const httpTimeout = 24 * time.Hour
|
||||||
return &handler{
|
return &handler{
|
||||||
@@ -30,7 +28,7 @@ type handler struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
wg *sync.WaitGroup
|
wg *sync.WaitGroup
|
||||||
client *http.Client
|
client *http.Client
|
||||||
logger logging.Logger
|
logger Logger
|
||||||
verbose, stealth bool
|
verbose, stealth bool
|
||||||
username, password string
|
username, password string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package httpproxy
|
||||||
|
|
||||||
|
type Logger interface {
|
||||||
|
Debug(s string)
|
||||||
|
infoer
|
||||||
|
Warn(s string)
|
||||||
|
errorer
|
||||||
|
}
|
||||||
|
|
||||||
|
type infoErrorer interface {
|
||||||
|
infoer
|
||||||
|
errorer
|
||||||
|
}
|
||||||
|
|
||||||
|
type infoer interface {
|
||||||
|
Info(s string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorer interface {
|
||||||
|
Error(s string)
|
||||||
|
}
|
||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/httpproxy/state"
|
"github.com/qdm12/gluetun/internal/httpproxy/state"
|
||||||
"github.com/qdm12/gluetun/internal/loopstate"
|
"github.com/qdm12/gluetun/internal/loopstate"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Looper = (*Loop)(nil)
|
var _ Looper = (*Loop)(nil)
|
||||||
@@ -26,7 +25,7 @@ type Loop struct {
|
|||||||
statusManager loopstate.Manager
|
statusManager loopstate.Manager
|
||||||
state state.Manager
|
state state.Manager
|
||||||
// Other objects
|
// Other objects
|
||||||
logger logging.Logger
|
logger Logger
|
||||||
// Internal channels and locks
|
// Internal channels and locks
|
||||||
running chan models.LoopStatus
|
running chan models.LoopStatus
|
||||||
stop, stopped chan struct{}
|
stop, stopped chan struct{}
|
||||||
@@ -37,7 +36,7 @@ type Loop struct {
|
|||||||
|
|
||||||
const defaultBackoffTime = 10 * time.Second
|
const defaultBackoffTime = 10 * time.Second
|
||||||
|
|
||||||
func NewLoop(logger logging.Logger, settings configuration.HTTPProxy) *Loop {
|
func NewLoop(logger Logger, settings configuration.HTTPProxy) *Loop {
|
||||||
start := make(chan struct{})
|
start := make(chan struct{})
|
||||||
running := make(chan models.LoopStatus)
|
running := make(chan models.LoopStatus)
|
||||||
stop := make(chan struct{})
|
stop := make(chan struct{})
|
||||||
|
|||||||
@@ -5,18 +5,16 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
address string
|
address string
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
logger logging.Logger
|
logger infoErrorer
|
||||||
internalWG *sync.WaitGroup
|
internalWG *sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ctx context.Context, address string, logger logging.Logger,
|
func New(ctx context.Context, address string, logger Logger,
|
||||||
stealth, verbose bool, username, password string) *Server {
|
stealth, verbose bool, username, password string) *Server {
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
return &Server{
|
return &Server{
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
func (a AllServers) GetCopy() (servers AllServers) {
|
func (a AllServers) GetCopy() (servers AllServers) {
|
||||||
servers = a // copy versions and timestamps
|
servers = a // copy versions and timestamps
|
||||||
servers.Cyberghost.Servers = a.GetCyberghost()
|
servers.Cyberghost.Servers = a.GetCyberghost()
|
||||||
|
servers.Expressvpn.Servers = a.GetExpressvpn()
|
||||||
servers.Fastestvpn.Servers = a.GetFastestvpn()
|
servers.Fastestvpn.Servers = a.GetFastestvpn()
|
||||||
servers.HideMyAss.Servers = a.GetHideMyAss()
|
servers.HideMyAss.Servers = a.GetHideMyAss()
|
||||||
servers.Ipvanish.Servers = a.GetIpvanish()
|
servers.Ipvanish.Servers = a.GetIpvanish()
|
||||||
@@ -38,6 +39,18 @@ func (a *AllServers) GetCyberghost() (servers []CyberghostServer) {
|
|||||||
return servers
|
return servers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *AllServers) GetExpressvpn() (servers []ExpressvpnServer) {
|
||||||
|
if a.Expressvpn.Servers == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
servers = make([]ExpressvpnServer, len(a.Expressvpn.Servers))
|
||||||
|
for i, serverToCopy := range a.Expressvpn.Servers {
|
||||||
|
servers[i] = serverToCopy
|
||||||
|
servers[i].IPs = copyIPs(serverToCopy.IPs)
|
||||||
|
}
|
||||||
|
return servers
|
||||||
|
}
|
||||||
|
|
||||||
func (a *AllServers) GetFastestvpn() (servers []FastestvpnServer) {
|
func (a *AllServers) GetFastestvpn() (servers []FastestvpnServer) {
|
||||||
if a.Fastestvpn.Servers == nil {
|
if a.Fastestvpn.Servers == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -220,6 +233,18 @@ func (a *AllServers) GetVyprvpn() (servers []VyprvpnServer) {
|
|||||||
return servers
|
return servers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *AllServers) GetWevpn() (servers []WevpnServer) {
|
||||||
|
if a.Windscribe.Servers == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
servers = make([]WevpnServer, len(a.Wevpn.Servers))
|
||||||
|
for i, serverToCopy := range a.Wevpn.Servers {
|
||||||
|
servers[i] = serverToCopy
|
||||||
|
servers[i].IPs = copyIPs(serverToCopy.IPs)
|
||||||
|
}
|
||||||
|
return servers
|
||||||
|
}
|
||||||
|
|
||||||
func (a *AllServers) GetWindscribe() (servers []WindscribeServer) {
|
func (a *AllServers) GetWindscribe() (servers []WindscribeServer) {
|
||||||
if a.Windscribe.Servers == nil {
|
if a.Windscribe.Servers == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ func Test_AllServers_GetCopy(t *testing.T) {
|
|||||||
IPs: []net.IP{{1, 2, 3, 4}},
|
IPs: []net.IP{{1, 2, 3, 4}},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
Expressvpn: ExpressvpnServers{
|
||||||
|
Servers: []ExpressvpnServer{{
|
||||||
|
IPs: []net.IP{{1, 2, 3, 4}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
Fastestvpn: FastestvpnServers{
|
Fastestvpn: FastestvpnServers{
|
||||||
Servers: []FastestvpnServer{{
|
Servers: []FastestvpnServer{{
|
||||||
IPs: []net.IP{{1, 2, 3, 4}},
|
IPs: []net.IP{{1, 2, 3, 4}},
|
||||||
|
|||||||
@@ -0,0 +1,280 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func boolToMarkdown(b bool) string {
|
||||||
|
if b {
|
||||||
|
return "✅"
|
||||||
|
}
|
||||||
|
return "❎"
|
||||||
|
}
|
||||||
|
|
||||||
|
func markdownTableHeading(legendFields ...string) (markdown string) {
|
||||||
|
return "| " + strings.Join(legendFields, " | ") + " |\n" +
|
||||||
|
"|" + strings.Repeat(" --- |", len(legendFields)) + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CyberghostServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s CyberghostServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | `%s` | %s | %s |", s.Country, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ExpressvpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "City", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ExpressvpnServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | `%s` | %s | %s |",
|
||||||
|
s.Country, s.City, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FastestvpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FastestvpnServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | `%s` | %s | %s |",
|
||||||
|
s.Country, s.Hostname, boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HideMyAssServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "Region", "City", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HideMyAssServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | %s | `%s` | %s | %s |",
|
||||||
|
s.Country, s.Region, s.City, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IpvanishServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "City", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IpvanishServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | `%s` | %s | %s |",
|
||||||
|
s.Country, s.City, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IvpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "City", "ISP", "Hostname", "VPN", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IvpnServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | %s | `%s` | %s | %s | %s |",
|
||||||
|
s.Country, s.City, s.ISP, s.Hostname, s.VPN,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MullvadServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "City", "ISP", "Owned",
|
||||||
|
"Hostname", "VPN")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MullvadServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | %s | %s | `%s` | %s |",
|
||||||
|
s.Country, s.City, s.ISP, boolToMarkdown(s.Owned),
|
||||||
|
s.Hostname, s.VPN)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NordvpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Region", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NordvpnServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | `%s` | %s | %s |",
|
||||||
|
s.Region, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PrivadoServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "Region", "City", "Hostname")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PrivadoServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | %s | `%s` |",
|
||||||
|
s.Country, s.Region, s.City, s.Hostname)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PiaServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Region", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PIAServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | `%s` | %s | %s |",
|
||||||
|
s.Region, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PrivatevpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "City", "Hostname")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PrivatevpnServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | `%s` |",
|
||||||
|
s.Country, s.City, s.Hostname)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProtonvpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "Region", "City", "Hostname", "Free tier")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProtonvpnServer) ToMarkdown() (markdown string) {
|
||||||
|
isFree := strings.Contains(strings.ToLower(s.Name), "free")
|
||||||
|
return fmt.Sprintf("| %s | %s | %s | `%s` | %s |",
|
||||||
|
s.Country, s.Region, s.City, s.Hostname, boolToMarkdown(isFree))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurevpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "Region", "City", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurevpnServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | %s | `%s` | %s | %s |",
|
||||||
|
s.Country, s.Region, s.City, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SurfsharkServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Region", "Country", "City", "Hostname", "Multi-hop", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SurfsharkServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | %s | `%s` | %s | %s | %s |",
|
||||||
|
s.Region, s.Country, s.City, s.Hostname, boolToMarkdown(s.MultiHop),
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TorguardServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "City", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TorguardServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | `%s` | %s | %s |",
|
||||||
|
s.Country, s.City, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *VPNUnlimitedServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "City", "Hostname", "Free tier", "Streaming", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *VPNUnlimitedServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | `%s` | %s | %s | %s | %s |",
|
||||||
|
s.Country, s.City, s.Hostname,
|
||||||
|
boolToMarkdown(s.Free), boolToMarkdown(s.Stream),
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *VyprvpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Region", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *VyprvpnServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | `%s` | %s | %s |",
|
||||||
|
s.Region, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WevpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("City", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WevpnServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | `%s` | %s | %s |",
|
||||||
|
s.City, s.Hostname, boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WindscribeServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Region", "City", "Hostname", "VPN")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WindscribeServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | `%s` | %s |",
|
||||||
|
s.Region, s.City, s.Hostname, s.VPN)
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_CyberghostServers_ToMarkdown(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
servers := CyberghostServers{
|
||||||
|
Servers: []CyberghostServer{
|
||||||
|
{Country: "a", UDP: true, Hostname: "xa"},
|
||||||
|
{Country: "b", TCP: true, Hostname: "xb"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
markdown := servers.ToMarkdown()
|
||||||
|
const expected = "| Country | Hostname | TCP | UDP |\n" +
|
||||||
|
"| --- | --- | --- | --- |\n" +
|
||||||
|
"| a | `xa` | ❎ | ✅ |\n" +
|
||||||
|
"| b | `xb` | ✅ | ❎ |\n"
|
||||||
|
|
||||||
|
assert.Equal(t, expected, markdown)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_FastestvpnServers_ToMarkdown(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
servers := FastestvpnServers{
|
||||||
|
Servers: []FastestvpnServer{
|
||||||
|
{Country: "a", Hostname: "xa", TCP: true},
|
||||||
|
{Country: "b", Hostname: "xb", UDP: true},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
markdown := servers.ToMarkdown()
|
||||||
|
const expected = "| Country | Hostname | TCP | UDP |\n" +
|
||||||
|
"| --- | --- | --- | --- |\n" +
|
||||||
|
"| a | `xa` | ✅ | ❎ |\n" +
|
||||||
|
"| b | `xb` | ❎ | ✅ |\n"
|
||||||
|
|
||||||
|
assert.Equal(t, expected, markdown)
|
||||||
|
}
|
||||||
@@ -5,9 +5,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type CyberghostServer struct {
|
type CyberghostServer struct {
|
||||||
Region string `json:"region"`
|
Country string `json:"country"`
|
||||||
Group string `json:"group"`
|
|
||||||
Hostname string `json:"hostname"`
|
Hostname string `json:"hostname"`
|
||||||
|
TCP bool `json:"tcp"`
|
||||||
|
UDP bool `json:"udp"`
|
||||||
|
IPs []net.IP `json:"ips"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExpressvpnServer struct {
|
||||||
|
Country string `json:"country"`
|
||||||
|
City string `json:"city,omitempty"`
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
TCP bool `json:"tcp"`
|
||||||
|
UDP bool `json:"udp"`
|
||||||
IPs []net.IP `json:"ips"`
|
IPs []net.IP `json:"ips"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +75,6 @@ type MullvadServer struct {
|
|||||||
type NordvpnServer struct { //nolint:maligned
|
type NordvpnServer struct { //nolint:maligned
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
Hostname string `json:"hostname"`
|
Hostname string `json:"hostname"`
|
||||||
Name string `json:"name"`
|
|
||||||
Number uint16 `json:"number"`
|
Number uint16 `json:"number"`
|
||||||
IP net.IP `json:"ip"`
|
IP net.IP `json:"ip"`
|
||||||
TCP bool `json:"tcp"`
|
TCP bool `json:"tcp"`
|
||||||
@@ -157,6 +166,14 @@ type VyprvpnServer struct {
|
|||||||
IPs []net.IP `json:"ips"`
|
IPs []net.IP `json:"ips"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WevpnServer struct {
|
||||||
|
City string `json:"city"`
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
TCP bool `json:"tcp"`
|
||||||
|
UDP bool `json:"udp"`
|
||||||
|
IPs []net.IP `json:"ips"`
|
||||||
|
}
|
||||||
|
|
||||||
type WindscribeServer struct {
|
type WindscribeServer struct {
|
||||||
VPN string `json:"vpn"`
|
VPN string `json:"vpn"`
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package models
|
|||||||
type AllServers struct {
|
type AllServers struct {
|
||||||
Version uint16 `json:"version"` // used for migration of the top level scheme
|
Version uint16 `json:"version"` // used for migration of the top level scheme
|
||||||
Cyberghost CyberghostServers `json:"cyberghost"`
|
Cyberghost CyberghostServers `json:"cyberghost"`
|
||||||
|
Expressvpn ExpressvpnServers `json:"expressvpn"`
|
||||||
Fastestvpn FastestvpnServers `json:"fastestvpn"`
|
Fastestvpn FastestvpnServers `json:"fastestvpn"`
|
||||||
HideMyAss HideMyAssServers `json:"hidemyass"`
|
HideMyAss HideMyAssServers `json:"hidemyass"`
|
||||||
Ipvanish IpvanishServers `json:"ipvanish"`
|
Ipvanish IpvanishServers `json:"ipvanish"`
|
||||||
@@ -18,11 +19,13 @@ type AllServers struct {
|
|||||||
Torguard TorguardServers `json:"torguard"`
|
Torguard TorguardServers `json:"torguard"`
|
||||||
VPNUnlimited VPNUnlimitedServers `json:"vpnunlimited"`
|
VPNUnlimited VPNUnlimitedServers `json:"vpnunlimited"`
|
||||||
Vyprvpn VyprvpnServers `json:"vyprvpn"`
|
Vyprvpn VyprvpnServers `json:"vyprvpn"`
|
||||||
|
Wevpn WevpnServers `json:"wevpn"`
|
||||||
Windscribe WindscribeServers `json:"windscribe"`
|
Windscribe WindscribeServers `json:"windscribe"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AllServers) Count() int {
|
func (a *AllServers) Count() int {
|
||||||
return len(a.Cyberghost.Servers) +
|
return len(a.Cyberghost.Servers) +
|
||||||
|
len(a.Expressvpn.Servers) +
|
||||||
len(a.Fastestvpn.Servers) +
|
len(a.Fastestvpn.Servers) +
|
||||||
len(a.HideMyAss.Servers) +
|
len(a.HideMyAss.Servers) +
|
||||||
len(a.Ipvanish.Servers) +
|
len(a.Ipvanish.Servers) +
|
||||||
@@ -38,6 +41,7 @@ func (a *AllServers) Count() int {
|
|||||||
len(a.Torguard.Servers) +
|
len(a.Torguard.Servers) +
|
||||||
len(a.VPNUnlimited.Servers) +
|
len(a.VPNUnlimited.Servers) +
|
||||||
len(a.Vyprvpn.Servers) +
|
len(a.Vyprvpn.Servers) +
|
||||||
|
len(a.Wevpn.Servers) +
|
||||||
len(a.Windscribe.Servers)
|
len(a.Windscribe.Servers)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +50,11 @@ type CyberghostServers struct {
|
|||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
Servers []CyberghostServer `json:"servers"`
|
Servers []CyberghostServer `json:"servers"`
|
||||||
}
|
}
|
||||||
|
type ExpressvpnServers struct {
|
||||||
|
Version uint16 `json:"version"`
|
||||||
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
Servers []ExpressvpnServer `json:"servers"`
|
||||||
|
}
|
||||||
type FastestvpnServers struct {
|
type FastestvpnServers struct {
|
||||||
Version uint16 `json:"version"`
|
Version uint16 `json:"version"`
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
@@ -121,6 +130,11 @@ type VyprvpnServers struct {
|
|||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
Servers []VyprvpnServer `json:"servers"`
|
Servers []VyprvpnServer `json:"servers"`
|
||||||
}
|
}
|
||||||
|
type WevpnServers struct {
|
||||||
|
Version uint16 `json:"version"`
|
||||||
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
Servers []WevpnServer `json:"servers"`
|
||||||
|
}
|
||||||
type WindscribeServers struct {
|
type WindscribeServers struct {
|
||||||
Version uint16 `json:"version"`
|
Version uint16 `json:"version"`
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ type Addresser interface {
|
|||||||
AddrAdd(link netlink.Link, addr *netlink.Addr) error
|
AddrAdd(link netlink.Link, addr *netlink.Addr) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) AddrList(link netlink.Link, family int) (
|
func (n *NetLink) AddrList(link Link, family int) (
|
||||||
addresses []netlink.Addr, err error) {
|
addresses []Addr, err error) {
|
||||||
return netlink.AddrList(link, family)
|
return netlink.AddrList(link, family)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) AddrAdd(link netlink.Link, addr *netlink.Addr) error {
|
func (n *NetLink) AddrAdd(link Link, addr *Addr) error {
|
||||||
return netlink.AddrAdd(link, addr)
|
return netlink.AddrAdd(link, addr)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ import "github.com/vishvananda/netlink"
|
|||||||
const (
|
const (
|
||||||
FAMILY_ALL = netlink.FAMILY_ALL
|
FAMILY_ALL = netlink.FAMILY_ALL
|
||||||
FAMILY_V4 = netlink.FAMILY_V4
|
FAMILY_V4 = netlink.FAMILY_V4
|
||||||
|
FAMILY_V6 = netlink.FAMILY_V6
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,30 +19,30 @@ type Linker interface {
|
|||||||
LinkSetDown(link netlink.Link) (err error)
|
LinkSetDown(link netlink.Link) (err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) LinkList() (links []netlink.Link, err error) {
|
func (n *NetLink) LinkList() (links []Link, err error) {
|
||||||
return netlink.LinkList()
|
return netlink.LinkList()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) LinkByName(name string) (link netlink.Link, err error) {
|
func (n *NetLink) LinkByName(name string) (link Link, err error) {
|
||||||
return netlink.LinkByName(name)
|
return netlink.LinkByName(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) LinkByIndex(index int) (link netlink.Link, err error) {
|
func (n *NetLink) LinkByIndex(index int) (link Link, err error) {
|
||||||
return netlink.LinkByIndex(index)
|
return netlink.LinkByIndex(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) LinkAdd(link netlink.Link) (err error) {
|
func (n *NetLink) LinkAdd(link Link) (err error) {
|
||||||
return netlink.LinkAdd(link)
|
return netlink.LinkAdd(link)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) LinkDel(link netlink.Link) (err error) {
|
func (n *NetLink) LinkDel(link Link) (err error) {
|
||||||
return netlink.LinkDel(link)
|
return netlink.LinkDel(link)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) LinkSetUp(link netlink.Link) (err error) {
|
func (n *NetLink) LinkSetUp(link Link) (err error) {
|
||||||
return netlink.LinkSetUp(link)
|
return netlink.LinkSetUp(link)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) LinkSetDown(link netlink.Link) (err error) {
|
func (n *NetLink) LinkSetDown(link Link) (err error) {
|
||||||
return netlink.LinkSetDown(link)
|
return netlink.LinkSetDown(link)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,19 +14,19 @@ type Router interface {
|
|||||||
RouteReplace(route *netlink.Route) error
|
RouteReplace(route *netlink.Route) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) RouteList(link netlink.Link, family int) (
|
func (n *NetLink) RouteList(link Link, family int) (
|
||||||
routes []netlink.Route, err error) {
|
routes []Route, err error) {
|
||||||
return netlink.RouteList(link, family)
|
return netlink.RouteList(link, family)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) RouteAdd(route *netlink.Route) error {
|
func (n *NetLink) RouteAdd(route *Route) error {
|
||||||
return netlink.RouteAdd(route)
|
return netlink.RouteAdd(route)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) RouteDel(route *netlink.Route) error {
|
func (n *NetLink) RouteDel(route *Route) error {
|
||||||
return netlink.RouteDel(route)
|
return netlink.RouteDel(route)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) RouteReplace(route *netlink.Route) error {
|
func (n *NetLink) RouteReplace(route *Route) error {
|
||||||
return netlink.RouteReplace(route)
|
return netlink.RouteReplace(route)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ type Ruler interface {
|
|||||||
RuleDel(rule *netlink.Rule) error
|
RuleDel(rule *netlink.Rule) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) RuleList(family int) (rules []netlink.Rule, err error) {
|
func (n *NetLink) RuleList(family int) (rules []Rule, err error) {
|
||||||
return netlink.RuleList(family)
|
return netlink.RuleList(family)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) RuleAdd(rule *netlink.Rule) error {
|
func (n *NetLink) RuleAdd(rule *Rule) error {
|
||||||
return netlink.RuleAdd(rule)
|
return netlink.RuleAdd(rule)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetLink) RuleDel(rule *netlink.Rule) error {
|
func (n *NetLink) RuleDel(rule *Rule) error {
|
||||||
return netlink.RuleDel(rule)
|
return netlink.RuleDel(rule)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
package custom
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration"
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrReadCustomConfig = errors.New("cannot read custom configuration file")
|
|
||||||
ErrExtractConnection = errors.New("cannot extract connection from custom configuration file")
|
|
||||||
)
|
|
||||||
|
|
||||||
func BuildConfig(settings configuration.OpenVPN) (
|
|
||||||
lines []string, connection models.Connection, intf string, err error) {
|
|
||||||
lines, err = readCustomConfigLines(settings.Config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, connection, "", fmt.Errorf("%w: %s", ErrReadCustomConfig, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
connection, intf, err = extractDataFromLines(lines)
|
|
||||||
if err != nil {
|
|
||||||
return nil, connection, "", fmt.Errorf("%w: %s", ErrExtractConnection, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if intf == "" {
|
|
||||||
intf = settings.Interface
|
|
||||||
}
|
|
||||||
|
|
||||||
lines = modifyCustomConfig(lines, settings, connection, intf)
|
|
||||||
|
|
||||||
return lines, connection, intf, nil
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
package custom
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration"
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_BuildConfig(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
file, err := os.CreateTemp("", "")
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer removeFile(t, file.Name())
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
_, err = file.WriteString("remote 1.9.8.7\nkeep me\ncipher remove")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err = file.Close()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
settings := configuration.OpenVPN{
|
|
||||||
Cipher: "cipher",
|
|
||||||
MSSFix: 999,
|
|
||||||
Config: file.Name(),
|
|
||||||
Interface: "tun0",
|
|
||||||
}
|
|
||||||
|
|
||||||
lines, connection, intf, err := BuildConfig(settings)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
expectedLines := []string{
|
|
||||||
"keep me",
|
|
||||||
"proto udp",
|
|
||||||
"remote 1.9.8.7 1194",
|
|
||||||
"dev tun0",
|
|
||||||
"mute-replay-warnings",
|
|
||||||
"auth-nocache",
|
|
||||||
"pull-filter ignore \"auth-token\"",
|
|
||||||
"auth-retry nointeract",
|
|
||||||
"suppress-timestamps",
|
|
||||||
"verb 0",
|
|
||||||
"data-ciphers-fallback cipher",
|
|
||||||
"data-ciphers cipher",
|
|
||||||
"mssfix 999",
|
|
||||||
"pull-filter ignore \"route-ipv6\"",
|
|
||||||
"pull-filter ignore \"ifconfig-ipv6\"",
|
|
||||||
"user ",
|
|
||||||
}
|
|
||||||
assert.Equal(t, expectedLines, lines)
|
|
||||||
|
|
||||||
expectedConnection := models.Connection{
|
|
||||||
IP: net.IPv4(1, 9, 8, 7),
|
|
||||||
Port: 1194,
|
|
||||||
Protocol: constants.UDP,
|
|
||||||
}
|
|
||||||
assert.Equal(t, expectedConnection, connection)
|
|
||||||
|
|
||||||
assert.Equal(t, "tun0", intf)
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user