Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63303bc311 | ||
|
|
5200ee5722 | ||
|
|
86ec75722a | ||
|
|
0a29337c3b | ||
|
|
00ee6ff9a7 | ||
|
|
6d0a2a968f | ||
|
|
4bb77ebcc5 | ||
|
|
56ecfcb9f4 | ||
|
|
9a0fcbc011 | ||
|
|
b6c8399c3b | ||
|
|
7a88a09341 | ||
|
|
912b31cfc6 | ||
|
|
d21a943779 | ||
|
|
801a7fd6fe | ||
|
|
80053f6b7d | ||
|
|
e165bb6870 | ||
|
|
67bd1171ae | ||
|
|
4e2e46014d | ||
|
|
1693c59e0d | ||
|
|
9d4105ee59 | ||
|
|
19585da3bc | ||
|
|
51f830cfc1 | ||
|
|
804ea7ebd6 | ||
|
|
3294b8df60 | ||
|
|
d77ec7a6cb | ||
|
|
219d1f371c | ||
|
|
fa7fd5f076 | ||
|
|
d4f8eea7bf | ||
|
|
723d0f5e12 | ||
|
|
20f4d8cc0b | ||
|
|
64cca69bf3 | ||
|
|
fc8a2abb8f | ||
|
|
16ecf48b89 | ||
|
|
8fa4fd1b64 | ||
|
|
4db6d1ecf9 | ||
|
|
3b86927ca7 | ||
|
|
8bfa2f9b27 | ||
|
|
fe2a3e4d11 | ||
|
|
b0451d8e50 | ||
|
|
a0b9044fd3 | ||
|
|
c7a841f4b4 | ||
|
|
4ba159e483 | ||
|
|
63a696d7e7 | ||
|
|
d457342b46 | ||
|
|
c246dae2cc | ||
|
|
0f4a2e5224 | ||
|
|
db262050d5 | ||
|
|
227cdea0c8 | ||
|
|
33a6f1c01b | ||
|
|
f6f3c110f0 | ||
|
|
27a3f2c846 | ||
|
|
62169baeea | ||
|
|
4b18636a91 | ||
|
|
51432ca05f | ||
|
|
b5ebdcd040 | ||
|
|
416c1ee113 | ||
|
|
fe97e28461 | ||
|
|
cbd8711a21 | ||
|
|
7578e52ed5 | ||
|
|
0df68f76d5 | ||
|
|
9a528c42f8 | ||
|
|
5607916af6 | ||
|
|
4ad7a2a444 | ||
|
|
ab5dbdca97 | ||
|
|
a97fcda283 | ||
|
|
e955adc1e1 | ||
|
|
ac5141b411 | ||
|
|
f8c189e48a | ||
|
|
2f2a904c64 | ||
|
|
9261dca8ab | ||
|
|
7b5d5c3884 | ||
|
|
7c80d80904 | ||
|
|
ea40b84ec0 | ||
|
|
4e6ef649c4 | ||
|
|
dd40f1d2e6 | ||
|
|
490693bb26 | ||
|
|
c8d33ca5f3 | ||
|
|
e6df026332 | ||
|
|
7a30343053 | ||
|
|
fc02ae9c13 | ||
|
|
f70f0aca9c | ||
|
|
16acd1b162 | ||
|
|
2e3eb1fd7b | ||
|
|
a4cf17f81e | ||
|
|
c0a301611d | ||
|
|
cc934f5c68 | ||
|
|
74426f6202 | ||
|
|
03ed3cb1c8 | ||
|
|
1b1335835b | ||
|
|
5070dbcf7f | ||
|
|
90b9d85742 | ||
|
|
7a3b9941aa | ||
|
|
698095f0a0 | ||
|
|
5a06d8e155 | ||
|
|
7421dcb45f | ||
|
|
554a6cdb92 | ||
|
|
5aa39be973 | ||
|
|
192a7a56a3 | ||
|
|
1d1657e9be | ||
|
|
49b7301295 |
@@ -1,2 +1,2 @@
|
|||||||
FROM qmcgaw/godevcontainer
|
FROM qmcgaw/godevcontainer
|
||||||
RUN apk add wireguard-tools htop
|
RUN apk add wireguard-tools htop openssl
|
||||||
|
|||||||
@@ -12,14 +12,12 @@ services:
|
|||||||
# Docker configuration
|
# Docker configuration
|
||||||
- ~/.docker:/root/.docker
|
- ~/.docker:/root/.docker
|
||||||
# SSH directory for Linux, OSX and WSL
|
# SSH directory for Linux, OSX and WSL
|
||||||
- ~/.ssh:/root/.ssh
|
# On Linux and OSX, a symlink /mnt/ssh <-> ~/.ssh is
|
||||||
# For Windows without WSL, a copy will be made
|
# created in the container. On Windows, files are copied
|
||||||
# from /tmp/.ssh to ~/.ssh to fix permissions
|
# from /mnt/ssh to ~/.ssh to fix permissions.
|
||||||
#- ~/.ssh:/tmp/.ssh:ro
|
- ~/.ssh:/mnt/ssh
|
||||||
# Shell history persistence
|
# Shell history persistence
|
||||||
- ~/.zsh_history:/root/.zsh_history
|
- ~/.zsh_history:/root/.zsh_history
|
||||||
# Git config
|
|
||||||
- ~/.gitconfig:/root/.gitconfig
|
|
||||||
environment:
|
environment:
|
||||||
- TZ=
|
- TZ=
|
||||||
cap_add:
|
cap_add:
|
||||||
|
|||||||
31
.github/labels.yml
vendored
31
.github/labels.yml
vendored
@@ -1,18 +1,13 @@
|
|||||||
- name: "Bug :bug:"
|
# Temporary status
|
||||||
color: "b60205"
|
- name: "🗯️ Waiting for feedback"
|
||||||
description: ""
|
color: "aadefa"
|
||||||
- name: "Feature request :bulb:"
|
|
||||||
color: "0e8a16"
|
|
||||||
description: ""
|
|
||||||
- name: "Help wanted :pray:"
|
|
||||||
color: "4caf50"
|
|
||||||
description: ""
|
|
||||||
- name: "Documentation :memo:"
|
|
||||||
color: "c5def5"
|
|
||||||
description: ""
|
|
||||||
- name: "Needs more info :thinking:"
|
|
||||||
color: "795548"
|
|
||||||
description: ""
|
description: ""
|
||||||
|
- name: "🔴 Blocked"
|
||||||
|
color: "ff3f14"
|
||||||
|
description: "Blocked by another issue or pull request"
|
||||||
|
- name: "🔒 After next release"
|
||||||
|
color: "e8f274"
|
||||||
|
description: "Will be done after the next release"
|
||||||
|
|
||||||
# Priority
|
# Priority
|
||||||
- name: "🚨 Urgent"
|
- name: "🚨 Urgent"
|
||||||
@@ -22,6 +17,14 @@
|
|||||||
color: "4285f4"
|
color: "4285f4"
|
||||||
description: ""
|
description: ""
|
||||||
|
|
||||||
|
# Complexity
|
||||||
|
- name: "☣️ Hard to do"
|
||||||
|
color: "7d0008"
|
||||||
|
description: ""
|
||||||
|
- name: "🟩 Easy to do"
|
||||||
|
color: "34cf43"
|
||||||
|
description: ""
|
||||||
|
|
||||||
# VPN providers
|
# VPN providers
|
||||||
- name: ":cloud: AirVPN"
|
- name: ":cloud: AirVPN"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
|
|||||||
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -93,6 +93,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
actions: read
|
actions: read
|
||||||
contents: read
|
contents: read
|
||||||
|
packages: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -106,6 +107,7 @@ jobs:
|
|||||||
flavor: |
|
flavor: |
|
||||||
latest=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
latest=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
||||||
images: |
|
images: |
|
||||||
|
ghcr.io/qdm12/gluetun
|
||||||
qmcgaw/gluetun
|
qmcgaw/gluetun
|
||||||
qmcgaw/private-internet-access
|
qmcgaw/private-internet-access
|
||||||
tags: |
|
tags: |
|
||||||
@@ -123,12 +125,18 @@ jobs:
|
|||||||
username: qmcgaw
|
username: qmcgaw
|
||||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||||
|
|
||||||
|
- uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: qdm12
|
||||||
|
password: ${{ github.token }}
|
||||||
|
|
||||||
- name: Short commit
|
- name: Short commit
|
||||||
id: shortcommit
|
id: shortcommit
|
||||||
run: echo "::set-output name=value::$(git rev-parse --short HEAD)"
|
run: echo "::set-output name=value::$(git rev-parse --short HEAD)"
|
||||||
|
|
||||||
- name: Build and push final image
|
- name: Build and push final image
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v4.0.0
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
|
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ issues:
|
|||||||
- text: "returns interface \\(github\\.com\\/vishvananda\\/netlink\\.Link\\)"
|
- text: "returns interface \\(github\\.com\\/vishvananda\\/netlink\\.Link\\)"
|
||||||
linters:
|
linters:
|
||||||
- ireturn
|
- ireturn
|
||||||
|
- path: "internal\\/openvpn\\/pkcs8\\/descbc\\.go"
|
||||||
|
text: "newCipherDESCBCBlock returns interface \\(github\\.com\\/youmark\\/pkcs8\\.Cipher\\)"
|
||||||
|
linters:
|
||||||
|
- ireturn
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
enable:
|
enable:
|
||||||
|
|||||||
17
Dockerfile
17
Dockerfile
@@ -1,8 +1,8 @@
|
|||||||
ARG ALPINE_VERSION=3.16
|
ARG ALPINE_VERSION=3.17
|
||||||
ARG GO_ALPINE_VERSION=3.16
|
ARG GO_ALPINE_VERSION=3.17
|
||||||
ARG GO_VERSION=1.19
|
ARG GO_VERSION=1.20
|
||||||
ARG XCPUTRANSLATE_VERSION=v0.6.0
|
ARG XCPUTRANSLATE_VERSION=v0.6.0
|
||||||
ARG GOLANGCI_LINT_VERSION=v1.49.0
|
ARG GOLANGCI_LINT_VERSION=v1.52.2
|
||||||
ARG MOCKGEN_VERSION=v1.6.0
|
ARG MOCKGEN_VERSION=v1.6.0
|
||||||
ARG BUILDPLATFORM=linux/amd64
|
ARG BUILDPLATFORM=linux/amd64
|
||||||
|
|
||||||
@@ -97,6 +97,7 @@ ENV VPN_SERVICE_PROVIDER=pia \
|
|||||||
WIREGUARD_PRESHARED_KEY= \
|
WIREGUARD_PRESHARED_KEY= \
|
||||||
WIREGUARD_PUBLIC_KEY= \
|
WIREGUARD_PUBLIC_KEY= \
|
||||||
WIREGUARD_ADDRESSES= \
|
WIREGUARD_ADDRESSES= \
|
||||||
|
WIREGUARD_IMPLEMENTATION=auto \
|
||||||
# VPN server filtering
|
# VPN server filtering
|
||||||
SERVER_REGIONS= \
|
SERVER_REGIONS= \
|
||||||
SERVER_COUNTRIES= \
|
SERVER_COUNTRIES= \
|
||||||
@@ -107,8 +108,8 @@ ENV VPN_SERVICE_PROVIDER=pia \
|
|||||||
OWNED_ONLY=no \
|
OWNED_ONLY=no \
|
||||||
# # Private Internet Access only:
|
# # Private Internet Access only:
|
||||||
PRIVATE_INTERNET_ACCESS_OPENVPN_ENCRYPTION_PRESET= \
|
PRIVATE_INTERNET_ACCESS_OPENVPN_ENCRYPTION_PRESET= \
|
||||||
PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING=off \
|
VPN_PORT_FORWARDING=off \
|
||||||
PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \
|
VPN_PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \
|
||||||
# # Cyberghost only:
|
# # Cyberghost only:
|
||||||
OPENVPN_CERT= \
|
OPENVPN_CERT= \
|
||||||
OPENVPN_KEY= \
|
OPENVPN_KEY= \
|
||||||
@@ -140,6 +141,7 @@ ENV VPN_SERVICE_PROVIDER=pia \
|
|||||||
# Health
|
# Health
|
||||||
HEALTH_SERVER_ADDRESS=127.0.0.1:9999 \
|
HEALTH_SERVER_ADDRESS=127.0.0.1:9999 \
|
||||||
HEALTH_TARGET_ADDRESS=cloudflare.com:443 \
|
HEALTH_TARGET_ADDRESS=cloudflare.com:443 \
|
||||||
|
HEALTH_SUCCESS_WAIT_DURATION=5s \
|
||||||
HEALTH_VPN_DURATION_INITIAL=6s \
|
HEALTH_VPN_DURATION_INITIAL=6s \
|
||||||
HEALTH_VPN_DURATION_ADDITION=5s \
|
HEALTH_VPN_DURATION_ADDITION=5s \
|
||||||
# DNS over TLS
|
# DNS over TLS
|
||||||
@@ -196,8 +198,9 @@ ENTRYPOINT ["/gluetun-entrypoint"]
|
|||||||
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
|
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
|
||||||
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /gluetun-entrypoint healthcheck
|
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /gluetun-entrypoint healthcheck
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
RUN apk add --no-cache --update -l apk-tools && \
|
RUN apk add --no-cache --update -l wget && \
|
||||||
apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.12/main" openvpn==2.4.12-r0 && \
|
apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.12/main" openvpn==2.4.12-r0 && \
|
||||||
|
apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.16/main" openssl\~1.1 && \
|
||||||
mv /usr/sbin/openvpn /usr/sbin/openvpn2.4 && \
|
mv /usr/sbin/openvpn /usr/sbin/openvpn2.4 && \
|
||||||
apk del openvpn && \
|
apk del openvpn && \
|
||||||
apk add --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \
|
apk add --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ Lightweight swiss-knife-like VPN client to multiple VPN service providers
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Based on Alpine 3.16 for a small Docker image of 29MB
|
- Based on Alpine 3.17 for a small Docker image of 42MB
|
||||||
- Supports: **AirVPN**, **Cyberghost**, **ExpressVPN**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Perfect Privacy**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **SlickVPN**, **Surfshark**, **TorGuard**, **VPNSecure.me**, **VPNUnlimited**, **Vyprvpn**, **WeVPN**, **Windscribe** servers
|
- Supports: **AirVPN**, **Cyberghost**, **ExpressVPN**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Perfect Privacy**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **SlickVPN**, **Surfshark**, **TorGuard**, **VPNSecure.me**, **VPNUnlimited**, **Vyprvpn**, **WeVPN**, **Windscribe** servers
|
||||||
- Supports OpenVPN for all providers listed
|
- Supports OpenVPN for all providers listed
|
||||||
- Supports Wireguard both kernelspace and userspace
|
- Supports Wireguard both kernelspace and userspace
|
||||||
@@ -118,8 +118,13 @@ services:
|
|||||||
# - WIREGUARD_ADDRESSES=10.64.222.21/32
|
# - WIREGUARD_ADDRESSES=10.64.222.21/32
|
||||||
# Timezone for accurate log times
|
# Timezone for accurate log times
|
||||||
- TZ=
|
- TZ=
|
||||||
|
# Server list updater. See https://github.com/qdm12/gluetun/wiki/Updating-Servers#periodic-update
|
||||||
|
- UPDATER_PERIOD=
|
||||||
|
- UPDATER_VPN_SERVICE_PROVIDERS=
|
||||||
```
|
```
|
||||||
|
|
||||||
|
🆕 Image also available as `ghcr.io/qdm12/gluetun`
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[](https://github.com/qdm12/gluetun/master/LICENSE)
|
[](https://github.com/qdm12/gluetun/master/LICENSE)
|
||||||
|
|||||||
@@ -77,7 +77,8 @@ func main() {
|
|||||||
|
|
||||||
args := os.Args
|
args := os.Args
|
||||||
tun := tun.New()
|
tun := tun.New()
|
||||||
netLinker := netlink.New()
|
netLinkDebugLogger := logger.New(log.SetComponent("netlink"))
|
||||||
|
netLinker := netlink.New(netLinkDebugLogger)
|
||||||
cli := cli.New()
|
cli := cli.New()
|
||||||
cmder := command.NewCmder()
|
cmder := command.NewCmder()
|
||||||
|
|
||||||
@@ -91,12 +92,13 @@ func main() {
|
|||||||
errorCh <- _main(ctx, buildInfo, args, logger, muxReader, tun, netLinker, cmder, cli)
|
errorCh <- _main(ctx, buildInfo, args, logger, muxReader, tun, netLinker, cmder, cli)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
var err error
|
||||||
select {
|
select {
|
||||||
case signal := <-signalCh:
|
case signal := <-signalCh:
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
logger.Warn("Caught OS signal " + signal.String() + ", shutting down")
|
logger.Warn("Caught OS signal " + signal.String() + ", shutting down")
|
||||||
cancel()
|
cancel()
|
||||||
case err := <-errorCh:
|
case err = <-errorCh:
|
||||||
close(errorCh)
|
close(errorCh)
|
||||||
if err == nil { // expected exit such as healthcheck
|
if err == nil { // expected exit such as healthcheck
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
@@ -108,23 +110,28 @@ func main() {
|
|||||||
const shutdownGracePeriod = 5 * time.Second
|
const shutdownGracePeriod = 5 * time.Second
|
||||||
timer := time.NewTimer(shutdownGracePeriod)
|
timer := time.NewTimer(shutdownGracePeriod)
|
||||||
select {
|
select {
|
||||||
case err := <-errorCh:
|
case shutdownErr := <-errorCh:
|
||||||
if !timer.Stop() {
|
if !timer.Stop() {
|
||||||
<-timer.C
|
<-timer.C
|
||||||
}
|
}
|
||||||
if err == nil {
|
if shutdownErr != nil {
|
||||||
logger.Info("Shutdown successful")
|
logger.Warnf("Shutdown not completed gracefully: %s", shutdownErr)
|
||||||
os.Exit(0)
|
os.Exit(1)
|
||||||
}
|
|
||||||
logger.Warnf("Shutdown not completed gracefully: %s", err)
|
|
||||||
case <-timer.C:
|
|
||||||
logger.Warn("Shutdown timed out")
|
|
||||||
case signal := <-signalCh:
|
|
||||||
logger.Warn("Caught OS signal " + signal.String() + ", forcing shut down")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Info("Shutdown successful")
|
||||||
|
if err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
case <-timer.C:
|
||||||
|
logger.Warn("Shutdown timed out")
|
||||||
|
os.Exit(1)
|
||||||
|
case signal := <-signalCh:
|
||||||
|
logger.Warn("Caught OS signal " + signal.String() + ", forcing shut down")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errCommandUnknown = errors.New("command is unknown")
|
errCommandUnknown = errors.New("command is unknown")
|
||||||
@@ -183,6 +190,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
// - firewall Debug and Enabled are booleans parsed from source
|
// - firewall Debug and Enabled are booleans parsed from source
|
||||||
|
|
||||||
logger.Patch(log.SetLevel(*allSettings.Log.Level))
|
logger.Patch(log.SetLevel(*allSettings.Log.Level))
|
||||||
|
netLinker.PatchLoggerLevel(*allSettings.Log.Level)
|
||||||
|
|
||||||
routingLogger := logger.New(log.SetComponent("routing"))
|
routingLogger := logger.New(log.SetComponent("routing"))
|
||||||
if *allSettings.Firewall.Debug { // To remove in v4
|
if *allSettings.Firewall.Debug { // To remove in v4
|
||||||
@@ -224,7 +232,12 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = allSettings.Validate(storage)
|
ipv6Supported, err := netLinker.IsIPv6Supported()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("checking for IPv6 support: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = allSettings.Validate(storage, ipv6Supported)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -232,7 +245,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
allSettings.Pprof.HTTPServer.Logger = logger.New(log.SetComponent("pprof"))
|
allSettings.Pprof.HTTPServer.Logger = logger.New(log.SetComponent("pprof"))
|
||||||
pprofServer, err := pprof.New(allSettings.Pprof)
|
pprofServer, err := pprof.New(allSettings.Pprof)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot create Pprof server: %w", err)
|
return fmt.Errorf("creating Pprof server: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
puid, pgid := int(*allSettings.System.PUID), int(*allSettings.System.PGID)
|
puid, pgid := int(*allSettings.System.PUID), int(*allSettings.System.PGID)
|
||||||
@@ -264,6 +277,10 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
|
|
||||||
logger.Info(allSettings.String())
|
logger.Info(allSettings.String())
|
||||||
|
|
||||||
|
for _, warning := range allSettings.Warnings() {
|
||||||
|
logger.Warn(warning)
|
||||||
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil {
|
if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -274,7 +291,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
const defaultUsername = "nonrootuser"
|
const defaultUsername = "nonrootuser"
|
||||||
nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid)
|
nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot create user: %w", err)
|
return fmt.Errorf("creating user: %w", err)
|
||||||
}
|
}
|
||||||
if nonRootUsername != defaultUsername {
|
if nonRootUsername != defaultUsername {
|
||||||
logger.Info("using existing username " + nonRootUsername + " corresponding to user id " + fmt.Sprint(puid))
|
logger.Info("using existing username " + nonRootUsername + " corresponding to user id " + fmt.Sprint(puid))
|
||||||
@@ -288,22 +305,11 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ipv6Supported, err := netLinker.IsIPv6Supported()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("checking for IPv6 support: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ipv6Supported {
|
|
||||||
logger.Info("IPv6 is supported")
|
|
||||||
} else {
|
|
||||||
logger.Info("IPv6 is not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := routingConf.Setup(); err != nil {
|
if err := routingConf.Setup(); err != nil {
|
||||||
if strings.Contains(err.Error(), "operation not permitted") {
|
if strings.Contains(err.Error(), "operation not permitted") {
|
||||||
logger.Warn("💡 Tip: Are you passing NET_ADMIN capability to gluetun?")
|
logger.Warn("💡 Tip: Are you passing NET_ADMIN capability to gluetun?")
|
||||||
}
|
}
|
||||||
return fmt.Errorf("cannot setup routing: %w", err)
|
return fmt.Errorf("setting up routing: %w", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
routingLogger.Info("routing cleanup...")
|
routingLogger.Info("routing cleanup...")
|
||||||
@@ -319,6 +325,11 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = routingConf.AddLocalRules(localNetworks)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("adding local rules: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
const tunDevice = "/dev/net/tun"
|
const tunDevice = "/dev/net/tun"
|
||||||
if err := tun.Check(tunDevice); err != nil {
|
if err := tun.Check(tunDevice); err != nil {
|
||||||
logger.Info(err.Error() + "; creating it...")
|
logger.Info(err.Error() + "; creating it...")
|
||||||
@@ -354,11 +365,14 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
tickersGroupHandler := goshutdown.NewGroupHandler("tickers", defaultGroupOptions...)
|
tickersGroupHandler := goshutdown.NewGroupHandler("tickers", defaultGroupOptions...)
|
||||||
otherGroupHandler := goshutdown.NewGroupHandler("other", defaultGroupOptions...)
|
otherGroupHandler := goshutdown.NewGroupHandler("other", defaultGroupOptions...)
|
||||||
|
|
||||||
|
if *allSettings.Pprof.Enabled {
|
||||||
|
// TODO run in run loop so this can be patched at runtime
|
||||||
pprofReady := make(chan struct{})
|
pprofReady := make(chan struct{})
|
||||||
pprofHandler, pprofCtx, pprofDone := goshutdown.NewGoRoutineHandler("pprof server")
|
pprofHandler, pprofCtx, pprofDone := goshutdown.NewGoRoutineHandler("pprof server")
|
||||||
go pprofServer.Run(pprofCtx, pprofReady, pprofDone)
|
go pprofServer.Run(pprofCtx, pprofReady, pprofDone)
|
||||||
otherGroupHandler.Add(pprofHandler)
|
otherGroupHandler.Add(pprofHandler)
|
||||||
<-pprofReady
|
<-pprofReady
|
||||||
|
}
|
||||||
|
|
||||||
portForwardLogger := logger.New(log.SetComponent("port forwarding"))
|
portForwardLogger := logger.New(log.SetComponent("port forwarding"))
|
||||||
portForwardLooper := portforward.NewLoop(allSettings.VPN.Provider.PortForwarding,
|
portForwardLooper := portforward.NewLoop(allSettings.VPN.Provider.PortForwarding,
|
||||||
@@ -446,9 +460,10 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
"http server", goroutine.OptionTimeout(defaultShutdownTimeout))
|
"http server", goroutine.OptionTimeout(defaultShutdownTimeout))
|
||||||
httpServer, err := server.New(httpServerCtx, controlServerAddress, controlServerLogging,
|
httpServer, err := server.New(httpServerCtx, controlServerAddress, controlServerLogging,
|
||||||
logger.New(log.SetComponent("http server")),
|
logger.New(log.SetComponent("http server")),
|
||||||
buildInfo, vpnLooper, portForwardLooper, unboundLooper, updaterLooper, publicIPLooper, storage)
|
buildInfo, vpnLooper, portForwardLooper, unboundLooper, updaterLooper, publicIPLooper,
|
||||||
|
storage, ipv6Supported)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot setup control server: %w", err)
|
return fmt.Errorf("setting up control server: %w", err)
|
||||||
}
|
}
|
||||||
httpServerReady := make(chan struct{})
|
httpServerReady := make(chan struct{})
|
||||||
go httpServer.Run(httpServerCtx, httpServerReady, httpServerDone)
|
go httpServer.Run(httpServerCtx, httpServerReady, httpServerDone)
|
||||||
@@ -495,7 +510,7 @@ func printVersions(ctx context.Context, logger infoer,
|
|||||||
for _, element := range elements {
|
for _, element := range elements {
|
||||||
version, err := element.getVersion(ctx)
|
version, err := element.getVersion(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("getting %s version: %w", element.name, err)
|
||||||
}
|
}
|
||||||
logger.Info(element.name + " version: " + version)
|
logger.Info(element.name + " version: " + version)
|
||||||
}
|
}
|
||||||
@@ -510,6 +525,7 @@ type netLinker interface {
|
|||||||
Linker
|
Linker
|
||||||
IsWireguardSupported() (ok bool, err error)
|
IsWireguardSupported() (ok bool, err error)
|
||||||
IsIPv6Supported() (ok bool, err error)
|
IsIPv6Supported() (ok bool, err error)
|
||||||
|
PatchLoggerLevel(level log.Level)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Addresser interface {
|
type Addresser interface {
|
||||||
|
|||||||
42
go.mod
42
go.mod
@@ -1,10 +1,10 @@
|
|||||||
module github.com/qdm12/gluetun
|
module github.com/qdm12/gluetun
|
||||||
|
|
||||||
go 1.19
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/breml/rootcerts v0.2.8
|
github.com/breml/rootcerts v0.2.10
|
||||||
github.com/fatih/color v1.13.0
|
github.com/fatih/color v1.15.0
|
||||||
github.com/golang/mock v1.6.0
|
github.com/golang/mock v1.6.0
|
||||||
github.com/qdm12/dns v1.11.0
|
github.com/qdm12/dns v1.11.0
|
||||||
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6
|
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6
|
||||||
@@ -15,34 +15,36 @@ require (
|
|||||||
github.com/qdm12/log v0.1.0
|
github.com/qdm12/log v0.1.0
|
||||||
github.com/qdm12/ss-server v0.4.0
|
github.com/qdm12/ss-server v0.4.0
|
||||||
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
|
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.2
|
||||||
github.com/vishvananda/netlink v1.1.1-0.20211129163951-9ada19101fc5
|
github.com/vishvananda/netlink v1.2.1-beta.2
|
||||||
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2
|
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
|
golang.org/x/exp v0.0.0-20230519143937-03e91628a987
|
||||||
golang.org/x/text v0.4.0
|
golang.org/x/net v0.10.0
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478
|
golang.org/x/sys v0.8.0
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220504211119-3d4a969bb56b
|
golang.org/x/text v0.9.0
|
||||||
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722
|
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b
|
||||||
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde
|
||||||
|
inet.af/netaddr v0.0.0-20220811202034-502d2d690317
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/google/go-cmp v0.5.7 // indirect
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
github.com/josharian/native v1.0.0 // indirect
|
github.com/josharian/native v1.0.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||||
github.com/mdlayher/genetlink v1.2.0 // indirect
|
github.com/mdlayher/genetlink v1.2.0 // indirect
|
||||||
github.com/mdlayher/netlink v1.6.0 // indirect
|
github.com/mdlayher/netlink v1.6.2 // indirect
|
||||||
github.com/mdlayher/socket v0.2.3 // indirect
|
github.com/mdlayher/socket v0.2.3 // indirect
|
||||||
github.com/miekg/dns v1.1.40 // indirect
|
github.com/miekg/dns v1.1.40 // indirect
|
||||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
|
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
|
||||||
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
|
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230221090011-e4bae7ad2296 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
|
golang.org/x/crypto v0.6.0 // indirect
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
golang.org/x/sync v0.1.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
89
go.sum
89
go.sum
@@ -4,8 +4,8 @@ github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/g
|
|||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
github.com/breml/rootcerts v0.2.8 h1:hNPyNa+MghU9ZKqWy+MYxvouNqE70jvOBIa5v70z/P8=
|
github.com/breml/rootcerts v0.2.10 h1:UGVZ193UTSUASpGtg6pbDwzOd7XQP+at0Ssg1/2E4h8=
|
||||||
github.com/breml/rootcerts v0.2.8/go.mod h1:24FDtzYMpqIeYC7QzaE8VPRQaFZU5TIUDlyk8qwjD88=
|
github.com/breml/rootcerts v0.2.10/go.mod h1:24FDtzYMpqIeYC7QzaE8VPRQaFZU5TIUDlyk8qwjD88=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
@@ -14,8 +14,8 @@ github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
|
|||||||
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
@@ -36,10 +36,12 @@ github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3K
|
|||||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||||
|
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
|
||||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||||
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
|
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
@@ -56,15 +58,17 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|||||||
github.com/kyokomi/emoji v2.2.4+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
|
github.com/kyokomi/emoji v2.2.4+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
|
||||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||||
|
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mdlayher/genetlink v1.2.0 h1:4yrIkRV5Wfk1WfpWTcoOlGmsWgQj3OtQN9ZsbrE+XtU=
|
github.com/mdlayher/genetlink v1.2.0 h1:4yrIkRV5Wfk1WfpWTcoOlGmsWgQj3OtQN9ZsbrE+XtU=
|
||||||
github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ=
|
github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ=
|
||||||
github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0=
|
|
||||||
github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
|
github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
|
||||||
|
github.com/mdlayher/netlink v1.6.2 h1:D2zGSkvYsJ6NreeED3JiVTu1lj2sIYATqSaZlhPzUgQ=
|
||||||
|
github.com/mdlayher/netlink v1.6.2/go.mod h1:O1HXX2sIWSMJ3Qn1BYZk1yZM+7iMki/uYGGiwGyq/iU=
|
||||||
github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
|
github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
|
||||||
github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM=
|
github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM=
|
||||||
github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY=
|
github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY=
|
||||||
@@ -115,36 +119,45 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
|||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/vishvananda/netlink v1.1.1-0.20211129163951-9ada19101fc5 h1:b/k/BVWzWRS5v6AB0gf2ckFSbFsHN5jR0HoNso1pN+w=
|
github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs=
|
||||||
github.com/vishvananda/netlink v1.1.1-0.20211129163951-9ada19101fc5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
||||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns=
|
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns=
|
||||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||||
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg=
|
|
||||||
go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc=
|
go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc=
|
||||||
|
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=
|
||||||
|
go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4=
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230221090011-e4bae7ad2296 h1:QJ/xcIANMLApehfgPCHnfK1hZiaMmbaTVmPv7DAoTbo=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230221090011-e4bae7ad2296/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
|
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
|
golang.org/x/exp v0.0.0-20230519143937-03e91628a987 h1:3xJIFvzUFbu4ls0BTBYcgbCGhA63eAOEMxIHugyXJqA=
|
||||||
|
golang.org/x/exp v0.0.0-20230519143937-03e91628a987/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-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-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
@@ -153,12 +166,15 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 h1:6mzvA99KwZxbOrxww4EvWVQUnN1+xEu9tafK5ZxkYeA=
|
golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||||
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -175,12 +191,14 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@@ -188,8 +206,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
@@ -199,14 +218,13 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478 h1:vDy//hdR+GnROE3OdYbQKt9rdtNdHkDtONvpRwmls/0=
|
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b h1:J1CaxgLerRR5lgx3wnr6L04cJFbWoceSK9JWBdglINo=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478/go.mod h1:bVQfyl2sCM/QIIGHpWbFGfHPuDvqnCNkT6MQLTCjO/U=
|
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b/go.mod h1:tqur9LnfstdR9ep2LaJT4lFUl0EjlHtge+gAjmsHUG4=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220504211119-3d4a969bb56b h1:9JncmKXcUwE918my+H6xmjBdhK2jM/UTUNXxhRG1BAk=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde h1:ybF7AMzIUikL9x4LgwEmzhXtzRpKNqngme1VGDWz+Nk=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220504211119-3d4a969bb56b/go.mod h1:yp4gl6zOlnDGOZeWeDfMwQcsdOIQnMdhuPx9mwwWBL4=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde/go.mod h1:mQqgjkW8GQQcJQsbBvK890TKqUK1DfKWkuBGbOkuMHQ=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -219,6 +237,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 h1:Wobr37noukisGxpKo5jAsLREcpj61RxrWYzD8uwveOY=
|
||||||
inet.af/netaddr v0.0.0-20210511181906-37180328850c/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
|
inet.af/netaddr v0.0.0-20210511181906-37180328850c/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
|
||||||
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722 h1:Qws2rZnQudC58cIagVucPQDLmMi3kAXgxscsgD0v6DU=
|
inet.af/netaddr v0.0.0-20220811202034-502d2d690317 h1:U2fwK6P2EqmopP/hFLTOAjWTki0qgd4GMJn5X8wOleU=
|
||||||
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
|
inet.af/netaddr v0.0.0-20220811202034-502d2d690317/go.mod h1:OIezDfdzOgFhuw4HuWapWq2e9l0H9tK4F1j+ETRtF3k=
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *Alpine) Version(ctx context.Context) (version string, err error) {
|
func (a *Alpine) Version(context.Context) (version string, err error) {
|
||||||
file, err := os.OpenFile(a.alpineReleasePath, os.O_RDONLY, 0)
|
file, err := os.OpenFile(a.alpineReleasePath, os.O_RDONLY, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ package cli
|
|||||||
|
|
||||||
import "context"
|
import "context"
|
||||||
|
|
||||||
func (c *CLI) CI(context context.Context) error {
|
func (c *CLI) CI(context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ func (c *CLI) FormatServers(args []string) error {
|
|||||||
}
|
}
|
||||||
switch len(providers) {
|
switch len(providers) {
|
||||||
case 0:
|
case 0:
|
||||||
return ErrProviderUnspecified
|
return fmt.Errorf("%w", ErrProviderUnspecified)
|
||||||
case 1:
|
case 1:
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("%w: %d specified: %s",
|
return fmt.Errorf("%w: %d specified: %s",
|
||||||
@@ -73,7 +73,7 @@ func (c *CLI) FormatServers(args []string) error {
|
|||||||
logger := newNoopLogger()
|
logger := newNoopLogger()
|
||||||
storage, err := storage.New(logger, constants.ServersData)
|
storage, err := storage.New(logger, constants.ServersData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot create servers storage: %w", err)
|
return fmt.Errorf("creating servers storage: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
formatted := storage.FormatToMarkdown(providerToFormat)
|
formatted := storage.FormatToMarkdown(providerToFormat)
|
||||||
@@ -81,18 +81,18 @@ func (c *CLI) FormatServers(args []string) error {
|
|||||||
output = filepath.Clean(output)
|
output = filepath.Clean(output)
|
||||||
file, err := os.OpenFile(output, os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0644)
|
file, err := os.OpenFile(output, os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot open output file: %w", err)
|
return fmt.Errorf("opening output file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = fmt.Fprint(file, formatted)
|
_, err = fmt.Fprint(file, formatted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = file.Close()
|
_ = file.Close()
|
||||||
return fmt.Errorf("cannot write to output file: %w", err)
|
return fmt.Errorf("writing to output file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = file.Close()
|
err = file.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot close output file: %w", err)
|
return fmt.Errorf("closing output file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -9,13 +9,15 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/healthcheck"
|
"github.com/qdm12/gluetun/internal/healthcheck"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *CLI) HealthCheck(ctx context.Context, source Source, warner Warner) error {
|
func (c *CLI) HealthCheck(ctx context.Context, source Source, _ Warner) error {
|
||||||
// Extract the health server port from the configuration.
|
// Extract the health server port from the configuration.
|
||||||
config, err := source.ReadHealth()
|
config, err := source.ReadHealth()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.SetDefaults()
|
||||||
|
|
||||||
err = config.Validate()
|
err = config.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ func newNoopLogger() *noopLogger {
|
|||||||
return new(noopLogger)
|
return new(noopLogger)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *noopLogger) Debug(s string) {}
|
func (l *noopLogger) Debug(string) {}
|
||||||
func (l *noopLogger) Info(s string) {}
|
func (l *noopLogger) Info(string) {}
|
||||||
func (l *noopLogger) Warn(s string) {}
|
func (l *noopLogger) Warn(string) {}
|
||||||
func (l *noopLogger) Error(s string) {}
|
func (l *noopLogger) Error(string) {}
|
||||||
func (l *noopLogger) PatchLevel(level logging.Level) {}
|
func (l *noopLogger) PatchLevel(logging.Level) {}
|
||||||
func (l *noopLogger) PatchPrefix(prefix string) {}
|
func (l *noopLogger) PatchPrefix(string) {}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -28,11 +28,11 @@ type Unzipper interface {
|
|||||||
|
|
||||||
type ParallelResolver interface {
|
type ParallelResolver interface {
|
||||||
Resolve(ctx context.Context, settings resolver.ParallelSettings) (
|
Resolve(ctx context.Context, settings resolver.ParallelSettings) (
|
||||||
hostToIPs map[string][]net.IP, warnings []string, err error)
|
hostToIPs map[string][]netip.Addr, warnings []string, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type IPFetcher interface {
|
type IPFetcher interface {
|
||||||
FetchMultiInfo(ctx context.Context, ips []net.IP) (data []ipinfo.Response, err error)
|
FetchMultiInfo(ctx context.Context, ips []netip.Addr) (data []ipinfo.Response, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type IPv6Checker interface {
|
type IPv6Checker interface {
|
||||||
@@ -51,15 +51,15 @@ func (c *CLI) OpenvpnConfig(logger OpenvpnConfigLogger, source Source,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = allSettings.Validate(storage); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ipv6Supported, err := ipv6Checker.IsIPv6Supported()
|
ipv6Supported, err := ipv6Checker.IsIPv6Supported()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("checking for IPv6 support: %w", err)
|
return fmt.Errorf("checking for IPv6 support: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = allSettings.Validate(storage, ipv6Supported); err != nil {
|
||||||
|
return fmt.Errorf("validating settings: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Unused by this CLI command
|
// Unused by this CLI command
|
||||||
unzipper := (Unzipper)(nil)
|
unzipper := (Unzipper)(nil)
|
||||||
client := (*http.Client)(nil)
|
client := (*http.Client)(nil)
|
||||||
|
|||||||
@@ -51,14 +51,14 @@ func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !endUserMode && !maintainerMode {
|
if !endUserMode && !maintainerMode {
|
||||||
return ErrModeUnspecified
|
return fmt.Errorf("%w", ErrModeUnspecified)
|
||||||
}
|
}
|
||||||
|
|
||||||
if updateAll {
|
if updateAll {
|
||||||
options.Providers = providers.All()
|
options.Providers = providers.All()
|
||||||
} else {
|
} else {
|
||||||
if csvProviders == "" {
|
if csvProviders == "" {
|
||||||
return ErrNoProviderSpecified
|
return fmt.Errorf("%w", ErrNoProviderSpecified)
|
||||||
}
|
}
|
||||||
options.Providers = strings.Split(csvProviders, ",")
|
options.Providers = strings.Split(csvProviders, ",")
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) e
|
|||||||
|
|
||||||
storage, err := storage.New(logger, constants.ServersData)
|
storage, err := storage.New(logger, constants.ServersData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot create servers storage: %w", err)
|
return fmt.Errorf("creating servers storage: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientTimeout = 10 * time.Second
|
const clientTimeout = 10 * time.Second
|
||||||
@@ -88,13 +88,13 @@ func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) e
|
|||||||
updater := updater.New(httpClient, storage, providers, logger)
|
updater := updater.New(httpClient, storage, providers, logger)
|
||||||
err = updater.UpdateServers(ctx, options.Providers, options.MinRatio)
|
err = updater.UpdateServers(ctx, options.Providers, options.MinRatio)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot update server information: %w", err)
|
return fmt.Errorf("updating server information: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if maintainerMode {
|
if maintainerMode {
|
||||||
err := storage.FlushToFile(c.repoServersPath)
|
err := storage.FlushToFile(c.repoServersPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot write servers data to embedded JSON file: %w", err)
|
return fmt.Errorf("writing servers data to embedded JSON file: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package settings
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
@@ -13,9 +13,9 @@ type DNS struct {
|
|||||||
// ServerAddress is the DNS server to use inside
|
// ServerAddress is the DNS server to use inside
|
||||||
// the Go program and for the system.
|
// the Go program and for the system.
|
||||||
// It defaults to '127.0.0.1' to be used with the
|
// It defaults to '127.0.0.1' to be used with the
|
||||||
// DoT server. It cannot be nil in the internal
|
// DoT server. It cannot be the zero value in the internal
|
||||||
// state.
|
// state.
|
||||||
ServerAddress net.IP
|
ServerAddress netip.Addr
|
||||||
// KeepNameserver is true if the Docker DNS server
|
// KeepNameserver is true if the Docker DNS server
|
||||||
// found in /etc/resolv.conf should be kept.
|
// found in /etc/resolv.conf should be kept.
|
||||||
// Note settings this to true will go around the
|
// Note settings this to true will go around the
|
||||||
@@ -31,7 +31,7 @@ type DNS struct {
|
|||||||
func (d DNS) validate() (err error) {
|
func (d DNS) validate() (err error) {
|
||||||
err = d.DoT.validate()
|
err = d.DoT.validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed validating DoT settings: %w", err)
|
return fmt.Errorf("validating DoT settings: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -39,8 +39,8 @@ func (d DNS) validate() (err error) {
|
|||||||
|
|
||||||
func (d *DNS) Copy() (copied DNS) {
|
func (d *DNS) Copy() (copied DNS) {
|
||||||
return DNS{
|
return DNS{
|
||||||
ServerAddress: helpers.CopyIP(d.ServerAddress),
|
ServerAddress: d.ServerAddress,
|
||||||
KeepNameserver: helpers.CopyBoolPtr(d.KeepNameserver),
|
KeepNameserver: helpers.CopyPointer(d.KeepNameserver),
|
||||||
DoT: d.DoT.copy(),
|
DoT: d.DoT.copy(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ func (d *DNS) Copy() (copied DNS) {
|
|||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (d *DNS) mergeWith(other DNS) {
|
func (d *DNS) mergeWith(other DNS) {
|
||||||
d.ServerAddress = helpers.MergeWithIP(d.ServerAddress, other.ServerAddress)
|
d.ServerAddress = helpers.MergeWithIP(d.ServerAddress, other.ServerAddress)
|
||||||
d.KeepNameserver = helpers.MergeWithBool(d.KeepNameserver, other.KeepNameserver)
|
d.KeepNameserver = helpers.MergeWithPointer(d.KeepNameserver, other.KeepNameserver)
|
||||||
d.DoT.mergeWith(other.DoT)
|
d.DoT.mergeWith(other.DoT)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,14 +58,14 @@ func (d *DNS) mergeWith(other DNS) {
|
|||||||
// settings.
|
// settings.
|
||||||
func (d *DNS) overrideWith(other DNS) {
|
func (d *DNS) overrideWith(other DNS) {
|
||||||
d.ServerAddress = helpers.OverrideWithIP(d.ServerAddress, other.ServerAddress)
|
d.ServerAddress = helpers.OverrideWithIP(d.ServerAddress, other.ServerAddress)
|
||||||
d.KeepNameserver = helpers.OverrideWithBool(d.KeepNameserver, other.KeepNameserver)
|
d.KeepNameserver = helpers.OverrideWithPointer(d.KeepNameserver, other.KeepNameserver)
|
||||||
d.DoT.overrideWith(other.DoT)
|
d.DoT.overrideWith(other.DoT)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNS) setDefaults() {
|
func (d *DNS) setDefaults() {
|
||||||
localhost := net.IPv4(127, 0, 0, 1) //nolint:gomnd
|
localhost := netip.AddrFrom4([4]byte{127, 0, 0, 1})
|
||||||
d.ServerAddress = helpers.DefaultIP(d.ServerAddress, localhost)
|
d.ServerAddress = helpers.DefaultIP(d.ServerAddress, localhost)
|
||||||
d.KeepNameserver = helpers.DefaultBool(d.KeepNameserver, false)
|
d.KeepNameserver = helpers.DefaultPointer(d.KeepNameserver, false)
|
||||||
d.DoT.setDefaults()
|
d.DoT.setDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ package settings
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/qdm12/dns/pkg/blacklist"
|
"github.com/qdm12/dns/pkg/blacklist"
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
"inet.af/netaddr"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// DNSBlacklist is settings for the DNS blacklist building.
|
// DNSBlacklist is settings for the DNS blacklist building.
|
||||||
@@ -18,14 +18,14 @@ type DNSBlacklist struct {
|
|||||||
BlockSurveillance *bool
|
BlockSurveillance *bool
|
||||||
AllowedHosts []string
|
AllowedHosts []string
|
||||||
AddBlockedHosts []string
|
AddBlockedHosts []string
|
||||||
AddBlockedIPs []netaddr.IP
|
AddBlockedIPs []netip.Addr
|
||||||
AddBlockedIPPrefixes []netaddr.IPPrefix
|
AddBlockedIPPrefixes []netip.Prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *DNSBlacklist) setDefaults() {
|
func (b *DNSBlacklist) setDefaults() {
|
||||||
b.BlockMalicious = helpers.DefaultBool(b.BlockMalicious, true)
|
b.BlockMalicious = helpers.DefaultPointer(b.BlockMalicious, true)
|
||||||
b.BlockAds = helpers.DefaultBool(b.BlockAds, false)
|
b.BlockAds = helpers.DefaultPointer(b.BlockAds, false)
|
||||||
b.BlockSurveillance = helpers.DefaultBool(b.BlockSurveillance, true)
|
b.BlockSurveillance = helpers.DefaultPointer(b.BlockSurveillance, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostRegex = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9_][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9_])(\.([a-zA-Z0-9]|[a-zA-Z0-9_][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9]))*$`) //nolint:lll
|
var hostRegex = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9_][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9_])(\.([a-zA-Z0-9]|[a-zA-Z0-9_][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9]))*$`) //nolint:lll
|
||||||
@@ -53,34 +53,34 @@ func (b DNSBlacklist) validate() (err error) {
|
|||||||
|
|
||||||
func (b DNSBlacklist) copy() (copied DNSBlacklist) {
|
func (b DNSBlacklist) copy() (copied DNSBlacklist) {
|
||||||
return DNSBlacklist{
|
return DNSBlacklist{
|
||||||
BlockMalicious: helpers.CopyBoolPtr(b.BlockMalicious),
|
BlockMalicious: helpers.CopyPointer(b.BlockMalicious),
|
||||||
BlockAds: helpers.CopyBoolPtr(b.BlockAds),
|
BlockAds: helpers.CopyPointer(b.BlockAds),
|
||||||
BlockSurveillance: helpers.CopyBoolPtr(b.BlockSurveillance),
|
BlockSurveillance: helpers.CopyPointer(b.BlockSurveillance),
|
||||||
AllowedHosts: helpers.CopyStringSlice(b.AllowedHosts),
|
AllowedHosts: helpers.CopySlice(b.AllowedHosts),
|
||||||
AddBlockedHosts: helpers.CopyStringSlice(b.AddBlockedHosts),
|
AddBlockedHosts: helpers.CopySlice(b.AddBlockedHosts),
|
||||||
AddBlockedIPs: helpers.CopyNetaddrIPsSlice(b.AddBlockedIPs),
|
AddBlockedIPs: helpers.CopySlice(b.AddBlockedIPs),
|
||||||
AddBlockedIPPrefixes: helpers.CopyIPPrefixSlice(b.AddBlockedIPPrefixes),
|
AddBlockedIPPrefixes: helpers.CopySlice(b.AddBlockedIPPrefixes),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *DNSBlacklist) mergeWith(other DNSBlacklist) {
|
func (b *DNSBlacklist) mergeWith(other DNSBlacklist) {
|
||||||
b.BlockMalicious = helpers.MergeWithBool(b.BlockMalicious, other.BlockMalicious)
|
b.BlockMalicious = helpers.MergeWithPointer(b.BlockMalicious, other.BlockMalicious)
|
||||||
b.BlockAds = helpers.MergeWithBool(b.BlockAds, other.BlockAds)
|
b.BlockAds = helpers.MergeWithPointer(b.BlockAds, other.BlockAds)
|
||||||
b.BlockSurveillance = helpers.MergeWithBool(b.BlockSurveillance, other.BlockSurveillance)
|
b.BlockSurveillance = helpers.MergeWithPointer(b.BlockSurveillance, other.BlockSurveillance)
|
||||||
b.AllowedHosts = helpers.MergeStringSlices(b.AllowedHosts, other.AllowedHosts)
|
b.AllowedHosts = helpers.MergeSlices(b.AllowedHosts, other.AllowedHosts)
|
||||||
b.AddBlockedHosts = helpers.MergeStringSlices(b.AddBlockedHosts, other.AddBlockedHosts)
|
b.AddBlockedHosts = helpers.MergeSlices(b.AddBlockedHosts, other.AddBlockedHosts)
|
||||||
b.AddBlockedIPs = helpers.MergeNetaddrIPsSlices(b.AddBlockedIPs, other.AddBlockedIPs)
|
b.AddBlockedIPs = helpers.MergeSlices(b.AddBlockedIPs, other.AddBlockedIPs)
|
||||||
b.AddBlockedIPPrefixes = helpers.MergeIPPrefixesSlices(b.AddBlockedIPPrefixes, other.AddBlockedIPPrefixes)
|
b.AddBlockedIPPrefixes = helpers.MergeSlices(b.AddBlockedIPPrefixes, other.AddBlockedIPPrefixes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *DNSBlacklist) overrideWith(other DNSBlacklist) {
|
func (b *DNSBlacklist) overrideWith(other DNSBlacklist) {
|
||||||
b.BlockMalicious = helpers.OverrideWithBool(b.BlockMalicious, other.BlockMalicious)
|
b.BlockMalicious = helpers.OverrideWithPointer(b.BlockMalicious, other.BlockMalicious)
|
||||||
b.BlockAds = helpers.OverrideWithBool(b.BlockAds, other.BlockAds)
|
b.BlockAds = helpers.OverrideWithPointer(b.BlockAds, other.BlockAds)
|
||||||
b.BlockSurveillance = helpers.OverrideWithBool(b.BlockSurveillance, other.BlockSurveillance)
|
b.BlockSurveillance = helpers.OverrideWithPointer(b.BlockSurveillance, other.BlockSurveillance)
|
||||||
b.AllowedHosts = helpers.OverrideWithStringSlice(b.AllowedHosts, other.AllowedHosts)
|
b.AllowedHosts = helpers.OverrideWithSlice(b.AllowedHosts, other.AllowedHosts)
|
||||||
b.AddBlockedHosts = helpers.OverrideWithStringSlice(b.AddBlockedHosts, other.AddBlockedHosts)
|
b.AddBlockedHosts = helpers.OverrideWithSlice(b.AddBlockedHosts, other.AddBlockedHosts)
|
||||||
b.AddBlockedIPs = helpers.OverrideWithNetaddrIPsSlice(b.AddBlockedIPs, other.AddBlockedIPs)
|
b.AddBlockedIPs = helpers.OverrideWithSlice(b.AddBlockedIPs, other.AddBlockedIPs)
|
||||||
b.AddBlockedIPPrefixes = helpers.OverrideWithIPPrefixesSlice(b.AddBlockedIPPrefixes, other.AddBlockedIPPrefixes)
|
b.AddBlockedIPPrefixes = helpers.OverrideWithSlice(b.AddBlockedIPPrefixes, other.AddBlockedIPPrefixes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b DNSBlacklist) ToBlacklistFormat() (settings blacklist.BuilderSettings, err error) {
|
func (b DNSBlacklist) ToBlacklistFormat() (settings blacklist.BuilderSettings, err error) {
|
||||||
@@ -90,8 +90,8 @@ func (b DNSBlacklist) ToBlacklistFormat() (settings blacklist.BuilderSettings, e
|
|||||||
BlockSurveillance: *b.BlockSurveillance,
|
BlockSurveillance: *b.BlockSurveillance,
|
||||||
AllowedHosts: b.AllowedHosts,
|
AllowedHosts: b.AllowedHosts,
|
||||||
AddBlockedHosts: b.AddBlockedHosts,
|
AddBlockedHosts: b.AddBlockedHosts,
|
||||||
AddBlockedIPs: b.AddBlockedIPs,
|
AddBlockedIPs: netipAddressesToNetaddrIPs(b.AddBlockedIPs),
|
||||||
AddBlockedIPPrefixes: b.AddBlockedIPPrefixes,
|
AddBlockedIPPrefixes: netipPrefixesToNetaddrIPPrefixes(b.AddBlockedIPPrefixes),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ func (d DoT) validate() (err error) {
|
|||||||
|
|
||||||
func (d *DoT) copy() (copied DoT) {
|
func (d *DoT) copy() (copied DoT) {
|
||||||
return DoT{
|
return DoT{
|
||||||
Enabled: helpers.CopyBoolPtr(d.Enabled),
|
Enabled: helpers.CopyPointer(d.Enabled),
|
||||||
UpdatePeriod: helpers.CopyDurationPtr(d.UpdatePeriod),
|
UpdatePeriod: helpers.CopyPointer(d.UpdatePeriod),
|
||||||
Unbound: d.Unbound.copy(),
|
Unbound: d.Unbound.copy(),
|
||||||
Blacklist: d.Blacklist.copy(),
|
Blacklist: d.Blacklist.copy(),
|
||||||
}
|
}
|
||||||
@@ -64,8 +64,8 @@ func (d *DoT) copy() (copied DoT) {
|
|||||||
// mergeWith merges the other settings into any
|
// mergeWith merges the other settings into any
|
||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (d *DoT) mergeWith(other DoT) {
|
func (d *DoT) mergeWith(other DoT) {
|
||||||
d.Enabled = helpers.MergeWithBool(d.Enabled, other.Enabled)
|
d.Enabled = helpers.MergeWithPointer(d.Enabled, other.Enabled)
|
||||||
d.UpdatePeriod = helpers.MergeWithDurationPtr(d.UpdatePeriod, other.UpdatePeriod)
|
d.UpdatePeriod = helpers.MergeWithPointer(d.UpdatePeriod, other.UpdatePeriod)
|
||||||
d.Unbound.mergeWith(other.Unbound)
|
d.Unbound.mergeWith(other.Unbound)
|
||||||
d.Blacklist.mergeWith(other.Blacklist)
|
d.Blacklist.mergeWith(other.Blacklist)
|
||||||
}
|
}
|
||||||
@@ -74,16 +74,16 @@ func (d *DoT) mergeWith(other DoT) {
|
|||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (d *DoT) overrideWith(other DoT) {
|
func (d *DoT) overrideWith(other DoT) {
|
||||||
d.Enabled = helpers.OverrideWithBool(d.Enabled, other.Enabled)
|
d.Enabled = helpers.OverrideWithPointer(d.Enabled, other.Enabled)
|
||||||
d.UpdatePeriod = helpers.OverrideWithDurationPtr(d.UpdatePeriod, other.UpdatePeriod)
|
d.UpdatePeriod = helpers.OverrideWithPointer(d.UpdatePeriod, other.UpdatePeriod)
|
||||||
d.Unbound.overrideWith(other.Unbound)
|
d.Unbound.overrideWith(other.Unbound)
|
||||||
d.Blacklist.overrideWith(other.Blacklist)
|
d.Blacklist.overrideWith(other.Blacklist)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DoT) setDefaults() {
|
func (d *DoT) setDefaults() {
|
||||||
d.Enabled = helpers.DefaultBool(d.Enabled, true)
|
d.Enabled = helpers.DefaultPointer(d.Enabled, true)
|
||||||
const defaultUpdatePeriod = 24 * time.Hour
|
const defaultUpdatePeriod = 24 * time.Hour
|
||||||
d.UpdatePeriod = helpers.DefaultDurationPtr(d.UpdatePeriod, defaultUpdatePeriod)
|
d.UpdatePeriod = helpers.DefaultPointer(d.UpdatePeriod, defaultUpdatePeriod)
|
||||||
d.Unbound.setDefaults()
|
d.Unbound.setDefaults()
|
||||||
d.Blacklist.setDefaults()
|
d.Blacklist.setDefaults()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,9 +39,11 @@ var (
|
|||||||
ErrWireguardEndpointPortNotSet = errors.New("endpoint port is not set")
|
ErrWireguardEndpointPortNotSet = errors.New("endpoint port is not set")
|
||||||
ErrWireguardEndpointPortSet = errors.New("endpoint port is set")
|
ErrWireguardEndpointPortSet = errors.New("endpoint port is set")
|
||||||
ErrWireguardInterfaceAddressNotSet = errors.New("interface address is not set")
|
ErrWireguardInterfaceAddressNotSet = errors.New("interface address is not set")
|
||||||
|
ErrWireguardInterfaceAddressIPv6 = errors.New("interface address is IPv6 but IPv6 is not supported")
|
||||||
ErrWireguardInterfaceNotValid = errors.New("interface name is not valid")
|
ErrWireguardInterfaceNotValid = errors.New("interface name is not valid")
|
||||||
ErrWireguardPreSharedKeyNotSet = errors.New("pre-shared key is not set")
|
ErrWireguardPreSharedKeyNotSet = errors.New("pre-shared key is not set")
|
||||||
ErrWireguardPrivateKeyNotSet = errors.New("private key is not set")
|
ErrWireguardPrivateKeyNotSet = errors.New("private key is not set")
|
||||||
ErrWireguardPublicKeyNotSet = errors.New("public key is not set")
|
ErrWireguardPublicKeyNotSet = errors.New("public key is not set")
|
||||||
ErrWireguardPublicKeyNotValid = errors.New("public key is not valid")
|
ErrWireguardPublicKeyNotValid = errors.New("public key is not valid")
|
||||||
|
ErrWireguardImplementationNotValid = errors.New("implementation is not valid")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package settings
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
type Firewall struct {
|
type Firewall struct {
|
||||||
VPNInputPorts []uint16
|
VPNInputPorts []uint16
|
||||||
InputPorts []uint16
|
InputPorts []uint16
|
||||||
OutboundSubnets []net.IPNet
|
OutboundSubnets []netip.Prefix
|
||||||
Enabled *bool
|
Enabled *bool
|
||||||
Debug *bool
|
Debug *bool
|
||||||
}
|
}
|
||||||
@@ -40,11 +40,11 @@ func hasZeroPort(ports []uint16) (has bool) {
|
|||||||
|
|
||||||
func (f *Firewall) copy() (copied Firewall) {
|
func (f *Firewall) copy() (copied Firewall) {
|
||||||
return Firewall{
|
return Firewall{
|
||||||
VPNInputPorts: helpers.CopyUint16Slice(f.VPNInputPorts),
|
VPNInputPorts: helpers.CopySlice(f.VPNInputPorts),
|
||||||
InputPorts: helpers.CopyUint16Slice(f.InputPorts),
|
InputPorts: helpers.CopySlice(f.InputPorts),
|
||||||
OutboundSubnets: helpers.CopyIPNetSlice(f.OutboundSubnets),
|
OutboundSubnets: helpers.CopySlice(f.OutboundSubnets),
|
||||||
Enabled: helpers.CopyBoolPtr(f.Enabled),
|
Enabled: helpers.CopyPointer(f.Enabled),
|
||||||
Debug: helpers.CopyBoolPtr(f.Debug),
|
Debug: helpers.CopyPointer(f.Debug),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,27 +53,27 @@ func (f *Firewall) copy() (copied Firewall) {
|
|||||||
// It merges values of slices together, even if they
|
// It merges values of slices together, even if they
|
||||||
// are set in the receiver settings.
|
// are set in the receiver settings.
|
||||||
func (f *Firewall) mergeWith(other Firewall) {
|
func (f *Firewall) mergeWith(other Firewall) {
|
||||||
f.VPNInputPorts = helpers.MergeUint16Slices(f.VPNInputPorts, other.VPNInputPorts)
|
f.VPNInputPorts = helpers.MergeSlices(f.VPNInputPorts, other.VPNInputPorts)
|
||||||
f.InputPorts = helpers.MergeUint16Slices(f.InputPorts, other.InputPorts)
|
f.InputPorts = helpers.MergeSlices(f.InputPorts, other.InputPorts)
|
||||||
f.OutboundSubnets = helpers.MergeIPNetsSlices(f.OutboundSubnets, other.OutboundSubnets)
|
f.OutboundSubnets = helpers.MergeSlices(f.OutboundSubnets, other.OutboundSubnets)
|
||||||
f.Enabled = helpers.MergeWithBool(f.Enabled, other.Enabled)
|
f.Enabled = helpers.MergeWithPointer(f.Enabled, other.Enabled)
|
||||||
f.Debug = helpers.MergeWithBool(f.Debug, other.Debug)
|
f.Debug = helpers.MergeWithPointer(f.Debug, other.Debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (f *Firewall) overrideWith(other Firewall) {
|
func (f *Firewall) overrideWith(other Firewall) {
|
||||||
f.VPNInputPorts = helpers.OverrideWithUint16Slice(f.VPNInputPorts, other.VPNInputPorts)
|
f.VPNInputPorts = helpers.OverrideWithSlice(f.VPNInputPorts, other.VPNInputPorts)
|
||||||
f.InputPorts = helpers.OverrideWithUint16Slice(f.InputPorts, other.InputPorts)
|
f.InputPorts = helpers.OverrideWithSlice(f.InputPorts, other.InputPorts)
|
||||||
f.OutboundSubnets = helpers.OverrideWithIPNetsSlice(f.OutboundSubnets, other.OutboundSubnets)
|
f.OutboundSubnets = helpers.OverrideWithSlice(f.OutboundSubnets, other.OutboundSubnets)
|
||||||
f.Enabled = helpers.OverrideWithBool(f.Enabled, other.Enabled)
|
f.Enabled = helpers.OverrideWithPointer(f.Enabled, other.Enabled)
|
||||||
f.Debug = helpers.OverrideWithBool(f.Debug, other.Debug)
|
f.Debug = helpers.OverrideWithPointer(f.Debug, other.Debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Firewall) setDefaults() {
|
func (f *Firewall) setDefaults() {
|
||||||
f.Enabled = helpers.DefaultBool(f.Enabled, true)
|
f.Enabled = helpers.DefaultPointer(f.Enabled, true)
|
||||||
f.Debug = helpers.DefaultBool(f.Debug, false)
|
f.Debug = helpers.DefaultPointer(f.Debug, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Firewall) String() string {
|
func (f Firewall) String() string {
|
||||||
@@ -109,7 +109,8 @@ func (f Firewall) toLinesNode() (node *gotree.Node) {
|
|||||||
if len(f.OutboundSubnets) > 0 {
|
if len(f.OutboundSubnets) > 0 {
|
||||||
outboundSubnets := node.Appendf("Outbound subnets:")
|
outboundSubnets := node.Appendf("Outbound subnets:")
|
||||||
for _, subnet := range f.OutboundSubnets {
|
for _, subnet := range f.OutboundSubnets {
|
||||||
outboundSubnets.Appendf("%s", subnet)
|
subnet := subnet
|
||||||
|
outboundSubnets.Appendf("%s", &subnet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ type Health struct {
|
|||||||
// to TCP dial to periodically for the health check.
|
// to TCP dial to periodically for the health check.
|
||||||
// It cannot be the empty string in the internal state.
|
// It cannot be the empty string in the internal state.
|
||||||
TargetAddress string
|
TargetAddress string
|
||||||
|
// SuccessWait is the duration to wait to re-run the
|
||||||
|
// healthcheck after a successful healthcheck.
|
||||||
|
// It defaults to 5 seconds and cannot be zero in
|
||||||
|
// the internal state.
|
||||||
|
SuccessWait time.Duration
|
||||||
|
// VPN has health settings specific to the VPN loop.
|
||||||
VPN HealthyWait
|
VPN HealthyWait
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +57,7 @@ func (h *Health) copy() (copied Health) {
|
|||||||
ReadHeaderTimeout: h.ReadHeaderTimeout,
|
ReadHeaderTimeout: h.ReadHeaderTimeout,
|
||||||
ReadTimeout: h.ReadTimeout,
|
ReadTimeout: h.ReadTimeout,
|
||||||
TargetAddress: h.TargetAddress,
|
TargetAddress: h.TargetAddress,
|
||||||
|
SuccessWait: h.SuccessWait,
|
||||||
VPN: h.VPN.copy(),
|
VPN: h.VPN.copy(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,9 +66,10 @@ func (h *Health) copy() (copied Health) {
|
|||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (h *Health) MergeWith(other Health) {
|
func (h *Health) MergeWith(other Health) {
|
||||||
h.ServerAddress = helpers.MergeWithString(h.ServerAddress, other.ServerAddress)
|
h.ServerAddress = helpers.MergeWithString(h.ServerAddress, other.ServerAddress)
|
||||||
h.ReadHeaderTimeout = helpers.MergeWithDuration(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
h.ReadHeaderTimeout = helpers.MergeWithNumber(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
||||||
h.ReadTimeout = helpers.MergeWithDuration(h.ReadTimeout, other.ReadTimeout)
|
h.ReadTimeout = helpers.MergeWithNumber(h.ReadTimeout, other.ReadTimeout)
|
||||||
h.TargetAddress = helpers.MergeWithString(h.TargetAddress, other.TargetAddress)
|
h.TargetAddress = helpers.MergeWithString(h.TargetAddress, other.TargetAddress)
|
||||||
|
h.SuccessWait = helpers.MergeWithNumber(h.SuccessWait, other.SuccessWait)
|
||||||
h.VPN.mergeWith(other.VPN)
|
h.VPN.mergeWith(other.VPN)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,19 +78,22 @@ func (h *Health) MergeWith(other Health) {
|
|||||||
// settings.
|
// settings.
|
||||||
func (h *Health) OverrideWith(other Health) {
|
func (h *Health) OverrideWith(other Health) {
|
||||||
h.ServerAddress = helpers.OverrideWithString(h.ServerAddress, other.ServerAddress)
|
h.ServerAddress = helpers.OverrideWithString(h.ServerAddress, other.ServerAddress)
|
||||||
h.ReadHeaderTimeout = helpers.OverrideWithDuration(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
h.ReadHeaderTimeout = helpers.OverrideWithNumber(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
||||||
h.ReadTimeout = helpers.OverrideWithDuration(h.ReadTimeout, other.ReadTimeout)
|
h.ReadTimeout = helpers.OverrideWithNumber(h.ReadTimeout, other.ReadTimeout)
|
||||||
h.TargetAddress = helpers.OverrideWithString(h.TargetAddress, other.TargetAddress)
|
h.TargetAddress = helpers.OverrideWithString(h.TargetAddress, other.TargetAddress)
|
||||||
|
h.SuccessWait = helpers.OverrideWithNumber(h.SuccessWait, other.SuccessWait)
|
||||||
h.VPN.overrideWith(other.VPN)
|
h.VPN.overrideWith(other.VPN)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Health) SetDefaults() {
|
func (h *Health) SetDefaults() {
|
||||||
h.ServerAddress = helpers.DefaultString(h.ServerAddress, "127.0.0.1:9999")
|
h.ServerAddress = helpers.DefaultString(h.ServerAddress, "127.0.0.1:9999")
|
||||||
const defaultReadHeaderTimeout = 100 * time.Millisecond
|
const defaultReadHeaderTimeout = 100 * time.Millisecond
|
||||||
h.ReadHeaderTimeout = helpers.DefaultDuration(h.ReadHeaderTimeout, defaultReadHeaderTimeout)
|
h.ReadHeaderTimeout = helpers.DefaultNumber(h.ReadHeaderTimeout, defaultReadHeaderTimeout)
|
||||||
const defaultReadTimeout = 500 * time.Millisecond
|
const defaultReadTimeout = 500 * time.Millisecond
|
||||||
h.ReadTimeout = helpers.DefaultDuration(h.ReadTimeout, defaultReadTimeout)
|
h.ReadTimeout = helpers.DefaultNumber(h.ReadTimeout, defaultReadTimeout)
|
||||||
h.TargetAddress = helpers.DefaultString(h.TargetAddress, "cloudflare.com:443")
|
h.TargetAddress = helpers.DefaultString(h.TargetAddress, "cloudflare.com:443")
|
||||||
|
const defaultSuccessWait = 5 * time.Second
|
||||||
|
h.SuccessWait = helpers.DefaultNumber(h.SuccessWait, defaultSuccessWait)
|
||||||
h.VPN.setDefaults()
|
h.VPN.setDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +105,7 @@ func (h Health) toLinesNode() (node *gotree.Node) {
|
|||||||
node = gotree.New("Health settings:")
|
node = gotree.New("Health settings:")
|
||||||
node.Appendf("Server listening address: %s", h.ServerAddress)
|
node.Appendf("Server listening address: %s", h.ServerAddress)
|
||||||
node.Appendf("Target address: %s", h.TargetAddress)
|
node.Appendf("Target address: %s", h.TargetAddress)
|
||||||
|
node.Appendf("Duration to wait after success: %s", h.SuccessWait)
|
||||||
node.Appendf("Read header timeout: %s", h.ReadHeaderTimeout)
|
node.Appendf("Read header timeout: %s", h.ReadHeaderTimeout)
|
||||||
node.Appendf("Read timeout: %s", h.ReadTimeout)
|
node.Appendf("Read timeout: %s", h.ReadTimeout)
|
||||||
node.AppendNode(h.VPN.toLinesNode("VPN"))
|
node.AppendNode(h.VPN.toLinesNode("VPN"))
|
||||||
|
|||||||
@@ -27,31 +27,31 @@ func (h HealthyWait) validate() (err error) {
|
|||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (h *HealthyWait) copy() (copied HealthyWait) {
|
func (h *HealthyWait) copy() (copied HealthyWait) {
|
||||||
return HealthyWait{
|
return HealthyWait{
|
||||||
Initial: helpers.CopyDurationPtr(h.Initial),
|
Initial: helpers.CopyPointer(h.Initial),
|
||||||
Addition: helpers.CopyDurationPtr(h.Addition),
|
Addition: helpers.CopyPointer(h.Addition),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
// mergeWith merges the other settings into any
|
||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (h *HealthyWait) mergeWith(other HealthyWait) {
|
func (h *HealthyWait) mergeWith(other HealthyWait) {
|
||||||
h.Initial = helpers.MergeWithDurationPtr(h.Initial, other.Initial)
|
h.Initial = helpers.MergeWithPointer(h.Initial, other.Initial)
|
||||||
h.Addition = helpers.MergeWithDurationPtr(h.Addition, other.Addition)
|
h.Addition = helpers.MergeWithPointer(h.Addition, other.Addition)
|
||||||
}
|
}
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (h *HealthyWait) overrideWith(other HealthyWait) {
|
func (h *HealthyWait) overrideWith(other HealthyWait) {
|
||||||
h.Initial = helpers.OverrideWithDurationPtr(h.Initial, other.Initial)
|
h.Initial = helpers.OverrideWithPointer(h.Initial, other.Initial)
|
||||||
h.Addition = helpers.OverrideWithDurationPtr(h.Addition, other.Addition)
|
h.Addition = helpers.OverrideWithPointer(h.Addition, other.Addition)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HealthyWait) setDefaults() {
|
func (h *HealthyWait) setDefaults() {
|
||||||
const initialDurationDefault = 6 * time.Second
|
const initialDurationDefault = 6 * time.Second
|
||||||
const additionDurationDefault = 5 * time.Second
|
const additionDurationDefault = 5 * time.Second
|
||||||
h.Initial = helpers.DefaultDurationPtr(h.Initial, initialDurationDefault)
|
h.Initial = helpers.DefaultPointer(h.Initial, initialDurationDefault)
|
||||||
h.Addition = helpers.DefaultDurationPtr(h.Addition, additionDurationDefault)
|
h.Addition = helpers.DefaultPointer(h.Addition, additionDurationDefault)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h HealthyWait) String() string {
|
func (h HealthyWait) String() string {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ var (
|
|||||||
|
|
||||||
func AreAllOneOf(values, choices []string) (err error) {
|
func AreAllOneOf(values, choices []string) (err error) {
|
||||||
if len(values) > 0 && len(choices) == 0 {
|
if len(values) > 0 && len(choices) == 0 {
|
||||||
return ErrNoChoice
|
return fmt.Errorf("%w", ErrNoChoice)
|
||||||
}
|
}
|
||||||
|
|
||||||
set := make(map[string]struct{}, len(choices))
|
set := make(map[string]struct{}, len(choices))
|
||||||
|
|||||||
@@ -1,199 +1,20 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net/netip"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/qdm12/log"
|
"golang.org/x/exp/slices"
|
||||||
"inet.af/netaddr"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func CopyStringPtr(original *string) (copied *string) {
|
func CopyPointer[T any](original *T) (copied *T) {
|
||||||
if original == nil {
|
if original == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
copied = new(string)
|
copied = new(T)
|
||||||
*copied = *original
|
*copied = *original
|
||||||
return copied
|
return copied
|
||||||
}
|
}
|
||||||
|
|
||||||
func CopyBoolPtr(original *bool) (copied *bool) {
|
func CopySlice[T string | uint16 | netip.Addr | netip.Prefix](original []T) (copied []T) {
|
||||||
if original == nil {
|
return slices.Clone(original)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
copied = new(bool)
|
|
||||||
*copied = *original
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyUint8Ptr(original *uint8) (copied *uint8) {
|
|
||||||
if original == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
copied = new(uint8)
|
|
||||||
*copied = *original
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyUint16Ptr(original *uint16) (copied *uint16) {
|
|
||||||
if original == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
copied = new(uint16)
|
|
||||||
*copied = *original
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyUint32Ptr(original *uint32) (copied *uint32) {
|
|
||||||
if original == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
copied = new(uint32)
|
|
||||||
*copied = *original
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyIntPtr(original *int) (copied *int) {
|
|
||||||
if original == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
copied = new(int)
|
|
||||||
*copied = *original
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyDurationPtr(original *time.Duration) (copied *time.Duration) {
|
|
||||||
if original == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
copied = new(time.Duration)
|
|
||||||
*copied = *original
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyLogLevelPtr(original *log.Level) (copied *log.Level) {
|
|
||||||
if original == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
copied = new(log.Level)
|
|
||||||
*copied = *original
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyIP(original net.IP) (copied net.IP) {
|
|
||||||
if original == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
copied = make(net.IP, len(original))
|
|
||||||
copy(copied, original)
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyIPNet(original net.IPNet) (copied net.IPNet) {
|
|
||||||
if original.IP != nil {
|
|
||||||
copied.IP = make(net.IP, len(original.IP))
|
|
||||||
copy(copied.IP, original.IP)
|
|
||||||
}
|
|
||||||
|
|
||||||
if original.Mask != nil {
|
|
||||||
copied.Mask = make(net.IPMask, len(original.Mask))
|
|
||||||
copy(copied.Mask, original.Mask)
|
|
||||||
}
|
|
||||||
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyIPNetPtr(original *net.IPNet) (copied *net.IPNet) {
|
|
||||||
if original == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
copied = new(net.IPNet)
|
|
||||||
*copied = CopyIPNet(*original)
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyNetaddrIP(original netaddr.IP) (copied netaddr.IP) {
|
|
||||||
b, err := original.MarshalBinary()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = copied.UnmarshalBinary(b)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyIPPrefix(original netaddr.IPPrefix) (copied netaddr.IPPrefix) {
|
|
||||||
b, err := original.MarshalText()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = copied.UnmarshalText(b)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyStringSlice(original []string) (copied []string) {
|
|
||||||
if original == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
copied = make([]string, len(original))
|
|
||||||
copy(copied, original)
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyUint16Slice(original []uint16) (copied []uint16) {
|
|
||||||
if original == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
copied = make([]uint16, len(original))
|
|
||||||
copy(copied, original)
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyIPNetSlice(original []net.IPNet) (copied []net.IPNet) {
|
|
||||||
if original == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
copied = make([]net.IPNet, len(original))
|
|
||||||
for i := range original {
|
|
||||||
copied[i] = CopyIPNet(original[i])
|
|
||||||
}
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyIPPrefixSlice(original []netaddr.IPPrefix) (copied []netaddr.IPPrefix) {
|
|
||||||
if original == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
copied = make([]netaddr.IPPrefix, len(original))
|
|
||||||
for i := range original {
|
|
||||||
copied[i] = CopyIPPrefix(original[i])
|
|
||||||
}
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyNetaddrIPsSlice(original []netaddr.IP) (copied []netaddr.IP) {
|
|
||||||
if original == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
copied = make([]netaddr.IP, len(original))
|
|
||||||
for i := range original {
|
|
||||||
copied[i] = CopyNetaddrIP(original[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
return copied
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,15 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net/netip"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/qdm12/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func DefaultInt(existing *int, defaultValue int) (
|
func DefaultPointer[T any](existing *T, defaultValue T) (
|
||||||
result *int) {
|
result *T) {
|
||||||
if existing != nil {
|
if existing != nil {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
result = new(int)
|
result = new(T)
|
||||||
*result = defaultValue
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func DefaultUint8(existing *uint8, defaultValue uint8) (
|
|
||||||
result *uint8) {
|
|
||||||
if existing != nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = new(uint8)
|
|
||||||
*result = defaultValue
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func DefaultUint16(existing *uint16, defaultValue uint16) (
|
|
||||||
result *uint16) {
|
|
||||||
if existing != nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = new(uint16)
|
|
||||||
*result = defaultValue
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
func DefaultUint32(existing *uint32, defaultValue uint32) (
|
|
||||||
result *uint32) {
|
|
||||||
if existing != nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = new(uint32)
|
|
||||||
*result = defaultValue
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func DefaultBool(existing *bool, defaultValue bool) (
|
|
||||||
result *bool) {
|
|
||||||
if existing != nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = new(bool)
|
|
||||||
*result = defaultValue
|
*result = defaultValue
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -64,46 +22,17 @@ func DefaultString(existing string, defaultValue string) (
|
|||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultStringPtr(existing *string, defaultValue string) (result *string) {
|
func DefaultNumber[T Number](existing T, defaultValue T) ( //nolint:ireturn
|
||||||
if existing != nil {
|
result T) {
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = new(string)
|
|
||||||
*result = defaultValue
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func DefaultDuration(existing time.Duration,
|
|
||||||
defaultValue time.Duration) (result time.Duration) {
|
|
||||||
if existing != 0 {
|
if existing != 0 {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultDurationPtr(existing *time.Duration,
|
func DefaultIP(existing netip.Addr, defaultValue netip.Addr) (
|
||||||
defaultValue time.Duration) (result *time.Duration) {
|
result netip.Addr) {
|
||||||
if existing != nil {
|
if existing.IsValid() {
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = new(time.Duration)
|
|
||||||
*result = defaultValue
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func DefaultLogLevel(existing *log.Level,
|
|
||||||
defaultValue log.Level) (result *log.Level) {
|
|
||||||
if existing != nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = new(log.Level)
|
|
||||||
*result = defaultValue
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func DefaultIP(existing net.IP, defaultValue net.IP) (
|
|
||||||
result net.IP) {
|
|
||||||
if existing != nil {
|
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
return defaultValue
|
return defaultValue
|
||||||
|
|||||||
10
internal/configuration/settings/helpers/generics.go
Normal file
10
internal/configuration/settings/helpers/generics.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Number interface {
|
||||||
|
uint8 | uint16 | uint32 | uint64 | uint |
|
||||||
|
int8 | int16 | int32 | int64 | int |
|
||||||
|
float32 | float64 |
|
||||||
|
time.Duration
|
||||||
|
}
|
||||||
@@ -1,21 +1,17 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/log"
|
|
||||||
"inet.af/netaddr"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func MergeWithBool(existing, other *bool) (result *bool) {
|
func MergeWithPointer[T any](existing, other *T) (result *T) {
|
||||||
if existing != nil {
|
if existing != nil {
|
||||||
return existing
|
return existing
|
||||||
} else if other == nil {
|
} else if other == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
result = new(bool)
|
result = new(T)
|
||||||
*result = *other
|
*result = *other
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -27,111 +23,20 @@ func MergeWithString(existing, other string) (result string) {
|
|||||||
return other
|
return other
|
||||||
}
|
}
|
||||||
|
|
||||||
func MergeWithInt(existing, other int) (result int) {
|
func MergeWithNumber[T Number](existing, other T) (result T) { //nolint:ireturn
|
||||||
if existing != 0 {
|
if existing != 0 {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
return other
|
return other
|
||||||
}
|
}
|
||||||
|
|
||||||
func MergeWithFloat64(existing, other float64) (result float64) {
|
func MergeWithIP(existing, other netip.Addr) (result netip.Addr) {
|
||||||
if existing != 0 {
|
if existing.IsValid() {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
return other
|
return other
|
||||||
}
|
}
|
||||||
|
|
||||||
func MergeWithStringPtr(existing, other *string) (result *string) {
|
|
||||||
if existing != nil {
|
|
||||||
return existing
|
|
||||||
} else if other == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
result = new(string)
|
|
||||||
*result = *other
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeWithIntPtr(existing, other *int) (result *int) {
|
|
||||||
if existing != nil {
|
|
||||||
return existing
|
|
||||||
} else if other == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
result = new(int)
|
|
||||||
*result = *other
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeWithUint8(existing, other *uint8) (result *uint8) {
|
|
||||||
if existing != nil {
|
|
||||||
return existing
|
|
||||||
} else if other == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
result = new(uint8)
|
|
||||||
*result = *other
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeWithUint16(existing, other *uint16) (result *uint16) {
|
|
||||||
if existing != nil {
|
|
||||||
return existing
|
|
||||||
} else if other == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
result = new(uint16)
|
|
||||||
*result = *other
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeWithUint32(existing, other *uint32) (result *uint32) {
|
|
||||||
if existing != nil {
|
|
||||||
return existing
|
|
||||||
} else if other == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
result = new(uint32)
|
|
||||||
*result = *other
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeWithIP(existing, other net.IP) (result net.IP) {
|
|
||||||
if existing != nil {
|
|
||||||
return existing
|
|
||||||
} else if other == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
result = make(net.IP, len(other))
|
|
||||||
copy(result, other)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeWithDuration(existing, other time.Duration) (result time.Duration) {
|
|
||||||
if existing != 0 {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
return other
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeWithDurationPtr(existing, other *time.Duration) (result *time.Duration) {
|
|
||||||
if existing != nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
return other
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeWithLogLevel(existing, other *log.Level) (result *log.Level) {
|
|
||||||
if existing != nil {
|
|
||||||
return existing
|
|
||||||
} else if other == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
result = new(log.Level)
|
|
||||||
*result = *other
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeWithHTTPHandler(existing, other http.Handler) (result http.Handler) {
|
func MergeWithHTTPHandler(existing, other http.Handler) (result http.Handler) {
|
||||||
if existing != nil {
|
if existing != nil {
|
||||||
return existing
|
return existing
|
||||||
@@ -139,13 +44,13 @@ func MergeWithHTTPHandler(existing, other http.Handler) (result http.Handler) {
|
|||||||
return other
|
return other
|
||||||
}
|
}
|
||||||
|
|
||||||
func MergeStringSlices(a, b []string) (result []string) {
|
func MergeSlices[T comparable](a, b []T) (result []T) {
|
||||||
if a == nil && b == nil {
|
if a == nil && b == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
seen := make(map[string]struct{}, len(a)+len(b))
|
seen := make(map[T]struct{}, len(a)+len(b))
|
||||||
result = make([]string, 0, len(a)+len(b))
|
result = make([]T, 0, len(a)+len(b))
|
||||||
for _, s := range a {
|
for _, s := range a {
|
||||||
if _, ok := seen[s]; ok {
|
if _, ok := seen[s]; ok {
|
||||||
continue // duplicate
|
continue // duplicate
|
||||||
@@ -162,105 +67,3 @@ func MergeStringSlices(a, b []string) (result []string) {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func MergeUint16Slices(a, b []uint16) (result []uint16) {
|
|
||||||
if a == nil && b == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
seen := make(map[uint16]struct{}, len(a)+len(b))
|
|
||||||
result = make([]uint16, 0, len(a)+len(b))
|
|
||||||
for _, n := range a {
|
|
||||||
if _, ok := seen[n]; ok {
|
|
||||||
continue // duplicate
|
|
||||||
}
|
|
||||||
result = append(result, n)
|
|
||||||
seen[n] = struct{}{}
|
|
||||||
}
|
|
||||||
for _, n := range b {
|
|
||||||
if _, ok := seen[n]; ok {
|
|
||||||
continue // duplicate
|
|
||||||
}
|
|
||||||
result = append(result, n)
|
|
||||||
seen[n] = struct{}{}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeIPNetsSlices(a, b []net.IPNet) (result []net.IPNet) {
|
|
||||||
if a == nil && b == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
seen := make(map[string]struct{}, len(a)+len(b))
|
|
||||||
result = make([]net.IPNet, 0, len(a)+len(b))
|
|
||||||
for _, ipNet := range a {
|
|
||||||
key := ipNet.String()
|
|
||||||
if _, ok := seen[key]; ok {
|
|
||||||
continue // duplicate
|
|
||||||
}
|
|
||||||
result = append(result, ipNet)
|
|
||||||
seen[key] = struct{}{}
|
|
||||||
}
|
|
||||||
for _, ipNet := range b {
|
|
||||||
key := ipNet.String()
|
|
||||||
if _, ok := seen[key]; ok {
|
|
||||||
continue // duplicate
|
|
||||||
}
|
|
||||||
result = append(result, ipNet)
|
|
||||||
seen[key] = struct{}{}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeNetaddrIPsSlices(a, b []netaddr.IP) (result []netaddr.IP) {
|
|
||||||
if a == nil && b == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
seen := make(map[string]struct{}, len(a)+len(b))
|
|
||||||
result = make([]netaddr.IP, 0, len(a)+len(b))
|
|
||||||
for _, ip := range a {
|
|
||||||
key := ip.String()
|
|
||||||
if _, ok := seen[key]; ok {
|
|
||||||
continue // duplicate
|
|
||||||
}
|
|
||||||
result = append(result, ip)
|
|
||||||
seen[key] = struct{}{}
|
|
||||||
}
|
|
||||||
for _, ip := range b {
|
|
||||||
key := ip.String()
|
|
||||||
if _, ok := seen[key]; ok {
|
|
||||||
continue // duplicate
|
|
||||||
}
|
|
||||||
result = append(result, ip)
|
|
||||||
seen[key] = struct{}{}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeIPPrefixesSlices(a, b []netaddr.IPPrefix) (result []netaddr.IPPrefix) {
|
|
||||||
if a == nil && b == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
seen := make(map[string]struct{}, len(a)+len(b))
|
|
||||||
result = make([]netaddr.IPPrefix, 0, len(a)+len(b))
|
|
||||||
for _, ipPrefix := range a {
|
|
||||||
key := ipPrefix.String()
|
|
||||||
if _, ok := seen[key]; ok {
|
|
||||||
continue // duplicate
|
|
||||||
}
|
|
||||||
result = append(result, ipPrefix)
|
|
||||||
seen[key] = struct{}{}
|
|
||||||
}
|
|
||||||
for _, ipPrefix := range b {
|
|
||||||
key := ipPrefix.String()
|
|
||||||
if _, ok := seen[key]; ok {
|
|
||||||
continue // duplicate
|
|
||||||
}
|
|
||||||
result = append(result, ipPrefix)
|
|
||||||
seen[key] = struct{}{}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/log"
|
|
||||||
"inet.af/netaddr"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func OverrideWithBool(existing, other *bool) (result *bool) {
|
func OverrideWithPointer[T any](existing, other *T) (result *T) {
|
||||||
if other == nil {
|
if other == nil {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
result = new(bool)
|
result = new(T)
|
||||||
*result = *other
|
*result = *other
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -25,101 +21,20 @@ func OverrideWithString(existing, other string) (result string) {
|
|||||||
return other
|
return other
|
||||||
}
|
}
|
||||||
|
|
||||||
func OverrideWithInt(existing, other int) (result int) {
|
func OverrideWithNumber[T Number](existing, other T) (result T) { //nolint:ireturn
|
||||||
if other == 0 {
|
if other == 0 {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
return other
|
return other
|
||||||
}
|
}
|
||||||
|
|
||||||
func OverrideWithFloat64(existing, other float64) (result float64) {
|
func OverrideWithIP(existing, other netip.Addr) (result netip.Addr) {
|
||||||
if other == 0 {
|
if !other.IsValid() {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
return other
|
return other
|
||||||
}
|
}
|
||||||
|
|
||||||
func OverrideWithStringPtr(existing, other *string) (result *string) {
|
|
||||||
if other == nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = new(string)
|
|
||||||
*result = *other
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func OverrideWithIntPtr(existing, other *int) (result *int) {
|
|
||||||
if other == nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = new(int)
|
|
||||||
*result = *other
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func OverrideWithUint8(existing, other *uint8) (result *uint8) {
|
|
||||||
if other == nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = new(uint8)
|
|
||||||
*result = *other
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func OverrideWithUint16(existing, other *uint16) (result *uint16) {
|
|
||||||
if other == nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = new(uint16)
|
|
||||||
*result = *other
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func OverrideWithUint32(existing, other *uint32) (result *uint32) {
|
|
||||||
if other == nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = new(uint32)
|
|
||||||
*result = *other
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func OverrideWithIP(existing, other net.IP) (result net.IP) {
|
|
||||||
if other == nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = make(net.IP, len(other))
|
|
||||||
copy(result, other)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func OverrideWithDuration(existing, other time.Duration) (
|
|
||||||
result time.Duration) {
|
|
||||||
if other == 0 {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
return other
|
|
||||||
}
|
|
||||||
|
|
||||||
func OverrideWithDurationPtr(existing, other *time.Duration) (
|
|
||||||
result *time.Duration) {
|
|
||||||
if other == nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = new(time.Duration)
|
|
||||||
*result = *other
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func OverrideWithLogLevel(existing, other *log.Level) (result *log.Level) {
|
|
||||||
if other == nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = new(log.Level)
|
|
||||||
*result = *other
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func OverrideWithHTTPHandler(existing, other http.Handler) (result http.Handler) {
|
func OverrideWithHTTPHandler(existing, other http.Handler) (result http.Handler) {
|
||||||
if other != nil {
|
if other != nil {
|
||||||
return other
|
return other
|
||||||
@@ -127,47 +42,11 @@ func OverrideWithHTTPHandler(existing, other http.Handler) (result http.Handler)
|
|||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
|
|
||||||
func OverrideWithStringSlice(existing, other []string) (result []string) {
|
func OverrideWithSlice[T any](existing, other []T) (result []T) {
|
||||||
if other == nil {
|
if other == nil {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
result = make([]string, len(other))
|
result = make([]T, len(other))
|
||||||
copy(result, other)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func OverrideWithUint16Slice(existing, other []uint16) (result []uint16) {
|
|
||||||
if other == nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = make([]uint16, len(other))
|
|
||||||
copy(result, other)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func OverrideWithIPNetsSlice(existing, other []net.IPNet) (result []net.IPNet) {
|
|
||||||
if other == nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = make([]net.IPNet, len(other))
|
|
||||||
copy(result, other)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func OverrideWithNetaddrIPsSlice(existing, other []netaddr.IP) (result []netaddr.IP) {
|
|
||||||
if other == nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = make([]netaddr.IP, len(other))
|
|
||||||
copy(result, other)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func OverrideWithIPPrefixesSlice(existing, other []netaddr.IPPrefix) (result []netaddr.IPPrefix) {
|
|
||||||
if other == nil {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
result = make([]netaddr.IPPrefix, len(other))
|
|
||||||
copy(result, other)
|
copy(result, other)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
package helpers
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// StringPtr returns a pointer to the string value
|
|
||||||
// passed as argument.
|
|
||||||
func StringPtr(s string) *string { return &s }
|
|
||||||
|
|
||||||
// DurationPtr returns a pointer to the duration value
|
|
||||||
// passed as argument.
|
|
||||||
func DurationPtr(d time.Duration) *time.Duration { return &d }
|
|
||||||
@@ -56,12 +56,12 @@ func (h HTTPProxy) validate() (err error) {
|
|||||||
|
|
||||||
func (h *HTTPProxy) copy() (copied HTTPProxy) {
|
func (h *HTTPProxy) copy() (copied HTTPProxy) {
|
||||||
return HTTPProxy{
|
return HTTPProxy{
|
||||||
User: helpers.CopyStringPtr(h.User),
|
User: helpers.CopyPointer(h.User),
|
||||||
Password: helpers.CopyStringPtr(h.Password),
|
Password: helpers.CopyPointer(h.Password),
|
||||||
ListeningAddress: h.ListeningAddress,
|
ListeningAddress: h.ListeningAddress,
|
||||||
Enabled: helpers.CopyBoolPtr(h.Enabled),
|
Enabled: helpers.CopyPointer(h.Enabled),
|
||||||
Stealth: helpers.CopyBoolPtr(h.Stealth),
|
Stealth: helpers.CopyPointer(h.Stealth),
|
||||||
Log: helpers.CopyBoolPtr(h.Log),
|
Log: helpers.CopyPointer(h.Log),
|
||||||
ReadHeaderTimeout: h.ReadHeaderTimeout,
|
ReadHeaderTimeout: h.ReadHeaderTimeout,
|
||||||
ReadTimeout: h.ReadTimeout,
|
ReadTimeout: h.ReadTimeout,
|
||||||
}
|
}
|
||||||
@@ -70,41 +70,41 @@ func (h *HTTPProxy) copy() (copied HTTPProxy) {
|
|||||||
// mergeWith merges the other settings into any
|
// mergeWith merges the other settings into any
|
||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (h *HTTPProxy) mergeWith(other HTTPProxy) {
|
func (h *HTTPProxy) mergeWith(other HTTPProxy) {
|
||||||
h.User = helpers.MergeWithStringPtr(h.User, other.User)
|
h.User = helpers.MergeWithPointer(h.User, other.User)
|
||||||
h.Password = helpers.MergeWithStringPtr(h.Password, other.Password)
|
h.Password = helpers.MergeWithPointer(h.Password, other.Password)
|
||||||
h.ListeningAddress = helpers.MergeWithString(h.ListeningAddress, other.ListeningAddress)
|
h.ListeningAddress = helpers.MergeWithString(h.ListeningAddress, other.ListeningAddress)
|
||||||
h.Enabled = helpers.MergeWithBool(h.Enabled, other.Enabled)
|
h.Enabled = helpers.MergeWithPointer(h.Enabled, other.Enabled)
|
||||||
h.Stealth = helpers.MergeWithBool(h.Stealth, other.Stealth)
|
h.Stealth = helpers.MergeWithPointer(h.Stealth, other.Stealth)
|
||||||
h.Log = helpers.MergeWithBool(h.Log, other.Log)
|
h.Log = helpers.MergeWithPointer(h.Log, other.Log)
|
||||||
h.ReadHeaderTimeout = helpers.MergeWithDuration(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
h.ReadHeaderTimeout = helpers.MergeWithNumber(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
||||||
h.ReadTimeout = helpers.MergeWithDuration(h.ReadTimeout, other.ReadTimeout)
|
h.ReadTimeout = helpers.MergeWithNumber(h.ReadTimeout, other.ReadTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (h *HTTPProxy) overrideWith(other HTTPProxy) {
|
func (h *HTTPProxy) overrideWith(other HTTPProxy) {
|
||||||
h.User = helpers.OverrideWithStringPtr(h.User, other.User)
|
h.User = helpers.OverrideWithPointer(h.User, other.User)
|
||||||
h.Password = helpers.OverrideWithStringPtr(h.Password, other.Password)
|
h.Password = helpers.OverrideWithPointer(h.Password, other.Password)
|
||||||
h.ListeningAddress = helpers.OverrideWithString(h.ListeningAddress, other.ListeningAddress)
|
h.ListeningAddress = helpers.OverrideWithString(h.ListeningAddress, other.ListeningAddress)
|
||||||
h.Enabled = helpers.OverrideWithBool(h.Enabled, other.Enabled)
|
h.Enabled = helpers.OverrideWithPointer(h.Enabled, other.Enabled)
|
||||||
h.Stealth = helpers.OverrideWithBool(h.Stealth, other.Stealth)
|
h.Stealth = helpers.OverrideWithPointer(h.Stealth, other.Stealth)
|
||||||
h.Log = helpers.OverrideWithBool(h.Log, other.Log)
|
h.Log = helpers.OverrideWithPointer(h.Log, other.Log)
|
||||||
h.ReadHeaderTimeout = helpers.OverrideWithDuration(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
h.ReadHeaderTimeout = helpers.OverrideWithNumber(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
||||||
h.ReadTimeout = helpers.OverrideWithDuration(h.ReadTimeout, other.ReadTimeout)
|
h.ReadTimeout = helpers.OverrideWithNumber(h.ReadTimeout, other.ReadTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HTTPProxy) setDefaults() {
|
func (h *HTTPProxy) setDefaults() {
|
||||||
h.User = helpers.DefaultStringPtr(h.User, "")
|
h.User = helpers.DefaultPointer(h.User, "")
|
||||||
h.Password = helpers.DefaultStringPtr(h.Password, "")
|
h.Password = helpers.DefaultPointer(h.Password, "")
|
||||||
h.ListeningAddress = helpers.DefaultString(h.ListeningAddress, ":8888")
|
h.ListeningAddress = helpers.DefaultString(h.ListeningAddress, ":8888")
|
||||||
h.Enabled = helpers.DefaultBool(h.Enabled, false)
|
h.Enabled = helpers.DefaultPointer(h.Enabled, false)
|
||||||
h.Stealth = helpers.DefaultBool(h.Stealth, false)
|
h.Stealth = helpers.DefaultPointer(h.Stealth, false)
|
||||||
h.Log = helpers.DefaultBool(h.Log, false)
|
h.Log = helpers.DefaultPointer(h.Log, false)
|
||||||
const defaultReadHeaderTimeout = time.Second
|
const defaultReadHeaderTimeout = time.Second
|
||||||
h.ReadHeaderTimeout = helpers.DefaultDuration(h.ReadHeaderTimeout, defaultReadHeaderTimeout)
|
h.ReadHeaderTimeout = helpers.DefaultNumber(h.ReadHeaderTimeout, defaultReadHeaderTimeout)
|
||||||
const defaultReadTimeout = 3 * time.Second
|
const defaultReadTimeout = 3 * time.Second
|
||||||
h.ReadTimeout = helpers.DefaultDuration(h.ReadTimeout, defaultReadTimeout)
|
h.ReadTimeout = helpers.DefaultNumber(h.ReadTimeout, defaultReadTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h HTTPProxy) String() string {
|
func (h HTTPProxy) String() string {
|
||||||
|
|||||||
@@ -19,25 +19,25 @@ func (l Log) validate() (err error) {
|
|||||||
|
|
||||||
func (l *Log) copy() (copied Log) {
|
func (l *Log) copy() (copied Log) {
|
||||||
return Log{
|
return Log{
|
||||||
Level: helpers.CopyLogLevelPtr(l.Level),
|
Level: helpers.CopyPointer(l.Level),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
// mergeWith merges the other settings into any
|
||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (l *Log) mergeWith(other Log) {
|
func (l *Log) mergeWith(other Log) {
|
||||||
l.Level = helpers.MergeWithLogLevel(l.Level, other.Level)
|
l.Level = helpers.MergeWithPointer(l.Level, other.Level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (l *Log) overrideWith(other Log) {
|
func (l *Log) overrideWith(other Log) {
|
||||||
l.Level = helpers.OverrideWithLogLevel(l.Level, other.Level)
|
l.Level = helpers.OverrideWithPointer(l.Level, other.Level)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Log) setDefaults() {
|
func (l *Log) setDefaults() {
|
||||||
l.Level = helpers.DefaultLogLevel(l.Level, log.LevelInfo)
|
l.Level = helpers.DefaultPointer(l.Level, log.LevelInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Log) String() string {
|
func (l Log) String() string {
|
||||||
|
|||||||
36
internal/configuration/settings/netaddr.go
Normal file
36
internal/configuration/settings/netaddr.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
"inet.af/netaddr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func netipAddressToNetaddrIP(address netip.Addr) (ip netaddr.IP) {
|
||||||
|
if address.Is4() {
|
||||||
|
return netaddr.IPFrom4(address.As4())
|
||||||
|
}
|
||||||
|
return netaddr.IPFrom16(address.As16())
|
||||||
|
}
|
||||||
|
|
||||||
|
func netipAddressesToNetaddrIPs(addresses []netip.Addr) (ips []netaddr.IP) {
|
||||||
|
ips = make([]netaddr.IP, len(addresses))
|
||||||
|
for i := range addresses {
|
||||||
|
ips[i] = netipAddressToNetaddrIP(addresses[i])
|
||||||
|
}
|
||||||
|
return ips
|
||||||
|
}
|
||||||
|
|
||||||
|
func netipPrefixToNetaddrIPPrefix(prefix netip.Prefix) (ipPrefix netaddr.IPPrefix) {
|
||||||
|
netaddrIP := netipAddressToNetaddrIP(prefix.Addr())
|
||||||
|
bits := prefix.Bits()
|
||||||
|
return netaddr.IPPrefixFrom(netaddrIP, uint8(bits))
|
||||||
|
}
|
||||||
|
|
||||||
|
func netipPrefixesToNetaddrIPPrefixes(prefixes []netip.Prefix) (ipPrefixes []netaddr.IPPrefix) {
|
||||||
|
ipPrefixes = make([]netaddr.IPPrefix, len(prefixes))
|
||||||
|
for i := range ipPrefixes {
|
||||||
|
ipPrefixes[i] = netipPrefixToNetaddrIPPrefix(prefixes[i])
|
||||||
|
}
|
||||||
|
return ipPrefixes
|
||||||
|
}
|
||||||
@@ -42,18 +42,18 @@ type OpenVPN struct {
|
|||||||
// It cannot be nil in the internal state.
|
// It cannot be nil in the internal state.
|
||||||
// It is ignored if it is set to the empty string.
|
// It is ignored if it is set to the empty string.
|
||||||
Auth *string
|
Auth *string
|
||||||
// Cert is the OpenVPN certificate for the <cert> block.
|
// Cert is the base64 encoded DER of an OpenVPN certificate for the <cert> block.
|
||||||
// This is notably used by Cyberghost and VPN secure.
|
// This is notably used by Cyberghost and VPN secure.
|
||||||
// It can be set to the empty string to be ignored.
|
// It can be set to the empty string to be ignored.
|
||||||
// It cannot be nil in the internal state.
|
// It cannot be nil in the internal state.
|
||||||
Cert *string
|
Cert *string
|
||||||
// Key is the OpenVPN key.
|
// Key is the base64 encoded DER of an OpenVPN key.
|
||||||
// This is used by Cyberghost and VPN Unlimited.
|
// This is used by Cyberghost and VPN Unlimited.
|
||||||
// It can be set to the empty string to be ignored.
|
// It can be set to the empty string to be ignored.
|
||||||
// It cannot be nil in the internal state.
|
// It cannot be nil in the internal state.
|
||||||
Key *string
|
Key *string
|
||||||
// EncryptedKey is the content of an encrypted
|
// EncryptedKey is the base64 encoded DER of an encrypted key for OpenVPN.
|
||||||
// key for OpenVPN. It is used by VPN secure.
|
// It is used by VPN secure.
|
||||||
// It defaults to the empty string meaning it is not
|
// It defaults to the empty string meaning it is not
|
||||||
// to be used. KeyPassphrase must be set if this one is set.
|
// to be used. KeyPassphrase must be set if this one is set.
|
||||||
EncryptedKey *string
|
EncryptedKey *string
|
||||||
@@ -100,14 +100,14 @@ func (o OpenVPN) validate(vpnProvider string) (err error) {
|
|||||||
vpnProvider != providers.VPNSecure
|
vpnProvider != providers.VPNSecure
|
||||||
|
|
||||||
if isUserRequired && *o.User == "" {
|
if isUserRequired && *o.User == "" {
|
||||||
return ErrOpenVPNUserIsEmpty
|
return fmt.Errorf("%w", ErrOpenVPNUserIsEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
passwordRequired := isUserRequired &&
|
passwordRequired := isUserRequired &&
|
||||||
(vpnProvider != providers.Ivpn || !ivpnAccountID.MatchString(*o.User))
|
(vpnProvider != providers.Ivpn || !ivpnAccountID.MatchString(*o.User))
|
||||||
|
|
||||||
if passwordRequired && *o.Password == "" {
|
if passwordRequired && *o.Password == "" {
|
||||||
return ErrOpenVPNPasswordIsEmpty
|
return fmt.Errorf("%w", ErrOpenVPNPasswordIsEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validateOpenVPNConfigFilepath(isCustom, *o.ConfFile)
|
err = validateOpenVPNConfigFilepath(isCustom, *o.ConfFile)
|
||||||
@@ -160,7 +160,7 @@ func validateOpenVPNConfigFilepath(isCustom bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if confFile == "" {
|
if confFile == "" {
|
||||||
return ErrFilepathMissing
|
return fmt.Errorf("%w", ErrFilepathMissing)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = helpers.FileExists(confFile)
|
err = helpers.FileExists(confFile)
|
||||||
@@ -171,7 +171,7 @@ func validateOpenVPNConfigFilepath(isCustom bool,
|
|||||||
extractor := extract.New()
|
extractor := extract.New()
|
||||||
_, _, err = extractor.Data(confFile)
|
_, _, err = extractor.Data(confFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed extracting information from custom configuration file: %w", err)
|
return fmt.Errorf("extracting information from custom configuration file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -186,7 +186,7 @@ func validateOpenVPNClientCertificate(vpnProvider,
|
|||||||
providers.VPNSecure,
|
providers.VPNSecure,
|
||||||
providers.VPNUnlimited:
|
providers.VPNUnlimited:
|
||||||
if clientCert == "" {
|
if clientCert == "" {
|
||||||
return ErrMissingValue
|
return fmt.Errorf("%w", ErrMissingValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,7 +209,7 @@ func validateOpenVPNClientKey(vpnProvider, clientKey string) (err error) {
|
|||||||
providers.VPNUnlimited,
|
providers.VPNUnlimited,
|
||||||
providers.Wevpn:
|
providers.Wevpn:
|
||||||
if clientKey == "" {
|
if clientKey == "" {
|
||||||
return ErrMissingValue
|
return fmt.Errorf("%w", ErrMissingValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +227,7 @@ func validateOpenVPNClientKey(vpnProvider, clientKey string) (err error) {
|
|||||||
func validateOpenVPNEncryptedKey(vpnProvider,
|
func validateOpenVPNEncryptedKey(vpnProvider,
|
||||||
encryptedPrivateKey string) (err error) {
|
encryptedPrivateKey string) (err error) {
|
||||||
if vpnProvider == providers.VPNSecure && encryptedPrivateKey == "" {
|
if vpnProvider == providers.VPNSecure && encryptedPrivateKey == "" {
|
||||||
return ErrMissingValue
|
return fmt.Errorf("%w", ErrMissingValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
if encryptedPrivateKey == "" {
|
if encryptedPrivateKey == "" {
|
||||||
@@ -244,21 +244,21 @@ func validateOpenVPNEncryptedKey(vpnProvider,
|
|||||||
func (o *OpenVPN) copy() (copied OpenVPN) {
|
func (o *OpenVPN) copy() (copied OpenVPN) {
|
||||||
return OpenVPN{
|
return OpenVPN{
|
||||||
Version: o.Version,
|
Version: o.Version,
|
||||||
User: helpers.CopyStringPtr(o.User),
|
User: helpers.CopyPointer(o.User),
|
||||||
Password: helpers.CopyStringPtr(o.Password),
|
Password: helpers.CopyPointer(o.Password),
|
||||||
ConfFile: helpers.CopyStringPtr(o.ConfFile),
|
ConfFile: helpers.CopyPointer(o.ConfFile),
|
||||||
Ciphers: helpers.CopyStringSlice(o.Ciphers),
|
Ciphers: helpers.CopySlice(o.Ciphers),
|
||||||
Auth: helpers.CopyStringPtr(o.Auth),
|
Auth: helpers.CopyPointer(o.Auth),
|
||||||
Cert: helpers.CopyStringPtr(o.Cert),
|
Cert: helpers.CopyPointer(o.Cert),
|
||||||
Key: helpers.CopyStringPtr(o.Key),
|
Key: helpers.CopyPointer(o.Key),
|
||||||
EncryptedKey: helpers.CopyStringPtr(o.EncryptedKey),
|
EncryptedKey: helpers.CopyPointer(o.EncryptedKey),
|
||||||
KeyPassphrase: helpers.CopyStringPtr(o.KeyPassphrase),
|
KeyPassphrase: helpers.CopyPointer(o.KeyPassphrase),
|
||||||
PIAEncPreset: helpers.CopyStringPtr(o.PIAEncPreset),
|
PIAEncPreset: helpers.CopyPointer(o.PIAEncPreset),
|
||||||
MSSFix: helpers.CopyUint16Ptr(o.MSSFix),
|
MSSFix: helpers.CopyPointer(o.MSSFix),
|
||||||
Interface: o.Interface,
|
Interface: o.Interface,
|
||||||
ProcessUser: o.ProcessUser,
|
ProcessUser: o.ProcessUser,
|
||||||
Verbosity: helpers.CopyIntPtr(o.Verbosity),
|
Verbosity: helpers.CopyPointer(o.Verbosity),
|
||||||
Flags: helpers.CopyStringSlice(o.Flags),
|
Flags: helpers.CopySlice(o.Flags),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,21 +266,21 @@ func (o *OpenVPN) copy() (copied OpenVPN) {
|
|||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (o *OpenVPN) mergeWith(other OpenVPN) {
|
func (o *OpenVPN) mergeWith(other OpenVPN) {
|
||||||
o.Version = helpers.MergeWithString(o.Version, other.Version)
|
o.Version = helpers.MergeWithString(o.Version, other.Version)
|
||||||
o.User = helpers.MergeWithStringPtr(o.User, other.User)
|
o.User = helpers.MergeWithPointer(o.User, other.User)
|
||||||
o.Password = helpers.MergeWithStringPtr(o.Password, other.Password)
|
o.Password = helpers.MergeWithPointer(o.Password, other.Password)
|
||||||
o.ConfFile = helpers.MergeWithStringPtr(o.ConfFile, other.ConfFile)
|
o.ConfFile = helpers.MergeWithPointer(o.ConfFile, other.ConfFile)
|
||||||
o.Ciphers = helpers.MergeStringSlices(o.Ciphers, other.Ciphers)
|
o.Ciphers = helpers.MergeSlices(o.Ciphers, other.Ciphers)
|
||||||
o.Auth = helpers.MergeWithStringPtr(o.Auth, other.Auth)
|
o.Auth = helpers.MergeWithPointer(o.Auth, other.Auth)
|
||||||
o.Cert = helpers.MergeWithStringPtr(o.Cert, other.Cert)
|
o.Cert = helpers.MergeWithPointer(o.Cert, other.Cert)
|
||||||
o.Key = helpers.MergeWithStringPtr(o.Key, other.Key)
|
o.Key = helpers.MergeWithPointer(o.Key, other.Key)
|
||||||
o.EncryptedKey = helpers.MergeWithStringPtr(o.EncryptedKey, other.EncryptedKey)
|
o.EncryptedKey = helpers.MergeWithPointer(o.EncryptedKey, other.EncryptedKey)
|
||||||
o.KeyPassphrase = helpers.MergeWithStringPtr(o.KeyPassphrase, other.KeyPassphrase)
|
o.KeyPassphrase = helpers.MergeWithPointer(o.KeyPassphrase, other.KeyPassphrase)
|
||||||
o.PIAEncPreset = helpers.MergeWithStringPtr(o.PIAEncPreset, other.PIAEncPreset)
|
o.PIAEncPreset = helpers.MergeWithPointer(o.PIAEncPreset, other.PIAEncPreset)
|
||||||
o.MSSFix = helpers.MergeWithUint16(o.MSSFix, other.MSSFix)
|
o.MSSFix = helpers.MergeWithPointer(o.MSSFix, other.MSSFix)
|
||||||
o.Interface = helpers.MergeWithString(o.Interface, other.Interface)
|
o.Interface = helpers.MergeWithString(o.Interface, other.Interface)
|
||||||
o.ProcessUser = helpers.MergeWithString(o.ProcessUser, other.ProcessUser)
|
o.ProcessUser = helpers.MergeWithString(o.ProcessUser, other.ProcessUser)
|
||||||
o.Verbosity = helpers.MergeWithIntPtr(o.Verbosity, other.Verbosity)
|
o.Verbosity = helpers.MergeWithPointer(o.Verbosity, other.Verbosity)
|
||||||
o.Flags = helpers.MergeStringSlices(o.Flags, other.Flags)
|
o.Flags = helpers.MergeSlices(o.Flags, other.Flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
@@ -288,48 +288,48 @@ func (o *OpenVPN) mergeWith(other OpenVPN) {
|
|||||||
// settings.
|
// settings.
|
||||||
func (o *OpenVPN) overrideWith(other OpenVPN) {
|
func (o *OpenVPN) overrideWith(other OpenVPN) {
|
||||||
o.Version = helpers.OverrideWithString(o.Version, other.Version)
|
o.Version = helpers.OverrideWithString(o.Version, other.Version)
|
||||||
o.User = helpers.OverrideWithStringPtr(o.User, other.User)
|
o.User = helpers.OverrideWithPointer(o.User, other.User)
|
||||||
o.Password = helpers.OverrideWithStringPtr(o.Password, other.Password)
|
o.Password = helpers.OverrideWithPointer(o.Password, other.Password)
|
||||||
o.ConfFile = helpers.OverrideWithStringPtr(o.ConfFile, other.ConfFile)
|
o.ConfFile = helpers.OverrideWithPointer(o.ConfFile, other.ConfFile)
|
||||||
o.Ciphers = helpers.OverrideWithStringSlice(o.Ciphers, other.Ciphers)
|
o.Ciphers = helpers.OverrideWithSlice(o.Ciphers, other.Ciphers)
|
||||||
o.Auth = helpers.OverrideWithStringPtr(o.Auth, other.Auth)
|
o.Auth = helpers.OverrideWithPointer(o.Auth, other.Auth)
|
||||||
o.Cert = helpers.OverrideWithStringPtr(o.Cert, other.Cert)
|
o.Cert = helpers.OverrideWithPointer(o.Cert, other.Cert)
|
||||||
o.Key = helpers.OverrideWithStringPtr(o.Key, other.Key)
|
o.Key = helpers.OverrideWithPointer(o.Key, other.Key)
|
||||||
o.EncryptedKey = helpers.OverrideWithStringPtr(o.EncryptedKey, other.EncryptedKey)
|
o.EncryptedKey = helpers.OverrideWithPointer(o.EncryptedKey, other.EncryptedKey)
|
||||||
o.KeyPassphrase = helpers.OverrideWithStringPtr(o.KeyPassphrase, other.KeyPassphrase)
|
o.KeyPassphrase = helpers.OverrideWithPointer(o.KeyPassphrase, other.KeyPassphrase)
|
||||||
o.PIAEncPreset = helpers.OverrideWithStringPtr(o.PIAEncPreset, other.PIAEncPreset)
|
o.PIAEncPreset = helpers.OverrideWithPointer(o.PIAEncPreset, other.PIAEncPreset)
|
||||||
o.MSSFix = helpers.OverrideWithUint16(o.MSSFix, other.MSSFix)
|
o.MSSFix = helpers.OverrideWithPointer(o.MSSFix, other.MSSFix)
|
||||||
o.Interface = helpers.OverrideWithString(o.Interface, other.Interface)
|
o.Interface = helpers.OverrideWithString(o.Interface, other.Interface)
|
||||||
o.ProcessUser = helpers.OverrideWithString(o.ProcessUser, other.ProcessUser)
|
o.ProcessUser = helpers.OverrideWithString(o.ProcessUser, other.ProcessUser)
|
||||||
o.Verbosity = helpers.OverrideWithIntPtr(o.Verbosity, other.Verbosity)
|
o.Verbosity = helpers.OverrideWithPointer(o.Verbosity, other.Verbosity)
|
||||||
o.Flags = helpers.OverrideWithStringSlice(o.Flags, other.Flags)
|
o.Flags = helpers.OverrideWithSlice(o.Flags, other.Flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OpenVPN) setDefaults(vpnProvider string) {
|
func (o *OpenVPN) setDefaults(vpnProvider string) {
|
||||||
o.Version = helpers.DefaultString(o.Version, openvpn.Openvpn25)
|
o.Version = helpers.DefaultString(o.Version, openvpn.Openvpn25)
|
||||||
o.User = helpers.DefaultStringPtr(o.User, "")
|
o.User = helpers.DefaultPointer(o.User, "")
|
||||||
if vpnProvider == providers.Mullvad {
|
if vpnProvider == providers.Mullvad {
|
||||||
o.Password = helpers.DefaultStringPtr(o.Password, "m")
|
o.Password = helpers.DefaultPointer(o.Password, "m")
|
||||||
} else {
|
} else {
|
||||||
o.Password = helpers.DefaultStringPtr(o.Password, "")
|
o.Password = helpers.DefaultPointer(o.Password, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
o.ConfFile = helpers.DefaultStringPtr(o.ConfFile, "")
|
o.ConfFile = helpers.DefaultPointer(o.ConfFile, "")
|
||||||
o.Auth = helpers.DefaultStringPtr(o.Auth, "")
|
o.Auth = helpers.DefaultPointer(o.Auth, "")
|
||||||
o.Cert = helpers.DefaultStringPtr(o.Cert, "")
|
o.Cert = helpers.DefaultPointer(o.Cert, "")
|
||||||
o.Key = helpers.DefaultStringPtr(o.Key, "")
|
o.Key = helpers.DefaultPointer(o.Key, "")
|
||||||
o.EncryptedKey = helpers.DefaultStringPtr(o.EncryptedKey, "")
|
o.EncryptedKey = helpers.DefaultPointer(o.EncryptedKey, "")
|
||||||
o.KeyPassphrase = helpers.DefaultStringPtr(o.KeyPassphrase, "")
|
o.KeyPassphrase = helpers.DefaultPointer(o.KeyPassphrase, "")
|
||||||
|
|
||||||
var defaultEncPreset string
|
var defaultEncPreset string
|
||||||
if vpnProvider == providers.PrivateInternetAccess {
|
if vpnProvider == providers.PrivateInternetAccess {
|
||||||
defaultEncPreset = presets.Strong
|
defaultEncPreset = presets.Strong
|
||||||
}
|
}
|
||||||
o.PIAEncPreset = helpers.DefaultStringPtr(o.PIAEncPreset, defaultEncPreset)
|
o.PIAEncPreset = helpers.DefaultPointer(o.PIAEncPreset, defaultEncPreset)
|
||||||
o.MSSFix = helpers.DefaultUint16(o.MSSFix, 0)
|
o.MSSFix = helpers.DefaultPointer(o.MSSFix, 0)
|
||||||
o.Interface = helpers.DefaultString(o.Interface, "tun0")
|
o.Interface = helpers.DefaultString(o.Interface, "tun0")
|
||||||
o.ProcessUser = helpers.DefaultString(o.ProcessUser, "root")
|
o.ProcessUser = helpers.DefaultString(o.ProcessUser, "root")
|
||||||
o.Verbosity = helpers.DefaultInt(o.Verbosity, 1)
|
o.Verbosity = helpers.DefaultPointer(o.Verbosity, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o OpenVPN) String() string {
|
func (o OpenVPN) String() string {
|
||||||
|
|||||||
@@ -130,37 +130,37 @@ func (o OpenVPNSelection) validate(vpnProvider string) (err error) {
|
|||||||
|
|
||||||
func (o *OpenVPNSelection) copy() (copied OpenVPNSelection) {
|
func (o *OpenVPNSelection) copy() (copied OpenVPNSelection) {
|
||||||
return OpenVPNSelection{
|
return OpenVPNSelection{
|
||||||
ConfFile: helpers.CopyStringPtr(o.ConfFile),
|
ConfFile: helpers.CopyPointer(o.ConfFile),
|
||||||
TCP: helpers.CopyBoolPtr(o.TCP),
|
TCP: helpers.CopyPointer(o.TCP),
|
||||||
CustomPort: helpers.CopyUint16Ptr(o.CustomPort),
|
CustomPort: helpers.CopyPointer(o.CustomPort),
|
||||||
PIAEncPreset: helpers.CopyStringPtr(o.PIAEncPreset),
|
PIAEncPreset: helpers.CopyPointer(o.PIAEncPreset),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OpenVPNSelection) mergeWith(other OpenVPNSelection) {
|
func (o *OpenVPNSelection) mergeWith(other OpenVPNSelection) {
|
||||||
o.ConfFile = helpers.MergeWithStringPtr(o.ConfFile, other.ConfFile)
|
o.ConfFile = helpers.MergeWithPointer(o.ConfFile, other.ConfFile)
|
||||||
o.TCP = helpers.MergeWithBool(o.TCP, other.TCP)
|
o.TCP = helpers.MergeWithPointer(o.TCP, other.TCP)
|
||||||
o.CustomPort = helpers.MergeWithUint16(o.CustomPort, other.CustomPort)
|
o.CustomPort = helpers.MergeWithPointer(o.CustomPort, other.CustomPort)
|
||||||
o.PIAEncPreset = helpers.MergeWithStringPtr(o.PIAEncPreset, other.PIAEncPreset)
|
o.PIAEncPreset = helpers.MergeWithPointer(o.PIAEncPreset, other.PIAEncPreset)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OpenVPNSelection) overrideWith(other OpenVPNSelection) {
|
func (o *OpenVPNSelection) overrideWith(other OpenVPNSelection) {
|
||||||
o.ConfFile = helpers.OverrideWithStringPtr(o.ConfFile, other.ConfFile)
|
o.ConfFile = helpers.OverrideWithPointer(o.ConfFile, other.ConfFile)
|
||||||
o.TCP = helpers.OverrideWithBool(o.TCP, other.TCP)
|
o.TCP = helpers.OverrideWithPointer(o.TCP, other.TCP)
|
||||||
o.CustomPort = helpers.OverrideWithUint16(o.CustomPort, other.CustomPort)
|
o.CustomPort = helpers.OverrideWithPointer(o.CustomPort, other.CustomPort)
|
||||||
o.PIAEncPreset = helpers.OverrideWithStringPtr(o.PIAEncPreset, other.PIAEncPreset)
|
o.PIAEncPreset = helpers.OverrideWithPointer(o.PIAEncPreset, other.PIAEncPreset)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OpenVPNSelection) setDefaults(vpnProvider string) {
|
func (o *OpenVPNSelection) setDefaults(vpnProvider string) {
|
||||||
o.ConfFile = helpers.DefaultStringPtr(o.ConfFile, "")
|
o.ConfFile = helpers.DefaultPointer(o.ConfFile, "")
|
||||||
o.TCP = helpers.DefaultBool(o.TCP, false)
|
o.TCP = helpers.DefaultPointer(o.TCP, false)
|
||||||
o.CustomPort = helpers.DefaultUint16(o.CustomPort, 0)
|
o.CustomPort = helpers.DefaultPointer(o.CustomPort, 0)
|
||||||
|
|
||||||
var defaultEncPreset string
|
var defaultEncPreset string
|
||||||
if vpnProvider == providers.PrivateInternetAccess {
|
if vpnProvider == providers.PrivateInternetAccess {
|
||||||
defaultEncPreset = presets.Strong
|
defaultEncPreset = presets.Strong
|
||||||
}
|
}
|
||||||
o.PIAEncPreset = helpers.DefaultStringPtr(o.PIAEncPreset, defaultEncPreset)
|
o.PIAEncPreset = helpers.DefaultPointer(o.PIAEncPreset, defaultEncPreset)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o OpenVPNSelection) String() string {
|
func (o OpenVPNSelection) String() string {
|
||||||
|
|||||||
@@ -47,24 +47,24 @@ func (p PortForwarding) validate(vpnProvider string) (err error) {
|
|||||||
|
|
||||||
func (p *PortForwarding) copy() (copied PortForwarding) {
|
func (p *PortForwarding) copy() (copied PortForwarding) {
|
||||||
return PortForwarding{
|
return PortForwarding{
|
||||||
Enabled: helpers.CopyBoolPtr(p.Enabled),
|
Enabled: helpers.CopyPointer(p.Enabled),
|
||||||
Filepath: helpers.CopyStringPtr(p.Filepath),
|
Filepath: helpers.CopyPointer(p.Filepath),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PortForwarding) mergeWith(other PortForwarding) {
|
func (p *PortForwarding) mergeWith(other PortForwarding) {
|
||||||
p.Enabled = helpers.MergeWithBool(p.Enabled, other.Enabled)
|
p.Enabled = helpers.MergeWithPointer(p.Enabled, other.Enabled)
|
||||||
p.Filepath = helpers.MergeWithStringPtr(p.Filepath, other.Filepath)
|
p.Filepath = helpers.MergeWithPointer(p.Filepath, other.Filepath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PortForwarding) overrideWith(other PortForwarding) {
|
func (p *PortForwarding) overrideWith(other PortForwarding) {
|
||||||
p.Enabled = helpers.OverrideWithBool(p.Enabled, other.Enabled)
|
p.Enabled = helpers.OverrideWithPointer(p.Enabled, other.Enabled)
|
||||||
p.Filepath = helpers.OverrideWithStringPtr(p.Filepath, other.Filepath)
|
p.Filepath = helpers.OverrideWithPointer(p.Filepath, other.Filepath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PortForwarding) setDefaults() {
|
func (p *PortForwarding) setDefaults() {
|
||||||
p.Enabled = helpers.DefaultBool(p.Enabled, false)
|
p.Enabled = helpers.DefaultPointer(p.Enabled, false)
|
||||||
p.Filepath = helpers.DefaultStringPtr(p.Filepath, "/tmp/gluetun/forwarded_port")
|
p.Filepath = helpers.DefaultPointer(p.Filepath, "/tmp/gluetun/forwarded_port")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p PortForwarding) String() string {
|
func (p PortForwarding) String() string {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func (p *Provider) validate(vpnType string, storage Storage) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !helpers.IsOneOf(*p.Name, validNames...) {
|
if !helpers.IsOneOf(*p.Name, validNames...) {
|
||||||
return fmt.Errorf("%w: %q can only be one of %s",
|
return fmt.Errorf("%w for Wireguard: %q can only be one of %s",
|
||||||
ErrVPNProviderNameNotValid, *p.Name, helpers.ChoicesOrString(validNames))
|
ErrVPNProviderNameNotValid, *p.Name, helpers.ChoicesOrString(validNames))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,26 +58,26 @@ func (p *Provider) validate(vpnType string, storage Storage) (err error) {
|
|||||||
|
|
||||||
func (p *Provider) copy() (copied Provider) {
|
func (p *Provider) copy() (copied Provider) {
|
||||||
return Provider{
|
return Provider{
|
||||||
Name: helpers.CopyStringPtr(p.Name),
|
Name: helpers.CopyPointer(p.Name),
|
||||||
ServerSelection: p.ServerSelection.copy(),
|
ServerSelection: p.ServerSelection.copy(),
|
||||||
PortForwarding: p.PortForwarding.copy(),
|
PortForwarding: p.PortForwarding.copy(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) mergeWith(other Provider) {
|
func (p *Provider) mergeWith(other Provider) {
|
||||||
p.Name = helpers.MergeWithStringPtr(p.Name, other.Name)
|
p.Name = helpers.MergeWithPointer(p.Name, other.Name)
|
||||||
p.ServerSelection.mergeWith(other.ServerSelection)
|
p.ServerSelection.mergeWith(other.ServerSelection)
|
||||||
p.PortForwarding.mergeWith(other.PortForwarding)
|
p.PortForwarding.mergeWith(other.PortForwarding)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) overrideWith(other Provider) {
|
func (p *Provider) overrideWith(other Provider) {
|
||||||
p.Name = helpers.OverrideWithStringPtr(p.Name, other.Name)
|
p.Name = helpers.OverrideWithPointer(p.Name, other.Name)
|
||||||
p.ServerSelection.overrideWith(other.ServerSelection)
|
p.ServerSelection.overrideWith(other.ServerSelection)
|
||||||
p.PortForwarding.overrideWith(other.PortForwarding)
|
p.PortForwarding.overrideWith(other.PortForwarding)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) setDefaults() {
|
func (p *Provider) setDefaults() {
|
||||||
p.Name = helpers.DefaultStringPtr(p.Name, providers.PrivateInternetAccess)
|
p.Name = helpers.DefaultPointer(p.Name, providers.PrivateInternetAccess)
|
||||||
p.ServerSelection.setDefaults(*p.Name)
|
p.ServerSelection.setDefaults(*p.Name)
|
||||||
p.PortForwarding.setDefaults()
|
p.PortForwarding.setDefaults()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,25 +42,25 @@ func (p PublicIP) validate() (err error) {
|
|||||||
|
|
||||||
func (p *PublicIP) copy() (copied PublicIP) {
|
func (p *PublicIP) copy() (copied PublicIP) {
|
||||||
return PublicIP{
|
return PublicIP{
|
||||||
Period: helpers.CopyDurationPtr(p.Period),
|
Period: helpers.CopyPointer(p.Period),
|
||||||
IPFilepath: helpers.CopyStringPtr(p.IPFilepath),
|
IPFilepath: helpers.CopyPointer(p.IPFilepath),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PublicIP) mergeWith(other PublicIP) {
|
func (p *PublicIP) mergeWith(other PublicIP) {
|
||||||
p.Period = helpers.MergeWithDurationPtr(p.Period, other.Period)
|
p.Period = helpers.MergeWithPointer(p.Period, other.Period)
|
||||||
p.IPFilepath = helpers.MergeWithStringPtr(p.IPFilepath, other.IPFilepath)
|
p.IPFilepath = helpers.MergeWithPointer(p.IPFilepath, other.IPFilepath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PublicIP) overrideWith(other PublicIP) {
|
func (p *PublicIP) overrideWith(other PublicIP) {
|
||||||
p.Period = helpers.OverrideWithDurationPtr(p.Period, other.Period)
|
p.Period = helpers.OverrideWithPointer(p.Period, other.Period)
|
||||||
p.IPFilepath = helpers.OverrideWithStringPtr(p.IPFilepath, other.IPFilepath)
|
p.IPFilepath = helpers.OverrideWithPointer(p.IPFilepath, other.IPFilepath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PublicIP) setDefaults() {
|
func (p *PublicIP) setDefaults() {
|
||||||
const defaultPeriod = 12 * time.Hour
|
const defaultPeriod = 12 * time.Hour
|
||||||
p.Period = helpers.DefaultDurationPtr(p.Period, defaultPeriod)
|
p.Period = helpers.DefaultPointer(p.Period, defaultPeriod)
|
||||||
p.IPFilepath = helpers.DefaultStringPtr(p.IPFilepath, "/tmp/gluetun/ip")
|
p.IPFilepath = helpers.DefaultPointer(p.IPFilepath, "/tmp/gluetun/ip")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p PublicIP) String() string {
|
func (p PublicIP) String() string {
|
||||||
|
|||||||
@@ -43,29 +43,29 @@ func (c ControlServer) validate() (err error) {
|
|||||||
|
|
||||||
func (c *ControlServer) copy() (copied ControlServer) {
|
func (c *ControlServer) copy() (copied ControlServer) {
|
||||||
return ControlServer{
|
return ControlServer{
|
||||||
Address: helpers.CopyStringPtr(c.Address),
|
Address: helpers.CopyPointer(c.Address),
|
||||||
Log: helpers.CopyBoolPtr(c.Log),
|
Log: helpers.CopyPointer(c.Log),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
// mergeWith merges the other settings into any
|
||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (c *ControlServer) mergeWith(other ControlServer) {
|
func (c *ControlServer) mergeWith(other ControlServer) {
|
||||||
c.Address = helpers.MergeWithStringPtr(c.Address, other.Address)
|
c.Address = helpers.MergeWithPointer(c.Address, other.Address)
|
||||||
c.Log = helpers.MergeWithBool(c.Log, other.Log)
|
c.Log = helpers.MergeWithPointer(c.Log, other.Log)
|
||||||
}
|
}
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (c *ControlServer) overrideWith(other ControlServer) {
|
func (c *ControlServer) overrideWith(other ControlServer) {
|
||||||
c.Address = helpers.OverrideWithStringPtr(c.Address, other.Address)
|
c.Address = helpers.OverrideWithPointer(c.Address, other.Address)
|
||||||
c.Log = helpers.OverrideWithBool(c.Log, other.Log)
|
c.Log = helpers.OverrideWithPointer(c.Log, other.Log)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ControlServer) setDefaults() {
|
func (c *ControlServer) setDefaults() {
|
||||||
c.Address = helpers.DefaultStringPtr(c.Address, ":8000")
|
c.Address = helpers.DefaultPointer(c.Address, ":8000")
|
||||||
c.Log = helpers.DefaultBool(c.Log, true)
|
c.Log = helpers.DefaultPointer(c.Log, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c ControlServer) String() string {
|
func (c ControlServer) String() string {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package settings
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||||
@@ -21,10 +21,10 @@ type ServerSelection struct { //nolint:maligned
|
|||||||
VPN string
|
VPN string
|
||||||
// TargetIP is the server endpoint IP address to use.
|
// TargetIP is the server endpoint IP address to use.
|
||||||
// It will override any IP address from the picked
|
// It will override any IP address from the picked
|
||||||
// built-in server. It cannot be nil in the internal
|
// built-in server. It cannot be the empty value in the internal
|
||||||
// state, and can be set to an empty net.IP{} to indicate
|
// state, and can be set to the unspecified address to indicate
|
||||||
// there is not target IP address to use.
|
// there is not target IP address to use.
|
||||||
TargetIP net.IP
|
TargetIP netip.Addr
|
||||||
// Counties is the list of countries to filter VPN servers with.
|
// Counties is the list of countries to filter VPN servers with.
|
||||||
Countries []string
|
Countries []string
|
||||||
// Regions is the list of regions to filter VPN servers with.
|
// Regions is the list of regions to filter VPN servers with.
|
||||||
@@ -118,7 +118,7 @@ func (ss *ServerSelection) validate(vpnServiceProvider string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *ss.FreeOnly && *ss.PremiumOnly {
|
if *ss.FreeOnly && *ss.PremiumOnly {
|
||||||
return ErrFreePremiumBothSet
|
return fmt.Errorf("%w", ErrFreePremiumBothSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *ss.StreamOnly &&
|
if *ss.StreamOnly &&
|
||||||
@@ -202,19 +202,19 @@ func validateServerFilters(settings ServerSelection, filterChoices models.Filter
|
|||||||
func (ss *ServerSelection) copy() (copied ServerSelection) {
|
func (ss *ServerSelection) copy() (copied ServerSelection) {
|
||||||
return ServerSelection{
|
return ServerSelection{
|
||||||
VPN: ss.VPN,
|
VPN: ss.VPN,
|
||||||
TargetIP: helpers.CopyIP(ss.TargetIP),
|
TargetIP: ss.TargetIP,
|
||||||
Countries: helpers.CopyStringSlice(ss.Countries),
|
Countries: helpers.CopySlice(ss.Countries),
|
||||||
Regions: helpers.CopyStringSlice(ss.Regions),
|
Regions: helpers.CopySlice(ss.Regions),
|
||||||
Cities: helpers.CopyStringSlice(ss.Cities),
|
Cities: helpers.CopySlice(ss.Cities),
|
||||||
ISPs: helpers.CopyStringSlice(ss.ISPs),
|
ISPs: helpers.CopySlice(ss.ISPs),
|
||||||
Hostnames: helpers.CopyStringSlice(ss.Hostnames),
|
Hostnames: helpers.CopySlice(ss.Hostnames),
|
||||||
Names: helpers.CopyStringSlice(ss.Names),
|
Names: helpers.CopySlice(ss.Names),
|
||||||
Numbers: helpers.CopyUint16Slice(ss.Numbers),
|
Numbers: helpers.CopySlice(ss.Numbers),
|
||||||
OwnedOnly: helpers.CopyBoolPtr(ss.OwnedOnly),
|
OwnedOnly: helpers.CopyPointer(ss.OwnedOnly),
|
||||||
FreeOnly: helpers.CopyBoolPtr(ss.FreeOnly),
|
FreeOnly: helpers.CopyPointer(ss.FreeOnly),
|
||||||
PremiumOnly: helpers.CopyBoolPtr(ss.PremiumOnly),
|
PremiumOnly: helpers.CopyPointer(ss.PremiumOnly),
|
||||||
StreamOnly: helpers.CopyBoolPtr(ss.StreamOnly),
|
StreamOnly: helpers.CopyPointer(ss.StreamOnly),
|
||||||
MultiHopOnly: helpers.CopyBoolPtr(ss.MultiHopOnly),
|
MultiHopOnly: helpers.CopyPointer(ss.MultiHopOnly),
|
||||||
OpenVPN: ss.OpenVPN.copy(),
|
OpenVPN: ss.OpenVPN.copy(),
|
||||||
Wireguard: ss.Wireguard.copy(),
|
Wireguard: ss.Wireguard.copy(),
|
||||||
}
|
}
|
||||||
@@ -223,18 +223,18 @@ func (ss *ServerSelection) copy() (copied ServerSelection) {
|
|||||||
func (ss *ServerSelection) mergeWith(other ServerSelection) {
|
func (ss *ServerSelection) mergeWith(other ServerSelection) {
|
||||||
ss.VPN = helpers.MergeWithString(ss.VPN, other.VPN)
|
ss.VPN = helpers.MergeWithString(ss.VPN, other.VPN)
|
||||||
ss.TargetIP = helpers.MergeWithIP(ss.TargetIP, other.TargetIP)
|
ss.TargetIP = helpers.MergeWithIP(ss.TargetIP, other.TargetIP)
|
||||||
ss.Countries = helpers.MergeStringSlices(ss.Countries, other.Countries)
|
ss.Countries = helpers.MergeSlices(ss.Countries, other.Countries)
|
||||||
ss.Regions = helpers.MergeStringSlices(ss.Regions, other.Regions)
|
ss.Regions = helpers.MergeSlices(ss.Regions, other.Regions)
|
||||||
ss.Cities = helpers.MergeStringSlices(ss.Cities, other.Cities)
|
ss.Cities = helpers.MergeSlices(ss.Cities, other.Cities)
|
||||||
ss.ISPs = helpers.MergeStringSlices(ss.ISPs, other.ISPs)
|
ss.ISPs = helpers.MergeSlices(ss.ISPs, other.ISPs)
|
||||||
ss.Hostnames = helpers.MergeStringSlices(ss.Hostnames, other.Hostnames)
|
ss.Hostnames = helpers.MergeSlices(ss.Hostnames, other.Hostnames)
|
||||||
ss.Names = helpers.MergeStringSlices(ss.Names, other.Names)
|
ss.Names = helpers.MergeSlices(ss.Names, other.Names)
|
||||||
ss.Numbers = helpers.MergeUint16Slices(ss.Numbers, other.Numbers)
|
ss.Numbers = helpers.MergeSlices(ss.Numbers, other.Numbers)
|
||||||
ss.OwnedOnly = helpers.MergeWithBool(ss.OwnedOnly, other.OwnedOnly)
|
ss.OwnedOnly = helpers.MergeWithPointer(ss.OwnedOnly, other.OwnedOnly)
|
||||||
ss.FreeOnly = helpers.MergeWithBool(ss.FreeOnly, other.FreeOnly)
|
ss.FreeOnly = helpers.MergeWithPointer(ss.FreeOnly, other.FreeOnly)
|
||||||
ss.PremiumOnly = helpers.MergeWithBool(ss.PremiumOnly, other.PremiumOnly)
|
ss.PremiumOnly = helpers.MergeWithPointer(ss.PremiumOnly, other.PremiumOnly)
|
||||||
ss.StreamOnly = helpers.MergeWithBool(ss.StreamOnly, other.StreamOnly)
|
ss.StreamOnly = helpers.MergeWithPointer(ss.StreamOnly, other.StreamOnly)
|
||||||
ss.MultiHopOnly = helpers.MergeWithBool(ss.MultiHopOnly, other.MultiHopOnly)
|
ss.MultiHopOnly = helpers.MergeWithPointer(ss.MultiHopOnly, other.MultiHopOnly)
|
||||||
|
|
||||||
ss.OpenVPN.mergeWith(other.OpenVPN)
|
ss.OpenVPN.mergeWith(other.OpenVPN)
|
||||||
ss.Wireguard.mergeWith(other.Wireguard)
|
ss.Wireguard.mergeWith(other.Wireguard)
|
||||||
@@ -243,30 +243,30 @@ func (ss *ServerSelection) mergeWith(other ServerSelection) {
|
|||||||
func (ss *ServerSelection) overrideWith(other ServerSelection) {
|
func (ss *ServerSelection) overrideWith(other ServerSelection) {
|
||||||
ss.VPN = helpers.OverrideWithString(ss.VPN, other.VPN)
|
ss.VPN = helpers.OverrideWithString(ss.VPN, other.VPN)
|
||||||
ss.TargetIP = helpers.OverrideWithIP(ss.TargetIP, other.TargetIP)
|
ss.TargetIP = helpers.OverrideWithIP(ss.TargetIP, other.TargetIP)
|
||||||
ss.Countries = helpers.OverrideWithStringSlice(ss.Countries, other.Countries)
|
ss.Countries = helpers.OverrideWithSlice(ss.Countries, other.Countries)
|
||||||
ss.Regions = helpers.OverrideWithStringSlice(ss.Regions, other.Regions)
|
ss.Regions = helpers.OverrideWithSlice(ss.Regions, other.Regions)
|
||||||
ss.Cities = helpers.OverrideWithStringSlice(ss.Cities, other.Cities)
|
ss.Cities = helpers.OverrideWithSlice(ss.Cities, other.Cities)
|
||||||
ss.ISPs = helpers.OverrideWithStringSlice(ss.ISPs, other.ISPs)
|
ss.ISPs = helpers.OverrideWithSlice(ss.ISPs, other.ISPs)
|
||||||
ss.Hostnames = helpers.OverrideWithStringSlice(ss.Hostnames, other.Hostnames)
|
ss.Hostnames = helpers.OverrideWithSlice(ss.Hostnames, other.Hostnames)
|
||||||
ss.Names = helpers.OverrideWithStringSlice(ss.Names, other.Names)
|
ss.Names = helpers.OverrideWithSlice(ss.Names, other.Names)
|
||||||
ss.Numbers = helpers.OverrideWithUint16Slice(ss.Numbers, other.Numbers)
|
ss.Numbers = helpers.OverrideWithSlice(ss.Numbers, other.Numbers)
|
||||||
ss.OwnedOnly = helpers.OverrideWithBool(ss.OwnedOnly, other.OwnedOnly)
|
ss.OwnedOnly = helpers.OverrideWithPointer(ss.OwnedOnly, other.OwnedOnly)
|
||||||
ss.FreeOnly = helpers.OverrideWithBool(ss.FreeOnly, other.FreeOnly)
|
ss.FreeOnly = helpers.OverrideWithPointer(ss.FreeOnly, other.FreeOnly)
|
||||||
ss.PremiumOnly = helpers.OverrideWithBool(ss.PremiumOnly, other.PremiumOnly)
|
ss.PremiumOnly = helpers.OverrideWithPointer(ss.PremiumOnly, other.PremiumOnly)
|
||||||
ss.StreamOnly = helpers.OverrideWithBool(ss.StreamOnly, other.StreamOnly)
|
ss.StreamOnly = helpers.OverrideWithPointer(ss.StreamOnly, other.StreamOnly)
|
||||||
ss.MultiHopOnly = helpers.OverrideWithBool(ss.MultiHopOnly, other.MultiHopOnly)
|
ss.MultiHopOnly = helpers.OverrideWithPointer(ss.MultiHopOnly, other.MultiHopOnly)
|
||||||
ss.OpenVPN.overrideWith(other.OpenVPN)
|
ss.OpenVPN.overrideWith(other.OpenVPN)
|
||||||
ss.Wireguard.overrideWith(other.Wireguard)
|
ss.Wireguard.overrideWith(other.Wireguard)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *ServerSelection) setDefaults(vpnProvider string) {
|
func (ss *ServerSelection) setDefaults(vpnProvider string) {
|
||||||
ss.VPN = helpers.DefaultString(ss.VPN, vpn.OpenVPN)
|
ss.VPN = helpers.DefaultString(ss.VPN, vpn.OpenVPN)
|
||||||
ss.TargetIP = helpers.DefaultIP(ss.TargetIP, net.IP{})
|
ss.TargetIP = helpers.DefaultIP(ss.TargetIP, netip.IPv4Unspecified())
|
||||||
ss.OwnedOnly = helpers.DefaultBool(ss.OwnedOnly, false)
|
ss.OwnedOnly = helpers.DefaultPointer(ss.OwnedOnly, false)
|
||||||
ss.FreeOnly = helpers.DefaultBool(ss.FreeOnly, false)
|
ss.FreeOnly = helpers.DefaultPointer(ss.FreeOnly, false)
|
||||||
ss.PremiumOnly = helpers.DefaultBool(ss.PremiumOnly, false)
|
ss.PremiumOnly = helpers.DefaultPointer(ss.PremiumOnly, false)
|
||||||
ss.StreamOnly = helpers.DefaultBool(ss.StreamOnly, false)
|
ss.StreamOnly = helpers.DefaultPointer(ss.StreamOnly, false)
|
||||||
ss.MultiHopOnly = helpers.DefaultBool(ss.MultiHopOnly, false)
|
ss.MultiHopOnly = helpers.DefaultPointer(ss.MultiHopOnly, false)
|
||||||
ss.OpenVPN.setDefaults(vpnProvider)
|
ss.OpenVPN.setDefaults(vpnProvider)
|
||||||
ss.Wireguard.setDefaults()
|
ss.Wireguard.setDefaults()
|
||||||
}
|
}
|
||||||
@@ -278,7 +278,7 @@ func (ss ServerSelection) String() string {
|
|||||||
func (ss ServerSelection) toLinesNode() (node *gotree.Node) {
|
func (ss ServerSelection) toLinesNode() (node *gotree.Node) {
|
||||||
node = gotree.New("Server selection settings:")
|
node = gotree.New("Server selection settings:")
|
||||||
node.Appendf("VPN type: %s", ss.VPN)
|
node.Appendf("VPN type: %s", ss.VPN)
|
||||||
if len(ss.TargetIP) > 0 {
|
if !ss.TargetIP.IsUnspecified() {
|
||||||
node.Appendf("Target IP address: %s", ss.TargetIP)
|
node.Appendf("Target IP address: %s", ss.TargetIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ package settings
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||||
|
"github.com/qdm12/gluetun/internal/constants/openvpn"
|
||||||
|
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||||
|
"github.com/qdm12/gluetun/internal/constants/vpn"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/gluetun/internal/pprof"
|
"github.com/qdm12/gluetun/internal/pprof"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
@@ -31,7 +35,7 @@ type Storage interface {
|
|||||||
// Validate validates all the settings and returns an error
|
// Validate validates all the settings and returns an error
|
||||||
// if one of them is not valid.
|
// if one of them is not valid.
|
||||||
// TODO v4 remove pointer for receiver (because of Surfshark).
|
// TODO v4 remove pointer for receiver (because of Surfshark).
|
||||||
func (s *Settings) Validate(storage Storage) (err error) {
|
func (s *Settings) Validate(storage Storage, ipv6Supported bool) (err error) {
|
||||||
nameToValidation := map[string]func() error{
|
nameToValidation := map[string]func() error{
|
||||||
"control server": s.ControlServer.validate,
|
"control server": s.ControlServer.validate,
|
||||||
"dns": s.DNS.validate,
|
"dns": s.DNS.validate,
|
||||||
@@ -46,7 +50,7 @@ func (s *Settings) Validate(storage Storage) (err error) {
|
|||||||
"version": s.Version.validate,
|
"version": s.Version.validate,
|
||||||
// Pprof validation done in pprof constructor
|
// Pprof validation done in pprof constructor
|
||||||
"VPN": func() error {
|
"VPN": func() error {
|
||||||
return s.VPN.Validate(storage)
|
return s.VPN.Validate(storage, ipv6Supported)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +99,7 @@ func (s *Settings) MergeWith(other Settings) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Settings) OverrideWith(other Settings,
|
func (s *Settings) OverrideWith(other Settings,
|
||||||
storage Storage) (err error) {
|
storage Storage, ipv6Supported bool) (err error) {
|
||||||
patchedSettings := s.copy()
|
patchedSettings := s.copy()
|
||||||
patchedSettings.ControlServer.overrideWith(other.ControlServer)
|
patchedSettings.ControlServer.overrideWith(other.ControlServer)
|
||||||
patchedSettings.DNS.overrideWith(other.DNS)
|
patchedSettings.DNS.overrideWith(other.DNS)
|
||||||
@@ -110,7 +114,7 @@ func (s *Settings) OverrideWith(other Settings,
|
|||||||
patchedSettings.Version.overrideWith(other.Version)
|
patchedSettings.Version.overrideWith(other.Version)
|
||||||
patchedSettings.VPN.OverrideWith(other.VPN)
|
patchedSettings.VPN.OverrideWith(other.VPN)
|
||||||
patchedSettings.Pprof.OverrideWith(other.Pprof)
|
patchedSettings.Pprof.OverrideWith(other.Pprof)
|
||||||
err = patchedSettings.Validate(storage)
|
err = patchedSettings.Validate(storage, ipv6Supported)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -157,3 +161,37 @@ func (s Settings) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s Settings) Warnings() (warnings []string) {
|
||||||
|
if *s.VPN.Provider.Name == providers.HideMyAss {
|
||||||
|
warnings = append(warnings, "HideMyAss dropped support for Linux OpenVPN "+
|
||||||
|
" so this will likely not work anymore. See https://github.com/qdm12/gluetun/issues/1498.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if helpers.IsOneOf(*s.VPN.Provider.Name, providers.SlickVPN) &&
|
||||||
|
s.VPN.Type == vpn.OpenVPN {
|
||||||
|
if s.VPN.OpenVPN.Version == openvpn.Openvpn24 {
|
||||||
|
warnings = append(warnings, "OpenVPN 2.4 uses OpenSSL 1.1.1 "+
|
||||||
|
"which allows the usage of weak security in today's standards. "+
|
||||||
|
"This can be ok if good security is enforced by the VPN provider. "+
|
||||||
|
"However, "+*s.VPN.Provider.Name+" uses weak security so you should use "+
|
||||||
|
"OpenVPN 2.5 to enforce good security practices.")
|
||||||
|
} else {
|
||||||
|
warnings = append(warnings, "OpenVPN 2.5 uses OpenSSL 3 "+
|
||||||
|
"which prohibits the usage of weak security in today's standards. "+
|
||||||
|
*s.VPN.Provider.Name+" uses weak security which is out "+
|
||||||
|
"of Gluetun's control so the only workaround is to allow such weaknesses "+
|
||||||
|
`using the OpenVPN option tls-cipher "DEFAULT:@SECLEVEL=0". `+
|
||||||
|
"You might want to reach to your provider so they upgrade their certificates. "+
|
||||||
|
"Once this is done, you will have to let the Gluetun maintainers know "+
|
||||||
|
"by creating an issue, attaching the new certificate and we will update Gluetun.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.VPN.OpenVPN.Version == openvpn.Openvpn24 {
|
||||||
|
warnings = append(warnings, "OpenVPN 2.4 will be removed in release v3.34.0 (around June 2023). "+
|
||||||
|
"Please create an issue if you have a compelling reason to keep it.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return warnings
|
||||||
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ func Test_Settings_String(t *testing.T) {
|
|||||||
├── Health settings:
|
├── Health settings:
|
||||||
| ├── Server listening address: 127.0.0.1:9999
|
| ├── Server listening address: 127.0.0.1:9999
|
||||||
| ├── Target address: cloudflare.com:443
|
| ├── Target address: cloudflare.com:443
|
||||||
|
| ├── Duration to wait after success: 5s
|
||||||
| ├── Read header timeout: 100ms
|
| ├── Read header timeout: 100ms
|
||||||
| ├── Read timeout: 500ms
|
| ├── Read timeout: 500ms
|
||||||
| └── VPN wait durations:
|
| └── VPN wait durations:
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func (s Shadowsocks) validate() (err error) {
|
|||||||
|
|
||||||
func (s *Shadowsocks) copy() (copied Shadowsocks) {
|
func (s *Shadowsocks) copy() (copied Shadowsocks) {
|
||||||
return Shadowsocks{
|
return Shadowsocks{
|
||||||
Enabled: helpers.CopyBoolPtr(s.Enabled),
|
Enabled: helpers.CopyPointer(s.Enabled),
|
||||||
Settings: s.Settings.Copy(),
|
Settings: s.Settings.Copy(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ func (s *Shadowsocks) copy() (copied Shadowsocks) {
|
|||||||
// mergeWith merges the other settings into any
|
// mergeWith merges the other settings into any
|
||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (s *Shadowsocks) mergeWith(other Shadowsocks) {
|
func (s *Shadowsocks) mergeWith(other Shadowsocks) {
|
||||||
s.Enabled = helpers.MergeWithBool(s.Enabled, other.Enabled)
|
s.Enabled = helpers.MergeWithPointer(s.Enabled, other.Enabled)
|
||||||
s.Settings.MergeWith(other.Settings)
|
s.Settings.MergeWith(other.Settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,12 +37,12 @@ func (s *Shadowsocks) mergeWith(other Shadowsocks) {
|
|||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (s *Shadowsocks) overrideWith(other Shadowsocks) {
|
func (s *Shadowsocks) overrideWith(other Shadowsocks) {
|
||||||
s.Enabled = helpers.OverrideWithBool(s.Enabled, other.Enabled)
|
s.Enabled = helpers.OverrideWithPointer(s.Enabled, other.Enabled)
|
||||||
s.Settings.OverrideWith(other.Settings)
|
s.Settings.OverrideWith(other.Settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Shadowsocks) setDefaults() {
|
func (s *Shadowsocks) setDefaults() {
|
||||||
s.Enabled = helpers.DefaultBool(s.Enabled, false)
|
s.Enabled = helpers.DefaultPointer(s.Enabled, false)
|
||||||
s.Settings.SetDefaults()
|
s.Settings.SetDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,28 +19,28 @@ func (s System) validate() (err error) {
|
|||||||
|
|
||||||
func (s *System) copy() (copied System) {
|
func (s *System) copy() (copied System) {
|
||||||
return System{
|
return System{
|
||||||
PUID: helpers.CopyUint32Ptr(s.PUID),
|
PUID: helpers.CopyPointer(s.PUID),
|
||||||
PGID: helpers.CopyUint32Ptr(s.PGID),
|
PGID: helpers.CopyPointer(s.PGID),
|
||||||
Timezone: s.Timezone,
|
Timezone: s.Timezone,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *System) mergeWith(other System) {
|
func (s *System) mergeWith(other System) {
|
||||||
s.PUID = helpers.MergeWithUint32(s.PUID, other.PUID)
|
s.PUID = helpers.MergeWithPointer(s.PUID, other.PUID)
|
||||||
s.PGID = helpers.MergeWithUint32(s.PGID, other.PGID)
|
s.PGID = helpers.MergeWithPointer(s.PGID, other.PGID)
|
||||||
s.Timezone = helpers.MergeWithString(s.Timezone, other.Timezone)
|
s.Timezone = helpers.MergeWithString(s.Timezone, other.Timezone)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *System) overrideWith(other System) {
|
func (s *System) overrideWith(other System) {
|
||||||
s.PUID = helpers.OverrideWithUint32(s.PUID, other.PUID)
|
s.PUID = helpers.OverrideWithPointer(s.PUID, other.PUID)
|
||||||
s.PGID = helpers.OverrideWithUint32(s.PGID, other.PGID)
|
s.PGID = helpers.OverrideWithPointer(s.PGID, other.PGID)
|
||||||
s.Timezone = helpers.OverrideWithString(s.Timezone, other.Timezone)
|
s.Timezone = helpers.OverrideWithString(s.Timezone, other.Timezone)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *System) setDefaults() {
|
func (s *System) setDefaults() {
|
||||||
const defaultID = 1000
|
const defaultID = 1000
|
||||||
s.PUID = helpers.DefaultUint32(s.PUID, defaultID)
|
s.PUID = helpers.DefaultPointer(s.PUID, defaultID)
|
||||||
s.PGID = helpers.DefaultUint32(s.PGID, defaultID)
|
s.PGID = helpers.DefaultPointer(s.PGID, defaultID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s System) String() string {
|
func (s System) String() string {
|
||||||
|
|||||||
@@ -3,13 +3,12 @@ package settings
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/dns/pkg/provider"
|
"github.com/qdm12/dns/pkg/provider"
|
||||||
"github.com/qdm12/dns/pkg/unbound"
|
"github.com/qdm12/dns/pkg/unbound"
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
"inet.af/netaddr"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Unbound is settings for the Unbound program.
|
// Unbound is settings for the Unbound program.
|
||||||
@@ -21,7 +20,7 @@ type Unbound struct {
|
|||||||
VerbosityDetailsLevel *uint8
|
VerbosityDetailsLevel *uint8
|
||||||
ValidationLogLevel *uint8
|
ValidationLogLevel *uint8
|
||||||
Username string
|
Username string
|
||||||
Allowed []netaddr.IPPrefix
|
Allowed []netip.Prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Unbound) setDefaults() {
|
func (u *Unbound) setDefaults() {
|
||||||
@@ -31,22 +30,22 @@ func (u *Unbound) setDefaults() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u.Caching = helpers.DefaultBool(u.Caching, true)
|
u.Caching = helpers.DefaultPointer(u.Caching, true)
|
||||||
u.IPv6 = helpers.DefaultBool(u.IPv6, false)
|
u.IPv6 = helpers.DefaultPointer(u.IPv6, false)
|
||||||
|
|
||||||
const defaultVerbosityLevel = 1
|
const defaultVerbosityLevel = 1
|
||||||
u.VerbosityLevel = helpers.DefaultUint8(u.VerbosityLevel, defaultVerbosityLevel)
|
u.VerbosityLevel = helpers.DefaultPointer(u.VerbosityLevel, defaultVerbosityLevel)
|
||||||
|
|
||||||
const defaultVerbosityDetailsLevel = 0
|
const defaultVerbosityDetailsLevel = 0
|
||||||
u.VerbosityDetailsLevel = helpers.DefaultUint8(u.VerbosityDetailsLevel, defaultVerbosityDetailsLevel)
|
u.VerbosityDetailsLevel = helpers.DefaultPointer(u.VerbosityDetailsLevel, defaultVerbosityDetailsLevel)
|
||||||
|
|
||||||
const defaultValidationLogLevel = 0
|
const defaultValidationLogLevel = 0
|
||||||
u.ValidationLogLevel = helpers.DefaultUint8(u.ValidationLogLevel, defaultValidationLogLevel)
|
u.ValidationLogLevel = helpers.DefaultPointer(u.ValidationLogLevel, defaultValidationLogLevel)
|
||||||
|
|
||||||
if u.Allowed == nil {
|
if u.Allowed == nil {
|
||||||
u.Allowed = []netaddr.IPPrefix{
|
u.Allowed = []netip.Prefix{
|
||||||
netaddr.IPPrefixFrom(netaddr.IPv4(0, 0, 0, 0), 0),
|
netip.PrefixFrom(netip.AddrFrom4([4]byte{}), 0),
|
||||||
netaddr.IPPrefixFrom(netaddr.IPv6Raw([16]byte{}), 0),
|
netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,37 +94,37 @@ func (u Unbound) validate() (err error) {
|
|||||||
|
|
||||||
func (u Unbound) copy() (copied Unbound) {
|
func (u Unbound) copy() (copied Unbound) {
|
||||||
return Unbound{
|
return Unbound{
|
||||||
Providers: helpers.CopyStringSlice(u.Providers),
|
Providers: helpers.CopySlice(u.Providers),
|
||||||
Caching: helpers.CopyBoolPtr(u.Caching),
|
Caching: helpers.CopyPointer(u.Caching),
|
||||||
IPv6: helpers.CopyBoolPtr(u.IPv6),
|
IPv6: helpers.CopyPointer(u.IPv6),
|
||||||
VerbosityLevel: helpers.CopyUint8Ptr(u.VerbosityLevel),
|
VerbosityLevel: helpers.CopyPointer(u.VerbosityLevel),
|
||||||
VerbosityDetailsLevel: helpers.CopyUint8Ptr(u.VerbosityDetailsLevel),
|
VerbosityDetailsLevel: helpers.CopyPointer(u.VerbosityDetailsLevel),
|
||||||
ValidationLogLevel: helpers.CopyUint8Ptr(u.ValidationLogLevel),
|
ValidationLogLevel: helpers.CopyPointer(u.ValidationLogLevel),
|
||||||
Username: u.Username,
|
Username: u.Username,
|
||||||
Allowed: helpers.CopyIPPrefixSlice(u.Allowed),
|
Allowed: helpers.CopySlice(u.Allowed),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Unbound) mergeWith(other Unbound) {
|
func (u *Unbound) mergeWith(other Unbound) {
|
||||||
u.Providers = helpers.MergeStringSlices(u.Providers, other.Providers)
|
u.Providers = helpers.MergeSlices(u.Providers, other.Providers)
|
||||||
u.Caching = helpers.MergeWithBool(u.Caching, other.Caching)
|
u.Caching = helpers.MergeWithPointer(u.Caching, other.Caching)
|
||||||
u.IPv6 = helpers.MergeWithBool(u.IPv6, other.IPv6)
|
u.IPv6 = helpers.MergeWithPointer(u.IPv6, other.IPv6)
|
||||||
u.VerbosityLevel = helpers.MergeWithUint8(u.VerbosityLevel, other.VerbosityLevel)
|
u.VerbosityLevel = helpers.MergeWithPointer(u.VerbosityLevel, other.VerbosityLevel)
|
||||||
u.VerbosityDetailsLevel = helpers.MergeWithUint8(u.VerbosityDetailsLevel, other.VerbosityDetailsLevel)
|
u.VerbosityDetailsLevel = helpers.MergeWithPointer(u.VerbosityDetailsLevel, other.VerbosityDetailsLevel)
|
||||||
u.ValidationLogLevel = helpers.MergeWithUint8(u.ValidationLogLevel, other.ValidationLogLevel)
|
u.ValidationLogLevel = helpers.MergeWithPointer(u.ValidationLogLevel, other.ValidationLogLevel)
|
||||||
u.Username = helpers.MergeWithString(u.Username, other.Username)
|
u.Username = helpers.MergeWithString(u.Username, other.Username)
|
||||||
u.Allowed = helpers.MergeIPPrefixesSlices(u.Allowed, other.Allowed)
|
u.Allowed = helpers.MergeSlices(u.Allowed, other.Allowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Unbound) overrideWith(other Unbound) {
|
func (u *Unbound) overrideWith(other Unbound) {
|
||||||
u.Providers = helpers.OverrideWithStringSlice(u.Providers, other.Providers)
|
u.Providers = helpers.OverrideWithSlice(u.Providers, other.Providers)
|
||||||
u.Caching = helpers.OverrideWithBool(u.Caching, other.Caching)
|
u.Caching = helpers.OverrideWithPointer(u.Caching, other.Caching)
|
||||||
u.IPv6 = helpers.OverrideWithBool(u.IPv6, other.IPv6)
|
u.IPv6 = helpers.OverrideWithPointer(u.IPv6, other.IPv6)
|
||||||
u.VerbosityLevel = helpers.OverrideWithUint8(u.VerbosityLevel, other.VerbosityLevel)
|
u.VerbosityLevel = helpers.OverrideWithPointer(u.VerbosityLevel, other.VerbosityLevel)
|
||||||
u.VerbosityDetailsLevel = helpers.OverrideWithUint8(u.VerbosityDetailsLevel, other.VerbosityDetailsLevel)
|
u.VerbosityDetailsLevel = helpers.OverrideWithPointer(u.VerbosityDetailsLevel, other.VerbosityDetailsLevel)
|
||||||
u.ValidationLogLevel = helpers.OverrideWithUint8(u.ValidationLogLevel, other.ValidationLogLevel)
|
u.ValidationLogLevel = helpers.OverrideWithPointer(u.ValidationLogLevel, other.ValidationLogLevel)
|
||||||
u.Username = helpers.OverrideWithString(u.Username, other.Username)
|
u.Username = helpers.OverrideWithString(u.Username, other.Username)
|
||||||
u.Allowed = helpers.OverrideWithIPPrefixesSlice(u.Allowed, other.Allowed)
|
u.Allowed = helpers.OverrideWithSlice(u.Allowed, other.Allowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u Unbound) ToUnboundFormat() (settings unbound.Settings, err error) {
|
func (u Unbound) ToUnboundFormat() (settings unbound.Settings, err error) {
|
||||||
@@ -149,20 +148,30 @@ func (u Unbound) ToUnboundFormat() (settings unbound.Settings, err error) {
|
|||||||
VerbosityDetailsLevel: *u.VerbosityDetailsLevel,
|
VerbosityDetailsLevel: *u.VerbosityDetailsLevel,
|
||||||
ValidationLogLevel: *u.ValidationLogLevel,
|
ValidationLogLevel: *u.ValidationLogLevel,
|
||||||
AccessControl: unbound.AccessControlSettings{
|
AccessControl: unbound.AccessControlSettings{
|
||||||
Allowed: u.Allowed,
|
Allowed: netipPrefixesToNetaddrIPPrefixes(u.Allowed),
|
||||||
},
|
},
|
||||||
Username: u.Username,
|
Username: u.Username,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u Unbound) GetFirstPlaintextIPv4() (ipv4 net.IP, err error) {
|
var (
|
||||||
|
ErrConvertingNetip = errors.New("converting net.IP to netip.Addr failed")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (u Unbound) GetFirstPlaintextIPv4() (ipv4 netip.Addr, err error) {
|
||||||
s := u.Providers[0]
|
s := u.Providers[0]
|
||||||
provider, err := provider.Parse(s)
|
provider, err := provider.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return ipv4, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider.DNS().IPv4[0], nil
|
ip := provider.DNS().IPv4[0]
|
||||||
|
ipv4, ok := netip.AddrFromSlice(ip)
|
||||||
|
if !ok {
|
||||||
|
return ipv4, fmt.Errorf("%w: for ip %s (%#v)",
|
||||||
|
ErrConvertingNetip, ip, ip)
|
||||||
|
}
|
||||||
|
return ipv4.Unmap(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u Unbound) String() string {
|
func (u Unbound) String() string {
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package settings
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"net/netip"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"inet.af/netaddr"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Unbound_JSON(t *testing.T) {
|
func Test_Unbound_JSON(t *testing.T) {
|
||||||
@@ -20,9 +20,9 @@ func Test_Unbound_JSON(t *testing.T) {
|
|||||||
VerbosityDetailsLevel: nil,
|
VerbosityDetailsLevel: nil,
|
||||||
ValidationLogLevel: uint8Ptr(0),
|
ValidationLogLevel: uint8Ptr(0),
|
||||||
Username: "user",
|
Username: "user",
|
||||||
Allowed: []netaddr.IPPrefix{
|
Allowed: []netip.Prefix{
|
||||||
netaddr.IPPrefixFrom(netaddr.IPv4(0, 0, 0, 0), 0),
|
netip.PrefixFrom(netip.AddrFrom4([4]byte{}), 0),
|
||||||
netaddr.IPPrefixFrom(netaddr.IPv6Raw([16]byte{}), 0),
|
netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 0),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,34 +63,34 @@ func (u Updater) Validate() (err error) {
|
|||||||
|
|
||||||
func (u *Updater) copy() (copied Updater) {
|
func (u *Updater) copy() (copied Updater) {
|
||||||
return Updater{
|
return Updater{
|
||||||
Period: helpers.CopyDurationPtr(u.Period),
|
Period: helpers.CopyPointer(u.Period),
|
||||||
DNSAddress: u.DNSAddress,
|
DNSAddress: u.DNSAddress,
|
||||||
MinRatio: u.MinRatio,
|
MinRatio: u.MinRatio,
|
||||||
Providers: helpers.CopyStringSlice(u.Providers),
|
Providers: helpers.CopySlice(u.Providers),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
// mergeWith merges the other settings into any
|
||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (u *Updater) mergeWith(other Updater) {
|
func (u *Updater) mergeWith(other Updater) {
|
||||||
u.Period = helpers.MergeWithDurationPtr(u.Period, other.Period)
|
u.Period = helpers.MergeWithPointer(u.Period, other.Period)
|
||||||
u.DNSAddress = helpers.MergeWithString(u.DNSAddress, other.DNSAddress)
|
u.DNSAddress = helpers.MergeWithString(u.DNSAddress, other.DNSAddress)
|
||||||
u.MinRatio = helpers.MergeWithFloat64(u.MinRatio, other.MinRatio)
|
u.MinRatio = helpers.MergeWithNumber(u.MinRatio, other.MinRatio)
|
||||||
u.Providers = helpers.MergeStringSlices(u.Providers, other.Providers)
|
u.Providers = helpers.MergeSlices(u.Providers, other.Providers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (u *Updater) overrideWith(other Updater) {
|
func (u *Updater) overrideWith(other Updater) {
|
||||||
u.Period = helpers.OverrideWithDurationPtr(u.Period, other.Period)
|
u.Period = helpers.OverrideWithPointer(u.Period, other.Period)
|
||||||
u.DNSAddress = helpers.OverrideWithString(u.DNSAddress, other.DNSAddress)
|
u.DNSAddress = helpers.OverrideWithString(u.DNSAddress, other.DNSAddress)
|
||||||
u.MinRatio = helpers.OverrideWithFloat64(u.MinRatio, other.MinRatio)
|
u.MinRatio = helpers.OverrideWithNumber(u.MinRatio, other.MinRatio)
|
||||||
u.Providers = helpers.OverrideWithStringSlice(u.Providers, other.Providers)
|
u.Providers = helpers.OverrideWithSlice(u.Providers, other.Providers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Updater) SetDefaults(vpnProvider string) {
|
func (u *Updater) SetDefaults(vpnProvider string) {
|
||||||
u.Period = helpers.DefaultDurationPtr(u.Period, 0)
|
u.Period = helpers.DefaultPointer(u.Period, 0)
|
||||||
u.DNSAddress = helpers.DefaultString(u.DNSAddress, "1.1.1.1:53")
|
u.DNSAddress = helpers.DefaultString(u.DNSAddress, "1.1.1.1:53")
|
||||||
|
|
||||||
if u.MinRatio == 0 {
|
if u.MinRatio == 0 {
|
||||||
|
|||||||
@@ -19,25 +19,25 @@ func (v Version) validate() (err error) {
|
|||||||
|
|
||||||
func (v *Version) copy() (copied Version) {
|
func (v *Version) copy() (copied Version) {
|
||||||
return Version{
|
return Version{
|
||||||
Enabled: helpers.CopyBoolPtr(v.Enabled),
|
Enabled: helpers.CopyPointer(v.Enabled),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
// mergeWith merges the other settings into any
|
||||||
// unset field of the receiver settings object.
|
// unset field of the receiver settings object.
|
||||||
func (v *Version) mergeWith(other Version) {
|
func (v *Version) mergeWith(other Version) {
|
||||||
v.Enabled = helpers.MergeWithBool(v.Enabled, other.Enabled)
|
v.Enabled = helpers.MergeWithPointer(v.Enabled, other.Enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (v *Version) overrideWith(other Version) {
|
func (v *Version) overrideWith(other Version) {
|
||||||
v.Enabled = helpers.OverrideWithBool(v.Enabled, other.Enabled)
|
v.Enabled = helpers.OverrideWithPointer(v.Enabled, other.Enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Version) setDefaults() {
|
func (v *Version) setDefaults() {
|
||||||
v.Enabled = helpers.DefaultBool(v.Enabled, true)
|
v.Enabled = helpers.DefaultPointer(v.Enabled, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Version) String() string {
|
func (v Version) String() string {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ type VPN struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO v4 remove pointer for receiver (because of Surfshark).
|
// TODO v4 remove pointer for receiver (because of Surfshark).
|
||||||
func (v *VPN) Validate(storage Storage) (err error) {
|
func (v *VPN) Validate(storage Storage, ipv6Supported bool) (err error) {
|
||||||
// Validate Type
|
// Validate Type
|
||||||
validVPNTypes := []string{vpn.OpenVPN, vpn.Wireguard}
|
validVPNTypes := []string{vpn.OpenVPN, vpn.Wireguard}
|
||||||
if !helpers.IsOneOf(v.Type, validVPNTypes...) {
|
if !helpers.IsOneOf(v.Type, validVPNTypes...) {
|
||||||
@@ -39,7 +39,7 @@ func (v *VPN) Validate(storage Storage) (err error) {
|
|||||||
return fmt.Errorf("OpenVPN settings: %w", err)
|
return fmt.Errorf("OpenVPN settings: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err := v.Wireguard.validate(*v.Provider.Name)
|
err := v.Wireguard.validate(*v.Provider.Name, ipv6Supported)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Wireguard settings: %w", err)
|
return fmt.Errorf("Wireguard settings: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package settings
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||||
@@ -22,18 +22,23 @@ type Wireguard struct {
|
|||||||
// It cannot be nil in the internal state.
|
// It cannot be nil in the internal state.
|
||||||
PreSharedKey *string
|
PreSharedKey *string
|
||||||
// Addresses are the Wireguard interface addresses.
|
// Addresses are the Wireguard interface addresses.
|
||||||
Addresses []net.IPNet
|
Addresses []netip.Prefix
|
||||||
// Interface is the name of the Wireguard interface
|
// Interface is the name of the Wireguard interface
|
||||||
// to create. It cannot be the empty string in the
|
// to create. It cannot be the empty string in the
|
||||||
// internal state.
|
// internal state.
|
||||||
Interface string
|
Interface string
|
||||||
|
// Implementation is the Wireguard implementation to use.
|
||||||
|
// It can be "auto", "userspace" or "kernelspace".
|
||||||
|
// It defaults to "auto" and cannot be the empty string
|
||||||
|
// in the internal state.
|
||||||
|
Implementation string
|
||||||
}
|
}
|
||||||
|
|
||||||
var regexpInterfaceName = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
|
var regexpInterfaceName = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
|
||||||
|
|
||||||
// Validate validates Wireguard settings.
|
// Validate validates Wireguard settings.
|
||||||
// It should only be ran if the VPN type chosen is Wireguard.
|
// It should only be ran if the VPN type chosen is Wireguard.
|
||||||
func (w Wireguard) validate(vpnProvider string) (err error) {
|
func (w Wireguard) validate(vpnProvider string, ipv6Supported bool) (err error) {
|
||||||
if !helpers.IsOneOf(vpnProvider,
|
if !helpers.IsOneOf(vpnProvider,
|
||||||
providers.Custom,
|
providers.Custom,
|
||||||
providers.Ivpn,
|
providers.Ivpn,
|
||||||
@@ -47,7 +52,7 @@ func (w Wireguard) validate(vpnProvider string) (err error) {
|
|||||||
|
|
||||||
// Validate PrivateKey
|
// Validate PrivateKey
|
||||||
if *w.PrivateKey == "" {
|
if *w.PrivateKey == "" {
|
||||||
return ErrWireguardPrivateKeyNotSet
|
return fmt.Errorf("%w", ErrWireguardPrivateKeyNotSet)
|
||||||
}
|
}
|
||||||
_, err = wgtypes.ParseKey(*w.PrivateKey)
|
_, err = wgtypes.ParseKey(*w.PrivateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -70,13 +75,18 @@ func (w Wireguard) validate(vpnProvider string) (err error) {
|
|||||||
|
|
||||||
// Validate Addresses
|
// Validate Addresses
|
||||||
if len(w.Addresses) == 0 {
|
if len(w.Addresses) == 0 {
|
||||||
return ErrWireguardInterfaceAddressNotSet
|
return fmt.Errorf("%w", ErrWireguardInterfaceAddressNotSet)
|
||||||
}
|
}
|
||||||
for i, ipNet := range w.Addresses {
|
for i, ipNet := range w.Addresses {
|
||||||
if ipNet.IP == nil || ipNet.Mask == nil {
|
if !ipNet.IsValid() {
|
||||||
return fmt.Errorf("%w: for address at index %d: %s",
|
return fmt.Errorf("%w: for address at index %d: %s",
|
||||||
ErrWireguardInterfaceAddressNotSet, i, ipNet.String())
|
ErrWireguardInterfaceAddressNotSet, i, ipNet.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !ipv6Supported && ipNet.Addr().Is6() {
|
||||||
|
return fmt.Errorf("%w: address %s",
|
||||||
|
ErrWireguardInterfaceAddressIPv6, ipNet)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate interface
|
// Validate interface
|
||||||
@@ -85,36 +95,46 @@ func (w Wireguard) validate(vpnProvider string) (err error) {
|
|||||||
ErrWireguardInterfaceNotValid, w.Interface, regexpInterfaceName)
|
ErrWireguardInterfaceNotValid, w.Interface, regexpInterfaceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validImplementations := []string{"auto", "userspace", "kernelspace"}
|
||||||
|
if !helpers.IsOneOf(w.Implementation, validImplementations...) {
|
||||||
|
return fmt.Errorf("%w: %s must be one of %s", ErrWireguardImplementationNotValid,
|
||||||
|
w.Implementation, helpers.ChoicesOrString(validImplementations))
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wireguard) copy() (copied Wireguard) {
|
func (w *Wireguard) copy() (copied Wireguard) {
|
||||||
return Wireguard{
|
return Wireguard{
|
||||||
PrivateKey: helpers.CopyStringPtr(w.PrivateKey),
|
PrivateKey: helpers.CopyPointer(w.PrivateKey),
|
||||||
PreSharedKey: helpers.CopyStringPtr(w.PreSharedKey),
|
PreSharedKey: helpers.CopyPointer(w.PreSharedKey),
|
||||||
Addresses: helpers.CopyIPNetSlice(w.Addresses),
|
Addresses: helpers.CopySlice(w.Addresses),
|
||||||
Interface: w.Interface,
|
Interface: w.Interface,
|
||||||
|
Implementation: w.Implementation,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wireguard) mergeWith(other Wireguard) {
|
func (w *Wireguard) mergeWith(other Wireguard) {
|
||||||
w.PrivateKey = helpers.MergeWithStringPtr(w.PrivateKey, other.PrivateKey)
|
w.PrivateKey = helpers.MergeWithPointer(w.PrivateKey, other.PrivateKey)
|
||||||
w.PreSharedKey = helpers.MergeWithStringPtr(w.PreSharedKey, other.PreSharedKey)
|
w.PreSharedKey = helpers.MergeWithPointer(w.PreSharedKey, other.PreSharedKey)
|
||||||
w.Addresses = helpers.MergeIPNetsSlices(w.Addresses, other.Addresses)
|
w.Addresses = helpers.MergeSlices(w.Addresses, other.Addresses)
|
||||||
w.Interface = helpers.MergeWithString(w.Interface, other.Interface)
|
w.Interface = helpers.MergeWithString(w.Interface, other.Interface)
|
||||||
|
w.Implementation = helpers.MergeWithString(w.Implementation, other.Implementation)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wireguard) overrideWith(other Wireguard) {
|
func (w *Wireguard) overrideWith(other Wireguard) {
|
||||||
w.PrivateKey = helpers.OverrideWithStringPtr(w.PrivateKey, other.PrivateKey)
|
w.PrivateKey = helpers.OverrideWithPointer(w.PrivateKey, other.PrivateKey)
|
||||||
w.PreSharedKey = helpers.OverrideWithStringPtr(w.PreSharedKey, other.PreSharedKey)
|
w.PreSharedKey = helpers.OverrideWithPointer(w.PreSharedKey, other.PreSharedKey)
|
||||||
w.Addresses = helpers.OverrideWithIPNetsSlice(w.Addresses, other.Addresses)
|
w.Addresses = helpers.OverrideWithSlice(w.Addresses, other.Addresses)
|
||||||
w.Interface = helpers.OverrideWithString(w.Interface, other.Interface)
|
w.Interface = helpers.OverrideWithString(w.Interface, other.Interface)
|
||||||
|
w.Implementation = helpers.OverrideWithString(w.Implementation, other.Implementation)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wireguard) setDefaults() {
|
func (w *Wireguard) setDefaults() {
|
||||||
w.PrivateKey = helpers.DefaultStringPtr(w.PrivateKey, "")
|
w.PrivateKey = helpers.DefaultPointer(w.PrivateKey, "")
|
||||||
w.PreSharedKey = helpers.DefaultStringPtr(w.PreSharedKey, "")
|
w.PreSharedKey = helpers.DefaultPointer(w.PreSharedKey, "")
|
||||||
w.Interface = helpers.DefaultString(w.Interface, "wg0")
|
w.Interface = helpers.DefaultString(w.Interface, "wg0")
|
||||||
|
w.Implementation = helpers.DefaultString(w.Implementation, "auto")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w Wireguard) String() string {
|
func (w Wireguard) String() string {
|
||||||
@@ -141,5 +161,9 @@ func (w Wireguard) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
node.Appendf("Network interface: %s", w.Interface)
|
node.Appendf("Network interface: %s", w.Interface)
|
||||||
|
|
||||||
|
if w.Implementation != "auto" {
|
||||||
|
node.Appendf("Implementation: %s", w.Implementation)
|
||||||
|
}
|
||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package settings
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||||
@@ -15,9 +15,9 @@ type WireguardSelection struct {
|
|||||||
// It is only used with VPN providers generating Wireguard
|
// It is only used with VPN providers generating Wireguard
|
||||||
// configurations specific to each server and user.
|
// configurations specific to each server and user.
|
||||||
// To indicate it should not be used, it should be set
|
// To indicate it should not be used, it should be set
|
||||||
// to the empty net.IP{} slice. It can never be nil
|
// to netaddr.IPv4Unspecified(). It can never be the zero value
|
||||||
// in the internal state.
|
// in the internal state.
|
||||||
EndpointIP net.IP
|
EndpointIP netip.Addr
|
||||||
// EndpointPort is a the server port to use for the VPN server.
|
// EndpointPort is a the server port to use for the VPN server.
|
||||||
// It is optional for VPN providers IVPN, Mullvad, Surfshark
|
// It is optional for VPN providers IVPN, Mullvad, Surfshark
|
||||||
// and Windscribe, and compulsory for the others.
|
// and Windscribe, and compulsory for the others.
|
||||||
@@ -40,8 +40,8 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {
|
|||||||
providers.Surfshark, providers.Windscribe:
|
providers.Surfshark, providers.Windscribe:
|
||||||
// endpoint IP addresses are baked in
|
// endpoint IP addresses are baked in
|
||||||
case providers.Custom:
|
case providers.Custom:
|
||||||
if len(w.EndpointIP) == 0 {
|
if !w.EndpointIP.IsValid() || w.EndpointIP.IsUnspecified() {
|
||||||
return ErrWireguardEndpointIPNotSet
|
return fmt.Errorf("%w", ErrWireguardEndpointIPNotSet)
|
||||||
}
|
}
|
||||||
default: // Providers not supporting Wireguard
|
default: // Providers not supporting Wireguard
|
||||||
}
|
}
|
||||||
@@ -51,12 +51,12 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {
|
|||||||
// EndpointPort is required
|
// EndpointPort is required
|
||||||
case providers.Custom:
|
case providers.Custom:
|
||||||
if *w.EndpointPort == 0 {
|
if *w.EndpointPort == 0 {
|
||||||
return ErrWireguardEndpointPortNotSet
|
return fmt.Errorf("%w", ErrWireguardEndpointPortNotSet)
|
||||||
}
|
}
|
||||||
// EndpointPort cannot be set
|
// EndpointPort cannot be set
|
||||||
case providers.Surfshark:
|
case providers.Surfshark:
|
||||||
if *w.EndpointPort != 0 {
|
if *w.EndpointPort != 0 {
|
||||||
return ErrWireguardEndpointPortSet
|
return fmt.Errorf("%w", ErrWireguardEndpointPortSet)
|
||||||
}
|
}
|
||||||
case providers.Airvpn, providers.Ivpn, providers.Mullvad, providers.Windscribe:
|
case providers.Airvpn, providers.Ivpn, providers.Mullvad, providers.Windscribe:
|
||||||
// EndpointPort is optional and can be 0
|
// EndpointPort is optional and can be 0
|
||||||
@@ -92,7 +92,7 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {
|
|||||||
// public keys are baked in
|
// public keys are baked in
|
||||||
case providers.Custom:
|
case providers.Custom:
|
||||||
if w.PublicKey == "" {
|
if w.PublicKey == "" {
|
||||||
return ErrWireguardPublicKeyNotSet
|
return fmt.Errorf("%w", ErrWireguardPublicKeyNotSet)
|
||||||
}
|
}
|
||||||
default: // Providers not supporting Wireguard
|
default: // Providers not supporting Wireguard
|
||||||
}
|
}
|
||||||
@@ -109,27 +109,27 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {
|
|||||||
|
|
||||||
func (w *WireguardSelection) copy() (copied WireguardSelection) {
|
func (w *WireguardSelection) copy() (copied WireguardSelection) {
|
||||||
return WireguardSelection{
|
return WireguardSelection{
|
||||||
EndpointIP: helpers.CopyIP(w.EndpointIP),
|
EndpointIP: w.EndpointIP,
|
||||||
EndpointPort: helpers.CopyUint16Ptr(w.EndpointPort),
|
EndpointPort: helpers.CopyPointer(w.EndpointPort),
|
||||||
PublicKey: w.PublicKey,
|
PublicKey: w.PublicKey,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WireguardSelection) mergeWith(other WireguardSelection) {
|
func (w *WireguardSelection) mergeWith(other WireguardSelection) {
|
||||||
w.EndpointIP = helpers.MergeWithIP(w.EndpointIP, other.EndpointIP)
|
w.EndpointIP = helpers.MergeWithIP(w.EndpointIP, other.EndpointIP)
|
||||||
w.EndpointPort = helpers.MergeWithUint16(w.EndpointPort, other.EndpointPort)
|
w.EndpointPort = helpers.MergeWithPointer(w.EndpointPort, other.EndpointPort)
|
||||||
w.PublicKey = helpers.MergeWithString(w.PublicKey, other.PublicKey)
|
w.PublicKey = helpers.MergeWithString(w.PublicKey, other.PublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WireguardSelection) overrideWith(other WireguardSelection) {
|
func (w *WireguardSelection) overrideWith(other WireguardSelection) {
|
||||||
w.EndpointIP = helpers.OverrideWithIP(w.EndpointIP, other.EndpointIP)
|
w.EndpointIP = helpers.OverrideWithIP(w.EndpointIP, other.EndpointIP)
|
||||||
w.EndpointPort = helpers.OverrideWithUint16(w.EndpointPort, other.EndpointPort)
|
w.EndpointPort = helpers.OverrideWithPointer(w.EndpointPort, other.EndpointPort)
|
||||||
w.PublicKey = helpers.OverrideWithString(w.PublicKey, other.PublicKey)
|
w.PublicKey = helpers.OverrideWithString(w.PublicKey, other.PublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WireguardSelection) setDefaults() {
|
func (w *WireguardSelection) setDefaults() {
|
||||||
w.EndpointIP = helpers.DefaultIP(w.EndpointIP, net.IP{})
|
w.EndpointIP = helpers.DefaultIP(w.EndpointIP, netip.IPv4Unspecified())
|
||||||
w.EndpointPort = helpers.DefaultUint16(w.EndpointPort, 0)
|
w.EndpointPort = helpers.DefaultPointer(w.EndpointPort, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WireguardSelection) String() string {
|
func (w WireguardSelection) String() string {
|
||||||
@@ -139,7 +139,7 @@ func (w WireguardSelection) String() string {
|
|||||||
func (w WireguardSelection) toLinesNode() (node *gotree.Node) {
|
func (w WireguardSelection) toLinesNode() (node *gotree.Node) {
|
||||||
node = gotree.New("Wireguard selection settings:")
|
node = gotree.New("Wireguard selection settings:")
|
||||||
|
|
||||||
if len(w.EndpointIP) > 0 {
|
if !w.EndpointIP.IsUnspecified() {
|
||||||
node.Appendf("Endpoint IP address: %s", w.EndpointIP)
|
node.Appendf("Endpoint IP address: %s", w.EndpointIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
14
internal/configuration/sources/env/dns.go
vendored
14
internal/configuration/sources/env/dns.go
vendored
@@ -2,7 +2,7 @@ package env
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
)
|
)
|
||||||
@@ -26,19 +26,19 @@ func (s *Source) readDNS() (dns settings.DNS, err error) {
|
|||||||
return dns, nil
|
return dns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readDNSServerAddress() (address net.IP, err error) {
|
func (s *Source) readDNSServerAddress() (address netip.Addr, err error) {
|
||||||
key, value := s.getEnvWithRetro("DNS_ADDRESS", "DNS_PLAINTEXT_ADDRESS")
|
key, value := s.getEnvWithRetro("DNS_ADDRESS", "DNS_PLAINTEXT_ADDRESS")
|
||||||
if value == "" {
|
if value == "" {
|
||||||
return nil, nil
|
return address, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
address = net.ParseIP(value)
|
address, err = netip.ParseAddr(value)
|
||||||
if address == nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("environment variable %s: %w: %s", key, ErrIPAddressParse, value)
|
return address, fmt.Errorf("environment variable %s: %w", key, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO remove in v4
|
// TODO remove in v4
|
||||||
if !address.Equal(net.IPv4(127, 0, 0, 1)) { //nolint:gomnd
|
if address.Unmap().Compare(netip.AddrFrom4([4]byte{127, 0, 0, 1})) != 0 {
|
||||||
s.warner.Warn(key + " is set to " + value +
|
s.warner.Warn(key + " is set to " + value +
|
||||||
" so the DNS over TLS (DoT) server will not be used." +
|
" so the DNS over TLS (DoT) server will not be used." +
|
||||||
" The default value changed to 127.0.0.1 so it uses the internal DoT serves." +
|
" The default value changed to 127.0.0.1 so it uses the internal DoT serves." +
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package env
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
"github.com/qdm12/govalid/binary"
|
"github.com/qdm12/govalid/binary"
|
||||||
"inet.af/netaddr"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readDNSBlacklist() (blacklist settings.DNSBlacklist, err error) {
|
func (s *Source) readDNSBlacklist() (blacklist settings.DNSBlacklist, err error) {
|
||||||
@@ -55,24 +55,24 @@ var (
|
|||||||
ErrPrivateAddressNotValid = errors.New("private address is not a valid IP or CIDR range")
|
ErrPrivateAddressNotValid = errors.New("private address is not a valid IP or CIDR range")
|
||||||
)
|
)
|
||||||
|
|
||||||
func readDoTPrivateAddresses() (ips []netaddr.IP,
|
func readDoTPrivateAddresses() (ips []netip.Addr,
|
||||||
ipPrefixes []netaddr.IPPrefix, err error) {
|
ipPrefixes []netip.Prefix, err error) {
|
||||||
privateAddresses := envToCSV("DOT_PRIVATE_ADDRESS")
|
privateAddresses := envToCSV("DOT_PRIVATE_ADDRESS")
|
||||||
if len(privateAddresses) == 0 {
|
if len(privateAddresses) == 0 {
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ips = make([]netaddr.IP, 0, len(privateAddresses))
|
ips = make([]netip.Addr, 0, len(privateAddresses))
|
||||||
ipPrefixes = make([]netaddr.IPPrefix, 0, len(privateAddresses))
|
ipPrefixes = make([]netip.Prefix, 0, len(privateAddresses))
|
||||||
|
|
||||||
for _, privateAddress := range privateAddresses {
|
for _, privateAddress := range privateAddresses {
|
||||||
ip, err := netaddr.ParseIP(privateAddress)
|
ip, err := netip.ParseAddr(privateAddress)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ips = append(ips, ip)
|
ips = append(ips, ip)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ipPrefix, err := netaddr.ParseIPPrefix(privateAddress)
|
ipPrefix, err := netip.ParsePrefix(privateAddress)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ipPrefixes = append(ipPrefixes, ipPrefix)
|
ipPrefixes = append(ipPrefixes, ipPrefix)
|
||||||
continue
|
continue
|
||||||
|
|||||||
16
internal/configuration/sources/env/firewall.go
vendored
16
internal/configuration/sources/env/firewall.go
vendored
@@ -3,7 +3,7 @@ package env
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
@@ -24,7 +24,7 @@ func (s *Source) readFirewall() (firewall settings.Firewall, err error) {
|
|||||||
|
|
||||||
outboundSubnetsKey, _ := s.getEnvWithRetro("FIREWALL_OUTBOUND_SUBNETS", "EXTRA_SUBNETS")
|
outboundSubnetsKey, _ := s.getEnvWithRetro("FIREWALL_OUTBOUND_SUBNETS", "EXTRA_SUBNETS")
|
||||||
outboundSubnetStrings := envToCSV(outboundSubnetsKey)
|
outboundSubnetStrings := envToCSV(outboundSubnetsKey)
|
||||||
firewall.OutboundSubnets, err = stringsToIPNets(outboundSubnetStrings)
|
firewall.OutboundSubnets, err = stringsToNetipPrefixes(outboundSubnetStrings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return firewall, fmt.Errorf("environment variable %s: %w", outboundSubnetsKey, err)
|
return firewall, fmt.Errorf("environment variable %s: %w", outboundSubnetsKey, err)
|
||||||
}
|
}
|
||||||
@@ -65,18 +65,16 @@ func stringsToPorts(ss []string) (ports []uint16, err error) {
|
|||||||
return ports, nil
|
return ports, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func stringsToIPNets(ss []string) (ipNets []net.IPNet, err error) {
|
func stringsToNetipPrefixes(ss []string) (ipPrefixes []netip.Prefix, err error) {
|
||||||
if len(ss) == 0 {
|
if len(ss) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
ipNets = make([]net.IPNet, len(ss))
|
ipPrefixes = make([]netip.Prefix, len(ss))
|
||||||
for i, s := range ss {
|
for i, s := range ss {
|
||||||
ip, ipNet, err := net.ParseCIDR(s)
|
ipPrefixes[i], err = netip.ParsePrefix(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot parse IP network %q: %w", s, err)
|
return nil, fmt.Errorf("parsing IP network %q: %w", s, err)
|
||||||
}
|
}
|
||||||
ipNet.IP = ip
|
|
||||||
ipNets[i] = *ipNet
|
|
||||||
}
|
}
|
||||||
return ipNets, nil
|
return ipPrefixes, nil
|
||||||
}
|
}
|
||||||
|
|||||||
7
internal/configuration/sources/env/health.go
vendored
7
internal/configuration/sources/env/health.go
vendored
@@ -11,6 +11,13 @@ func (s *Source) ReadHealth() (health settings.Health, err error) {
|
|||||||
health.ServerAddress = getCleanedEnv("HEALTH_SERVER_ADDRESS")
|
health.ServerAddress = getCleanedEnv("HEALTH_SERVER_ADDRESS")
|
||||||
_, health.TargetAddress = s.getEnvWithRetro("HEALTH_TARGET_ADDRESS", "HEALTH_ADDRESS_TO_PING")
|
_, health.TargetAddress = s.getEnvWithRetro("HEALTH_TARGET_ADDRESS", "HEALTH_ADDRESS_TO_PING")
|
||||||
|
|
||||||
|
successWaitPtr, err := envToDurationPtr("HEALTH_SUCCESS_WAIT_DURATION")
|
||||||
|
if err != nil {
|
||||||
|
return health, fmt.Errorf("environment variable HEALTH_SUCCESS_WAIT_DURATION: %w", err)
|
||||||
|
} else if successWaitPtr != nil {
|
||||||
|
health.SuccessWait = *successWaitPtr
|
||||||
|
}
|
||||||
|
|
||||||
health.VPN.Initial, err = s.readDurationWithRetro(
|
health.VPN.Initial, err = s.readDurationWithRetro(
|
||||||
"HEALTH_VPN_DURATION_INITIAL",
|
"HEALTH_VPN_DURATION_INITIAL",
|
||||||
"HEALTH_OPENVPN_DURATION_INITIAL")
|
"HEALTH_OPENVPN_DURATION_INITIAL")
|
||||||
|
|||||||
10
internal/configuration/sources/env/helpers.go
vendored
10
internal/configuration/sources/env/helpers.go
vendored
@@ -29,14 +29,6 @@ func envToCSV(envKey string) (values []string) {
|
|||||||
return lowerAndSplit(csv)
|
return lowerAndSplit(csv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func envToInt(envKey string) (n int, err error) {
|
|
||||||
s := getCleanedEnv(envKey)
|
|
||||||
if s == "" {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
return strconv.Atoi(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func envToFloat64(envKey string) (f float64, err error) {
|
func envToFloat64(envKey string) (f float64, err error) {
|
||||||
s := getCleanedEnv(envKey)
|
s := getCleanedEnv(envKey)
|
||||||
if s == "" {
|
if s == "" {
|
||||||
@@ -137,7 +129,7 @@ func unsetEnvKeys(envKeys []string, err error) (newErr error) {
|
|||||||
for _, envKey := range envKeys {
|
for _, envKey := range envKeys {
|
||||||
unsetErr := os.Unsetenv(envKey)
|
unsetErr := os.Unsetenv(envKey)
|
||||||
if unsetErr != nil && newErr == nil {
|
if unsetErr != nil && newErr == nil {
|
||||||
newErr = fmt.Errorf("cannot unset environment variable %s: %w", envKey, unsetErr)
|
newErr = fmt.Errorf("unsetting environment variable %s: %w", envKey, unsetErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newErr
|
return newErr
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
func (s *Source) readPortForward() (
|
func (s *Source) readPortForward() (
|
||||||
portForwarding settings.PortForwarding, err error) {
|
portForwarding settings.PortForwarding, err error) {
|
||||||
key, _ := s.getEnvWithRetro(
|
key, _ := s.getEnvWithRetro(
|
||||||
|
"VPN_PORT_FORWARDING",
|
||||||
"PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING",
|
"PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING",
|
||||||
"PORT_FORWARDING")
|
"PORT_FORWARDING")
|
||||||
portForwarding.Enabled, err = envToBoolPtr(key)
|
portForwarding.Enabled, err = envToBoolPtr(key)
|
||||||
@@ -17,6 +18,7 @@ func (s *Source) readPortForward() (
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, value := s.getEnvWithRetro(
|
_, value := s.getEnvWithRetro(
|
||||||
|
"VPN_PORT_FORWARDING_STATUS_FILE",
|
||||||
"PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING_STATUS_FILE",
|
"PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING_STATUS_FILE",
|
||||||
"PORT_FORWARDING_STATUS_FILE")
|
"PORT_FORWARDING_STATUS_FILE")
|
||||||
if value != "" {
|
if value != "" {
|
||||||
|
|||||||
4
internal/configuration/sources/env/pprof.go
vendored
4
internal/configuration/sources/env/pprof.go
vendored
@@ -12,12 +12,12 @@ func readPprof() (settings pprof.Settings, err error) {
|
|||||||
return settings, fmt.Errorf("environment variable PPROF_ENABLED: %w", err)
|
return settings, fmt.Errorf("environment variable PPROF_ENABLED: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.BlockProfileRate, err = envToInt("PPROF_BLOCK_PROFILE_RATE")
|
settings.BlockProfileRate, err = envToIntPtr("PPROF_BLOCK_PROFILE_RATE")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("environment variable PPROF_BLOCK_PROFILE_RATE: %w", err)
|
return settings, fmt.Errorf("environment variable PPROF_BLOCK_PROFILE_RATE: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.MutexProfileRate, err = envToInt("PPROF_MUTEX_PROFILE_RATE")
|
settings.MutexProfileRate, err = envToIntPtr("PPROF_MUTEX_PROFILE_RATE")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("environment variable PPROF_MUTEX_PROFILE_RATE: %w", err)
|
return settings, fmt.Errorf("environment variable PPROF_MUTEX_PROFILE_RATE: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package env
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -113,16 +113,15 @@ var (
|
|||||||
ErrInvalidIP = errors.New("invalid IP address")
|
ErrInvalidIP = errors.New("invalid IP address")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readOpenVPNTargetIP() (ip net.IP, err error) {
|
func (s *Source) readOpenVPNTargetIP() (ip netip.Addr, err error) {
|
||||||
envKey, value := s.getEnvWithRetro("VPN_ENDPOINT_IP", "OPENVPN_TARGET_IP")
|
envKey, value := s.getEnvWithRetro("VPN_ENDPOINT_IP", "OPENVPN_TARGET_IP")
|
||||||
if value == "" {
|
if value == "" {
|
||||||
return nil, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ip = net.ParseIP(value)
|
ip, err = netip.ParseAddr(value)
|
||||||
if ip == nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("environment variable %s: %w: %s",
|
return ip, fmt.Errorf("environment variable %s: %w", envKey, err)
|
||||||
envKey, ErrInvalidIP, value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ip, nil
|
return ip, nil
|
||||||
|
|||||||
12
internal/configuration/sources/env/wireguard.go
vendored
12
internal/configuration/sources/env/wireguard.go
vendored
@@ -2,7 +2,8 @@ package env
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
@@ -15,6 +16,7 @@ func (s *Source) readWireguard() (wireguard settings.Wireguard, err error) {
|
|||||||
wireguard.PrivateKey = envToStringPtr("WIREGUARD_PRIVATE_KEY")
|
wireguard.PrivateKey = envToStringPtr("WIREGUARD_PRIVATE_KEY")
|
||||||
wireguard.PreSharedKey = envToStringPtr("WIREGUARD_PRESHARED_KEY")
|
wireguard.PreSharedKey = envToStringPtr("WIREGUARD_PRESHARED_KEY")
|
||||||
_, wireguard.Interface = s.getEnvWithRetro("VPN_INTERFACE", "WIREGUARD_INTERFACE")
|
_, wireguard.Interface = s.getEnvWithRetro("VPN_INTERFACE", "WIREGUARD_INTERFACE")
|
||||||
|
wireguard.Implementation = os.Getenv("WIREGUARD_IMPLEMENTATION")
|
||||||
wireguard.Addresses, err = s.readWireguardAddresses()
|
wireguard.Addresses, err = s.readWireguardAddresses()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wireguard, err // already wrapped
|
return wireguard, err // already wrapped
|
||||||
@@ -22,22 +24,20 @@ func (s *Source) readWireguard() (wireguard settings.Wireguard, err error) {
|
|||||||
return wireguard, nil
|
return wireguard, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) readWireguardAddresses() (addresses []net.IPNet, err error) {
|
func (s *Source) readWireguardAddresses() (addresses []netip.Prefix, err error) {
|
||||||
key, addressesCSV := s.getEnvWithRetro("WIREGUARD_ADDRESSES", "WIREGUARD_ADDRESS")
|
key, addressesCSV := s.getEnvWithRetro("WIREGUARD_ADDRESSES", "WIREGUARD_ADDRESS")
|
||||||
if addressesCSV == "" {
|
if addressesCSV == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
addressStrings := strings.Split(addressesCSV, ",")
|
addressStrings := strings.Split(addressesCSV, ",")
|
||||||
addresses = make([]net.IPNet, len(addressStrings))
|
addresses = make([]netip.Prefix, len(addressStrings))
|
||||||
for i, addressString := range addressStrings {
|
for i, addressString := range addressStrings {
|
||||||
addressString = strings.TrimSpace(addressString)
|
addressString = strings.TrimSpace(addressString)
|
||||||
ip, ipNet, err := net.ParseCIDR(addressString)
|
addresses[i], err = netip.ParsePrefix(addressString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("environment variable %s: %w", key, err)
|
return nil, fmt.Errorf("environment variable %s: %w", key, err)
|
||||||
}
|
}
|
||||||
ipNet.IP = ip
|
|
||||||
addresses[i] = *ipNet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return addresses, nil
|
return addresses, nil
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package env
|
package env
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
"github.com/qdm12/govalid/port"
|
"github.com/qdm12/govalid/port"
|
||||||
@@ -26,18 +25,15 @@ func (s *Source) readWireguardSelection() (
|
|||||||
return selection, nil
|
return selection, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrIPAddressParse = errors.New("cannot parse IP address")
|
func (s *Source) readWireguardEndpointIP() (endpointIP netip.Addr, err error) {
|
||||||
|
|
||||||
func (s *Source) readWireguardEndpointIP() (endpointIP net.IP, err error) {
|
|
||||||
key, value := s.getEnvWithRetro("VPN_ENDPOINT_IP", "WIREGUARD_ENDPOINT_IP")
|
key, value := s.getEnvWithRetro("VPN_ENDPOINT_IP", "WIREGUARD_ENDPOINT_IP")
|
||||||
if value == "" {
|
if value == "" {
|
||||||
return nil, nil
|
return endpointIP, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
endpointIP = net.ParseIP(value)
|
endpointIP, err = netip.ParseAddr(value)
|
||||||
if endpointIP == nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("environment variable %s: %w: %s",
|
return endpointIP, fmt.Errorf("environment variable %s: %w", key, err)
|
||||||
key, ErrIPAddressParse, value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return endpointIP, nil
|
return endpointIP, nil
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ func readHTTPProxy() (settings settings.HTTPProxy, err error) {
|
|||||||
"/run/secrets/httpproxy_user",
|
"/run/secrets/httpproxy_user",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("cannot read HTTP proxy user secret file: %w", err)
|
return settings, fmt.Errorf("reading HTTP proxy user secret file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Password, err = readSecretFileAsStringPtr(
|
settings.Password, err = readSecretFileAsStringPtr(
|
||||||
@@ -20,7 +20,7 @@ func readHTTPProxy() (settings settings.HTTPProxy, err error) {
|
|||||||
"/run/secrets/httpproxy_password",
|
"/run/secrets/httpproxy_password",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("cannot read OpenVPN password secret file: %w", err)
|
return settings, fmt.Errorf("reading OpenVPN password secret file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings, nil
|
return settings, nil
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ func readOpenVPN() (
|
|||||||
"/run/secrets/openvpn_user",
|
"/run/secrets/openvpn_user",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("cannot read user file: %w", err)
|
return settings, fmt.Errorf("reading user file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Password, err = readSecretFileAsStringPtr(
|
settings.Password, err = readSecretFileAsStringPtr(
|
||||||
@@ -21,7 +21,7 @@ func readOpenVPN() (
|
|||||||
"/run/secrets/openvpn_password",
|
"/run/secrets/openvpn_password",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("cannot read password file: %w", err)
|
return settings, fmt.Errorf("reading password file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Key, err = readPEMSecretFile(
|
settings.Key, err = readPEMSecretFile(
|
||||||
@@ -29,7 +29,7 @@ func readOpenVPN() (
|
|||||||
"/run/secrets/openvpn_clientkey",
|
"/run/secrets/openvpn_clientkey",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("cannot read client key file: %w", err)
|
return settings, fmt.Errorf("reading client key file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.EncryptedKey, err = readPEMSecretFile(
|
settings.EncryptedKey, err = readPEMSecretFile(
|
||||||
@@ -53,7 +53,7 @@ func readOpenVPN() (
|
|||||||
"/run/secrets/openvpn_clientcrt",
|
"/run/secrets/openvpn_clientcrt",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("cannot read client certificate file: %w", err)
|
return settings, fmt.Errorf("reading client certificate file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings, nil
|
return settings, nil
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ func readShadowsocks() (settings settings.Shadowsocks, err error) {
|
|||||||
"/run/secrets/shadowsocks_password",
|
"/run/secrets/shadowsocks_password",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("cannot read Shadowsocks password secret file: %w", err)
|
return settings, fmt.Errorf("reading Shadowsocks password secret file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings, nil
|
return settings, nil
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
func readVPN() (vpn settings.VPN, err error) {
|
func readVPN() (vpn settings.VPN, err error) {
|
||||||
vpn.OpenVPN, err = readOpenVPN()
|
vpn.OpenVPN, err = readOpenVPN()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return vpn, fmt.Errorf("cannot read OpenVPN settings: %w", err)
|
return vpn, fmt.Errorf("reading OpenVPN settings: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return vpn, nil
|
return vpn, nil
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/dns/pkg/nameserver"
|
"github.com/qdm12/dns/pkg/nameserver"
|
||||||
)
|
)
|
||||||
@@ -12,14 +12,14 @@ func (l *Loop) useUnencryptedDNS(fallback bool) {
|
|||||||
// Try with user provided plaintext ip address
|
// Try with user provided plaintext ip address
|
||||||
// if it's not 127.0.0.1 (default for DoT)
|
// if it's not 127.0.0.1 (default for DoT)
|
||||||
targetIP := settings.ServerAddress
|
targetIP := settings.ServerAddress
|
||||||
if targetIP != nil && !targetIP.Equal(net.IPv4(127, 0, 0, 1)) { //nolint:gomnd
|
if targetIP.Compare(netip.AddrFrom4([4]byte{127, 0, 0, 1})) != 0 {
|
||||||
if fallback {
|
if fallback {
|
||||||
l.logger.Info("falling back on plaintext DNS at address " + targetIP.String())
|
l.logger.Info("falling back on plaintext DNS at address " + targetIP.String())
|
||||||
} else {
|
} else {
|
||||||
l.logger.Info("using plaintext DNS at address " + targetIP.String())
|
l.logger.Info("using plaintext DNS at address " + targetIP.String())
|
||||||
}
|
}
|
||||||
nameserver.UseDNSInternally(targetIP)
|
nameserver.UseDNSInternally(targetIP.AsSlice())
|
||||||
err := nameserver.UseDNSSystemWide(l.resolvConf, targetIP, *settings.KeepNameserver)
|
err := nameserver.UseDNSSystemWide(l.resolvConf, targetIP.AsSlice(), *settings.KeepNameserver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.logger.Error(err.Error())
|
l.logger.Error(err.Error())
|
||||||
}
|
}
|
||||||
@@ -38,8 +38,8 @@ func (l *Loop) useUnencryptedDNS(fallback bool) {
|
|||||||
} else {
|
} else {
|
||||||
l.logger.Info("using plaintext DNS at address " + targetIP.String())
|
l.logger.Info("using plaintext DNS at address " + targetIP.String())
|
||||||
}
|
}
|
||||||
nameserver.UseDNSInternally(targetIP)
|
nameserver.UseDNSInternally(targetIP.AsSlice())
|
||||||
err = nameserver.UseDNSSystemWide(l.resolvConf, targetIP, *settings.KeepNameserver)
|
err = nameserver.UseDNSSystemWide(l.resolvConf, targetIP.AsSlice(), *settings.KeepNameserver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.logger.Error(err.Error())
|
l.logger.Error(err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ func (l *Loop) setupUnbound(ctx context.Context) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// use Unbound
|
// use Unbound
|
||||||
nameserver.UseDNSInternally(settings.ServerAddress)
|
nameserver.UseDNSInternally(settings.ServerAddress.AsSlice())
|
||||||
err = nameserver.UseDNSSystemWide(l.resolvConf, settings.ServerAddress,
|
err = nameserver.UseDNSSystemWide(l.resolvConf, settings.ServerAddress.AsSlice(),
|
||||||
*settings.KeepNameserver)
|
*settings.KeepNameserver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.logger.Error(err.Error())
|
l.logger.Error(err.Error())
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func (cm *cmdMatcher) String() string {
|
|||||||
return fmt.Sprintf("path %s, argument regular expressions %v", cm.path, cm.argsRegex)
|
return fmt.Sprintf("path %s, argument regular expressions %v", cm.path, cm.argsRegex)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCmdMatcher(path string, argsRegex ...string) *cmdMatcher { //nolint:unparam
|
func newCmdMatcher(path string, argsRegex ...string) *cmdMatcher {
|
||||||
argsRegexp := make([]*regexp.Regexp, len(argsRegex))
|
argsRegexp := make([]*regexp.Regexp, len(argsRegex))
|
||||||
for i, argRegex := range argsRegex {
|
for i, argRegex := range argsRegex {
|
||||||
argsRegexp[i] = regexp.MustCompile(argRegex)
|
argsRegexp[i] = regexp.MustCompile(argRegex)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func (c *Config) SetEnabled(ctx context.Context, enabled bool) (err error) {
|
|||||||
if !enabled {
|
if !enabled {
|
||||||
c.logger.Info("disabling...")
|
c.logger.Info("disabling...")
|
||||||
if err = c.disable(ctx); err != nil {
|
if err = c.disable(ctx); err != nil {
|
||||||
return fmt.Errorf("cannot disable firewall: %w", err)
|
return fmt.Errorf("disabling firewall: %w", err)
|
||||||
}
|
}
|
||||||
c.enabled = false
|
c.enabled = false
|
||||||
c.logger.Info("disabled successfully")
|
c.logger.Info("disabled successfully")
|
||||||
@@ -31,7 +31,7 @@ func (c *Config) SetEnabled(ctx context.Context, enabled bool) (err error) {
|
|||||||
c.logger.Info("enabling...")
|
c.logger.Info("enabling...")
|
||||||
|
|
||||||
if err := c.enable(ctx); err != nil {
|
if err := c.enable(ctx); err != nil {
|
||||||
return fmt.Errorf("cannot enable firewall: %w", err)
|
return fmt.Errorf("enabling firewall: %w", err)
|
||||||
}
|
}
|
||||||
c.enabled = true
|
c.enabled = true
|
||||||
c.logger.Info("enabled successfully")
|
c.logger.Info("enabled successfully")
|
||||||
@@ -41,13 +41,13 @@ func (c *Config) SetEnabled(ctx context.Context, enabled bool) (err error) {
|
|||||||
|
|
||||||
func (c *Config) disable(ctx context.Context) (err error) {
|
func (c *Config) disable(ctx context.Context) (err error) {
|
||||||
if err = c.clearAllRules(ctx); err != nil {
|
if err = c.clearAllRules(ctx); err != nil {
|
||||||
return fmt.Errorf("cannot clear all rules: %w", err)
|
return fmt.Errorf("clearing all rules: %w", err)
|
||||||
}
|
}
|
||||||
if err = c.setIPv4AllPolicies(ctx, "ACCEPT"); err != nil {
|
if err = c.setIPv4AllPolicies(ctx, "ACCEPT"); err != nil {
|
||||||
return fmt.Errorf("cannot set ipv4 policies: %w", err)
|
return fmt.Errorf("setting ipv4 policies: %w", err)
|
||||||
}
|
}
|
||||||
if err = c.setIPv6AllPolicies(ctx, "ACCEPT"); err != nil {
|
if err = c.setIPv6AllPolicies(ctx, "ACCEPT"); err != nil {
|
||||||
return fmt.Errorf("cannot set ipv6 policies: %w", err)
|
return fmt.Errorf("setting ipv6 policies: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -98,7 +98,7 @@ func (c *Config) enable(ctx context.Context) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, network := range c.localNetworks {
|
for _, network := range c.localNetworks {
|
||||||
if err := c.acceptOutputFromIPToSubnet(ctx, network.InterfaceName, network.IP, *network.IPNet, remove); err != nil {
|
if err := c.acceptOutputFromIPToSubnet(ctx, network.InterfaceName, network.IP, network.IPNet, remove); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = c.acceptIpv6MulticastOutput(ctx, network.InterfaceName, remove); err != nil {
|
if err = c.acceptIpv6MulticastOutput(ctx, network.InterfaceName, remove); err != nil {
|
||||||
@@ -113,7 +113,7 @@ func (c *Config) enable(ctx context.Context) (err error) {
|
|||||||
// Allows packets from any IP address to go through eth0 / local network
|
// Allows packets from any IP address to go through eth0 / local network
|
||||||
// to reach Gluetun.
|
// to reach Gluetun.
|
||||||
for _, network := range c.localNetworks {
|
for _, network := range c.localNetworks {
|
||||||
if err := c.acceptInputToSubnet(ctx, network.InterfaceName, *network.IPNet, remove); err != nil {
|
if err := c.acceptInputToSubnet(ctx, network.InterfaceName, network.IPNet, remove); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,14 +123,14 @@ func (c *Config) enable(ctx context.Context) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := c.runUserPostRules(ctx, c.customRulesPath, remove); err != nil {
|
if err := c.runUserPostRules(ctx, c.customRulesPath, remove); err != nil {
|
||||||
return fmt.Errorf("cannot run user defined post firewall rules: %w", err)
|
return fmt.Errorf("running user defined post firewall rules: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) allowVPNIP(ctx context.Context) (err error) {
|
func (c *Config) allowVPNIP(ctx context.Context) (err error) {
|
||||||
if c.vpnConnection.IP == nil {
|
if !c.vpnConnection.IP.IsValid() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ func (c *Config) allowVPNIP(ctx context.Context) (err error) {
|
|||||||
for _, defaultRoute := range c.defaultRoutes {
|
for _, defaultRoute := range c.defaultRoutes {
|
||||||
err = c.acceptOutputTrafficToVPN(ctx, defaultRoute.NetInterface, c.vpnConnection, remove)
|
err = c.acceptOutputTrafficToVPN(ctx, defaultRoute.NetInterface, c.vpnConnection, remove)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot accept output traffic through VPN: %w", err)
|
return fmt.Errorf("accepting output traffic through VPN: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ func (c *Config) allowInputPorts(ctx context.Context) (err error) {
|
|||||||
const remove = false
|
const remove = false
|
||||||
err = c.acceptInputToPort(ctx, netInterface, port, remove)
|
err = c.acceptInputToPort(ctx, netInterface, port, remove)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot accept input port %d on interface %s: %w",
|
return fmt.Errorf("accepting input port %d on interface %s: %w",
|
||||||
port, netInterface, err)
|
port, netInterface, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package firewall
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net/netip"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
@@ -27,7 +27,7 @@ type Config struct { //nolint:maligned
|
|||||||
enabled bool
|
enabled bool
|
||||||
vpnConnection models.Connection
|
vpnConnection models.Connection
|
||||||
vpnIntf string
|
vpnIntf string
|
||||||
outboundSubnets []net.IPNet
|
outboundSubnets []netip.Prefix
|
||||||
allowedInputPorts map[uint16]map[string]struct{} // port to interfaces set mapping
|
allowedInputPorts map[uint16]map[string]struct{} // port to interfaces set mapping
|
||||||
stateMutex sync.Mutex
|
stateMutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -95,14 +95,11 @@ func (c *Config) setIPv4AllPolicies(ctx context.Context, policy string) error {
|
|||||||
default:
|
default:
|
||||||
return fmt.Errorf("%w: %s", ErrPolicyUnknown, policy)
|
return fmt.Errorf("%w: %s", ErrPolicyUnknown, policy)
|
||||||
}
|
}
|
||||||
if err := c.runIptablesInstructions(ctx, []string{
|
return c.runIptablesInstructions(ctx, []string{
|
||||||
"--policy INPUT " + policy,
|
"--policy INPUT " + policy,
|
||||||
"--policy OUTPUT " + policy,
|
"--policy OUTPUT " + policy,
|
||||||
"--policy FORWARD " + policy,
|
"--policy FORWARD " + policy,
|
||||||
}); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) acceptInputThroughInterface(ctx context.Context, intf string, remove bool) error {
|
func (c *Config) acceptInputThroughInterface(ctx context.Context, intf string, remove bool) error {
|
||||||
@@ -111,9 +108,8 @@ func (c *Config) acceptInputThroughInterface(ctx context.Context, intf string, r
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) acceptInputToSubnet(ctx context.Context, intf string, destination net.IPNet, remove bool) error {
|
func (c *Config) acceptInputToSubnet(ctx context.Context, intf string,
|
||||||
isIP4Subnet := destination.IP.To4() != nil
|
destination netip.Prefix, remove bool) error {
|
||||||
|
|
||||||
interfaceFlag := "-i " + intf
|
interfaceFlag := "-i " + intf
|
||||||
if intf == "*" { // all interfaces
|
if intf == "*" { // all interfaces
|
||||||
interfaceFlag = ""
|
interfaceFlag = ""
|
||||||
@@ -122,7 +118,7 @@ func (c *Config) acceptInputToSubnet(ctx context.Context, intf string, destinati
|
|||||||
instruction := fmt.Sprintf("%s INPUT %s -d %s -j ACCEPT",
|
instruction := fmt.Sprintf("%s INPUT %s -d %s -j ACCEPT",
|
||||||
appendOrDelete(remove), interfaceFlag, destination.String())
|
appendOrDelete(remove), interfaceFlag, destination.String())
|
||||||
|
|
||||||
if isIP4Subnet {
|
if destination.Addr().Is4() {
|
||||||
return c.runIptablesInstruction(ctx, instruction)
|
return c.runIptablesInstruction(ctx, instruction)
|
||||||
}
|
}
|
||||||
if c.ip6Tables == "" {
|
if c.ip6Tables == "" {
|
||||||
@@ -149,8 +145,7 @@ func (c *Config) acceptOutputTrafficToVPN(ctx context.Context,
|
|||||||
instruction := fmt.Sprintf("%s OUTPUT -d %s -o %s -p %s -m %s --dport %d -j ACCEPT",
|
instruction := fmt.Sprintf("%s OUTPUT -d %s -o %s -p %s -m %s --dport %d -j ACCEPT",
|
||||||
appendOrDelete(remove), connection.IP, defaultInterface, connection.Protocol,
|
appendOrDelete(remove), connection.IP, defaultInterface, connection.Protocol,
|
||||||
connection.Protocol, connection.Port)
|
connection.Protocol, connection.Port)
|
||||||
isIPv4 := connection.IP.To4() != nil
|
if connection.IP.Is4() {
|
||||||
if isIPv4 {
|
|
||||||
return c.runIptablesInstruction(ctx, instruction)
|
return c.runIptablesInstruction(ctx, instruction)
|
||||||
} else if c.ip6Tables == "" {
|
} else if c.ip6Tables == "" {
|
||||||
return fmt.Errorf("accept output to VPN server: %w", ErrNeedIP6Tables)
|
return fmt.Errorf("accept output to VPN server: %w", ErrNeedIP6Tables)
|
||||||
@@ -160,8 +155,8 @@ func (c *Config) acceptOutputTrafficToVPN(ctx context.Context,
|
|||||||
|
|
||||||
// Thanks to @npawelek.
|
// Thanks to @npawelek.
|
||||||
func (c *Config) acceptOutputFromIPToSubnet(ctx context.Context,
|
func (c *Config) acceptOutputFromIPToSubnet(ctx context.Context,
|
||||||
intf string, sourceIP net.IP, destinationSubnet net.IPNet, remove bool) error {
|
intf string, sourceIP netip.Addr, destinationSubnet netip.Prefix, remove bool) error {
|
||||||
doIPv4 := sourceIP.To4() != nil && destinationSubnet.IP.To4() != nil
|
doIPv4 := sourceIP.Is4() && destinationSubnet.Addr().Is4()
|
||||||
|
|
||||||
interfaceFlag := "-o " + intf
|
interfaceFlag := "-o " + intf
|
||||||
if intf == "*" { // all interfaces
|
if intf == "*" { // all interfaces
|
||||||
@@ -257,7 +252,7 @@ func (c *Config) runUserPostRules(ctx context.Context, filepath string, remove b
|
|||||||
case ipv4:
|
case ipv4:
|
||||||
err = c.runIptablesInstruction(ctx, rule)
|
err = c.runIptablesInstruction(ctx, rule)
|
||||||
case c.ip6Tables == "":
|
case c.ip6Tables == "":
|
||||||
err = fmt.Errorf("cannot run user ip6tables rule: %w", ErrNeedIP6Tables)
|
err = fmt.Errorf("running user ip6tables rule: %w", ErrNeedIP6Tables)
|
||||||
default: // ipv6
|
default: // ipv6
|
||||||
err = c.runIP6tablesInstruction(ctx, rule)
|
err = c.runIP6tablesInstruction(ctx, rule)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,18 @@ package firewall
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/subnet"
|
"github.com/qdm12/gluetun/internal/subnet"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Config) SetOutboundSubnets(ctx context.Context, subnets []net.IPNet) (err error) {
|
func (c *Config) SetOutboundSubnets(ctx context.Context, subnets []netip.Prefix) (err error) {
|
||||||
c.stateMutex.Lock()
|
c.stateMutex.Lock()
|
||||||
defer c.stateMutex.Unlock()
|
defer c.stateMutex.Unlock()
|
||||||
|
|
||||||
if !c.enabled {
|
if !c.enabled {
|
||||||
c.logger.Info("firewall disabled, only updating allowed subnets internal list")
|
c.logger.Info("firewall disabled, only updating allowed subnets internal list")
|
||||||
c.outboundSubnets = make([]net.IPNet, len(subnets))
|
c.outboundSubnets = make([]netip.Prefix, len(subnets))
|
||||||
copy(c.outboundSubnets, subnets)
|
copy(c.outboundSubnets, subnets)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -28,13 +28,13 @@ func (c *Config) SetOutboundSubnets(ctx context.Context, subnets []net.IPNet) (e
|
|||||||
|
|
||||||
c.removeOutboundSubnets(ctx, subnetsToRemove)
|
c.removeOutboundSubnets(ctx, subnetsToRemove)
|
||||||
if err := c.addOutboundSubnets(ctx, subnetsToAdd); err != nil {
|
if err := c.addOutboundSubnets(ctx, subnetsToAdd); err != nil {
|
||||||
return fmt.Errorf("cannot set allowed outbound subnets: %w", err)
|
return fmt.Errorf("setting allowed outbound subnets: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) removeOutboundSubnets(ctx context.Context, subnets []net.IPNet) {
|
func (c *Config) removeOutboundSubnets(ctx context.Context, subnets []netip.Prefix) {
|
||||||
const remove = true
|
const remove = true
|
||||||
for _, subNet := range subnets {
|
for _, subNet := range subnets {
|
||||||
for _, defaultRoute := range c.defaultRoutes {
|
for _, defaultRoute := range c.defaultRoutes {
|
||||||
@@ -49,7 +49,7 @@ func (c *Config) removeOutboundSubnets(ctx context.Context, subnets []net.IPNet)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) addOutboundSubnets(ctx context.Context, subnets []net.IPNet) error {
|
func (c *Config) addOutboundSubnets(ctx context.Context, subnets []netip.Prefix) error {
|
||||||
const remove = false
|
const remove = false
|
||||||
for _, subnet := range subnets {
|
for _, subnet := range subnets {
|
||||||
for _, defaultRoute := range c.defaultRoutes {
|
for _, defaultRoute := range c.defaultRoutes {
|
||||||
|
|||||||
@@ -36,10 +36,11 @@ func (c *Config) SetAllowedPort(ctx context.Context, port uint16, intf string) (
|
|||||||
|
|
||||||
const remove = false
|
const remove = false
|
||||||
if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
|
if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
|
||||||
return fmt.Errorf("cannot allow input to port %d through interface %s: %w",
|
return fmt.Errorf("allowing input to port %d through interface %s: %w",
|
||||||
port, intf, err)
|
port, intf, err)
|
||||||
}
|
}
|
||||||
netInterfaces[intf] = struct{}{}
|
netInterfaces[intf] = struct{}{}
|
||||||
|
c.allowedInputPorts[port] = netInterfaces
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -69,7 +70,7 @@ func (c *Config) RemoveAllowedPort(ctx context.Context, port uint16) (err error)
|
|||||||
for netInterface := range interfacesSet {
|
for netInterface := range interfacesSet {
|
||||||
err := c.acceptInputToPort(ctx, netInterface, port, remove)
|
err := c.acceptInputToPort(ctx, netInterface, port, remove)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot remove allowed port %d on interface %s: %w",
|
return fmt.Errorf("removing allowed port %d on interface %s: %w",
|
||||||
port, netInterface, err)
|
port, netInterface, err)
|
||||||
}
|
}
|
||||||
delete(interfacesSet, netInterface)
|
delete(interfacesSet, netInterface)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/golibs/command"
|
"github.com/qdm12/golibs/command"
|
||||||
@@ -20,7 +21,7 @@ var (
|
|||||||
|
|
||||||
func checkIptablesSupport(ctx context.Context, runner command.Runner,
|
func checkIptablesSupport(ctx context.Context, runner command.Runner,
|
||||||
iptablesPathsToTry ...string) (iptablesPath string, err error) {
|
iptablesPathsToTry ...string) (iptablesPath string, err error) {
|
||||||
var lastUnsupportedMessage string
|
iptablesPathToUnsupportedMessage := make(map[string]string, len(iptablesPathsToTry))
|
||||||
for _, pathToTest := range iptablesPathsToTry {
|
for _, pathToTest := range iptablesPathsToTry {
|
||||||
ok, unsupportedMessage, err := testIptablesPath(ctx, pathToTest, runner)
|
ok, unsupportedMessage, err := testIptablesPath(ctx, pathToTest, runner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -29,19 +30,37 @@ func checkIptablesSupport(ctx context.Context, runner command.Runner,
|
|||||||
iptablesPath = pathToTest
|
iptablesPath = pathToTest
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
iptablesPathToUnsupportedMessage[pathToTest] = unsupportedMessage
|
||||||
lastUnsupportedMessage = unsupportedMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
if iptablesPath == "" { // all iptables to try failed
|
|
||||||
return "", fmt.Errorf("%w: from %s: last error is: %s",
|
|
||||||
ErrIPTablesNotSupported, strings.Join(iptablesPathsToTry, ", "),
|
|
||||||
lastUnsupportedMessage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if iptablesPath != "" {
|
||||||
|
// some paths may be unsupported but that does not matter
|
||||||
|
// since we found one working.
|
||||||
return iptablesPath, nil
|
return iptablesPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allArePermissionDenied := true
|
||||||
|
allUnsupportedMessages := make(sort.StringSlice, 0, len(iptablesPathToUnsupportedMessage))
|
||||||
|
for iptablesPath, unsupportedMessage := range iptablesPathToUnsupportedMessage {
|
||||||
|
if !isPermissionDenied(unsupportedMessage) {
|
||||||
|
allArePermissionDenied = false
|
||||||
|
}
|
||||||
|
unsupportedMessage = iptablesPath + ": " + unsupportedMessage
|
||||||
|
allUnsupportedMessages = append(allUnsupportedMessages, unsupportedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
allUnsupportedMessages.Sort() // predictable order for tests
|
||||||
|
|
||||||
|
if allArePermissionDenied {
|
||||||
|
// If the error is related to a denied permission for all iptables path,
|
||||||
|
// return an error describing what to do from an end-user perspective.
|
||||||
|
return "", fmt.Errorf("%w: %s", ErrNetAdminMissing, strings.Join(allUnsupportedMessages, "; "))
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("%w: errors encountered are: %s",
|
||||||
|
ErrIPTablesNotSupported, strings.Join(allUnsupportedMessages, "; "))
|
||||||
|
}
|
||||||
|
|
||||||
func testIptablesPath(ctx context.Context, path string,
|
func testIptablesPath(ctx context.Context, path string,
|
||||||
runner command.Runner) (ok bool, unsupportedMessage string,
|
runner command.Runner) (ok bool, unsupportedMessage string,
|
||||||
criticalErr error) {
|
criticalErr error) {
|
||||||
@@ -56,14 +75,6 @@ func testIptablesPath(ctx context.Context, path string,
|
|||||||
"-A", "OUTPUT", "-o", testInterfaceName, "-j", "DROP")
|
"-A", "OUTPUT", "-o", testInterfaceName, "-j", "DROP")
|
||||||
output, err := runner.Run(cmd)
|
output, err := runner.Run(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isPermissionDenied(output) {
|
|
||||||
// If the error is related to a denied permission,
|
|
||||||
// return an error describing what to do from an end-user
|
|
||||||
// perspective. This is a critical error and likely
|
|
||||||
// applies to all iptables.
|
|
||||||
criticalErr = fmt.Errorf("%w: %s", ErrNetAdminMissing, output)
|
|
||||||
return false, "", criticalErr
|
|
||||||
}
|
|
||||||
unsupportedMessage = fmt.Sprintf("%s (%s)", output, err)
|
unsupportedMessage = fmt.Sprintf("%s (%s)", output, err)
|
||||||
return false, unsupportedMessage, nil
|
return false, unsupportedMessage, nil
|
||||||
}
|
}
|
||||||
@@ -84,10 +95,6 @@ func testIptablesPath(ctx context.Context, path string,
|
|||||||
cmd = exec.CommandContext(ctx, path, "-L", "INPUT")
|
cmd = exec.CommandContext(ctx, path, "-L", "INPUT")
|
||||||
output, err = runner.Run(cmd)
|
output, err = runner.Run(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isPermissionDenied(output) {
|
|
||||||
criticalErr = fmt.Errorf("%w: %s", ErrNetAdminMissing, output)
|
|
||||||
return false, "", criticalErr
|
|
||||||
}
|
|
||||||
unsupportedMessage = fmt.Sprintf("%s (%s)", output, err)
|
unsupportedMessage = fmt.Sprintf("%s (%s)", output, err)
|
||||||
return false, unsupportedMessage, nil
|
return false, unsupportedMessage, nil
|
||||||
}
|
}
|
||||||
@@ -109,10 +116,6 @@ func testIptablesPath(ctx context.Context, path string,
|
|||||||
cmd = exec.CommandContext(ctx, path, "--policy", "INPUT", inputPolicy)
|
cmd = exec.CommandContext(ctx, path, "--policy", "INPUT", inputPolicy)
|
||||||
output, err = runner.Run(cmd)
|
output, err = runner.Run(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isPermissionDenied(output) {
|
|
||||||
criticalErr = fmt.Errorf("%w: %s", ErrNetAdminMissing, output)
|
|
||||||
return false, "", criticalErr
|
|
||||||
}
|
|
||||||
unsupportedMessage = fmt.Sprintf("%s (%s)", output, err)
|
unsupportedMessage = fmt.Sprintf("%s (%s)", output, err)
|
||||||
return false, unsupportedMessage, nil
|
return false, unsupportedMessage, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,130 @@ import (
|
|||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/qdm12/golibs/command"
|
"github.com/qdm12/golibs/command"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate mockgen -destination=runner_mock_test.go -package $GOPACKAGE github.com/qdm12/golibs/command Runner
|
//go:generate mockgen -destination=runner_mock_test.go -package $GOPACKAGE github.com/qdm12/golibs/command Runner
|
||||||
|
|
||||||
|
func newAppendTestRuleMatcher(path string) *cmdMatcher {
|
||||||
|
return newCmdMatcher(path,
|
||||||
|
"^-A$", "^OUTPUT$", "^-o$", "^[a-z0-9]{15}$",
|
||||||
|
"^-j$", "^DROP$")
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDeleteTestRuleMatcher(path string) *cmdMatcher {
|
||||||
|
return newCmdMatcher(path,
|
||||||
|
"^-D$", "^OUTPUT$", "^-o$", "^[a-z0-9]{15}$",
|
||||||
|
"^-j$", "^DROP$")
|
||||||
|
}
|
||||||
|
|
||||||
|
func newListInputRulesMatcher(path string) *cmdMatcher {
|
||||||
|
return newCmdMatcher(path,
|
||||||
|
"^-L$", "^INPUT$")
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSetPolicyMatcher(path, inputPolicy string) *cmdMatcher { //nolint:unparam
|
||||||
|
return newCmdMatcher(path,
|
||||||
|
"^--policy$", "^INPUT$", "^"+inputPolicy+"$")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_checkIptablesSupport(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
errDummy := errors.New("exit code 4")
|
||||||
|
const inputPolicy = "ACCEPT"
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
buildRunner func(ctrl *gomock.Controller) command.Runner
|
||||||
|
iptablesPathsToTry []string
|
||||||
|
iptablesPath string
|
||||||
|
errSentinel error
|
||||||
|
errMessage string
|
||||||
|
}{
|
||||||
|
"critical error when checking": {
|
||||||
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
|
runner := NewMockRunner(ctrl)
|
||||||
|
runner.EXPECT().Run(newAppendTestRuleMatcher("path1")).
|
||||||
|
Return("", nil)
|
||||||
|
runner.EXPECT().Run(newDeleteTestRuleMatcher("path1")).
|
||||||
|
Return("output", errDummy)
|
||||||
|
return runner
|
||||||
|
},
|
||||||
|
iptablesPathsToTry: []string{"path1", "path2"},
|
||||||
|
errSentinel: ErrTestRuleCleanup,
|
||||||
|
errMessage: "for path1: failed cleaning up test rule: " +
|
||||||
|
"output (exit code 4)",
|
||||||
|
},
|
||||||
|
"found valid path": {
|
||||||
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
|
runner := NewMockRunner(ctrl)
|
||||||
|
runner.EXPECT().Run(newAppendTestRuleMatcher("path1")).
|
||||||
|
Return("", nil)
|
||||||
|
runner.EXPECT().Run(newDeleteTestRuleMatcher("path1")).
|
||||||
|
Return("", nil)
|
||||||
|
runner.EXPECT().Run(newListInputRulesMatcher("path1")).
|
||||||
|
Return("Chain INPUT (policy "+inputPolicy+")", nil)
|
||||||
|
runner.EXPECT().Run(newSetPolicyMatcher("path1", inputPolicy)).
|
||||||
|
Return("", nil)
|
||||||
|
return runner
|
||||||
|
},
|
||||||
|
iptablesPathsToTry: []string{"path1", "path2"},
|
||||||
|
iptablesPath: "path1",
|
||||||
|
},
|
||||||
|
"all permission denied": {
|
||||||
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
|
runner := NewMockRunner(ctrl)
|
||||||
|
runner.EXPECT().Run(newAppendTestRuleMatcher("path1")).
|
||||||
|
Return("Permission denied (you must be root) more context", errDummy)
|
||||||
|
runner.EXPECT().Run(newAppendTestRuleMatcher("path2")).
|
||||||
|
Return("context: Permission denied (you must be root)", errDummy)
|
||||||
|
return runner
|
||||||
|
},
|
||||||
|
iptablesPathsToTry: []string{"path1", "path2"},
|
||||||
|
errSentinel: ErrNetAdminMissing,
|
||||||
|
errMessage: "NET_ADMIN capability is missing: " +
|
||||||
|
"path1: Permission denied (you must be root) more context (exit code 4); " +
|
||||||
|
"path2: context: Permission denied (you must be root) (exit code 4)",
|
||||||
|
},
|
||||||
|
"no valid path": {
|
||||||
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
|
runner := NewMockRunner(ctrl)
|
||||||
|
runner.EXPECT().Run(newAppendTestRuleMatcher("path1")).
|
||||||
|
Return("output 1", errDummy)
|
||||||
|
runner.EXPECT().Run(newAppendTestRuleMatcher("path2")).
|
||||||
|
Return("output 2", errDummy)
|
||||||
|
return runner
|
||||||
|
},
|
||||||
|
iptablesPathsToTry: []string{"path1", "path2"},
|
||||||
|
errSentinel: ErrIPTablesNotSupported,
|
||||||
|
errMessage: "no iptables supported found: " +
|
||||||
|
"errors encountered are: " +
|
||||||
|
"path1: output 1 (exit code 4); " +
|
||||||
|
"path2: output 2 (exit code 4)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
|
||||||
|
runner := testCase.buildRunner(ctrl)
|
||||||
|
|
||||||
|
iptablesPath, err :=
|
||||||
|
checkIptablesSupport(ctx, runner, testCase.iptablesPathsToTry...)
|
||||||
|
|
||||||
|
require.ErrorIs(t, err, testCase.errSentinel)
|
||||||
|
if testCase.errSentinel != nil {
|
||||||
|
assert.EqualError(t, err, testCase.errMessage)
|
||||||
|
}
|
||||||
|
assert.Equal(t, testCase.iptablesPath, iptablesPath)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Test_testIptablesPath(t *testing.T) {
|
func Test_testIptablesPath(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -20,17 +140,6 @@ func Test_testIptablesPath(t *testing.T) {
|
|||||||
errDummy := errors.New("exit code 4")
|
errDummy := errors.New("exit code 4")
|
||||||
const inputPolicy = "ACCEPT"
|
const inputPolicy = "ACCEPT"
|
||||||
|
|
||||||
appendTestRuleMatcher := newCmdMatcher(path,
|
|
||||||
"^-A$", "^OUTPUT$", "^-o$", "^[a-z0-9]{15}$",
|
|
||||||
"^-j$", "^DROP$")
|
|
||||||
deleteTestRuleMatcher := newCmdMatcher(path,
|
|
||||||
"^-D$", "^OUTPUT$", "^-o$", "^[a-z0-9]{15}$",
|
|
||||||
"^-j$", "^DROP$")
|
|
||||||
listInputRulesMatcher := newCmdMatcher(path,
|
|
||||||
"^-L$", "^INPUT$")
|
|
||||||
setPolicyMatcher := newCmdMatcher(path,
|
|
||||||
"^--policy$", "^INPUT$", "^"+inputPolicy+"$")
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
buildRunner func(ctrl *gomock.Controller) command.Runner
|
buildRunner func(ctrl *gomock.Controller) command.Runner
|
||||||
ok bool
|
ok bool
|
||||||
@@ -41,18 +150,16 @@ func Test_testIptablesPath(t *testing.T) {
|
|||||||
"append test rule permission denied": {
|
"append test rule permission denied": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(appendTestRuleMatcher).
|
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).
|
||||||
Return("Permission denied (you must be root)", errDummy)
|
Return("Permission denied (you must be root)", errDummy)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
criticalErrWrapped: ErrNetAdminMissing,
|
unsupportedMessage: "Permission denied (you must be root) (exit code 4)",
|
||||||
criticalErrMessage: "NET_ADMIN capability is missing: " +
|
|
||||||
"Permission denied (you must be root)",
|
|
||||||
},
|
},
|
||||||
"append test rule unsupported": {
|
"append test rule unsupported": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(appendTestRuleMatcher).
|
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).
|
||||||
Return("some output", errDummy)
|
Return("some output", errDummy)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
@@ -61,8 +168,8 @@ func Test_testIptablesPath(t *testing.T) {
|
|||||||
"remove test rule error": {
|
"remove test rule error": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).Return("", nil)
|
||||||
runner.EXPECT().Run(deleteTestRuleMatcher).
|
runner.EXPECT().Run(newDeleteTestRuleMatcher(path)).
|
||||||
Return("some output", errDummy)
|
Return("some output", errDummy)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
@@ -72,22 +179,20 @@ func Test_testIptablesPath(t *testing.T) {
|
|||||||
"list input rules permission denied": {
|
"list input rules permission denied": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).Return("", nil)
|
||||||
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
runner.EXPECT().Run(newDeleteTestRuleMatcher(path)).Return("", nil)
|
||||||
runner.EXPECT().Run(listInputRulesMatcher).
|
runner.EXPECT().Run(newListInputRulesMatcher(path)).
|
||||||
Return("Permission denied (you must be root)", errDummy)
|
Return("Permission denied (you must be root)", errDummy)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
criticalErrWrapped: ErrNetAdminMissing,
|
unsupportedMessage: "Permission denied (you must be root) (exit code 4)",
|
||||||
criticalErrMessage: "NET_ADMIN capability is missing: " +
|
|
||||||
"Permission denied (you must be root)",
|
|
||||||
},
|
},
|
||||||
"list input rules unsupported": {
|
"list input rules unsupported": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).Return("", nil)
|
||||||
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
runner.EXPECT().Run(newDeleteTestRuleMatcher(path)).Return("", nil)
|
||||||
runner.EXPECT().Run(listInputRulesMatcher).
|
runner.EXPECT().Run(newListInputRulesMatcher(path)).
|
||||||
Return("some output", errDummy)
|
Return("some output", errDummy)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
@@ -96,9 +201,9 @@ func Test_testIptablesPath(t *testing.T) {
|
|||||||
"list input rules no policy": {
|
"list input rules no policy": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).Return("", nil)
|
||||||
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
runner.EXPECT().Run(newDeleteTestRuleMatcher(path)).Return("", nil)
|
||||||
runner.EXPECT().Run(listInputRulesMatcher).
|
runner.EXPECT().Run(newListInputRulesMatcher(path)).
|
||||||
Return("some\noutput", nil)
|
Return("some\noutput", nil)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
@@ -108,26 +213,24 @@ func Test_testIptablesPath(t *testing.T) {
|
|||||||
"set policy permission denied": {
|
"set policy permission denied": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).Return("", nil)
|
||||||
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
runner.EXPECT().Run(newDeleteTestRuleMatcher(path)).Return("", nil)
|
||||||
runner.EXPECT().Run(listInputRulesMatcher).
|
runner.EXPECT().Run(newListInputRulesMatcher(path)).
|
||||||
Return("\nChain INPUT (policy "+inputPolicy+")\nxx\n", nil)
|
Return("\nChain INPUT (policy "+inputPolicy+")\nxx\n", nil)
|
||||||
runner.EXPECT().Run(setPolicyMatcher).
|
runner.EXPECT().Run(newSetPolicyMatcher(path, inputPolicy)).
|
||||||
Return("Permission denied (you must be root)", errDummy)
|
Return("Permission denied (you must be root)", errDummy)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
criticalErrWrapped: ErrNetAdminMissing,
|
unsupportedMessage: "Permission denied (you must be root) (exit code 4)",
|
||||||
criticalErrMessage: "NET_ADMIN capability is missing: " +
|
|
||||||
"Permission denied (you must be root)",
|
|
||||||
},
|
},
|
||||||
"set policy unsupported": {
|
"set policy unsupported": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).Return("", nil)
|
||||||
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
runner.EXPECT().Run(newDeleteTestRuleMatcher(path)).Return("", nil)
|
||||||
runner.EXPECT().Run(listInputRulesMatcher).
|
runner.EXPECT().Run(newListInputRulesMatcher(path)).
|
||||||
Return("\nChain INPUT (policy "+inputPolicy+")\nxx\n", nil)
|
Return("\nChain INPUT (policy "+inputPolicy+")\nxx\n", nil)
|
||||||
runner.EXPECT().Run(setPolicyMatcher).
|
runner.EXPECT().Run(newSetPolicyMatcher(path, inputPolicy)).
|
||||||
Return("some output", errDummy)
|
Return("some output", errDummy)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
@@ -136,11 +239,12 @@ func Test_testIptablesPath(t *testing.T) {
|
|||||||
"success": {
|
"success": {
|
||||||
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
||||||
runner := NewMockRunner(ctrl)
|
runner := NewMockRunner(ctrl)
|
||||||
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
runner.EXPECT().Run(newAppendTestRuleMatcher(path)).Return("", nil)
|
||||||
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
runner.EXPECT().Run(newDeleteTestRuleMatcher(path)).Return("", nil)
|
||||||
runner.EXPECT().Run(listInputRulesMatcher).
|
runner.EXPECT().Run(newListInputRulesMatcher(path)).
|
||||||
Return("\nChain INPUT (policy "+inputPolicy+")\nxx\n", nil)
|
Return("\nChain INPUT (policy "+inputPolicy+")\nxx\n", nil)
|
||||||
runner.EXPECT().Run(setPolicyMatcher).Return("some output", nil)
|
runner.EXPECT().Run(newSetPolicyMatcher(path, inputPolicy)).
|
||||||
|
Return("some output", nil)
|
||||||
return runner
|
return runner
|
||||||
},
|
},
|
||||||
ok: true,
|
ok: true,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func (c *Config) SetVPNConnection(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
remove := true
|
remove := true
|
||||||
if c.vpnConnection.IP != nil {
|
if c.vpnConnection.IP.IsValid() {
|
||||||
for _, defaultRoute := range c.defaultRoutes {
|
for _, defaultRoute := range c.defaultRoutes {
|
||||||
if err := c.acceptOutputTrafficToVPN(ctx, defaultRoute.NetInterface, c.vpnConnection, remove); err != nil {
|
if err := c.acceptOutputTrafficToVPN(ctx, defaultRoute.NetInterface, c.vpnConnection, remove); err != nil {
|
||||||
c.logger.Error("cannot remove outdated VPN connection rule: " + err.Error())
|
c.logger.Error("cannot remove outdated VPN connection rule: " + err.Error())
|
||||||
@@ -45,13 +45,13 @@ func (c *Config) SetVPNConnection(ctx context.Context,
|
|||||||
|
|
||||||
for _, defaultRoute := range c.defaultRoutes {
|
for _, defaultRoute := range c.defaultRoutes {
|
||||||
if err := c.acceptOutputTrafficToVPN(ctx, defaultRoute.NetInterface, connection, remove); err != nil {
|
if err := c.acceptOutputTrafficToVPN(ctx, defaultRoute.NetInterface, connection, remove); err != nil {
|
||||||
return fmt.Errorf("cannot allow output traffic through VPN connection: %w", err)
|
return fmt.Errorf("allowing output traffic through VPN connection: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.vpnConnection = connection
|
c.vpnConnection = connection
|
||||||
|
|
||||||
if err = c.acceptOutputThroughInterface(ctx, vpnIntf, remove); err != nil {
|
if err = c.acceptOutputThroughInterface(ctx, vpnIntf, remove); err != nil {
|
||||||
return fmt.Errorf("cannot accept output traffic through interface %s: %w", vpnIntf, err)
|
return fmt.Errorf("accepting output traffic through interface %s: %w", vpnIntf, err)
|
||||||
}
|
}
|
||||||
c.vpnIntf = vpnIntf
|
c.vpnIntf = vpnIntf
|
||||||
|
|
||||||
|
|||||||
@@ -49,9 +49,8 @@ func (s *Server) runHealthcheckLoop(ctx context.Context, done chan<- struct{}) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Success, check again in 5 seconds
|
// Success, check again after the success wait duration
|
||||||
const period = 5 * time.Second
|
timer := time.NewTimer(s.config.SuccessWait)
|
||||||
timer := time.NewTimer(period)
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
if !timer.Stop() {
|
if !timer.Stop() {
|
||||||
@@ -74,12 +73,12 @@ func (s *Server) healthCheck(ctx context.Context) (err error) {
|
|||||||
const dialNetwork = "tcp4"
|
const dialNetwork = "tcp4"
|
||||||
connection, err := s.dialer.DialContext(ctx, dialNetwork, address)
|
connection, err := s.dialer.DialContext(ctx, dialNetwork, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot dial: %w", err)
|
return fmt.Errorf("dialing: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = connection.Close()
|
err = connection.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot close connection: %w", err)
|
return fmt.Errorf("closing connection: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -91,7 +90,7 @@ func makeAddressToDial(address string) (addressToDial string, err error) {
|
|||||||
addrErr := new(net.AddrError)
|
addrErr := new(net.AddrError)
|
||||||
ok := errors.As(err, &addrErr)
|
ok := errors.As(err, &addrErr)
|
||||||
if !ok || addrErr.Err != "missing port in address" {
|
if !ok || addrErr.Err != "missing port in address" {
|
||||||
return "", fmt.Errorf("cannot split host and port from address: %w", err)
|
return "", fmt.Errorf("splitting host and port from address: %w", err)
|
||||||
}
|
}
|
||||||
host = address
|
host = address
|
||||||
const defaultPort = "443"
|
const defaultPort = "443"
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ func Test_makeAddressToDial(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"bad address": {
|
"bad address": {
|
||||||
address: "test.com::",
|
address: "test.com::",
|
||||||
err: fmt.Errorf("cannot split host and port from address: address test.com::: too many colons in address"), //nolint:lll
|
err: fmt.Errorf("splitting host and port from address: address test.com::: too many colons in address"), //nolint:lll
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ type vpnHealth struct {
|
|||||||
|
|
||||||
func (s *Server) onUnhealthyVPN(ctx context.Context) {
|
func (s *Server) onUnhealthyVPN(ctx context.Context) {
|
||||||
s.logger.Info("program has been unhealthy for " +
|
s.logger.Info("program has been unhealthy for " +
|
||||||
s.vpn.healthyWait.String() + ": restarting VPN")
|
s.vpn.healthyWait.String() + ": restarting VPN " +
|
||||||
|
"(see https://github.com/qdm12/gluetun/wiki/Healthcheck)")
|
||||||
_, _ = s.vpn.loop.ApplyStatus(ctx, constants.Stopped)
|
_, _ = s.vpn.loop.ApplyStatus(ctx, constants.Stopped)
|
||||||
_, _ = s.vpn.loop.ApplyStatus(ctx, constants.Running)
|
_, _ = s.vpn.loop.ApplyStatus(ctx, constants.Running)
|
||||||
s.vpn.healthyWait += *s.config.VPN.Addition
|
s.vpn.healthyWait += *s.config.VPN.Addition
|
||||||
|
|||||||
@@ -21,7 +21,11 @@ func NewServer(config settings.Health,
|
|||||||
return &Server{
|
return &Server{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
handler: newHandler(),
|
handler: newHandler(),
|
||||||
dialer: &net.Dialer{},
|
dialer: &net.Dialer{
|
||||||
|
Resolver: &net.Resolver{
|
||||||
|
PreferGo: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
config: config,
|
config: config,
|
||||||
vpn: vpnHealth{
|
vpn: vpnHealth{
|
||||||
loop: vpnLoop,
|
loop: vpnLoop,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package httpproxy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -64,6 +65,6 @@ var hopHeaders = [...]string{ //nolint:gochecknoglobals
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Do not follow redirect, but directly return the redirect response.
|
// Do not follow redirect, but directly return the redirect response.
|
||||||
func returnRedirect(req *http.Request, via []*http.Request) error {
|
func returnRedirect(*http.Request, []*http.Request) error {
|
||||||
return http.ErrUseLastResponse
|
return fmt.Errorf("%w", http.ErrUseLastResponse)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func (s *Server) Run(ctx context.Context, errorCh chan<- error) {
|
|||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
const shutdownGraceDuration = 2 * time.Second
|
const shutdownGraceDuration = 100 * time.Millisecond
|
||||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownGraceDuration)
|
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownGraceDuration)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if err := server.Shutdown(shutdownCtx); err != nil {
|
if err := server.Shutdown(shutdownCtx); err != nil {
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ var _ Logger = (*testLogger)(nil)
|
|||||||
|
|
||||||
type testLogger struct{}
|
type testLogger struct{}
|
||||||
|
|
||||||
func (t *testLogger) Info(msg string) {}
|
func (t *testLogger) Info(string) {}
|
||||||
func (t *testLogger) Warn(msg string) {}
|
func (t *testLogger) Warn(string) {}
|
||||||
func (t *testLogger) Error(msg string) {}
|
func (t *testLogger) Error(string) {}
|
||||||
|
|
||||||
var _ gomock.Matcher = (*regexMatcher)(nil)
|
var _ gomock.Matcher = (*regexMatcher)(nil)
|
||||||
|
|
||||||
|
|||||||
@@ -36,10 +36,10 @@ type Settings struct {
|
|||||||
func (s *Settings) SetDefaults() {
|
func (s *Settings) SetDefaults() {
|
||||||
s.Address = helpers.DefaultString(s.Address, ":8000")
|
s.Address = helpers.DefaultString(s.Address, ":8000")
|
||||||
const defaultReadTimeout = 3 * time.Second
|
const defaultReadTimeout = 3 * time.Second
|
||||||
s.ReadHeaderTimeout = helpers.DefaultDuration(s.ReadHeaderTimeout, defaultReadTimeout)
|
s.ReadHeaderTimeout = helpers.DefaultNumber(s.ReadHeaderTimeout, defaultReadTimeout)
|
||||||
s.ReadTimeout = helpers.DefaultDuration(s.ReadTimeout, defaultReadTimeout)
|
s.ReadTimeout = helpers.DefaultNumber(s.ReadTimeout, defaultReadTimeout)
|
||||||
const defaultShutdownTimeout = 3 * time.Second
|
const defaultShutdownTimeout = 3 * time.Second
|
||||||
s.ShutdownTimeout = helpers.DefaultDuration(s.ShutdownTimeout, defaultShutdownTimeout)
|
s.ShutdownTimeout = helpers.DefaultNumber(s.ShutdownTimeout, defaultShutdownTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Settings) Copy() Settings {
|
func (s Settings) Copy() Settings {
|
||||||
@@ -59,9 +59,9 @@ func (s *Settings) MergeWith(other Settings) {
|
|||||||
if s.Logger == nil {
|
if s.Logger == nil {
|
||||||
s.Logger = other.Logger
|
s.Logger = other.Logger
|
||||||
}
|
}
|
||||||
s.ReadHeaderTimeout = helpers.MergeWithDuration(s.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
s.ReadHeaderTimeout = helpers.MergeWithNumber(s.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
||||||
s.ReadTimeout = helpers.MergeWithDuration(s.ReadTimeout, other.ReadTimeout)
|
s.ReadTimeout = helpers.MergeWithNumber(s.ReadTimeout, other.ReadTimeout)
|
||||||
s.ShutdownTimeout = helpers.MergeWithDuration(s.ShutdownTimeout, other.ShutdownTimeout)
|
s.ShutdownTimeout = helpers.MergeWithNumber(s.ShutdownTimeout, other.ShutdownTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Settings) OverrideWith(other Settings) {
|
func (s *Settings) OverrideWith(other Settings) {
|
||||||
@@ -70,9 +70,9 @@ func (s *Settings) OverrideWith(other Settings) {
|
|||||||
if other.Logger != nil {
|
if other.Logger != nil {
|
||||||
s.Logger = other.Logger
|
s.Logger = other.Logger
|
||||||
}
|
}
|
||||||
s.ReadHeaderTimeout = helpers.OverrideWithDuration(s.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
s.ReadHeaderTimeout = helpers.OverrideWithNumber(s.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
||||||
s.ReadTimeout = helpers.OverrideWithDuration(s.ReadTimeout, other.ReadTimeout)
|
s.ReadTimeout = helpers.OverrideWithNumber(s.ReadTimeout, other.ReadTimeout)
|
||||||
s.ShutdownTimeout = helpers.OverrideWithDuration(s.ShutdownTimeout, other.ShutdownTimeout)
|
s.ShutdownTimeout = helpers.OverrideWithNumber(s.ShutdownTimeout, other.ShutdownTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -91,11 +91,11 @@ func (s Settings) Validate() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.Handler == nil {
|
if s.Handler == nil {
|
||||||
return ErrHandlerIsNotSet
|
return fmt.Errorf("%w", ErrHandlerIsNotSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Logger == nil {
|
if s.Logger == nil {
|
||||||
return ErrLoggerIsNotSet
|
return fmt.Errorf("%w", ErrLoggerIsNotSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
const minReadTimeout = time.Millisecond
|
const minReadTimeout = time.Millisecond
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net/netip"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Connection struct {
|
type Connection struct {
|
||||||
// Type is the connection type and can be "openvpn" or "wireguard"
|
// Type is the connection type and can be "openvpn" or "wireguard"
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
// IP is the VPN server IP address.
|
// IP is the VPN server IP address.
|
||||||
IP net.IP `json:"ip"`
|
IP netip.Addr `json:"ip"`
|
||||||
// Port is the VPN server port.
|
// Port is the VPN server port.
|
||||||
Port uint16 `json:"port"`
|
Port uint16 `json:"port"`
|
||||||
// Protocol can be "tcp" or "udp".
|
// Protocol can be "tcp" or "udp".
|
||||||
@@ -24,15 +24,15 @@ type Connection struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Connection) Equal(other Connection) bool {
|
func (c *Connection) Equal(other Connection) bool {
|
||||||
return c.IP.Equal(other.IP) && c.Port == other.Port &&
|
return c.IP.Compare(other.IP) == 0 && c.Port == other.Port &&
|
||||||
c.Protocol == other.Protocol && c.Hostname == other.Hostname &&
|
c.Protocol == other.Protocol && c.Hostname == other.Hostname &&
|
||||||
c.ServerName == other.ServerName && c.PubKey == other.PubKey
|
c.ServerName == other.ServerName && c.PubKey == other.PubKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateEmptyWith updates each field of the connection where the
|
// UpdateEmptyWith updates each field of the connection where the
|
||||||
// value is not set using the value given as arguments.
|
// value is not set using the value given as arguments.
|
||||||
func (c *Connection) UpdateEmptyWith(ip net.IP, port uint16, protocol string) {
|
func (c *Connection) UpdateEmptyWith(ip netip.Addr, port uint16, protocol string) {
|
||||||
if c.IP == nil {
|
if !c.IP.IsValid() {
|
||||||
c.IP = ip
|
c.IP = ip
|
||||||
}
|
}
|
||||||
if c.Port == 0 {
|
if c.Port == 0 {
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "net"
|
import (
|
||||||
|
"net/netip"
|
||||||
|
)
|
||||||
|
|
||||||
type PublicIP struct {
|
type PublicIP struct {
|
||||||
IP net.IP `json:"public_ip,omitempty"`
|
IP netip.Addr `json:"public_ip,omitempty"`
|
||||||
Region string `json:"region,omitempty"`
|
Region string `json:"region,omitempty"`
|
||||||
Country string `json:"country,omitempty"`
|
Country string `json:"country,omitempty"`
|
||||||
City string `json:"city,omitempty"`
|
City string `json:"city,omitempty"`
|
||||||
@@ -16,7 +18,7 @@ type PublicIP struct {
|
|||||||
|
|
||||||
func (p *PublicIP) Copy() (publicIPCopy PublicIP) {
|
func (p *PublicIP) Copy() (publicIPCopy PublicIP) {
|
||||||
publicIPCopy = PublicIP{
|
publicIPCopy = PublicIP{
|
||||||
IP: make(net.IP, len(p.IP)),
|
IP: p.IP,
|
||||||
Region: p.Region,
|
Region: p.Region,
|
||||||
Country: p.Country,
|
Country: p.Country,
|
||||||
City: p.City,
|
City: p.City,
|
||||||
@@ -26,6 +28,5 @@ func (p *PublicIP) Copy() (publicIPCopy PublicIP) {
|
|||||||
PostalCode: p.PostalCode,
|
PostalCode: p.PostalCode,
|
||||||
Timezone: p.Timezone,
|
Timezone: p.Timezone,
|
||||||
}
|
}
|
||||||
copy(publicIPCopy.IP, p.IP)
|
|
||||||
return publicIPCopy
|
return publicIPCopy
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package models
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ type Server struct {
|
|||||||
Premium bool `json:"premium,omitempty"`
|
Premium bool `json:"premium,omitempty"`
|
||||||
PortForward bool `json:"port_forward,omitempty"`
|
PortForward bool `json:"port_forward,omitempty"`
|
||||||
Keep bool `json:"keep,omitempty"`
|
Keep bool `json:"keep,omitempty"`
|
||||||
IPs []net.IP `json:"ips,omitempty"`
|
IPs []netip.Addr `json:"ips,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -47,17 +47,15 @@ var (
|
|||||||
func (s *Server) HasMinimumInformation() (err error) {
|
func (s *Server) HasMinimumInformation() (err error) {
|
||||||
switch {
|
switch {
|
||||||
case s.VPN == "":
|
case s.VPN == "":
|
||||||
return ErrVPNFieldEmpty
|
return fmt.Errorf("%w", ErrVPNFieldEmpty)
|
||||||
case s.Hostname == "":
|
|
||||||
return ErrHostnameFieldEmpty
|
|
||||||
case len(s.IPs) == 0:
|
case len(s.IPs) == 0:
|
||||||
return ErrIPsFieldEmpty
|
return fmt.Errorf("%w", ErrIPsFieldEmpty)
|
||||||
case s.VPN == vpn.Wireguard && (s.TCP || s.UDP):
|
case s.VPN == vpn.Wireguard && (s.TCP || s.UDP):
|
||||||
return ErrNetworkProtocolSet
|
return fmt.Errorf("%w", ErrNetworkProtocolSet)
|
||||||
case s.VPN == vpn.OpenVPN && !s.TCP && !s.UDP:
|
case s.VPN == vpn.OpenVPN && !s.TCP && !s.UDP:
|
||||||
return ErrNoNetworkProtocol
|
return fmt.Errorf("%w", ErrNoNetworkProtocol)
|
||||||
case s.VPN == vpn.Wireguard && s.WgPubKey == "":
|
case s.VPN == vpn.Wireguard && s.WgPubKey == "":
|
||||||
return ErrWireguardPublicKeyEmpty
|
return fmt.Errorf("%w", ErrWireguardPublicKeyEmpty)
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -74,13 +72,13 @@ func (s *Server) Equal(other Server) (equal bool) {
|
|||||||
return reflect.DeepEqual(serverCopy, other)
|
return reflect.DeepEqual(serverCopy, other)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipsAreEqual(a, b []net.IP) (equal bool) {
|
func ipsAreEqual(a, b []netip.Addr) (equal bool) {
|
||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range a {
|
for i := range a {
|
||||||
if !a[i].Equal(b[i]) {
|
if a[i].Compare(b[i]) != 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net/netip"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -17,28 +17,28 @@ func Test_Server_Equal(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
"same IPs": {
|
"same IPs": {
|
||||||
a: &Server{
|
a: &Server{
|
||||||
IPs: []net.IP{net.IPv4(1, 2, 3, 4)},
|
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
|
||||||
},
|
},
|
||||||
b: Server{
|
b: Server{
|
||||||
IPs: []net.IP{net.IPv4(1, 2, 3, 4)},
|
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
|
||||||
},
|
},
|
||||||
equal: true,
|
equal: true,
|
||||||
},
|
},
|
||||||
"same IP strings": {
|
"same IP strings": {
|
||||||
a: &Server{
|
a: &Server{
|
||||||
IPs: []net.IP{net.IPv4(1, 2, 3, 4)},
|
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
|
||||||
},
|
},
|
||||||
b: Server{
|
b: Server{
|
||||||
IPs: []net.IP{{1, 2, 3, 4}},
|
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
|
||||||
},
|
},
|
||||||
equal: true,
|
equal: true,
|
||||||
},
|
},
|
||||||
"different IPs": {
|
"different IPs": {
|
||||||
a: &Server{
|
a: &Server{
|
||||||
IPs: []net.IP{{1, 2, 3, 4}, {2, 3, 4, 5}},
|
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4}), netip.AddrFrom4([4]byte{2, 3, 4, 5})},
|
||||||
},
|
},
|
||||||
b: Server{
|
b: Server{
|
||||||
IPs: []net.IP{{1, 2, 3, 4}, {1, 2, 3, 4}},
|
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4}), netip.AddrFrom4([4]byte{1, 2, 3, 4})},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"all fields equal": {
|
"all fields equal": {
|
||||||
@@ -61,7 +61,7 @@ func Test_Server_Equal(t *testing.T) {
|
|||||||
Free: true,
|
Free: true,
|
||||||
Stream: true,
|
Stream: true,
|
||||||
PortForward: true,
|
PortForward: true,
|
||||||
IPs: []net.IP{net.IPv4(1, 2, 3, 4)},
|
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
|
||||||
Keep: true,
|
Keep: true,
|
||||||
},
|
},
|
||||||
b: Server{
|
b: Server{
|
||||||
@@ -83,7 +83,7 @@ func Test_Server_Equal(t *testing.T) {
|
|||||||
Free: true,
|
Free: true,
|
||||||
Stream: true,
|
Stream: true,
|
||||||
PortForward: true,
|
PortForward: true,
|
||||||
IPs: []net.IP{net.IPv4(1, 2, 3, 4)},
|
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
|
||||||
Keep: true,
|
Keep: true,
|
||||||
},
|
},
|
||||||
equal: true,
|
equal: true,
|
||||||
|
|||||||
@@ -27,13 +27,13 @@ func (a *AllServers) MarshalJSON() (data []byte, err error) {
|
|||||||
|
|
||||||
_, err = buffer.WriteString("{")
|
_, err = buffer.WriteString("{")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot write opening bracket: %w", err)
|
return nil, fmt.Errorf("writing opening bracket: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
versionString := fmt.Sprintf(`"version":%d`, a.Version)
|
versionString := fmt.Sprintf(`"version":%d`, a.Version)
|
||||||
_, err = buffer.WriteString(versionString)
|
_, err = buffer.WriteString(versionString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot write schema version string: %w", err)
|
return nil, fmt.Errorf("writing schema version string: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sortedProviders := make(sort.StringSlice, 0, len(a.ProviderToServers))
|
sortedProviders := make(sort.StringSlice, 0, len(a.ProviderToServers))
|
||||||
@@ -46,26 +46,26 @@ func (a *AllServers) MarshalJSON() (data []byte, err error) {
|
|||||||
providerKey := fmt.Sprintf(`,"%s":`, provider)
|
providerKey := fmt.Sprintf(`,"%s":`, provider)
|
||||||
_, err = buffer.WriteString(providerKey)
|
_, err = buffer.WriteString(providerKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot write provider key %s: %w",
|
return nil, fmt.Errorf("writing provider key %s: %w",
|
||||||
providerKey, err)
|
providerKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
servers := a.ProviderToServers[provider]
|
servers := a.ProviderToServers[provider]
|
||||||
serversJSON, err := json.Marshal(servers)
|
serversJSON, err := json.Marshal(servers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed encoding servers for provider %s: %w",
|
return nil, fmt.Errorf("encoding servers for provider %s: %w",
|
||||||
provider, err)
|
provider, err)
|
||||||
}
|
}
|
||||||
_, err = buffer.Write(serversJSON)
|
_, err = buffer.Write(serversJSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot write JSON servers data for provider %s: %w",
|
return nil, fmt.Errorf("writing JSON servers data for provider %s: %w",
|
||||||
provider, err)
|
provider, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = buffer.WriteString("}")
|
_, err = buffer.WriteString("}")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot write closing bracket: %w", err)
|
return nil, fmt.Errorf("writing closing bracket: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer.Bytes(), nil
|
return buffer.Bytes(), nil
|
||||||
@@ -127,14 +127,14 @@ func (a *AllServers) UnmarshalJSON(data []byte) (err error) {
|
|||||||
|
|
||||||
jsonValue, err := json.Marshal(value)
|
jsonValue, err := json.Marshal(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot marshal %s servers: %w",
|
return fmt.Errorf("encoding %s servers: %w",
|
||||||
key, err)
|
key, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var servers Servers
|
var servers Servers
|
||||||
err = json.Unmarshal(jsonValue, &servers)
|
err = json.Unmarshal(jsonValue, &servers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot unmarshal %s servers: %w",
|
return fmt.Errorf("decoding %s servers: %w",
|
||||||
key, err)
|
key, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,23 @@ const (
|
|||||||
FAMILY_V6 = netlink.FAMILY_V6
|
FAMILY_V6 = netlink.FAMILY_V6
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func FamilyToString(family int) string {
|
||||||
|
switch family {
|
||||||
|
case FAMILY_ALL:
|
||||||
|
return "all"
|
||||||
|
case FAMILY_V4:
|
||||||
|
return "v4"
|
||||||
|
case FAMILY_V6:
|
||||||
|
return "v6"
|
||||||
|
default:
|
||||||
|
return fmt.Sprint(family)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (n *NetLink) IsWireguardSupported() (ok bool, err error) {
|
func (n *NetLink) IsWireguardSupported() (ok bool, err error) {
|
||||||
families, err := netlink.GenlFamilyList()
|
families, err := netlink.GenlFamilyList()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("cannot list gen 1 families: %w", err)
|
return false, fmt.Errorf("listing gen 1 families: %w", err)
|
||||||
}
|
}
|
||||||
for _, family := range families {
|
for _, family := range families {
|
||||||
if family.Name == "wireguard" {
|
if family.Name == "wireguard" {
|
||||||
|
|||||||
8
internal/netlink/interfaces.go
Normal file
8
internal/netlink/interfaces.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package netlink
|
||||||
|
|
||||||
|
import "github.com/qdm12/log"
|
||||||
|
|
||||||
|
type DebugLogger interface {
|
||||||
|
Debugf(format string, args ...any)
|
||||||
|
Patch(options ...log.Option)
|
||||||
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package netlink
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewIPNet(ip net.IP) *net.IPNet {
|
|
||||||
return netlink.NewIPNet(ip)
|
|
||||||
}
|
|
||||||
@@ -12,6 +12,7 @@ func (n *NetLink) IsIPv6Supported() (supported bool, err error) {
|
|||||||
return false, fmt.Errorf("listing links: %w", err)
|
return false, fmt.Errorf("listing links: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var totalRoutes uint
|
||||||
for _, link := range links {
|
for _, link := range links {
|
||||||
routes, err := n.RouteList(link, netlink.FAMILY_V6)
|
routes, err := n.RouteList(link, netlink.FAMILY_V6)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -19,12 +20,21 @@ func (n *NetLink) IsIPv6Supported() (supported bool, err error) {
|
|||||||
link.Attrs().Name, err)
|
link.Attrs().Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(routes) == 0 {
|
// Check each route for IPv6 due to Podman bug listing IPv4 routes
|
||||||
continue
|
// as IPv6 routes at container start, see:
|
||||||
}
|
// https://github.com/qdm12/gluetun/issues/1241#issuecomment-1333405949
|
||||||
|
for _, route := range routes {
|
||||||
|
sourceIsIPv6 := route.Src != nil && route.Src.To4() == nil
|
||||||
|
destinationIsIPv6 := route.Dst != nil && route.Dst.IP.To4() == nil
|
||||||
|
if sourceIsIPv6 || destinationIsIPv6 {
|
||||||
|
n.debugLogger.Debugf("IPv6 is supported by link %s", link.Attrs().Name)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
totalRoutes++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n.debugLogger.Debugf("IPv6 is not supported after searching %d links and %d routes",
|
||||||
|
len(links), totalRoutes)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
package netlink
|
package netlink
|
||||||
|
|
||||||
type NetLink struct{}
|
import "github.com/qdm12/log"
|
||||||
|
|
||||||
func New() *NetLink {
|
type NetLink struct {
|
||||||
return &NetLink{}
|
debugLogger DebugLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(debugLogger DebugLogger) *NetLink {
|
||||||
|
return &NetLink{
|
||||||
|
debugLogger: debugLogger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NetLink) PatchLoggerLevel(level log.Level) {
|
||||||
|
n.debugLogger.Patch(log.SetLevel(level))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,12 @@ func (e *Extractor) Data(filepath string) (lines []string,
|
|||||||
connection models.Connection, err error) {
|
connection models.Connection, err error) {
|
||||||
lines, err = readCustomConfigLines(filepath)
|
lines, err = readCustomConfigLines(filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, connection, fmt.Errorf("cannot read configuration file: %w", err)
|
return nil, connection, fmt.Errorf("reading configuration file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
connection, err = extractDataFromLines(lines)
|
connection, err = extractDataFromLines(lines)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, connection, fmt.Errorf("cannot extract connection from file: %w", err)
|
return nil, connection, fmt.Errorf("extracting connection from file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines, connection, nil
|
return lines, connection, nil
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package extract
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -25,12 +25,12 @@ func extractDataFromLines(lines []string) (
|
|||||||
|
|
||||||
connection.UpdateEmptyWith(ip, port, protocol)
|
connection.UpdateEmptyWith(ip, port, protocol)
|
||||||
|
|
||||||
if connection.Protocol != "" && connection.IP != nil {
|
if connection.Protocol != "" && connection.IP.IsValid() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if connection.IP == nil {
|
if !connection.IP.IsValid() {
|
||||||
return connection, errRemoteLineNotFound
|
return connection, errRemoteLineNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,24 +49,24 @@ func extractDataFromLines(lines []string) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func extractDataFromLine(line string) (
|
func extractDataFromLine(line string) (
|
||||||
ip net.IP, port uint16, protocol string, err error) {
|
ip netip.Addr, port uint16, protocol string, err error) {
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(line, "proto "):
|
case strings.HasPrefix(line, "proto "):
|
||||||
protocol, err = extractProto(line)
|
protocol, err = extractProto(line)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, "", fmt.Errorf("failed extracting protocol from proto line: %w", err)
|
return ip, 0, "", fmt.Errorf("extracting protocol from proto line: %w", err)
|
||||||
}
|
}
|
||||||
return nil, 0, protocol, nil
|
return ip, 0, protocol, nil
|
||||||
|
|
||||||
case strings.HasPrefix(line, "remote "):
|
case strings.HasPrefix(line, "remote "):
|
||||||
ip, port, protocol, err = extractRemote(line)
|
ip, port, protocol, err = extractRemote(line)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, "", fmt.Errorf("failed extracting from remote line: %w", err)
|
return ip, 0, "", fmt.Errorf("extracting from remote line: %w", err)
|
||||||
}
|
}
|
||||||
return ip, port, protocol, nil
|
return ip, port, protocol, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, 0, "", nil
|
return ip, 0, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -95,19 +95,19 @@ var (
|
|||||||
errPortNotValid = errors.New("port is not valid")
|
errPortNotValid = errors.New("port is not valid")
|
||||||
)
|
)
|
||||||
|
|
||||||
func extractRemote(line string) (ip net.IP, port uint16,
|
func extractRemote(line string) (ip netip.Addr, port uint16,
|
||||||
protocol string, err error) {
|
protocol string, err error) {
|
||||||
fields := strings.Fields(line)
|
fields := strings.Fields(line)
|
||||||
n := len(fields)
|
n := len(fields)
|
||||||
|
|
||||||
if n < 2 || n > 4 {
|
if n < 2 || n > 4 {
|
||||||
return nil, 0, "", fmt.Errorf("%w: %s", errRemoteLineFieldsCount, line)
|
return netip.Addr{}, 0, "", fmt.Errorf("%w: %s", errRemoteLineFieldsCount, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
host := fields[1]
|
host := fields[1]
|
||||||
ip = net.ParseIP(host)
|
ip, err = netip.ParseAddr(host)
|
||||||
if ip == nil {
|
if err != nil {
|
||||||
return nil, 0, "", fmt.Errorf("%w: %s", errHostNotIP, host)
|
return netip.Addr{}, 0, "", fmt.Errorf("%w: %s", errHostNotIP, host)
|
||||||
// TODO resolve hostname once there is an option to allow it through
|
// TODO resolve hostname once there is an option to allow it through
|
||||||
// the firewall before the VPN is up.
|
// the firewall before the VPN is up.
|
||||||
}
|
}
|
||||||
@@ -115,9 +115,9 @@ func extractRemote(line string) (ip net.IP, port uint16,
|
|||||||
if n > 2 { //nolint:gomnd
|
if n > 2 { //nolint:gomnd
|
||||||
portInt, err := strconv.Atoi(fields[2])
|
portInt, err := strconv.Atoi(fields[2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, "", fmt.Errorf("%w: %s", errPortNotValid, line)
|
return netip.Addr{}, 0, "", fmt.Errorf("%w: %s", errPortNotValid, line)
|
||||||
} else if portInt < 1 || portInt > 65535 {
|
} else if portInt < 1 || portInt > 65535 {
|
||||||
return nil, 0, "", fmt.Errorf("%w: %d must be between 1 and 65535", errPortNotValid, portInt)
|
return netip.Addr{}, 0, "", fmt.Errorf("%w: %d must be between 1 and 65535", errPortNotValid, portInt)
|
||||||
}
|
}
|
||||||
port = uint16(portInt)
|
port = uint16(portInt)
|
||||||
}
|
}
|
||||||
@@ -127,7 +127,7 @@ func extractRemote(line string) (ip net.IP, port uint16,
|
|||||||
case "tcp", "udp":
|
case "tcp", "udp":
|
||||||
protocol = fields[3]
|
protocol = fields[3]
|
||||||
default:
|
default:
|
||||||
return nil, 0, "", fmt.Errorf("%w: %s", errProtocolNotSupported, fields[3])
|
return netip.Addr{}, 0, "", fmt.Errorf("%w: %s", errProtocolNotSupported, fields[3])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package extract
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net/netip"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
@@ -22,19 +22,19 @@ func Test_extractDataFromLines(t *testing.T) {
|
|||||||
"success": {
|
"success": {
|
||||||
lines: []string{"bla bla", "proto tcp", "remote 1.2.3.4 1194 tcp", "dev tun6"},
|
lines: []string{"bla bla", "proto tcp", "remote 1.2.3.4 1194 tcp", "dev tun6"},
|
||||||
connection: models.Connection{
|
connection: models.Connection{
|
||||||
IP: net.IPv4(1, 2, 3, 4),
|
IP: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
|
||||||
Port: 1194,
|
Port: 1194,
|
||||||
Protocol: constants.TCP,
|
Protocol: constants.TCP,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"extraction error": {
|
"extraction error": {
|
||||||
lines: []string{"bla bla", "proto bad", "remote 1.2.3.4 1194 tcp"},
|
lines: []string{"bla bla", "proto bad", "remote 1.2.3.4 1194 tcp"},
|
||||||
err: errors.New("on line 2: failed extracting protocol from proto line: network protocol not supported: bad"),
|
err: errors.New("on line 2: extracting protocol from proto line: network protocol not supported: bad"),
|
||||||
},
|
},
|
||||||
"only use first values found": {
|
"only use first values found": {
|
||||||
lines: []string{"proto udp", "proto tcp", "remote 1.2.3.4 443 tcp", "remote 5.2.3.4 1194 udp"},
|
lines: []string{"proto udp", "proto tcp", "remote 1.2.3.4 443 tcp", "remote 5.2.3.4 1194 udp"},
|
||||||
connection: models.Connection{
|
connection: models.Connection{
|
||||||
IP: net.IPv4(1, 2, 3, 4),
|
IP: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
|
||||||
Port: 443,
|
Port: 443,
|
||||||
Protocol: constants.UDP,
|
Protocol: constants.UDP,
|
||||||
},
|
},
|
||||||
@@ -49,7 +49,7 @@ func Test_extractDataFromLines(t *testing.T) {
|
|||||||
"default TCP port": {
|
"default TCP port": {
|
||||||
lines: []string{"remote 1.2.3.4", "proto tcp"},
|
lines: []string{"remote 1.2.3.4", "proto tcp"},
|
||||||
connection: models.Connection{
|
connection: models.Connection{
|
||||||
IP: net.IPv4(1, 2, 3, 4),
|
IP: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
|
||||||
Port: 443,
|
Port: 443,
|
||||||
Protocol: constants.TCP,
|
Protocol: constants.TCP,
|
||||||
},
|
},
|
||||||
@@ -57,7 +57,7 @@ func Test_extractDataFromLines(t *testing.T) {
|
|||||||
"default UDP port": {
|
"default UDP port": {
|
||||||
lines: []string{"remote 1.2.3.4", "proto udp"},
|
lines: []string{"remote 1.2.3.4", "proto udp"},
|
||||||
connection: models.Connection{
|
connection: models.Connection{
|
||||||
IP: net.IPv4(1, 2, 3, 4),
|
IP: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
|
||||||
Port: 1194,
|
Port: 1194,
|
||||||
Protocol: constants.UDP,
|
Protocol: constants.UDP,
|
||||||
},
|
},
|
||||||
@@ -88,7 +88,7 @@ func Test_extractDataFromLine(t *testing.T) {
|
|||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
line string
|
line string
|
||||||
ip net.IP
|
ip netip.Addr
|
||||||
port uint16
|
port uint16
|
||||||
protocol string
|
protocol string
|
||||||
isErr error
|
isErr error
|
||||||
@@ -110,7 +110,7 @@ func Test_extractDataFromLine(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"extract remote success": {
|
"extract remote success": {
|
||||||
line: "remote 1.2.3.4 1194 udp",
|
line: "remote 1.2.3.4 1194 udp",
|
||||||
ip: net.IPv4(1, 2, 3, 4),
|
ip: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
|
||||||
port: 1194,
|
port: 1194,
|
||||||
protocol: constants.UDP,
|
protocol: constants.UDP,
|
||||||
},
|
},
|
||||||
@@ -186,7 +186,7 @@ func Test_extractRemote(t *testing.T) {
|
|||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
line string
|
line string
|
||||||
ip net.IP
|
ip netip.Addr
|
||||||
port uint16
|
port uint16
|
||||||
protocol string
|
protocol string
|
||||||
err error
|
err error
|
||||||
@@ -205,7 +205,7 @@ func Test_extractRemote(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"only IP host": {
|
"only IP host": {
|
||||||
line: "remote 1.2.3.4",
|
line: "remote 1.2.3.4",
|
||||||
ip: net.IPv4(1, 2, 3, 4),
|
ip: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
|
||||||
},
|
},
|
||||||
"port not an integer": {
|
"port not an integer": {
|
||||||
line: "remote 1.2.3.4 bad",
|
line: "remote 1.2.3.4 bad",
|
||||||
@@ -225,7 +225,7 @@ func Test_extractRemote(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"IP host and port": {
|
"IP host and port": {
|
||||||
line: "remote 1.2.3.4 8000",
|
line: "remote 1.2.3.4 8000",
|
||||||
ip: net.IPv4(1, 2, 3, 4),
|
ip: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
|
||||||
port: 8000,
|
port: 8000,
|
||||||
},
|
},
|
||||||
"invalid protocol": {
|
"invalid protocol": {
|
||||||
@@ -234,7 +234,7 @@ func Test_extractRemote(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"IP host and port and protocol": {
|
"IP host and port and protocol": {
|
||||||
line: "remote 1.2.3.4 8000 udp",
|
line: "remote 1.2.3.4 8000 udp",
|
||||||
ip: net.IPv4(1, 2, 3, 4),
|
ip: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
|
||||||
port: 8000,
|
port: 8000,
|
||||||
protocol: constants.UDP,
|
protocol: constants.UDP,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,33 +1,23 @@
|
|||||||
package extract
|
package extract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"regexp"
|
"fmt"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errPEMDecode = errors.New("cannot decode PEM encoded block")
|
errPEMDecode = errors.New("cannot decode PEM encoded block")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
regexPEMBegin = regexp.MustCompile(`-----BEGIN [A-Za-z ]+-----`)
|
|
||||||
regexPEMEnd = regexp.MustCompile(`-----END [A-Za-z ]+-----`)
|
|
||||||
)
|
|
||||||
|
|
||||||
func PEM(b []byte) (encodedData string, err error) {
|
func PEM(b []byte) (encodedData string, err error) {
|
||||||
pemBlock, _ := pem.Decode(b)
|
pemBlock, _ := pem.Decode(b)
|
||||||
if pemBlock == nil {
|
if pemBlock == nil {
|
||||||
return "", errPEMDecode
|
return "", fmt.Errorf("%w", errPEMDecode)
|
||||||
}
|
}
|
||||||
|
|
||||||
encodedBytes := pem.EncodeToMemory(pemBlock)
|
der := pemBlock.Bytes
|
||||||
encodedData = string(encodedBytes)
|
encodedData = base64.StdEncoding.EncodeToString(der)
|
||||||
encodedData = strings.ReplaceAll(encodedData, "\n", "")
|
|
||||||
beginPrefix := regexPEMBegin.FindString(encodedData)
|
|
||||||
encodedData = strings.TrimPrefix(encodedData, beginPrefix)
|
|
||||||
endPrefix := regexPEMEnd.FindString(encodedData)
|
|
||||||
encodedData = strings.TrimSuffix(encodedData, endPrefix)
|
|
||||||
return encodedData, nil
|
return encodedData, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package extract
|
package extract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -24,6 +25,14 @@ func Test_PEM(t *testing.T) {
|
|||||||
errWrapped: errPEMDecode,
|
errWrapped: errPEMDecode,
|
||||||
errMessage: "cannot decode PEM encoded block",
|
errMessage: "cannot decode PEM encoded block",
|
||||||
},
|
},
|
||||||
|
"valid data with extras": {
|
||||||
|
b: bytes.Join([][]byte{
|
||||||
|
{1, 2, 3},
|
||||||
|
[]byte(validCertPEM),
|
||||||
|
{4, 5, 6},
|
||||||
|
}, []byte("\n")),
|
||||||
|
encodedData: validCertData,
|
||||||
|
},
|
||||||
"valid data": {
|
"valid data": {
|
||||||
b: []byte(validCertPEM),
|
b: []byte(validCertPEM),
|
||||||
encodedData: validCertData,
|
encodedData: validCertData,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user