Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
732f826ec2 | ||
|
|
0413a0a1ab | ||
|
|
cb6e9cb761 | ||
|
|
420ae40901 | ||
|
|
34e67f9f99 | ||
|
|
18c53aa597 | ||
|
|
6d2f9b9508 | ||
|
|
6826b05d58 | ||
|
|
9f959dbc6a | ||
|
|
87dbae5745 | ||
|
|
037f19e852 | ||
|
|
62ad8bcd8f | ||
|
|
2805c3388a | ||
|
|
535297dcf5 | ||
|
|
b3b6933ef4 | ||
|
|
edbbcc041a | ||
|
|
d430ebc34f | ||
|
|
0e9abc6e1d | ||
|
|
0c0dd10766 | ||
|
|
75454be6b6 | ||
|
|
4952e3b74e | ||
|
|
04b34a266c | ||
|
|
89b6a031b0 | ||
|
|
d4c6a9bdb5 | ||
|
|
cdc29d48b7 | ||
|
|
f4b464a7cf | ||
|
|
76690d3add | ||
|
|
9898387579 | ||
|
|
1ea15a1a13 | ||
|
|
bda6707685 | ||
|
|
89277828ac | ||
|
|
83b4a3fe55 | ||
|
|
45c9e780c0 | ||
|
|
33b8f5f596 | ||
|
|
447a7c9891 | ||
|
|
1bd355ab96 | ||
|
|
578ef768ab | ||
|
|
0378fe4a7b | ||
|
|
ebd94723c1 | ||
|
|
11b55abff3 | ||
|
|
7f32b43895 | ||
|
|
899f10c35e | ||
|
|
415cb7a945 | ||
|
|
e37f557cd5 | ||
|
|
79f213d97a | ||
|
|
11e1c9f9bb | ||
|
|
3ff3816d77 | ||
|
|
c0bdae8baf | ||
|
|
46e6bd16c9 | ||
|
|
5359257c65 | ||
|
|
5e659dc5b3 | ||
|
|
85e9d7d522 | ||
|
|
b71c8e58f4 | ||
|
|
e998372ce2 | ||
|
|
1216326867 | ||
|
|
f53f0cfffd | ||
|
|
f5f65d534a | ||
|
|
684cef6eab | ||
|
|
b4f6ae030d | ||
|
|
e95c94294f | ||
|
|
36b504609b | ||
|
|
1e6b4ed5eb | ||
|
|
0549326dfb | ||
|
|
87c6ebe1c5 | ||
|
|
f0afac243b | ||
|
|
53472077f4 | ||
|
|
55afdf33e1 | ||
|
|
d3c1f9263c | ||
|
|
6341d1dda6 | ||
|
|
e62e1883c2 | ||
|
|
501b98dbd3 | ||
|
|
029fd1da1f | ||
|
|
fd0267efef | ||
|
|
4414366370 | ||
|
|
08553bc90b | ||
|
|
6f850c4ad4 | ||
|
|
8e1316bd8a | ||
|
|
b345368257 | ||
|
|
90dd3b1b5c | ||
|
|
22455ac76f | ||
|
|
eb18eaf0a9 | ||
|
|
90c6c8485b | ||
|
|
381089ebdf | ||
|
|
292813831d | ||
|
|
991d75a1d0 | ||
|
|
d9dfb81cb4 | ||
|
|
67a9cacb61 | ||
|
|
a91eb95456 | ||
|
|
a295269518 | ||
|
|
42904b6749 | ||
|
|
364f9de756 | ||
|
|
7fd45cf17f | ||
|
|
eb71cfb144 | ||
|
|
48e469917e | ||
|
|
4bcd8ee9f5 | ||
|
|
1b2bcf901a | ||
|
|
306de8feda | ||
|
|
e3696f1eea | ||
|
|
7ff14a356c | ||
|
|
4bde50fb3a | ||
|
|
bd0868d764 | ||
|
|
5ffe8555ba | ||
|
|
78ccbb21cd | ||
|
|
92dbe1ebad | ||
|
|
2eec60cdd2 | ||
|
|
da8c104ebd | ||
|
|
0ef7b66047 |
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -96,7 +96,7 @@ body:
|
||||
attributes:
|
||||
label: Share your logs
|
||||
description: No sensitive information is logged out except when running with `LOG_LEVEL=debug`.
|
||||
render: log
|
||||
render: plain text
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
37
.github/workflows/ci-skip.yml
vendored
Normal file
37
.github/workflows/ci-skip.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: No trigger file paths
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- .github/workflows/ci.yml
|
||||
- cmd/**
|
||||
- internal/**
|
||||
- pkg/**
|
||||
- .dockerignore
|
||||
- .golangci.yml
|
||||
- Dockerfile
|
||||
- go.mod
|
||||
- go.sum
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- .github/workflows/ci.yml
|
||||
- cmd/**
|
||||
- internal/**
|
||||
- pkg/**
|
||||
- .dockerignore
|
||||
- .golangci.yml
|
||||
- Dockerfile
|
||||
- go.mod
|
||||
- go.sum
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
steps:
|
||||
- name: No trigger path triggered for required verify workflow.
|
||||
run: exit 0
|
||||
62
.github/workflows/ci.yml
vendored
62
.github/workflows/ci.yml
vendored
@@ -32,13 +32,10 @@ on:
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
# Only run if it's a push event or if it's a PR from this repository, and it is not dependabot.
|
||||
if: |
|
||||
github.actor != 'dependabot[bot]' &&
|
||||
(github.event_name == 'push' ||
|
||||
github.event_name == 'release' ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository))
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
env:
|
||||
DOCKER_BUILDKIT: "1"
|
||||
steps:
|
||||
@@ -52,9 +49,6 @@ jobs:
|
||||
- name: Linting
|
||||
run: docker build --target lint .
|
||||
|
||||
- name: Go mod tidy check
|
||||
run: docker build --target tidy .
|
||||
|
||||
- name: Build test image
|
||||
run: docker build --target test -t test-container .
|
||||
|
||||
@@ -65,28 +59,35 @@ jobs:
|
||||
-v "$(pwd)/coverage.txt:/tmp/gobuild/coverage.txt" \
|
||||
test-container
|
||||
|
||||
- name: Code security analysis
|
||||
uses: snyk/actions/golang@master
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
|
||||
- name: Build final image
|
||||
run: docker build -t final-image .
|
||||
|
||||
# - name: Image security analysis
|
||||
# uses: snyk/actions/docker@master
|
||||
# env:
|
||||
# SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
# with:
|
||||
# image: final-image
|
||||
codeql:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: go
|
||||
- uses: github/codeql-action/autobuild@v2
|
||||
- uses: github/codeql-action/analyze@v2
|
||||
|
||||
publish:
|
||||
# Only run if it's a push event or if it's a PR from this repository
|
||||
if: |
|
||||
github.event_name == 'push' ||
|
||||
github.event_name == 'release' ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)
|
||||
needs: [verify]
|
||||
github.repository == 'qdm12/gluetun' &&
|
||||
(
|
||||
github.event_name == 'push' ||
|
||||
github.event_name == 'release' ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]')
|
||||
)
|
||||
needs: [verify, codeql]
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -95,7 +96,7 @@ jobs:
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
flavor: |
|
||||
latest=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
||||
@@ -103,17 +104,16 @@ jobs:
|
||||
qmcgaw/gluetun
|
||||
qmcgaw/private-internet-access
|
||||
tags: |
|
||||
type=ref,event=branch,enable=${{ github.ref != format('refs/heads/{0}', github.event.repository.default_branch) }}
|
||||
type=ref,event=pr
|
||||
type=semver,pattern=v{{major}}.{{minor}}.{{patch}}
|
||||
type=semver,pattern=v{{major}}.{{minor}}
|
||||
type=semver,pattern=v{{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }}
|
||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
||||
|
||||
- uses: docker/setup-qemu-action@v1
|
||||
- uses: docker/setup-buildx-action@v1
|
||||
- uses: docker/setup-qemu-action@v2
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
|
||||
- uses: docker/login-action@v1
|
||||
- uses: docker/login-action@v2
|
||||
with:
|
||||
username: qmcgaw
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
@@ -123,7 +123,7 @@ jobs:
|
||||
run: echo "::set-output name=value::$(git rev-parse --short HEAD)"
|
||||
|
||||
- name: Build and push final image
|
||||
uses: docker/build-push-action@v2.10.0
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
with:
|
||||
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
25
.github/workflows/codeql.yml
vendored
25
.github/workflows/codeql.yml
vendored
@@ -1,25 +0,0 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '44 9 * * 0'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: go
|
||||
- uses: github/codeql-action/autobuild@v2
|
||||
- uses: github/codeql-action/analyze@v2
|
||||
37
.github/workflows/dependabot.yml
vendored
37
.github/workflows/dependabot.yml
vendored
@@ -1,37 +0,0 @@
|
||||
name: Dependabot
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- .github/workflows/dependabot.yml
|
||||
- cmd/**
|
||||
- internal/**
|
||||
- pkg/**
|
||||
- .dockerignore
|
||||
- .golangci.yml
|
||||
- Dockerfile
|
||||
- go.mod
|
||||
- go.sum
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
if: ${{ github.actor == 'dependabot[bot]' }}
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_BUILDKIT: "1"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Build test image
|
||||
run: docker build --target test -t test-container .
|
||||
|
||||
- name: Run tests in test container
|
||||
run: |
|
||||
touch coverage.txt
|
||||
docker run --rm \
|
||||
-v "$(pwd)/coverage.txt:/tmp/gobuild/coverage.txt" \
|
||||
test-container
|
||||
|
||||
- name: Build final image
|
||||
run: docker build -t final-image .
|
||||
16
.github/workflows/dockerhub-description.yml
vendored
16
.github/workflows/dockerhub-description.yml
vendored
@@ -1,18 +1,22 @@
|
||||
name: Docker Hub description
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- README.md
|
||||
- .github/workflows/dockerhub-description.yml
|
||||
jobs:
|
||||
dockerHubDescription:
|
||||
docker-hub-description:
|
||||
if: github.repository == 'qdm12/gluetun'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Docker Hub Description
|
||||
uses: peter-evans/dockerhub-description@v3
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: peter-evans/dockerhub-description@v3
|
||||
with:
|
||||
username: qmcgaw
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
40
.github/workflows/fork.yml
vendored
40
.github/workflows/fork.yml
vendored
@@ -1,40 +0,0 @@
|
||||
name: Fork
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- .github/workflows/fork.yml
|
||||
- cmd/**
|
||||
- internal/**
|
||||
- pkg/**
|
||||
- .dockerignore
|
||||
- .golangci.yml
|
||||
- Dockerfile
|
||||
- go.mod
|
||||
- go.sum
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
if: github.event.pull_request.head.repo.full_name != github.repository && github.actor != 'dependabot[bot]'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_BUILDKIT: "1"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Linting
|
||||
run: docker build --target lint .
|
||||
|
||||
- name: Build test image
|
||||
run: docker build --target test -t test-container .
|
||||
|
||||
- name: Run tests in test container
|
||||
run: |
|
||||
touch coverage.txt
|
||||
docker run --rm \
|
||||
-v "$(pwd)/coverage.txt:/tmp/gobuild/coverage.txt" \
|
||||
test-container
|
||||
|
||||
- name: Build final image
|
||||
run: docker build -t final-image .
|
||||
2
.github/workflows/labels.yml
vendored
2
.github/workflows/labels.yml
vendored
@@ -10,6 +10,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: crazy-max/ghaction-github-labeler@v3
|
||||
- uses: crazy-max/ghaction-github-labeler@v4
|
||||
with:
|
||||
yaml-file: .github/labels.yml
|
||||
|
||||
@@ -7,44 +7,34 @@ issues:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- dupl
|
||||
- maligned
|
||||
- goerr113
|
||||
- containedctx
|
||||
- path: internal/server/
|
||||
- path: "internal\\/server\\/.+\\.go"
|
||||
linters:
|
||||
- dupl
|
||||
- path: internal/configuration/
|
||||
- path: "internal\\/configuration\\/settings\\/.+\\.go"
|
||||
linters:
|
||||
- dupl
|
||||
- path: internal/constants/
|
||||
linters:
|
||||
- dupl
|
||||
- text: "exported: exported var Err*"
|
||||
linters:
|
||||
- revive
|
||||
- text: "mnd: Magic number: 0644*"
|
||||
- text: "^mnd: Magic number: 0[0-9]{3}, in <argument> detected$"
|
||||
source: "^.+= os\\.OpenFile\\(.+, .+, 0[0-9]{3}\\)"
|
||||
linters:
|
||||
- gomnd
|
||||
- text: "mnd: Magic number: 0400*"
|
||||
|
||||
- text: "^mnd: Magic number: 0[0-9]{3}, in <argument> detected$"
|
||||
source: "^.+= os\\.MkdirAll\\(.+, 0[0-9]{3}\\)"
|
||||
linters:
|
||||
- gomnd
|
||||
- text: "variable 'mssFix' is only used in the if-statement*"
|
||||
path: "openvpnconf.go"
|
||||
linters:
|
||||
- ifshort
|
||||
- text: "variable 'auth' is only used in the if-statement*"
|
||||
path: "openvpnconf.go"
|
||||
linters:
|
||||
- ifshort
|
||||
- linters:
|
||||
- lll
|
||||
source: "^//go:generate "
|
||||
source: "^//go:generate .+$"
|
||||
- text: "returns interface \\(github\\.com\\/vishvananda\\/netlink\\.Link\\)"
|
||||
linters:
|
||||
- ireturn
|
||||
|
||||
linters:
|
||||
enable:
|
||||
# - cyclop
|
||||
# - errorlint
|
||||
# - ireturn
|
||||
# - varnamelen
|
||||
# - wrapcheck
|
||||
- asciicheck
|
||||
@@ -57,6 +47,7 @@ linters:
|
||||
- durationcheck
|
||||
- errchkjson
|
||||
- errname
|
||||
- execinquery
|
||||
- exhaustive
|
||||
- exportloopref
|
||||
- forcetypeassert
|
||||
@@ -78,6 +69,7 @@ linters:
|
||||
- grouper
|
||||
- ifshort
|
||||
- importas
|
||||
- ireturn
|
||||
- lll
|
||||
- maintidx
|
||||
- makezero
|
||||
@@ -88,6 +80,7 @@ linters:
|
||||
- nilnil
|
||||
- noctx
|
||||
- nolintlint
|
||||
- nosprintfhostport
|
||||
- prealloc
|
||||
- predeclared
|
||||
- predeclared
|
||||
|
||||
25
.vscode/launch.json
vendored
Normal file
25
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Update a VPN provider servers data",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"program": "cmd/gluetun/main.go",
|
||||
"args": [
|
||||
"update",
|
||||
"-enduser",
|
||||
"-providers",
|
||||
"${input:provider}"
|
||||
],
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"id": "provider",
|
||||
"type": "promptString",
|
||||
"description": "Please enter a provider (or comma separated list of providers)",
|
||||
}
|
||||
]
|
||||
}
|
||||
18
Dockerfile
18
Dockerfile
@@ -1,8 +1,8 @@
|
||||
ARG ALPINE_VERSION=3.15
|
||||
ARG GO_ALPINE_VERSION=3.15
|
||||
ARG ALPINE_VERSION=3.16
|
||||
ARG GO_ALPINE_VERSION=3.16
|
||||
ARG GO_VERSION=1.17
|
||||
ARG XCPUTRANSLATE_VERSION=v0.6.0
|
||||
ARG GOLANGCI_LINT_VERSION=v1.44.2
|
||||
ARG GOLANGCI_LINT_VERSION=v1.46.2
|
||||
ARG BUILDPLATFORM=linux/amd64
|
||||
|
||||
FROM --platform=${BUILDPLATFORM} qmcgaw/xcputranslate:${XCPUTRANSLATE_VERSION} AS xcputranslate
|
||||
@@ -30,15 +30,6 @@ FROM --platform=${BUILDPLATFORM} base AS lint
|
||||
COPY .golangci.yml ./
|
||||
RUN golangci-lint run --timeout=10m
|
||||
|
||||
FROM --platform=${BUILDPLATFORM} base AS tidy
|
||||
RUN git init && \
|
||||
git config user.email ci@localhost && \
|
||||
git config user.name ci && \
|
||||
git add -A && git commit -m ci && \
|
||||
sed -i '/\/\/ indirect/d' go.mod && \
|
||||
go mod tidy && \
|
||||
git diff --exit-code -- go.mod
|
||||
|
||||
FROM --platform=${BUILDPLATFORM} base AS build
|
||||
ARG TARGETPLATFORM
|
||||
ARG VERSION=unknown
|
||||
@@ -108,7 +99,7 @@ ENV VPN_SERVICE_PROVIDER=pia \
|
||||
OPENVPN_CLIENTKEY_SECRETFILE=/run/secrets/openvpn_clientkey \
|
||||
# # Nordvpn only:
|
||||
SERVER_NUMBER= \
|
||||
# # PIA and ProtonVPN only:
|
||||
# # PIA only:
|
||||
SERVER_NAMES= \
|
||||
# # ProtonVPN only:
|
||||
FREE_ONLY= \
|
||||
@@ -162,6 +153,7 @@ ENV VPN_SERVICE_PROVIDER=pia \
|
||||
HTTP_CONTROL_SERVER_ADDRESS=":8000" \
|
||||
# Server data updater
|
||||
UPDATER_PERIOD=0 \
|
||||
UPDATER_MIN_RATIO=0.8 \
|
||||
UPDATER_VPN_SERVICE_PROVIDERS= \
|
||||
# Public IP
|
||||
PUBLICIP_FILE="/tmp/gluetun/ip" \
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
# Gluetun VPN client
|
||||
|
||||
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, ExpressVPN, FastestVPN,
|
||||
HideMyAss, IPVanish, IVPN, Mullvad, NordVPN, Perfect Privacy, Privado, Private Internet Access, PrivateVPN,
|
||||
ProtonVPN, PureVPN, Surfshark, TorGuard, VPNUnlimited, VyprVPN, WeVPN and Windscribe VPN servers
|
||||
using Go, OpenVPN or Wireguard, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
||||
|
||||
Lightweight swiss-knife-like VPN client to multiple VPN sercice providers
|
||||
|
||||

|
||||
|
||||
@@ -52,6 +48,7 @@ using Go, OpenVPN or Wireguard, iptables, DNS over TLS, ShadowSocks and an HTTP
|
||||
- Sponsor me on [github.com/sponsors/qdm12](https://github.com/sponsors/qdm12)
|
||||
- Donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw)
|
||||
- Drop me [an email](mailto:quentin.mcgaw@gmail.com)
|
||||
- **Want to add a VPN provider?** check [Development](https://github.com/qdm12/gluetun/wiki/Development) and [Add a provider](https://github.com/qdm12/gluetun/wiki/Add-a-provider)
|
||||
- Video:
|
||||
|
||||
[](https://youtu.be/0F6I03LQcI4)
|
||||
@@ -60,7 +57,7 @@ using Go, OpenVPN or Wireguard, iptables, DNS over TLS, ShadowSocks and an HTTP
|
||||
|
||||
## Features
|
||||
|
||||
- Based on Alpine 3.15 for a small Docker image of 29MB
|
||||
- Based on Alpine 3.16 for a small Docker image of 29MB
|
||||
- Supports: **Cyberghost**, **ExpressVPN**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Perfect Privacy**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **WeVPN**, **Windscribe** servers
|
||||
- Supports OpenVPN for all providers listed
|
||||
- Supports Wireguard both kernelspace and userspace
|
||||
|
||||
@@ -29,15 +29,20 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/netlink"
|
||||
"github.com/qdm12/gluetun/internal/openvpn"
|
||||
"github.com/qdm12/gluetun/internal/openvpn/extract"
|
||||
"github.com/qdm12/gluetun/internal/portforward"
|
||||
"github.com/qdm12/gluetun/internal/pprof"
|
||||
"github.com/qdm12/gluetun/internal/provider"
|
||||
"github.com/qdm12/gluetun/internal/publicip"
|
||||
"github.com/qdm12/gluetun/internal/publicip/ipinfo"
|
||||
"github.com/qdm12/gluetun/internal/routing"
|
||||
"github.com/qdm12/gluetun/internal/server"
|
||||
"github.com/qdm12/gluetun/internal/shadowsocks"
|
||||
"github.com/qdm12/gluetun/internal/storage"
|
||||
"github.com/qdm12/gluetun/internal/tun"
|
||||
"github.com/qdm12/gluetun/internal/updater"
|
||||
updater "github.com/qdm12/gluetun/internal/updater/loop"
|
||||
"github.com/qdm12/gluetun/internal/updater/resolver"
|
||||
"github.com/qdm12/gluetun/internal/updater/unzip"
|
||||
"github.com/qdm12/gluetun/internal/vpn"
|
||||
"github.com/qdm12/golibs/command"
|
||||
"github.com/qdm12/goshutdown"
|
||||
@@ -124,8 +129,8 @@ var (
|
||||
//nolint:gocognit,gocyclo,maintidx
|
||||
func _main(ctx context.Context, buildInfo models.BuildInformation,
|
||||
args []string, logger log.LoggerInterface, source sources.Source,
|
||||
tun tun.Interface, netLinker netlink.NetLinker, cmder command.RunStarter,
|
||||
cli cli.CLIer) error {
|
||||
tun Tun, netLinker netLinker, cmder command.RunStarter,
|
||||
cli clier) error {
|
||||
if len(args) > 1 { // cli operation
|
||||
switch args[1] {
|
||||
case "healthcheck":
|
||||
@@ -215,9 +220,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
||||
return err
|
||||
}
|
||||
|
||||
allServers := storage.GetServers()
|
||||
|
||||
err = allSettings.Validate(allServers)
|
||||
err = allSettings.Validate(storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -344,7 +347,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
||||
|
||||
portForwardLogger := logger.New(log.SetComponent("port forwarding"))
|
||||
portForwardLooper := portforward.NewLoop(allSettings.VPN.Provider.PortForwarding,
|
||||
httpClient, firewallConf, portForwardLogger)
|
||||
httpClient, firewallConf, portForwardLogger, puid, pgid)
|
||||
portForwardHandler, portForwardCtx, portForwardDone := goshutdown.NewGoRoutineHandler(
|
||||
"port forwarding", goroutine.OptionTimeout(time.Second))
|
||||
go portForwardLooper.Run(portForwardCtx, portForwardDone)
|
||||
@@ -363,7 +366,8 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
||||
go unboundLooper.RunRestartTicker(dnsTickerCtx, dnsTickerDone)
|
||||
controlGroupHandler.Add(dnsTickerHandler)
|
||||
|
||||
publicIPLooper := publicip.NewLoop(httpClient,
|
||||
ipFetcher := ipinfo.New(httpClient)
|
||||
publicIPLooper := publicip.NewLoop(ipFetcher,
|
||||
logger.New(log.SetComponent("ip getter")),
|
||||
allSettings.PublicIP, puid, pgid)
|
||||
pubIPHandler, pubIPCtx, pubIPDone := goshutdown.NewGoRoutineHandler(
|
||||
@@ -376,18 +380,25 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
||||
go publicIPLooper.RunRestartTicker(pubIPTickerCtx, pubIPTickerDone)
|
||||
tickersGroupHandler.Add(pubIPTickerHandler)
|
||||
|
||||
updaterLogger := logger.New(log.SetComponent("updater"))
|
||||
|
||||
unzipper := unzip.New(httpClient)
|
||||
parallelResolver := resolver.NewParallelResolver(allSettings.Updater.DNSAddress)
|
||||
openvpnFileExtractor := extract.New()
|
||||
providers := provider.NewProviders(storage, time.Now, updaterLogger,
|
||||
httpClient, unzipper, parallelResolver, ipFetcher, openvpnFileExtractor)
|
||||
|
||||
vpnLogger := logger.New(log.SetComponent("vpn"))
|
||||
vpnLooper := vpn.NewLoop(allSettings.VPN, allSettings.Firewall.VPNInputPorts,
|
||||
allServers, ovpnConf, netLinker, firewallConf, routingConf, portForwardLooper,
|
||||
providers, storage, ovpnConf, netLinker, firewallConf, routingConf, portForwardLooper,
|
||||
cmder, publicIPLooper, unboundLooper, vpnLogger, httpClient,
|
||||
buildInfo, *allSettings.Version.Enabled)
|
||||
vpnHandler, vpnCtx, vpnDone := goshutdown.NewGoRoutineHandler(
|
||||
"vpn", goroutine.OptionTimeout(time.Second))
|
||||
go vpnLooper.Run(vpnCtx, vpnDone)
|
||||
|
||||
updaterLooper := updater.NewLooper(allSettings.Updater,
|
||||
allServers, storage, vpnLooper.SetServers, httpClient,
|
||||
logger.New(log.SetComponent("updater")))
|
||||
updaterLooper := updater.NewLoop(allSettings.Updater,
|
||||
providers, storage, httpClient, updaterLogger)
|
||||
updaterHandler, updaterCtx, updaterDone := goshutdown.NewGoRoutineHandler(
|
||||
"updater", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||
// wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker
|
||||
@@ -407,7 +418,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
||||
go httpProxyLooper.Run(httpProxyCtx, httpProxyDone)
|
||||
otherGroupHandler.Add(httpProxyHandler)
|
||||
|
||||
shadowsocksLooper := shadowsocks.NewLooper(allSettings.Shadowsocks,
|
||||
shadowsocksLooper := shadowsocks.NewLoop(allSettings.Shadowsocks,
|
||||
logger.New(log.SetComponent("shadowsocks")))
|
||||
shadowsocksHandler, shadowsocksCtx, shadowsocksDone := goshutdown.NewGoRoutineHandler(
|
||||
"shadowsocks proxy", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||
@@ -476,3 +487,38 @@ func printVersions(ctx context.Context, logger infoer,
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type netLinker interface {
|
||||
AddrList(link netlink.Link, family int) (
|
||||
addresses []netlink.Addr, err error)
|
||||
AddrAdd(link netlink.Link, addr *netlink.Addr) error
|
||||
IsWireguardSupported() (ok bool, err error)
|
||||
RouteList(link netlink.Link, family int) (
|
||||
routes []netlink.Route, err error)
|
||||
RouteAdd(route *netlink.Route) error
|
||||
RouteDel(route *netlink.Route) error
|
||||
RouteReplace(route *netlink.Route) error
|
||||
RuleList(family int) (rules []netlink.Rule, err error)
|
||||
RuleAdd(rule *netlink.Rule) error
|
||||
RuleDel(rule *netlink.Rule) error
|
||||
LinkList() (links []netlink.Link, err error)
|
||||
LinkByName(name string) (link netlink.Link, err error)
|
||||
LinkByIndex(index int) (link netlink.Link, err error)
|
||||
LinkAdd(link netlink.Link) (err error)
|
||||
LinkDel(link netlink.Link) (err error)
|
||||
LinkSetUp(link netlink.Link) (err error)
|
||||
LinkSetDown(link netlink.Link) (err error)
|
||||
}
|
||||
|
||||
type clier interface {
|
||||
ClientKey(args []string) error
|
||||
FormatServers(args []string) error
|
||||
OpenvpnConfig(logger cli.OpenvpnConfigLogger, source sources.Source) error
|
||||
HealthCheck(ctx context.Context, source sources.Source, warner cli.Warner) error
|
||||
Update(ctx context.Context, args []string, logger cli.UpdaterLogger) error
|
||||
}
|
||||
|
||||
type Tun interface {
|
||||
Check(tunDevice string) error
|
||||
Create(tunDevice string) error
|
||||
}
|
||||
|
||||
7
go.mod
7
go.mod
@@ -15,9 +15,10 @@ require (
|
||||
github.com/qdm12/log v0.1.0
|
||||
github.com/qdm12/ss-server v0.4.0
|
||||
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
|
||||
github.com/stretchr/testify v1.7.1
|
||||
github.com/stretchr/testify v1.7.2
|
||||
github.com/vishvananda/netlink v1.1.1-0.20211129163951-9ada19101fc5
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||
golang.org/x/text v0.3.7
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210805125648-3957e9b9dd19
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c
|
||||
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722
|
||||
@@ -37,8 +38,8 @@ require (
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
|
||||
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 // indirect
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
|
||||
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
11
go.sum
11
go.sum
@@ -131,8 +131,9 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/vishvananda/netlink v1.1.1-0.20211129163951-9ada19101fc5 h1:b/k/BVWzWRS5v6AB0gf2ckFSbFsHN5jR0HoNso1pN+w=
|
||||
github.com/vishvananda/netlink v1.1.1-0.20211129163951-9ada19101fc5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns=
|
||||
@@ -144,8 +145,9 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
|
||||
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg=
|
||||
go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 h1:1tk03FUNpulq2cuWpXZWj649rwJpk0d20rxWiopKRmc=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -222,6 +224,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@@ -247,8 +251,9 @@ gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQb
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
inet.af/netaddr v0.0.0-20210511181906-37180328850c/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
|
||||
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722 h1:Qws2rZnQudC58cIagVucPQDLmMi3kAXgxscsgD0v6DU=
|
||||
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
// Package alpine defines a configurator to interact with the Alpine operating system.
|
||||
package alpine
|
||||
|
||||
import (
|
||||
"os/user"
|
||||
)
|
||||
|
||||
var _ Alpiner = (*Alpine)(nil)
|
||||
|
||||
type Alpiner interface {
|
||||
UserCreater
|
||||
VersionGetter
|
||||
}
|
||||
|
||||
type Alpine struct {
|
||||
alpineReleasePath string
|
||||
passwdPath string
|
||||
|
||||
@@ -12,10 +12,6 @@ var (
|
||||
ErrUserAlreadyExists = errors.New("user already exists")
|
||||
)
|
||||
|
||||
type UserCreater interface {
|
||||
CreateUser(username string, uid int) (createdUsername string, err error)
|
||||
}
|
||||
|
||||
// CreateUser creates a user in Alpine with the given UID.
|
||||
func (a *Alpine) CreateUser(username string, uid int) (createdUsername string, err error) {
|
||||
UIDStr := strconv.Itoa(uid)
|
||||
|
||||
@@ -7,10 +7,6 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type VersionGetter interface {
|
||||
Version(ctx context.Context) (version string, err error)
|
||||
}
|
||||
|
||||
func (a *Alpine) Version(ctx context.Context) (version string, err error) {
|
||||
file, err := os.OpenFile(a.alpineReleasePath, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
// Package cli defines an interface CLI to run command line operations.
|
||||
package cli
|
||||
|
||||
var _ CLIer = (*CLI)(nil)
|
||||
|
||||
type CLIer interface {
|
||||
ClientKeyFormatter
|
||||
HealthChecker
|
||||
OpenvpnConfigMaker
|
||||
Updater
|
||||
ServersFormatter
|
||||
}
|
||||
|
||||
type CLI struct {
|
||||
repoServersPath string
|
||||
}
|
||||
|
||||
@@ -10,10 +10,6 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/configuration/sources/files"
|
||||
)
|
||||
|
||||
type ClientKeyFormatter interface {
|
||||
ClientKey(args []string) error
|
||||
}
|
||||
|
||||
func (c *CLI) ClientKey(args []string) error {
|
||||
flagSet := flag.NewFlagSet("clientkey", flag.ExitOnError)
|
||||
filepath := flagSet.String("path", files.OpenVPNClientKeyPath, "file path to the client.key file")
|
||||
|
||||
@@ -6,49 +6,44 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||
"github.com/qdm12/gluetun/internal/storage"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
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")
|
||||
ErrFormatNotRecognized = errors.New("format is not recognized")
|
||||
ErrProviderUnspecified = errors.New("VPN provider to format was not specified")
|
||||
ErrMultipleProvidersToFormat = errors.New("more than one VPN provider to format were specified")
|
||||
)
|
||||
|
||||
func addProviderFlag(flagSet *flag.FlagSet, providerToFormat map[string]*bool,
|
||||
provider string, titleCaser cases.Caser) {
|
||||
boolPtr, ok := providerToFormat[provider]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown provider in format map: %s", provider))
|
||||
}
|
||||
flagSet.BoolVar(boolPtr, provider, false, "Format "+titleCaser.String(provider)+" servers")
|
||||
}
|
||||
|
||||
func (c *CLI) FormatServers(args []string) error {
|
||||
var format, output string
|
||||
var cyberghost, expressvpn, fastestvpn, hideMyAss, ipvanish, ivpn, mullvad,
|
||||
nordvpn, perfectPrivacy, pia, privado, privatevpn, protonvpn, purevpn, surfshark,
|
||||
torguard, vpnUnlimited, vyprvpn, wevpn, windscribe bool
|
||||
allProviders := providers.All()
|
||||
providersToFormat := make(map[string]*bool, len(allProviders))
|
||||
for _, provider := range allProviders {
|
||||
providersToFormat[provider] = new(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, providers.Cyberghost, false, "Format Cyberghost servers")
|
||||
flagSet.BoolVar(&expressvpn, providers.Expressvpn, false, "Format ExpressVPN servers")
|
||||
flagSet.BoolVar(&fastestvpn, providers.Fastestvpn, false, "Format FastestVPN servers")
|
||||
flagSet.BoolVar(&hideMyAss, providers.HideMyAss, false, "Format HideMyAss servers")
|
||||
flagSet.BoolVar(&ipvanish, providers.Ipvanish, false, "Format IpVanish servers")
|
||||
flagSet.BoolVar(&ivpn, providers.Ivpn, false, "Format IVPN servers")
|
||||
flagSet.BoolVar(&mullvad, providers.Mullvad, false, "Format Mullvad servers")
|
||||
flagSet.BoolVar(&nordvpn, providers.Nordvpn, false, "Format Nordvpn servers")
|
||||
flagSet.BoolVar(&perfectPrivacy, providers.Perfectprivacy, false, "Format Perfect Privacy servers")
|
||||
flagSet.BoolVar(&pia, providers.PrivateInternetAccess, false, "Format Private Internet Access servers")
|
||||
flagSet.BoolVar(&privado, providers.Privado, false, "Format Privado servers")
|
||||
flagSet.BoolVar(&privatevpn, providers.Privatevpn, false, "Format Private VPN servers")
|
||||
flagSet.BoolVar(&protonvpn, providers.Protonvpn, false, "Format Protonvpn servers")
|
||||
flagSet.BoolVar(&purevpn, providers.Purevpn, false, "Format Purevpn servers")
|
||||
flagSet.BoolVar(&surfshark, providers.Surfshark, false, "Format Surfshark servers")
|
||||
flagSet.BoolVar(&torguard, providers.Torguard, false, "Format Torguard servers")
|
||||
flagSet.BoolVar(&vpnUnlimited, providers.VPNUnlimited, false, "Format VPN Unlimited servers")
|
||||
flagSet.BoolVar(&vyprvpn, providers.Vyprvpn, false, "Format Vyprvpn servers")
|
||||
flagSet.BoolVar(&wevpn, providers.Wevpn, false, "Format WeVPN servers")
|
||||
flagSet.BoolVar(&windscribe, providers.Windscribe, false, "Format Windscribe servers")
|
||||
titleCaser := cases.Title(language.English)
|
||||
for _, provider := range allProviders {
|
||||
addProviderFlag(flagSet, providersToFormat, provider, titleCaser)
|
||||
}
|
||||
if err := flagSet.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -57,58 +52,31 @@ func (c *CLI) FormatServers(args []string) error {
|
||||
return fmt.Errorf("%w: %s", ErrFormatNotRecognized, format)
|
||||
}
|
||||
|
||||
// Verify only one provider is set to be formatted.
|
||||
var providers []string
|
||||
for provider, formatPtr := range providersToFormat {
|
||||
if *formatPtr {
|
||||
providers = append(providers, provider)
|
||||
}
|
||||
}
|
||||
switch len(providers) {
|
||||
case 0:
|
||||
return ErrProviderUnspecified
|
||||
case 1:
|
||||
default:
|
||||
return fmt.Errorf("%w: %d specified: %s",
|
||||
ErrMultipleProvidersToFormat, len(providers),
|
||||
strings.Join(providers, ", "))
|
||||
}
|
||||
providerToFormat := providers[0]
|
||||
|
||||
logger := newNoopLogger()
|
||||
storage, err := storage.New(logger, constants.ServersData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create servers storage: %w", err)
|
||||
}
|
||||
currentServers := storage.GetServers()
|
||||
|
||||
var formatted string
|
||||
switch {
|
||||
case cyberghost:
|
||||
formatted = currentServers.Cyberghost.ToMarkdown(providers.Cyberghost)
|
||||
case expressvpn:
|
||||
formatted = currentServers.Expressvpn.ToMarkdown(providers.Expressvpn)
|
||||
case fastestvpn:
|
||||
formatted = currentServers.Fastestvpn.ToMarkdown(providers.Fastestvpn)
|
||||
case hideMyAss:
|
||||
formatted = currentServers.HideMyAss.ToMarkdown(providers.HideMyAss)
|
||||
case ipvanish:
|
||||
formatted = currentServers.Ipvanish.ToMarkdown(providers.Ipvanish)
|
||||
case ivpn:
|
||||
formatted = currentServers.Ivpn.ToMarkdown(providers.Ivpn)
|
||||
case mullvad:
|
||||
formatted = currentServers.Mullvad.ToMarkdown(providers.Mullvad)
|
||||
case nordvpn:
|
||||
formatted = currentServers.Nordvpn.ToMarkdown(providers.Nordvpn)
|
||||
case perfectPrivacy:
|
||||
formatted = currentServers.Perfectprivacy.ToMarkdown(providers.Perfectprivacy)
|
||||
case pia:
|
||||
formatted = currentServers.Pia.ToMarkdown(providers.PrivateInternetAccess)
|
||||
case privado:
|
||||
formatted = currentServers.Privado.ToMarkdown(providers.Privado)
|
||||
case privatevpn:
|
||||
formatted = currentServers.Privatevpn.ToMarkdown(providers.Privatevpn)
|
||||
case protonvpn:
|
||||
formatted = currentServers.Protonvpn.ToMarkdown(providers.Protonvpn)
|
||||
case purevpn:
|
||||
formatted = currentServers.Purevpn.ToMarkdown(providers.Purevpn)
|
||||
case surfshark:
|
||||
formatted = currentServers.Surfshark.ToMarkdown(providers.Surfshark)
|
||||
case torguard:
|
||||
formatted = currentServers.Torguard.ToMarkdown(providers.Torguard)
|
||||
case vpnUnlimited:
|
||||
formatted = currentServers.VPNUnlimited.ToMarkdown(providers.VPNUnlimited)
|
||||
case vyprvpn:
|
||||
formatted = currentServers.Vyprvpn.ToMarkdown(providers.Vyprvpn)
|
||||
case wevpn:
|
||||
formatted = currentServers.Wevpn.ToMarkdown(providers.Wevpn)
|
||||
case windscribe:
|
||||
formatted = currentServers.Windscribe.ToMarkdown(providers.Windscribe)
|
||||
default:
|
||||
return ErrProviderUnspecified
|
||||
}
|
||||
formatted := storage.FormatToMarkdown(providerToFormat)
|
||||
|
||||
output = filepath.Clean(output)
|
||||
file, err := os.OpenFile(output, os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0644)
|
||||
|
||||
@@ -10,10 +10,6 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/healthcheck"
|
||||
)
|
||||
|
||||
type HealthChecker interface {
|
||||
HealthCheck(ctx context.Context, source sources.Source, warner Warner) error
|
||||
}
|
||||
|
||||
func (c *CLI) HealthCheck(ctx context.Context, source sources.Source, warner Warner) error {
|
||||
// Extract the health server port from the configuration.
|
||||
config, err := source.ReadHealth()
|
||||
|
||||
@@ -1,50 +1,73 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/sources"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/openvpn/extract"
|
||||
"github.com/qdm12/gluetun/internal/provider"
|
||||
"github.com/qdm12/gluetun/internal/publicip/ipinfo"
|
||||
"github.com/qdm12/gluetun/internal/storage"
|
||||
"github.com/qdm12/gluetun/internal/updater/resolver"
|
||||
)
|
||||
|
||||
type OpenvpnConfigMaker interface {
|
||||
OpenvpnConfig(logger OpenvpnConfigLogger, source sources.Source) error
|
||||
}
|
||||
|
||||
type OpenvpnConfigLogger interface {
|
||||
Info(s string)
|
||||
Warn(s string)
|
||||
}
|
||||
|
||||
type Unzipper interface {
|
||||
FetchAndExtract(ctx context.Context, url string) (
|
||||
contents map[string][]byte, err error)
|
||||
}
|
||||
|
||||
type ParallelResolver interface {
|
||||
Resolve(ctx context.Context, settings resolver.ParallelSettings) (
|
||||
hostToIPs map[string][]net.IP, warnings []string, err error)
|
||||
}
|
||||
|
||||
type IPFetcher interface {
|
||||
FetchMultiInfo(ctx context.Context, ips []net.IP) (data []ipinfo.Response, err error)
|
||||
}
|
||||
|
||||
func (c *CLI) OpenvpnConfig(logger OpenvpnConfigLogger, source sources.Source) error {
|
||||
storage, err := storage.New(logger, constants.ServersData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
allServers := storage.GetServers()
|
||||
|
||||
allSettings, err := source.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = allSettings.Validate(allServers); err != nil {
|
||||
if err = allSettings.Validate(storage); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
providerConf := provider.New(*allSettings.VPN.Provider.Name, allServers, time.Now)
|
||||
// Unused by this CLI command
|
||||
unzipper := (Unzipper)(nil)
|
||||
client := (*http.Client)(nil)
|
||||
warner := (Warner)(nil)
|
||||
parallelResolver := (ParallelResolver)(nil)
|
||||
ipFetcher := (IPFetcher)(nil)
|
||||
openvpnFileExtractor := extract.New()
|
||||
|
||||
providers := provider.NewProviders(storage, time.Now, warner, client,
|
||||
unzipper, parallelResolver, ipFetcher, openvpnFileExtractor)
|
||||
providerConf := providers.Get(*allSettings.VPN.Provider.Name)
|
||||
connection, err := providerConf.GetConnection(allSettings.VPN.Provider.ServerSelection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lines, err := providerConf.BuildConf(connection, allSettings.VPN.OpenVPN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lines := providerConf.OpenVPNConfig(connection, allSettings.VPN.OpenVPN)
|
||||
|
||||
fmt.Println(strings.Join(lines, "\n"))
|
||||
return nil
|
||||
|
||||
@@ -2,51 +2,48 @@ package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/openvpn/extract"
|
||||
"github.com/qdm12/gluetun/internal/provider"
|
||||
"github.com/qdm12/gluetun/internal/publicip/ipinfo"
|
||||
"github.com/qdm12/gluetun/internal/storage"
|
||||
"github.com/qdm12/gluetun/internal/updater"
|
||||
"github.com/qdm12/gluetun/internal/updater/resolver"
|
||||
"github.com/qdm12/gluetun/internal/updater/unzip"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrModeUnspecified = errors.New("at least one of -enduser or -maintainer must be specified")
|
||||
ErrDNSAddress = errors.New("DNS address is not valid")
|
||||
ErrNoProviderSpecified = errors.New("no provider was specified")
|
||||
)
|
||||
|
||||
type Updater interface {
|
||||
Update(ctx context.Context, args []string, logger UpdaterLogger) error
|
||||
}
|
||||
|
||||
type UpdaterLogger interface {
|
||||
Info(s string)
|
||||
Warn(s string)
|
||||
Error(s string)
|
||||
}
|
||||
|
||||
func boolPtr(b bool) *bool { return &b }
|
||||
|
||||
func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) error {
|
||||
options := settings.Updater{CLI: boolPtr(true)}
|
||||
options := settings.Updater{}
|
||||
var endUserMode, maintainerMode, updateAll bool
|
||||
var dnsAddress, csvProviders string
|
||||
var csvProviders string
|
||||
flagSet := flag.NewFlagSet("update", flag.ExitOnError)
|
||||
flagSet.BoolVar(&endUserMode, "enduser", false, "Write results to /gluetun/servers.json (for end users)")
|
||||
flagSet.BoolVar(&maintainerMode, "maintainer", false,
|
||||
"Write results to ./internal/storage/servers.json to modify the program (for maintainers)")
|
||||
flagSet.StringVar(&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")
|
||||
const defaultMinRatio = 0.8
|
||||
flagSet.Float64Var(&options.MinRatio, "minratio", defaultMinRatio,
|
||||
"Minimum ratio of servers to find for the update to succeed")
|
||||
flagSet.BoolVar(&updateAll, "all", false, "Update servers for all VPN providers")
|
||||
flagSet.StringVar(&csvProviders, "providers", "", "CSV string of VPN providers to update server data for")
|
||||
if err := flagSet.Parse(args); err != nil {
|
||||
@@ -57,18 +54,8 @@ func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) e
|
||||
return ErrModeUnspecified
|
||||
}
|
||||
|
||||
options.DNSAddress = net.ParseIP(dnsAddress)
|
||||
if options.DNSAddress == nil {
|
||||
return fmt.Errorf("%w: %s", ErrDNSAddress, dnsAddress)
|
||||
}
|
||||
|
||||
if updateAll {
|
||||
for _, provider := range providers.All() {
|
||||
if provider == providers.Custom {
|
||||
continue
|
||||
}
|
||||
options.Providers = append(options.Providers, provider)
|
||||
}
|
||||
options.Providers = providers.All()
|
||||
} else {
|
||||
if csvProviders == "" {
|
||||
return ErrNoProviderSpecified
|
||||
@@ -83,48 +70,33 @@ func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) e
|
||||
return fmt.Errorf("options validation failed: %w", err)
|
||||
}
|
||||
|
||||
const clientTimeout = 10 * time.Second
|
||||
httpClient := &http.Client{Timeout: clientTimeout}
|
||||
|
||||
storage, err := storage.New(logger, constants.ServersData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create servers storage: %w", err)
|
||||
}
|
||||
currentServers := storage.GetServers()
|
||||
|
||||
updater := updater.New(options, httpClient, currentServers, logger)
|
||||
allServers, err := updater.UpdateServers(ctx)
|
||||
const clientTimeout = 10 * time.Second
|
||||
httpClient := &http.Client{Timeout: clientTimeout}
|
||||
unzipper := unzip.New(httpClient)
|
||||
parallelResolver := resolver.NewParallelResolver(options.DNSAddress)
|
||||
ipFetcher := ipinfo.New(httpClient)
|
||||
openvpnFileExtractor := extract.New()
|
||||
|
||||
providers := provider.NewProviders(storage, time.Now, logger, httpClient,
|
||||
unzipper, parallelResolver, ipFetcher, openvpnFileExtractor)
|
||||
|
||||
updater := updater.New(httpClient, storage, providers, logger)
|
||||
err = updater.UpdateServers(ctx, options.Providers, options.MinRatio)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot update server information: %w", err)
|
||||
}
|
||||
|
||||
if endUserMode {
|
||||
if err := storage.FlushToFile(allServers); err != nil {
|
||||
return fmt.Errorf("cannot write updated information to file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if maintainerMode {
|
||||
if err := writeToEmbeddedJSON(c.repoServersPath, allServers); err != nil {
|
||||
return fmt.Errorf("cannot write updated information to file: %w", err)
|
||||
err := storage.FlushToFile(c.repoServersPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot write servers data to embedded JSON file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeToEmbeddedJSON(repoServersPath string,
|
||||
allServers models.AllServers) error {
|
||||
const perms = 0600
|
||||
f, err := os.OpenFile(repoServersPath,
|
||||
os.O_TRUNC|os.O_WRONLY|os.O_CREATE, perms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
encoder := json.NewEncoder(f)
|
||||
encoder.SetIndent("", " ")
|
||||
return encoder.Encode(allServers)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ var (
|
||||
ErrFirewallZeroPort = errors.New("cannot have a zero port to block")
|
||||
ErrHostnameNotValid = errors.New("the hostname specified is not valid")
|
||||
ErrISPNotValid = errors.New("the ISP specified is not valid")
|
||||
ErrMinRatioNotValid = errors.New("minimum ratio is not valid")
|
||||
ErrMissingValue = errors.New("missing value")
|
||||
ErrNameNotValid = errors.New("the server name specified is not valid")
|
||||
ErrOpenVPNClientKeyMissing = errors.New("client key is missing")
|
||||
|
||||
@@ -34,6 +34,13 @@ func MergeWithInt(existing, other int) (result int) {
|
||||
return other
|
||||
}
|
||||
|
||||
func MergeWithFloat64(existing, other float64) (result float64) {
|
||||
if existing != 0 {
|
||||
return existing
|
||||
}
|
||||
return other
|
||||
}
|
||||
|
||||
func MergeWithStringPtr(existing, other *string) (result *string) {
|
||||
if existing != nil {
|
||||
return existing
|
||||
|
||||
@@ -32,6 +32,13 @@ func OverrideWithInt(existing, other int) (result int) {
|
||||
return other
|
||||
}
|
||||
|
||||
func OverrideWithFloat64(existing, other float64) (result float64) {
|
||||
if other == 0 {
|
||||
return existing
|
||||
}
|
||||
return other
|
||||
}
|
||||
|
||||
func OverrideWithStringPtr(existing, other *string) (result *string) {
|
||||
if other == nil {
|
||||
return existing
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/constants/openvpn"
|
||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||
"github.com/qdm12/gluetun/internal/openvpn/parse"
|
||||
"github.com/qdm12/gluetun/internal/openvpn/extract"
|
||||
"github.com/qdm12/gluetun/internal/provider/privateinternetaccess/presets"
|
||||
"github.com/qdm12/gotree"
|
||||
)
|
||||
|
||||
@@ -79,7 +81,7 @@ var ivpnAccountID = regexp.MustCompile(`^(i|ivpn)\-[a-zA-Z0-9]{4}\-[a-zA-Z0-9]{4
|
||||
|
||||
func (o OpenVPN) validate(vpnProvider string) (err error) {
|
||||
// Validate version
|
||||
validVersions := []string{constants.Openvpn24, constants.Openvpn25}
|
||||
validVersions := []string{openvpn.Openvpn24, openvpn.Openvpn25}
|
||||
if !helpers.IsOneOf(o.Version, validVersions...) {
|
||||
return fmt.Errorf("%w: %q can only be one of %s",
|
||||
ErrOpenVPNVersionIsNotValid, o.Version, strings.Join(validVersions, ", "))
|
||||
@@ -147,6 +149,12 @@ func validateOpenVPNConfigFilepath(isCustom bool,
|
||||
return err
|
||||
}
|
||||
|
||||
extractor := extract.New()
|
||||
_, _, err = extractor.Data(confFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed extracting information from custom configuration file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -165,7 +173,7 @@ func validateOpenVPNClientCertificate(vpnProvider,
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = parse.ExtractCert([]byte(clientCert))
|
||||
_, err = base64.StdEncoding.DecodeString(clientCert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -187,7 +195,7 @@ func validateOpenVPNClientKey(vpnProvider, clientKey string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = parse.ExtractPrivateKey([]byte(clientKey))
|
||||
_, err = base64.StdEncoding.DecodeString(clientKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -256,7 +264,7 @@ func (o *OpenVPN) overrideWith(other OpenVPN) {
|
||||
}
|
||||
|
||||
func (o *OpenVPN) setDefaults(vpnProvider string) {
|
||||
o.Version = helpers.DefaultString(o.Version, constants.Openvpn25)
|
||||
o.Version = helpers.DefaultString(o.Version, openvpn.Openvpn25)
|
||||
if vpnProvider == providers.Mullvad {
|
||||
o.Password = "m"
|
||||
}
|
||||
@@ -268,7 +276,7 @@ func (o *OpenVPN) setDefaults(vpnProvider string) {
|
||||
|
||||
var defaultEncPreset string
|
||||
if vpnProvider == providers.PrivateInternetAccess {
|
||||
defaultEncPreset = constants.PIAEncryptionPresetStrong
|
||||
defaultEncPreset = presets.Strong
|
||||
}
|
||||
o.PIAEncPreset = helpers.DefaultStringPtr(o.PIAEncPreset, defaultEncPreset)
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||
"github.com/qdm12/gluetun/internal/provider/privateinternetaccess/presets"
|
||||
"github.com/qdm12/gotree"
|
||||
)
|
||||
|
||||
@@ -55,8 +55,7 @@ func (o OpenVPNSelection) validate(vpnProvider string) (err error) {
|
||||
switch vpnProvider {
|
||||
// no restriction on port
|
||||
case providers.Cyberghost, providers.HideMyAss,
|
||||
providers.PrivateInternetAccess, providers.Privatevpn,
|
||||
providers.Protonvpn, providers.Torguard:
|
||||
providers.Privatevpn, providers.Torguard:
|
||||
// no custom port allowed
|
||||
case providers.Expressvpn, providers.Fastestvpn,
|
||||
providers.Ipvanish, providers.Nordvpn,
|
||||
@@ -106,9 +105,9 @@ func (o OpenVPNSelection) validate(vpnProvider string) (err error) {
|
||||
// Validate EncPreset
|
||||
if vpnProvider == providers.PrivateInternetAccess {
|
||||
validEncryptionPresets := []string{
|
||||
constants.PIAEncryptionPresetNone,
|
||||
constants.PIAEncryptionPresetNormal,
|
||||
constants.PIAEncryptionPresetStrong,
|
||||
presets.None,
|
||||
presets.Normal,
|
||||
presets.Strong,
|
||||
}
|
||||
if !helpers.IsOneOf(*o.PIAEncPreset, validEncryptionPresets...) {
|
||||
return fmt.Errorf("%w: %s; valid presets are %s",
|
||||
@@ -150,7 +149,7 @@ func (o *OpenVPNSelection) setDefaults(vpnProvider string) {
|
||||
|
||||
var defaultEncPreset string
|
||||
if vpnProvider == providers.PrivateInternetAccess {
|
||||
defaultEncPreset = constants.PIAEncryptionPresetStrong
|
||||
defaultEncPreset = presets.Strong
|
||||
}
|
||||
o.PIAEncPreset = helpers.DefaultStringPtr(o.PIAEncPreset, defaultEncPreset)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gotree"
|
||||
)
|
||||
|
||||
@@ -23,11 +22,11 @@ type Provider struct {
|
||||
}
|
||||
|
||||
// TODO v4 remove pointer for receiver (because of Surfshark).
|
||||
func (p *Provider) validate(vpnType string, allServers models.AllServers) (err error) {
|
||||
func (p *Provider) validate(vpnType string, storage Storage) (err error) {
|
||||
// Validate Name
|
||||
var validNames []string
|
||||
if vpnType == vpn.OpenVPN {
|
||||
validNames = providers.All()
|
||||
validNames = providers.AllWithCustom()
|
||||
validNames = append(validNames, "pia") // Retro-compatibility
|
||||
} else { // Wireguard
|
||||
validNames = []string{
|
||||
@@ -42,7 +41,7 @@ func (p *Provider) validate(vpnType string, allServers models.AllServers) (err e
|
||||
ErrVPNProviderNameNotValid, *p.Name, helpers.ChoicesOrString(validNames))
|
||||
}
|
||||
|
||||
err = p.ServerSelection.validate(*p.Name, allServers)
|
||||
err = p.ServerSelection.validate(*p.Name, storage)
|
||||
if err != nil {
|
||||
return fmt.Errorf("server selection: %w", err)
|
||||
}
|
||||
|
||||
@@ -68,21 +68,19 @@ var (
|
||||
)
|
||||
|
||||
func (ss *ServerSelection) validate(vpnServiceProvider string,
|
||||
allServers models.AllServers) (err error) {
|
||||
storage Storage) (err error) {
|
||||
switch ss.VPN {
|
||||
case vpn.OpenVPN, vpn.Wireguard:
|
||||
default:
|
||||
return fmt.Errorf("%w: %s", ErrVPNTypeNotValid, ss.VPN)
|
||||
}
|
||||
|
||||
countryChoices, regionChoices, cityChoices,
|
||||
ispChoices, nameChoices, hostnameChoices, err := getLocationFilterChoices(vpnServiceProvider, ss, allServers)
|
||||
filterChoices, err := getLocationFilterChoices(vpnServiceProvider, ss, storage)
|
||||
if err != nil {
|
||||
return err // already wrapped error
|
||||
}
|
||||
|
||||
err = validateServerFilters(*ss, countryChoices, regionChoices, cityChoices,
|
||||
ispChoices, nameChoices, hostnameChoices)
|
||||
err = validateServerFilters(*ss, filterChoices)
|
||||
if err != nil {
|
||||
if errors.Is(err, helpers.ErrNoChoice) {
|
||||
return fmt.Errorf("for VPN service provider %s: %w", vpnServiceProvider, err)
|
||||
@@ -135,155 +133,48 @@ func (ss *ServerSelection) validate(vpnServiceProvider string,
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLocationFilterChoices(vpnServiceProvider string, ss *ServerSelection,
|
||||
allServers models.AllServers) (
|
||||
countryChoices, regionChoices, cityChoices,
|
||||
ispChoices, nameChoices, hostnameChoices []string,
|
||||
func getLocationFilterChoices(vpnServiceProvider string,
|
||||
ss *ServerSelection, storage Storage) (filterChoices models.FilterChoices,
|
||||
err error) {
|
||||
switch vpnServiceProvider {
|
||||
case providers.Custom:
|
||||
case providers.Cyberghost:
|
||||
servers := allServers.GetCyberghost()
|
||||
countryChoices = validation.ExtractCountries(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
case providers.Expressvpn:
|
||||
servers := allServers.GetExpressvpn()
|
||||
countryChoices = validation.ExtractCountries(servers)
|
||||
cityChoices = validation.ExtractCities(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
case providers.Fastestvpn:
|
||||
servers := allServers.GetFastestvpn()
|
||||
countryChoices = validation.ExtractCountries(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
case providers.HideMyAss:
|
||||
servers := allServers.GetHideMyAss()
|
||||
countryChoices = validation.ExtractCountries(servers)
|
||||
regionChoices = validation.ExtractRegions(servers)
|
||||
cityChoices = validation.ExtractCities(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
case providers.Ipvanish:
|
||||
servers := allServers.GetIpvanish()
|
||||
countryChoices = validation.ExtractCountries(servers)
|
||||
cityChoices = validation.ExtractCities(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
case providers.Ivpn:
|
||||
servers := allServers.GetIvpn()
|
||||
countryChoices = validation.ExtractCountries(servers)
|
||||
cityChoices = validation.ExtractCities(servers)
|
||||
ispChoices = validation.ExtractISPs(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
case providers.Mullvad:
|
||||
servers := allServers.GetMullvad()
|
||||
countryChoices = validation.ExtractCountries(servers)
|
||||
cityChoices = validation.ExtractCities(servers)
|
||||
ispChoices = validation.ExtractISPs(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
case providers.Nordvpn:
|
||||
servers := allServers.GetNordvpn()
|
||||
regionChoices = validation.ExtractRegions(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
case providers.Perfectprivacy:
|
||||
servers := allServers.GetPerfectprivacy()
|
||||
cityChoices = validation.ExtractCities(servers)
|
||||
case providers.Privado:
|
||||
servers := allServers.GetPrivado()
|
||||
countryChoices = validation.ExtractCountries(servers)
|
||||
regionChoices = validation.ExtractRegions(servers)
|
||||
cityChoices = validation.ExtractCities(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
case providers.PrivateInternetAccess:
|
||||
servers := allServers.GetPia()
|
||||
regionChoices = validation.ExtractRegions(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
nameChoices = validation.ExtractServerNames(servers)
|
||||
case providers.Privatevpn:
|
||||
servers := allServers.GetPrivatevpn()
|
||||
countryChoices = validation.ExtractCountries(servers)
|
||||
cityChoices = validation.ExtractCities(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
case providers.Protonvpn:
|
||||
servers := allServers.GetProtonvpn()
|
||||
countryChoices = validation.ExtractCountries(servers)
|
||||
regionChoices = validation.ExtractRegions(servers)
|
||||
cityChoices = validation.ExtractCities(servers)
|
||||
nameChoices = validation.ExtractServerNames(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
case providers.Purevpn:
|
||||
servers := allServers.GetPurevpn()
|
||||
countryChoices = validation.ExtractCountries(servers)
|
||||
regionChoices = validation.ExtractRegions(servers)
|
||||
cityChoices = validation.ExtractCities(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
case providers.Surfshark:
|
||||
servers := allServers.GetSurfshark()
|
||||
countryChoices = validation.ExtractCountries(servers)
|
||||
cityChoices = validation.ExtractCities(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
regionChoices = validation.ExtractRegions(servers)
|
||||
filterChoices = storage.GetFilterChoices(vpnServiceProvider)
|
||||
|
||||
if vpnServiceProvider == providers.Surfshark {
|
||||
// // Retro compatibility
|
||||
// TODO v4 remove
|
||||
regionChoices = append(regionChoices, validation.SurfsharkRetroLocChoices()...)
|
||||
if err := helpers.AreAllOneOf(ss.Regions, regionChoices); err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, fmt.Errorf("%w: %s", ErrRegionNotValid, err)
|
||||
filterChoices.Regions = append(filterChoices.Regions, validation.SurfsharkRetroLocChoices()...)
|
||||
if err := helpers.AreAllOneOf(ss.Regions, filterChoices.Regions); err != nil {
|
||||
return models.FilterChoices{}, fmt.Errorf("%w: %s", ErrRegionNotValid, err)
|
||||
}
|
||||
// Retro compatibility
|
||||
// TODO remove in v4
|
||||
*ss = surfsharkRetroRegion(*ss)
|
||||
case providers.Torguard:
|
||||
servers := allServers.GetTorguard()
|
||||
countryChoices = validation.ExtractCountries(servers)
|
||||
cityChoices = validation.ExtractCities(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
case providers.VPNUnlimited:
|
||||
servers := allServers.GetVPNUnlimited()
|
||||
countryChoices = validation.ExtractCountries(servers)
|
||||
cityChoices = validation.ExtractCities(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
case providers.Vyprvpn:
|
||||
servers := allServers.GetVyprvpn()
|
||||
regionChoices = validation.ExtractRegions(servers)
|
||||
case providers.Wevpn:
|
||||
servers := allServers.GetWevpn()
|
||||
cityChoices = validation.ExtractCities(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
case providers.Windscribe:
|
||||
servers := allServers.GetWindscribe()
|
||||
regionChoices = validation.ExtractRegions(servers)
|
||||
cityChoices = validation.ExtractCities(servers)
|
||||
hostnameChoices = validation.ExtractHostnames(servers)
|
||||
default:
|
||||
return nil, nil, nil, nil, nil, nil, fmt.Errorf("%w: %s", ErrVPNProviderNameNotValid, vpnServiceProvider)
|
||||
}
|
||||
|
||||
return countryChoices, regionChoices, cityChoices,
|
||||
ispChoices, nameChoices, hostnameChoices, nil
|
||||
return filterChoices, nil
|
||||
}
|
||||
|
||||
// validateServerFilters validates filters against the choices given as arguments.
|
||||
// Set an argument to nil to pass the check for a particular filter.
|
||||
func validateServerFilters(settings ServerSelection,
|
||||
countryChoices, regionChoices, cityChoices, ispChoices,
|
||||
nameChoices, hostnameChoices []string) (err error) {
|
||||
if err := helpers.AreAllOneOf(settings.Countries, countryChoices); err != nil {
|
||||
func validateServerFilters(settings ServerSelection, filterChoices models.FilterChoices) (err error) {
|
||||
if err := helpers.AreAllOneOf(settings.Countries, filterChoices.Countries); err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrCountryNotValid, err)
|
||||
}
|
||||
|
||||
if err := helpers.AreAllOneOf(settings.Regions, regionChoices); err != nil {
|
||||
if err := helpers.AreAllOneOf(settings.Regions, filterChoices.Regions); err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrRegionNotValid, err)
|
||||
}
|
||||
|
||||
if err := helpers.AreAllOneOf(settings.Cities, cityChoices); err != nil {
|
||||
if err := helpers.AreAllOneOf(settings.Cities, filterChoices.Cities); err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrCityNotValid, err)
|
||||
}
|
||||
|
||||
if err := helpers.AreAllOneOf(settings.ISPs, ispChoices); err != nil {
|
||||
if err := helpers.AreAllOneOf(settings.ISPs, filterChoices.ISPs); err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrISPNotValid, err)
|
||||
}
|
||||
|
||||
if err := helpers.AreAllOneOf(settings.Hostnames, hostnameChoices); err != nil {
|
||||
if err := helpers.AreAllOneOf(settings.Hostnames, filterChoices.Hostnames); err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrHostnameNotValid, err)
|
||||
}
|
||||
|
||||
if err := helpers.AreAllOneOf(settings.Names, nameChoices); err != nil {
|
||||
if err := helpers.AreAllOneOf(settings.Names, filterChoices.Names); err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrNameNotValid, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,10 +24,14 @@ type Settings struct {
|
||||
Pprof pprof.Settings
|
||||
}
|
||||
|
||||
type Storage interface {
|
||||
GetFilterChoices(provider string) models.FilterChoices
|
||||
}
|
||||
|
||||
// Validate validates all the settings and returns an error
|
||||
// if one of them is not valid.
|
||||
// TODO v4 remove pointer for receiver (because of Surfshark).
|
||||
func (s *Settings) Validate(allServers models.AllServers) (err error) {
|
||||
func (s *Settings) Validate(storage Storage) (err error) {
|
||||
nameToValidation := map[string]func() error{
|
||||
"control server": s.ControlServer.validate,
|
||||
"dns": s.DNS.validate,
|
||||
@@ -42,7 +46,7 @@ func (s *Settings) Validate(allServers models.AllServers) (err error) {
|
||||
"version": s.Version.validate,
|
||||
// Pprof validation done in pprof constructor
|
||||
"VPN": func() error {
|
||||
return s.VPN.validate(allServers)
|
||||
return s.VPN.validate(storage)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -91,7 +95,7 @@ func (s *Settings) MergeWith(other Settings) {
|
||||
}
|
||||
|
||||
func (s *Settings) OverrideWith(other Settings,
|
||||
allServers models.AllServers) (err error) {
|
||||
storage Storage) (err error) {
|
||||
patchedSettings := s.copy()
|
||||
patchedSettings.ControlServer.overrideWith(other.ControlServer)
|
||||
patchedSettings.DNS.overrideWith(other.DNS)
|
||||
@@ -105,8 +109,8 @@ func (s *Settings) OverrideWith(other Settings,
|
||||
patchedSettings.Updater.overrideWith(other.Updater)
|
||||
patchedSettings.Version.overrideWith(other.Version)
|
||||
patchedSettings.VPN.overrideWith(other.VPN)
|
||||
patchedSettings.Pprof.MergeWith(other.Pprof)
|
||||
err = patchedSettings.Validate(allServers)
|
||||
patchedSettings.Pprof.OverrideWith(other.Pprof)
|
||||
err = patchedSettings.Validate(storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,15 +3,14 @@ package settings
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/provider/surfshark/servers"
|
||||
)
|
||||
|
||||
func surfsharkRetroRegion(selection ServerSelection) (
|
||||
updatedSelection ServerSelection) {
|
||||
locationData := constants.SurfsharkLocationData()
|
||||
locationData := servers.LocationData()
|
||||
|
||||
retroToLocation := make(map[string]models.SurfsharkLocationData, len(locationData))
|
||||
retroToLocation := make(map[string]servers.ServerLocation, len(locationData))
|
||||
for _, data := range locationData {
|
||||
if data.RetroLoc == "" {
|
||||
continue
|
||||
|
||||
@@ -2,7 +2,6 @@ package settings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -21,16 +20,15 @@ type Updater struct {
|
||||
Period *time.Duration
|
||||
// DNSAddress is the DNS server address to use
|
||||
// to resolve VPN server hostnames to IP addresses.
|
||||
// It cannot be nil in the internal state.
|
||||
DNSAddress net.IP
|
||||
// It cannot be the empty string in the internal state.
|
||||
DNSAddress string
|
||||
// MinRatio is the minimum ratio of servers to
|
||||
// find per provider, compared to the total current
|
||||
// number of servers. It defaults to 0.8.
|
||||
MinRatio float64
|
||||
// Providers is the list of VPN service providers
|
||||
// to update server information for.
|
||||
Providers []string
|
||||
// CLI is to precise the updater is running in CLI
|
||||
// mode. This is set automatically and cannot be set
|
||||
// by settings sources. It cannot be nil in the
|
||||
// internal state.
|
||||
CLI *bool
|
||||
}
|
||||
|
||||
func (u Updater) Validate() (err error) {
|
||||
@@ -40,21 +38,23 @@ func (u Updater) Validate() (err error) {
|
||||
ErrUpdaterPeriodTooSmall, *u.Period, minPeriod)
|
||||
}
|
||||
|
||||
for i, provider := range u.Providers {
|
||||
valid := false
|
||||
for _, validProvider := range providers.All() {
|
||||
if validProvider == providers.Custom {
|
||||
continue
|
||||
}
|
||||
if u.MinRatio <= 0 || u.MinRatio > 1 {
|
||||
return fmt.Errorf("%w: %.2f must be between 0+ and 1",
|
||||
ErrMinRatioNotValid, u.MinRatio)
|
||||
}
|
||||
|
||||
validProviders := providers.All()
|
||||
for _, provider := range u.Providers {
|
||||
valid := false
|
||||
for _, validProvider := range validProviders {
|
||||
if provider == validProvider {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return fmt.Errorf("%w: %s at index %d",
|
||||
ErrVPNProviderNameNotValid, provider, i)
|
||||
return fmt.Errorf("%w: %q can only be one of %s",
|
||||
ErrVPNProviderNameNotValid, provider, helpers.ChoicesOrString(validProviders))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,9 +64,9 @@ func (u Updater) Validate() (err error) {
|
||||
func (u *Updater) copy() (copied Updater) {
|
||||
return Updater{
|
||||
Period: helpers.CopyDurationPtr(u.Period),
|
||||
DNSAddress: helpers.CopyIP(u.DNSAddress),
|
||||
DNSAddress: u.DNSAddress,
|
||||
MinRatio: u.MinRatio,
|
||||
Providers: helpers.CopyStringSlice(u.Providers),
|
||||
CLI: u.CLI,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,9 +74,9 @@ func (u *Updater) copy() (copied Updater) {
|
||||
// unset field of the receiver settings object.
|
||||
func (u *Updater) mergeWith(other Updater) {
|
||||
u.Period = helpers.MergeWithDuration(u.Period, other.Period)
|
||||
u.DNSAddress = helpers.MergeWithIP(u.DNSAddress, other.DNSAddress)
|
||||
u.DNSAddress = helpers.MergeWithString(u.DNSAddress, other.DNSAddress)
|
||||
u.MinRatio = helpers.MergeWithFloat64(u.MinRatio, other.MinRatio)
|
||||
u.Providers = helpers.MergeStringSlices(u.Providers, other.Providers)
|
||||
u.CLI = helpers.MergeWithBool(u.CLI, other.CLI)
|
||||
}
|
||||
|
||||
// overrideWith overrides fields of the receiver
|
||||
@@ -84,15 +84,20 @@ func (u *Updater) mergeWith(other Updater) {
|
||||
// settings.
|
||||
func (u *Updater) overrideWith(other Updater) {
|
||||
u.Period = helpers.OverrideWithDuration(u.Period, other.Period)
|
||||
u.DNSAddress = helpers.OverrideWithIP(u.DNSAddress, other.DNSAddress)
|
||||
u.DNSAddress = helpers.OverrideWithString(u.DNSAddress, other.DNSAddress)
|
||||
u.MinRatio = helpers.OverrideWithFloat64(u.MinRatio, other.MinRatio)
|
||||
u.Providers = helpers.OverrideWithStringSlice(u.Providers, other.Providers)
|
||||
u.CLI = helpers.MergeWithBool(u.CLI, other.CLI)
|
||||
}
|
||||
|
||||
func (u *Updater) SetDefaults(vpnProvider string) {
|
||||
u.Period = helpers.DefaultDuration(u.Period, 0)
|
||||
u.DNSAddress = helpers.DefaultIP(u.DNSAddress, net.IPv4(1, 1, 1, 1))
|
||||
u.CLI = helpers.DefaultBool(u.CLI, false)
|
||||
u.DNSAddress = helpers.DefaultString(u.DNSAddress, "1.1.1.1:53")
|
||||
|
||||
if u.MinRatio == 0 {
|
||||
const defaultMinRatio = 0.8
|
||||
u.MinRatio = defaultMinRatio
|
||||
}
|
||||
|
||||
if len(u.Providers) == 0 && vpnProvider != providers.Custom {
|
||||
u.Providers = []string{vpnProvider}
|
||||
}
|
||||
@@ -110,11 +115,8 @@ func (u Updater) toLinesNode() (node *gotree.Node) {
|
||||
node = gotree.New("Server data updater settings:")
|
||||
node.Appendf("Update period: %s", *u.Period)
|
||||
node.Appendf("DNS address: %s", u.DNSAddress)
|
||||
node.Appendf("Minimum ratio: %.1f", u.MinRatio)
|
||||
node.Appendf("Providers to update: %s", strings.Join(u.Providers, ", "))
|
||||
|
||||
if *u.CLI {
|
||||
node.Appendf("CLI mode: enabled")
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@ func ExtractCountries(servers []models.Server) (values []string) {
|
||||
values = make([]string, 0, len(servers))
|
||||
for _, server := range servers {
|
||||
value := server.Country
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
_, alreadySeen := seen[value]
|
||||
if alreadySeen {
|
||||
continue
|
||||
@@ -35,6 +38,9 @@ func ExtractRegions(servers []models.Server) (values []string) {
|
||||
values = make([]string, 0, len(servers))
|
||||
for _, server := range servers {
|
||||
value := server.Region
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
_, alreadySeen := seen[value]
|
||||
if alreadySeen {
|
||||
continue
|
||||
@@ -51,6 +57,9 @@ func ExtractCities(servers []models.Server) (values []string) {
|
||||
values = make([]string, 0, len(servers))
|
||||
for _, server := range servers {
|
||||
value := server.City
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
_, alreadySeen := seen[value]
|
||||
if alreadySeen {
|
||||
continue
|
||||
@@ -67,6 +76,9 @@ func ExtractISPs(servers []models.Server) (values []string) {
|
||||
values = make([]string, 0, len(servers))
|
||||
for _, server := range servers {
|
||||
value := server.ISP
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
_, alreadySeen := seen[value]
|
||||
if alreadySeen {
|
||||
continue
|
||||
@@ -83,6 +95,9 @@ func ExtractServerNames(servers []models.Server) (values []string) {
|
||||
values = make([]string, 0, len(servers))
|
||||
for _, server := range servers {
|
||||
value := server.ServerName
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
_, alreadySeen := seen[value]
|
||||
if alreadySeen {
|
||||
continue
|
||||
@@ -99,6 +114,9 @@ func ExtractHostnames(servers []models.Server) (values []string) {
|
||||
values = make([]string, 0, len(servers))
|
||||
for _, server := range servers {
|
||||
value := server.Hostname
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
_, alreadySeen := seen[value]
|
||||
if alreadySeen {
|
||||
continue
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package validation
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/provider/surfshark/servers"
|
||||
)
|
||||
|
||||
// TODO remove in v4.
|
||||
func SurfsharkRetroLocChoices() (choices []string) {
|
||||
locationData := constants.SurfsharkLocationData()
|
||||
locationData := servers.LocationData()
|
||||
choices = make([]string, 0, len(locationData))
|
||||
seen := make(map[string]struct{}, len(locationData))
|
||||
for _, data := range locationData {
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gotree"
|
||||
)
|
||||
|
||||
@@ -21,7 +20,7 @@ type VPN struct {
|
||||
}
|
||||
|
||||
// TODO v4 remove pointer for receiver (because of Surfshark).
|
||||
func (v *VPN) validate(allServers models.AllServers) (err error) {
|
||||
func (v *VPN) validate(storage Storage) (err error) {
|
||||
// Validate Type
|
||||
validVPNTypes := []string{vpn.OpenVPN, vpn.Wireguard}
|
||||
if !helpers.IsOneOf(v.Type, validVPNTypes...) {
|
||||
@@ -29,7 +28,7 @@ func (v *VPN) validate(allServers models.AllServers) (err error) {
|
||||
ErrVPNTypeNotValid, v.Type, strings.Join(validVPNTypes, ", "))
|
||||
}
|
||||
|
||||
err = v.Provider.validate(v.Type, allServers)
|
||||
err = v.Provider.validate(v.Type, storage)
|
||||
if err != nil {
|
||||
return fmt.Errorf("provider settings: %w", err)
|
||||
}
|
||||
|
||||
3
internal/configuration/sources/env/health.go
vendored
3
internal/configuration/sources/env/health.go
vendored
@@ -2,14 +2,13 @@ package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func (r *Reader) ReadHealth() (health settings.Health, err error) {
|
||||
health.ServerAddress = os.Getenv("HEALTH_SERVER_ADDRESS")
|
||||
health.ServerAddress = getCleanedEnv("HEALTH_SERVER_ADDRESS")
|
||||
_, health.TargetAddress = r.getEnvWithRetro("HEALTH_TARGET_ADDRESS", "HEALTH_ADDRESS_TO_PING")
|
||||
|
||||
health.VPN.Initial, err = r.readDurationWithRetro(
|
||||
|
||||
45
internal/configuration/sources/env/helpers.go
vendored
45
internal/configuration/sources/env/helpers.go
vendored
@@ -1,7 +1,6 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
@@ -12,8 +11,18 @@ import (
|
||||
"github.com/qdm12/govalid/integer"
|
||||
)
|
||||
|
||||
// getCleanedEnv returns an environment variable value with
|
||||
// surrounding spaces and trailing new line characters removed.
|
||||
func getCleanedEnv(envKey string) (value string) {
|
||||
value = os.Getenv(envKey)
|
||||
value = strings.TrimSpace(value)
|
||||
value = strings.TrimSuffix(value, "\r\n")
|
||||
value = strings.TrimSuffix(value, "\n")
|
||||
return value
|
||||
}
|
||||
|
||||
func envToCSV(envKey string) (values []string) {
|
||||
csv := os.Getenv(envKey)
|
||||
csv := getCleanedEnv(envKey)
|
||||
if csv == "" {
|
||||
return nil
|
||||
}
|
||||
@@ -21,15 +30,24 @@ func envToCSV(envKey string) (values []string) {
|
||||
}
|
||||
|
||||
func envToInt(envKey string) (n int, err error) {
|
||||
s := os.Getenv(envKey)
|
||||
s := getCleanedEnv(envKey)
|
||||
if s == "" {
|
||||
return 0, nil
|
||||
}
|
||||
return strconv.Atoi(s)
|
||||
}
|
||||
|
||||
func envToFloat64(envKey string) (f float64, err error) {
|
||||
s := getCleanedEnv(envKey)
|
||||
if s == "" {
|
||||
return 0, nil
|
||||
}
|
||||
const bits = 64
|
||||
return strconv.ParseFloat(s, bits)
|
||||
}
|
||||
|
||||
func envToStringPtr(envKey string) (stringPtr *string) {
|
||||
s := os.Getenv(envKey)
|
||||
s := getCleanedEnv(envKey)
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
@@ -37,7 +55,7 @@ func envToStringPtr(envKey string) (stringPtr *string) {
|
||||
}
|
||||
|
||||
func envToBoolPtr(envKey string) (boolPtr *bool, err error) {
|
||||
s := os.Getenv(envKey)
|
||||
s := getCleanedEnv(envKey)
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
@@ -49,7 +67,7 @@ func envToBoolPtr(envKey string) (boolPtr *bool, err error) {
|
||||
}
|
||||
|
||||
func envToIntPtr(envKey string) (intPtr *int, err error) {
|
||||
s := os.Getenv(envKey)
|
||||
s := getCleanedEnv(envKey)
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
@@ -61,7 +79,7 @@ func envToIntPtr(envKey string) (intPtr *int, err error) {
|
||||
}
|
||||
|
||||
func envToUint8Ptr(envKey string) (uint8Ptr *uint8, err error) {
|
||||
s := os.Getenv(envKey)
|
||||
s := getCleanedEnv(envKey)
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
@@ -78,7 +96,7 @@ func envToUint8Ptr(envKey string) (uint8Ptr *uint8, err error) {
|
||||
}
|
||||
|
||||
func envToUint16Ptr(envKey string) (uint16Ptr *uint16, err error) {
|
||||
s := os.Getenv(envKey)
|
||||
s := getCleanedEnv(envKey)
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
@@ -95,7 +113,7 @@ func envToUint16Ptr(envKey string) (uint16Ptr *uint16, err error) {
|
||||
}
|
||||
|
||||
func envToDurationPtr(envKey string) (durationPtr *time.Duration, err error) {
|
||||
s := os.Getenv(envKey)
|
||||
s := getCleanedEnv(envKey)
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
@@ -114,15 +132,6 @@ func lowerAndSplit(csv string) (values []string) {
|
||||
return strings.Split(csv, ",")
|
||||
}
|
||||
|
||||
func decodeBase64(b64String string) (decoded string, err error) {
|
||||
b, err := base64.StdEncoding.DecodeString(b64String)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot decode base64 string %q: %w",
|
||||
b64String, err)
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
func unsetEnvKeys(envKeys []string, err error) (newErr error) {
|
||||
newErr = err
|
||||
for _, envKey := range envKeys {
|
||||
|
||||
@@ -20,7 +20,3 @@ func setTestEnv(t *testing.T, key, value string) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestXxx(t *testing.T) {
|
||||
t.Log(int(^uint32(0)))
|
||||
}
|
||||
|
||||
3
internal/configuration/sources/env/log.go
vendored
3
internal/configuration/sources/env/log.go
vendored
@@ -3,7 +3,6 @@ package env
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
@@ -20,7 +19,7 @@ func readLog() (log settings.Log, err error) {
|
||||
}
|
||||
|
||||
func readLogLevel() (level *log.Level, err error) {
|
||||
s := os.Getenv("LOG_LEVEL")
|
||||
s := getCleanedEnv("LOG_LEVEL")
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
34
internal/configuration/sources/env/openvpn.go
vendored
34
internal/configuration/sources/env/openvpn.go
vendored
@@ -2,7 +2,6 @@ package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
@@ -15,10 +14,10 @@ func (r *Reader) readOpenVPN() (
|
||||
err = unsetEnvKeys([]string{"OPENVPN_CLIENTKEY", "OPENVPN_CLIENTCRT"}, err)
|
||||
}()
|
||||
|
||||
openVPN.Version = os.Getenv("OPENVPN_VERSION")
|
||||
openVPN.Version = getCleanedEnv("OPENVPN_VERSION")
|
||||
openVPN.User = r.readOpenVPNUser()
|
||||
openVPN.Password = r.readOpenVPNPassword()
|
||||
confFile := os.Getenv("OPENVPN_CUSTOM_CONFIG")
|
||||
confFile := getCleanedEnv("OPENVPN_CUSTOM_CONFIG")
|
||||
if confFile != "" {
|
||||
openVPN.ConfFile = &confFile
|
||||
}
|
||||
@@ -26,20 +25,13 @@ func (r *Reader) readOpenVPN() (
|
||||
ciphersKey, _ := r.getEnvWithRetro("OPENVPN_CIPHERS", "OPENVPN_CIPHER")
|
||||
openVPN.Ciphers = envToCSV(ciphersKey)
|
||||
|
||||
auth := os.Getenv("OPENVPN_AUTH")
|
||||
auth := getCleanedEnv("OPENVPN_AUTH")
|
||||
if auth != "" {
|
||||
openVPN.Auth = &auth
|
||||
}
|
||||
|
||||
openVPN.ClientCrt, err = readBase64OrNil("OPENVPN_CLIENTCRT")
|
||||
if err != nil {
|
||||
return openVPN, fmt.Errorf("environment variable OPENVPN_CLIENTCRT: %w", err)
|
||||
}
|
||||
|
||||
openVPN.ClientKey, err = readBase64OrNil("OPENVPN_CLIENTKEY")
|
||||
if err != nil {
|
||||
return openVPN, fmt.Errorf("environment variable OPENVPN_CLIENTKEY: %w", err)
|
||||
}
|
||||
openVPN.ClientCrt = envToStringPtr("OPENVPN_CLIENTCRT")
|
||||
openVPN.ClientKey = envToStringPtr("OPENVPN_CLIENTKEY")
|
||||
|
||||
openVPN.PIAEncPreset = r.readPIAEncryptionPreset()
|
||||
|
||||
@@ -65,7 +57,7 @@ func (r *Reader) readOpenVPN() (
|
||||
return openVPN, fmt.Errorf("environment variable OPENVPN_VERBOSITY: %w", err)
|
||||
}
|
||||
|
||||
flagsStr := os.Getenv("OPENVPN_FLAGS")
|
||||
flagsStr := getCleanedEnv("OPENVPN_FLAGS")
|
||||
if flagsStr != "" {
|
||||
openVPN.Flags = strings.Fields(flagsStr)
|
||||
}
|
||||
@@ -84,20 +76,6 @@ func (r *Reader) readOpenVPNPassword() (password string) {
|
||||
return password
|
||||
}
|
||||
|
||||
func readBase64OrNil(envKey string) (valueOrNil *string, err error) {
|
||||
value := os.Getenv(envKey)
|
||||
if value == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
decoded, err := decodeBase64(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &decoded, nil
|
||||
}
|
||||
|
||||
func (r *Reader) readPIAEncryptionPreset() (presetPtr *string) {
|
||||
_, preset := r.getEnvWithRetro(
|
||||
"PRIVATE_INTERNET_ACCESS_OPENVPN_ENCRYPTION_PRESET",
|
||||
|
||||
@@ -3,7 +3,6 @@ package env
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
@@ -13,7 +12,7 @@ import (
|
||||
|
||||
func (r *Reader) readOpenVPNSelection() (
|
||||
selection settings.OpenVPNSelection, err error) {
|
||||
confFile := os.Getenv("OPENVPN_CUSTOM_CONFIG")
|
||||
confFile := getCleanedEnv("OPENVPN_CUSTOM_CONFIG")
|
||||
if confFile != "" {
|
||||
selection.ConfFile = &confFile
|
||||
}
|
||||
|
||||
3
internal/configuration/sources/env/pprof.go
vendored
3
internal/configuration/sources/env/pprof.go
vendored
@@ -2,7 +2,6 @@ package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/pprof"
|
||||
)
|
||||
@@ -23,7 +22,7 @@ func readPprof() (settings pprof.Settings, err error) {
|
||||
return settings, fmt.Errorf("environment variable PPROF_MUTEX_PROFILE_RATE: %w", err)
|
||||
}
|
||||
|
||||
settings.HTTPServer.Address = os.Getenv("PPROF_HTTP_SERVER_ADDRESS")
|
||||
settings.HTTPServer.Address = getCleanedEnv("PPROF_HTTP_SERVER_ADDRESS")
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
@@ -35,7 +34,7 @@ func (r *Reader) readVPNServiceProvider(vpnType string) (vpnProviderPtr *string)
|
||||
s = strings.ToLower(s)
|
||||
switch {
|
||||
case vpnType != vpn.Wireguard &&
|
||||
os.Getenv("OPENVPN_CUSTOM_CONFIG") != "": // retro compatibility
|
||||
getCleanedEnv("OPENVPN_CUSTOM_CONFIG") != "": // retro compatibility
|
||||
return stringPtr(providers.Custom)
|
||||
case s == "":
|
||||
return nil
|
||||
|
||||
@@ -2,7 +2,6 @@ package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
@@ -20,7 +19,7 @@ func (r *Reader) readPublicIP() (publicIP settings.PublicIP, err error) {
|
||||
}
|
||||
|
||||
func readPublicIPPeriod() (period *time.Duration, err error) {
|
||||
s := os.Getenv("PUBLICIP_PERIOD")
|
||||
s := getCleanedEnv("PUBLICIP_PERIOD")
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
6
internal/configuration/sources/env/reader.go
vendored
6
internal/configuration/sources/env/reader.go
vendored
@@ -1,8 +1,6 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/configuration/sources"
|
||||
)
|
||||
@@ -111,12 +109,12 @@ func (r *Reader) getEnvWithRetro(currentKey string,
|
||||
// We check retro-compatibility keys first since
|
||||
// the current key might be set in the Dockerfile.
|
||||
for _, key = range retroKeys {
|
||||
value = os.Getenv(key)
|
||||
value = getCleanedEnv(key)
|
||||
if value != "" {
|
||||
r.onRetroActive(key, currentKey)
|
||||
return key, value
|
||||
}
|
||||
}
|
||||
|
||||
return currentKey, os.Getenv(currentKey)
|
||||
return currentKey, getCleanedEnv(currentKey)
|
||||
}
|
||||
|
||||
3
internal/configuration/sources/env/server.go
vendored
3
internal/configuration/sources/env/server.go
vendored
@@ -2,7 +2,6 @@ package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/govalid/binary"
|
||||
@@ -20,7 +19,7 @@ func (r *Reader) readControlServer() (controlServer settings.ControlServer, err
|
||||
}
|
||||
|
||||
func readControlServerLog() (enabled *bool, err error) {
|
||||
s := os.Getenv("HTTP_CONTROL_SERVER_LOG")
|
||||
s := getCleanedEnv("HTTP_CONTROL_SERVER_LOG")
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -49,7 +48,7 @@ func (r *Reader) readServerSelection(vpnProvider, vpnType string) (
|
||||
serverNamesKey, _ := r.getEnvWithRetro("SERVER_NAMES", "SERVER_NAME")
|
||||
ss.Names = envToCSV(serverNamesKey)
|
||||
|
||||
if csv := os.Getenv("SERVER_NUMBER"); csv != "" {
|
||||
if csv := getCleanedEnv("SERVER_NUMBER"); csv != "" {
|
||||
numbersStrings := strings.Split(csv, ",")
|
||||
numbers := make([]uint16, len(numbersStrings))
|
||||
for i, numberString := range numbersStrings {
|
||||
|
||||
3
internal/configuration/sources/env/system.go
vendored
3
internal/configuration/sources/env/system.go
vendored
@@ -3,7 +3,6 @@ package env
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
@@ -26,7 +25,7 @@ func (r *Reader) readSystem() (system settings.System, err error) {
|
||||
return system, err
|
||||
}
|
||||
|
||||
system.Timezone = os.Getenv("TZ")
|
||||
system.Timezone = getCleanedEnv("TZ")
|
||||
|
||||
return system, nil
|
||||
}
|
||||
|
||||
13
internal/configuration/sources/env/updater.go
vendored
13
internal/configuration/sources/env/updater.go
vendored
@@ -2,8 +2,6 @@ package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
@@ -20,13 +18,18 @@ func readUpdater() (updater settings.Updater, err error) {
|
||||
return updater, err
|
||||
}
|
||||
|
||||
updater.MinRatio, err = envToFloat64("UPDATER_MIN_RATIO")
|
||||
if err != nil {
|
||||
return updater, fmt.Errorf("environment variable UPDATER_MIN_RATIO: %w", err)
|
||||
}
|
||||
|
||||
updater.Providers = envToCSV("UPDATER_VPN_SERVICE_PROVIDERS")
|
||||
|
||||
return updater, nil
|
||||
}
|
||||
|
||||
func readUpdaterPeriod() (period *time.Duration, err error) {
|
||||
s := os.Getenv("UPDATER_PERIOD")
|
||||
s := getCleanedEnv("UPDATER_PERIOD")
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
@@ -38,11 +41,11 @@ func readUpdaterPeriod() (period *time.Duration, err error) {
|
||||
return period, nil
|
||||
}
|
||||
|
||||
func readUpdaterDNSAddress() (ip net.IP, err error) {
|
||||
func readUpdaterDNSAddress() (address string, err error) {
|
||||
// TODO this is currently using Cloudflare in
|
||||
// plaintext to not be blocked by DNS over TLS by default.
|
||||
// If a plaintext address is set in the DNS settings, this one will be used.
|
||||
// use custom future encrypted DNS written in Go without blocking
|
||||
// as it's too much trouble to start another parallel unbound instance for now.
|
||||
return nil, nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/govalid/binary"
|
||||
@@ -18,7 +17,7 @@ func readVersion() (version settings.Version, err error) {
|
||||
}
|
||||
|
||||
func readVersionEnabled() (enabled *bool, err error) {
|
||||
s := os.Getenv("VERSION_INFORMATION")
|
||||
s := getCleanedEnv("VERSION_INFORMATION")
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
3
internal/configuration/sources/env/vpn.go
vendored
3
internal/configuration/sources/env/vpn.go
vendored
@@ -2,14 +2,13 @@ package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func (r *Reader) readVPN() (vpn settings.VPN, err error) {
|
||||
vpn.Type = strings.ToLower(os.Getenv("VPN_TYPE"))
|
||||
vpn.Type = strings.ToLower(getCleanedEnv("VPN_TYPE"))
|
||||
|
||||
vpn.Provider, err = r.readProvider(vpn.Type)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/govalid/port"
|
||||
@@ -22,7 +21,7 @@ func (r *Reader) readWireguardSelection() (
|
||||
return selection, err
|
||||
}
|
||||
|
||||
selection.PublicKey = os.Getenv("WIREGUARD_PUBLIC_KEY")
|
||||
selection.PublicKey = getCleanedEnv("WIREGUARD_PUBLIC_KEY")
|
||||
|
||||
return selection, nil
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/openvpn/extract"
|
||||
)
|
||||
|
||||
// ReadFromFile reads the content of the file as a string.
|
||||
@@ -32,3 +35,21 @@ func ReadFromFile(filepath string) (s *string, err error) {
|
||||
content = strings.TrimSuffix(content, "\n")
|
||||
return &content, nil
|
||||
}
|
||||
|
||||
func readPEMFile(filepath string) (base64Ptr *string, err error) {
|
||||
pemData, err := ReadFromFile(filepath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading file: %w", err)
|
||||
}
|
||||
|
||||
if pemData == nil {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
base64Data, err := extract.PEM([]byte(*pemData))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("extracting base64 encoded data from PEM content: %w", err)
|
||||
}
|
||||
|
||||
return &base64Data, nil
|
||||
}
|
||||
|
||||
@@ -14,12 +14,12 @@ const (
|
||||
)
|
||||
|
||||
func (r *Reader) readOpenVPN() (settings settings.OpenVPN, err error) {
|
||||
settings.ClientKey, err = ReadFromFile(OpenVPNClientKeyPath)
|
||||
settings.ClientKey, err = readPEMFile(OpenVPNClientKeyPath)
|
||||
if err != nil {
|
||||
return settings, fmt.Errorf("client key: %w", err)
|
||||
}
|
||||
|
||||
settings.ClientCrt, err = ReadFromFile(OpenVPNClientCertificatePath)
|
||||
settings.ClientCrt, err = readPEMFile(OpenVPNClientCertificatePath)
|
||||
if err != nil {
|
||||
return settings, fmt.Errorf("client certificate: %w", err)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
package secrets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/sources/files"
|
||||
"github.com/qdm12/gluetun/internal/openvpn/extract"
|
||||
)
|
||||
|
||||
// getCleanedEnv returns an environment variable value with
|
||||
// surrounding spaces and trailing new line characters removed.
|
||||
func getCleanedEnv(envKey string) (value string) {
|
||||
value = os.Getenv(envKey)
|
||||
value = strings.TrimSpace(value)
|
||||
value = strings.TrimSuffix(value, "\r\n")
|
||||
value = strings.TrimSuffix(value, "\n")
|
||||
return value
|
||||
}
|
||||
|
||||
func readSecretFileAsStringPtr(secretPathEnvKey, defaultSecretPath string) (
|
||||
stringPtr *string, err error) {
|
||||
path := os.Getenv(secretPathEnvKey)
|
||||
path := getCleanedEnv(secretPathEnvKey)
|
||||
if path == "" {
|
||||
path = defaultSecretPath
|
||||
}
|
||||
@@ -17,7 +30,7 @@ func readSecretFileAsStringPtr(secretPathEnvKey, defaultSecretPath string) (
|
||||
|
||||
func readSecretFileAsString(secretPathEnvKey, defaultSecretPath string) (
|
||||
s string, err error) {
|
||||
path := os.Getenv(secretPathEnvKey)
|
||||
path := getCleanedEnv(secretPathEnvKey)
|
||||
if path == "" {
|
||||
path = defaultSecretPath
|
||||
}
|
||||
@@ -29,3 +42,22 @@ func readSecretFileAsString(secretPathEnvKey, defaultSecretPath string) (
|
||||
}
|
||||
return *stringPtr, nil
|
||||
}
|
||||
|
||||
func readPEMSecretFile(secretPathEnvKey, defaultSecretPath string) (
|
||||
base64Ptr *string, err error) {
|
||||
pemData, err := readSecretFileAsStringPtr(secretPathEnvKey, defaultSecretPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading secret file: %w", err)
|
||||
}
|
||||
|
||||
if pemData == nil {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
base64Data, err := extract.PEM([]byte(*pemData))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("extracting base64 encoded data from PEM content: %w", err)
|
||||
}
|
||||
|
||||
return &base64Data, nil
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func readOpenVPN() (
|
||||
return settings, fmt.Errorf("cannot read password file: %w", err)
|
||||
}
|
||||
|
||||
settings.ClientKey, err = readSecretFileAsStringPtr(
|
||||
settings.ClientKey, err = readPEMSecretFile(
|
||||
"OPENVPN_CLIENTKEY_SECRETFILE",
|
||||
"/run/secrets/openvpn_clientkey",
|
||||
)
|
||||
@@ -32,7 +32,7 @@ func readOpenVPN() (
|
||||
return settings, fmt.Errorf("cannot read client key file: %w", err)
|
||||
}
|
||||
|
||||
settings.ClientCrt, err = readSecretFileAsStringPtr(
|
||||
settings.ClientCrt, err = readPEMSecretFile(
|
||||
"OPENVPN_CLIENTCRT_SECRETFILE",
|
||||
"/run/secrets/openvpn_clientcrt",
|
||||
)
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
AES128cbc = "aes-128-cbc"
|
||||
AES256cbc = "aes-256-cbc"
|
||||
AES128gcm = "aes-128-gcm"
|
||||
AES256gcm = "aes-256-gcm"
|
||||
SHA1 = "sha1"
|
||||
SHA256 = "sha256"
|
||||
SHA512 = "sha512"
|
||||
)
|
||||
|
||||
const (
|
||||
Openvpn24 = "2.4"
|
||||
Openvpn25 = "2.5"
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
const (
|
||||
CyberghostCA = "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"
|
||||
ExpressvpnCA = "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"
|
||||
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=="
|
||||
ExpressvpnTLSAuth = "48d9999bd71095b10649c7cb471c1051b1afdece597cea06909b99303a18c67401597b12c04a787e98cdb619ee960d90a0165529dc650f3a5c6fbe77c91c137dcf55d863fcbe314df5f0b45dbe974d9bde33ef5b4803c3985531c6c23ca6906d6cd028efc8585d1b9e71003566bd7891b9cc9212bcba510109922eed87f5c8e66d8e59cbd82575261f02777372b2cd4ca5214c4a6513ff26dd568f574fd40d6cd450fc788160ff68434ce2bf6afb00e710a3198538f14c4d45d84ab42637872e778a6b35a124e700920879f1d003ba93dccdb953cdf32bea03f365760b0ed8002098d4ce20d045b45a83a8432cc737677aed27125592a7148d25c87fdbe0a3f6"
|
||||
FastestvpnCA = "MIIFQjCCAyqgAwIBAgIIUfxepT+rr8owDQYJKoZIhvcNAQEMBQAwPzELMAkGA1UEBhMCS1kxEzARBgNVBAoTCkZhc3Rlc3RWUE4xGzAZBgNVBAMTEkZhc3Rlc3RWUE4gUm9vdCBDQTAeFw0xNzA5MTYwMDAxNDZaFw0yNzA5MTQwMDAxNDZaMD8xCzAJBgNVBAYTAktZMRMwEQYDVQQKEwpGYXN0ZXN0VlBOMRswGQYDVQQDExJGYXN0ZXN0VlBOIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC1Xj+WfPTozFynFqc+c3CVrggIllaXEl5bY5VgFynXkqCTM6lSrfC4pNjGXUbqWe6RnGJbM4/6kUn+lQDjFSQV1rzP2eDS8+r5+X2WXh4AoeNRUWhvSG+HiHD/B2EFK+Nd5BRSdUjpKWAtsCmT2bBt7nT0jN1OdeNrLJeyF8siAqv/oQzKznF9aIe/N01b2M8ZOFTzoXi2fZAckgGWui8NB/lzkVIJqSkAPRL8qiJLuRCPVOX1PFD8vV//R8/QumtfbcYBMo6vCk2HmWdrh5OQHPxb3KJtbtG+Z1j8x6HGEAe17djYepBiRMyCEQvYgfD6tvFylc4IquhqE9yaP60PJod5TxpWnRQ6HIGSeBm+S+rYSMalTZ8+pUqOOA+IQCYpfpx6EKIJL/VsW2C7cXdvudxDhXPI5lR/QidCb9Ohq3WkfxXaYwzrngdg2avmNqId9R4KESuM9GoHW0dszfyBCh5wYfeaffMElfDam3B92NUwyhZwtIiv623WVXY9PPz+EDjSJsIAu2Vi1vdJyA4nD4k9Lwmx/1zTc/UaYVLsiBqL2WdfvFTeoWoV+dNxQXSEPhB8gwi8x4O4lZW0cwVy/6fa8KMY8gZbcbSTr7U5bRERfW8l+jY+mYKQ/M/ccgpxaHiw1/+4LWfbJQ7VhJJrTyN0C36FQzY1URkSXg+53wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmVEL4x6xdCqiqu2OBLs27EA8xGYwDQYJKoZIhvcNAQEMBQADggIBABCpITvO1+R4T9v2+onHiFxU5JjtCZ0zkXqRCMp/Z0UIYbeo1p07pZCPAUjBfGPCkAaR++OiG9sysALdJf8Y6HQKcyuAcWUqQnaIhoZ2JcAP7EKq7uCqsMhcYZD/j3O/3RPtSW5UOx6ItDU+Ua0t9Edho9whNw0VQXmo1JjYoP3FzPjuKoDWTSO1q5eYlZfwcTcs55O2shNkFafPg/6cCm5j6v9nyHrM3sk4LjkrBPUXVx2m/aoz219t8O9Ha9/CdMKXsPO/8gTUzpgnzSgPnGnBmi5xr1nspVN8X4E2f3D+DKqBim3YgslD68NcuFQvJ0/BxZzWVbrr+QXoyzaiCgXuogpIDc2bB6oRXqFnHNz36d4QJmJdWdSaijiS/peQ6EOPgOZ1GuObLWlDCBZLNeQ+N6QaiJxVO4XUj/s22i1IRtwdz84TRHrbWiIpEymsqmb/Ep5r4xV5d6+791axclfOTH7tQrY/SbPtTJI4OEgNekI8YfadQifpelF82MsFFEZuaQn0lj+fvLGtE/zKh3OdLTxRc5TAgBB+0T81+JQosygNr2aFFG0hxar1eyw/gLeG8H+7Ie50pyPvXO4OgB6Key8rSExpilQXlvAT1qX0qS3/K1i/9QkSE9ftIPT6vtwLV2sVQzfyanI4IZgWC6ryhvNLsRn0NFnQclor0+aq"
|
||||
FastestvpnTLSAuth = "697fe793b32cb5091d30f2326d5d124a9412e93d0a44ef7361395d76528fcbfc82c3859dccea70a93cfa8fae409709bff75f844cf5ff0c237f426d0c20969233db0e706edb6bdf195ec3dc11b3f76bc807a77e74662d9a800c8cd1144ebb67b7f0d3f1281d1baf522bfe03b7c3f963b1364fc0769400e413b61ca7b43ab19fac9e0f77e41efd4bda7fd77b1de2d7d7855cbbe3e620cecceac72c21a825b243e651f44d90e290e09c3ad650de8fca99c858bc7caad584bc69b11e5c9fd9381c69c505ec487a65912c672d83ed0113b5a74ddfbd3ab33b3683cec593557520a72c4d6cce46111f56f3396cc3ce7183edce553c68ea0796cf6c4375fad00aaa2a42"
|
||||
HideMyAssCA = "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"
|
||||
HideMyAssCert = "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"
|
||||
HideMyAssRSAKey = "MIIEpAIBAAKCAQEA5XY3ERJYWs/YIeBoybivNlu+M32rJs+CAZsh7BnnetTxytI4ngsMRoqXETuis8udp2hsqEHsglLR9tlk9C8yCuKhxbkpdrXFWdISmUq5sa7/wqg/zJF1AZm5Jy0oHNyTHfG6XW61I/h9IN5dmcR9YLir8DVDBNllbtt0z+DnvOhYJOqC30ENahWkTmNKl1cT7EBrR5slddiBJleAb08z77pwsD310e6jWTBySsBcPy+xu/Jj2QgVil/3mstZZDI+noFzs3SkTFBkha/lNTP7NODBQ6m39iaJxz6ZR1xE3v7XU0H5WnpZIcQ2+kmu5Krk2y1GYMKL+9oaotXFPz9v+QIDAQABAoIBAQCcMcssOMOiFWc3MC3EWo4SP4MKQ9n0Uj5Z34LI151FdJyehlj54+VYQ1Cv71tCbjED2sZUBoP69mtsT/EzcsjqtfiOwgrifrs2+BOm+0HKHKiGlcbP9peiHkT10PxEITWXpYtJvGlbcfOjIxqt6B28cBjCK09ShrVQL9ylAKBearRRUacszppntMNTMtN/uG48ZR9Wm+xAczImdG6CrG5sLI/++JwM5PDChLvn5JgMGyOfQZdjNe1oSOVLmqFeG5uu/FS4oMon9+HtfjHJr4ZgA1yQ2wQh3GvEjlP8zwHxEpRJYbxpj6ZbjHZJ2HLX/Gcd9/cXiN8+fQ2zPIYQyG9dAoGBAPUUmt2nJNvl7gj0GbZZ3XR9o+hvj7bJ74W2NhMrw6kjrrzHTAUQd1sBQS8szAQCLqf2ou1aw9AMMBdsLAHydXxvbH7IBAla7rKr23iethtSfjhTNSgQLJHVZlNHfp3hzNtCQZ7j0qVjrteNotrdVF7kKPHDXAK00ICy6SPNjvrXAoGBAO+vdnO15jLeZbbi3lQNS4r8oCadyqyX7ouKE6MtKNhiPsNPGqHKiGcKs/+QylVgYvSmm7TgpsCAiEYeLSPT+Yq3y7HtwVpULlpfAhEJXmvn/6hGpOizx1WNGWhw7nHPWPDzf+jqCGzHdhK0aEZR3MZZQ+U+uKfGiJ8vrvgB7eGvAoGAWxxp5nU48rcsIw/8bxpBhgkfYk33M5EnBqKSv9XJS5wEXhIJZOiWNrLktNEGl4boKXE7aNoRacreJhcE1UR6AOS7hPZ+6atwiePyF4mJUeb9HZtxa493wk9/Vv6BR9il++1Jz/QKX4oLef8hyBP4Rb60qgxirG7kBLR+j9zfhskCgYEAzA5y5xIeuIIU0H4XUDG9dcebxSSjbwsuYIgeLdb9pjMGQhsvjjyyoh8/nT20tLkJpkXN3FFCRjNnUWLRhWYrVkkh1wqWiYOPrwqh5MU4KN/sDWSPcznTY+drkTpMFoKzsvdrl2zf3VR3FneXKv742bkXj601Ykko+XWMHcLutisCgYBSq8IrsjzfaTQiTGI9a7WWsvzK92bq7Abnfq7swAXWcJd/bnjTQKLrrvt2bmwNvlWKAb3c69BFMn0X4t4PuN0iJQ39D6aQAEaM7HwWAmjf5TbodbmgbGxdsUB4xcCIQQ1mvTkigXWrCg0YAD2GZSoaslXAAVv6nR5qWEIa0Hx9GA=="
|
||||
IpvanishCA = "MIIErTCCA5WgAwIBAgIJAMYKzSS8uPKDMA0GCSqGSIb3DQEBDQUAMIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCRkwxFDASBgNVBAcTC1dpbnRlciBQYXJrMREwDwYDVQQKEwhJUFZhbmlzaDEVMBMGA1UECxMMSVBWYW5pc2ggVlBOMRQwEgYDVQQDEwtJUFZhbmlzaCBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBpcHZhbmlzaC5jb20wHhcNMTIwMTExMTkzMjIwWhcNMjgxMTAyMTkzMjIwWjCBlTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkZMMRQwEgYDVQQHEwtXaW50ZXIgUGFyazERMA8GA1UEChMISVBWYW5pc2gxFTATBgNVBAsTDElQVmFuaXNoIFZQTjEUMBIGA1UEAxMLSVBWYW5pc2ggQ0ExIzAhBgkqhkiG9w0BCQEWFHN1cHBvcnRAaXB2YW5pc2guY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt9DBWNr/IKOuY3TmDP5x7vYZR0DGxLbXU8TyAzBbjUtFFMbhxlHiXVQrZHmgzih94x7BgXM7tWpmMKYVb+gNaqMdWE680Qm3nOwmhy/dulXDkEHAwD05i/iTx4ZaUdtV2vsKBxRg1vdC4AEiwD7bqV4HOi13xcG971aQ55Mj1KeCdA0aNvpat1LWx2jjWxsfI8s2Lv5Fkoi1HO1+vTnnaEsJZrBgAkLXpItqP29Lik3/OBIvkBIxlKrhiVPixE5qNiD+eSPirsmROvsyIonoJtuY4Dw5K6pcNlKyYiwo1IOFYU3YxffwFJk+bSW4WVBhsdf5dGxq/uOHmuz5gdwxCwIDAQABo4H9MIH6MAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFEv9FCWJHefBcIPX9p8RHCVOGe6uMIHKBgNVHSMEgcIwgb+AFEv9FCWJHefBcIPX9p8RHCVOGe6uoYGbpIGYMIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCRkwxFDASBgNVBAcTC1dpbnRlciBQYXJrMREwDwYDVQQKEwhJUFZhbmlzaDEVMBMGA1UECxMMSVBWYW5pc2ggVlBOMRQwEgYDVQQDEwtJUFZhbmlzaCBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBpcHZhbmlzaC5jb22CCQDGCs0kvLjygzANBgkqhkiG9w0BAQ0FAAOCAQEAI2dkh/43ksV2fdYpVGhYaFZPVqCJoToCez0IvOmLeLGzow+EOSrY508oyjYeNP4VJEjApqo0NrMbKl8g/8bpLBcotOCF1c1HZ+y9v7648uumh01SMjsbBeHOuQcLb+7gX6c0pEmxWv8qj5JiW3/1L1bktnjW5Yp5oFkFSMXjOnIoYKHyKLjN2jtwH6XowUNYpg4qVtKU0CXPdOznWcd9/zSfa393HwJPeeVLbKYaFMC4IEbIUmKYtWyoJ9pJ58smU3pWsHZUg9Zc0LZZNjkNlBdQSLmUHAJ33Bd7pJS0JQeiWviC+4UTmzEWRKa7pDGnYRYNu2cUo0/voStphv8EVA=="
|
||||
IvpnCA = "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"
|
||||
IvpnTLSAuth = "ac470c93ff9f5602a8aab37dee84a52814d10f20490ad23c47d5d82120c1bf859e93d0696b455d4a1b8d55d40c2685c41ca1d0aef29a3efd27274c4ef09020a3978fe45784b335da6df2d12db97bbb838416515f2a96f04715fd28949c6fe296a925cfada3f8b8928ed7fc963c1563272f5cf46e5e1d9c845d7703ca881497b7e6564a9d1dea9358adffd435295479f47d5298fabf5359613ff5992cb57ff081a04dfb81a26513a6b44a9b5490ad265f8a02384832a59cc3e075ad545461060b7bcab49bac815163cb80983dd51d5b1fd76170ffd904d8291071e96efc3fb777856c717b148d08a510f5687b8a8285dcffe737b98916dd15ef6235dee4266d3b"
|
||||
MullvadCA = "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"
|
||||
NordvpnCA = "MIIFCjCCAvKgAwIBAgIBATANBgkqhkiG9w0BAQ0FADA5MQswCQYDVQQGEwJQQTEQMA4GA1UEChMHTm9yZFZQTjEYMBYGA1UEAxMPTm9yZFZQTiBSb290IENBMB4XDTE2MDEwMTAwMDAwMFoXDTM1MTIzMTIzNTk1OVowOTELMAkGA1UEBhMCUEExEDAOBgNVBAoTB05vcmRWUE4xGDAWBgNVBAMTD05vcmRWUE4gUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMkr/BYhyo0F2upsIMXwC6QvkZps3NN2/eQFkfQIS1gql0aejsKsEnmY0Kaon8uZCTXPsRH1gQNgg5D2gixdd1mJUvV3dE3y9FJrXMoDkXdCGBodvKJyU6lcfEVF6/UxHcbBguZK9UtRHS9eJYm3rpL/5huQMCppX7kUeQ8dpCwd3iKITqwd1ZudDqsWaU0vqzC2H55IyaZ/5/TnCk31Q1UP6BksbbuRcwOVskEDsm6YoWDnn/IIzGOYnFJRzQH5jTz3j1QBvRIuQuBuvUkfhx1FEwhwZigrcxXuMP+QgM54kezgziJUaZcOM2zF3lvrwMvXDMfNeIoJABv9ljw969xQ8czQCU5lMVmA37ltv5Ec9U5hZuwk/9QO1Z+d/r6Jx0mlurS8gnCAKJgwa3kyZw6e4FZ8mYL4vpRRhPdvRTWCMJkeB4yBHyhxUmTRgJHm6YR3D6hcFAc9cQcTEl/I60tMdz33G6m0O42sQt/+AR3YCY/RusWVBJB/qNS94EtNtj8iaebCQW1jHAhvGmFILVR9lzD0EzWKHkvyWEjmUVRgCDd6Ne3eFRNS73gdv/C3l5boYySeu4exkEYVxVRn8DhCxs0MnkMHWFK6MyzXCCn+JnWFDYPfDKHvpff/kLDobtPBf+Lbch5wQy9quY27xaj0XwLyjOltpiSTLWae/Q4vAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBDQUAA4ICAQC9fUL2sZPxIN2mD32VeNySTgZlCEdVmlq471o/bDMP4B8gnQesFRtXY2ZCjs50Jm73B2LViL9qlREmI6vE5IC8IsRBJSV4ce1WYxyXro5rmVg/k6a10rlsbK/eg//GHoJxDdXDOokLUSnxt7gk3QKpX6eCdh67p0PuWm/7WUJQxH2SDxsT9vB/iZriTIEe/ILoOQF0Aqp7AgNCcLcLAmbxXQkXYCCSB35Vp06u+eTWjG0/pyS5V14stGtw+fA0DJp5ZJV4eqJ5LqxMlYvEZ/qKTEdoCeaXv2QEmN6dVqjDoTAok0t5u4YRXzEVCfXAC3ocplNdtCA72wjFJcSbfif4BSC8bDACTXtnPC7nD0VndZLp+RiNLeiENhk0oTC+UVdSc+n2nJOzkCK0vYu0Ads4JGIB7g8IB3z2t9ICmsWrgnhdNdcOe15BincrGA8avQ1cWXsfIKEjbrnEuEk9b5jel6NfHtPKoHc9mDpRdNPISeVawDBM1mJChneHt59Nh8Gah74+TM1jBsw4fhJPvoc7Atcg740JErb904mZfkIEmojCVPhBHVQ9LHBAdM8qFI2kRK0IynOmAZhexlP/aT/kpEsEPyaZQlnBn3An1CRz8h0SPApL8PytggYKeQmRhl499+6jLxcZ2IegLfqq41dzIjwHwTMplg+1pKIOVojpWA=="
|
||||
NordvpnTLSAuth = "e685bdaf659a25a200e2b9e39e51ff030fc72cf1ce07232bd8b2be5e6c670143f51e937e670eee09d4f2ea5a6e4e69965db852c275351b86fc4ca892d78ae002d6f70d029bd79c4d1c26cf14e9588033cf639f8a74809f29f72b9d58f9b8f5fefc7938eade40e9fed6cb92184abb2cc10eb1a296df243b251df0643d53724cdb5a92a1d6cb817804c4a9319b57d53be580815bcfcb2df55018cc83fc43bc7ff82d51f9b88364776ee9d12fc85cc7ea5b9741c4f598c485316db066d52db4540e212e1518a9bd4828219e24b20d88f598a196c9de96012090e333519ae18d35099427e7b372d348d352dc4c85e18cd4b93f8a56ddb2e64eb67adfc9b337157ff4"
|
||||
PerfectprivacyCA = "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"
|
||||
PerfectprivacyCert = "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"
|
||||
PerfectprivacyKey = "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"
|
||||
PerfectprivacyTLSCrypt = "d10a8e2641f5834f6c5e04a6ee9a798553d338fa2836ef2a91057c1f6174a3a12b36f16d1110b20e42ae94d3bd579213e9c3770be6c74804348dddba876945a5a3ab7660f9436f85f331641f6efc81315f0d12b2766a9f15c10a53cf9ba32dc80f03b5f15a6cc6987bda795dbe83443ec81f3d5e161cd47fab6b1f125b3adeee1eae33370d018594e0ff6b25b815228d27371b32c82a95f4929d3abb5fa36e57bf1f42353542568fbb8233f4645f05820275f79570cb8bbcf8010fc5d20f07d031a8227d45daf7349e34158c91a3d4e5add19cfa02f683f87609f6525fa0594016d11abf2de649f83ad54edd3e74e032e34b1bca685b8499916826d9aee11c13"
|
||||
PiaCANormal = "MIIFqzCCBJOgAwIBAgIJAKZ7D5Yv87qDMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzM1MThaFw0zNDA0MTIxNzM1MThaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPXDL1L9tX6DGf36liA7UBTy5I869z0UVo3lImfOs/GSiFKPtInlesP65577nd7UNzzXlH/P/CnFPdBWlLp5ze3HRBCc/Avgr5CdMRkEsySL5GHBZsx6w2cayQ2EcRhVTwWpcdldeNO+pPr9rIgPrtXqT4SWViTQRBeGM8CDxAyTopTsobjSiYZCF9Ta1gunl0G/8Vfp+SXfYCC+ZzWvP+L1pFhPRqzQQ8k+wMZIovObK1s+nlwPaLyayzw9a8sUnvWB/5rGPdIYnQWPgoNlLN9HpSmsAcw2z8DXI9pIxbr74cb3/HSfuYGOLkRqrOk6h4RCOfuWoTrZup1uEOn+fw8CAwEAAaOCAVQwggFQMB0GA1UdDgQWBBQv63nQ/pJAt5tLy8VJcbHe22ZOsjCCAR8GA1UdIwSCARYwggESgBQv63nQ/pJAt5tLy8VJcbHe22ZOsqGB7qSB6zCB6DELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRMwEQYDVQQHEwpMb3NBbmdlbGVzMSAwHgYDVQQKExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UECxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAMTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQpExdQcml2YXRlIEludGVybmV0IEFjY2VzczEvMC0GCSqGSIb3DQEJARYgc2VjdXJlQHByaXZhdGVpbnRlcm5ldGFjY2Vzcy5jb22CCQCmew+WL/O6gzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQAna5PgrtxfwTumD4+3/SYvwoD66cB8IcK//h1mCzAduU8KgUXocLx7QgJWo9lnZ8xUryXvWab2usg4fqk7FPi00bED4f4qVQFVfGfPZIH9QQ7/48bPM9RyfzImZWUCenK37pdw4Bvgoys2rHLHbGen7f28knT2j/cbMxd78tQc20TIObGjo8+ISTRclSTRBtyCGohseKYpTS9himFERpUgNtefvYHbn70mIOzfOJFTVqfrptf9jXa9N8Mpy3ayfodz1wiqdteqFXkTYoSDctgKMiZ6GdocK9nMroQipIQtpnwd4yBDWIyC6Bvlkrq5TQUtYDQ8z9v+DMO6iwyIDRiU"
|
||||
PiaCAStrong = "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"
|
||||
PiaX509CRLNormal = "MIICWDCCAUAwDQYJKoZIhvcNAQENBQAwgegxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRlaW50ZXJuZXRhY2Nlc3MuY29tFw0xNjA3MDgxOTAwNDZaFw0zNjA3MDMxOTAwNDZaMCYwEQIBARcMMTYwNzA4MTkwMDQ2MBECAQYXDDE2MDcwODE5MDA0NjANBgkqhkiG9w0BAQ0FAAOCAQEAQZo9X97ci8EcPYu/uK2HB152OZbeZCINmYyluLDOdcSvg6B5jI+ffKN3laDvczsG6CxmY3jNyc79XVpEYUnq4rT3FfveW1+Ralf+Vf38HdpwB8EWB4hZlQ205+21CALLvZvR8HcPxC9KEnev1mU46wkTiov0EKc+EdRxkj5yMgv0V2Reze7AP+NQ9ykvDScH4eYCsmufNpIjBLhpLE2cuZZXBLcPhuRzVoU3l7A9lvzG9mjA5YijHJGHNjlWFqyrn1CfYS6koa4TGEPngBoAziWRbDGdhEgJABHrpoaFYaL61zqyMR6jC0K2ps9qyZAN74LEBedEfK7tBOzWMwr58A=="
|
||||
PiaX509CRLStrong = "MIIDWDCCAUAwDQYJKoZIhvcNAQENBQAwgegxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRlaW50ZXJuZXRhY2Nlc3MuY29tFw0xNjA3MDgxOTAwNDZaFw0zNjA3MDMxOTAwNDZaMCYwEQIBARcMMTYwNzA4MTkwMDQ2MBECAQYXDDE2MDcwODE5MDA0NjANBgkqhkiG9w0BAQ0FAAOCAgEAppFfEpGsasjB1QgJcosGpzbf2kfRhM84o2TlqY1ua+Gi5TMdKydA3LJcNTjlI9a0TYAJfeRX5IkpoglSUuHuJgXhP3nEvX10mjXDpcu/YvM8TdE5JV2+EGqZ80kFtBeOq94WcpiVKFTR4fO+VkOK9zwspFfb1cNs9rHvgJ1QMkRUF8PpLN6AkntHY0+6DnigtSaKqldqjKTDTv2OeH3nPoh80SGrt0oCOmYKfWTJGpggMGKvIdvU3vH9+EuILZKKIskt+1dwdfA5Bkz1GLmiQG7+9ZZBQUjBG9Dos4hfX/rwJ3eU8oUIm4WoTz9rb71SOEuUUjP5NPy9HNx2vx+cVvLsTF4ZDZaUztW9o9JmIURDtbeyqxuHN3prlPWB6aj73IIm2dsDQvs3XXwRIxs8NwLbJ6CyEuvEOVCskdM8rdADWx1J0lRNlOJ0Z8ieLLEmYAA834VN1SboB6wJIAPxQU3rcBhXqO9y8aa2oRMg8NxZ5gr+PnKVMqag1x0IxbIgLxtkXQvxXxQHEMSODzvcOfK/nBRBsqTj30P+R87sU8titOoxNeRnBDRNhdEy/QGAqGh62ShPpQUCJdnKRiRTjnil9hMQHevoSuFKeEMO30FQL7BZyo37GFU+q1WPCplVZgCP9hC8Rn5K2+f6KLFo5bhtowSmu+GY1yZtg+RTtsA="
|
||||
PrivadoCA = "MIIFKDCCAxCgAwIBAgIJAMtrmqZxIV/OMA0GCSqGSIb3DQEBDQUAMBIxEDAOBgNVBAMMB1ByaXZhZG8wHhcNMjAwMTA4MjEyODQ1WhcNMzUwMTA5MjEyODQ1WjASMRAwDgYDVQQDDAdQcml2YWRvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxPwOgiwNJzZTnKIXwAB0TSu/Lu2qt2U2I8obtQjwhi/7OrfmbmYykSdro70al2XPhnwAGGdCxW6LDnp0UN/IOhD11mgBPo14f5CLkBQjSJ6VN5miPbvK746LsNZl9H8rQGvDuPo4CG9BfPZMiDRGlsMxij/jztzgT1gmuxQ7WHfFRcNzBas1dHa9hV/d3TU6/t47x4SE/ljdcCtJiu7Zn6ODKQoys3mB7Luz2ngqUJWvkqsg+E4+3eJ0M8Hlbn5TPaRJBID7DAdYo6Vs6xGCYr981ThFcmoIQ10js10yANrrfGAzd03b3TnLAgko0uQMHjliMZL6L8sWOPHxyxJI0us88SFh4UgcFyRHKHPKux7w24SxAlZUYoUcTHp9VjG5XvDKYxzgV2RdM4ulBGbQRQ3y3/CyddsyQYMvA55Ets0LfPaBvDIcct70iXijGsdvlX1du3ArGpG7Vaje/RU4nbbGT6HYRdt5YyZfof288ukMOSj20nVcmS+c/4tqsxSerRb1aq5LOi1IemSkTMeC5gCbexk+L1vl7NT/58sxjGmu5bXwnvev/lIItfi2AlITrfUSEv19iDMKkeshwn/+sFJBMWYyluP+yJ56yR+MWoXvLlSWphLDTqq19yx3BZn0P1tgbXoR0g8PTdJFcz8z3RIb7myVLYulV1oGG/3rka0CAwEAAaOBgDB+MB0GA1UdDgQWBBTFtJkZCVDuDAD6k5bJzefjJdO3DTBCBgNVHSMEOzA5gBTFtJkZCVDuDAD6k5bJzefjJdO3DaEWpBQwEjEQMA4GA1UEAwwHUHJpdmFkb4IJAMtrmqZxIV/OMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBDQUAA4ICAQB7MUSXMeBb9wlSv4sUaT1JHEwE26nlBw+TKmezfuPU5pBlY0LYr6qQZY95DHqsRJ7ByUzGUrGo17dNGXlcuNc6TAaQQEDRPo6y+LVh2TWMk15TUMI+MkqryJtCret7xGvDigKYMJgBy58HN3RAVr1B7cL9youwzLgc2Y/NcFKvnQJKeiIYAJ7g0CcnJiQvgZTS7xdwkEBXfsngmUCIG320DLPEL+Ze0HiUrxwWljMRya6i40AeH3Zu2i532xX1wV5+cjA4RJWIKg6ri/Q54iFGtZrA9/nc6y9uoQHkmz8cGyVUmJxFzMrrIICVqUtVRxLhkTMe4UzwRWTBeGgtW4tS0yq1QonAKfOyjgRw/CeY55D2UGvnAFZdTadtYXS4Alu2P9zdwoEk3fzHiVmDjqfJVr5wz9383aABUFrPI3nz6ed/Z6LZflKh1k+DUDEp8NxU4klUULWsSOKoa5zGX51G8cdHxwQLImXvtGuN5eSR8jCTgxFZhdps/xes4KkyfIz9FMYG748M+uOTgKITf4zdJ9BAyiQaOufVQZ8WjhWzWk9YHec9VqPkzpWNGkVjiRI5ewuXwZzZ164tMv2hikBXSuUCnFz37/ZNwGlDi0oBdDszCk2GxccdFHHaCSmpjU5MrdJ+5IhtTKGeTx+US2hTIVHQFIO99DmacxSYvLNcSQ=="
|
||||
PrivatevpnCA = "MIIErTCCA5WgAwIBAgIJAPp3HmtYGCIOMA0GCSqGSIb3DQEBCwUAMIGVMQswCQYDVQQGEwJTRTELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVN0b2NraG9sbTETMBEGA1UEChMKUHJpdmF0ZVZQTjEWMBQGA1UEAxMNUHJpdmF0ZVZQTiBDQTETMBEGA1UEKRMKUHJpdmF0ZVZQTjEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBwcml2YXR2cG4uc2UwHhcNMTcwNTI0MjAxNTM3WhcNMjcwNTIyMjAxNTM3WjCBlTELMAkGA1UEBhMCU0UxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdG9ja2hvbG0xEzARBgNVBAoTClByaXZhdGVWUE4xFjAUBgNVBAMTDVByaXZhdGVWUE4gQ0ExEzARBgNVBCkTClByaXZhdGVWUE4xIzAhBgkqhkiG9w0BCQEWFHN1cHBvcnRAcHJpdmF0dnBuLnNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwjqTWbKk85WN8nd1TaBgBnBHceQWosp8mMHr4xWMTLagWRcq2Modfy7RPnBo9kyn5j/ZZwL/21gLWJbxidurGyZZdEV9Wb5KQl3DUNxa19kwAbkkEchdES61e99MjmQlWq4vGPXAHjEuDxOZ906AXglCyAvQoXcYW0mNm9yybWllVp1aBrCaZQrNYr7eoFvolqJXdQQ3FFsTBCYa5bHJcKQLBfsiqdJ/BAxhNkQtcmWNSgLy16qoxQpCsxNCxAcYnasuL4rwOP+RazBkJTPXA/2neCJC5rt+sXR9CSfiXdJGwMpYso5m31ZEd7JL2+is0FeAZ6ETrKMnEZMsTpTkdwIDAQABo4H9MIH6MB0GA1UdDgQWBBRCkBlC94zCY6VNncMnK36JxT7bazCBygYDVR0jBIHCMIG/gBRCkBlC94zCY6VNncMnK36JxT7ba6GBm6SBmDCBlTELMAkGA1UEBhMCU0UxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdG9ja2hvbG0xEzARBgNVBAoTClByaXZhdGVWUE4xFjAUBgNVBAMTDVByaXZhdGVWUE4gQ0ExEzARBgNVBCkTClByaXZhdGVWUE4xIzAhBgkqhkiG9w0BCQEWFHN1cHBvcnRAcHJpdmF0dnBuLnNlggkA+ncea1gYIg4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAayugvExKDHar7t1zyYn99Vt1NMf46J8x4Dt9TNjBml5mR9nKvWmreMUuuOhLaO8Da466KGdXeDFNLcBYZd/J2iTawE6/3fmrML9H2sa+k/+E4uU5nQ84ZGOwCinCkMalVjM8EZ0/H2RZvLAVUnvPuUz2JfJhmiRkbeE75fVuqpAm9qdE+/7lg3oICYzxa6BJPxT+Imdjy3Q/FWdsXqX6aallhohPAZlMZgZL4eXECnV8rAfzyjOJggkMDZQt3Flc0Y4iDMfzrEhSOWMkNFBFwjK0F/dnhsX+fPX6GGRpUZgZcCt/hWvypqc05/SnrdKM/vV/jV/yZe0NVzY7S8Ur5g=="
|
||||
PrivatevpnTLSAuth = "f035a3acaeffb5aedb5bc920bca26ca7ac701da88249008e03563eba6af6d2625ac8ba1e5e0921f76be004c24ae4fd43e42caf0f84269ad44d8d4c14ba45b1386f251c7330d8cc56afd16d516835645651ef7e87a723ac78ae0d49da5b2f2d78ceafcff7a6367d0712628a6547e5fc8fef93c87f7bcd6107c7b1ae68396e944aadae50111d01a5d0c67223d667bdbf1bf434bdef03644ecc5386e102724eef3872f66547eb66dc0fea8286069cb082a41c89083b28fe9f4cec25d48017f26c4fd85b25ddf2ae5448dd2bccf3eef2aacf42ef1e88c3248c689423d0b05a641e9e79dd6b9b5c40f0cc21ffdc891b9eee951477b537261cb56a958a4f490d961ecb"
|
||||
ProtonvpnCA = "MIIFozCCA4ugAwIBAgIBATANBgkqhkiG9w0BAQ0FADBAMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMUHJvdG9uVlBOIEFHMRowGAYDVQQDExFQcm90b25WUE4gUm9vdCBDQTAeFw0xNzAyMTUxNDM4MDBaFw0yNzAyMTUxNDM4MDBaMEAxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxQcm90b25WUE4gQUcxGjAYBgNVBAMTEVByb3RvblZQTiBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAt+BsSsZg7+AuqTq7vDbPzfygtl9f8fLJqO4amsyOXlI7pquL5IsEZhpWyJIIvYybqS4s1/T7BbvHPLVEwlrq8A5DBIXcfuXrBbKoYkmpICGc2u1KYVGOZ9A+PH9z4Tr6OXFfXRnsbZToie8t2Xjv/dZDdUDAqeW89I/mXg3k5x08m2nfGCQDm4gCanN1r5MT7ge56z0MkY3FFGCOqRwspIEUzu1ZqGSTkG1eQiOYIrdOF5cc7n2APyvBIcfvp/W3cpTOEmEBJ7/14RnXnHo0fcx61Inx/6ZxzKkW8BMdGGQF3tF6u2M0FjVN0lLH9S0ul1TgoOS56yEJ34hrJSRTqHuar3t/xdCbKFZjyXFZFNsXVvgJu34CNLrHHTGJj9jiUfFnxWQYMo9UNUd4a3PPG1HnbG7LAjlvj5JlJ5aqO5gshdnqb9uIQeR2CdzcCJgklwRGCyDT1pm7eoivWV19YBd81vKulLzgPavu3kRRe83yl29It2hwQ9FMs5w6ZV/X6ciTKo3etkX9nBD9ZzJPsGQsBUy7CzO1jK4W01+u3ItmQS+1s4xtcFxdFY8o/q1zoqBlxpe5MQIWN6QalryiET74gMHE/S5WrPlsq/gehxsdgc6GDUXG4dk8vn6OUMa6wb5wRO3VXGEc67IYm4mDFTYiPvLaFOxtndlUWuCruKcCAwEAAaOBpzCBpDAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBSDkIaYhLVZTwyLNTetNB2qV0gkVDBoBgNVHSMEYTBfgBSDkIaYhLVZTwyLNTetNB2qV0gkVKFEpEIwQDELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFByb3RvblZQTiBBRzEaMBgGA1UEAxMRUHJvdG9uVlBOIFJvb3QgQ0GCAQEwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBDQUAA4ICAQCYr7LpvnfZXBCxVIVc2ea1fjxQ6vkTj0zMhtFs3qfeXpMRf+g1NAh4vv1UIwLsczilMt87SjpJ25pZPyS3O+/VlI9ceZMvtGXdMGfXhTDp//zRoL1cbzSHee9tQlmEm1tKFxB0wfWd/inGRjZxpJCTQh8oc7CTziHZufS+Jkfpc4Rasr31fl7mHhJahF1j/ka/OOWmFbiHBNjzmNWPQInJm+0ygFqij5qs51OEvubR8yh5Mdq4TNuWhFuTxpqoJ87VKaSOx/Aefca44Etwcj4gHb7LThidw/kyzysZiWjyrbfX/31RX7QanKiMk2RDtgZaWi/lMfsl5O+6E2lJ1vo4xv9pW8225B5XeAeXHCfjV/vrrCFqeCprNF6a3Tn/LX6VNy3jbeC+167QagBOaoDA01XPOx7OdhsbGd7cJ5VkgyycZgLnT9zrChgwjx59JQosFEG1DsaAgHfpEl/N3YPJh68N7fwN41Cjzsk39v6iZdfuet/sP7oiP5/gLmA/CIPNhdIYxaojbLjFPkftVjVPn49RqwqzJJPRN8BOyb94yhQ7KO4F3IcLT/y/dsWitY0ZH4lCnAVV/v2YjWAWS3OWyC8BFx/Jmc3WDK/yPwECUcPgHIeXiRjHnJt0Zcm23O2Q3RphpU+1SO3XixsXpOVOYP6rJIXW9bMZA1gTTlpi7A=="
|
||||
ProtonvpnTLSAuth = "6acef03f62675b4b1bbd03e53b187727423cea742242106cb2916a8a4c8297563d22c7e5cef430b1103c6f66eb1fc5b375a672f158e2e2e936c3faa48b035a6de17beaac23b5f03b10b868d53d03521d8ba115059da777a60cbfd7b2c9c5747278a15b8f6e68a3ef7fd583ec9f398c8bd4735dab40cbd1e3c62a822e97489186c30a0b48c7c38ea32ceb056d3fa5a710e10ccc7a0ddb363b08c3d2777a3395e10c0b6080f56309192ab5aacd4b45f55da61fc77af39bd81a19218a79762c33862df55785075f37d8c71dc8a42097ee43344739a0dd48d03025b0450cf1fb5e8caeb893d9a96d1f15519bb3c4dcb40ee316672ea16c012664f8a9f11255518deb"
|
||||
PurevpnCA = "MIIE6DCCA9CgAwIBAgIJAMjXFoeo5uSlMA0GCSqGSIb3DQEBCwUAMIGoMQswCQYDVQQGEwJISzEQMA4GA1UECBMHQ2VudHJhbDELMAkGA1UEBxMCSEsxGDAWBgNVBAoTD1NlY3VyZS1TZXJ2ZXJDQTELMAkGA1UECxMCSVQxGDAWBgNVBAMTD1NlY3VyZS1TZXJ2ZXJDQTEYMBYGA1UEKRMPU2VjdXJlLVNlcnZlckNBMR8wHQYJKoZIhvcNAQkBFhBtYWlsQGhvc3QuZG9tYWluMB4XDTE2MDExNTE1MzQwOVoXDTI2MDExMjE1MzQwOVowgagxCzAJBgNVBAYTAkhLMRAwDgYDVQQIEwdDZW50cmFsMQswCQYDVQQHEwJISzEYMBYGA1UEChMPU2VjdXJlLVNlcnZlckNBMQswCQYDVQQLEwJJVDEYMBYGA1UEAxMPU2VjdXJlLVNlcnZlckNBMRgwFgYDVQQpEw9TZWN1cmUtU2VydmVyQ0ExHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDluufhyLlyvXzPUL16kAWAdivl1roQv3QHbuRshyKacf/1Er1JqEbtW3Mx9Fvr/u27qU2W8lQI6DaJhU2BfijPe/KHkib55mvHzIVvoexxya26nk79F2c+d9PnuuMdThWQO3El5a/i2AASnM7T7piIBT2WRZW2i8RbfJaTT7G7LP7OpMKIV1qyBg/cWoO7cIWQW4jmzqrNryIkF0AzStLN1DxvnQZwgXBGv0CwuAkfQuNSLu0PQgPp0PhdukNZFllv5D29IhPr0Z+kwPtrAgPQo+lHlOBHBMUpDT4XChTPeAvMaUSBsqmonAE8UUHEabWrqYN/kWNHCNkYXMkiVmK1AgMBAAGjggERMIIBDTAdBgNVHQ4EFgQU456ijsFrYnzHBShLAPpOUqQ+Z2cwgd0GA1UdIwSB1TCB0oAU456ijsFrYnzHBShLAPpOUqQ+Z2ehga6kgaswgagxCzAJBgNVBAYTAkhLMRAwDgYDVQQIEwdDZW50cmFsMQswCQYDVQQHEwJISzEYMBYGA1UEChMPU2VjdXJlLVNlcnZlckNBMQswCQYDVQQLEwJJVDEYMBYGA1UEAxMPU2VjdXJlLVNlcnZlckNBMRgwFgYDVQQpEw9TZWN1cmUtU2VydmVyQ0ExHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQDI1xaHqObkpTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCvga2HMwOtUxWH/inL2qk24KX2pxLg939JNhqoyNrUpbDHag5xPQYXUmUpKrNJZ0z+o/ZnNUPHydTSXE7Z7E45J0GDN5E7g4pakndKnDLSjp03NgGsCGW+cXnz6UBPM5FStFvGdDeModeSUyoS9fjk+mYROvmiy5EiVDP91sKGcPLR7Ym0M7zl2aaqV7bb98HmMoBOxpeZQinof67nKrCsgz/xjktWFgcmPl4/PQSsmqQD0fTtWxGuRX+FzwvF2OCMCAJgp1RqJNlk2g50/kBIoJVPPCfjDFeDU5zGaWGSQ9+z1L6/z7VXdjUiHL0ouOcHwbiS4ZjTr9nMn6WdAHU2"
|
||||
PurevpnCert = "MIIEnzCCA4egAwIBAgIBAzANBgkqhkiG9w0BAQsFADCBqDELMAkGA1UEBhMCSEsxEDAOBgNVBAgTB0NlbnRyYWwxCzAJBgNVBAcTAkhLMRgwFgYDVQQKEw9TZWN1cmUtU2VydmVyQ0ExCzAJBgNVBAsTAklUMRgwFgYDVQQDEw9TZWN1cmUtU2VydmVyQ0ExGDAWBgNVBCkTD1NlY3VyZS1TZXJ2ZXJDQTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRvbWFpbjAeFw0xNjAxMTUxNjE1MzhaFw0yNjAxMTIxNjE1MzhaMIGdMQswCQYDVQQGEwJISzEQMA4GA1UECBMHQ2VudHJhbDELMAkGA1UEBxMCSEsxFjAUBgNVBAoTDVNlY3VyZS1DbGllbnQxCzAJBgNVBAsTAklUMRYwFAYDVQQDEw1TZWN1cmUtQ2xpZW50MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRvbWFpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxsnyn4v6xxDPnuDaYS0b9M1N8nxgg7OBPBlK+FWRxdTQ8yxt5U5CZGm7riVp7fya2J2iPZIgmHQEv/KbxztsHAVlYSfYYlalrnhEL3bDP2tY+N43AwB1k5BrPq2s1pPLT2XG951drDKG4PUuFHUP1sHzW5oQlfVCmxgIMAP8OYkCAwEAAaOCAV8wggFbMAkGA1UdEwQCMAAwLQYJYIZIAYb4QgENBCAWHkVhc3ktUlNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU9MwUnUDbQKKZKjoeieD2OD5NlAEwgd0GA1UdIwSB1TCB0oAU456ijsFrYnzHBShLAPpOUqQ+Z2ehga6kgaswgagxCzAJBgNVBAYTAkhLMRAwDgYDVQQIEwdDZW50cmFsMQswCQYDVQQHEwJISzEYMBYGA1UEChMPU2VjdXJlLVNlcnZlckNBMQswCQYDVQQLEwJJVDEYMBYGA1UEAxMPU2VjdXJlLVNlcnZlckNBMRgwFgYDVQQpEw9TZWN1cmUtU2VydmVyQ0ExHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQDI1xaHqObkpTATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADggEBAFyFo2VUX/UFixsdPdK9/Yt6mkCWc+XS1xbapGXXb9U1d+h1iBCIV9odUHgNCXWpz1hR5Uu/OCzaZ0asLE4IFMZlQmJs8sMT0c1tfPPGW45vxbL0lhqnQ8PNcBH7huNK7VFjUh4szXRKmaQPaM4S91R3L4CaNfVeHfAg7mN2m9Zn5Gto1Q1/CFMGKu2hxwGEw5p+X1czBWEvg/O09ckx/ggkkI1NcZsNiYQ+6Pz8DdGGX3+05YwLZu94+O6iIMrzxl/il0eK83g3YPbsOrASARvw6w/8sOnJCK5eOacl21oww875KisnYdWjHB1FiI+VzQ1/gyoDsL5kPTJVuu2CoG8="
|
||||
PurevpnKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMbJ8p+L+scQz57g2mEtG/TNTfJ8YIOzgTwZSvhVkcXU0PMsbeVOQmRpu64lae38mtidoj2SIJh0BL/ym8c7bBwFZWEn2GJWpa54RC92wz9rWPjeNwMAdZOQaz6trNaTy09lxvedXawyhuD1LhR1D9bB81uaEJX1QpsYCDAD/DmJAgMBAAECgYEAvTHbDupE5U0krUvHzBEIuHblptGlcfNYHoDcD3oxYR3pOGeiuElBexv+mgHVzcFLBrsQfJUlHLPfCWi3xmjRvDQcr7N7U1u7NIzazy/PpRBaKolMRiM1KMYi2DG0i4ZONwFT8bvNHOIrZzCLY54KDrqOn55OzC70WYjWh4t5evkCQQDkkzZUAeskBC9+JP/zLps8jhwfoLBWGw/zbC9ePDmX0N8MTZdcUpg6KUTf1wbkLUyVtIRjS2ao6qu1jWG6K0x3AkEA3qPWyaWQWCynhNDqu2U1cPb2kh5AJip+gqxO3emikAdajsSxeoyEC2AfyBITbeB1tvCUZH17J4i/0+OFTEQp/wJAb/zEOGJ8PzghwK8GC7JA8mk51DEZVAaMSRovFv9wxDXcoh191AjPdmdzzCuAv9iF1i8MUc3GbWoUWK39PIYsPwJAWh63sqfx5b8tj/WBDpnJKBDPfhYAoXJSA1L8GZeY1fQkE+ZKcPCwAmrGcpXeh3t0Krj3WDXyw+32uC5Apr5wwQJAPZwOOReaC4YNfBPZN9BdHvVjOYGGUffpI+X+hWpLRnQFJteAi+eqwyk0Oi0SkJB+a7jcerK2d7q7xhec5WHlng=="
|
||||
PurevpnTLSAuth = "e30af995f56d07426d9ba1f824730521d4283db4b4d0cdda9c6e8759a3799dcb7939b6a5989160c9660de0f6125cbb1f585b41c074b2fe88ecfcf17eab9a33be1352379cdf74952b588fb161a93e13df9135b2b29038231e02d657a6225705e6868ccb0c384ed11614690a1894bfbeb274cebf1fe9c2329bdd5c8a40fe8820624d2ea7540cd79ab76892db51fc371a3ac5fc9573afecb3fffe3281e61d72e91579d9b03d8cbf7909b3aebf4d90850321ee6b7d0a7846d15c27d8290e031e951e19438a4654663cad975e138f5bc5af89c737ad822f27e19057731f41e1e254cc9c95b7175c622422cde9f1f2cfd3510add94498b4d7133d3729dd214a16b27fb"
|
||||
SurfsharkCA = "MIIFTTCCAzWgAwIBAgIJAMs9S3fqwv+mMA0GCSqGSIb3DQEBCwUAMD0xCzAJBgNVBAYTAlZHMRIwEAYDVQQKDAlTdXJmc2hhcmsxGjAYBgNVBAMMEVN1cmZzaGFyayBSb290IENBMB4XDTE4MDMxNDA4NTkyM1oXDTI4MDMxMTA4NTkyM1owPTELMAkGA1UEBhMCVkcxEjAQBgNVBAoMCVN1cmZzaGFyazEaMBgGA1UEAwwRU3VyZnNoYXJrIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDEGMNj0aisM63oSkmVJyZPaYX7aPsZtzsxo6m6p5Wta3MGASoryRsBuRaH6VVa0fwbI1nw5ubyxkuaNa4v3zHVwuSq6F1p8S811+1YP1av+jqDcMyojH0ujZSHIcb/i5LtaHNXBQ3qN48Cc7sqBnTIIFpmb5HthQ/4pW+a82b1guM5dZHsh7q+LKQDIGmvtMtO1+NEnmj81BApFayiaD1ggvwDI4x7o/Y3ksfWSCHnqXGyqzSFLh8QuQrTmWUm84YHGFxoI1/8AKdIyVoB6BjcaMKtKs/pbctk6vkzmYf0XmGovDKPQF6MwUekchLjB5gSBNnptSQ9kNgnTLqi0OpSwI6ixX52Ksva6UM8P01ZIhWZ6ua/T/tArgODy5JZMW+pQ1A6L0b7egIeghpwKnPRG+5CzgO0J5UE6gv000mqbmC3CbiS8xi2xuNgruAyY2hUOoV9/BuBev8ttE5ZCsJH3YlG6NtbZ9hPc61GiBSx8NJnX5QHyCnfic/X87eST/amZsZCAOJ5v4EPSaKrItt+HrEFWZQIq4fJmHJNNbYvWzCE08AL+5/6Z+lxb/Bm3dapx2zdit3x2e+miGHekuiE8lQWD0rXD4+T+nDRi3X+kyt8Ex/8qRiUfrisrSHFzVMRungIMGdO9O/zCINFrb7wahm4PqU2f12Z9TRCOTXciQIDAQABo1AwTjAdBgNVHQ4EFgQUYRpbQwyDahLMN3F2ony3+UqOYOgwHwYDVR0jBBgwFoAUYRpbQwyDahLMN3F2ony3+UqOYOgwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAn9zV7F/XVnFNZhHFrt0ZS1Yqz+qM9CojLmiyblMFh0p7t+Hh+VKVgMwrz0LwDH4UsOosXA28eJPmech6/bjfymkoXISy/NUSTFpUChGO9RabGGxJsT4dugOw9MPaIVZffny4qYOc/rXDXDSfF2b+303lLPI43y9qoe0oyZ1vtk/UKG75FkWfFUogGNbpOkuz+et5Y0aIEiyg0yh6/l5Q5h8+yom0HZnREHhqieGbkaGKLkyu7zQ4D4tRK/mBhd8nv+09GtPEG+D5LPbabFVxKjBMP4Vp24WuSUOqcGSsURHevawPVBfgmsxf1UCjelaIwngdh6WfNCRXa5QQPQTKubQvkvXONCDdhmdXQccnRX1nJWhPYi0onffvjsWUfztRypsKzX4dvM9k7xnIcGSGEnCC4RCgt1UiZIj7frcCMssbA6vJ9naM0s7JF7N3VKeHJtqe1OCRHMYnWUZt9vrqX6IoIHlZCoLlv39wFW9QNxelcAOCVbD+19MZ0ZXt7LitjIqe7yF5WxDQN4xru087FzQ4Hfj7eH1SNLLyKZkA1eecjmRoi/OoqAt7afSnwtQLtMUc2bQDg6rHt5C0e4dCLqP/9PGZTSJiwmtRHJ/N5qYWIh9ju83APvLm/AGBTR2pXmj9G3KdVOkpIC7L35dI623cSEC3Q3UZutsEm/UplsM="
|
||||
SurfsharkTLSAuth = "b02cb1d7c6fee5d4f89b8de72b51a8d0c7b282631d6fc19be1df6ebae9e2779e6d9f097058a31c97f57f0c35526a44ae09a01d1284b50b954d9246725a1ead1ff224a102ed9ab3da0152a15525643b2eee226c37041dc55539d475183b889a10e18bb94f079a4a49888da566b99783460ece01daaf93548beea6c827d9674897e7279ff1a19cb092659e8c1860fbad0db4ad0ad5732f1af4655dbd66214e552f04ed8fd0104e1d4bf99c249ac229ce169d9ba22068c6c0ab742424760911d4636aafb4b85f0c952a9ce4275bc821391aa65fcd0d2394f006e3fba0fd34c4bc4ab260f4b45dec3285875589c97d3087c9134d3a3aa2f904512e85aa2dc2202498"
|
||||
TorguardCA = "MIIDMTCCAhmgAwIBAgIJAKnGGJK6qLqSMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNVBAMMCVRHLVZQTi1DQTAgFw0xOTA1MjExNDIzMTFaGA8yMDU5MDUxMTE0MjMxMVowFDESMBAGA1UEAwwJVEctVlBOLUNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlv0UgPD3xVAvhhP6q1HCmeAWbH+9HPkyQ2P6qM5oHY5dntjmq8YT48FZGHWv7+s9O47v6Bv7rEc4UwQx15cc2LByivX2JwmE8JACvNfwEnZXYAPq9WU3ZgRrAGvA09ItuLqK2fQ4A7h8bFhmyxCbSzP1sSIT/zJY6ebuh5rDQSMJRMaoI0t1zorEZ7PlEmh+o0w5GPs0D0vY50UcnEzB4GOdWC9pJREwEqppWYLN7RRdG8JyIqmA59mhARCnQFUo38HWic4trxFe71jtD7YInNV7ShQtg0S0sXo36Rqfz72Jo08qqI70dNs5DN1aGNkQ/tRK9DhL5DLmTkaCw7mEFQIDAQABo4GDMIGAMB0GA1UdDgQWBBR7DcymXBp6u/jAaZOPUjUhEyhXfjBEBgNVHSMEPTA7gBR7DcymXBp6u/jAaZOPUjUhEyhXfqEYpBYwFDESMBAGA1UEAwwJVEctVlBOLUNBggkAqcYYkrqoupIwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAE79ngbdSlP7IBbfnJ+2Ju7vqt9/GyhcsYtjibp6gsMUxKlD8HuvlSGj5kNO5wiwN7XXqsjYtJfdhmzzVbXksi8Fnbnfa8GhFl4IAjLJ5cxaWOxjr6wx2AhIs+BVVARjaU7iTK91RXJnl6u7UDHTkQylBTl7wgpMeG6GjhaHfcOL1t7D2w8x23cTO+p+n53P3cBq+9TiAUORdzXJvbCxlPMDSDArsgBjC57W7dtdnZo7gTfQG77JTDFBeSwPwLF7PjBB4S6rzU/4fcYwy83XKP6zDn9tgUJDnpFb/7jJ/PbNkK4BWYJp3XytOtt66v9SEKw+v/fJ+VkjU16vE/9Q3h4="
|
||||
TorguardTLSAuth = "770e8de5fc56e0248cc7b5aab56be80d0e19cbf003c1b3ed68efbaf08613c3a1a019dac6a4b84f13a6198f73229ffc21fa512394e288f82aa2cf0180f01fb3eb1a71e00a077a20f6d7a83633f5b4f47f27e30617eaf8485dd8c722a8606d56b3c183f65da5d3c9001a8cbdb96c793d936251098b24fe52a6dd2472e98cfccbc466e63520d63ade7a0eacc36208c3142a1068236a52142fbb7b3ed83d785e12a28261bccfb3bcb62a8d2f6d18f5df5f3652e59c5627d8d9c8f7877c4d7b08e19a5c363556ba68d392be78b75152dd55ba0f74d45089e84f77f4492d886524ea6c82b9f4dd83d46528d4f5c3b51cfeaf2838d938bd0597c426b0e440434f2c451f"
|
||||
VPNUnlimitedCA = "MIIEjjCCA/egAwIBAgIJAKsVbHBdakXuMA0GCSqGSIb3DQEBBQUAMIHgMQswCQYDVQQGEwJVUzELMAkGA1UECBMCTlkxETAPBgNVBAcTCE5ldyBZb3JrMR8wHQYDVQQKExZTaW1wbGV4IFNvbHV0aW9ucyBJbmMuMRYwFAYDVQQLEw1WcG4gVW5saW1pdGVkMSMwIQYDVQQDExpzZXJ2ZXIudnBudW5saW1pdGVkYXBwLmNvbTEjMCEGA1UEKRMac2VydmVyLnZwbnVubGltaXRlZGFwcC5jb20xLjAsBgkqhkiG9w0BCQEWH3N1cHBvcnRAc2ltcGxleHNvbHV0aW9uc2luYy5jb20wHhcNMTMxMjE2MTM1OTQ0WhcNMjMxMjE0MTM1OTQ0WjCB4DELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk5ZMREwDwYDVQQHEwhOZXcgWW9yazEfMB0GA1UEChMWU2ltcGxleCBTb2x1dGlvbnMgSW5jLjEWMBQGA1UECxMNVnBuIFVubGltaXRlZDEjMCEGA1UEAxMac2VydmVyLnZwbnVubGltaXRlZGFwcC5jb20xIzAhBgNVBCkTGnNlcnZlci52cG51bmxpbWl0ZWRhcHAuY29tMS4wLAYJKoZIhvcNAQkBFh9zdXBwb3J0QHNpbXBsZXhzb2x1dGlvbnNpbmMuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDADUzS8QWGvdhLFKsEzAiq5+b0ukKjBza0k6/dmCeYTvCVqGKg/h1IAtQdVVLAkmEp0zvGH7PuOhXm7zZrCouBr/RiG4tEcoRHwc6AJmowkYERlY7+xGx3OuNrD00QceNTsan0bn78jwt0zhFNmHdoTtFjgK3oqmQYSAtbEVWYgwIDAQABo4IBTDCCAUgwHQYDVR0OBBYEFKClmYP+tMNgWagUJCCHjtaui2k+MIIBFwYDVR0jBIIBDjCCAQqAFKClmYP+tMNgWagUJCCHjtaui2k+oYHmpIHjMIHgMQswCQYDVQQGEwJVUzELMAkGA1UECBMCTlkxETAPBgNVBAcTCE5ldyBZb3JrMR8wHQYDVQQKExZTaW1wbGV4IFNvbHV0aW9ucyBJbmMuMRYwFAYDVQQLEw1WcG4gVW5saW1pdGVkMSMwIQYDVQQDExpzZXJ2ZXIudnBudW5saW1pdGVkYXBwLmNvbTEjMCEGA1UEKRMac2VydmVyLnZwbnVubGltaXRlZGFwcC5jb20xLjAsBgkqhkiG9w0BCQEWH3N1cHBvcnRAc2ltcGxleHNvbHV0aW9uc2luYy5jb22CCQCrFWxwXWpF7jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBALkWhfw7SSV7it+ZYZmT+cQbExjlYgQ40zae2J2CqIYACRcfsDHvh7Q+fiwSocevv2NE0dWi6WB2H6SiudYjvDvubAX/QbzfBxtbxCCoAIlfPCm8xOnWFN7TUJAzWwHJkKgEnu29GZHu2x8J+7VeDbKH5RTMHHe8FkSxh6Zz/BMN"
|
||||
VyprvpnCA = "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"
|
||||
WevpnCA = "MIIDQjCCAiqgAwIBAgIUPppqnRZfvGGrT4GjXFE4Q29QzgowDQYJKoZIhvcNAQELBQAwEzERMA8GA1UEAwwIQ2hhbmdlTWUwHhcNMTkxMTA1MjMzMzIzWhcNMjkxMTAyMjMzMzIzWjATMREwDwYDVQQDDAhDaGFuZ2VNZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL5DFBJlTqhXukJFWlI8TNW9+HEQCZXhyVFvQhJFF2xIGVNx51XzqxiRANjVJZJrA68kV8az0v2Dxj0SFnRWDR6pOjjdp2CyHFcgHyfv+4MrsreAtkue86bB/1ECPWaoIwtaLnwI6SEmFZl98RlI9v4M/8IE4chOnMrM/F22+2OXI//TduvTcbyOMUiiouIP8UG1FB3J5FyuaW6qPZz2G0efDoaOI+E9LSxE87OoFrII7UqdHlWxRb3nUuPU1Ee4rN/d4tFyP4AvPKfsGhVOwyGG21IdRnbXIuDi0xytkCGOZ4j2bq5zqudnp4Izt6yJgdzZpQQWK3kSHB3qTT/Yzl8CAwEAAaOBjTCBijAdBgNVHQ4EFgQUXYkoo4WbkkvbgLVdGob9RScRf3AwTgYDVR0jBEcwRYAUXYkoo4WbkkvbgLVdGob9RScRf3ChF6QVMBMxETAPBgNVBAMMCENoYW5nZU1lghQ+mmqdFl+8YatPgaNcUThDb1DOCjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAOr1XmyWBRYfTQPNvZZ+DjCfiRYzLOi2AGefZt/jETqPDF8deVbyL1fLhXZzuX+5Etlsil3PflJjpzc/FSeZRuYRaShtwF3j6I08Eww9rBkaCnsukMUcLtMOvhdAU8dUakcRA2wkQ7Z+TWdMBv5+/6MnX10An1fIz7bAy3btMEOPTEFLo8Bst1SxJtUMaqhUteSOJ1VorpK3CWfOFaXxbJAb4E0+3zt6Vsc8mY5tt6wAi8IqiN4WD79ZdvKxENK4FMkR1kNpBY97mvdf82rzpwiBuJgN5ywmH78Ghj+9T8nI6/UIqJ1y22IRYGv6dMif8fHo5WWhCv3qmCqqY8vwuxw=="
|
||||
WevpnCert = "MIIDTDCCAjSgAwIBAgIRAKxt8SMIXezjmHm2KDCAQdIwDQYJKoZIhvcNAQELBQAwEzERMA8GA1UEAwwIQ2hhbmdlTWUwHhcNMTkxMTA1MjMzMzI0WhcNMjkxMTAyMjMzMzI0WjAOMQwwCgYDVQQDDAN0Y3AwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvEwY2erLhMm3Mpsnybm3G6zvGyeblUAaehQVEUs+KM2/5np0Ovx0y8Iz9pIC9ITaWM0B3dM6uBsNEtylZIe4Dd9aFujunSeCFsLRf8i9AbrUombpQ6P4jzYFBxwcEw//UShwa4HZI6JuSYikdpx/dyXdBH2skahwDVc8VUFdBLLSglfKGbuzP9GsdSwQCeBRWgA3dvIzIkQkBwfnt9WQKUfRAe8e5NybaAn8Yuu9sjLkQe6eyV7toxkZTcEXdABG2vtdTEzlAsQilZzIxg3jcdeEgMgRKngng+YNP0rR5nofZ1iDlp+vBj0nuqTTJLHMrRWPIc7bdYFD/f2J49WORAgMBAAGjgZ8wgZwwCQYDVR0TBAIwADAdBgNVHQ4EFgQUmSAFmCo1FAKVq8RQF7jMxMxcMtUwTgYDVR0jBEcwRYAUXYkoo4WbkkvbgLVdGob9RScRf3ChF6QVMBMxETAPBgNVBAMMCENoYW5nZU1lghQ+mmqdFl+8YatPgaNcUThDb1DOCjATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADggEBADPqdEgL+0kou8P974QEaNg1XOAXpwP0NNqbkZ/Oj9+Lp96YAhAHOAJig+RWbBktK8zu8oUUGR1qLXAWCmirlXErVuBRnadTEh3A7SOuY02BcsYAtpQ2EU9j5K/LV7nTfagkVdWy7x/av361UD4t9fv1j4YYTh4XLRp7KVXs6AGZ7T1hqPYFMUIoPpFhPzFxH4euJjfazr4SkTR6k6Vhw3pyFd6HP65vcqpzHGxFytSa8HtltBk2DpzIf8yV9TEy+gOXFaaGss0YKQ5OU1ieqZRuLVEGiu17lByYiQGyemIETJbdkyiSg93dDJRxjaTk7c8CEdpipt07ndSIPldMtXA="
|
||||
WevpnTLSCrypt = "7be66c0df0b8855e076d9e37b19f9ff3c1735ed537dee6dc786e51bdb8502f878077eeba0420a25e2b04814d22bbdcc0191a4fc396fdba1af6eb090a9d8664f18e70012ee98a2e32c28620a771d13cf3a619c417480c2c312562fffaebfd7ba73f57a28edde6c287365e6ce28291a29728da211cb53e01aa46b92f5f276c61fb46bd810b41219022c8f3d9e699fe9ade6bfcbb937fbbf6f49d741740e71c7c008a9a13c2432608038c6310b4f33588d8d234b3dffcf0823395267d73140d0e9a40e323ca92866c37073bfb072ab9de518bb9f2c65df7e219c2f114afbcf7c6e3c401cb08c3ed2901725b0601d2b5de89245719dd32506d52f149d14156215c1e"
|
||||
WindscribeCA = "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"
|
||||
WindscribeTLSAuth = "5801926a57ac2ce27e3dfd1dd6ef82042d82bd4f3f0021296f57734f6f1ea714a6623845541c4b0c3dea0a050fe6746cb66dfab14cda27e5ae09d7c155aa554f399fa4a863f0e8c1af787e5c602a801d3a2ec41e395a978d56729457fe6102d7d9e9119aa83643210b33c678f9d4109e3154ac9c759e490cb309b319cf708cae83ddadc3060a7a26564d1a24411cd552fe6620ea16b755697a4fc5e6e9d0cfc0c5c4a1874685429046a424c026db672e4c2c492898052ba59128d46200b40f880027a8b6610a4d559bdc9346d33a0a6b08e75c7fd43192b162bfd0aef0c716b31584827693f676f9a5047123466f0654eade34972586b31c6ce7e395f4b478cb"
|
||||
)
|
||||
7
internal/constants/openvpn/auth.go
Normal file
7
internal/constants/openvpn/auth.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package openvpn
|
||||
|
||||
const (
|
||||
SHA1 = "sha1"
|
||||
SHA256 = "sha256"
|
||||
SHA512 = "sha512"
|
||||
)
|
||||
8
internal/constants/openvpn/ciphers.go
Normal file
8
internal/constants/openvpn/ciphers.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package openvpn
|
||||
|
||||
const (
|
||||
AES128cbc = "aes-128-cbc"
|
||||
AES256cbc = "aes-256-cbc"
|
||||
AES128gcm = "aes-128-gcm"
|
||||
AES256gcm = "aes-256-gcm"
|
||||
)
|
||||
6
internal/constants/openvpn/paths.go
Normal file
6
internal/constants/openvpn/paths.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package openvpn
|
||||
|
||||
const (
|
||||
// AuthConf is the file path to the OpenVPN auth file.
|
||||
AuthConf = "/etc/openvpn/auth.conf"
|
||||
)
|
||||
6
internal/constants/openvpn/versions.go
Normal file
6
internal/constants/openvpn/versions.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package openvpn
|
||||
|
||||
const (
|
||||
Openvpn24 = "2.4"
|
||||
Openvpn25 = "2.5"
|
||||
)
|
||||
@@ -1,8 +1,6 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
// OpenVPNAuthConf is the file path to the OpenVPN auth file.
|
||||
OpenVPNAuthConf = "/etc/openvpn/auth.conf"
|
||||
// ServersData is the server information filepath.
|
||||
ServersData = "/gluetun/servers.json"
|
||||
)
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
PIAEncryptionPresetNone = "none"
|
||||
PIAEncryptionPresetNormal = "normal"
|
||||
PIAEncryptionPresetStrong = "strong"
|
||||
)
|
||||
@@ -5,6 +5,7 @@ const (
|
||||
// VPN configurations.
|
||||
Custom = "custom"
|
||||
Cyberghost = "cyberghost"
|
||||
Example = "example"
|
||||
Expressvpn = "expressvpn"
|
||||
Fastestvpn = "fastestvpn"
|
||||
HideMyAss = "hidemyass"
|
||||
@@ -26,9 +27,9 @@ const (
|
||||
Windscribe = "windscribe"
|
||||
)
|
||||
|
||||
// All returns all the providers except the custom provider.
|
||||
func All() []string {
|
||||
return []string{
|
||||
Custom,
|
||||
Cyberghost,
|
||||
Expressvpn,
|
||||
Fastestvpn,
|
||||
@@ -51,3 +52,11 @@ func All() []string {
|
||||
Windscribe,
|
||||
}
|
||||
}
|
||||
|
||||
func AllWithCustom() []string {
|
||||
allProviders := All()
|
||||
allProvidersWithCustom := make([]string, len(allProviders)+1)
|
||||
copy(allProvidersWithCustom, allProviders)
|
||||
allProvidersWithCustom[len(allProvidersWithCustom)-1] = Custom
|
||||
return allProvidersWithCustom
|
||||
}
|
||||
|
||||
23
internal/constants/providers/providers_test.go
Normal file
23
internal/constants/providers/providers_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_All(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
all := All()
|
||||
assert.NotContains(t, all, Custom)
|
||||
assert.NotEmpty(t, all)
|
||||
}
|
||||
|
||||
func Test_AllWithCustom(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
all := AllWithCustom()
|
||||
assert.Contains(t, all, Custom)
|
||||
assert.Len(t, all, len(All())+1)
|
||||
}
|
||||
15
internal/dns/interfaces.go
Normal file
15
internal/dns/interfaces.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdm12/dns/pkg/unbound"
|
||||
)
|
||||
|
||||
type Configurator interface {
|
||||
SetupFiles(ctx context.Context) error
|
||||
MakeUnboundConf(settings unbound.Settings) (err error)
|
||||
Start(ctx context.Context, verbosityDetailsLevel uint8) (
|
||||
stdoutLines, stderrLines chan string, waitError chan error, err error)
|
||||
Version(ctx context.Context) (version string, err error)
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
// Package dns defines interfaces to interact with DNS and DNS over TLS.
|
||||
package dns
|
||||
|
||||
import (
|
||||
@@ -7,7 +6,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/dns/pkg/blacklist"
|
||||
"github.com/qdm12/dns/pkg/unbound"
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/dns/state"
|
||||
@@ -15,20 +13,10 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
var _ Looper = (*Loop)(nil)
|
||||
|
||||
type Looper interface {
|
||||
Runner
|
||||
RestartTickerRunner
|
||||
loopstate.Applier
|
||||
loopstate.Getter
|
||||
SettingsGetSetter
|
||||
}
|
||||
|
||||
type Loop struct {
|
||||
statusManager loopstate.Manager
|
||||
state state.Manager
|
||||
conf unbound.Configurator
|
||||
statusManager *loopstate.State
|
||||
state *state.State
|
||||
conf Configurator
|
||||
resolvConf string
|
||||
blockBuilder blacklist.Builder
|
||||
client *http.Client
|
||||
@@ -46,7 +34,7 @@ type Loop struct {
|
||||
|
||||
const defaultBackoffTime = 10 * time.Second
|
||||
|
||||
func NewLoop(conf unbound.Configurator, settings settings.DNS,
|
||||
func NewLoop(conf Configurator, settings settings.DNS,
|
||||
client *http.Client, logger Logger) *Loop {
|
||||
start := make(chan struct{})
|
||||
running := make(chan models.LoopStatus)
|
||||
|
||||
@@ -7,10 +7,6 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
type Runner interface {
|
||||
Run(ctx context.Context, done chan<- struct{})
|
||||
}
|
||||
|
||||
func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
|
||||
defer close(done)
|
||||
|
||||
@@ -86,7 +82,6 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
|
||||
l.logger.Info("starting")
|
||||
stayHere = false
|
||||
case err := <-waitError: // unexpected error
|
||||
close(waitError)
|
||||
closeStreams()
|
||||
|
||||
unboundCancel()
|
||||
|
||||
@@ -6,22 +6,8 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
type SettingsGetSetter interface {
|
||||
SettingsGetter
|
||||
SettingsSetter
|
||||
}
|
||||
|
||||
type SettingsGetter interface {
|
||||
GetSettings() (settings settings.DNS)
|
||||
}
|
||||
|
||||
func (l *Loop) GetSettings() (settings settings.DNS) { return l.state.GetSettings() }
|
||||
|
||||
type SettingsSetter interface {
|
||||
SetSettings(ctx context.Context, settings settings.DNS) (
|
||||
outcome string)
|
||||
}
|
||||
|
||||
func (l *Loop) SetSettings(ctx context.Context, settings settings.DNS) (
|
||||
outcome string) {
|
||||
return l.state.SetSettings(ctx, settings)
|
||||
|
||||
@@ -8,12 +8,6 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
type SettingsGetSetter interface {
|
||||
GetSettings() (settings settings.DNS)
|
||||
SetSettings(ctx context.Context,
|
||||
settings settings.DNS) (outcome string)
|
||||
}
|
||||
|
||||
func (s *State) GetSettings() (settings settings.DNS) {
|
||||
s.settingsMu.RLock()
|
||||
defer s.settingsMu.RUnlock()
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/loopstate"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
var _ Manager = (*State)(nil)
|
||||
|
||||
type Manager interface {
|
||||
SettingsGetSetter
|
||||
}
|
||||
|
||||
func New(statusApplier loopstate.Applier,
|
||||
func New(statusApplier StatusApplier,
|
||||
settings settings.DNS,
|
||||
updateTicker chan<- struct{}) *State {
|
||||
return &State{
|
||||
@@ -24,10 +19,15 @@ func New(statusApplier loopstate.Applier,
|
||||
}
|
||||
|
||||
type State struct {
|
||||
statusApplier loopstate.Applier
|
||||
statusApplier StatusApplier
|
||||
|
||||
settings settings.DNS
|
||||
settingsMu sync.RWMutex
|
||||
|
||||
updateTicker chan<- struct{}
|
||||
}
|
||||
|
||||
type StatusApplier interface {
|
||||
ApplyStatus(ctx context.Context, status models.LoopStatus) (
|
||||
outcome string, err error)
|
||||
}
|
||||
|
||||
@@ -7,10 +7,6 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
type RestartTickerRunner interface {
|
||||
RunRestartTicker(ctx context.Context, done chan<- struct{})
|
||||
}
|
||||
|
||||
func (l *Loop) RunRestartTicker(ctx context.Context, done chan<- struct{}) {
|
||||
defer close(done)
|
||||
// Timer that acts as a ticker
|
||||
|
||||
@@ -5,10 +5,6 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Enabler interface {
|
||||
SetEnabled(ctx context.Context, enabled bool) (err error)
|
||||
}
|
||||
|
||||
func (c *Config) SetEnabled(ctx context.Context, enabled bool) (err error) {
|
||||
c.stateMutex.Lock()
|
||||
defer c.stateMutex.Unlock()
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// Package firewall defines a configurator used to change the state
|
||||
// of the firewall as well as do some light routing changes.
|
||||
package firewall
|
||||
|
||||
import (
|
||||
@@ -12,16 +10,6 @@ import (
|
||||
"github.com/qdm12/golibs/command"
|
||||
)
|
||||
|
||||
var _ Configurator = (*Config)(nil)
|
||||
|
||||
// Configurator allows to change firewall rules and modify network routes.
|
||||
type Configurator interface {
|
||||
Enabler
|
||||
VPNConnectionSetter
|
||||
PortAllower
|
||||
OutboundSubnetsSetter
|
||||
}
|
||||
|
||||
type Config struct { //nolint:maligned
|
||||
runner command.Runner
|
||||
logger Logger
|
||||
|
||||
@@ -8,10 +8,6 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/subnet"
|
||||
)
|
||||
|
||||
type OutboundSubnetsSetter interface {
|
||||
SetOutboundSubnets(ctx context.Context, subnets []net.IPNet) (err error)
|
||||
}
|
||||
|
||||
func (c *Config) SetOutboundSubnets(ctx context.Context, subnets []net.IPNet) (err error) {
|
||||
c.stateMutex.Lock()
|
||||
defer c.stateMutex.Unlock()
|
||||
|
||||
@@ -6,11 +6,6 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type PortAllower interface {
|
||||
SetAllowedPort(ctx context.Context, port uint16, intf string) (err error)
|
||||
RemoveAllowedPort(ctx context.Context, port uint16) (err error)
|
||||
}
|
||||
|
||||
func (c *Config) SetAllowedPort(ctx context.Context, port uint16, intf string) (err error) {
|
||||
c.stateMutex.Lock()
|
||||
defer c.stateMutex.Unlock()
|
||||
|
||||
@@ -7,11 +7,6 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
type VPNConnectionSetter interface {
|
||||
SetVPNConnection(ctx context.Context,
|
||||
connection models.Connection, vpnIntf string) error
|
||||
}
|
||||
|
||||
func (c *Config) SetVPNConnection(ctx context.Context,
|
||||
connection models.Connection, vpnIntf string) (err error) {
|
||||
c.stateMutex.Lock()
|
||||
|
||||
@@ -12,12 +12,6 @@ var (
|
||||
ErrHTTPStatusNotOK = errors.New("HTTP response status is not OK")
|
||||
)
|
||||
|
||||
var _ Checker = (*Client)(nil)
|
||||
|
||||
type Checker interface {
|
||||
Check(ctx context.Context, url string) error
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Package healthcheck defines the client and server side of the built in healthcheck.
|
||||
package healthcheck
|
||||
|
||||
import (
|
||||
|
||||
@@ -5,11 +5,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/vpn"
|
||||
)
|
||||
|
||||
type vpnHealth struct {
|
||||
looper vpn.Looper
|
||||
loop StatusApplier
|
||||
healthyWait time.Duration
|
||||
healthyTimer *time.Timer
|
||||
}
|
||||
@@ -17,8 +16,8 @@ type vpnHealth struct {
|
||||
func (s *Server) onUnhealthyVPN(ctx context.Context) {
|
||||
s.logger.Info("program has been unhealthy for " +
|
||||
s.vpn.healthyWait.String() + ": restarting VPN")
|
||||
_, _ = s.vpn.looper.ApplyStatus(ctx, constants.Stopped)
|
||||
_, _ = s.vpn.looper.ApplyStatus(ctx, constants.Running)
|
||||
_, _ = s.vpn.loop.ApplyStatus(ctx, constants.Stopped)
|
||||
_, _ = s.vpn.loop.ApplyStatus(ctx, constants.Running)
|
||||
s.vpn.healthyWait += *s.config.VPN.Addition
|
||||
s.vpn.healthyTimer = time.NewTimer(s.vpn.healthyWait)
|
||||
}
|
||||
|
||||
@@ -5,15 +5,9 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/vpn"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
var _ ServerRunner = (*Server)(nil)
|
||||
|
||||
type ServerRunner interface {
|
||||
Run(ctx context.Context, done chan<- struct{})
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
logger Logger
|
||||
handler *handler
|
||||
@@ -23,15 +17,20 @@ type Server struct {
|
||||
}
|
||||
|
||||
func NewServer(config settings.Health,
|
||||
logger Logger, vpnLooper vpn.Looper) *Server {
|
||||
logger Logger, vpnLoop StatusApplier) *Server {
|
||||
return &Server{
|
||||
logger: logger,
|
||||
handler: newHandler(),
|
||||
dialer: &net.Dialer{},
|
||||
config: config,
|
||||
vpn: vpnHealth{
|
||||
looper: vpnLooper,
|
||||
loop: vpnLoop,
|
||||
healthyWait: *config.VPN.Initial,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type StatusApplier interface {
|
||||
ApplyStatus(ctx context.Context, status models.LoopStatus) (
|
||||
outcome string, err error)
|
||||
}
|
||||
|
||||
@@ -1,21 +1,12 @@
|
||||
package httpproxy
|
||||
|
||||
type Logger interface {
|
||||
infoErrorer
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Package httpproxy defines an interface to run an HTTP(s) proxy server.
|
||||
package httpproxy
|
||||
|
||||
import (
|
||||
@@ -12,18 +11,9 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
var _ Looper = (*Loop)(nil)
|
||||
|
||||
type Looper interface {
|
||||
Runner
|
||||
loopstate.Getter
|
||||
loopstate.Applier
|
||||
SettingsGetSetter
|
||||
}
|
||||
|
||||
type Loop struct {
|
||||
statusManager loopstate.Manager
|
||||
state state.Manager
|
||||
statusManager *loopstate.State
|
||||
state *state.State
|
||||
// Other objects
|
||||
logger Logger
|
||||
// Internal channels and locks
|
||||
|
||||
@@ -6,10 +6,6 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
type Runner interface {
|
||||
Run(ctx context.Context, done chan<- struct{})
|
||||
}
|
||||
|
||||
func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
|
||||
defer close(done)
|
||||
|
||||
|
||||
@@ -4,11 +4,8 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/httpproxy/state"
|
||||
)
|
||||
|
||||
type SettingsGetSetter = state.SettingsGetSetter
|
||||
|
||||
func (l *Loop) GetSettings() (settings settings.HTTPProxy) {
|
||||
return l.state.GetSettings()
|
||||
}
|
||||
|
||||
@@ -8,26 +8,12 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
type SettingsGetSetter interface {
|
||||
SettingsGetter
|
||||
SettingsSetter
|
||||
}
|
||||
|
||||
type SettingsGetter interface {
|
||||
GetSettings() (settings settings.HTTPProxy)
|
||||
}
|
||||
|
||||
func (s *State) GetSettings() (settings settings.HTTPProxy) {
|
||||
s.settingsMu.RLock()
|
||||
defer s.settingsMu.RUnlock()
|
||||
return s.settings
|
||||
}
|
||||
|
||||
type SettingsSetter interface {
|
||||
SetSettings(ctx context.Context,
|
||||
settings settings.HTTPProxy) (outcome string)
|
||||
}
|
||||
|
||||
func (s *State) SetSettings(ctx context.Context,
|
||||
settings settings.HTTPProxy) (outcome string) {
|
||||
s.settingsMu.Lock()
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/loopstate"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
var _ Manager = (*State)(nil)
|
||||
|
||||
type Manager interface {
|
||||
SettingsGetSetter
|
||||
}
|
||||
|
||||
func New(statusApplier loopstate.Applier,
|
||||
func New(statusApplier StatusApplier,
|
||||
settings settings.HTTPProxy) *State {
|
||||
return &State{
|
||||
statusApplier: statusApplier,
|
||||
@@ -22,7 +17,12 @@ func New(statusApplier loopstate.Applier,
|
||||
}
|
||||
|
||||
type State struct {
|
||||
statusApplier loopstate.Applier
|
||||
statusApplier StatusApplier
|
||||
settings settings.HTTPProxy
|
||||
settingsMu sync.RWMutex
|
||||
}
|
||||
|
||||
type StatusApplier interface {
|
||||
ApplyStatus(ctx context.Context, status models.LoopStatus) (
|
||||
outcome string, err error)
|
||||
}
|
||||
|
||||
@@ -1,31 +1,11 @@
|
||||
// Package httpserver implements an HTTP server.
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ Interface = (*Server)(nil)
|
||||
|
||||
// Interface is the HTTP server composite interface.
|
||||
type Interface interface {
|
||||
Runner
|
||||
AddressGetter
|
||||
}
|
||||
|
||||
// Runner is the interface for an HTTP server with a Run method.
|
||||
type Runner interface {
|
||||
Run(ctx context.Context, ready chan<- struct{}, done chan<- struct{})
|
||||
}
|
||||
|
||||
// AddressGetter obtains the address the HTTP server is listening on.
|
||||
type AddressGetter interface {
|
||||
GetAddress() (address string)
|
||||
}
|
||||
|
||||
// Server is an HTTP server implementation, which uses
|
||||
// the HTTP handler provided.
|
||||
type Server struct {
|
||||
|
||||
@@ -9,11 +9,6 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
type Applier interface {
|
||||
ApplyStatus(ctx context.Context, status models.LoopStatus) (
|
||||
outcome string, err error)
|
||||
}
|
||||
|
||||
var ErrInvalidStatus = errors.New("invalid status")
|
||||
|
||||
// ApplyStatus sends signals to the running loop depending on the
|
||||
|
||||
@@ -2,10 +2,6 @@ package loopstate
|
||||
|
||||
import "github.com/qdm12/gluetun/internal/models"
|
||||
|
||||
type Getter interface {
|
||||
GetStatus() (status models.LoopStatus)
|
||||
}
|
||||
|
||||
// GetStatus gets the status thread safely.
|
||||
func (s *State) GetStatus() (status models.LoopStatus) {
|
||||
s.statusMu.RLock()
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
package loopstate
|
||||
|
||||
type Locker interface {
|
||||
Lock()
|
||||
Unlock()
|
||||
}
|
||||
|
||||
func (s *State) Lock() { s.loopMu.Lock() }
|
||||
func (s *State) Unlock() { s.loopMu.Unlock() }
|
||||
|
||||
@@ -2,10 +2,6 @@ package loopstate
|
||||
|
||||
import "github.com/qdm12/gluetun/internal/models"
|
||||
|
||||
type Setter interface {
|
||||
SetStatus(status models.LoopStatus)
|
||||
}
|
||||
|
||||
// SetStatus sets the status thread safely.
|
||||
// It should only be called by the loop internal code since
|
||||
// it does not interact with the loop code directly.
|
||||
|
||||
@@ -6,15 +6,6 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
var _ Manager = (*State)(nil)
|
||||
|
||||
type Manager interface {
|
||||
Locker
|
||||
Getter
|
||||
Setter
|
||||
Applier
|
||||
}
|
||||
|
||||
func New(status models.LoopStatus,
|
||||
start chan<- struct{}, running <-chan models.LoopStatus,
|
||||
stop chan<- struct{}, stopped <-chan struct{}) *State {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
@@ -17,6 +16,8 @@ type Connection struct {
|
||||
// Hostname is used for IPVanish, IVPN, Privado
|
||||
// and Windscribe for TLS verification.
|
||||
Hostname string `json:"hostname"`
|
||||
// ServerName is used for PIA for port forwarding
|
||||
ServerName string `json:"server_name,omitempty"`
|
||||
// PubKey is the public key of the VPN server,
|
||||
// used only for Wireguard.
|
||||
PubKey string `json:"pubkey"`
|
||||
@@ -25,15 +26,7 @@ type Connection struct {
|
||||
func (c *Connection) Equal(other Connection) bool {
|
||||
return c.IP.Equal(other.IP) && c.Port == other.Port &&
|
||||
c.Protocol == other.Protocol && c.Hostname == other.Hostname &&
|
||||
c.PubKey == other.PubKey
|
||||
}
|
||||
|
||||
func (c Connection) OpenVPNRemoteLine() (line string) {
|
||||
return "remote " + c.IP.String() + " " + fmt.Sprint(c.Port)
|
||||
}
|
||||
|
||||
func (c Connection) OpenVPNProtoLine() (line string) {
|
||||
return "proto " + c.Protocol
|
||||
c.ServerName == other.ServerName && c.PubKey == other.PubKey
|
||||
}
|
||||
|
||||
// UpdateEmptyWith updates each field of the connection where the
|
||||
|
||||
10
internal/models/filters.go
Normal file
10
internal/models/filters.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package models
|
||||
|
||||
type FilterChoices struct {
|
||||
Countries []string
|
||||
Regions []string
|
||||
Cities []string
|
||||
ISPs []string
|
||||
Names []string
|
||||
Hostnames []string
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func (a AllServers) GetCopy() (servers AllServers) {
|
||||
servers = a // copy versions and timestamps
|
||||
servers.Cyberghost.Servers = a.GetCyberghost()
|
||||
servers.Expressvpn.Servers = a.GetExpressvpn()
|
||||
servers.Fastestvpn.Servers = a.GetFastestvpn()
|
||||
servers.HideMyAss.Servers = a.GetHideMyAss()
|
||||
servers.Ipvanish.Servers = a.GetIpvanish()
|
||||
servers.Ivpn.Servers = a.GetIvpn()
|
||||
servers.Mullvad.Servers = a.GetMullvad()
|
||||
servers.Nordvpn.Servers = a.GetNordvpn()
|
||||
servers.Perfectprivacy.Servers = a.GetPerfectprivacy()
|
||||
servers.Privado.Servers = a.GetPrivado()
|
||||
servers.Pia.Servers = a.GetPia()
|
||||
servers.Privatevpn.Servers = a.GetPrivatevpn()
|
||||
servers.Protonvpn.Servers = a.GetProtonvpn()
|
||||
servers.Purevpn.Servers = a.GetPurevpn()
|
||||
servers.Surfshark.Servers = a.GetSurfshark()
|
||||
servers.Torguard.Servers = a.GetTorguard()
|
||||
servers.VPNUnlimited.Servers = a.GetVPNUnlimited()
|
||||
servers.Vyprvpn.Servers = a.GetVyprvpn()
|
||||
servers.Windscribe.Servers = a.GetWindscribe()
|
||||
return servers
|
||||
}
|
||||
|
||||
func (a *AllServers) GetCyberghost() (servers []Server) {
|
||||
return copyServers(a.Cyberghost.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetExpressvpn() (servers []Server) {
|
||||
return copyServers(a.Expressvpn.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetFastestvpn() (servers []Server) {
|
||||
return copyServers(a.Fastestvpn.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetHideMyAss() (servers []Server) {
|
||||
return copyServers(a.HideMyAss.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetIpvanish() (servers []Server) {
|
||||
return copyServers(a.Ipvanish.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetIvpn() (servers []Server) {
|
||||
return copyServers(a.Ivpn.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetMullvad() (servers []Server) {
|
||||
return copyServers(a.Mullvad.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetNordvpn() (servers []Server) {
|
||||
return copyServers(a.Nordvpn.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetPerfectprivacy() (servers []Server) {
|
||||
return copyServers(a.Perfectprivacy.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetPia() (servers []Server) {
|
||||
return copyServers(a.Pia.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetPrivado() (servers []Server) {
|
||||
return copyServers(a.Privado.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetPrivatevpn() (servers []Server) {
|
||||
return copyServers(a.Privatevpn.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetProtonvpn() (servers []Server) {
|
||||
return copyServers(a.Protonvpn.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetPurevpn() (servers []Server) {
|
||||
return copyServers(a.Purevpn.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetSurfshark() (servers []Server) {
|
||||
return copyServers(a.Surfshark.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetTorguard() (servers []Server) {
|
||||
return copyServers(a.Torguard.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetVPNUnlimited() (servers []Server) {
|
||||
return copyServers(a.VPNUnlimited.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetVyprvpn() (servers []Server) {
|
||||
return copyServers(a.Vyprvpn.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetWevpn() (servers []Server) {
|
||||
return copyServers(a.Wevpn.Servers)
|
||||
}
|
||||
|
||||
func (a *AllServers) GetWindscribe() (servers []Server) {
|
||||
return copyServers(a.Windscribe.Servers)
|
||||
}
|
||||
|
||||
func copyServers(servers []Server) (serversCopy []Server) {
|
||||
if servers == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
serversCopy = make([]Server, len(servers))
|
||||
for i, server := range servers {
|
||||
serversCopy[i] = server
|
||||
serversCopy[i].IPs = copyIPs(server.IPs)
|
||||
}
|
||||
|
||||
return serversCopy
|
||||
}
|
||||
|
||||
func copyIPs(toCopy []net.IP) (copied []net.IP) {
|
||||
if toCopy == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
copied = make([]net.IP, len(toCopy))
|
||||
for i := range toCopy {
|
||||
copied[i] = copyIP(toCopy[i])
|
||||
}
|
||||
|
||||
return copied
|
||||
}
|
||||
|
||||
func copyIP(toCopy net.IP) (copied net.IP) {
|
||||
copied = make(net.IP, len(toCopy))
|
||||
copy(copied, toCopy)
|
||||
return copied
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_AllServers_GetCopy(t *testing.T) {
|
||||
allServers := AllServers{
|
||||
Cyberghost: Servers{
|
||||
Version: 2,
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Expressvpn: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Fastestvpn: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
HideMyAss: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Ipvanish: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Ivpn: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Mullvad: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Nordvpn: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Perfectprivacy: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Privado: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Pia: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Privatevpn: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Protonvpn: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Purevpn: Servers{
|
||||
Version: 1,
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Surfshark: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Torguard: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
VPNUnlimited: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Vyprvpn: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Windscribe: Servers{
|
||||
Servers: []Server{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
servers := allServers.GetCopy()
|
||||
|
||||
assert.Equal(t, allServers, servers)
|
||||
}
|
||||
|
||||
func Test_AllServers_GetVyprvpn(t *testing.T) {
|
||||
allServers := AllServers{
|
||||
Vyprvpn: Servers{
|
||||
Servers: []Server{
|
||||
{Hostname: "a", IPs: []net.IP{{1, 1, 1, 1}}},
|
||||
{Hostname: "b", IPs: []net.IP{{2, 2, 2, 2}}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
servers := allServers.GetVyprvpn()
|
||||
|
||||
expectedServers := []Server{
|
||||
{Hostname: "a", IPs: []net.IP{{1, 1, 1, 1}}},
|
||||
{Hostname: "b", IPs: []net.IP{{2, 2, 2, 2}}},
|
||||
}
|
||||
assert.Equal(t, expectedServers, servers)
|
||||
|
||||
allServers.Vyprvpn.Servers[0].IPs[0][0] = 9
|
||||
assert.NotEqual(t, 9, servers[0].IPs[0][0])
|
||||
|
||||
allServers.Vyprvpn.Servers[0].IPs[0][0] = 1
|
||||
servers[0].IPs[0][0] = 9
|
||||
assert.NotEqual(t, 9, allServers.Vyprvpn.Servers[0].IPs[0][0])
|
||||
}
|
||||
|
||||
func Test_copyIPs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := map[string]struct {
|
||||
toCopy []net.IP
|
||||
copied []net.IP
|
||||
}{
|
||||
"nil": {},
|
||||
"empty": {
|
||||
toCopy: []net.IP{},
|
||||
copied: []net.IP{},
|
||||
},
|
||||
"single IP": {
|
||||
toCopy: []net.IP{{1, 1, 1, 1}},
|
||||
copied: []net.IP{{1, 1, 1, 1}},
|
||||
},
|
||||
"two IPs": {
|
||||
toCopy: []net.IP{{1, 1, 1, 1}, {2, 2, 2, 2}},
|
||||
copied: []net.IP{{1, 1, 1, 1}, {2, 2, 2, 2}},
|
||||
},
|
||||
}
|
||||
|
||||
for name, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Reserver leading 9 for copy modifications below
|
||||
for _, ipToCopy := range testCase.toCopy {
|
||||
require.NotEqual(t, 9, ipToCopy[0])
|
||||
}
|
||||
|
||||
copied := copyIPs(testCase.toCopy)
|
||||
|
||||
assert.Equal(t, testCase.copied, copied)
|
||||
|
||||
if len(copied) > 0 {
|
||||
original := testCase.toCopy[0][0]
|
||||
testCase.toCopy[0][0] = 9
|
||||
assert.NotEqual(t, 9, copied[0][0])
|
||||
testCase.toCopy[0][0] = original
|
||||
|
||||
copied[0][0] = 9
|
||||
assert.NotEqual(t, 9, testCase.toCopy[0][0])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package models
|
||||
|
||||
// SurfsharkLocationData is required to keep location data on Surfshark
|
||||
// servers that are not obtained through their API.
|
||||
type SurfsharkLocationData struct {
|
||||
Region string
|
||||
Country string
|
||||
City string
|
||||
RetroLoc string // TODO remove in v4
|
||||
Hostname string
|
||||
MultiHop bool
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user