Configuration package (#369)

This commit is contained in:
Quentin McGaw
2021-02-06 11:05:50 -05:00
committed by GitHub
parent 4f2570865c
commit 90aaf71270
93 changed files with 2371 additions and 2453 deletions

View File

@@ -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
} }

2
go.mod
View File

@@ -7,7 +7,7 @@ require (
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 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

2
go.sum
View File

@@ -97,6 +97,8 @@ github.com/qdm12/dns v1.4.0/go.mod h1:WUY4/U8Z2O8888DPrahrIBv8GdYeoIcEy4aUDecZ+U
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=

View File

@@ -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
} }

View File

@@ -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)")

View File

@@ -0,0 +1,3 @@
// Package configuration reads initial settings from environment variables
// and secret files.
package configuration

View File

@@ -0,0 +1,6 @@
package configuration
const (
lastIndent = "|--"
indent = " "
)

View File

@@ -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", 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
}
func readCyberghostClientCertificate(r reader) (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
}

View File

@@ -1,4 +1,4 @@
package params package configuration
import ( import (
"fmt" "fmt"

View File

@@ -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
}

View File

@@ -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)
})
}
}

View File

@@ -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.VPNInputPorts, 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
}

View File

@@ -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_USER", 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.Enabled = true
// Retro compatibility
case "info", "connect", "notice":
settings.Enabled = true
}
return nil
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -0,0 +1,139 @@
package configuration
import (
"errors"
"fmt"
"strconv"
"strings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"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",
"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 = models.VPNProvider(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("no"))
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
default:
return fmt.Errorf("%w: %s", ErrInvalidVPNProvider, settings.Provider.Name)
}
return readProvider(r)
}

View File

@@ -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",
}, },
} }

View File

@@ -0,0 +1,36 @@
package configuration
import (
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
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(), params.RetroKeys([]string{"HOSTNAME"}, r.onRetroActive))
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,79 @@
package configuration
import (
"strconv"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"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 {
filepathStr, err := r.env.Path("PORT_FORWARDING_STATUS_FILE",
params.Default("/tmp/gluetun/forwarded_port"), params.CaseSensitiveValue())
if err != nil {
return err
}
settings.PortForwarding.Filepath = models.Filepath(filepathStr)
}
return nil
}

View File

@@ -0,0 +1,112 @@
package configuration
import (
"errors"
"fmt"
"net"
"strings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/params"
)
// Provider contains settings specific to a VPN provider.
type Provider struct {
Name models.VPNProvider `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(string(settings.Name))+" settings:")
lines = append(lines, indent+lastIndent+"Network protocol: "+string(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(string(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 "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 models.NetworkProtocol, err error) {
s, err := env.Inside("PROTOCOL",
[]string{string(constants.TCP), string(constants.UDP)},
params.Default(string(constants.UDP)))
if err != nil {
return "", err
}
return models.NetworkProtocol(s), nil
}
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 models.NetworkProtocol,
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 allowedTCP[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)
}
}

View File

@@ -0,0 +1,246 @@
package configuration
import (
"errors"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"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: models.Filepath("/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",
},
},
"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 models.NetworkProtocol
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)
})
}
}

View File

@@ -0,0 +1,48 @@
package configuration
import (
"strings"
"time"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/params"
)
type PublicIP struct {
Period time.Duration `json:"period"`
IPFilepath models.Filepath `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: "+string(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
}
filepathStr, 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
}
settings.IPFilepath = models.Filepath(filepathStr)
return nil
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -0,0 +1,55 @@
package configuration
import (
"net"
"github.com/qdm12/gluetun/internal/models"
)
type ServerSelection struct {
// Common
Protocol models.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 models.Filepath `json:"filepath"`
}
func (p *PortForwarding) lines() (lines []string) {
return []string{
lastIndent + "File path: " + string(p.Filepath),
}
}

View File

@@ -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
}

View File

@@ -0,0 +1,93 @@
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 err := settings.PublicIP.read(r); err != nil {
return err
}
return nil
}

View File

@@ -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)
})
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -0,0 +1,62 @@
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"`
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.Vyprvpn = true
settings.Windscribe = true
settings.Stdout = false
settings.CLI = false
settings.DNSAddress = "127.0.0.1"
settings.Period, err = r.env.Duration("UPDATER_PERIOD", params.Default("0"))
if err != nil {
return err
}
return nil
}

View File

@@ -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
}

View File

@@ -0,0 +1,65 @@
package configuration
import (
"strconv"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
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(), params.RetroKeys([]string{"HOSTNAME"}, r.onRetroActive))
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
}

View File

@@ -9,9 +9,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 +20,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 +45,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{

View File

@@ -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 {

View File

@@ -6,9 +6,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 +16,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 +34,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,

View File

@@ -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 {

View File

@@ -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, ", ")
}

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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"))
}

View File

@@ -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"))
}

View File

@@ -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"))
}

View File

@@ -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"))
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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,
)
}

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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
}

View File

@@ -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())
}

View File

@@ -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
}

View File

@@ -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"))
}

View File

@@ -1,37 +0,0 @@
package params
import (
libparams "github.com/qdm12/golibs/params"
)
// GetShadowSocks obtains if ShadowSocks is on from the environment variable
// SHADOWSOCKS.
func (r *reader) GetShadowSocks() (activated bool, err error) {
return r.env.OnOff("SHADOWSOCKS", libparams.Default("off"))
}
// GetShadowSocksLog obtains the ShadowSocks log level from the environment variable
// SHADOWSOCKS_LOG.
func (r *reader) GetShadowSocksLog() (activated bool, err error) {
return r.env.OnOff("SHADOWSOCKS_LOG", libparams.Default("off"))
}
// GetShadowSocksPort obtains the ShadowSocks listening port from the environment variable
// SHADOWSOCKS_PORT.
func (r *reader) GetShadowSocksPort() (port uint16, warning string, err error) {
return r.env.ListeningPort("SHADOWSOCKS_PORT", libparams.Default("8388"))
}
// GetShadowSocksPassword obtains the ShadowSocks server password.
// It first tries to use the SHADOWSOCKS_PASSWORD environment variable (easier for the end user)
// and then tries to read from the secret file shadowsocks_password if nothing was found.
func (r *reader) GetShadowSocksPassword() (password string, err error) {
const compulsory = false
return r.getFromEnvOrSecretFile("SHADOWSOCKS_PASSWORD", compulsory, nil)
}
// GetShadowSocksMethod obtains the ShadowSocks method to use from the environment variable
// SHADOWSOCKS_METHOD.
func (r *reader) GetShadowSocksMethod() (method string, err error) {
return r.env.Get("SHADOWSOCKS_METHOD", libparams.Default("chacha20-ietf-poly1305"))
}

View File

@@ -1,11 +0,0 @@
package params
import (
"github.com/qdm12/gluetun/internal/constants"
)
// GetSurfsharkRegions obtains the regions for the Surfshark servers from the
// environment variable REGION.
func (r *reader) GetSurfsharkRegions() (regions []string, err error) {
return r.env.CSVInside("REGION", constants.SurfsharkRegionChoices())
}

View File

