Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
732f826ec2 |
@@ -1,2 +1,2 @@
|
|||||||
FROM qmcgaw/godevcontainer
|
FROM qmcgaw/godevcontainer
|
||||||
RUN apk add wireguard-tools htop openssl
|
RUN apk add wireguard-tools htop
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ services:
|
|||||||
# Docker configuration
|
# Docker configuration
|
||||||
- ~/.docker:/root/.docker
|
- ~/.docker:/root/.docker
|
||||||
# SSH directory for Linux, OSX and WSL
|
# SSH directory for Linux, OSX and WSL
|
||||||
# On Linux and OSX, a symlink /mnt/ssh <-> ~/.ssh is
|
- ~/.ssh:/root/.ssh
|
||||||
# created in the container. On Windows, files are copied
|
# For Windows without WSL, a copy will be made
|
||||||
# from /mnt/ssh to ~/.ssh to fix permissions.
|
# from /tmp/.ssh to ~/.ssh to fix permissions
|
||||||
- ~/.ssh:/mnt/ssh
|
#- ~/.ssh:/tmp/.ssh:ro
|
||||||
# Shell history persistence
|
# Shell history persistence
|
||||||
- ~/.zsh_history:/root/.zsh_history
|
- ~/.zsh_history:/root/.zsh_history
|
||||||
# Git config
|
# Git config
|
||||||
|
|||||||
3
.github/ISSUE_TEMPLATE/bug.yml
vendored
3
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -40,7 +40,6 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: VPN service provider
|
label: VPN service provider
|
||||||
options:
|
options:
|
||||||
- AirVPN
|
|
||||||
- Custom
|
- Custom
|
||||||
- Cyberghost
|
- Cyberghost
|
||||||
- ExpressVPN
|
- ExpressVPN
|
||||||
@@ -55,10 +54,8 @@ body:
|
|||||||
- PrivateVPN
|
- PrivateVPN
|
||||||
- ProtonVPN
|
- ProtonVPN
|
||||||
- PureVPN
|
- PureVPN
|
||||||
- SlickVPN
|
|
||||||
- Surfshark
|
- Surfshark
|
||||||
- TorGuard
|
- TorGuard
|
||||||
- VPNSecure.me
|
|
||||||
- VPNUnlimited
|
- VPNUnlimited
|
||||||
- VyprVPN
|
- VyprVPN
|
||||||
- WeVPN
|
- WeVPN
|
||||||
|
|||||||
39
.github/labels.yml
vendored
39
.github/labels.yml
vendored
@@ -1,13 +1,18 @@
|
|||||||
# Temporary status
|
- name: "Bug :bug:"
|
||||||
- name: "🗯️ Waiting for feedback"
|
color: "b60205"
|
||||||
color: "aadefa"
|
description: ""
|
||||||
|
- name: "Feature request :bulb:"
|
||||||
|
color: "0e8a16"
|
||||||
|
description: ""
|
||||||
|
- name: "Help wanted :pray:"
|
||||||
|
color: "4caf50"
|
||||||
|
description: ""
|
||||||
|
- name: "Documentation :memo:"
|
||||||
|
color: "c5def5"
|
||||||
|
description: ""
|
||||||
|
- name: "Needs more info :thinking:"
|
||||||
|
color: "795548"
|
||||||
description: ""
|
description: ""
|
||||||
- name: "🔴 Blocked"
|
|
||||||
color: "ff3f14"
|
|
||||||
description: "Blocked by another issue or pull request"
|
|
||||||
- name: "🔒 After next release"
|
|
||||||
color: "e8f274"
|
|
||||||
description: "Will be done after the next release"
|
|
||||||
|
|
||||||
# Priority
|
# Priority
|
||||||
- name: "🚨 Urgent"
|
- name: "🚨 Urgent"
|
||||||
@@ -17,18 +22,7 @@
|
|||||||
color: "4285f4"
|
color: "4285f4"
|
||||||
description: ""
|
description: ""
|
||||||
|
|
||||||
# Complexity
|
|
||||||
- name: "☣️ Hard to do"
|
|
||||||
color: "7d0008"
|
|
||||||
description: ""
|
|
||||||
- name: "🟩 Easy to do"
|
|
||||||
color: "34cf43"
|
|
||||||
description: ""
|
|
||||||
|
|
||||||
# VPN providers
|
# VPN providers
|
||||||
- name: ":cloud: AirVPN"
|
|
||||||
color: "cfe8d4"
|
|
||||||
description: ""
|
|
||||||
- name: ":cloud: Cyberghost"
|
- name: ":cloud: Cyberghost"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
@@ -70,17 +64,12 @@
|
|||||||
- name: ":cloud: PureVPN"
|
- name: ":cloud: PureVPN"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
- name: ":cloud: SlickVPN"
|
|
||||||
color: "cfe8d4"
|
|
||||||
description: ""
|
|
||||||
- name: ":cloud: Surfshark"
|
- name: ":cloud: Surfshark"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
- name: ":cloud: Torguard"
|
- name: ":cloud: Torguard"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
- name: ":cloud: VPNSecure.me"
|
|
||||||
color: "cfe8d4"
|
|
||||||
- name: ":cloud: VPNUnlimited"
|
- name: ":cloud: VPNUnlimited"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
|
|||||||
15
.github/workflows/ci.yml
vendored
15
.github/workflows/ci.yml
vendored
@@ -45,15 +45,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
locale: "US"
|
locale: "US"
|
||||||
level: error
|
level: error
|
||||||
exclude: |
|
|
||||||
./internal/storage/servers.json
|
|
||||||
|
|
||||||
- name: Linting
|
- name: Linting
|
||||||
run: docker build --target lint .
|
run: docker build --target lint .
|
||||||
|
|
||||||
- name: Mocks check
|
|
||||||
run: docker build --target mocks .
|
|
||||||
|
|
||||||
- name: Build test image
|
- name: Build test image
|
||||||
run: docker build --target test -t test-container .
|
run: docker build --target test -t test-container .
|
||||||
|
|
||||||
@@ -93,7 +88,6 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
actions: read
|
actions: read
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -107,7 +101,6 @@ jobs:
|
|||||||
flavor: |
|
flavor: |
|
||||||
latest=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
latest=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
||||||
images: |
|
images: |
|
||||||
ghcr.io/qdm12/gluetun
|
|
||||||
qmcgaw/gluetun
|
qmcgaw/gluetun
|
||||||
qmcgaw/private-internet-access
|
qmcgaw/private-internet-access
|
||||||
tags: |
|
tags: |
|
||||||
@@ -125,18 +118,12 @@ jobs:
|
|||||||
username: qmcgaw
|
username: qmcgaw
|
||||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||||
|
|
||||||
- uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: qdm12
|
|
||||||
password: ${{ github.token }}
|
|
||||||
|
|
||||||
- name: Short commit
|
- name: Short commit
|
||||||
id: shortcommit
|
id: shortcommit
|
||||||
run: echo "::set-output name=value::$(git rev-parse --short HEAD)"
|
run: echo "::set-output name=value::$(git rev-parse --short HEAD)"
|
||||||
|
|
||||||
- name: Build and push final image
|
- name: Build and push final image
|
||||||
uses: docker/build-push-action@v4.0.0
|
uses: docker/build-push-action@v3.0.0
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
|
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|||||||
2
.github/workflows/labels.yml
vendored
2
.github/workflows/labels.yml
vendored
@@ -7,8 +7,6 @@ on:
|
|||||||
- .github/workflows/labels.yml
|
- .github/workflows/labels.yml
|
||||||
jobs:
|
jobs:
|
||||||
labeler:
|
labeler:
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ issues:
|
|||||||
source: "^.+= os\\.OpenFile\\(.+, .+, 0[0-9]{3}\\)"
|
source: "^.+= os\\.OpenFile\\(.+, .+, 0[0-9]{3}\\)"
|
||||||
linters:
|
linters:
|
||||||
- gomnd
|
- gomnd
|
||||||
|
|
||||||
- text: "^mnd: Magic number: 0[0-9]{3}, in <argument> detected$"
|
- text: "^mnd: Magic number: 0[0-9]{3}, in <argument> detected$"
|
||||||
source: "^.+= os\\.MkdirAll\\(.+, 0[0-9]{3}\\)"
|
source: "^.+= os\\.MkdirAll\\(.+, 0[0-9]{3}\\)"
|
||||||
linters:
|
linters:
|
||||||
@@ -34,7 +35,8 @@ linters:
|
|||||||
enable:
|
enable:
|
||||||
# - cyclop
|
# - cyclop
|
||||||
# - errorlint
|
# - errorlint
|
||||||
- asasalint
|
# - varnamelen
|
||||||
|
# - wrapcheck
|
||||||
- asciicheck
|
- asciicheck
|
||||||
- bidichk
|
- bidichk
|
||||||
- bodyclose
|
- bodyclose
|
||||||
@@ -65,8 +67,8 @@ linters:
|
|||||||
- goprintffuncname
|
- goprintffuncname
|
||||||
- gosec
|
- gosec
|
||||||
- grouper
|
- grouper
|
||||||
|
- ifshort
|
||||||
- importas
|
- importas
|
||||||
- interfacebloat
|
|
||||||
- ireturn
|
- ireturn
|
||||||
- lll
|
- lll
|
||||||
- maintidx
|
- maintidx
|
||||||
@@ -81,8 +83,8 @@ linters:
|
|||||||
- nosprintfhostport
|
- nosprintfhostport
|
||||||
- prealloc
|
- prealloc
|
||||||
- predeclared
|
- predeclared
|
||||||
|
- predeclared
|
||||||
- promlinter
|
- promlinter
|
||||||
- reassign
|
|
||||||
- revive
|
- revive
|
||||||
- rowserrcheck
|
- rowserrcheck
|
||||||
- sqlclosecheck
|
- sqlclosecheck
|
||||||
@@ -91,7 +93,6 @@ linters:
|
|||||||
- tparallel
|
- tparallel
|
||||||
- unconvert
|
- unconvert
|
||||||
- unparam
|
- unparam
|
||||||
- usestdlibvars
|
|
||||||
- wastedassign
|
- wastedassign
|
||||||
- whitespace
|
- whitespace
|
||||||
|
|
||||||
|
|||||||
16
.vscode/launch.json
vendored
16
.vscode/launch.json
vendored
@@ -5,11 +5,11 @@
|
|||||||
"name": "Update a VPN provider servers data",
|
"name": "Update a VPN provider servers data",
|
||||||
"type": "go",
|
"type": "go",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"cwd": "${workspaceFolder}",
|
"mode": "debug",
|
||||||
"program": "cmd/gluetun/main.go",
|
"program": "cmd/gluetun/main.go",
|
||||||
"args": [
|
"args": [
|
||||||
"update",
|
"update",
|
||||||
"${input:updateMode}",
|
"-enduser",
|
||||||
"-providers",
|
"-providers",
|
||||||
"${input:provider}"
|
"${input:provider}"
|
||||||
],
|
],
|
||||||
@@ -20,16 +20,6 @@
|
|||||||
"id": "provider",
|
"id": "provider",
|
||||||
"type": "promptString",
|
"type": "promptString",
|
||||||
"description": "Please enter a provider (or comma separated list of providers)",
|
"description": "Please enter a provider (or comma separated list of providers)",
|
||||||
},
|
}
|
||||||
{
|
|
||||||
"id": "updateMode",
|
|
||||||
"type": "pickString",
|
|
||||||
"description": "Update mode to use",
|
|
||||||
"options": [
|
|
||||||
"-maintainer",
|
|
||||||
"-enduser"
|
|
||||||
],
|
|
||||||
"default": "-maintainer"
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
40
Dockerfile
40
Dockerfile
@@ -1,22 +1,18 @@
|
|||||||
ARG ALPINE_VERSION=3.17
|
ARG ALPINE_VERSION=3.16
|
||||||
ARG GO_ALPINE_VERSION=3.17
|
ARG GO_ALPINE_VERSION=3.16
|
||||||
ARG GO_VERSION=1.20
|
ARG GO_VERSION=1.17
|
||||||
ARG XCPUTRANSLATE_VERSION=v0.6.0
|
ARG XCPUTRANSLATE_VERSION=v0.6.0
|
||||||
ARG GOLANGCI_LINT_VERSION=v1.51.2
|
ARG GOLANGCI_LINT_VERSION=v1.46.2
|
||||||
ARG MOCKGEN_VERSION=v1.6.0
|
|
||||||
ARG BUILDPLATFORM=linux/amd64
|
ARG BUILDPLATFORM=linux/amd64
|
||||||
|
|
||||||
FROM --platform=${BUILDPLATFORM} qmcgaw/xcputranslate:${XCPUTRANSLATE_VERSION} AS xcputranslate
|
FROM --platform=${BUILDPLATFORM} qmcgaw/xcputranslate:${XCPUTRANSLATE_VERSION} AS xcputranslate
|
||||||
FROM --platform=${BUILDPLATFORM} qmcgaw/binpot:golangci-lint-${GOLANGCI_LINT_VERSION} AS golangci-lint
|
FROM --platform=${BUILDPLATFORM} qmcgaw/binpot:golangci-lint-${GOLANGCI_LINT_VERSION} AS golangci-lint
|
||||||
FROM --platform=${BUILDPLATFORM} qmcgaw/binpot:mockgen-${MOCKGEN_VERSION} AS mockgen
|
|
||||||
|
|
||||||
FROM --platform=${BUILDPLATFORM} golang:${GO_VERSION}-alpine${GO_ALPINE_VERSION} AS base
|
FROM --platform=${BUILDPLATFORM} golang:${GO_VERSION}-alpine${GO_ALPINE_VERSION} AS base
|
||||||
COPY --from=xcputranslate /xcputranslate /usr/local/bin/xcputranslate
|
COPY --from=xcputranslate /xcputranslate /usr/local/bin/xcputranslate
|
||||||
# Note: findutils needed to have xargs support `-d` flag for mocks stage.
|
RUN apk --update add git g++
|
||||||
RUN apk --update add git g++ findutils
|
|
||||||
ENV CGO_ENABLED=0
|
ENV CGO_ENABLED=0
|
||||||
COPY --from=golangci-lint /bin /go/bin/golangci-lint
|
COPY --from=golangci-lint /bin /go/bin/golangci-lint
|
||||||
COPY --from=mockgen /bin /go/bin/mockgen
|
|
||||||
WORKDIR /tmp/gobuild
|
WORKDIR /tmp/gobuild
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
@@ -34,18 +30,6 @@ FROM --platform=${BUILDPLATFORM} base AS lint
|
|||||||
COPY .golangci.yml ./
|
COPY .golangci.yml ./
|
||||||
RUN golangci-lint run --timeout=10m
|
RUN golangci-lint run --timeout=10m
|
||||||
|
|
||||||
FROM --platform=${BUILDPLATFORM} base AS mocks
|
|
||||||
RUN git init && \
|
|
||||||
git config user.email ci@localhost && \
|
|
||||||
git config user.name ci && \
|
|
||||||
git config core.fileMode false && \
|
|
||||||
git add -A && \
|
|
||||||
git commit -m "snapshot" && \
|
|
||||||
grep -lr -E '^// Code generated by MockGen\. DO NOT EDIT\.$' . | xargs -r -d '\n' rm && \
|
|
||||||
go generate -run "mockgen" ./... && \
|
|
||||||
git diff --exit-code && \
|
|
||||||
rm -rf .git/
|
|
||||||
|
|
||||||
FROM --platform=${BUILDPLATFORM} base AS build
|
FROM --platform=${BUILDPLATFORM} base AS build
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
ARG VERSION=unknown
|
ARG VERSION=unknown
|
||||||
@@ -91,13 +75,13 @@ ENV VPN_SERVICE_PROVIDER=pia \
|
|||||||
OPENVPN_CIPHERS= \
|
OPENVPN_CIPHERS= \
|
||||||
OPENVPN_AUTH= \
|
OPENVPN_AUTH= \
|
||||||
OPENVPN_PROCESS_USER= \
|
OPENVPN_PROCESS_USER= \
|
||||||
|
OPENVPN_IPV6=off \
|
||||||
OPENVPN_CUSTOM_CONFIG= \
|
OPENVPN_CUSTOM_CONFIG= \
|
||||||
# Wireguard
|
# Wireguard
|
||||||
WIREGUARD_PRIVATE_KEY= \
|
WIREGUARD_PRIVATE_KEY= \
|
||||||
WIREGUARD_PRESHARED_KEY= \
|
WIREGUARD_PRESHARED_KEY= \
|
||||||
WIREGUARD_PUBLIC_KEY= \
|
WIREGUARD_PUBLIC_KEY= \
|
||||||
WIREGUARD_ADDRESSES= \
|
WIREGUARD_ADDRESSES= \
|
||||||
WIREGUARD_IMPLEMENTATION=auto \
|
|
||||||
# VPN server filtering
|
# VPN server filtering
|
||||||
SERVER_REGIONS= \
|
SERVER_REGIONS= \
|
||||||
SERVER_COUNTRIES= \
|
SERVER_COUNTRIES= \
|
||||||
@@ -111,15 +95,8 @@ ENV VPN_SERVICE_PROVIDER=pia \
|
|||||||
PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING=off \
|
PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING=off \
|
||||||
PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \
|
PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \
|
||||||
# # Cyberghost only:
|
# # Cyberghost only:
|
||||||
OPENVPN_CERT= \
|
|
||||||
OPENVPN_KEY= \
|
|
||||||
OPENVPN_CLIENTCRT_SECRETFILE=/run/secrets/openvpn_clientcrt \
|
OPENVPN_CLIENTCRT_SECRETFILE=/run/secrets/openvpn_clientcrt \
|
||||||
OPENVPN_CLIENTKEY_SECRETFILE=/run/secrets/openvpn_clientkey \
|
OPENVPN_CLIENTKEY_SECRETFILE=/run/secrets/openvpn_clientkey \
|
||||||
# # VPNSecure only:
|
|
||||||
OPENVPN_ENCRYPTED_KEY= \
|
|
||||||
OPENVPN_ENCRYPTED_KEY_SECRETFILE=/run/secrets/openvpn_encrypted_key \
|
|
||||||
OPENVPN_KEY_PASSPHRASE= \
|
|
||||||
OPENVPN_KEY_PASSPHRASE_SECRETFILE=/run/secrets/openvpn_key_passphrase \
|
|
||||||
# # Nordvpn only:
|
# # Nordvpn only:
|
||||||
SERVER_NUMBER= \
|
SERVER_NUMBER= \
|
||||||
# # PIA only:
|
# # PIA only:
|
||||||
@@ -128,8 +105,6 @@ ENV VPN_SERVICE_PROVIDER=pia \
|
|||||||
FREE_ONLY= \
|
FREE_ONLY= \
|
||||||
# # Surfshark only:
|
# # Surfshark only:
|
||||||
MULTIHOP_ONLY= \
|
MULTIHOP_ONLY= \
|
||||||
# # VPN Secure only:
|
|
||||||
PREMIUM_ONLY= \
|
|
||||||
# Firewall
|
# Firewall
|
||||||
FIREWALL=on \
|
FIREWALL=on \
|
||||||
FIREWALL_VPN_INPUT_PORTS= \
|
FIREWALL_VPN_INPUT_PORTS= \
|
||||||
@@ -197,9 +172,8 @@ ENTRYPOINT ["/gluetun-entrypoint"]
|
|||||||
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
|
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
|
||||||
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /gluetun-entrypoint healthcheck
|
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /gluetun-entrypoint healthcheck
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
RUN apk add --no-cache --update -l wget && \
|
RUN apk add --no-cache --update -l apk-tools && \
|
||||||
apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.12/main" openvpn==2.4.12-r0 && \
|
apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.12/main" openvpn==2.4.12-r0 && \
|
||||||
apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.16/main" openssl\~1.1 && \
|
|
||||||
mv /usr/sbin/openvpn /usr/sbin/openvpn2.4 && \
|
mv /usr/sbin/openvpn /usr/sbin/openvpn2.4 && \
|
||||||
apk del openvpn && \
|
apk del openvpn && \
|
||||||
apk add --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \
|
apk add --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \
|
||||||
|
|||||||
255
README.md
255
README.md
@@ -1,130 +1,125 @@
|
|||||||
# Gluetun VPN client
|
# Gluetun VPN client
|
||||||
|
|
||||||
Lightweight swiss-knife-like VPN client to multiple VPN service providers
|
Lightweight swiss-knife-like VPN client to multiple VPN sercice providers
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
[](https://github.com/qdm12/gluetun/actions/workflows/ci.yml)
|
[](https://github.com/qdm12/gluetun/actions/workflows/ci.yml)
|
||||||
|
|
||||||
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||||
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||||
|
|
||||||
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||||
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||||
|
|
||||||

|

|
||||||

|

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

|

|
||||||

|

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

|

|
||||||

|

|
||||||

|

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

|

|
||||||
|
|
||||||
## Quick links
|
## Quick links
|
||||||
|
|
||||||
- [Setup](#Setup)
|
- [Setup](#Setup)
|
||||||
- [Features](#Features)
|
- [Features](#Features)
|
||||||
- Problem?
|
- Problem?
|
||||||
- [Check the Wiki](https://github.com/qdm12/gluetun/wiki)
|
- [Check the Wiki](https://github.com/qdm12/gluetun/wiki)
|
||||||
- [Start a discussion](https://github.com/qdm12/gluetun/discussions)
|
- [Start a discussion](https://github.com/qdm12/gluetun/discussions)
|
||||||
- [Fix the Unraid template](https://github.com/qdm12/gluetun/discussions/550)
|
- [Fix the Unraid template](https://github.com/qdm12/gluetun/discussions/550)
|
||||||
- Suggestion?
|
- Suggestion?
|
||||||
- [Create an issue](https://github.com/qdm12/gluetun/issues)
|
- [Create an issue](https://github.com/qdm12/gluetun/issues)
|
||||||
- [Join the Slack channel](https://join.slack.com/t/qdm12/shared_invite/enQtOTE0NjcxNTM1ODc5LTYyZmVlOTM3MGI4ZWU0YmJkMjUxNmQ4ODQ2OTAwYzMxMTlhY2Q1MWQyOWUyNjc2ODliNjFjMDUxNWNmNzk5MDk)
|
- [Join the Slack channel](https://join.slack.com/t/qdm12/shared_invite/enQtOTE0NjcxNTM1ODc5LTYyZmVlOTM3MGI4ZWU0YmJkMjUxNmQ4ODQ2OTAwYzMxMTlhY2Q1MWQyOWUyNjc2ODliNjFjMDUxNWNmNzk5MDk)
|
||||||
- Happy?
|
- Happy?
|
||||||
- Sponsor me on [github.com/sponsors/qdm12](https://github.com/sponsors/qdm12)
|
- Sponsor me on [github.com/sponsors/qdm12](https://github.com/sponsors/qdm12)
|
||||||
- Donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw)
|
- Donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw)
|
||||||
- Drop me [an email](mailto:quentin.mcgaw@gmail.com)
|
- Drop me [an email](mailto:quentin.mcgaw@gmail.com)
|
||||||
- **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)
|
- **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:
|
- Video:
|
||||||
|
|
||||||
[](https://youtu.be/0F6I03LQcI4)
|
[](https://youtu.be/0F6I03LQcI4)
|
||||||
|
|
||||||
- [Substack Console interview](https://console.substack.com/p/console-72)
|
- [Substack Console interview](https://console.substack.com/p/console-72)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Based on Alpine 3.17 for a small Docker image of 42MB
|
- Based on Alpine 3.16 for a small Docker image of 29MB
|
||||||
- Supports: **AirVPN**, **Cyberghost**, **ExpressVPN**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Perfect Privacy**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **SlickVPN**, **Surfshark**, **TorGuard**, **VPNSecure.me**, **VPNUnlimited**, **Vyprvpn**, **WeVPN**, **Windscribe** servers
|
- Supports: **Cyberghost**, **ExpressVPN**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Perfect Privacy**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **WeVPN**, **Windscribe** servers
|
||||||
- Supports OpenVPN for all providers listed
|
- Supports OpenVPN for all providers listed
|
||||||
- Supports Wireguard both kernelspace and userspace
|
- Supports Wireguard both kernelspace and userspace
|
||||||
- For **Mullvad**, **Ivpn**, **Surfshark** and **Windscribe**
|
- For **Mullvad**, **Ivpn** and **Windscribe**
|
||||||
- For **ProtonVPN**, **PureVPN**, **Torguard**, **VPN Unlimited** and **WeVPN** using [the custom provider](https://github.com/qdm12/gluetun/wiki/Custom-provider)
|
- For **Torguard**, **VPN Unlimited** and **WeVPN** using [the custom provider](https://github.com/qdm12/gluetun/wiki/Custom-provider)
|
||||||
- For custom Wireguard configurations using [the custom provider](https://github.com/qdm12/gluetun/wiki/Custom-provider)
|
- For custom Wireguard configurations using [the custom provider](https://github.com/qdm12/gluetun/wiki/Custom-provider)
|
||||||
- More in progress, see [#134](https://github.com/qdm12/gluetun/issues/134)
|
- More in progress, see [#134](https://github.com/qdm12/gluetun/issues/134)
|
||||||
- DNS over TLS baked in with service provider(s) of your choice
|
- DNS over TLS baked in with service provider(s) of your choice
|
||||||
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
|
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
|
||||||
- Choose the vpn network protocol, `udp` or `tcp`
|
- Choose the vpn network protocol, `udp` or `tcp`
|
||||||
- Built in firewall kill switch to allow traffic only with needed the VPN servers and LAN devices
|
- Built in firewall kill switch to allow traffic only with needed the VPN servers and LAN devices
|
||||||
- Built in Shadowsocks proxy (protocol based on SOCKS5 with an encryption layer, tunnels TCP+UDP)
|
- Built in Shadowsocks proxy (protocol based on SOCKS5 with an encryption layer, tunnels TCP+UDP)
|
||||||
- Built in HTTP proxy (tunnels HTTP and HTTPS through TCP)
|
- Built in HTTP proxy (tunnels HTTP and HTTPS through TCP)
|
||||||
- [Connect other containers to it](https://github.com/qdm12/gluetun/wiki/Connect-a-container-to-gluetun)
|
- [Connect other containers to it](https://github.com/qdm12/gluetun/wiki/Connect-a-container-to-gluetun)
|
||||||
- [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-a-LAN-device-to-gluetun)
|
- [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-a-LAN-device-to-gluetun)
|
||||||
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even ppc64le 🎆
|
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even ppc64le 🎆
|
||||||
- [Custom VPN server side port forwarding for Private Internet Access](https://github.com/qdm12/gluetun/wiki/Private-internet-access#vpn-server-port-forwarding)
|
- [Custom VPN server side port forwarding for Private Internet Access](https://github.com/qdm12/gluetun/wiki/Private-internet-access#vpn-server-port-forwarding)
|
||||||
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
|
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
|
||||||
- Unbound subprogram drops root privileges once launched
|
- Unbound subprogram drops root privileges once launched
|
||||||
- Can work as a Kubernetes sidecar container, thanks @rorph
|
- Can work as a Kubernetes sidecar container, thanks @rorph
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
🎉 There are now instructions specific to each VPN provider with examples to help you get started as quickly as possible!
|
🎉 There are now instructions specific to each VPN provider with examples to help you get started as quickly as possible!
|
||||||
|
|
||||||
Go to the [Wiki](https://github.com/qdm12/gluetun/wiki)!
|
Go to the [Wiki](https://github.com/qdm12/gluetun/wiki)!
|
||||||
|
|
||||||
[🐛 Found a bug in the Wiki?!](https://github.com/qdm12/gluetun/issues/new?assignees=&labels=%F0%9F%93%84+Wiki+issue&template=wiki+issue.yml&title=Wiki+issue%3A+)
|
[🐛 Found a bug in the Wiki?!](https://github.com/qdm12/gluetun/issues/new?assignees=&labels=%F0%9F%93%84+Wiki+issue&template=wiki+issue.yml&title=Wiki+issue%3A+)
|
||||||
|
|
||||||
Here's a docker-compose.yml for the laziest:
|
Here's a docker-compose.yml for the laziest:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
version: "3"
|
version: "3"
|
||||||
services:
|
services:
|
||||||
gluetun:
|
gluetun:
|
||||||
image: qmcgaw/gluetun
|
image: qmcgaw/gluetun
|
||||||
# container_name: gluetun
|
# container_name: gluetun
|
||||||
# line above must be uncommented to allow external containers to connect. See https://github.com/qdm12/gluetun/wiki/Connect-a-container-to-gluetun#external-container-to-gluetun
|
# line above must be uncommented to allow external containers to connect. See https://github.com/qdm12/gluetun/wiki/Connect-a-container-to-gluetun#external-container-to-gluetun
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
devices:
|
devices:
|
||||||
- /dev/net/tun:/dev/net/tun
|
- /dev/net/tun:/dev/net/tun
|
||||||
ports:
|
ports:
|
||||||
- 8888:8888/tcp # HTTP proxy
|
- 8888:8888/tcp # HTTP proxy
|
||||||
- 8388:8388/tcp # Shadowsocks
|
- 8388:8388/tcp # Shadowsocks
|
||||||
- 8388:8388/udp # Shadowsocks
|
- 8388:8388/udp # Shadowsocks
|
||||||
volumes:
|
volumes:
|
||||||
- /yourpath:/gluetun
|
- /yourpath:/gluetun
|
||||||
environment:
|
environment:
|
||||||
# See https://github.com/qdm12/gluetun/wiki
|
# See https://github.com/qdm12/gluetun/wiki
|
||||||
- VPN_SERVICE_PROVIDER=ivpn
|
- VPN_SERVICE_PROVIDER=ivpn
|
||||||
- VPN_TYPE=openvpn
|
- VPN_TYPE=openvpn
|
||||||
# OpenVPN:
|
# OpenVPN:
|
||||||
- OPENVPN_USER=
|
- OPENVPN_USER=
|
||||||
- OPENVPN_PASSWORD=
|
- OPENVPN_PASSWORD=
|
||||||
# Wireguard:
|
# Wireguard:
|
||||||
# - WIREGUARD_PRIVATE_KEY=wOEI9rqqbDwnN8/Bpp22sVz48T71vJ4fYmFWujulwUU=
|
# - WIREGUARD_PRIVATE_KEY=wOEI9rqqbDwnN8/Bpp22sVz48T71vJ4fYmFWujulwUU=
|
||||||
# - WIREGUARD_ADDRESSES=10.64.222.21/32
|
# - WIREGUARD_ADDRESSES=10.64.222.21/32
|
||||||
# Timezone for accurate log times
|
# Timezone for accurate log times
|
||||||
- TZ=
|
- TZ=
|
||||||
# Server list updater. See https://github.com/qdm12/gluetun/wiki/Updating-Servers#periodic-update
|
```
|
||||||
- UPDATER_PERIOD=
|
|
||||||
- UPDATER_VPN_SERVICE_PROVIDERS=
|
## License
|
||||||
```
|
|
||||||
|
[](https://github.com/qdm12/gluetun/master/LICENSE)
|
||||||
🆕 Image also available as `ghcr.io/qdm12/gluetun`
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[](https://github.com/qdm12/gluetun/master/LICENSE)
|
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ import (
|
|||||||
"github.com/qdm12/dns/pkg/unbound"
|
"github.com/qdm12/dns/pkg/unbound"
|
||||||
"github.com/qdm12/gluetun/internal/alpine"
|
"github.com/qdm12/gluetun/internal/alpine"
|
||||||
"github.com/qdm12/gluetun/internal/cli"
|
"github.com/qdm12/gluetun/internal/cli"
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/sources"
|
||||||
"github.com/qdm12/gluetun/internal/configuration/sources/env"
|
"github.com/qdm12/gluetun/internal/configuration/sources/env"
|
||||||
"github.com/qdm12/gluetun/internal/configuration/sources/files"
|
"github.com/qdm12/gluetun/internal/configuration/sources/files"
|
||||||
mux "github.com/qdm12/gluetun/internal/configuration/sources/merge"
|
"github.com/qdm12/gluetun/internal/configuration/sources/mux"
|
||||||
"github.com/qdm12/gluetun/internal/configuration/sources/secrets"
|
"github.com/qdm12/gluetun/internal/configuration/sources/secrets"
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/dns"
|
"github.com/qdm12/gluetun/internal/dns"
|
||||||
@@ -77,8 +77,7 @@ func main() {
|
|||||||
|
|
||||||
args := os.Args
|
args := os.Args
|
||||||
tun := tun.New()
|
tun := tun.New()
|
||||||
netLinkDebugLogger := logger.New(log.SetComponent("netlink"))
|
netLinker := netlink.New()
|
||||||
netLinker := netlink.New(netLinkDebugLogger)
|
|
||||||
cli := cli.New()
|
cli := cli.New()
|
||||||
cmder := command.NewCmder()
|
cmder := command.NewCmder()
|
||||||
|
|
||||||
@@ -92,13 +91,12 @@ func main() {
|
|||||||
errorCh <- _main(ctx, buildInfo, args, logger, muxReader, tun, netLinker, cmder, cli)
|
errorCh <- _main(ctx, buildInfo, args, logger, muxReader, tun, netLinker, cmder, cli)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var err error
|
|
||||||
select {
|
select {
|
||||||
case signal := <-signalCh:
|
case signal := <-signalCh:
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
logger.Warn("Caught OS signal " + signal.String() + ", shutting down")
|
logger.Warn("Caught OS signal " + signal.String() + ", shutting down")
|
||||||
cancel()
|
cancel()
|
||||||
case err = <-errorCh:
|
case err := <-errorCh:
|
||||||
close(errorCh)
|
close(errorCh)
|
||||||
if err == nil { // expected exit such as healthcheck
|
if err == nil { // expected exit such as healthcheck
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
@@ -110,27 +108,18 @@ func main() {
|
|||||||
const shutdownGracePeriod = 5 * time.Second
|
const shutdownGracePeriod = 5 * time.Second
|
||||||
timer := time.NewTimer(shutdownGracePeriod)
|
timer := time.NewTimer(shutdownGracePeriod)
|
||||||
select {
|
select {
|
||||||
case shutdownErr := <-errorCh:
|
case <-errorCh:
|
||||||
if !timer.Stop() {
|
if !timer.Stop() {
|
||||||
<-timer.C
|
<-timer.C
|
||||||
}
|
}
|
||||||
if shutdownErr != nil {
|
|
||||||
logger.Warnf("Shutdown not completed gracefully: %s", shutdownErr)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Info("Shutdown successful")
|
logger.Info("Shutdown successful")
|
||||||
if err != nil {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
os.Exit(0)
|
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
logger.Warn("Shutdown timed out")
|
logger.Warn("Shutdown timed out")
|
||||||
os.Exit(1)
|
|
||||||
case signal := <-signalCh:
|
case signal := <-signalCh:
|
||||||
logger.Warn("Caught OS signal " + signal.String() + ", forcing shut down")
|
logger.Warn("Caught OS signal " + signal.String() + ", forcing shut down")
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -139,7 +128,7 @@ var (
|
|||||||
|
|
||||||
//nolint:gocognit,gocyclo,maintidx
|
//nolint:gocognit,gocyclo,maintidx
|
||||||
func _main(ctx context.Context, buildInfo models.BuildInformation,
|
func _main(ctx context.Context, buildInfo models.BuildInformation,
|
||||||
args []string, logger log.LoggerInterface, source Source,
|
args []string, logger log.LoggerInterface, source sources.Source,
|
||||||
tun Tun, netLinker netLinker, cmder command.RunStarter,
|
tun Tun, netLinker netLinker, cmder command.RunStarter,
|
||||||
cli clier) error {
|
cli clier) error {
|
||||||
if len(args) > 1 { // cli operation
|
if len(args) > 1 { // cli operation
|
||||||
@@ -149,7 +138,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
case "clientkey":
|
case "clientkey":
|
||||||
return cli.ClientKey(args[2:])
|
return cli.ClientKey(args[2:])
|
||||||
case "openvpnconfig":
|
case "openvpnconfig":
|
||||||
return cli.OpenvpnConfig(logger, source, netLinker)
|
return cli.OpenvpnConfig(logger, source)
|
||||||
case "update":
|
case "update":
|
||||||
return cli.Update(ctx, args[2:], logger)
|
return cli.Update(ctx, args[2:], logger)
|
||||||
case "format-servers":
|
case "format-servers":
|
||||||
@@ -190,7 +179,6 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
// - firewall Debug and Enabled are booleans parsed from source
|
// - firewall Debug and Enabled are booleans parsed from source
|
||||||
|
|
||||||
logger.Patch(log.SetLevel(*allSettings.Log.Level))
|
logger.Patch(log.SetLevel(*allSettings.Log.Level))
|
||||||
netLinker.PatchLoggerLevel(*allSettings.Log.Level)
|
|
||||||
|
|
||||||
routingLogger := logger.New(log.SetComponent("routing"))
|
routingLogger := logger.New(log.SetComponent("routing"))
|
||||||
if *allSettings.Firewall.Debug { // To remove in v4
|
if *allSettings.Firewall.Debug { // To remove in v4
|
||||||
@@ -232,12 +220,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ipv6Supported, err := netLinker.IsIPv6Supported()
|
err = allSettings.Validate(storage)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("checking for IPv6 support: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = allSettings.Validate(storage, ipv6Supported)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -245,7 +228,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
allSettings.Pprof.HTTPServer.Logger = logger.New(log.SetComponent("pprof"))
|
allSettings.Pprof.HTTPServer.Logger = logger.New(log.SetComponent("pprof"))
|
||||||
pprofServer, err := pprof.New(allSettings.Pprof)
|
pprofServer, err := pprof.New(allSettings.Pprof)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating Pprof server: %w", err)
|
return fmt.Errorf("cannot create Pprof server: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
puid, pgid := int(*allSettings.System.PUID), int(*allSettings.System.PGID)
|
puid, pgid := int(*allSettings.System.PUID), int(*allSettings.System.PGID)
|
||||||
@@ -277,10 +260,6 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
|
|
||||||
logger.Info(allSettings.String())
|
logger.Info(allSettings.String())
|
||||||
|
|
||||||
for _, warning := range allSettings.Warnings() {
|
|
||||||
logger.Warn(warning)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil {
|
if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -291,7 +270,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
const defaultUsername = "nonrootuser"
|
const defaultUsername = "nonrootuser"
|
||||||
nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid)
|
nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating user: %w", err)
|
return fmt.Errorf("cannot create user: %w", err)
|
||||||
}
|
}
|
||||||
if nonRootUsername != defaultUsername {
|
if nonRootUsername != defaultUsername {
|
||||||
logger.Info("using existing username " + nonRootUsername + " corresponding to user id " + fmt.Sprint(puid))
|
logger.Info("using existing username " + nonRootUsername + " corresponding to user id " + fmt.Sprint(puid))
|
||||||
@@ -309,7 +288,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
if strings.Contains(err.Error(), "operation not permitted") {
|
if strings.Contains(err.Error(), "operation not permitted") {
|
||||||
logger.Warn("💡 Tip: Are you passing NET_ADMIN capability to gluetun?")
|
logger.Warn("💡 Tip: Are you passing NET_ADMIN capability to gluetun?")
|
||||||
}
|
}
|
||||||
return fmt.Errorf("setting up routing: %w", err)
|
return fmt.Errorf("cannot setup routing: %w", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
routingLogger.Info("routing cleanup...")
|
routingLogger.Info("routing cleanup...")
|
||||||
@@ -360,14 +339,11 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
tickersGroupHandler := goshutdown.NewGroupHandler("tickers", defaultGroupOptions...)
|
tickersGroupHandler := goshutdown.NewGroupHandler("tickers", defaultGroupOptions...)
|
||||||
otherGroupHandler := goshutdown.NewGroupHandler("other", defaultGroupOptions...)
|
otherGroupHandler := goshutdown.NewGroupHandler("other", defaultGroupOptions...)
|
||||||
|
|
||||||
if *allSettings.Pprof.Enabled {
|
pprofReady := make(chan struct{})
|
||||||
// TODO run in run loop so this can be patched at runtime
|
pprofHandler, pprofCtx, pprofDone := goshutdown.NewGoRoutineHandler("pprof server")
|
||||||
pprofReady := make(chan struct{})
|
go pprofServer.Run(pprofCtx, pprofReady, pprofDone)
|
||||||
pprofHandler, pprofCtx, pprofDone := goshutdown.NewGoRoutineHandler("pprof server")
|
otherGroupHandler.Add(pprofHandler)
|
||||||
go pprofServer.Run(pprofCtx, pprofReady, pprofDone)
|
<-pprofReady
|
||||||
otherGroupHandler.Add(pprofHandler)
|
|
||||||
<-pprofReady
|
|
||||||
}
|
|
||||||
|
|
||||||
portForwardLogger := logger.New(log.SetComponent("port forwarding"))
|
portForwardLogger := logger.New(log.SetComponent("port forwarding"))
|
||||||
portForwardLooper := portforward.NewLoop(allSettings.VPN.Provider.PortForwarding,
|
portForwardLooper := portforward.NewLoop(allSettings.VPN.Provider.PortForwarding,
|
||||||
@@ -413,7 +389,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
httpClient, unzipper, parallelResolver, ipFetcher, openvpnFileExtractor)
|
httpClient, unzipper, parallelResolver, ipFetcher, openvpnFileExtractor)
|
||||||
|
|
||||||
vpnLogger := logger.New(log.SetComponent("vpn"))
|
vpnLogger := logger.New(log.SetComponent("vpn"))
|
||||||
vpnLooper := vpn.NewLoop(allSettings.VPN, ipv6Supported, allSettings.Firewall.VPNInputPorts,
|
vpnLooper := vpn.NewLoop(allSettings.VPN, allSettings.Firewall.VPNInputPorts,
|
||||||
providers, storage, ovpnConf, netLinker, firewallConf, routingConf, portForwardLooper,
|
providers, storage, ovpnConf, netLinker, firewallConf, routingConf, portForwardLooper,
|
||||||
cmder, publicIPLooper, unboundLooper, vpnLogger, httpClient,
|
cmder, publicIPLooper, unboundLooper, vpnLogger, httpClient,
|
||||||
buildInfo, *allSettings.Version.Enabled)
|
buildInfo, *allSettings.Version.Enabled)
|
||||||
@@ -455,10 +431,9 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
"http server", goroutine.OptionTimeout(defaultShutdownTimeout))
|
"http server", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
httpServer, err := server.New(httpServerCtx, controlServerAddress, controlServerLogging,
|
httpServer, err := server.New(httpServerCtx, controlServerAddress, controlServerLogging,
|
||||||
logger.New(log.SetComponent("http server")),
|
logger.New(log.SetComponent("http server")),
|
||||||
buildInfo, vpnLooper, portForwardLooper, unboundLooper, updaterLooper, publicIPLooper,
|
buildInfo, vpnLooper, portForwardLooper, unboundLooper, updaterLooper, publicIPLooper)
|
||||||
storage, ipv6Supported)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("setting up control server: %w", err)
|
return fmt.Errorf("cannot setup control server: %w", err)
|
||||||
}
|
}
|
||||||
httpServerReady := make(chan struct{})
|
httpServerReady := make(chan struct{})
|
||||||
go httpServer.Run(httpServerCtx, httpServerReady, httpServerDone)
|
go httpServer.Run(httpServerCtx, httpServerReady, httpServerDone)
|
||||||
@@ -505,7 +480,7 @@ func printVersions(ctx context.Context, logger infoer,
|
|||||||
for _, element := range elements {
|
for _, element := range elements {
|
||||||
version, err := element.getVersion(ctx)
|
version, err := element.getVersion(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("getting %s version: %w", element.name, err)
|
return err
|
||||||
}
|
}
|
||||||
logger.Info(element.name + " version: " + version)
|
logger.Info(element.name + " version: " + version)
|
||||||
}
|
}
|
||||||
@@ -514,36 +489,18 @@ func printVersions(ctx context.Context, logger infoer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
type netLinker interface {
|
type netLinker interface {
|
||||||
Addresser
|
|
||||||
Router
|
|
||||||
Ruler
|
|
||||||
Linker
|
|
||||||
IsWireguardSupported() (ok bool, err error)
|
|
||||||
IsIPv6Supported() (ok bool, err error)
|
|
||||||
PatchLoggerLevel(level log.Level)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Addresser interface {
|
|
||||||
AddrList(link netlink.Link, family int) (
|
AddrList(link netlink.Link, family int) (
|
||||||
addresses []netlink.Addr, err error)
|
addresses []netlink.Addr, err error)
|
||||||
AddrAdd(link netlink.Link, addr *netlink.Addr) error
|
AddrAdd(link netlink.Link, addr *netlink.Addr) error
|
||||||
}
|
IsWireguardSupported() (ok bool, err error)
|
||||||
|
|
||||||
type Router interface {
|
|
||||||
RouteList(link netlink.Link, family int) (
|
RouteList(link netlink.Link, family int) (
|
||||||
routes []netlink.Route, err error)
|
routes []netlink.Route, err error)
|
||||||
RouteAdd(route *netlink.Route) error
|
RouteAdd(route *netlink.Route) error
|
||||||
RouteDel(route *netlink.Route) error
|
RouteDel(route *netlink.Route) error
|
||||||
RouteReplace(route *netlink.Route) error
|
RouteReplace(route *netlink.Route) error
|
||||||
}
|
|
||||||
|
|
||||||
type Ruler interface {
|
|
||||||
RuleList(family int) (rules []netlink.Rule, err error)
|
RuleList(family int) (rules []netlink.Rule, err error)
|
||||||
RuleAdd(rule *netlink.Rule) error
|
RuleAdd(rule *netlink.Rule) error
|
||||||
RuleDel(rule *netlink.Rule) error
|
RuleDel(rule *netlink.Rule) error
|
||||||
}
|
|
||||||
|
|
||||||
type Linker interface {
|
|
||||||
LinkList() (links []netlink.Link, err error)
|
LinkList() (links []netlink.Link, err error)
|
||||||
LinkByName(name string) (link netlink.Link, err error)
|
LinkByName(name string) (link netlink.Link, err error)
|
||||||
LinkByIndex(index int) (link netlink.Link, err error)
|
LinkByIndex(index int) (link netlink.Link, err error)
|
||||||
@@ -556,8 +513,8 @@ type Linker interface {
|
|||||||
type clier interface {
|
type clier interface {
|
||||||
ClientKey(args []string) error
|
ClientKey(args []string) error
|
||||||
FormatServers(args []string) error
|
FormatServers(args []string) error
|
||||||
OpenvpnConfig(logger cli.OpenvpnConfigLogger, source cli.Source, ipv6Checker cli.IPv6Checker) error
|
OpenvpnConfig(logger cli.OpenvpnConfigLogger, source sources.Source) error
|
||||||
HealthCheck(ctx context.Context, source cli.Source, warner cli.Warner) error
|
HealthCheck(ctx context.Context, source sources.Source, warner cli.Warner) error
|
||||||
Update(ctx context.Context, args []string, logger cli.UpdaterLogger) error
|
Update(ctx context.Context, args []string, logger cli.UpdaterLogger) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,9 +522,3 @@ type Tun interface {
|
|||||||
Check(tunDevice string) error
|
Check(tunDevice string) error
|
||||||
Create(tunDevice string) error
|
Create(tunDevice string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type Source interface {
|
|
||||||
Read() (settings settings.Settings, err error)
|
|
||||||
ReadHealth() (health settings.Health, err error)
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|||||||
38
go.mod
38
go.mod
@@ -1,10 +1,10 @@
|
|||||||
module github.com/qdm12/gluetun
|
module github.com/qdm12/gluetun
|
||||||
|
|
||||||
go 1.20
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/breml/rootcerts v0.2.10
|
github.com/breml/rootcerts v0.2.3
|
||||||
github.com/fatih/color v1.14.1
|
github.com/fatih/color v1.13.0
|
||||||
github.com/golang/mock v1.6.0
|
github.com/golang/mock v1.6.0
|
||||||
github.com/qdm12/dns v1.11.0
|
github.com/qdm12/dns v1.11.0
|
||||||
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6
|
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6
|
||||||
@@ -15,35 +15,31 @@ require (
|
|||||||
github.com/qdm12/log v0.1.0
|
github.com/qdm12/log v0.1.0
|
||||||
github.com/qdm12/ss-server v0.4.0
|
github.com/qdm12/ss-server v0.4.0
|
||||||
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
|
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
|
||||||
github.com/stretchr/testify v1.8.2
|
github.com/stretchr/testify v1.7.2
|
||||||
github.com/vishvananda/netlink v1.1.1-0.20211129163951-9ada19101fc5
|
github.com/vishvananda/netlink v1.1.1-0.20211129163951-9ada19101fc5
|
||||||
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||||
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2
|
golang.org/x/text v0.3.7
|
||||||
golang.org/x/sys v0.6.0
|
golang.zx2c4.com/wireguard v0.0.0-20210805125648-3957e9b9dd19
|
||||||
golang.org/x/text v0.8.0
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478
|
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220504211119-3d4a969bb56b
|
|
||||||
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722
|
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/google/go-cmp v0.5.7 // indirect
|
github.com/google/go-cmp v0.5.5 // indirect
|
||||||
github.com/josharian/native v1.0.0 // indirect
|
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
github.com/mdlayher/genetlink v1.2.0 // indirect
|
github.com/mdlayher/genetlink v1.0.0 // indirect
|
||||||
github.com/mdlayher/netlink v1.6.0 // indirect
|
github.com/mdlayher/netlink v1.4.0 // indirect
|
||||||
github.com/mdlayher/socket v0.2.3 // indirect
|
|
||||||
github.com/miekg/dns v1.1.40 // indirect
|
github.com/miekg/dns v1.1.40 // indirect
|
||||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
|
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
|
||||||
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
|
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230221090011-e4bae7ad2296 // indirect
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
135
go.sum
135
go.sum
@@ -4,8 +4,8 @@ github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/g
|
|||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
github.com/breml/rootcerts v0.2.10 h1:UGVZ193UTSUASpGtg6pbDwzOd7XQP+at0Ssg1/2E4h8=
|
github.com/breml/rootcerts v0.2.3 h1:1vkYjKOiHVSyuz9Ue4AOrViEvUm8gk8phTg0vbcuU0A=
|
||||||
github.com/breml/rootcerts v0.2.10/go.mod h1:24FDtzYMpqIeYC7QzaE8VPRQaFZU5TIUDlyk8qwjD88=
|
github.com/breml/rootcerts v0.2.3/go.mod h1:24FDtzYMpqIeYC7QzaE8VPRQaFZU5TIUDlyk8qwjD88=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
@@ -14,8 +14,8 @@ github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
|
|||||||
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
@@ -36,16 +36,28 @@ github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3K
|
|||||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
|
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
|
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA=
|
||||||
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b h1:c3NTyLNozICy8B4mlMXemD3z/gXgQzVXZS/HqT+i3do=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U=
|
||||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
@@ -56,22 +68,29 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|||||||
github.com/kyokomi/emoji v2.2.4+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
|
github.com/kyokomi/emoji v2.2.4+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
|
||||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JAQI/ZLUkCZ7VJS3r74hwFIGXJsgZlY=
|
||||||
github.com/mdlayher/genetlink v1.2.0 h1:4yrIkRV5Wfk1WfpWTcoOlGmsWgQj3OtQN9ZsbrE+XtU=
|
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
|
||||||
github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ=
|
github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0=
|
||||||
github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0=
|
github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=
|
||||||
github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
|
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||||
github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
|
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||||
github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM=
|
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
|
||||||
github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY=
|
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
|
||||||
|
github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8=
|
||||||
|
github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
|
||||||
|
github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
|
||||||
|
github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys=
|
||||||
|
github.com/mdlayher/netlink v1.4.0 h1:n3ARR+Fm0dDv37dj5wSWZXDKcy+U0zwcXS3zKMnSiT0=
|
||||||
|
github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8=
|
||||||
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
|
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
|
||||||
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
||||||
|
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||||
@@ -108,92 +127,105 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm
|
|||||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
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 h1:b/k/BVWzWRS5v6AB0gf2ckFSbFsHN5jR0HoNso1pN+w=
|
||||||
github.com/vishvananda/netlink v1.1.1-0.20211129163951-9ada19101fc5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
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=
|
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns=
|
||||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||||
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
|
|
||||||
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
|
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg=
|
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg=
|
||||||
go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc=
|
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-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
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-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230221090011-e4bae7ad2296 h1:QJ/xcIANMLApehfgPCHnfK1hZiaMmbaTVmPv7DAoTbo=
|
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-20230221090011-e4bae7ad2296/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
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-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
|
golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
|
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d h1:nTDGCTeAu2LhcsHTRzjyIUbZHCJ4QePArsm27Hka0UM=
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 h1:6mzvA99KwZxbOrxww4EvWVQUnN1+xEu9tafK5ZxkYeA=
|
|
||||||
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/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/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
@@ -205,12 +237,11 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
|
|||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
|
golang.zx2c4.com/wireguard v0.0.0-20210427022245-097af6e1351b/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wireguard v0.0.0-20210805125648-3957e9b9dd19 h1:ab2jcw2W91Rz07eHAb8Lic7sFQKO0NhBftjv6m/gL/0=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478 h1:vDy//hdR+GnROE3OdYbQKt9rdtNdHkDtONvpRwmls/0=
|
golang.zx2c4.com/wireguard v0.0.0-20210805125648-3957e9b9dd19/go.mod h1:laHzsbfMhGSobUmruXWAyMKKHSqvIcrqZJMyHD+/3O8=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478/go.mod h1:bVQfyl2sCM/QIIGHpWbFGfHPuDvqnCNkT6MQLTCjO/U=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c h1:ADNrRDI5NR23/TUCnEmlLZLt4u9DnZ2nwRkPrAcFvto=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220504211119-3d4a969bb56b h1:9JncmKXcUwE918my+H6xmjBdhK2jM/UTUNXxhRG1BAk=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c/go.mod h1:+1XihzyZUBJcSc5WO9SwNA7v26puQwOEDwanaxfNXPQ=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220504211119-3d4a969bb56b/go.mod h1:yp4gl6zOlnDGOZeWeDfMwQcsdOIQnMdhuPx9mwwWBL4=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ func (c *CLI) FormatServers(args []string) error {
|
|||||||
logger := newNoopLogger()
|
logger := newNoopLogger()
|
||||||
storage, err := storage.New(logger, constants.ServersData)
|
storage, err := storage.New(logger, constants.ServersData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating servers storage: %w", err)
|
return fmt.Errorf("cannot create servers storage: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
formatted := storage.FormatToMarkdown(providerToFormat)
|
formatted := storage.FormatToMarkdown(providerToFormat)
|
||||||
@@ -81,18 +81,18 @@ func (c *CLI) FormatServers(args []string) error {
|
|||||||
output = filepath.Clean(output)
|
output = filepath.Clean(output)
|
||||||
file, err := os.OpenFile(output, os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0644)
|
file, err := os.OpenFile(output, os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("opening output file: %w", err)
|
return fmt.Errorf("cannot open output file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = fmt.Fprint(file, formatted)
|
_, err = fmt.Fprint(file, formatted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = file.Close()
|
_ = file.Close()
|
||||||
return fmt.Errorf("writing to output file: %w", err)
|
return fmt.Errorf("cannot write to output file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = file.Close()
|
err = file.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("closing output file: %w", err)
|
return fmt.Errorf("cannot close output file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -6,18 +6,17 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration/sources"
|
||||||
"github.com/qdm12/gluetun/internal/healthcheck"
|
"github.com/qdm12/gluetun/internal/healthcheck"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *CLI) HealthCheck(ctx context.Context, source 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.
|
// Extract the health server port from the configuration.
|
||||||
config, err := source.ReadHealth()
|
config, err := source.ReadHealth()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
config.SetDefaults()
|
|
||||||
|
|
||||||
err = config.Validate()
|
err = config.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration/sources"
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/openvpn/extract"
|
"github.com/qdm12/gluetun/internal/openvpn/extract"
|
||||||
"github.com/qdm12/gluetun/internal/provider"
|
"github.com/qdm12/gluetun/internal/provider"
|
||||||
@@ -35,12 +36,7 @@ type IPFetcher interface {
|
|||||||
FetchMultiInfo(ctx context.Context, ips []net.IP) (data []ipinfo.Response, err error)
|
FetchMultiInfo(ctx context.Context, ips []net.IP) (data []ipinfo.Response, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type IPv6Checker interface {
|
func (c *CLI) OpenvpnConfig(logger OpenvpnConfigLogger, source sources.Source) error {
|
||||||
IsIPv6Supported() (supported bool, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CLI) OpenvpnConfig(logger OpenvpnConfigLogger, source Source,
|
|
||||||
ipv6Checker IPv6Checker) error {
|
|
||||||
storage, err := storage.New(logger, constants.ServersData)
|
storage, err := storage.New(logger, constants.ServersData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -51,13 +47,8 @@ func (c *CLI) OpenvpnConfig(logger OpenvpnConfigLogger, source Source,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ipv6Supported, err := ipv6Checker.IsIPv6Supported()
|
if err = allSettings.Validate(storage); err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return fmt.Errorf("checking for IPv6 support: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = allSettings.Validate(storage, ipv6Supported); err != nil {
|
|
||||||
return fmt.Errorf("validating settings: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unused by this CLI command
|
// Unused by this CLI command
|
||||||
@@ -71,14 +62,12 @@ func (c *CLI) OpenvpnConfig(logger OpenvpnConfigLogger, source Source,
|
|||||||
providers := provider.NewProviders(storage, time.Now, warner, client,
|
providers := provider.NewProviders(storage, time.Now, warner, client,
|
||||||
unzipper, parallelResolver, ipFetcher, openvpnFileExtractor)
|
unzipper, parallelResolver, ipFetcher, openvpnFileExtractor)
|
||||||
providerConf := providers.Get(*allSettings.VPN.Provider.Name)
|
providerConf := providers.Get(*allSettings.VPN.Provider.Name)
|
||||||
connection, err := providerConf.GetConnection(
|
connection, err := providerConf.GetConnection(allSettings.VPN.Provider.ServerSelection)
|
||||||
allSettings.VPN.Provider.ServerSelection, ipv6Supported)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
lines := providerConf.OpenVPNConfig(connection,
|
lines := providerConf.OpenVPNConfig(connection, allSettings.VPN.OpenVPN)
|
||||||
allSettings.VPN.OpenVPN, ipv6Supported)
|
|
||||||
|
|
||||||
fmt.Println(strings.Join(lines, "\n"))
|
fmt.Println(strings.Join(lines, "\n"))
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) e
|
|||||||
|
|
||||||
storage, err := storage.New(logger, constants.ServersData)
|
storage, err := storage.New(logger, constants.ServersData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating servers storage: %w", err)
|
return fmt.Errorf("cannot create servers storage: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientTimeout = 10 * time.Second
|
const clientTimeout = 10 * time.Second
|
||||||
@@ -88,13 +88,13 @@ func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) e
|
|||||||
updater := updater.New(httpClient, storage, providers, logger)
|
updater := updater.New(httpClient, storage, providers, logger)
|
||||||
err = updater.UpdateServers(ctx, options.Providers, options.MinRatio)
|
err = updater.UpdateServers(ctx, options.Providers, options.MinRatio)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("updating server information: %w", err)
|
return fmt.Errorf("cannot update server information: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if maintainerMode {
|
if maintainerMode {
|
||||||
err := storage.FlushToFile(c.repoServersPath)
|
err := storage.FlushToFile(c.repoServersPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("writing servers data to embedded JSON file: %w", err)
|
return fmt.Errorf("cannot write servers data to embedded JSON file: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ type DNS struct {
|
|||||||
func (d DNS) validate() (err error) {
|
func (d DNS) validate() (err error) {
|
||||||
err = d.DoT.validate()
|
err = d.DoT.validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("validating DoT settings: %w", err)
|
return fmt.Errorf("failed validating DoT settings: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ func (d *DoT) copy() (copied DoT) {
|
|||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (d *DoT) mergeWith(other DoT) {
|
func (d *DoT) mergeWith(other DoT) {
|
||||||
d.Enabled = helpers.MergeWithBool(d.Enabled, other.Enabled)
|
d.Enabled = helpers.MergeWithBool(d.Enabled, other.Enabled)
|
||||||
d.UpdatePeriod = helpers.MergeWithDurationPtr(d.UpdatePeriod, other.UpdatePeriod)
|
d.UpdatePeriod = helpers.MergeWithDuration(d.UpdatePeriod, other.UpdatePeriod)
|
||||||
d.Unbound.mergeWith(other.Unbound)
|
d.Unbound.mergeWith(other.Unbound)
|
||||||
d.Blacklist.mergeWith(other.Blacklist)
|
d.Blacklist.mergeWith(other.Blacklist)
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ func (d *DoT) mergeWith(other DoT) {
|
|||||||
// settings.
|
// settings.
|
||||||
func (d *DoT) overrideWith(other DoT) {
|
func (d *DoT) overrideWith(other DoT) {
|
||||||
d.Enabled = helpers.OverrideWithBool(d.Enabled, other.Enabled)
|
d.Enabled = helpers.OverrideWithBool(d.Enabled, other.Enabled)
|
||||||
d.UpdatePeriod = helpers.OverrideWithDurationPtr(d.UpdatePeriod, other.UpdatePeriod)
|
d.UpdatePeriod = helpers.OverrideWithDuration(d.UpdatePeriod, other.UpdatePeriod)
|
||||||
d.Unbound.overrideWith(other.Unbound)
|
d.Unbound.overrideWith(other.Unbound)
|
||||||
d.Blacklist.overrideWith(other.Blacklist)
|
d.Blacklist.overrideWith(other.Blacklist)
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ func (d *DoT) overrideWith(other DoT) {
|
|||||||
func (d *DoT) setDefaults() {
|
func (d *DoT) setDefaults() {
|
||||||
d.Enabled = helpers.DefaultBool(d.Enabled, true)
|
d.Enabled = helpers.DefaultBool(d.Enabled, true)
|
||||||
const defaultUpdatePeriod = 24 * time.Hour
|
const defaultUpdatePeriod = 24 * time.Hour
|
||||||
d.UpdatePeriod = helpers.DefaultDurationPtr(d.UpdatePeriod, defaultUpdatePeriod)
|
d.UpdatePeriod = helpers.DefaultDuration(d.UpdatePeriod, defaultUpdatePeriod)
|
||||||
d.Unbound.setDefaults()
|
d.Unbound.setDefaults()
|
||||||
d.Blacklist.setDefaults()
|
d.Blacklist.setDefaults()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ var (
|
|||||||
ErrOpenVPNCustomPortNotAllowed = errors.New("custom endpoint port is not allowed")
|
ErrOpenVPNCustomPortNotAllowed = errors.New("custom endpoint port is not allowed")
|
||||||
ErrOpenVPNEncryptionPresetNotValid = errors.New("PIA encryption preset is not valid")
|
ErrOpenVPNEncryptionPresetNotValid = errors.New("PIA encryption preset is not valid")
|
||||||
ErrOpenVPNInterfaceNotValid = errors.New("interface name is not valid")
|
ErrOpenVPNInterfaceNotValid = errors.New("interface name is not valid")
|
||||||
ErrOpenVPNKeyPassphraseIsEmpty = errors.New("key passphrase is empty")
|
|
||||||
ErrOpenVPNMSSFixIsTooHigh = errors.New("mssfix option value is too high")
|
ErrOpenVPNMSSFixIsTooHigh = errors.New("mssfix option value is too high")
|
||||||
ErrOpenVPNPasswordIsEmpty = errors.New("password is empty")
|
ErrOpenVPNPasswordIsEmpty = errors.New("password is empty")
|
||||||
ErrOpenVPNTCPNotSupported = errors.New("TCP protocol is not supported")
|
ErrOpenVPNTCPNotSupported = errors.New("TCP protocol is not supported")
|
||||||
@@ -37,13 +36,10 @@ var (
|
|||||||
ErrWireguardEndpointIPNotSet = errors.New("endpoint IP is not set")
|
ErrWireguardEndpointIPNotSet = errors.New("endpoint IP is not set")
|
||||||
ErrWireguardEndpointPortNotAllowed = errors.New("endpoint port is not allowed")
|
ErrWireguardEndpointPortNotAllowed = errors.New("endpoint port is not allowed")
|
||||||
ErrWireguardEndpointPortNotSet = errors.New("endpoint port is not set")
|
ErrWireguardEndpointPortNotSet = errors.New("endpoint port is not set")
|
||||||
ErrWireguardEndpointPortSet = errors.New("endpoint port is set")
|
|
||||||
ErrWireguardInterfaceAddressNotSet = errors.New("interface address is not set")
|
ErrWireguardInterfaceAddressNotSet = errors.New("interface address is not set")
|
||||||
ErrWireguardInterfaceAddressIPv6 = errors.New("interface address is IPv6 but IPv6 is not supported")
|
|
||||||
ErrWireguardInterfaceNotValid = errors.New("interface name is not valid")
|
ErrWireguardInterfaceNotValid = errors.New("interface name is not valid")
|
||||||
ErrWireguardPreSharedKeyNotSet = errors.New("pre-shared key is not set")
|
ErrWireguardPreSharedKeyNotSet = errors.New("pre-shared key is not set")
|
||||||
ErrWireguardPrivateKeyNotSet = errors.New("private key is not set")
|
ErrWireguardPrivateKeyNotSet = errors.New("private key is not set")
|
||||||
ErrWireguardPublicKeyNotSet = errors.New("public key is not set")
|
ErrWireguardPublicKeyNotSet = errors.New("public key is not set")
|
||||||
ErrWireguardPublicKeyNotValid = errors.New("public key is not valid")
|
ErrWireguardPublicKeyNotValid = errors.New("public key is not valid")
|
||||||
ErrWireguardImplementationNotValid = errors.New("implementation is not valid")
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -109,8 +109,7 @@ func (f Firewall) toLinesNode() (node *gotree.Node) {
|
|||||||
if len(f.OutboundSubnets) > 0 {
|
if len(f.OutboundSubnets) > 0 {
|
||||||
outboundSubnets := node.Appendf("Outbound subnets:")
|
outboundSubnets := node.Appendf("Outbound subnets:")
|
||||||
for _, subnet := range f.OutboundSubnets {
|
for _, subnet := range f.OutboundSubnets {
|
||||||
subnet := subnet
|
outboundSubnets.Appendf("%s", subnet)
|
||||||
outboundSubnets.Appendf("%s", &subnet)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package settings
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
@@ -16,12 +15,6 @@ type Health struct {
|
|||||||
// for the health check server.
|
// for the health check server.
|
||||||
// It cannot be the empty string in the internal state.
|
// It cannot be the empty string in the internal state.
|
||||||
ServerAddress string
|
ServerAddress string
|
||||||
// ReadHeaderTimeout is the HTTP server header read timeout
|
|
||||||
// duration of the HTTP server. It defaults to 100 milliseconds.
|
|
||||||
ReadHeaderTimeout time.Duration
|
|
||||||
// ReadTimeout is the HTTP read timeout duration of the
|
|
||||||
// HTTP server. It defaults to 500 milliseconds.
|
|
||||||
ReadTimeout time.Duration
|
|
||||||
// TargetAddress is the address (host or host:port)
|
// TargetAddress is the address (host or host:port)
|
||||||
// to TCP dial to periodically for the health check.
|
// to TCP dial to periodically for the health check.
|
||||||
// It cannot be the empty string in the internal state.
|
// It cannot be the empty string in the internal state.
|
||||||
@@ -47,11 +40,9 @@ func (h Health) Validate() (err error) {
|
|||||||
|
|
||||||
func (h *Health) copy() (copied Health) {
|
func (h *Health) copy() (copied Health) {
|
||||||
return Health{
|
return Health{
|
||||||
ServerAddress: h.ServerAddress,
|
ServerAddress: h.ServerAddress,
|
||||||
ReadHeaderTimeout: h.ReadHeaderTimeout,
|
TargetAddress: h.TargetAddress,
|
||||||
ReadTimeout: h.ReadTimeout,
|
VPN: h.VPN.copy(),
|
||||||
TargetAddress: h.TargetAddress,
|
|
||||||
VPN: h.VPN.copy(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,8 +50,6 @@ func (h *Health) copy() (copied Health) {
|
|||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (h *Health) MergeWith(other Health) {
|
func (h *Health) MergeWith(other Health) {
|
||||||
h.ServerAddress = helpers.MergeWithString(h.ServerAddress, other.ServerAddress)
|
h.ServerAddress = helpers.MergeWithString(h.ServerAddress, other.ServerAddress)
|
||||||
h.ReadHeaderTimeout = helpers.MergeWithDuration(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
|
||||||
h.ReadTimeout = helpers.MergeWithDuration(h.ReadTimeout, other.ReadTimeout)
|
|
||||||
h.TargetAddress = helpers.MergeWithString(h.TargetAddress, other.TargetAddress)
|
h.TargetAddress = helpers.MergeWithString(h.TargetAddress, other.TargetAddress)
|
||||||
h.VPN.mergeWith(other.VPN)
|
h.VPN.mergeWith(other.VPN)
|
||||||
}
|
}
|
||||||
@@ -70,18 +59,12 @@ func (h *Health) MergeWith(other Health) {
|
|||||||
// settings.
|
// settings.
|
||||||
func (h *Health) OverrideWith(other Health) {
|
func (h *Health) OverrideWith(other Health) {
|
||||||
h.ServerAddress = helpers.OverrideWithString(h.ServerAddress, other.ServerAddress)
|
h.ServerAddress = helpers.OverrideWithString(h.ServerAddress, other.ServerAddress)
|
||||||
h.ReadHeaderTimeout = helpers.OverrideWithDuration(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
|
||||||
h.ReadTimeout = helpers.OverrideWithDuration(h.ReadTimeout, other.ReadTimeout)
|
|
||||||
h.TargetAddress = helpers.OverrideWithString(h.TargetAddress, other.TargetAddress)
|
h.TargetAddress = helpers.OverrideWithString(h.TargetAddress, other.TargetAddress)
|
||||||
h.VPN.overrideWith(other.VPN)
|
h.VPN.overrideWith(other.VPN)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Health) SetDefaults() {
|
func (h *Health) SetDefaults() {
|
||||||
h.ServerAddress = helpers.DefaultString(h.ServerAddress, "127.0.0.1:9999")
|
h.ServerAddress = helpers.DefaultString(h.ServerAddress, "127.0.0.1:9999")
|
||||||
const defaultReadHeaderTimeout = 100 * time.Millisecond
|
|
||||||
h.ReadHeaderTimeout = helpers.DefaultDuration(h.ReadHeaderTimeout, defaultReadHeaderTimeout)
|
|
||||||
const defaultReadTimeout = 500 * time.Millisecond
|
|
||||||
h.ReadTimeout = helpers.DefaultDuration(h.ReadTimeout, defaultReadTimeout)
|
|
||||||
h.TargetAddress = helpers.DefaultString(h.TargetAddress, "cloudflare.com:443")
|
h.TargetAddress = helpers.DefaultString(h.TargetAddress, "cloudflare.com:443")
|
||||||
h.VPN.setDefaults()
|
h.VPN.setDefaults()
|
||||||
}
|
}
|
||||||
@@ -94,8 +77,6 @@ func (h Health) toLinesNode() (node *gotree.Node) {
|
|||||||
node = gotree.New("Health settings:")
|
node = gotree.New("Health settings:")
|
||||||
node.Appendf("Server listening address: %s", h.ServerAddress)
|
node.Appendf("Server listening address: %s", h.ServerAddress)
|
||||||
node.Appendf("Target address: %s", h.TargetAddress)
|
node.Appendf("Target address: %s", h.TargetAddress)
|
||||||
node.Appendf("Read header timeout: %s", h.ReadHeaderTimeout)
|
|
||||||
node.Appendf("Read timeout: %s", h.ReadTimeout)
|
|
||||||
node.AppendNode(h.VPN.toLinesNode("VPN"))
|
node.AppendNode(h.VPN.toLinesNode("VPN"))
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,23 +35,23 @@ func (h *HealthyWait) copy() (copied HealthyWait) {
|
|||||||
// mergeWith merges the other settings into any
|
// mergeWith merges the other settings into any
|
||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (h *HealthyWait) mergeWith(other HealthyWait) {
|
func (h *HealthyWait) mergeWith(other HealthyWait) {
|
||||||
h.Initial = helpers.MergeWithDurationPtr(h.Initial, other.Initial)
|
h.Initial = helpers.MergeWithDuration(h.Initial, other.Initial)
|
||||||
h.Addition = helpers.MergeWithDurationPtr(h.Addition, other.Addition)
|
h.Addition = helpers.MergeWithDuration(h.Addition, other.Addition)
|
||||||
}
|
}
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (h *HealthyWait) overrideWith(other HealthyWait) {
|
func (h *HealthyWait) overrideWith(other HealthyWait) {
|
||||||
h.Initial = helpers.OverrideWithDurationPtr(h.Initial, other.Initial)
|
h.Initial = helpers.OverrideWithDuration(h.Initial, other.Initial)
|
||||||
h.Addition = helpers.OverrideWithDurationPtr(h.Addition, other.Addition)
|
h.Addition = helpers.OverrideWithDuration(h.Addition, other.Addition)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HealthyWait) setDefaults() {
|
func (h *HealthyWait) setDefaults() {
|
||||||
const initialDurationDefault = 6 * time.Second
|
const initialDurationDefault = 6 * time.Second
|
||||||
const additionDurationDefault = 5 * time.Second
|
const additionDurationDefault = 5 * time.Second
|
||||||
h.Initial = helpers.DefaultDurationPtr(h.Initial, initialDurationDefault)
|
h.Initial = helpers.DefaultDuration(h.Initial, initialDurationDefault)
|
||||||
h.Addition = helpers.DefaultDurationPtr(h.Addition, additionDurationDefault)
|
h.Addition = helpers.DefaultDuration(h.Addition, additionDurationDefault)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h HealthyWait) String() string {
|
func (h HealthyWait) String() string {
|
||||||
|
|||||||
@@ -73,15 +73,7 @@ func DefaultStringPtr(existing *string, defaultValue string) (result *string) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultDuration(existing time.Duration,
|
func DefaultDuration(existing *time.Duration,
|
||||||
defaultValue time.Duration) (result time.Duration) {
|
|
||||||
if existing != 0 {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func DefaultDurationPtr(existing *time.Duration,
|
|
||||||
defaultValue time.Duration) (result *time.Duration) {
|
defaultValue time.Duration) (result *time.Duration) {
|
||||||
if existing != nil {
|
if existing != nil {
|
||||||
return existing
|
return existing
|
||||||
|
|||||||
@@ -107,14 +107,7 @@ func MergeWithIP(existing, other net.IP) (result net.IP) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func MergeWithDuration(existing, other time.Duration) (result time.Duration) {
|
func MergeWithDuration(existing, other *time.Duration) (result *time.Duration) {
|
||||||
if existing != 0 {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
return other
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeWithDurationPtr(existing, other *time.Duration) (result *time.Duration) {
|
|
||||||
if existing != nil {
|
if existing != nil {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,16 +93,7 @@ func OverrideWithIP(existing, other net.IP) (result net.IP) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func OverrideWithDuration(existing, other time.Duration) (
|
func OverrideWithDuration(existing, other *time.Duration) (result *time.Duration) {
|
||||||
result time.Duration) {
|
|
||||||
if other == 0 {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
return other
|
|
||||||
}
|
|
||||||
|
|
||||||
func OverrideWithDurationPtr(existing, other *time.Duration) (
|
|
||||||
result *time.Duration) {
|
|
||||||
if other == nil {
|
if other == nil {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package settings
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
@@ -34,12 +33,6 @@ type HTTPProxy struct {
|
|||||||
// each request/response. It cannot be nil in the
|
// each request/response. It cannot be nil in the
|
||||||
// internal state.
|
// internal state.
|
||||||
Log *bool
|
Log *bool
|
||||||
// ReadHeaderTimeout is the HTTP header read timeout duration
|
|
||||||
// of the HTTP server. It defaults to 1 second if left unset.
|
|
||||||
ReadHeaderTimeout time.Duration
|
|
||||||
// ReadTimeout is the HTTP read timeout duration
|
|
||||||
// of the HTTP server. It defaults to 3 seconds if left unset.
|
|
||||||
ReadTimeout time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h HTTPProxy) validate() (err error) {
|
func (h HTTPProxy) validate() (err error) {
|
||||||
@@ -56,14 +49,12 @@ func (h HTTPProxy) validate() (err error) {
|
|||||||
|
|
||||||
func (h *HTTPProxy) copy() (copied HTTPProxy) {
|
func (h *HTTPProxy) copy() (copied HTTPProxy) {
|
||||||
return HTTPProxy{
|
return HTTPProxy{
|
||||||
User: helpers.CopyStringPtr(h.User),
|
User: helpers.CopyStringPtr(h.User),
|
||||||
Password: helpers.CopyStringPtr(h.Password),
|
Password: helpers.CopyStringPtr(h.Password),
|
||||||
ListeningAddress: h.ListeningAddress,
|
ListeningAddress: h.ListeningAddress,
|
||||||
Enabled: helpers.CopyBoolPtr(h.Enabled),
|
Enabled: helpers.CopyBoolPtr(h.Enabled),
|
||||||
Stealth: helpers.CopyBoolPtr(h.Stealth),
|
Stealth: helpers.CopyBoolPtr(h.Stealth),
|
||||||
Log: helpers.CopyBoolPtr(h.Log),
|
Log: helpers.CopyBoolPtr(h.Log),
|
||||||
ReadHeaderTimeout: h.ReadHeaderTimeout,
|
|
||||||
ReadTimeout: h.ReadTimeout,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,8 +67,6 @@ func (h *HTTPProxy) mergeWith(other HTTPProxy) {
|
|||||||
h.Enabled = helpers.MergeWithBool(h.Enabled, other.Enabled)
|
h.Enabled = helpers.MergeWithBool(h.Enabled, other.Enabled)
|
||||||
h.Stealth = helpers.MergeWithBool(h.Stealth, other.Stealth)
|
h.Stealth = helpers.MergeWithBool(h.Stealth, other.Stealth)
|
||||||
h.Log = helpers.MergeWithBool(h.Log, other.Log)
|
h.Log = helpers.MergeWithBool(h.Log, other.Log)
|
||||||
h.ReadHeaderTimeout = helpers.MergeWithDuration(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
|
||||||
h.ReadTimeout = helpers.MergeWithDuration(h.ReadTimeout, other.ReadTimeout)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
@@ -90,8 +79,6 @@ func (h *HTTPProxy) overrideWith(other HTTPProxy) {
|
|||||||
h.Enabled = helpers.OverrideWithBool(h.Enabled, other.Enabled)
|
h.Enabled = helpers.OverrideWithBool(h.Enabled, other.Enabled)
|
||||||
h.Stealth = helpers.OverrideWithBool(h.Stealth, other.Stealth)
|
h.Stealth = helpers.OverrideWithBool(h.Stealth, other.Stealth)
|
||||||
h.Log = helpers.OverrideWithBool(h.Log, other.Log)
|
h.Log = helpers.OverrideWithBool(h.Log, other.Log)
|
||||||
h.ReadHeaderTimeout = helpers.OverrideWithDuration(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
|
||||||
h.ReadTimeout = helpers.OverrideWithDuration(h.ReadTimeout, other.ReadTimeout)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HTTPProxy) setDefaults() {
|
func (h *HTTPProxy) setDefaults() {
|
||||||
@@ -101,10 +88,6 @@ func (h *HTTPProxy) setDefaults() {
|
|||||||
h.Enabled = helpers.DefaultBool(h.Enabled, false)
|
h.Enabled = helpers.DefaultBool(h.Enabled, false)
|
||||||
h.Stealth = helpers.DefaultBool(h.Stealth, false)
|
h.Stealth = helpers.DefaultBool(h.Stealth, false)
|
||||||
h.Log = helpers.DefaultBool(h.Log, false)
|
h.Log = helpers.DefaultBool(h.Log, false)
|
||||||
const defaultReadHeaderTimeout = time.Second
|
|
||||||
h.ReadHeaderTimeout = helpers.DefaultDuration(h.ReadHeaderTimeout, defaultReadHeaderTimeout)
|
|
||||||
const defaultReadTimeout = 3 * time.Second
|
|
||||||
h.ReadTimeout = helpers.DefaultDuration(h.ReadTimeout, defaultReadTimeout)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h HTTPProxy) String() string {
|
func (h HTTPProxy) String() string {
|
||||||
@@ -123,8 +106,6 @@ func (h HTTPProxy) toLinesNode() (node *gotree.Node) {
|
|||||||
node.Appendf("Password: %s", helpers.ObfuscatePassword(*h.Password))
|
node.Appendf("Password: %s", helpers.ObfuscatePassword(*h.Password))
|
||||||
node.Appendf("Stealth mode: %s", helpers.BoolPtrToYesNo(h.Stealth))
|
node.Appendf("Stealth mode: %s", helpers.BoolPtrToYesNo(h.Stealth))
|
||||||
node.Appendf("Log: %s", helpers.BoolPtrToYesNo(h.Log))
|
node.Appendf("Log: %s", helpers.BoolPtrToYesNo(h.Log))
|
||||||
node.Appendf("Read header timeout: %s", h.ReadHeaderTimeout)
|
|
||||||
node.Appendf("Read timeout: %s", h.ReadTimeout)
|
|
||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,15 +20,13 @@ type OpenVPN struct {
|
|||||||
// It can only be "2.4" or "2.5".
|
// It can only be "2.4" or "2.5".
|
||||||
Version string
|
Version string
|
||||||
// User is the OpenVPN authentication username.
|
// User is the OpenVPN authentication username.
|
||||||
// It cannot be nil in the internal state if OpenVPN is used.
|
// It cannot be an empty string in the internal state
|
||||||
// It is usually required but in some cases can be the empty string
|
// if OpenVPN is used.
|
||||||
// to indicate no user+password authentication is needed.
|
User string
|
||||||
User *string
|
|
||||||
// Password is the OpenVPN authentication password.
|
// Password is the OpenVPN authentication password.
|
||||||
// It cannot be nil in the internal state if OpenVPN is used.
|
// It cannot be an empty string in the internal state
|
||||||
// It is usually required but in some cases can be the empty string
|
// if OpenVPN is used.
|
||||||
// to indicate no user+password authentication is needed.
|
Password string
|
||||||
Password *string
|
|
||||||
// ConfFile is a custom OpenVPN configuration file path.
|
// ConfFile is a custom OpenVPN configuration file path.
|
||||||
// It can be set to the empty string for it to be ignored.
|
// It can be set to the empty string for it to be ignored.
|
||||||
// It cannot be nil in the internal state.
|
// It cannot be nil in the internal state.
|
||||||
@@ -42,29 +40,24 @@ type OpenVPN struct {
|
|||||||
// It cannot be nil in the internal state.
|
// It cannot be nil in the internal state.
|
||||||
// It is ignored if it is set to the empty string.
|
// It is ignored if it is set to the empty string.
|
||||||
Auth *string
|
Auth *string
|
||||||
// Cert is the base64 encoded DER of an OpenVPN certificate for the <cert> block.
|
// ClientCrt is the OpenVPN client certificate.
|
||||||
// This is notably used by Cyberghost and VPN secure.
|
// This is notably used by Cyberghost.
|
||||||
// It can be set to the empty string to be ignored.
|
// It can be set to the empty string to be ignored.
|
||||||
// It cannot be nil in the internal state.
|
// It cannot be nil in the internal state.
|
||||||
Cert *string
|
ClientCrt *string
|
||||||
// Key is the base64 encoded DER of an OpenVPN key.
|
// ClientKey is the OpenVPN client key.
|
||||||
// This is used by Cyberghost and VPN Unlimited.
|
// This is used by Cyberghost and VPN Unlimited.
|
||||||
// It can be set to the empty string to be ignored.
|
// It can be set to the empty string to be ignored.
|
||||||
// It cannot be nil in the internal state.
|
// It cannot be nil in the internal state.
|
||||||
Key *string
|
ClientKey *string
|
||||||
// EncryptedKey is the base64 encoded DER of an encrypted key for OpenVPN.
|
|
||||||
// It is used by VPN secure.
|
|
||||||
// It defaults to the empty string meaning it is not
|
|
||||||
// to be used. KeyPassphrase must be set if this one is set.
|
|
||||||
EncryptedKey *string
|
|
||||||
// KeyPassphrase is the key passphrase to be used by OpenVPN
|
|
||||||
// to decrypt the EncryptedPrivateKey. It defaults to the
|
|
||||||
// empty string and must be set if EncryptedPrivateKey is set.
|
|
||||||
KeyPassphrase *string
|
|
||||||
// PIAEncPreset is the encryption preset for
|
// PIAEncPreset is the encryption preset for
|
||||||
// Private Internet Access. It can be set to an
|
// Private Internet Access. It can be set to an
|
||||||
// empty string for other providers.
|
// empty string for other providers.
|
||||||
PIAEncPreset *string
|
PIAEncPreset *string
|
||||||
|
// IPv6 is set to true if IPv6 routing should be
|
||||||
|
// set to be tunnel in OpenVPN, and false otherwise.
|
||||||
|
// It cannot be nil in the internal state.
|
||||||
|
IPv6 *bool // TODO automate like with Wireguard
|
||||||
// MSSFix is the value (1 to 10000) to set for the
|
// MSSFix is the value (1 to 10000) to set for the
|
||||||
// mssfix option for OpenVPN. It is ignored if set to 0.
|
// mssfix option for OpenVPN. It is ignored if set to 0.
|
||||||
// It cannot be nil in the internal state.
|
// It cannot be nil in the internal state.
|
||||||
@@ -95,18 +88,15 @@ func (o OpenVPN) validate(vpnProvider string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isCustom := vpnProvider == providers.Custom
|
isCustom := vpnProvider == providers.Custom
|
||||||
isUserRequired := !isCustom &&
|
|
||||||
vpnProvider != providers.Airvpn &&
|
|
||||||
vpnProvider != providers.VPNSecure
|
|
||||||
|
|
||||||
if isUserRequired && *o.User == "" {
|
if !isCustom && o.User == "" {
|
||||||
return ErrOpenVPNUserIsEmpty
|
return ErrOpenVPNUserIsEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
passwordRequired := isUserRequired &&
|
passwordRequired := !isCustom &&
|
||||||
(vpnProvider != providers.Ivpn || !ivpnAccountID.MatchString(*o.User))
|
(vpnProvider != providers.Ivpn || !ivpnAccountID.MatchString(o.User))
|
||||||
|
|
||||||
if passwordRequired && *o.Password == "" {
|
if passwordRequired && o.Password == "" {
|
||||||
return ErrOpenVPNPasswordIsEmpty
|
return ErrOpenVPNPasswordIsEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,25 +105,16 @@ func (o OpenVPN) validate(vpnProvider string) (err error) {
|
|||||||
return fmt.Errorf("custom configuration file: %w", err)
|
return fmt.Errorf("custom configuration file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validateOpenVPNClientCertificate(vpnProvider, *o.Cert)
|
err = validateOpenVPNClientCertificate(vpnProvider, *o.ClientCrt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("client certificate: %w", err)
|
return fmt.Errorf("client certificate: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validateOpenVPNClientKey(vpnProvider, *o.Key)
|
err = validateOpenVPNClientKey(vpnProvider, *o.ClientKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("client key: %w", err)
|
return fmt.Errorf("client key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validateOpenVPNEncryptedKey(vpnProvider, *o.EncryptedKey)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("encrypted key: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *o.EncryptedKey != "" && *o.KeyPassphrase == "" {
|
|
||||||
return fmt.Errorf("%w", ErrOpenVPNKeyPassphraseIsEmpty)
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxMSSFix = 10000
|
const maxMSSFix = 10000
|
||||||
if *o.MSSFix > maxMSSFix {
|
if *o.MSSFix > maxMSSFix {
|
||||||
return fmt.Errorf("%w: %d is over the maximum value of %d",
|
return fmt.Errorf("%w: %d is over the maximum value of %d",
|
||||||
@@ -171,7 +152,7 @@ func validateOpenVPNConfigFilepath(isCustom bool,
|
|||||||
extractor := extract.New()
|
extractor := extract.New()
|
||||||
_, _, err = extractor.Data(confFile)
|
_, _, err = extractor.Data(confFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("extracting information from custom configuration file: %w", err)
|
return fmt.Errorf("failed extracting information from custom configuration file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -181,9 +162,7 @@ func validateOpenVPNClientCertificate(vpnProvider,
|
|||||||
clientCert string) (err error) {
|
clientCert string) (err error) {
|
||||||
switch vpnProvider {
|
switch vpnProvider {
|
||||||
case
|
case
|
||||||
providers.Airvpn,
|
|
||||||
providers.Cyberghost,
|
providers.Cyberghost,
|
||||||
providers.VPNSecure,
|
|
||||||
providers.VPNUnlimited:
|
providers.VPNUnlimited:
|
||||||
if clientCert == "" {
|
if clientCert == "" {
|
||||||
return ErrMissingValue
|
return ErrMissingValue
|
||||||
@@ -204,7 +183,6 @@ func validateOpenVPNClientCertificate(vpnProvider,
|
|||||||
func validateOpenVPNClientKey(vpnProvider, clientKey string) (err error) {
|
func validateOpenVPNClientKey(vpnProvider, clientKey string) (err error) {
|
||||||
switch vpnProvider {
|
switch vpnProvider {
|
||||||
case
|
case
|
||||||
providers.Airvpn,
|
|
||||||
providers.Cyberghost,
|
providers.Cyberghost,
|
||||||
providers.VPNUnlimited,
|
providers.VPNUnlimited,
|
||||||
providers.Wevpn:
|
providers.Wevpn:
|
||||||
@@ -224,41 +202,23 @@ func validateOpenVPNClientKey(vpnProvider, clientKey string) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateOpenVPNEncryptedKey(vpnProvider,
|
|
||||||
encryptedPrivateKey string) (err error) {
|
|
||||||
if vpnProvider == providers.VPNSecure && encryptedPrivateKey == "" {
|
|
||||||
return ErrMissingValue
|
|
||||||
}
|
|
||||||
|
|
||||||
if encryptedPrivateKey == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = base64.StdEncoding.DecodeString(encryptedPrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *OpenVPN) copy() (copied OpenVPN) {
|
func (o *OpenVPN) copy() (copied OpenVPN) {
|
||||||
return OpenVPN{
|
return OpenVPN{
|
||||||
Version: o.Version,
|
Version: o.Version,
|
||||||
User: helpers.CopyStringPtr(o.User),
|
User: o.User,
|
||||||
Password: helpers.CopyStringPtr(o.Password),
|
Password: o.Password,
|
||||||
ConfFile: helpers.CopyStringPtr(o.ConfFile),
|
ConfFile: helpers.CopyStringPtr(o.ConfFile),
|
||||||
Ciphers: helpers.CopyStringSlice(o.Ciphers),
|
Ciphers: helpers.CopyStringSlice(o.Ciphers),
|
||||||
Auth: helpers.CopyStringPtr(o.Auth),
|
Auth: helpers.CopyStringPtr(o.Auth),
|
||||||
Cert: helpers.CopyStringPtr(o.Cert),
|
ClientCrt: helpers.CopyStringPtr(o.ClientCrt),
|
||||||
Key: helpers.CopyStringPtr(o.Key),
|
ClientKey: helpers.CopyStringPtr(o.ClientKey),
|
||||||
EncryptedKey: helpers.CopyStringPtr(o.EncryptedKey),
|
PIAEncPreset: helpers.CopyStringPtr(o.PIAEncPreset),
|
||||||
KeyPassphrase: helpers.CopyStringPtr(o.KeyPassphrase),
|
IPv6: helpers.CopyBoolPtr(o.IPv6),
|
||||||
PIAEncPreset: helpers.CopyStringPtr(o.PIAEncPreset),
|
MSSFix: helpers.CopyUint16Ptr(o.MSSFix),
|
||||||
MSSFix: helpers.CopyUint16Ptr(o.MSSFix),
|
Interface: o.Interface,
|
||||||
Interface: o.Interface,
|
ProcessUser: o.ProcessUser,
|
||||||
ProcessUser: o.ProcessUser,
|
Verbosity: helpers.CopyIntPtr(o.Verbosity),
|
||||||
Verbosity: helpers.CopyIntPtr(o.Verbosity),
|
Flags: helpers.CopyStringSlice(o.Flags),
|
||||||
Flags: helpers.CopyStringSlice(o.Flags),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,16 +226,15 @@ func (o *OpenVPN) copy() (copied OpenVPN) {
|
|||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (o *OpenVPN) mergeWith(other OpenVPN) {
|
func (o *OpenVPN) mergeWith(other OpenVPN) {
|
||||||
o.Version = helpers.MergeWithString(o.Version, other.Version)
|
o.Version = helpers.MergeWithString(o.Version, other.Version)
|
||||||
o.User = helpers.MergeWithStringPtr(o.User, other.User)
|
o.User = helpers.MergeWithString(o.User, other.User)
|
||||||
o.Password = helpers.MergeWithStringPtr(o.Password, other.Password)
|
o.Password = helpers.MergeWithString(o.Password, other.Password)
|
||||||
o.ConfFile = helpers.MergeWithStringPtr(o.ConfFile, other.ConfFile)
|
o.ConfFile = helpers.MergeWithStringPtr(o.ConfFile, other.ConfFile)
|
||||||
o.Ciphers = helpers.MergeStringSlices(o.Ciphers, other.Ciphers)
|
o.Ciphers = helpers.MergeStringSlices(o.Ciphers, other.Ciphers)
|
||||||
o.Auth = helpers.MergeWithStringPtr(o.Auth, other.Auth)
|
o.Auth = helpers.MergeWithStringPtr(o.Auth, other.Auth)
|
||||||
o.Cert = helpers.MergeWithStringPtr(o.Cert, other.Cert)
|
o.ClientCrt = helpers.MergeWithStringPtr(o.ClientCrt, other.ClientCrt)
|
||||||
o.Key = helpers.MergeWithStringPtr(o.Key, other.Key)
|
o.ClientKey = helpers.MergeWithStringPtr(o.ClientKey, other.ClientKey)
|
||||||
o.EncryptedKey = helpers.MergeWithStringPtr(o.EncryptedKey, other.EncryptedKey)
|
|
||||||
o.KeyPassphrase = helpers.MergeWithStringPtr(o.KeyPassphrase, other.KeyPassphrase)
|
|
||||||
o.PIAEncPreset = helpers.MergeWithStringPtr(o.PIAEncPreset, other.PIAEncPreset)
|
o.PIAEncPreset = helpers.MergeWithStringPtr(o.PIAEncPreset, other.PIAEncPreset)
|
||||||
|
o.IPv6 = helpers.MergeWithBool(o.IPv6, other.IPv6)
|
||||||
o.MSSFix = helpers.MergeWithUint16(o.MSSFix, other.MSSFix)
|
o.MSSFix = helpers.MergeWithUint16(o.MSSFix, other.MSSFix)
|
||||||
o.Interface = helpers.MergeWithString(o.Interface, other.Interface)
|
o.Interface = helpers.MergeWithString(o.Interface, other.Interface)
|
||||||
o.ProcessUser = helpers.MergeWithString(o.ProcessUser, other.ProcessUser)
|
o.ProcessUser = helpers.MergeWithString(o.ProcessUser, other.ProcessUser)
|
||||||
@@ -288,16 +247,15 @@ func (o *OpenVPN) mergeWith(other OpenVPN) {
|
|||||||
// settings.
|
// settings.
|
||||||
func (o *OpenVPN) overrideWith(other OpenVPN) {
|
func (o *OpenVPN) overrideWith(other OpenVPN) {
|
||||||
o.Version = helpers.OverrideWithString(o.Version, other.Version)
|
o.Version = helpers.OverrideWithString(o.Version, other.Version)
|
||||||
o.User = helpers.OverrideWithStringPtr(o.User, other.User)
|
o.User = helpers.OverrideWithString(o.User, other.User)
|
||||||
o.Password = helpers.OverrideWithStringPtr(o.Password, other.Password)
|
o.Password = helpers.OverrideWithString(o.Password, other.Password)
|
||||||
o.ConfFile = helpers.OverrideWithStringPtr(o.ConfFile, other.ConfFile)
|
o.ConfFile = helpers.OverrideWithStringPtr(o.ConfFile, other.ConfFile)
|
||||||
o.Ciphers = helpers.OverrideWithStringSlice(o.Ciphers, other.Ciphers)
|
o.Ciphers = helpers.OverrideWithStringSlice(o.Ciphers, other.Ciphers)
|
||||||
o.Auth = helpers.OverrideWithStringPtr(o.Auth, other.Auth)
|
o.Auth = helpers.OverrideWithStringPtr(o.Auth, other.Auth)
|
||||||
o.Cert = helpers.OverrideWithStringPtr(o.Cert, other.Cert)
|
o.ClientCrt = helpers.OverrideWithStringPtr(o.ClientCrt, other.ClientCrt)
|
||||||
o.Key = helpers.OverrideWithStringPtr(o.Key, other.Key)
|
o.ClientKey = helpers.OverrideWithStringPtr(o.ClientKey, other.ClientKey)
|
||||||
o.EncryptedKey = helpers.OverrideWithStringPtr(o.EncryptedKey, other.EncryptedKey)
|
|
||||||
o.KeyPassphrase = helpers.OverrideWithStringPtr(o.KeyPassphrase, other.KeyPassphrase)
|
|
||||||
o.PIAEncPreset = helpers.OverrideWithStringPtr(o.PIAEncPreset, other.PIAEncPreset)
|
o.PIAEncPreset = helpers.OverrideWithStringPtr(o.PIAEncPreset, other.PIAEncPreset)
|
||||||
|
o.IPv6 = helpers.OverrideWithBool(o.IPv6, other.IPv6)
|
||||||
o.MSSFix = helpers.OverrideWithUint16(o.MSSFix, other.MSSFix)
|
o.MSSFix = helpers.OverrideWithUint16(o.MSSFix, other.MSSFix)
|
||||||
o.Interface = helpers.OverrideWithString(o.Interface, other.Interface)
|
o.Interface = helpers.OverrideWithString(o.Interface, other.Interface)
|
||||||
o.ProcessUser = helpers.OverrideWithString(o.ProcessUser, other.ProcessUser)
|
o.ProcessUser = helpers.OverrideWithString(o.ProcessUser, other.ProcessUser)
|
||||||
@@ -307,25 +265,22 @@ func (o *OpenVPN) overrideWith(other OpenVPN) {
|
|||||||
|
|
||||||
func (o *OpenVPN) setDefaults(vpnProvider string) {
|
func (o *OpenVPN) setDefaults(vpnProvider string) {
|
||||||
o.Version = helpers.DefaultString(o.Version, openvpn.Openvpn25)
|
o.Version = helpers.DefaultString(o.Version, openvpn.Openvpn25)
|
||||||
o.User = helpers.DefaultStringPtr(o.User, "")
|
|
||||||
if vpnProvider == providers.Mullvad {
|
if vpnProvider == providers.Mullvad {
|
||||||
o.Password = helpers.DefaultStringPtr(o.Password, "m")
|
o.Password = "m"
|
||||||
} else {
|
|
||||||
o.Password = helpers.DefaultStringPtr(o.Password, "")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
o.ConfFile = helpers.DefaultStringPtr(o.ConfFile, "")
|
o.ConfFile = helpers.DefaultStringPtr(o.ConfFile, "")
|
||||||
o.Auth = helpers.DefaultStringPtr(o.Auth, "")
|
o.Auth = helpers.DefaultStringPtr(o.Auth, "")
|
||||||
o.Cert = helpers.DefaultStringPtr(o.Cert, "")
|
o.ClientCrt = helpers.DefaultStringPtr(o.ClientCrt, "")
|
||||||
o.Key = helpers.DefaultStringPtr(o.Key, "")
|
o.ClientKey = helpers.DefaultStringPtr(o.ClientKey, "")
|
||||||
o.EncryptedKey = helpers.DefaultStringPtr(o.EncryptedKey, "")
|
|
||||||
o.KeyPassphrase = helpers.DefaultStringPtr(o.KeyPassphrase, "")
|
|
||||||
|
|
||||||
var defaultEncPreset string
|
var defaultEncPreset string
|
||||||
if vpnProvider == providers.PrivateInternetAccess {
|
if vpnProvider == providers.PrivateInternetAccess {
|
||||||
defaultEncPreset = presets.Strong
|
defaultEncPreset = presets.Strong
|
||||||
}
|
}
|
||||||
o.PIAEncPreset = helpers.DefaultStringPtr(o.PIAEncPreset, defaultEncPreset)
|
o.PIAEncPreset = helpers.DefaultStringPtr(o.PIAEncPreset, defaultEncPreset)
|
||||||
|
|
||||||
|
o.IPv6 = helpers.DefaultBool(o.IPv6, false)
|
||||||
o.MSSFix = helpers.DefaultUint16(o.MSSFix, 0)
|
o.MSSFix = helpers.DefaultUint16(o.MSSFix, 0)
|
||||||
o.Interface = helpers.DefaultString(o.Interface, "tun0")
|
o.Interface = helpers.DefaultString(o.Interface, "tun0")
|
||||||
o.ProcessUser = helpers.DefaultString(o.ProcessUser, "root")
|
o.ProcessUser = helpers.DefaultString(o.ProcessUser, "root")
|
||||||
@@ -339,8 +294,8 @@ func (o OpenVPN) String() string {
|
|||||||
func (o OpenVPN) toLinesNode() (node *gotree.Node) {
|
func (o OpenVPN) toLinesNode() (node *gotree.Node) {
|
||||||
node = gotree.New("OpenVPN settings:")
|
node = gotree.New("OpenVPN settings:")
|
||||||
node.Appendf("OpenVPN version: %s", o.Version)
|
node.Appendf("OpenVPN version: %s", o.Version)
|
||||||
node.Appendf("User: %s", helpers.ObfuscatePassword(*o.User))
|
node.Appendf("User: %s", helpers.ObfuscatePassword(o.User))
|
||||||
node.Appendf("Password: %s", helpers.ObfuscatePassword(*o.Password))
|
node.Appendf("Password: %s", helpers.ObfuscatePassword(o.Password))
|
||||||
|
|
||||||
if *o.ConfFile != "" {
|
if *o.ConfFile != "" {
|
||||||
node.Appendf("Custom configuration file: %s", *o.ConfFile)
|
node.Appendf("Custom configuration file: %s", *o.ConfFile)
|
||||||
@@ -354,23 +309,20 @@ func (o OpenVPN) toLinesNode() (node *gotree.Node) {
|
|||||||
node.Appendf("Auth: %s", *o.Auth)
|
node.Appendf("Auth: %s", *o.Auth)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *o.Cert != "" {
|
if *o.ClientCrt != "" {
|
||||||
node.Appendf("Client crt: %s", helpers.ObfuscateData(*o.Cert))
|
node.Appendf("Client crt: %s", helpers.ObfuscateData(*o.ClientCrt))
|
||||||
}
|
}
|
||||||
|
|
||||||
if *o.Key != "" {
|
if *o.ClientKey != "" {
|
||||||
node.Appendf("Client key: %s", helpers.ObfuscateData(*o.Key))
|
node.Appendf("Client key: %s", helpers.ObfuscateData(*o.ClientKey))
|
||||||
}
|
|
||||||
|
|
||||||
if *o.EncryptedKey != "" {
|
|
||||||
node.Appendf("Encrypted key: %s (key passhrapse %s)",
|
|
||||||
helpers.ObfuscateData(*o.EncryptedKey), helpers.ObfuscatePassword(*o.KeyPassphrase))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *o.PIAEncPreset != "" {
|
if *o.PIAEncPreset != "" {
|
||||||
node.Appendf("Private Internet Access encryption preset: %s", *o.PIAEncPreset)
|
node.Appendf("Private Internet Access encryption preset: %s", *o.PIAEncPreset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node.Appendf("Tunnel IPv6: %s", helpers.BoolPtrToYesNo(o.IPv6))
|
||||||
|
|
||||||
if *o.MSSFix > 0 {
|
if *o.MSSFix > 0 {
|
||||||
node.Appendf("MSS Fix: %d", *o.MSSFix)
|
node.Appendf("MSS Fix: %d", *o.MSSFix)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,19 +60,13 @@ func (o OpenVPNSelection) validate(vpnProvider string) (err error) {
|
|||||||
case providers.Expressvpn, providers.Fastestvpn,
|
case providers.Expressvpn, providers.Fastestvpn,
|
||||||
providers.Ipvanish, providers.Nordvpn,
|
providers.Ipvanish, providers.Nordvpn,
|
||||||
providers.Privado, providers.Purevpn,
|
providers.Privado, providers.Purevpn,
|
||||||
providers.Surfshark, providers.VPNSecure,
|
providers.Surfshark, providers.VPNUnlimited,
|
||||||
providers.VPNUnlimited, providers.Vyprvpn:
|
providers.Vyprvpn:
|
||||||
return fmt.Errorf("%w: for VPN service provider %s",
|
return fmt.Errorf("%w: for VPN service provider %s",
|
||||||
ErrOpenVPNCustomPortNotAllowed, vpnProvider)
|
ErrOpenVPNCustomPortNotAllowed, vpnProvider)
|
||||||
default:
|
default:
|
||||||
var allowedTCP, allowedUDP []uint16
|
var allowedTCP, allowedUDP []uint16
|
||||||
switch vpnProvider {
|
switch vpnProvider {
|
||||||
case providers.Airvpn:
|
|
||||||
allowedTCP = []uint16{
|
|
||||||
53, 80, 443, // IP in 1, 3
|
|
||||||
1194, 2018, 41185, // IP in 1, 2, 3, 4
|
|
||||||
}
|
|
||||||
allowedUDP = []uint16{53, 80, 443, 1194, 2018, 41185}
|
|
||||||
case providers.Ivpn:
|
case providers.Ivpn:
|
||||||
allowedTCP = []uint16{80, 443, 1143}
|
allowedTCP = []uint16{80, 443, 1143}
|
||||||
allowedUDP = []uint16{53, 1194, 2049, 2050}
|
allowedUDP = []uint16{53, 1194, 2049, 2050}
|
||||||
@@ -88,9 +82,6 @@ func (o OpenVPNSelection) validate(vpnProvider string) (err error) {
|
|||||||
case providers.Protonvpn:
|
case providers.Protonvpn:
|
||||||
allowedTCP = []uint16{443, 5995, 8443}
|
allowedTCP = []uint16{443, 5995, 8443}
|
||||||
allowedUDP = []uint16{80, 443, 1194, 4569, 5060}
|
allowedUDP = []uint16{80, 443, 1194, 4569, 5060}
|
||||||
case providers.SlickVPN:
|
|
||||||
allowedTCP = []uint16{443, 8080, 8888}
|
|
||||||
allowedUDP = []uint16{443, 8080, 8888}
|
|
||||||
case providers.Wevpn:
|
case providers.Wevpn:
|
||||||
allowedTCP = []uint16{53, 1195, 1199, 2018}
|
allowedTCP = []uint16{53, 1195, 1199, 2018}
|
||||||
allowedUDP = []uint16{80, 1194, 1198}
|
allowedUDP = []uint16{80, 1194, 1198}
|
||||||
|
|||||||
@@ -30,11 +30,9 @@ func (p *Provider) validate(vpnType string, storage Storage) (err error) {
|
|||||||
validNames = append(validNames, "pia") // Retro-compatibility
|
validNames = append(validNames, "pia") // Retro-compatibility
|
||||||
} else { // Wireguard
|
} else { // Wireguard
|
||||||
validNames = []string{
|
validNames = []string{
|
||||||
providers.Airvpn,
|
|
||||||
providers.Custom,
|
providers.Custom,
|
||||||
providers.Ivpn,
|
providers.Ivpn,
|
||||||
providers.Mullvad,
|
providers.Mullvad,
|
||||||
providers.Surfshark,
|
|
||||||
providers.Windscribe,
|
providers.Windscribe,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,18 +48,18 @@ func (p *PublicIP) copy() (copied PublicIP) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *PublicIP) mergeWith(other PublicIP) {
|
func (p *PublicIP) mergeWith(other PublicIP) {
|
||||||
p.Period = helpers.MergeWithDurationPtr(p.Period, other.Period)
|
p.Period = helpers.MergeWithDuration(p.Period, other.Period)
|
||||||
p.IPFilepath = helpers.MergeWithStringPtr(p.IPFilepath, other.IPFilepath)
|
p.IPFilepath = helpers.MergeWithStringPtr(p.IPFilepath, other.IPFilepath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PublicIP) overrideWith(other PublicIP) {
|
func (p *PublicIP) overrideWith(other PublicIP) {
|
||||||
p.Period = helpers.OverrideWithDurationPtr(p.Period, other.Period)
|
p.Period = helpers.OverrideWithDuration(p.Period, other.Period)
|
||||||
p.IPFilepath = helpers.OverrideWithStringPtr(p.IPFilepath, other.IPFilepath)
|
p.IPFilepath = helpers.OverrideWithStringPtr(p.IPFilepath, other.IPFilepath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PublicIP) setDefaults() {
|
func (p *PublicIP) setDefaults() {
|
||||||
const defaultPeriod = 12 * time.Hour
|
const defaultPeriod = 12 * time.Hour
|
||||||
p.Period = helpers.DefaultDurationPtr(p.Period, defaultPeriod)
|
p.Period = helpers.DefaultDuration(p.Period, defaultPeriod)
|
||||||
p.IPFilepath = helpers.DefaultStringPtr(p.IPFilepath, "/tmp/gluetun/ip")
|
p.IPFilepath = helpers.DefaultStringPtr(p.IPFilepath, "/tmp/gluetun/ip")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,10 +45,6 @@ type ServerSelection struct { //nolint:maligned
|
|||||||
// FreeOnly is true if VPN servers that are not free should
|
// FreeOnly is true if VPN servers that are not free should
|
||||||
// be filtered. This is used with ProtonVPN and VPN Unlimited.
|
// be filtered. This is used with ProtonVPN and VPN Unlimited.
|
||||||
FreeOnly *bool
|
FreeOnly *bool
|
||||||
// PremiumOnly is true if VPN servers that are not premium should
|
|
||||||
// be filtered. This is used with VPN Secure.
|
|
||||||
// TODO extend to providers using FreeOnly.
|
|
||||||
PremiumOnly *bool
|
|
||||||
// StreamOnly is true if VPN servers not for streaming should
|
// StreamOnly is true if VPN servers not for streaming should
|
||||||
// be filtered. This is used with VPNUnlimited.
|
// be filtered. This is used with VPNUnlimited.
|
||||||
StreamOnly *bool
|
StreamOnly *bool
|
||||||
@@ -67,10 +63,8 @@ type ServerSelection struct { //nolint:maligned
|
|||||||
var (
|
var (
|
||||||
ErrOwnedOnlyNotSupported = errors.New("owned only filter is not supported")
|
ErrOwnedOnlyNotSupported = errors.New("owned only filter is not supported")
|
||||||
ErrFreeOnlyNotSupported = errors.New("free only filter is not supported")
|
ErrFreeOnlyNotSupported = errors.New("free only filter is not supported")
|
||||||
ErrPremiumOnlyNotSupported = errors.New("premium only filter is not supported")
|
|
||||||
ErrStreamOnlyNotSupported = errors.New("stream only filter is not supported")
|
ErrStreamOnlyNotSupported = errors.New("stream only filter is not supported")
|
||||||
ErrMultiHopOnlyNotSupported = errors.New("multi hop only filter is not supported")
|
ErrMultiHopOnlyNotSupported = errors.New("multi hop only filter is not supported")
|
||||||
ErrFreePremiumBothSet = errors.New("free only and premium only filters are both set")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ss *ServerSelection) validate(vpnServiceProvider string,
|
func (ss *ServerSelection) validate(vpnServiceProvider string,
|
||||||
@@ -109,18 +103,6 @@ func (ss *ServerSelection) validate(vpnServiceProvider string,
|
|||||||
ErrFreeOnlyNotSupported, vpnServiceProvider)
|
ErrFreeOnlyNotSupported, vpnServiceProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *ss.PremiumOnly &&
|
|
||||||
!helpers.IsOneOf(vpnServiceProvider,
|
|
||||||
providers.VPNSecure,
|
|
||||||
) {
|
|
||||||
return fmt.Errorf("%w: for VPN service provider %s",
|
|
||||||
ErrPremiumOnlyNotSupported, vpnServiceProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *ss.FreeOnly && *ss.PremiumOnly {
|
|
||||||
return ErrFreePremiumBothSet
|
|
||||||
}
|
|
||||||
|
|
||||||
if *ss.StreamOnly &&
|
if *ss.StreamOnly &&
|
||||||
!helpers.IsOneOf(vpnServiceProvider,
|
!helpers.IsOneOf(vpnServiceProvider,
|
||||||
providers.Protonvpn,
|
providers.Protonvpn,
|
||||||
@@ -212,7 +194,6 @@ func (ss *ServerSelection) copy() (copied ServerSelection) {
|
|||||||
Numbers: helpers.CopyUint16Slice(ss.Numbers),
|
Numbers: helpers.CopyUint16Slice(ss.Numbers),
|
||||||
OwnedOnly: helpers.CopyBoolPtr(ss.OwnedOnly),
|
OwnedOnly: helpers.CopyBoolPtr(ss.OwnedOnly),
|
||||||
FreeOnly: helpers.CopyBoolPtr(ss.FreeOnly),
|
FreeOnly: helpers.CopyBoolPtr(ss.FreeOnly),
|
||||||
PremiumOnly: helpers.CopyBoolPtr(ss.PremiumOnly),
|
|
||||||
StreamOnly: helpers.CopyBoolPtr(ss.StreamOnly),
|
StreamOnly: helpers.CopyBoolPtr(ss.StreamOnly),
|
||||||
MultiHopOnly: helpers.CopyBoolPtr(ss.MultiHopOnly),
|
MultiHopOnly: helpers.CopyBoolPtr(ss.MultiHopOnly),
|
||||||
OpenVPN: ss.OpenVPN.copy(),
|
OpenVPN: ss.OpenVPN.copy(),
|
||||||
@@ -232,7 +213,6 @@ func (ss *ServerSelection) mergeWith(other ServerSelection) {
|
|||||||
ss.Numbers = helpers.MergeUint16Slices(ss.Numbers, other.Numbers)
|
ss.Numbers = helpers.MergeUint16Slices(ss.Numbers, other.Numbers)
|
||||||
ss.OwnedOnly = helpers.MergeWithBool(ss.OwnedOnly, other.OwnedOnly)
|
ss.OwnedOnly = helpers.MergeWithBool(ss.OwnedOnly, other.OwnedOnly)
|
||||||
ss.FreeOnly = helpers.MergeWithBool(ss.FreeOnly, other.FreeOnly)
|
ss.FreeOnly = helpers.MergeWithBool(ss.FreeOnly, other.FreeOnly)
|
||||||
ss.PremiumOnly = helpers.MergeWithBool(ss.PremiumOnly, other.PremiumOnly)
|
|
||||||
ss.StreamOnly = helpers.MergeWithBool(ss.StreamOnly, other.StreamOnly)
|
ss.StreamOnly = helpers.MergeWithBool(ss.StreamOnly, other.StreamOnly)
|
||||||
ss.MultiHopOnly = helpers.MergeWithBool(ss.MultiHopOnly, other.MultiHopOnly)
|
ss.MultiHopOnly = helpers.MergeWithBool(ss.MultiHopOnly, other.MultiHopOnly)
|
||||||
|
|
||||||
@@ -252,7 +232,6 @@ func (ss *ServerSelection) overrideWith(other ServerSelection) {
|
|||||||
ss.Numbers = helpers.OverrideWithUint16Slice(ss.Numbers, other.Numbers)
|
ss.Numbers = helpers.OverrideWithUint16Slice(ss.Numbers, other.Numbers)
|
||||||
ss.OwnedOnly = helpers.OverrideWithBool(ss.OwnedOnly, other.OwnedOnly)
|
ss.OwnedOnly = helpers.OverrideWithBool(ss.OwnedOnly, other.OwnedOnly)
|
||||||
ss.FreeOnly = helpers.OverrideWithBool(ss.FreeOnly, other.FreeOnly)
|
ss.FreeOnly = helpers.OverrideWithBool(ss.FreeOnly, other.FreeOnly)
|
||||||
ss.PremiumOnly = helpers.OverrideWithBool(ss.PremiumOnly, other.PremiumOnly)
|
|
||||||
ss.StreamOnly = helpers.OverrideWithBool(ss.StreamOnly, other.StreamOnly)
|
ss.StreamOnly = helpers.OverrideWithBool(ss.StreamOnly, other.StreamOnly)
|
||||||
ss.MultiHopOnly = helpers.OverrideWithBool(ss.MultiHopOnly, other.MultiHopOnly)
|
ss.MultiHopOnly = helpers.OverrideWithBool(ss.MultiHopOnly, other.MultiHopOnly)
|
||||||
ss.OpenVPN.overrideWith(other.OpenVPN)
|
ss.OpenVPN.overrideWith(other.OpenVPN)
|
||||||
@@ -264,7 +243,6 @@ func (ss *ServerSelection) setDefaults(vpnProvider string) {
|
|||||||
ss.TargetIP = helpers.DefaultIP(ss.TargetIP, net.IP{})
|
ss.TargetIP = helpers.DefaultIP(ss.TargetIP, net.IP{})
|
||||||
ss.OwnedOnly = helpers.DefaultBool(ss.OwnedOnly, false)
|
ss.OwnedOnly = helpers.DefaultBool(ss.OwnedOnly, false)
|
||||||
ss.FreeOnly = helpers.DefaultBool(ss.FreeOnly, false)
|
ss.FreeOnly = helpers.DefaultBool(ss.FreeOnly, false)
|
||||||
ss.PremiumOnly = helpers.DefaultBool(ss.PremiumOnly, false)
|
|
||||||
ss.StreamOnly = helpers.DefaultBool(ss.StreamOnly, false)
|
ss.StreamOnly = helpers.DefaultBool(ss.StreamOnly, false)
|
||||||
ss.MultiHopOnly = helpers.DefaultBool(ss.MultiHopOnly, false)
|
ss.MultiHopOnly = helpers.DefaultBool(ss.MultiHopOnly, false)
|
||||||
ss.OpenVPN.setDefaults(vpnProvider)
|
ss.OpenVPN.setDefaults(vpnProvider)
|
||||||
@@ -321,10 +299,6 @@ func (ss ServerSelection) toLinesNode() (node *gotree.Node) {
|
|||||||
node.Appendf("Free only servers: yes")
|
node.Appendf("Free only servers: yes")
|
||||||
}
|
}
|
||||||
|
|
||||||
if *ss.PremiumOnly {
|
|
||||||
node.Appendf("Premium only servers: yes")
|
|
||||||
}
|
|
||||||
|
|
||||||
if *ss.StreamOnly {
|
if *ss.StreamOnly {
|
||||||
node.Appendf("Stream only servers: yes")
|
node.Appendf("Stream only servers: yes")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,6 @@ package settings
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/openvpn"
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/gluetun/internal/pprof"
|
"github.com/qdm12/gluetun/internal/pprof"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
@@ -35,7 +31,7 @@ type Storage interface {
|
|||||||
// Validate validates all the settings and returns an error
|
// Validate validates all the settings and returns an error
|
||||||
// if one of them is not valid.
|
// if one of them is not valid.
|
||||||
// TODO v4 remove pointer for receiver (because of Surfshark).
|
// TODO v4 remove pointer for receiver (because of Surfshark).
|
||||||
func (s *Settings) Validate(storage Storage, ipv6Supported bool) (err error) {
|
func (s *Settings) Validate(storage Storage) (err error) {
|
||||||
nameToValidation := map[string]func() error{
|
nameToValidation := map[string]func() error{
|
||||||
"control server": s.ControlServer.validate,
|
"control server": s.ControlServer.validate,
|
||||||
"dns": s.DNS.validate,
|
"dns": s.DNS.validate,
|
||||||
@@ -50,7 +46,7 @@ func (s *Settings) Validate(storage Storage, ipv6Supported bool) (err error) {
|
|||||||
"version": s.Version.validate,
|
"version": s.Version.validate,
|
||||||
// Pprof validation done in pprof constructor
|
// Pprof validation done in pprof constructor
|
||||||
"VPN": func() error {
|
"VPN": func() error {
|
||||||
return s.VPN.Validate(storage, ipv6Supported)
|
return s.VPN.validate(storage)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +73,7 @@ func (s *Settings) copy() (copied Settings) {
|
|||||||
System: s.System.copy(),
|
System: s.System.copy(),
|
||||||
Updater: s.Updater.copy(),
|
Updater: s.Updater.copy(),
|
||||||
Version: s.Version.copy(),
|
Version: s.Version.copy(),
|
||||||
VPN: s.VPN.Copy(),
|
VPN: s.VPN.copy(),
|
||||||
Pprof: s.Pprof.Copy(),
|
Pprof: s.Pprof.Copy(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +95,7 @@ func (s *Settings) MergeWith(other Settings) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Settings) OverrideWith(other Settings,
|
func (s *Settings) OverrideWith(other Settings,
|
||||||
storage Storage, ipv6Supported bool) (err error) {
|
storage Storage) (err error) {
|
||||||
patchedSettings := s.copy()
|
patchedSettings := s.copy()
|
||||||
patchedSettings.ControlServer.overrideWith(other.ControlServer)
|
patchedSettings.ControlServer.overrideWith(other.ControlServer)
|
||||||
patchedSettings.DNS.overrideWith(other.DNS)
|
patchedSettings.DNS.overrideWith(other.DNS)
|
||||||
@@ -112,9 +108,9 @@ func (s *Settings) OverrideWith(other Settings,
|
|||||||
patchedSettings.System.overrideWith(other.System)
|
patchedSettings.System.overrideWith(other.System)
|
||||||
patchedSettings.Updater.overrideWith(other.Updater)
|
patchedSettings.Updater.overrideWith(other.Updater)
|
||||||
patchedSettings.Version.overrideWith(other.Version)
|
patchedSettings.Version.overrideWith(other.Version)
|
||||||
patchedSettings.VPN.OverrideWith(other.VPN)
|
patchedSettings.VPN.overrideWith(other.VPN)
|
||||||
patchedSettings.Pprof.OverrideWith(other.Pprof)
|
patchedSettings.Pprof.OverrideWith(other.Pprof)
|
||||||
err = patchedSettings.Validate(storage, ipv6Supported)
|
err = patchedSettings.Validate(storage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -161,37 +157,3 @@ func (s Settings) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Settings) Warnings() (warnings []string) {
|
|
||||||
if *s.VPN.Provider.Name == providers.HideMyAss {
|
|
||||||
warnings = append(warnings, "HideMyAss dropped support for Linux OpenVPN "+
|
|
||||||
" so this will likely not work anymore. See https://github.com/qdm12/gluetun/issues/1498.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if helpers.IsOneOf(*s.VPN.Provider.Name, providers.SlickVPN) &&
|
|
||||||
s.VPN.Type == vpn.OpenVPN {
|
|
||||||
if s.VPN.OpenVPN.Version == openvpn.Openvpn24 {
|
|
||||||
warnings = append(warnings, "OpenVPN 2.4 uses OpenSSL 1.1.1 "+
|
|
||||||
"which allows the usage of weak security in today's standards. "+
|
|
||||||
"This can be ok if good security is enforced by the VPN provider. "+
|
|
||||||
"However, "+*s.VPN.Provider.Name+" uses weak security so you should use "+
|
|
||||||
"OpenVPN 2.5 to enforce good security practices.")
|
|
||||||
} else {
|
|
||||||
warnings = append(warnings, "OpenVPN 2.5 uses OpenSSL 3 "+
|
|
||||||
"which prohibits the usage of weak security in today's standards. "+
|
|
||||||
*s.VPN.Provider.Name+" uses weak security which is out "+
|
|
||||||
"of Gluetun's control so the only workaround is to allow such weaknesses "+
|
|
||||||
`using the OpenVPN option tls-cipher "DEFAULT:@SECLEVEL=0". `+
|
|
||||||
"You might want to reach to your provider so they upgrade their certificates. "+
|
|
||||||
"Once this is done, you will have to let the Gluetun maintainers know "+
|
|
||||||
"by creating an issue, attaching the new certificate and we will update Gluetun.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.VPN.OpenVPN.Version == openvpn.Openvpn24 {
|
|
||||||
warnings = append(warnings, "OpenVPN 2.4 will be removed in release v3.34.0 (around June 2023). "+
|
|
||||||
"Please create an issue if you have a compelling reason to keep it.")
|
|
||||||
}
|
|
||||||
|
|
||||||
return warnings
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ func Test_Settings_String(t *testing.T) {
|
|||||||
| ├── User: [not set]
|
| ├── User: [not set]
|
||||||
| ├── Password: [not set]
|
| ├── Password: [not set]
|
||||||
| ├── Private Internet Access encryption preset: strong
|
| ├── Private Internet Access encryption preset: strong
|
||||||
|
| ├── Tunnel IPv6: no
|
||||||
| ├── Network interface: tun0
|
| ├── Network interface: tun0
|
||||||
| ├── Run OpenVPN as: root
|
| ├── Run OpenVPN as: root
|
||||||
| └── Verbosity level: 1
|
| └── Verbosity level: 1
|
||||||
@@ -66,8 +67,6 @@ func Test_Settings_String(t *testing.T) {
|
|||||||
├── Health settings:
|
├── Health settings:
|
||||||
| ├── Server listening address: 127.0.0.1:9999
|
| ├── Server listening address: 127.0.0.1:9999
|
||||||
| ├── Target address: cloudflare.com:443
|
| ├── Target address: cloudflare.com:443
|
||||||
| ├── Read header timeout: 100ms
|
|
||||||
| ├── Read timeout: 500ms
|
|
||||||
| └── VPN wait durations:
|
| └── VPN wait durations:
|
||||||
| ├── Initial duration: 6s
|
| ├── Initial duration: 6s
|
||||||
| └── Additional duration: 5s
|
| └── Additional duration: 5s
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ func (u *Updater) copy() (copied Updater) {
|
|||||||
// mergeWith merges the other settings into any
|
// mergeWith merges the other settings into any
|
||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (u *Updater) mergeWith(other Updater) {
|
func (u *Updater) mergeWith(other Updater) {
|
||||||
u.Period = helpers.MergeWithDurationPtr(u.Period, other.Period)
|
u.Period = helpers.MergeWithDuration(u.Period, other.Period)
|
||||||
u.DNSAddress = helpers.MergeWithString(u.DNSAddress, other.DNSAddress)
|
u.DNSAddress = helpers.MergeWithString(u.DNSAddress, other.DNSAddress)
|
||||||
u.MinRatio = helpers.MergeWithFloat64(u.MinRatio, other.MinRatio)
|
u.MinRatio = helpers.MergeWithFloat64(u.MinRatio, other.MinRatio)
|
||||||
u.Providers = helpers.MergeStringSlices(u.Providers, other.Providers)
|
u.Providers = helpers.MergeStringSlices(u.Providers, other.Providers)
|
||||||
@@ -83,14 +83,14 @@ func (u *Updater) mergeWith(other Updater) {
|
|||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (u *Updater) overrideWith(other Updater) {
|
func (u *Updater) overrideWith(other Updater) {
|
||||||
u.Period = helpers.OverrideWithDurationPtr(u.Period, other.Period)
|
u.Period = helpers.OverrideWithDuration(u.Period, other.Period)
|
||||||
u.DNSAddress = helpers.OverrideWithString(u.DNSAddress, other.DNSAddress)
|
u.DNSAddress = helpers.OverrideWithString(u.DNSAddress, other.DNSAddress)
|
||||||
u.MinRatio = helpers.OverrideWithFloat64(u.MinRatio, other.MinRatio)
|
u.MinRatio = helpers.OverrideWithFloat64(u.MinRatio, other.MinRatio)
|
||||||
u.Providers = helpers.OverrideWithStringSlice(u.Providers, other.Providers)
|
u.Providers = helpers.OverrideWithStringSlice(u.Providers, other.Providers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Updater) SetDefaults(vpnProvider string) {
|
func (u *Updater) SetDefaults(vpnProvider string) {
|
||||||
u.Period = helpers.DefaultDurationPtr(u.Period, 0)
|
u.Period = helpers.DefaultDuration(u.Period, 0)
|
||||||
u.DNSAddress = helpers.DefaultString(u.DNSAddress, "1.1.1.1:53")
|
u.DNSAddress = helpers.DefaultString(u.DNSAddress, "1.1.1.1:53")
|
||||||
|
|
||||||
if u.MinRatio == 0 {
|
if u.MinRatio == 0 {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ type VPN struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO v4 remove pointer for receiver (because of Surfshark).
|
// TODO v4 remove pointer for receiver (because of Surfshark).
|
||||||
func (v *VPN) Validate(storage Storage, ipv6Supported bool) (err error) {
|
func (v *VPN) validate(storage Storage) (err error) {
|
||||||
// Validate Type
|
// Validate Type
|
||||||
validVPNTypes := []string{vpn.OpenVPN, vpn.Wireguard}
|
validVPNTypes := []string{vpn.OpenVPN, vpn.Wireguard}
|
||||||
if !helpers.IsOneOf(v.Type, validVPNTypes...) {
|
if !helpers.IsOneOf(v.Type, validVPNTypes...) {
|
||||||
@@ -39,7 +39,7 @@ func (v *VPN) Validate(storage Storage, ipv6Supported bool) (err error) {
|
|||||||
return fmt.Errorf("OpenVPN settings: %w", err)
|
return fmt.Errorf("OpenVPN settings: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err := v.Wireguard.validate(*v.Provider.Name, ipv6Supported)
|
err := v.Wireguard.validate(*v.Provider.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Wireguard settings: %w", err)
|
return fmt.Errorf("Wireguard settings: %w", err)
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ func (v *VPN) Validate(storage Storage, ipv6Supported bool) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VPN) Copy() (copied VPN) {
|
func (v *VPN) copy() (copied VPN) {
|
||||||
return VPN{
|
return VPN{
|
||||||
Type: v.Type,
|
Type: v.Type,
|
||||||
Provider: v.Provider.copy(),
|
Provider: v.Provider.copy(),
|
||||||
@@ -64,7 +64,7 @@ func (v *VPN) mergeWith(other VPN) {
|
|||||||
v.Wireguard.mergeWith(other.Wireguard)
|
v.Wireguard.mergeWith(other.Wireguard)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VPN) OverrideWith(other VPN) {
|
func (v *VPN) overrideWith(other VPN) {
|
||||||
v.Type = helpers.OverrideWithString(v.Type, other.Type)
|
v.Type = helpers.OverrideWithString(v.Type, other.Type)
|
||||||
v.Provider.overrideWith(other.Provider)
|
v.Provider.overrideWith(other.Provider)
|
||||||
v.OpenVPN.overrideWith(other.OpenVPN)
|
v.OpenVPN.overrideWith(other.OpenVPN)
|
||||||
|
|||||||
@@ -27,23 +27,17 @@ type Wireguard struct {
|
|||||||
// to create. It cannot be the empty string in the
|
// to create. It cannot be the empty string in the
|
||||||
// internal state.
|
// internal state.
|
||||||
Interface string
|
Interface string
|
||||||
// Implementation is the Wireguard implementation to use.
|
|
||||||
// It can be "auto", "userspace" or "kernelspace".
|
|
||||||
// It defaults to "auto" and cannot be the empty string
|
|
||||||
// in the internal state.
|
|
||||||
Implementation string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var regexpInterfaceName = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
|
var regexpInterfaceName = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
|
||||||
|
|
||||||
// Validate validates Wireguard settings.
|
// Validate validates Wireguard settings.
|
||||||
// It should only be ran if the VPN type chosen is Wireguard.
|
// It should only be ran if the VPN type chosen is Wireguard.
|
||||||
func (w Wireguard) validate(vpnProvider string, ipv6Supported bool) (err error) {
|
func (w Wireguard) validate(vpnProvider string) (err error) {
|
||||||
if !helpers.IsOneOf(vpnProvider,
|
if !helpers.IsOneOf(vpnProvider,
|
||||||
providers.Custom,
|
providers.Custom,
|
||||||
providers.Ivpn,
|
providers.Ivpn,
|
||||||
providers.Mullvad,
|
providers.Mullvad,
|
||||||
providers.Surfshark,
|
|
||||||
providers.Windscribe,
|
providers.Windscribe,
|
||||||
) {
|
) {
|
||||||
// do not validate for VPN provider not supporting Wireguard
|
// do not validate for VPN provider not supporting Wireguard
|
||||||
@@ -59,12 +53,6 @@ func (w Wireguard) validate(vpnProvider string, ipv6Supported bool) (err error)
|
|||||||
return fmt.Errorf("private key is not valid: %w", err)
|
return fmt.Errorf("private key is not valid: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if vpnProvider == providers.Airvpn {
|
|
||||||
if *w.PreSharedKey == "" {
|
|
||||||
return fmt.Errorf("%w", ErrWireguardPreSharedKeyNotSet)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate PreSharedKey
|
// Validate PreSharedKey
|
||||||
if *w.PreSharedKey != "" { // Note: this is optional
|
if *w.PreSharedKey != "" { // Note: this is optional
|
||||||
_, err = wgtypes.ParseKey(*w.PreSharedKey)
|
_, err = wgtypes.ParseKey(*w.PreSharedKey)
|
||||||
@@ -82,12 +70,6 @@ func (w Wireguard) validate(vpnProvider string, ipv6Supported bool) (err error)
|
|||||||
return fmt.Errorf("%w: for address at index %d: %s",
|
return fmt.Errorf("%w: for address at index %d: %s",
|
||||||
ErrWireguardInterfaceAddressNotSet, i, ipNet.String())
|
ErrWireguardInterfaceAddressNotSet, i, ipNet.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
ipv6Net := ipNet.IP.To4() == nil
|
|
||||||
if ipv6Net && !ipv6Supported {
|
|
||||||
return fmt.Errorf("%w: address %s",
|
|
||||||
ErrWireguardInterfaceAddressIPv6, ipNet)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate interface
|
// Validate interface
|
||||||
@@ -96,22 +78,15 @@ func (w Wireguard) validate(vpnProvider string, ipv6Supported bool) (err error)
|
|||||||
ErrWireguardInterfaceNotValid, w.Interface, regexpInterfaceName)
|
ErrWireguardInterfaceNotValid, w.Interface, regexpInterfaceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
validImplementations := []string{"auto", "userspace", "kernelspace"}
|
|
||||||
if !helpers.IsOneOf(w.Implementation, validImplementations...) {
|
|
||||||
return fmt.Errorf("%w: %s must be one of %s", ErrWireguardImplementationNotValid,
|
|
||||||
w.Implementation, helpers.ChoicesOrString(validImplementations))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wireguard) copy() (copied Wireguard) {
|
func (w *Wireguard) copy() (copied Wireguard) {
|
||||||
return Wireguard{
|
return Wireguard{
|
||||||
PrivateKey: helpers.CopyStringPtr(w.PrivateKey),
|
PrivateKey: helpers.CopyStringPtr(w.PrivateKey),
|
||||||
PreSharedKey: helpers.CopyStringPtr(w.PreSharedKey),
|
PreSharedKey: helpers.CopyStringPtr(w.PreSharedKey),
|
||||||
Addresses: helpers.CopyIPNetSlice(w.Addresses),
|
Addresses: helpers.CopyIPNetSlice(w.Addresses),
|
||||||
Interface: w.Interface,
|
Interface: w.Interface,
|
||||||
Implementation: w.Implementation,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +95,6 @@ func (w *Wireguard) mergeWith(other Wireguard) {
|
|||||||
w.PreSharedKey = helpers.MergeWithStringPtr(w.PreSharedKey, other.PreSharedKey)
|
w.PreSharedKey = helpers.MergeWithStringPtr(w.PreSharedKey, other.PreSharedKey)
|
||||||
w.Addresses = helpers.MergeIPNetsSlices(w.Addresses, other.Addresses)
|
w.Addresses = helpers.MergeIPNetsSlices(w.Addresses, other.Addresses)
|
||||||
w.Interface = helpers.MergeWithString(w.Interface, other.Interface)
|
w.Interface = helpers.MergeWithString(w.Interface, other.Interface)
|
||||||
w.Implementation = helpers.MergeWithString(w.Implementation, other.Implementation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wireguard) overrideWith(other Wireguard) {
|
func (w *Wireguard) overrideWith(other Wireguard) {
|
||||||
@@ -128,14 +102,12 @@ func (w *Wireguard) overrideWith(other Wireguard) {
|
|||||||
w.PreSharedKey = helpers.OverrideWithStringPtr(w.PreSharedKey, other.PreSharedKey)
|
w.PreSharedKey = helpers.OverrideWithStringPtr(w.PreSharedKey, other.PreSharedKey)
|
||||||
w.Addresses = helpers.OverrideWithIPNetsSlice(w.Addresses, other.Addresses)
|
w.Addresses = helpers.OverrideWithIPNetsSlice(w.Addresses, other.Addresses)
|
||||||
w.Interface = helpers.OverrideWithString(w.Interface, other.Interface)
|
w.Interface = helpers.OverrideWithString(w.Interface, other.Interface)
|
||||||
w.Implementation = helpers.OverrideWithString(w.Implementation, other.Implementation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wireguard) setDefaults() {
|
func (w *Wireguard) setDefaults() {
|
||||||
w.PrivateKey = helpers.DefaultStringPtr(w.PrivateKey, "")
|
w.PrivateKey = helpers.DefaultStringPtr(w.PrivateKey, "")
|
||||||
w.PreSharedKey = helpers.DefaultStringPtr(w.PreSharedKey, "")
|
w.PreSharedKey = helpers.DefaultStringPtr(w.PreSharedKey, "")
|
||||||
w.Interface = helpers.DefaultString(w.Interface, "wg0")
|
w.Interface = helpers.DefaultString(w.Interface, "wg0")
|
||||||
w.Implementation = helpers.DefaultString(w.Implementation, "auto")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w Wireguard) String() string {
|
func (w Wireguard) String() string {
|
||||||
@@ -162,9 +134,5 @@ func (w Wireguard) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
node.Appendf("Network interface: %s", w.Interface)
|
node.Appendf("Network interface: %s", w.Interface)
|
||||||
|
|
||||||
if w.Implementation != "auto" {
|
|
||||||
node.Appendf("Implementation: %s", w.Implementation)
|
|
||||||
}
|
|
||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ type WireguardSelection struct {
|
|||||||
// in the internal state.
|
// in the internal state.
|
||||||
EndpointIP net.IP
|
EndpointIP net.IP
|
||||||
// EndpointPort is a the server port to use for the VPN server.
|
// EndpointPort is a the server port to use for the VPN server.
|
||||||
// It is optional for VPN providers IVPN, Mullvad, Surfshark
|
// It is optional for VPN providers IVPN, Mullvad
|
||||||
// and Windscribe, and compulsory for the others.
|
// and Windscribe, and compulsory for the others.
|
||||||
// When optional, it can be set to 0 to indicate not use
|
// When optional, it can be set to 0 to indicate not use
|
||||||
// a custom endpoint port. It cannot be nil in the internal
|
// a custom endpoint port. It cannot be nil in the internal
|
||||||
@@ -36,9 +36,7 @@ type WireguardSelection struct {
|
|||||||
func (w WireguardSelection) validate(vpnProvider string) (err error) {
|
func (w WireguardSelection) validate(vpnProvider string) (err error) {
|
||||||
// Validate EndpointIP
|
// Validate EndpointIP
|
||||||
switch vpnProvider {
|
switch vpnProvider {
|
||||||
case providers.Airvpn, providers.Ivpn, providers.Mullvad,
|
case providers.Ivpn, providers.Mullvad, providers.Windscribe: // endpoint IP addresses are baked in
|
||||||
providers.Surfshark, providers.Windscribe:
|
|
||||||
// endpoint IP addresses are baked in
|
|
||||||
case providers.Custom:
|
case providers.Custom:
|
||||||
if len(w.EndpointIP) == 0 {
|
if len(w.EndpointIP) == 0 {
|
||||||
return ErrWireguardEndpointIPNotSet
|
return ErrWireguardEndpointIPNotSet
|
||||||
@@ -53,12 +51,7 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {
|
|||||||
if *w.EndpointPort == 0 {
|
if *w.EndpointPort == 0 {
|
||||||
return ErrWireguardEndpointPortNotSet
|
return ErrWireguardEndpointPortNotSet
|
||||||
}
|
}
|
||||||
// EndpointPort cannot be set
|
case providers.Ivpn, providers.Mullvad, providers.Windscribe:
|
||||||
case providers.Surfshark:
|
|
||||||
if *w.EndpointPort != 0 {
|
|
||||||
return ErrWireguardEndpointPortSet
|
|
||||||
}
|
|
||||||
case providers.Airvpn, providers.Ivpn, providers.Mullvad, providers.Windscribe:
|
|
||||||
// EndpointPort is optional and can be 0
|
// EndpointPort is optional and can be 0
|
||||||
if *w.EndpointPort == 0 {
|
if *w.EndpointPort == 0 {
|
||||||
break // no custom endpoint port set
|
break // no custom endpoint port set
|
||||||
@@ -68,8 +61,6 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {
|
|||||||
}
|
}
|
||||||
var allowed []uint16
|
var allowed []uint16
|
||||||
switch vpnProvider {
|
switch vpnProvider {
|
||||||
case providers.Airvpn:
|
|
||||||
allowed = []uint16{1637, 47107}
|
|
||||||
case providers.Ivpn:
|
case providers.Ivpn:
|
||||||
allowed = []uint16{2049, 2050, 53, 30587, 41893, 48574, 58237}
|
allowed = []uint16{2049, 2050, 53, 30587, 41893, 48574, 58237}
|
||||||
case providers.Windscribe:
|
case providers.Windscribe:
|
||||||
@@ -87,9 +78,7 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {
|
|||||||
|
|
||||||
// Validate PublicKey
|
// Validate PublicKey
|
||||||
switch vpnProvider {
|
switch vpnProvider {
|
||||||
case providers.Ivpn, providers.Mullvad,
|
case providers.Ivpn, providers.Mullvad, providers.Windscribe: // public keys are baked in
|
||||||
providers.Surfshark, providers.Windscribe:
|
|
||||||
// public keys are baked in
|
|
||||||
case providers.Custom:
|
case providers.Custom:
|
||||||
if w.PublicKey == "" {
|
if w.PublicKey == "" {
|
||||||
return ErrWireguardPublicKeyNotSet
|
return ErrWireguardPublicKeyNotSet
|
||||||
|
|||||||
20
internal/configuration/sources/env/dns.go
vendored
20
internal/configuration/sources/env/dns.go
vendored
@@ -7,8 +7,8 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readDNS() (dns settings.DNS, err error) {
|
func (r *Reader) readDNS() (dns settings.DNS, err error) {
|
||||||
dns.ServerAddress, err = s.readDNSServerAddress()
|
dns.ServerAddress, err = r.readDNSServerAddress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dns, err
|
return dns, err
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ func (s *Source) readDNS() (dns settings.DNS, err error) {
|
|||||||
return dns, fmt.Errorf("environment variable DNS_KEEP_NAMESERVER: %w", err)
|
return dns, fmt.Errorf("environment variable DNS_KEEP_NAMESERVER: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dns.DoT, err = s.readDoT()
|
dns.DoT, err = r.readDoT()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dns, fmt.Errorf("DoT settings: %w", err)
|
return dns, fmt.Errorf("DoT settings: %w", err)
|
||||||
}
|
}
|
||||||
@@ -26,22 +26,22 @@ func (s *Source) readDNS() (dns settings.DNS, err error) {
|
|||||||
return dns, nil
|
return dns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readDNSServerAddress() (address net.IP, err error) {
|
func (r *Reader) readDNSServerAddress() (address net.IP, err error) {
|
||||||
key, value := s.getEnvWithRetro("DNS_ADDRESS", "DNS_PLAINTEXT_ADDRESS")
|
key, s := r.getEnvWithRetro("DNS_ADDRESS", "DNS_PLAINTEXT_ADDRESS")
|
||||||
if value == "" {
|
if s == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
address = net.ParseIP(value)
|
address = net.ParseIP(s)
|
||||||
if address == nil {
|
if address == nil {
|
||||||
return nil, fmt.Errorf("environment variable %s: %w: %s", key, ErrIPAddressParse, value)
|
return nil, fmt.Errorf("environment variable %s: %w: %s", key, ErrIPAddressParse, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO remove in v4
|
// TODO remove in v4
|
||||||
if !address.Equal(net.IPv4(127, 0, 0, 1)) { //nolint:gomnd
|
if !address.Equal(net.IPv4(127, 0, 0, 1)) { //nolint:gomnd
|
||||||
s.warner.Warn(key + " is set to " + value +
|
r.warner.Warn(key + " is set to " + s +
|
||||||
" so the DNS over TLS (DoT) server will not be used." +
|
" so the DNS over TLS (DoT) server will not be used." +
|
||||||
" The default value changed to 127.0.0.1 so it uses the internal DoT serves." +
|
" The default value changed to 127.0.0.1 so it uses the internal DoT server." +
|
||||||
" If the DoT server fails to start, the IPv4 address of the first plaintext DNS server" +
|
" If the DoT server fails to start, the IPv4 address of the first plaintext DNS server" +
|
||||||
" corresponding to the first DoT provider chosen is used.")
|
" corresponding to the first DoT provider chosen is used.")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import (
|
|||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readDNSBlacklist() (blacklist settings.DNSBlacklist, err error) {
|
func (r *Reader) readDNSBlacklist() (blacklist settings.DNSBlacklist, err error) {
|
||||||
blacklist.BlockMalicious, err = envToBoolPtr("BLOCK_MALICIOUS")
|
blacklist.BlockMalicious, err = envToBoolPtr("BLOCK_MALICIOUS")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return blacklist, fmt.Errorf("environment variable BLOCK_MALICIOUS: %w", err)
|
return blacklist, fmt.Errorf("environment variable BLOCK_MALICIOUS: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
blacklist.BlockSurveillance, err = s.readBlockSurveillance()
|
blacklist.BlockSurveillance, err = r.readBlockSurveillance()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return blacklist, err
|
return blacklist, err
|
||||||
}
|
}
|
||||||
@@ -36,8 +36,8 @@ func (s *Source) readDNSBlacklist() (blacklist settings.DNSBlacklist, err error)
|
|||||||
return blacklist, nil
|
return blacklist, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readBlockSurveillance() (blocked *bool, err error) {
|
func (r *Reader) readBlockSurveillance() (blocked *bool, err error) {
|
||||||
key, value := s.getEnvWithRetro("BLOCK_SURVEILLANCE", "BLOCK_NSA")
|
key, value := r.getEnvWithRetro("BLOCK_SURVEILLANCE", "BLOCK_NSA")
|
||||||
if value == "" {
|
if value == "" {
|
||||||
return nil, nil //nolint:nilnil
|
return nil, nil //nolint:nilnil
|
||||||
}
|
}
|
||||||
|
|||||||
4
internal/configuration/sources/env/dot.go
vendored
4
internal/configuration/sources/env/dot.go
vendored
@@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readDoT() (dot settings.DoT, err error) {
|
func (r *Reader) readDoT() (dot settings.DoT, err error) {
|
||||||
dot.Enabled, err = envToBoolPtr("DOT")
|
dot.Enabled, err = envToBoolPtr("DOT")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dot, fmt.Errorf("environment variable DOT: %w", err)
|
return dot, fmt.Errorf("environment variable DOT: %w", err)
|
||||||
@@ -22,7 +22,7 @@ func (s *Source) readDoT() (dot settings.DoT, err error) {
|
|||||||
return dot, err
|
return dot, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dot.Blacklist, err = s.readDNSBlacklist()
|
dot.Blacklist, err = r.readDNSBlacklist()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dot, err
|
return dot, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readFirewall() (firewall settings.Firewall, err error) {
|
func (r *Reader) readFirewall() (firewall settings.Firewall, err error) {
|
||||||
vpnInputPortStrings := envToCSV("FIREWALL_VPN_INPUT_PORTS")
|
vpnInputPortStrings := envToCSV("FIREWALL_VPN_INPUT_PORTS")
|
||||||
firewall.VPNInputPorts, err = stringsToPorts(vpnInputPortStrings)
|
firewall.VPNInputPorts, err = stringsToPorts(vpnInputPortStrings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -22,7 +22,7 @@ func (s *Source) readFirewall() (firewall settings.Firewall, err error) {
|
|||||||
return firewall, fmt.Errorf("environment variable FIREWALL_INPUT_PORTS: %w", err)
|
return firewall, fmt.Errorf("environment variable FIREWALL_INPUT_PORTS: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
outboundSubnetsKey, _ := s.getEnvWithRetro("FIREWALL_OUTBOUND_SUBNETS", "EXTRA_SUBNETS")
|
outboundSubnetsKey, _ := r.getEnvWithRetro("FIREWALL_OUTBOUND_SUBNETS", "EXTRA_SUBNETS")
|
||||||
outboundSubnetStrings := envToCSV(outboundSubnetsKey)
|
outboundSubnetStrings := envToCSV(outboundSubnetsKey)
|
||||||
firewall.OutboundSubnets, err = stringsToIPNets(outboundSubnetStrings)
|
firewall.OutboundSubnets, err = stringsToIPNets(outboundSubnetStrings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -73,7 +73,7 @@ func stringsToIPNets(ss []string) (ipNets []net.IPNet, err error) {
|
|||||||
for i, s := range ss {
|
for i, s := range ss {
|
||||||
ip, ipNet, err := net.ParseCIDR(s)
|
ip, ipNet, err := net.ParseCIDR(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parsing IP network %q: %w", s, err)
|
return nil, fmt.Errorf("cannot parse IP network %q: %w", s, err)
|
||||||
}
|
}
|
||||||
ipNet.IP = ip
|
ipNet.IP = ip
|
||||||
ipNets[i] = *ipNet
|
ipNets[i] = *ipNet
|
||||||
|
|||||||
16
internal/configuration/sources/env/health.go
vendored
16
internal/configuration/sources/env/health.go
vendored
@@ -7,18 +7,18 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) ReadHealth() (health settings.Health, err error) {
|
func (r *Reader) ReadHealth() (health settings.Health, err error) {
|
||||||
health.ServerAddress = getCleanedEnv("HEALTH_SERVER_ADDRESS")
|
health.ServerAddress = getCleanedEnv("HEALTH_SERVER_ADDRESS")
|
||||||
_, health.TargetAddress = s.getEnvWithRetro("HEALTH_TARGET_ADDRESS", "HEALTH_ADDRESS_TO_PING")
|
_, health.TargetAddress = r.getEnvWithRetro("HEALTH_TARGET_ADDRESS", "HEALTH_ADDRESS_TO_PING")
|
||||||
|
|
||||||
health.VPN.Initial, err = s.readDurationWithRetro(
|
health.VPN.Initial, err = r.readDurationWithRetro(
|
||||||
"HEALTH_VPN_DURATION_INITIAL",
|
"HEALTH_VPN_DURATION_INITIAL",
|
||||||
"HEALTH_OPENVPN_DURATION_INITIAL")
|
"HEALTH_OPENVPN_DURATION_INITIAL")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return health, err
|
return health, err
|
||||||
}
|
}
|
||||||
|
|
||||||
health.VPN.Addition, err = s.readDurationWithRetro(
|
health.VPN.Addition, err = r.readDurationWithRetro(
|
||||||
"HEALTH_VPN_DURATION_ADDITION",
|
"HEALTH_VPN_DURATION_ADDITION",
|
||||||
"HEALTH_OPENVPN_DURATION_ADDITION")
|
"HEALTH_OPENVPN_DURATION_ADDITION")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -28,14 +28,14 @@ func (s *Source) ReadHealth() (health settings.Health, err error) {
|
|||||||
return health, nil
|
return health, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readDurationWithRetro(envKey, retroEnvKey string) (d *time.Duration, err error) {
|
func (r *Reader) readDurationWithRetro(envKey, retroEnvKey string) (d *time.Duration, err error) {
|
||||||
envKey, value := s.getEnvWithRetro(envKey, retroEnvKey)
|
envKey, s := r.getEnvWithRetro(envKey, retroEnvKey)
|
||||||
if value == "" {
|
if s == "" {
|
||||||
return nil, nil //nolint:nilnil
|
return nil, nil //nolint:nilnil
|
||||||
}
|
}
|
||||||
|
|
||||||
d = new(time.Duration)
|
d = new(time.Duration)
|
||||||
*d, err = time.ParseDuration(value)
|
*d, err = time.ParseDuration(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("environment variable %s: %w", envKey, err)
|
return nil, fmt.Errorf("environment variable %s: %w", envKey, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ func unsetEnvKeys(envKeys []string, err error) (newErr error) {
|
|||||||
for _, envKey := range envKeys {
|
for _, envKey := range envKeys {
|
||||||
unsetErr := os.Unsetenv(envKey)
|
unsetErr := os.Unsetenv(envKey)
|
||||||
if unsetErr != nil && newErr == nil {
|
if unsetErr != nil && newErr == nil {
|
||||||
newErr = fmt.Errorf("unsetting environment variable %s: %w", envKey, unsetErr)
|
newErr = fmt.Errorf("cannot unset environment variable %s: %w", envKey, unsetErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newErr
|
return newErr
|
||||||
|
|||||||
48
internal/configuration/sources/env/httproxy.go
vendored
48
internal/configuration/sources/env/httproxy.go
vendored
@@ -7,12 +7,12 @@ import (
|
|||||||
"github.com/qdm12/govalid/binary"
|
"github.com/qdm12/govalid/binary"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readHTTPProxy() (httpProxy settings.HTTPProxy, err error) {
|
func (r *Reader) readHTTPProxy() (httpProxy settings.HTTPProxy, err error) {
|
||||||
httpProxy.User = s.readHTTProxyUser()
|
httpProxy.User = r.readHTTProxyUser()
|
||||||
httpProxy.Password = s.readHTTProxyPassword()
|
httpProxy.Password = r.readHTTProxyPassword()
|
||||||
httpProxy.ListeningAddress = s.readHTTProxyListeningAddress()
|
httpProxy.ListeningAddress = r.readHTTProxyListeningAddress()
|
||||||
|
|
||||||
httpProxy.Enabled, err = s.readHTTProxyEnabled()
|
httpProxy.Enabled, err = r.readHTTProxyEnabled()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return httpProxy, err
|
return httpProxy, err
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ func (s *Source) readHTTPProxy() (httpProxy settings.HTTPProxy, err error) {
|
|||||||
return httpProxy, fmt.Errorf("environment variable HTTPPROXY_STEALTH: %w", err)
|
return httpProxy, fmt.Errorf("environment variable HTTPPROXY_STEALTH: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
httpProxy.Log, err = s.readHTTProxyLog()
|
httpProxy.Log, err = r.readHTTProxyLog()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return httpProxy, err
|
return httpProxy, err
|
||||||
}
|
}
|
||||||
@@ -30,38 +30,38 @@ func (s *Source) readHTTPProxy() (httpProxy settings.HTTPProxy, err error) {
|
|||||||
return httpProxy, nil
|
return httpProxy, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readHTTProxyUser() (user *string) {
|
func (r *Reader) readHTTProxyUser() (user *string) {
|
||||||
_, value := s.getEnvWithRetro("HTTPPROXY_USER", "PROXY_USER", "TINYPROXY_USER")
|
_, s := r.getEnvWithRetro("HTTPPROXY_USER", "PROXY_USER", "TINYPROXY_USER")
|
||||||
if value != "" {
|
if s != "" {
|
||||||
return &value
|
return &s
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readHTTProxyPassword() (user *string) {
|
func (r *Reader) readHTTProxyPassword() (user *string) {
|
||||||
_, value := s.getEnvWithRetro("HTTPPROXY_PASSWORD", "PROXY_PASSWORD", "TINYPROXY_PASSWORD")
|
_, s := r.getEnvWithRetro("HTTPPROXY_PASSWORD", "PROXY_PASSWORD", "TINYPROXY_PASSWORD")
|
||||||
if value != "" {
|
if s != "" {
|
||||||
return &value
|
return &s
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readHTTProxyListeningAddress() (listeningAddress string) {
|
func (r *Reader) readHTTProxyListeningAddress() (listeningAddress string) {
|
||||||
key, value := s.getEnvWithRetro("HTTPPROXY_LISTENING_ADDRESS", "PROXY_PORT", "TINYPROXY_PORT", "HTTPPROXY_PORT")
|
key, value := r.getEnvWithRetro("HTTPPROXY_LISTENING_ADDRESS", "PROXY_PORT", "TINYPROXY_PORT", "HTTPPROXY_PORT")
|
||||||
if key == "HTTPPROXY_LISTENING_ADDRESS" {
|
if key == "HTTPPROXY_LISTENING_ADDRESS" {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
return ":" + value
|
return ":" + value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readHTTProxyEnabled() (enabled *bool, err error) {
|
func (r *Reader) readHTTProxyEnabled() (enabled *bool, err error) {
|
||||||
key, value := s.getEnvWithRetro("HTTPPROXY", "PROXY", "TINYPROXY")
|
key, s := r.getEnvWithRetro("HTTPPROXY", "PROXY", "TINYPROXY")
|
||||||
if value == "" {
|
if s == "" {
|
||||||
return nil, nil //nolint:nilnil
|
return nil, nil //nolint:nilnil
|
||||||
}
|
}
|
||||||
|
|
||||||
enabled = new(bool)
|
enabled = new(bool)
|
||||||
*enabled, err = binary.Validate(value)
|
*enabled, err = binary.Validate(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("environment variable %s: %w", key, err)
|
return nil, fmt.Errorf("environment variable %s: %w", key, err)
|
||||||
}
|
}
|
||||||
@@ -69,9 +69,9 @@ func (s *Source) readHTTProxyEnabled() (enabled *bool, err error) {
|
|||||||
return enabled, nil
|
return enabled, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readHTTProxyLog() (enabled *bool, err error) {
|
func (r *Reader) readHTTProxyLog() (enabled *bool, err error) {
|
||||||
key, value := s.getEnvWithRetro("HTTPPROXY_LOG", "PROXY_LOG_LEVEL", "TINYPROXY_LOG")
|
key, s := r.getEnvWithRetro("HTTPPROXY_LOG", "PROXY_LOG_LEVEL", "TINYPROXY_LOG")
|
||||||
if value == "" {
|
if s == "" {
|
||||||
return nil, nil //nolint:nilnil
|
return nil, nil //nolint:nilnil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ func (s *Source) readHTTProxyLog() (enabled *bool, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enabled = new(bool)
|
enabled = new(bool)
|
||||||
*enabled, err = binary.Validate(value, binaryOptions...)
|
*enabled, err = binary.Validate(s, binaryOptions...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("environment variable %s: %w", key, err)
|
return nil, fmt.Errorf("environment variable %s: %w", key, err)
|
||||||
}
|
}
|
||||||
|
|||||||
65
internal/configuration/sources/env/openvpn.go
vendored
65
internal/configuration/sources/env/openvpn.go
vendored
@@ -8,22 +8,21 @@ import (
|
|||||||
"github.com/qdm12/govalid/binary"
|
"github.com/qdm12/govalid/binary"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readOpenVPN() (
|
func (r *Reader) readOpenVPN() (
|
||||||
openVPN settings.OpenVPN, err error) {
|
openVPN settings.OpenVPN, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
err = unsetEnvKeys([]string{"OPENVPN_KEY", "OPENVPN_CERT",
|
err = unsetEnvKeys([]string{"OPENVPN_CLIENTKEY", "OPENVPN_CLIENTCRT"}, err)
|
||||||
"OPENVPN_KEY_PASSPHRASE", "OPENVPN_ENCRYPTED_KEY"}, err)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
openVPN.Version = getCleanedEnv("OPENVPN_VERSION")
|
openVPN.Version = getCleanedEnv("OPENVPN_VERSION")
|
||||||
openVPN.User = s.readOpenVPNUser()
|
openVPN.User = r.readOpenVPNUser()
|
||||||
openVPN.Password = s.readOpenVPNPassword()
|
openVPN.Password = r.readOpenVPNPassword()
|
||||||
confFile := getCleanedEnv("OPENVPN_CUSTOM_CONFIG")
|
confFile := getCleanedEnv("OPENVPN_CUSTOM_CONFIG")
|
||||||
if confFile != "" {
|
if confFile != "" {
|
||||||
openVPN.ConfFile = &confFile
|
openVPN.ConfFile = &confFile
|
||||||
}
|
}
|
||||||
|
|
||||||
ciphersKey, _ := s.getEnvWithRetro("OPENVPN_CIPHERS", "OPENVPN_CIPHER")
|
ciphersKey, _ := r.getEnvWithRetro("OPENVPN_CIPHERS", "OPENVPN_CIPHER")
|
||||||
openVPN.Ciphers = envToCSV(ciphersKey)
|
openVPN.Ciphers = envToCSV(ciphersKey)
|
||||||
|
|
||||||
auth := getCleanedEnv("OPENVPN_AUTH")
|
auth := getCleanedEnv("OPENVPN_AUTH")
|
||||||
@@ -31,22 +30,24 @@ func (s *Source) readOpenVPN() (
|
|||||||
openVPN.Auth = &auth
|
openVPN.Auth = &auth
|
||||||
}
|
}
|
||||||
|
|
||||||
openVPN.Cert = envToStringPtr("OPENVPN_CERT")
|
openVPN.ClientCrt = envToStringPtr("OPENVPN_CLIENTCRT")
|
||||||
openVPN.Key = envToStringPtr("OPENVPN_KEY")
|
openVPN.ClientKey = envToStringPtr("OPENVPN_CLIENTKEY")
|
||||||
openVPN.EncryptedKey = envToStringPtr("OPENVPN_ENCRYPTED_KEY")
|
|
||||||
|
|
||||||
openVPN.KeyPassphrase = s.readOpenVPNKeyPassphrase()
|
openVPN.PIAEncPreset = r.readPIAEncryptionPreset()
|
||||||
|
|
||||||
openVPN.PIAEncPreset = s.readPIAEncryptionPreset()
|
openVPN.IPv6, err = envToBoolPtr("OPENVPN_IPV6")
|
||||||
|
if err != nil {
|
||||||
|
return openVPN, fmt.Errorf("environment variable OPENVPN_IPV6: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
openVPN.MSSFix, err = envToUint16Ptr("OPENVPN_MSSFIX")
|
openVPN.MSSFix, err = envToUint16Ptr("OPENVPN_MSSFIX")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openVPN, fmt.Errorf("environment variable OPENVPN_MSSFIX: %w", err)
|
return openVPN, fmt.Errorf("environment variable OPENVPN_MSSFIX: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, openVPN.Interface = s.getEnvWithRetro("VPN_INTERFACE", "OPENVPN_INTERFACE")
|
_, openVPN.Interface = r.getEnvWithRetro("VPN_INTERFACE", "OPENVPN_INTERFACE")
|
||||||
|
|
||||||
openVPN.ProcessUser, err = s.readOpenVPNProcessUser()
|
openVPN.ProcessUser, err = r.readOpenVPNProcessUser()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openVPN, err
|
return openVPN, err
|
||||||
}
|
}
|
||||||
@@ -64,39 +65,19 @@ func (s *Source) readOpenVPN() (
|
|||||||
return openVPN, nil
|
return openVPN, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readOpenVPNUser() (user *string) {
|
func (r *Reader) readOpenVPNUser() (user string) {
|
||||||
user = new(string)
|
_, user = r.getEnvWithRetro("OPENVPN_USER", "USER")
|
||||||
_, *user = s.getEnvWithRetro("OPENVPN_USER", "USER")
|
|
||||||
if *user == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove spaces in user ID to simplify user's life, thanks @JeordyR
|
// Remove spaces in user ID to simplify user's life, thanks @JeordyR
|
||||||
*user = strings.ReplaceAll(*user, " ", "")
|
return strings.ReplaceAll(user, " ", "")
|
||||||
return user
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readOpenVPNPassword() (password *string) {
|
func (r *Reader) readOpenVPNPassword() (password string) {
|
||||||
password = new(string)
|
_, password = r.getEnvWithRetro("OPENVPN_PASSWORD", "PASSWORD")
|
||||||
_, *password = s.getEnvWithRetro("OPENVPN_PASSWORD", "PASSWORD")
|
|
||||||
if *password == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return password
|
return password
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readOpenVPNKeyPassphrase() (passphrase *string) {
|
func (r *Reader) readPIAEncryptionPreset() (presetPtr *string) {
|
||||||
passphrase = new(string)
|
_, preset := r.getEnvWithRetro(
|
||||||
*passphrase = getCleanedEnv("OPENVPN_KEY_PASSPHRASE")
|
|
||||||
if *passphrase == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return passphrase
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Source) readPIAEncryptionPreset() (presetPtr *string) {
|
|
||||||
_, preset := s.getEnvWithRetro(
|
|
||||||
"PRIVATE_INTERNET_ACCESS_OPENVPN_ENCRYPTION_PRESET",
|
"PRIVATE_INTERNET_ACCESS_OPENVPN_ENCRYPTION_PRESET",
|
||||||
"PIA_ENCRYPTION", "ENCRYPTION")
|
"PIA_ENCRYPTION", "ENCRYPTION")
|
||||||
if preset != "" {
|
if preset != "" {
|
||||||
@@ -105,8 +86,8 @@ func (s *Source) readPIAEncryptionPreset() (presetPtr *string) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readOpenVPNProcessUser() (processUser string, err error) {
|
func (r *Reader) readOpenVPNProcessUser() (processUser string, err error) {
|
||||||
key, value := s.getEnvWithRetro("OPENVPN_PROCESS_USER", "OPENVPN_ROOT")
|
key, value := r.getEnvWithRetro("OPENVPN_PROCESS_USER", "OPENVPN_ROOT")
|
||||||
if key == "OPENVPN_PROCESS_USER" {
|
if key == "OPENVPN_PROCESS_USER" {
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,32 +10,32 @@ import (
|
|||||||
"github.com/qdm12/govalid/port"
|
"github.com/qdm12/govalid/port"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readOpenVPNSelection() (
|
func (r *Reader) readOpenVPNSelection() (
|
||||||
selection settings.OpenVPNSelection, err error) {
|
selection settings.OpenVPNSelection, err error) {
|
||||||
confFile := getCleanedEnv("OPENVPN_CUSTOM_CONFIG")
|
confFile := getCleanedEnv("OPENVPN_CUSTOM_CONFIG")
|
||||||
if confFile != "" {
|
if confFile != "" {
|
||||||
selection.ConfFile = &confFile
|
selection.ConfFile = &confFile
|
||||||
}
|
}
|
||||||
|
|
||||||
selection.TCP, err = s.readOpenVPNProtocol()
|
selection.TCP, err = r.readOpenVPNProtocol()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return selection, err
|
return selection, err
|
||||||
}
|
}
|
||||||
|
|
||||||
selection.CustomPort, err = s.readOpenVPNCustomPort()
|
selection.CustomPort, err = r.readOpenVPNCustomPort()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return selection, err
|
return selection, err
|
||||||
}
|
}
|
||||||
|
|
||||||
selection.PIAEncPreset = s.readPIAEncryptionPreset()
|
selection.PIAEncPreset = r.readPIAEncryptionPreset()
|
||||||
|
|
||||||
return selection, nil
|
return selection, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrOpenVPNProtocolNotValid = errors.New("OpenVPN protocol is not valid")
|
var ErrOpenVPNProtocolNotValid = errors.New("OpenVPN protocol is not valid")
|
||||||
|
|
||||||
func (s *Source) readOpenVPNProtocol() (tcp *bool, err error) {
|
func (r *Reader) readOpenVPNProtocol() (tcp *bool, err error) {
|
||||||
envKey, protocol := s.getEnvWithRetro("OPENVPN_PROTOCOL", "PROTOCOL")
|
envKey, protocol := r.getEnvWithRetro("OPENVPN_PROTOCOL", "PROTOCOL")
|
||||||
|
|
||||||
switch strings.ToLower(protocol) {
|
switch strings.ToLower(protocol) {
|
||||||
case "":
|
case "":
|
||||||
@@ -50,14 +50,14 @@ func (s *Source) readOpenVPNProtocol() (tcp *bool, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readOpenVPNCustomPort() (customPort *uint16, err error) {
|
func (r *Reader) readOpenVPNCustomPort() (customPort *uint16, err error) {
|
||||||
key, value := s.getEnvWithRetro("VPN_ENDPOINT_PORT", "PORT", "OPENVPN_PORT")
|
key, s := r.getEnvWithRetro("VPN_ENDPOINT_PORT", "PORT", "OPENVPN_PORT")
|
||||||
if value == "" {
|
if s == "" {
|
||||||
return nil, nil //nolint:nilnil
|
return nil, nil //nolint:nilnil
|
||||||
}
|
}
|
||||||
|
|
||||||
customPort = new(uint16)
|
customPort = new(uint16)
|
||||||
*customPort, err = port.Validate(value)
|
*customPort, err = port.Validate(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("environment variable %s: %w", key, err)
|
return nil, fmt.Errorf("environment variable %s: %w", key, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readPortForward() (
|
func (r *Reader) readPortForward() (
|
||||||
portForwarding settings.PortForwarding, err error) {
|
portForwarding settings.PortForwarding, err error) {
|
||||||
key, _ := s.getEnvWithRetro(
|
key, _ := r.getEnvWithRetro(
|
||||||
"PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING",
|
"PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING",
|
||||||
"PORT_FORWARDING")
|
"PORT_FORWARDING")
|
||||||
portForwarding.Enabled, err = envToBoolPtr(key)
|
portForwarding.Enabled, err = envToBoolPtr(key)
|
||||||
@@ -16,7 +16,7 @@ func (s *Source) readPortForward() (
|
|||||||
return portForwarding, fmt.Errorf("environment variable %s: %w", key, err)
|
return portForwarding, fmt.Errorf("environment variable %s: %w", key, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, value := s.getEnvWithRetro(
|
_, value := r.getEnvWithRetro(
|
||||||
"PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING_STATUS_FILE",
|
"PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING_STATUS_FILE",
|
||||||
"PORT_FORWARDING_STATUS_FILE")
|
"PORT_FORWARDING_STATUS_FILE")
|
||||||
if value != "" {
|
if value != "" {
|
||||||
|
|||||||
31
internal/configuration/sources/env/provider.go
vendored
31
internal/configuration/sources/env/provider.go
vendored
@@ -9,19 +9,19 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
"github.com/qdm12/gluetun/internal/constants/vpn"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readProvider(vpnType string) (provider settings.Provider, err error) {
|
func (r *Reader) readProvider(vpnType string) (provider settings.Provider, err error) {
|
||||||
provider.Name = s.readVPNServiceProvider(vpnType)
|
provider.Name = r.readVPNServiceProvider(vpnType)
|
||||||
var providerName string
|
var providerName string
|
||||||
if provider.Name != nil {
|
if provider.Name != nil {
|
||||||
providerName = *provider.Name
|
providerName = *provider.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.ServerSelection, err = s.readServerSelection(providerName, vpnType)
|
provider.ServerSelection, err = r.readServerSelection(providerName, vpnType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return provider, fmt.Errorf("server selection: %w", err)
|
return provider, fmt.Errorf("server selection: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.PortForwarding, err = s.readPortForward()
|
provider.PortForwarding, err = r.readPortForward()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return provider, fmt.Errorf("port forwarding: %w", err)
|
return provider, fmt.Errorf("port forwarding: %w", err)
|
||||||
}
|
}
|
||||||
@@ -29,20 +29,17 @@ func (s *Source) readProvider(vpnType string) (provider settings.Provider, err e
|
|||||||
return provider, nil
|
return provider, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readVPNServiceProvider(vpnType string) (vpnProviderPtr *string) {
|
func (r *Reader) readVPNServiceProvider(vpnType string) (vpnProviderPtr *string) {
|
||||||
_, value := s.getEnvWithRetro("VPN_SERVICE_PROVIDER", "VPNSP")
|
_, s := r.getEnvWithRetro("VPN_SERVICE_PROVIDER", "VPNSP")
|
||||||
if value == "" {
|
s = strings.ToLower(s)
|
||||||
if vpnType != vpn.Wireguard && getCleanedEnv("OPENVPN_CUSTOM_CONFIG") != "" {
|
switch {
|
||||||
// retro compatibility
|
case vpnType != vpn.Wireguard &&
|
||||||
return stringPtr(providers.Custom)
|
getCleanedEnv("OPENVPN_CUSTOM_CONFIG") != "": // retro compatibility
|
||||||
}
|
return stringPtr(providers.Custom)
|
||||||
|
case s == "":
|
||||||
return nil
|
return nil
|
||||||
}
|
case s == "pia": // retro compatibility
|
||||||
|
|
||||||
value = strings.ToLower(value)
|
|
||||||
if value == "pia" { // retro compatibility
|
|
||||||
return stringPtr(providers.PrivateInternetAccess)
|
return stringPtr(providers.PrivateInternetAccess)
|
||||||
}
|
}
|
||||||
|
return stringPtr(s)
|
||||||
return stringPtr(value)
|
|
||||||
}
|
}
|
||||||
|
|||||||
12
internal/configuration/sources/env/publicip.go
vendored
12
internal/configuration/sources/env/publicip.go
vendored
@@ -7,13 +7,13 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readPublicIP() (publicIP settings.PublicIP, err error) {
|
func (r *Reader) readPublicIP() (publicIP settings.PublicIP, err error) {
|
||||||
publicIP.Period, err = readPublicIPPeriod()
|
publicIP.Period, err = readPublicIPPeriod()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return publicIP, err
|
return publicIP, err
|
||||||
}
|
}
|
||||||
|
|
||||||
publicIP.IPFilepath = s.readPublicIPFilepath()
|
publicIP.IPFilepath = r.readPublicIPFilepath()
|
||||||
|
|
||||||
return publicIP, nil
|
return publicIP, nil
|
||||||
}
|
}
|
||||||
@@ -33,10 +33,10 @@ func readPublicIPPeriod() (period *time.Duration, err error) {
|
|||||||
return period, nil
|
return period, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readPublicIPFilepath() (filepath *string) {
|
func (r *Reader) readPublicIPFilepath() (filepath *string) {
|
||||||
_, value := s.getEnvWithRetro("PUBLICIP_FILE", "IP_STATUS_FILE")
|
_, s := r.getEnvWithRetro("PUBLICIP_FILE", "IP_STATUS_FILE")
|
||||||
if value != "" {
|
if s != "" {
|
||||||
return &value
|
return &s
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
39
internal/configuration/sources/env/reader.go
vendored
39
internal/configuration/sources/env/reader.go
vendored
@@ -2,9 +2,12 @@ package env
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration/sources"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Source struct {
|
var _ sources.Source = (*Reader)(nil)
|
||||||
|
|
||||||
|
type Reader struct {
|
||||||
warner Warner
|
warner Warner
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,36 +15,36 @@ type Warner interface {
|
|||||||
Warn(s string)
|
Warn(s string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(warner Warner) *Source {
|
func New(warner Warner) *Reader {
|
||||||
return &Source{
|
return &Reader{
|
||||||
warner: warner,
|
warner: warner,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) String() string { return "environment variables" }
|
func (r *Reader) String() string { return "environment variables" }
|
||||||
|
|
||||||
func (s *Source) Read() (settings settings.Settings, err error) {
|
func (r *Reader) Read() (settings settings.Settings, err error) {
|
||||||
settings.VPN, err = s.readVPN()
|
settings.VPN, err = r.readVPN()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, err
|
return settings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Firewall, err = s.readFirewall()
|
settings.Firewall, err = r.readFirewall()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, err
|
return settings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.System, err = s.readSystem()
|
settings.System, err = r.readSystem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, err
|
return settings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Health, err = s.ReadHealth()
|
settings.Health, err = r.ReadHealth()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, err
|
return settings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.HTTPProxy, err = s.readHTTPProxy()
|
settings.HTTPProxy, err = r.readHTTPProxy()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, err
|
return settings, err
|
||||||
}
|
}
|
||||||
@@ -51,7 +54,7 @@ func (s *Source) Read() (settings settings.Settings, err error) {
|
|||||||
return settings, err
|
return settings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.PublicIP, err = s.readPublicIP()
|
settings.PublicIP, err = r.readPublicIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, err
|
return settings, err
|
||||||
}
|
}
|
||||||
@@ -66,17 +69,17 @@ func (s *Source) Read() (settings settings.Settings, err error) {
|
|||||||
return settings, err
|
return settings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Shadowsocks, err = s.readShadowsocks()
|
settings.Shadowsocks, err = r.readShadowsocks()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, err
|
return settings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.DNS, err = s.readDNS()
|
settings.DNS, err = r.readDNS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, err
|
return settings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ControlServer, err = s.readControlServer()
|
settings.ControlServer, err = r.readControlServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, err
|
return settings, err
|
||||||
}
|
}
|
||||||
@@ -89,8 +92,8 @@ func (s *Source) Read() (settings settings.Settings, err error) {
|
|||||||
return settings, nil
|
return settings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) onRetroActive(oldKey, newKey string) {
|
func (r *Reader) onRetroActive(oldKey, newKey string) {
|
||||||
s.warner.Warn(
|
r.warner.Warn(
|
||||||
"You are using the old environment variable " + oldKey +
|
"You are using the old environment variable " + oldKey +
|
||||||
", please consider changing it to " + newKey)
|
", please consider changing it to " + newKey)
|
||||||
}
|
}
|
||||||
@@ -101,14 +104,14 @@ func (s *Source) onRetroActive(oldKey, newKey string) {
|
|||||||
// and end on returning the value corresponding to the currentKey.
|
// and end on returning the value corresponding to the currentKey.
|
||||||
// Note retroKeys should be in order from oldest to most
|
// Note retroKeys should be in order from oldest to most
|
||||||
// recent retro-compatibility key.
|
// recent retro-compatibility key.
|
||||||
func (s *Source) getEnvWithRetro(currentKey string,
|
func (r *Reader) getEnvWithRetro(currentKey string,
|
||||||
retroKeys ...string) (key, value string) {
|
retroKeys ...string) (key, value string) {
|
||||||
// We check retro-compatibility keys first since
|
// We check retro-compatibility keys first since
|
||||||
// the current key might be set in the Dockerfile.
|
// the current key might be set in the Dockerfile.
|
||||||
for _, key = range retroKeys {
|
for _, key = range retroKeys {
|
||||||
value = getCleanedEnv(key)
|
value = getCleanedEnv(key)
|
||||||
if value != "" {
|
if value != "" {
|
||||||
s.onRetroActive(key, currentKey)
|
r.onRetroActive(key, currentKey)
|
||||||
return key, value
|
return key, value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
internal/configuration/sources/env/server.go
vendored
14
internal/configuration/sources/env/server.go
vendored
@@ -7,13 +7,13 @@ import (
|
|||||||
"github.com/qdm12/govalid/binary"
|
"github.com/qdm12/govalid/binary"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readControlServer() (controlServer settings.ControlServer, err error) {
|
func (r *Reader) readControlServer() (controlServer settings.ControlServer, err error) {
|
||||||
controlServer.Log, err = readControlServerLog()
|
controlServer.Log, err = readControlServerLog()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return controlServer, err
|
return controlServer, err
|
||||||
}
|
}
|
||||||
|
|
||||||
controlServer.Address = s.readControlServerAddress()
|
controlServer.Address = r.readControlServerAddress()
|
||||||
|
|
||||||
return controlServer, nil
|
return controlServer, nil
|
||||||
}
|
}
|
||||||
@@ -32,17 +32,17 @@ func readControlServerLog() (enabled *bool, err error) {
|
|||||||
return &log, nil
|
return &log, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readControlServerAddress() (address *string) {
|
func (r *Reader) readControlServerAddress() (address *string) {
|
||||||
key, value := s.getEnvWithRetro("HTTP_CONTROL_SERVER_ADDRESS", "HTTP_CONTROL_SERVER_PORT")
|
key, s := r.getEnvWithRetro("HTTP_CONTROL_SERVER_ADDRESS", "HTTP_CONTROL_SERVER_PORT")
|
||||||
if value == "" {
|
if s == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if key == "HTTP_CONTROL_SERVER_ADDRESS" {
|
if key == "HTTP_CONTROL_SERVER_ADDRESS" {
|
||||||
return &value
|
return &s
|
||||||
}
|
}
|
||||||
|
|
||||||
address = new(string)
|
address = new(string)
|
||||||
*address = ":" + value
|
*address = ":" + s
|
||||||
return address
|
return address
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,45 +15,44 @@ var (
|
|||||||
ErrServerNumberNotValid = errors.New("server number is not valid")
|
ErrServerNumberNotValid = errors.New("server number is not valid")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readServerSelection(vpnProvider, vpnType string) (
|
func (r *Reader) readServerSelection(vpnProvider, vpnType string) (
|
||||||
ss settings.ServerSelection, err error) {
|
ss settings.ServerSelection, err error) {
|
||||||
ss.VPN = vpnType
|
ss.VPN = vpnType
|
||||||
|
|
||||||
ss.TargetIP, err = s.readOpenVPNTargetIP()
|
ss.TargetIP, err = r.readOpenVPNTargetIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ss, err
|
return ss, err
|
||||||
}
|
}
|
||||||
|
|
||||||
countriesKey, _ := s.getEnvWithRetro("SERVER_COUNTRIES", "COUNTRY")
|
countriesKey, _ := r.getEnvWithRetro("SERVER_COUNTRIES", "COUNTRY")
|
||||||
ss.Countries = envToCSV(countriesKey)
|
ss.Countries = envToCSV(countriesKey)
|
||||||
if vpnProvider == providers.Cyberghost && len(ss.Countries) == 0 {
|
if vpnProvider == providers.Cyberghost && len(ss.Countries) == 0 {
|
||||||
// Retro-compatibility for Cyberghost using the REGION variable
|
// Retro-compatibility for Cyberghost using the REGION variable
|
||||||
ss.Countries = envToCSV("REGION")
|
ss.Countries = envToCSV("REGION")
|
||||||
if len(ss.Countries) > 0 {
|
if len(ss.Countries) > 0 {
|
||||||
s.onRetroActive("REGION", "SERVER_COUNTRIES")
|
r.onRetroActive("REGION", "SERVER_COUNTRIES")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
regionsKey, _ := s.getEnvWithRetro("SERVER_REGIONS", "REGION")
|
regionsKey, _ := r.getEnvWithRetro("SERVER_REGIONS", "REGION")
|
||||||
ss.Regions = envToCSV(regionsKey)
|
ss.Regions = envToCSV(regionsKey)
|
||||||
|
|
||||||
citiesKey, _ := s.getEnvWithRetro("SERVER_CITIES", "CITY")
|
citiesKey, _ := r.getEnvWithRetro("SERVER_CITIES", "CITY")
|
||||||
ss.Cities = envToCSV(citiesKey)
|
ss.Cities = envToCSV(citiesKey)
|
||||||
|
|
||||||
ss.ISPs = envToCSV("ISP")
|
ss.ISPs = envToCSV("ISP")
|
||||||
|
|
||||||
hostnamesKey, _ := s.getEnvWithRetro("SERVER_HOSTNAMES", "SERVER_HOSTNAME")
|
hostnamesKey, _ := r.getEnvWithRetro("SERVER_HOSTNAMES", "SERVER_HOSTNAME")
|
||||||
ss.Hostnames = envToCSV(hostnamesKey)
|
ss.Hostnames = envToCSV(hostnamesKey)
|
||||||
|
|
||||||
serverNamesKey, _ := s.getEnvWithRetro("SERVER_NAMES", "SERVER_NAME")
|
serverNamesKey, _ := r.getEnvWithRetro("SERVER_NAMES", "SERVER_NAME")
|
||||||
ss.Names = envToCSV(serverNamesKey)
|
ss.Names = envToCSV(serverNamesKey)
|
||||||
|
|
||||||
if csv := getCleanedEnv("SERVER_NUMBER"); csv != "" {
|
if csv := getCleanedEnv("SERVER_NUMBER"); csv != "" {
|
||||||
numbersStrings := strings.Split(csv, ",")
|
numbersStrings := strings.Split(csv, ",")
|
||||||
numbers := make([]uint16, len(numbersStrings))
|
numbers := make([]uint16, len(numbersStrings))
|
||||||
for i, numberString := range numbersStrings {
|
for i, numberString := range numbersStrings {
|
||||||
const base, bitSize = 10, 16
|
number, err := strconv.Atoi(numberString)
|
||||||
number, err := strconv.ParseInt(numberString, base, bitSize)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ss, fmt.Errorf("%w: %s",
|
return ss, fmt.Errorf("%w: %s",
|
||||||
ErrServerNumberNotValid, numberString)
|
ErrServerNumberNotValid, numberString)
|
||||||
@@ -67,7 +66,7 @@ func (s *Source) readServerSelection(vpnProvider, vpnType string) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mullvad only
|
// Mullvad only
|
||||||
ss.OwnedOnly, err = s.readOwnedOnly()
|
ss.OwnedOnly, err = r.readOwnedOnly()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ss, err
|
return ss, err
|
||||||
}
|
}
|
||||||
@@ -78,12 +77,6 @@ func (s *Source) readServerSelection(vpnProvider, vpnType string) (
|
|||||||
return ss, fmt.Errorf("environment variable FREE_ONLY: %w", err)
|
return ss, fmt.Errorf("environment variable FREE_ONLY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VPNSecure only
|
|
||||||
ss.PremiumOnly, err = envToBoolPtr("PREMIUM_ONLY")
|
|
||||||
if err != nil {
|
|
||||||
return ss, fmt.Errorf("environment variable PREMIUM_ONLY: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// VPNUnlimited only
|
// VPNUnlimited only
|
||||||
ss.MultiHopOnly, err = envToBoolPtr("MULTIHOP_ONLY")
|
ss.MultiHopOnly, err = envToBoolPtr("MULTIHOP_ONLY")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -96,12 +89,12 @@ func (s *Source) readServerSelection(vpnProvider, vpnType string) (
|
|||||||
return ss, fmt.Errorf("environment variable STREAM_ONLY: %w", err)
|
return ss, fmt.Errorf("environment variable STREAM_ONLY: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ss.OpenVPN, err = s.readOpenVPNSelection()
|
ss.OpenVPN, err = r.readOpenVPNSelection()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ss, err
|
return ss, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ss.Wireguard, err = s.readWireguardSelection()
|
ss.Wireguard, err = r.readWireguardSelection()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ss, err
|
return ss, err
|
||||||
}
|
}
|
||||||
@@ -113,23 +106,23 @@ var (
|
|||||||
ErrInvalidIP = errors.New("invalid IP address")
|
ErrInvalidIP = errors.New("invalid IP address")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readOpenVPNTargetIP() (ip net.IP, err error) {
|
func (r *Reader) readOpenVPNTargetIP() (ip net.IP, err error) {
|
||||||
envKey, value := s.getEnvWithRetro("VPN_ENDPOINT_IP", "OPENVPN_TARGET_IP")
|
envKey, s := r.getEnvWithRetro("VPN_ENDPOINT_IP", "OPENVPN_TARGET_IP")
|
||||||
if value == "" {
|
if s == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ip = net.ParseIP(value)
|
ip = net.ParseIP(s)
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return nil, fmt.Errorf("environment variable %s: %w: %s",
|
return nil, fmt.Errorf("environment variable %s: %w: %s",
|
||||||
envKey, ErrInvalidIP, value)
|
envKey, ErrInvalidIP, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ip, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readOwnedOnly() (ownedOnly *bool, err error) {
|
func (r *Reader) readOwnedOnly() (ownedOnly *bool, err error) {
|
||||||
envKey, _ := s.getEnvWithRetro("OWNED_ONLY", "OWNED")
|
envKey, _ := r.getEnvWithRetro("OWNED_ONLY", "OWNED")
|
||||||
ownedOnly, err = envToBoolPtr(envKey)
|
ownedOnly, err = envToBoolPtr(envKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("environment variable %s: %w", envKey, err)
|
return nil, fmt.Errorf("environment variable %s: %w", envKey, err)
|
||||||
|
|||||||
@@ -7,25 +7,25 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readShadowsocks() (shadowsocks settings.Shadowsocks, err error) {
|
func (r *Reader) readShadowsocks() (shadowsocks settings.Shadowsocks, err error) {
|
||||||
shadowsocks.Enabled, err = envToBoolPtr("SHADOWSOCKS")
|
shadowsocks.Enabled, err = envToBoolPtr("SHADOWSOCKS")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return shadowsocks, fmt.Errorf("environment variable SHADOWSOCKS: %w", err)
|
return shadowsocks, fmt.Errorf("environment variable SHADOWSOCKS: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowsocks.Address = s.readShadowsocksAddress()
|
shadowsocks.Address = r.readShadowsocksAddress()
|
||||||
shadowsocks.LogAddresses, err = envToBoolPtr("SHADOWSOCKS_LOG")
|
shadowsocks.LogAddresses, err = envToBoolPtr("SHADOWSOCKS_LOG")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return shadowsocks, fmt.Errorf("environment variable SHADOWSOCKS_LOG: %w", err)
|
return shadowsocks, fmt.Errorf("environment variable SHADOWSOCKS_LOG: %w", err)
|
||||||
}
|
}
|
||||||
shadowsocks.CipherName = s.readShadowsocksCipher()
|
shadowsocks.CipherName = r.readShadowsocksCipher()
|
||||||
shadowsocks.Password = envToStringPtr("SHADOWSOCKS_PASSWORD")
|
shadowsocks.Password = envToStringPtr("SHADOWSOCKS_PASSWORD")
|
||||||
|
|
||||||
return shadowsocks, nil
|
return shadowsocks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readShadowsocksAddress() (address string) {
|
func (r *Reader) readShadowsocksAddress() (address string) {
|
||||||
key, value := s.getEnvWithRetro("SHADOWSOCKS_LISTENING_ADDRESS", "SHADOWSOCKS_PORT")
|
key, value := r.getEnvWithRetro("SHADOWSOCKS_LISTENING_ADDRESS", "SHADOWSOCKS_PORT")
|
||||||
if value == "" {
|
if value == "" {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@ func (s *Source) readShadowsocksAddress() (address string) {
|
|||||||
return ":" + value
|
return ":" + value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readShadowsocksCipher() (cipher string) {
|
func (r *Reader) readShadowsocksCipher() (cipher string) {
|
||||||
_, cipher = s.getEnvWithRetro("SHADOWSOCKS_CIPHER", "SHADOWSOCKS_METHOD")
|
_, cipher = r.getEnvWithRetro("SHADOWSOCKS_CIPHER", "SHADOWSOCKS_METHOD")
|
||||||
return strings.ToLower(cipher)
|
return strings.ToLower(cipher)
|
||||||
}
|
}
|
||||||
|
|||||||
10
internal/configuration/sources/env/system.go
vendored
10
internal/configuration/sources/env/system.go
vendored
@@ -14,13 +14,13 @@ var (
|
|||||||
ErrSystemTimezoneNotValid = errors.New("timezone is not valid")
|
ErrSystemTimezoneNotValid = errors.New("timezone is not valid")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readSystem() (system settings.System, err error) {
|
func (r *Reader) readSystem() (system settings.System, err error) {
|
||||||
system.PUID, err = s.readID("PUID", "UID")
|
system.PUID, err = r.readID("PUID", "UID")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return system, err
|
return system, err
|
||||||
}
|
}
|
||||||
|
|
||||||
system.PGID, err = s.readID("PGID", "GID")
|
system.PGID, err = r.readID("PGID", "GID")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return system, err
|
return system, err
|
||||||
}
|
}
|
||||||
@@ -32,9 +32,9 @@ func (s *Source) readSystem() (system settings.System, err error) {
|
|||||||
|
|
||||||
var ErrSystemIDNotValid = errors.New("system ID is not valid")
|
var ErrSystemIDNotValid = errors.New("system ID is not valid")
|
||||||
|
|
||||||
func (s *Source) readID(key, retroKey string) (
|
func (r *Reader) readID(key, retroKey string) (
|
||||||
id *uint32, err error) {
|
id *uint32, err error) {
|
||||||
idEnvKey, idString := s.getEnvWithRetro(key, retroKey)
|
idEnvKey, idString := r.getEnvWithRetro(key, retroKey)
|
||||||
if idString == "" {
|
if idString == "" {
|
||||||
return nil, nil //nolint:nilnil
|
return nil, nil //nolint:nilnil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ func Test_Reader_readID(t *testing.T) {
|
|||||||
setTestEnv(t, key, testCase.keyValue)
|
setTestEnv(t, key, testCase.keyValue)
|
||||||
setTestEnv(t, retroKey, testCase.retroValue)
|
setTestEnv(t, retroKey, testCase.retroValue)
|
||||||
|
|
||||||
source := &Source{}
|
reader := &Reader{}
|
||||||
id, err := source.readID(key, retroKey)
|
id, err := reader.readID(key, retroKey)
|
||||||
|
|
||||||
assert.ErrorIs(t, err, testCase.errWrapped)
|
assert.ErrorIs(t, err, testCase.errWrapped)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
8
internal/configuration/sources/env/vpn.go
vendored
8
internal/configuration/sources/env/vpn.go
vendored
@@ -7,20 +7,20 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readVPN() (vpn settings.VPN, err error) {
|
func (r *Reader) readVPN() (vpn settings.VPN, err error) {
|
||||||
vpn.Type = strings.ToLower(getCleanedEnv("VPN_TYPE"))
|
vpn.Type = strings.ToLower(getCleanedEnv("VPN_TYPE"))
|
||||||
|
|
||||||
vpn.Provider, err = s.readProvider(vpn.Type)
|
vpn.Provider, err = r.readProvider(vpn.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return vpn, fmt.Errorf("VPN provider: %w", err)
|
return vpn, fmt.Errorf("VPN provider: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
vpn.OpenVPN, err = s.readOpenVPN()
|
vpn.OpenVPN, err = r.readOpenVPN()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return vpn, fmt.Errorf("OpenVPN: %w", err)
|
return vpn, fmt.Errorf("OpenVPN: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
vpn.Wireguard, err = s.readWireguard()
|
vpn.Wireguard, err = r.readWireguard()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return vpn, fmt.Errorf("wireguard: %w", err)
|
return vpn, fmt.Errorf("wireguard: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
13
internal/configuration/sources/env/wireguard.go
vendored
13
internal/configuration/sources/env/wireguard.go
vendored
@@ -3,29 +3,27 @@ package env
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readWireguard() (wireguard settings.Wireguard, err error) {
|
func (r *Reader) readWireguard() (wireguard settings.Wireguard, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
err = unsetEnvKeys([]string{"WIREGUARD_PRIVATE_KEY", "WIREGUARD_PRESHARED_KEY"}, err)
|
err = unsetEnvKeys([]string{"WIREGUARD_PRIVATE_KEY", "WIREGUARD_PRESHARED_KEY"}, err)
|
||||||
}()
|
}()
|
||||||
wireguard.PrivateKey = envToStringPtr("WIREGUARD_PRIVATE_KEY")
|
wireguard.PrivateKey = envToStringPtr("WIREGUARD_PRIVATE_KEY")
|
||||||
wireguard.PreSharedKey = envToStringPtr("WIREGUARD_PRESHARED_KEY")
|
wireguard.PreSharedKey = envToStringPtr("WIREGUARD_PRESHARED_KEY")
|
||||||
_, wireguard.Interface = s.getEnvWithRetro("VPN_INTERFACE", "WIREGUARD_INTERFACE")
|
_, wireguard.Interface = r.getEnvWithRetro("VPN_INTERFACE", "WIREGUARD_INTERFACE")
|
||||||
wireguard.Implementation = os.Getenv("WIREGUARD_IMPLEMENTATION")
|
wireguard.Addresses, err = r.readWireguardAddresses()
|
||||||
wireguard.Addresses, err = s.readWireguardAddresses()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wireguard, err // already wrapped
|
return wireguard, err // already wrapped
|
||||||
}
|
}
|
||||||
return wireguard, nil
|
return wireguard, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readWireguardAddresses() (addresses []net.IPNet, err error) {
|
func (r *Reader) readWireguardAddresses() (addresses []net.IPNet, err error) {
|
||||||
key, addressesCSV := s.getEnvWithRetro("WIREGUARD_ADDRESSES", "WIREGUARD_ADDRESS")
|
key, addressesCSV := r.getEnvWithRetro("WIREGUARD_ADDRESSES", "WIREGUARD_ADDRESS")
|
||||||
if addressesCSV == "" {
|
if addressesCSV == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -33,7 +31,6 @@ func (s *Source) readWireguardAddresses() (addresses []net.IPNet, err error) {
|
|||||||
addressStrings := strings.Split(addressesCSV, ",")
|
addressStrings := strings.Split(addressesCSV, ",")
|
||||||
addresses = make([]net.IPNet, len(addressStrings))
|
addresses = make([]net.IPNet, len(addressStrings))
|
||||||
for i, addressString := range addressStrings {
|
for i, addressString := range addressStrings {
|
||||||
addressString = strings.TrimSpace(addressString)
|
|
||||||
ip, ipNet, err := net.ParseCIDR(addressString)
|
ip, ipNet, err := net.ParseCIDR(addressString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("environment variable %s: %w", key, err)
|
return nil, fmt.Errorf("environment variable %s: %w", key, err)
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ import (
|
|||||||
"github.com/qdm12/govalid/port"
|
"github.com/qdm12/govalid/port"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readWireguardSelection() (
|
func (r *Reader) readWireguardSelection() (
|
||||||
selection settings.WireguardSelection, err error) {
|
selection settings.WireguardSelection, err error) {
|
||||||
selection.EndpointIP, err = s.readWireguardEndpointIP()
|
selection.EndpointIP, err = r.readWireguardEndpointIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return selection, err
|
return selection, err
|
||||||
}
|
}
|
||||||
|
|
||||||
selection.EndpointPort, err = s.readWireguardCustomPort()
|
selection.EndpointPort, err = r.readWireguardCustomPort()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return selection, err
|
return selection, err
|
||||||
}
|
}
|
||||||
@@ -28,29 +28,29 @@ func (s *Source) readWireguardSelection() (
|
|||||||
|
|
||||||
var ErrIPAddressParse = errors.New("cannot parse IP address")
|
var ErrIPAddressParse = errors.New("cannot parse IP address")
|
||||||
|
|
||||||
func (s *Source) readWireguardEndpointIP() (endpointIP net.IP, err error) {
|
func (r *Reader) readWireguardEndpointIP() (endpointIP net.IP, err error) {
|
||||||
key, value := s.getEnvWithRetro("VPN_ENDPOINT_IP", "WIREGUARD_ENDPOINT_IP")
|
key, s := r.getEnvWithRetro("VPN_ENDPOINT_IP", "WIREGUARD_ENDPOINT_IP")
|
||||||
if value == "" {
|
if s == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
endpointIP = net.ParseIP(value)
|
endpointIP = net.ParseIP(s)
|
||||||
if endpointIP == nil {
|
if endpointIP == nil {
|
||||||
return nil, fmt.Errorf("environment variable %s: %w: %s",
|
return nil, fmt.Errorf("environment variable %s: %w: %s",
|
||||||
key, ErrIPAddressParse, value)
|
key, ErrIPAddressParse, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
return endpointIP, nil
|
return endpointIP, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readWireguardCustomPort() (customPort *uint16, err error) {
|
func (r *Reader) readWireguardCustomPort() (customPort *uint16, err error) {
|
||||||
key, value := s.getEnvWithRetro("VPN_ENDPOINT_PORT", "WIREGUARD_ENDPOINT_PORT")
|
key, s := r.getEnvWithRetro("VPN_ENDPOINT_PORT", "WIREGUARD_ENDPOINT_PORT")
|
||||||
if value == "" {
|
if s == "" {
|
||||||
return nil, nil //nolint:nilnil
|
return nil, nil //nolint:nilnil
|
||||||
}
|
}
|
||||||
|
|
||||||
customPort = new(uint16)
|
customPort = new(uint16)
|
||||||
*customPort, err = port.Validate(value)
|
*customPort, err = port.Validate(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("environment variable %s: %w", key, err)
|
return nil, fmt.Errorf("environment variable %s: %w", key, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ package files
|
|||||||
|
|
||||||
import "github.com/qdm12/gluetun/internal/configuration/settings"
|
import "github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
|
|
||||||
func (s *Source) ReadHealth() (settings settings.Health, err error) { return settings, nil }
|
func (r *Reader) ReadHealth() (settings settings.Health, err error) { return settings, nil }
|
||||||
|
|||||||
@@ -11,23 +11,18 @@ const (
|
|||||||
OpenVPNClientKeyPath = "/gluetun/client.key"
|
OpenVPNClientKeyPath = "/gluetun/client.key"
|
||||||
// OpenVPNClientCertificatePath is the OpenVPN client certificate filepath.
|
// OpenVPNClientCertificatePath is the OpenVPN client certificate filepath.
|
||||||
OpenVPNClientCertificatePath = "/gluetun/client.crt"
|
OpenVPNClientCertificatePath = "/gluetun/client.crt"
|
||||||
openVPNEncryptedKey = "/gluetun/openvpn_encrypted_key"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readOpenVPN() (settings settings.OpenVPN, err error) {
|
func (r *Reader) readOpenVPN() (settings settings.OpenVPN, err error) {
|
||||||
settings.Key, err = readPEMFile(OpenVPNClientKeyPath)
|
settings.ClientKey, err = readPEMFile(OpenVPNClientKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("client key: %w", err)
|
return settings, fmt.Errorf("client key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Cert, err = readPEMFile(OpenVPNClientCertificatePath)
|
settings.ClientCrt, err = readPEMFile(OpenVPNClientCertificatePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("client certificate: %w", err)
|
return settings, fmt.Errorf("client certificate: %w", err)
|
||||||
}
|
}
|
||||||
settings.EncryptedKey, err = readPEMFile(openVPNEncryptedKey)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading encrypted key file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return settings, nil
|
return settings, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,23 +2,26 @@ package files
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration/sources"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Source struct{}
|
var _ sources.Source = (*Reader)(nil)
|
||||||
|
|
||||||
func New() *Source {
|
type Reader struct{}
|
||||||
return &Source{}
|
|
||||||
|
func New() *Reader {
|
||||||
|
return &Reader{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) String() string { return "files" }
|
func (r *Reader) String() string { return "files" }
|
||||||
|
|
||||||
func (s *Source) Read() (settings settings.Settings, err error) {
|
func (r *Reader) Read() (settings settings.Settings, err error) {
|
||||||
settings.VPN, err = s.readVPN()
|
settings.VPN, err = r.readVPN()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, err
|
return settings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.System, err = s.readSystem()
|
settings.System, err = r.readSystem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, err
|
return settings, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readSystem() (system settings.System, err error) {
|
func (r *Reader) readSystem() (system settings.System, err error) {
|
||||||
// TODO timezone from /etc/localtime
|
// TODO timezone from /etc/localtime
|
||||||
return system, nil
|
return system, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readVPN() (vpn settings.VPN, err error) {
|
func (r *Reader) readVPN() (vpn settings.VPN, err error) {
|
||||||
vpn.OpenVPN, err = s.readOpenVPN()
|
vpn.OpenVPN, err = r.readOpenVPN()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return vpn, fmt.Errorf("OpenVPN: %w", err)
|
return vpn, fmt.Errorf("OpenVPN: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,29 @@
|
|||||||
package merge
|
package mux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration/sources"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigSource interface {
|
var _ sources.Source = (*Reader)(nil)
|
||||||
Read() (settings settings.Settings, err error)
|
|
||||||
ReadHealth() (settings settings.Health, err error)
|
type Reader struct {
|
||||||
String() string
|
sources []sources.Source
|
||||||
}
|
}
|
||||||
|
|
||||||
type Source struct {
|
func New(sources ...sources.Source) *Reader {
|
||||||
sources []ConfigSource
|
return &Reader{
|
||||||
}
|
|
||||||
|
|
||||||
func New(sources ...ConfigSource) *Source {
|
|
||||||
return &Source{
|
|
||||||
sources: sources,
|
sources: sources,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) String() string {
|
func (r *Reader) String() string {
|
||||||
sources := make([]string, len(s.sources))
|
sources := make([]string, len(r.sources))
|
||||||
for i := range s.sources {
|
for i := range r.sources {
|
||||||
sources[i] = s.sources[i].String()
|
sources[i] = r.sources[i].String()
|
||||||
}
|
}
|
||||||
return strings.Join(sources, ", ")
|
return strings.Join(sources, ", ")
|
||||||
}
|
}
|
||||||
@@ -34,8 +31,8 @@ func (s *Source) String() string {
|
|||||||
// Read reads the settings for each source, merging unset fields
|
// Read reads the settings for each source, merging unset fields
|
||||||
// with field set by the next source.
|
// with field set by the next source.
|
||||||
// It then set defaults to remaining unset fields.
|
// It then set defaults to remaining unset fields.
|
||||||
func (s *Source) Read() (settings settings.Settings, err error) {
|
func (r *Reader) Read() (settings settings.Settings, err error) {
|
||||||
for _, source := range s.sources {
|
for _, source := range r.sources {
|
||||||
settingsFromSource, err := source.Read()
|
settingsFromSource, err := source.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("reading from %s: %w", source, err)
|
return settings, fmt.Errorf("reading from %s: %w", source, err)
|
||||||
@@ -50,8 +47,8 @@ func (s *Source) Read() (settings settings.Settings, err error) {
|
|||||||
// with field set by the next source.
|
// with field set by the next source.
|
||||||
// It then set defaults to remaining unset fields, and validate
|
// It then set defaults to remaining unset fields, and validate
|
||||||
// all the fields.
|
// all the fields.
|
||||||
func (s *Source) ReadHealth() (settings settings.Health, err error) {
|
func (r *Reader) ReadHealth() (settings settings.Health, err error) {
|
||||||
for _, source := range s.sources {
|
for _, source := range r.sources {
|
||||||
settingsFromSource, err := source.ReadHealth()
|
settingsFromSource, err := source.ReadHealth()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("reading from %s: %w", source, err)
|
return settings, fmt.Errorf("reading from %s: %w", source, err)
|
||||||
@@ -2,4 +2,4 @@ package secrets
|
|||||||
|
|
||||||
import "github.com/qdm12/gluetun/internal/configuration/settings"
|
import "github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
|
|
||||||
func (s *Source) ReadHealth() (settings settings.Health, err error) { return settings, nil }
|
func (r *Reader) ReadHealth() (settings settings.Health, err error) { return settings, nil }
|
||||||
|
|||||||
@@ -28,6 +28,21 @@ func readSecretFileAsStringPtr(secretPathEnvKey, defaultSecretPath string) (
|
|||||||
return files.ReadFromFile(path)
|
return files.ReadFromFile(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readSecretFileAsString(secretPathEnvKey, defaultSecretPath string) (
|
||||||
|
s string, err error) {
|
||||||
|
path := getCleanedEnv(secretPathEnvKey)
|
||||||
|
if path == "" {
|
||||||
|
path = defaultSecretPath
|
||||||
|
}
|
||||||
|
stringPtr, err := files.ReadFromFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
} else if stringPtr == nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return *stringPtr, nil
|
||||||
|
}
|
||||||
|
|
||||||
func readPEMSecretFile(secretPathEnvKey, defaultSecretPath string) (
|
func readPEMSecretFile(secretPathEnvKey, defaultSecretPath string) (
|
||||||
base64Ptr *string, err error) {
|
base64Ptr *string, err error) {
|
||||||
pemData, err := readSecretFileAsStringPtr(secretPathEnvKey, defaultSecretPath)
|
pemData, err := readSecretFileAsStringPtr(secretPathEnvKey, defaultSecretPath)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ func readHTTPProxy() (settings settings.HTTPProxy, err error) {
|
|||||||
"/run/secrets/httpproxy_user",
|
"/run/secrets/httpproxy_user",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("reading HTTP proxy user secret file: %w", err)
|
return settings, fmt.Errorf("cannot read HTTP proxy user secret file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Password, err = readSecretFileAsStringPtr(
|
settings.Password, err = readSecretFileAsStringPtr(
|
||||||
@@ -20,7 +20,7 @@ func readHTTPProxy() (settings settings.HTTPProxy, err error) {
|
|||||||
"/run/secrets/httpproxy_password",
|
"/run/secrets/httpproxy_password",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("reading OpenVPN password secret file: %w", err)
|
return settings, fmt.Errorf("cannot read OpenVPN password secret file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings, nil
|
return settings, nil
|
||||||
|
|||||||
@@ -8,52 +8,36 @@ import (
|
|||||||
|
|
||||||
func readOpenVPN() (
|
func readOpenVPN() (
|
||||||
settings settings.OpenVPN, err error) {
|
settings settings.OpenVPN, err error) {
|
||||||
settings.User, err = readSecretFileAsStringPtr(
|
settings.User, err = readSecretFileAsString(
|
||||||
"OPENVPN_USER_SECRETFILE",
|
"OPENVPN_USER_SECRETFILE",
|
||||||
"/run/secrets/openvpn_user",
|
"/run/secrets/openvpn_user",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("reading user file: %w", err)
|
return settings, fmt.Errorf("cannot read user file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Password, err = readSecretFileAsStringPtr(
|
settings.Password, err = readSecretFileAsString(
|
||||||
"OPENVPN_PASSWORD_SECRETFILE",
|
"OPENVPN_PASSWORD_SECRETFILE",
|
||||||
"/run/secrets/openvpn_password",
|
"/run/secrets/openvpn_password",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("reading password file: %w", err)
|
return settings, fmt.Errorf("cannot read password file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Key, err = readPEMSecretFile(
|
settings.ClientKey, err = readPEMSecretFile(
|
||||||
"OPENVPN_CLIENTKEY_SECRETFILE",
|
"OPENVPN_CLIENTKEY_SECRETFILE",
|
||||||
"/run/secrets/openvpn_clientkey",
|
"/run/secrets/openvpn_clientkey",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("reading client key file: %w", err)
|
return settings, fmt.Errorf("cannot read client key file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.EncryptedKey, err = readPEMSecretFile(
|
settings.ClientCrt, err = readPEMSecretFile(
|
||||||
"OPENVPN_ENCRYPTED_KEY_SECRETFILE",
|
|
||||||
"/run/secrets/openvpn_encrypted_key",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading encrypted key file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.KeyPassphrase, err = readSecretFileAsStringPtr(
|
|
||||||
"OPENVPN_KEY_PASSPHRASE_SECRETFILE",
|
|
||||||
"/run/secrets/openvpn_key_passphrase",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading key passphrase file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.Cert, err = readPEMSecretFile(
|
|
||||||
"OPENVPN_CLIENTCRT_SECRETFILE",
|
"OPENVPN_CLIENTCRT_SECRETFILE",
|
||||||
"/run/secrets/openvpn_clientcrt",
|
"/run/secrets/openvpn_clientcrt",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("reading client certificate file: %w", err)
|
return settings, fmt.Errorf("cannot read client certificate file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings, nil
|
return settings, nil
|
||||||
|
|||||||
@@ -2,17 +2,21 @@ package secrets
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration/sources"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Source struct{}
|
var _ sources.Source = (*Reader)(nil)
|
||||||
|
|
||||||
func New() *Source {
|
type Reader struct {
|
||||||
return &Source{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) String() string { return "secret files" }
|
func New() *Reader {
|
||||||
|
return &Reader{}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Source) Read() (settings settings.Settings, err error) {
|
func (r *Reader) String() string { return "secret files" }
|
||||||
|
|
||||||
|
func (r *Reader) Read() (settings settings.Settings, err error) {
|
||||||
settings.VPN, err = readVPN()
|
settings.VPN, err = readVPN()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, err
|
return settings, err
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ func readShadowsocks() (settings settings.Shadowsocks, err error) {
|
|||||||
"/run/secrets/shadowsocks_password",
|
"/run/secrets/shadowsocks_password",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("reading Shadowsocks password secret file: %w", err)
|
return settings, fmt.Errorf("cannot read Shadowsocks password secret file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings, nil
|
return settings, nil
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
func readVPN() (vpn settings.VPN, err error) {
|
func readVPN() (vpn settings.VPN, err error) {
|
||||||
vpn.OpenVPN, err = readOpenVPN()
|
vpn.OpenVPN, err = readOpenVPN()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return vpn, fmt.Errorf("reading OpenVPN settings: %w", err)
|
return vpn, fmt.Errorf("cannot read OpenVPN settings: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return vpn, nil
|
return vpn, nil
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package cli
|
package sources
|
||||||
|
|
||||||
import "github.com/qdm12/gluetun/internal/configuration/settings"
|
import "github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
|
|
||||||
type Source interface {
|
type Source interface {
|
||||||
Read() (settings settings.Settings, err error)
|
Read() (settings settings.Settings, err error)
|
||||||
ReadHealth() (health settings.Health, err error)
|
ReadHealth() (settings settings.Health, err error)
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
package openvpn
|
package openvpn
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AES128cbc = "aes-128-cbc"
|
AES128cbc = "aes-128-cbc"
|
||||||
AES192cbc = "aes-192-cbc"
|
AES256cbc = "aes-256-cbc"
|
||||||
AES256cbc = "aes-256-cbc"
|
AES128gcm = "aes-128-gcm"
|
||||||
AES128gcm = "aes-128-gcm"
|
AES256gcm = "aes-256-gcm"
|
||||||
AES192gcm = "aes-192-gcm"
|
|
||||||
AES256gcm = "aes-256-gcm"
|
|
||||||
Chacha20Poly1305 = "chacha20-poly1305"
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,7 +3,4 @@ package openvpn
|
|||||||
const (
|
const (
|
||||||
// AuthConf is the file path to the OpenVPN auth file.
|
// AuthConf is the file path to the OpenVPN auth file.
|
||||||
AuthConf = "/etc/openvpn/auth.conf"
|
AuthConf = "/etc/openvpn/auth.conf"
|
||||||
// AskPassPath is the file path to the decryption passphrase for
|
|
||||||
// and encrypted private key, which is pointed by `askpass`.
|
|
||||||
AskPassPath = "/etc/openvpn/askpass" //nolint:gosec
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package providers
|
|||||||
const (
|
const (
|
||||||
// Custom is the VPN provider name for custom
|
// Custom is the VPN provider name for custom
|
||||||
// VPN configurations.
|
// VPN configurations.
|
||||||
Airvpn = "airvpn"
|
|
||||||
Custom = "custom"
|
Custom = "custom"
|
||||||
Cyberghost = "cyberghost"
|
Cyberghost = "cyberghost"
|
||||||
Example = "example"
|
Example = "example"
|
||||||
@@ -20,10 +19,8 @@ const (
|
|||||||
Privatevpn = "privatevpn"
|
Privatevpn = "privatevpn"
|
||||||
Protonvpn = "protonvpn"
|
Protonvpn = "protonvpn"
|
||||||
Purevpn = "purevpn"
|
Purevpn = "purevpn"
|
||||||
SlickVPN = "slickvpn"
|
|
||||||
Surfshark = "surfshark"
|
Surfshark = "surfshark"
|
||||||
Torguard = "torguard"
|
Torguard = "torguard"
|
||||||
VPNSecure = "vpnsecure"
|
|
||||||
VPNUnlimited = "vpn unlimited"
|
VPNUnlimited = "vpn unlimited"
|
||||||
Vyprvpn = "vyprvpn"
|
Vyprvpn = "vyprvpn"
|
||||||
Wevpn = "wevpn"
|
Wevpn = "wevpn"
|
||||||
@@ -33,7 +30,6 @@ const (
|
|||||||
// All returns all the providers except the custom provider.
|
// All returns all the providers except the custom provider.
|
||||||
func All() []string {
|
func All() []string {
|
||||||
return []string{
|
return []string{
|
||||||
Airvpn,
|
|
||||||
Cyberghost,
|
Cyberghost,
|
||||||
Expressvpn,
|
Expressvpn,
|
||||||
Fastestvpn,
|
Fastestvpn,
|
||||||
@@ -48,10 +44,8 @@ func All() []string {
|
|||||||
Privatevpn,
|
Privatevpn,
|
||||||
Protonvpn,
|
Protonvpn,
|
||||||
Purevpn,
|
Purevpn,
|
||||||
SlickVPN,
|
|
||||||
Surfshark,
|
Surfshark,
|
||||||
Torguard,
|
Torguard,
|
||||||
VPNSecure,
|
|
||||||
VPNUnlimited,
|
VPNUnlimited,
|
||||||
Vyprvpn,
|
Vyprvpn,
|
||||||
Wevpn,
|
Wevpn,
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func (cm *cmdMatcher) String() string {
|
|||||||
return fmt.Sprintf("path %s, argument regular expressions %v", cm.path, cm.argsRegex)
|
return fmt.Sprintf("path %s, argument regular expressions %v", cm.path, cm.argsRegex)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCmdMatcher(path string, argsRegex ...string) *cmdMatcher {
|
func newCmdMatcher(path string, argsRegex ...string) *cmdMatcher { //nolint:unparam
|
||||||
argsRegexp := make([]*regexp.Regexp, len(argsRegex))
|
argsRegexp := make([]*regexp.Regexp, len(argsRegex))
|
||||||
for i, argRegex := range argsRegex {
|
for i, argRegex := range argsRegex {
|
||||||
argsRegexp[i] = regexp.MustCompile(argRegex)
|
argsRegexp[i] = regexp.MustCompile(argRegex)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func (c *Config) SetEnabled(ctx context.Context, enabled bool) (err error) {
|
|||||||
if !enabled {
|
if !enabled {
|
||||||
c.logger.Info("disabling...")
|
c.logger.Info("disabling...")
|
||||||
if err = c.disable(ctx); err != nil {
|
if err = c.disable(ctx); err != nil {
|
||||||
return fmt.Errorf("disabling firewall: %w", err)
|
return fmt.Errorf("cannot disable firewall: %w", err)
|
||||||
}
|
}
|
||||||
c.enabled = false
|
c.enabled = false
|
||||||
c.logger.Info("disabled successfully")
|
c.logger.Info("disabled successfully")
|
||||||
@@ -31,7 +31,7 @@ func (c *Config) SetEnabled(ctx context.Context, enabled bool) (err error) {
|
|||||||
c.logger.Info("enabling...")
|
c.logger.Info("enabling...")
|
||||||
|
|
||||||
if err := c.enable(ctx); err != nil {
|
if err := c.enable(ctx); err != nil {
|
||||||
return fmt.Errorf("enabling firewall: %w", err)
|
return fmt.Errorf("cannot enable firewall: %w", err)
|
||||||
}
|
}
|
||||||
c.enabled = true
|
c.enabled = true
|
||||||
c.logger.Info("enabled successfully")
|
c.logger.Info("enabled successfully")
|
||||||
@@ -41,13 +41,13 @@ func (c *Config) SetEnabled(ctx context.Context, enabled bool) (err error) {
|
|||||||
|
|
||||||
func (c *Config) disable(ctx context.Context) (err error) {
|
func (c *Config) disable(ctx context.Context) (err error) {
|
||||||
if err = c.clearAllRules(ctx); err != nil {
|
if err = c.clearAllRules(ctx); err != nil {
|
||||||
return fmt.Errorf("clearing all rules: %w", err)
|
return fmt.Errorf("cannot clear all rules: %w", err)
|
||||||
}
|
}
|
||||||
if err = c.setIPv4AllPolicies(ctx, "ACCEPT"); err != nil {
|
if err = c.setIPv4AllPolicies(ctx, "ACCEPT"); err != nil {
|
||||||
return fmt.Errorf("setting ipv4 policies: %w", err)
|
return fmt.Errorf("cannot set ipv4 policies: %w", err)
|
||||||
}
|
}
|
||||||
if err = c.setIPv6AllPolicies(ctx, "ACCEPT"); err != nil {
|
if err = c.setIPv6AllPolicies(ctx, "ACCEPT"); err != nil {
|
||||||
return fmt.Errorf("setting ipv6 policies: %w", err)
|
return fmt.Errorf("cannot set ipv6 policies: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -101,9 +101,6 @@ func (c *Config) enable(ctx context.Context) (err error) {
|
|||||||
if err := c.acceptOutputFromIPToSubnet(ctx, network.InterfaceName, network.IP, *network.IPNet, remove); err != nil {
|
if err := c.acceptOutputFromIPToSubnet(ctx, network.InterfaceName, network.IP, *network.IPNet, remove); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = c.acceptIpv6MulticastOutput(ctx, network.InterfaceName, remove); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = c.allowOutboundSubnets(ctx); err != nil {
|
if err = c.allowOutboundSubnets(ctx); err != nil {
|
||||||
@@ -123,7 +120,7 @@ func (c *Config) enable(ctx context.Context) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := c.runUserPostRules(ctx, c.customRulesPath, remove); err != nil {
|
if err := c.runUserPostRules(ctx, c.customRulesPath, remove); err != nil {
|
||||||
return fmt.Errorf("running user defined post firewall rules: %w", err)
|
return fmt.Errorf("cannot run user defined post firewall rules: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -138,7 +135,7 @@ func (c *Config) allowVPNIP(ctx context.Context) (err error) {
|
|||||||
for _, defaultRoute := range c.defaultRoutes {
|
for _, defaultRoute := range c.defaultRoutes {
|
||||||
err = c.acceptOutputTrafficToVPN(ctx, defaultRoute.NetInterface, c.vpnConnection, remove)
|
err = c.acceptOutputTrafficToVPN(ctx, defaultRoute.NetInterface, c.vpnConnection, remove)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("accepting output traffic through VPN: %w", err)
|
return fmt.Errorf("cannot accept output traffic through VPN: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +162,7 @@ func (c *Config) allowInputPorts(ctx context.Context) (err error) {
|
|||||||
const remove = false
|
const remove = false
|
||||||
err = c.acceptInputToPort(ctx, netInterface, port, remove)
|
err = c.acceptInputToPort(ctx, netInterface, port, remove)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("accepting input port %d on interface %s: %w",
|
return fmt.Errorf("cannot accept input port %d on interface %s: %w",
|
||||||
port, netInterface, err)
|
port, netInterface, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,18 +179,6 @@ func (c *Config) acceptOutputFromIPToSubnet(ctx context.Context,
|
|||||||
return c.runIP6tablesInstruction(ctx, instruction)
|
return c.runIP6tablesInstruction(ctx, instruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NDP uses multicast address (theres no broadcast in IPv6 like ARP uses in IPv4).
|
|
||||||
func (c *Config) acceptIpv6MulticastOutput(ctx context.Context,
|
|
||||||
intf string, remove bool) error {
|
|
||||||
interfaceFlag := "-o " + intf
|
|
||||||
if intf == "*" { // all interfaces
|
|
||||||
interfaceFlag = ""
|
|
||||||
}
|
|
||||||
instruction := fmt.Sprintf("%s OUTPUT %s -d ff02::1:ff/104 -j ACCEPT",
|
|
||||||
appendOrDelete(remove), interfaceFlag)
|
|
||||||
return c.runIP6tablesInstruction(ctx, instruction)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used for port forwarding, with intf set to tun.
|
// Used for port forwarding, with intf set to tun.
|
||||||
func (c *Config) acceptInputToPort(ctx context.Context, intf string, port uint16, remove bool) error {
|
func (c *Config) acceptInputToPort(ctx context.Context, intf string, port uint16, remove bool) error {
|
||||||
interfaceFlag := "-i " + intf
|
interfaceFlag := "-i " + intf
|
||||||
@@ -257,7 +245,7 @@ func (c *Config) runUserPostRules(ctx context.Context, filepath string, remove b
|
|||||||
case ipv4:
|
case ipv4:
|
||||||
err = c.runIptablesInstruction(ctx, rule)
|
err = c.runIptablesInstruction(ctx, rule)
|
||||||
case c.ip6Tables == "":
|
case c.ip6Tables == "":
|
||||||
err = fmt.Errorf("running user ip6tables rule: %w", ErrNeedIP6Tables)
|
err = fmt.Errorf("cannot run user ip6tables rule: %w", ErrNeedIP6Tables)
|
||||||
default: // ipv6
|
default: // ipv6
|
||||||
err = c.runIP6tablesInstruction(ctx, rule)
|
err = c.runIP6tablesInstruction(ctx, rule)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func (c *Config) SetOutboundSubnets(ctx context.Context, subnets []net.IPNet) (e
|
|||||||
|
|
||||||
c.removeOutboundSubnets(ctx, subnetsToRemove)
|
c.removeOutboundSubnets(ctx, subnetsToRemove)
|
||||||
if err := c.addOutboundSubnets(ctx, subnetsToAdd); err != nil {
|
if err := c.addOutboundSubnets(ctx, subnetsToAdd); err != nil {
|
||||||
return fmt.Errorf("setting allowed outbound subnets: %w", err)
|
return fmt.Errorf("cannot set allowed outbound subnets: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -36,11 +36,10 @@ func (c *Config) SetAllowedPort(ctx context.Context, port uint16, intf string) (
|
|||||||
|
|
||||||
const remove = false
|
const remove = false
|
||||||
if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
|
if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
|
||||||
return fmt.Errorf("allowing input to port %d through interface %s: %w",
|
return fmt.Errorf("cannot allow input to port %d through interface %s: %w",
|
||||||
port, intf, err)
|
port, intf, err)
|
||||||
}
|
}
|
||||||
netInterfaces[intf] = struct{}{}
|
netInterfaces[intf] = struct{}{}
|
||||||
c.allowedInputPorts[port] = netInterfaces
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -70,7 +69,7 @@ func (c *Config) RemoveAllowedPort(ctx context.Context, port uint16) (err error)
|
|||||||
for netInterface := range interfacesSet {
|
for netInterface := range interfacesSet {
|
||||||
err := c.acceptInputToPort(ctx, netInterface, port, remove)
|
err := c.acceptInputToPort(ctx, netInterface, port, remove)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("removing allowed port %d on interface %s: %w",
|
return fmt.Errorf("cannot remove allowed port %d on interface %s: %w",
|
||||||
port, netInterface, err)
|
port, netInterface, err)
|
||||||
}
|
}
|
||||||
delete(interfacesSet, netInterface)
|
delete(interfacesSet, netInterface)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/golibs/command"
|
"github.com/qdm12/golibs/command"
|
||||||
@@ -21,7 +20,7 @@ var (
|
|||||||
|
|
||||||
func checkIptablesSupport(ctx context.Context, runner command.Runner,
|
func checkIptablesSupport(ctx context.Context, runner command.Runner,
|
||||||
iptablesPathsToTry ...string) (iptablesPath string, err error) {
|
iptablesPathsToTry ...string) (iptablesPath string, err error) {
|
||||||
iptablesPathToUnsupportedMessage := make(map[string]string, len(iptablesPathsToTry))
|
var lastUnsupportedMessage string
|
||||||
for _, pathToTest := range iptablesPathsToTry {
|
for _, pathToTest := range iptablesPathsToTry {
|
||||||
ok, unsupportedMessage, err := testIptablesPath(ctx, pathToTest, runner)
|
ok, unsupportedMessage, err := testIptablesPath(ctx, pathToTest, runner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -30,35 +29,17 @@ func checkIptablesSupport(ctx context.Context, runner command.Runner,
|
|||||||
iptablesPath = pathToTest
|
iptablesPath = pathToTest
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
iptablesPathToUnsupportedMessage[pathToTest] = unsupportedMessage
|
|
||||||
|
lastUnsupportedMessage = unsupportedMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
if iptablesPath != "" {
|
if iptablesPath == "" { // all iptables to try failed
|
||||||
// some paths may be unsupported but that does not matter
|
return "", fmt.Errorf("%w: from %s: last error is: %s",
|
||||||
// since we found one working.
|
ErrIPTablesNotSupported, strings.Join(iptablesPathsToTry, ", "),
|
||||||
return iptablesPath, nil
|
lastUnsupportedMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
allArePermissionDenied := true
|
return iptablesPath, nil
|
||||||
allUnsupportedMessages := make(sort.StringSlice, 0, len(iptablesPathToUnsupportedMessage))
|
|
||||||
for iptablesPath, unsupportedMessage := range iptablesPathToUnsupportedMessage {
|
|
||||||
if !isPermissionDenied(unsupportedMessage) {
|
|
||||||
allArePermissionDenied = false
|
|
||||||
}
|
|
||||||
unsupportedMessage = iptablesPath + ": " + unsupportedMessage
|
|
||||||
allUnsupportedMessages = append(allUnsupportedMessages, unsupportedMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
allUnsupportedMessages.Sort() // predictable order for tests
|
|
||||||
|
|
||||||
if allArePermissionDenied {
|
|
||||||
// If the error is related to a denied permission for all iptables path,
|
|
||||||
// return an error describing what to do from an end-user perspective.
|
|
||||||
return "", fmt.Errorf("%w: %s", ErrNetAdminMissing, strings.Join(allUnsupportedMessages, "; "))
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", fmt.Errorf("%w: errors encountered are: %s",
|
|
||||||
ErrIPTablesNotSupported, strings.Join(allUnsupportedMessages, "; "))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testIptablesPath(ctx context.Context, path string,
|
func testIptablesPath(ctx context.Context, path string,
|
||||||
@@ -75,6 +56,14 @@ func testIptablesPath(ctx context.Context, path string,
|
|||||||
"-A", "OUTPUT", "-o", testInterfaceName, "-j", "DROP")
|
"-A", "OUTPUT", "-o", testInterfaceName, "-j", "DROP")
|
||||||
output, err := runner.Run(cmd)
|
output, err := runner.Run(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if isPermissionDenied(output) {
|
||||||
|
// If the error is related to a denied permission,
|
||||||
|
// return an error describing what to do from an end-user
|
||||||
|
// perspective. This is a critical error and likely
|
||||||
|
// applies to all iptables.
|
||||||
|
criticalErr = fmt.Errorf("%w: %s", ErrNetAdminMissing, output)
|
||||||
|
return false, "", criticalErr
|
||||||
|
}
|
||||||
unsupportedMessage = fmt.Sprintf("%s (%s)", output, err)
|
unsupportedMessage = fmt.Sprintf("%s (%s)", output, err)
|
||||||
return false, unsupportedMessage, nil
|
return false, unsupportedMessage, nil
|
||||||
}
|
}
|
||||||
@@ -95,6 +84,10 @@ func testIptablesPath(ctx context.Context, path string,
|
|||||||
cmd = exec.CommandContext(ctx, path, "-L", "INPUT")
|
cmd = exec.CommandContext(ctx, path, "-L", "INPUT")
|
||||||
output, err = runner.Run(cmd)
|
output, err = runner.Run(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if isPermissionDenied(output) {
|
||||||
|
criticalErr = fmt.Errorf("%w: %s", ErrNetAdminMissing, output)
|
||||||
|
return false, "", criticalErr
|
||||||
|
}
|
||||||
unsupportedMessage = fmt.Sprintf("%s (%s)", output, err)
|
unsupportedMessage = fmt.Sprintf("%s (%s)", output, err)
|
||||||
return false, unsupportedMessage, nil
|
return false, unsupportedMessage, nil
|
||||||
}
|
}
|
||||||
@@ -116,6 +109,10 @@ func testIptablesPath(ctx context.Context, path string,
|
|||||||
cmd = exec.CommandContext(ctx, path, "--policy", "INPUT", inputPolicy)
|
cmd = exec.CommandContext(ctx, path, "--policy", "INPUT", inputPolicy)
|
||||||
output, err = runner.Run(cmd)
|
output, err = runner.Run(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if isPermissionDenied(output) {
|
||||||
|
criticalErr = fmt.Errorf("%w: %s", ErrNetAdminMissing, output)
|
||||||
|
return false, "", criticalErr
|
||||||
|
}
|
||||||
unsupportedMessage = fmt.Sprintf("%s (%s)", output, err)
|
unsupportedMessage = fmt.Sprintf("%s (%s)", output, err)
|
||||||
return false, unsupportedMessage, nil
|
return false, unsupportedMessage, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,130 +8,10 @@ import (
|
|||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/qdm12/golibs/command"
|
"github.com/qdm12/golibs/command"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate mockgen -destination=runner_mock_test.go -package $GOPACKAGE github.com/qdm12/golibs/command Runner
|
//go:generate mockgen -destination=runner_mock_test.go -package $GOPACKAGE github.com/qdm12/golibs/command Runner
|
||||||
|
|
||||||
func newAppendTestRuleMatcher(path string) *cmdMatcher {
|
|
||||||
return newCmdMatcher(path,
|
|
||||||
"^-A$", "^OUTPUT$", "^-o$", "^[a-z0-9]{15}$",
|
|
||||||
"^-j$", "^DROP$")
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDeleteTestRuleMatcher(path string) *cmdMatcher {
|
|
||||||
return newCmdMatcher(path,
|
|
||||||
"^-D$", "^OUTPUT$", "^-o$", "^[a-z0-9]{15}$",
|
|
||||||
"^-j$", "^DROP$")
|
|
||||||
}
|
|
||||||
|
|
||||||
func newListInputRulesMatcher(path string) *cmdMatcher {
|
|
||||||
return newCmdMatcher(path,
|
|
||||||
"^-L$", "^INPUT$")
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSetPolicyMatcher(path, inputPolicy string) *cmdMatcher { //nolint:unparam
|
|
||||||
return newCmdMatcher(path,
|
|
||||||
"^--policy$", "^INPUT$", "^"+inputPolicy+"$")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_checkIptablesSupport(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
errDummy := errors.New("exit code 4")
|
|
||||||
const inputPolicy = "ACCEPT"
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
buildRunner func(ctrl *gomock.Controller) command.Runner
|
|
||||||
iptablesPathsToTry []string
|
|
||||||
iptablesPath string
|
|
||||||
errSentinel error
|
|
||||||
errMessage string
|
|
||||||
}{
|
|
||||||
"critical error when checking": {
|
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
|
||||||
runner := NewMockRunner(ctrl)
|
|
||||||
runner.EXPECT().Run(newAppendTestRuleMatcher("path1")).
|
|
||||||
Return("", nil)
|
|
||||||
runner.EXPECT().Run(newDeleteTestRuleMatcher("path1")).
|
|
||||||
Return("output", errDummy)
|
|
||||||
return runner
|
|
||||||
},
|
|
||||||
iptablesPathsToTry: []string{"path1", "path2"},
|
|
||||||
errSentinel: ErrTestRuleCleanup,
|
|
||||||
errMessage: "for path1: failed cleaning up test rule: " +
|
|
||||||
"output (exit code 4)",
|
|
||||||
},
|
|
||||||
"found valid path": {
|
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
|
||||||
runner := NewMockRunner(ctrl)
|
|
||||||
runner.EXPECT().Run(newAppendTestRuleMatcher("path1")).
|
|
||||||
Return("", nil)
|
|
||||||
runner.EXPECT().Run(newDeleteTestRuleMatcher("path1")).
|
|
||||||
Return("", nil)
|
|
||||||
runner.EXPECT().Run(newListInputRulesMatcher("path1")).
|
|
||||||
Return("Chain INPUT (policy "+inputPolicy+")", nil)
|
|
||||||
runner.EXPECT().Run(newSetPolicyMatcher("path1", inputPolicy)).
|
|
||||||
Return("", nil)
|
|
||||||
return runner
|
|
||||||
},
|
|
||||||
iptablesPathsToTry: []string{"path1", "path2"},
|
|
||||||
iptablesPath: "path1",
|
|
||||||
},
|
|
||||||
"all permission denied": {
|
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
|
||||||
runner := NewMockRunner(ctrl)
|
|
||||||
runner.EXPECT().Run(newAppendTestRuleMatcher("path1")).
|
|
||||||
Return("Permission denied (you must be root) more context", errDummy)
|
|
||||||
runner.EXPECT().Run(newAppendTestRuleMatcher("path2")).
|
|
||||||
Return("context: Permission denied (you must be root)", errDummy)
|
|
||||||
return runner
|
|
||||||
},
|
|
||||||
iptablesPathsToTry: []string{"path1", "path2"},
|
|
||||||
errSentinel: ErrNetAdminMissing,
|
|
||||||
errMessage: "NET_ADMIN capability is missing: " +
|
|
||||||
"path1: Permission denied (you must be root) more context (exit code 4); " +
|
|
||||||
"path2: context: Permission denied (you must be root) (exit code 4)",
|
|
||||||
},
|
|
||||||
"no valid path": {
|
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
|
||||||
runner := NewMockRunner(ctrl)
|
|
||||||
runner.EXPECT().Run(newAppendTestRuleMatcher("path1")).
|
|
||||||
Return("output 1", errDummy)
|
|
||||||
runner.EXPECT().Run(newAppendTestRuleMatcher("path2")).
|
|
||||||
Return("output 2", errDummy)
|
|
||||||
return runner
|
|
||||||
},
|
|
||||||
iptablesPathsToTry: []string{"path1", "path2"},
|
|
||||||
errSentinel: ErrIPTablesNotSupported,
|
|
||||||
errMessage: "no iptables supported found: " +
|
|
||||||
"errors encountered are: " +
|
|
||||||
"path1: output 1 (exit code 4); " +
|
|
||||||
"path2: output 2 (exit code 4)",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, testCase := range testCases {
|
|
||||||
testCase := testCase
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
|
|
||||||
runner := testCase.buildRunner(ctrl)
|
|
||||||
|
|
||||||
iptablesPath, err :=
|
|
||||||
checkIptablesSupport(ctx, runner, testCase.iptablesPathsToTry...)
|
|
||||||
|
|
||||||
require.ErrorIs(t, err, testCase.errSentinel)
|
|
||||||
if testCase.errSentinel != nil {
|
|
||||||
assert.EqualError(t, err, testCase.errMessage)
|
|
||||||
}
|
|
||||||
assert.Equal(t, testCase.iptablesPath, iptablesPath)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_testIptablesPath(t *testing.T) {
|
func Test_testIptablesPath(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -140,6 +20,17 @@ func Test_testIptablesPath(t *testing.T) {
|
|||||||
errDummy := errors.New("exit code 4")
|
errDummy := errors.New("exit code 4")
|
||||||
const inputPolicy = "ACCEPT"
|
const inputPolicy = "ACCEPT"
|
||||||
|
|
||||||
|
appendTestRuleMatcher := newCmdMatcher(path,
|
||||||
|
"^-A$", "^OUTPUT$", "^-o$", "^[a-z0-9]{15}$",
|
||||||
|
"^-j$", "^DROP$")
|
||||||
|
deleteTestRuleMatcher := newCmdMatcher(path,
|
||||||
|
"^-D$", "^OUTPUT$", "^-o$", "^[a-z0-9]{15}$",
|
||||||
|
"^-j$", "^DROP$")
|
||||||
|
listInputRulesMatcher := newCmdMatcher(path,
|
||||||
|
"^-L$", "^INPUT$")
|
||||||
|
setPolicyMatcher := newCmdMatcher(path,
|
||||||
|
"^--policy$", "^INPUT$", "^"+inputPolicy+"$")
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
buildRunner func(ctrl *gomock.Controller) command.Runner
|
buildRunner func(ctrl *gomock.Controller) command.Runner
|
||||||
ok bool
|
ok bool
|
||||||
@@ -150,16 +41,18 @@ func Test_testIptablesPath(t *testing.T) {
|
|||||||
"append test rule permission denied": {
|
"append test rule permission denied": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).
|
runner.EXPECT().Run(appendTestRuleMatcher).
|
||||||
Return("Permission denied (you must be root)", errDummy)
|
Return("Permission denied (you must be root)", errDummy)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
unsupportedMessage: "Permission denied (you must be root) (exit code 4)",
|
criticalErrWrapped: ErrNetAdminMissing,
|
||||||
|
criticalErrMessage: "NET_ADMIN capability is missing: " +
|
||||||
|
"Permission denied (you must be root)",
|
||||||
},
|
},
|
||||||
"append test rule unsupported": {
|
"append test rule unsupported": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).
|
runner.EXPECT().Run(appendTestRuleMatcher).
|
||||||
Return("some output", errDummy)
|
Return("some output", errDummy)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
@@ -168,8 +61,8 @@ func Test_testIptablesPath(t *testing.T) {
|
|||||||
"remove test rule error": {
|
"remove test rule error": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).Return("", nil)
|
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
||||||
runner.EXPECT().Run(newDeleteTestRuleMatcher(path)).
|
runner.EXPECT().Run(deleteTestRuleMatcher).
|
||||||
Return("some output", errDummy)
|
Return("some output", errDummy)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
@@ -179,20 +72,22 @@ func Test_testIptablesPath(t *testing.T) {
|
|||||||
"list input rules permission denied": {
|
"list input rules permission denied": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).Return("", nil)
|
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
||||||
runner.EXPECT().Run(newDeleteTestRuleMatcher(path)).Return("", nil)
|
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
||||||
runner.EXPECT().Run(newListInputRulesMatcher(path)).
|
runner.EXPECT().Run(listInputRulesMatcher).
|
||||||
Return("Permission denied (you must be root)", errDummy)
|
Return("Permission denied (you must be root)", errDummy)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
unsupportedMessage: "Permission denied (you must be root) (exit code 4)",
|
criticalErrWrapped: ErrNetAdminMissing,
|
||||||
|
criticalErrMessage: "NET_ADMIN capability is missing: " +
|
||||||
|
"Permission denied (you must be root)",
|
||||||
},
|
},
|
||||||
"list input rules unsupported": {
|
"list input rules unsupported": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).Return("", nil)
|
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
||||||
runner.EXPECT().Run(newDeleteTestRuleMatcher(path)).Return("", nil)
|
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
||||||
runner.EXPECT().Run(newListInputRulesMatcher(path)).
|
runner.EXPECT().Run(listInputRulesMatcher).
|
||||||
Return("some output", errDummy)
|
Return("some output", errDummy)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
@@ -201,9 +96,9 @@ func Test_testIptablesPath(t *testing.T) {
|
|||||||
"list input rules no policy": {
|
"list input rules no policy": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).Return("", nil)
|
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
||||||
runner.EXPECT().Run(newDeleteTestRuleMatcher(path)).Return("", nil)
|
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
||||||
runner.EXPECT().Run(newListInputRulesMatcher(path)).
|
runner.EXPECT().Run(listInputRulesMatcher).
|
||||||
Return("some\noutput", nil)
|
Return("some\noutput", nil)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
@@ -213,24 +108,26 @@ func Test_testIptablesPath(t *testing.T) {
|
|||||||
"set policy permission denied": {
|
"set policy permission denied": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).Return("", nil)
|
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
||||||
runner.EXPECT().Run(newDeleteTestRuleMatcher(path)).Return("", nil)
|
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
||||||
runner.EXPECT().Run(newListInputRulesMatcher(path)).
|
runner.EXPECT().Run(listInputRulesMatcher).
|
||||||
Return("\nChain INPUT (policy "+inputPolicy+")\nxx\n", nil)
|
Return("\nChain INPUT (policy "+inputPolicy+")\nxx\n", nil)
|
||||||
runner.EXPECT().Run(newSetPolicyMatcher(path, inputPolicy)).
|
runner.EXPECT().Run(setPolicyMatcher).
|
||||||
Return("Permission denied (you must be root)", errDummy)
|
Return("Permission denied (you must be root)", errDummy)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
unsupportedMessage: "Permission denied (you must be root) (exit code 4)",
|
criticalErrWrapped: ErrNetAdminMissing,
|
||||||
|
criticalErrMessage: "NET_ADMIN capability is missing: " +
|
||||||
|
"Permission denied (you must be root)",
|
||||||
},
|
},
|
||||||
"set policy unsupported": {
|
"set policy unsupported": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).Return("", nil)
|
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
||||||
runner.EXPECT().Run(newDeleteTestRuleMatcher(path)).Return("", nil)
|
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
||||||
runner.EXPECT().Run(newListInputRulesMatcher(path)).
|
runner.EXPECT().Run(listInputRulesMatcher).
|
||||||
Return("\nChain INPUT (policy "+inputPolicy+")\nxx\n", nil)
|
Return("\nChain INPUT (policy "+inputPolicy+")\nxx\n", nil)
|
||||||
runner.EXPECT().Run(newSetPolicyMatcher(path, inputPolicy)).
|
runner.EXPECT().Run(setPolicyMatcher).
|
||||||
Return("some output", errDummy)
|
Return("some output", errDummy)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
@@ -239,12 +136,11 @@ func Test_testIptablesPath(t *testing.T) {
|
|||||||
"success": {
|
"success": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).Return("", nil)
|
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
||||||
runner.EXPECT().Run(newDeleteTestRuleMatcher(path)).Return("", nil)
|
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
||||||
runner.EXPECT().Run(newListInputRulesMatcher(path)).
|
runner.EXPECT().Run(listInputRulesMatcher).
|
||||||
Return("\nChain INPUT (policy "+inputPolicy+")\nxx\n", nil)
|
Return("\nChain INPUT (policy "+inputPolicy+")\nxx\n", nil)
|
||||||
runner.EXPECT().Run(newSetPolicyMatcher(path, inputPolicy)).
|
runner.EXPECT().Run(setPolicyMatcher).Return("some output", nil)
|
||||||
Return("some output", nil)
|
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
ok: true,
|
ok: true,
|
||||||
|
|||||||
@@ -45,13 +45,13 @@ func (c *Config) SetVPNConnection(ctx context.Context,
|
|||||||
|
|
||||||
for _, defaultRoute := range c.defaultRoutes {
|
for _, defaultRoute := range c.defaultRoutes {
|
||||||
if err := c.acceptOutputTrafficToVPN(ctx, defaultRoute.NetInterface, connection, remove); err != nil {
|
if err := c.acceptOutputTrafficToVPN(ctx, defaultRoute.NetInterface, connection, remove); err != nil {
|
||||||
return fmt.Errorf("allowing output traffic through VPN connection: %w", err)
|
return fmt.Errorf("cannot allow output traffic through VPN connection: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.vpnConnection = connection
|
c.vpnConnection = connection
|
||||||
|
|
||||||
if err = c.acceptOutputThroughInterface(ctx, vpnIntf, remove); err != nil {
|
if err = c.acceptOutputThroughInterface(ctx, vpnIntf, remove); err != nil {
|
||||||
return fmt.Errorf("accepting output traffic through interface %s: %w", vpnIntf, err)
|
return fmt.Errorf("cannot accept output traffic through interface %s: %w", vpnIntf, err)
|
||||||
}
|
}
|
||||||
c.vpnIntf = vpnIntf
|
c.vpnIntf = vpnIntf
|
||||||
|
|
||||||
|
|||||||
@@ -74,12 +74,12 @@ func (s *Server) healthCheck(ctx context.Context) (err error) {
|
|||||||
const dialNetwork = "tcp4"
|
const dialNetwork = "tcp4"
|
||||||
connection, err := s.dialer.DialContext(ctx, dialNetwork, address)
|
connection, err := s.dialer.DialContext(ctx, dialNetwork, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("dialing: %w", err)
|
return fmt.Errorf("cannot dial: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = connection.Close()
|
err = connection.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("closing connection: %w", err)
|
return fmt.Errorf("cannot close connection: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -91,7 +91,7 @@ func makeAddressToDial(address string) (addressToDial string, err error) {
|
|||||||
addrErr := new(net.AddrError)
|
addrErr := new(net.AddrError)
|
||||||
ok := errors.As(err, &addrErr)
|
ok := errors.As(err, &addrErr)
|
||||||
if !ok || addrErr.Err != "missing port in address" {
|
if !ok || addrErr.Err != "missing port in address" {
|
||||||
return "", fmt.Errorf("splitting host and port from address: %w", err)
|
return "", fmt.Errorf("cannot split host and port from address: %w", err)
|
||||||
}
|
}
|
||||||
host = address
|
host = address
|
||||||
const defaultPort = "443"
|
const defaultPort = "443"
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ func Test_makeAddressToDial(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"bad address": {
|
"bad address": {
|
||||||
address: "test.com::",
|
address: "test.com::",
|
||||||
err: fmt.Errorf("splitting host and port from address: address test.com::: too many colons in address"), //nolint:lll
|
err: fmt.Errorf("cannot split host and port from address: address test.com::: too many colons in address"), //nolint:lll
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ type vpnHealth struct {
|
|||||||
|
|
||||||
func (s *Server) onUnhealthyVPN(ctx context.Context) {
|
func (s *Server) onUnhealthyVPN(ctx context.Context) {
|
||||||
s.logger.Info("program has been unhealthy for " +
|
s.logger.Info("program has been unhealthy for " +
|
||||||
s.vpn.healthyWait.String() + ": restarting VPN " +
|
s.vpn.healthyWait.String() + ": restarting VPN")
|
||||||
"(see https://github.com/qdm12/gluetun/wiki/Healthcheck)")
|
|
||||||
_, _ = s.vpn.loop.ApplyStatus(ctx, constants.Stopped)
|
_, _ = s.vpn.loop.ApplyStatus(ctx, constants.Stopped)
|
||||||
_, _ = s.vpn.loop.ApplyStatus(ctx, constants.Running)
|
_, _ = s.vpn.loop.ApplyStatus(ctx, constants.Running)
|
||||||
s.vpn.healthyWait += *s.config.VPN.Addition
|
s.vpn.healthyWait += *s.config.VPN.Addition
|
||||||
|
|||||||
@@ -14,10 +14,8 @@ func (s *Server) Run(ctx context.Context, done chan<- struct{}) {
|
|||||||
go s.runHealthcheckLoop(ctx, loopDone)
|
go s.runHealthcheckLoop(ctx, loopDone)
|
||||||
|
|
||||||
server := http.Server{
|
server := http.Server{
|
||||||
Addr: s.config.ServerAddress,
|
Addr: s.config.ServerAddress,
|
||||||
Handler: s.handler,
|
Handler: s.handler,
|
||||||
ReadHeaderTimeout: s.config.ReadHeaderTimeout,
|
|
||||||
ReadTimeout: s.config.ReadTimeout,
|
|
||||||
}
|
}
|
||||||
serverDone := make(chan struct{})
|
serverDone := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package httpproxy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -33,8 +32,7 @@ func (h *handler) isAuthorized(responseWriter http.ResponseWriter, request *http
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if h.username != usernamePassword[0] || h.password != usernamePassword[1] {
|
if h.username != usernamePassword[0] || h.password != usernamePassword[1] {
|
||||||
h.logger.Info(fmt.Sprintf("Username (%q) or password (%q) mismatch from %s",
|
h.logger.Info("Username or password mismatch from " + request.RemoteAddr)
|
||||||
usernamePassword[0], usernamePassword[1], request.RemoteAddr))
|
|
||||||
h.logger.Debug("username provided \"" + usernamePassword[0] +
|
h.logger.Debug("username provided \"" + usernamePassword[0] +
|
||||||
"\" and password provided \"" + usernamePassword[1] + "\"")
|
"\" and password provided \"" + usernamePassword[1] + "\"")
|
||||||
responseWriter.WriteHeader(http.StatusUnauthorized)
|
responseWriter.WriteHeader(http.StatusUnauthorized)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
|
|||||||
settings := l.state.GetSettings()
|
settings := l.state.GetSettings()
|
||||||
server := New(runCtx, settings.ListeningAddress, l.logger,
|
server := New(runCtx, settings.ListeningAddress, l.logger,
|
||||||
*settings.Stealth, *settings.Log, *settings.User,
|
*settings.Stealth, *settings.Log, *settings.User,
|
||||||
*settings.Password, settings.ReadHeaderTimeout, settings.ReadTimeout)
|
*settings.Password)
|
||||||
|
|
||||||
errorCh := make(chan error)
|
errorCh := make(chan error)
|
||||||
go server.Run(runCtx, errorCh)
|
go server.Run(runCtx, errorCh)
|
||||||
|
|||||||
@@ -8,38 +8,28 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
address string
|
address string
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
logger infoErrorer
|
logger infoErrorer
|
||||||
internalWG *sync.WaitGroup
|
internalWG *sync.WaitGroup
|
||||||
readHeaderTimeout time.Duration
|
|
||||||
readTimeout time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ctx context.Context, address string, logger Logger,
|
func New(ctx context.Context, address string, logger Logger,
|
||||||
stealth, verbose bool, username, password string,
|
stealth, verbose bool, username, password string) *Server {
|
||||||
readHeaderTimeout, readTimeout time.Duration) *Server {
|
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
return &Server{
|
return &Server{
|
||||||
address: address,
|
address: address,
|
||||||
handler: newHandler(ctx, wg, logger, stealth, verbose, username, password),
|
handler: newHandler(ctx, wg, logger, stealth, verbose, username, password),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
internalWG: wg,
|
internalWG: wg,
|
||||||
readHeaderTimeout: readHeaderTimeout,
|
|
||||||
readTimeout: readTimeout,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Run(ctx context.Context, errorCh chan<- error) {
|
func (s *Server) Run(ctx context.Context, errorCh chan<- error) {
|
||||||
server := http.Server{
|
server := http.Server{Addr: s.address, Handler: s.handler}
|
||||||
Addr: s.address,
|
|
||||||
Handler: s.handler,
|
|
||||||
ReadHeaderTimeout: s.readHeaderTimeout,
|
|
||||||
ReadTimeout: s.readTimeout,
|
|
||||||
}
|
|
||||||
go func() {
|
go func() {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
const shutdownGraceDuration = 100 * time.Millisecond
|
const shutdownGraceDuration = 2 * time.Second
|
||||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownGraceDuration)
|
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownGraceDuration)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if err := server.Shutdown(shutdownCtx); err != nil {
|
if err := server.Shutdown(shutdownCtx); err != nil {
|
||||||
|
|||||||
@@ -2,10 +2,13 @@ package httpserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func durationPtr(d time.Duration) *time.Duration { return &d }
|
||||||
|
|
||||||
var _ Logger = (*testLogger)(nil)
|
var _ Logger = (*testLogger)(nil)
|
||||||
|
|
||||||
type testLogger struct{}
|
type testLogger struct{}
|
||||||
|
|||||||
@@ -11,12 +11,7 @@ import (
|
|||||||
// The done channel has an error written to when the HTTP server
|
// The done channel has an error written to when the HTTP server
|
||||||
// is terminated, and can be nil or not nil.
|
// is terminated, and can be nil or not nil.
|
||||||
func (s *Server) Run(ctx context.Context, ready chan<- struct{}, done chan<- struct{}) {
|
func (s *Server) Run(ctx context.Context, ready chan<- struct{}, done chan<- struct{}) {
|
||||||
server := http.Server{
|
server := http.Server{Addr: s.address, Handler: s.handler}
|
||||||
Addr: s.address,
|
|
||||||
Handler: s.handler,
|
|
||||||
ReadHeaderTimeout: s.readHeaderTimeout,
|
|
||||||
ReadTimeout: s.readTimeout,
|
|
||||||
}
|
|
||||||
|
|
||||||
crashed := make(chan struct{})
|
crashed := make(chan struct{})
|
||||||
shutdownDone := make(chan struct{})
|
shutdownDone := make(chan struct{})
|
||||||
|
|||||||
@@ -9,13 +9,11 @@ import (
|
|||||||
// Server is an HTTP server implementation, which uses
|
// Server is an HTTP server implementation, which uses
|
||||||
// the HTTP handler provided.
|
// the HTTP handler provided.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
address string
|
address string
|
||||||
addressSet chan struct{}
|
addressSet chan struct{}
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
logger Logger
|
logger Logger
|
||||||
readHeaderTimeout time.Duration
|
shutdownTimeout time.Duration
|
||||||
readTimeout time.Duration
|
|
||||||
shutdownTimeout time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new HTTP server with the given settings.
|
// New creates a new HTTP server with the given settings.
|
||||||
@@ -28,12 +26,10 @@ func New(settings Settings) (s *Server, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &Server{
|
return &Server{
|
||||||
address: settings.Address,
|
address: settings.Address,
|
||||||
addressSet: make(chan struct{}),
|
addressSet: make(chan struct{}),
|
||||||
handler: settings.Handler,
|
handler: settings.Handler,
|
||||||
logger: settings.Logger,
|
logger: settings.Logger,
|
||||||
readHeaderTimeout: settings.ReadHeaderTimeout,
|
shutdownTimeout: *settings.ShutdownTimeout,
|
||||||
readTimeout: settings.ReadTimeout,
|
|
||||||
shutdownTimeout: settings.ShutdownTimeout,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,20 +29,16 @@ func Test_New(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"filled settings": {
|
"filled settings": {
|
||||||
settings: Settings{
|
settings: Settings{
|
||||||
Address: ":8001",
|
Address: ":8001",
|
||||||
Handler: someHandler,
|
Handler: someHandler,
|
||||||
Logger: someLogger,
|
Logger: someLogger,
|
||||||
ReadHeaderTimeout: time.Second,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
expected: &Server{
|
expected: &Server{
|
||||||
address: ":8001",
|
address: ":8001",
|
||||||
handler: someHandler,
|
handler: someHandler,
|
||||||
logger: someLogger,
|
logger: someLogger,
|
||||||
readHeaderTimeout: time.Second,
|
shutdownTimeout: time.Second,
|
||||||
readTimeout: time.Second,
|
|
||||||
shutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,34 +22,23 @@ type Settings struct {
|
|||||||
// Logger is the logger to use.
|
// Logger is the logger to use.
|
||||||
// It must be set and cannot be left to nil.
|
// It must be set and cannot be left to nil.
|
||||||
Logger Logger
|
Logger Logger
|
||||||
// ReadHeaderTimeout is the HTTP header read timeout duration
|
|
||||||
// of the HTTP server. It defaults to 3 seconds if left unset.
|
|
||||||
ReadHeaderTimeout time.Duration
|
|
||||||
// ReadTimeout is the HTTP read timeout duration
|
|
||||||
// of the HTTP server. It defaults to 3 seconds if left unset.
|
|
||||||
ReadTimeout time.Duration
|
|
||||||
// ShutdownTimeout is the shutdown timeout duration
|
// ShutdownTimeout is the shutdown timeout duration
|
||||||
// of the HTTP server. It defaults to 3 seconds if left unset.
|
// of the HTTP server. It defaults to 3 seconds.
|
||||||
ShutdownTimeout time.Duration
|
ShutdownTimeout *time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Settings) SetDefaults() {
|
func (s *Settings) SetDefaults() {
|
||||||
s.Address = helpers.DefaultString(s.Address, ":8000")
|
s.Address = helpers.DefaultString(s.Address, ":8000")
|
||||||
const defaultReadTimeout = 3 * time.Second
|
|
||||||
s.ReadHeaderTimeout = helpers.DefaultDuration(s.ReadHeaderTimeout, defaultReadTimeout)
|
|
||||||
s.ReadTimeout = helpers.DefaultDuration(s.ReadTimeout, defaultReadTimeout)
|
|
||||||
const defaultShutdownTimeout = 3 * time.Second
|
const defaultShutdownTimeout = 3 * time.Second
|
||||||
s.ShutdownTimeout = helpers.DefaultDuration(s.ShutdownTimeout, defaultShutdownTimeout)
|
s.ShutdownTimeout = helpers.DefaultDuration(s.ShutdownTimeout, defaultShutdownTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Settings) Copy() Settings {
|
func (s Settings) Copy() Settings {
|
||||||
return Settings{
|
return Settings{
|
||||||
Address: s.Address,
|
Address: s.Address,
|
||||||
Handler: s.Handler,
|
Handler: s.Handler,
|
||||||
Logger: s.Logger,
|
Logger: s.Logger,
|
||||||
ReadHeaderTimeout: s.ReadHeaderTimeout,
|
ShutdownTimeout: helpers.CopyDurationPtr(s.ShutdownTimeout),
|
||||||
ReadTimeout: s.ReadTimeout,
|
|
||||||
ShutdownTimeout: s.ShutdownTimeout,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,8 +48,6 @@ func (s *Settings) MergeWith(other Settings) {
|
|||||||
if s.Logger == nil {
|
if s.Logger == nil {
|
||||||
s.Logger = other.Logger
|
s.Logger = other.Logger
|
||||||
}
|
}
|
||||||
s.ReadHeaderTimeout = helpers.MergeWithDuration(s.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
|
||||||
s.ReadTimeout = helpers.MergeWithDuration(s.ReadTimeout, other.ReadTimeout)
|
|
||||||
s.ShutdownTimeout = helpers.MergeWithDuration(s.ShutdownTimeout, other.ShutdownTimeout)
|
s.ShutdownTimeout = helpers.MergeWithDuration(s.ShutdownTimeout, other.ShutdownTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,17 +57,13 @@ func (s *Settings) OverrideWith(other Settings) {
|
|||||||
if other.Logger != nil {
|
if other.Logger != nil {
|
||||||
s.Logger = other.Logger
|
s.Logger = other.Logger
|
||||||
}
|
}
|
||||||
s.ReadHeaderTimeout = helpers.OverrideWithDuration(s.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
|
||||||
s.ReadTimeout = helpers.OverrideWithDuration(s.ReadTimeout, other.ReadTimeout)
|
|
||||||
s.ShutdownTimeout = helpers.OverrideWithDuration(s.ShutdownTimeout, other.ShutdownTimeout)
|
s.ShutdownTimeout = helpers.OverrideWithDuration(s.ShutdownTimeout, other.ShutdownTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrHandlerIsNotSet = errors.New("HTTP handler cannot be left unset")
|
ErrHandlerIsNotSet = errors.New("HTTP handler cannot be left unset")
|
||||||
ErrLoggerIsNotSet = errors.New("logger cannot be left unset")
|
ErrLoggerIsNotSet = errors.New("logger cannot be left unset")
|
||||||
ErrReadHeaderTimeoutTooSmall = errors.New("read header timeout is too small")
|
ErrShutdownTimeoutTooSmall = errors.New("shutdown timeout is too small")
|
||||||
ErrReadTimeoutTooSmall = errors.New("read timeout is too small")
|
|
||||||
ErrShutdownTimeoutTooSmall = errors.New("shutdown timeout is too small")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s Settings) Validate() (err error) {
|
func (s Settings) Validate() (err error) {
|
||||||
@@ -98,24 +81,11 @@ func (s Settings) Validate() (err error) {
|
|||||||
return ErrLoggerIsNotSet
|
return ErrLoggerIsNotSet
|
||||||
}
|
}
|
||||||
|
|
||||||
const minReadTimeout = time.Millisecond
|
|
||||||
if s.ReadHeaderTimeout < minReadTimeout {
|
|
||||||
return fmt.Errorf("%w: %s must be at least %s",
|
|
||||||
ErrReadHeaderTimeoutTooSmall,
|
|
||||||
s.ReadHeaderTimeout, minReadTimeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.ReadTimeout < minReadTimeout {
|
|
||||||
return fmt.Errorf("%w: %s must be at least %s",
|
|
||||||
ErrReadTimeoutTooSmall,
|
|
||||||
s.ReadTimeout, minReadTimeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
const minShutdownTimeout = 5 * time.Millisecond
|
const minShutdownTimeout = 5 * time.Millisecond
|
||||||
if s.ShutdownTimeout < minShutdownTimeout {
|
if *s.ShutdownTimeout < minShutdownTimeout {
|
||||||
return fmt.Errorf("%w: %s must be at least %s",
|
return fmt.Errorf("%w: %s must be at least %s",
|
||||||
ErrShutdownTimeoutTooSmall,
|
ErrShutdownTimeoutTooSmall,
|
||||||
s.ShutdownTimeout, minShutdownTimeout)
|
*s.ShutdownTimeout, minShutdownTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -124,9 +94,7 @@ func (s Settings) Validate() (err error) {
|
|||||||
func (s Settings) ToLinesNode() (node *gotree.Node) {
|
func (s Settings) ToLinesNode() (node *gotree.Node) {
|
||||||
node = gotree.New("HTTP server settings:")
|
node = gotree.New("HTTP server settings:")
|
||||||
node.Appendf("Listening address: %s", s.Address)
|
node.Appendf("Listening address: %s", s.Address)
|
||||||
node.Appendf("Read header timeout: %s", s.ReadHeaderTimeout)
|
node.Appendf("Shutdown timeout: %s", *s.ShutdownTimeout)
|
||||||
node.Appendf("Read timeout: %s", s.ReadTimeout)
|
|
||||||
node.Appendf("Shutdown timeout: %s", s.ShutdownTimeout)
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,24 +21,18 @@ func Test_Settings_SetDefaults(t *testing.T) {
|
|||||||
"empty settings": {
|
"empty settings": {
|
||||||
settings: Settings{},
|
settings: Settings{},
|
||||||
expected: Settings{
|
expected: Settings{
|
||||||
Address: ":8000",
|
Address: ":8000",
|
||||||
ReadHeaderTimeout: defaultTimeout,
|
ShutdownTimeout: durationPtr(defaultTimeout),
|
||||||
ReadTimeout: defaultTimeout,
|
|
||||||
ShutdownTimeout: defaultTimeout,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"filled settings": {
|
"filled settings": {
|
||||||
settings: Settings{
|
settings: Settings{
|
||||||
Address: ":8001",
|
Address: ":8001",
|
||||||
ReadHeaderTimeout: time.Second,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
expected: Settings{
|
expected: Settings{
|
||||||
Address: ":8001",
|
Address: ":8001",
|
||||||
ReadHeaderTimeout: time.Second,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -68,20 +62,16 @@ func Test_Settings_Copy(t *testing.T) {
|
|||||||
"empty settings": {},
|
"empty settings": {},
|
||||||
"filled settings": {
|
"filled settings": {
|
||||||
settings: Settings{
|
settings: Settings{
|
||||||
Address: ":8001",
|
Address: ":8001",
|
||||||
Handler: someHandler,
|
Handler: someHandler,
|
||||||
Logger: someLogger,
|
Logger: someLogger,
|
||||||
ReadHeaderTimeout: time.Second,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
expected: Settings{
|
expected: Settings{
|
||||||
Address: ":8001",
|
Address: ":8001",
|
||||||
Handler: someHandler,
|
Handler: someHandler,
|
||||||
Logger: someLogger,
|
Logger: someLogger,
|
||||||
ReadHeaderTimeout: time.Second,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -112,38 +102,30 @@ func Test_Settings_MergeWith(t *testing.T) {
|
|||||||
"merge empty with empty": {},
|
"merge empty with empty": {},
|
||||||
"merge empty with filled": {
|
"merge empty with filled": {
|
||||||
other: Settings{
|
other: Settings{
|
||||||
Address: ":8001",
|
Address: ":8001",
|
||||||
Handler: someHandler,
|
Handler: someHandler,
|
||||||
Logger: someLogger,
|
Logger: someLogger,
|
||||||
ReadHeaderTimeout: time.Second,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
expected: Settings{
|
expected: Settings{
|
||||||
Address: ":8001",
|
Address: ":8001",
|
||||||
Handler: someHandler,
|
Handler: someHandler,
|
||||||
Logger: someLogger,
|
Logger: someLogger,
|
||||||
ReadHeaderTimeout: time.Second,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"merge filled with empty": {
|
"merge filled with empty": {
|
||||||
settings: Settings{
|
settings: Settings{
|
||||||
Address: ":8001",
|
Address: ":8001",
|
||||||
Handler: someHandler,
|
Handler: someHandler,
|
||||||
Logger: someLogger,
|
Logger: someLogger,
|
||||||
ReadHeaderTimeout: time.Second,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
expected: Settings{
|
expected: Settings{
|
||||||
Address: ":8001",
|
Address: ":8001",
|
||||||
Handler: someHandler,
|
Handler: someHandler,
|
||||||
Logger: someLogger,
|
Logger: someLogger,
|
||||||
ReadHeaderTimeout: time.Second,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -174,62 +156,48 @@ func Test_Settings_OverrideWith(t *testing.T) {
|
|||||||
"override empty with empty": {},
|
"override empty with empty": {},
|
||||||
"override empty with filled": {
|
"override empty with filled": {
|
||||||
other: Settings{
|
other: Settings{
|
||||||
Address: ":8001",
|
Address: ":8001",
|
||||||
Handler: someHandler,
|
Handler: someHandler,
|
||||||
Logger: someLogger,
|
Logger: someLogger,
|
||||||
ReadHeaderTimeout: time.Second,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
expected: Settings{
|
expected: Settings{
|
||||||
Address: ":8001",
|
Address: ":8001",
|
||||||
Handler: someHandler,
|
Handler: someHandler,
|
||||||
Logger: someLogger,
|
Logger: someLogger,
|
||||||
ReadHeaderTimeout: time.Second,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"override filled with empty": {
|
"override filled with empty": {
|
||||||
settings: Settings{
|
settings: Settings{
|
||||||
Address: ":8001",
|
Address: ":8001",
|
||||||
Handler: someHandler,
|
Handler: someHandler,
|
||||||
Logger: someLogger,
|
Logger: someLogger,
|
||||||
ReadHeaderTimeout: time.Second,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
expected: Settings{
|
expected: Settings{
|
||||||
Address: ":8001",
|
Address: ":8001",
|
||||||
Handler: someHandler,
|
Handler: someHandler,
|
||||||
Logger: someLogger,
|
Logger: someLogger,
|
||||||
ReadHeaderTimeout: time.Second,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"override filled with filled": {
|
"override filled with filled": {
|
||||||
settings: Settings{
|
settings: Settings{
|
||||||
Address: ":8001",
|
Address: ":8001",
|
||||||
Handler: someHandler,
|
Handler: someHandler,
|
||||||
Logger: someLogger,
|
Logger: someLogger,
|
||||||
ReadHeaderTimeout: time.Second,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
other: Settings{
|
other: Settings{
|
||||||
Address: ":8002",
|
Address: ":8002",
|
||||||
ReadHeaderTimeout: time.Hour,
|
ShutdownTimeout: durationPtr(time.Hour),
|
||||||
ReadTimeout: time.Hour,
|
|
||||||
ShutdownTimeout: time.Hour,
|
|
||||||
},
|
},
|
||||||
expected: Settings{
|
expected: Settings{
|
||||||
Address: ":8002",
|
Address: ":8002",
|
||||||
Handler: someHandler,
|
Handler: someHandler,
|
||||||
Logger: someLogger,
|
Logger: someLogger,
|
||||||
ReadHeaderTimeout: time.Hour,
|
ShutdownTimeout: durationPtr(time.Hour),
|
||||||
ReadTimeout: time.Hour,
|
|
||||||
ShutdownTimeout: time.Hour,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -279,47 +247,22 @@ func Test_Settings_Validate(t *testing.T) {
|
|||||||
errWrapped: ErrLoggerIsNotSet,
|
errWrapped: ErrLoggerIsNotSet,
|
||||||
errMessage: ErrLoggerIsNotSet.Error(),
|
errMessage: ErrLoggerIsNotSet.Error(),
|
||||||
},
|
},
|
||||||
"read header timeout too small": {
|
|
||||||
settings: Settings{
|
|
||||||
Address: ":8000",
|
|
||||||
Handler: someHandler,
|
|
||||||
Logger: someLogger,
|
|
||||||
ReadHeaderTimeout: time.Nanosecond,
|
|
||||||
},
|
|
||||||
errWrapped: ErrReadHeaderTimeoutTooSmall,
|
|
||||||
errMessage: "read header timeout is too small: 1ns must be at least 1ms",
|
|
||||||
},
|
|
||||||
"read timeout too small": {
|
|
||||||
settings: Settings{
|
|
||||||
Address: ":8000",
|
|
||||||
Handler: someHandler,
|
|
||||||
Logger: someLogger,
|
|
||||||
ReadHeaderTimeout: time.Millisecond,
|
|
||||||
ReadTimeout: time.Nanosecond,
|
|
||||||
},
|
|
||||||
errWrapped: ErrReadTimeoutTooSmall,
|
|
||||||
errMessage: "read timeout is too small: 1ns must be at least 1ms",
|
|
||||||
},
|
|
||||||
"shutdown timeout too small": {
|
"shutdown timeout too small": {
|
||||||
settings: Settings{
|
settings: Settings{
|
||||||
Address: ":8000",
|
Address: ":8000",
|
||||||
Handler: someHandler,
|
Handler: someHandler,
|
||||||
Logger: someLogger,
|
Logger: someLogger,
|
||||||
ReadHeaderTimeout: time.Millisecond,
|
ShutdownTimeout: durationPtr(time.Millisecond),
|
||||||
ReadTimeout: time.Millisecond,
|
|
||||||
ShutdownTimeout: time.Millisecond,
|
|
||||||
},
|
},
|
||||||
errWrapped: ErrShutdownTimeoutTooSmall,
|
errWrapped: ErrShutdownTimeoutTooSmall,
|
||||||
errMessage: "shutdown timeout is too small: 1ms must be at least 5ms",
|
errMessage: "shutdown timeout is too small: 1ms must be at least 5ms",
|
||||||
},
|
},
|
||||||
"valid settings": {
|
"valid settings": {
|
||||||
settings: Settings{
|
settings: Settings{
|
||||||
Address: ":8000",
|
Address: ":8000",
|
||||||
Handler: someHandler,
|
Handler: someHandler,
|
||||||
Logger: someLogger,
|
Logger: someLogger,
|
||||||
ReadHeaderTimeout: time.Millisecond,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Millisecond,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -348,15 +291,11 @@ func Test_Settings_String(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
"all values": {
|
"all values": {
|
||||||
settings: Settings{
|
settings: Settings{
|
||||||
Address: ":8000",
|
Address: ":8000",
|
||||||
ReadHeaderTimeout: time.Millisecond,
|
ShutdownTimeout: durationPtr(time.Second),
|
||||||
ReadTimeout: time.Millisecond,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
},
|
||||||
s: `HTTP server settings:
|
s: `HTTP server settings:
|
||||||
├── Listening address: :8000
|
├── Listening address: :8000
|
||||||
├── Read header timeout: 1ms
|
|
||||||
├── Read timeout: 1ms
|
|
||||||
└── Shutdown timeout: 1s`,
|
└── Shutdown timeout: 1s`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func boolToMarkdown(b bool) string {
|
func boolToMarkdown(b bool) string {
|
||||||
@@ -27,11 +26,9 @@ const (
|
|||||||
hostnameHeader = "Hostname"
|
hostnameHeader = "Hostname"
|
||||||
ispHeader = "ISP"
|
ispHeader = "ISP"
|
||||||
multiHopHeader = "MultiHop"
|
multiHopHeader = "MultiHop"
|
||||||
nameHeader = "Name"
|
|
||||||
numberHeader = "Number"
|
numberHeader = "Number"
|
||||||
ownedHeader = "Owned"
|
ownedHeader = "Owned"
|
||||||
portForwardHeader = "Port forwarding"
|
portForwardHeader = "Port forwarding"
|
||||||
premiumHeader = "Premium"
|
|
||||||
regionHeader = "Region"
|
regionHeader = "Region"
|
||||||
streamHeader = "Stream"
|
streamHeader = "Stream"
|
||||||
tcpHeader = "TCP"
|
tcpHeader = "TCP"
|
||||||
@@ -59,16 +56,12 @@ func (s *Server) ToMarkdown(headers ...string) (markdown string) {
|
|||||||
fields[i] = s.ISP
|
fields[i] = s.ISP
|
||||||
case multiHopHeader:
|
case multiHopHeader:
|
||||||
fields[i] = boolToMarkdown(s.MultiHop)
|
fields[i] = boolToMarkdown(s.MultiHop)
|
||||||
case nameHeader:
|
|
||||||
fields[i] = s.ServerName
|
|
||||||
case numberHeader:
|
case numberHeader:
|
||||||
fields[i] = fmt.Sprint(s.Number)
|
fields[i] = fmt.Sprint(s.Number)
|
||||||
case ownedHeader:
|
case ownedHeader:
|
||||||
fields[i] = boolToMarkdown(s.Owned)
|
fields[i] = boolToMarkdown(s.Owned)
|
||||||
case portForwardHeader:
|
case portForwardHeader:
|
||||||
fields[i] = boolToMarkdown(s.PortForward)
|
fields[i] = boolToMarkdown(s.PortForward)
|
||||||
case premiumHeader:
|
|
||||||
fields[i] = boolToMarkdown(s.Premium)
|
|
||||||
case regionHeader:
|
case regionHeader:
|
||||||
fields[i] = s.Region
|
fields[i] = s.Region
|
||||||
case streamHeader:
|
case streamHeader:
|
||||||
@@ -76,7 +69,7 @@ func (s *Server) ToMarkdown(headers ...string) (markdown string) {
|
|||||||
case tcpHeader:
|
case tcpHeader:
|
||||||
fields[i] = boolToMarkdown(s.TCP)
|
fields[i] = boolToMarkdown(s.TCP)
|
||||||
case udpHeader:
|
case udpHeader:
|
||||||
fields[i] = boolToMarkdown(s.UDP || s.VPN == vpn.Wireguard)
|
fields[i] = boolToMarkdown(s.UDP)
|
||||||
case vpnHeader:
|
case vpnHeader:
|
||||||
fields[i] = s.VPN
|
fields[i] = s.VPN
|
||||||
}
|
}
|
||||||
@@ -102,9 +95,6 @@ func (s *Servers) ToMarkdown(vpnProvider string) (markdown string) {
|
|||||||
|
|
||||||
func getMarkdownHeaders(vpnProvider string) (headers []string) {
|
func getMarkdownHeaders(vpnProvider string) (headers []string) {
|
||||||
switch vpnProvider {
|
switch vpnProvider {
|
||||||
case providers.Airvpn:
|
|
||||||
return []string{regionHeader, countryHeader, cityHeader, vpnHeader,
|
|
||||||
udpHeader, tcpHeader, hostnameHeader, nameHeader}
|
|
||||||
case providers.Cyberghost:
|
case providers.Cyberghost:
|
||||||
return []string{countryHeader, hostnameHeader, tcpHeader, udpHeader}
|
return []string{countryHeader, hostnameHeader, tcpHeader, udpHeader}
|
||||||
case providers.Expressvpn:
|
case providers.Expressvpn:
|
||||||
@@ -133,14 +123,10 @@ func getMarkdownHeaders(vpnProvider string) (headers []string) {
|
|||||||
return []string{countryHeader, regionHeader, cityHeader, hostnameHeader, freeHeader}
|
return []string{countryHeader, regionHeader, cityHeader, hostnameHeader, freeHeader}
|
||||||
case providers.Purevpn:
|
case providers.Purevpn:
|
||||||
return []string{countryHeader, regionHeader, cityHeader, hostnameHeader, tcpHeader, udpHeader}
|
return []string{countryHeader, regionHeader, cityHeader, hostnameHeader, tcpHeader, udpHeader}
|
||||||
case providers.SlickVPN:
|
|
||||||
return []string{regionHeader, countryHeader, cityHeader, hostnameHeader}
|
|
||||||
case providers.Surfshark:
|
case providers.Surfshark:
|
||||||
return []string{regionHeader, countryHeader, cityHeader, hostnameHeader, multiHopHeader, tcpHeader, udpHeader}
|
return []string{regionHeader, countryHeader, cityHeader, hostnameHeader, multiHopHeader, tcpHeader, udpHeader}
|
||||||
case providers.Torguard:
|
case providers.Torguard:
|
||||||
return []string{countryHeader, cityHeader, hostnameHeader, tcpHeader, udpHeader}
|
return []string{countryHeader, cityHeader, hostnameHeader, tcpHeader, udpHeader}
|
||||||
case providers.VPNSecure:
|
|
||||||
return []string{regionHeader, cityHeader, hostnameHeader, premiumHeader}
|
|
||||||
case providers.VPNUnlimited:
|
case providers.VPNUnlimited:
|
||||||
return []string{countryHeader, cityHeader, hostnameHeader, freeHeader, streamHeader, tcpHeader, udpHeader}
|
return []string{countryHeader, cityHeader, hostnameHeader, freeHeader, streamHeader, tcpHeader, udpHeader}
|
||||||
case providers.Vyprvpn:
|
case providers.Vyprvpn:
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ type Server struct {
|
|||||||
WgPubKey string `json:"wgpubkey,omitempty"`
|
WgPubKey string `json:"wgpubkey,omitempty"`
|
||||||
Free bool `json:"free,omitempty"`
|
Free bool `json:"free,omitempty"`
|
||||||
Stream bool `json:"stream,omitempty"`
|
Stream bool `json:"stream,omitempty"`
|
||||||
Premium bool `json:"premium,omitempty"`
|
|
||||||
PortForward bool `json:"port_forward,omitempty"`
|
PortForward bool `json:"port_forward,omitempty"`
|
||||||
Keep bool `json:"keep,omitempty"`
|
Keep bool `json:"keep,omitempty"`
|
||||||
IPs []net.IP `json:"ips,omitempty"`
|
IPs []net.IP `json:"ips,omitempty"`
|
||||||
@@ -39,8 +38,7 @@ var (
|
|||||||
ErrVPNFieldEmpty = errors.New("vpn field is empty")
|
ErrVPNFieldEmpty = errors.New("vpn field is empty")
|
||||||
ErrHostnameFieldEmpty = errors.New("hostname field is empty")
|
ErrHostnameFieldEmpty = errors.New("hostname field is empty")
|
||||||
ErrIPsFieldEmpty = errors.New("ips field is empty")
|
ErrIPsFieldEmpty = errors.New("ips field is empty")
|
||||||
ErrNoNetworkProtocol = errors.New("both TCP and UDP fields are false for OpenVPN")
|
ErrNoNetworkProtocol = errors.New("both TCP and UDP fields are false")
|
||||||
ErrNetworkProtocolSet = errors.New("no network protocol should be set")
|
|
||||||
ErrWireguardPublicKeyEmpty = errors.New("wireguard public key field is empty")
|
ErrWireguardPublicKeyEmpty = errors.New("wireguard public key field is empty")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -52,9 +50,7 @@ func (s *Server) HasMinimumInformation() (err error) {
|
|||||||
return ErrHostnameFieldEmpty
|
return ErrHostnameFieldEmpty
|
||||||
case len(s.IPs) == 0:
|
case len(s.IPs) == 0:
|
||||||
return ErrIPsFieldEmpty
|
return ErrIPsFieldEmpty
|
||||||
case s.VPN == vpn.Wireguard && (s.TCP || s.UDP):
|
case !s.TCP && !s.UDP:
|
||||||
return ErrNetworkProtocolSet
|
|
||||||
case s.VPN == vpn.OpenVPN && !s.TCP && !s.UDP:
|
|
||||||
return ErrNoNetworkProtocol
|
return ErrNoNetworkProtocol
|
||||||
case s.VPN == vpn.Wireguard && s.WgPubKey == "":
|
case s.VPN == vpn.Wireguard && s.WgPubKey == "":
|
||||||
return ErrWireguardPublicKeyEmpty
|
return ErrWireguardPublicKeyEmpty
|
||||||
|
|||||||
@@ -27,13 +27,13 @@ func (a *AllServers) MarshalJSON() (data []byte, err error) {
|
|||||||
|
|
||||||
_, err = buffer.WriteString("{")
|
_, err = buffer.WriteString("{")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("writing opening bracket: %w", err)
|
return nil, fmt.Errorf("cannot write opening bracket: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
versionString := fmt.Sprintf(`"version":%d`, a.Version)
|
versionString := fmt.Sprintf(`"version":%d`, a.Version)
|
||||||
_, err = buffer.WriteString(versionString)
|
_, err = buffer.WriteString(versionString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("writing schema version string: %w", err)
|
return nil, fmt.Errorf("cannot write schema version string: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sortedProviders := make(sort.StringSlice, 0, len(a.ProviderToServers))
|
sortedProviders := make(sort.StringSlice, 0, len(a.ProviderToServers))
|
||||||
@@ -46,26 +46,26 @@ func (a *AllServers) MarshalJSON() (data []byte, err error) {
|
|||||||
providerKey := fmt.Sprintf(`,"%s":`, provider)
|
providerKey := fmt.Sprintf(`,"%s":`, provider)
|
||||||
_, err = buffer.WriteString(providerKey)
|
_, err = buffer.WriteString(providerKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("writing provider key %s: %w",
|
return nil, fmt.Errorf("cannot write provider key %s: %w",
|
||||||
providerKey, err)
|
providerKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
servers := a.ProviderToServers[provider]
|
servers := a.ProviderToServers[provider]
|
||||||
serversJSON, err := json.Marshal(servers)
|
serversJSON, err := json.Marshal(servers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("encoding servers for provider %s: %w",
|
return nil, fmt.Errorf("failed encoding servers for provider %s: %w",
|
||||||
provider, err)
|
provider, err)
|
||||||
}
|
}
|
||||||
_, err = buffer.Write(serversJSON)
|
_, err = buffer.Write(serversJSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("writing JSON servers data for provider %s: %w",
|
return nil, fmt.Errorf("cannot write JSON servers data for provider %s: %w",
|
||||||
provider, err)
|
provider, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = buffer.WriteString("}")
|
_, err = buffer.WriteString("}")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("writing closing bracket: %w", err)
|
return nil, fmt.Errorf("cannot write closing bracket: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer.Bytes(), nil
|
return buffer.Bytes(), nil
|
||||||
@@ -127,14 +127,14 @@ func (a *AllServers) UnmarshalJSON(data []byte) (err error) {
|
|||||||
|
|
||||||
jsonValue, err := json.Marshal(value)
|
jsonValue, err := json.Marshal(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("encoding %s servers: %w",
|
return fmt.Errorf("cannot marshal %s servers: %w",
|
||||||
key, err)
|
key, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var servers Servers
|
var servers Servers
|
||||||
err = json.Unmarshal(jsonValue, &servers)
|
err = json.Unmarshal(jsonValue, &servers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("decoding %s servers: %w",
|
return fmt.Errorf("cannot unmarshal %s servers: %w",
|
||||||
key, err)
|
key, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user