mirror of
https://github.com/qdm12/gluetun.git
synced 2026-07-03 17:19:55 +02:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1748a2ae12 | |||
| eff46aa97a | |||
| 9fb186af75 | |||
| f1b1001863 | |||
| c5af536299 | |||
| b9b2f691a5 | |||
| bdc8817672 | |||
| a55acb2816 | |||
| d686c76db3 | |||
| 30c1ae651e | |||
| adaad62fbd | |||
| fe5ec205fc | |||
| 576400e0d9 | |||
| f08a03106f | |||
| f852b7789e | |||
| b0bd06bdc5 | |||
| 84787f0ea2 | |||
| f69b3dbbe6 | |||
| ec5ec6f02c | |||
| 5d681e635b | |||
| 3deb65b529 | |||
| 3e527fee8b | |||
| b1f1f94a76 | |||
| 43e140e6cc | |||
| 7ca9d445f1 | |||
| 90aaf71270 | |||
| 4f2570865c | |||
| 81556ec2e1 | |||
| dd5a9c6067 | |||
| 982c50c756 |
@@ -7,9 +7,11 @@ assignees: qdm12
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
**Is this urgent?**: No
|
||||||
|
|
||||||
**Host OS** (approximate answer is fine too): Ubuntu 18
|
**Host OS** (approximate answer is fine too): Ubuntu 18
|
||||||
|
|
||||||
**Is this urgent?**: No
|
**CPU arch** or **device name**: amd64
|
||||||
|
|
||||||
**What VPN provider are you using**:
|
**What VPN provider are you using**:
|
||||||
|
|
||||||
@@ -25,9 +27,7 @@ Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
|
|||||||
|
|
||||||
That feature doesn't work
|
That feature doesn't work
|
||||||
|
|
||||||
**Share your logs...**
|
**Share your logs... (careful to remove in example tokens)**
|
||||||
|
|
||||||
...*careful to remove i.e. token information with PIA port forwarding*
|
|
||||||
|
|
||||||
```log
|
```log
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ assignees:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
**Is this urgent?**: No
|
||||||
|
|
||||||
**Host OS** (approximate answer is fine too): Ubuntu 18
|
**Host OS** (approximate answer is fine too): Ubuntu 18
|
||||||
|
|
||||||
**Is this urgent?**: No
|
**CPU arch** or **device name**: amd64
|
||||||
|
|
||||||
**What VPN provider are you using**:
|
**What VPN provider are you using**:
|
||||||
|
|
||||||
@@ -23,9 +25,7 @@ Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
|
|||||||
|
|
||||||
That feature doesn't work
|
That feature doesn't work
|
||||||
|
|
||||||
**Share your logs...**
|
**Share your logs... (careful to remove in example tokens)**
|
||||||
|
|
||||||
...*careful to remove i.e. token information with PIA port forwarding*
|
|
||||||
|
|
||||||
```log
|
```log
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
name: Support a VPN provider
|
||||||
|
about: Suggest a VPN provider to be supported
|
||||||
|
title: 'VPN provider support: NAME OF THE PROVIDER'
|
||||||
|
labels: ":bulb: New provider"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
One of the following is required:
|
||||||
|
|
||||||
|
- Publicly accessible URL to a zip file containing the Openvpn configuration files
|
||||||
|
- Publicly accessible URL to a structured (JSON etc.) list of servers **and attach** an example Openvpn configuration file for both TCP and UDP
|
||||||
|
- Publicly accessible URL to the list of servers **and attach** an example Openvpn configuration file for both TCP and UDP
|
||||||
|
|
||||||
|
If the list of servers requires to login **or** is hidden behind an interactive configurator,
|
||||||
|
it's not possible to support the provider yet. Please instead subscribe to issue #223 which would solve it.
|
||||||
@@ -36,6 +36,9 @@
|
|||||||
- name: ":cloud: Surfshark"
|
- name: ":cloud: Surfshark"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
|
- name: ":cloud: Torguard"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
- name: ":cloud: Vyprvpn"
|
- name: ":cloud: Vyprvpn"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
|
|||||||
@@ -72,10 +72,10 @@ jobs:
|
|||||||
echo ::set-output name=build_date::$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
echo ::set-output name=build_date::$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||||
if [ "$TAG" != "$GITHUB_REF" ]; then
|
if [ "$TAG" != "$GITHUB_REF" ]; then
|
||||||
echo ::set-output name=version::$TAG
|
echo ::set-output name=version::$TAG
|
||||||
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/s390x,linux/ppc64le
|
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
|
||||||
elif [ "$BRANCH" = "master" ]; then
|
elif [ "$BRANCH" = "master" ]; then
|
||||||
echo ::set-output name=version::latest
|
echo ::set-output name=version::latest
|
||||||
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/s390x,linux/ppc64le
|
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
|
||||||
else
|
else
|
||||||
echo ::set-output name=version::$BRANCH
|
echo ::set-output name=version::$BRANCH
|
||||||
echo ::set-output name=platforms::linux/amd64
|
echo ::set-output name=platforms::linux/amd64
|
||||||
|
|||||||
+5
-3
@@ -10,10 +10,12 @@ issues:
|
|||||||
linters:
|
linters:
|
||||||
- dupl
|
- dupl
|
||||||
- maligned
|
- maligned
|
||||||
- path: internal/unix/constants\.go
|
- path: internal/server/
|
||||||
linters:
|
linters:
|
||||||
- golint
|
- dupl
|
||||||
text: don't use ALL_CAPS in Go names; use CamelCase
|
- path: internal/configuration/
|
||||||
|
linters:
|
||||||
|
- dupl
|
||||||
linters:
|
linters:
|
||||||
disable-all: true
|
disable-all: true
|
||||||
enable:
|
enable:
|
||||||
|
|||||||
+1
-1
@@ -66,7 +66,7 @@ ENV VPNSP=pia \
|
|||||||
VERSION_INFORMATION=on \
|
VERSION_INFORMATION=on \
|
||||||
PROTOCOL=udp \
|
PROTOCOL=udp \
|
||||||
OPENVPN_VERBOSITY=1 \
|
OPENVPN_VERBOSITY=1 \
|
||||||
OPENVPN_ROOT=no \
|
OPENVPN_ROOT=yes \
|
||||||
OPENVPN_TARGET_IP= \
|
OPENVPN_TARGET_IP= \
|
||||||
OPENVPN_IPV6=off \
|
OPENVPN_IPV6=off \
|
||||||
TZ= \
|
TZ= \
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Gluetun VPN client
|
# Gluetun VPN client
|
||||||
|
|
||||||
*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access,
|
*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access,
|
||||||
Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN and Privado VPN servers, using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN, Privado and TorGuard VPN servers, using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
||||||
|
|
||||||
**ANNOUNCEMENT**: *New Docker image name `qmcgaw/gluetun`*
|
**ANNOUNCEMENT**: *New Docker image name `qmcgaw/gluetun`*
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN and Privado
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Based on Alpine 3.12 for a small Docker image of 52MB
|
- Based on Alpine 3.12 for a small Docker image of 52MB
|
||||||
- Supports **Private Internet Access**, **Mullvad**, **Windscribe**, **Surfshark**, **Cyberghost**, **Vyprvpn**, **NordVPN**, **PureVPN** and **Privado** servers
|
- Supports **Private Internet Access**, **Mullvad**, **Windscribe**, **Surfshark**, **Cyberghost**, **Vyprvpn**, **NordVPN**, **PureVPN**, **Privado** and **TorGuard** servers
|
||||||
- Supports Openvpn only for now
|
- Supports Openvpn only for now
|
||||||
- DNS over TLS baked in with service provider(s) of your choice
|
- DNS over TLS baked in with service provider(s) of your choice
|
||||||
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
|
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
|
||||||
@@ -47,7 +47,7 @@ Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN and Privado
|
|||||||
- Built in HTTP proxy (tunnels HTTP and HTTPS through TCP)
|
- Built in HTTP proxy (tunnels HTTP and HTTPS through TCP)
|
||||||
- [Connect other containers to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
|
- [Connect other containers to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
|
||||||
- [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
|
- [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
|
||||||
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even s390x as well as ppc64le 🎆
|
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even ppc64le 🎆
|
||||||
- VPN server side port forwarding for Private Internet Access and Vyprvpn
|
- VPN server side port forwarding for Private Internet Access and Vyprvpn
|
||||||
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
|
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
|
||||||
- Subprograms all drop root privileges once launched
|
- Subprograms all drop root privileges once launched
|
||||||
|
|||||||
+5
-8
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/qdm12/dns/pkg/unbound"
|
"github.com/qdm12/dns/pkg/unbound"
|
||||||
"github.com/qdm12/gluetun/internal/alpine"
|
"github.com/qdm12/gluetun/internal/alpine"
|
||||||
"github.com/qdm12/gluetun/internal/cli"
|
"github.com/qdm12/gluetun/internal/cli"
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/dns"
|
"github.com/qdm12/gluetun/internal/dns"
|
||||||
"github.com/qdm12/gluetun/internal/firewall"
|
"github.com/qdm12/gluetun/internal/firewall"
|
||||||
@@ -22,11 +23,9 @@ import (
|
|||||||
gluetunLogging "github.com/qdm12/gluetun/internal/logging"
|
gluetunLogging "github.com/qdm12/gluetun/internal/logging"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/gluetun/internal/openvpn"
|
"github.com/qdm12/gluetun/internal/openvpn"
|
||||||
"github.com/qdm12/gluetun/internal/params"
|
|
||||||
"github.com/qdm12/gluetun/internal/publicip"
|
"github.com/qdm12/gluetun/internal/publicip"
|
||||||
"github.com/qdm12/gluetun/internal/routing"
|
"github.com/qdm12/gluetun/internal/routing"
|
||||||
"github.com/qdm12/gluetun/internal/server"
|
"github.com/qdm12/gluetun/internal/server"
|
||||||
"github.com/qdm12/gluetun/internal/settings"
|
|
||||||
"github.com/qdm12/gluetun/internal/shadowsocks"
|
"github.com/qdm12/gluetun/internal/shadowsocks"
|
||||||
"github.com/qdm12/gluetun/internal/storage"
|
"github.com/qdm12/gluetun/internal/storage"
|
||||||
"github.com/qdm12/gluetun/internal/unix"
|
"github.com/qdm12/gluetun/internal/unix"
|
||||||
@@ -35,6 +34,7 @@ import (
|
|||||||
"github.com/qdm12/golibs/logging"
|
"github.com/qdm12/golibs/logging"
|
||||||
"github.com/qdm12/golibs/os"
|
"github.com/qdm12/golibs/os"
|
||||||
"github.com/qdm12/golibs/os/user"
|
"github.com/qdm12/golibs/os/user"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
"github.com/qdm12/updated/pkg/dnscrypto"
|
"github.com/qdm12/updated/pkg/dnscrypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -140,7 +140,6 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
routingConf := routing.NewRouting(logger)
|
routingConf := routing.NewRouting(logger)
|
||||||
firewallConf := firewall.NewConfigurator(logger, routingConf, os.OpenFile)
|
firewallConf := firewall.NewConfigurator(logger, routingConf, os.OpenFile)
|
||||||
|
|
||||||
paramsReader := params.NewReader(logger, os)
|
|
||||||
fmt.Println(gluetunLogging.Splash(buildInfo))
|
fmt.Println(gluetunLogging.Splash(buildInfo))
|
||||||
|
|
||||||
printVersions(ctx, logger, map[string]func(ctx context.Context) (string, error){
|
printVersions(ctx, logger, map[string]func(ctx context.Context) (string, error){
|
||||||
@@ -149,10 +148,8 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
"IPtables": firewallConf.Version,
|
"IPtables": firewallConf.Version,
|
||||||
})
|
})
|
||||||
|
|
||||||
allSettings, warnings, err := settings.GetAllSettings(paramsReader)
|
var allSettings configuration.Settings
|
||||||
for _, warning := range warnings {
|
err := allSettings.Read(params.NewEnv(), os, logger.WithPrefix("configuration: "))
|
||||||
logger.Warn(warning)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -319,7 +316,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
|
|
||||||
if allSettings.OpenVPN.Provider.PortForwarding.Enabled {
|
if allSettings.OpenVPN.Provider.PortForwarding.Enabled {
|
||||||
logger.Info("Clearing forwarded port status file %s", allSettings.OpenVPN.Provider.PortForwarding.Filepath)
|
logger.Info("Clearing forwarded port status file %s", allSettings.OpenVPN.Provider.PortForwarding.Filepath)
|
||||||
if err := os.Remove(string(allSettings.OpenVPN.Provider.PortForwarding.Filepath)); err != nil {
|
if err := os.Remove(allSettings.OpenVPN.Provider.PortForwarding.Filepath); err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ require (
|
|||||||
github.com/fatih/color v1.10.0
|
github.com/fatih/color v1.10.0
|
||||||
github.com/golang/mock v1.4.4
|
github.com/golang/mock v1.4.4
|
||||||
github.com/kyokomi/emoji v2.2.4+incompatible
|
github.com/kyokomi/emoji v2.2.4+incompatible
|
||||||
github.com/qdm12/dns v1.4.0-rc5
|
github.com/qdm12/dns v1.4.0
|
||||||
github.com/qdm12/golibs v0.0.0-20210124192933-79a950eaf217
|
github.com/qdm12/golibs v0.0.0-20210206072445-35759e951561
|
||||||
github.com/qdm12/ss-server v0.1.0
|
github.com/qdm12/ss-server v0.1.0
|
||||||
github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a
|
github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
|
|||||||
@@ -92,11 +92,13 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
|||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/qdm12/dns v1.4.0-rc5 h1:XXjYaFI3pDY1U4YFH5t5AI5IEKlIALmnE34VFhgkdQE=
|
github.com/qdm12/dns v1.4.0 h1:P8kVMGo7yIEZSk18fA9XQh9faL1CW20aHosWP064MAA=
|
||||||
github.com/qdm12/dns v1.4.0-rc5/go.mod h1:WUY4/U8Z2O8888DPrahrIBv8GdYeoIcEy4aUDecZ+UM=
|
github.com/qdm12/dns v1.4.0/go.mod h1:WUY4/U8Z2O8888DPrahrIBv8GdYeoIcEy4aUDecZ+UM=
|
||||||
github.com/qdm12/golibs v0.0.0-20201227203847-2fd99ffdfdba/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
|
github.com/qdm12/golibs v0.0.0-20201227203847-2fd99ffdfdba/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
|
||||||
github.com/qdm12/golibs v0.0.0-20210124192933-79a950eaf217 h1:/eMBq0vbc/KmVPXbwLfssp547pp6APRS1x/JNmPvm0s=
|
github.com/qdm12/golibs v0.0.0-20210124192933-79a950eaf217 h1:/eMBq0vbc/KmVPXbwLfssp547pp6APRS1x/JNmPvm0s=
|
||||||
github.com/qdm12/golibs v0.0.0-20210124192933-79a950eaf217/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
|
github.com/qdm12/golibs v0.0.0-20210124192933-79a950eaf217/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
|
||||||
|
github.com/qdm12/golibs v0.0.0-20210206072445-35759e951561 h1:YgdQYYj4cEq8jK9TWCItlOOLfmDMVMajcp0YGVCW7cA=
|
||||||
|
github.com/qdm12/golibs v0.0.0-20210206072445-35759e951561/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
|
||||||
github.com/qdm12/ss-server v0.1.0 h1:WV9MkHCDEWRwe4WpnYFeR/zcZAxYoTbfntLDnw9AQ50=
|
github.com/qdm12/ss-server v0.1.0 h1:WV9MkHCDEWRwe4WpnYFeR/zcZAxYoTbfntLDnw9AQ50=
|
||||||
github.com/qdm12/ss-server v0.1.0/go.mod h1:ABVUkxubboL3vqBkOwDV9glX1/x7SnYrckBe5d+M/zw=
|
github.com/qdm12/ss-server v0.1.0/go.mod h1:ABVUkxubboL3vqBkOwDV9glX1/x7SnYrckBe5d+M/zw=
|
||||||
github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a h1:gkyP+gMEeBgMgyRYGrVNcoy6cL1065IvXsyfB6xboIc=
|
github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a h1:gkyP+gMEeBgMgyRYGrVNcoy6cL1065IvXsyfB6xboIc=
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Package alpine defines a configurator to interact with the Alpine operating system.
|
||||||
package alpine
|
package alpine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Package cli defines an interface CLI to run command line operations.
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
func (c *cli) ClientKey(args []string, openFile os.OpenFileFunc) error {
|
func (c *cli) ClientKey(args []string, openFile os.OpenFileFunc) error {
|
||||||
flagSet := flag.NewFlagSet("clientkey", flag.ExitOnError)
|
flagSet := flag.NewFlagSet("clientkey", flag.ExitOnError)
|
||||||
filepath := flagSet.String("path", string(constants.ClientKey), "file path to the client.key file")
|
filepath := flagSet.String("path", constants.ClientKey, "file path to the client.key file")
|
||||||
if err := flagSet.Parse(args); err != nil {
|
if err := flagSet.Parse(args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/params"
|
|
||||||
"github.com/qdm12/gluetun/internal/provider"
|
"github.com/qdm12/gluetun/internal/provider"
|
||||||
"github.com/qdm12/gluetun/internal/settings"
|
|
||||||
"github.com/qdm12/gluetun/internal/storage"
|
"github.com/qdm12/gluetun/internal/storage"
|
||||||
"github.com/qdm12/golibs/logging"
|
"github.com/qdm12/golibs/logging"
|
||||||
"github.com/qdm12/golibs/os"
|
"github.com/qdm12/golibs/os"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *cli) OpenvpnConfig(os os.OS) error {
|
func (c *cli) OpenvpnConfig(os os.OS) error {
|
||||||
@@ -19,8 +19,9 @@ func (c *cli) OpenvpnConfig(os os.OS) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
paramsReader := params.NewReader(logger, os)
|
|
||||||
allSettings, _, err := settings.GetAllSettings(paramsReader)
|
var allSettings configuration.Settings
|
||||||
|
err = allSettings.Read(params.NewEnv(), os, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/settings"
|
|
||||||
"github.com/qdm12/gluetun/internal/storage"
|
"github.com/qdm12/gluetun/internal/storage"
|
||||||
"github.com/qdm12/gluetun/internal/updater"
|
"github.com/qdm12/gluetun/internal/updater"
|
||||||
"github.com/qdm12/golibs/logging"
|
"github.com/qdm12/golibs/logging"
|
||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *cli) Update(ctx context.Context, args []string, os os.OS) error {
|
func (c *cli) Update(ctx context.Context, args []string, os os.OS) error {
|
||||||
options := settings.Updater{CLI: true}
|
options := configuration.Updater{CLI: true}
|
||||||
var flushToFile bool
|
var flushToFile bool
|
||||||
flagSet := flag.NewFlagSet("update", flag.ExitOnError)
|
flagSet := flag.NewFlagSet("update", flag.ExitOnError)
|
||||||
flagSet.BoolVar(&flushToFile, "file", false, "Write results to /gluetun/servers.json (for end users)")
|
flagSet.BoolVar(&flushToFile, "file", false, "Write results to /gluetun/servers.json (for end users)")
|
||||||
@@ -29,6 +29,7 @@ func (c *cli) Update(ctx context.Context, args []string, os os.OS) error {
|
|||||||
flagSet.BoolVar(&options.Privado, "privado", false, "Update Privado servers")
|
flagSet.BoolVar(&options.Privado, "privado", false, "Update Privado servers")
|
||||||
flagSet.BoolVar(&options.Purevpn, "purevpn", false, "Update Purevpn servers")
|
flagSet.BoolVar(&options.Purevpn, "purevpn", false, "Update Purevpn servers")
|
||||||
flagSet.BoolVar(&options.Surfshark, "surfshark", false, "Update Surfshark servers")
|
flagSet.BoolVar(&options.Surfshark, "surfshark", false, "Update Surfshark servers")
|
||||||
|
flagSet.BoolVar(&options.Torguard, "torguard", false, "Update Torguard servers")
|
||||||
flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers")
|
flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers")
|
||||||
flagSet.BoolVar(&options.Windscribe, "windscribe", false, "Update Windscribe servers")
|
flagSet.BoolVar(&options.Windscribe, "windscribe", false, "Update Windscribe servers")
|
||||||
if err := flagSet.Parse(args); err != nil {
|
if err := flagSet.Parse(args); err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// Package configuration reads initial settings from environment variables
|
||||||
|
// and secret files.
|
||||||
|
package configuration
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
const (
|
||||||
|
lastIndent = "|--"
|
||||||
|
indent = " "
|
||||||
|
)
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) cyberghostLines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+"Server group: "+settings.ServerSelection.Group)
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.ExtraConfigOptions.ClientKey != "" {
|
||||||
|
lines = append(lines, lastIndent+"Client key is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.ExtraConfigOptions.ClientCertificate != "" {
|
||||||
|
lines = append(lines, lastIndent+"Client certificate is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readCyberghost(r reader) (err error) {
|
||||||
|
settings.Name = constants.Cyberghost
|
||||||
|
|
||||||
|
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ExtraConfigOptions.ClientKey, err = readCyberghostClientKey(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ExtraConfigOptions.ClientCertificate, err = readCyberghostClientCertificate(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Group, err = r.env.Inside("CYBERGHOST_GROUP",
|
||||||
|
constants.CyberghostGroupChoices(), params.Default("Premium UDP Europe"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.CyberghostRegionChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readCyberghostClientKey(r reader) (clientKey string, err error) {
|
||||||
|
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTKEY", constants.ClientKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return extractClientKey(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractClientKey(b []byte) (key string, err error) {
|
||||||
|
pemBlock, _ := pem.Decode(b)
|
||||||
|
if pemBlock == nil {
|
||||||
|
return "", fmt.Errorf("cannot decode PEM block from client key")
|
||||||
|
}
|
||||||
|
parsedBytes := pem.EncodeToMemory(pemBlock)
|
||||||
|
s := string(parsedBytes)
|
||||||
|
s = strings.ReplaceAll(s, "\n", "")
|
||||||
|
s = strings.TrimPrefix(s, "-----BEGIN PRIVATE KEY-----")
|
||||||
|
s = strings.TrimSuffix(s, "-----END PRIVATE KEY-----")
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readCyberghostClientCertificate(r reader) (clientCertificate string, err error) {
|
||||||
|
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTCRT", constants.ClientCertificate)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return extractClientCertificate(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractClientCertificate(b []byte) (certificate string, err error) {
|
||||||
|
pemBlock, _ := pem.Decode(b)
|
||||||
|
if pemBlock == nil {
|
||||||
|
return "", fmt.Errorf("cannot decode PEM block from client certificate")
|
||||||
|
}
|
||||||
|
parsedBytes := pem.EncodeToMemory(pemBlock)
|
||||||
|
s := string(parsedBytes)
|
||||||
|
s = strings.ReplaceAll(s, "\n", "")
|
||||||
|
s = strings.TrimPrefix(s, "-----BEGIN CERTIFICATE-----")
|
||||||
|
s = strings.TrimSuffix(s, "-----END CERTIFICATE-----")
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package params
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
unboundmodels "github.com/qdm12/dns/pkg/models"
|
||||||
|
unbound "github.com/qdm12/dns/pkg/unbound"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DNS contains settings to configure Unbound for DNS over TLS operation.
|
||||||
|
type DNS struct { //nolint:maligned
|
||||||
|
Enabled bool
|
||||||
|
PlaintextAddress net.IP
|
||||||
|
KeepNameserver bool
|
||||||
|
BlockMalicious bool
|
||||||
|
BlockAds bool
|
||||||
|
BlockSurveillance bool
|
||||||
|
UpdatePeriod time.Duration
|
||||||
|
Unbound unboundmodels.Settings
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *DNS) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *DNS) lines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+"DNS:")
|
||||||
|
|
||||||
|
if settings.PlaintextAddress != nil {
|
||||||
|
lines = append(lines, indent+lastIndent+"Plaintext address: "+settings.PlaintextAddress.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.KeepNameserver {
|
||||||
|
lines = append(lines, indent+lastIndent+"Keep nameserver (disabled blocking): yes")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !settings.Enabled {
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"DNS over TLS:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+indent+lastIndent+"Unbound:")
|
||||||
|
for _, line := range settings.Unbound.Lines() {
|
||||||
|
lines = append(lines, indent+indent+indent+line)
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.BlockMalicious {
|
||||||
|
lines = append(lines, indent+indent+lastIndent+"Block malicious: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.BlockAds {
|
||||||
|
lines = append(lines, indent+indent+lastIndent+"Block ads: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.BlockSurveillance {
|
||||||
|
lines = append(lines, indent+indent+lastIndent+"Block surveillance: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.UpdatePeriod > 0 {
|
||||||
|
lines = append(lines, indent+indent+lastIndent+"Update: every "+settings.UpdatePeriod.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnboundSettings = errors.New("failed getting Unbound settings")
|
||||||
|
ErrDNSProviderNoData = errors.New("DNS provider has no associated data")
|
||||||
|
ErrDNSProviderNoTLS = errors.New("DNS provider does not support DNS over TLS")
|
||||||
|
ErrDNSNoIPv6Support = errors.New("no DNS provider supports IPv6")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *DNS) read(r reader) (err error) {
|
||||||
|
settings.Enabled, err = r.env.OnOff("DOT", params.Default("on"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plain DNS settings
|
||||||
|
if err := settings.readDNSPlaintext(r.env); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settings.KeepNameserver, err = r.env.OnOff("DNS_KEEP_NAMESERVER", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNS over TLS external settings
|
||||||
|
settings.BlockMalicious, err = r.env.OnOff("BLOCK_MALICIOUS", params.Default("on"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settings.BlockSurveillance, err = r.env.OnOff("BLOCK_SURVEILLANCE", params.Default("on"),
|
||||||
|
params.RetroKeys([]string{"BLOCK_NSA"}, r.onRetroActive))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settings.BlockAds, err = r.env.OnOff("BLOCK_ADS", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settings.UpdatePeriod, err = r.env.Duration("DNS_UPDATE_PERIOD", params.Default("24h"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.readUnbound(r); err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrUnboundSettings, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consistency check
|
||||||
|
IPv6Support := false
|
||||||
|
for _, provider := range settings.Unbound.Providers {
|
||||||
|
providerData, ok := unbound.GetProviderData(provider)
|
||||||
|
switch {
|
||||||
|
case !ok:
|
||||||
|
return fmt.Errorf("%w: %s", ErrDNSProviderNoData, provider)
|
||||||
|
case !providerData.SupportsTLS:
|
||||||
|
return fmt.Errorf("%w: %s", ErrDNSProviderNoTLS, provider)
|
||||||
|
case providerData.SupportsIPv6:
|
||||||
|
IPv6Support = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Unbound.IPv6 && !IPv6Support {
|
||||||
|
return ErrDNSNoIPv6Support
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrDNSAddressNotAnIP = errors.New("DNS plaintext address is not an IP address")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *DNS) readDNSPlaintext(env params.Env) error {
|
||||||
|
s, err := env.Get("DNS_PLAINTEXT_ADDRESS", params.Default("1.1.1.1"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.PlaintextAddress = net.ParseIP(s)
|
||||||
|
if settings.PlaintextAddress == nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrDNSAddressNotAnIP, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/dns/pkg/models"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_DNS_Lines(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
testCases := map[string]struct {
|
||||||
|
settings DNS
|
||||||
|
lines []string
|
||||||
|
}{
|
||||||
|
"disabled DOT": {
|
||||||
|
settings: DNS{
|
||||||
|
PlaintextAddress: net.IP{1, 1, 1, 1},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--DNS:",
|
||||||
|
" |--Plaintext address: 1.1.1.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"enabled DOT": {
|
||||||
|
settings: DNS{
|
||||||
|
Enabled: true,
|
||||||
|
KeepNameserver: true,
|
||||||
|
Unbound: models.Settings{
|
||||||
|
Providers: []string{"cloudflare"},
|
||||||
|
},
|
||||||
|
BlockMalicious: true,
|
||||||
|
BlockAds: true,
|
||||||
|
BlockSurveillance: true,
|
||||||
|
UpdatePeriod: time.Hour,
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--DNS:",
|
||||||
|
" |--Keep nameserver (disabled blocking): yes",
|
||||||
|
" |--DNS over TLS:",
|
||||||
|
" |--Unbound:",
|
||||||
|
" |--DNS over TLS providers:",
|
||||||
|
" |--cloudflare",
|
||||||
|
" |--Listening port: 0",
|
||||||
|
" |--Access control:",
|
||||||
|
" |--Allowed:",
|
||||||
|
" |--Caching: disabled",
|
||||||
|
" |--IPv4 resolution: disabled",
|
||||||
|
" |--IPv6 resolution: disabled",
|
||||||
|
" |--Verbosity level: 0/5",
|
||||||
|
" |--Verbosity details level: 0/4",
|
||||||
|
" |--Validation log level: 0/2",
|
||||||
|
" |--Blocked hostnames:",
|
||||||
|
" |--Blocked IP addresses:",
|
||||||
|
" |--Allowed hostnames:",
|
||||||
|
" |--Block malicious: enabled",
|
||||||
|
" |--Block ads: enabled",
|
||||||
|
" |--Block surveillance: enabled",
|
||||||
|
" |--Update: every 1h0m0s",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
lines := testCase.settings.lines()
|
||||||
|
assert.Equal(t, testCase.lines, lines)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Firewall contains settings to customize the firewall operation.
|
||||||
|
type Firewall struct {
|
||||||
|
VPNInputPorts []uint16
|
||||||
|
InputPorts []uint16
|
||||||
|
OutboundSubnets []net.IPNet
|
||||||
|
Enabled bool
|
||||||
|
Debug bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Firewall) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Firewall) lines() (lines []string) {
|
||||||
|
if !settings.Enabled {
|
||||||
|
lines = append(lines, lastIndent+"Firewall: disabled ⚠️")
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, lastIndent+"Firewall:")
|
||||||
|
|
||||||
|
if settings.Debug {
|
||||||
|
lines = append(lines, indent+lastIndent+"Debug: on")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.VPNInputPorts) > 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"VPN input ports: "+
|
||||||
|
strings.Join(uint16sToStrings(settings.VPNInputPorts), ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.InputPorts) > 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Input ports: "+
|
||||||
|
strings.Join(uint16sToStrings(settings.InputPorts), ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.OutboundSubnets) > 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Outbound subnets: "+
|
||||||
|
strings.Join(ipNetsToStrings(settings.OutboundSubnets), ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Firewall) read(r reader) (err error) {
|
||||||
|
settings.Enabled, err = r.env.OnOff("FIREWALL", params.Default("on"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Debug, err = r.env.OnOff("FIREWALL_DEBUG", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.readVPNInputPorts(r.env); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.readInputPorts(r.env); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.readOutboundSubnets(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Firewall) readVPNInputPorts(env params.Env) (err error) {
|
||||||
|
settings.VPNInputPorts, err = readCSVPorts(env, "FIREWALL_VPN_INPUT_PORTS")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Firewall) readInputPorts(env params.Env) (err error) {
|
||||||
|
settings.InputPorts, err = readCSVPorts(env, "FIREWALL_INPUT_PORTS")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Firewall) readOutboundSubnets(r reader) (err error) {
|
||||||
|
retroOption := params.RetroKeys([]string{"EXTRA_SUBNETS"}, r.onRetroActive)
|
||||||
|
settings.OutboundSubnets, err = readCSVIPNets(r.env, "FIREWALL_OUTBOUND_SUBNETS", retroOption)
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPProxy contains settings to configure the HTTP proxy.
|
||||||
|
type HTTPProxy struct {
|
||||||
|
User string
|
||||||
|
Password string
|
||||||
|
Port uint16
|
||||||
|
Enabled bool
|
||||||
|
Stealth bool
|
||||||
|
Log bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *HTTPProxy) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *HTTPProxy) lines() (lines []string) {
|
||||||
|
if !settings.Enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, lastIndent+"HTTP proxy:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Port: "+strconv.Itoa(int(settings.Port)))
|
||||||
|
|
||||||
|
if settings.User != "" {
|
||||||
|
lines = append(lines, indent+lastIndent+"Authentication: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Log {
|
||||||
|
lines = append(lines, indent+lastIndent+"Log: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Stealth {
|
||||||
|
lines = append(lines, indent+lastIndent+"Stealth: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *HTTPProxy) read(r reader) (err error) {
|
||||||
|
settings.Enabled, err = r.env.OnOff("HTTPPROXY", params.Default("off"),
|
||||||
|
params.RetroKeys([]string{"TINYPROXY", "PROXY"}, r.onRetroActive))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.User, err = r.getFromEnvOrSecretFile("HTTPPROXY_USER", false, // compulsory
|
||||||
|
[]string{"TINYPROXY_USER", "PROXY_USER"})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Password, err = r.getFromEnvOrSecretFile("HTTPPROXY_PASSWORD", false,
|
||||||
|
[]string{"TINYPROXY_PASSWORD", "PROXY_PASSWORD"})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Stealth, err = r.env.OnOff("HTTPPROXY_STEALTH", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.readLog(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var warning string
|
||||||
|
settings.Port, warning, err = r.env.ListeningPort("HTTPPROXY_PORT", params.Default("8888"),
|
||||||
|
params.RetroKeys([]string{"TINYPROXY_PORT", "PROXY_PORT"}, r.onRetroActive))
|
||||||
|
if len(warning) > 0 {
|
||||||
|
r.logger.Warn(warning)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *HTTPProxy) readLog(r reader) error {
|
||||||
|
s, err := r.env.Get("HTTPPROXY_LOG",
|
||||||
|
params.RetroKeys([]string{"PROXY_LOG_LEVEL", "TINYPROXY_LOG"}, r.onRetroActive))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch strings.ToLower(s) {
|
||||||
|
case "on":
|
||||||
|
settings.Log = true
|
||||||
|
// Retro compatibility
|
||||||
|
case "info", "connect", "notice":
|
||||||
|
settings.Log = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func uint16sToStrings(uint16s []uint16) (strings []string) {
|
||||||
|
strings = make([]string, len(uint16s))
|
||||||
|
for i := range uint16s {
|
||||||
|
strings[i] = strconv.Itoa(int(uint16s[i]))
|
||||||
|
}
|
||||||
|
return strings
|
||||||
|
}
|
||||||
|
|
||||||
|
func ipNetsToStrings(ipNets []net.IPNet) (strings []string) {
|
||||||
|
strings = make([]string, len(ipNets))
|
||||||
|
for i := range ipNets {
|
||||||
|
strings[i] = ipNets[i].String()
|
||||||
|
}
|
||||||
|
return strings
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) mullvadLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Countries) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.ISPs) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"ISPs: "+commaJoin(settings.ServerSelection.ISPs))
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.ServerSelection.CustomPort > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.ExtraConfigOptions.OpenVPNIPv6 {
|
||||||
|
lines = append(lines, lastIndent+"IPv6: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readMullvad(r reader) (err error) {
|
||||||
|
settings.Name = constants.Mullvad
|
||||||
|
|
||||||
|
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.MullvadCountryChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.MullvadCityChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.MullvadISPChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.CustomPort, err = readCustomPort(r.env, settings.ServerSelection.Protocol,
|
||||||
|
[]uint16{80, 443, 1401}, []uint16{53, 1194, 1195, 1196, 1197, 1300, 1301, 1302, 1303, 1400})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Owned, err = r.env.YesNo("OWNED", params.Default("no"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ExtraConfigOptions.OpenVPNIPv6, err = r.env.OnOff("OPENVPN_IPV6", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) nordvpnLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
if numbersUint16 := settings.ServerSelection.Numbers; len(numbersUint16) > 0 {
|
||||||
|
numbersString := make([]string, len(numbersUint16))
|
||||||
|
for i, numberUint16 := range numbersUint16 {
|
||||||
|
numbersString[i] = strconv.Itoa(int(numberUint16))
|
||||||
|
}
|
||||||
|
lines = append(lines, lastIndent+"Numbers: "+commaJoin(numbersString))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readNordvpn(r reader) (err error) {
|
||||||
|
settings.Name = constants.Nordvpn
|
||||||
|
|
||||||
|
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.NordvpnRegionChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Numbers, err = readNordVPNServerNumbers(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readNordVPNServerNumbers(env params.Env) (numbers []uint16, err error) {
|
||||||
|
possibilities := make([]string, 65537)
|
||||||
|
for i := range possibilities {
|
||||||
|
possibilities[i] = fmt.Sprintf("%d", i)
|
||||||
|
}
|
||||||
|
possibilities[65536] = ""
|
||||||
|
values, err := env.CSVInside("SERVER_NUMBER", possibilities)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
numbers = make([]uint16, len(values))
|
||||||
|
for i := range values {
|
||||||
|
n, err := strconv.Atoi(values[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
numbers[i] = uint16(n)
|
||||||
|
}
|
||||||
|
return numbers, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OpenVPN contains settings to configure the OpenVPN client.
|
||||||
|
type OpenVPN struct {
|
||||||
|
User string `json:"user"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Verbosity int `json:"verbosity"`
|
||||||
|
MSSFix uint16 `json:"mssfix"`
|
||||||
|
Root bool `json:"run_as_root"`
|
||||||
|
Cipher string `json:"cipher"`
|
||||||
|
Auth string `json:"auth"`
|
||||||
|
Provider Provider `json:"provider"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPN) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *OpenVPN) lines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+"OpenVPN:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Verbosity level: "+strconv.Itoa(settings.Verbosity))
|
||||||
|
|
||||||
|
if settings.Root {
|
||||||
|
lines = append(lines, indent+lastIndent+"Run as root: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.Cipher) > 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Custom cipher: "+settings.Cipher)
|
||||||
|
}
|
||||||
|
if len(settings.Auth) > 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Custom auth algorithm: "+settings.Auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Provider:")
|
||||||
|
for _, line := range settings.Provider.lines() {
|
||||||
|
lines = append(lines, indent+indent+line)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidVPNProvider = errors.New("invalid VPN provider")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *OpenVPN) read(r reader) (err error) {
|
||||||
|
vpnsp, err := r.env.Inside("VPNSP", []string{
|
||||||
|
"pia", "private internet access", "mullvad", "windscribe", "surfshark", "torguard",
|
||||||
|
"cyberghost", "vyprvpn", "nordvpn", "purevpn", "privado"},
|
||||||
|
params.Default("private internet access"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vpnsp == "pia" { // retro compatibility
|
||||||
|
vpnsp = "private internet access"
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Provider.Name = vpnsp
|
||||||
|
|
||||||
|
settings.User, err = r.getFromEnvOrSecretFile("OPENVPN_USER", true, []string{"USER"})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Remove spaces in user ID to simplify user's life, thanks @JeordyR
|
||||||
|
settings.User = strings.ReplaceAll(settings.User, " ", "")
|
||||||
|
|
||||||
|
if settings.Provider.Name == constants.Mullvad {
|
||||||
|
settings.Password = "m"
|
||||||
|
} else {
|
||||||
|
settings.Password, err = r.getFromEnvOrSecretFile("OPENVPN_PASSWORD", true, []string{"PASSWORD"})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Verbosity, err = r.env.IntRange("OPENVPN_VERBOSITY", 0, 6, params.Default("1"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Root, err = r.env.YesNo("OPENVPN_ROOT", params.Default("yes"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Cipher, err = r.env.Get("OPENVPN_CIPHER")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Auth, err = r.env.Get("OPENVPN_AUTH")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
mssFix, err := r.env.IntRange("OPENVPN_MSSFIX", 0, 10000, params.Default("0"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settings.MSSFix = uint16(mssFix)
|
||||||
|
|
||||||
|
var readProvider func(r reader) error
|
||||||
|
switch settings.Provider.Name {
|
||||||
|
case constants.PrivateInternetAccess:
|
||||||
|
readProvider = settings.Provider.readPrivateInternetAccess
|
||||||
|
case constants.Mullvad:
|
||||||
|
readProvider = settings.Provider.readMullvad
|
||||||
|
case constants.Windscribe:
|
||||||
|
readProvider = settings.Provider.readWindscribe
|
||||||
|
case constants.Surfshark:
|
||||||
|
readProvider = settings.Provider.readSurfshark
|
||||||
|
case constants.Cyberghost:
|
||||||
|
readProvider = settings.Provider.readCyberghost
|
||||||
|
case constants.Vyprvpn:
|
||||||
|
readProvider = settings.Provider.readVyprvpn
|
||||||
|
case constants.Nordvpn:
|
||||||
|
readProvider = settings.Provider.readNordvpn
|
||||||
|
case constants.Purevpn:
|
||||||
|
readProvider = settings.Provider.readPurevpn
|
||||||
|
case constants.Privado:
|
||||||
|
readProvider = settings.Provider.readPrivado
|
||||||
|
case constants.Torguard:
|
||||||
|
readProvider = settings.Provider.readTorguard
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%w: %s", ErrInvalidVPNProvider, settings.Provider.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return readProvider(r)
|
||||||
|
}
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
package settings
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -13,7 +12,7 @@ func Test_OpenVPN_JSON(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
in := OpenVPN{
|
in := OpenVPN{
|
||||||
Root: true,
|
Root: true,
|
||||||
Provider: models.ProviderSettings{
|
Provider: Provider{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) privadoLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readPrivado(r reader) (err error) {
|
||||||
|
settings.Name = constants.Privado
|
||||||
|
|
||||||
|
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivadoHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) privateinternetaccessLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, lastIndent+"Encryption preset: "+settings.ServerSelection.EncryptionPreset)
|
||||||
|
|
||||||
|
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
|
||||||
|
|
||||||
|
if settings.PortForwarding.Enabled {
|
||||||
|
lines = append(lines, lastIndent+"Port forwarding:")
|
||||||
|
for _, line := range settings.PortForwarding.lines() {
|
||||||
|
lines = append(lines, indent+line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readPrivateInternetAccess(r reader) (err error) {
|
||||||
|
settings.Name = constants.PrivateInternetAccess
|
||||||
|
|
||||||
|
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptionPreset, err := r.env.Inside("PIA_ENCRYPTION",
|
||||||
|
[]string{constants.PIAEncryptionPresetNormal, constants.PIAEncryptionPresetStrong},
|
||||||
|
params.RetroKeys([]string{"ENCRYPTION"}, r.onRetroActive),
|
||||||
|
params.Default(constants.PIACertificateStrong),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settings.ServerSelection.EncryptionPreset = encryptionPreset
|
||||||
|
settings.ExtraConfigOptions.EncryptionPreset = encryptionPreset
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PIAGeoChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.CustomPort, err = readPortOrZero(r.env, "PORT")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.PortForwarding.Enabled, err = r.env.OnOff("PORT_FORWARDING", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.PortForwarding.Enabled {
|
||||||
|
settings.PortForwarding.Filepath, err = r.env.Path("PORT_FORWARDING_STATUS_FILE",
|
||||||
|
params.Default("/tmp/gluetun/forwarded_port"), params.CaseSensitiveValue())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Provider contains settings specific to a VPN provider.
|
||||||
|
type Provider struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ServerSelection ServerSelection `json:"server_selection"`
|
||||||
|
ExtraConfigOptions ExtraConfigOptions `json:"extra_config"`
|
||||||
|
PortForwarding PortForwarding `json:"port_forwarding"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) lines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+strings.Title(settings.Name)+" settings:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Network protocol: "+settings.ServerSelection.Protocol)
|
||||||
|
|
||||||
|
if settings.ServerSelection.TargetIP != nil {
|
||||||
|
lines = append(lines, indent+lastIndent+"Target IP address: "+settings.ServerSelection.TargetIP.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
var providerLines []string
|
||||||
|
switch strings.ToLower(settings.Name) {
|
||||||
|
case "cyberghost":
|
||||||
|
providerLines = settings.cyberghostLines()
|
||||||
|
case "mullvad":
|
||||||
|
providerLines = settings.mullvadLines()
|
||||||
|
case "nordvpn":
|
||||||
|
providerLines = settings.nordvpnLines()
|
||||||
|
case "privado":
|
||||||
|
providerLines = settings.privadoLines()
|
||||||
|
case "private internet access":
|
||||||
|
providerLines = settings.privateinternetaccessLines()
|
||||||
|
case "purevpn":
|
||||||
|
providerLines = settings.purevpnLines()
|
||||||
|
case "surfshark":
|
||||||
|
providerLines = settings.surfsharkLines()
|
||||||
|
case "torguard":
|
||||||
|
providerLines = settings.torguardLines()
|
||||||
|
case "vyprvpn":
|
||||||
|
providerLines = settings.vyprvpnLines()
|
||||||
|
case "windscribe":
|
||||||
|
providerLines = settings.windscribeLines()
|
||||||
|
default:
|
||||||
|
panic(`Missing lines method for provider "` +
|
||||||
|
settings.Name + `"! Please create a Github issue.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range providerLines {
|
||||||
|
lines = append(lines, indent+line)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func commaJoin(slice []string) string {
|
||||||
|
return strings.Join(slice, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func readProtocol(env params.Env) (protocol string, err error) {
|
||||||
|
return env.Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, params.Default(constants.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTargetIP(env params.Env) (targetIP net.IP, err error) {
|
||||||
|
return readIP(env, "OPENVPN_TARGET_IP")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidProtocol = errors.New("invalid network protocol")
|
||||||
|
)
|
||||||
|
|
||||||
|
func readCustomPort(env params.Env, protocol string,
|
||||||
|
allowedTCP, allowedUDP []uint16) (port uint16, err error) {
|
||||||
|
port, err = readPortOrZero(env, "PORT")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else if port == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch protocol {
|
||||||
|
case constants.TCP:
|
||||||
|
for i := range allowedTCP {
|
||||||
|
if allowedTCP[i] == port {
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("%w: port %d for TCP protocol", ErrInvalidPort, port)
|
||||||
|
case constants.UDP:
|
||||||
|
for i := range allowedUDP {
|
||||||
|
if allowedUDP[i] == port {
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("%w: port %d for UDP protocol", ErrInvalidPort, port)
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("%w: %s", ErrInvalidProtocol, protocol)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,263 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/golibs/params/mock_params"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errDummy = errors.New("dummy")
|
||||||
|
|
||||||
|
func Test_Provider_lines(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
settings Provider
|
||||||
|
lines []string
|
||||||
|
}{
|
||||||
|
"cyberghost": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Cyberghost,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Protocol: constants.UDP,
|
||||||
|
Group: "group",
|
||||||
|
Regions: []string{"a", "El country"},
|
||||||
|
},
|
||||||
|
ExtraConfigOptions: ExtraConfigOptions{
|
||||||
|
ClientKey: "a",
|
||||||
|
ClientCertificate: "a",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Cyberghost settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Server group: group",
|
||||||
|
" |--Regions: a, El country",
|
||||||
|
" |--Client key is set",
|
||||||
|
" |--Client certificate is set",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"mullvad": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Mullvad,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Protocol: constants.UDP,
|
||||||
|
Countries: []string{"a", "b"},
|
||||||
|
Cities: []string{"c", "d"},
|
||||||
|
ISPs: []string{"e", "f"},
|
||||||
|
CustomPort: 1,
|
||||||
|
},
|
||||||
|
ExtraConfigOptions: ExtraConfigOptions{
|
||||||
|
OpenVPNIPv6: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Mullvad settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Countries: a, b",
|
||||||
|
" |--Cities: c, d",
|
||||||
|
" |--ISPs: e, f",
|
||||||
|
" |--Custom port: 1",
|
||||||
|
" |--IPv6: enabled",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"nordvpn": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Nordvpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Protocol: constants.UDP,
|
||||||
|
Regions: []string{"a", "b"},
|
||||||
|
Numbers: []uint16{1, 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Nordvpn settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Regions: a, b",
|
||||||
|
" |--Numbers: 1, 2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"privado": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Privado,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Protocol: constants.UDP,
|
||||||
|
Hostnames: []string{"a", "b"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Privado settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Hostnames: a, b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"private internet access": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.PrivateInternetAccess,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Protocol: constants.UDP,
|
||||||
|
Regions: []string{"a", "b"},
|
||||||
|
EncryptionPreset: constants.PIAEncryptionPresetStrong,
|
||||||
|
CustomPort: 1,
|
||||||
|
},
|
||||||
|
PortForwarding: PortForwarding{
|
||||||
|
Enabled: true,
|
||||||
|
Filepath: string("/here"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Private Internet Access settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Regions: a, b",
|
||||||
|
" |--Encryption preset: strong",
|
||||||
|
" |--Custom port: 1",
|
||||||
|
" |--Port forwarding:",
|
||||||
|
" |--File path: /here",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"purevpn": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Purevpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Protocol: constants.UDP,
|
||||||
|
Regions: []string{"a", "b"},
|
||||||
|
Countries: []string{"c", "d"},
|
||||||
|
Cities: []string{"e", "f"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Purevpn settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Regions: a, b",
|
||||||
|
" |--Countries: c, d",
|
||||||
|
" |--Cities: e, f",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"surfshark": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Surfshark,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Protocol: constants.UDP,
|
||||||
|
Regions: []string{"a", "b"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Surfshark settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Regions: a, b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"torguard": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Torguard,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Protocol: constants.UDP,
|
||||||
|
Countries: []string{"a", "b"},
|
||||||
|
Cities: []string{"c", "d"},
|
||||||
|
Hostnames: []string{"e"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Torguard settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Countries: a, b",
|
||||||
|
" |--Cities: c, d",
|
||||||
|
" |--Hostnames: e",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"vyprvpn": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Vyprvpn,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Protocol: constants.UDP,
|
||||||
|
Regions: []string{"a", "b"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Vyprvpn settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Regions: a, b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"windscribe": {
|
||||||
|
settings: Provider{
|
||||||
|
Name: constants.Windscribe,
|
||||||
|
ServerSelection: ServerSelection{
|
||||||
|
Protocol: constants.UDP,
|
||||||
|
Regions: []string{"a", "b"},
|
||||||
|
Cities: []string{"c", "d"},
|
||||||
|
Hostnames: []string{"e", "f"},
|
||||||
|
CustomPort: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"|--Windscribe settings:",
|
||||||
|
" |--Network protocol: udp",
|
||||||
|
" |--Regions: a, b",
|
||||||
|
" |--Cities: c, d",
|
||||||
|
" |--Hostnames: e, f",
|
||||||
|
" |--Custom port: 1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
lines := testCase.settings.lines()
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.lines, lines)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_readProtocol(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
mockStr string
|
||||||
|
mockErr error
|
||||||
|
protocol string
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
"error": {
|
||||||
|
mockErr: errDummy,
|
||||||
|
err: errDummy,
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
mockStr: "tcp",
|
||||||
|
protocol: constants.TCP,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
|
||||||
|
env := mock_params.NewMockEnv(ctrl)
|
||||||
|
env.EXPECT().
|
||||||
|
Inside("PROTOCOL", []string{"tcp", "udp"}, gomock.Any()).
|
||||||
|
Return(testCase.mockStr, testCase.mockErr)
|
||||||
|
|
||||||
|
protocol, err := readProtocol(env)
|
||||||
|
|
||||||
|
if testCase.err != nil {
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Equal(t, testCase.err.Error(), err.Error())
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.protocol, protocol)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PublicIP struct {
|
||||||
|
Period time.Duration `json:"period"`
|
||||||
|
IPFilepath string `json:"ip_filepath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *PublicIP) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *PublicIP) lines() (lines []string) {
|
||||||
|
if settings.Period == 0 {
|
||||||
|
lines = append(lines, lastIndent+"Public IP getter: disabled")
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, lastIndent+"Public IP getter:")
|
||||||
|
lines = append(lines, indent+lastIndent+"Fetch period: "+settings.Period.String())
|
||||||
|
lines = append(lines, indent+lastIndent+"IP file: "+settings.IPFilepath)
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *PublicIP) read(r reader) (err error) {
|
||||||
|
settings.Period, err = r.env.Duration("PUBLICIP_PERIOD", params.Default("12h"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.IPFilepath, err = r.env.Path("PUBLICIP_FILE", params.CaseSensitiveValue(),
|
||||||
|
params.Default("/tmp/gluetun/ip"),
|
||||||
|
params.RetroKeys([]string{"IP_STATUS_FILE"}, r.onRetroActive))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) purevpnLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Countries) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readPurevpn(r reader) (err error) {
|
||||||
|
settings.Name = constants.Purevpn
|
||||||
|
|
||||||
|
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PurevpnRegionChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PurevpnCountryChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PurevpnCityChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/logging"
|
||||||
|
"github.com/qdm12/golibs/os"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
"github.com/qdm12/golibs/verification"
|
||||||
|
)
|
||||||
|
|
||||||
|
type reader struct {
|
||||||
|
env params.Env
|
||||||
|
logger logging.Logger
|
||||||
|
regex verification.Regex
|
||||||
|
os os.OS
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReader(env params.Env, os os.OS, logger logging.Logger) reader {
|
||||||
|
return reader{
|
||||||
|
env: env,
|
||||||
|
logger: logger,
|
||||||
|
regex: verification.NewRegex(),
|
||||||
|
os: os,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reader) onRetroActive(oldKey, newKey string) {
|
||||||
|
r.logger.Warn(
|
||||||
|
"You are using the old environment variable %s, please consider changing it to %s",
|
||||||
|
oldKey, newKey,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidPort = errors.New("invalid port")
|
||||||
|
)
|
||||||
|
|
||||||
|
func readCSVPorts(env params.Env, key string) (ports []uint16, err error) {
|
||||||
|
s, err := env.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if len(s) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
portsStr := strings.Split(s, ",")
|
||||||
|
ports = make([]uint16, len(portsStr))
|
||||||
|
for i, portStr := range portsStr {
|
||||||
|
portInt, err := strconv.Atoi(portStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: %q from environment variable %s: %s",
|
||||||
|
ErrInvalidPort, portStr, key, err)
|
||||||
|
} else if portInt <= 0 || portInt > 65535 {
|
||||||
|
return nil, fmt.Errorf("%w: %d from environment variable %s: must be between 1 and 65535",
|
||||||
|
ErrInvalidPort, portInt, key)
|
||||||
|
}
|
||||||
|
ports[i] = uint16(portInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ports, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidIPNet = errors.New("invalid IP network")
|
||||||
|
)
|
||||||
|
|
||||||
|
func readCSVIPNets(env params.Env, key string, options ...params.OptionSetter) (
|
||||||
|
ipNets []net.IPNet, err error) {
|
||||||
|
s, err := env.Get(key, options...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if s == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ipNetsStr := strings.Split(s, ",")
|
||||||
|
ipNets = make([]net.IPNet, len(ipNetsStr))
|
||||||
|
for i, ipNetStr := range ipNetsStr {
|
||||||
|
_, ipNet, err := net.ParseCIDR(ipNetStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: %q from environment variable %s: %s",
|
||||||
|
ErrInvalidIPNet, ipNetStr, key, err)
|
||||||
|
} else if ipNet == nil {
|
||||||
|
return nil, fmt.Errorf("%w: %q from environment variable %s: subnet is nil",
|
||||||
|
ErrInvalidIPNet, ipNetStr, key)
|
||||||
|
}
|
||||||
|
ipNets[i] = *ipNet
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipNets, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidIP = errors.New("invalid IP address")
|
||||||
|
)
|
||||||
|
|
||||||
|
func readIP(env params.Env, key string) (ip net.IP, err error) {
|
||||||
|
s, err := env.Get(key)
|
||||||
|
if len(s) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = net.ParseIP(s)
|
||||||
|
if ip == nil {
|
||||||
|
return nil, fmt.Errorf("%w: %s", ErrInvalidIP, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readPortOrZero(env params.Env, key string) (port uint16, err error) {
|
||||||
|
s, err := env.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s) == 0 || s == "0" {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.Port(key)
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package params
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/golibs/os"
|
"github.com/qdm12/golibs/os"
|
||||||
libparams "github.com/qdm12/golibs/params"
|
"github.com/qdm12/golibs/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -19,11 +19,11 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKeys []string) (value string, err error) {
|
func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKeys []string) (value string, err error) {
|
||||||
envOptions := []libparams.OptionSetter{
|
envOptions := []params.OptionSetter{
|
||||||
libparams.Compulsory(), // to fallback on file reading
|
params.Compulsory(), // to fallback on file reading
|
||||||
libparams.CaseSensitiveValue(),
|
params.CaseSensitiveValue(),
|
||||||
libparams.Unset(),
|
params.Unset(),
|
||||||
libparams.RetroKeys(retroKeys, r.onRetroActive),
|
params.RetroKeys(retroKeys, r.onRetroActive),
|
||||||
}
|
}
|
||||||
value, envErr := r.env.Get(envKey, envOptions...)
|
value, envErr := r.env.Get(envKey, envOptions...)
|
||||||
if envErr == nil {
|
if envErr == nil {
|
||||||
@@ -32,8 +32,8 @@ func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKey
|
|||||||
|
|
||||||
defaultSecretFile := "/run/secrets/" + strings.ToLower(envKey)
|
defaultSecretFile := "/run/secrets/" + strings.ToLower(envKey)
|
||||||
filepath, err := r.env.Get(envKey+"_SECRETFILE",
|
filepath, err := r.env.Get(envKey+"_SECRETFILE",
|
||||||
libparams.CaseSensitiveValue(),
|
params.CaseSensitiveValue(),
|
||||||
libparams.Default(defaultSecretFile),
|
params.Default(defaultSecretFile),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("%w: %s", ErrGetSecretFilepath, err)
|
return "", fmt.Errorf("%w: %s", ErrGetSecretFilepath, err)
|
||||||
@@ -68,8 +68,8 @@ func (r *reader) getFromFileOrSecretFile(secretName, filepath string) (
|
|||||||
b []byte, err error) {
|
b []byte, err error) {
|
||||||
defaultSecretFile := "/run/secrets/" + strings.ToLower(secretName)
|
defaultSecretFile := "/run/secrets/" + strings.ToLower(secretName)
|
||||||
secretFilepath, err := r.env.Get(strings.ToUpper(secretName)+"_SECRETFILE",
|
secretFilepath, err := r.env.Get(strings.ToUpper(secretName)+"_SECRETFILE",
|
||||||
libparams.CaseSensitiveValue(),
|
params.CaseSensitiveValue(),
|
||||||
libparams.Default(defaultSecretFile),
|
params.Default(defaultSecretFile),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return b, fmt.Errorf("%w: %s", ErrGetSecretFilepath, err)
|
return b, fmt.Errorf("%w: %s", ErrGetSecretFilepath, err)
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerSelection struct {
|
||||||
|
// Common
|
||||||
|
Protocol string `json:"network_protocol"`
|
||||||
|
TargetIP net.IP `json:"target_ip,omitempty"`
|
||||||
|
// TODO comments
|
||||||
|
// Cyberghost, PIA, Surfshark, Windscribe, Vyprvpn, NordVPN
|
||||||
|
Regions []string `json:"regions"`
|
||||||
|
|
||||||
|
// Cyberghost
|
||||||
|
Group string `json:"group"`
|
||||||
|
|
||||||
|
Countries []string `json:"countries"` // Mullvad, PureVPN
|
||||||
|
Cities []string `json:"cities"` // Mullvad, PureVPN, Windscribe
|
||||||
|
Hostnames []string `json:"hostnames"` // Windscribe, Privado
|
||||||
|
|
||||||
|
// Mullvad
|
||||||
|
ISPs []string `json:"isps"`
|
||||||
|
Owned bool `json:"owned"`
|
||||||
|
|
||||||
|
// Mullvad, Windscribe, PIA
|
||||||
|
CustomPort uint16 `json:"custom_port"`
|
||||||
|
|
||||||
|
// NordVPN
|
||||||
|
Numbers []uint16 `json:"numbers"`
|
||||||
|
|
||||||
|
// PIA
|
||||||
|
EncryptionPreset string `json:"encryption_preset"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExtraConfigOptions struct {
|
||||||
|
ClientCertificate string `json:"-"` // Cyberghost
|
||||||
|
ClientKey string `json:"-"` // Cyberghost
|
||||||
|
EncryptionPreset string `json:"encryption_preset"` // PIA
|
||||||
|
OpenVPNIPv6 bool `json:"openvpn_ipv6"` // Mullvad
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortForwarding contains settings for port forwarding.
|
||||||
|
type PortForwarding struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Filepath string `json:"filepath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PortForwarding) lines() (lines []string) {
|
||||||
|
return []string{
|
||||||
|
lastIndent + "File path: " + p.Filepath,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ControlServer contains settings to customize the control server operation.
|
||||||
|
type ControlServer struct {
|
||||||
|
Port uint16
|
||||||
|
Log bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *ControlServer) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *ControlServer) lines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+"HTTP control server:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Listening port: "+strconv.Itoa(int(settings.Port)))
|
||||||
|
|
||||||
|
if settings.Log {
|
||||||
|
lines = append(lines, indent+lastIndent+"Logging: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *ControlServer) read(r reader) (err error) {
|
||||||
|
settings.Log, err = r.env.OnOff("HTTP_CONTROL_SERVER_LOG", params.Default("on"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var warning string
|
||||||
|
settings.Port, warning, err = r.env.ListeningPort(
|
||||||
|
"HTTP_CONTROL_SERVER_PORT", params.Default("8000"))
|
||||||
|
if len(warning) > 0 {
|
||||||
|
r.logger.Warn(warning)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/logging"
|
||||||
|
"github.com/qdm12/golibs/os"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Settings contains all settings for the program to run.
|
||||||
|
type Settings struct {
|
||||||
|
OpenVPN OpenVPN
|
||||||
|
System System
|
||||||
|
DNS DNS
|
||||||
|
Firewall Firewall
|
||||||
|
HTTPProxy HTTPProxy
|
||||||
|
ShadowSocks ShadowSocks
|
||||||
|
Updater Updater
|
||||||
|
PublicIP PublicIP
|
||||||
|
VersionInformation bool
|
||||||
|
ControlServer ControlServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Settings) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Settings) lines() (lines []string) {
|
||||||
|
lines = append(lines, "Settings summary below:")
|
||||||
|
lines = append(lines, settings.OpenVPN.lines()...)
|
||||||
|
lines = append(lines, settings.DNS.lines()...)
|
||||||
|
lines = append(lines, settings.Firewall.lines()...)
|
||||||
|
lines = append(lines, settings.System.lines()...)
|
||||||
|
lines = append(lines, settings.HTTPProxy.lines()...)
|
||||||
|
lines = append(lines, settings.ShadowSocks.lines()...)
|
||||||
|
lines = append(lines, settings.ControlServer.lines()...)
|
||||||
|
lines = append(lines, settings.Updater.lines()...)
|
||||||
|
lines = append(lines, settings.PublicIP.lines()...)
|
||||||
|
if settings.VersionInformation {
|
||||||
|
lines = append(lines, lastIndent+"Github version information: enabled")
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read obtains all configuration options for the program and returns an error as soon
|
||||||
|
// as an error is encountered reading them.
|
||||||
|
func (settings *Settings) Read(env params.Env, os os.OS, logger logging.Logger) (err error) {
|
||||||
|
r := newReader(env, os, logger)
|
||||||
|
|
||||||
|
settings.VersionInformation, err = r.env.OnOff("VERSION_INFORMATION", params.Default("on"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.OpenVPN.read(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.System.read(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.DNS.read(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.Firewall.read(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.HTTPProxy.read(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.ShadowSocks.read(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.ControlServer.read(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.Updater.read(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip := settings.DNS.PlaintextAddress; ip != nil {
|
||||||
|
settings.Updater.DNSAddress = ip.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.PublicIP.read(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Settings_lines(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
settings Settings
|
||||||
|
lines []string
|
||||||
|
}{
|
||||||
|
"default settings": {
|
||||||
|
settings: Settings{
|
||||||
|
OpenVPN: OpenVPN{
|
||||||
|
Provider: Provider{
|
||||||
|
Name: constants.Mullvad,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lines: []string{
|
||||||
|
"Settings summary below:",
|
||||||
|
"|--OpenVPN:",
|
||||||
|
" |--Verbosity level: 0",
|
||||||
|
" |--Provider:",
|
||||||
|
" |--Mullvad settings:",
|
||||||
|
" |--Network protocol: ",
|
||||||
|
"|--DNS:",
|
||||||
|
"|--Firewall: disabled ⚠️",
|
||||||
|
"|--System:",
|
||||||
|
" |--Process user ID: 0",
|
||||||
|
" |--Process group ID: 0",
|
||||||
|
" |--Timezone: NOT SET ⚠️ - it can cause time related issues",
|
||||||
|
"|--HTTP control server:",
|
||||||
|
" |--Listening port: 0",
|
||||||
|
"|--Public IP getter: disabled",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
lines := testCase.settings.lines()
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.lines, lines)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ShadowSocks contains settings to configure the Shadowsocks server.
|
||||||
|
type ShadowSocks struct {
|
||||||
|
Method string
|
||||||
|
Password string
|
||||||
|
Port uint16
|
||||||
|
Enabled bool
|
||||||
|
Log bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *ShadowSocks) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *ShadowSocks) lines() (lines []string) {
|
||||||
|
if !settings.Enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, lastIndent+"Shadowsocks server:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Listening port: "+strconv.Itoa(int(settings.Port)))
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Method: "+settings.Method)
|
||||||
|
|
||||||
|
if settings.Log {
|
||||||
|
lines = append(lines, indent+lastIndent+"Logging: enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *ShadowSocks) read(r reader) (err error) {
|
||||||
|
settings.Enabled, err = r.env.OnOff("SHADOWSOCKS", params.Default("off"))
|
||||||
|
if err != nil || !settings.Enabled {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Password, err = r.getFromEnvOrSecretFile("SHADOWSOCKS_PASSWORD", false, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Log, err = r.env.OnOff("SHADOWSOCKS_LOG", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Method, err = r.env.Get("SHADOWSOCKS_METHOD", params.Default("chacha20-ietf-poly1305"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var warning string
|
||||||
|
settings.Port, warning, err = r.env.ListeningPort("SHADOWSOCKS_PORT", params.Default("8388"))
|
||||||
|
if len(warning) > 0 {
|
||||||
|
r.logger.Warn(warning)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) surfsharkLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readSurfshark(r reader) (err error) {
|
||||||
|
settings.Name = constants.Surfshark
|
||||||
|
|
||||||
|
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.SurfsharkRegionChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// System contains settings to configure system related elements.
|
||||||
|
type System struct {
|
||||||
|
PUID int
|
||||||
|
PGID int
|
||||||
|
Timezone string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *System) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *System) lines() (lines []string) {
|
||||||
|
lines = append(lines, lastIndent+"System:")
|
||||||
|
lines = append(lines, indent+lastIndent+"Process user ID: "+strconv.Itoa(settings.PUID))
|
||||||
|
lines = append(lines, indent+lastIndent+"Process group ID: "+strconv.Itoa(settings.PGID))
|
||||||
|
|
||||||
|
if len(settings.Timezone) > 0 {
|
||||||
|
lines = append(lines, indent+lastIndent+"Timezone: "+settings.Timezone)
|
||||||
|
} else {
|
||||||
|
lines = append(lines, indent+lastIndent+"Timezone: NOT SET ⚠️ - it can cause time related issues")
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *System) read(r reader) (err error) {
|
||||||
|
settings.PUID, err = r.env.IntRange("PUID", 0, 65535, params.Default("1000"),
|
||||||
|
params.RetroKeys([]string{"UID"}, r.onRetroActive))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.PGID, err = r.env.IntRange("PGID", 0, 65535, params.Default("1000"),
|
||||||
|
params.RetroKeys([]string{"GID"}, r.onRetroActive))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Timezone, err = r.env.Get("TZ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) torguardLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Countries) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readTorguard(r reader) (err error) {
|
||||||
|
settings.Name = constants.Torguard
|
||||||
|
|
||||||
|
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.TorguardCountryChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.TorguardCityChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.TorguardHostnamesChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
unbound "github.com/qdm12/dns/pkg/unbound"
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *DNS) readUnbound(r reader) (err error) {
|
||||||
|
if err := settings.readUnboundProviders(r.env); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Unbound.ListeningPort = 53
|
||||||
|
|
||||||
|
settings.Unbound.Caching, err = r.env.OnOff("DOT_CACHING", params.Default("on"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Unbound.IPv4 = true
|
||||||
|
|
||||||
|
settings.Unbound.IPv6, err = r.env.OnOff("DOT_IPV6", params.Default("off"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
verbosityLevel, err := r.env.IntRange("DOT_VERBOSITY", 0, 5, params.Default("1"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settings.Unbound.VerbosityLevel = uint8(verbosityLevel)
|
||||||
|
|
||||||
|
verbosityDetailsLevel, err := r.env.IntRange("DOT_VERBOSITY_DETAILS", 0, 4, params.Default("0"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settings.Unbound.VerbosityDetailsLevel = uint8(verbosityDetailsLevel)
|
||||||
|
|
||||||
|
validationLogLevel, err := r.env.IntRange("DOT_VALIDATION_LOGLEVEL", 0, 2, params.Default("0"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settings.Unbound.ValidationLogLevel = uint8(validationLogLevel)
|
||||||
|
|
||||||
|
if err := settings.readUnboundPrivateAddresses(r.env); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.readUnboundUnblockedHostnames(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.Unbound.AccessControl.Allowed = []net.IPNet{
|
||||||
|
{
|
||||||
|
IP: net.IPv4zero,
|
||||||
|
Mask: net.IPv4Mask(0, 0, 0, 0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: net.IPv6zero,
|
||||||
|
Mask: net.IPMask{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidDNSOverTLSProvider = errors.New("invalid DNS over TLS provider")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *DNS) readUnboundProviders(env params.Env) (err error) {
|
||||||
|
s, err := env.Get("DOT_PROVIDERS", params.Default("cloudflare"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, provider := range strings.Split(s, ",") {
|
||||||
|
_, ok := unbound.GetProviderData(provider)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("%w: %s", ErrInvalidDNSOverTLSProvider, provider)
|
||||||
|
}
|
||||||
|
settings.Unbound.Providers = append(settings.Unbound.Providers, provider)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidPrivateAddress = errors.New("private address is not a valid IP or CIDR range")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *DNS) readUnboundPrivateAddresses(env params.Env) (err error) {
|
||||||
|
privateAddresses, err := env.CSV("DOT_PRIVATE_ADDRESS")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if len(privateAddresses) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, address := range privateAddresses {
|
||||||
|
ip := net.ParseIP(address)
|
||||||
|
_, _, err := net.ParseCIDR(address)
|
||||||
|
if ip == nil && err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrInvalidPrivateAddress, address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settings.Unbound.BlockedIPs = append(
|
||||||
|
settings.Unbound.BlockedIPs, privateAddresses...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidHostname = errors.New("invalid hostname")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *DNS) readUnboundUnblockedHostnames(r reader) (err error) {
|
||||||
|
hostnames, err := r.env.CSV("UNBLOCK")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if len(hostnames) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, hostname := range hostnames {
|
||||||
|
if !r.regex.MatchHostname(hostname) {
|
||||||
|
return fmt.Errorf("%w: %s", ErrInvalidHostname, hostname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settings.Unbound.AllowedHostnames = append(
|
||||||
|
settings.Unbound.AllowedHostnames, hostnames...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/golibs/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Updater struct {
|
||||||
|
Period time.Duration `json:"period"`
|
||||||
|
DNSAddress string `json:"dns_address"`
|
||||||
|
Cyberghost bool `json:"cyberghost"`
|
||||||
|
Mullvad bool `json:"mullvad"`
|
||||||
|
Nordvpn bool `json:"nordvpn"`
|
||||||
|
PIA bool `json:"pia"`
|
||||||
|
Privado bool `json:"privado"`
|
||||||
|
Purevpn bool `json:"purevpn"`
|
||||||
|
Surfshark bool `json:"surfshark"`
|
||||||
|
Torguard bool `json:"torguard"`
|
||||||
|
Vyprvpn bool `json:"vyprvpn"`
|
||||||
|
Windscribe bool `json:"windscribe"`
|
||||||
|
// The two below should be used in CLI mode only
|
||||||
|
Stdout bool `json:"-"` // in order to update constants file (maintainer side)
|
||||||
|
CLI bool `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Updater) String() string {
|
||||||
|
return strings.Join(settings.lines(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Updater) lines() (lines []string) {
|
||||||
|
if settings.Period == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, lastIndent+"Updater:")
|
||||||
|
|
||||||
|
lines = append(lines, indent+lastIndent+"Period: every "+settings.Period.String())
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Updater) read(r reader) (err error) {
|
||||||
|
settings.Cyberghost = true
|
||||||
|
settings.Mullvad = true
|
||||||
|
settings.Nordvpn = true
|
||||||
|
settings.PIA = true
|
||||||
|
settings.Purevpn = true
|
||||||
|
settings.Surfshark = true
|
||||||
|
settings.Torguard = true
|
||||||
|
settings.Vyprvpn = true
|
||||||
|
settings.Windscribe = true
|
||||||
|
settings.Stdout = false
|
||||||
|
settings.CLI = false
|
||||||
|
// use cloudflare in plaintext to not be blocked by DNS over TLS by default.
|
||||||
|
// If a plaintext address is set in the DNS settings, this one will be used.
|
||||||
|
// TODO use custom future encrypted DNS written in Go without blocking
|
||||||
|
// as it's too much trouble to start another parallel unbound instance for now.
|
||||||
|
settings.DNSAddress = "1.1.1.1"
|
||||||
|
|
||||||
|
settings.Period, err = r.env.Duration("UPDATER_PERIOD", params.Default("0"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) vyprvpnLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readVyprvpn(r reader) (err error) {
|
||||||
|
settings.Name = constants.Vyprvpn
|
||||||
|
|
||||||
|
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.VyprvpnRegionChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (settings *Provider) windscribeLines() (lines []string) {
|
||||||
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Cities) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||||
|
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Provider) readWindscribe(r reader) (err error) {
|
||||||
|
settings.Name = constants.Windscribe
|
||||||
|
|
||||||
|
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.WindscribeRegionChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.WindscribeCityChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.WindscribeHostnameChoices())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ServerSelection.CustomPort, err = readCustomPort(r.env, settings.ServerSelection.Protocol,
|
||||||
|
[]uint16{21, 22, 80, 123, 143, 443, 587, 1194, 3306, 8080, 54783},
|
||||||
|
[]uint16{53, 80, 123, 443, 1194, 54783})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// Package constants defines constants shared throughout the program.
|
||||||
|
// It also defines constant maps and slices using functions.
|
||||||
|
package constants
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package updater
|
package constants
|
||||||
|
|
||||||
func getCountryCodes() map[string]string { //nolint:dupl
|
func CountryCodes() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"af": "Afghanistan",
|
"af": "Afghanistan",
|
||||||
"ax": "Aland Islands",
|
"ax": "Aland Islands",
|
||||||
@@ -41,6 +41,8 @@ func CyberghostGroupChoices() (choices []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//nolint:lll
|
//nolint:lll
|
||||||
|
// CyberghostServers returns a slice with the server information for each
|
||||||
|
// of the Cyberghost server.
|
||||||
func CyberghostServers() []models.CyberghostServer {
|
func CyberghostServers() []models.CyberghostServer {
|
||||||
return []models.CyberghostServer{
|
return []models.CyberghostServer{
|
||||||
{Region: "Albania", Group: "Premium TCP Europe", IPs: []net.IP{{31, 171, 152, 99}, {31, 171, 152, 100}, {31, 171, 152, 101}, {31, 171, 152, 102}, {31, 171, 152, 105}, {31, 171, 152, 108}, {31, 171, 152, 132}, {31, 171, 152, 136}, {31, 171, 152, 139}, {31, 171, 152, 140}}},
|
{Region: "Albania", Group: "Premium TCP Europe", IPs: []net.IP{{31, 171, 152, 99}, {31, 171, 152, 100}, {31, 171, 152, 101}, {31, 171, 152, 102}, {31, 171, 152, 105}, {31, 171, 152, 108}, {31, 171, 152, 132}, {31, 171, 152, 136}, {31, 171, 152, 139}, {31, 171, 152, 140}}},
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ func MullvadISPChoices() (choices []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//nolint:dupl,lll
|
//nolint:dupl,lll
|
||||||
|
// MullvadServers returns a slice of all the server information for Mullvad.
|
||||||
func MullvadServers() []models.MullvadServer {
|
func MullvadServers() []models.MullvadServer {
|
||||||
return []models.MullvadServer{
|
return []models.MullvadServer{
|
||||||
{Country: "Albania", City: "Tirana", ISP: "iRegister", Owned: false, IPs: []net.IP{{31, 171, 154, 210}}, IPsV6: []net.IP{{0x2a, 0x4, 0x27, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
{Country: "Albania", City: "Tirana", ISP: "iRegister", Owned: false, IPs: []net.IP{{31, 171, 154, 210}}, IPsV6: []net.IP{{0x2a, 0x4, 0x27, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ func NordvpnRegionChoices() (choices []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//nolint:gomnd
|
//nolint:gomnd
|
||||||
|
// NordvpnServers returns a slice of all the server information for Nordvpn.
|
||||||
func NordvpnServers() []models.NordvpnServer {
|
func NordvpnServers() []models.NordvpnServer {
|
||||||
return []models.NordvpnServer{
|
return []models.NordvpnServer{
|
||||||
{Region: "Albania", Number: 20, TCP: true, UDP: true, IP: net.IP{31, 171, 152, 11}},
|
{Region: "Albania", Number: 20, TCP: true, UDP: true, IP: net.IP{31, 171, 152, 11}},
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
package constants
|
package constants
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TUN models.VPNDevice = "tun0"
|
TUN = "tun0"
|
||||||
TAP models.VPNDevice = "tap0"
|
TAP = "tap0"
|
||||||
)
|
)
|
||||||
|
|||||||
+12
-16
@@ -1,34 +1,30 @@
|
|||||||
package constants
|
package constants
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// UnboundConf is the file path to the Unbound configuration file.
|
// UnboundConf is the file path to the Unbound configuration file.
|
||||||
UnboundConf models.Filepath = "/etc/unbound/unbound.conf"
|
UnboundConf string = "/etc/unbound/unbound.conf"
|
||||||
// ResolvConf is the file path to the system resolv.conf file.
|
// ResolvConf is the file path to the system resolv.conf file.
|
||||||
ResolvConf models.Filepath = "/etc/resolv.conf"
|
ResolvConf string = "/etc/resolv.conf"
|
||||||
// CACertificates is the file path to the CA certificates file.
|
// CACertificates is the file path to the CA certificates file.
|
||||||
CACertificates models.Filepath = "/etc/ssl/certs/ca-certificates.crt"
|
CACertificates string = "/etc/ssl/certs/ca-certificates.crt"
|
||||||
// OpenVPNAuthConf is the file path to the OpenVPN auth file.
|
// OpenVPNAuthConf is the file path to the OpenVPN auth file.
|
||||||
OpenVPNAuthConf models.Filepath = "/etc/openvpn/auth.conf"
|
OpenVPNAuthConf string = "/etc/openvpn/auth.conf"
|
||||||
// OpenVPNConf is the file path to the OpenVPN client configuration file.
|
// OpenVPNConf is the file path to the OpenVPN client configuration file.
|
||||||
OpenVPNConf models.Filepath = "/etc/openvpn/target.ovpn"
|
OpenVPNConf string = "/etc/openvpn/target.ovpn"
|
||||||
// PIAPortForward is the file path to the port forwarding JSON information for PIA servers.
|
// PIAPortForward is the file path to the port forwarding JSON information for PIA servers.
|
||||||
PIAPortForward models.Filepath = "/gluetun/piaportforward.json"
|
PIAPortForward string = "/gluetun/piaportforward.json"
|
||||||
// TunnelDevice is the file path to tun device.
|
// TunnelDevice is the file path to tun device.
|
||||||
TunnelDevice models.Filepath = "/dev/net/tun"
|
TunnelDevice string = "/dev/net/tun"
|
||||||
// NetRoute is the path to the file containing information on the network route.
|
// NetRoute is the path to the file containing information on the network route.
|
||||||
NetRoute models.Filepath = "/proc/net/route"
|
NetRoute string = "/proc/net/route"
|
||||||
// RootHints is the filepath to the root.hints file used by Unbound.
|
// RootHints is the filepath to the root.hints file used by Unbound.
|
||||||
RootHints models.Filepath = "/etc/unbound/root.hints"
|
RootHints string = "/etc/unbound/root.hints"
|
||||||
// RootKey is the filepath to the root.key file used by Unbound.
|
// RootKey is the filepath to the root.key file used by Unbound.
|
||||||
RootKey models.Filepath = "/etc/unbound/root.key"
|
RootKey string = "/etc/unbound/root.key"
|
||||||
// Client key filepath, used by Cyberghost.
|
// Client key filepath, used by Cyberghost.
|
||||||
ClientKey models.Filepath = "/gluetun/client.key"
|
ClientKey string = "/gluetun/client.key"
|
||||||
// Client certificate filepath, used by Cyberghost.
|
// Client certificate filepath, used by Cyberghost.
|
||||||
ClientCertificate models.Filepath = "/gluetun/client.crt"
|
ClientCertificate string = "/gluetun/client.crt"
|
||||||
// Servers information filepath.
|
// Servers information filepath.
|
||||||
ServersData = "/gluetun/servers.json"
|
ServersData = "/gluetun/servers.json"
|
||||||
)
|
)
|
||||||
|
|||||||
+561
-630
File diff suppressed because it is too large
Load Diff
@@ -42,6 +42,7 @@ func PurevpnCityChoices() (choices []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//nolint:lll
|
//nolint:lll
|
||||||
|
// PurevpnServers returns a slice of all the server information for Purevpn.
|
||||||
func PurevpnServers() []models.PurevpnServer {
|
func PurevpnServers() []models.PurevpnServer {
|
||||||
return []models.PurevpnServer{
|
return []models.PurevpnServer{
|
||||||
{Country: "Australia", Region: "New South Wales", City: "Sydney", IPs: []net.IP{{192, 253, 241, 4}, {43, 245, 161, 84}}},
|
{Country: "Australia", Region: "New South Wales", City: "Sydney", IPs: []net.IP{{192, 253, 241, 4}, {43, 245, 161, 84}}},
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ func GetAllServers() (allServers models.AllServers) {
|
|||||||
Servers: NordvpnServers(),
|
Servers: NordvpnServers(),
|
||||||
},
|
},
|
||||||
Pia: models.PiaServers{
|
Pia: models.PiaServers{
|
||||||
Version: 3,
|
Version: 4,
|
||||||
Timestamp: 1611877630,
|
Timestamp: 1613480675,
|
||||||
Servers: PIAServers(),
|
Servers: PIAServers(),
|
||||||
},
|
},
|
||||||
Purevpn: models.PurevpnServers{
|
Purevpn: models.PurevpnServers{
|
||||||
@@ -41,6 +41,11 @@ func GetAllServers() (allServers models.AllServers) {
|
|||||||
Timestamp: 1612031135,
|
Timestamp: 1612031135,
|
||||||
Servers: SurfsharkServers(),
|
Servers: SurfsharkServers(),
|
||||||
},
|
},
|
||||||
|
Torguard: models.TorguardServers{
|
||||||
|
Version: 1,
|
||||||
|
Timestamp: 1613357861,
|
||||||
|
Servers: TorguardServers(),
|
||||||
|
},
|
||||||
Vyprvpn: models.VyprvpnServers{
|
Vyprvpn: models.VyprvpnServers{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
Timestamp: 1612031135,
|
Timestamp: 1612031135,
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ func Test_versions(t *testing.T) {
|
|||||||
"Private Internet Access": {
|
"Private Internet Access": {
|
||||||
model: models.PIAServer{},
|
model: models.PIAServer{},
|
||||||
version: allServers.Pia.Version,
|
version: allServers.Pia.Version,
|
||||||
digest: "b90147aa",
|
digest: "3e6066ec",
|
||||||
},
|
},
|
||||||
"Privado": {
|
"Privado": {
|
||||||
model: models.PrivadoServer{},
|
model: models.PrivadoServer{},
|
||||||
@@ -69,6 +69,11 @@ func Test_versions(t *testing.T) {
|
|||||||
version: allServers.Surfshark.Version,
|
version: allServers.Surfshark.Version,
|
||||||
digest: "042bef64",
|
digest: "042bef64",
|
||||||
},
|
},
|
||||||
|
"Torguard": {
|
||||||
|
model: models.TorguardServer{},
|
||||||
|
version: allServers.Torguard.Version,
|
||||||
|
digest: "752702f3",
|
||||||
|
},
|
||||||
"Vyprvpn": {
|
"Vyprvpn": {
|
||||||
model: models.VyprvpnServer{},
|
model: models.VyprvpnServer{},
|
||||||
version: allServers.Vyprvpn.Version,
|
version: allServers.Vyprvpn.Version,
|
||||||
@@ -133,7 +138,7 @@ func Test_timestamps(t *testing.T) {
|
|||||||
"Private Internet Access": {
|
"Private Internet Access": {
|
||||||
servers: allServers.Pia.Servers,
|
servers: allServers.Pia.Servers,
|
||||||
timestamp: allServers.Pia.Timestamp,
|
timestamp: allServers.Pia.Timestamp,
|
||||||
digest: "1d2938a1",
|
digest: "e0f95a01",
|
||||||
},
|
},
|
||||||
"Purevpn": {
|
"Purevpn": {
|
||||||
servers: allServers.Purevpn.Servers,
|
servers: allServers.Purevpn.Servers,
|
||||||
@@ -150,6 +155,11 @@ func Test_timestamps(t *testing.T) {
|
|||||||
timestamp: allServers.Surfshark.Timestamp,
|
timestamp: allServers.Surfshark.Timestamp,
|
||||||
digest: "1a7f38bb",
|
digest: "1a7f38bb",
|
||||||
},
|
},
|
||||||
|
"Torguard": {
|
||||||
|
servers: allServers.Torguard.Servers,
|
||||||
|
timestamp: allServers.Torguard.Timestamp,
|
||||||
|
digest: "dffab93e",
|
||||||
|
},
|
||||||
"Vyprvpn": {
|
"Vyprvpn": {
|
||||||
servers: allServers.Vyprvpn.Servers,
|
servers: allServers.Vyprvpn.Servers,
|
||||||
timestamp: allServers.Vyprvpn.Timestamp,
|
timestamp: allServers.Vyprvpn.Timestamp,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ func SurfsharkRegionChoices() (choices []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//nolint:lll
|
//nolint:lll
|
||||||
|
// SurfsharkServers returns a slice of all the server information for Surfshark.
|
||||||
func SurfsharkServers() []models.SurfsharkServer {
|
func SurfsharkServers() []models.SurfsharkServer {
|
||||||
return []models.SurfsharkServer{
|
return []models.SurfsharkServer{
|
||||||
{Region: "Albania", IPs: []net.IP{{31, 171, 153, 99}, {31, 171, 153, 131}, {31, 171, 154, 101}, {31, 171, 154, 149}, {31, 171, 154, 163}, {31, 171, 154, 165}}},
|
{Region: "Albania", IPs: []net.IP{{31, 171, 153, 99}, {31, 171, 153, 131}, {31, 171, 154, 101}, {31, 171, 154, 149}, {31, 171, 154, 163}, {31, 171, 154, 165}}},
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package constants
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:lll
|
||||||
|
const (
|
||||||
|
TorguardCertificate = "MIIDMTCCAhmgAwIBAgIJAKnGGJK6qLqSMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNVBAMMCVRHLVZQTi1DQTAgFw0xOTA1MjExNDIzMTFaGA8yMDU5MDUxMTE0MjMxMVowFDESMBAGA1UEAwwJVEctVlBOLUNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlv0UgPD3xVAvhhP6q1HCmeAWbH+9HPkyQ2P6qM5oHY5dntjmq8YT48FZGHWv7+s9O47v6Bv7rEc4UwQx15cc2LByivX2JwmE8JACvNfwEnZXYAPq9WU3ZgRrAGvA09ItuLqK2fQ4A7h8bFhmyxCbSzP1sSIT/zJY6ebuh5rDQSMJRMaoI0t1zorEZ7PlEmh+o0w5GPs0D0vY50UcnEzB4GOdWC9pJREwEqppWYLN7RRdG8JyIqmA59mhARCnQFUo38HWic4trxFe71jtD7YInNV7ShQtg0S0sXo36Rqfz72Jo08qqI70dNs5DN1aGNkQ/tRK9DhL5DLmTkaCw7mEFQIDAQABo4GDMIGAMB0GA1UdDgQWBBR7DcymXBp6u/jAaZOPUjUhEyhXfjBEBgNVHSMEPTA7gBR7DcymXBp6u/jAaZOPUjUhEyhXfqEYpBYwFDESMBAGA1UEAwwJVEctVlBOLUNBggkAqcYYkrqoupIwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAE79ngbdSlP7IBbfnJ+2Ju7vqt9/GyhcsYtjibp6gsMUxKlD8HuvlSGj5kNO5wiwN7XXqsjYtJfdhmzzVbXksi8Fnbnfa8GhFl4IAjLJ5cxaWOxjr6wx2AhIs+BVVARjaU7iTK91RXJnl6u7UDHTkQylBTl7wgpMeG6GjhaHfcOL1t7D2w8x23cTO+p+n53P3cBq+9TiAUORdzXJvbCxlPMDSDArsgBjC57W7dtdnZo7gTfQG77JTDFBeSwPwLF7PjBB4S6rzU/4fcYwy83XKP6zDn9tgUJDnpFb/7jJ/PbNkK4BWYJp3XytOtt66v9SEKw+v/fJ+VkjU16vE/9Q3h4="
|
||||||
|
TorguardOpenvpnStaticKeyV1 = "770e8de5fc56e0248cc7b5aab56be80d0e19cbf003c1b3ed68efbaf08613c3a1a019dac6a4b84f13a6198f73229ffc21fa512394e288f82aa2cf0180f01fb3eb1a71e00a077a20f6d7a83633f5b4f47f27e30617eaf8485dd8c722a8606d56b3c183f65da5d3c9001a8cbdb96c793d936251098b24fe52a6dd2472e98cfccbc466e63520d63ade7a0eacc36208c3142a1068236a52142fbb7b3ed83d785e12a28261bccfb3bcb62a8d2f6d18f5df5f3652e59c5627d8d9c8f7877c4d7b08e19a5c363556ba68d392be78b75152dd55ba0f74d45089e84f77f4492d886524ea6c82b9f4dd83d46528d4f5c3b51cfeaf2838d938bd0597c426b0e440434f2c451f"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TorguardCountryChoices() (choices []string) {
|
||||||
|
servers := TorguardServers()
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Country
|
||||||
|
}
|
||||||
|
return choices
|
||||||
|
}
|
||||||
|
|
||||||
|
func TorguardCityChoices() (choices []string) {
|
||||||
|
servers := TorguardServers()
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].City
|
||||||
|
}
|
||||||
|
return choices
|
||||||
|
}
|
||||||
|
|
||||||
|
func TorguardHostnamesChoices() (choices []string) {
|
||||||
|
servers := TorguardServers()
|
||||||
|
choices = make([]string, len(servers))
|
||||||
|
for i := range servers {
|
||||||
|
choices[i] = servers[i].Hostname
|
||||||
|
}
|
||||||
|
return choices
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:lll
|
||||||
|
// TorguardServers returns a slice of all the server information for Torguard.
|
||||||
|
func TorguardServers() []models.TorguardServer {
|
||||||
|
return []models.TorguardServer{
|
||||||
|
{Country: "Australia", City: "Sydney", Hostname: "au.torguardvpnaccess.com", IP: net.IP{168, 1, 210, 188}},
|
||||||
|
{Country: "Austria", City: "", Hostname: "aus.torguardvpnaccess.com", IP: net.IP{37, 120, 155, 18}},
|
||||||
|
{Country: "Belarus", City: "", Hostname: "bl.torguardvpnaccess.com", IP: net.IP{95, 47, 99, 12}},
|
||||||
|
{Country: "Belgium", City: "", Hostname: "bg.torguardvpnaccess.com", IP: net.IP{89, 249, 73, 250}},
|
||||||
|
{Country: "Brazil", City: "", Hostname: "br.torguardvpnaccess.com", IP: net.IP{169, 57, 191, 3}},
|
||||||
|
{Country: "Bulgaria", City: "", Hostname: "bul.torguardvpnaccess.com", IP: net.IP{82, 102, 23, 186}},
|
||||||
|
{Country: "Canada", City: "Toronto", Hostname: "ca.torguardvpnaccess.com", IP: net.IP{184, 75, 209, 250}},
|
||||||
|
{Country: "Canada", City: "Vancouver", Hostname: "vanc.ca.west.torguardvpnaccess.com", IP: net.IP{107, 181, 189, 48}},
|
||||||
|
{Country: "Chile", City: "", Hostname: "chil.torguardvpnaccess.com", IP: net.IP{37, 235, 52, 19}},
|
||||||
|
{Country: "Cyprus", City: "", Hostname: "cp.torguardvpnaccess.com", IP: net.IP{185, 173, 226, 49}},
|
||||||
|
{Country: "Czech", City: "", Hostname: "czech.torguardvpnaccess.com", IP: net.IP{185, 189, 115, 103}},
|
||||||
|
{Country: "Denmark", City: "", Hostname: "den.torguardvpnaccess.com", IP: net.IP{37, 120, 131, 29}},
|
||||||
|
{Country: "Finland", City: "", Hostname: "fin.torguardvpnaccess.com", IP: net.IP{91, 233, 116, 228}},
|
||||||
|
{Country: "France", City: "", Hostname: "fr.torguardvpnaccess.com", IP: net.IP{93, 177, 75, 90}},
|
||||||
|
{Country: "Germany", City: "", Hostname: "gr.torguardvpnaccess.com", IP: net.IP{93, 177, 73, 90}},
|
||||||
|
{Country: "Greece", City: "", Hostname: "gre.torguardvpnaccess.com", IP: net.IP{45, 92, 33, 10}},
|
||||||
|
{Country: "Hong", City: "Kong", Hostname: "hk.torguardvpnaccess.com", IP: net.IP{45, 133, 181, 158}},
|
||||||
|
{Country: "Hungary", City: "", Hostname: "hg.torguardvpnaccess.com", IP: net.IP{37, 120, 144, 106}},
|
||||||
|
{Country: "Iceland", City: "", Hostname: "ice.torguardvpnaccess.com", IP: net.IP{82, 221, 111, 11}},
|
||||||
|
{Country: "India", City: "Bangalore", Hostname: "in.torguardvpnaccess.com", IP: net.IP{139, 59, 62, 109}},
|
||||||
|
{Country: "Ireland", City: "", Hostname: "ire.torguardvpnaccess.com", IP: net.IP{81, 17, 246, 108}},
|
||||||
|
{Country: "Israel", City: "", Hostname: "isr.torguardvpnaccess.com", IP: net.IP{91, 223, 106, 201}},
|
||||||
|
{Country: "Italy", City: "", Hostname: "it.torguardvpnaccess.com", IP: net.IP{192, 145, 127, 190}},
|
||||||
|
{Country: "Japan", City: "", Hostname: "jp.torguardvpnaccess.com", IP: net.IP{91, 207, 174, 50}},
|
||||||
|
{Country: "Latvia", City: "", Hostname: "lv.torguardvpnaccess.com", IP: net.IP{109, 248, 149, 167}},
|
||||||
|
{Country: "Luxembourg", City: "", Hostname: "lux.torguardvpnaccess.com", IP: net.IP{94, 242, 238, 66}},
|
||||||
|
{Country: "Mexico", City: "", Hostname: "mx.torguardvpnaccess.com", IP: net.IP{45, 133, 180, 34}},
|
||||||
|
{Country: "Moldova", City: "", Hostname: "md.torguardvpnaccess.com", IP: net.IP{178, 175, 128, 74}},
|
||||||
|
{Country: "Netherlands", City: "", Hostname: "nl.torguardvpnaccess.com", IP: net.IP{88, 202, 177, 181}},
|
||||||
|
{Country: "New", City: "Zealand", Hostname: "nz.torguardvpnaccess.com", IP: net.IP{103, 108, 94, 58}},
|
||||||
|
{Country: "Norway", City: "", Hostname: "no.torguardvpnaccess.com", IP: net.IP{185, 125, 169, 31}},
|
||||||
|
{Country: "Poland", City: "", Hostname: "pl.torguardvpnaccess.com", IP: net.IP{37, 120, 156, 194}},
|
||||||
|
{Country: "Portugal", City: "", Hostname: "por.torguardvpnaccess.com", IP: net.IP{94, 46, 179, 75}},
|
||||||
|
{Country: "Romania", City: "", Hostname: "ro.torguardvpnaccess.com", IP: net.IP{93, 120, 27, 162}},
|
||||||
|
{Country: "Singapore", City: "", Hostname: "singp.torguardvpnaccess.com", IP: net.IP{206, 189, 43, 152}},
|
||||||
|
{Country: "Slovakia", City: "", Hostname: "slk.torguardvpnaccess.com", IP: net.IP{46, 29, 2, 113}},
|
||||||
|
{Country: "South", City: "Korea", Hostname: "sk.torguardvpnaccess.com", IP: net.IP{169, 56, 83, 216}},
|
||||||
|
{Country: "Spain", City: "", Hostname: "sp.torguardvpnaccess.com", IP: net.IP{192, 145, 124, 242}},
|
||||||
|
{Country: "Sweden", City: "", Hostname: "swe.torguardvpnaccess.com", IP: net.IP{37, 120, 153, 72}},
|
||||||
|
{Country: "Switzerland", City: "", Hostname: "swiss.torguardvpnaccess.com", IP: net.IP{195, 206, 105, 37}},
|
||||||
|
{Country: "Taiwan", City: "", Hostname: "tw.torguardvpnaccess.com", IP: net.IP{61, 216, 159, 176}},
|
||||||
|
{Country: "Thailand", City: "", Hostname: "thai.torguardvpnaccess.com", IP: net.IP{202, 129, 16, 104}},
|
||||||
|
{Country: "UAE", City: "", Hostname: "uae.secureconnect.me", IP: net.IP{45, 9, 250, 10}},
|
||||||
|
{Country: "UK", City: "London", Hostname: "uk.torguardvpnaccess.com", IP: net.IP{109, 123, 118, 13}},
|
||||||
|
{Country: "USA", City: "Atlanta", Hostname: "atl.east.usa.torguardvpnaccess.com", IP: net.IP{104, 223, 95, 50}},
|
||||||
|
{Country: "USA", City: "Chicago", Hostname: "chi.central.usa.torguardvpnaccess.com", IP: net.IP{167, 160, 172, 106}},
|
||||||
|
{Country: "USA", City: "Dallas", Hostname: "dal.central.usa.torguardvpnaccess.com", IP: net.IP{96, 44, 145, 26}},
|
||||||
|
{Country: "USA", City: "Las Vegas", Hostname: "lv.west.usa.torguardvpnaccess.com", IP: net.IP{76, 164, 203, 130}},
|
||||||
|
{Country: "USA", City: "Los Angeles", Hostname: "la.west.usa.torguardvpnaccess.com", IP: net.IP{67, 215, 236, 58}},
|
||||||
|
{Country: "USA", City: "Miami", Hostname: "fl.east.usa.torguardvpnaccess.com", IP: net.IP{96, 47, 226, 42}},
|
||||||
|
{Country: "USA", City: "New Jersey", Hostname: "nj.east.usa.torguardvpnaccess.com", IP: net.IP{23, 226, 128, 146}},
|
||||||
|
{Country: "USA", City: "New York", Hostname: "ny.east.usa.torguardvpnaccess.com", IP: net.IP{209, 95, 50, 116}},
|
||||||
|
{Country: "USA", City: "San Francisco", Hostname: "sf.west.usa.torguardvpnaccess.com", IP: net.IP{206, 189, 218, 238}},
|
||||||
|
{Country: "USA", City: "Seattle", Hostname: "sa.west.usa.torguardvpnaccess.com", IP: net.IP{199, 229, 250, 38}},
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
-15
@@ -1,33 +1,31 @@
|
|||||||
package constants
|
package constants
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// PrivateInternetAccess is a VPN provider.
|
// PrivateInternetAccess is a VPN provider.
|
||||||
PrivateInternetAccess models.VPNProvider = "private internet access"
|
PrivateInternetAccess = "private internet access"
|
||||||
// Mullvad is a VPN provider.
|
// Mullvad is a VPN provider.
|
||||||
Mullvad models.VPNProvider = "mullvad"
|
Mullvad = "mullvad"
|
||||||
// Windscribe is a VPN provider.
|
// Windscribe is a VPN provider.
|
||||||
Windscribe models.VPNProvider = "windscribe"
|
Windscribe = "windscribe"
|
||||||
// Surfshark is a VPN provider.
|
// Surfshark is a VPN provider.
|
||||||
Surfshark models.VPNProvider = "surfshark"
|
Surfshark = "surfshark"
|
||||||
// Cyberghost is a VPN provider.
|
// Cyberghost is a VPN provider.
|
||||||
Cyberghost models.VPNProvider = "cyberghost"
|
Cyberghost = "cyberghost"
|
||||||
// Vyprvpn is a VPN provider.
|
// Vyprvpn is a VPN provider.
|
||||||
Vyprvpn models.VPNProvider = "vyprvpn"
|
Vyprvpn = "vyprvpn"
|
||||||
// NordVPN is a VPN provider.
|
// NordVPN is a VPN provider.
|
||||||
Nordvpn models.VPNProvider = "nordvpn"
|
Nordvpn = "nordvpn"
|
||||||
// PureVPN is a VPN provider.
|
// PureVPN is a VPN provider.
|
||||||
Purevpn models.VPNProvider = "purevpn"
|
Purevpn = "purevpn"
|
||||||
// Privado is a VPN provider.
|
// Privado is a VPN provider.
|
||||||
Privado models.VPNProvider = "privado"
|
Privado = "privado"
|
||||||
|
// Torguard is a VPN provider.
|
||||||
|
Torguard = "torguard"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// TCP is a network protocol (reliable and slower than UDP).
|
// TCP is a network protocol (reliable and slower than UDP).
|
||||||
TCP models.NetworkProtocol = "tcp"
|
TCP string = "tcp"
|
||||||
// UDP is a network protocol (unreliable and faster than TCP).
|
// UDP is a network protocol (unreliable and faster than TCP).
|
||||||
UDP models.NetworkProtocol = "udp"
|
UDP string = "udp"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Package dns defines interfaces to interact with DNS and DNS over TLS.
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -9,9 +10,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/dns/pkg/unbound"
|
"github.com/qdm12/dns/pkg/unbound"
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/gluetun/internal/settings"
|
|
||||||
"github.com/qdm12/golibs/logging"
|
"github.com/qdm12/golibs/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,8 +21,8 @@ type Looper interface {
|
|||||||
RunRestartTicker(ctx context.Context, wg *sync.WaitGroup)
|
RunRestartTicker(ctx context.Context, wg *sync.WaitGroup)
|
||||||
GetStatus() (status models.LoopStatus)
|
GetStatus() (status models.LoopStatus)
|
||||||
SetStatus(status models.LoopStatus) (outcome string, err error)
|
SetStatus(status models.LoopStatus) (outcome string, err error)
|
||||||
GetSettings() (settings settings.DNS)
|
GetSettings() (settings configuration.DNS)
|
||||||
SetSettings(settings settings.DNS) (outcome string)
|
SetSettings(settings configuration.DNS) (outcome string)
|
||||||
}
|
}
|
||||||
|
|
||||||
type looper struct {
|
type looper struct {
|
||||||
@@ -45,7 +46,7 @@ type looper struct {
|
|||||||
|
|
||||||
const defaultBackoffTime = 10 * time.Second
|
const defaultBackoffTime = 10 * time.Second
|
||||||
|
|
||||||
func NewLooper(conf unbound.Configurator, settings settings.DNS, client *http.Client,
|
func NewLooper(conf unbound.Configurator, settings configuration.DNS, client *http.Client,
|
||||||
logger logging.Logger, username string, puid, pgid int) Looper {
|
logger logging.Logger, username string, puid, pgid int) Looper {
|
||||||
return &looper{
|
return &looper{
|
||||||
state: state{
|
state: state{
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/gluetun/internal/settings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type state struct {
|
type state struct {
|
||||||
status models.LoopStatus
|
status models.LoopStatus
|
||||||
settings settings.DNS
|
settings configuration.DNS
|
||||||
statusMu sync.RWMutex
|
statusMu sync.RWMutex
|
||||||
settingsMu sync.RWMutex
|
settingsMu sync.RWMutex
|
||||||
}
|
}
|
||||||
@@ -69,13 +69,13 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *looper) GetSettings() (settings settings.DNS) {
|
func (l *looper) GetSettings() (settings configuration.DNS) {
|
||||||
l.state.settingsMu.RLock()
|
l.state.settingsMu.RLock()
|
||||||
defer l.state.settingsMu.RUnlock()
|
defer l.state.settingsMu.RUnlock()
|
||||||
return l.state.settings
|
return l.state.settings
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *looper) SetSettings(settings settings.DNS) (outcome string) {
|
func (l *looper) SetSettings(settings configuration.DNS) (outcome string) {
|
||||||
l.state.settingsMu.Lock()
|
l.state.settingsMu.Lock()
|
||||||
settingsUnchanged := reflect.DeepEqual(l.state.settings, settings)
|
settingsUnchanged := reflect.DeepEqual(l.state.settings, settings)
|
||||||
if settingsUnchanged {
|
if settingsUnchanged {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// Package firewall defines a configurator used to change the state
|
||||||
|
// of the firewall as well as do some light routing changes.
|
||||||
package firewall
|
package firewall
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ func (h *handler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Re
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := h.getErr(); err != nil {
|
if err := h.getErr(); err != nil {
|
||||||
h.logger.Error(err)
|
|
||||||
http.Error(responseWriter, err.Error(), http.StatusInternalServerError)
|
http.Error(responseWriter, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Package healthcheck defines the client and server side of the built in healthcheck.
|
||||||
package healthcheck
|
package healthcheck
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -18,7 +19,9 @@ func (s *server) runHealthcheckLoop(ctx context.Context, wg *sync.WaitGroup) {
|
|||||||
s.handler.setErr(err)
|
s.handler.setErr(err)
|
||||||
|
|
||||||
if previousErr != nil && err == nil {
|
if previousErr != nil && err == nil {
|
||||||
s.logger.Info("passed")
|
s.logger.Info("healthy!")
|
||||||
|
} else if previousErr == nil && err != nil {
|
||||||
|
s.logger.Info("unhealthy: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil { // try again after 1 second
|
if err != nil { // try again after 1 second
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ func newHandler(ctx context.Context, wg *sync.WaitGroup, logger logging.Logger,
|
|||||||
stealth, verbose bool, username, password string) http.Handler {
|
stealth, verbose bool, username, password string) http.Handler {
|
||||||
const httpTimeout = 24 * time.Hour
|
const httpTimeout = 24 * time.Hour
|
||||||
return &handler{
|
return &handler{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
wg: wg,
|
wg: wg,
|
||||||
client: &http.Client{Timeout: httpTimeout},
|
client: &http.Client{
|
||||||
|
Timeout: httpTimeout,
|
||||||
|
CheckRedirect: returnRedirect},
|
||||||
logger: logger,
|
logger: logger,
|
||||||
verbose: verbose,
|
verbose: verbose,
|
||||||
stealth: stealth,
|
stealth: stealth,
|
||||||
@@ -62,3 +64,8 @@ var hopHeaders = [...]string{ //nolint:gochecknoglobals
|
|||||||
"Transfer-Encoding",
|
"Transfer-Encoding",
|
||||||
"Upgrade",
|
"Upgrade",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not follow redirect, but directly return the redirect response.
|
||||||
|
func returnRedirect(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Package httpproxy defines an interface to run an HTTP(s) proxy server.
|
||||||
package httpproxy
|
package httpproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -6,9 +7,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/gluetun/internal/settings"
|
|
||||||
"github.com/qdm12/golibs/logging"
|
"github.com/qdm12/golibs/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,8 +17,8 @@ type Looper interface {
|
|||||||
Run(ctx context.Context, wg *sync.WaitGroup)
|
Run(ctx context.Context, wg *sync.WaitGroup)
|
||||||
SetStatus(status models.LoopStatus) (outcome string, err error)
|
SetStatus(status models.LoopStatus) (outcome string, err error)
|
||||||
GetStatus() (status models.LoopStatus)
|
GetStatus() (status models.LoopStatus)
|
||||||
GetSettings() (settings settings.HTTPProxy)
|
GetSettings() (settings configuration.HTTPProxy)
|
||||||
SetSettings(settings settings.HTTPProxy) (outcome string)
|
SetSettings(settings configuration.HTTPProxy) (outcome string)
|
||||||
}
|
}
|
||||||
|
|
||||||
type looper struct {
|
type looper struct {
|
||||||
@@ -34,7 +35,7 @@ type looper struct {
|
|||||||
|
|
||||||
const defaultBackoffTime = 10 * time.Second
|
const defaultBackoffTime = 10 * time.Second
|
||||||
|
|
||||||
func NewLooper(logger logging.Logger, settings settings.HTTPProxy) Looper {
|
func NewLooper(logger logging.Logger, settings configuration.HTTPProxy) Looper {
|
||||||
return &looper{
|
return &looper{
|
||||||
state: state{
|
state: state{
|
||||||
status: constants.Stopped,
|
status: constants.Stopped,
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/gluetun/internal/settings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type state struct {
|
type state struct {
|
||||||
status models.LoopStatus
|
status models.LoopStatus
|
||||||
settings settings.HTTPProxy
|
settings configuration.HTTPProxy
|
||||||
statusMu sync.RWMutex
|
statusMu sync.RWMutex
|
||||||
settingsMu sync.RWMutex
|
settingsMu sync.RWMutex
|
||||||
}
|
}
|
||||||
@@ -69,13 +69,13 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *looper) GetSettings() (settings settings.HTTPProxy) {
|
func (l *looper) GetSettings() (settings configuration.HTTPProxy) {
|
||||||
l.state.settingsMu.RLock()
|
l.state.settingsMu.RLock()
|
||||||
defer l.state.settingsMu.RUnlock()
|
defer l.state.settingsMu.RUnlock()
|
||||||
return l.state.settings
|
return l.state.settings
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *looper) SetSettings(settings settings.HTTPProxy) (outcome string) {
|
func (l *looper) SetSettings(settings configuration.HTTPProxy) (outcome string) {
|
||||||
l.state.settingsMu.Lock()
|
l.state.settingsMu.Lock()
|
||||||
settingsUnchanged := reflect.DeepEqual(settings, l.state.settings)
|
settingsUnchanged := reflect.DeepEqual(settings, l.state.settings)
|
||||||
if settingsUnchanged {
|
if settingsUnchanged {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Package logging defines helper functions for logging.
|
||||||
package logging
|
package logging
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,65 +1,10 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// VPNDevice is the device name used to tunnel using Openvpn.
|
// LoopStatus status such as stopped or running.
|
||||||
VPNDevice string
|
|
||||||
// DNSHost is the DNS host to use for TLS validation.
|
|
||||||
DNSHost string
|
|
||||||
// URL is an HTTP(s) URL address.
|
|
||||||
URL string
|
|
||||||
// Filepath is a local filesytem file path.
|
|
||||||
Filepath string
|
|
||||||
// VPNProvider is the name of the VPN provider to be used.
|
|
||||||
VPNProvider string
|
|
||||||
// NetworkProtocol contains the network protocol to be used to communicate with the VPN servers.
|
|
||||||
NetworkProtocol string
|
|
||||||
// Loop status such as stopped or running.
|
|
||||||
LoopStatus string
|
LoopStatus string
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ls LoopStatus) String() string {
|
func (ls LoopStatus) String() string {
|
||||||
return string(ls)
|
return string(ls)
|
||||||
}
|
}
|
||||||
|
|
||||||
func marshalJSONString(s string) (data []byte, err error) {
|
|
||||||
return []byte(fmt.Sprintf("%q", s)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshalJSONString(data []byte) (s string) {
|
|
||||||
s = string(data)
|
|
||||||
s = strings.TrimPrefix(s, "\"")
|
|
||||||
s = strings.TrimSuffix(s, "\"")
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *VPNProvider) MarshalJSON() ([]byte, error) {
|
|
||||||
return marshalJSONString(string(*v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *VPNProvider) UnmarshalJSON(data []byte) error {
|
|
||||||
*v = VPNProvider(unmarshalJSONString(data))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NetworkProtocol) MarshalJSON() ([]byte, error) {
|
|
||||||
return marshalJSONString(string(*n))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NetworkProtocol) UnmarshalJSON(data []byte) error {
|
|
||||||
*n = NetworkProtocol(unmarshalJSONString(data))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Filepath) MarshalJSON() ([]byte, error) {
|
|
||||||
return marshalJSONString(string(*f))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Filepath) UnmarshalJSON(data []byte) error {
|
|
||||||
*f = Filepath(unmarshalJSONString(data))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_VPNProvider_JSON(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
v := VPNProvider("name")
|
|
||||||
data, err := v.MarshalJSON()
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, []byte{0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22}, data)
|
|
||||||
err = v.UnmarshalJSON(data)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, VPNProvider("name"), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_NetworkProtocol_JSON(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
v := NetworkProtocol("name")
|
|
||||||
data, err := v.MarshalJSON()
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, []byte{0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22}, data)
|
|
||||||
err = v.UnmarshalJSON(data)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, NetworkProtocol("name"), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Filepath_JSON(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
v := Filepath("name")
|
|
||||||
data, err := v.MarshalJSON()
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, []byte{0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22}, data)
|
|
||||||
err = v.UnmarshalJSON(data)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, Filepath("name"), v)
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import "net"
|
|
||||||
|
|
||||||
// DNSProviderData contains information for a DNS provider.
|
|
||||||
type DNSProviderData struct {
|
|
||||||
IPs []net.IP
|
|
||||||
SupportsTLS bool
|
|
||||||
SupportsIPv6 bool
|
|
||||||
SupportsDNSSec bool
|
|
||||||
Host DNSHost
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// Package models defines struct with methods shared across other
|
||||||
|
// packages in the program.
|
||||||
|
package models
|
||||||
@@ -5,10 +5,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type OpenVPNConnection struct {
|
type OpenVPNConnection struct {
|
||||||
IP net.IP
|
IP net.IP `json:"ip"`
|
||||||
Port uint16
|
Port uint16 `json:"port"`
|
||||||
Protocol NetworkProtocol
|
Protocol string `json:"protocol"`
|
||||||
Hostname string // Privado for tls verification
|
Hostname string `json:"hostname"` // Privado for tls verification
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OpenVPNConnection) Equal(other OpenVPNConnection) bool {
|
func (o *OpenVPNConnection) Equal(other OpenVPNConnection) bool {
|
||||||
|
|||||||
@@ -1,155 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ProviderSettings contains settings specific to a VPN provider.
|
|
||||||
type ProviderSettings struct {
|
|
||||||
Name VPNProvider `json:"name"`
|
|
||||||
ServerSelection ServerSelection `json:"server_selection"`
|
|
||||||
ExtraConfigOptions ExtraConfigOptions `json:"extra_config"`
|
|
||||||
PortForwarding PortForwarding `json:"port_forwarding"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServerSelection struct {
|
|
||||||
// Common
|
|
||||||
Protocol NetworkProtocol `json:"network_protocol"`
|
|
||||||
TargetIP net.IP `json:"target_ip,omitempty"`
|
|
||||||
|
|
||||||
// Cyberghost, PIA, Surfshark, Windscribe, Vyprvpn, NordVPN
|
|
||||||
Regions []string `json:"regions"`
|
|
||||||
|
|
||||||
// Cyberghost
|
|
||||||
Group string `json:"group"`
|
|
||||||
|
|
||||||
Countries []string `json:"countries"` // Mullvad, PureVPN
|
|
||||||
Cities []string `json:"cities"` // Mullvad, PureVPN, Windscribe
|
|
||||||
Hostnames []string `json:"hostnames"` // Windscribe, Privado
|
|
||||||
|
|
||||||
// Mullvad
|
|
||||||
ISPs []string `json:"isps"`
|
|
||||||
Owned bool `json:"owned"`
|
|
||||||
|
|
||||||
// Mullvad, Windscribe, PIA
|
|
||||||
CustomPort uint16 `json:"custom_port"`
|
|
||||||
|
|
||||||
// NordVPN
|
|
||||||
Numbers []uint16 `json:"numbers"`
|
|
||||||
|
|
||||||
// PIA
|
|
||||||
EncryptionPreset string `json:"encryption_preset"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExtraConfigOptions struct {
|
|
||||||
ClientCertificate string `json:"-"` // Cyberghost
|
|
||||||
ClientKey string `json:"-"` // Cyberghost
|
|
||||||
EncryptionPreset string `json:"encryption_preset"` // PIA
|
|
||||||
OpenVPNIPv6 bool `json:"openvpn_ipv6"` // Mullvad
|
|
||||||
}
|
|
||||||
|
|
||||||
// PortForwarding contains settings for port forwarding.
|
|
||||||
type PortForwarding struct {
|
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
Filepath Filepath `json:"filepath"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PortForwarding) String() string {
|
|
||||||
if p.Enabled {
|
|
||||||
return fmt.Sprintf("on, saved in %s", p.Filepath)
|
|
||||||
}
|
|
||||||
return "off"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ProviderSettings) String() string {
|
|
||||||
settingsList := []string{
|
|
||||||
fmt.Sprintf("%s settings:", strings.Title(string(p.Name))),
|
|
||||||
"Network protocol: " + string(p.ServerSelection.Protocol),
|
|
||||||
}
|
|
||||||
customPort := ""
|
|
||||||
if p.ServerSelection.CustomPort > 0 {
|
|
||||||
customPort = fmt.Sprintf("%d", p.ServerSelection.CustomPort)
|
|
||||||
}
|
|
||||||
numbers := make([]string, len(p.ServerSelection.Numbers))
|
|
||||||
for i, number := range p.ServerSelection.Numbers {
|
|
||||||
numbers[i] = fmt.Sprintf("%d", number)
|
|
||||||
}
|
|
||||||
ipv6 := "off"
|
|
||||||
if p.ExtraConfigOptions.OpenVPNIPv6 {
|
|
||||||
ipv6 = "on"
|
|
||||||
}
|
|
||||||
switch strings.ToLower(string(p.Name)) {
|
|
||||||
case "private internet access old":
|
|
||||||
settingsList = append(settingsList,
|
|
||||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
|
||||||
"Encryption preset: "+p.ExtraConfigOptions.EncryptionPreset,
|
|
||||||
"Port forwarding: "+p.PortForwarding.String(),
|
|
||||||
)
|
|
||||||
case "private internet access":
|
|
||||||
settingsList = append(settingsList,
|
|
||||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
|
||||||
"Encryption preset: "+p.ExtraConfigOptions.EncryptionPreset,
|
|
||||||
"Port forwarding: "+p.PortForwarding.String(),
|
|
||||||
"Custom port: "+customPort,
|
|
||||||
)
|
|
||||||
case "mullvad":
|
|
||||||
settingsList = append(settingsList,
|
|
||||||
"Countries: "+commaJoin(p.ServerSelection.Countries),
|
|
||||||
"Cities: "+commaJoin(p.ServerSelection.Cities),
|
|
||||||
"ISPs: "+commaJoin(p.ServerSelection.ISPs),
|
|
||||||
"Custom port: "+customPort,
|
|
||||||
"IPv6: "+ipv6,
|
|
||||||
)
|
|
||||||
case "windscribe":
|
|
||||||
settingsList = append(settingsList,
|
|
||||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
|
||||||
"Custom port: "+customPort,
|
|
||||||
)
|
|
||||||
case "surfshark":
|
|
||||||
settingsList = append(settingsList,
|
|
||||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
|
||||||
)
|
|
||||||
case "cyberghost":
|
|
||||||
settingsList = append(settingsList,
|
|
||||||
"Client key: [redacted]",
|
|
||||||
"Client certificate: [redacted]",
|
|
||||||
"Group: "+p.ServerSelection.Group,
|
|
||||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
|
||||||
)
|
|
||||||
case "vyprvpn":
|
|
||||||
settingsList = append(settingsList,
|
|
||||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
|
||||||
)
|
|
||||||
case "nordvpn":
|
|
||||||
settingsList = append(settingsList,
|
|
||||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
|
||||||
"Numbers: "+commaJoin(numbers),
|
|
||||||
)
|
|
||||||
case "purevpn":
|
|
||||||
settingsList = append(settingsList,
|
|
||||||
"Regions: "+commaJoin(p.ServerSelection.Regions),
|
|
||||||
"Countries: "+commaJoin(p.ServerSelection.Countries),
|
|
||||||
"Cities: "+commaJoin(p.ServerSelection.Cities),
|
|
||||||
)
|
|
||||||
case "privado":
|
|
||||||
settingsList = append(settingsList,
|
|
||||||
"Hostnames: "+commaJoin(p.ServerSelection.Hostnames),
|
|
||||||
)
|
|
||||||
default:
|
|
||||||
settingsList = append(settingsList,
|
|
||||||
"<Missing String method, please implement me!>",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if p.ServerSelection.TargetIP != nil {
|
|
||||||
settingsList = append(settingsList,
|
|
||||||
"Target IP address: "+string(p.ServerSelection.TargetIP),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return strings.Join(settingsList, "\n |--")
|
|
||||||
}
|
|
||||||
|
|
||||||
func commaJoin(slice []string) string {
|
|
||||||
return strings.Join(slice, ", ")
|
|
||||||
}
|
|
||||||
@@ -8,16 +8,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type PIAServer struct {
|
type PIAServer struct {
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
ServerName string `json:"server_name"`
|
ServerName string `json:"server_name"`
|
||||||
Protocol NetworkProtocol `json:"protocol"`
|
TCP bool `json:"tcp"`
|
||||||
PortForward bool `json:"port_forward"`
|
UDP bool `json:"udp"`
|
||||||
IP net.IP `json:"ip"`
|
PortForward bool `json:"port_forward"`
|
||||||
|
IP net.IP `json:"ip"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PIAServer) String() string {
|
func (p *PIAServer) String() string {
|
||||||
return fmt.Sprintf("{Region: %q, ServerName: %q, Protocol: %q, PortForward: %t, IP: %s}",
|
return fmt.Sprintf("{Region: %q, ServerName: %q, TCP: %t, UDP: %t, PortForward: %t, IP: %s}",
|
||||||
p.Region, p.ServerName, p.Protocol, p.PortForward, goStringifyIP(p.IP))
|
p.Region, p.ServerName, p.TCP, p.UDP, p.PortForward, goStringifyIP(p.IP))
|
||||||
}
|
}
|
||||||
|
|
||||||
type MullvadServer struct {
|
type MullvadServer struct {
|
||||||
@@ -55,6 +56,18 @@ func (s *SurfsharkServer) String() string {
|
|||||||
return fmt.Sprintf("{Region: %q, IPs: %s}", s.Region, goStringifyIPs(s.IPs))
|
return fmt.Sprintf("{Region: %q, IPs: %s}", s.Region, goStringifyIPs(s.IPs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TorguardServer struct {
|
||||||
|
Country string `json:"country"`
|
||||||
|
City string `json:"city"`
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
IP net.IP `json:"ip"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TorguardServer) String() string {
|
||||||
|
return fmt.Sprintf("{Country: %q, City: %q, Hostname: %q, IP: %s}",
|
||||||
|
s.Country, s.City, s.Hostname, goStringifyIP(s.IP))
|
||||||
|
}
|
||||||
|
|
||||||
type CyberghostServer struct {
|
type CyberghostServer struct {
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
Group string `json:"group"`
|
Group string `json:"group"`
|
||||||
|
|||||||
@@ -9,10 +9,24 @@ type AllServers struct {
|
|||||||
Privado PrivadoServers `json:"privado"`
|
Privado PrivadoServers `json:"privado"`
|
||||||
Purevpn PurevpnServers `json:"purevpn"`
|
Purevpn PurevpnServers `json:"purevpn"`
|
||||||
Surfshark SurfsharkServers `json:"surfshark"`
|
Surfshark SurfsharkServers `json:"surfshark"`
|
||||||
|
Torguard TorguardServers `json:"torguard"`
|
||||||
Vyprvpn VyprvpnServers `json:"vyprvpn"`
|
Vyprvpn VyprvpnServers `json:"vyprvpn"`
|
||||||
Windscribe WindscribeServers `json:"windscribe"`
|
Windscribe WindscribeServers `json:"windscribe"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *AllServers) Count() int {
|
||||||
|
return len(a.Cyberghost.Servers) +
|
||||||
|
len(a.Mullvad.Servers) +
|
||||||
|
len(a.Nordvpn.Servers) +
|
||||||
|
len(a.Pia.Servers) +
|
||||||
|
len(a.Privado.Servers) +
|
||||||
|
len(a.Purevpn.Servers) +
|
||||||
|
len(a.Surfshark.Servers) +
|
||||||
|
len(a.Torguard.Servers) +
|
||||||
|
len(a.Vyprvpn.Servers) +
|
||||||
|
len(a.Windscribe.Servers)
|
||||||
|
}
|
||||||
|
|
||||||
type CyberghostServers struct {
|
type CyberghostServers struct {
|
||||||
Version uint16 `json:"version"`
|
Version uint16 `json:"version"`
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
@@ -48,6 +62,11 @@ type SurfsharkServers struct {
|
|||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
Servers []SurfsharkServer `json:"servers"`
|
Servers []SurfsharkServer `json:"servers"`
|
||||||
}
|
}
|
||||||
|
type TorguardServers struct {
|
||||||
|
Version uint16 `json:"version"`
|
||||||
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
Servers []TorguardServer `json:"servers"`
|
||||||
|
}
|
||||||
type VyprvpnServers struct {
|
type VyprvpnServers struct {
|
||||||
Version uint16 `json:"version"`
|
Version uint16 `json:"version"`
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
|||||||
@@ -10,15 +10,14 @@ import (
|
|||||||
|
|
||||||
// WriteAuthFile writes the OpenVPN auth file to disk with the right permissions.
|
// WriteAuthFile writes the OpenVPN auth file to disk with the right permissions.
|
||||||
func (c *configurator) WriteAuthFile(user, password string, puid, pgid int) error {
|
func (c *configurator) WriteAuthFile(user, password string, puid, pgid int) error {
|
||||||
const filepath = string(constants.OpenVPNAuthConf)
|
file, err := c.os.OpenFile(constants.OpenVPNAuthConf, os.O_RDONLY, 0)
|
||||||
file, err := c.os.OpenFile(filepath, os.O_RDONLY, 0)
|
|
||||||
|
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
file, err = c.os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE, 0400)
|
file, err = c.os.OpenFile(constants.OpenVPNAuthConf, os.O_WRONLY|os.O_CREATE, 0400)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -50,7 +49,7 @@ func (c *configurator) WriteAuthFile(user, password string, puid, pgid int) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.logger.Info("username and password changed in %s", constants.OpenVPNAuthConf)
|
c.logger.Info("username and password changed in %s", constants.OpenVPNAuthConf)
|
||||||
file, err = c.os.OpenFile(filepath, os.O_TRUNC|os.O_WRONLY, 0400)
|
file, err = c.os.OpenFile(constants.OpenVPNAuthConf, os.O_TRUNC|os.O_WRONLY, 0400)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
func (c *configurator) Start(ctx context.Context) (
|
func (c *configurator) Start(ctx context.Context) (
|
||||||
stdoutLines, stderrLines chan string, waitError chan error, err error) {
|
stdoutLines, stderrLines chan string, waitError chan error, err error) {
|
||||||
c.logger.Info("starting openvpn")
|
c.logger.Info("starting openvpn")
|
||||||
return c.commander.Start(ctx, "openvpn", "--config", string(constants.OpenVPNConf))
|
return c.commander.Start(ctx, "openvpn", "--config", constants.OpenVPNConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *configurator) Version(ctx context.Context) (string, error) {
|
func (c *configurator) Version(ctx context.Context) (string, error) {
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/firewall"
|
"github.com/qdm12/gluetun/internal/firewall"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/gluetun/internal/provider"
|
"github.com/qdm12/gluetun/internal/provider"
|
||||||
"github.com/qdm12/gluetun/internal/routing"
|
"github.com/qdm12/gluetun/internal/routing"
|
||||||
"github.com/qdm12/gluetun/internal/settings"
|
|
||||||
"github.com/qdm12/golibs/logging"
|
"github.com/qdm12/golibs/logging"
|
||||||
"github.com/qdm12/golibs/os"
|
"github.com/qdm12/golibs/os"
|
||||||
)
|
)
|
||||||
@@ -22,8 +22,8 @@ type Looper interface {
|
|||||||
Run(ctx context.Context, wg *sync.WaitGroup)
|
Run(ctx context.Context, wg *sync.WaitGroup)
|
||||||
GetStatus() (status models.LoopStatus)
|
GetStatus() (status models.LoopStatus)
|
||||||
SetStatus(status models.LoopStatus) (outcome string, err error)
|
SetStatus(status models.LoopStatus) (outcome string, err error)
|
||||||
GetSettings() (settings settings.OpenVPN)
|
GetSettings() (settings configuration.OpenVPN)
|
||||||
SetSettings(settings settings.OpenVPN) (outcome string)
|
SetSettings(settings configuration.OpenVPN) (outcome string)
|
||||||
GetServers() (servers models.AllServers)
|
GetServers() (servers models.AllServers)
|
||||||
SetServers(servers models.AllServers)
|
SetServers(servers models.AllServers)
|
||||||
GetPortForwarded() (port uint16)
|
GetPortForwarded() (port uint16)
|
||||||
@@ -58,7 +58,7 @@ type looper struct {
|
|||||||
|
|
||||||
const defaultBackoffTime = 15 * time.Second
|
const defaultBackoffTime = 15 * time.Second
|
||||||
|
|
||||||
func NewLooper(settings settings.OpenVPN,
|
func NewLooper(settings configuration.OpenVPN,
|
||||||
username string, puid, pgid int, allServers models.AllServers,
|
username string, puid, pgid int, allServers models.AllServers,
|
||||||
conf Configurator, fw firewall.Configurator, routing routing.Routing,
|
conf Configurator, fw firewall.Configurator, routing routing.Routing,
|
||||||
logger logging.Logger, client *http.Client, openFile os.OpenFileFunc,
|
logger logging.Logger, client *http.Client, openFile os.OpenFileFunc,
|
||||||
@@ -237,7 +237,7 @@ func (l *looper) portForward(ctx context.Context, wg *sync.WaitGroup,
|
|||||||
if !settings.Provider.PortForwarding.Enabled {
|
if !settings.Provider.PortForwarding.Enabled {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
syncState := func(port uint16) (pfFilepath models.Filepath) {
|
syncState := func(port uint16) (pfFilepath string) {
|
||||||
l.state.portForwardedMu.Lock()
|
l.state.portForwardedMu.Lock()
|
||||||
defer l.state.portForwardedMu.Unlock()
|
defer l.state.portForwardedMu.Unlock()
|
||||||
l.state.portForwarded = port
|
l.state.portForwarded = port
|
||||||
@@ -251,8 +251,7 @@ func (l *looper) portForward(ctx context.Context, wg *sync.WaitGroup,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func writeOpenvpnConf(lines []string, openFile os.OpenFileFunc) error {
|
func writeOpenvpnConf(lines []string, openFile os.OpenFileFunc) error {
|
||||||
const filepath = string(constants.OpenVPNConf)
|
file, err := openFile(constants.OpenVPNConf, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
|
||||||
file, err := openFile(filepath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// Package openvpn defines interfaces to interact with openvpn
|
||||||
|
// and run it in a stateful loop.
|
||||||
package openvpn
|
package openvpn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/gluetun/internal/settings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type state struct {
|
type state struct {
|
||||||
status models.LoopStatus
|
status models.LoopStatus
|
||||||
settings settings.OpenVPN
|
settings configuration.OpenVPN
|
||||||
allServers models.AllServers
|
allServers models.AllServers
|
||||||
portForwarded uint16
|
portForwarded uint16
|
||||||
statusMu sync.RWMutex
|
statusMu sync.RWMutex
|
||||||
@@ -27,7 +27,7 @@ func (s *state) setStatusWithLock(status models.LoopStatus) {
|
|||||||
s.status = status
|
s.status = status
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state) getSettingsAndServers() (settings settings.OpenVPN, allServers models.AllServers) {
|
func (s *state) getSettingsAndServers() (settings configuration.OpenVPN, allServers models.AllServers) {
|
||||||
s.settingsMu.RLock()
|
s.settingsMu.RLock()
|
||||||
s.allServersMu.RLock()
|
s.allServersMu.RLock()
|
||||||
settings = s.settings
|
settings = s.settings
|
||||||
@@ -83,13 +83,13 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *looper) GetSettings() (settings settings.OpenVPN) {
|
func (l *looper) GetSettings() (settings configuration.OpenVPN) {
|
||||||
l.state.settingsMu.RLock()
|
l.state.settingsMu.RLock()
|
||||||
defer l.state.settingsMu.RUnlock()
|
defer l.state.settingsMu.RUnlock()
|
||||||
return l.state.settings
|
return l.state.settings
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *looper) SetSettings(settings settings.OpenVPN) (outcome string) {
|
func (l *looper) SetSettings(settings configuration.OpenVPN) (outcome string) {
|
||||||
l.state.settingsMu.Lock()
|
l.state.settingsMu.Lock()
|
||||||
settingsUnchanged := reflect.DeepEqual(l.state.settings, settings)
|
settingsUnchanged := reflect.DeepEqual(l.state.settings, settings)
|
||||||
if settingsUnchanged {
|
if settingsUnchanged {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
// CheckTUN checks the tunnel device is present and accessible.
|
// CheckTUN checks the tunnel device is present and accessible.
|
||||||
func (c *configurator) CheckTUN() error {
|
func (c *configurator) CheckTUN() error {
|
||||||
c.logger.Info("checking for device %s", constants.TunnelDevice)
|
c.logger.Info("checking for device %s", constants.TunnelDevice)
|
||||||
f, err := c.os.OpenFile(string(constants.TunnelDevice), os.O_RDWR, 0)
|
f, err := c.os.OpenFile(constants.TunnelDevice, os.O_RDWR, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("TUN device is not available: %w", err)
|
return fmt.Errorf("TUN device is not available: %w", err)
|
||||||
}
|
}
|
||||||
@@ -32,12 +32,11 @@ func (c *configurator) CreateTUN() error {
|
|||||||
minor = 200
|
minor = 200
|
||||||
)
|
)
|
||||||
dev := c.unix.Mkdev(major, minor)
|
dev := c.unix.Mkdev(major, minor)
|
||||||
if err := c.unix.Mknod(string(constants.TunnelDevice), unix.S_IFCHR, int(dev)); err != nil {
|
if err := c.unix.Mknod(constants.TunnelDevice, unix.S_IFCHR, int(dev)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
const filepath = string(constants.TunnelDevice)
|
file, err := c.os.OpenFile(constants.TunnelDevice, os.O_WRONLY, 0666)
|
||||||
file, err := c.os.OpenFile(filepath, os.O_WRONLY, 0666)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
package params
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
|
||||||
libparams "github.com/qdm12/golibs/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetCyberghostGroup obtains the server group for the Cyberghost server from the
|
|
||||||
// environment variable CYBERGHOST_GROUP.
|
|
||||||
func (r *reader) GetCyberghostGroup() (group string, err error) {
|
|
||||||
s, err := r.env.Inside("CYBERGHOST_GROUP",
|
|
||||||
constants.CyberghostGroupChoices(), libparams.Default("Premium UDP Europe"))
|
|
||||||
return s, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCyberghostRegions obtains the country names for the Cyberghost servers from the
|
|
||||||
// environment variable REGION.
|
|
||||||
func (r *reader) GetCyberghostRegions() (regions []string, err error) {
|
|
||||||
return r.env.CSVInside("REGION", constants.CyberghostRegionChoices())
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCyberghostClientKey obtains the client key to use for openvpn
|
|
||||||
// from the secret file /run/secrets/openvpn_clientkey or from the file
|
|
||||||
// /gluetun/client.key.
|
|
||||||
func (r *reader) GetCyberghostClientKey() (clientKey string, err error) {
|
|
||||||
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTKEY", string(constants.ClientKey))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return extractClientKey(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractClientKey(b []byte) (key string, err error) {
|
|
||||||
pemBlock, _ := pem.Decode(b)
|
|
||||||
if pemBlock == nil {
|
|
||||||
return "", fmt.Errorf("cannot decode PEM block from client key")
|
|
||||||
}
|
|
||||||
parsedBytes := pem.EncodeToMemory(pemBlock)
|
|
||||||
s := string(parsedBytes)
|
|
||||||
s = strings.ReplaceAll(s, "\n", "")
|
|
||||||
s = strings.TrimPrefix(s, "-----BEGIN PRIVATE KEY-----")
|
|
||||||
s = strings.TrimSuffix(s, "-----END PRIVATE KEY-----")
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCyberghostClientCertificate obtains the client certificate to use for openvpn
|
|
||||||
// from the secret file /run/secrets/openvpn_clientcrt or from the file
|
|
||||||
// /gluetun/client.crt.
|
|
||||||
func (r *reader) GetCyberghostClientCertificate() (clientCertificate string, err error) {
|
|
||||||
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTCRT", string(constants.ClientCertificate))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return extractClientCertificate(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractClientCertificate(b []byte) (certificate string, err error) {
|
|
||||||
pemBlock, _ := pem.Decode(b)
|
|
||||||
if pemBlock == nil {
|
|
||||||
return "", fmt.Errorf("cannot decode PEM block from client certificate")
|
|
||||||
}
|
|
||||||
parsedBytes := pem.EncodeToMemory(pemBlock)
|
|
||||||
s := string(parsedBytes)
|
|
||||||
s = strings.ReplaceAll(s, "\n", "")
|
|
||||||
s = strings.TrimPrefix(s, "-----BEGIN CERTIFICATE-----")
|
|
||||||
s = strings.TrimSuffix(s, "-----END CERTIFICATE-----")
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
package params
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
dns "github.com/qdm12/dns/pkg/unbound"
|
|
||||||
libparams "github.com/qdm12/golibs/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetDNSOverTLS obtains if the DNS over TLS should be enabled
|
|
||||||
// from the environment variable DOT.
|
|
||||||
func (r *reader) GetDNSOverTLS() (DNSOverTLS bool, err error) { //nolint:gocritic
|
|
||||||
return r.env.OnOff("DOT", libparams.Default("on"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDNSOverTLSProviders obtains the DNS over TLS providers to use
|
|
||||||
// from the environment variable DOT_PROVIDERS.
|
|
||||||
func (r *reader) GetDNSOverTLSProviders() (providers []string, err error) {
|
|
||||||
s, err := r.env.Get("DOT_PROVIDERS", libparams.Default("cloudflare"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, provider := range strings.Split(s, ",") {
|
|
||||||
_, ok := dns.GetProviderData(provider)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("DNS over TLS provider %q is not valid", provider)
|
|
||||||
}
|
|
||||||
providers = append(providers, provider)
|
|
||||||
}
|
|
||||||
return providers, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDNSOverTLSVerbosity obtains the verbosity level to use for Unbound
|
|
||||||
// from the environment variable DOT_VERBOSITY.
|
|
||||||
func (r *reader) GetDNSOverTLSVerbosity() (verbosityLevel uint8, err error) {
|
|
||||||
n, err := r.env.IntRange("DOT_VERBOSITY", 0, 5, libparams.Default("1"))
|
|
||||||
return uint8(n), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDNSOverTLSVerbosityDetails obtains the log level to use for Unbound
|
|
||||||
// from the environment variable DOT_VERBOSITY_DETAILS.
|
|
||||||
func (r *reader) GetDNSOverTLSVerbosityDetails() (verbosityDetailsLevel uint8, err error) {
|
|
||||||
n, err := r.env.IntRange("DOT_VERBOSITY_DETAILS", 0, 4, libparams.Default("0"))
|
|
||||||
return uint8(n), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDNSOverTLSValidationLogLevel obtains the log level to use for Unbound DOT validation
|
|
||||||
// from the environment variable DOT_VALIDATION_LOGLEVEL.
|
|
||||||
func (r *reader) GetDNSOverTLSValidationLogLevel() (validationLogLevel uint8, err error) {
|
|
||||||
n, err := r.env.IntRange("DOT_VALIDATION_LOGLEVEL", 0, 2, libparams.Default("0"))
|
|
||||||
return uint8(n), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDNSMaliciousBlocking obtains if malicious hostnames/IPs should be blocked
|
|
||||||
// from being resolved by Unbound, using the environment variable BLOCK_MALICIOUS.
|
|
||||||
func (r *reader) GetDNSMaliciousBlocking() (blocking bool, err error) {
|
|
||||||
return r.env.OnOff("BLOCK_MALICIOUS", libparams.Default("on"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDNSSurveillanceBlocking obtains if surveillance hostnames/IPs should be blocked
|
|
||||||
// from being resolved by Unbound, using the environment variable BLOCK_SURVEILLANCE
|
|
||||||
// and BLOCK_NSA for retrocompatibility.
|
|
||||||
func (r *reader) GetDNSSurveillanceBlocking() (blocking bool, err error) {
|
|
||||||
// Retro-compatibility
|
|
||||||
s, err := r.env.Get("BLOCK_NSA")
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
} else if len(s) != 0 {
|
|
||||||
r.logger.Warn("You are using the old environment variable BLOCK_NSA, please consider changing it to BLOCK_SURVEILLANCE") //nolint:lll
|
|
||||||
return r.env.OnOff("BLOCK_NSA", libparams.Compulsory())
|
|
||||||
}
|
|
||||||
return r.env.OnOff("BLOCK_SURVEILLANCE", libparams.Default("off"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDNSAdsBlocking obtains if ads hostnames/IPs should be blocked
|
|
||||||
// from being resolved by Unbound, using the environment variable BLOCK_ADS.
|
|
||||||
func (r *reader) GetDNSAdsBlocking() (blocking bool, err error) {
|
|
||||||
return r.env.OnOff("BLOCK_ADS", libparams.Default("off"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDNSUnblockedHostnames obtains a list of hostnames to unblock from block lists
|
|
||||||
// from the comma separated list for the environment variable UNBLOCK.
|
|
||||||
func (r *reader) GetDNSUnblockedHostnames() (hostnames []string, err error) {
|
|
||||||
s, err := r.env.Get("UNBLOCK")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if len(s) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
hostnames = strings.Split(s, ",")
|
|
||||||
for _, hostname := range hostnames {
|
|
||||||
if !r.regex.MatchHostname(hostname) {
|
|
||||||
return nil, fmt.Errorf("hostname %q does not seem valid", hostname)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hostnames, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDNSOverTLSCaching obtains if Unbound caching should be enable or not
|
|
||||||
// from the environment variable DOT_CACHING.
|
|
||||||
func (r *reader) GetDNSOverTLSCaching() (caching bool, err error) {
|
|
||||||
return r.env.OnOff("DOT_CACHING", libparams.Default("on"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDNSOverTLSPrivateAddresses obtains if Unbound caching should be enable or not
|
|
||||||
// from the environment variable DOT_PRIVATE_ADDRESS.
|
|
||||||
func (r *reader) GetDNSOverTLSPrivateAddresses() (privateAddresses []string, err error) {
|
|
||||||
s, err := r.env.Get("DOT_PRIVATE_ADDRESS")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if len(s) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
privateAddresses = strings.Split(s, ",")
|
|
||||||
for _, address := range privateAddresses {
|
|
||||||
ip := net.ParseIP(address)
|
|
||||||
_, _, err := net.ParseCIDR(address)
|
|
||||||
if ip == nil && err != nil {
|
|
||||||
return nil, fmt.Errorf("private address %q is not a valid IP or CIDR range", address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return privateAddresses, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDNSOverTLSIPv6 obtains if Unbound should resolve ipv6 addresses using
|
|
||||||
// ipv6 DNS over TLS from the environment variable DOT_IPV6.
|
|
||||||
func (r *reader) GetDNSOverTLSIPv6() (ipv6 bool, err error) {
|
|
||||||
return r.env.OnOff("DOT_IPV6", libparams.Default("off"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDNSUpdatePeriod obtains the period to use to update the block lists and cryptographic files
|
|
||||||
// and restart Unbound from the environment variable DNS_UPDATE_PERIOD.
|
|
||||||
func (r *reader) GetDNSUpdatePeriod() (period time.Duration, err error) {
|
|
||||||
s, err := r.env.Get("DNS_UPDATE_PERIOD", libparams.Default("24h"))
|
|
||||||
if err != nil {
|
|
||||||
return period, err
|
|
||||||
}
|
|
||||||
return time.ParseDuration(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDNSPlaintext obtains the plaintext DNS address to use if DNS over TLS is disabled
|
|
||||||
// from the environment variable DNS_PLAINTEXT_ADDRESS.
|
|
||||||
func (r *reader) GetDNSPlaintext() (ip net.IP, err error) {
|
|
||||||
s, err := r.env.Get("DNS_PLAINTEXT_ADDRESS", libparams.Default("1.1.1.1"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ip = net.ParseIP(s)
|
|
||||||
if ip == nil {
|
|
||||||
return nil, fmt.Errorf("DNS plaintext address %q is not a valid IP address", s)
|
|
||||||
}
|
|
||||||
return ip, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDNSKeepNameserver obtains if the nameserver present in /etc/resolv.conf
|
|
||||||
// should be kept instead of overridden, from the environment variable DNS_KEEP_NAMESERVER.
|
|
||||||
func (r *reader) GetDNSKeepNameserver() (on bool, err error) {
|
|
||||||
return r.env.OnOff("DNS_KEEP_NAMESERVER", libparams.Default("off"))
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package params
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
libparams "github.com/qdm12/golibs/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetFirewall obtains if the firewall should be enabled from the environment variable FIREWALL.
|
|
||||||
func (r *reader) GetFirewall() (enabled bool, err error) {
|
|
||||||
return r.env.OnOff("FIREWALL", libparams.Default("on"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllowedVPNInputPorts obtains a list of input ports to allow from the
|
|
||||||
// VPN server side in the firewall, from the environment variable FIREWALL_VPN_INPUT_PORTS.
|
|
||||||
func (r *reader) GetVPNInputPorts() (ports []uint16, err error) {
|
|
||||||
s, err := r.env.Get("FIREWALL_VPN_INPUT_PORTS", libparams.Default(""))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(s) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
portsStr := strings.Split(s, ",")
|
|
||||||
ports = make([]uint16, len(portsStr))
|
|
||||||
for i := range portsStr {
|
|
||||||
portInt, err := strconv.Atoi(portsStr[i])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("VPN input port %q is not valid (%s)", portInt, err)
|
|
||||||
} else if portInt <= 0 || portInt > 65535 {
|
|
||||||
return nil, fmt.Errorf("VPN input port %d must be between 1 and 65535", portInt)
|
|
||||||
}
|
|
||||||
ports[i] = uint16(portInt)
|
|
||||||
}
|
|
||||||
return ports, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInputPorts obtains a list of input ports to allow through the
|
|
||||||
// default interface in the firewall, from the environment variable FIREWALL_INPUT_PORTS.
|
|
||||||
func (r *reader) GetInputPorts() (ports []uint16, err error) {
|
|
||||||
s, err := r.env.Get("FIREWALL_INPUT_PORTS", libparams.Default(""))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(s) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
portsStr := strings.Split(s, ",")
|
|
||||||
ports = make([]uint16, len(portsStr))
|
|
||||||
for i := range portsStr {
|
|
||||||
portInt, err := strconv.Atoi(portsStr[i])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Input port %q is not valid (%s)", portInt, err)
|
|
||||||
} else if portInt <= 0 || portInt > 65535 {
|
|
||||||
return nil, fmt.Errorf("Input port %d must be between 1 and 65535", portInt)
|
|
||||||
}
|
|
||||||
ports[i] = uint16(portInt)
|
|
||||||
}
|
|
||||||
return ports, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFirewallDebug obtains if the firewall should run in debug verbose mode
|
|
||||||
// from the environment variable FIREWALL_DEBUG.
|
|
||||||
func (r *reader) GetFirewallDebug() (debug bool, err error) {
|
|
||||||
return r.env.OnOff("FIREWALL_DEBUG", libparams.Default("off"))
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
package params
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
libparams "github.com/qdm12/golibs/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetHTTPProxy obtains if the HTTP proxy is on from the environment variable
|
|
||||||
// HTTPPROXY, and using PROXY and TINYPROXY as retro-compatibility names.
|
|
||||||
func (r *reader) GetHTTPProxy() (enabled bool, err error) {
|
|
||||||
retroKeysOption := libparams.RetroKeys(
|
|
||||||
[]string{"TINYPROXY", "PROXY"},
|
|
||||||
r.onRetroActive,
|
|
||||||
)
|
|
||||||
return r.env.OnOff("HTTPPROXY", retroKeysOption, libparams.Default("off"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHTTPProxyLog obtains the if http proxy requests should be logged from
|
|
||||||
// the environment variable HTTPPROXY_LOG, and using PROXY_LOG_LEVEL and
|
|
||||||
// TINYPROXY_LOG as retro-compatibility names.
|
|
||||||
func (r *reader) GetHTTPProxyLog() (log bool, err error) {
|
|
||||||
s, _ := r.env.Get("HTTPPROXY_LOG")
|
|
||||||
if len(s) == 0 {
|
|
||||||
s, _ = r.env.Get("PROXY_LOG_LEVEL")
|
|
||||||
if len(s) == 0 {
|
|
||||||
s, _ = r.env.Get("TINYPROXY_LOG")
|
|
||||||
if len(s) == 0 {
|
|
||||||
return false, nil // default log disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch strings.ToLower(s) {
|
|
||||||
case "info", "connect", "notice":
|
|
||||||
return true, nil
|
|
||||||
default:
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r.env.OnOff("HTTPPROXY_LOG", libparams.Default("off"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHTTPProxyPort obtains the HTTP proxy listening port from the environment variable
|
|
||||||
// HTTPPROXY_PORT, and using PROXY_PORT and TINYPROXY_PORT as retro-compatibility names.
|
|
||||||
func (r *reader) GetHTTPProxyPort() (port uint16, warning string, err error) {
|
|
||||||
retroKeysOption := libparams.RetroKeys(
|
|
||||||
[]string{"TINYPROXY_PORT", "PROXY_PORT"},
|
|
||||||
r.onRetroActive,
|
|
||||||
)
|
|
||||||
return r.env.ListeningPort("HTTPPROXY_PORT", retroKeysOption, libparams.Default("8888"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHTTPProxyUser obtains the HTTP proxy server user.
|
|
||||||
// It first tries to use the HTTPPROXY_USER environment variable (easier for the end user)
|
|
||||||
// and then tries to read from the secret file httpproxy_user if nothing was found.
|
|
||||||
func (r *reader) GetHTTPProxyUser() (user string, err error) {
|
|
||||||
const compulsory = false
|
|
||||||
return r.getFromEnvOrSecretFile(
|
|
||||||
"HTTPPROXY_USER",
|
|
||||||
compulsory,
|
|
||||||
[]string{"TINYPROXY_USER", "PROXY_USER"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHTTPProxyPassword obtains the HTTP proxy server password.
|
|
||||||
// It first tries to use the HTTPPROXY_PASSWORD environment variable (easier for the end user)
|
|
||||||
// and then tries to read from the secret file httpproxy_password if nothing was found.
|
|
||||||
func (r *reader) GetHTTPProxyPassword() (password string, err error) {
|
|
||||||
const compulsory = false
|
|
||||||
return r.getFromEnvOrSecretFile(
|
|
||||||
"HTTPPROXY_USER",
|
|
||||||
compulsory,
|
|
||||||
[]string{"TINYPROXY_PASSWORD", "PROXY_PASSWORD"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHTTPProxyStealth obtains the HTTP proxy server stealth mode
|
|
||||||
// from the environment variable HTTPPROXY_STEALTH.
|
|
||||||
func (r *reader) GetHTTPProxyStealth() (stealth bool, err error) {
|
|
||||||
return r.env.OnOff("HTTPPROXY_STEALTH", libparams.Default("off"))
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package params
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
|
||||||
libparams "github.com/qdm12/golibs/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetMullvadCountries obtains the countries for the Mullvad servers from the
|
|
||||||
// environment variable COUNTRY.
|
|
||||||
func (r *reader) GetMullvadCountries() (countries []string, err error) {
|
|
||||||
return r.env.CSVInside("COUNTRY", constants.MullvadCountryChoices())
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMullvadCity obtains the cities for the Mullvad servers from the
|
|
||||||
// environment variable CITY.
|
|
||||||
func (r *reader) GetMullvadCities() (cities []string, err error) {
|
|
||||||
return r.env.CSVInside("CITY", constants.MullvadCityChoices())
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMullvadISPs obtains the ISPs for the Mullvad servers from the
|
|
||||||
// environment variable ISP.
|
|
||||||
func (r *reader) GetMullvadISPs() (isps []string, err error) {
|
|
||||||
return r.env.CSVInside("ISP", constants.MullvadISPChoices())
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMullvadPort obtains the port to reach the Mullvad server on from the
|
|
||||||
// environment variable PORT.
|
|
||||||
func (r *reader) GetMullvadPort() (port uint16, err error) {
|
|
||||||
n, err := r.env.IntRange("PORT", 0, 65535, libparams.Default("0"))
|
|
||||||
return uint16(n), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMullvadOwned obtains if the server should be owned by Mullvad or not from the
|
|
||||||
// environment variable OWNED.
|
|
||||||
func (r *reader) GetMullvadOwned() (owned bool, err error) {
|
|
||||||
return r.env.YesNo("OWNED", libparams.Default("no"))
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package params
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetNordvpnRegions obtains the regions (countries) for the NordVPN server from the
|
|
||||||
// environment variable REGION.
|
|
||||||
func (r *reader) GetNordvpnRegions() (regions []string, err error) {
|
|
||||||
return r.env.CSVInside("REGION", constants.NordvpnRegionChoices())
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNordvpnRegion obtains the server numbers (optional) for the NordVPN servers from the
|
|
||||||
// environment variable SERVER_NUMBER.
|
|
||||||
func (r *reader) GetNordvpnNumbers() (numbers []uint16, err error) {
|
|
||||||
possibilities := make([]string, 65537)
|
|
||||||
for i := range possibilities {
|
|
||||||
possibilities[i] = fmt.Sprintf("%d", i)
|
|
||||||
}
|
|
||||||
possibilities[65536] = ""
|
|
||||||
values, err := r.env.CSVInside("SERVER_NUMBER", possibilities)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
numbers = make([]uint16, len(values))
|
|
||||||
for i := range values {
|
|
||||||
n, err := strconv.Atoi(values[i])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
numbers[i] = uint16(n)
|
|
||||||
}
|
|
||||||
return numbers, nil
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
package params
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
libparams "github.com/qdm12/golibs/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetUser obtains the user to use to connect to the VPN servers.
|
|
||||||
// It first tries to use the OPENVPN_USER environment variable (easier for the end user)
|
|
||||||
// and then tries to read from the secret file openvpn_user if nothing was found.
|
|
||||||
func (r *reader) GetUser() (user string, err error) {
|
|
||||||
const compulsory = true
|
|
||||||
return r.getFromEnvOrSecretFile("OPENVPN_USER", compulsory, []string{"USER"})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPassword obtains the password to use to connect to the VPN servers.
|
|
||||||
// It first tries to use the OPENVPN_PASSWORD environment variable (easier for the end user)
|
|
||||||
// and then tries to read from the secret file openvpn_password if nothing was found.
|
|
||||||
func (r *reader) GetPassword() (s string, err error) {
|
|
||||||
const compulsory = true
|
|
||||||
return r.getFromEnvOrSecretFile("OPENVPN_PASSWORD", compulsory, []string{"PASSWORD"})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNetworkProtocol obtains the network protocol to use to connect to the
|
|
||||||
// VPN servers from the environment variable PROTOCOL.
|
|
||||||
func (r *reader) GetNetworkProtocol() (protocol models.NetworkProtocol, err error) {
|
|
||||||
s, err := r.env.Inside("PROTOCOL", []string{"tcp", "udp"}, libparams.Default("udp"))
|
|
||||||
return models.NetworkProtocol(s), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOpenVPNVerbosity obtains the verbosity level for verbosity between 0 and 6
|
|
||||||
// from the environment variable OPENVPN_VERBOSITY.
|
|
||||||
func (r *reader) GetOpenVPNVerbosity() (verbosity int, err error) {
|
|
||||||
return r.env.IntRange("OPENVPN_VERBOSITY", 0, 6, libparams.Default("1"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOpenVPNRoot obtains if openvpn should be run as root
|
|
||||||
// from the environment variable OPENVPN_ROOT.
|
|
||||||
func (r *reader) GetOpenVPNRoot() (root bool, err error) {
|
|
||||||
return r.env.YesNo("OPENVPN_ROOT", libparams.Default("no"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTargetIP obtains the IP address to override over the list of IP addresses filtered
|
|
||||||
// from the environment variable OPENVPN_TARGET_IP.
|
|
||||||
func (r *reader) GetTargetIP() (ip net.IP, err error) {
|
|
||||||
s, err := r.env.Get("OPENVPN_TARGET_IP")
|
|
||||||
if len(s) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ip = net.ParseIP(s)
|
|
||||||
if ip == nil {
|
|
||||||
return nil, fmt.Errorf("target IP address %q is not valid", s)
|
|
||||||
}
|
|
||||||
return ip, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOpenVPNCipher obtains a custom cipher to use with OpenVPN
|
|
||||||
// from the environment variable OPENVPN_CIPHER.
|
|
||||||
func (r *reader) GetOpenVPNCipher() (cipher string, err error) {
|
|
||||||
return r.env.Get("OPENVPN_CIPHER")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOpenVPNAuth obtains a custom auth algorithm to use with OpenVPN
|
|
||||||
// from the environment variable OPENVPN_AUTH.
|
|
||||||
func (r *reader) GetOpenVPNAuth() (auth string, err error) {
|
|
||||||
return r.env.Get("OPENVPN_AUTH")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOpenVPNIPv6 obtains if ipv6 should be tunneled through the
|
|
||||||
// openvpn tunnel from the environment variable OPENVPN_IPV6.
|
|
||||||
func (r *reader) GetOpenVPNIPv6() (ipv6 bool, err error) {
|
|
||||||
return r.env.OnOff("OPENVPN_IPV6", libparams.Default("off"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) GetOpenVPNMSSFix() (mssFix uint16, err error) {
|
|
||||||
n, err := r.env.IntRange("OPENVPN_MSSFIX", 0, 10000, libparams.Default("0"))
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return uint16(n), nil
|
|
||||||
}
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
package params
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
"github.com/qdm12/golibs/logging"
|
|
||||||
"github.com/qdm12/golibs/os"
|
|
||||||
libparams "github.com/qdm12/golibs/params"
|
|
||||||
"github.com/qdm12/golibs/verification"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reader contains methods to obtain parameters.
|
|
||||||
type Reader interface {
|
|
||||||
GetVPNSP() (vpnServiceProvider models.VPNProvider, err error)
|
|
||||||
|
|
||||||
// DNS over TLS getters
|
|
||||||
GetDNSOverTLS() (DNSOverTLS bool, err error)
|
|
||||||
GetDNSOverTLSProviders() (providers []string, err error)
|
|
||||||
GetDNSOverTLSCaching() (caching bool, err error)
|
|
||||||
GetDNSOverTLSVerbosity() (verbosityLevel uint8, err error)
|
|
||||||
GetDNSOverTLSVerbosityDetails() (verbosityDetailsLevel uint8, err error)
|
|
||||||
GetDNSOverTLSValidationLogLevel() (validationLogLevel uint8, err error)
|
|
||||||
GetDNSMaliciousBlocking() (blocking bool, err error)
|
|
||||||
GetDNSSurveillanceBlocking() (blocking bool, err error)
|
|
||||||
GetDNSAdsBlocking() (blocking bool, err error)
|
|
||||||
GetDNSUnblockedHostnames() (hostnames []string, err error)
|
|
||||||
GetDNSOverTLSPrivateAddresses() (privateAddresses []string, err error)
|
|
||||||
GetDNSOverTLSIPv6() (ipv6 bool, err error)
|
|
||||||
GetDNSUpdatePeriod() (period time.Duration, err error)
|
|
||||||
GetDNSPlaintext() (ip net.IP, err error)
|
|
||||||
GetDNSKeepNameserver() (on bool, err error)
|
|
||||||
|
|
||||||
// System
|
|
||||||
GetPUID() (puid int, err error)
|
|
||||||
GetPGID() (pgid int, err error)
|
|
||||||
GetTimezone() (timezone string, err error)
|
|
||||||
GetPublicIPFilepath() (filepath models.Filepath, err error)
|
|
||||||
|
|
||||||
// Firewall getters
|
|
||||||
GetFirewall() (enabled bool, err error)
|
|
||||||
GetVPNInputPorts() (ports []uint16, err error)
|
|
||||||
GetInputPorts() (ports []uint16, err error)
|
|
||||||
GetOutboundSubnets() (outboundSubnets []net.IPNet, err error)
|
|
||||||
GetFirewallDebug() (debug bool, err error)
|
|
||||||
|
|
||||||
// VPN getters
|
|
||||||
GetUser() (s string, err error)
|
|
||||||
GetPassword() (s string, err error)
|
|
||||||
GetNetworkProtocol() (protocol models.NetworkProtocol, err error)
|
|
||||||
GetOpenVPNVerbosity() (verbosity int, err error)
|
|
||||||
GetOpenVPNRoot() (root bool, err error)
|
|
||||||
GetTargetIP() (ip net.IP, err error)
|
|
||||||
GetOpenVPNCipher() (cipher string, err error)
|
|
||||||
GetOpenVPNAuth() (auth string, err error)
|
|
||||||
GetOpenVPNIPv6() (tunnel bool, err error)
|
|
||||||
GetOpenVPNMSSFix() (mssFix uint16, err error)
|
|
||||||
|
|
||||||
// PIA getters
|
|
||||||
GetPortForwarding() (activated bool, err error)
|
|
||||||
GetPortForwardingStatusFilepath() (filepath models.Filepath, err error)
|
|
||||||
GetPIAEncryptionPreset() (preset string, err error)
|
|
||||||
GetPIARegions() (regions []string, err error)
|
|
||||||
GetPIAPort() (port uint16, err error)
|
|
||||||
|
|
||||||
// Mullvad getters
|
|
||||||
GetMullvadCountries() (countries []string, err error)
|
|
||||||
GetMullvadCities() (cities []string, err error)
|
|
||||||
GetMullvadISPs() (ips []string, err error)
|
|
||||||
GetMullvadPort() (port uint16, err error)
|
|
||||||
GetMullvadOwned() (owned bool, err error)
|
|
||||||
|
|
||||||
// Windscribe getters
|
|
||||||
GetWindscribeRegions() (countries []string, err error)
|
|
||||||
GetWindscribeCities() (cities []string, err error)
|
|
||||||
GetWindscribeHostnames() (hostnames []string, err error)
|
|
||||||
GetWindscribePort(protocol models.NetworkProtocol) (port uint16, err error)
|
|
||||||
|
|
||||||
// Surfshark getters
|
|
||||||
GetSurfsharkRegions() (countries []string, err error)
|
|
||||||
|
|
||||||
// Cyberghost getters
|
|
||||||
GetCyberghostGroup() (group string, err error)
|
|
||||||
GetCyberghostRegions() (regions []string, err error)
|
|
||||||
GetCyberghostClientKey() (clientKey string, err error)
|
|
||||||
GetCyberghostClientCertificate() (clientCertificate string, err error)
|
|
||||||
|
|
||||||
// Vyprvpn getters
|
|
||||||
GetVyprvpnRegions() (regions []string, err error)
|
|
||||||
|
|
||||||
// NordVPN getters
|
|
||||||
GetNordvpnRegions() (regions []string, err error)
|
|
||||||
GetNordvpnNumbers() (numbers []uint16, err error)
|
|
||||||
|
|
||||||
// Privado getters
|
|
||||||
GetPrivadoHostnames() (hostnames []string, err error)
|
|
||||||
|
|
||||||
// PureVPN getters
|
|
||||||
GetPurevpnRegions() (regions []string, err error)
|
|
||||||
GetPurevpnCountries() (countries []string, err error)
|
|
||||||
GetPurevpnCities() (cities []string, err error)
|
|
||||||
|
|
||||||
// Shadowsocks getters
|
|
||||||
GetShadowSocks() (activated bool, err error)
|
|
||||||
GetShadowSocksLog() (activated bool, err error)
|
|
||||||
GetShadowSocksPort() (port uint16, warning string, err error)
|
|
||||||
GetShadowSocksPassword() (password string, err error)
|
|
||||||
GetShadowSocksMethod() (method string, err error)
|
|
||||||
|
|
||||||
// HTTP proxy getters
|
|
||||||
GetHTTPProxy() (activated bool, err error)
|
|
||||||
GetHTTPProxyLog() (log bool, err error)
|
|
||||||
GetHTTPProxyPort() (port uint16, warning string, err error)
|
|
||||||
GetHTTPProxyUser() (user string, err error)
|
|
||||||
GetHTTPProxyPassword() (password string, err error)
|
|
||||||
GetHTTPProxyStealth() (stealth bool, err error)
|
|
||||||
|
|
||||||
// Public IP getters
|
|
||||||
GetPublicIPPeriod() (period time.Duration, err error)
|
|
||||||
|
|
||||||
// Control server
|
|
||||||
GetControlServerPort() (port uint16, warning string, err error)
|
|
||||||
GetControlServerLog() (enabled bool, err error)
|
|
||||||
|
|
||||||
GetVersionInformation() (enabled bool, err error)
|
|
||||||
|
|
||||||
GetUpdaterPeriod() (period time.Duration, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type reader struct {
|
|
||||||
env libparams.Env
|
|
||||||
logger logging.Logger
|
|
||||||
regex verification.Regex
|
|
||||||
os os.OS
|
|
||||||
}
|
|
||||||
|
|
||||||
// Newreader returns a paramsReadeer object to read parameters from
|
|
||||||
// environment variables.
|
|
||||||
func NewReader(logger logging.Logger, os os.OS) Reader {
|
|
||||||
return &reader{
|
|
||||||
env: libparams.NewEnv(),
|
|
||||||
logger: logger,
|
|
||||||
regex: verification.NewRegex(),
|
|
||||||
os: os,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetVPNSP obtains the VPN service provider to use from the environment variable VPNSP.
|
|
||||||
func (r *reader) GetVPNSP() (vpnServiceProvider models.VPNProvider, err error) {
|
|
||||||
s, err := r.env.Inside(
|
|
||||||
"VPNSP",
|
|
||||||
[]string{
|
|
||||||
"pia", "private internet access",
|
|
||||||
"mullvad", "windscribe", "surfshark", "cyberghost",
|
|
||||||
"vyprvpn", "nordvpn", "purevpn", "privado",
|
|
||||||
}, libparams.Default("private internet access"))
|
|
||||||
if s == "pia" {
|
|
||||||
s = "private internet access"
|
|
||||||
}
|
|
||||||
return models.VPNProvider(s), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) GetVersionInformation() (enabled bool, err error) {
|
|
||||||
return r.env.OnOff("VERSION_INFORMATION", libparams.Default("on"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) onRetroActive(oldKey, newKey string) {
|
|
||||||
r.logger.Warn(
|
|
||||||
"You are using the old environment variable %s, please consider changing it to %s",
|
|
||||||
oldKey, newKey,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
package params
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
libparams "github.com/qdm12/golibs/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetPortForwarding obtains if port forwarding on the VPN provider server
|
|
||||||
// side is enabled or not from the environment variable PORT_FORWARDING
|
|
||||||
// Only valid for older PIA servers for now.
|
|
||||||
func (r *reader) GetPortForwarding() (activated bool, err error) {
|
|
||||||
s, err := r.env.Get("PORT_FORWARDING", libparams.Default("off"))
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
// Custom for retro-compatibility
|
|
||||||
if s == "false" || s == "off" {
|
|
||||||
return false, nil
|
|
||||||
} else if s == "true" || s == "on" {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
return false, fmt.Errorf("PORT_FORWARDING can only be \"on\" or \"off\"")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPortForwardingStatusFilepath obtains the port forwarding status file path
|
|
||||||
// from the environment variable PORT_FORWARDING_STATUS_FILE.
|
|
||||||
func (r *reader) GetPortForwardingStatusFilepath() (filepath models.Filepath, err error) {
|
|
||||||
filepathStr, err := r.env.Path(
|
|
||||||
"PORT_FORWARDING_STATUS_FILE",
|
|
||||||
libparams.Default("/tmp/gluetun/forwarded_port"),
|
|
||||||
libparams.CaseSensitiveValue())
|
|
||||||
return models.Filepath(filepathStr), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPIAEncryptionPreset obtains the encryption level for the PIA connection
|
|
||||||
// from the environment variable PIA_ENCRYPTION, and using ENCRYPTION for
|
|
||||||
// retro compatibility.
|
|
||||||
func (r *reader) GetPIAEncryptionPreset() (preset string, err error) {
|
|
||||||
// Retro-compatibility
|
|
||||||
s, err := r.env.Inside("ENCRYPTION", []string{
|
|
||||||
constants.PIAEncryptionPresetNormal,
|
|
||||||
constants.PIAEncryptionPresetStrong})
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
} else if len(s) != 0 {
|
|
||||||
r.logger.Warn("You are using the old environment variable ENCRYPTION, please consider changing it to PIA_ENCRYPTION")
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
return r.env.Inside(
|
|
||||||
"PIA_ENCRYPTION",
|
|
||||||
[]string{
|
|
||||||
constants.PIAEncryptionPresetNormal,
|
|
||||||
constants.PIAEncryptionPresetStrong,
|
|
||||||
},
|
|
||||||
libparams.Default(constants.PIAEncryptionPresetStrong))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPIARegions obtains the regions for the PIA servers from the
|
|
||||||
// environment variable REGION.
|
|
||||||
func (r *reader) GetPIARegions() (regions []string, err error) {
|
|
||||||
return r.env.CSVInside("REGION", constants.PIAGeoChoices())
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPIAPort obtains the port to reach the PIA server on from the
|
|
||||||
// environment variable PORT.
|
|
||||||
func (r *reader) GetPIAPort() (port uint16, err error) {
|
|
||||||
n, err := r.env.IntRange("PORT", 0, 65535, libparams.Default("0"))
|
|
||||||
return uint16(n), err
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package params
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
|
||||||
libparams "github.com/qdm12/golibs/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetPrivadoHostnames obtains the hostnames for the Privado server from the
|
|
||||||
// environment variable SERVER_HOSTNAME.
|
|
||||||
func (r *reader) GetPrivadoHostnames() (hosts []string, err error) {
|
|
||||||
return r.env.CSVInside("SERVER_HOSTNAME",
|
|
||||||
constants.PrivadoHostnameChoices(),
|
|
||||||
libparams.RetroKeys([]string{"HOSTNAME"}, r.onRetroActive))
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package params
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
libparams "github.com/qdm12/golibs/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetPublicIPPeriod obtains the period to fetch the IP address periodically.
|
|
||||||
// Set to 0 to disable.
|
|
||||||
func (r *reader) GetPublicIPPeriod() (period time.Duration, err error) {
|
|
||||||
s, err := r.env.Get("PUBLICIP_PERIOD", libparams.Default("12h"))
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return time.ParseDuration(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPublicIPFilepath obtains the public IP filepath
|
|
||||||
// from the environment variable PUBLICIP_FILE with retro-compatible
|
|
||||||
// environment variable IP_STATUS_FILE.
|
|
||||||
func (r *reader) GetPublicIPFilepath() (filepath models.Filepath, err error) {
|
|
||||||
filepathStr, err := r.env.Path("PUBLICIP_FILE",
|
|
||||||
libparams.RetroKeys([]string{"IP_STATUS_FILE"}, r.onRetroActive),
|
|
||||||
libparams.Default("/tmp/gluetun/ip"), libparams.CaseSensitiveValue())
|
|
||||||
return models.Filepath(filepathStr), err
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package params
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetPurevpnRegions obtains the regions (continents) for the PureVPN servers from the
|
|
||||||
// environment variable REGION.
|
|
||||||
func (r *reader) GetPurevpnRegions() (regions []string, err error) {
|
|
||||||
return r.env.CSVInside("REGION", constants.PurevpnRegionChoices())
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPurevpnCountries obtains the countries for the PureVPN servers from the
|
|
||||||
// environment variable COUNTRY.
|
|
||||||
func (r *reader) GetPurevpnCountries() (countries []string, err error) {
|
|
||||||
return r.env.CSVInside("COUNTRY", constants.PurevpnCountryChoices())
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPurevpnCities obtains the cities for the PureVPN servers from the
|
|
||||||
// environment variable CITY.
|
|
||||||
func (r *reader) GetPurevpnCities() (cities []string, err error) {
|
|
||||||
return r.env.CSVInside("CITY", constants.PurevpnCityChoices())
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package params
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
libparams "github.com/qdm12/golibs/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetOutboundSubnets obtains the CIDR subnets from the comma separated list of the
|
|
||||||
// environment variable FIREWALL_OUTBOUND_SUBNETS.
|
|
||||||
func (r *reader) GetOutboundSubnets() (outboundSubnets []net.IPNet, err error) {
|
|
||||||
const key = "FIREWALL_OUTBOUND_SUBNETS"
|
|
||||||
retroOption := libparams.RetroKeys(
|
|
||||||
[]string{"EXTRA_SUBNETS"},
|
|
||||||
r.onRetroActive,
|
|
||||||
)
|
|
||||||
s, err := r.env.Get(key, retroOption)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if s == "" {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
subnets := strings.Split(s, ",")
|
|
||||||
for _, subnet := range subnets {
|
|
||||||
_, cidr, err := net.ParseCIDR(subnet)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot parse outbound subnet %q from environment variable with key %s: %w", subnet, key, err)
|
|
||||||
} else if cidr == nil {
|
|
||||||
return nil, fmt.Errorf("cannot parse outbound subnet %q from environment variable with key %s: subnet is nil",
|
|
||||||
subnet, key)
|
|
||||||
}
|
|
||||||
outboundSubnets = append(outboundSubnets, *cidr)
|
|
||||||
}
|
|
||||||
return outboundSubnets, nil
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package params
|
|
||||||
|
|
||||||
import (
|
|
||||||
libparams "github.com/qdm12/golibs/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (r *reader) GetControlServerPort() (port uint16, warning string, err error) {
|
|
||||||
return r.env.ListeningPort("HTTP_CONTROL_SERVER_PORT", libparams.Default("8000"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) GetControlServerLog() (enabled bool, err error) {
|
|
||||||
return r.env.OnOff("HTTP_CONTROL_SERVER_LOG", libparams.Default("on"))
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user