@@ -1,26 +0,0 @@
package params
import (
libparams "github.com/qdm12/golibs/params"
)
// GetPUID obtains the user ID to use from the environment variable PUID
// with retro compatible variable UID.
func (r *reader) GetPUID() (ppuid int, err error) {
return r.env.IntRange("PUID", 0, 65535,
libparams.Default("1000"),
libparams.RetroKeys([]string{"UID"}, r.onRetroActive))
}
// GetGID obtains the group ID to use from the environment variable PGID
// with retro compatible variable PGID.
func (r *reader) GetPGID() (pgid int, err error) {
return r.env.IntRange("PGID", 0, 65535,
libparams.Default("1000"),
libparams.RetroKeys([]string{"GID"}, r.onRetroActive))
}
// GetTZ obtains the timezone from the environment variable TZ.
func (r *reader) GetTimezone() (timezone string, err error) {
return r.env.Get("TZ")
}

View File

@@ -1,17 +0,0 @@
package params
import (
"time"
libparams "github.com/qdm12/golibs/params"
)
// GetUpdaterPeriod obtains the period to fetch the servers information when the tunnel is up.
// Set to 0 to disable.
func (r *reader) GetUpdaterPeriod() (period time.Duration, err error) {
s, err := r.env.Get("UPDATER_PERIOD", libparams.Default("0"))
if err != nil {
return 0, err
}
return time.ParseDuration(s)
}

View File

@@ -1,11 +0,0 @@
package params
import (
"github.com/qdm12/gluetun/internal/constants"
)
// GetVyprvpnRegions obtains the regions for the Vyprvpn servers from the
// environment variable REGION.
func (r *reader) GetVyprvpnRegions() (regions []string, err error) {
return r.env.CSVInside("REGION", constants.VyprvpnRegionChoices())
}

View File

@@ -1,58 +0,0 @@
package params
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
libparams "github.com/qdm12/golibs/params"
)
// GetWindscribeRegions obtains the regions for the Windscribe servers from the
// environment variable REGION.
func (r *reader) GetWindscribeRegions() (regions []string, err error) {
return r.env.CSVInside("REGION", constants.WindscribeRegionChoices())
}
// GetWindscribeCities obtains the cities for the Windscribe servers from the
// environment variable CITY.
func (r *reader) GetWindscribeCities() (cities []string, err error) {
return r.env.CSVInside("CITY", constants.WindscribeCityChoices())
}
// GetWindscribeHostnames obtains the hostnames for the Windscribe servers from the
// environment variable SERVER_HOSTNAME.
func (r *reader) GetWindscribeHostnames() (hostnames []string, err error) {
return r.env.CSVInside("SERVER_HOSTNAME",
constants.WindscribeHostnameChoices(),
libparams.RetroKeys([]string{"HOSTNAME"}, r.onRetroActive),
)
}
// GetWindscribePort obtains the port to reach the Windscribe server on from the
// environment variable PORT.
//nolint:gomnd
func (r *reader) GetWindscribePort(protocol models.NetworkProtocol) (port uint16, err error) {
n, err := r.env.IntRange("PORT", 0, 65535, libparams.Default("0"))
if err != nil {
return 0, err
}
if n == 0 {
return 0, nil
}
switch protocol {
case constants.TCP:
switch n {
case 21, 22, 80, 123, 143, 443, 587, 1194, 3306, 8080, 54783:
default:
return 0, fmt.Errorf("port %d is not valid for protocol %s", n, protocol)
}
case constants.UDP:
switch n {
case 53, 80, 123, 443, 1194, 54783:
default:
return 0, fmt.Errorf("port %d is not valid for protocol %s", n, protocol)
}
}
return uint16(n), nil
}

View File

