Compare commits

..

2 Commits

Author SHA1 Message Date
Quentin McGaw 1f52df9747 DNS ready signaling fixed 2021-01-02 23:55:53 +00:00
Quentin McGaw f04fd845bb Bug fix: DNS setup failure loop behavior 2021-01-02 23:55:29 +00:00
86 changed files with 3734 additions and 1975 deletions
+11 -22
View File
@@ -31,34 +31,25 @@
"remote.extensionKind": { "remote.extensionKind": {
"ms-azuretools.vscode-docker": "workspace" "ms-azuretools.vscode-docker": "workspace"
}, },
"editor.codeActionsOnSaveTimeout": 3000,
"go.useLanguageServer": true, "go.useLanguageServer": true,
"[go]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true,
},
// Optional: Disable snippets, as they conflict with completion ranking.
"editor.snippetSuggestions": "none"
},
"[go.mod]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true,
},
},
"gopls": {
"usePlaceholders": false,
"staticcheck": true
},
"go.autocompleteUnimportedPackages": true, "go.autocompleteUnimportedPackages": true,
"go.gotoSymbol.includeImports": true, "go.gotoSymbol.includeImports": true,
"go.gotoSymbol.includeGoroot": true, "go.gotoSymbol.includeGoroot": true,
"gopls": {
"completeUnimported": true,
"deepCompletion": true,
"usePlaceholders": false
},
"go.lintTool": "golangci-lint", "go.lintTool": "golangci-lint",
"go.buildOnSave": "workspace", "go.buildOnSave": "workspace",
"go.lintOnSave": "workspace", "go.lintOnSave": "workspace",
"go.vetOnSave": "workspace", "go.vetOnSave": "workspace",
"editor.formatOnSave": true, "editor.formatOnSave": true,
"[go]": {
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
"go.toolsEnvVars": { "go.toolsEnvVars": {
"GOFLAGS": "-tags=", "GOFLAGS": "-tags=",
// "CGO_ENABLED": 1 // for the race detector // "CGO_ENABLED": 1 // for the race detector
@@ -66,9 +57,7 @@
"gopls.env": { "gopls.env": {
"GOFLAGS": "-tags=" "GOFLAGS": "-tags="
}, },
"go.testEnvVars": { "go.testEnvVars": {},
"": ""
},
"go.testFlags": [ "go.testFlags": [
"-v", "-v",
// "-race" // "-race"
+3 -1
View File
@@ -1,9 +1,11 @@
.devcontainer .devcontainer
.git .git
.github .github
.vscode
cmd
!cmd/gluetun
doc doc
docker-compose.yml docker-compose.yml
Dockerfile
LICENSE LICENSE
README.md README.md
title.svg title.svg
+13 -2
View File
@@ -7,12 +7,23 @@ Contributions are [released](https://help.github.com/articles/github-terms-of-se
1. [Fork](https://github.com/qdm12/gluetun/fork) and clone the repository 1. [Fork](https://github.com/qdm12/gluetun/fork) and clone the repository
1. Create a new branch `git checkout -b my-branch-name` 1. Create a new branch `git checkout -b my-branch-name`
1. Modify the code 1. Modify the code
1. Ensure the docker build succeeds `docker build .` (you might need `export DOCKER_BUILDKIT=1`) 1. Ensure the docker build succeeds `docker build .`
1. Commit your modifications 1. Commit your modifications
1. Push to your fork and [submit a pull request](https://github.com/qdm12/gluetun/compare) 1. Push to your fork and [submit a pull request](https://github.com/qdm12/gluetun/compare)
## Resources ## Resources
- [Gluetun guide on development](https://github.com/qdm12/gluetun/wiki/Development)
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) - [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
## Contributors
Thanks for all the contributions, whether small or not so small!
- [@JeordyR](https://github.com/JeordyR) for testing the Mullvad version and opening a [PR with a few fixes](https://github.com/qdm12/gluetun/pull/84/files) 👍
- [@rorph](https://github.com/rorph) for a [PR to pick a random region for PIA](https://github.com/qdm12/gluetun/pull/70) and a [PR to make the container work with kubernetes](https://github.com/qdm12/gluetun/pull/69)
- [@JesterEE](https://github.com/JesterEE) for a [PR to fix silly line endings in block lists back then](https://github.com/qdm12/gluetun/pull/55) 📎
- [@elmerfdz](https://github.com/elmerfdz) for a [PR to add timezone information to have correct log timestampts](https://github.com/qdm12/gluetun/pull/51) 🕙
- [@Juggels](https://github.com/Juggels) for a [PR to write the PIA forwarded port to a file](https://github.com/qdm12/gluetun/pull/43)
- [@gdlx](https://github.com/gdlx) for a [PR to fix and improve PIA port forwarding script](https://github.com/qdm12/gluetun/pull/32)
- [@janaz](https://github.com/janaz) for keeping an eye on [updating things in the Dockerfile](https://github.com/qdm12/gluetun/pull/8)
-15
View File
@@ -1,15 +0,0 @@
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: docker
directory: /
schedule:
interval: "daily"
- package-ecosystem: gomod
directory: /
schedule:
interval: "daily"
+7 -86
View File
@@ -1,100 +1,21 @@
name: CI name: Docker build
on: on:
push: pull_request:
branches: [master]
paths: paths:
- .github/workflows/build.yml - .github/workflows/build.yml
- cmd/** - cmd/**
- internal/** - internal/**
- pkg/**
- .dockerignore - .dockerignore
- .golangci.yml - .golangci.yml
- Dockerfile - Dockerfile
- go.mod - go.mod
- go.sum - go.sum
jobs: jobs:
verify: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
DOCKER_BUILDKIT: "1"
steps: steps:
- uses: actions/checkout@v2 - name: Checkout
uses: actions/checkout@v2
- name: Linting - name: Build image
run: docker build --target lint .
- name: Go mod tidy check
run: docker build --target tidy .
- name: Build test image
run: docker build --target test -t test-container .
- name: Run tests in test container
run: |
touch coverage.txt
docker run --rm \
-v "$(pwd)/coverage.txt:/tmp/gobuild/coverage.txt" \
test-container \
go test \
-race \
-coverpkg=./... \
-coverprofile=coverage.txt \
-covermode=atomic \
./...
# We run this here to use the caching of the previous steps
- if: github.event_name == 'push'
name: Build final image
run: docker build . run: docker build .
publish:
needs: [verify]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: docker/setup-qemu-action@v1
- uses: docker/setup-buildx-action@v1
- uses: docker/login-action@v1
with:
username: qmcgaw
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Set variables
id: vars
env:
EVENT_NAME: ${{ github.event_name }}
run: |
BRANCH=${GITHUB_REF#refs/heads/}
TAG=${GITHUB_REF#refs/tags/}
echo ::set-output name=commit::$(git rev-parse --short HEAD)
echo ::set-output name=build_date::$(date -u +%Y-%m-%dT%H:%M:%SZ)
if [ "$TAG" != "$GITHUB_REF" ]; then
echo ::set-output name=version::$TAG
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/s390x,linux/ppc64le
elif [ "$BRANCH" = "master" ]; then
echo ::set-output name=version::latest
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/s390x,linux/ppc64le
else
echo ::set-output name=version::$BRANCH
echo ::set-output name=platforms::linux/amd64
fi
- name: Build and push final image
uses: docker/build-push-action@v2
with:
platforms: ${{ steps.vars.outputs.platforms }}
build-args: |
BUILD_DATE=${{ steps.vars.outputs.build_date }}
COMMIT=${{ steps.vars.outputs.commit }}
VERSION=${{ steps.vars.outputs.version }}
tags: |
qmcgaw/gluetun:${{ steps.vars.outputs.version }}
qmcgaw/private-internet-access:${{ steps.vars.outputs.version }}
push: true
- if: github.event_name == 'push' && github.event.ref == 'refs/heads/master'
name: Microbadger hook
run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/gluetun/l-keGI7p4IhX4QuIDMFYKhsZ1L0=
continue-on-error: true
+39
View File
@@ -0,0 +1,39 @@
name: Buildx branch
on:
push:
branches:
- "*"
- "*/*"
- "!master"
paths:
- .github/workflows/buildx-branch.yml
- cmd/**
- internal/**
- .dockerignore
- .golangci.yml
- Dockerfile
- go.mod
- go.sum
jobs:
buildx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Buildx setup
uses: crazy-max/ghaction-docker-buildx@v3
- name: Dockerhub login
run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u qmcgaw --password-stdin 2>&1
- name: Run Buildx
run: |
docker buildx build \
--progress plain \
--platform=linux/amd64 \
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg COMMIT=`git rev-parse --short HEAD` \
--build-arg VERSION=${GITHUB_REF##*/} \
-t qmcgaw/private-internet-access:${GITHUB_REF##*/} \
-t qmcgaw/gluetun:${GITHUB_REF##*/} \
--push \
.
- run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/private-internet-access/tQFy7AxtSUNANPe6aoVChYdsI_I=
continue-on-error: true
+36
View File
@@ -0,0 +1,36 @@
name: Buildx latest
on:
push:
branches: [master]
paths:
- .github/workflows/buildx-latest.yml
- cmd/**
- internal/**
- .dockerignore
- .golangci.yml
- Dockerfile
- go.mod
- go.sum
jobs:
buildx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Buildx setup
uses: crazy-max/ghaction-docker-buildx@v3
- name: Dockerhub login
run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u qmcgaw --password-stdin 2>&1
- name: Run Buildx
run: |
docker buildx build \
--progress plain \
--platform=linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6 \
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg COMMIT=`git rev-parse --short HEAD` \
--build-arg VERSION=latest \
-t qmcgaw/private-internet-access:latest \
-t qmcgaw/gluetun:latest \
--push \
.
- run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/private-internet-access/tQFy7AxtSUNANPe6aoVChYdsI_I=
continue-on-error: true
+36
View File
@@ -0,0 +1,36 @@
name: Buildx release
on:
release:
types: [published]
paths:
- .github/workflows/buildx-release.yml
- cmd/**
- internal/**
- .dockerignore
- .golangci.yml
- Dockerfile
- go.mod
- go.sum
jobs:
buildx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Buildx setup
uses: crazy-max/ghaction-docker-buildx@v3
- name: Dockerhub login
run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u qmcgaw --password-stdin 2>&1
- name: Run Buildx
run: |
docker buildx build \
--progress plain \
--platform=linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6 \
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg COMMIT=`git rev-parse --short HEAD` \
--build-arg VERSION=${GITHUB_REF##*/} \
-t qmcgaw/private-internet-access:${GITHUB_REF##*/} \
-t qmcgaw/gluetun:${GITHUB_REF##*/} \
--push \
.
- run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/private-internet-access/tQFy7AxtSUNANPe6aoVChYdsI_I=
continue-on-error: true
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Docker Hub Description - name: Docker Hub Description
uses: peter-evans/dockerhub-description@v2 uses: peter-evans/dockerhub-description@v2.4.1
with: with:
username: qmcgaw username: qmcgaw
password: ${{ secrets.DOCKERHUB_PASSWORD }} password: ${{ secrets.DOCKERHUB_PASSWORD }}
+7 -4
View File
@@ -9,7 +9,10 @@ jobs:
labeler: labeler:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - name: Checkout
- uses: crazy-max/ghaction-github-labeler@v3 uses: actions/checkout@v2
with: - name: Labeler
yaml-file: .github/labels.yml if: success()
uses: crazy-max/ghaction-github-labeler@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+2 -1
View File
@@ -9,7 +9,8 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: reviewdog/action-misspell@v1 - uses: reviewdog/action-misspell@master
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }}
locale: "US" locale: "US"
level: error level: error
+9
View File
@@ -0,0 +1,9 @@
{
"recommendations": [
"shardulm94.trailing-spaces",
"ms-azuretools.vscode-docker",
"davidanson.vscode-markdownlint",
"IBM.output-colorizer",
"golang.go"
]
}
+91
View File
@@ -0,0 +1,91 @@
{
// General settings
"files.eol": "\n",
// Docker
"remote.extensionKind": {
"ms-azuretools.vscode-docker": "workspace"
},
// Golang general settings
"go.useLanguageServer": true,
"go.autocompleteUnimportedPackages": true,
"go.gotoSymbol.includeImports": true,
"go.gotoSymbol.includeGoroot": true,
"gopls": {
"completeUnimported": true,
"deepCompletion": true,
"usePlaceholders": false
},
"go.lintTool": "golangci-lint",
"go.lintFlags": [
"--fast",
"--enable",
"rowserrcheck",
"--enable",
"bodyclose",
"--enable",
"dogsled",
"--enable",
"dupl",
"--enable",
"gochecknoglobals",
"--enable",
"gochecknoinits",
"--enable",
"gocognit",
"--enable",
"goconst",
"--enable",
"gocritic",
"--enable",
"gocyclo",
"--enable",
"goimports",
"--enable",
"golint",
"--enable",
"gosec",
"--enable",
"interfacer",
"--enable",
"maligned",
"--enable",
"misspell",
"--enable",
"nakedret",
"--enable",
"prealloc",
"--enable",
"scopelint",
"--enable",
"unconvert",
"--enable",
"unparam",
"--enable",
"whitespace"
],
// Golang on save
"go.buildOnSave": "workspace",
"go.lintOnSave": "workspace",
"go.vetOnSave": "workspace",
"editor.formatOnSave": true,
"[go]": {
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
// Golang testing
"go.toolsEnvVars": {
"GOFLAGS": "-tags="
},
"gopls.env": {
"GOFLAGS": "-tags="
},
"go.testEnvVars": {},
"go.testFlags": [
"-v",
// "-race"
],
"go.testTimeout": "600s",
"go.coverOnSingleTestFile": true,
"go.coverOnSingleTest": true
}
+12 -37
View File
@@ -1,52 +1,27 @@
ARG ALPINE_VERSION=3.13 ARG ALPINE_VERSION=3.12
ARG GO_VERSION=1.15 ARG GO_VERSION=1.15
ARG BUILDPLATFORM=linux/amd64
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS builder
RUN apk --update add git RUN apk --update add git
ENV CGO_ENABLED=0 ENV CGO_ENABLED=0
ARG GOLANGCI_LINT_VERSION=v1.34.1
RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s ${GOLANGCI_LINT_VERSION}
WORKDIR /tmp/gobuild WORKDIR /tmp/gobuild
COPY .golangci.yml .
COPY go.mod go.sum ./ COPY go.mod go.sum ./
RUN go mod download RUN go mod download
COPY cmd/ ./cmd/
COPY internal/ ./internal/
FROM --platform=$BUILDPLATFORM base AS test
# Note on the go race detector:
# - we set CGO_ENABLED=1 to have it enabled
# - we install g++ to support the race detector
ENV CGO_ENABLED=1
RUN apk --update --no-cache add g++
FROM --platform=$BUILDPLATFORM base AS lint
ARG GOLANGCI_LINT_VERSION=v1.35.2
RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | \
sh -s -- -b /usr/local/bin ${GOLANGCI_LINT_VERSION}
COPY .golangci.yml ./
RUN golangci-lint run --timeout=10m
FROM --platform=$BUILDPLATFORM base AS tidy
RUN git init && \
git config user.email ci@localhost && \
git config user.name ci && \
git add -A && git commit -m ci && \
sed -i '/\/\/ indirect/d' go.mod && \
go mod tidy && \
git diff --exit-code -- go.mod
FROM --platform=$BUILDPLATFORM base AS build
COPY --from=qmcgaw/xcputranslate:v0.4.0 /xcputranslate /usr/local/bin/xcputranslate
ARG TARGETPLATFORM
ARG VERSION=unknown ARG VERSION=unknown
ARG BUILD_DATE="an unknown date" ARG BUILD_DATE="an unknown date"
ARG COMMIT=unknown ARG COMMIT=unknown
RUN GOARCH="$(xcputranslate -field arch -targetplatform ${TARGETPLATFORM})" \ COPY cmd/gluetun/main.go .
GOARM="$(xcputranslate -field arm -targetplatform ${TARGETPLATFORM})" \ COPY internal/ ./internal/
go build -trimpath -ldflags="-s -w \ RUN go test ./...
RUN golangci-lint run --timeout=10m
RUN go build -trimpath -ldflags="-s -w \
-X 'main.version=$VERSION' \ -X 'main.version=$VERSION' \
-X 'main.buildDate=$BUILD_DATE' \ -X 'main.buildDate=$BUILD_DATE' \
-X 'main.commit=$COMMIT' \ -X 'main.commit=$COMMIT' \
" -o entrypoint cmd/gluetun/main.go " -o entrypoint main.go
FROM alpine:${ALPINE_VERSION} FROM alpine:${ALPINE_VERSION}
ARG VERSION=unknown ARG VERSION=unknown
@@ -151,4 +126,4 @@ RUN apk add -q --progress --no-cache --update openvpn ca-certificates iptables i
mkdir /gluetun mkdir /gluetun
# TODO remove once SAN is added to PIA servers certificates, see https://github.com/pia-foss/manual-connections/issues/10 # TODO remove once SAN is added to PIA servers certificates, see https://github.com/pia-foss/manual-connections/issues/10
ENV GODEBUG=x509ignoreCN=0 ENV GODEBUG=x509ignoreCN=0
COPY --from=build /tmp/gobuild/entrypoint /entrypoint COPY --from=builder /tmp/gobuild/entrypoint /entrypoint
+17 -1
View File
@@ -47,7 +47,7 @@ Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN and Privado
- Built in HTTP proxy (tunnels HTTP and HTTPS through TCP) - Built in HTTP proxy (tunnels HTTP and HTTPS through TCP)
- [Connect other containers to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun) - [Connect other containers to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
- [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun) - [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even s390x as well as ppc64le 🎆 - Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7 🎆
- VPN server side port forwarding for Private Internet Access and Vyprvpn - VPN server side port forwarding for Private Internet Access and Vyprvpn
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers - Possibility of split horizon DNS by selecting multiple DNS over TLS providers
- Subprograms all drop root privileges once launched - Subprograms all drop root privileges once launched
@@ -89,10 +89,26 @@ The following points are all optional but should give you insights on all the po
- [HTTP control server](https://github.com/qdm12/gluetun/wiki/HTTP-Control-server) to automate things, restart Openvpn etc. - [HTTP control server](https://github.com/qdm12/gluetun/wiki/HTTP-Control-server) to automate things, restart Openvpn etc.
- Update the image with `docker pull qmcgaw/gluetun:latest`. See this [Wiki document](https://github.com/qdm12/gluetun/wiki/Docker-image-tags) for Docker tags available. - Update the image with `docker pull qmcgaw/gluetun:latest`. See this [Wiki document](https://github.com/qdm12/gluetun/wiki/Docker-image-tags) for Docker tags available.
## Development
- 💻 [Contribute with code](https://github.com/qdm12/gluetun/wiki/Development) ([existing contributors 👍](https://github.com/qdm12/gluetun/blob/master/.github/CONTRIBUTING.md#Contributors))
- [List of issues and feature requests](https://github.com/qdm12/gluetun/issues)
- [Kanban board](https://github.com/qdm12/gluetun/projects/1)
## License ## License
[![MIT](https://img.shields.io/github/license/qdm12/gluetun)](https://github.com/qdm12/gluetun/master/LICENSE) [![MIT](https://img.shields.io/github/license/qdm12/gluetun)](https://github.com/qdm12/gluetun/master/LICENSE)
## Support
- Sponsor me on [Github](https://github.com/sponsors/qdm12) or donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw)
[![https://github.com/sponsors/qdm12](https://raw.githubusercontent.com/qdm12/gluetun/master/doc/sponsors.jpg)](https://github.com/sponsors/qdm12)
[![https://www.paypal.me/qmcgaw](https://raw.githubusercontent.com/qdm12/gluetun/master/doc/paypal.jpg)](https://www.paypal.me/qmcgaw)
- Contribute to the issues and discussions on Github
- Many thanks to @Frepke, @Ralph521, G. Mendez, M. Otmar Weber, J. Perez, A. Cooper and **others** for supporting me financially 🥇👍
## Metadata ## Metadata
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/commits) [![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/commits)
+104 -102
View File
@@ -12,7 +12,6 @@ import (
"syscall" "syscall"
"time" "time"
"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/constants" "github.com/qdm12/gluetun/internal/constants"
@@ -37,7 +36,6 @@ import (
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
"github.com/qdm12/golibs/os/user" "github.com/qdm12/golibs/os/user"
"github.com/qdm12/updated/pkg/dnscrypto"
) )
//nolint:gochecknoglobals //nolint:gochecknoglobals
@@ -53,92 +51,49 @@ func main() {
Commit: commit, Commit: commit,
BuildDate: buildDate, BuildDate: buildDate,
} }
ctx := context.Background() ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
logger, err := logging.NewLogger(logging.ConsoleEncoding, logging.InfoLevel)
if err != nil {
fmt.Println(err)
nativeos.Exit(1)
}
args := nativeos.Args args := nativeos.Args
os := os.New() os := os.New()
osUser := user.New() osUser := user.New()
unix := unix.New() unix := unix.New()
cli := cli.New() cli := cli.New()
nativeos.Exit(_main(ctx, buildInfo, args, os, osUser, unix, cli))
errorCh := make(chan error)
go func() {
errorCh <- _main(ctx, buildInfo, args, logger, os, osUser, unix, cli)
}()
signalsCh := make(chan nativeos.Signal, 1)
signal.Notify(signalsCh,
syscall.SIGINT,
syscall.SIGTERM,
nativeos.Interrupt,
)
select {
case signal := <-signalsCh:
logger.Warn("Caught OS signal %s, shutting down", signal)
case err := <-errorCh:
close(errorCh)
if err == nil { // expected exit such as healthcheck
nativeos.Exit(0)
}
logger.Error(err)
}
cancel()
const shutdownGracePeriod = 5 * time.Second
timer := time.NewTimer(shutdownGracePeriod)
select {
case <-errorCh:
if !timer.Stop() {
<-timer.C
}
logger.Info("Shutdown successful")
case <-timer.C:
logger.Warn("Shutdown timed out")
}
nativeos.Exit(1)
} }
//nolint:gocognit,gocyclo //nolint:gocognit,gocyclo
func _main(background context.Context, buildInfo models.BuildInformation, func _main(background context.Context, buildInfo models.BuildInformation,
args []string, logger logging.Logger, os os.OS, osUser user.OSUser, unix unix.Unix, args []string, os os.OS, osUser user.OSUser, unix unix.Unix,
cli cli.CLI) error { cli cli.CLI) int {
if len(args) > 1 { // cli operation if len(args) > 1 { // cli operation
var err error
switch args[1] { switch args[1] {
case "healthcheck": case "healthcheck":
return cli.HealthCheck(background) err = cli.HealthCheck(background)
case "clientkey": case "clientkey":
return cli.ClientKey(args[2:], os.OpenFile) err = cli.ClientKey(args[2:], os.OpenFile)
case "openvpnconfig": case "openvpnconfig":
return cli.OpenvpnConfig(os) err = cli.OpenvpnConfig(os)
case "update": case "update":
return cli.Update(args[2:], os) err = cli.Update(args[2:], os)
default: default:
return fmt.Errorf("command %q is unknown", args[1]) err = fmt.Errorf("command %q is unknown", args[1])
} }
if err != nil {
fmt.Println(err)
return 1
}
return 0
} }
ctx, cancel := context.WithCancel(background) ctx, cancel := context.WithCancel(background)
defer cancel() defer cancel()
logger := createLogger()
const clientTimeout = 15 * time.Second const clientTimeout = 15 * time.Second
httpClient := &http.Client{Timeout: clientTimeout} httpClient := &http.Client{Timeout: clientTimeout}
// Create configurators // Create configurators
alpineConf := alpine.NewConfigurator(os.OpenFile, osUser) alpineConf := alpine.NewConfigurator(os.OpenFile, osUser)
ovpnConf := openvpn.NewConfigurator(logger, os, unix) ovpnConf := openvpn.NewConfigurator(logger, os, unix)
dnsCrypto := dnscrypto.New(httpClient, "", "") dnsConf := dns.NewConfigurator(logger, httpClient, os.OpenFile)
const cacertsPath = "/etc/ssl/certs/ca-certificates.crt"
dnsConf := unbound.NewConfigurator(logger, os.OpenFile, dnsCrypto,
"/etc/unbound", "/usr/sbin/unbound", cacertsPath)
routingConf := routing.NewRouting(logger) routingConf := routing.NewRouting(logger)
firewallConf := firewall.NewConfigurator(logger, routingConf, os.OpenFile) firewallConf := firewall.NewConfigurator(logger, routingConf, os.OpenFile)
streamMerger := command.NewStreamMerger() streamMerger := command.NewStreamMerger()
@@ -152,27 +107,28 @@ func _main(background context.Context, buildInfo models.BuildInformation,
"IPtables": firewallConf.Version, "IPtables": firewallConf.Version,
}) })
allSettings, warnings, err := settings.GetAllSettings(paramsReader) allSettings, err := settings.GetAllSettings(paramsReader)
for _, warning := range warnings {
logger.Warn(warning)
}
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
logger.Info(allSettings.String()) logger.Info(allSettings.String())
if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil { if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil {
return err logger.Error(err)
return 1
} }
if err := os.MkdirAll("/gluetun", 0644); err != nil { if err := os.MkdirAll("/gluetun", 0644); err != nil {
return err logger.Error(err)
return 1
} }
// TODO run this in a loop or in openvpn to reload from file without restarting // TODO run this in a loop or in openvpn to reload from file without restarting
storage := storage.New(logger, os, constants.ServersData) storage := storage.New(logger, os, constants.ServersData)
allServers, err := storage.SyncServers(constants.GetAllServers()) allServers, err := storage.SyncServers(constants.GetAllServers())
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
// Should never change // Should never change
@@ -181,14 +137,16 @@ func _main(background 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 err logger.Error(err)
return 1
} }
if nonRootUsername != defaultUsername { if nonRootUsername != defaultUsername {
logger.Info("using existing username %s corresponding to user id %d", nonRootUsername, puid) logger.Info("using existing username %s corresponding to user id %d", nonRootUsername, puid)
} }
if err := os.Chown("/etc/unbound", puid, pgid); err != nil { if err := os.Chown("/etc/unbound", puid, pgid); err != nil {
return err logger.Error(err)
return 1
} }
if allSettings.Firewall.Debug { if allSettings.Firewall.Debug {
@@ -198,23 +156,27 @@ func _main(background context.Context, buildInfo models.BuildInformation,
defaultInterface, defaultGateway, err := routingConf.DefaultRoute() defaultInterface, defaultGateway, err := routingConf.DefaultRoute()
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
localSubnet, err := routingConf.LocalSubnet() localSubnet, err := routingConf.LocalSubnet()
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
defaultIP, err := routingConf.DefaultIP() defaultIP, err := routingConf.DefaultIP()
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
firewallConf.SetNetworkInformation(defaultInterface, defaultGateway, localSubnet, defaultIP) firewallConf.SetNetworkInformation(defaultInterface, defaultGateway, localSubnet, defaultIP)
if err := routingConf.Setup(); err != nil { if err := routingConf.Setup(); err != nil {
return err logger.Error(err)
return 1
} }
defer func() { defer func() {
routingConf.SetVerbose(false) routingConf.SetVerbose(false)
@@ -224,50 +186,56 @@ func _main(background context.Context, buildInfo models.BuildInformation,
}() }()
if err := firewallConf.SetOutboundSubnets(ctx, allSettings.Firewall.OutboundSubnets); err != nil { if err := firewallConf.SetOutboundSubnets(ctx, allSettings.Firewall.OutboundSubnets); err != nil {
return err logger.Error(err)
return 1
} }
if err := routingConf.SetOutboundRoutes(allSettings.Firewall.OutboundSubnets); err != nil { if err := routingConf.SetOutboundRoutes(allSettings.Firewall.OutboundSubnets); err != nil {
return err logger.Error(err)
return 1
} }
if err := ovpnConf.CheckTUN(); err != nil { if err := ovpnConf.CheckTUN(); err != nil {
logger.Warn(err) logger.Warn(err)
err = ovpnConf.CreateTUN() err = ovpnConf.CreateTUN()
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
} }
tunnelReadyCh := make(chan struct{}) tunnelReadyCh, dnsReadyCh := make(chan struct{}), make(chan struct{})
dnsReadyCh := make(chan struct{}) signalTunnelReady := func() { tunnelReadyCh <- struct{}{} }
signalDNSReady := func() { dnsReadyCh <- struct{}{} }
defer close(tunnelReadyCh) defer close(tunnelReadyCh)
defer close(dnsReadyCh) defer close(dnsReadyCh)
if allSettings.Firewall.Enabled { if allSettings.Firewall.Enabled {
err := firewallConf.SetEnabled(ctx, true) // disabled by default err := firewallConf.SetEnabled(ctx, true) // disabled by default
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
} }
for _, vpnPort := range allSettings.Firewall.VPNInputPorts { for _, vpnPort := range allSettings.Firewall.VPNInputPorts {
err = firewallConf.SetAllowedPort(ctx, vpnPort, string(constants.TUN)) err = firewallConf.SetAllowedPort(ctx, vpnPort, string(constants.TUN))
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
} }
for _, port := range allSettings.Firewall.InputPorts { for _, port := range allSettings.Firewall.InputPorts {
err = firewallConf.SetAllowedPort(ctx, port, defaultInterface) err = firewallConf.SetAllowedPort(ctx, port, defaultInterface)
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
} // TODO move inside firewall? } // TODO move inside firewall?
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
wg.Add(1) go collectStreamLines(ctx, streamMerger, logger, signalTunnelReady)
go collectStreamLines(ctx, wg, streamMerger, logger, tunnelReadyCh)
openvpnLooper := openvpn.NewLooper(allSettings.OpenVPN, nonRootUsername, puid, pgid, allServers, openvpnLooper := openvpn.NewLooper(allSettings.OpenVPN, nonRootUsername, puid, pgid, allServers,
ovpnConf, firewallConf, routingConf, logger, httpClient, os.OpenFile, streamMerger, cancel) ovpnConf, firewallConf, routingConf, logger, httpClient, os.OpenFile, streamMerger, cancel)
@@ -281,11 +249,10 @@ func _main(background context.Context, buildInfo models.BuildInformation,
// wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker // wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker
go updaterLooper.Run(ctx, wg) go updaterLooper.Run(ctx, wg)
unboundLooper := dns.NewLooper(dnsConf, allSettings.DNS, httpClient, unboundLooper := dns.NewLooper(dnsConf, allSettings.DNS, logger, streamMerger, nonRootUsername, puid, pgid)
logger, streamMerger, nonRootUsername, puid, pgid)
wg.Add(1) wg.Add(1)
// wait for unboundLooper.Restart or its ticker launched with RunRestartTicker // wait for unboundLooper.Restart or its ticker launched with RunRestartTicker
go unboundLooper.Run(ctx, wg, dnsReadyCh) go unboundLooper.Run(ctx, wg, signalDNSReady)
publicIPLooper := publicip.NewLooper( publicIPLooper := publicip.NewLooper(
httpClient, logger, allSettings.PublicIP, puid, pgid, os) httpClient, logger, allSettings.PublicIP, puid, pgid, os)
@@ -323,18 +290,55 @@ func _main(background context.Context, buildInfo models.BuildInformation,
// until openvpn is launched // until openvpn is launched
_, _ = openvpnLooper.SetStatus(constants.Running) // TODO option to disable with variable _, _ = openvpnLooper.SetStatus(constants.Running) // TODO option to disable with variable
<-ctx.Done() signalsCh := make(chan nativeos.Signal, 1)
signal.Notify(signalsCh,
syscall.SIGINT,
syscall.SIGTERM,
nativeos.Interrupt,
)
shutdownErrorsCount := 0
select {
case signal := <-signalsCh:
logger.Warn("Caught OS signal %s, shutting down", signal)
cancel()
case <-ctx.Done():
logger.Warn("context canceled, shutting down")
}
if allSettings.OpenVPN.Provider.PortForwarding.Enabled { if allSettings.OpenVPN.Provider.PortForwarding.Enabled {
logger.Info("Clearing forwarded port status file %s", allSettings.OpenVPN.Provider.PortForwarding.Filepath) logger.Info("Clearing forwarded port status file %s", allSettings.OpenVPN.Provider.PortForwarding.Filepath)
if err := os.Remove(string(allSettings.OpenVPN.Provider.PortForwarding.Filepath)); err != nil { if err := os.Remove(string(allSettings.OpenVPN.Provider.PortForwarding.Filepath)); err != nil {
logger.Error(err) logger.Error(err)
shutdownErrorsCount++
} }
} }
const shutdownGracePeriod = 5 * time.Second
waiting, waited := context.WithTimeout(context.Background(), shutdownGracePeriod)
go func() {
defer waited()
wg.Wait()
}()
<-waiting.Done()
if waiting.Err() == context.DeadlineExceeded {
if shutdownErrorsCount > 0 {
logger.Warn("Shutdown had %d errors", shutdownErrorsCount)
}
logger.Warn("Shutdown timed out")
return 1
}
if shutdownErrorsCount > 0 {
logger.Warn("Shutdown had %d errors")
return 1
}
logger.Info("Shutdown successful")
return 0
}
wg.Wait() func createLogger() logging.Logger {
logger, err := logging.NewLogger(logging.ConsoleEncoding, logging.InfoLevel)
return nil if err != nil {
panic(err)
}
return logger
} }
func printVersions(ctx context.Context, logger logging.Logger, func printVersions(ctx context.Context, logger logging.Logger,
@@ -352,10 +356,9 @@ func printVersions(ctx context.Context, logger logging.Logger,
} }
} }
func collectStreamLines(ctx context.Context, wg *sync.WaitGroup, //nolint:lll
streamMerger command.StreamMerger, func collectStreamLines(ctx context.Context, streamMerger command.StreamMerger,
logger logging.Logger, tunnelReadyCh chan<- struct{}) { logger logging.Logger, signalTunnelReady func()) {
defer wg.Done()
// Blocking line merging paramsReader for openvpn and unbound // Blocking line merging paramsReader for openvpn and unbound
logger.Info("Launching standard output merger") logger.Info("Launching standard output merger")
streamMerger.CollectLines(ctx, func(line string) { streamMerger.CollectLines(ctx, func(line string) {
@@ -375,10 +378,10 @@ func collectStreamLines(ctx context.Context, wg *sync.WaitGroup,
} }
switch { switch {
case strings.Contains(line, "Initialization Sequence Completed"): case strings.Contains(line, "Initialization Sequence Completed"):
tunnelReadyCh <- struct{}{} signalTunnelReady()
case strings.Contains(line, "TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity)"): //nolint:lll case strings.Contains(line, "TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity)"):
logger.Warn("This means that either...") logger.Warn("This means that either...")
logger.Warn("1. The VPN server IP address you are trying to connect to is no longer valid, see https://github.com/qdm12/gluetun/wiki/Update-servers-information") //nolint:lll logger.Warn("1. The VPN server IP address you are trying to connect to is no longer valid, see https://github.com/qdm12/gluetun/wiki/Update-servers-information")
logger.Warn("2. The VPN server crashed, try changing region") logger.Warn("2. The VPN server crashed, try changing region")
logger.Warn("3. Your Internet connection is not working, ensure it works") logger.Warn("3. Your Internet connection is not working, ensure it works")
logger.Warn("Feel free to create an issue at https://github.com/qdm12/gluetun/issues/new/choose") logger.Warn("Feel free to create an issue at https://github.com/qdm12/gluetun/issues/new/choose")
@@ -411,8 +414,7 @@ func routeReadyEvents(ctx context.Context, wg *sync.WaitGroup, buildInfo models.
restartTickerCancel() // stop previous restart tickers restartTickerCancel() // stop previous restart tickers
tickerWg.Wait() tickerWg.Wait()
restartTickerContext, restartTickerCancel = context.WithCancel(ctx) restartTickerContext, restartTickerCancel = context.WithCancel(ctx)
//nolint:gomnd tickerWg.Add(2) //nolint:gomnd
tickerWg.Add(2)
go unboundLooper.RunRestartTicker(restartTickerContext, tickerWg) go unboundLooper.RunRestartTicker(restartTickerContext, tickerWg)
go updaterLooper.RunRestartTicker(restartTickerContext, tickerWg) go updaterLooper.RunRestartTicker(restartTickerContext, tickerWg)
vpnDestination, err := routing.VPNDestinationIP() vpnDestination, err := routing.VPNDestinationIP()
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

+2 -4
View File
@@ -6,11 +6,9 @@ require (
github.com/fatih/color v1.10.0 github.com/fatih/color v1.10.0
github.com/golang/mock v1.4.4 github.com/golang/mock v1.4.4
github.com/kyokomi/emoji v2.2.4+incompatible github.com/kyokomi/emoji v2.2.4+incompatible
github.com/qdm12/dns v1.4.0-rc4 github.com/qdm12/golibs v0.0.0-20210102015428-6e1d159e61a3
github.com/qdm12/golibs v0.0.0-20210110211000-0a3a4541ae09
github.com/qdm12/ss-server v0.1.0 github.com/qdm12/ss-server v0.1.0
github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a github.com/stretchr/testify v1.6.1
github.com/stretchr/testify v1.7.0
github.com/vishvananda/netlink v1.1.0 github.com/vishvananda/netlink v1.1.0
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 golang.org/x/sys v0.0.0-20201223074533-0d417f636930
) )
+2 -45
View File
@@ -4,23 +4,16 @@ github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVk
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb h1:D4uzjWwKYQ5XnAvUbuvHW93esHg7F8N/OYeBBcJoTr0= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb h1:D4uzjWwKYQ5XnAvUbuvHW93esHg7F8N/OYeBBcJoTr0=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
@@ -51,19 +44,14 @@ github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gotify/go-api-client/v2 v2.0.4 h1:0w8skCr8aLBDKaQDg31LKKHUGF7rt7zdRpR+6cqIAlE= github.com/gotify/go-api-client/v2 v2.0.4 h1:0w8skCr8aLBDKaQDg31LKKHUGF7rt7zdRpR+6cqIAlE=
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU= github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kyokomi/emoji v2.2.4+incompatible h1:np0woGKwx9LiHAQmwZx79Oc0rHpNw3o+3evou4BEPv4= github.com/kyokomi/emoji v2.2.4+incompatible h1:np0woGKwx9LiHAQmwZx79Oc0rHpNw3o+3evou4BEPv4=
@@ -79,51 +67,35 @@ github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGe
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
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/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc= github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee h1:P6U24L02WMfj9ymZTxl7CxS73JC99x3ukk+DBkgQGQs= github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee h1:P6U24L02WMfj9ymZTxl7CxS73JC99x3ukk+DBkgQGQs=
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee/go.mod h1:3uODdxMgOaPYeWU7RzZLxVtJHZ/x1f/iHkBZuKJDzuY= github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee/go.mod h1:3uODdxMgOaPYeWU7RzZLxVtJHZ/x1f/iHkBZuKJDzuY=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/qdm12/dns v1.4.0-rc4 h1:pCcFMqismbktPQX7yrtdmJZq30Y05JBfWRTXY1ZLVFw= github.com/qdm12/golibs v0.0.0-20210102015428-6e1d159e61a3 h1:tnkjZkYZuAFNga7Wd/j3z3gPJLkv0OXow4q/YTkRdmE=
github.com/qdm12/dns v1.4.0-rc4/go.mod h1:JhUKBhuDRYBUQ2XwW/jbeWx/qS0sSJjIFjGTCFGP5I8= github.com/qdm12/golibs v0.0.0-20210102015428-6e1d159e61a3/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
github.com/qdm12/golibs v0.0.0-20201227203847-2fd99ffdfdba/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
github.com/qdm12/golibs v0.0.0-20210102020307-17bc97def973 h1:5YeJALmDjvg2wSi6XB8MpQQekbT/eBnwGahJrh01HHQ=
github.com/qdm12/golibs v0.0.0-20210102020307-17bc97def973/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
github.com/qdm12/golibs v0.0.0-20210110211000-0a3a4541ae09 h1:zP+ZRwV3GldgTWFgKNBQ2zoFA8mIczb+fvTvrX8LZRo=
github.com/qdm12/golibs v0.0.0-20210110211000-0a3a4541ae09/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
github.com/qdm12/ss-server v0.1.0 h1:WV9MkHCDEWRwe4WpnYFeR/zcZAxYoTbfntLDnw9AQ50= github.com/qdm12/ss-server v0.1.0 h1:WV9MkHCDEWRwe4WpnYFeR/zcZAxYoTbfntLDnw9AQ50=
github.com/qdm12/ss-server v0.1.0/go.mod h1:ABVUkxubboL3vqBkOwDV9glX1/x7SnYrckBe5d+M/zw= github.com/qdm12/ss-server v0.1.0/go.mod h1:ABVUkxubboL3vqBkOwDV9glX1/x7SnYrckBe5d+M/zw=
github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a h1:gkyP+gMEeBgMgyRYGrVNcoy6cL1065IvXsyfB6xboIc=
github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a/go.mod h1:bbJGxEYCnsA8WU4vBcXYU6mOoHyzdP458FIKP4mfLJM=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
@@ -132,15 +104,11 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEa
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@@ -148,28 +116,21 @@ golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 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/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/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-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-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-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo= golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -178,10 +139,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
-7
View File
@@ -1,7 +0,0 @@
package cli
import "context"
func (c *cli) CI(context context.Context) error {
return nil
}
+10 -2
View File
@@ -20,7 +20,7 @@ func (c *cli) OpenvpnConfig(os os.OS) error {
return err return err
} }
paramsReader := params.NewReader(logger, os) paramsReader := params.NewReader(logger, os)
allSettings, _, err := settings.GetAllSettings(paramsReader) allSettings, err := settings.GetAllSettings(paramsReader)
if err != nil { if err != nil {
return err return err
} }
@@ -34,7 +34,15 @@ func (c *cli) OpenvpnConfig(os os.OS) error {
if err != nil { if err != nil {
return err return err
} }
lines := providerConf.BuildConf(connection, "nonroortuser", allSettings.OpenVPN) lines := providerConf.BuildConf(
connection,
allSettings.OpenVPN.Verbosity,
"nonroortuser",
allSettings.OpenVPN.Root,
allSettings.OpenVPN.Cipher,
allSettings.OpenVPN.Auth,
allSettings.OpenVPN.Provider.ExtraConfigOptions,
)
fmt.Println(strings.Join(lines, "\n")) fmt.Println(strings.Join(lines, "\n"))
return nil return nil
} }
+98
View File
@@ -0,0 +1,98 @@
package constants
import (
"net"
"github.com/qdm12/gluetun/internal/models"
)
const (
// Cloudflare is a DNS over TLS provider.
Cloudflare models.DNSProvider = "cloudflare"
// Google is a DNS over TLS provider.
Google models.DNSProvider = "google"
// Quad9 is a DNS over TLS provider.
Quad9 models.DNSProvider = "quad9"
// Quadrant is a DNS over TLS provider.
Quadrant models.DNSProvider = "quadrant"
// CleanBrowsing is a DNS over TLS provider.
CleanBrowsing models.DNSProvider = "cleanbrowsing"
)
// DNSProviderMapping returns a constant mapping of dns provider name
// to their data such as IP addresses or TLS host name.
func DNSProviderMapping() map[models.DNSProvider]models.DNSProviderData {
return map[models.DNSProvider]models.DNSProviderData{
Cloudflare: {
IPs: []net.IP{
{1, 1, 1, 1},
{1, 0, 0, 1},
{0x26, 0x6, 0x47, 0x0, 0x47, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11},
{0x26, 0x6, 0x47, 0x0, 0x47, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x01},
},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("cloudflare-dns.com"),
},
Google: {
IPs: []net.IP{
{8, 8, 8, 8},
{8, 8, 4, 4},
{0x20, 0x1, 0x48, 0x60, 0x48, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88, 0x88},
{0x20, 0x1, 0x48, 0x60, 0x48, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88, 0x44},
},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("dns.google"),
},
Quad9: {
IPs: []net.IP{
{9, 9, 9, 9},
{149, 112, 112, 112},
{0x26, 0x20, 0x0, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe},
{0x26, 0x20, 0x0, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9},
},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("dns.quad9.net"),
},
Quadrant: {
IPs: []net.IP{
{12, 159, 2, 159},
{0x20, 0x1, 0x18, 0x90, 0x14, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x59},
},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("dns-tls.qis.io"),
},
CleanBrowsing: {
IPs: []net.IP{
{185, 228, 168, 9},
{185, 228, 169, 9},
{0x2a, 0xd, 0x2a, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2},
{0x2a, 0xd, 0x2a, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2},
},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("security-filter-dns.cleanbrowsing.org"),
},
}
}
// Block lists URLs.
//nolint:lll
const (
AdsBlockListHostnamesURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/ads-hostnames.updated"
AdsBlockListIPsURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/ads-ips.updated"
MaliciousBlockListHostnamesURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/malicious-hostnames.updated"
MaliciousBlockListIPsURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/malicious-ips.updated"
SurveillanceBlockListHostnamesURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/surveillance-hostnames.updated"
SurveillanceBlockListIPsURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/surveillance-ips.updated"
)
// DNS certificates to fetch.
// TODO obtain from source directly, see qdm12/updated).
const (
NamedRootURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/named.root.updated"
RootKeyURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/root.key.updated"
)
+1187 -1005
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -18,7 +18,7 @@ func GetAllServers() (allServers models.AllServers) {
}, },
Nordvpn: models.NordvpnServers{ Nordvpn: models.NordvpnServers{
Version: 1, Version: 1,
Timestamp: 1611096594, Timestamp: 1599323261,
Servers: NordvpnServers(), Servers: NordvpnServers(),
}, },
Pia: models.PiaServers{ Pia: models.PiaServers{
+1 -1
View File
@@ -128,7 +128,7 @@ func Test_timestamps(t *testing.T) {
"Nordvpn": { "Nordvpn": {
servers: allServers.Nordvpn.Servers, servers: allServers.Nordvpn.Servers,
timestamp: allServers.Nordvpn.Timestamp, timestamp: allServers.Nordvpn.Timestamp,
digest: "5f70b19b", digest: "9fc9a579",
}, },
"Private Internet Access": { "Private Internet Access": {
servers: allServers.Pia.Servers, servers: allServers.Pia.Servers,
+43
View File
@@ -0,0 +1,43 @@
package dns
import (
"context"
"fmt"
"io"
"strings"
"github.com/qdm12/gluetun/internal/constants"
)
func (c *configurator) Start(ctx context.Context, verbosityDetailsLevel uint8) (
stdout io.ReadCloser, waitFn func() error, err error) {
c.logger.Info("starting unbound")
args := []string{"-d", "-c", string(constants.UnboundConf)}
if verbosityDetailsLevel > 0 {
args = append(args, "-"+strings.Repeat("v", int(verbosityDetailsLevel)))
}
// Only logs to stderr
_, stdout, waitFn, err = c.commander.Start(ctx, "unbound", args...)
return stdout, waitFn, err
}
func (c *configurator) Version(ctx context.Context) (version string, err error) {
output, err := c.commander.Run(ctx, "unbound", "-V")
if err != nil {
return "", fmt.Errorf("unbound version: %w", err)
}
for _, line := range strings.Split(output, "\n") {
if strings.Contains(line, "Version ") {
words := strings.Fields(line)
const minWords = 2
if len(words) < minWords {
continue
}
version = words[1]
}
}
if version == "" {
return "", fmt.Errorf("unbound version was not found in %q", output)
}
return version, nil
}
+70
View File
@@ -0,0 +1,70 @@
package dns
import (
"context"
"fmt"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/command/mock_command"
"github.com/qdm12/golibs/logging/mock_logging"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Start(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
logger := mock_logging.NewMockLogger(mockCtrl)
logger.EXPECT().Info("starting unbound")
commander := mock_command.NewMockCommander(mockCtrl)
commander.EXPECT().Start(context.Background(), "unbound", "-d", "-c", string(constants.UnboundConf), "-vv").
Return(nil, nil, nil, nil)
c := &configurator{commander: commander, logger: logger}
stdout, waitFn, err := c.Start(context.Background(), 2)
assert.Nil(t, stdout)
assert.Nil(t, waitFn)
assert.NoError(t, err)
}
func Test_Version(t *testing.T) {
t.Parallel()
tests := map[string]struct {
runOutput string
runErr error
version string
err error
}{
"no data": {
err: fmt.Errorf(`unbound version was not found in ""`),
},
"2 lines with version": {
runOutput: "Version \nVersion 1.0-a hello\n",
version: "1.0-a",
},
"run error": {
runErr: fmt.Errorf("error"),
err: fmt.Errorf("unbound version: error"),
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
commander := mock_command.NewMockCommander(mockCtrl)
commander.EXPECT().Run(context.Background(), "unbound", "-V").
Return(tc.runOutput, tc.runErr)
c := &configurator{commander: commander}
version, err := c.Version(context.Background())
if tc.err != nil {
require.Error(t, err)
assert.Equal(t, tc.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, tc.version, version)
})
}
}
+325
View File
@@ -0,0 +1,325 @@
package dns
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"sort"
"strings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os"
)
func (c *configurator) MakeUnboundConf(ctx context.Context, settings settings.DNS,
username string, puid, pgid int) (err error) {
c.logger.Info("generating Unbound configuration")
lines, warnings := generateUnboundConf(ctx, settings, username, c.client, c.logger)
for _, warning := range warnings {
c.logger.Warn(warning)
}
const filepath = string(constants.UnboundConf)
file, err := c.openFile(filepath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0400)
if err != nil {
return err
}
_, err = file.WriteString(strings.Join(lines, "\n"))
if err != nil {
_ = file.Close()
return err
}
if err := file.Chown(puid, pgid); err != nil {
_ = file.Close()
return err
}
if err := file.Close(); err != nil {
return err
}
return nil
}
// MakeUnboundConf generates an Unbound configuration from the user provided settings.
func generateUnboundConf(ctx context.Context, settings settings.DNS, username string,
client *http.Client, logger logging.Logger) (
lines []string, warnings []error) {
doIPv6 := "no"
if settings.IPv6 {
doIPv6 = "yes"
}
serverSection := map[string]string{
// Logging
"verbosity": fmt.Sprintf("%d", settings.VerbosityLevel),
"val-log-level": fmt.Sprintf("%d", settings.ValidationLogLevel),
"use-syslog": "no",
// Performance
"num-threads": "1",
"prefetch": "yes",
"prefetch-key": "yes",
"key-cache-size": "16m",
"key-cache-slabs": "4",
"msg-cache-size": "4m",
"msg-cache-slabs": "4",
"rrset-cache-size": "4m",
"rrset-cache-slabs": "4",
"cache-min-ttl": "3600",
"cache-max-ttl": "9000",
// Privacy
"rrset-roundrobin": "yes",
"hide-identity": "yes",
"hide-version": "yes",
// Security
"tls-cert-bundle": fmt.Sprintf("%q", constants.CACertificates),
"root-hints": fmt.Sprintf("%q", constants.RootHints),
"trust-anchor-file": fmt.Sprintf("%q", constants.RootKey),
"harden-below-nxdomain": "yes",
"harden-referral-path": "yes",
"harden-algo-downgrade": "yes",
// Network
"do-ip4": "yes",
"do-ip6": doIPv6,
"interface": "0.0.0.0",
"port": "53",
// Other
"username": fmt.Sprintf("%q", username),
}
// Block lists
hostnamesLines, ipsLines, warnings := buildBlocked(ctx, client,
settings.BlockMalicious, settings.BlockAds, settings.BlockSurveillance,
settings.AllowedHostnames, settings.PrivateAddresses,
)
logger.Info("%d hostnames blocked overall", len(hostnamesLines))
logger.Info("%d IP addresses blocked overall", len(ipsLines))
sort.Slice(hostnamesLines, func(i, j int) bool { // for unit tests really
return hostnamesLines[i] < hostnamesLines[j]
})
sort.Slice(ipsLines, func(i, j int) bool { // for unit tests really
return ipsLines[i] < ipsLines[j]
})
// Server
lines = append(lines, "server:")
serverLines := make([]string, len(serverSection))
i := 0
for k, v := range serverSection {
serverLines[i] = " " + k + ": " + v
i++
}
sort.Slice(serverLines, func(i, j int) bool {
return serverLines[i] < serverLines[j]
})
lines = append(lines, serverLines...)
lines = append(lines, hostnamesLines...)
lines = append(lines, ipsLines...)
// Forward zone
lines = append(lines, "forward-zone:")
forwardZoneSection := map[string]string{
"name": "\".\"",
"forward-tls-upstream": "yes",
}
if settings.Caching {
forwardZoneSection["forward-no-cache"] = "no"
} else {
forwardZoneSection["forward-no-cache"] = "yes"
}
forwardZoneLines := make([]string, len(forwardZoneSection))
i = 0
for k, v := range forwardZoneSection {
forwardZoneLines[i] = " " + k + ": " + v
i++
}
sort.Slice(forwardZoneLines, func(i, j int) bool {
return forwardZoneLines[i] < forwardZoneLines[j]
})
for _, provider := range settings.Providers {
providerData := constants.DNSProviderMapping()[provider]
for _, IP := range providerData.IPs {
forwardZoneLines = append(forwardZoneLines,
fmt.Sprintf(" forward-addr: %s@853#%s", IP, providerData.Host))
}
}
lines = append(lines, forwardZoneLines...)
return lines, warnings
}
func buildBlocked(ctx context.Context, client *http.Client, blockMalicious, blockAds, blockSurveillance bool,
allowedHostnames, privateAddresses []string) (hostnamesLines, ipsLines []string, errs []error) {
chHostnames := make(chan []string)
chIPs := make(chan []string)
chErrors := make(chan []error)
go func() {
lines, errs := buildBlockedHostnames(ctx, client, blockMalicious, blockAds, blockSurveillance, allowedHostnames)
chHostnames <- lines
chErrors <- errs
}()
go func() {
lines, errs := buildBlockedIPs(ctx, client, blockMalicious, blockAds, blockSurveillance, privateAddresses)
chIPs <- lines
chErrors <- errs
}()
n := 2
for n > 0 {
select {
case lines := <-chHostnames:
hostnamesLines = append(hostnamesLines, lines...)
case lines := <-chIPs:
ipsLines = append(ipsLines, lines...)
case routineErrs := <-chErrors:
errs = append(errs, routineErrs...)
n--
}
}
return hostnamesLines, ipsLines, errs
}
func getList(ctx context.Context, client *http.Client, url string) (results []string, err error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, err
}
response, err := client.Do(req)
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%w from %s: %s", ErrBadStatusCode, url, response.Status)
}
content, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrCannotReadBody, err)
}
results = strings.Split(string(content), "\n")
// remove empty lines
last := len(results) - 1
for i := range results {
if len(results[i]) == 0 {
results[i] = results[last]
last--
}
}
results = results[:last+1]
if len(results) == 0 {
return nil, nil
}
return results, nil
}
func buildBlockedHostnames(ctx context.Context, client *http.Client, blockMalicious, blockAds, blockSurveillance bool,
allowedHostnames []string) (lines []string, errs []error) {
chResults := make(chan []string)
chError := make(chan error)
listsLeftToFetch := 0
if blockMalicious {
listsLeftToFetch++
go func() {
results, err := getList(ctx, client, string(constants.MaliciousBlockListHostnamesURL))
chResults <- results
chError <- err
}()
}
if blockAds {
listsLeftToFetch++
go func() {
results, err := getList(ctx, client, string(constants.AdsBlockListHostnamesURL))
chResults <- results
chError <- err
}()
}
if blockSurveillance {
listsLeftToFetch++
go func() {
results, err := getList(ctx, client, string(constants.SurveillanceBlockListHostnamesURL))
chResults <- results
chError <- err
}()
}
uniqueResults := make(map[string]struct{})
for listsLeftToFetch > 0 {
select {
case results := <-chResults:
for _, result := range results {
uniqueResults[result] = struct{}{}
}
case err := <-chError:
listsLeftToFetch--
if err != nil {
errs = append(errs, err)
}
}
}
for _, allowedHostname := range allowedHostnames {
delete(uniqueResults, allowedHostname)
}
for result := range uniqueResults {
lines = append(lines, " local-zone: \""+result+"\" static")
}
return lines, errs
}
func buildBlockedIPs(ctx context.Context, client *http.Client, blockMalicious, blockAds, blockSurveillance bool,
privateAddresses []string) (lines []string, errs []error) {
chResults := make(chan []string)
chError := make(chan error)
listsLeftToFetch := 0
if blockMalicious {
listsLeftToFetch++
go func() {
results, err := getList(ctx, client, string(constants.MaliciousBlockListIPsURL))
chResults <- results
chError <- err
}()
}
if blockAds {
listsLeftToFetch++
go func() {
results, err := getList(ctx, client, string(constants.AdsBlockListIPsURL))
chResults <- results
chError <- err
}()
}
if blockSurveillance {
listsLeftToFetch++
go func() {
results, err := getList(ctx, client, string(constants.SurveillanceBlockListIPsURL))
chResults <- results
chError <- err
}()
}
uniqueResults := make(map[string]struct{})
for listsLeftToFetch > 0 {
select {
case results := <-chResults:
for _, result := range results {
uniqueResults[result] = struct{}{}
}
case err := <-chError:
listsLeftToFetch--
if err != nil {
errs = append(errs, err)
}
}
}
for _, privateAddress := range privateAddresses {
uniqueResults[privateAddress] = struct{}{}
}
for result := range uniqueResults {
lines = append(lines, " private-address: "+result)
}
return lines, errs
}
+702
View File
@@ -0,0 +1,702 @@
package dns
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging/mock_logging"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_generateUnboundConf(t *testing.T) {
t.Parallel()
settings := settings.DNS{
Providers: []models.DNSProvider{constants.Cloudflare, constants.Quad9},
AllowedHostnames: []string{"a"},
PrivateAddresses: []string{"9.9.9.9"},
BlockMalicious: true,
BlockSurveillance: false,
BlockAds: false,
VerbosityLevel: 2,
ValidationLogLevel: 3,
Caching: true,
IPv6: true,
}
mockCtrl := gomock.NewController(t)
ctx := context.Background()
clientCalls := map[models.URL]int{
constants.MaliciousBlockListIPsURL: 0,
constants.MaliciousBlockListHostnamesURL: 0,
}
client := &http.Client{
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
url := models.URL(r.URL.String())
if _, ok := clientCalls[url]; !ok {
t.Errorf("unknown URL %q", url)
return nil, nil
}
clientCalls[url]++
var body string
switch url {
case constants.MaliciousBlockListIPsURL:
body = "c\nd"
case constants.MaliciousBlockListHostnamesURL:
body = "b\na\nc"
default:
t.Errorf("unknown URL %q", url)
return nil, nil
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(strings.NewReader(body)),
}, nil
}),
}
logger := mock_logging.NewMockLogger(mockCtrl)
logger.EXPECT().Info("%d hostnames blocked overall", 2)
logger.EXPECT().Info("%d IP addresses blocked overall", 3)
lines, warnings := generateUnboundConf(ctx, settings, "nonrootuser", client, logger)
require.Len(t, warnings, 0)
for url, count := range clientCalls {
assert.Equalf(t, 1, count, "for url %q", url)
}
const expected = `
server:
cache-max-ttl: 9000
cache-min-ttl: 3600
do-ip4: yes
do-ip6: yes
harden-algo-downgrade: yes
harden-below-nxdomain: yes
harden-referral-path: yes
hide-identity: yes
hide-version: yes
interface: 0.0.0.0
key-cache-size: 16m
key-cache-slabs: 4
msg-cache-size: 4m
msg-cache-slabs: 4
num-threads: 1
port: 53
prefetch-key: yes
prefetch: yes
root-hints: "/etc/unbound/root.hints"
rrset-cache-size: 4m
rrset-cache-slabs: 4
rrset-roundrobin: yes
tls-cert-bundle: "/etc/ssl/certs/ca-certificates.crt"
trust-anchor-file: "/etc/unbound/root.key"
use-syslog: no
username: "nonrootuser"
val-log-level: 3
verbosity: 2
local-zone: "b" static
local-zone: "c" static
private-address: 9.9.9.9
private-address: c
private-address: d
forward-zone:
forward-no-cache: no
forward-tls-upstream: yes
name: "."
forward-addr: 1.1.1.1@853#cloudflare-dns.com
forward-addr: 1.0.0.1@853#cloudflare-dns.com
forward-addr: 2606:4700:4700::1111@853#cloudflare-dns.com
forward-addr: 2606:4700:4700::1001@853#cloudflare-dns.com
forward-addr: 9.9.9.9@853#dns.quad9.net
forward-addr: 149.112.112.112@853#dns.quad9.net
forward-addr: 2620:fe::fe@853#dns.quad9.net
forward-addr: 2620:fe::9@853#dns.quad9.net`
assert.Equal(t, expected, "\n"+strings.Join(lines, "\n"))
}
func Test_buildBlocked(t *testing.T) {
t.Parallel()
type blockParams struct {
blocked bool
content []byte
clientErr error
}
tests := map[string]struct {
malicious blockParams
ads blockParams
surveillance blockParams
allowedHostnames []string
privateAddresses []string
hostnamesLines []string
ipsLines []string
errsString []string
}{
"none blocked": {},
"all blocked without lists": {
malicious: blockParams{
blocked: true,
},
ads: blockParams{
blocked: true,
},
surveillance: blockParams{
blocked: true,
},
},
"all blocked with lists": {
malicious: blockParams{
blocked: true,
content: []byte("malicious"),
},
ads: blockParams{
blocked: true,
content: []byte("ads"),
},
surveillance: blockParams{
blocked: true,
content: []byte("surveillance"),
},
hostnamesLines: []string{
" local-zone: \"ads\" static",
" local-zone: \"malicious\" static",
" local-zone: \"surveillance\" static"},
ipsLines: []string{
" private-address: ads",
" private-address: malicious",
" private-address: surveillance"},
},
"all blocked with allowed hostnames": {
malicious: blockParams{
blocked: true,
content: []byte("malicious"),
},
ads: blockParams{
blocked: true,
content: []byte("ads"),
},
surveillance: blockParams{
blocked: true,
content: []byte("surveillance"),
},
allowedHostnames: []string{"ads"},
hostnamesLines: []string{
" local-zone: \"malicious\" static",
" local-zone: \"surveillance\" static"},
ipsLines: []string{
" private-address: ads",
" private-address: malicious",
" private-address: surveillance"},
},
"all blocked with private addresses": {
malicious: blockParams{
blocked: true,
content: []byte("malicious"),
},
ads: blockParams{
blocked: true,
content: []byte("ads"),
},
surveillance: blockParams{
blocked: true,
content: []byte("surveillance"),
},
privateAddresses: []string{"ads", "192.100.1.5"},
hostnamesLines: []string{
" local-zone: \"ads\" static",
" local-zone: \"malicious\" static",
" local-zone: \"surveillance\" static"},
ipsLines: []string{
" private-address: 192.100.1.5",
" private-address: ads",
" private-address: malicious",
" private-address: surveillance"},
},
"all blocked with lists and one error": {
malicious: blockParams{
blocked: true,
content: []byte("malicious"),
},
ads: blockParams{
blocked: true,
content: []byte("ads"),
clientErr: fmt.Errorf("ads error"),
},
surveillance: blockParams{
blocked: true,
content: []byte("surveillance"),
},
hostnamesLines: []string{
" local-zone: \"malicious\" static",
" local-zone: \"surveillance\" static"},
ipsLines: []string{
" private-address: malicious",
" private-address: surveillance"},
errsString: []string{
`Get "https://raw.githubusercontent.com/qdm12/files/master/ads-ips.updated": ads error`,
`Get "https://raw.githubusercontent.com/qdm12/files/master/ads-hostnames.updated": ads error`,
},
},
"all blocked with errors": {
malicious: blockParams{
blocked: true,
clientErr: fmt.Errorf("malicious"),
},
ads: blockParams{
blocked: true,
clientErr: fmt.Errorf("ads"),
},
surveillance: blockParams{
blocked: true,
clientErr: fmt.Errorf("surveillance"),
},
errsString: []string{
`Get "https://raw.githubusercontent.com/qdm12/files/master/malicious-ips.updated": malicious`,
`Get "https://raw.githubusercontent.com/qdm12/files/master/malicious-hostnames.updated": malicious`,
`Get "https://raw.githubusercontent.com/qdm12/files/master/ads-ips.updated": ads`,
`Get "https://raw.githubusercontent.com/qdm12/files/master/ads-hostnames.updated": ads`,
`Get "https://raw.githubusercontent.com/qdm12/files/master/surveillance-ips.updated": surveillance`,
`Get "https://raw.githubusercontent.com/qdm12/files/master/surveillance-hostnames.updated": surveillance`,
},
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
ctx := context.Background()
clientCalls := map[models.URL]int{}
if tc.malicious.blocked {
clientCalls[constants.MaliciousBlockListIPsURL] = 0
clientCalls[constants.MaliciousBlockListHostnamesURL] = 0
}
if tc.ads.blocked {
clientCalls[constants.AdsBlockListIPsURL] = 0
clientCalls[constants.AdsBlockListHostnamesURL] = 0
}
if tc.surveillance.blocked {
clientCalls[constants.SurveillanceBlockListIPsURL] = 0
clientCalls[constants.SurveillanceBlockListHostnamesURL] = 0
}
client := &http.Client{
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
url := models.URL(r.URL.String())
if _, ok := clientCalls[url]; !ok {
t.Errorf("unknown URL %q", url)
return nil, nil
}
clientCalls[url]++
var body []byte
var err error
switch url {
case constants.MaliciousBlockListIPsURL, constants.MaliciousBlockListHostnamesURL:
body = tc.malicious.content
err = tc.malicious.clientErr
case constants.AdsBlockListIPsURL, constants.AdsBlockListHostnamesURL:
body = tc.ads.content
err = tc.ads.clientErr
case constants.SurveillanceBlockListIPsURL, constants.SurveillanceBlockListHostnamesURL:
body = tc.surveillance.content
err = tc.surveillance.clientErr
default: // just in case if the test is badly written
t.Errorf("unknown URL %q", url)
return nil, nil
}
if err != nil {
return nil, err
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader(body)),
}, nil
}),
}
hostnamesLines, ipsLines, errs := buildBlocked(ctx, client,
tc.malicious.blocked, tc.ads.blocked, tc.surveillance.blocked,
tc.allowedHostnames, tc.privateAddresses)
var errsString []string
for _, err := range errs {
errsString = append(errsString, err.Error())
}
assert.ElementsMatch(t, tc.errsString, errsString)
assert.ElementsMatch(t, tc.hostnamesLines, hostnamesLines)
assert.ElementsMatch(t, tc.ipsLines, ipsLines)
for url, count := range clientCalls {
assert.Equalf(t, 1, count, "for url %q", url)
}
})
}
}
func Test_getList(t *testing.T) {
t.Parallel()
tests := map[string]struct {
content []byte
status int
clientErr error
results []string
err error
}{
"no result": {
status: http.StatusOK,
},
"bad status": {
status: http.StatusInternalServerError,
err: fmt.Errorf("bad HTTP status from irrelevant_url: Internal Server Error"),
},
"network error": {
status: http.StatusOK,
clientErr: fmt.Errorf("error"),
err: fmt.Errorf(`Get "irrelevant_url": error`),
},
"results": {
content: []byte("a\nb\nc\n"),
status: http.StatusOK,
results: []string{"a", "b", "c"},
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
ctx := context.Background()
client := &http.Client{
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
assert.Equal(t, "irrelevant_url", r.URL.String())
if tc.clientErr != nil {
return nil, tc.clientErr
}
return &http.Response{
StatusCode: tc.status,
Status: http.StatusText(tc.status),
Body: ioutil.NopCloser(bytes.NewReader(tc.content)),
}, nil
}),
}
results, err := getList(ctx, client, "irrelevant_url")
if tc.err != nil {
require.Error(t, err)
assert.Equal(t, tc.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, tc.results, results)
})
}
}
func Test_buildBlockedHostnames(t *testing.T) {
t.Parallel()
type blockParams struct {
blocked bool
content []byte
clientErr error
}
tests := map[string]struct {
malicious blockParams
ads blockParams
surveillance blockParams
allowedHostnames []string
lines []string
errsString []string
}{
"nothing blocked": {},
"only malicious blocked": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
clientErr: nil,
},
lines: []string{
" local-zone: \"site_a\" static",
" local-zone: \"site_b\" static"},
},
"all blocked with some duplicates": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
},
ads: blockParams{
blocked: true,
content: []byte("site_a\nsite_c"),
},
surveillance: blockParams{
blocked: true,
content: []byte("site_c\nsite_a"),
},
lines: []string{
" local-zone: \"site_a\" static",
" local-zone: \"site_b\" static",
" local-zone: \"site_c\" static"},
},
"all blocked with one errored": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
},
ads: blockParams{
blocked: true,
content: []byte("site_a\nsite_c"),
},
surveillance: blockParams{
blocked: true,
clientErr: fmt.Errorf("surveillance error"),
},
lines: []string{
" local-zone: \"site_a\" static",
" local-zone: \"site_b\" static",
" local-zone: \"site_c\" static"},
errsString: []string{
`Get "https://raw.githubusercontent.com/qdm12/files/master/surveillance-hostnames.updated": surveillance error`,
},
},
"blocked with allowed hostnames": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
},
ads: blockParams{
blocked: true,
content: []byte("site_c\nsite_d"),
},
allowedHostnames: []string{"site_b", "site_c"},
lines: []string{
" local-zone: \"site_a\" static",
" local-zone: \"site_d\" static"},
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
ctx := context.Background()
clientCalls := map[models.URL]int{}
if tc.malicious.blocked {
clientCalls[constants.MaliciousBlockListHostnamesURL] = 0
}
if tc.ads.blocked {
clientCalls[constants.AdsBlockListHostnamesURL] = 0
}
if tc.surveillance.blocked {
clientCalls[constants.SurveillanceBlockListHostnamesURL] = 0
}
client := &http.Client{
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
url := models.URL(r.URL.String())
if _, ok := clientCalls[url]; !ok {
t.Errorf("unknown URL %q", url)
return nil, nil
}
clientCalls[url]++
var body []byte
var err error
switch url {
case constants.MaliciousBlockListHostnamesURL:
body = tc.malicious.content
err = tc.malicious.clientErr
case constants.AdsBlockListHostnamesURL:
body = tc.ads.content
err = tc.ads.clientErr
case constants.SurveillanceBlockListHostnamesURL:
body = tc.surveillance.content
err = tc.surveillance.clientErr
default: // just in case if the test is badly written
t.Errorf("unknown URL %q", url)
return nil, nil
}
if err != nil {
return nil, err
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader(body)),
}, nil
}),
}
lines, errs := buildBlockedHostnames(ctx, client,
tc.malicious.blocked, tc.ads.blocked,
tc.surveillance.blocked, tc.allowedHostnames)
var errsString []string
for _, err := range errs {
errsString = append(errsString, err.Error())
}
assert.ElementsMatch(t, tc.errsString, errsString)
assert.ElementsMatch(t, tc.lines, lines)
for url, count := range clientCalls {
assert.Equalf(t, 1, count, "for url %q", url)
}
})
}
}
func Test_buildBlockedIPs(t *testing.T) {
t.Parallel()
type blockParams struct {
blocked bool
content []byte
clientErr error
}
tests := map[string]struct {
malicious blockParams
ads blockParams
surveillance blockParams
privateAddresses []string
lines []string
errsString []string
}{
"nothing blocked": {},
"only malicious blocked": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
clientErr: nil,
},
lines: []string{
" private-address: site_a",
" private-address: site_b"},
},
"all blocked with some duplicates": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
},
ads: blockParams{
blocked: true,
content: []byte("site_a\nsite_c"),
},
surveillance: blockParams{
blocked: true,
content: []byte("site_c\nsite_a"),
},
lines: []string{
" private-address: site_a",
" private-address: site_b",
" private-address: site_c"},
},
"all blocked with one errored": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
},
ads: blockParams{
blocked: true,
content: []byte("site_a\nsite_c"),
},
surveillance: blockParams{
blocked: true,
clientErr: fmt.Errorf("surveillance error"),
},
lines: []string{
" private-address: site_a",
" private-address: site_b",
" private-address: site_c"},
errsString: []string{
`Get "https://raw.githubusercontent.com/qdm12/files/master/surveillance-ips.updated": surveillance error`,
},
},
"blocked with private addresses": {
malicious: blockParams{
blocked: true,
content: []byte("site_a\nsite_b"),
},
ads: blockParams{
blocked: true,
content: []byte("site_c"),
},
privateAddresses: []string{"site_c", "site_d"},
lines: []string{
" private-address: site_a",
" private-address: site_b",
" private-address: site_c",
" private-address: site_d"},
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
ctx := context.Background()
clientCalls := map[models.URL]int{}
if tc.malicious.blocked {
clientCalls[constants.MaliciousBlockListIPsURL] = 0
}
if tc.ads.blocked {
clientCalls[constants.AdsBlockListIPsURL] = 0
}
if tc.surveillance.blocked {
clientCalls[constants.SurveillanceBlockListIPsURL] = 0
}
client := &http.Client{
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
url := models.URL(r.URL.String())
if _, ok := clientCalls[url]; !ok {
t.Errorf("unknown URL %q", url)
return nil, nil
}
clientCalls[url]++
var body []byte
var err error
switch url {
case constants.MaliciousBlockListIPsURL:
body = tc.malicious.content
err = tc.malicious.clientErr
case constants.AdsBlockListIPsURL:
body = tc.ads.content
err = tc.ads.clientErr
case constants.SurveillanceBlockListIPsURL:
body = tc.surveillance.content
err = tc.surveillance.clientErr
default: // just in case if the test is badly written
t.Errorf("unknown URL %q", url)
return nil, nil
}
if err != nil {
return nil, err
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader(body)),
}, nil
}),
}
lines, errs := buildBlockedIPs(ctx, client,
tc.malicious.blocked, tc.ads.blocked,
tc.surveillance.blocked, tc.privateAddresses)
var errsString []string
for _, err := range errs {
errsString = append(errsString, err.Error())
}
assert.ElementsMatch(t, tc.errsString, errsString)
assert.ElementsMatch(t, tc.lines, lines)
for url, count := range clientCalls {
assert.Equalf(t, 1, count, "for url %q", url)
}
})
}
}
+43
View File
@@ -0,0 +1,43 @@
package dns
import (
"context"
"io"
"net"
"net/http"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/command"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os"
)
type Configurator interface {
DownloadRootHints(ctx context.Context, puid, pgid int) error
DownloadRootKey(ctx context.Context, puid, pgid int) error
MakeUnboundConf(ctx context.Context, settings settings.DNS, username string, puid, pgid int) (err error)
UseDNSInternally(IP net.IP)
UseDNSSystemWide(ip net.IP, keepNameserver bool) error
Start(ctx context.Context, logLevel uint8) (stdout io.ReadCloser, waitFn func() error, err error)
WaitForUnbound() (err error)
Version(ctx context.Context) (version string, err error)
}
type configurator struct {
logger logging.Logger
client *http.Client
openFile os.OpenFileFunc
commander command.Commander
lookupIP func(host string) ([]net.IP, error)
}
func NewConfigurator(logger logging.Logger, httpClient *http.Client,
openFile os.OpenFileFunc) Configurator {
return &configurator{
logger: logger.WithPrefix("dns configurator: "),
client: httpClient,
openFile: openFile,
commander: command.NewCommander(),
lookupIP: net.LookupIP,
}
}
+8
View File
@@ -0,0 +1,8 @@
package dns
import "errors"
var (
ErrBadStatusCode = errors.New("bad HTTP status")
ErrCannotReadBody = errors.New("cannot read response body")
)
+16 -32
View File
@@ -4,11 +4,9 @@ import (
"context" "context"
"errors" "errors"
"net" "net"
"net/http"
"sync" "sync"
"time" "time"
"github.com/qdm12/dns/pkg/unbound"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/settings" "github.com/qdm12/gluetun/internal/settings"
@@ -17,7 +15,7 @@ import (
) )
type Looper interface { type Looper interface {
Run(ctx context.Context, wg *sync.WaitGroup, dnsReadyCh chan<- struct{}) Run(ctx context.Context, wg *sync.WaitGroup, signalDNSReady func())
RunRestartTicker(ctx context.Context, wg *sync.WaitGroup) RunRestartTicker(ctx context.Context, wg *sync.WaitGroup)
GetStatus() (status models.LoopStatus) GetStatus() (status models.LoopStatus)
SetStatus(status models.LoopStatus) (outcome string, err error) SetStatus(status models.LoopStatus) (outcome string, err error)
@@ -27,8 +25,7 @@ type Looper interface {
type looper struct { type looper struct {
state state state state
conf unbound.Configurator conf Configurator
client *http.Client
logger logging.Logger logger logging.Logger
streamMerger command.StreamMerger streamMerger command.StreamMerger
username string username string
@@ -47,16 +44,14 @@ type looper struct {
const defaultBackoffTime = 10 * time.Second const defaultBackoffTime = 10 * time.Second
func NewLooper(conf unbound.Configurator, settings settings.DNS, client *http.Client, func NewLooper(conf Configurator, settings settings.DNS, logger logging.Logger,
logger logging.Logger, streamMerger command.StreamMerger, streamMerger command.StreamMerger, username string, puid, pgid int) Looper {
username string, puid, pgid int) Looper {
return &looper{ return &looper{
state: state{ state: state{
status: constants.Stopped, status: constants.Stopped,
settings: settings, settings: settings,
}, },
conf: conf, conf: conf,
client: client,
logger: logger.WithPrefix("dns over tls: "), logger: logger.WithPrefix("dns over tls: "),
username: username, username: username,
puid: puid, puid: puid,
@@ -87,12 +82,11 @@ func (l *looper) logAndWait(ctx context.Context, err error) {
} }
} }
func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup, dnsReadyCh chan<- struct{}) { func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup, signalDNSReady func()) {
defer wg.Done() defer wg.Done()
const fallback = false const fallback = false
l.useUnencryptedDNS(fallback) // TODO remove? Use default DNS by default for Docker resolution? l.useUnencryptedDNS(fallback) // TODO remove? Use default DNS by default for Docker resolution?
// TODO this one is kept if DNS_KEEP_NAMESERVER=on and should be replaced
select { select {
case <-l.start: case <-l.start:
@@ -132,7 +126,7 @@ func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup, dnsReadyCh chan<-
l.useUnencryptedDNS(fallback) l.useUnencryptedDNS(fallback)
} }
dnsReadyCh <- struct{}{} signalDNSReady()
stayHere := true stayHere := true
for stayHere { for stayHere {
@@ -183,7 +177,7 @@ func (l *looper) setupUnbound(ctx context.Context,
settings := l.GetSettings() settings := l.GetSettings()
unboundCtx, cancel := context.WithCancel(context.Background()) unboundCtx, cancel := context.WithCancel(context.Background())
stream, waitFn, err := l.conf.Start(unboundCtx, settings.Unbound.VerbosityDetailsLevel) stream, waitFn, err := l.conf.Start(unboundCtx, settings.VerbosityDetailsLevel)
if err != nil { if err != nil {
cancel() cancel()
if !previousCrashed { if !previousCrashed {
@@ -200,7 +194,7 @@ func (l *looper) setupUnbound(ctx context.Context,
l.logger.Error(err) l.logger.Error(err)
} }
if err := l.conf.WaitForUnbound(ctx); err != nil { if err := l.conf.WaitForUnbound(); err != nil {
if !previousCrashed { if !previousCrashed {
l.running <- constants.Crashed l.running <- constants.Crashed
} }
@@ -242,8 +236,8 @@ func (l *looper) useUnencryptedDNS(fallback bool) {
} }
// Try with any IPv4 address from the providers chosen // Try with any IPv4 address from the providers chosen
for _, provider := range settings.Unbound.Providers { for _, provider := range settings.Providers {
data, _ := unbound.GetProviderData(provider) data := constants.DNSProviderMapping()[provider]
for _, targetIP = range data.IPs { for _, targetIP = range data.IPs {
if targetIP.To4() != nil { if targetIP.To4() != nil {
if fallback { if fallback {
@@ -261,7 +255,7 @@ func (l *looper) useUnencryptedDNS(fallback bool) {
} }
// No IPv4 address found // No IPv4 address found
l.logger.Error("no ipv4 DNS address found for providers %s", settings.Unbound.Providers) l.logger.Error("no ipv4 DNS address found for providers %s", settings.Providers)
} }
func (l *looper) RunRestartTicker(ctx context.Context, wg *sync.WaitGroup) { func (l *looper) RunRestartTicker(ctx context.Context, wg *sync.WaitGroup) {
@@ -323,24 +317,14 @@ func (l *looper) RunRestartTicker(ctx context.Context, wg *sync.WaitGroup) {
} }
func (l *looper) updateFiles(ctx context.Context) (err error) { func (l *looper) updateFiles(ctx context.Context) (err error) {
l.logger.Info("downloading DNS over TLS cryptographic files") if err := l.conf.DownloadRootHints(ctx, l.puid, l.pgid); err != nil {
if err := l.conf.SetupFiles(ctx); err != nil { return err
}
if err := l.conf.DownloadRootKey(ctx, l.puid, l.pgid); err != nil {
return err return err
} }
settings := l.GetSettings() settings := l.GetSettings()
if err := l.conf.MakeUnboundConf(ctx, settings, l.username, l.puid, l.pgid); err != nil {
l.logger.Info("downloading hostnames and IP block lists")
hostnameLines, ipLines, errs := l.conf.BuildBlocked(ctx, l.client,
settings.BlockMalicious, settings.BlockAds, settings.BlockSurveillance,
settings.Unbound.BlockedHostnames, settings.Unbound.BlockedIPs,
settings.Unbound.AllowedHostnames)
for _, err := range errs {
l.logger.Warn(err)
}
if err := l.conf.MakeUnboundConf(
settings.Unbound, hostnameLines, ipLines,
l.username, l.puid, l.pgid); err != nil {
return err return err
} }
return nil return nil
+62
View File
@@ -0,0 +1,62 @@
package dns
import (
"context"
"io/ioutil"
"net"
"strings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/os"
)
// UseDNSInternally is to change the Go program DNS only.
func (c *configurator) UseDNSInternally(ip net.IP) {
c.logger.Info("using DNS address %s internally", ip.String())
net.DefaultResolver = &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
d := net.Dialer{}
return d.DialContext(ctx, "udp", net.JoinHostPort(ip.String(), "53"))
},
}
}
// UseDNSSystemWide changes the nameserver to use for DNS system wide.
func (c *configurator) UseDNSSystemWide(ip net.IP, keepNameserver bool) error {
c.logger.Info("using DNS address %s system wide", ip.String())
const filepath = string(constants.ResolvConf)
file, err := c.openFile(filepath, os.O_RDWR|os.O_TRUNC, 0644)
if err != nil {
return err
}
data, err := ioutil.ReadAll(file)
if err != nil {
_ = file.Close()
return err
}
s := strings.TrimSuffix(string(data), "\n")
lines := strings.Split(s, "\n")
if len(lines) == 1 && lines[0] == "" {
lines = nil
}
found := false
if !keepNameserver { // default
for i := range lines {
if strings.HasPrefix(lines[i], "nameserver ") {
lines[i] = "nameserver " + ip.String()
found = true
}
}
}
if !found {
lines = append(lines, "nameserver "+ip.String())
}
s = strings.Join(lines, "\n") + "\n"
_, err = file.WriteString(s)
if err != nil {
_ = file.Close()
return err
}
return file.Close()
}
+106
View File
@@ -0,0 +1,106 @@
package dns
import (
"fmt"
"io"
"net"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/logging/mock_logging"
"github.com/qdm12/golibs/os"
"github.com/qdm12/golibs/os/mock_os"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_UseDNSSystemWide(t *testing.T) {
t.Parallel()
tests := map[string]struct {
data []byte
writtenData string
openErr error
readErr error
writeErr error
closeErr error
err error
}{
"no data": {
writtenData: "nameserver 127.0.0.1\n",
},
"open error": {
openErr: fmt.Errorf("error"),
err: fmt.Errorf("error"),
},
"read error": {
readErr: fmt.Errorf("error"),
err: fmt.Errorf("error"),
},
"write error": {
writtenData: "nameserver 127.0.0.1\n",
writeErr: fmt.Errorf("error"),
err: fmt.Errorf("error"),
},
"lines without nameserver": {
data: []byte("abc\ndef\n"),
writtenData: "abc\ndef\nnameserver 127.0.0.1\n",
},
"lines with nameserver": {
data: []byte("abc\nnameserver abc def\ndef\n"),
writtenData: "abc\nnameserver 127.0.0.1\ndef\n",
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
file := mock_os.NewMockFile(mockCtrl)
if tc.openErr == nil {
firstReadCall := file.EXPECT().
Read(gomock.AssignableToTypeOf([]byte{})).
DoAndReturn(func(b []byte) (int, error) {
copy(b, tc.data)
return len(tc.data), nil
})
readErr := tc.readErr
if readErr == nil {
readErr = io.EOF
}
finalReadCall := file.EXPECT().
Read(gomock.AssignableToTypeOf([]byte{})).
Return(0, readErr).After(firstReadCall)
if tc.readErr == nil {
writeCall := file.EXPECT().WriteString(tc.writtenData).
Return(0, tc.writeErr).After(finalReadCall)
file.EXPECT().Close().Return(tc.closeErr).After(writeCall)
} else {
file.EXPECT().Close().Return(tc.closeErr).After(finalReadCall)
}
}
openFile := func(name string, flag int, perm os.FileMode) (os.File, error) {
assert.Equal(t, string(constants.ResolvConf), name)
assert.Equal(t, os.O_RDWR|os.O_TRUNC, flag)
assert.Equal(t, os.FileMode(0644), perm)
return file, tc.openErr
}
logger := mock_logging.NewMockLogger(mockCtrl)
logger.EXPECT().Info("using DNS address %s system wide", "127.0.0.1")
c := &configurator{
openFile: openFile,
logger: logger,
}
err := c.UseDNSSystemWide(net.IP{127, 0, 0, 1}, false)
if tc.err != nil {
require.Error(t, err)
assert.Equal(t, tc.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
})
}
}
+58
View File
@@ -0,0 +1,58 @@
package dns
import (
"context"
"fmt"
"io"
"net/http"
"os"
"github.com/qdm12/gluetun/internal/constants"
)
func (c *configurator) DownloadRootHints(ctx context.Context, puid, pgid int) error {
return c.downloadAndSave(ctx, "root hints",
string(constants.NamedRootURL), string(constants.RootHints), puid, pgid)
}
func (c *configurator) DownloadRootKey(ctx context.Context, puid, pgid int) error {
return c.downloadAndSave(ctx, "root key",
string(constants.RootKeyURL), string(constants.RootKey), puid, pgid)
}
func (c *configurator) downloadAndSave(ctx context.Context, logName, url, filepath string, puid, pgid int) error {
c.logger.Info("downloading %s from %s", logName, url)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return err
}
response, err := c.client.Do(req)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return fmt.Errorf("%w from %s: %s", ErrBadStatusCode, url, response.Status)
}
file, err := c.openFile(filepath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0400)
if err != nil {
return err
}
_, err = io.Copy(file, response.Body)
if err != nil {
_ = file.Close()
return err
}
err = file.Chown(puid, pgid)
if err != nil {
_ = file.Close()
return err
}
return file.Close()
}
+195
View File
@@ -0,0 +1,195 @@
package dns
import (
"bytes"
"context"
"errors"
"fmt"
"io/ioutil"
"net/http"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/logging/mock_logging"
"github.com/qdm12/golibs/os"
"github.com/qdm12/golibs/os/mock_os"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_downloadAndSave(t *testing.T) {
t.Parallel()
const defaultURL = "https://test.com"
tests := map[string]struct {
url string // to trigger a new request error
content []byte
status int
clientErr error
openErr error
writeErr error
chownErr error
closeErr error
err error
}{
"no data": {
url: defaultURL,
status: http.StatusOK,
},
"bad status": {
url: defaultURL,
status: http.StatusBadRequest,
err: fmt.Errorf("bad HTTP status from %s: Bad Request", defaultURL),
},
"client error": {
url: defaultURL,
clientErr: fmt.Errorf("error"),
err: fmt.Errorf("Get %q: error", defaultURL),
},
"open error": {
url: defaultURL,
status: http.StatusOK,
openErr: fmt.Errorf("error"),
err: fmt.Errorf("error"),
},
"chown error": {
url: defaultURL,
status: http.StatusOK,
chownErr: fmt.Errorf("error"),
err: fmt.Errorf("error"),
},
"close error": {
url: defaultURL,
status: http.StatusOK,
closeErr: fmt.Errorf("error"),
err: fmt.Errorf("error"),
},
"data": {
url: defaultURL,
content: []byte("content"),
status: http.StatusOK,
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
ctx := context.Background()
logger := mock_logging.NewMockLogger(mockCtrl)
logger.EXPECT().Info("downloading %s from %s", "root hints", tc.url)
client := &http.Client{
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
assert.Equal(t, tc.url, r.URL.String())
if tc.clientErr != nil {
return nil, tc.clientErr
}
return &http.Response{
StatusCode: tc.status,
Status: http.StatusText(tc.status),
Body: ioutil.NopCloser(bytes.NewReader(tc.content)),
}, nil
}),
}
openFile := func(name string, flag int, perm os.FileMode) (os.File, error) {
return nil, nil
}
const filepath = "/test"
if tc.clientErr == nil && tc.status == http.StatusOK {
file := mock_os.NewMockFile(mockCtrl)
if tc.openErr == nil {
if len(tc.content) > 0 {
file.EXPECT().
Write(tc.content).
Return(len(tc.content), tc.writeErr)
}
file.EXPECT().
Close().
Return(tc.closeErr)
file.EXPECT().
Chown(1000, 1000).
Return(tc.chownErr)
}
openFile = func(name string, flag int, perm os.FileMode) (os.File, error) {
assert.Equal(t, filepath, name)
assert.Equal(t, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, flag)
assert.Equal(t, os.FileMode(0400), perm)
return file, tc.openErr
}
}
c := &configurator{
logger: logger,
client: client,
openFile: openFile,
}
err := c.downloadAndSave(ctx, "root hints",
tc.url, filepath,
1000, 1000)
if tc.err != nil {
require.Error(t, err)
assert.Equal(t, tc.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
})
}
}
func Test_DownloadRootHints(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
ctx := context.Background()
logger := mock_logging.NewMockLogger(mockCtrl)
logger.EXPECT().Info("downloading %s from %s", "root hints", string(constants.NamedRootURL))
client := &http.Client{
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
assert.Equal(t, string(constants.NamedRootURL), r.URL.String())
return nil, errors.New("test")
}),
}
c := &configurator{
logger: logger,
client: client,
}
err := c.DownloadRootHints(ctx, 1000, 1000)
require.Error(t, err)
assert.Equal(t, `Get "https://raw.githubusercontent.com/qdm12/files/master/named.root.updated": test`, err.Error())
}
func Test_DownloadRootKey(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
ctx := context.Background()
logger := mock_logging.NewMockLogger(mockCtrl)
logger.EXPECT().Info("downloading %s from %s", "root key", string(constants.RootKeyURL))
client := &http.Client{
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
assert.Equal(t, string(constants.RootKeyURL), r.URL.String())
return nil, errors.New("test")
}),
}
c := &configurator{
logger: logger,
client: client,
}
err := c.DownloadRootKey(ctx, 1000, 1000)
require.Error(t, err)
assert.Equal(t, `Get "https://raw.githubusercontent.com/qdm12/files/master/root.key.updated": test`, err.Error())
}
+9
View File
@@ -0,0 +1,9 @@
package dns
import "net/http"
type roundTripFunc func(r *http.Request) (*http.Response, error)
func (s roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) {
return s(r)
}
+28
View File
@@ -0,0 +1,28 @@
package dns
import (
"fmt"
"time"
)
func (c *configurator) WaitForUnbound() (err error) {
const hostToResolve = "github.com"
waitDurations := [...]time.Duration{
300 * time.Millisecond,
100 * time.Millisecond,
300 * time.Millisecond,
500 * time.Millisecond,
time.Second,
2 * time.Second,
}
maxTries := len(waitDurations)
for i, waitDuration := range waitDurations {
time.Sleep(waitDuration)
_, err := c.lookupIP(hostToResolve)
if err == nil {
return nil
}
c.logger.Warn("could not resolve %s (try %d of %d): %s", hostToResolve, i+1, maxTries, err)
}
return fmt.Errorf("Unbound does not seem to be working after %d tries", maxTries)
}
-7
View File
@@ -12,15 +12,8 @@ import (
func (s *server) runHealthcheckLoop(ctx context.Context, wg *sync.WaitGroup) { func (s *server) runHealthcheckLoop(ctx context.Context, wg *sync.WaitGroup) {
defer wg.Done() defer wg.Done()
for { for {
previousErr := s.handler.getErr()
err := healthCheck(ctx, s.resolver) err := healthCheck(ctx, s.resolver)
s.handler.setErr(err) s.handler.setErr(err)
if previousErr != nil && err == nil {
s.logger.Info("passed")
}
if err != nil { // try again after 1 second if err != nil { // try again after 1 second
timer := time.NewTimer(time.Second) timer := time.NewTimer(time.Second)
select { select {
+1 -1
View File
@@ -13,7 +13,7 @@ import (
var regularExpressions = struct { //nolint:gochecknoglobals var regularExpressions = struct { //nolint:gochecknoglobals
unboundPrefix *regexp.Regexp unboundPrefix *regexp.Regexp
}{ }{
unboundPrefix: regexp.MustCompile(`unbound: \[[0-9]{10}\] unbound\[[0-9]+:[0|1]\] `), unboundPrefix: regexp.MustCompile(`unbound: \[[0-9]{10}\] unbound\[[0-9]+:0\] `),
} }
func PostProcessLine(s string) (filtered string, level logging.Level) { func PostProcessLine(s string) (filtered string, level logging.Level) {
+2
View File
@@ -8,6 +8,8 @@ import (
type ( type (
// VPNDevice is the device name used to tunnel using Openvpn. // VPNDevice is the device name used to tunnel using Openvpn.
VPNDevice string VPNDevice string
// DNSProvider is a DNS over TLS server provider name.
DNSProvider string
// DNSHost is the DNS host to use for TLS validation. // DNSHost is the DNS host to use for TLS validation.
DNSHost string DNSHost string
// URL is an HTTP(s) URL address. // URL is an HTTP(s) URL address.
+4 -5
View File
@@ -4,9 +4,8 @@ import "net"
// DNSProviderData contains information for a DNS provider. // DNSProviderData contains information for a DNS provider.
type DNSProviderData struct { type DNSProviderData struct {
IPs []net.IP IPs []net.IP
SupportsTLS bool SupportsTLS bool
SupportsIPv6 bool SupportsIPv6 bool
SupportsDNSSec bool Host DNSHost
Host DNSHost
} }
+9 -1
View File
@@ -119,7 +119,15 @@ func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
l.cancel() l.cancel()
return return
} }
lines := providerConf.BuildConf(connection, l.username, settings) lines := providerConf.BuildConf(
connection,
settings.Verbosity,
l.username,
settings.Root,
settings.Cipher,
settings.Auth,
settings.Provider.ExtraConfigOptions,
)
if err := writeOpenvpnConf(lines, l.openFile); err != nil { if err := writeOpenvpnConf(lines, l.openFile); err != nil {
l.logger.Error(err) l.logger.Error(err)
+8 -8
View File
@@ -11,23 +11,23 @@ import (
// GetCyberghostGroup obtains the server group for the Cyberghost server from the // GetCyberghostGroup obtains the server group for the Cyberghost server from the
// environment variable CYBERGHOST_GROUP. // environment variable CYBERGHOST_GROUP.
func (r *reader) GetCyberghostGroup() (group string, err error) { func (p *reader) GetCyberghostGroup() (group string, err error) {
s, err := r.env.Inside("CYBERGHOST_GROUP", s, err := p.envParams.GetValueIfInside("CYBERGHOST_GROUP",
constants.CyberghostGroupChoices(), libparams.Default("Premium UDP Europe")) constants.CyberghostGroupChoices(), libparams.Default("Premium UDP Europe"))
return s, err return s, err
} }
// GetCyberghostRegions obtains the country names for the Cyberghost servers from the // GetCyberghostRegions obtains the country names for the Cyberghost servers from the
// environment variable REGION. // environment variable REGION.
func (r *reader) GetCyberghostRegions() (regions []string, err error) { func (p *reader) GetCyberghostRegions() (regions []string, err error) {
return r.env.CSVInside("REGION", constants.CyberghostRegionChoices()) return p.envParams.GetCSVInPossibilities("REGION", constants.CyberghostRegionChoices())
} }
// GetCyberghostClientKey obtains the client key to use for openvpn // GetCyberghostClientKey obtains the client key to use for openvpn
// from the secret file /run/secrets/openvpn_clientkey or from the file // from the secret file /run/secrets/openvpn_clientkey or from the file
// /gluetun/client.key. // /gluetun/client.key.
func (r *reader) GetCyberghostClientKey() (clientKey string, err error) { func (p *reader) GetCyberghostClientKey() (clientKey string, err error) {
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTKEY", string(constants.ClientKey)) b, err := p.getFromFileOrSecretFile("OPENVPN_CLIENTKEY", string(constants.ClientKey))
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -50,8 +50,8 @@ func extractClientKey(b []byte) (key string, err error) {
// GetCyberghostClientCertificate obtains the client certificate to use for openvpn // GetCyberghostClientCertificate obtains the client certificate to use for openvpn
// from the secret file /run/secrets/openvpn_clientcrt or from the file // from the secret file /run/secrets/openvpn_clientcrt or from the file
// /gluetun/client.crt. // /gluetun/client.crt.
func (r *reader) GetCyberghostClientCertificate() (clientCertificate string, err error) { func (p *reader) GetCyberghostClientCertificate() (clientCertificate string, err error) {
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTCRT", string(constants.ClientCertificate)) b, err := p.getFromFileOrSecretFile("OPENVPN_CLIENTCRT", string(constants.ClientCertificate))
if err != nil { if err != nil {
return "", err return "", err
} }
+28 -24
View File
@@ -6,29 +6,33 @@ import (
"strings" "strings"
"time" "time"
dns "github.com/qdm12/dns/pkg/unbound" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
libparams "github.com/qdm12/golibs/params" libparams "github.com/qdm12/golibs/params"
) )
// GetDNSOverTLS obtains if the DNS over TLS should be enabled // GetDNSOverTLS obtains if the DNS over TLS should be enabled
// from the environment variable DOT. // from the environment variable DOT.
func (r *reader) GetDNSOverTLS() (DNSOverTLS bool, err error) { //nolint:gocritic func (r *reader) GetDNSOverTLS() (DNSOverTLS bool, err error) { //nolint:gocritic
return r.env.OnOff("DOT", libparams.Default("on")) return r.envParams.GetOnOff("DOT", libparams.Default("on"))
} }
// GetDNSOverTLSProviders obtains the DNS over TLS providers to use // GetDNSOverTLSProviders obtains the DNS over TLS providers to use
// from the environment variable DOT_PROVIDERS. // from the environment variable DOT_PROVIDERS.
func (r *reader) GetDNSOverTLSProviders() (providers []string, err error) { func (r *reader) GetDNSOverTLSProviders() (providers []models.DNSProvider, err error) {
s, err := r.env.Get("DOT_PROVIDERS", libparams.Default("cloudflare")) s, err := r.envParams.GetEnv("DOT_PROVIDERS", libparams.Default("cloudflare"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, provider := range strings.Split(s, ",") { for _, word := range strings.Split(s, ",") {
_, ok := dns.GetProviderData(provider) provider := models.DNSProvider(word)
if !ok { switch provider {
case constants.Cloudflare, constants.Google, constants.Quad9,
constants.Quadrant, constants.CleanBrowsing:
providers = append(providers, provider)
default:
return nil, fmt.Errorf("DNS over TLS provider %q is not valid", provider) return nil, fmt.Errorf("DNS over TLS provider %q is not valid", provider)
} }
providers = append(providers, provider)
} }
return providers, nil return providers, nil
} }
@@ -36,28 +40,28 @@ func (r *reader) GetDNSOverTLSProviders() (providers []string, err error) {
// GetDNSOverTLSVerbosity obtains the verbosity level to use for Unbound // GetDNSOverTLSVerbosity obtains the verbosity level to use for Unbound
// from the environment variable DOT_VERBOSITY. // from the environment variable DOT_VERBOSITY.
func (r *reader) GetDNSOverTLSVerbosity() (verbosityLevel uint8, err error) { func (r *reader) GetDNSOverTLSVerbosity() (verbosityLevel uint8, err error) {
n, err := r.env.IntRange("DOT_VERBOSITY", 0, 5, libparams.Default("1")) n, err := r.envParams.GetEnvIntRange("DOT_VERBOSITY", 0, 5, libparams.Default("1"))
return uint8(n), err return uint8(n), err
} }
// GetDNSOverTLSVerbosityDetails obtains the log level to use for Unbound // GetDNSOverTLSVerbosityDetails obtains the log level to use for Unbound
// from the environment variable DOT_VERBOSITY_DETAILS. // from the environment variable DOT_VERBOSITY_DETAILS.
func (r *reader) GetDNSOverTLSVerbosityDetails() (verbosityDetailsLevel uint8, err error) { func (r *reader) GetDNSOverTLSVerbosityDetails() (verbosityDetailsLevel uint8, err error) {
n, err := r.env.IntRange("DOT_VERBOSITY_DETAILS", 0, 4, libparams.Default("0")) n, err := r.envParams.GetEnvIntRange("DOT_VERBOSITY_DETAILS", 0, 4, libparams.Default("0"))
return uint8(n), err return uint8(n), err
} }
// GetDNSOverTLSValidationLogLevel obtains the log level to use for Unbound DOT validation // GetDNSOverTLSValidationLogLevel obtains the log level to use for Unbound DOT validation
// from the environment variable DOT_VALIDATION_LOGLEVEL. // from the environment variable DOT_VALIDATION_LOGLEVEL.
func (r *reader) GetDNSOverTLSValidationLogLevel() (validationLogLevel uint8, err error) { func (r *reader) GetDNSOverTLSValidationLogLevel() (validationLogLevel uint8, err error) {
n, err := r.env.IntRange("DOT_VALIDATION_LOGLEVEL", 0, 2, libparams.Default("0")) n, err := r.envParams.GetEnvIntRange("DOT_VALIDATION_LOGLEVEL", 0, 2, libparams.Default("0"))
return uint8(n), err return uint8(n), err
} }
// GetDNSMaliciousBlocking obtains if malicious hostnames/IPs should be blocked // GetDNSMaliciousBlocking obtains if malicious hostnames/IPs should be blocked
// from being resolved by Unbound, using the environment variable BLOCK_MALICIOUS. // from being resolved by Unbound, using the environment variable BLOCK_MALICIOUS.
func (r *reader) GetDNSMaliciousBlocking() (blocking bool, err error) { func (r *reader) GetDNSMaliciousBlocking() (blocking bool, err error) {
return r.env.OnOff("BLOCK_MALICIOUS", libparams.Default("on")) return r.envParams.GetOnOff("BLOCK_MALICIOUS", libparams.Default("on"))
} }
// GetDNSSurveillanceBlocking obtains if surveillance hostnames/IPs should be blocked // GetDNSSurveillanceBlocking obtains if surveillance hostnames/IPs should be blocked
@@ -65,26 +69,26 @@ func (r *reader) GetDNSMaliciousBlocking() (blocking bool, err error) {
// and BLOCK_NSA for retrocompatibility. // and BLOCK_NSA for retrocompatibility.
func (r *reader) GetDNSSurveillanceBlocking() (blocking bool, err error) { func (r *reader) GetDNSSurveillanceBlocking() (blocking bool, err error) {
// Retro-compatibility // Retro-compatibility
s, err := r.env.Get("BLOCK_NSA") s, err := r.envParams.GetEnv("BLOCK_NSA")
if err != nil { if err != nil {
return false, err return false, err
} else if len(s) != 0 { } else if len(s) != 0 {
r.logger.Warn("You are using the old environment variable BLOCK_NSA, please consider changing it to BLOCK_SURVEILLANCE") //nolint:lll r.logger.Warn("You are using the old environment variable BLOCK_NSA, please consider changing it to BLOCK_SURVEILLANCE") //nolint:lll
return r.env.OnOff("BLOCK_NSA", libparams.Compulsory()) return r.envParams.GetOnOff("BLOCK_NSA", libparams.Compulsory())
} }
return r.env.OnOff("BLOCK_SURVEILLANCE", libparams.Default("off")) return r.envParams.GetOnOff("BLOCK_SURVEILLANCE", libparams.Default("off"))
} }
// GetDNSAdsBlocking obtains if ads hostnames/IPs should be blocked // GetDNSAdsBlocking obtains if ads hostnames/IPs should be blocked
// from being resolved by Unbound, using the environment variable BLOCK_ADS. // from being resolved by Unbound, using the environment variable BLOCK_ADS.
func (r *reader) GetDNSAdsBlocking() (blocking bool, err error) { func (r *reader) GetDNSAdsBlocking() (blocking bool, err error) {
return r.env.OnOff("BLOCK_ADS", libparams.Default("off")) return r.envParams.GetOnOff("BLOCK_ADS", libparams.Default("off"))
} }
// GetDNSUnblockedHostnames obtains a list of hostnames to unblock from block lists // GetDNSUnblockedHostnames obtains a list of hostnames to unblock from block lists
// from the comma separated list for the environment variable UNBLOCK. // from the comma separated list for the environment variable UNBLOCK.
func (r *reader) GetDNSUnblockedHostnames() (hostnames []string, err error) { func (r *reader) GetDNSUnblockedHostnames() (hostnames []string, err error) {
s, err := r.env.Get("UNBLOCK") s, err := r.envParams.GetEnv("UNBLOCK")
if err != nil { if err != nil {
return nil, err return nil, err
} else if len(s) == 0 { } else if len(s) == 0 {
@@ -92,7 +96,7 @@ func (r *reader) GetDNSUnblockedHostnames() (hostnames []string, err error) {
} }
hostnames = strings.Split(s, ",") hostnames = strings.Split(s, ",")
for _, hostname := range hostnames { for _, hostname := range hostnames {
if !r.regex.MatchHostname(hostname) { if !r.verifier.MatchHostname(hostname) {
return nil, fmt.Errorf("hostname %q does not seem valid", hostname) return nil, fmt.Errorf("hostname %q does not seem valid", hostname)
} }
} }
@@ -102,13 +106,13 @@ func (r *reader) GetDNSUnblockedHostnames() (hostnames []string, err error) {
// GetDNSOverTLSCaching obtains if Unbound caching should be enable or not // GetDNSOverTLSCaching obtains if Unbound caching should be enable or not
// from the environment variable DOT_CACHING. // from the environment variable DOT_CACHING.
func (r *reader) GetDNSOverTLSCaching() (caching bool, err error) { func (r *reader) GetDNSOverTLSCaching() (caching bool, err error) {
return r.env.OnOff("DOT_CACHING", libparams.Default("on")) return r.envParams.GetOnOff("DOT_CACHING", libparams.Default("on"))
} }
// GetDNSOverTLSPrivateAddresses obtains if Unbound caching should be enable or not // GetDNSOverTLSPrivateAddresses obtains if Unbound caching should be enable or not
// from the environment variable DOT_PRIVATE_ADDRESS. // from the environment variable DOT_PRIVATE_ADDRESS.
func (r *reader) GetDNSOverTLSPrivateAddresses() (privateAddresses []string, err error) { func (r *reader) GetDNSOverTLSPrivateAddresses() (privateAddresses []string, err error) {
s, err := r.env.Get("DOT_PRIVATE_ADDRESS") s, err := r.envParams.GetEnv("DOT_PRIVATE_ADDRESS")
if err != nil { if err != nil {
return nil, err return nil, err
} else if len(s) == 0 { } else if len(s) == 0 {
@@ -128,13 +132,13 @@ func (r *reader) GetDNSOverTLSPrivateAddresses() (privateAddresses []string, err
// GetDNSOverTLSIPv6 obtains if Unbound should resolve ipv6 addresses using // GetDNSOverTLSIPv6 obtains if Unbound should resolve ipv6 addresses using
// ipv6 DNS over TLS from the environment variable DOT_IPV6. // ipv6 DNS over TLS from the environment variable DOT_IPV6.
func (r *reader) GetDNSOverTLSIPv6() (ipv6 bool, err error) { func (r *reader) GetDNSOverTLSIPv6() (ipv6 bool, err error) {
return r.env.OnOff("DOT_IPV6", libparams.Default("off")) return r.envParams.GetOnOff("DOT_IPV6", libparams.Default("off"))
} }
// GetDNSUpdatePeriod obtains the period to use to update the block lists and cryptographic files // GetDNSUpdatePeriod obtains the period to use to update the block lists and cryptographic files
// and restart Unbound from the environment variable DNS_UPDATE_PERIOD. // and restart Unbound from the environment variable DNS_UPDATE_PERIOD.
func (r *reader) GetDNSUpdatePeriod() (period time.Duration, err error) { func (r *reader) GetDNSUpdatePeriod() (period time.Duration, err error) {
s, err := r.env.Get("DNS_UPDATE_PERIOD", libparams.Default("24h")) s, err := r.envParams.GetEnv("DNS_UPDATE_PERIOD", libparams.Default("24h"))
if err != nil { if err != nil {
return period, err return period, err
} }
@@ -144,7 +148,7 @@ func (r *reader) GetDNSUpdatePeriod() (period time.Duration, err error) {
// GetDNSPlaintext obtains the plaintext DNS address to use if DNS over TLS is disabled // GetDNSPlaintext obtains the plaintext DNS address to use if DNS over TLS is disabled
// from the environment variable DNS_PLAINTEXT_ADDRESS. // from the environment variable DNS_PLAINTEXT_ADDRESS.
func (r *reader) GetDNSPlaintext() (ip net.IP, err error) { func (r *reader) GetDNSPlaintext() (ip net.IP, err error) {
s, err := r.env.Get("DNS_PLAINTEXT_ADDRESS", libparams.Default("1.1.1.1")) s, err := r.envParams.GetEnv("DNS_PLAINTEXT_ADDRESS", libparams.Default("1.1.1.1"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -158,5 +162,5 @@ func (r *reader) GetDNSPlaintext() (ip net.IP, err error) {
// GetDNSKeepNameserver obtains if the nameserver present in /etc/resolv.conf // GetDNSKeepNameserver obtains if the nameserver present in /etc/resolv.conf
// should be kept instead of overridden, from the environment variable DNS_KEEP_NAMESERVER. // should be kept instead of overridden, from the environment variable DNS_KEEP_NAMESERVER.
func (r *reader) GetDNSKeepNameserver() (on bool, err error) { func (r *reader) GetDNSKeepNameserver() (on bool, err error) {
return r.env.OnOff("DNS_KEEP_NAMESERVER", libparams.Default("off")) return r.envParams.GetOnOff("DNS_KEEP_NAMESERVER", libparams.Default("off"))
} }
+4 -4
View File
@@ -10,13 +10,13 @@ import (
// GetFirewall obtains if the firewall should be enabled from the environment variable FIREWALL. // GetFirewall obtains if the firewall should be enabled from the environment variable FIREWALL.
func (r *reader) GetFirewall() (enabled bool, err error) { func (r *reader) GetFirewall() (enabled bool, err error) {
return r.env.OnOff("FIREWALL", libparams.Default("on")) return r.envParams.GetOnOff("FIREWALL", libparams.Default("on"))
} }
// GetAllowedVPNInputPorts obtains a list of input ports to allow from the // GetAllowedVPNInputPorts obtains a list of input ports to allow from the
// VPN server side in the firewall, from the environment variable FIREWALL_VPN_INPUT_PORTS. // VPN server side in the firewall, from the environment variable FIREWALL_VPN_INPUT_PORTS.
func (r *reader) GetVPNInputPorts() (ports []uint16, err error) { func (r *reader) GetVPNInputPorts() (ports []uint16, err error) {
s, err := r.env.Get("FIREWALL_VPN_INPUT_PORTS", libparams.Default("")) s, err := r.envParams.GetEnv("FIREWALL_VPN_INPUT_PORTS", libparams.Default(""))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -40,7 +40,7 @@ func (r *reader) GetVPNInputPorts() (ports []uint16, err error) {
// GetInputPorts obtains a list of input ports to allow through the // GetInputPorts obtains a list of input ports to allow through the
// default interface in the firewall, from the environment variable FIREWALL_INPUT_PORTS. // default interface in the firewall, from the environment variable FIREWALL_INPUT_PORTS.
func (r *reader) GetInputPorts() (ports []uint16, err error) { func (r *reader) GetInputPorts() (ports []uint16, err error) {
s, err := r.env.Get("FIREWALL_INPUT_PORTS", libparams.Default("")) s, err := r.envParams.GetEnv("FIREWALL_INPUT_PORTS", libparams.Default(""))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -64,5 +64,5 @@ func (r *reader) GetInputPorts() (ports []uint16, err error) {
// GetFirewallDebug obtains if the firewall should run in debug verbose mode // GetFirewallDebug obtains if the firewall should run in debug verbose mode
// from the environment variable FIREWALL_DEBUG. // from the environment variable FIREWALL_DEBUG.
func (r *reader) GetFirewallDebug() (debug bool, err error) { func (r *reader) GetFirewallDebug() (debug bool, err error) {
return r.env.OnOff("FIREWALL_DEBUG", libparams.Default("off")) return r.envParams.GetOnOff("FIREWALL_DEBUG", libparams.Default("off"))
} }
+8 -8
View File
@@ -13,18 +13,18 @@ func (r *reader) GetHTTPProxy() (enabled bool, err error) {
[]string{"TINYPROXY", "PROXY"}, []string{"TINYPROXY", "PROXY"},
r.onRetroActive, r.onRetroActive,
) )
return r.env.OnOff("HTTPPROXY", retroKeysOption, libparams.Default("off")) return r.envParams.GetOnOff("HTTPPROXY", retroKeysOption, libparams.Default("off"))
} }
// GetHTTPProxyLog obtains the if http proxy requests should be logged from // GetHTTPProxyLog obtains the if http proxy requests should be logged from
// the environment variable HTTPPROXY_LOG, and using PROXY_LOG_LEVEL and // the environment variable HTTPPROXY_LOG, and using PROXY_LOG_LEVEL and
// TINYPROXY_LOG as retro-compatibility names. // TINYPROXY_LOG as retro-compatibility names.
func (r *reader) GetHTTPProxyLog() (log bool, err error) { func (r *reader) GetHTTPProxyLog() (log bool, err error) {
s, _ := r.env.Get("HTTPPROXY_LOG") s, _ := r.envParams.GetEnv("HTTPPROXY_LOG")
if len(s) == 0 { if len(s) == 0 {
s, _ = r.env.Get("PROXY_LOG_LEVEL") s, _ = r.envParams.GetEnv("PROXY_LOG_LEVEL")
if len(s) == 0 { if len(s) == 0 {
s, _ = r.env.Get("TINYPROXY_LOG") s, _ = r.envParams.GetEnv("TINYPROXY_LOG")
if len(s) == 0 { if len(s) == 0 {
return false, nil // default log disabled return false, nil // default log disabled
} }
@@ -36,17 +36,17 @@ func (r *reader) GetHTTPProxyLog() (log bool, err error) {
return false, nil return false, nil
} }
} }
return r.env.OnOff("HTTPPROXY_LOG", libparams.Default("off")) return r.envParams.GetOnOff("HTTPPROXY_LOG", libparams.Default("off"))
} }
// GetHTTPProxyPort obtains the HTTP proxy listening port from the environment variable // GetHTTPProxyPort obtains the HTTP proxy listening port from the environment variable
// HTTPPROXY_PORT, and using PROXY_PORT and TINYPROXY_PORT as retro-compatibility names. // HTTPPROXY_PORT, and using PROXY_PORT and TINYPROXY_PORT as retro-compatibility names.
func (r *reader) GetHTTPProxyPort() (port uint16, warning string, err error) { func (r *reader) GetHTTPProxyPort() (port uint16, err error) {
retroKeysOption := libparams.RetroKeys( retroKeysOption := libparams.RetroKeys(
[]string{"TINYPROXY_PORT", "PROXY_PORT"}, []string{"TINYPROXY_PORT", "PROXY_PORT"},
r.onRetroActive, r.onRetroActive,
) )
return r.env.ListeningPort("HTTPPROXY_PORT", retroKeysOption, libparams.Default("8888")) return r.envParams.GetPort("HTTPPROXY_PORT", retroKeysOption, libparams.Default("8888"))
} }
// GetHTTPProxyUser obtains the HTTP proxy server user. // GetHTTPProxyUser obtains the HTTP proxy server user.
@@ -76,5 +76,5 @@ func (r *reader) GetHTTPProxyPassword() (password string, err error) {
// GetHTTPProxyStealth obtains the HTTP proxy server stealth mode // GetHTTPProxyStealth obtains the HTTP proxy server stealth mode
// from the environment variable HTTPPROXY_STEALTH. // from the environment variable HTTPPROXY_STEALTH.
func (r *reader) GetHTTPProxyStealth() (stealth bool, err error) { func (r *reader) GetHTTPProxyStealth() (stealth bool, err error) {
return r.env.OnOff("HTTPPROXY_STEALTH", libparams.Default("off")) return r.envParams.GetOnOff("HTTPPROXY_STEALTH", libparams.Default("off"))
} }
+5 -5
View File
@@ -8,30 +8,30 @@ import (
// GetMullvadCountries obtains the countries for the Mullvad servers from the // GetMullvadCountries obtains the countries for the Mullvad servers from the
// environment variable COUNTRY. // environment variable COUNTRY.
func (r *reader) GetMullvadCountries() (countries []string, err error) { func (r *reader) GetMullvadCountries() (countries []string, err error) {
return r.env.CSVInside("COUNTRY", constants.MullvadCountryChoices()) return r.envParams.GetCSVInPossibilities("COUNTRY", constants.MullvadCountryChoices())
} }
// GetMullvadCity obtains the cities for the Mullvad servers from the // GetMullvadCity obtains the cities for the Mullvad servers from the
// environment variable CITY. // environment variable CITY.
func (r *reader) GetMullvadCities() (cities []string, err error) { func (r *reader) GetMullvadCities() (cities []string, err error) {
return r.env.CSVInside("CITY", constants.MullvadCityChoices()) return r.envParams.GetCSVInPossibilities("CITY", constants.MullvadCityChoices())
} }
// GetMullvadISPs obtains the ISPs for the Mullvad servers from the // GetMullvadISPs obtains the ISPs for the Mullvad servers from the
// environment variable ISP. // environment variable ISP.
func (r *reader) GetMullvadISPs() (isps []string, err error) { func (r *reader) GetMullvadISPs() (isps []string, err error) {
return r.env.CSVInside("ISP", constants.MullvadISPChoices()) return r.envParams.GetCSVInPossibilities("ISP", constants.MullvadISPChoices())
} }
// GetMullvadPort obtains the port to reach the Mullvad server on from the // GetMullvadPort obtains the port to reach the Mullvad server on from the
// environment variable PORT. // environment variable PORT.
func (r *reader) GetMullvadPort() (port uint16, err error) { func (r *reader) GetMullvadPort() (port uint16, err error) {
n, err := r.env.IntRange("PORT", 0, 65535, libparams.Default("0")) n, err := r.envParams.GetEnvIntRange("PORT", 0, 65535, libparams.Default("0"))
return uint16(n), err return uint16(n), err
} }
// GetMullvadOwned obtains if the server should be owned by Mullvad or not from the // GetMullvadOwned obtains if the server should be owned by Mullvad or not from the
// environment variable OWNED. // environment variable OWNED.
func (r *reader) GetMullvadOwned() (owned bool, err error) { func (r *reader) GetMullvadOwned() (owned bool, err error) {
return r.env.YesNo("OWNED", libparams.Default("no")) return r.envParams.GetYesNo("OWNED", libparams.Default("no"))
} }
+2 -2
View File
@@ -10,7 +10,7 @@ import (
// GetNordvpnRegions obtains the regions (countries) for the NordVPN server from the // GetNordvpnRegions obtains the regions (countries) for the NordVPN server from the
// environment variable REGION. // environment variable REGION.
func (r *reader) GetNordvpnRegions() (regions []string, err error) { func (r *reader) GetNordvpnRegions() (regions []string, err error) {
return r.env.CSVInside("REGION", constants.NordvpnRegionChoices()) return r.envParams.GetCSVInPossibilities("REGION", constants.NordvpnRegionChoices())
} }
// GetNordvpnRegion obtains the server numbers (optional) for the NordVPN servers from the // GetNordvpnRegion obtains the server numbers (optional) for the NordVPN servers from the
@@ -21,7 +21,7 @@ func (r *reader) GetNordvpnNumbers() (numbers []uint16, err error) {
possibilities[i] = fmt.Sprintf("%d", i) possibilities[i] = fmt.Sprintf("%d", i)
} }
possibilities[65536] = "" possibilities[65536] = ""
values, err := r.env.CSVInside("SERVER_NUMBER", possibilities) values, err := r.envParams.GetCSVInPossibilities("SERVER_NUMBER", possibilities)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+7 -15
View File
@@ -27,26 +27,26 @@ func (r *reader) GetPassword() (s string, err error) {
// GetNetworkProtocol obtains the network protocol to use to connect to the // GetNetworkProtocol obtains the network protocol to use to connect to the
// VPN servers from the environment variable PROTOCOL. // VPN servers from the environment variable PROTOCOL.
func (r *reader) GetNetworkProtocol() (protocol models.NetworkProtocol, err error) { func (r *reader) GetNetworkProtocol() (protocol models.NetworkProtocol, err error) {
s, err := r.env.Inside("PROTOCOL", []string{"tcp", "udp"}, libparams.Default("udp")) s, err := r.envParams.GetValueIfInside("PROTOCOL", []string{"tcp", "udp"}, libparams.Default("udp"))
return models.NetworkProtocol(s), err return models.NetworkProtocol(s), err
} }
// GetOpenVPNVerbosity obtains the verbosity level for verbosity between 0 and 6 // GetOpenVPNVerbosity obtains the verbosity level for verbosity between 0 and 6
// from the environment variable OPENVPN_VERBOSITY. // from the environment variable OPENVPN_VERBOSITY.
func (r *reader) GetOpenVPNVerbosity() (verbosity int, err error) { func (r *reader) GetOpenVPNVerbosity() (verbosity int, err error) {
return r.env.IntRange("OPENVPN_VERBOSITY", 0, 6, libparams.Default("1")) return r.envParams.GetEnvIntRange("OPENVPN_VERBOSITY", 0, 6, libparams.Default("1"))
} }
// GetOpenVPNRoot obtains if openvpn should be run as root // GetOpenVPNRoot obtains if openvpn should be run as root
// from the environment variable OPENVPN_ROOT. // from the environment variable OPENVPN_ROOT.
func (r *reader) GetOpenVPNRoot() (root bool, err error) { func (r *reader) GetOpenVPNRoot() (root bool, err error) {
return r.env.YesNo("OPENVPN_ROOT", libparams.Default("no")) return r.envParams.GetYesNo("OPENVPN_ROOT", libparams.Default("no"))
} }
// GetTargetIP obtains the IP address to override over the list of IP addresses filtered // GetTargetIP obtains the IP address to override over the list of IP addresses filtered
// from the environment variable OPENVPN_TARGET_IP. // from the environment variable OPENVPN_TARGET_IP.
func (r *reader) GetTargetIP() (ip net.IP, err error) { func (r *reader) GetTargetIP() (ip net.IP, err error) {
s, err := r.env.Get("OPENVPN_TARGET_IP") s, err := r.envParams.GetEnv("OPENVPN_TARGET_IP")
if len(s) == 0 { if len(s) == 0 {
return nil, nil return nil, nil
} else if err != nil { } else if err != nil {
@@ -62,25 +62,17 @@ func (r *reader) GetTargetIP() (ip net.IP, err error) {
// GetOpenVPNCipher obtains a custom cipher to use with OpenVPN // GetOpenVPNCipher obtains a custom cipher to use with OpenVPN
// from the environment variable OPENVPN_CIPHER. // from the environment variable OPENVPN_CIPHER.
func (r *reader) GetOpenVPNCipher() (cipher string, err error) { func (r *reader) GetOpenVPNCipher() (cipher string, err error) {
return r.env.Get("OPENVPN_CIPHER") return r.envParams.GetEnv("OPENVPN_CIPHER")
} }
// GetOpenVPNAuth obtains a custom auth algorithm to use with OpenVPN // GetOpenVPNAuth obtains a custom auth algorithm to use with OpenVPN
// from the environment variable OPENVPN_AUTH. // from the environment variable OPENVPN_AUTH.
func (r *reader) GetOpenVPNAuth() (auth string, err error) { func (r *reader) GetOpenVPNAuth() (auth string, err error) {
return r.env.Get("OPENVPN_AUTH") return r.envParams.GetEnv("OPENVPN_AUTH")
} }
// GetOpenVPNIPv6 obtains if ipv6 should be tunneled through the // GetOpenVPNIPv6 obtains if ipv6 should be tunneled through the
// openvpn tunnel from the environment variable OPENVPN_IPV6. // openvpn tunnel from the environment variable OPENVPN_IPV6.
func (r *reader) GetOpenVPNIPv6() (ipv6 bool, err error) { func (r *reader) GetOpenVPNIPv6() (ipv6 bool, err error) {
return r.env.OnOff("OPENVPN_IPV6", libparams.Default("off")) return r.envParams.GetOnOff("OPENVPN_IPV6", libparams.Default("off"))
}
func (r *reader) GetOpenVPNMSSFix() (mssFix uint16, err error) {
n, err := r.env.IntRange("OPENVPN_MSSFIX", 0, 10000, libparams.Default("0"))
if err != nil {
return 0, err
}
return uint16(n), nil
} }
+14 -15
View File
@@ -17,7 +17,7 @@ type Reader interface {
// DNS over TLS getters // DNS over TLS getters
GetDNSOverTLS() (DNSOverTLS bool, err error) GetDNSOverTLS() (DNSOverTLS bool, err error)
GetDNSOverTLSProviders() (providers []string, err error) GetDNSOverTLSProviders() (providers []models.DNSProvider, err error)
GetDNSOverTLSCaching() (caching bool, err error) GetDNSOverTLSCaching() (caching bool, err error)
GetDNSOverTLSVerbosity() (verbosityLevel uint8, err error) GetDNSOverTLSVerbosity() (verbosityLevel uint8, err error)
GetDNSOverTLSVerbosityDetails() (verbosityDetailsLevel uint8, err error) GetDNSOverTLSVerbosityDetails() (verbosityDetailsLevel uint8, err error)
@@ -55,7 +55,6 @@ type Reader interface {
GetOpenVPNCipher() (cipher string, err error) GetOpenVPNCipher() (cipher string, err error)
GetOpenVPNAuth() (auth string, err error) GetOpenVPNAuth() (auth string, err error)
GetOpenVPNIPv6() (tunnel bool, err error) GetOpenVPNIPv6() (tunnel bool, err error)
GetOpenVPNMSSFix() (mssFix uint16, err error)
// PIA getters // PIA getters
GetPortForwarding() (activated bool, err error) GetPortForwarding() (activated bool, err error)
@@ -103,14 +102,14 @@ type Reader interface {
// Shadowsocks getters // Shadowsocks getters
GetShadowSocks() (activated bool, err error) GetShadowSocks() (activated bool, err error)
GetShadowSocksLog() (activated bool, err error) GetShadowSocksLog() (activated bool, err error)
GetShadowSocksPort() (port uint16, warning string, err error) GetShadowSocksPort() (port uint16, err error)
GetShadowSocksPassword() (password string, err error) GetShadowSocksPassword() (password string, err error)
GetShadowSocksMethod() (method string, err error) GetShadowSocksMethod() (method string, err error)
// HTTP proxy getters // HTTP proxy getters
GetHTTPProxy() (activated bool, err error) GetHTTPProxy() (activated bool, err error)
GetHTTPProxyLog() (log bool, err error) GetHTTPProxyLog() (log bool, err error)
GetHTTPProxyPort() (port uint16, warning string, err error) GetHTTPProxyPort() (port uint16, err error)
GetHTTPProxyUser() (user string, err error) GetHTTPProxyUser() (user string, err error)
GetHTTPProxyPassword() (password string, err error) GetHTTPProxyPassword() (password string, err error)
GetHTTPProxyStealth() (stealth bool, err error) GetHTTPProxyStealth() (stealth bool, err error)
@@ -119,7 +118,7 @@ type Reader interface {
GetPublicIPPeriod() (period time.Duration, err error) GetPublicIPPeriod() (period time.Duration, err error)
// Control server // Control server
GetControlServerPort() (port uint16, warning string, err error) GetControlServerPort() (port uint16, err error)
GetControlServerLog() (enabled bool, err error) GetControlServerLog() (enabled bool, err error)
GetVersionInformation() (enabled bool, err error) GetVersionInformation() (enabled bool, err error)
@@ -128,26 +127,26 @@ type Reader interface {
} }
type reader struct { type reader struct {
env libparams.Env envParams libparams.EnvParams
logger logging.Logger logger logging.Logger
regex verification.Regex verifier verification.Verifier
os os.OS os os.OS
} }
// Newreader returns a paramsReadeer object to read parameters from // Newreader returns a paramsReadeer object to read parameters from
// environment variables. // environment variables.
func NewReader(logger logging.Logger, os os.OS) Reader { func NewReader(logger logging.Logger, os os.OS) Reader {
return &reader{ return &reader{
env: libparams.NewEnv(), envParams: libparams.NewEnvParams(),
logger: logger, logger: logger,
regex: verification.NewRegex(), verifier: verification.NewVerifier(),
os: os, os: os,
} }
} }
// GetVPNSP obtains the VPN service provider to use from the environment variable VPNSP. // GetVPNSP obtains the VPN service provider to use from the environment variable VPNSP.
func (r *reader) GetVPNSP() (vpnServiceProvider models.VPNProvider, err error) { func (r *reader) GetVPNSP() (vpnServiceProvider models.VPNProvider, err error) {
s, err := r.env.Inside( s, err := r.envParams.GetValueIfInside(
"VPNSP", "VPNSP",
[]string{ []string{
"pia", "private internet access", "pia", "private internet access",
@@ -161,7 +160,7 @@ func (r *reader) GetVPNSP() (vpnServiceProvider models.VPNProvider, err error) {
} }
func (r *reader) GetVersionInformation() (enabled bool, err error) { func (r *reader) GetVersionInformation() (enabled bool, err error) {
return r.env.OnOff("VERSION_INFORMATION", libparams.Default("on")) return r.envParams.GetOnOff("VERSION_INFORMATION", libparams.Default("on"))
} }
func (r *reader) onRetroActive(oldKey, newKey string) { func (r *reader) onRetroActive(oldKey, newKey string) {
+5 -5
View File
@@ -12,7 +12,7 @@ import (
// side is enabled or not from the environment variable PORT_FORWARDING // side is enabled or not from the environment variable PORT_FORWARDING
// Only valid for older PIA servers for now. // Only valid for older PIA servers for now.
func (r *reader) GetPortForwarding() (activated bool, err error) { func (r *reader) GetPortForwarding() (activated bool, err error) {
s, err := r.env.Get("PORT_FORWARDING", libparams.Default("off")) s, err := r.envParams.GetEnv("PORT_FORWARDING", libparams.Default("off"))
if err != nil { if err != nil {
return false, err return false, err
} }
@@ -28,7 +28,7 @@ func (r *reader) GetPortForwarding() (activated bool, err error) {
// GetPortForwardingStatusFilepath obtains the port forwarding status file path // GetPortForwardingStatusFilepath obtains the port forwarding status file path
// from the environment variable PORT_FORWARDING_STATUS_FILE. // from the environment variable PORT_FORWARDING_STATUS_FILE.
func (r *reader) GetPortForwardingStatusFilepath() (filepath models.Filepath, err error) { func (r *reader) GetPortForwardingStatusFilepath() (filepath models.Filepath, err error) {
filepathStr, err := r.env.Path( filepathStr, err := r.envParams.GetPath(
"PORT_FORWARDING_STATUS_FILE", "PORT_FORWARDING_STATUS_FILE",
libparams.Default("/tmp/gluetun/forwarded_port"), libparams.Default("/tmp/gluetun/forwarded_port"),
libparams.CaseSensitiveValue()) libparams.CaseSensitiveValue())
@@ -40,7 +40,7 @@ func (r *reader) GetPortForwardingStatusFilepath() (filepath models.Filepath, er
// retro compatibility. // retro compatibility.
func (r *reader) GetPIAEncryptionPreset() (preset string, err error) { func (r *reader) GetPIAEncryptionPreset() (preset string, err error) {
// Retro-compatibility // Retro-compatibility
s, err := r.env.Inside("ENCRYPTION", []string{ s, err := r.envParams.GetValueIfInside("ENCRYPTION", []string{
constants.PIAEncryptionPresetNormal, constants.PIAEncryptionPresetNormal,
constants.PIAEncryptionPresetStrong}) constants.PIAEncryptionPresetStrong})
if err != nil { if err != nil {
@@ -49,7 +49,7 @@ func (r *reader) GetPIAEncryptionPreset() (preset string, err error) {
r.logger.Warn("You are using the old environment variable ENCRYPTION, please consider changing it to PIA_ENCRYPTION") r.logger.Warn("You are using the old environment variable ENCRYPTION, please consider changing it to PIA_ENCRYPTION")
return s, nil return s, nil
} }
return r.env.Inside( return r.envParams.GetValueIfInside(
"PIA_ENCRYPTION", "PIA_ENCRYPTION",
[]string{ []string{
constants.PIAEncryptionPresetNormal, constants.PIAEncryptionPresetNormal,
@@ -61,5 +61,5 @@ func (r *reader) GetPIAEncryptionPreset() (preset string, err error) {
// GetPIARegions obtains the regions for the PIA servers from the // GetPIARegions obtains the regions for the PIA servers from the
// environment variable REGION. // environment variable REGION.
func (r *reader) GetPIARegions() (regions []string, err error) { func (r *reader) GetPIARegions() (regions []string, err error) {
return r.env.CSVInside("REGION", constants.PIAGeoChoices()) return r.envParams.GetCSVInPossibilities("REGION", constants.PIAGeoChoices())
} }
+1 -1
View File
@@ -8,7 +8,7 @@ import (
// GetPrivadoHostnames obtains the hostnames for the Privado server from the // GetPrivadoHostnames obtains the hostnames for the Privado server from the
// environment variable SERVER_HOSTNAME. // environment variable SERVER_HOSTNAME.
func (r *reader) GetPrivadoHostnames() (hosts []string, err error) { func (r *reader) GetPrivadoHostnames() (hosts []string, err error) {
return r.env.CSVInside("SERVER_HOSTNAME", return r.envParams.GetCSVInPossibilities("SERVER_HOSTNAME",
constants.PrivadoHostnameChoices(), constants.PrivadoHostnameChoices(),
libparams.RetroKeys([]string{"HOSTNAME"}, r.onRetroActive)) libparams.RetroKeys([]string{"HOSTNAME"}, r.onRetroActive))
} }
+2 -2
View File
@@ -10,7 +10,7 @@ import (
// GetPublicIPPeriod obtains the period to fetch the IP address periodically. // GetPublicIPPeriod obtains the period to fetch the IP address periodically.
// Set to 0 to disable. // Set to 0 to disable.
func (r *reader) GetPublicIPPeriod() (period time.Duration, err error) { func (r *reader) GetPublicIPPeriod() (period time.Duration, err error) {
s, err := r.env.Get("PUBLICIP_PERIOD", libparams.Default("12h")) s, err := r.envParams.GetEnv("PUBLICIP_PERIOD", libparams.Default("12h"))
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -21,7 +21,7 @@ func (r *reader) GetPublicIPPeriod() (period time.Duration, err error) {
// from the environment variable PUBLICIP_FILE with retro-compatible // from the environment variable PUBLICIP_FILE with retro-compatible
// environment variable IP_STATUS_FILE. // environment variable IP_STATUS_FILE.
func (r *reader) GetPublicIPFilepath() (filepath models.Filepath, err error) { func (r *reader) GetPublicIPFilepath() (filepath models.Filepath, err error) {
filepathStr, err := r.env.Path("PUBLICIP_FILE", filepathStr, err := r.envParams.GetPath("PUBLICIP_FILE",
libparams.RetroKeys([]string{"IP_STATUS_FILE"}, r.onRetroActive), libparams.RetroKeys([]string{"IP_STATUS_FILE"}, r.onRetroActive),
libparams.Default("/tmp/gluetun/ip"), libparams.CaseSensitiveValue()) libparams.Default("/tmp/gluetun/ip"), libparams.CaseSensitiveValue())
return models.Filepath(filepathStr), err return models.Filepath(filepathStr), err
+3 -3
View File
@@ -7,17 +7,17 @@ import (
// GetPurevpnRegions obtains the regions (continents) for the PureVPN servers from the // GetPurevpnRegions obtains the regions (continents) for the PureVPN servers from the
// environment variable REGION. // environment variable REGION.
func (r *reader) GetPurevpnRegions() (regions []string, err error) { func (r *reader) GetPurevpnRegions() (regions []string, err error) {
return r.env.CSVInside("REGION", constants.PurevpnRegionChoices()) return r.envParams.GetCSVInPossibilities("REGION", constants.PurevpnRegionChoices())
} }
// GetPurevpnCountries obtains the countries for the PureVPN servers from the // GetPurevpnCountries obtains the countries for the PureVPN servers from the
// environment variable COUNTRY. // environment variable COUNTRY.
func (r *reader) GetPurevpnCountries() (countries []string, err error) { func (r *reader) GetPurevpnCountries() (countries []string, err error) {
return r.env.CSVInside("COUNTRY", constants.PurevpnCountryChoices()) return r.envParams.GetCSVInPossibilities("COUNTRY", constants.PurevpnCountryChoices())
} }
// GetPurevpnCities obtains the cities for the PureVPN servers from the // GetPurevpnCities obtains the cities for the PureVPN servers from the
// environment variable CITY. // environment variable CITY.
func (r *reader) GetPurevpnCities() (cities []string, err error) { func (r *reader) GetPurevpnCities() (cities []string, err error) {
return r.env.CSVInside("CITY", constants.PurevpnCityChoices()) return r.envParams.GetCSVInPossibilities("CITY", constants.PurevpnCityChoices())
} }
+1 -1
View File
@@ -16,7 +16,7 @@ func (r *reader) GetOutboundSubnets() (outboundSubnets []net.IPNet, err error) {
[]string{"EXTRA_SUBNETS"}, []string{"EXTRA_SUBNETS"},
r.onRetroActive, r.onRetroActive,
) )
s, err := r.env.Get(key, retroOption) s, err := r.envParams.GetEnv(key, retroOption)
if err != nil { if err != nil {
return nil, err return nil, err
} else if s == "" { } else if s == "" {
+4 -4
View File
@@ -19,19 +19,19 @@ var (
) )
func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKeys []string) (value string, err error) { func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKeys []string) (value string, err error) {
envOptions := []libparams.OptionSetter{ envOptions := []libparams.GetEnvSetter{
libparams.Compulsory(), // to fallback on file reading libparams.Compulsory(), // to fallback on file reading
libparams.CaseSensitiveValue(), libparams.CaseSensitiveValue(),
libparams.Unset(), libparams.Unset(),
libparams.RetroKeys(retroKeys, r.onRetroActive), libparams.RetroKeys(retroKeys, r.onRetroActive),
} }
value, envErr := r.env.Get(envKey, envOptions...) value, envErr := r.envParams.GetEnv(envKey, envOptions...)
if envErr == nil { if envErr == nil {
return value, nil return value, nil
} }
defaultSecretFile := "/run/secrets/" + strings.ToLower(envKey) defaultSecretFile := "/run/secrets/" + strings.ToLower(envKey)
filepath, err := r.env.Get(envKey+"_SECRETFILE", filepath, err := r.envParams.GetEnv(envKey+"_SECRETFILE",
libparams.CaseSensitiveValue(), libparams.CaseSensitiveValue(),
libparams.Default(defaultSecretFile), libparams.Default(defaultSecretFile),
) )
@@ -67,7 +67,7 @@ func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKey
func (r *reader) getFromFileOrSecretFile(secretName, filepath string) ( func (r *reader) getFromFileOrSecretFile(secretName, filepath string) (
b []byte, err error) { b []byte, err error) {
defaultSecretFile := "/run/secrets/" + strings.ToLower(secretName) defaultSecretFile := "/run/secrets/" + strings.ToLower(secretName)
secretFilepath, err := r.env.Get(strings.ToUpper(secretName)+"_SECRETFILE", secretFilepath, err := r.envParams.GetEnv(strings.ToUpper(secretName)+"_SECRETFILE",
libparams.CaseSensitiveValue(), libparams.CaseSensitiveValue(),
libparams.Default(defaultSecretFile), libparams.Default(defaultSecretFile),
) )
+7 -3
View File
@@ -4,10 +4,14 @@ import (
libparams "github.com/qdm12/golibs/params" libparams "github.com/qdm12/golibs/params"
) )
func (r *reader) GetControlServerPort() (port uint16, warning string, err error) { func (r *reader) GetControlServerPort() (port uint16, err error) {
return r.env.ListeningPort("HTTP_CONTROL_SERVER_PORT", libparams.Default("8000")) n, err := r.envParams.GetEnvIntRange("HTTP_CONTROL_SERVER_PORT", 1, 65535, libparams.Default("8000"))
if err != nil {
return 0, err
}
return uint16(n), nil
} }
func (r *reader) GetControlServerLog() (enabled bool, err error) { func (r *reader) GetControlServerLog() (enabled bool, err error) {
return r.env.OnOff("HTTP_CONTROL_SERVER_LOG", libparams.Default("on")) return r.envParams.GetOnOff("HTTP_CONTROL_SERVER_LOG", libparams.Default("on"))
} }
+15 -5
View File
@@ -1,25 +1,35 @@
package params package params
import ( import (
"strconv"
libparams "github.com/qdm12/golibs/params" libparams "github.com/qdm12/golibs/params"
) )
// GetShadowSocks obtains if ShadowSocks is on from the environment variable // GetShadowSocks obtains if ShadowSocks is on from the environment variable
// SHADOWSOCKS. // SHADOWSOCKS.
func (r *reader) GetShadowSocks() (activated bool, err error) { func (r *reader) GetShadowSocks() (activated bool, err error) {
return r.env.OnOff("SHADOWSOCKS", libparams.Default("off")) return r.envParams.GetOnOff("SHADOWSOCKS", libparams.Default("off"))
} }
// GetShadowSocksLog obtains the ShadowSocks log level from the environment variable // GetShadowSocksLog obtains the ShadowSocks log level from the environment variable
// SHADOWSOCKS_LOG. // SHADOWSOCKS_LOG.
func (r *reader) GetShadowSocksLog() (activated bool, err error) { func (r *reader) GetShadowSocksLog() (activated bool, err error) {
return r.env.OnOff("SHADOWSOCKS_LOG", libparams.Default("off")) return r.envParams.GetOnOff("SHADOWSOCKS_LOG", libparams.Default("off"))
} }
// GetShadowSocksPort obtains the ShadowSocks listening port from the environment variable // GetShadowSocksPort obtains the ShadowSocks listening port from the environment variable
// SHADOWSOCKS_PORT. // SHADOWSOCKS_PORT.
func (r *reader) GetShadowSocksPort() (port uint16, warning string, err error) { func (r *reader) GetShadowSocksPort() (port uint16, err error) {
return r.env.ListeningPort("SHADOWSOCKS_PORT", libparams.Default("8388")) portStr, err := r.envParams.GetEnv("SHADOWSOCKS_PORT", libparams.Default("8388"))
if err != nil {
return 0, err
}
if err := r.verifier.VerifyPort(portStr); err != nil {
return 0, err
}
portUint64, err := strconv.ParseUint(portStr, 10, 16)
return uint16(portUint64), err
} }
// GetShadowSocksPassword obtains the ShadowSocks server password. // GetShadowSocksPassword obtains the ShadowSocks server password.
@@ -33,5 +43,5 @@ func (r *reader) GetShadowSocksPassword() (password string, err error) {
// GetShadowSocksMethod obtains the ShadowSocks method to use from the environment variable // GetShadowSocksMethod obtains the ShadowSocks method to use from the environment variable
// SHADOWSOCKS_METHOD. // SHADOWSOCKS_METHOD.
func (r *reader) GetShadowSocksMethod() (method string, err error) { func (r *reader) GetShadowSocksMethod() (method string, err error) {
return r.env.Get("SHADOWSOCKS_METHOD", libparams.Default("chacha20-ietf-poly1305")) return r.envParams.GetEnv("SHADOWSOCKS_METHOD", libparams.Default("chacha20-ietf-poly1305"))
} }
+1 -1
View File
@@ -7,5 +7,5 @@ import (
// GetSurfsharkRegions obtains the regions for the Surfshark servers from the // GetSurfsharkRegions obtains the regions for the Surfshark servers from the
// environment variable REGION. // environment variable REGION.
func (r *reader) GetSurfsharkRegions() (regions []string, err error) { func (r *reader) GetSurfsharkRegions() (regions []string, err error) {
return r.env.CSVInside("REGION", constants.SurfsharkRegionChoices()) return r.envParams.GetCSVInPossibilities("REGION", constants.SurfsharkRegionChoices())
} }
+3 -3
View File
@@ -7,7 +7,7 @@ import (
// GetPUID obtains the user ID to use from the environment variable PUID // GetPUID obtains the user ID to use from the environment variable PUID
// with retro compatible variable UID. // with retro compatible variable UID.
func (r *reader) GetPUID() (ppuid int, err error) { func (r *reader) GetPUID() (ppuid int, err error) {
return r.env.IntRange("PUID", 0, 65535, return r.envParams.GetEnvIntRange("PUID", 0, 65535,
libparams.Default("1000"), libparams.Default("1000"),
libparams.RetroKeys([]string{"UID"}, r.onRetroActive)) libparams.RetroKeys([]string{"UID"}, r.onRetroActive))
} }
@@ -15,12 +15,12 @@ func (r *reader) GetPUID() (ppuid int, err error) {
// GetGID obtains the group ID to use from the environment variable PGID // GetGID obtains the group ID to use from the environment variable PGID
// with retro compatible variable PGID. // with retro compatible variable PGID.
func (r *reader) GetPGID() (pgid int, err error) { func (r *reader) GetPGID() (pgid int, err error) {
return r.env.IntRange("PGID", 0, 65535, return r.envParams.GetEnvIntRange("PGID", 0, 65535,
libparams.Default("1000"), libparams.Default("1000"),
libparams.RetroKeys([]string{"GID"}, r.onRetroActive)) libparams.RetroKeys([]string{"GID"}, r.onRetroActive))
} }
// GetTZ obtains the timezone from the environment variable TZ. // GetTZ obtains the timezone from the environment variable TZ.
func (r *reader) GetTimezone() (timezone string, err error) { func (r *reader) GetTimezone() (timezone string, err error) {
return r.env.Get("TZ") return r.envParams.GetEnv("TZ")
} }
+1 -1
View File
@@ -9,7 +9,7 @@ import (
// GetUpdaterPeriod obtains the period to fetch the servers information when the tunnel is up. // GetUpdaterPeriod obtains the period to fetch the servers information when the tunnel is up.
// Set to 0 to disable. // Set to 0 to disable.
func (r *reader) GetUpdaterPeriod() (period time.Duration, err error) { func (r *reader) GetUpdaterPeriod() (period time.Duration, err error) {
s, err := r.env.Get("UPDATER_PERIOD", libparams.Default("0")) s, err := r.envParams.GetEnv("UPDATER_PERIOD", libparams.Default("0"))
if err != nil { if err != nil {
return 0, err return 0, err
} }
+1 -1
View File
@@ -7,5 +7,5 @@ import (
// GetVyprvpnRegions obtains the regions for the Vyprvpn servers from the // GetVyprvpnRegions obtains the regions for the Vyprvpn servers from the
// environment variable REGION. // environment variable REGION.
func (r *reader) GetVyprvpnRegions() (regions []string, err error) { func (r *reader) GetVyprvpnRegions() (regions []string, err error) {
return r.env.CSVInside("REGION", constants.VyprvpnRegionChoices()) return r.envParams.GetCSVInPossibilities("REGION", constants.VyprvpnRegionChoices())
} }
+4 -4
View File
@@ -11,19 +11,19 @@ import (
// GetWindscribeRegions obtains the regions for the Windscribe servers from the // GetWindscribeRegions obtains the regions for the Windscribe servers from the
// environment variable REGION. // environment variable REGION.
func (r *reader) GetWindscribeRegions() (regions []string, err error) { func (r *reader) GetWindscribeRegions() (regions []string, err error) {
return r.env.CSVInside("REGION", constants.WindscribeRegionChoices()) return r.envParams.GetCSVInPossibilities("REGION", constants.WindscribeRegionChoices())
} }
// GetWindscribeCities obtains the cities for the Windscribe servers from the // GetWindscribeCities obtains the cities for the Windscribe servers from the
// environment variable CITY. // environment variable CITY.
func (r *reader) GetWindscribeCities() (cities []string, err error) { func (r *reader) GetWindscribeCities() (cities []string, err error) {
return r.env.CSVInside("CITY", constants.WindscribeCityChoices()) return r.envParams.GetCSVInPossibilities("CITY", constants.WindscribeCityChoices())
} }
// GetWindscribeHostnames obtains the hostnames for the Windscribe servers from the // GetWindscribeHostnames obtains the hostnames for the Windscribe servers from the
// environment variable SERVER_HOSTNAME. // environment variable SERVER_HOSTNAME.
func (r *reader) GetWindscribeHostnames() (hostnames []string, err error) { func (r *reader) GetWindscribeHostnames() (hostnames []string, err error) {
return r.env.CSVInside("SERVER_HOSTNAME", return r.envParams.GetCSVInPossibilities("SERVER_HOSTNAME",
constants.WindscribeHostnameChoices(), constants.WindscribeHostnameChoices(),
libparams.RetroKeys([]string{"HOSTNAME"}, r.onRetroActive), libparams.RetroKeys([]string{"HOSTNAME"}, r.onRetroActive),
) )
@@ -33,7 +33,7 @@ func (r *reader) GetWindscribeHostnames() (hostnames []string, err error) {
// environment variable PORT. // environment variable PORT.
//nolint:gomnd //nolint:gomnd
func (r *reader) GetWindscribePort(protocol models.NetworkProtocol) (port uint16, err error) { func (r *reader) GetWindscribePort(protocol models.NetworkProtocol) (port uint16, err error) {
n, err := r.env.IntRange("PORT", 0, 65535, libparams.Default("0")) n, err := r.envParams.GetEnvIntRange("PORT", 0, 65535, libparams.Default("0"))
if err != nil { if err != nil {
return 0, err return 0, err
} }
+14 -22
View File
@@ -6,13 +6,11 @@ import (
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
"strconv"
"strings" "strings"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/firewall" "github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -64,13 +62,13 @@ func (c *cyberghost) GetOpenVPNConnection(selection models.ServerSelection) (
return pickRandomConnection(connections, c.randSource), nil return pickRandomConnection(connections, c.randSource), nil
} }
func (c *cyberghost) BuildConf(connection models.OpenVPNConnection, func (c *cyberghost) BuildConf(connection models.OpenVPNConnection, verbosity int,
username string, settings settings.OpenVPN) (lines []string) { username string, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(settings.Cipher) == 0 { if len(cipher) == 0 {
settings.Cipher = aes256cbc cipher = aes256cbc
} }
if len(settings.Auth) == 0 { if len(auth) == 0 {
settings.Auth = sha256 auth = sha256
} }
lines = []string{ lines = []string{
"client", "client",
@@ -79,14 +77,11 @@ func (c *cyberghost) BuildConf(connection models.OpenVPNConnection,
"persist-key", "persist-key",
"persist-tun", "persist-tun",
"remote-cert-tls server", "remote-cert-tls server",
"ping 10",
"ping-exit 60",
"ping-timer-rem",
"tls-exit",
// Cyberghost specific // Cyberghost specific
// "redirect-gateway def1", // "redirect-gateway def1",
"ncp-disable", "ncp-disable",
"ping 5",
"explicit-exit-notify 2", "explicit-exit-notify 2",
"script-security 2", "script-security 2",
"route-delay 5", "route-delay 5",
@@ -99,22 +94,19 @@ func (c *cyberghost) BuildConf(connection models.OpenVPNConnection,
"suppress-timestamps", "suppress-timestamps",
// Modified variables // Modified variables
fmt.Sprintf("verb %d", settings.Verbosity), fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf), fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", connection.Protocol), fmt.Sprintf("proto %s", connection.Protocol),
fmt.Sprintf("remote %s %d", connection.IP, connection.Port), fmt.Sprintf("remote %s %d", connection.IP, connection.Port),
fmt.Sprintf("cipher %s", settings.Cipher), fmt.Sprintf("cipher %s", cipher),
fmt.Sprintf("auth %s", settings.Auth), fmt.Sprintf("auth %s", auth),
} }
if strings.HasSuffix(settings.Cipher, "-gcm") { if strings.HasSuffix(cipher, "-gcm") {
lines = append(lines, "ncp-ciphers AES-256-GCM:AES-256-CBC:AES-128-GCM") lines = append(lines, "ncp-ciphers AES-256-GCM:AES-256-CBC:AES-128-GCM")
} }
if !settings.Root { if !root {
lines = append(lines, "user "+username) lines = append(lines, "user "+username)
} }
if settings.MSSFix > 0 {
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
lines = append(lines, []string{ lines = append(lines, []string{
"<ca>", "<ca>",
"-----BEGIN CERTIFICATE-----", "-----BEGIN CERTIFICATE-----",
@@ -125,14 +117,14 @@ func (c *cyberghost) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, []string{ lines = append(lines, []string{
"<cert>", "<cert>",
"-----BEGIN CERTIFICATE-----", "-----BEGIN CERTIFICATE-----",
settings.Provider.ExtraConfigOptions.ClientCertificate, extras.ClientCertificate,
"-----END CERTIFICATE-----", "-----END CERTIFICATE-----",
"</cert>", "</cert>",
}...) }...)
lines = append(lines, []string{ lines = append(lines, []string{
"<key>", "<key>",
"-----BEGIN PRIVATE KEY-----", "-----BEGIN PRIVATE KEY-----",
settings.Provider.ExtraConfigOptions.ClientKey, extras.ClientKey,
"-----END PRIVATE KEY-----", "-----END PRIVATE KEY-----",
"</key>", "</key>",
"", "",
+9 -16
View File
@@ -6,12 +6,10 @@ import (
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
"strconv"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/firewall" "github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -75,9 +73,9 @@ func (m *mullvad) GetOpenVPNConnection(selection models.ServerSelection) (
} }
func (m *mullvad) BuildConf(connection models.OpenVPNConnection, func (m *mullvad) BuildConf(connection models.OpenVPNConnection,
username string, settings settings.OpenVPN) (lines []string) { verbosity int, username string, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(settings.Cipher) == 0 { if len(cipher) == 0 {
settings.Cipher = aes256cbc cipher = aes256cbc
} }
lines = []string{ lines = []string{
"client", "client",
@@ -85,12 +83,10 @@ func (m *mullvad) BuildConf(connection models.OpenVPNConnection,
"nobind", "nobind",
"persist-key", "persist-key",
"remote-cert-tls server", "remote-cert-tls server",
"ping 10",
"ping-exit 60",
"ping-timer-rem",
"tls-exit",
// Mullvad specific // Mullvad specific
"ping 10",
"ping-restart 60",
"sndbuf 524288", "sndbuf 524288",
"rcvbuf 524288", "rcvbuf 524288",
"tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA", "tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA",
@@ -105,24 +101,21 @@ func (m *mullvad) BuildConf(connection models.OpenVPNConnection,
"suppress-timestamps", "suppress-timestamps",
// Modified variables // Modified variables
fmt.Sprintf("verb %d", settings.Verbosity), fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf), fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", connection.Protocol), fmt.Sprintf("proto %s", connection.Protocol),
fmt.Sprintf("remote %s %d", connection.IP, connection.Port), fmt.Sprintf("remote %s %d", connection.IP, connection.Port),
fmt.Sprintf("cipher %s", settings.Cipher), fmt.Sprintf("cipher %s", cipher),
} }
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 { if extras.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6") lines = append(lines, "tun-ipv6")
} else { } else {
lines = append(lines, `pull-filter ignore "route-ipv6"`) lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`) lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
} }
if !settings.Root { if !root {
lines = append(lines, "user "+username) lines = append(lines, "user "+username)
} }
if settings.MSSFix > 0 {
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
lines = append(lines, []string{ lines = append(lines, []string{
"<ca>", "<ca>",
"-----BEGIN CERTIFICATE-----", "-----BEGIN CERTIFICATE-----",
+14 -22
View File
@@ -6,12 +6,10 @@ import (
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
"strconv"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/firewall" "github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -80,18 +78,13 @@ func (n *nordvpn) GetOpenVPNConnection(selection models.ServerSelection) (
return pickRandomConnection(connections, n.randSource), nil return pickRandomConnection(connections, n.randSource), nil
} }
func (n *nordvpn) BuildConf(connection models.OpenVPNConnection, func (n *nordvpn) BuildConf(connection models.OpenVPNConnection, verbosity int, username string, root bool,
username string, settings settings.OpenVPN) (lines []string) { cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(settings.Cipher) == 0 { if len(cipher) == 0 {
settings.Cipher = aes256cbc cipher = aes256cbc
} }
if len(settings.Auth) == 0 { if len(auth) == 0 {
settings.Auth = "sha512" auth = "sha512"
}
const defaultMSSFix = 1450
if settings.MSSFix == 0 {
settings.MSSFix = defaultMSSFix
} }
lines = []string{ lines = []string{
"client", "client",
@@ -99,15 +92,14 @@ func (n *nordvpn) BuildConf(connection models.OpenVPNConnection,
"nobind", "nobind",
"persist-key", "persist-key",
"remote-cert-tls server", "remote-cert-tls server",
"ping 10",
"ping-exit 60",
"ping-timer-rem",
"tls-exit",
// Nordvpn specific // Nordvpn specific
"tun-mtu 1500", "tun-mtu 1500",
"tun-mtu-extra 32", "tun-mtu-extra 32",
"mssfix " + strconv.Itoa(int(settings.MSSFix)), "mssfix 1450",
"ping 15",
"ping-restart 0",
"ping-timer-rem",
"reneg-sec 0", "reneg-sec 0",
"comp-lzo no", "comp-lzo no",
"fast-io", "fast-io",
@@ -121,14 +113,14 @@ func (n *nordvpn) BuildConf(connection models.OpenVPNConnection,
"suppress-timestamps", "suppress-timestamps",
// Modified variables // Modified variables
fmt.Sprintf("verb %d", settings.Verbosity), fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf), fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", connection.Protocol), fmt.Sprintf("proto %s", connection.Protocol),
fmt.Sprintf("remote %s %d", connection.IP.String(), connection.Port), fmt.Sprintf("remote %s %d", connection.IP.String(), connection.Port),
fmt.Sprintf("cipher %s", settings.Cipher), fmt.Sprintf("cipher %s", cipher),
fmt.Sprintf("auth %s", settings.Auth), fmt.Sprintf("auth %s", auth),
} }
if !settings.Root { if !root {
lines = append(lines, "user "+username) lines = append(lines, "user "+username)
} }
lines = append(lines, []string{ lines = append(lines, []string{
+13 -21
View File
@@ -12,7 +12,6 @@ import (
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"strconv"
"strings" "strings"
"time" "time"
@@ -20,7 +19,6 @@ import (
"github.com/qdm12/gluetun/internal/firewall" "github.com/qdm12/gluetun/internal/firewall"
gluetunLog "github.com/qdm12/gluetun/internal/logging" gluetunLog "github.com/qdm12/gluetun/internal/logging"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -111,11 +109,11 @@ func (p *pia) GetOpenVPNConnection(selection models.ServerSelection) (
return connection, nil return connection, nil
} }
func (p *pia) BuildConf(connection models.OpenVPNConnection, func (p *pia) BuildConf(connection models.OpenVPNConnection, verbosity int, username string, root bool,
username string, settings settings.OpenVPN) (lines []string) { cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
var X509CRL, certificate string var X509CRL, certificate string
var defaultCipher, defaultAuth string var defaultCipher, defaultAuth string
if settings.Provider.ExtraConfigOptions.EncryptionPreset == constants.PIAEncryptionPresetNormal { if extras.EncryptionPreset == constants.PIAEncryptionPresetNormal {
defaultCipher = "aes-128-cbc" defaultCipher = "aes-128-cbc"
defaultAuth = "sha1" defaultAuth = "sha1"
X509CRL = constants.PiaX509CRLNormal X509CRL = constants.PiaX509CRLNormal
@@ -126,11 +124,11 @@ func (p *pia) BuildConf(connection models.OpenVPNConnection,
X509CRL = constants.PiaX509CRLStrong X509CRL = constants.PiaX509CRLStrong
certificate = constants.PIACertificateStrong certificate = constants.PIACertificateStrong
} }
if len(settings.Cipher) == 0 { if len(cipher) == 0 {
settings.Cipher = defaultCipher cipher = defaultCipher
} }
if len(settings.Auth) == 0 { if len(auth) == 0 {
settings.Auth = defaultAuth auth = defaultAuth
} }
lines = []string{ lines = []string{
"client", "client",
@@ -138,12 +136,9 @@ func (p *pia) BuildConf(connection models.OpenVPNConnection,
"nobind", "nobind",
"persist-key", "persist-key",
"remote-cert-tls server", "remote-cert-tls server",
"ping 10",
"ping-exit 60",
"ping-timer-rem",
"tls-exit",
// PIA specific // PIA specific
"ping 300", // Ping every 5 minutes to prevent a timeout error
"reneg-sec 0", "reneg-sec 0",
"compress", // allow PIA server to choose the compression to use "compress", // allow PIA server to choose the compression to use
@@ -155,22 +150,19 @@ func (p *pia) BuildConf(connection models.OpenVPNConnection,
"suppress-timestamps", "suppress-timestamps",
// Modified variables // Modified variables
fmt.Sprintf("verb %d", settings.Verbosity), fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf), fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", connection.Protocol), fmt.Sprintf("proto %s", connection.Protocol),
fmt.Sprintf("remote %s %d", connection.IP, connection.Port), fmt.Sprintf("remote %s %d", connection.IP, connection.Port),
fmt.Sprintf("cipher %s", settings.Cipher), fmt.Sprintf("cipher %s", cipher),
fmt.Sprintf("auth %s", settings.Auth), fmt.Sprintf("auth %s", auth),
} }
if strings.HasSuffix(settings.Cipher, "-gcm") { if strings.HasSuffix(cipher, "-gcm") {
lines = append(lines, "ncp-disable") lines = append(lines, "ncp-disable")
} }
if !settings.Root { if !root {
lines = append(lines, "user "+username) lines = append(lines, "user "+username)
} }
if settings.MSSFix > 0 {
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
lines = append(lines, []string{ lines = append(lines, []string{
"<crl-verify>", "<crl-verify>",
"-----BEGIN X509 CRL-----", "-----BEGIN X509 CRL-----",
+10 -19
View File
@@ -6,12 +6,10 @@ import (
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
"strconv"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/firewall" "github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -72,23 +70,19 @@ func (s *privado) GetOpenVPNConnection(selection models.ServerSelection) (
return pickRandomConnection(connections, s.randSource), nil return pickRandomConnection(connections, s.randSource), nil
} }
func (s *privado) BuildConf(connection models.OpenVPNConnection, func (s *privado) BuildConf(connection models.OpenVPNConnection, verbosity int, username string, root bool,
username string, settings settings.OpenVPN) (lines []string) { cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(settings.Cipher) == 0 { if len(cipher) == 0 {
settings.Cipher = aes256cbc cipher = aes256cbc
} }
if len(settings.Auth) == 0 { if len(auth) == 0 {
settings.Auth = sha256 auth = sha256
} }
lines = []string{ lines = []string{
"client", "client",
"dev tun", "dev tun",
"nobind", "nobind",
"persist-key", "persist-key",
"ping 10",
"ping-exit 60",
"ping-timer-rem",
"tls-exit",
// Privado specific // Privado specific
"tls-cipher TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-DSS-WITH-AES-256-CBC-SHA:TLS-RSA-WITH-AES-256-CBC-SHA", "tls-cipher TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-DSS-WITH-AES-256-CBC-SHA:TLS-RSA-WITH-AES-256-CBC-SHA",
@@ -102,19 +96,16 @@ func (s *privado) BuildConf(connection models.OpenVPNConnection,
"suppress-timestamps", "suppress-timestamps",
// Modified variables // Modified variables
fmt.Sprintf("verb %d", settings.Verbosity), fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf), fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", connection.Protocol), fmt.Sprintf("proto %s", connection.Protocol),
fmt.Sprintf("remote %s %d", connection.IP, connection.Port), fmt.Sprintf("remote %s %d", connection.IP, connection.Port),
fmt.Sprintf("cipher %s", settings.Cipher), fmt.Sprintf("cipher %s", cipher),
fmt.Sprintf("auth %s", settings.Auth), fmt.Sprintf("auth %s", auth),
} }
if !settings.Root { if !root {
lines = append(lines, "user "+username) lines = append(lines, "user "+username)
} }
if settings.MSSFix > 0 {
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
lines = append(lines, []string{ lines = append(lines, []string{
"<ca>", "<ca>",
"-----BEGIN CERTIFICATE-----", "-----BEGIN CERTIFICATE-----",
+2 -2
View File
@@ -8,7 +8,6 @@ import (
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/firewall" "github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -16,7 +15,8 @@ import (
// Provider contains methods to read and modify the openvpn configuration to connect as a client. // Provider contains methods to read and modify the openvpn configuration to connect as a client.
type Provider interface { type Provider interface {
GetOpenVPNConnection(selection models.ServerSelection) (connection models.OpenVPNConnection, err error) GetOpenVPNConnection(selection models.ServerSelection) (connection models.OpenVPNConnection, err error)
BuildConf(connection models.OpenVPNConnection, username string, settings settings.OpenVPN) (lines []string) BuildConf(connection models.OpenVPNConnection, verbosity int, username string,
root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string)
PortForward(ctx context.Context, client *http.Client, PortForward(ctx context.Context, client *http.Client,
openFile os.OpenFileFunc, pfLogger logging.Logger, gateway net.IP, fw firewall.Configurator, openFile os.OpenFileFunc, pfLogger logging.Logger, gateway net.IP, fw firewall.Configurator,
syncState func(port uint16) (pfFilepath models.Filepath)) syncState func(port uint16) (pfFilepath models.Filepath))
+9 -18
View File
@@ -6,12 +6,10 @@ import (
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
"strconv"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/firewall" "github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -74,10 +72,10 @@ func (p *purevpn) GetOpenVPNConnection(selection models.ServerSelection) (
return pickRandomConnection(connections, p.randSource), nil return pickRandomConnection(connections, p.randSource), nil
} }
func (p *purevpn) BuildConf(connection models.OpenVPNConnection, func (p *purevpn) BuildConf(connection models.OpenVPNConnection, verbosity int, username string, root bool,
username string, settings settings.OpenVPN) (lines []string) { cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(settings.Cipher) == 0 { if len(cipher) == 0 {
settings.Cipher = aes256cbc cipher = aes256cbc
} }
lines = []string{ lines = []string{
"client", "client",
@@ -85,10 +83,6 @@ func (p *purevpn) BuildConf(connection models.OpenVPNConnection,
"nobind", "nobind",
"persist-key", "persist-key",
"remote-cert-tls server", "remote-cert-tls server",
"ping 10",
"ping-exit 60",
"ping-timer-rem",
"tls-exit",
// Purevpn specific // Purevpn specific
"key-direction 1", "key-direction 1",
@@ -107,18 +101,15 @@ func (p *purevpn) BuildConf(connection models.OpenVPNConnection,
"suppress-timestamps", "suppress-timestamps",
// Modified variables // Modified variables
fmt.Sprintf("verb %d", settings.Verbosity), fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf), fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", connection.Protocol), fmt.Sprintf("proto %s", connection.Protocol),
fmt.Sprintf("remote %s %d", connection.IP.String(), connection.Port), fmt.Sprintf("remote %s %d", connection.IP.String(), connection.Port),
fmt.Sprintf("cipher %s", settings.Cipher), fmt.Sprintf("cipher %s", cipher),
} }
if !settings.Root { if !root {
lines = append(lines, "user "+username) lines = append(lines, "user "+username)
} }
if settings.MSSFix > 0 {
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
lines = append(lines, []string{ lines = append(lines, []string{
"<ca>", "<ca>",
"-----BEGIN CERTIFICATE-----", "-----BEGIN CERTIFICATE-----",
@@ -149,8 +140,8 @@ func (p *purevpn) BuildConf(connection models.OpenVPNConnection,
"</tls-auth>", "</tls-auth>",
"", "",
}...) }...)
if len(settings.Auth) > 0 { if len(auth) > 0 {
lines = append(lines, "auth "+settings.Auth) lines = append(lines, "auth "+auth)
} }
if connection.Protocol == constants.UDP { if connection.Protocol == constants.UDP {
lines = append(lines, "explicit-exit-notify") lines = append(lines, "explicit-exit-notify")
+14 -23
View File
@@ -6,12 +6,10 @@ import (
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
"strconv"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/firewall" "github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -75,35 +73,28 @@ func (s *surfshark) GetOpenVPNConnection(selection models.ServerSelection) (
return pickRandomConnection(connections, s.randSource), nil return pickRandomConnection(connections, s.randSource), nil
} }
func (s *surfshark) BuildConf(connection models.OpenVPNConnection, func (s *surfshark) BuildConf(connection models.OpenVPNConnection, verbosity int, username string, root bool,
username string, settings settings.OpenVPN) (lines []string) { cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(settings.Cipher) == 0 { if len(cipher) == 0 {
settings.Cipher = aes256cbc cipher = aes256cbc
} }
if len(settings.Auth) == 0 { if len(auth) == 0 {
settings.Auth = "SHA512" auth = "SHA512"
} }
const defaultMSSFix = 1450
if settings.MSSFix == 0 {
settings.MSSFix = defaultMSSFix
}
lines = []string{ lines = []string{
"client", "client",
"dev tun", "dev tun",
"nobind", "nobind",
"persist-key", "persist-key",
"remote-cert-tls server", "remote-cert-tls server",
"ping 10",
"ping-exit 60",
"ping-timer-rem",
"tls-exit",
// Surfshark specific // Surfshark specific
"tun-mtu 1500", "tun-mtu 1500",
"tun-mtu-extra 32", "tun-mtu-extra 32",
"mssfix " + strconv.Itoa(int(settings.MSSFix)), "mssfix 1450",
"ping 15",
"ping-restart 60",
"ping-timer-rem",
"reneg-sec 0", "reneg-sec 0",
"fast-io", "fast-io",
"key-direction 1", "key-direction 1",
@@ -118,14 +109,14 @@ func (s *surfshark) BuildConf(connection models.OpenVPNConnection,
"suppress-timestamps", "suppress-timestamps",
// Modified variables // Modified variables
fmt.Sprintf("verb %d", settings.Verbosity), fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf), fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", connection.Protocol), fmt.Sprintf("proto %s", connection.Protocol),
fmt.Sprintf("remote %s %d", connection.IP, connection.Port), fmt.Sprintf("remote %s %d", connection.IP, connection.Port),
fmt.Sprintf("cipher %s", settings.Cipher), fmt.Sprintf("cipher %s", cipher),
fmt.Sprintf("auth %s", settings.Auth), fmt.Sprintf("auth %s", auth),
} }
if !settings.Root { if !root {
lines = append(lines, "user "+username) lines = append(lines, "user "+username)
} }
lines = append(lines, []string{ lines = append(lines, []string{
-54
View File
@@ -1,54 +0,0 @@
package provider
import (
"math/rand"
"testing"
"github.com/qdm12/gluetun/internal/models"
"github.com/stretchr/testify/assert"
)
func Test_pickRandomConnection(t *testing.T) {
t.Parallel()
connections := []models.OpenVPNConnection{
{Port: 1}, {Port: 2}, {Port: 3}, {Port: 4},
}
source := rand.NewSource(0)
connection := pickRandomConnection(connections, source)
assert.Equal(t, models.OpenVPNConnection{Port: 3}, connection)
connection = pickRandomConnection(connections, source)
assert.Equal(t, models.OpenVPNConnection{Port: 3}, connection)
connection = pickRandomConnection(connections, source)
assert.Equal(t, models.OpenVPNConnection{Port: 2}, connection)
}
func Test_filterByPossibilities(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
value string
possibilities []string
filtered bool
}{
"no possibilities": {},
"value not in possibilities": {
value: "c",
possibilities: []string{"a", "b"},
filtered: true,
},
"value in possibilities": {
value: "c",
possibilities: []string{"a", "b", "c"},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
filtered := filterByPossibilities(testCase.value, testCase.possibilities)
assert.Equal(t, testCase.filtered, filtered)
})
}
}
+11 -19
View File
@@ -6,12 +6,10 @@ import (
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
"strconv"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/firewall" "github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -71,13 +69,13 @@ func (v *vyprvpn) GetOpenVPNConnection(selection models.ServerSelection) (
return pickRandomConnection(connections, v.randSource), nil return pickRandomConnection(connections, v.randSource), nil
} }
func (v *vyprvpn) BuildConf(connection models.OpenVPNConnection, func (v *vyprvpn) BuildConf(connection models.OpenVPNConnection, verbosity int, username string,
username string, settings settings.OpenVPN) (lines []string) { root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(settings.Cipher) == 0 { if len(cipher) == 0 {
settings.Cipher = aes256cbc cipher = aes256cbc
} }
if len(settings.Auth) == 0 { if len(auth) == 0 {
settings.Auth = "SHA256" auth = "SHA256"
} }
lines = []string{ lines = []string{
"client", "client",
@@ -85,13 +83,10 @@ func (v *vyprvpn) BuildConf(connection models.OpenVPNConnection,
"nobind", "nobind",
"persist-key", "persist-key",
"remote-cert-tls server", "remote-cert-tls server",
"ping 10",
"ping-exit 60",
"ping-timer-rem",
"tls-exit",
// Vyprvpn specific // Vyprvpn specific
"comp-lzo", "comp-lzo",
"keepalive 10 60",
// "verify-x509-name lu1.vyprvpn.com name", // "verify-x509-name lu1.vyprvpn.com name",
"tls-cipher TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA", //nolint:lll "tls-cipher TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA", //nolint:lll
@@ -103,19 +98,16 @@ func (v *vyprvpn) BuildConf(connection models.OpenVPNConnection,
"suppress-timestamps", "suppress-timestamps",
// Modified variables // Modified variables
fmt.Sprintf("verb %d", settings.Verbosity), fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf), fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", connection.Protocol), fmt.Sprintf("proto %s", connection.Protocol),
fmt.Sprintf("remote %s %d", connection.IP, connection.Port), fmt.Sprintf("remote %s %d", connection.IP, connection.Port),
fmt.Sprintf("cipher %s", settings.Cipher), fmt.Sprintf("cipher %s", cipher),
fmt.Sprintf("auth %s", settings.Auth), fmt.Sprintf("auth %s", auth),
} }
if !settings.Root { if !root {
lines = append(lines, "user "+username) lines = append(lines, "user "+username)
} }
if settings.MSSFix > 0 {
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
lines = append(lines, []string{ lines = append(lines, []string{
"<ca>", "<ca>",
"-----BEGIN CERTIFICATE-----", "-----BEGIN CERTIFICATE-----",
+11 -20
View File
@@ -6,13 +6,11 @@ import (
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
"strconv"
"strings" "strings"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/firewall" "github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -74,13 +72,13 @@ func (w *windscribe) GetOpenVPNConnection(selection models.ServerSelection) (con
return pickRandomConnection(connections, w.randSource), nil return pickRandomConnection(connections, w.randSource), nil
} }
func (w *windscribe) BuildConf(connection models.OpenVPNConnection, func (w *windscribe) BuildConf(connection models.OpenVPNConnection, verbosity int, username string,
username string, settings settings.OpenVPN) (lines []string) { root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(settings.Cipher) == 0 { if len(cipher) == 0 {
settings.Cipher = aes256cbc cipher = aes256cbc
} }
if len(settings.Auth) == 0 { if len(auth) == 0 {
settings.Auth = "sha512" auth = "sha512"
} }
lines = []string{ lines = []string{
"client", "client",
@@ -88,10 +86,6 @@ func (w *windscribe) BuildConf(connection models.OpenVPNConnection,
"nobind", "nobind",
"persist-key", "persist-key",
"remote-cert-tls server", "remote-cert-tls server",
"ping 10",
"ping-exit 60",
"ping-timer-rem",
"tls-exit",
// Windscribe specific // Windscribe specific
"comp-lzo", "comp-lzo",
@@ -106,22 +100,19 @@ func (w *windscribe) BuildConf(connection models.OpenVPNConnection,
"suppress-timestamps", "suppress-timestamps",
// Modified variables // Modified variables
fmt.Sprintf("verb %d", settings.Verbosity), fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf), fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", connection.Protocol), fmt.Sprintf("proto %s", connection.Protocol),
fmt.Sprintf("remote %s %d", connection.IP, connection.Port), fmt.Sprintf("remote %s %d", connection.IP, connection.Port),
fmt.Sprintf("cipher %s", settings.Cipher), fmt.Sprintf("cipher %s", cipher),
fmt.Sprintf("auth %s", settings.Auth), fmt.Sprintf("auth %s", auth),
} }
if strings.HasSuffix(settings.Cipher, "-gcm") { if strings.HasSuffix(cipher, "-gcm") {
lines = append(lines, "ncp-ciphers AES-256-GCM:AES-256-CBC:AES-128-GCM") lines = append(lines, "ncp-ciphers AES-256-GCM:AES-256-CBC:AES-128-GCM")
} }
if !settings.Root { if !root {
lines = append(lines, "user "+username) lines = append(lines, "user "+username)
} }
if settings.MSSFix > 0 {
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
lines = append(lines, []string{ lines = append(lines, []string{
"<ca>", "<ca>",
"-----BEGIN CERTIFICATE-----", "-----BEGIN CERTIFICATE-----",
+84 -109
View File
@@ -6,77 +6,79 @@ import (
"strings" "strings"
"time" "time"
unboundmodels "github.com/qdm12/dns/pkg/models" "github.com/qdm12/gluetun/internal/constants"
unbound "github.com/qdm12/dns/pkg/unbound" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/params" "github.com/qdm12/gluetun/internal/params"
) )
// DNS contains settings to configure Unbound for DNS over TLS operation. // DNS contains settings to configure Unbound for DNS over TLS operation.
type DNS struct { //nolint:maligned type DNS struct {
Enabled bool Enabled bool
PlaintextAddress net.IP KeepNameserver bool
KeepNameserver bool Providers []models.DNSProvider
BlockMalicious bool PlaintextAddress net.IP
BlockAds bool AllowedHostnames []string
BlockSurveillance bool PrivateAddresses []string
UpdatePeriod time.Duration Caching bool
Unbound unboundmodels.Settings BlockMalicious bool
BlockSurveillance bool
BlockAds bool
VerbosityLevel uint8
VerbosityDetailsLevel uint8
ValidationLogLevel uint8
IPv6 bool
UpdatePeriod time.Duration
} }
func (d *DNS) String() string { func (d *DNS) String() string {
return strings.Join(d.lines(), "\n")
}
func (d *DNS) lines() (lines []string) {
if !d.Enabled { if !d.Enabled {
return []string{"DNS over TLS disabled, using plaintext DNS " + d.PlaintextAddress.String()} return fmt.Sprintf("DNS over TLS disabled, using plaintext DNS %s", d.PlaintextAddress)
} }
caching, blockMalicious, blockSurveillance, blockAds, ipv6 := disabled, disabled, disabled, disabled, disabled
const prefix = " |--" if d.Caching {
caching = enabled
lines = append(lines, "DNS settings:")
lines = append(lines, prefix+"Unbound:")
for _, line := range d.Unbound.Lines() {
indent := " " + prefix
if strings.HasPrefix(line, prefix) {
indent = " "
}
lines = append(lines, indent+line)
} }
blockMalicious := disabled
if d.BlockMalicious { if d.BlockMalicious {
blockMalicious = enabled blockMalicious = enabled
} }
lines = append(lines, prefix+"Block malicious: "+blockMalicious)
blockAds := disabled
if d.BlockAds {
blockAds = enabled
}
lines = append(lines, prefix+"Block ads: "+blockAds)
blockSurveillance := disabled
if d.BlockSurveillance { if d.BlockSurveillance {
blockSurveillance = enabled blockSurveillance = enabled
} }
lines = append(lines, prefix+"Block surveillance: "+blockSurveillance) if d.BlockAds {
blockAds = enabled
}
if d.IPv6 {
ipv6 = enabled
}
providersStr := make([]string, len(d.Providers))
for i := range d.Providers {
providersStr[i] = string(d.Providers[i])
}
update := "deactivated" update := "deactivated"
if d.UpdatePeriod > 0 { if d.UpdatePeriod > 0 {
update = "every " + d.UpdatePeriod.String() update = fmt.Sprintf("every %s", d.UpdatePeriod)
} }
lines = append(lines, prefix+"Update: "+update)
keepNameserver := "no" keepNameserver := "no"
if d.KeepNameserver { if d.KeepNameserver {
keepNameserver = "yes" keepNameserver = "yes"
} }
lines = append(lines, settingsList := []string{
prefix+"Keep nameserver (disabled blocking): "+keepNameserver) "DNS over TLS settings:",
"DNS over TLS provider:\n |--" + strings.Join(providersStr, "\n |--"),
return lines "Caching: " + caching,
"Block malicious: " + blockMalicious,
"Block surveillance: " + blockSurveillance,
"Block ads: " + blockAds,
"Allowed hostnames:\n |--" + strings.Join(d.AllowedHostnames, "\n |--"),
"Private addresses:\n |--" + strings.Join(d.PrivateAddresses, "\n |--"),
"Verbosity level: " + fmt.Sprintf("%d/5", d.VerbosityLevel),
"Verbosity details level: " + fmt.Sprintf("%d/4", d.VerbosityDetailsLevel),
"Validation log level: " + fmt.Sprintf("%d/2", d.ValidationLogLevel),
"IPv6 resolution: " + ipv6,
"Update: " + update,
"Keep nameserver (disabled blocking): " + keepNameserver,
}
return strings.Join(settingsList, "\n |--")
} }
// GetDNSSettings obtains DNS over TLS settings from environment variables using the params package. // GetDNSSettings obtains DNS over TLS settings from environment variables using the params package.
@@ -85,18 +87,22 @@ func GetDNSSettings(paramsReader params.Reader) (settings DNS, err error) {
if err != nil { if err != nil {
return settings, err return settings, err
} }
if !settings.Enabled {
// Plain DNS settings settings.PlaintextAddress, err = paramsReader.GetDNSPlaintext()
settings.PlaintextAddress, err = paramsReader.GetDNSPlaintext() return settings, err
}
settings.Providers, err = paramsReader.GetDNSOverTLSProviders()
if err != nil { if err != nil {
return settings, err return settings, err
} }
settings.KeepNameserver, err = paramsReader.GetDNSKeepNameserver() settings.AllowedHostnames, err = paramsReader.GetDNSUnblockedHostnames()
if err != nil {
return settings, err
}
settings.Caching, err = paramsReader.GetDNSOverTLSCaching()
if err != nil { if err != nil {
return settings, err return settings, err
} }
// DNS over TLS external settings
settings.BlockMalicious, err = paramsReader.GetDNSMaliciousBlocking() settings.BlockMalicious, err = paramsReader.GetDNSMaliciousBlocking()
if err != nil { if err != nil {
return settings, err return settings, err
@@ -109,21 +115,39 @@ func GetDNSSettings(paramsReader params.Reader) (settings DNS, err error) {
if err != nil { if err != nil {
return settings, err return settings, err
} }
settings.VerbosityLevel, err = paramsReader.GetDNSOverTLSVerbosity()
if err != nil {
return settings, err
}
settings.VerbosityDetailsLevel, err = paramsReader.GetDNSOverTLSVerbosityDetails()
if err != nil {
return settings, err
}
settings.ValidationLogLevel, err = paramsReader.GetDNSOverTLSValidationLogLevel()
if err != nil {
return settings, err
}
settings.PrivateAddresses, err = paramsReader.GetDNSOverTLSPrivateAddresses()
if err != nil {
return settings, err
}
settings.IPv6, err = paramsReader.GetDNSOverTLSIPv6()
if err != nil {
return settings, err
}
settings.UpdatePeriod, err = paramsReader.GetDNSUpdatePeriod() settings.UpdatePeriod, err = paramsReader.GetDNSUpdatePeriod()
if err != nil { if err != nil {
return settings, err return settings, err
} }
settings.KeepNameserver, err = paramsReader.GetDNSKeepNameserver()
// Unbound specific settings
settings.Unbound, err = getUnboundSettings(paramsReader)
if err != nil { if err != nil {
return settings, err return settings, err
} }
// Consistency check // Consistency check
IPv6Support := false IPv6Support := false
for _, provider := range settings.Unbound.Providers { for _, provider := range settings.Providers {
providerData, ok := unbound.GetProviderData(provider) providerData, ok := constants.DNSProviderMapping()[provider]
switch { switch {
case !ok: case !ok:
return settings, fmt.Errorf("DNS provider %q does not have associated data", provider) return settings, fmt.Errorf("DNS provider %q does not have associated data", provider)
@@ -133,57 +157,8 @@ func GetDNSSettings(paramsReader params.Reader) (settings DNS, err error) {
IPv6Support = true IPv6Support = true
} }
} }
if settings.Unbound.IPv6 && !IPv6Support { if settings.IPv6 && !IPv6Support {
return settings, fmt.Errorf("None of the DNS over TLS provider(s) set support IPv6") return settings, fmt.Errorf("None of the DNS over TLS provider(s) set support IPv6")
} }
return settings, nil return settings, nil
} }
func getUnboundSettings(reader params.Reader) (settings unboundmodels.Settings, err error) {
settings.Providers, err = reader.GetDNSOverTLSProviders()
if err != nil {
return settings, err
}
settings.ListeningPort = 53
settings.Caching, err = reader.GetDNSOverTLSCaching()
if err != nil {
return settings, err
}
settings.IPv4 = true
settings.IPv6, err = reader.GetDNSOverTLSIPv6()
if err != nil {
return settings, err
}
settings.VerbosityLevel, err = reader.GetDNSOverTLSVerbosity()
if err != nil {
return settings, err
}
settings.VerbosityDetailsLevel, err = reader.GetDNSOverTLSVerbosityDetails()
if err != nil {
return settings, err
}
settings.ValidationLogLevel, err = reader.GetDNSOverTLSValidationLogLevel()
if err != nil {
return settings, err
}
settings.BlockedHostnames = []string{}
settings.BlockedIPs, err = reader.GetDNSOverTLSPrivateAddresses()
if err != nil {
return settings, err
}
settings.AllowedHostnames, err = reader.GetDNSUnblockedHostnames()
if err != nil {
return settings, err
}
settings.AccessControl.Allowed = []net.IPNet{
{
IP: net.IPv4zero,
Mask: net.IPv4Mask(0, 0, 0, 0),
},
{
IP: net.IPv6zero,
Mask: net.IPMask{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
},
}
return settings, nil
}
-60
View File
@@ -1,60 +0,0 @@
package settings
import (
"net"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_DNS_Lines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings DNS
lines []string
}{
"disabled DOT": {
settings: DNS{
PlaintextAddress: net.IP{1, 1, 1, 1},
},
lines: []string{
"DNS over TLS disabled, using plaintext DNS 1.1.1.1",
},
},
"enabled DOT": {
settings: DNS{
Enabled: true,
},
lines: []string{
"DNS settings:",
" |--Unbound:",
" |--DNS over TLS provider:",
" |--Listening port: 0",
" |--Access control:",
" |--Allowed:",
" |--Caching: disabled",
" |--IPv4 resolution: disabled",
" |--IPv6 resolution: disabled",
" |--Verbosity level: 0/5",
" |--Verbosity details level: 0/4",
" |--Validation log level: 0/2",
" |--Blocked hostnames:",
" |--Blocked IP addresses:",
" |--Allowed hostnames:",
" |--Block malicious: disabled",
" |--Block ads: disabled",
" |--Block surveillance: disabled",
" |--Update: deactivated",
" |--Keep nameserver (disabled blocking): no",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.lines()
assert.Equal(t, testCase.lines, lines)
})
}
}
+11 -11
View File
@@ -42,30 +42,30 @@ func (h *HTTPProxy) String() string {
} }
// GetHTTPProxySettings obtains HTTPProxy settings from environment variables using the params package. // GetHTTPProxySettings obtains HTTPProxy settings from environment variables using the params package.
func GetHTTPProxySettings(paramsReader params.Reader) (settings HTTPProxy, warning string, err error) { func GetHTTPProxySettings(paramsReader params.Reader) (settings HTTPProxy, err error) {
settings.Enabled, err = paramsReader.GetHTTPProxy() settings.Enabled, err = paramsReader.GetHTTPProxy()
if err != nil || !settings.Enabled { if err != nil || !settings.Enabled {
return settings, "", err return settings, err
}
settings.Port, err = paramsReader.GetHTTPProxyPort()
if err != nil {
return settings, err
} }
settings.User, err = paramsReader.GetHTTPProxyUser() settings.User, err = paramsReader.GetHTTPProxyUser()
if err != nil { if err != nil {
return settings, "", err return settings, err
} }
settings.Password, err = paramsReader.GetHTTPProxyPassword() settings.Password, err = paramsReader.GetHTTPProxyPassword()
if err != nil { if err != nil {
return settings, "", err return settings, err
} }
settings.Stealth, err = paramsReader.GetHTTPProxyStealth() settings.Stealth, err = paramsReader.GetHTTPProxyStealth()
if err != nil { if err != nil {
return settings, "", err return settings, err
} }
settings.Log, err = paramsReader.GetHTTPProxyLog() settings.Log, err = paramsReader.GetHTTPProxyLog()
if err != nil { if err != nil {
return settings, "", err return settings, err
} }
settings.Port, warning, err = paramsReader.GetHTTPProxyPort() return settings, nil
if err != nil {
return settings, warning, err
}
return settings, warning, nil
} }
-5
View File
@@ -14,7 +14,6 @@ type OpenVPN struct {
User string `json:"user"` User string `json:"user"`
Password string `json:"password"` Password string `json:"password"`
Verbosity int `json:"verbosity"` Verbosity int `json:"verbosity"`
MSSFix uint16 `json:"mssfix"`
Root bool `json:"run_as_root"` Root bool `json:"run_as_root"`
Cipher string `json:"cipher"` Cipher string `json:"cipher"`
Auth string `json:"auth"` Auth string `json:"auth"`
@@ -53,10 +52,6 @@ func GetOpenVPNSettings(paramsReader params.Reader, vpnProvider models.VPNProvid
if err != nil { if err != nil {
return settings, err return settings, err
} }
settings.MSSFix, err = paramsReader.GetOpenVPNMSSFix()
if err != nil {
return settings, err
}
switch vpnProvider { switch vpnProvider {
case constants.PrivateInternetAccess: case constants.PrivateInternetAccess:
settings.Provider, err = GetPIASettings(paramsReader) settings.Provider, err = GetPIASettings(paramsReader)
+1 -1
View File
@@ -20,7 +20,7 @@ func Test_OpenVPN_JSON(t *testing.T) {
data, err := json.Marshal(in) data, err := json.Marshal(in)
require.NoError(t, err) require.NoError(t, err)
//nolint:lll //nolint:lll
assert.Equal(t, `{"user":"","password":"","verbosity":0,"mssfix":0,"run_as_root":true,"cipher":"","auth":"","provider":{"name":"name","server_selection":{"network_protocol":"","regions":null,"group":"","countries":null,"cities":null,"hostnames":null,"isps":null,"owned":false,"custom_port":0,"numbers":null,"encryption_preset":""},"extra_config":{"encryption_preset":"","openvpn_ipv6":false},"port_forwarding":{"enabled":false,"filepath":""}}}`, string(data)) assert.Equal(t, `{"user":"","password":"","verbosity":0,"run_as_root":true,"cipher":"","auth":"","provider":{"name":"name","server_selection":{"network_protocol":"","regions":null,"group":"","countries":null,"cities":null,"hostnames":null,"isps":null,"owned":false,"custom_port":0,"numbers":null,"encryption_preset":""},"extra_config":{"encryption_preset":"","openvpn_ipv6":false},"port_forwarding":{"enabled":false,"filepath":""}}}`, string(data))
var out OpenVPN var out OpenVPN
err = json.Unmarshal(data, &out) err = json.Unmarshal(data, &out)
require.NoError(t, err) require.NoError(t, err)
+7 -7
View File
@@ -24,14 +24,14 @@ func (c *ControlServer) String() string {
// GetControlServerSettings obtains the HTTP control server settings from // GetControlServerSettings obtains the HTTP control server settings from
// environment variables using the params package. // environment variables using the params package.
func GetControlServerSettings(paramsReader params.Reader) (settings ControlServer, warning string, err error) { func GetControlServerSettings(paramsReader params.Reader) (settings ControlServer, err error) {
settings.Port, err = paramsReader.GetControlServerPort()
if err != nil {
return settings, err
}
settings.Log, err = paramsReader.GetControlServerLog() settings.Log, err = paramsReader.GetControlServerLog()
if err != nil { if err != nil {
return settings, "", err return settings, err
} }
settings.Port, warning, err = paramsReader.GetControlServerPort() return settings, nil
if err != nil {
return settings, warning, err
}
return settings, warning, nil
} }
+20 -34
View File
@@ -50,64 +50,50 @@ func (s *Settings) String() string {
// GetAllSettings obtains all settings for the program and returns an error as soon // GetAllSettings obtains all settings for the program and returns an error as soon
// as an error is encountered reading them. // as an error is encountered reading them.
func GetAllSettings(paramsReader params.Reader) (settings Settings, warnings []string, err error) { func GetAllSettings(paramsReader params.Reader) (settings Settings, err error) {
settings.VPNSP, err = paramsReader.GetVPNSP() settings.VPNSP, err = paramsReader.GetVPNSP()
if err != nil { if err != nil {
return settings, nil, err return settings, err
} }
settings.OpenVPN, err = GetOpenVPNSettings(paramsReader, settings.VPNSP) settings.OpenVPN, err = GetOpenVPNSettings(paramsReader, settings.VPNSP)
if err != nil { if err != nil {
return settings, nil, err return settings, err
} }
settings.DNS, err = GetDNSSettings(paramsReader) settings.DNS, err = GetDNSSettings(paramsReader)
if err != nil { if err != nil {
return settings, nil, err return settings, err
} }
settings.Firewall, err = GetFirewallSettings(paramsReader) settings.Firewall, err = GetFirewallSettings(paramsReader)
if err != nil { if err != nil {
return settings, nil, err return settings, err
}
settings.HTTPProxy, err = GetHTTPProxySettings(paramsReader)
if err != nil {
return settings, err
}
settings.ShadowSocks, err = GetShadowSocksSettings(paramsReader)
if err != nil {
return settings, err
} }
settings.System, err = GetSystemSettings(paramsReader) settings.System, err = GetSystemSettings(paramsReader)
if err != nil { if err != nil {
return settings, nil, err return settings, err
} }
settings.PublicIP, err = getPublicIPSettings(paramsReader) settings.PublicIP, err = getPublicIPSettings(paramsReader)
if err != nil { if err != nil {
return settings, nil, err return settings, err
} }
settings.VersionInformation, err = paramsReader.GetVersionInformation() settings.VersionInformation, err = paramsReader.GetVersionInformation()
if err != nil { if err != nil {
return settings, nil, err return settings, err
} }
settings.Updater, err = GetUpdaterSettings(paramsReader) settings.Updater, err = GetUpdaterSettings(paramsReader)
if err != nil { if err != nil {
return settings, nil, err return settings, err
}
var warning string
settings.HTTPProxy, warning, err = GetHTTPProxySettings(paramsReader)
if warning != "" {
warnings = append(warnings, warning)
} }
settings.ControlServer, err = GetControlServerSettings(paramsReader)
if err != nil { if err != nil {
return settings, warnings, err return settings, err
} }
return settings, nil
settings.ShadowSocks, warning, err = GetShadowSocksSettings(paramsReader)
if warning != "" {
warnings = append(warnings, warning)
}
if err != nil {
return settings, warnings, err
}
settings.ControlServer, warning, err = GetControlServerSettings(paramsReader)
if warning != "" {
warnings = append(warnings, warning)
}
if err != nil {
return settings, warnings, err
}
return settings, warnings, nil
} }
+10 -10
View File
@@ -35,26 +35,26 @@ func (s *ShadowSocks) String() string {
} }
// GetShadowSocksSettings obtains ShadowSocks settings from environment variables using the params package. // GetShadowSocksSettings obtains ShadowSocks settings from environment variables using the params package.
func GetShadowSocksSettings(paramsReader params.Reader) (settings ShadowSocks, warning string, err error) { func GetShadowSocksSettings(paramsReader params.Reader) (settings ShadowSocks, err error) {
settings.Enabled, err = paramsReader.GetShadowSocks() settings.Enabled, err = paramsReader.GetShadowSocks()
if err != nil || !settings.Enabled { if err != nil || !settings.Enabled {
return settings, "", err return settings, err
}
settings.Port, err = paramsReader.GetShadowSocksPort()
if err != nil {
return settings, err
} }
settings.Password, err = paramsReader.GetShadowSocksPassword() settings.Password, err = paramsReader.GetShadowSocksPassword()
if err != nil { if err != nil {
return settings, "", err return settings, err
} }
settings.Log, err = paramsReader.GetShadowSocksLog() settings.Log, err = paramsReader.GetShadowSocksLog()
if err != nil { if err != nil {
return settings, "", err return settings, err
} }
settings.Method, err = paramsReader.GetShadowSocksMethod() settings.Method, err = paramsReader.GetShadowSocksMethod()
if err != nil { if err != nil {
return settings, "", err return settings, err
} }
settings.Port, warning, err = paramsReader.GetShadowSocksPort() return settings, nil
if err != nil {
return settings, warning, err
}
return settings, warning, nil
} }
-4
View File
@@ -4,7 +4,6 @@ import (
"context" "context"
"fmt" "fmt"
"net" "net"
"sync"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -54,14 +53,11 @@ func Test_resolveRepeat(t *testing.T) {
} }
const host = "blabla" const host = "blabla"
i := 0 i := 0
mutex := &sync.Mutex{}
lookupIP := func(ctx context.Context, argHost string) ( lookupIP := func(ctx context.Context, argHost string) (
ips []net.IP, err error) { ips []net.IP, err error) {
assert.Equal(t, host, argHost) assert.Equal(t, host, argHost)
mutex.Lock()
result := testCase.lookupIPResult[i] result := testCase.lookupIPResult[i]
i++ i++
mutex.Unlock()
return result, testCase.err return result, testCase.err
} }