@@ -9,10 +9,10 @@ import (
"strconv" "strconv"
"strings" "strings"
"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/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -41,7 +41,7 @@ func (c *cyberghost) filterServers(regions []string, group string) (servers []mo
return servers return servers
} }
func (c *cyberghost) GetOpenVPNConnection(selection models.ServerSelection) ( func (c *cyberghost) GetOpenVPNConnection(selection configuration.ServerSelection) (
connection models.OpenVPNConnection, err error) { connection models.OpenVPNConnection, err error) {
const httpsPort = 443 const httpsPort = 443
if selection.TargetIP != nil { if selection.TargetIP != nil {
@@ -65,7 +65,7 @@ func (c *cyberghost) GetOpenVPNConnection(selection models.ServerSelection) (
} }
func (c *cyberghost) BuildConf(connection models.OpenVPNConnection, func (c *cyberghost) BuildConf(connection models.OpenVPNConnection,
username string, settings settings.OpenVPN) (lines []string) { username string, settings configuration.OpenVPN) (lines []string) {
if len(settings.Cipher) == 0 { if len(settings.Cipher) == 0 {
settings.Cipher = aes256cbc settings.Cipher = aes256cbc
} }

View File

@@ -8,10 +8,10 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"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/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -43,7 +43,7 @@ func (m *mullvad) filterServers(countries, cities, isps []string, owned bool) (s
return servers return servers
} }
func (m *mullvad) GetOpenVPNConnection(selection models.ServerSelection) ( func (m *mullvad) GetOpenVPNConnection(selection configuration.ServerSelection) (
connection models.OpenVPNConnection, err error) { connection models.OpenVPNConnection, err error) {
var defaultPort uint16 = 1194 var defaultPort uint16 = 1194
if selection.Protocol == constants.TCP { if selection.Protocol == constants.TCP {
@@ -75,7 +75,7 @@ func (m *mullvad) GetOpenVPNConnection(selection models.ServerSelection) (
} }
func (m *mullvad) BuildConf(connection models.OpenVPNConnection, func (m *mullvad) BuildConf(connection models.OpenVPNConnection,
username string, settings settings.OpenVPN) (lines []string) { username string, settings configuration.OpenVPN) (lines []string) {
if len(settings.Cipher) == 0 { if len(settings.Cipher) == 0 {
settings.Cipher = aes256cbc settings.Cipher = aes256cbc
} }

View File

@@ -8,10 +8,10 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"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/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -49,7 +49,7 @@ func (n *nordvpn) filterServers(regions []string, protocol models.NetworkProtoco
return servers return servers
} }
func (n *nordvpn) GetOpenVPNConnection(selection models.ServerSelection) ( func (n *nordvpn) GetOpenVPNConnection(selection configuration.ServerSelection) (
connection models.OpenVPNConnection, err error) { connection models.OpenVPNConnection, err error) {
var port uint16 var port uint16
switch { switch {
@@ -80,7 +80,7 @@ func (n *nordvpn) GetOpenVPNConnection(selection models.ServerSelection) (
} }
func (n *nordvpn) BuildConf(connection models.OpenVPNConnection, func (n *nordvpn) BuildConf(connection models.OpenVPNConnection,
username string, settings settings.OpenVPN) (lines []string) { username string, settings configuration.OpenVPN) (lines []string) {
if len(settings.Cipher) == 0 { if len(settings.Cipher) == 0 {
settings.Cipher = aes256cbc settings.Cipher = aes256cbc
} }

View File

@@ -17,11 +17,11 @@ 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/firewall" "github.com/qdm12/gluetun/internal/firewall"
gluetunLog "github.com/qdm12/gluetun/internal/logging" gluetunLog "github.com/qdm12/gluetun/internal/logging"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -45,7 +45,7 @@ var (
ErrInvalidPort = errors.New("invalid port number") ErrInvalidPort = errors.New("invalid port number")
) )
func (p *pia) getPort(selection models.ServerSelection) (port uint16, err error) { func (p *pia) getPort(selection configuration.ServerSelection) (port uint16, err error) {
if selection.CustomPort == 0 { if selection.CustomPort == 0 {
switch selection.Protocol { switch selection.Protocol {
case constants.TCP: case constants.TCP:
@@ -93,7 +93,7 @@ func (p *pia) getPort(selection models.ServerSelection) (port uint16, err error)
return port, nil return port, nil
} }
func (p *pia) GetOpenVPNConnection(selection models.ServerSelection) ( func (p *pia) GetOpenVPNConnection(selection configuration.ServerSelection) (
connection models.OpenVPNConnection, err error) { connection models.OpenVPNConnection, err error) {
port, err := p.getPort(selection) port, err := p.getPort(selection)
if err != nil { if err != nil {
@@ -131,7 +131,7 @@ func (p *pia) GetOpenVPNConnection(selection models.ServerSelection) (
} }
func (p *pia) BuildConf(connection models.OpenVPNConnection, func (p *pia) BuildConf(connection models.OpenVPNConnection,
username string, settings settings.OpenVPN) (lines []string) { username string, settings configuration.OpenVPN) (lines []string) {
var X509CRL, certificate string var X509CRL, certificate string
var defaultCipher, defaultAuth string var defaultCipher, defaultAuth string
if settings.Provider.ExtraConfigOptions.EncryptionPreset == constants.PIAEncryptionPresetNormal { if settings.Provider.ExtraConfigOptions.EncryptionPreset == constants.PIAEncryptionPresetNormal {

View File

@@ -8,10 +8,10 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"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/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -39,7 +39,7 @@ func (s *privado) filterServers(hostnames []string) (servers []models.PrivadoSer
return servers return servers
} }
func (s *privado) GetOpenVPNConnection(selection models.ServerSelection) ( func (s *privado) GetOpenVPNConnection(selection configuration.ServerSelection) (
connection models.OpenVPNConnection, err error) { connection models.OpenVPNConnection, err error) {
var port uint16 = 1194 var port uint16 = 1194
switch selection.Protocol { switch selection.Protocol {
@@ -73,7 +73,7 @@ func (s *privado) GetOpenVPNConnection(selection models.ServerSelection) (
} }
func (s *privado) BuildConf(connection models.OpenVPNConnection, func (s *privado) BuildConf(connection models.OpenVPNConnection,
username string, settings settings.OpenVPN) (lines []string) { username string, settings configuration.OpenVPN) (lines []string) {
if len(settings.Cipher) == 0 { if len(settings.Cipher) == 0 {
settings.Cipher = aes256cbc settings.Cipher = aes256cbc
} }

View File

@@ -5,18 +5,18 @@ import (
"net" "net"
"net/http" "net/http"
"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/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
// Provider contains methods to read and modify the openvpn configuration to connect as a client. // Provider contains methods to read and modify the openvpn configuration to connect as a client.
type Provider interface { type Provider interface {
GetOpenVPNConnection(selection models.ServerSelection) (connection models.OpenVPNConnection, err error) GetOpenVPNConnection(selection configuration.ServerSelection) (connection models.OpenVPNConnection, err error)
BuildConf(connection models.OpenVPNConnection, username string, settings settings.OpenVPN) (lines []string) BuildConf(connection models.OpenVPNConnection, username string, settings configuration.OpenVPN) (lines []string)
PortForward(ctx context.Context, client *http.Client, PortForward(ctx context.Context, client *http.Client,
openFile os.OpenFileFunc, pfLogger logging.Logger, gateway net.IP, fw firewall.Configurator, openFile os.OpenFileFunc, pfLogger logging.Logger, gateway net.IP, fw firewall.Configurator,
syncState func(port uint16) (pfFilepath models.Filepath)) syncState func(port uint16) (pfFilepath models.Filepath))

View File

@@ -8,10 +8,10 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"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/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -42,7 +42,7 @@ func (p *purevpn) filterServers(regions, countries, cities []string) (servers []
return servers return servers
} }
func (p *purevpn) GetOpenVPNConnection(selection models.ServerSelection) ( func (p *purevpn) GetOpenVPNConnection(selection configuration.ServerSelection) (
connection models.OpenVPNConnection, err error) { connection models.OpenVPNConnection, err error) {
var port uint16 var port uint16
switch { switch {
@@ -75,7 +75,7 @@ func (p *purevpn) GetOpenVPNConnection(selection models.ServerSelection) (
} }
func (p *purevpn) BuildConf(connection models.OpenVPNConnection, func (p *purevpn) BuildConf(connection models.OpenVPNConnection,
username string, settings settings.OpenVPN) (lines []string) { username string, settings configuration.OpenVPN) (lines []string) {
if len(settings.Cipher) == 0 { if len(settings.Cipher) == 0 {
settings.Cipher = aes256cbc settings.Cipher = aes256cbc
} }

View File

@@ -8,10 +8,10 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"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/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -40,7 +40,7 @@ func (s *surfshark) filterServers(regions []string) (servers []models.SurfsharkS
return servers return servers
} }
func (s *surfshark) GetOpenVPNConnection(selection models.ServerSelection) ( func (s *surfshark) GetOpenVPNConnection(selection configuration.ServerSelection) (
connection models.OpenVPNConnection, err error) { connection models.OpenVPNConnection, err error) {
var port uint16 var port uint16
switch { switch {
@@ -76,7 +76,7 @@ func (s *surfshark) GetOpenVPNConnection(selection models.ServerSelection) (
} }
func (s *surfshark) BuildConf(connection models.OpenVPNConnection, func (s *surfshark) BuildConf(connection models.OpenVPNConnection,
username string, settings settings.OpenVPN) (lines []string) { username string, settings configuration.OpenVPN) (lines []string) {
if len(settings.Cipher) == 0 { if len(settings.Cipher) == 0 {
settings.Cipher = aes256cbc settings.Cipher = aes256cbc
} }

View File

@@ -8,10 +8,10 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"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/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -40,7 +40,7 @@ func (v *vyprvpn) filterServers(regions []string) (servers []models.VyprvpnServe
return servers return servers
} }
func (v *vyprvpn) GetOpenVPNConnection(selection models.ServerSelection) ( func (v *vyprvpn) GetOpenVPNConnection(selection configuration.ServerSelection) (
connection models.OpenVPNConnection, err error) { connection models.OpenVPNConnection, err error) {
var port uint16 var port uint16
switch { switch {
@@ -72,7 +72,7 @@ func (v *vyprvpn) GetOpenVPNConnection(selection models.ServerSelection) (
} }
func (v *vyprvpn) BuildConf(connection models.OpenVPNConnection, func (v *vyprvpn) BuildConf(connection models.OpenVPNConnection,
username string, settings settings.OpenVPN) (lines []string) { username string, settings configuration.OpenVPN) (lines []string) {
if len(settings.Cipher) == 0 { if len(settings.Cipher) == 0 {
settings.Cipher = aes256cbc settings.Cipher = aes256cbc
} }

View File

@@ -9,10 +9,10 @@ import (
"strconv" "strconv"
"strings" "strings"
"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/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -44,7 +44,7 @@ func (w *windscribe) filterServers(regions, cities, hostnames []string) (servers
} }
//nolint:lll //nolint:lll
func (w *windscribe) GetOpenVPNConnection(selection models.ServerSelection) (connection models.OpenVPNConnection, err error) { func (w *windscribe) GetOpenVPNConnection(selection configuration.ServerSelection) (connection models.OpenVPNConnection, err error) {
var port uint16 var port uint16
switch { switch {
case selection.CustomPort > 0: case selection.CustomPort > 0:
@@ -75,7 +75,7 @@ func (w *windscribe) GetOpenVPNConnection(selection models.ServerSelection) (con
} }
func (w *windscribe) BuildConf(connection models.OpenVPNConnection, func (w *windscribe) BuildConf(connection models.OpenVPNConnection,
username string, settings settings.OpenVPN) (lines []string) { username string, settings configuration.OpenVPN) (lines []string) {
if len(settings.Cipher) == 0 { if len(settings.Cipher) == 0 {
settings.Cipher = aes256cbc settings.Cipher = aes256cbc
} }

View File

@@ -7,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"
"github.com/qdm12/golibs/os" "github.com/qdm12/golibs/os"
) )
@@ -19,8 +19,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.PublicIP) GetSettings() (settings configuration.PublicIP)
SetSettings(settings settings.PublicIP) (outcome string) SetSettings(settings configuration.PublicIP) (outcome string)
GetPublicIP() (publicIP net.IP) GetPublicIP() (publicIP net.IP)
} }
@@ -49,7 +49,7 @@ type looper struct {
const defaultBackoffTime = 5 * time.Second const defaultBackoffTime = 5 * time.Second
func NewLooper(client *http.Client, logger logging.Logger, func NewLooper(client *http.Client, logger logging.Logger,
settings settings.PublicIP, puid, pgid int, settings configuration.PublicIP, puid, pgid int,
os os.OS) Looper { os os.OS) Looper {
return &looper{ return &looper{
state: state{ state: state{

View File

@@ -6,14 +6,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.PublicIP settings configuration.PublicIP
ip net.IP ip net.IP
statusMu sync.RWMutex statusMu sync.RWMutex
settingsMu sync.RWMutex settingsMu sync.RWMutex
@@ -72,13 +72,13 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error)
} }
} }
func (l *looper) GetSettings() (settings settings.PublicIP) { func (l *looper) GetSettings() (settings configuration.PublicIP) {
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.PublicIP) (outcome string) { func (l *looper) SetSettings(settings configuration.PublicIP) (outcome string) {
l.state.settingsMu.Lock() l.state.settingsMu.Lock()
defer l.state.settingsMu.Unlock() defer l.state.settingsMu.Unlock()
settingsUnchanged := reflect.DeepEqual(settings, l.state.settings) settingsUnchanged := reflect.DeepEqual(settings, l.state.settings)

View File

@@ -1,191 +0,0 @@
package settings
import (
"fmt"
"net"
"strings"
"time"
unboundmodels "github.com/qdm12/dns/pkg/models"
unbound "github.com/qdm12/dns/pkg/unbound"
"github.com/qdm12/gluetun/internal/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 (d *DNS) String() string {
return strings.Join(d.lines(), "\n")
}
const (
subIndent = " |--"
indent = " " // used if lines already contain the subIndent
)
func (d *DNS) lines() (lines []string) {
lines = append(lines, subIndent+"DNS:")
if d.PlaintextAddress != nil {
lines = append(lines, indent+subIndent+"Plaintext address: "+d.PlaintextAddress.String())
}
keepNameserver := "no"
if d.KeepNameserver {
keepNameserver = "yes"
}
lines = append(lines,
indent+subIndent+"Keep nameserver (disabled blocking): "+keepNameserver)
if !d.Enabled {
lines = append(lines, indent+subIndent+"DNS over TLS: disabled")
return lines
}
lines = append(lines, indent+subIndent+"DNS over TLS:")
lines = append(lines, indent+indent+subIndent+"Unbound:")
for _, line := range d.Unbound.Lines() {
lines = append(lines, indent+indent+indent+line)
}
blockMalicious := disabled
if d.BlockMalicious {
blockMalicious = enabled
}
lines = append(lines, indent+indent+subIndent+"Block malicious: "+blockMalicious)
blockAds := disabled
if d.BlockAds {
blockAds = enabled
}
lines = append(lines, indent+indent+subIndent+"Block ads: "+blockAds)
blockSurveillance := disabled
if d.BlockSurveillance {
blockSurveillance = enabled
}
lines = append(lines, indent+indent+subIndent+"Block surveillance: "+blockSurveillance)
update := "deactivated"
if d.UpdatePeriod > 0 {
update = "every " + d.UpdatePeriod.String()
}
lines = append(lines, indent+indent+subIndent+"Update: "+update)
return lines
}
// GetDNSSettings obtains DNS over TLS settings from environment variables using the params package.
func GetDNSSettings(paramsReader params.Reader) (settings DNS, err error) {
settings.Enabled, err = paramsReader.GetDNSOverTLS()
if err != nil {
return settings, err
}
// Plain DNS settings
settings.PlaintextAddress, err = paramsReader.GetDNSPlaintext()
if err != nil {
return settings, err
}
settings.KeepNameserver, err = paramsReader.GetDNSKeepNameserver()
if err != nil {
return settings, err
}
// DNS over TLS external settings
settings.BlockMalicious, err = paramsReader.GetDNSMaliciousBlocking()
if err != nil {
return settings, err
}
settings.BlockSurveillance, err = paramsReader.GetDNSSurveillanceBlocking()
if err != nil {
return settings, err
}
settings.BlockAds, err = paramsReader.GetDNSAdsBlocking()
if err != nil {
return settings, err
}
settings.UpdatePeriod, err = paramsReader.GetDNSUpdatePeriod()
if err != nil {
return settings, err
}
// Unbound specific settings
settings.Unbound, err = getUnboundSettings(paramsReader)
if err != nil {
return settings, err
}
// Consistency check
IPv6Support := false
for _, provider := range settings.Unbound.Providers {
providerData, ok := unbound.GetProviderData(provider)
switch {
case !ok:
return settings, fmt.Errorf("DNS provider %q does not have associated data", provider)
case !providerData.SupportsTLS:
return settings, fmt.Errorf("DNS provider %q does not support DNS over TLS", provider)
case providerData.SupportsIPv6:
IPv6Support = true
}
}
if settings.Unbound.IPv6 && !IPv6Support {
return settings, fmt.Errorf("None of the DNS over TLS provider(s) set support IPv6")
}
return settings, nil
}
func getUnboundSettings(reader params.Reader) (settings unboundmodels.Settings, err error) {
settings.Providers, err = reader.GetDNSOverTLSProviders()
if err != nil {
return settings, err
}
settings.ListeningPort = 53
settings.Caching, err = reader.GetDNSOverTLSCaching()
if err != nil {
return settings, err
}
settings.IPv4 = true
settings.IPv6, err = reader.GetDNSOverTLSIPv6()
if err != nil {
return settings, err
}
settings.VerbosityLevel, err = reader.GetDNSOverTLSVerbosity()
if err != nil {
return settings, err
}
settings.VerbosityDetailsLevel, err = reader.GetDNSOverTLSVerbosityDetails()
if err != nil {
return settings, err
}
settings.ValidationLogLevel, err = reader.GetDNSOverTLSValidationLogLevel()
if err != nil {
return settings, err
}
settings.BlockedHostnames = []string{}
settings.BlockedIPs, err = reader.GetDNSOverTLSPrivateAddresses()
if err != nil {
return settings, err
}
settings.AllowedHostnames, err = reader.GetDNSUnblockedHostnames()
if err != nil {
return settings, err
}
settings.AccessControl.Allowed = []net.IPNet{
{
IP: net.IPv4zero,
Mask: net.IPv4Mask(0, 0, 0, 0),
},
{
IP: net.IPv6zero,
Mask: net.IPMask{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
},
}
return settings, nil
}

View File

@@ -1,75 +0,0 @@
package settings
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",
" |--Keep nameserver (disabled blocking): no",
" |--DNS over TLS: disabled",
},
},
"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)
})
}
}

View File

@@ -1,72 +0,0 @@
package settings
import (
"fmt"
"net"
"strings"
"github.com/qdm12/gluetun/internal/params"
)
// Firewall contains settings to customize the firewall operation.
type Firewall struct {
VPNInputPorts []uint16
InputPorts []uint16
OutboundSubnets []net.IPNet
Enabled bool
Debug bool
}
func (f *Firewall) String() string {
if !f.Enabled {
return "Firewall settings: disabled"
}
vpnInputPorts := make([]string, len(f.VPNInputPorts))
for i, port := range f.VPNInputPorts {
vpnInputPorts[i] = fmt.Sprintf("%d", port)
}
inputPorts := make([]string, len(f.InputPorts))
for i, port := range f.InputPorts {
inputPorts[i] = fmt.Sprintf("%d", port)
}
outboundSubnets := make([]string, len(f.OutboundSubnets))
for i := range f.OutboundSubnets {
outboundSubnets[i] = f.OutboundSubnets[i].String()
}
settingsList := []string{
"Firewall settings:",
"VPN input ports: " + strings.Join(vpnInputPorts, ", "),
"Input ports: " + strings.Join(inputPorts, ", "),
"Outbound subnets: " + strings.Join(outboundSubnets, ", "),
}
if f.Debug {
settingsList = append(settingsList, "Debug: on")
}
return strings.Join(settingsList, "\n |--")
}
// GetFirewallSettings obtains firewall settings from environment variables using the params package.
func GetFirewallSettings(paramsReader params.Reader) (settings Firewall, err error) {
settings.VPNInputPorts, err = paramsReader.GetVPNInputPorts()
if err != nil {
return settings, err
}
settings.InputPorts, err = paramsReader.GetInputPorts()
if err != nil {
return settings, err
}
settings.OutboundSubnets, err = paramsReader.GetOutboundSubnets()
if err != nil {
return settings, err
}
settings.Enabled, err = paramsReader.GetFirewall()
if err != nil {
return settings, err
}
settings.Debug, err = paramsReader.GetFirewallDebug()
if err != nil {
return settings, err
}
return settings, nil
}

View File

@@ -1,71 +0,0 @@
package settings
import (
"fmt"
"strings"
"github.com/qdm12/gluetun/internal/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 (h *HTTPProxy) String() string {
if !h.Enabled {
return "HTTP Proxy settings: disabled"
}
auth, log, stealth := disabled, disabled, disabled
if h.User != "" {
auth = enabled
}
if h.Log {
log = enabled
}
if h.Stealth {
stealth = enabled
}
settingsList := []string{
"HTTP proxy settings:",
fmt.Sprintf("Port: %d", h.Port),
"Authentication: " + auth,
"Stealth: " + stealth,
"Log: " + log,
}
return strings.Join(settingsList, "\n |--")
}
// GetHTTPProxySettings obtains HTTPProxy settings from environment variables using the params package.
func GetHTTPProxySettings(paramsReader params.Reader) (settings HTTPProxy, warning string, err error) {
settings.Enabled, err = paramsReader.GetHTTPProxy()
if err != nil || !settings.Enabled {
return settings, "", err
}
settings.User, err = paramsReader.GetHTTPProxyUser()
if err != nil {
return settings, "", err
}
settings.Password, err = paramsReader.GetHTTPProxyPassword()
if err != nil {
return settings, "", err
}
settings.Stealth, err = paramsReader.GetHTTPProxyStealth()
if err != nil {
return settings, "", err
}
settings.Log, err = paramsReader.GetHTTPProxyLog()
if err != nil {
return settings, "", err
}
settings.Port, warning, err = paramsReader.GetHTTPProxyPort()
if err != nil {
return settings, warning, err
}
return settings, warning, nil
}

View File

@@ -1,105 +0,0 @@
package settings
import (
"fmt"
"strings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/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 models.ProviderSettings `json:"provider"`
}
// GetOpenVPNSettings obtains the OpenVPN settings using the params functions.
func GetOpenVPNSettings(paramsReader params.Reader, vpnProvider models.VPNProvider) (settings OpenVPN, err error) {
settings.User, err = paramsReader.GetUser()
if err != nil {
return settings, err
}
// Remove spaces in user ID to simplify user's life, thanks @JeordyR
settings.User = strings.ReplaceAll(settings.User, " ", "")
if vpnProvider == constants.Mullvad {
settings.Password = "m"
} else {
settings.Password, err = paramsReader.GetPassword()
if err != nil {
return settings, err
}
}
settings.Verbosity, err = paramsReader.GetOpenVPNVerbosity()
if err != nil {
return settings, err
}
settings.Root, err = paramsReader.GetOpenVPNRoot()
if err != nil {
return settings, err
}
settings.Cipher, err = paramsReader.GetOpenVPNCipher()
if err != nil {
return settings, err
}
settings.Auth, err = paramsReader.GetOpenVPNAuth()
if err != nil {
return settings, err
}
settings.MSSFix, err = paramsReader.GetOpenVPNMSSFix()
if err != nil {
return settings, err
}
switch vpnProvider {
case constants.PrivateInternetAccess:
settings.Provider, err = GetPIASettings(paramsReader)
case constants.Mullvad:
settings.Provider, err = GetMullvadSettings(paramsReader)
case constants.Windscribe:
settings.Provider, err = GetWindscribeSettings(paramsReader)
case constants.Surfshark:
settings.Provider, err = GetSurfsharkSettings(paramsReader)
case constants.Cyberghost:
settings.Provider, err = GetCyberghostSettings(paramsReader)
case constants.Vyprvpn:
settings.Provider, err = GetVyprvpnSettings(paramsReader)
case constants.Nordvpn:
settings.Provider, err = GetNordvpnSettings(paramsReader)
case constants.Purevpn:
settings.Provider, err = GetPurevpnSettings(paramsReader)
case constants.Privado:
settings.Provider, err = GetPrivadoSettings(paramsReader)
default:
err = fmt.Errorf("VPN service provider %q is not valid", vpnProvider)
}
return settings, err
}
func (o *OpenVPN) String() string {
runAsRoot := "no"
if o.Root {
runAsRoot = "yes"
}
settingsList := []string{
"OpenVPN settings:",
"User: [redacted]",
"Password: [redacted]",
"Verbosity level: " + fmt.Sprintf("%d", o.Verbosity),
"Run as root: " + runAsRoot,
o.Provider.String(),
}
if len(o.Cipher) > 0 {
settingsList = append(settingsList, "Custom cipher: "+o.Cipher)
}
if len(o.Auth) > 0 {
settingsList = append(settingsList, "Custom auth algorithm: "+o.Auth)
}
return strings.Join(settingsList, "\n|--")
}

View File

@@ -1,260 +0,0 @@
package settings
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/params"
)
// GetPIASettings obtains PIA settings from environment variables using the params package.
func GetPIASettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
settings.Name = constants.PrivateInternetAccess
settings.ServerSelection.Protocol, err = paramsReader.GetNetworkProtocol()
if err != nil {
return settings, err
}
settings.ServerSelection.TargetIP, err = paramsReader.GetTargetIP()
if err != nil {
return settings, err
}
encryptionPreset, err := paramsReader.GetPIAEncryptionPreset()
if err != nil {
return settings, err
}
settings.ServerSelection.EncryptionPreset = encryptionPreset
settings.ExtraConfigOptions.EncryptionPreset = encryptionPreset
settings.ServerSelection.Regions, err = paramsReader.GetPIARegions()
if err != nil {
return settings, err
}
settings.ServerSelection.CustomPort, err = paramsReader.GetPIAPort()
if err != nil {
return settings, err
}
settings.PortForwarding.Enabled, err = paramsReader.GetPortForwarding()
if err != nil {
return settings, err
}
if settings.PortForwarding.Enabled {
settings.PortForwarding.Filepath, err = paramsReader.GetPortForwardingStatusFilepath()
if err != nil {
return settings, err
}
}
return settings, nil
}
// GetMullvadSettings obtains Mullvad settings from environment variables using the params package.
func GetMullvadSettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
settings.Name = constants.Mullvad
settings.ServerSelection.Protocol, err = paramsReader.GetNetworkProtocol()
if err != nil {
return settings, err
}
settings.ServerSelection.TargetIP, err = paramsReader.GetTargetIP()
if err != nil {
return settings, err
}
settings.ServerSelection.Countries, err = paramsReader.GetMullvadCountries()
if err != nil {
return settings, err
}
settings.ServerSelection.Cities, err = paramsReader.GetMullvadCities()
if err != nil {
return settings, err
}
settings.ServerSelection.ISPs, err = paramsReader.GetMullvadISPs()
if err != nil {
return settings, err
}
settings.ServerSelection.CustomPort, err = paramsReader.GetMullvadPort()
if err != nil {
return settings, err
}
if settings.ServerSelection.Protocol == constants.TCP {
switch settings.ServerSelection.CustomPort {
case 0, 80, 443, 1401: //nolint:gomnd
default:
return settings, fmt.Errorf("port %d is not valid for TCP protocol", settings.ServerSelection.CustomPort)
}
} else {
switch settings.ServerSelection.CustomPort {
case 0, 53, 1194, 1195, 1196, 1197, 1300, 1301, 1302, 1303, 1400: //nolint:gomnd
default:
return settings, fmt.Errorf("port %d is not valid for UDP protocol", settings.ServerSelection.CustomPort)
}
}
settings.ServerSelection.Owned, err = paramsReader.GetMullvadOwned()
if err != nil {
return settings, err
}
settings.ExtraConfigOptions.OpenVPNIPv6, err = paramsReader.GetOpenVPNIPv6()
if err != nil {
return settings, err
}
return settings, nil
}
// GetWindscribeSettings obtains Windscribe settings from environment variables using the params package.
func GetWindscribeSettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
settings.Name = constants.Windscribe
settings.ServerSelection.Protocol, err = paramsReader.GetNetworkProtocol()
if err != nil {
return settings, err
}
settings.ServerSelection.TargetIP, err = paramsReader.GetTargetIP()
if err != nil {
return settings, err
}
settings.ServerSelection.Regions, err = paramsReader.GetWindscribeRegions()
if err != nil {
return settings, err
}
settings.ServerSelection.Cities, err = paramsReader.GetWindscribeCities()
if err != nil {
return settings, err
}
settings.ServerSelection.Hostnames, err = paramsReader.GetWindscribeHostnames()
if err != nil {
return settings, err
}
settings.ServerSelection.CustomPort, err = paramsReader.GetWindscribePort(settings.ServerSelection.Protocol)
if err != nil {
return settings, err
}
return settings, nil
}
// GetSurfsharkSettings obtains Surfshark settings from environment variables using the params package.
func GetSurfsharkSettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
settings.Name = constants.Surfshark
settings.ServerSelection.Protocol, err = paramsReader.GetNetworkProtocol()
if err != nil {
return settings, err
}
settings.ServerSelection.TargetIP, err = paramsReader.GetTargetIP()
if err != nil {
return settings, err
}
settings.ServerSelection.Regions, err = paramsReader.GetSurfsharkRegions()
if err != nil {
return settings, err
}
return settings, nil
}
// GetCyberghostSettings obtains Cyberghost settings from environment variables using the params package.
func GetCyberghostSettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
settings.Name = constants.Cyberghost
settings.ServerSelection.Protocol, err = paramsReader.GetNetworkProtocol()
if err != nil {
return settings, err
}
settings.ServerSelection.TargetIP, err = paramsReader.GetTargetIP()
if err != nil {
return settings, err
}
settings.ExtraConfigOptions.ClientKey, err = paramsReader.GetCyberghostClientKey()
if err != nil {
return settings, err
}
settings.ExtraConfigOptions.ClientCertificate, err = paramsReader.GetCyberghostClientCertificate()
if err != nil {
return settings, err
}
settings.ServerSelection.Group, err = paramsReader.GetCyberghostGroup()
if err != nil {
return settings, err
}
settings.ServerSelection.Regions, err = paramsReader.GetCyberghostRegions()
if err != nil {
return settings, err
}
return settings, nil
}
// GetVyprvpnSettings obtains Vyprvpn settings from environment variables using the params package.
func GetVyprvpnSettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
settings.Name = constants.Vyprvpn
settings.ServerSelection.Protocol, err = paramsReader.GetNetworkProtocol()
if err != nil {
return settings, err
}
settings.ServerSelection.TargetIP, err = paramsReader.GetTargetIP()
if err != nil {
return settings, err
}
settings.ServerSelection.Regions, err = paramsReader.GetVyprvpnRegions()
if err != nil {
return settings, err
}
return settings, nil
}
// GetNordvpnSettings obtains NordVPN settings from environment variables using the params package.
func GetNordvpnSettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
settings.Name = constants.Nordvpn
settings.ServerSelection.Protocol, err = paramsReader.GetNetworkProtocol()
if err != nil {
return settings, err
}
settings.ServerSelection.TargetIP, err = paramsReader.GetTargetIP()
if err != nil {
return settings, err
}
settings.ServerSelection.Regions, err = paramsReader.GetNordvpnRegions()
if err != nil {
return settings, err
}
settings.ServerSelection.Numbers, err = paramsReader.GetNordvpnNumbers()
if err != nil {
return settings, err
}
return settings, nil
}
// GetPurevpnSettings obtains Purevpn settings from environment variables using the params package.
func GetPurevpnSettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
settings.Name = constants.Purevpn
settings.ServerSelection.Protocol, err = paramsReader.GetNetworkProtocol()
if err != nil {
return settings, err
}
settings.ServerSelection.TargetIP, err = paramsReader.GetTargetIP()
if err != nil {
return settings, err
}
settings.ServerSelection.Regions, err = paramsReader.GetPurevpnRegions()
if err != nil {
return settings, err
}
settings.ServerSelection.Countries, err = paramsReader.GetPurevpnCountries()
if err != nil {
return settings, err
}
settings.ServerSelection.Cities, err = paramsReader.GetPurevpnCities()
if err != nil {
return settings, err
}
return settings, nil
}
// GetPrivadoSettings obtains Privado settings from environment variables using the params package.
func GetPrivadoSettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
settings.Name = constants.Privado
settings.ServerSelection.Protocol, err = paramsReader.GetNetworkProtocol()
if err != nil {
return settings, err
}
settings.ServerSelection.TargetIP, err = paramsReader.GetTargetIP()
if err != nil {
return settings, err
}
settings.ServerSelection.Hostnames, err = paramsReader.GetPrivadoHostnames()
if err != nil {
return settings, err
}
return settings, nil
}

View File

@@ -1,39 +0,0 @@
package settings
import (
"fmt"
"strings"
"time"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/params"
)
type PublicIP struct {
Period time.Duration `json:"period"`
IPFilepath models.Filepath `json:"ip_filepath"`
}
func getPublicIPSettings(paramsReader params.Reader) (settings PublicIP, err error) {
settings.Period, err = paramsReader.GetPublicIPPeriod()
if err != nil {
return settings, err
}
settings.IPFilepath, err = paramsReader.GetPublicIPFilepath()
if err != nil {
return settings, err
}
return settings, nil
}
func (s *PublicIP) String() string {
if s.Period == 0 {
return "Public IP getter settings: disabled"
}
settingsList := []string{
"Public IP getter settings:",
fmt.Sprintf("Period: %s", s.Period),
fmt.Sprintf("IP file: %s", s.IPFilepath),
}
return strings.Join(settingsList, "\n|--")
}

View File

@@ -1,37 +0,0 @@
package settings
import (
"fmt"
"strings"
"github.com/qdm12/gluetun/internal/params"
)
// ControlServer contains settings to customize the control server operation.
type ControlServer struct {
Port uint16
Log bool
}
func (c *ControlServer) String() string {
settingsList := []string{
"HTTP Control server:",
fmt.Sprintf("Listening port: %d", c.Port),
fmt.Sprintf("Logging: %t", c.Log),
}
return strings.Join(settingsList, "\n |--")
}
// GetControlServerSettings obtains the HTTP control server settings from
// environment variables using the params package.
func GetControlServerSettings(paramsReader params.Reader) (settings ControlServer, warning string, err error) {
settings.Log, err = paramsReader.GetControlServerLog()
if err != nil {
return settings, "", err
}
settings.Port, warning, err = paramsReader.GetControlServerPort()
if err != nil {
return settings, warning, err
}
return settings, warning, nil
}

View File

@@ -1,113 +0,0 @@
package settings
import (
"strings"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/params"
)
const (
enabled = "enabled"
disabled = "disabled"
)
// Settings contains all settings for the program to run.
type Settings struct {
VPNSP models.VPNProvider
OpenVPN OpenVPN
System System
DNS DNS
Firewall Firewall
HTTPProxy HTTPProxy
ShadowSocks ShadowSocks
Updater Updater
PublicIP PublicIP
VersionInformation bool
ControlServer ControlServer
}
func (s *Settings) String() string {
versionInformation := disabled
if s.VersionInformation {
versionInformation = enabled
}
return strings.Join([]string{
"Settings summary below:",
s.OpenVPN.String(),
s.System.String(),
s.DNS.String(),
s.Firewall.String(),
s.HTTPProxy.String(),
s.ShadowSocks.String(),
s.ControlServer.String(),
s.Updater.String(),
s.PublicIP.String(),
"Version information: " + versionInformation,
"", // new line at the end
}, "\n")
}
// GetAllSettings obtains all settings for the program and returns an error as soon
// as an error is encountered reading them.
func GetAllSettings(paramsReader params.Reader) (settings Settings, warnings []string, err error) {
settings.VPNSP, err = paramsReader.GetVPNSP()
if err != nil {
return settings, nil, err
}
settings.OpenVPN, err = GetOpenVPNSettings(paramsReader, settings.VPNSP)
if err != nil {
return settings, nil, err
}
settings.DNS, err = GetDNSSettings(paramsReader)
if err != nil {
return settings, nil, err
}
settings.Firewall, err = GetFirewallSettings(paramsReader)
if err != nil {
return settings, nil, err
}
settings.System, err = GetSystemSettings(paramsReader)
if err != nil {
return settings, nil, err
}
settings.PublicIP, err = getPublicIPSettings(paramsReader)
if err != nil {
return settings, nil, err
}
settings.VersionInformation, err = paramsReader.GetVersionInformation()
if err != nil {
return settings, nil, err
}
settings.Updater, err = GetUpdaterSettings(paramsReader)
if err != nil {
return settings, nil, err
}
var warning string
settings.HTTPProxy, warning, err = GetHTTPProxySettings(paramsReader)
if warning != "" {
warnings = append(warnings, warning)
}
if err != nil {
return settings, warnings, err
}
settings.ShadowSocks, warning, err = GetShadowSocksSettings(paramsReader)
if warning != "" {
warnings = append(warnings, warning)
}
if err != nil {
return settings, warnings, err
}
settings.ControlServer, warning, err = GetControlServerSettings(paramsReader)
if warning != "" {
warnings = append(warnings, warning)
}
if err != nil {
return settings, warnings, err
}
return settings, warnings, nil
}

View File

@@ -1,60 +0,0 @@
package settings
import (
"fmt"
"strings"
"github.com/qdm12/gluetun/internal/params"
)
// ShadowSocks contains settings to configure the Shadowsocks server.
type ShadowSocks struct {
Method string
Password string
Port uint16
Enabled bool
Log bool
}
func (s *ShadowSocks) String() string {
if !s.Enabled {
return "ShadowSocks settings: disabled"
}
log := disabled
if s.Log {
log = enabled
}
settingsList := []string{
"ShadowSocks settings:",
"Password: [redacted]",
"Log: " + log,
fmt.Sprintf("Port: %d", s.Port),
"Method: " + s.Method,
}
return strings.Join(settingsList, "\n |--")
}
// GetShadowSocksSettings obtains ShadowSocks settings from environment variables using the params package.
func GetShadowSocksSettings(paramsReader params.Reader) (settings ShadowSocks, warning string, err error) {
settings.Enabled, err = paramsReader.GetShadowSocks()
if err != nil || !settings.Enabled {
return settings, "", err
}
settings.Password, err = paramsReader.GetShadowSocksPassword()
if err != nil {
return settings, "", err
}
settings.Log, err = paramsReader.GetShadowSocksLog()
if err != nil {
return settings, "", err
}
settings.Method, err = paramsReader.GetShadowSocksMethod()
if err != nil {
return settings, "", err
}
settings.Port, warning, err = paramsReader.GetShadowSocksPort()
if err != nil {
return settings, warning, err
}
return settings, warning, nil
}

View File

@@ -1,42 +0,0 @@
package settings
import (
"fmt"
"strings"
"github.com/qdm12/gluetun/internal/params"
)
// System contains settings to configure system related elements.
type System struct {
PUID int
PGID int
Timezone string
}
// GetSystemSettings obtains the System settings using the params functions.
func GetSystemSettings(paramsReader params.Reader) (settings System, err error) {
settings.PUID, err = paramsReader.GetPUID()
if err != nil {
return settings, err
}
settings.PGID, err = paramsReader.GetPGID()
if err != nil {
return settings, err
}
settings.Timezone, err = paramsReader.GetTimezone()
if err != nil {
return settings, err
}
return settings, nil
}
func (s *System) String() string {
settingsList := []string{
"System settings:",
fmt.Sprintf("Process user ID: %d", s.PUID),
fmt.Sprintf("Process group ID: %d", s.PGID),
fmt.Sprintf("Timezone: %s", s.Timezone),
}
return strings.Join(settingsList, "\n|--")
}

View File

@@ -1,59 +0,0 @@
package settings
import (
"fmt"
"strings"
"time"
"github.com/qdm12/gluetun/internal/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"`
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:"-"`
}
// GetUpdaterSettings obtains the server updater settings using the params functions.
func GetUpdaterSettings(paramsReader params.Reader) (settings Updater, err error) {
settings = Updater{
Cyberghost: true,
Mullvad: true,
Nordvpn: true,
PIA: true,
Purevpn: true,
Surfshark: true,
Vyprvpn: true,
Windscribe: true,
Stdout: false,
CLI: false,
DNSAddress: "127.0.0.1",
}
settings.Period, err = paramsReader.GetUpdaterPeriod()
if err != nil {
return settings, err
}
return settings, nil
}
func (s *Updater) String() string {
if s.Period == 0 {
return "Server updater settings: disabled"
}
settingsList := []string{
"Server updater settings:",
fmt.Sprintf("Period: %s", s.Period),
}
return strings.Join(settingsList, "\n|--")
}

View File

@@ -6,9 +6,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"
shadowsockslib "github.com/qdm12/ss-server/pkg" shadowsockslib "github.com/qdm12/ss-server/pkg"
) )
@@ -17,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.ShadowSocks) GetSettings() (settings configuration.ShadowSocks)
SetSettings(settings settings.ShadowSocks) (outcome string) SetSettings(settings configuration.ShadowSocks) (outcome string)
} }
type looper struct { type looper struct {
@@ -51,7 +51,7 @@ func (l *looper) logAndWait(ctx context.Context, err error) {
const defaultBackoffTime = 10 * time.Second const defaultBackoffTime = 10 * time.Second
func NewLooper(settings settings.ShadowSocks, logger logging.Logger) Looper { func NewLooper(settings configuration.ShadowSocks, logger logging.Logger) Looper {
return &looper{ return &looper{
state: state{ state: state{
status: constants.Stopped, status: constants.Stopped,

View File

@@ -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.ShadowSocks settings configuration.ShadowSocks
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.ShadowSocks) { func (l *looper) GetSettings() (settings configuration.ShadowSocks) {
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.ShadowSocks) (outcome string) { func (l *looper) SetSettings(settings configuration.ShadowSocks) (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 {

View File

@@ -6,9 +6,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/gluetun/internal/storage" "github.com/qdm12/gluetun/internal/storage"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
) )
@@ -18,8 +18,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.Updater) GetSettings() (settings configuration.Updater)
SetSettings(settings settings.Updater) (outcome string) SetSettings(settings configuration.Updater) (outcome string)
} }
type looper struct { type looper struct {
@@ -44,7 +44,7 @@ type looper struct {
const defaultBackoffTime = 5 * time.Second const defaultBackoffTime = 5 * time.Second
func NewLooper(settings settings.Updater, currentServers models.AllServers, func NewLooper(settings configuration.Updater, currentServers models.AllServers,
storage storage.Storage, setAllServers func(allServers models.AllServers), storage storage.Storage, setAllServers func(allServers models.AllServers),
client *http.Client, logger logging.Logger) Looper { client *http.Client, logger logging.Logger) Looper {
loggerWithPrefix := logger.WithPrefix("updater: ") loggerWithPrefix := logger.WithPrefix("updater: ")

View File

@@ -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.Updater settings configuration.Updater
statusMu sync.RWMutex statusMu sync.RWMutex
periodMu sync.RWMutex periodMu sync.RWMutex
} }
@@ -69,13 +69,13 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error)
} }
} }
func (l *looper) GetSettings() (settings settings.Updater) { func (l *looper) GetSettings() (settings configuration.Updater) {
l.state.periodMu.RLock() l.state.periodMu.RLock()
defer l.state.periodMu.RUnlock() defer l.state.periodMu.RUnlock()
return l.state.settings return l.state.settings
} }
func (l *looper) SetSettings(settings settings.Updater) (outcome string) { func (l *looper) SetSettings(settings configuration.Updater) (outcome string) {
l.state.periodMu.Lock() l.state.periodMu.Lock()
defer l.state.periodMu.Unlock() defer l.state.periodMu.Unlock()
settingsUnchanged := reflect.DeepEqual(settings, l.state.settings) settingsUnchanged := reflect.DeepEqual(settings, l.state.settings)

View File

@@ -6,8 +6,8 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/network" "github.com/qdm12/golibs/network"
) )
@@ -18,7 +18,7 @@ type Updater interface {
type updater struct { type updater struct {
// configuration // configuration
options settings.Updater options configuration.Updater
// state // state
servers models.AllServers servers models.AllServers
@@ -31,7 +31,7 @@ type updater struct {
client network.Client client network.Client
} }
func New(settings settings.Updater, httpClient *http.Client, func New(settings configuration.Updater, httpClient *http.Client,
currentServers models.AllServers, logger logging.Logger) Updater { currentServers models.AllServers, logger logging.Logger) Updater {
if len(settings.DNSAddress) == 0 { if len(settings.DNSAddress) == 0 {
settings.DNSAddress = "1.1.1.1" settings.DNSAddress = "1.1.1.1"