chore(config): upgrade to gosettings v0.4.0
- drop qdm12/govalid dependency - upgrade qdm12/ss-server to v0.6.0 - do not unset sensitive config settings (makes no sense to me)
This commit is contained in:
@@ -17,9 +17,7 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/alpine"
|
"github.com/qdm12/gluetun/internal/alpine"
|
||||||
"github.com/qdm12/gluetun/internal/cli"
|
"github.com/qdm12/gluetun/internal/cli"
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
"github.com/qdm12/gluetun/internal/configuration/sources/env"
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/sources/files"
|
"github.com/qdm12/gluetun/internal/configuration/sources/files"
|
||||||
mux "github.com/qdm12/gluetun/internal/configuration/sources/merge"
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/sources/secrets"
|
"github.com/qdm12/gluetun/internal/configuration/sources/secrets"
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/dns"
|
"github.com/qdm12/gluetun/internal/dns"
|
||||||
@@ -45,6 +43,8 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/updater/unzip"
|
"github.com/qdm12/gluetun/internal/updater/unzip"
|
||||||
"github.com/qdm12/gluetun/internal/vpn"
|
"github.com/qdm12/gluetun/internal/vpn"
|
||||||
"github.com/qdm12/golibs/command"
|
"github.com/qdm12/golibs/command"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
|
"github.com/qdm12/gosettings/reader/sources/env"
|
||||||
"github.com/qdm12/goshutdown"
|
"github.com/qdm12/goshutdown"
|
||||||
"github.com/qdm12/goshutdown/goroutine"
|
"github.com/qdm12/goshutdown/goroutine"
|
||||||
"github.com/qdm12/goshutdown/group"
|
"github.com/qdm12/goshutdown/group"
|
||||||
@@ -82,14 +82,21 @@ func main() {
|
|||||||
cli := cli.New()
|
cli := cli.New()
|
||||||
cmder := command.NewCmder()
|
cmder := command.NewCmder()
|
||||||
|
|
||||||
secretsReader := secrets.New()
|
reader := reader.New(reader.Settings{
|
||||||
filesReader := files.New()
|
Sources: []reader.Source{
|
||||||
envReader := env.New(logger)
|
secrets.New(logger),
|
||||||
muxReader := mux.New(secretsReader, filesReader, envReader)
|
files.New(logger),
|
||||||
|
env.New(env.Settings{}),
|
||||||
|
},
|
||||||
|
HandleDeprecatedKey: func(source, deprecatedKey, currentKey string) {
|
||||||
|
logger.Warn("You are using the old " + source + " " + deprecatedKey +
|
||||||
|
", please consider changing it to " + currentKey)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
errorCh := make(chan error)
|
errorCh := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
errorCh <- _main(ctx, buildInfo, args, logger, muxReader, tun, netLinker, cmder, cli)
|
errorCh <- _main(ctx, buildInfo, args, logger, reader, tun, netLinker, cmder, cli)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@@ -139,17 +146,17 @@ var (
|
|||||||
|
|
||||||
//nolint:gocognit,gocyclo,maintidx
|
//nolint:gocognit,gocyclo,maintidx
|
||||||
func _main(ctx context.Context, buildInfo models.BuildInformation,
|
func _main(ctx context.Context, buildInfo models.BuildInformation,
|
||||||
args []string, logger log.LoggerInterface, source Source,
|
args []string, logger log.LoggerInterface, reader *reader.Reader,
|
||||||
tun Tun, netLinker netLinker, cmder command.RunStarter,
|
tun Tun, netLinker netLinker, cmder command.RunStarter,
|
||||||
cli clier) error {
|
cli clier) error {
|
||||||
if len(args) > 1 { // cli operation
|
if len(args) > 1 { // cli operation
|
||||||
switch args[1] {
|
switch args[1] {
|
||||||
case "healthcheck":
|
case "healthcheck":
|
||||||
return cli.HealthCheck(ctx, source, logger)
|
return cli.HealthCheck(ctx, reader, logger)
|
||||||
case "clientkey":
|
case "clientkey":
|
||||||
return cli.ClientKey(args[2:])
|
return cli.ClientKey(args[2:])
|
||||||
case "openvpnconfig":
|
case "openvpnconfig":
|
||||||
return cli.OpenvpnConfig(logger, source, netLinker)
|
return cli.OpenvpnConfig(logger, reader, netLinker)
|
||||||
case "update":
|
case "update":
|
||||||
return cli.Update(ctx, args[2:], logger)
|
return cli.Update(ctx, args[2:], logger)
|
||||||
case "format-servers":
|
case "format-servers":
|
||||||
@@ -180,17 +187,22 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
fmt.Println(line)
|
fmt.Println(line)
|
||||||
}
|
}
|
||||||
|
|
||||||
allSettings, err := source.Read()
|
var allSettings settings.Settings
|
||||||
|
err = allSettings.Read(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
allSettings.SetDefaults()
|
||||||
|
|
||||||
// Note: no need to validate minimal settings for the firewall:
|
// Note: no need to validate minimal settings for the firewall:
|
||||||
// - global log level is parsed from source
|
// - global log level is parsed below
|
||||||
// - firewall Debug and Enabled are booleans parsed from source
|
// - firewall Debug and Enabled are booleans parsed from source
|
||||||
|
logLevel, err := log.ParseLevel(allSettings.Log.Level)
|
||||||
logger.Patch(log.SetLevel(*allSettings.Log.Level))
|
if err != nil {
|
||||||
netLinker.PatchLoggerLevel(*allSettings.Log.Level)
|
return fmt.Errorf("log level: %w", err)
|
||||||
|
}
|
||||||
|
logger.Patch(log.SetLevel(logLevel))
|
||||||
|
netLinker.PatchLoggerLevel(logLevel)
|
||||||
|
|
||||||
routingLogger := logger.New(log.SetComponent("routing"))
|
routingLogger := logger.New(log.SetComponent("routing"))
|
||||||
if *allSettings.Firewall.Debug { // To remove in v4
|
if *allSettings.Firewall.Debug { // To remove in v4
|
||||||
@@ -578,8 +590,8 @@ type Linker interface {
|
|||||||
type clier interface {
|
type clier interface {
|
||||||
ClientKey(args []string) error
|
ClientKey(args []string) error
|
||||||
FormatServers(args []string) error
|
FormatServers(args []string) error
|
||||||
OpenvpnConfig(logger cli.OpenvpnConfigLogger, source cli.Source, ipv6Checker cli.IPv6Checker) error
|
OpenvpnConfig(logger cli.OpenvpnConfigLogger, reader *reader.Reader, ipv6Checker cli.IPv6Checker) error
|
||||||
HealthCheck(ctx context.Context, source cli.Source, warner cli.Warner) error
|
HealthCheck(ctx context.Context, reader *reader.Reader, warner cli.Warner) error
|
||||||
Update(ctx context.Context, args []string, logger cli.UpdaterLogger) error
|
Update(ctx context.Context, args []string, logger cli.UpdaterLogger) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,9 +599,3 @@ type Tun interface {
|
|||||||
Check(tunDevice string) error
|
Check(tunDevice string) error
|
||||||
Create(tunDevice string) error
|
Create(tunDevice string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type Source interface {
|
|
||||||
Read() (settings settings.Settings, err error)
|
|
||||||
ReadHealth() (health settings.Health, err error)
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|||||||
9
go.mod
9
go.mod
@@ -10,13 +10,12 @@ require (
|
|||||||
github.com/klauspost/pgzip v1.2.6
|
github.com/klauspost/pgzip v1.2.6
|
||||||
github.com/qdm12/dns v1.11.0
|
github.com/qdm12/dns v1.11.0
|
||||||
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6
|
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6
|
||||||
github.com/qdm12/gosettings v0.4.0-rc1
|
github.com/qdm12/gosettings v0.4.0-rc9.0.20240323143821-6cc9afe3e4a0
|
||||||
github.com/qdm12/goshutdown v0.3.0
|
github.com/qdm12/goshutdown v0.3.0
|
||||||
github.com/qdm12/gosplash v0.1.0
|
github.com/qdm12/gosplash v0.1.0
|
||||||
github.com/qdm12/gotree v0.2.0
|
github.com/qdm12/gotree v0.2.0
|
||||||
github.com/qdm12/govalid v0.2.0-rc1
|
|
||||||
github.com/qdm12/log v0.1.0
|
github.com/qdm12/log v0.1.0
|
||||||
github.com/qdm12/ss-server v0.5.0
|
github.com/qdm12/ss-server v0.6.0
|
||||||
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
|
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/ulikunitz/xz v0.5.11
|
github.com/ulikunitz/xz v0.5.11
|
||||||
@@ -48,8 +47,10 @@ require (
|
|||||||
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
|
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect
|
||||||
golang.org/x/crypto v0.21.0 // indirect
|
golang.org/x/crypto v0.21.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
||||||
golang.org/x/sync v0.1.0 // indirect
|
golang.org/x/sync v0.1.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
kernel.org/pub/linux/libs/security/libcap/cap v1.2.69 // indirect
|
||||||
|
kernel.org/pub/linux/libs/security/libcap/psx v1.2.69 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
18
go.sum
18
go.sum
@@ -97,20 +97,18 @@ github.com/qdm12/golibs v0.0.0-20210603202746-e5494e9c2ebb/go.mod h1:15RBzkun0i8
|
|||||||
github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2/go.mod h1:6aRbg4Z/bTbm9JfxsGXfWKHi7zsOvPfUTK1S5HuAFKg=
|
github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2/go.mod h1:6aRbg4Z/bTbm9JfxsGXfWKHi7zsOvPfUTK1S5HuAFKg=
|
||||||
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6 h1:bge5AL7cjHJMPz+5IOz5yF01q/l8No6+lIEBieA8gMg=
|
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6 h1:bge5AL7cjHJMPz+5IOz5yF01q/l8No6+lIEBieA8gMg=
|
||||||
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6/go.mod h1:6aRbg4Z/bTbm9JfxsGXfWKHi7zsOvPfUTK1S5HuAFKg=
|
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6/go.mod h1:6aRbg4Z/bTbm9JfxsGXfWKHi7zsOvPfUTK1S5HuAFKg=
|
||||||
github.com/qdm12/gosettings v0.4.0-rc1 h1:UYA92yyeDPbmZysIuG65yrpZVPtdIoRmtEHft/AyI38=
|
github.com/qdm12/gosettings v0.4.0-rc9.0.20240323143821-6cc9afe3e4a0 h1:ocCLeoHvSD5ZSA1J0H1VyCzBeUzSJil4HvnhjHdceEc=
|
||||||
github.com/qdm12/gosettings v0.4.0-rc1/go.mod h1:JRV3opOpHvnKlIA29lKQMdYw1WSMVMfHYLLHPHol5ME=
|
github.com/qdm12/gosettings v0.4.0-rc9.0.20240323143821-6cc9afe3e4a0/go.mod h1:uItKwGXibJp2pQ0am6MBKilpjfvYTGiH+zXHd10jFj8=
|
||||||
github.com/qdm12/goshutdown v0.3.0 h1:pqBpJkdwlZlfTEx4QHtS8u8CXx6pG0fVo6S1N0MpSEM=
|
github.com/qdm12/goshutdown v0.3.0 h1:pqBpJkdwlZlfTEx4QHtS8u8CXx6pG0fVo6S1N0MpSEM=
|
||||||
github.com/qdm12/goshutdown v0.3.0/go.mod h1:EqZ46No00kCTZ5qzdd3qIzY6ayhMt24QI8Mh8LVQYmM=
|
github.com/qdm12/goshutdown v0.3.0/go.mod h1:EqZ46No00kCTZ5qzdd3qIzY6ayhMt24QI8Mh8LVQYmM=
|
||||||
github.com/qdm12/gosplash v0.1.0 h1:Sfl+zIjFZFP7b0iqf2l5UkmEY97XBnaKkH3FNY6Gf7g=
|
github.com/qdm12/gosplash v0.1.0 h1:Sfl+zIjFZFP7b0iqf2l5UkmEY97XBnaKkH3FNY6Gf7g=
|
||||||
github.com/qdm12/gosplash v0.1.0/go.mod h1:+A3fWW4/rUeDXhY3ieBzwghKdnIPFJgD8K3qQkenJlw=
|
github.com/qdm12/gosplash v0.1.0/go.mod h1:+A3fWW4/rUeDXhY3ieBzwghKdnIPFJgD8K3qQkenJlw=
|
||||||
github.com/qdm12/gotree v0.2.0 h1:+58ltxkNLUyHtATFereAcOjBVfY6ETqRex8XK90Fb/c=
|
github.com/qdm12/gotree v0.2.0 h1:+58ltxkNLUyHtATFereAcOjBVfY6ETqRex8XK90Fb/c=
|
||||||
github.com/qdm12/gotree v0.2.0/go.mod h1:1SdFaqKZuI46U1apbXIf25pDMNnrPuYLEqMF/qL4lY4=
|
github.com/qdm12/gotree v0.2.0/go.mod h1:1SdFaqKZuI46U1apbXIf25pDMNnrPuYLEqMF/qL4lY4=
|
||||||
github.com/qdm12/govalid v0.2.0-rc1 h1:4iYQvU4ibrASgzelsEgZX4JyKX3UTB/DcHObzQ7BXtw=
|
|
||||||
github.com/qdm12/govalid v0.2.0-rc1/go.mod h1:/uWzVWMuS71wmbsVnlUxpQiy6EAXqm8eQ2RbyA72roQ=
|
|
||||||
github.com/qdm12/log v0.1.0 h1:jYBd/xscHYpblzZAd2kjZp2YmuYHjAAfbTViJWxoPTw=
|
github.com/qdm12/log v0.1.0 h1:jYBd/xscHYpblzZAd2kjZp2YmuYHjAAfbTViJWxoPTw=
|
||||||
github.com/qdm12/log v0.1.0/go.mod h1:Vchi5M8uBvHfPNIblN4mjXn/oSbiWguQIbsgF1zdQPI=
|
github.com/qdm12/log v0.1.0/go.mod h1:Vchi5M8uBvHfPNIblN4mjXn/oSbiWguQIbsgF1zdQPI=
|
||||||
github.com/qdm12/ss-server v0.5.0 h1:ARAqJayohDM51BmJ/R5Yplkpo+Qxgp7xizBF1HWd7uQ=
|
github.com/qdm12/ss-server v0.6.0 h1:OaOdCIBXx0z3DGHPT6Th0v88vGa3MtAS4oRgUsDHGZE=
|
||||||
github.com/qdm12/ss-server v0.5.0/go.mod h1:eFd8PL/uy0ZvJ4KeSUzToruJctVQoYqXk+LRy9vcOiI=
|
github.com/qdm12/ss-server v0.6.0/go.mod h1:0BO/zEmtTiLDlmQEcjtoHTC+w+cWxwItjBuGP6TWM78=
|
||||||
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e h1:4q+uFLawkaQRq3yARYLsjJPZd2wYwxn4g6G/5v0xW1g=
|
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e h1:4q+uFLawkaQRq3yARYLsjJPZd2wYwxn4g6G/5v0xW1g=
|
||||||
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e/go.mod h1:UvJRGkZ9XL3/D7e7JiTTVLm1F3Cymd3/gFpD6frEpBo=
|
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e/go.mod h1:UvJRGkZ9XL3/D7e7JiTTVLm1F3Cymd3/gFpD6frEpBo=
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||||
@@ -156,8 +154,8 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5
|
|||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
|
||||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
@@ -252,3 +250,7 @@ gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0/go.mod h1:Dn5idtptoW1dIos9U
|
|||||||
inet.af/netaddr v0.0.0-20210511181906-37180328850c/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
|
inet.af/netaddr v0.0.0-20210511181906-37180328850c/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
|
||||||
inet.af/netaddr v0.0.0-20220811202034-502d2d690317 h1:U2fwK6P2EqmopP/hFLTOAjWTki0qgd4GMJn5X8wOleU=
|
inet.af/netaddr v0.0.0-20220811202034-502d2d690317 h1:U2fwK6P2EqmopP/hFLTOAjWTki0qgd4GMJn5X8wOleU=
|
||||||
inet.af/netaddr v0.0.0-20220811202034-502d2d690317/go.mod h1:OIezDfdzOgFhuw4HuWapWq2e9l0H9tK4F1j+ETRtF3k=
|
inet.af/netaddr v0.0.0-20220811202034-502d2d690317/go.mod h1:OIezDfdzOgFhuw4HuWapWq2e9l0H9tK4F1j+ETRtF3k=
|
||||||
|
kernel.org/pub/linux/libs/security/libcap/cap v1.2.69 h1:N0m3tKYbkRMmDobh/47ngz+AWeV7PcfXMDi8xu3Vrag=
|
||||||
|
kernel.org/pub/linux/libs/security/libcap/cap v1.2.69/go.mod h1:Tk5Ip2TuxaWGpccL7//rAsLRH6RQ/jfqTGxuN/+i/FQ=
|
||||||
|
kernel.org/pub/linux/libs/security/libcap/psx v1.2.69 h1:IdrOs1ZgwGw5CI+BH6GgVVlOt+LAXoPyh7enr8lfaXs=
|
||||||
|
kernel.org/pub/linux/libs/security/libcap/psx v1.2.69/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24=
|
||||||
|
|||||||
@@ -6,13 +6,12 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/sources/files"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *CLI) ClientKey(args []string) error {
|
func (c *CLI) ClientKey(args []string) error {
|
||||||
flagSet := flag.NewFlagSet("clientkey", flag.ExitOnError)
|
flagSet := flag.NewFlagSet("clientkey", flag.ExitOnError)
|
||||||
filepath := flagSet.String("path", files.OpenVPNClientKeyPath, "file path to the client.key file")
|
const openVPNClientKeyPath = "/gluetun/client.key" // TODO deduplicate?
|
||||||
|
filepath := flagSet.String("path", openVPNClientKeyPath, "file path to the client.key file")
|
||||||
if err := flagSet.Parse(args); err != nil {
|
if err := flagSet.Parse(args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,15 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
"github.com/qdm12/gluetun/internal/healthcheck"
|
"github.com/qdm12/gluetun/internal/healthcheck"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *CLI) HealthCheck(ctx context.Context, source Source, _ Warner) error {
|
func (c *CLI) HealthCheck(ctx context.Context, reader *reader.Reader, _ Warner) (err error) {
|
||||||
// Extract the health server port from the configuration.
|
// Extract the health server port from the configuration.
|
||||||
config, err := source.ReadHealth()
|
var config settings.Health
|
||||||
|
err = config.Read(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
"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/openvpn/extract"
|
"github.com/qdm12/gluetun/internal/openvpn/extract"
|
||||||
"github.com/qdm12/gluetun/internal/provider"
|
"github.com/qdm12/gluetun/internal/provider"
|
||||||
"github.com/qdm12/gluetun/internal/storage"
|
"github.com/qdm12/gluetun/internal/storage"
|
||||||
"github.com/qdm12/gluetun/internal/updater/resolver"
|
"github.com/qdm12/gluetun/internal/updater/resolver"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OpenvpnConfigLogger interface {
|
type OpenvpnConfigLogger interface {
|
||||||
@@ -39,14 +41,15 @@ type IPv6Checker interface {
|
|||||||
IsIPv6Supported() (supported bool, err error)
|
IsIPv6Supported() (supported bool, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CLI) OpenvpnConfig(logger OpenvpnConfigLogger, source Source,
|
func (c *CLI) OpenvpnConfig(logger OpenvpnConfigLogger, reader *reader.Reader,
|
||||||
ipv6Checker IPv6Checker) error {
|
ipv6Checker IPv6Checker) error {
|
||||||
storage, err := storage.New(logger, constants.ServersData)
|
storage, err := storage.New(logger, constants.ServersData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
allSettings, err := source.Read()
|
var allSettings settings.Settings
|
||||||
|
err = allSettings.Read(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,14 +50,6 @@ func (d *DNS) Copy() (copied DNS) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
|
||||||
// unset field of the receiver settings object.
|
|
||||||
func (d *DNS) mergeWith(other DNS) {
|
|
||||||
d.ServerAddress = gosettings.MergeWithValidator(d.ServerAddress, other.ServerAddress)
|
|
||||||
d.KeepNameserver = gosettings.MergeWithPointer(d.KeepNameserver, other.KeepNameserver)
|
|
||||||
d.DoT.mergeWith(other.DoT)
|
|
||||||
}
|
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
@@ -87,3 +80,22 @@ func (d DNS) toLinesNode() (node *gotree.Node) {
|
|||||||
node.AppendNode(d.DoT.toLinesNode())
|
node.AppendNode(d.DoT.toLinesNode())
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DNS) read(r *reader.Reader) (err error) {
|
||||||
|
d.ServerAddress, err = r.NetipAddr("DNS_ADDRESS", reader.RetroKeys("DNS_PLAINTEXT_ADDRESS"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.KeepNameserver, err = r.BoolPtr("DNS_KEEP_NAMESERVER")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.DoT.read(r)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("DNS over TLS settings: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/qdm12/dns/pkg/blacklist"
|
"github.com/qdm12/dns/pkg/blacklist"
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -63,16 +64,6 @@ func (b DNSBlacklist) copy() (copied DNSBlacklist) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *DNSBlacklist) mergeWith(other DNSBlacklist) {
|
|
||||||
b.BlockMalicious = gosettings.MergeWithPointer(b.BlockMalicious, other.BlockMalicious)
|
|
||||||
b.BlockAds = gosettings.MergeWithPointer(b.BlockAds, other.BlockAds)
|
|
||||||
b.BlockSurveillance = gosettings.MergeWithPointer(b.BlockSurveillance, other.BlockSurveillance)
|
|
||||||
b.AllowedHosts = gosettings.MergeWithSlice(b.AllowedHosts, other.AllowedHosts)
|
|
||||||
b.AddBlockedHosts = gosettings.MergeWithSlice(b.AddBlockedHosts, other.AddBlockedHosts)
|
|
||||||
b.AddBlockedIPs = gosettings.MergeWithSlice(b.AddBlockedIPs, other.AddBlockedIPs)
|
|
||||||
b.AddBlockedIPPrefixes = gosettings.MergeWithSlice(b.AddBlockedIPPrefixes, other.AddBlockedIPPrefixes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *DNSBlacklist) overrideWith(other DNSBlacklist) {
|
func (b *DNSBlacklist) overrideWith(other DNSBlacklist) {
|
||||||
b.BlockMalicious = gosettings.OverrideWithPointer(b.BlockMalicious, other.BlockMalicious)
|
b.BlockMalicious = gosettings.OverrideWithPointer(b.BlockMalicious, other.BlockMalicious)
|
||||||
b.BlockAds = gosettings.OverrideWithPointer(b.BlockAds, other.BlockAds)
|
b.BlockAds = gosettings.OverrideWithPointer(b.BlockAds, other.BlockAds)
|
||||||
@@ -136,3 +127,66 @@ func (b DNSBlacklist) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *DNSBlacklist) read(r *reader.Reader) (err error) {
|
||||||
|
b.BlockMalicious, err = r.BoolPtr("BLOCK_MALICIOUS")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.BlockSurveillance, err = r.BoolPtr("BLOCK_SURVEILLANCE",
|
||||||
|
reader.RetroKeys("BLOCK_NSA"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.BlockAds, err = r.BoolPtr("BLOCK_ADS")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.AddBlockedIPs, b.AddBlockedIPPrefixes,
|
||||||
|
err = readDoTPrivateAddresses(r) // TODO v4 split in 2
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.AllowedHosts = r.CSV("UNBLOCK") // TODO v4 change name
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrPrivateAddressNotValid = errors.New("private address is not a valid IP or CIDR range")
|
||||||
|
)
|
||||||
|
|
||||||
|
func readDoTPrivateAddresses(reader *reader.Reader) (ips []netip.Addr,
|
||||||
|
ipPrefixes []netip.Prefix, err error) {
|
||||||
|
privateAddresses := reader.CSV("DOT_PRIVATE_ADDRESS")
|
||||||
|
if len(privateAddresses) == 0 {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ips = make([]netip.Addr, 0, len(privateAddresses))
|
||||||
|
ipPrefixes = make([]netip.Prefix, 0, len(privateAddresses))
|
||||||
|
|
||||||
|
for _, privateAddress := range privateAddresses {
|
||||||
|
ip, err := netip.ParseAddr(privateAddress)
|
||||||
|
if err == nil {
|
||||||
|
ips = append(ips, ip)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ipPrefix, err := netip.ParsePrefix(privateAddress)
|
||||||
|
if err == nil {
|
||||||
|
ipPrefixes = append(ipPrefixes, ipPrefix)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, fmt.Errorf(
|
||||||
|
"environment variable DOT_PRIVATE_ADDRESS: %w: %s",
|
||||||
|
ErrPrivateAddressNotValid, privateAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ips, ipPrefixes, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -61,15 +62,6 @@ func (d *DoT) copy() (copied DoT) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
|
||||||
// unset field of the receiver settings object.
|
|
||||||
func (d *DoT) mergeWith(other DoT) {
|
|
||||||
d.Enabled = gosettings.MergeWithPointer(d.Enabled, other.Enabled)
|
|
||||||
d.UpdatePeriod = gosettings.MergeWithPointer(d.UpdatePeriod, other.UpdatePeriod)
|
|
||||||
d.Unbound.mergeWith(other.Unbound)
|
|
||||||
d.Blacklist.mergeWith(other.Blacklist)
|
|
||||||
}
|
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
@@ -111,3 +103,27 @@ func (d DoT) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DoT) read(reader *reader.Reader) (err error) {
|
||||||
|
d.Enabled, err = reader.BoolPtr("DOT")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.UpdatePeriod, err = reader.DurationPtr("DNS_UPDATE_PERIOD")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.Unbound.read(reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.Blacklist.read(reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package settings
|
|||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
ErrValueUnknown = errors.New("value is unknown")
|
||||||
ErrCityNotValid = errors.New("the city specified is not valid")
|
ErrCityNotValid = errors.New("the city specified is not valid")
|
||||||
ErrControlServerPrivilegedPort = errors.New("cannot use privileged port without running as root")
|
ErrControlServerPrivilegedPort = errors.New("cannot use privileged port without running as root")
|
||||||
ErrCategoryNotValid = errors.New("the category specified is not valid")
|
ErrCategoryNotValid = errors.New("the category specified is not valid")
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -54,18 +55,6 @@ func (f *Firewall) copy() (copied Firewall) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
|
||||||
// unset field of the receiver settings object.
|
|
||||||
// It merges values of slices together, even if they
|
|
||||||
// are set in the receiver settings.
|
|
||||||
func (f *Firewall) mergeWith(other Firewall) {
|
|
||||||
f.VPNInputPorts = gosettings.MergeWithSlice(f.VPNInputPorts, other.VPNInputPorts)
|
|
||||||
f.InputPorts = gosettings.MergeWithSlice(f.InputPorts, other.InputPorts)
|
|
||||||
f.OutboundSubnets = gosettings.MergeWithSlice(f.OutboundSubnets, other.OutboundSubnets)
|
|
||||||
f.Enabled = gosettings.MergeWithPointer(f.Enabled, other.Enabled)
|
|
||||||
f.Debug = gosettings.MergeWithPointer(f.Debug, other.Debug)
|
|
||||||
}
|
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
@@ -122,3 +111,33 @@ func (f Firewall) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Firewall) read(r *reader.Reader) (err error) {
|
||||||
|
f.VPNInputPorts, err = r.CSVUint16("FIREWALL_VPN_INPUT_PORTS")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.InputPorts, err = r.CSVUint16("FIREWALL_INPUT_PORTS")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.OutboundSubnets, err = r.CSVNetipPrefixes(
|
||||||
|
"FIREWALL_OUTBOUND_SUBNETS", reader.RetroKeys("EXTRA_SUBNETS"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Enabled, err = r.BoolPtr("FIREWALL")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Debug, err = r.BoolPtr("FIREWALL_DEBUG")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
|
"github.com/qdm12/gosettings/validate"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
"github.com/qdm12/govalid/address"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Health contains settings for the healthcheck and health server.
|
// Health contains settings for the healthcheck and health server.
|
||||||
@@ -36,9 +37,7 @@ type Health struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h Health) Validate() (err error) {
|
func (h Health) Validate() (err error) {
|
||||||
uid := os.Getuid()
|
err = validate.ListeningAddress(h.ServerAddress, os.Getuid())
|
||||||
err = address.Validate(h.ServerAddress,
|
|
||||||
address.OptionListening(uid))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("server listening address is not valid: %w", err)
|
return fmt.Errorf("server listening address is not valid: %w", err)
|
||||||
}
|
}
|
||||||
@@ -62,38 +61,27 @@ func (h *Health) copy() (copied Health) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MergeWith merges the other settings into any
|
|
||||||
// unset field of the receiver settings object.
|
|
||||||
func (h *Health) MergeWith(other Health) {
|
|
||||||
h.ServerAddress = gosettings.MergeWithString(h.ServerAddress, other.ServerAddress)
|
|
||||||
h.ReadHeaderTimeout = gosettings.MergeWithNumber(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
|
||||||
h.ReadTimeout = gosettings.MergeWithNumber(h.ReadTimeout, other.ReadTimeout)
|
|
||||||
h.TargetAddress = gosettings.MergeWithString(h.TargetAddress, other.TargetAddress)
|
|
||||||
h.SuccessWait = gosettings.MergeWithNumber(h.SuccessWait, other.SuccessWait)
|
|
||||||
h.VPN.mergeWith(other.VPN)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OverrideWith overrides fields of the receiver
|
// OverrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (h *Health) OverrideWith(other Health) {
|
func (h *Health) OverrideWith(other Health) {
|
||||||
h.ServerAddress = gosettings.OverrideWithString(h.ServerAddress, other.ServerAddress)
|
h.ServerAddress = gosettings.OverrideWithComparable(h.ServerAddress, other.ServerAddress)
|
||||||
h.ReadHeaderTimeout = gosettings.OverrideWithNumber(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
h.ReadHeaderTimeout = gosettings.OverrideWithComparable(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
||||||
h.ReadTimeout = gosettings.OverrideWithNumber(h.ReadTimeout, other.ReadTimeout)
|
h.ReadTimeout = gosettings.OverrideWithComparable(h.ReadTimeout, other.ReadTimeout)
|
||||||
h.TargetAddress = gosettings.OverrideWithString(h.TargetAddress, other.TargetAddress)
|
h.TargetAddress = gosettings.OverrideWithComparable(h.TargetAddress, other.TargetAddress)
|
||||||
h.SuccessWait = gosettings.OverrideWithNumber(h.SuccessWait, other.SuccessWait)
|
h.SuccessWait = gosettings.OverrideWithComparable(h.SuccessWait, other.SuccessWait)
|
||||||
h.VPN.overrideWith(other.VPN)
|
h.VPN.overrideWith(other.VPN)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Health) SetDefaults() {
|
func (h *Health) SetDefaults() {
|
||||||
h.ServerAddress = gosettings.DefaultString(h.ServerAddress, "127.0.0.1:9999")
|
h.ServerAddress = gosettings.DefaultComparable(h.ServerAddress, "127.0.0.1:9999")
|
||||||
const defaultReadHeaderTimeout = 100 * time.Millisecond
|
const defaultReadHeaderTimeout = 100 * time.Millisecond
|
||||||
h.ReadHeaderTimeout = gosettings.DefaultNumber(h.ReadHeaderTimeout, defaultReadHeaderTimeout)
|
h.ReadHeaderTimeout = gosettings.DefaultComparable(h.ReadHeaderTimeout, defaultReadHeaderTimeout)
|
||||||
const defaultReadTimeout = 500 * time.Millisecond
|
const defaultReadTimeout = 500 * time.Millisecond
|
||||||
h.ReadTimeout = gosettings.DefaultNumber(h.ReadTimeout, defaultReadTimeout)
|
h.ReadTimeout = gosettings.DefaultComparable(h.ReadTimeout, defaultReadTimeout)
|
||||||
h.TargetAddress = gosettings.DefaultString(h.TargetAddress, "cloudflare.com:443")
|
h.TargetAddress = gosettings.DefaultComparable(h.TargetAddress, "cloudflare.com:443")
|
||||||
const defaultSuccessWait = 5 * time.Second
|
const defaultSuccessWait = 5 * time.Second
|
||||||
h.SuccessWait = gosettings.DefaultNumber(h.SuccessWait, defaultSuccessWait)
|
h.SuccessWait = gosettings.DefaultComparable(h.SuccessWait, defaultSuccessWait)
|
||||||
h.VPN.setDefaults()
|
h.VPN.setDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,3 +99,21 @@ func (h Health) toLinesNode() (node *gotree.Node) {
|
|||||||
node.AppendNode(h.VPN.toLinesNode("VPN"))
|
node.AppendNode(h.VPN.toLinesNode("VPN"))
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Health) Read(r *reader.Reader) (err error) {
|
||||||
|
h.ServerAddress = r.String("HEALTH_SERVER_ADDRESS")
|
||||||
|
h.TargetAddress = r.String("HEALTH_TARGET_ADDRESS",
|
||||||
|
reader.RetroKeys("HEALTH_ADDRESS_TO_PING"))
|
||||||
|
|
||||||
|
h.SuccessWait, err = r.Duration("HEALTH_SUCCESS_WAIT_DURATION")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.VPN.read(r)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("VPN health settings: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,8 +24,6 @@ func (h HealthyWait) validate() (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
|
||||||
// unset field of the receiver settings object.
|
|
||||||
func (h *HealthyWait) copy() (copied HealthyWait) {
|
func (h *HealthyWait) copy() (copied HealthyWait) {
|
||||||
return HealthyWait{
|
return HealthyWait{
|
||||||
Initial: gosettings.CopyPointer(h.Initial),
|
Initial: gosettings.CopyPointer(h.Initial),
|
||||||
@@ -32,13 +31,6 @@ func (h *HealthyWait) copy() (copied HealthyWait) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
|
||||||
// unset field of the receiver settings object.
|
|
||||||
func (h *HealthyWait) mergeWith(other HealthyWait) {
|
|
||||||
h.Initial = gosettings.MergeWithPointer(h.Initial, other.Initial)
|
|
||||||
h.Addition = gosettings.MergeWithPointer(h.Addition, other.Addition)
|
|
||||||
}
|
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
@@ -64,3 +56,21 @@ func (h HealthyWait) toLinesNode(kind string) (node *gotree.Node) {
|
|||||||
node.Appendf("Additional duration: %s", *h.Addition)
|
node.Appendf("Additional duration: %s", *h.Addition)
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *HealthyWait) read(r *reader.Reader) (err error) {
|
||||||
|
h.Initial, err = r.DurationPtr(
|
||||||
|
"HEALTH_VPN_DURATION_INITIAL",
|
||||||
|
reader.RetroKeys("HEALTH_OPENVPN_DURATION_INITIAL"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Addition, err = r.DurationPtr(
|
||||||
|
"HEALTH_VPN_DURATION_ADDITION",
|
||||||
|
reader.RetroKeys("HEALTH_OPENVPN_DURATION_ADDITION"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
5
internal/configuration/settings/helpers.go
Normal file
5
internal/configuration/settings/helpers.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package settings
|
||||||
|
|
||||||
|
func ptrTo[T any](value T) *T {
|
||||||
|
return &value
|
||||||
|
}
|
||||||
@@ -3,11 +3,13 @@ package settings
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
|
"github.com/qdm12/gosettings/validate"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
"github.com/qdm12/govalid/address"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTPProxy contains settings to configure the HTTP proxy.
|
// HTTPProxy contains settings to configure the HTTP proxy.
|
||||||
@@ -44,9 +46,7 @@ type HTTPProxy struct {
|
|||||||
|
|
||||||
func (h HTTPProxy) validate() (err error) {
|
func (h HTTPProxy) validate() (err error) {
|
||||||
// Do not validate user and password
|
// Do not validate user and password
|
||||||
|
err = validate.ListeningAddress(h.ListeningAddress, os.Getuid())
|
||||||
uid := os.Getuid()
|
|
||||||
err = address.Validate(h.ListeningAddress, address.OptionListening(uid))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%w: %s", ErrServerAddressNotValid, h.ListeningAddress)
|
return fmt.Errorf("%w: %s", ErrServerAddressNotValid, h.ListeningAddress)
|
||||||
}
|
}
|
||||||
@@ -67,44 +67,31 @@ func (h *HTTPProxy) copy() (copied HTTPProxy) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
|
||||||
// unset field of the receiver settings object.
|
|
||||||
func (h *HTTPProxy) mergeWith(other HTTPProxy) {
|
|
||||||
h.User = gosettings.MergeWithPointer(h.User, other.User)
|
|
||||||
h.Password = gosettings.MergeWithPointer(h.Password, other.Password)
|
|
||||||
h.ListeningAddress = gosettings.MergeWithString(h.ListeningAddress, other.ListeningAddress)
|
|
||||||
h.Enabled = gosettings.MergeWithPointer(h.Enabled, other.Enabled)
|
|
||||||
h.Stealth = gosettings.MergeWithPointer(h.Stealth, other.Stealth)
|
|
||||||
h.Log = gosettings.MergeWithPointer(h.Log, other.Log)
|
|
||||||
h.ReadHeaderTimeout = gosettings.MergeWithNumber(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
|
||||||
h.ReadTimeout = gosettings.MergeWithNumber(h.ReadTimeout, other.ReadTimeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (h *HTTPProxy) overrideWith(other HTTPProxy) {
|
func (h *HTTPProxy) overrideWith(other HTTPProxy) {
|
||||||
h.User = gosettings.OverrideWithPointer(h.User, other.User)
|
h.User = gosettings.OverrideWithPointer(h.User, other.User)
|
||||||
h.Password = gosettings.OverrideWithPointer(h.Password, other.Password)
|
h.Password = gosettings.OverrideWithPointer(h.Password, other.Password)
|
||||||
h.ListeningAddress = gosettings.OverrideWithString(h.ListeningAddress, other.ListeningAddress)
|
h.ListeningAddress = gosettings.OverrideWithComparable(h.ListeningAddress, other.ListeningAddress)
|
||||||
h.Enabled = gosettings.OverrideWithPointer(h.Enabled, other.Enabled)
|
h.Enabled = gosettings.OverrideWithPointer(h.Enabled, other.Enabled)
|
||||||
h.Stealth = gosettings.OverrideWithPointer(h.Stealth, other.Stealth)
|
h.Stealth = gosettings.OverrideWithPointer(h.Stealth, other.Stealth)
|
||||||
h.Log = gosettings.OverrideWithPointer(h.Log, other.Log)
|
h.Log = gosettings.OverrideWithPointer(h.Log, other.Log)
|
||||||
h.ReadHeaderTimeout = gosettings.OverrideWithNumber(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
h.ReadHeaderTimeout = gosettings.OverrideWithComparable(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
||||||
h.ReadTimeout = gosettings.OverrideWithNumber(h.ReadTimeout, other.ReadTimeout)
|
h.ReadTimeout = gosettings.OverrideWithComparable(h.ReadTimeout, other.ReadTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HTTPProxy) setDefaults() {
|
func (h *HTTPProxy) setDefaults() {
|
||||||
h.User = gosettings.DefaultPointer(h.User, "")
|
h.User = gosettings.DefaultPointer(h.User, "")
|
||||||
h.Password = gosettings.DefaultPointer(h.Password, "")
|
h.Password = gosettings.DefaultPointer(h.Password, "")
|
||||||
h.ListeningAddress = gosettings.DefaultString(h.ListeningAddress, ":8888")
|
h.ListeningAddress = gosettings.DefaultComparable(h.ListeningAddress, ":8888")
|
||||||
h.Enabled = gosettings.DefaultPointer(h.Enabled, false)
|
h.Enabled = gosettings.DefaultPointer(h.Enabled, false)
|
||||||
h.Stealth = gosettings.DefaultPointer(h.Stealth, false)
|
h.Stealth = gosettings.DefaultPointer(h.Stealth, false)
|
||||||
h.Log = gosettings.DefaultPointer(h.Log, false)
|
h.Log = gosettings.DefaultPointer(h.Log, false)
|
||||||
const defaultReadHeaderTimeout = time.Second
|
const defaultReadHeaderTimeout = time.Second
|
||||||
h.ReadHeaderTimeout = gosettings.DefaultNumber(h.ReadHeaderTimeout, defaultReadHeaderTimeout)
|
h.ReadHeaderTimeout = gosettings.DefaultComparable(h.ReadHeaderTimeout, defaultReadHeaderTimeout)
|
||||||
const defaultReadTimeout = 3 * time.Second
|
const defaultReadTimeout = 3 * time.Second
|
||||||
h.ReadTimeout = gosettings.DefaultNumber(h.ReadTimeout, defaultReadTimeout)
|
h.ReadTimeout = gosettings.DefaultComparable(h.ReadTimeout, defaultReadTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h HTTPProxy) String() string {
|
func (h HTTPProxy) String() string {
|
||||||
@@ -128,3 +115,66 @@ func (h HTTPProxy) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *HTTPProxy) read(r *reader.Reader) (err error) {
|
||||||
|
h.User = r.Get("HTTPPROXY_USER",
|
||||||
|
reader.RetroKeys("PROXY_USER", "TINYPROXY_USER"),
|
||||||
|
reader.ForceLowercase(false))
|
||||||
|
|
||||||
|
h.Password = r.Get("HTTPPROXY_PASSWORD",
|
||||||
|
reader.RetroKeys("PROXY_PASSWORD", "TINYPROXY_PASSWORD"),
|
||||||
|
reader.ForceLowercase(false))
|
||||||
|
|
||||||
|
h.ListeningAddress, err = readHTTProxyListeningAddress(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Enabled, err = r.BoolPtr("HTTPPROXY", reader.RetroKeys("PROXY", "TINYPROXY"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Stealth, err = r.BoolPtr("HTTPPROXY_STEALTH")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Log, err = readHTTProxyLog(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHTTProxyListeningAddress(r *reader.Reader) (listeningAddress string, err error) {
|
||||||
|
// Retro-compatible keys using a port only
|
||||||
|
port, err := r.Uint16Ptr("",
|
||||||
|
reader.RetroKeys("HTTPPROXY_PORT", "TINYPROXY_PORT", "PROXY_PORT"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
} else if port != nil {
|
||||||
|
return fmt.Sprintf(":%d", *port), nil
|
||||||
|
}
|
||||||
|
const currentKey = "HTTPPROXY_LISTENING_ADDRESS"
|
||||||
|
return r.String(currentKey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHTTProxyLog(r *reader.Reader) (enabled *bool, err error) {
|
||||||
|
// Retro-compatible keys using different boolean verbs
|
||||||
|
value := r.String("",
|
||||||
|
reader.RetroKeys("PROXY_LOG", "TINYPROXY_LOG", "HTTPPROXY_LOG"))
|
||||||
|
switch strings.ToLower(value) {
|
||||||
|
case "":
|
||||||
|
const currentKey = "HTTPPROXY_LOG"
|
||||||
|
return r.BoolPtr(currentKey)
|
||||||
|
case "on", "info", "connect", "notice":
|
||||||
|
return ptrTo(true), nil
|
||||||
|
case "disabled", "no", "off":
|
||||||
|
return ptrTo(false), nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("HTTP retro-compatible proxy log setting: %w: %s",
|
||||||
|
ErrValueUnknown, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package settings
|
package settings
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
"github.com/qdm12/log"
|
"github.com/qdm12/log"
|
||||||
)
|
)
|
||||||
@@ -9,35 +12,33 @@ import (
|
|||||||
// Log contains settings to configure the logger.
|
// Log contains settings to configure the logger.
|
||||||
type Log struct {
|
type Log struct {
|
||||||
// Level is the log level of the logger.
|
// Level is the log level of the logger.
|
||||||
// It cannot be nil in the internal state.
|
// It cannot be empty in the internal state.
|
||||||
Level *log.Level
|
Level string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Log) validate() (err error) {
|
func (l Log) validate() (err error) {
|
||||||
|
_, err = log.ParseLevel(l.Level)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("level: %w", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Log) copy() (copied Log) {
|
func (l *Log) copy() (copied Log) {
|
||||||
return Log{
|
return Log{
|
||||||
Level: gosettings.CopyPointer(l.Level),
|
Level: l.Level,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
|
||||||
// unset field of the receiver settings object.
|
|
||||||
func (l *Log) mergeWith(other Log) {
|
|
||||||
l.Level = gosettings.MergeWithPointer(l.Level, other.Level)
|
|
||||||
}
|
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (l *Log) overrideWith(other Log) {
|
func (l *Log) overrideWith(other Log) {
|
||||||
l.Level = gosettings.OverrideWithPointer(l.Level, other.Level)
|
l.Level = gosettings.OverrideWithComparable(l.Level, other.Level)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Log) setDefaults() {
|
func (l *Log) setDefaults() {
|
||||||
l.Level = gosettings.DefaultPointer(l.Level, log.LevelInfo)
|
l.Level = gosettings.DefaultComparable(l.Level, log.LevelInfo.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Log) String() string {
|
func (l Log) String() string {
|
||||||
@@ -46,6 +47,11 @@ func (l Log) String() string {
|
|||||||
|
|
||||||
func (l Log) toLinesNode() (node *gotree.Node) {
|
func (l Log) toLinesNode() (node *gotree.Node) {
|
||||||
node = gotree.New("Log settings:")
|
node = gotree.New("Log settings:")
|
||||||
node.Appendf("Log level: %s", l.Level.String())
|
node.Appendf("Log level: %s", l.Level)
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Log) read(r *reader.Reader) (err error) {
|
||||||
|
l.Level = r.String("LOG_LEVEL")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/openvpn"
|
"github.com/qdm12/gluetun/internal/constants/openvpn"
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||||
"github.com/qdm12/gluetun/internal/openvpn/extract"
|
"github.com/qdm12/gluetun/internal/openvpn/extract"
|
||||||
"github.com/qdm12/gluetun/internal/provider/privateinternetaccess/presets"
|
"github.com/qdm12/gluetun/internal/provider/privateinternetaccess/presets"
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gosettings/validate"
|
"github.com/qdm12/gosettings/validate"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
@@ -261,32 +263,11 @@ func (o *OpenVPN) copy() (copied OpenVPN) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
|
||||||
// unset field of the receiver settings object.
|
|
||||||
func (o *OpenVPN) mergeWith(other OpenVPN) {
|
|
||||||
o.Version = gosettings.MergeWithString(o.Version, other.Version)
|
|
||||||
o.User = gosettings.MergeWithPointer(o.User, other.User)
|
|
||||||
o.Password = gosettings.MergeWithPointer(o.Password, other.Password)
|
|
||||||
o.ConfFile = gosettings.MergeWithPointer(o.ConfFile, other.ConfFile)
|
|
||||||
o.Ciphers = gosettings.MergeWithSlice(o.Ciphers, other.Ciphers)
|
|
||||||
o.Auth = gosettings.MergeWithPointer(o.Auth, other.Auth)
|
|
||||||
o.Cert = gosettings.MergeWithPointer(o.Cert, other.Cert)
|
|
||||||
o.Key = gosettings.MergeWithPointer(o.Key, other.Key)
|
|
||||||
o.EncryptedKey = gosettings.MergeWithPointer(o.EncryptedKey, other.EncryptedKey)
|
|
||||||
o.KeyPassphrase = gosettings.MergeWithPointer(o.KeyPassphrase, other.KeyPassphrase)
|
|
||||||
o.PIAEncPreset = gosettings.MergeWithPointer(o.PIAEncPreset, other.PIAEncPreset)
|
|
||||||
o.MSSFix = gosettings.MergeWithPointer(o.MSSFix, other.MSSFix)
|
|
||||||
o.Interface = gosettings.MergeWithString(o.Interface, other.Interface)
|
|
||||||
o.ProcessUser = gosettings.MergeWithString(o.ProcessUser, other.ProcessUser)
|
|
||||||
o.Verbosity = gosettings.MergeWithPointer(o.Verbosity, other.Verbosity)
|
|
||||||
o.Flags = gosettings.MergeWithSlice(o.Flags, other.Flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (o *OpenVPN) overrideWith(other OpenVPN) {
|
func (o *OpenVPN) overrideWith(other OpenVPN) {
|
||||||
o.Version = gosettings.OverrideWithString(o.Version, other.Version)
|
o.Version = gosettings.OverrideWithComparable(o.Version, other.Version)
|
||||||
o.User = gosettings.OverrideWithPointer(o.User, other.User)
|
o.User = gosettings.OverrideWithPointer(o.User, other.User)
|
||||||
o.Password = gosettings.OverrideWithPointer(o.Password, other.Password)
|
o.Password = gosettings.OverrideWithPointer(o.Password, other.Password)
|
||||||
o.ConfFile = gosettings.OverrideWithPointer(o.ConfFile, other.ConfFile)
|
o.ConfFile = gosettings.OverrideWithPointer(o.ConfFile, other.ConfFile)
|
||||||
@@ -298,14 +279,14 @@ func (o *OpenVPN) overrideWith(other OpenVPN) {
|
|||||||
o.KeyPassphrase = gosettings.OverrideWithPointer(o.KeyPassphrase, other.KeyPassphrase)
|
o.KeyPassphrase = gosettings.OverrideWithPointer(o.KeyPassphrase, other.KeyPassphrase)
|
||||||
o.PIAEncPreset = gosettings.OverrideWithPointer(o.PIAEncPreset, other.PIAEncPreset)
|
o.PIAEncPreset = gosettings.OverrideWithPointer(o.PIAEncPreset, other.PIAEncPreset)
|
||||||
o.MSSFix = gosettings.OverrideWithPointer(o.MSSFix, other.MSSFix)
|
o.MSSFix = gosettings.OverrideWithPointer(o.MSSFix, other.MSSFix)
|
||||||
o.Interface = gosettings.OverrideWithString(o.Interface, other.Interface)
|
o.Interface = gosettings.OverrideWithComparable(o.Interface, other.Interface)
|
||||||
o.ProcessUser = gosettings.OverrideWithString(o.ProcessUser, other.ProcessUser)
|
o.ProcessUser = gosettings.OverrideWithComparable(o.ProcessUser, other.ProcessUser)
|
||||||
o.Verbosity = gosettings.OverrideWithPointer(o.Verbosity, other.Verbosity)
|
o.Verbosity = gosettings.OverrideWithPointer(o.Verbosity, other.Verbosity)
|
||||||
o.Flags = gosettings.OverrideWithSlice(o.Flags, other.Flags)
|
o.Flags = gosettings.OverrideWithSlice(o.Flags, other.Flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OpenVPN) setDefaults(vpnProvider string) {
|
func (o *OpenVPN) setDefaults(vpnProvider string) {
|
||||||
o.Version = gosettings.DefaultString(o.Version, openvpn.Openvpn25)
|
o.Version = gosettings.DefaultComparable(o.Version, openvpn.Openvpn25)
|
||||||
o.User = gosettings.DefaultPointer(o.User, "")
|
o.User = gosettings.DefaultPointer(o.User, "")
|
||||||
if vpnProvider == providers.Mullvad {
|
if vpnProvider == providers.Mullvad {
|
||||||
o.Password = gosettings.DefaultPointer(o.Password, "m")
|
o.Password = gosettings.DefaultPointer(o.Password, "m")
|
||||||
@@ -326,8 +307,8 @@ func (o *OpenVPN) setDefaults(vpnProvider string) {
|
|||||||
}
|
}
|
||||||
o.PIAEncPreset = gosettings.DefaultPointer(o.PIAEncPreset, defaultEncPreset)
|
o.PIAEncPreset = gosettings.DefaultPointer(o.PIAEncPreset, defaultEncPreset)
|
||||||
o.MSSFix = gosettings.DefaultPointer(o.MSSFix, 0)
|
o.MSSFix = gosettings.DefaultPointer(o.MSSFix, 0)
|
||||||
o.Interface = gosettings.DefaultString(o.Interface, "tun0")
|
o.Interface = gosettings.DefaultComparable(o.Interface, "tun0")
|
||||||
o.ProcessUser = gosettings.DefaultString(o.ProcessUser, "root")
|
o.ProcessUser = gosettings.DefaultComparable(o.ProcessUser, "root")
|
||||||
o.Verbosity = gosettings.DefaultPointer(o.Verbosity, 1)
|
o.Verbosity = gosettings.DefaultPointer(o.Verbosity, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,3 +376,58 @@ func (o OpenVPN) WithDefaults(provider string) OpenVPN {
|
|||||||
o.setDefaults(provider)
|
o.setDefaults(provider)
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *OpenVPN) read(r *reader.Reader) (err error) {
|
||||||
|
o.Version = r.String("OPENVPN_VERSION")
|
||||||
|
o.User = r.Get("OPENVPN_USER", reader.RetroKeys("USER"), reader.ForceLowercase(false))
|
||||||
|
o.Password = r.Get("OPENVPN_PASSWORD", reader.RetroKeys("PASSWORD"), reader.ForceLowercase(false))
|
||||||
|
o.ConfFile = r.Get("OPENVPN_CUSTOM_CONFIG", reader.ForceLowercase(false))
|
||||||
|
o.Ciphers = r.CSV("OPENVPN_CIPHERS", reader.RetroKeys("OPENVPN_CIPHER"))
|
||||||
|
o.Auth = r.Get("OPENVPN_AUTH")
|
||||||
|
o.Cert = r.Get("OPENVPN_CERT", reader.ForceLowercase(false))
|
||||||
|
o.Key = r.Get("OPENVPN_KEY", reader.ForceLowercase(false))
|
||||||
|
o.EncryptedKey = r.Get("OPENVPN_ENCRYPTED_KEY", reader.ForceLowercase(false))
|
||||||
|
o.KeyPassphrase = r.Get("OPENVPN_KEY_PASSPHRASE", reader.ForceLowercase(false))
|
||||||
|
o.PIAEncPreset = r.Get("PRIVATE_INTERNET_ACCESS_OPENVPN_ENCRYPTION_PRESET",
|
||||||
|
reader.RetroKeys("ENCRYPTION", "PIA_ENCRYPTION"))
|
||||||
|
|
||||||
|
o.MSSFix, err = r.Uint16Ptr("OPENVPN_MSSFIX")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
o.Interface = r.String("VPN_INTERFACE",
|
||||||
|
reader.RetroKeys("OPENVPN_INTERFACE"), reader.ForceLowercase(false))
|
||||||
|
|
||||||
|
o.ProcessUser, err = readOpenVPNProcessUser(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
o.Verbosity, err = r.IntPtr("OPENVPN_VERBOSITY")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
flagsPtr := r.Get("OPENVPN_FLAGS", reader.ForceLowercase(false))
|
||||||
|
if flagsPtr != nil {
|
||||||
|
o.Flags = strings.Fields(*flagsPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readOpenVPNProcessUser(r *reader.Reader) (processUser string, err error) {
|
||||||
|
value, err := r.BoolPtr("OPENVPN_ROOT") // Retro-compatibility
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
} else if value != nil {
|
||||||
|
if *value {
|
||||||
|
return "root", nil
|
||||||
|
}
|
||||||
|
const defaultNonRootUser = "nonrootuser"
|
||||||
|
return defaultNonRootUser, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.String("OPENVPN_PROCESS_USER"), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||||
"github.com/qdm12/gluetun/internal/provider/privateinternetaccess/presets"
|
"github.com/qdm12/gluetun/internal/provider/privateinternetaccess/presets"
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gosettings/validate"
|
"github.com/qdm12/gosettings/validate"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
@@ -146,23 +147,16 @@ func (o *OpenVPNSelection) copy() (copied OpenVPNSelection) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OpenVPNSelection) mergeWith(other OpenVPNSelection) {
|
|
||||||
o.ConfFile = gosettings.MergeWithPointer(o.ConfFile, other.ConfFile)
|
|
||||||
o.Protocol = gosettings.MergeWithString(o.Protocol, other.Protocol)
|
|
||||||
o.CustomPort = gosettings.MergeWithPointer(o.CustomPort, other.CustomPort)
|
|
||||||
o.PIAEncPreset = gosettings.MergeWithPointer(o.PIAEncPreset, other.PIAEncPreset)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *OpenVPNSelection) overrideWith(other OpenVPNSelection) {
|
func (o *OpenVPNSelection) overrideWith(other OpenVPNSelection) {
|
||||||
o.ConfFile = gosettings.OverrideWithPointer(o.ConfFile, other.ConfFile)
|
o.ConfFile = gosettings.OverrideWithPointer(o.ConfFile, other.ConfFile)
|
||||||
o.Protocol = gosettings.OverrideWithString(o.Protocol, other.Protocol)
|
o.Protocol = gosettings.OverrideWithComparable(o.Protocol, other.Protocol)
|
||||||
o.CustomPort = gosettings.OverrideWithPointer(o.CustomPort, other.CustomPort)
|
o.CustomPort = gosettings.OverrideWithPointer(o.CustomPort, other.CustomPort)
|
||||||
o.PIAEncPreset = gosettings.OverrideWithPointer(o.PIAEncPreset, other.PIAEncPreset)
|
o.PIAEncPreset = gosettings.OverrideWithPointer(o.PIAEncPreset, other.PIAEncPreset)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OpenVPNSelection) setDefaults(vpnProvider string) {
|
func (o *OpenVPNSelection) setDefaults(vpnProvider string) {
|
||||||
o.ConfFile = gosettings.DefaultPointer(o.ConfFile, "")
|
o.ConfFile = gosettings.DefaultPointer(o.ConfFile, "")
|
||||||
o.Protocol = gosettings.DefaultString(o.Protocol, constants.UDP)
|
o.Protocol = gosettings.DefaultComparable(o.Protocol, constants.UDP)
|
||||||
o.CustomPort = gosettings.DefaultPointer(o.CustomPort, 0)
|
o.CustomPort = gosettings.DefaultPointer(o.CustomPort, 0)
|
||||||
|
|
||||||
var defaultEncPreset string
|
var defaultEncPreset string
|
||||||
@@ -194,3 +188,23 @@ func (o OpenVPNSelection) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *OpenVPNSelection) read(r *reader.Reader) (err error) {
|
||||||
|
o.ConfFile = r.Get("OPENVPN_CUSTOM_CONFIG", reader.ForceLowercase(false))
|
||||||
|
|
||||||
|
o.Protocol = r.String("OPENVPN_PROTOCOL", reader.RetroKeys("PROTOCOL"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
o.CustomPort, err = r.Uint16Ptr("VPN_ENDPOINT_PORT",
|
||||||
|
reader.RetroKeys("PORT", "OPENVPN_PORT"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
o.PIAEncPreset = r.Get("PRIVATE_INTERNET_ACCESS_OPENVPN_ENCRYPTION_PRESET",
|
||||||
|
reader.RetroKeys("ENCRYPTION", "PIA_ENCRYPTION"))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gosettings/validate"
|
"github.com/qdm12/gosettings/validate"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
@@ -72,13 +73,6 @@ func (p *PortForwarding) Copy() (copied PortForwarding) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PortForwarding) mergeWith(other PortForwarding) {
|
|
||||||
p.Enabled = gosettings.MergeWithPointer(p.Enabled, other.Enabled)
|
|
||||||
p.Provider = gosettings.MergeWithPointer(p.Provider, other.Provider)
|
|
||||||
p.Filepath = gosettings.MergeWithPointer(p.Filepath, other.Filepath)
|
|
||||||
p.ListeningPort = gosettings.MergeWithPointer(p.ListeningPort, other.ListeningPort)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PortForwarding) OverrideWith(other PortForwarding) {
|
func (p *PortForwarding) OverrideWith(other PortForwarding) {
|
||||||
p.Enabled = gosettings.OverrideWithPointer(p.Enabled, other.Enabled)
|
p.Enabled = gosettings.OverrideWithPointer(p.Enabled, other.Enabled)
|
||||||
p.Provider = gosettings.OverrideWithPointer(p.Provider, other.Provider)
|
p.Provider = gosettings.OverrideWithPointer(p.Provider, other.Provider)
|
||||||
@@ -124,3 +118,30 @@ func (p PortForwarding) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PortForwarding) read(r *reader.Reader) (err error) {
|
||||||
|
p.Enabled, err = r.BoolPtr("VPN_PORT_FORWARDING",
|
||||||
|
reader.RetroKeys(
|
||||||
|
"PORT_FORWARDING",
|
||||||
|
"PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING",
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Provider = r.Get("VPN_PORT_FORWARDING_PROVIDER")
|
||||||
|
|
||||||
|
p.Filepath = r.Get("VPN_PORT_FORWARDING_STATUS_FILE",
|
||||||
|
reader.ForceLowercase(false),
|
||||||
|
reader.RetroKeys(
|
||||||
|
"PORT_FORWARDING_STATUS_FILE",
|
||||||
|
"PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING_STATUS_FILE",
|
||||||
|
))
|
||||||
|
|
||||||
|
p.ListeningPort, err = r.Uint16Ptr("VPN_PORT_FORWARDING_LISTENING_PORT")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ package settings
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
"github.com/qdm12/gluetun/internal/constants/vpn"
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gosettings/validate"
|
"github.com/qdm12/gosettings/validate"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
@@ -65,20 +67,14 @@ func (p *Provider) copy() (copied Provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) mergeWith(other Provider) {
|
|
||||||
p.Name = gosettings.MergeWithString(p.Name, other.Name)
|
|
||||||
p.ServerSelection.mergeWith(other.ServerSelection)
|
|
||||||
p.PortForwarding.mergeWith(other.PortForwarding)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Provider) overrideWith(other Provider) {
|
func (p *Provider) overrideWith(other Provider) {
|
||||||
p.Name = gosettings.OverrideWithString(p.Name, other.Name)
|
p.Name = gosettings.OverrideWithComparable(p.Name, other.Name)
|
||||||
p.ServerSelection.overrideWith(other.ServerSelection)
|
p.ServerSelection.overrideWith(other.ServerSelection)
|
||||||
p.PortForwarding.OverrideWith(other.PortForwarding)
|
p.PortForwarding.OverrideWith(other.PortForwarding)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) setDefaults() {
|
func (p *Provider) setDefaults() {
|
||||||
p.Name = gosettings.DefaultString(p.Name, providers.PrivateInternetAccess)
|
p.Name = gosettings.DefaultComparable(p.Name, providers.PrivateInternetAccess)
|
||||||
p.ServerSelection.setDefaults(p.Name)
|
p.ServerSelection.setDefaults(p.Name)
|
||||||
p.PortForwarding.setDefaults()
|
p.PortForwarding.setDefaults()
|
||||||
}
|
}
|
||||||
@@ -94,3 +90,37 @@ func (p Provider) toLinesNode() (node *gotree.Node) {
|
|||||||
node.AppendNode(p.PortForwarding.toLinesNode())
|
node.AppendNode(p.PortForwarding.toLinesNode())
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provider) read(r *reader.Reader, vpnType string) (err error) {
|
||||||
|
p.Name = readVPNServiceProvider(r, vpnType)
|
||||||
|
|
||||||
|
err = p.ServerSelection.read(r, p.Name, vpnType)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("server selection: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.PortForwarding.read(r)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("port forwarding: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readVPNServiceProvider(r *reader.Reader, vpnType string) (vpnProvider string) {
|
||||||
|
vpnProvider = r.String("VPN_SERVICE_PROVIDER", reader.RetroKeys("VPNSP"))
|
||||||
|
if vpnProvider == "" {
|
||||||
|
if vpnType != vpn.Wireguard && r.Get("OPENVPN_CUSTOM_CONFIG") != nil {
|
||||||
|
// retro compatibility
|
||||||
|
return providers.Custom
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
vpnProvider = strings.ToLower(vpnProvider)
|
||||||
|
if vpnProvider == "pia" { // retro compatibility
|
||||||
|
return providers.PrivateInternetAccess
|
||||||
|
}
|
||||||
|
|
||||||
|
return vpnProvider
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/publicip/api"
|
"github.com/qdm12/gluetun/internal/publicip/api"
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -77,17 +78,10 @@ func (p *PublicIP) copy() (copied PublicIP) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PublicIP) mergeWith(other PublicIP) {
|
|
||||||
p.Period = gosettings.MergeWithPointer(p.Period, other.Period)
|
|
||||||
p.IPFilepath = gosettings.MergeWithPointer(p.IPFilepath, other.IPFilepath)
|
|
||||||
p.API = gosettings.MergeWithString(p.API, other.API)
|
|
||||||
p.APIToken = gosettings.MergeWithPointer(p.APIToken, other.APIToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PublicIP) overrideWith(other PublicIP) {
|
func (p *PublicIP) overrideWith(other PublicIP) {
|
||||||
p.Period = gosettings.OverrideWithPointer(p.Period, other.Period)
|
p.Period = gosettings.OverrideWithPointer(p.Period, other.Period)
|
||||||
p.IPFilepath = gosettings.OverrideWithPointer(p.IPFilepath, other.IPFilepath)
|
p.IPFilepath = gosettings.OverrideWithPointer(p.IPFilepath, other.IPFilepath)
|
||||||
p.API = gosettings.OverrideWithString(p.API, other.API)
|
p.API = gosettings.OverrideWithComparable(p.API, other.API)
|
||||||
p.APIToken = gosettings.OverrideWithPointer(p.APIToken, other.APIToken)
|
p.APIToken = gosettings.OverrideWithPointer(p.APIToken, other.APIToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +89,7 @@ func (p *PublicIP) setDefaults() {
|
|||||||
const defaultPeriod = 12 * time.Hour
|
const defaultPeriod = 12 * time.Hour
|
||||||
p.Period = gosettings.DefaultPointer(p.Period, defaultPeriod)
|
p.Period = gosettings.DefaultPointer(p.Period, defaultPeriod)
|
||||||
p.IPFilepath = gosettings.DefaultPointer(p.IPFilepath, "/tmp/gluetun/ip")
|
p.IPFilepath = gosettings.DefaultPointer(p.IPFilepath, "/tmp/gluetun/ip")
|
||||||
p.API = gosettings.DefaultString(p.API, "ipinfo")
|
p.API = gosettings.DefaultComparable(p.API, "ipinfo")
|
||||||
p.APIToken = gosettings.DefaultPointer(p.APIToken, "")
|
p.APIToken = gosettings.DefaultPointer(p.APIToken, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,3 +123,16 @@ func (p PublicIP) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PublicIP) read(r *reader.Reader) (err error) {
|
||||||
|
p.Period, err = r.DurationPtr("PUBLICIP_PERIOD")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.IPFilepath = r.Get("PUBLICIP_FILE",
|
||||||
|
reader.ForceLowercase(false), reader.RetroKeys("IP_STATUS_FILE"))
|
||||||
|
p.API = r.String("PUBLICIP_API")
|
||||||
|
p.APIToken = r.Get("PUBLICIP_API_TOKEN")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -48,13 +49,6 @@ func (c *ControlServer) copy() (copied ControlServer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
|
||||||
// unset field of the receiver settings object.
|
|
||||||
func (c *ControlServer) mergeWith(other ControlServer) {
|
|
||||||
c.Address = gosettings.MergeWithPointer(c.Address, other.Address)
|
|
||||||
c.Log = gosettings.MergeWithPointer(c.Log, other.Log)
|
|
||||||
}
|
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
@@ -78,3 +72,12 @@ func (c ControlServer) toLinesNode() (node *gotree.Node) {
|
|||||||
node.Appendf("Logging: %s", gosettings.BoolToYesNo(c.Log))
|
node.Appendf("Logging: %s", gosettings.BoolToYesNo(c.Log))
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ControlServer) read(r *reader.Reader) (err error) {
|
||||||
|
c.Log, err = r.BoolPtr("HTTP_CONTROL_SERVER_LOG")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Address = r.Get("HTTP_CONTROL_SERVER_ADDRESS")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
"github.com/qdm12/gluetun/internal/constants/vpn"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gosettings/validate"
|
"github.com/qdm12/gosettings/validate"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
@@ -257,30 +258,8 @@ func (ss *ServerSelection) copy() (copied ServerSelection) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *ServerSelection) mergeWith(other ServerSelection) {
|
|
||||||
ss.VPN = gosettings.MergeWithString(ss.VPN, other.VPN)
|
|
||||||
ss.TargetIP = gosettings.MergeWithValidator(ss.TargetIP, other.TargetIP)
|
|
||||||
ss.Countries = gosettings.MergeWithSlice(ss.Countries, other.Countries)
|
|
||||||
ss.Categories = gosettings.MergeWithSlice(ss.Categories, other.Categories)
|
|
||||||
ss.Regions = gosettings.MergeWithSlice(ss.Regions, other.Regions)
|
|
||||||
ss.Cities = gosettings.MergeWithSlice(ss.Cities, other.Cities)
|
|
||||||
ss.ISPs = gosettings.MergeWithSlice(ss.ISPs, other.ISPs)
|
|
||||||
ss.Hostnames = gosettings.MergeWithSlice(ss.Hostnames, other.Hostnames)
|
|
||||||
ss.Names = gosettings.MergeWithSlice(ss.Names, other.Names)
|
|
||||||
ss.Numbers = gosettings.MergeWithSlice(ss.Numbers, other.Numbers)
|
|
||||||
ss.OwnedOnly = gosettings.MergeWithPointer(ss.OwnedOnly, other.OwnedOnly)
|
|
||||||
ss.FreeOnly = gosettings.MergeWithPointer(ss.FreeOnly, other.FreeOnly)
|
|
||||||
ss.PremiumOnly = gosettings.MergeWithPointer(ss.PremiumOnly, other.PremiumOnly)
|
|
||||||
ss.StreamOnly = gosettings.MergeWithPointer(ss.StreamOnly, other.StreamOnly)
|
|
||||||
ss.MultiHopOnly = gosettings.MergeWithPointer(ss.MultiHopOnly, other.MultiHopOnly)
|
|
||||||
ss.PortForwardOnly = gosettings.MergeWithPointer(ss.PortForwardOnly, other.PortForwardOnly)
|
|
||||||
|
|
||||||
ss.OpenVPN.mergeWith(other.OpenVPN)
|
|
||||||
ss.Wireguard.mergeWith(other.Wireguard)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *ServerSelection) overrideWith(other ServerSelection) {
|
func (ss *ServerSelection) overrideWith(other ServerSelection) {
|
||||||
ss.VPN = gosettings.OverrideWithString(ss.VPN, other.VPN)
|
ss.VPN = gosettings.OverrideWithComparable(ss.VPN, other.VPN)
|
||||||
ss.TargetIP = gosettings.OverrideWithValidator(ss.TargetIP, other.TargetIP)
|
ss.TargetIP = gosettings.OverrideWithValidator(ss.TargetIP, other.TargetIP)
|
||||||
ss.Countries = gosettings.OverrideWithSlice(ss.Countries, other.Countries)
|
ss.Countries = gosettings.OverrideWithSlice(ss.Countries, other.Countries)
|
||||||
ss.Categories = gosettings.OverrideWithSlice(ss.Categories, other.Categories)
|
ss.Categories = gosettings.OverrideWithSlice(ss.Categories, other.Categories)
|
||||||
@@ -301,7 +280,7 @@ func (ss *ServerSelection) overrideWith(other ServerSelection) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ss *ServerSelection) setDefaults(vpnProvider string) {
|
func (ss *ServerSelection) setDefaults(vpnProvider string) {
|
||||||
ss.VPN = gosettings.DefaultString(ss.VPN, vpn.OpenVPN)
|
ss.VPN = gosettings.DefaultComparable(ss.VPN, vpn.OpenVPN)
|
||||||
ss.TargetIP = gosettings.DefaultValidator(ss.TargetIP, netip.IPv4Unspecified())
|
ss.TargetIP = gosettings.DefaultValidator(ss.TargetIP, netip.IPv4Unspecified())
|
||||||
ss.OwnedOnly = gosettings.DefaultPointer(ss.OwnedOnly, false)
|
ss.OwnedOnly = gosettings.DefaultPointer(ss.OwnedOnly, false)
|
||||||
ss.FreeOnly = gosettings.DefaultPointer(ss.FreeOnly, false)
|
ss.FreeOnly = gosettings.DefaultPointer(ss.FreeOnly, false)
|
||||||
@@ -394,3 +373,79 @@ func (ss ServerSelection) WithDefaults(provider string) ServerSelection {
|
|||||||
ss.setDefaults(provider)
|
ss.setDefaults(provider)
|
||||||
return ss
|
return ss
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ss *ServerSelection) read(r *reader.Reader,
|
||||||
|
vpnProvider, vpnType string) (err error) {
|
||||||
|
ss.VPN = vpnType
|
||||||
|
|
||||||
|
ss.TargetIP, err = r.NetipAddr("VPN_ENDPOINT_IP",
|
||||||
|
reader.RetroKeys("OPENVPN_TARGET_IP"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
countriesRetroKeys := []string{"COUNTRY"}
|
||||||
|
if vpnProvider == providers.Cyberghost {
|
||||||
|
countriesRetroKeys = append(countriesRetroKeys, "REGION")
|
||||||
|
}
|
||||||
|
ss.Countries = r.CSV("SERVER_COUNTRIES", reader.RetroKeys(countriesRetroKeys...))
|
||||||
|
|
||||||
|
ss.Regions = r.CSV("SERVER_REGIONS", reader.RetroKeys("REGION"))
|
||||||
|
ss.Cities = r.CSV("SERVER_CITIES", reader.RetroKeys("CITY"))
|
||||||
|
ss.ISPs = r.CSV("ISP")
|
||||||
|
ss.Hostnames = r.CSV("SERVER_HOSTNAMES", reader.RetroKeys("SERVER_HOSTNAME"))
|
||||||
|
ss.Names = r.CSV("SERVER_NAMES", reader.RetroKeys("SERVER_NAME"))
|
||||||
|
ss.Numbers, err = r.CSVUint16("SERVER_NUMBER")
|
||||||
|
ss.Categories = r.CSV("SERVER_CATEGORIES")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mullvad only
|
||||||
|
ss.OwnedOnly, err = r.BoolPtr("OWNED_ONLY", reader.RetroKeys("OWNED"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// VPNUnlimited and ProtonVPN only
|
||||||
|
ss.FreeOnly, err = r.BoolPtr("FREE_ONLY")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// VPNSecure only
|
||||||
|
ss.PremiumOnly, err = r.BoolPtr("PREMIUM_ONLY")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Surfshark only
|
||||||
|
ss.MultiHopOnly, err = r.BoolPtr("MULTIHOP_ONLY")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// VPNUnlimited only
|
||||||
|
ss.StreamOnly, err = r.BoolPtr("STREAM_ONLY")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PIA only
|
||||||
|
ss.PortForwardOnly, err = r.BoolPtr("PORT_FORWARD_ONLY")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ss.OpenVPN.read(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ss.Wireguard.read(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ package settings
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
"github.com/qdm12/gluetun/internal/constants/vpn"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/gluetun/internal/pprof"
|
"github.com/qdm12/gluetun/internal/pprof"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -81,22 +83,6 @@ func (s *Settings) copy() (copied Settings) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Settings) MergeWith(other Settings) {
|
|
||||||
s.ControlServer.mergeWith(other.ControlServer)
|
|
||||||
s.DNS.mergeWith(other.DNS)
|
|
||||||
s.Firewall.mergeWith(other.Firewall)
|
|
||||||
s.Health.MergeWith(other.Health)
|
|
||||||
s.HTTPProxy.mergeWith(other.HTTPProxy)
|
|
||||||
s.Log.mergeWith(other.Log)
|
|
||||||
s.PublicIP.mergeWith(other.PublicIP)
|
|
||||||
s.Shadowsocks.mergeWith(other.Shadowsocks)
|
|
||||||
s.System.mergeWith(other.System)
|
|
||||||
s.Updater.mergeWith(other.Updater)
|
|
||||||
s.Version.mergeWith(other.Version)
|
|
||||||
s.VPN.mergeWith(other.VPN)
|
|
||||||
s.Pprof.MergeWith(other.Pprof)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Settings) OverrideWith(other Settings,
|
func (s *Settings) OverrideWith(other Settings,
|
||||||
storage Storage, ipv6Supported bool) (err error) {
|
storage Storage, ipv6Supported bool) (err error) {
|
||||||
patchedSettings := s.copy()
|
patchedSettings := s.copy()
|
||||||
@@ -179,5 +165,41 @@ func (s Settings) Warnings() (warnings []string) {
|
|||||||
"by creating an issue, attaching the new certificate and we will update Gluetun.")
|
"by creating an issue, attaching the new certificate and we will update Gluetun.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO remove in v4
|
||||||
|
if s.DNS.ServerAddress.Unmap().Compare(netip.AddrFrom4([4]byte{127, 0, 0, 1})) != 0 {
|
||||||
|
warnings = append(warnings, "DNS address is set to "+s.DNS.ServerAddress.String()+
|
||||||
|
" so the DNS over TLS (DoT) server will not be used."+
|
||||||
|
" The default value changed to 127.0.0.1 so it uses the internal DoT serves."+
|
||||||
|
" If the DoT server fails to start, the IPv4 address of the first plaintext DNS server"+
|
||||||
|
" corresponding to the first DoT provider chosen is used.")
|
||||||
|
}
|
||||||
|
|
||||||
return warnings
|
return warnings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Settings) Read(r *reader.Reader) (err error) {
|
||||||
|
readFunctions := map[string]func(r *reader.Reader) error{
|
||||||
|
"control server": s.ControlServer.read,
|
||||||
|
"DNS": s.DNS.read,
|
||||||
|
"firewall": s.Firewall.read,
|
||||||
|
"health": s.Health.Read,
|
||||||
|
"http proxy": s.HTTPProxy.read,
|
||||||
|
"log": s.Log.read,
|
||||||
|
"public ip": s.PublicIP.read,
|
||||||
|
"shadowsocks": s.Shadowsocks.read,
|
||||||
|
"system": s.System.read,
|
||||||
|
"updater": s.Updater.read,
|
||||||
|
"version": s.Version.read,
|
||||||
|
"VPN": s.VPN.read,
|
||||||
|
"profiling": s.Pprof.Read,
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, read := range readFunctions {
|
||||||
|
err = read(r)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("reading %s settings: %w", name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package settings
|
package settings
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
"github.com/qdm12/ss-server/pkg/tcpudp"
|
"github.com/qdm12/ss-server/pkg/tcpudp"
|
||||||
)
|
)
|
||||||
@@ -26,13 +29,6 @@ func (s *Shadowsocks) copy() (copied Shadowsocks) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
|
||||||
// unset field of the receiver settings object.
|
|
||||||
func (s *Shadowsocks) mergeWith(other Shadowsocks) {
|
|
||||||
s.Enabled = gosettings.MergeWithPointer(s.Enabled, other.Enabled)
|
|
||||||
s.Settings = s.Settings.MergeWith(other.Settings)
|
|
||||||
}
|
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
@@ -59,10 +55,44 @@ func (s Shadowsocks) toLinesNode() (node *gotree.Node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO have ToLinesNode in qdm12/ss-server
|
// TODO have ToLinesNode in qdm12/ss-server
|
||||||
node.Appendf("Listening address: %s", *s.Address)
|
node.Appendf("Listening address: %s", *s.Settings.Address)
|
||||||
node.Appendf("Cipher: %s", s.CipherName)
|
node.Appendf("Cipher: %s", s.Settings.CipherName)
|
||||||
node.Appendf("Password: %s", gosettings.ObfuscateKey(*s.Password))
|
node.Appendf("Password: %s", gosettings.ObfuscateKey(*s.Settings.Password))
|
||||||
node.Appendf("Log addresses: %s", gosettings.BoolToYesNo(s.LogAddresses))
|
node.Appendf("Log addresses: %s", gosettings.BoolToYesNo(s.Settings.LogAddresses))
|
||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Shadowsocks) read(r *reader.Reader) (err error) {
|
||||||
|
s.Enabled, err = r.BoolPtr("SHADOWSOCKS")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Settings.Address, err = readShadowsocksAddress(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.Settings.LogAddresses, err = r.BoolPtr("SHADOWSOCKS_LOG")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.Settings.CipherName = r.String("SHADOWSOCKS_CIPHER",
|
||||||
|
reader.RetroKeys("SHADOWSOCKS_METHOD"))
|
||||||
|
s.Settings.Password = r.Get("SHADOWSOCKS_PASSWORD",
|
||||||
|
reader.ForceLowercase(false))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readShadowsocksAddress(r *reader.Reader) (address *string, err error) {
|
||||||
|
const currentKey = "SHADOWSOCKS_LISTENING_ADDRESS"
|
||||||
|
port, err := r.Uint16Ptr("SHADOWSOCKS_PORT", reader.IsRetro(currentKey)) // retro-compatibility
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if port != nil {
|
||||||
|
return ptrTo(fmt.Sprintf(":%d", *port)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Get(currentKey), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package settings
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,16 +26,10 @@ func (s *System) copy() (copied System) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *System) mergeWith(other System) {
|
|
||||||
s.PUID = gosettings.MergeWithPointer(s.PUID, other.PUID)
|
|
||||||
s.PGID = gosettings.MergeWithPointer(s.PGID, other.PGID)
|
|
||||||
s.Timezone = gosettings.MergeWithString(s.Timezone, other.Timezone)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *System) overrideWith(other System) {
|
func (s *System) overrideWith(other System) {
|
||||||
s.PUID = gosettings.OverrideWithPointer(s.PUID, other.PUID)
|
s.PUID = gosettings.OverrideWithPointer(s.PUID, other.PUID)
|
||||||
s.PGID = gosettings.OverrideWithPointer(s.PGID, other.PGID)
|
s.PGID = gosettings.OverrideWithPointer(s.PGID, other.PGID)
|
||||||
s.Timezone = gosettings.OverrideWithString(s.Timezone, other.Timezone)
|
s.Timezone = gosettings.OverrideWithComparable(s.Timezone, other.Timezone)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *System) setDefaults() {
|
func (s *System) setDefaults() {
|
||||||
@@ -59,3 +54,18 @@ func (s System) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *System) read(r *reader.Reader) (err error) {
|
||||||
|
s.PUID, err = r.Uint32Ptr("PUID", reader.RetroKeys("UID"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.PGID, err = r.Uint32Ptr("PGID", reader.RetroKeys("GID"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Timezone = r.String("TZ")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/qdm12/dns/pkg/provider"
|
"github.com/qdm12/dns/pkg/provider"
|
||||||
"github.com/qdm12/dns/pkg/unbound"
|
"github.com/qdm12/dns/pkg/unbound"
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ func (u *Unbound) setDefaults() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u.Username = gosettings.DefaultString(u.Username, "root")
|
u.Username = gosettings.DefaultComparable(u.Username, "root")
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -105,17 +106,6 @@ func (u Unbound) copy() (copied Unbound) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Unbound) mergeWith(other Unbound) {
|
|
||||||
u.Providers = gosettings.MergeWithSlice(u.Providers, other.Providers)
|
|
||||||
u.Caching = gosettings.MergeWithPointer(u.Caching, other.Caching)
|
|
||||||
u.IPv6 = gosettings.MergeWithPointer(u.IPv6, other.IPv6)
|
|
||||||
u.VerbosityLevel = gosettings.MergeWithPointer(u.VerbosityLevel, other.VerbosityLevel)
|
|
||||||
u.VerbosityDetailsLevel = gosettings.MergeWithPointer(u.VerbosityDetailsLevel, other.VerbosityDetailsLevel)
|
|
||||||
u.ValidationLogLevel = gosettings.MergeWithPointer(u.ValidationLogLevel, other.ValidationLogLevel)
|
|
||||||
u.Username = gosettings.MergeWithString(u.Username, other.Username)
|
|
||||||
u.Allowed = gosettings.MergeWithSlice(u.Allowed, other.Allowed)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *Unbound) overrideWith(other Unbound) {
|
func (u *Unbound) overrideWith(other Unbound) {
|
||||||
u.Providers = gosettings.OverrideWithSlice(u.Providers, other.Providers)
|
u.Providers = gosettings.OverrideWithSlice(u.Providers, other.Providers)
|
||||||
u.Caching = gosettings.OverrideWithPointer(u.Caching, other.Caching)
|
u.Caching = gosettings.OverrideWithPointer(u.Caching, other.Caching)
|
||||||
@@ -123,7 +113,7 @@ func (u *Unbound) overrideWith(other Unbound) {
|
|||||||
u.VerbosityLevel = gosettings.OverrideWithPointer(u.VerbosityLevel, other.VerbosityLevel)
|
u.VerbosityLevel = gosettings.OverrideWithPointer(u.VerbosityLevel, other.VerbosityLevel)
|
||||||
u.VerbosityDetailsLevel = gosettings.OverrideWithPointer(u.VerbosityDetailsLevel, other.VerbosityDetailsLevel)
|
u.VerbosityDetailsLevel = gosettings.OverrideWithPointer(u.VerbosityDetailsLevel, other.VerbosityDetailsLevel)
|
||||||
u.ValidationLogLevel = gosettings.OverrideWithPointer(u.ValidationLogLevel, other.ValidationLogLevel)
|
u.ValidationLogLevel = gosettings.OverrideWithPointer(u.ValidationLogLevel, other.ValidationLogLevel)
|
||||||
u.Username = gosettings.OverrideWithString(u.Username, other.Username)
|
u.Username = gosettings.OverrideWithComparable(u.Username, other.Username)
|
||||||
u.Allowed = gosettings.OverrideWithSlice(u.Allowed, other.Allowed)
|
u.Allowed = gosettings.OverrideWithSlice(u.Allowed, other.Allowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,3 +190,34 @@ func (u Unbound) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *Unbound) read(reader *reader.Reader) (err error) {
|
||||||
|
u.Providers = reader.CSV("DOT_PROVIDERS")
|
||||||
|
|
||||||
|
u.Caching, err = reader.BoolPtr("DOT_CACHING")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
u.IPv6, err = reader.BoolPtr("DOT_IPV6")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
u.VerbosityLevel, err = reader.Uint8Ptr("DOT_VERBOSITY")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
u.VerbosityDetailsLevel, err = reader.Uint8Ptr("DOT_VERBOSITY_DETAILS")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
u.ValidationLogLevel, err = reader.Uint8Ptr("DOT_VALIDATION_LOGLEVEL")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gosettings/validate"
|
"github.com/qdm12/gosettings/validate"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
@@ -64,28 +65,19 @@ func (u *Updater) copy() (copied Updater) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
|
||||||
// unset field of the receiver settings object.
|
|
||||||
func (u *Updater) mergeWith(other Updater) {
|
|
||||||
u.Period = gosettings.MergeWithPointer(u.Period, other.Period)
|
|
||||||
u.DNSAddress = gosettings.MergeWithString(u.DNSAddress, other.DNSAddress)
|
|
||||||
u.MinRatio = gosettings.MergeWithNumber(u.MinRatio, other.MinRatio)
|
|
||||||
u.Providers = gosettings.MergeWithSlice(u.Providers, other.Providers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (u *Updater) overrideWith(other Updater) {
|
func (u *Updater) overrideWith(other Updater) {
|
||||||
u.Period = gosettings.OverrideWithPointer(u.Period, other.Period)
|
u.Period = gosettings.OverrideWithPointer(u.Period, other.Period)
|
||||||
u.DNSAddress = gosettings.OverrideWithString(u.DNSAddress, other.DNSAddress)
|
u.DNSAddress = gosettings.OverrideWithComparable(u.DNSAddress, other.DNSAddress)
|
||||||
u.MinRatio = gosettings.OverrideWithNumber(u.MinRatio, other.MinRatio)
|
u.MinRatio = gosettings.OverrideWithComparable(u.MinRatio, other.MinRatio)
|
||||||
u.Providers = gosettings.OverrideWithSlice(u.Providers, other.Providers)
|
u.Providers = gosettings.OverrideWithSlice(u.Providers, other.Providers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Updater) SetDefaults(vpnProvider string) {
|
func (u *Updater) SetDefaults(vpnProvider string) {
|
||||||
u.Period = gosettings.DefaultPointer(u.Period, 0)
|
u.Period = gosettings.DefaultPointer(u.Period, 0)
|
||||||
u.DNSAddress = gosettings.DefaultString(u.DNSAddress, "1.1.1.1:53")
|
u.DNSAddress = gosettings.DefaultComparable(u.DNSAddress, "1.1.1.1:53")
|
||||||
|
|
||||||
if u.MinRatio == 0 {
|
if u.MinRatio == 0 {
|
||||||
const defaultMinRatio = 0.8
|
const defaultMinRatio = 0.8
|
||||||
@@ -114,3 +106,33 @@ func (u Updater) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *Updater) read(r *reader.Reader) (err error) {
|
||||||
|
u.Period, err = r.DurationPtr("UPDATER_PERIOD")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
u.DNSAddress, err = readUpdaterDNSAddress()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
u.MinRatio, err = r.Float64("UPDATER_MIN_RATIO")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
u.Providers = r.CSV("UPDATER_VPN_SERVICE_PROVIDERS")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readUpdaterDNSAddress() (address string, err error) {
|
||||||
|
// TODO this is currently using Cloudflare in
|
||||||
|
// plaintext to not be blocked by DNS over TLS by default.
|
||||||
|
// If a plaintext address is set in the DNS settings, this one will be used.
|
||||||
|
// use custom future encrypted DNS written in Go without blocking
|
||||||
|
// as it's too much trouble to start another parallel unbound instance for now.
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package settings
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,12 +24,6 @@ func (v *Version) copy() (copied Version) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeWith merges the other settings into any
|
|
||||||
// unset field of the receiver settings object.
|
|
||||||
func (v *Version) mergeWith(other Version) {
|
|
||||||
v.Enabled = gosettings.MergeWithPointer(v.Enabled, other.Enabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
// overrideWith overrides fields of the receiver
|
// overrideWith overrides fields of the receiver
|
||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
@@ -51,3 +46,12 @@ func (v Version) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Version) read(r *reader.Reader) (err error) {
|
||||||
|
v.Enabled, err = r.BoolPtr("VERSION_INFORMATION")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
"github.com/qdm12/gluetun/internal/constants/vpn"
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gosettings/validate"
|
"github.com/qdm12/gosettings/validate"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
@@ -56,22 +57,15 @@ func (v *VPN) Copy() (copied VPN) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VPN) mergeWith(other VPN) {
|
|
||||||
v.Type = gosettings.MergeWithString(v.Type, other.Type)
|
|
||||||
v.Provider.mergeWith(other.Provider)
|
|
||||||
v.OpenVPN.mergeWith(other.OpenVPN)
|
|
||||||
v.Wireguard.mergeWith(other.Wireguard)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *VPN) OverrideWith(other VPN) {
|
func (v *VPN) OverrideWith(other VPN) {
|
||||||
v.Type = gosettings.OverrideWithString(v.Type, other.Type)
|
v.Type = gosettings.OverrideWithComparable(v.Type, other.Type)
|
||||||
v.Provider.overrideWith(other.Provider)
|
v.Provider.overrideWith(other.Provider)
|
||||||
v.OpenVPN.overrideWith(other.OpenVPN)
|
v.OpenVPN.overrideWith(other.OpenVPN)
|
||||||
v.Wireguard.overrideWith(other.Wireguard)
|
v.Wireguard.overrideWith(other.Wireguard)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VPN) setDefaults() {
|
func (v *VPN) setDefaults() {
|
||||||
v.Type = gosettings.DefaultString(v.Type, vpn.OpenVPN)
|
v.Type = gosettings.DefaultComparable(v.Type, vpn.OpenVPN)
|
||||||
v.Provider.setDefaults()
|
v.Provider.setDefaults()
|
||||||
v.OpenVPN.setDefaults(v.Provider.Name)
|
v.OpenVPN.setDefaults(v.Provider.Name)
|
||||||
v.Wireguard.setDefaults(v.Provider.Name)
|
v.Wireguard.setDefaults(v.Provider.Name)
|
||||||
@@ -94,3 +88,24 @@ func (v VPN) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *VPN) read(r *reader.Reader) (err error) {
|
||||||
|
v.Type = r.String("VPN_TYPE")
|
||||||
|
|
||||||
|
err = v.Provider.read(r, v.Type)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("VPN provider: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = v.OpenVPN.read(r)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("OpenVPN: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = v.Wireguard.read(r)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("wireguard: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gosettings/validate"
|
"github.com/qdm12/gosettings/validate"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
@@ -147,24 +148,14 @@ func (w *Wireguard) copy() (copied Wireguard) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wireguard) mergeWith(other Wireguard) {
|
|
||||||
w.PrivateKey = gosettings.MergeWithPointer(w.PrivateKey, other.PrivateKey)
|
|
||||||
w.PreSharedKey = gosettings.MergeWithPointer(w.PreSharedKey, other.PreSharedKey)
|
|
||||||
w.Addresses = gosettings.MergeWithSlice(w.Addresses, other.Addresses)
|
|
||||||
w.AllowedIPs = gosettings.MergeWithSlice(w.AllowedIPs, other.AllowedIPs)
|
|
||||||
w.Interface = gosettings.MergeWithString(w.Interface, other.Interface)
|
|
||||||
w.MTU = gosettings.MergeWithNumber(w.MTU, other.MTU)
|
|
||||||
w.Implementation = gosettings.MergeWithString(w.Implementation, other.Implementation)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Wireguard) overrideWith(other Wireguard) {
|
func (w *Wireguard) overrideWith(other Wireguard) {
|
||||||
w.PrivateKey = gosettings.OverrideWithPointer(w.PrivateKey, other.PrivateKey)
|
w.PrivateKey = gosettings.OverrideWithPointer(w.PrivateKey, other.PrivateKey)
|
||||||
w.PreSharedKey = gosettings.OverrideWithPointer(w.PreSharedKey, other.PreSharedKey)
|
w.PreSharedKey = gosettings.OverrideWithPointer(w.PreSharedKey, other.PreSharedKey)
|
||||||
w.Addresses = gosettings.OverrideWithSlice(w.Addresses, other.Addresses)
|
w.Addresses = gosettings.OverrideWithSlice(w.Addresses, other.Addresses)
|
||||||
w.AllowedIPs = gosettings.OverrideWithSlice(w.AllowedIPs, other.AllowedIPs)
|
w.AllowedIPs = gosettings.OverrideWithSlice(w.AllowedIPs, other.AllowedIPs)
|
||||||
w.Interface = gosettings.OverrideWithString(w.Interface, other.Interface)
|
w.Interface = gosettings.OverrideWithComparable(w.Interface, other.Interface)
|
||||||
w.MTU = gosettings.OverrideWithNumber(w.MTU, other.MTU)
|
w.MTU = gosettings.OverrideWithComparable(w.MTU, other.MTU)
|
||||||
w.Implementation = gosettings.OverrideWithString(w.Implementation, other.Implementation)
|
w.Implementation = gosettings.OverrideWithComparable(w.Implementation, other.Implementation)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wireguard) setDefaults(vpnProvider string) {
|
func (w *Wireguard) setDefaults(vpnProvider string) {
|
||||||
@@ -180,10 +171,10 @@ func (w *Wireguard) setDefaults(vpnProvider string) {
|
|||||||
netip.PrefixFrom(netip.IPv6Unspecified(), 0),
|
netip.PrefixFrom(netip.IPv6Unspecified(), 0),
|
||||||
}
|
}
|
||||||
w.AllowedIPs = gosettings.DefaultSlice(w.AllowedIPs, defaultAllowedIPs)
|
w.AllowedIPs = gosettings.DefaultSlice(w.AllowedIPs, defaultAllowedIPs)
|
||||||
w.Interface = gosettings.DefaultString(w.Interface, "wg0")
|
w.Interface = gosettings.DefaultComparable(w.Interface, "wg0")
|
||||||
const defaultMTU = 1400
|
const defaultMTU = 1400
|
||||||
w.MTU = gosettings.DefaultNumber(w.MTU, defaultMTU)
|
w.MTU = gosettings.DefaultComparable(w.MTU, defaultMTU)
|
||||||
w.Implementation = gosettings.DefaultString(w.Implementation, "auto")
|
w.Implementation = gosettings.DefaultComparable(w.Implementation, "auto")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w Wireguard) String() string {
|
func (w Wireguard) String() string {
|
||||||
@@ -222,3 +213,27 @@ func (w Wireguard) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Wireguard) read(r *reader.Reader) (err error) {
|
||||||
|
w.PrivateKey = r.Get("WIREGUARD_PRIVATE_KEY", reader.ForceLowercase(false))
|
||||||
|
w.PreSharedKey = r.Get("WIREGUARD_PRESHARED_KEY", reader.ForceLowercase(false))
|
||||||
|
w.Interface = r.String("VPN_INTERFACE",
|
||||||
|
reader.RetroKeys("WIREGUARD_INTERFACE"), reader.ForceLowercase(false))
|
||||||
|
w.Implementation = r.String("WIREGUARD_IMPLEMENTATION")
|
||||||
|
w.Addresses, err = r.CSVNetipPrefixes("WIREGUARD_ADDRESSES",
|
||||||
|
reader.RetroKeys("WIREGUARD_ADDRESS"))
|
||||||
|
if err != nil {
|
||||||
|
return err // already wrapped
|
||||||
|
}
|
||||||
|
w.AllowedIPs, err = r.CSVNetipPrefixes("WIREGUARD_ALLOWED_IPS")
|
||||||
|
if err != nil {
|
||||||
|
return err // already wrapped
|
||||||
|
}
|
||||||
|
mtuPtr, err := r.Uint16Ptr("WIREGUARD_MTU")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if mtuPtr != nil {
|
||||||
|
w.MTU = *mtuPtr
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gosettings/validate"
|
"github.com/qdm12/gosettings/validate"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
@@ -116,16 +117,10 @@ func (w *WireguardSelection) copy() (copied WireguardSelection) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WireguardSelection) mergeWith(other WireguardSelection) {
|
|
||||||
w.EndpointIP = gosettings.MergeWithValidator(w.EndpointIP, other.EndpointIP)
|
|
||||||
w.EndpointPort = gosettings.MergeWithPointer(w.EndpointPort, other.EndpointPort)
|
|
||||||
w.PublicKey = gosettings.MergeWithString(w.PublicKey, other.PublicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WireguardSelection) overrideWith(other WireguardSelection) {
|
func (w *WireguardSelection) overrideWith(other WireguardSelection) {
|
||||||
w.EndpointIP = gosettings.OverrideWithValidator(w.EndpointIP, other.EndpointIP)
|
w.EndpointIP = gosettings.OverrideWithValidator(w.EndpointIP, other.EndpointIP)
|
||||||
w.EndpointPort = gosettings.OverrideWithPointer(w.EndpointPort, other.EndpointPort)
|
w.EndpointPort = gosettings.OverrideWithPointer(w.EndpointPort, other.EndpointPort)
|
||||||
w.PublicKey = gosettings.OverrideWithString(w.PublicKey, other.PublicKey)
|
w.PublicKey = gosettings.OverrideWithComparable(w.PublicKey, other.PublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WireguardSelection) setDefaults() {
|
func (w *WireguardSelection) setDefaults() {
|
||||||
@@ -154,3 +149,18 @@ func (w WireguardSelection) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *WireguardSelection) read(r *reader.Reader) (err error) {
|
||||||
|
w.EndpointIP, err = r.NetipAddr("VPN_ENDPOINT_IP", reader.RetroKeys("WIREGUARD_ENDPOINT_IP"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.EndpointPort, err = r.Uint16Ptr("VPN_ENDPOINT_PORT", reader.RetroKeys("WIREGUARD_ENDPOINT_PORT"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.PublicKey = r.String("WIREGUARD_PUBLIC_KEY", reader.ForceLowercase(false))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
55
internal/configuration/sources/env/dns.go
vendored
55
internal/configuration/sources/env/dns.go
vendored
@@ -1,55 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/netip"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readDNS() (dns settings.DNS, err error) {
|
|
||||||
dns.ServerAddress, err = s.readDNSServerAddress()
|
|
||||||
if err != nil {
|
|
||||||
return dns, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dns.KeepNameserver, err = s.env.BoolPtr("DNS_KEEP_NAMESERVER")
|
|
||||||
if err != nil {
|
|
||||||
return dns, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dns.DoT, err = s.readDoT()
|
|
||||||
if err != nil {
|
|
||||||
return dns, fmt.Errorf("DoT settings: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return dns, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Source) readDNSServerAddress() (address netip.Addr, err error) {
|
|
||||||
const currentKey = "DNS_ADDRESS"
|
|
||||||
key := firstKeySet(s.env, "DNS_PLAINTEXT_ADDRESS", currentKey)
|
|
||||||
switch key {
|
|
||||||
case "":
|
|
||||||
return address, nil
|
|
||||||
case currentKey:
|
|
||||||
default: // Retro-compatibility
|
|
||||||
s.handleDeprecatedKey(key, currentKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
address, err = s.env.NetipAddr(key)
|
|
||||||
if err != nil {
|
|
||||||
return address, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO remove in v4
|
|
||||||
if address.Unmap().Compare(netip.AddrFrom4([4]byte{127, 0, 0, 1})) != 0 {
|
|
||||||
s.warner.Warn(key + " is set to " + address.String() +
|
|
||||||
" so the DNS over TLS (DoT) server will not be used." +
|
|
||||||
" The default value changed to 127.0.0.1 so it uses the internal DoT serves." +
|
|
||||||
" If the DoT server fails to start, the IPv4 address of the first plaintext DNS server" +
|
|
||||||
" corresponding to the first DoT provider chosen is used.")
|
|
||||||
}
|
|
||||||
|
|
||||||
return address, nil
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/netip"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readDNSBlacklist() (blacklist settings.DNSBlacklist, err error) {
|
|
||||||
blacklist.BlockMalicious, err = s.env.BoolPtr("BLOCK_MALICIOUS")
|
|
||||||
if err != nil {
|
|
||||||
return blacklist, err
|
|
||||||
}
|
|
||||||
|
|
||||||
blacklist.BlockSurveillance, err = s.env.BoolPtr("BLOCK_SURVEILLANCE",
|
|
||||||
env.RetroKeys("BLOCK_NSA"))
|
|
||||||
if err != nil {
|
|
||||||
return blacklist, err
|
|
||||||
}
|
|
||||||
|
|
||||||
blacklist.BlockAds, err = s.env.BoolPtr("BLOCK_ADS")
|
|
||||||
if err != nil {
|
|
||||||
return blacklist, err
|
|
||||||
}
|
|
||||||
|
|
||||||
blacklist.AddBlockedIPs, blacklist.AddBlockedIPPrefixes,
|
|
||||||
err = s.readDoTPrivateAddresses() // TODO v4 split in 2
|
|
||||||
if err != nil {
|
|
||||||
return blacklist, err
|
|
||||||
}
|
|
||||||
|
|
||||||
blacklist.AllowedHosts = s.env.CSV("UNBLOCK") // TODO v4 change name
|
|
||||||
|
|
||||||
return blacklist, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrPrivateAddressNotValid = errors.New("private address is not a valid IP or CIDR range")
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readDoTPrivateAddresses() (ips []netip.Addr,
|
|
||||||
ipPrefixes []netip.Prefix, err error) {
|
|
||||||
privateAddresses := s.env.CSV("DOT_PRIVATE_ADDRESS")
|
|
||||||
if len(privateAddresses) == 0 {
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ips = make([]netip.Addr, 0, len(privateAddresses))
|
|
||||||
ipPrefixes = make([]netip.Prefix, 0, len(privateAddresses))
|
|
||||||
|
|
||||||
for _, privateAddress := range privateAddresses {
|
|
||||||
ip, err := netip.ParseAddr(privateAddress)
|
|
||||||
if err == nil {
|
|
||||||
ips = append(ips, ip)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ipPrefix, err := netip.ParsePrefix(privateAddress)
|
|
||||||
if err == nil {
|
|
||||||
ipPrefixes = append(ipPrefixes, ipPrefix)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil, fmt.Errorf(
|
|
||||||
"environment variable DOT_PRIVATE_ADDRESS: %w: %s",
|
|
||||||
ErrPrivateAddressNotValid, privateAddress)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ips, ipPrefixes, nil
|
|
||||||
}
|
|
||||||
29
internal/configuration/sources/env/dot.go
vendored
29
internal/configuration/sources/env/dot.go
vendored
@@ -1,29 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readDoT() (dot settings.DoT, err error) {
|
|
||||||
dot.Enabled, err = s.env.BoolPtr("DOT")
|
|
||||||
if err != nil {
|
|
||||||
return dot, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dot.UpdatePeriod, err = s.env.DurationPtr("DNS_UPDATE_PERIOD")
|
|
||||||
if err != nil {
|
|
||||||
return dot, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dot.Unbound, err = s.readUnbound()
|
|
||||||
if err != nil {
|
|
||||||
return dot, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dot.Blacklist, err = s.readDNSBlacklist()
|
|
||||||
if err != nil {
|
|
||||||
return dot, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return dot, nil
|
|
||||||
}
|
|
||||||
36
internal/configuration/sources/env/firewall.go
vendored
36
internal/configuration/sources/env/firewall.go
vendored
@@ -1,36 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readFirewall() (firewall settings.Firewall, err error) {
|
|
||||||
firewall.VPNInputPorts, err = s.env.CSVUint16("FIREWALL_VPN_INPUT_PORTS")
|
|
||||||
if err != nil {
|
|
||||||
return firewall, err
|
|
||||||
}
|
|
||||||
|
|
||||||
firewall.InputPorts, err = s.env.CSVUint16("FIREWALL_INPUT_PORTS")
|
|
||||||
if err != nil {
|
|
||||||
return firewall, err
|
|
||||||
}
|
|
||||||
|
|
||||||
firewall.OutboundSubnets, err = s.env.CSVNetipPrefixes("FIREWALL_OUTBOUND_SUBNETS",
|
|
||||||
env.RetroKeys("EXTRA_SUBNETS"))
|
|
||||||
if err != nil {
|
|
||||||
return firewall, err
|
|
||||||
}
|
|
||||||
|
|
||||||
firewall.Enabled, err = s.env.BoolPtr("FIREWALL")
|
|
||||||
if err != nil {
|
|
||||||
return firewall, err
|
|
||||||
}
|
|
||||||
|
|
||||||
firewall.Debug, err = s.env.BoolPtr("FIREWALL_DEBUG")
|
|
||||||
if err != nil {
|
|
||||||
return firewall, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return firewall, nil
|
|
||||||
}
|
|
||||||
35
internal/configuration/sources/env/health.go
vendored
35
internal/configuration/sources/env/health.go
vendored
@@ -1,35 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) ReadHealth() (health settings.Health, err error) {
|
|
||||||
health.ServerAddress = s.env.String("HEALTH_SERVER_ADDRESS")
|
|
||||||
health.TargetAddress = s.env.String("HEALTH_TARGET_ADDRESS",
|
|
||||||
env.RetroKeys("HEALTH_ADDRESS_TO_PING"))
|
|
||||||
|
|
||||||
successWaitPtr, err := s.env.DurationPtr("HEALTH_SUCCESS_WAIT_DURATION")
|
|
||||||
if err != nil {
|
|
||||||
return health, err
|
|
||||||
} else if successWaitPtr != nil {
|
|
||||||
health.SuccessWait = *successWaitPtr
|
|
||||||
}
|
|
||||||
|
|
||||||
health.VPN.Initial, err = s.env.DurationPtr(
|
|
||||||
"HEALTH_VPN_DURATION_INITIAL",
|
|
||||||
env.RetroKeys("HEALTH_OPENVPN_DURATION_INITIAL"))
|
|
||||||
if err != nil {
|
|
||||||
return health, err
|
|
||||||
}
|
|
||||||
|
|
||||||
health.VPN.Addition, err = s.env.DurationPtr(
|
|
||||||
"HEALTH_VPN_DURATION_ADDITION",
|
|
||||||
env.RetroKeys("HEALTH_OPENVPN_DURATION_ADDITION"))
|
|
||||||
if err != nil {
|
|
||||||
return health, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return health, nil
|
|
||||||
}
|
|
||||||
33
internal/configuration/sources/env/helpers.go
vendored
33
internal/configuration/sources/env/helpers.go
vendored
@@ -1,33 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
func unsetEnvKeys(envKeys []string, err error) (newErr error) {
|
|
||||||
newErr = err
|
|
||||||
for _, envKey := range envKeys {
|
|
||||||
unsetErr := os.Unsetenv(envKey)
|
|
||||||
if unsetErr != nil && newErr == nil {
|
|
||||||
newErr = fmt.Errorf("unsetting environment variable %s: %w", envKey, unsetErr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func ptrTo[T any](value T) *T {
|
|
||||||
return &value
|
|
||||||
}
|
|
||||||
|
|
||||||
func firstKeySet(e env.Env, keys ...string) (firstKeySet string) {
|
|
||||||
for _, key := range keys {
|
|
||||||
value := e.Get(key)
|
|
||||||
if value != nil {
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
84
internal/configuration/sources/env/httproxy.go
vendored
84
internal/configuration/sources/env/httproxy.go
vendored
@@ -1,84 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
"github.com/qdm12/govalid/binary"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readHTTPProxy() (httpProxy settings.HTTPProxy, err error) {
|
|
||||||
httpProxy.User = s.env.Get("HTTPPROXY_USER",
|
|
||||||
env.RetroKeys("PROXY_USER", "TINYPROXY_USER"),
|
|
||||||
env.ForceLowercase(false))
|
|
||||||
|
|
||||||
httpProxy.Password = s.env.Get("HTTPPROXY_PASSWORD",
|
|
||||||
env.RetroKeys("PROXY_PASSWORD", "TINYPROXY_PASSWORD"),
|
|
||||||
env.ForceLowercase(false))
|
|
||||||
|
|
||||||
httpProxy.ListeningAddress, err = s.readHTTProxyListeningAddress()
|
|
||||||
if err != nil {
|
|
||||||
return httpProxy, err
|
|
||||||
}
|
|
||||||
|
|
||||||
httpProxy.Enabled, err = s.env.BoolPtr("HTTPPROXY", env.RetroKeys("PROXY", "TINYPROXY"))
|
|
||||||
if err != nil {
|
|
||||||
return httpProxy, err
|
|
||||||
}
|
|
||||||
|
|
||||||
httpProxy.Stealth, err = s.env.BoolPtr("HTTPPROXY_STEALTH")
|
|
||||||
if err != nil {
|
|
||||||
return httpProxy, err
|
|
||||||
}
|
|
||||||
|
|
||||||
httpProxy.Log, err = s.readHTTProxyLog()
|
|
||||||
if err != nil {
|
|
||||||
return httpProxy, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return httpProxy, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Source) readHTTProxyListeningAddress() (listeningAddress string, err error) {
|
|
||||||
const currentKey = "HTTPPROXY_LISTENING_ADDRESS"
|
|
||||||
key := firstKeySet(s.env, "HTTPPROXY_PORT", "TINYPROXY_PORT", "PROXY_PORT",
|
|
||||||
currentKey)
|
|
||||||
switch key {
|
|
||||||
case "":
|
|
||||||
return "", nil
|
|
||||||
case currentKey:
|
|
||||||
return s.env.String(key), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retro-compatible keys using a port only
|
|
||||||
s.handleDeprecatedKey(key, currentKey)
|
|
||||||
port, err := s.env.Uint16Ptr(key)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(":%d", *port), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Source) readHTTProxyLog() (enabled *bool, err error) {
|
|
||||||
const currentKey = "HTTPPROXY_LOG"
|
|
||||||
key := firstKeySet(s.env, "PROXY_LOG", "TINYPROXY_LOG", "HTTPPROXY_LOG")
|
|
||||||
switch key {
|
|
||||||
case "":
|
|
||||||
return nil, nil //nolint:nilnil
|
|
||||||
case currentKey:
|
|
||||||
return s.env.BoolPtr(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retro-compatible keys using different boolean verbs
|
|
||||||
s.handleDeprecatedKey(key, currentKey)
|
|
||||||
value := s.env.String(key)
|
|
||||||
retroOption := binary.OptionEnabled("on", "info", "connect", "notice")
|
|
||||||
|
|
||||||
enabled, err = binary.Validate(value, retroOption)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("environment variable %s: %w", key, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return enabled, nil
|
|
||||||
}
|
|
||||||
53
internal/configuration/sources/env/log.go
vendored
53
internal/configuration/sources/env/log.go
vendored
@@ -1,53 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readLog() (log settings.Log, err error) {
|
|
||||||
log.Level, err = s.readLogLevel()
|
|
||||||
if err != nil {
|
|
||||||
return log, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return log, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Source) readLogLevel() (level *log.Level, err error) {
|
|
||||||
value := s.env.String("LOG_LEVEL")
|
|
||||||
if value == "" {
|
|
||||||
return nil, nil //nolint:nilnil
|
|
||||||
}
|
|
||||||
|
|
||||||
level = new(log.Level)
|
|
||||||
*level, err = parseLogLevel(value)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("environment variable LOG_LEVEL: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return level, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var ErrLogLevelUnknown = errors.New("log level is unknown")
|
|
||||||
|
|
||||||
func parseLogLevel(s string) (level log.Level, err error) {
|
|
||||||
switch strings.ToLower(s) {
|
|
||||||
case "debug":
|
|
||||||
return log.LevelDebug, nil
|
|
||||||
case "info":
|
|
||||||
return log.LevelInfo, nil
|
|
||||||
case "warning":
|
|
||||||
return log.LevelWarn, nil
|
|
||||||
case "error":
|
|
||||||
return log.LevelError, nil
|
|
||||||
default:
|
|
||||||
return level, fmt.Errorf(
|
|
||||||
"%w: %q is not valid and can be one of debug, info, warning or error",
|
|
||||||
ErrLogLevelUnknown, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
77
internal/configuration/sources/env/openvpn.go
vendored
77
internal/configuration/sources/env/openvpn.go
vendored
@@ -1,77 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readOpenVPN() (
|
|
||||||
openVPN settings.OpenVPN, err error) {
|
|
||||||
defer func() {
|
|
||||||
err = unsetEnvKeys([]string{"OPENVPN_KEY", "OPENVPN_CERT",
|
|
||||||
"OPENVPN_KEY_PASSPHRASE", "OPENVPN_ENCRYPTED_KEY"}, err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
openVPN.Version = s.env.String("OPENVPN_VERSION")
|
|
||||||
openVPN.User = s.env.Get("OPENVPN_USER",
|
|
||||||
env.RetroKeys("USER"), env.ForceLowercase(false))
|
|
||||||
openVPN.Password = s.env.Get("OPENVPN_PASSWORD",
|
|
||||||
env.RetroKeys("PASSWORD"), env.ForceLowercase(false))
|
|
||||||
openVPN.ConfFile = s.env.Get("OPENVPN_CUSTOM_CONFIG", env.ForceLowercase(false))
|
|
||||||
openVPN.Ciphers = s.env.CSV("OPENVPN_CIPHERS", env.RetroKeys("OPENVPN_CIPHER"))
|
|
||||||
openVPN.Auth = s.env.Get("OPENVPN_AUTH")
|
|
||||||
openVPN.Cert = s.env.Get("OPENVPN_CERT", env.ForceLowercase(false))
|
|
||||||
openVPN.Key = s.env.Get("OPENVPN_KEY", env.ForceLowercase(false))
|
|
||||||
openVPN.EncryptedKey = s.env.Get("OPENVPN_ENCRYPTED_KEY", env.ForceLowercase(false))
|
|
||||||
openVPN.KeyPassphrase = s.env.Get("OPENVPN_KEY_PASSPHRASE", env.ForceLowercase(false))
|
|
||||||
|
|
||||||
openVPN.PIAEncPreset = s.readPIAEncryptionPreset()
|
|
||||||
|
|
||||||
openVPN.MSSFix, err = s.env.Uint16Ptr("OPENVPN_MSSFIX")
|
|
||||||
if err != nil {
|
|
||||||
return openVPN, err
|
|
||||||
}
|
|
||||||
|
|
||||||
openVPN.Interface = s.env.String("VPN_INTERFACE",
|
|
||||||
env.RetroKeys("OPENVPN_INTERFACE"), env.ForceLowercase(false))
|
|
||||||
|
|
||||||
openVPN.ProcessUser, err = s.readOpenVPNProcessUser()
|
|
||||||
if err != nil {
|
|
||||||
return openVPN, err
|
|
||||||
}
|
|
||||||
|
|
||||||
openVPN.Verbosity, err = s.env.IntPtr("OPENVPN_VERBOSITY")
|
|
||||||
if err != nil {
|
|
||||||
return openVPN, err
|
|
||||||
}
|
|
||||||
|
|
||||||
flagsPtr := s.env.Get("OPENVPN_FLAGS", env.ForceLowercase(false))
|
|
||||||
if flagsPtr != nil {
|
|
||||||
openVPN.Flags = strings.Fields(*flagsPtr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return openVPN, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Source) readPIAEncryptionPreset() (presetPtr *string) {
|
|
||||||
return s.env.Get(
|
|
||||||
"PRIVATE_INTERNET_ACCESS_OPENVPN_ENCRYPTION_PRESET",
|
|
||||||
env.RetroKeys("ENCRYPTION", "PIA_ENCRYPTION"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Source) readOpenVPNProcessUser() (processUser string, err error) {
|
|
||||||
value, err := s.env.BoolPtr("OPENVPN_ROOT") // Retro-compatibility
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
} else if value != nil {
|
|
||||||
if *value {
|
|
||||||
return "root", nil
|
|
||||||
}
|
|
||||||
const defaultNonRootUser = "nonrootuser"
|
|
||||||
return defaultNonRootUser, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.env.String("OPENVPN_PROCESS_USER"), nil
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readOpenVPNSelection() (
|
|
||||||
selection settings.OpenVPNSelection, err error) {
|
|
||||||
selection.ConfFile = s.env.Get("OPENVPN_CUSTOM_CONFIG", env.ForceLowercase(false))
|
|
||||||
|
|
||||||
selection.Protocol = s.env.String("OPENVPN_PROTOCOL", env.RetroKeys("PROTOCOL"))
|
|
||||||
if err != nil {
|
|
||||||
return selection, err
|
|
||||||
}
|
|
||||||
|
|
||||||
selection.CustomPort, err = s.env.Uint16Ptr("VPN_ENDPOINT_PORT",
|
|
||||||
env.RetroKeys("PORT", "OPENVPN_PORT"))
|
|
||||||
if err != nil {
|
|
||||||
return selection, err
|
|
||||||
}
|
|
||||||
|
|
||||||
selection.PIAEncPreset = s.readPIAEncryptionPreset()
|
|
||||||
|
|
||||||
return selection, nil
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readPortForward() (
|
|
||||||
portForwarding settings.PortForwarding, err error) {
|
|
||||||
portForwarding.Enabled, err = s.env.BoolPtr("VPN_PORT_FORWARDING",
|
|
||||||
env.RetroKeys(
|
|
||||||
"PORT_FORWARDING",
|
|
||||||
"PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING",
|
|
||||||
))
|
|
||||||
if err != nil {
|
|
||||||
return portForwarding, err
|
|
||||||
}
|
|
||||||
|
|
||||||
portForwarding.Provider = s.env.Get("VPN_PORT_FORWARDING_PROVIDER")
|
|
||||||
|
|
||||||
portForwarding.Filepath = s.env.Get("VPN_PORT_FORWARDING_STATUS_FILE",
|
|
||||||
env.ForceLowercase(false),
|
|
||||||
env.RetroKeys(
|
|
||||||
"PORT_FORWARDING_STATUS_FILE",
|
|
||||||
"PRIVATE_INTERNET_ACCESS_VPN_PORT_FORWARDING_STATUS_FILE",
|
|
||||||
))
|
|
||||||
|
|
||||||
portForwarding.ListeningPort, err = s.env.Uint16Ptr("VPN_PORT_FORWARDING_LISTENING_PORT")
|
|
||||||
if err != nil {
|
|
||||||
return portForwarding, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return portForwarding, nil
|
|
||||||
}
|
|
||||||
26
internal/configuration/sources/env/pprof.go
vendored
26
internal/configuration/sources/env/pprof.go
vendored
@@ -1,26 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/pprof"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readPprof() (settings pprof.Settings, err error) {
|
|
||||||
settings.Enabled, err = s.env.BoolPtr("PPROF_ENABLED")
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.BlockProfileRate, err = s.env.IntPtr("PPROF_BLOCK_PROFILE_RATE")
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.MutexProfileRate, err = s.env.IntPtr("PPROF_MUTEX_PROFILE_RATE")
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.HTTPServer.Address = s.env.String("PPROF_HTTP_SERVER_ADDRESS")
|
|
||||||
|
|
||||||
return settings, nil
|
|
||||||
}
|
|
||||||
45
internal/configuration/sources/env/provider.go
vendored
45
internal/configuration/sources/env/provider.go
vendored
@@ -1,45 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readProvider(vpnType string) (provider settings.Provider, err error) {
|
|
||||||
provider.Name = s.readVPNServiceProvider(vpnType)
|
|
||||||
|
|
||||||
provider.ServerSelection, err = s.readServerSelection(provider.Name, vpnType)
|
|
||||||
if err != nil {
|
|
||||||
return provider, fmt.Errorf("server selection: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
provider.PortForwarding, err = s.readPortForward()
|
|
||||||
if err != nil {
|
|
||||||
return provider, fmt.Errorf("port forwarding: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return provider, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Source) readVPNServiceProvider(vpnType string) (vpnProvider string) {
|
|
||||||
vpnProvider = s.env.String("VPN_SERVICE_PROVIDER", env.RetroKeys("VPNSP"))
|
|
||||||
if vpnProvider == "" {
|
|
||||||
if vpnType != vpn.Wireguard && s.env.Get("OPENVPN_CUSTOM_CONFIG") != nil {
|
|
||||||
// retro compatibility
|
|
||||||
return providers.Custom
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
vpnProvider = strings.ToLower(vpnProvider)
|
|
||||||
if vpnProvider == "pia" { // retro compatibility
|
|
||||||
return providers.PrivateInternetAccess
|
|
||||||
}
|
|
||||||
|
|
||||||
return vpnProvider
|
|
||||||
}
|
|
||||||
22
internal/configuration/sources/env/publicip.go
vendored
22
internal/configuration/sources/env/publicip.go
vendored
@@ -1,22 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readPublicIP() (publicIP settings.PublicIP, err error) {
|
|
||||||
publicIP.Period, err = s.env.DurationPtr("PUBLICIP_PERIOD")
|
|
||||||
if err != nil {
|
|
||||||
return publicIP, err
|
|
||||||
}
|
|
||||||
|
|
||||||
publicIP.IPFilepath = s.env.Get("PUBLICIP_FILE",
|
|
||||||
env.ForceLowercase(false), env.RetroKeys("IP_STATUS_FILE"))
|
|
||||||
|
|
||||||
publicIP.API = s.env.String("PUBLICIP_API")
|
|
||||||
|
|
||||||
publicIP.APIToken = s.env.Get("PUBLICIP_API_TOKEN")
|
|
||||||
|
|
||||||
return publicIP, nil
|
|
||||||
}
|
|
||||||
103
internal/configuration/sources/env/reader.go
vendored
103
internal/configuration/sources/env/reader.go
vendored
@@ -1,103 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Source struct {
|
|
||||||
env env.Env
|
|
||||||
warner Warner
|
|
||||||
handleDeprecatedKey func(deprecatedKey, newKey string)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Warner interface {
|
|
||||||
Warn(s string)
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(warner Warner) *Source {
|
|
||||||
handleDeprecatedKey := func(deprecatedKey, newKey string) {
|
|
||||||
warner.Warn(
|
|
||||||
"You are using the old environment variable " + deprecatedKey +
|
|
||||||
", please consider changing it to " + newKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Source{
|
|
||||||
env: *env.New(os.Environ(), handleDeprecatedKey),
|
|
||||||
warner: warner,
|
|
||||||
handleDeprecatedKey: handleDeprecatedKey,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Source) String() string { return "environment variables" }
|
|
||||||
|
|
||||||
func (s *Source) Read() (settings settings.Settings, err error) {
|
|
||||||
settings.VPN, err = s.readVPN()
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.Firewall, err = s.readFirewall()
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.System, err = s.readSystem()
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.Health, err = s.ReadHealth()
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.HTTPProxy, err = s.readHTTPProxy()
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.Log, err = s.readLog()
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.PublicIP, err = s.readPublicIP()
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.Updater, err = s.readUpdater()
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.Version, err = s.readVersion()
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.Shadowsocks, err = s.readShadowsocks()
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.DNS, err = s.readDNS()
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ControlServer, err = s.readControlServer()
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.Pprof, err = s.readPprof()
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return settings, nil
|
|
||||||
}
|
|
||||||
16
internal/configuration/sources/env/server.go
vendored
16
internal/configuration/sources/env/server.go
vendored
@@ -1,16 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readControlServer() (controlServer settings.ControlServer, err error) {
|
|
||||||
controlServer.Log, err = s.env.BoolPtr("HTTP_CONTROL_SERVER_LOG")
|
|
||||||
if err != nil {
|
|
||||||
return controlServer, err
|
|
||||||
}
|
|
||||||
|
|
||||||
controlServer.Address = s.env.Get("HTTP_CONTROL_SERVER_ADDRESS")
|
|
||||||
|
|
||||||
return controlServer, nil
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readServerSelection(vpnProvider, vpnType string) (
|
|
||||||
ss settings.ServerSelection, err error) {
|
|
||||||
ss.VPN = vpnType
|
|
||||||
|
|
||||||
ss.TargetIP, err = s.env.NetipAddr("VPN_ENDPOINT_IP",
|
|
||||||
env.RetroKeys("OPENVPN_TARGET_IP"))
|
|
||||||
if err != nil {
|
|
||||||
return ss, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ss.Countries = s.env.CSV("SERVER_COUNTRIES", env.RetroKeys("COUNTRY"))
|
|
||||||
if vpnProvider == providers.Cyberghost && len(ss.Countries) == 0 {
|
|
||||||
// Retro-compatibility for Cyberghost using the REGION variable
|
|
||||||
ss.Countries = s.env.CSV("REGION")
|
|
||||||
if len(ss.Countries) > 0 {
|
|
||||||
s.handleDeprecatedKey("REGION", "SERVER_COUNTRIES")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ss.Regions = s.env.CSV("SERVER_REGIONS", env.RetroKeys("REGION"))
|
|
||||||
ss.Cities = s.env.CSV("SERVER_CITIES", env.RetroKeys("CITY"))
|
|
||||||
ss.ISPs = s.env.CSV("ISP")
|
|
||||||
ss.Hostnames = s.env.CSV("SERVER_HOSTNAMES", env.RetroKeys("SERVER_HOSTNAME"))
|
|
||||||
ss.Names = s.env.CSV("SERVER_NAMES", env.RetroKeys("SERVER_NAME"))
|
|
||||||
ss.Numbers, err = s.env.CSVUint16("SERVER_NUMBER")
|
|
||||||
ss.Categories = s.env.CSV("SERVER_CATEGORIES")
|
|
||||||
if err != nil {
|
|
||||||
return ss, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mullvad only
|
|
||||||
ss.OwnedOnly, err = s.env.BoolPtr("OWNED_ONLY", env.RetroKeys("OWNED"))
|
|
||||||
if err != nil {
|
|
||||||
return ss, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// VPNUnlimited and ProtonVPN only
|
|
||||||
ss.FreeOnly, err = s.env.BoolPtr("FREE_ONLY")
|
|
||||||
if err != nil {
|
|
||||||
return ss, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// VPNSecure only
|
|
||||||
ss.PremiumOnly, err = s.env.BoolPtr("PREMIUM_ONLY")
|
|
||||||
if err != nil {
|
|
||||||
return ss, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Surfshark only
|
|
||||||
ss.MultiHopOnly, err = s.env.BoolPtr("MULTIHOP_ONLY")
|
|
||||||
if err != nil {
|
|
||||||
return ss, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// VPNUnlimited only
|
|
||||||
ss.StreamOnly, err = s.env.BoolPtr("STREAM_ONLY")
|
|
||||||
if err != nil {
|
|
||||||
return ss, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// PIA only
|
|
||||||
ss.PortForwardOnly, err = s.env.BoolPtr("PORT_FORWARD_ONLY")
|
|
||||||
if err != nil {
|
|
||||||
return ss, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ss.OpenVPN, err = s.readOpenVPNSelection()
|
|
||||||
if err != nil {
|
|
||||||
return ss, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ss.Wireguard, err = s.readWireguardSelection()
|
|
||||||
if err != nil {
|
|
||||||
return ss, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ss, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidIP = errors.New("invalid IP address")
|
|
||||||
)
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readShadowsocks() (shadowsocks settings.Shadowsocks, err error) {
|
|
||||||
shadowsocks.Enabled, err = s.env.BoolPtr("SHADOWSOCKS")
|
|
||||||
if err != nil {
|
|
||||||
return shadowsocks, err
|
|
||||||
}
|
|
||||||
|
|
||||||
shadowsocks.Address, err = s.readShadowsocksAddress()
|
|
||||||
if err != nil {
|
|
||||||
return shadowsocks, err
|
|
||||||
}
|
|
||||||
shadowsocks.LogAddresses, err = s.env.BoolPtr("SHADOWSOCKS_LOG")
|
|
||||||
if err != nil {
|
|
||||||
return shadowsocks, err
|
|
||||||
}
|
|
||||||
shadowsocks.CipherName = s.env.String("SHADOWSOCKS_CIPHER",
|
|
||||||
env.RetroKeys("SHADOWSOCKS_METHOD"))
|
|
||||||
shadowsocks.Password = s.env.Get("SHADOWSOCKS_PASSWORD", env.ForceLowercase(false))
|
|
||||||
|
|
||||||
return shadowsocks, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Source) readShadowsocksAddress() (address *string, err error) {
|
|
||||||
const currentKey = "SHADOWSOCKS_LISTENING_ADDRESS"
|
|
||||||
port, err := s.env.Uint16Ptr("SHADOWSOCKS_PORT") // retro-compatibility
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if port != nil {
|
|
||||||
s.handleDeprecatedKey("SHADOWSOCKS_PORT", currentKey)
|
|
||||||
return ptrTo(fmt.Sprintf(":%d", *port)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.env.Get(currentKey), nil
|
|
||||||
}
|
|
||||||
22
internal/configuration/sources/env/system.go
vendored
22
internal/configuration/sources/env/system.go
vendored
@@ -1,22 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readSystem() (system settings.System, err error) {
|
|
||||||
system.PUID, err = s.env.Uint32Ptr("PUID", env.RetroKeys("UID"))
|
|
||||||
if err != nil {
|
|
||||||
return system, err
|
|
||||||
}
|
|
||||||
|
|
||||||
system.PGID, err = s.env.Uint32Ptr("PGID", env.RetroKeys("GID"))
|
|
||||||
if err != nil {
|
|
||||||
return system, err
|
|
||||||
}
|
|
||||||
|
|
||||||
system.Timezone = s.env.String("TZ")
|
|
||||||
|
|
||||||
return system, nil
|
|
||||||
}
|
|
||||||
36
internal/configuration/sources/env/unbound.go
vendored
36
internal/configuration/sources/env/unbound.go
vendored
@@ -1,36 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readUnbound() (unbound settings.Unbound, err error) {
|
|
||||||
unbound.Providers = s.env.CSV("DOT_PROVIDERS")
|
|
||||||
|
|
||||||
unbound.Caching, err = s.env.BoolPtr("DOT_CACHING")
|
|
||||||
if err != nil {
|
|
||||||
return unbound, err
|
|
||||||
}
|
|
||||||
|
|
||||||
unbound.IPv6, err = s.env.BoolPtr("DOT_IPV6")
|
|
||||||
if err != nil {
|
|
||||||
return unbound, err
|
|
||||||
}
|
|
||||||
|
|
||||||
unbound.VerbosityLevel, err = s.env.Uint8Ptr("DOT_VERBOSITY")
|
|
||||||
if err != nil {
|
|
||||||
return unbound, err
|
|
||||||
}
|
|
||||||
|
|
||||||
unbound.VerbosityDetailsLevel, err = s.env.Uint8Ptr("DOT_VERBOSITY_DETAILS")
|
|
||||||
if err != nil {
|
|
||||||
return unbound, err
|
|
||||||
}
|
|
||||||
|
|
||||||
unbound.ValidationLogLevel, err = s.env.Uint8Ptr("DOT_VALIDATION_LOGLEVEL")
|
|
||||||
if err != nil {
|
|
||||||
return unbound, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return unbound, nil
|
|
||||||
}
|
|
||||||
35
internal/configuration/sources/env/updater.go
vendored
35
internal/configuration/sources/env/updater.go
vendored
@@ -1,35 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readUpdater() (updater settings.Updater, err error) {
|
|
||||||
updater.Period, err = s.env.DurationPtr("UPDATER_PERIOD")
|
|
||||||
if err != nil {
|
|
||||||
return updater, err
|
|
||||||
}
|
|
||||||
|
|
||||||
updater.DNSAddress, err = readUpdaterDNSAddress()
|
|
||||||
if err != nil {
|
|
||||||
return updater, err
|
|
||||||
}
|
|
||||||
|
|
||||||
updater.MinRatio, err = s.env.Float64("UPDATER_MIN_RATIO")
|
|
||||||
if err != nil {
|
|
||||||
return updater, err
|
|
||||||
}
|
|
||||||
|
|
||||||
updater.Providers = s.env.CSV("UPDATER_VPN_SERVICE_PROVIDERS")
|
|
||||||
|
|
||||||
return updater, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readUpdaterDNSAddress() (address string, err error) {
|
|
||||||
// TODO this is currently using Cloudflare in
|
|
||||||
// plaintext to not be blocked by DNS over TLS by default.
|
|
||||||
// If a plaintext address is set in the DNS settings, this one will be used.
|
|
||||||
// use custom future encrypted DNS written in Go without blocking
|
|
||||||
// as it's too much trouble to start another parallel unbound instance for now.
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
14
internal/configuration/sources/env/version.go
vendored
14
internal/configuration/sources/env/version.go
vendored
@@ -1,14 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readVersion() (version settings.Version, err error) {
|
|
||||||
version.Enabled, err = s.env.BoolPtr("VERSION_INFORMATION")
|
|
||||||
if err != nil {
|
|
||||||
return version, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return version, nil
|
|
||||||
}
|
|
||||||
28
internal/configuration/sources/env/vpn.go
vendored
28
internal/configuration/sources/env/vpn.go
vendored
@@ -1,28 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readVPN() (vpn settings.VPN, err error) {
|
|
||||||
vpn.Type = s.env.String("VPN_TYPE")
|
|
||||||
|
|
||||||
vpn.Provider, err = s.readProvider(vpn.Type)
|
|
||||||
if err != nil {
|
|
||||||
return vpn, fmt.Errorf("VPN provider: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
vpn.OpenVPN, err = s.readOpenVPN()
|
|
||||||
if err != nil {
|
|
||||||
return vpn, fmt.Errorf("OpenVPN: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
vpn.Wireguard, err = s.readWireguard()
|
|
||||||
if err != nil {
|
|
||||||
return vpn, fmt.Errorf("wireguard: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return vpn, nil
|
|
||||||
}
|
|
||||||
33
internal/configuration/sources/env/wireguard.go
vendored
33
internal/configuration/sources/env/wireguard.go
vendored
@@ -1,33 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readWireguard() (wireguard settings.Wireguard, err error) {
|
|
||||||
defer func() {
|
|
||||||
err = unsetEnvKeys([]string{"WIREGUARD_PRIVATE_KEY", "WIREGUARD_PRESHARED_KEY"}, err)
|
|
||||||
}()
|
|
||||||
wireguard.PrivateKey = s.env.Get("WIREGUARD_PRIVATE_KEY", env.ForceLowercase(false))
|
|
||||||
wireguard.PreSharedKey = s.env.Get("WIREGUARD_PRESHARED_KEY", env.ForceLowercase(false))
|
|
||||||
wireguard.Interface = s.env.String("VPN_INTERFACE",
|
|
||||||
env.RetroKeys("WIREGUARD_INTERFACE"), env.ForceLowercase(false))
|
|
||||||
wireguard.Implementation = s.env.String("WIREGUARD_IMPLEMENTATION")
|
|
||||||
wireguard.Addresses, err = s.env.CSVNetipPrefixes("WIREGUARD_ADDRESSES",
|
|
||||||
env.RetroKeys("WIREGUARD_ADDRESS"))
|
|
||||||
if err != nil {
|
|
||||||
return wireguard, err // already wrapped
|
|
||||||
}
|
|
||||||
wireguard.AllowedIPs, err = s.env.CSVNetipPrefixes("WIREGUARD_ALLOWED_IPS")
|
|
||||||
if err != nil {
|
|
||||||
return wireguard, err // already wrapped
|
|
||||||
}
|
|
||||||
mtuPtr, err := s.env.Uint16Ptr("WIREGUARD_MTU")
|
|
||||||
if err != nil {
|
|
||||||
return wireguard, err
|
|
||||||
} else if mtuPtr != nil {
|
|
||||||
wireguard.MTU = *mtuPtr
|
|
||||||
}
|
|
||||||
return wireguard, nil
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package env
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readWireguardSelection() (
|
|
||||||
selection settings.WireguardSelection, err error) {
|
|
||||||
selection.EndpointIP, err = s.env.NetipAddr("VPN_ENDPOINT_IP", env.RetroKeys("WIREGUARD_ENDPOINT_IP"))
|
|
||||||
if err != nil {
|
|
||||||
return selection, err
|
|
||||||
}
|
|
||||||
|
|
||||||
selection.EndpointPort, err = s.env.Uint16Ptr("VPN_ENDPOINT_PORT", env.RetroKeys("WIREGUARD_ENDPOINT_PORT"))
|
|
||||||
if err != nil {
|
|
||||||
return selection, err
|
|
||||||
}
|
|
||||||
|
|
||||||
selection.PublicKey = s.env.String("WIREGUARD_PUBLIC_KEY", env.ForceLowercase(false))
|
|
||||||
|
|
||||||
return selection, nil
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package files
|
|
||||||
|
|
||||||
import "github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
|
|
||||||
func (s *Source) ReadHealth() (settings settings.Health, err error) { return settings, nil }
|
|
||||||
@@ -9,47 +9,45 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/openvpn/extract"
|
"github.com/qdm12/gluetun/internal/openvpn/extract"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadFromFile reads the content of the file as a string.
|
// ReadFromFile reads the content of the file as a string,
|
||||||
// It returns a nil string pointer if the file does not exist.
|
// and returns if the file was present or not with isSet.
|
||||||
func ReadFromFile(filepath string) (s *string, err error) {
|
func ReadFromFile(filepath string) (content string, isSet bool, err error) {
|
||||||
file, err := os.Open(filepath)
|
file, err := os.Open(filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil, nil //nolint:nilnil
|
return "", false, nil
|
||||||
}
|
}
|
||||||
return nil, err
|
return "", false, fmt.Errorf("opening file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := io.ReadAll(file)
|
b, err := io.ReadAll(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = file.Close()
|
_ = file.Close()
|
||||||
return nil, err
|
return "", false, fmt.Errorf("reading file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := file.Close(); err != nil {
|
if err := file.Close(); err != nil {
|
||||||
return nil, err
|
return "", false, fmt.Errorf("closing file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
content := string(b)
|
content = string(b)
|
||||||
content = strings.TrimSuffix(content, "\r\n")
|
content = strings.TrimSuffix(content, "\r\n")
|
||||||
content = strings.TrimSuffix(content, "\n")
|
content = strings.TrimSuffix(content, "\n")
|
||||||
return &content, nil
|
return content, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readPEMFile(filepath string) (base64Ptr *string, err error) {
|
func ReadPEMFile(filepath string) (base64Str string, isSet bool, err error) {
|
||||||
pemData, err := ReadFromFile(filepath)
|
pemData, isSet, err := ReadFromFile(filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("reading file: %w", err)
|
return "", false, fmt.Errorf("reading file: %w", err)
|
||||||
|
} else if !isSet {
|
||||||
|
return "", false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if pemData == nil {
|
base64Str, err = extract.PEM([]byte(pemData))
|
||||||
return nil, nil //nolint:nilnil
|
|
||||||
}
|
|
||||||
|
|
||||||
base64Data, err := extract.PEM([]byte(*pemData))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("extracting base64 encoded data from PEM content: %w", err)
|
return "", false, fmt.Errorf("extracting base64 encoded data from PEM content: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &base64Data, nil
|
return base64Str, true, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
package files
|
|
||||||
|
|
||||||
func ptrTo[T any](x T) *T { return &x }
|
|
||||||
5
internal/configuration/sources/files/interfaces.go
Normal file
5
internal/configuration/sources/files/interfaces.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package files
|
||||||
|
|
||||||
|
type Warner interface {
|
||||||
|
Warnf(format string, a ...interface{})
|
||||||
|
}
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package files
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// OpenVPNClientKeyPath is the OpenVPN client key filepath.
|
|
||||||
OpenVPNClientKeyPath = "/gluetun/client.key"
|
|
||||||
// OpenVPNClientCertificatePath is the OpenVPN client certificate filepath.
|
|
||||||
OpenVPNClientCertificatePath = "/gluetun/client.crt"
|
|
||||||
openVPNEncryptedKey = "/gluetun/openvpn_encrypted_key"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readOpenVPN() (settings settings.OpenVPN, err error) {
|
|
||||||
settings.Key, err = readPEMFile(OpenVPNClientKeyPath)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("client key: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.Cert, err = readPEMFile(OpenVPNClientCertificatePath)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("client certificate: %w", err)
|
|
||||||
}
|
|
||||||
settings.EncryptedKey, err = readPEMFile(openVPNEncryptedKey)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading encrypted key file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return settings, nil
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package files
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readProvider() (provider settings.Provider, err error) {
|
|
||||||
provider.ServerSelection, err = s.readServerSelection()
|
|
||||||
if err != nil {
|
|
||||||
return provider, fmt.Errorf("server selection: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return provider, nil
|
|
||||||
}
|
|
||||||
@@ -1,32 +1,99 @@
|
|||||||
package files
|
package files
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Source struct {
|
type Source struct {
|
||||||
wireguardConfigPath string
|
rootDirectory string
|
||||||
|
environ map[string]string
|
||||||
|
warner Warner
|
||||||
|
cached struct {
|
||||||
|
wireguardLoaded bool
|
||||||
|
wireguardConf WireguardConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(warner Warner) (source *Source) {
|
||||||
|
osEnviron := os.Environ()
|
||||||
|
environ := make(map[string]string, len(osEnviron))
|
||||||
|
for _, pair := range osEnviron {
|
||||||
|
const maxSplit = 2
|
||||||
|
split := strings.SplitN(pair, "=", maxSplit)
|
||||||
|
environ[split[0]] = split[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Source {
|
|
||||||
const wireguardConfigPath = "/gluetun/wireguard/wg0.conf"
|
|
||||||
return &Source{
|
return &Source{
|
||||||
wireguardConfigPath: wireguardConfigPath,
|
rootDirectory: "/gluetun",
|
||||||
|
environ: environ,
|
||||||
|
warner: warner,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) String() string { return "files" }
|
func (s *Source) String() string { return "files" }
|
||||||
|
|
||||||
func (s *Source) Read() (settings settings.Settings, err error) {
|
func (s *Source) Get(key string) (value string, isSet bool) {
|
||||||
settings.VPN, err = s.readVPN()
|
if key == "" {
|
||||||
if err != nil {
|
return "", false
|
||||||
return settings, err
|
}
|
||||||
|
// TODO v4 custom environment variable to set the files parent directory
|
||||||
|
// and not to set each file to a specific path
|
||||||
|
envKey := strings.ToUpper(key)
|
||||||
|
envKey = strings.ReplaceAll(envKey, "-", "_")
|
||||||
|
envKey += "_FILE"
|
||||||
|
path := s.environ[envKey]
|
||||||
|
if path == "" {
|
||||||
|
path = filepath.Join(s.rootDirectory, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.System, err = s.readSystem()
|
// Special file handling
|
||||||
|
switch key {
|
||||||
|
// TODO timezone from /etc/localtime
|
||||||
|
case "client.crt", "client.key":
|
||||||
|
value, isSet, err := ReadPEMFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, err
|
s.warner.Warnf("skipping %s: parsing PEM: %s", path, err)
|
||||||
|
}
|
||||||
|
return value, isSet
|
||||||
|
case "wireguard_private_key":
|
||||||
|
return strPtrToStringIsSet(s.lazyLoadWireguardConf().PrivateKey)
|
||||||
|
case "wireguard_preshared_key":
|
||||||
|
return strPtrToStringIsSet(s.lazyLoadWireguardConf().PreSharedKey)
|
||||||
|
case "wireguard_addresses":
|
||||||
|
return strPtrToStringIsSet(s.lazyLoadWireguardConf().Addresses)
|
||||||
|
case "wireguard_public_key":
|
||||||
|
return strPtrToStringIsSet(s.lazyLoadWireguardConf().PublicKey)
|
||||||
|
case "vpn_endpoint_ip":
|
||||||
|
return strPtrToStringIsSet(s.lazyLoadWireguardConf().EndpointIP)
|
||||||
|
case "vpn_endpoint_port":
|
||||||
|
return strPtrToStringIsSet(s.lazyLoadWireguardConf().EndpointPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings, nil
|
value, isSet, err := ReadFromFile(path)
|
||||||
|
if err != nil {
|
||||||
|
s.warner.Warnf("skipping %s: reading file: %s", path, err)
|
||||||
|
}
|
||||||
|
return value, isSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Source) KeyTransform(key string) string {
|
||||||
|
switch key {
|
||||||
|
// TODO v4 remove these irregular cases
|
||||||
|
case "OPENVPN_KEY":
|
||||||
|
return "client.key"
|
||||||
|
case "OPENVPN_CERT":
|
||||||
|
return "client.crt"
|
||||||
|
default:
|
||||||
|
key = strings.ToLower(key) // HTTPROXY_USER -> httpproxy_user
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func strPtrToStringIsSet(ptr *string) (s string, isSet bool) {
|
||||||
|
if ptr == nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return *ptr, true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
package files
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readServerSelection() (selection settings.ServerSelection, err error) {
|
|
||||||
selection.Wireguard, err = s.readWireguardSelection()
|
|
||||||
if err != nil {
|
|
||||||
return selection, fmt.Errorf("wireguard: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return selection, nil
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package files
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readSystem() (system settings.System, err error) {
|
|
||||||
// TODO timezone from /etc/localtime
|
|
||||||
return system, nil
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package files
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readVPN() (vpn settings.VPN, err error) {
|
|
||||||
vpn.Provider, err = s.readProvider()
|
|
||||||
if err != nil {
|
|
||||||
return vpn, fmt.Errorf("provider: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
vpn.OpenVPN, err = s.readOpenVPN()
|
|
||||||
if err != nil {
|
|
||||||
return vpn, fmt.Errorf("OpenVPN: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
vpn.Wireguard, err = s.readWireguard()
|
|
||||||
if err != nil {
|
|
||||||
return vpn, fmt.Errorf("wireguard: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return vpn, nil
|
|
||||||
}
|
|
||||||
@@ -1,124 +1,115 @@
|
|||||||
package files
|
package files
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readWireguard() (wireguard settings.Wireguard, err error) {
|
func (s *Source) lazyLoadWireguardConf() WireguardConfig {
|
||||||
fileStringPtr, err := ReadFromFile(s.wireguardConfigPath)
|
if s.cached.wireguardLoaded {
|
||||||
|
return s.cached.wireguardConf
|
||||||
|
}
|
||||||
|
|
||||||
|
s.cached.wireguardLoaded = true
|
||||||
|
var err error
|
||||||
|
s.cached.wireguardConf, err = ParseWireguardConf(filepath.Join(s.rootDirectory, "wg0.conf"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wireguard, fmt.Errorf("reading file: %w", err)
|
s.warner.Warnf("skipping Wireguard config: %s", err)
|
||||||
|
}
|
||||||
|
return s.cached.wireguardConf
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileStringPtr == nil {
|
type WireguardConfig struct {
|
||||||
return wireguard, nil
|
PrivateKey *string
|
||||||
}
|
PreSharedKey *string
|
||||||
|
Addresses *string
|
||||||
rawData := []byte(*fileStringPtr)
|
PublicKey *string
|
||||||
return ParseWireguardConf(rawData)
|
EndpointIP *string
|
||||||
|
EndpointPort *string
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
regexINISectionNotExist = regexp.MustCompile(`^section ".+" does not exist$`)
|
regexINISectionNotExist = regexp.MustCompile(`^section ".+" does not exist$`)
|
||||||
regexINIKeyNotExist = regexp.MustCompile(`key ".*" not exists$`)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseWireguardConf(rawData []byte) (wireguard settings.Wireguard, err error) {
|
func ParseWireguardConf(path string) (config WireguardConfig, err error) {
|
||||||
iniFile, err := ini.Load(rawData)
|
iniFile, err := ini.Load(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wireguard, fmt.Errorf("loading ini from reader: %w", err)
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
return WireguardConfig{}, nil
|
||||||
|
}
|
||||||
|
return WireguardConfig{}, fmt.Errorf("loading ini from reader: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
interfaceSection, err := iniFile.GetSection("Interface")
|
interfaceSection, err := iniFile.GetSection("Interface")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = parseWireguardInterfaceSection(interfaceSection, &wireguard)
|
config.PrivateKey, config.Addresses = parseWireguardInterfaceSection(interfaceSection)
|
||||||
if err != nil {
|
|
||||||
return wireguard, fmt.Errorf("parsing interface section: %w", err)
|
|
||||||
}
|
|
||||||
} else if !regexINISectionNotExist.MatchString(err.Error()) {
|
} else if !regexINISectionNotExist.MatchString(err.Error()) {
|
||||||
// can never happen
|
// can never happen
|
||||||
return wireguard, fmt.Errorf("getting interface section: %w", err)
|
return WireguardConfig{}, fmt.Errorf("getting interface section: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
peerSection, err := iniFile.GetSection("Peer")
|
peerSection, err := iniFile.GetSection("Peer")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
wireguard.PreSharedKey, err = parseINIWireguardKey(peerSection, "PresharedKey")
|
config.PreSharedKey, config.PublicKey, config.EndpointIP,
|
||||||
if err != nil {
|
config.EndpointPort = parseWireguardPeerSection(peerSection)
|
||||||
return wireguard, fmt.Errorf("parsing peer section: %w", err)
|
|
||||||
}
|
|
||||||
} else if !regexINISectionNotExist.MatchString(err.Error()) {
|
} else if !regexINISectionNotExist.MatchString(err.Error()) {
|
||||||
// can never happen
|
// can never happen
|
||||||
return wireguard, fmt.Errorf("getting peer section: %w", err)
|
return WireguardConfig{}, fmt.Errorf("getting peer section: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return wireguard, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseWireguardInterfaceSection(interfaceSection *ini.Section,
|
func parseWireguardInterfaceSection(interfaceSection *ini.Section) (
|
||||||
wireguard *settings.Wireguard) (err error) {
|
privateKey, addresses *string) {
|
||||||
wireguard.PrivateKey, err = parseINIWireguardKey(interfaceSection, "PrivateKey")
|
privateKey = getINIKeyFromSection(interfaceSection, "PrivateKey")
|
||||||
|
addresses = getINIKeyFromSection(interfaceSection, "Address")
|
||||||
|
return privateKey, addresses
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrEndpointHostNotIP = errors.New("endpoint host is not an IP")
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseWireguardPeerSection(peerSection *ini.Section) (
|
||||||
|
preSharedKey, publicKey, endpointIP, endpointPort *string) {
|
||||||
|
preSharedKey = getINIKeyFromSection(peerSection, "PresharedKey")
|
||||||
|
publicKey = getINIKeyFromSection(peerSection, "PublicKey")
|
||||||
|
endpoint := getINIKeyFromSection(peerSection, "Endpoint")
|
||||||
|
if endpoint != nil {
|
||||||
|
parts := strings.Split(*endpoint, ":")
|
||||||
|
endpointIP = &parts[0]
|
||||||
|
const partsWithPort = 2
|
||||||
|
if len(parts) >= partsWithPort {
|
||||||
|
endpointPort = new(string)
|
||||||
|
*endpointPort = strings.Join(parts[1:], ":")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return preSharedKey, publicKey, endpointIP, endpointPort
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
regexINIKeyNotExist = regexp.MustCompile(`key ".*" not exists$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
func getINIKeyFromSection(section *ini.Section, key string) (value *string) {
|
||||||
|
iniKey, err := section.GetKey(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err // error is already wrapped correctly
|
if regexINIKeyNotExist.MatchString(err.Error()) {
|
||||||
}
|
|
||||||
|
|
||||||
wireguard.Addresses, err = parseINIWireguardAddress(interfaceSection)
|
|
||||||
if err != nil {
|
|
||||||
return err // error is already wrapped correctly
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseINIWireguardKey(section *ini.Section, keyName string) (
|
|
||||||
key *string, err error) {
|
|
||||||
iniKey, err := section.GetKey(keyName)
|
|
||||||
if err != nil {
|
|
||||||
if regexINIKeyNotExist.MatchString(err.Error()) {
|
|
||||||
return nil, nil //nolint:nilnil
|
|
||||||
}
|
|
||||||
// can never happen
|
// can never happen
|
||||||
return nil, fmt.Errorf("getting %s key: %w", keyName, err)
|
panic(fmt.Sprintf("getting key %q: %s", key, err))
|
||||||
}
|
}
|
||||||
|
value = new(string)
|
||||||
key = new(string)
|
*value = iniKey.String()
|
||||||
*key = iniKey.String()
|
return value
|
||||||
_, err = wgtypes.ParseKey(*key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("parsing %s: %s: %w", keyName, *key, err)
|
|
||||||
}
|
|
||||||
return key, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseINIWireguardAddress(section *ini.Section) (
|
|
||||||
addresses []netip.Prefix, err error) {
|
|
||||||
addressKey, err := section.GetKey("Address")
|
|
||||||
if err != nil {
|
|
||||||
if regexINIKeyNotExist.MatchString(err.Error()) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
// can never happen
|
|
||||||
return nil, fmt.Errorf("getting Address key: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
addressStrings := strings.Split(addressKey.String(), ",")
|
|
||||||
addresses = make([]netip.Prefix, len(addressStrings))
|
|
||||||
for i, addressString := range addressStrings {
|
|
||||||
addressString = strings.TrimSpace(addressString)
|
|
||||||
if !strings.ContainsRune(addressString, '/') {
|
|
||||||
addressString += "/32"
|
|
||||||
}
|
|
||||||
addresses[i], err = netip.ParsePrefix(addressString)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("parsing address: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return addresses, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +1,42 @@
|
|||||||
package files
|
package files
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/netip"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Source_readWireguard(t *testing.T) {
|
func ptrTo[T any](value T) *T { return &value }
|
||||||
|
|
||||||
|
func Test_Source_ParseWireguardConf(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
t.Run("fail reading from file", func(t *testing.T) {
|
t.Run("fail reading from file", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
dirPath := t.TempDir()
|
dirPath := t.TempDir()
|
||||||
source := &Source{
|
wireguard, err := ParseWireguardConf(dirPath)
|
||||||
wireguardConfigPath: dirPath,
|
assert.Equal(t, WireguardConfig{}, wireguard)
|
||||||
}
|
|
||||||
wireguard, err := source.readWireguard()
|
|
||||||
assert.Equal(t, settings.Wireguard{}, wireguard)
|
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Regexp(t, `reading file: read .+: is a directory`, err.Error())
|
assert.Regexp(t, `loading ini from reader: BOM: read .+: is a directory`, err.Error())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("no file", func(t *testing.T) {
|
t.Run("no file", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
noFile := filepath.Join(t.TempDir(), "doesnotexist")
|
noFile := filepath.Join(t.TempDir(), "doesnotexist")
|
||||||
source := &Source{
|
wireguard, err := ParseWireguardConf(noFile)
|
||||||
wireguardConfigPath: noFile,
|
assert.Equal(t, WireguardConfig{}, wireguard)
|
||||||
}
|
|
||||||
wireguard, err := source.readWireguard()
|
|
||||||
assert.Equal(t, settings.Wireguard{}, wireguard)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
fileContent string
|
fileContent string
|
||||||
wireguard settings.Wireguard
|
wireguard WireguardConfig
|
||||||
errMessage string
|
errMessage string
|
||||||
}{
|
}{
|
||||||
"ini load error": {
|
"ini load error": {
|
||||||
@@ -50,14 +44,14 @@ func Test_Source_readWireguard(t *testing.T) {
|
|||||||
errMessage: "loading ini from reader: key-value delimiter not found: invalid",
|
errMessage: "loading ini from reader: key-value delimiter not found: invalid",
|
||||||
},
|
},
|
||||||
"empty file": {},
|
"empty file": {},
|
||||||
"interface section parsing error": {
|
"interface_section_missing": {
|
||||||
fileContent: `
|
fileContent: `
|
||||||
[Interface]
|
[Peer]
|
||||||
PrivateKey = x
|
PresharedKey = YJ680VN+dGrdsWNjSFqZ6vvwuiNhbq502ZL3G7Q3o3g=
|
||||||
`,
|
`,
|
||||||
errMessage: "parsing interface section: parsing PrivateKey: " +
|
wireguard: WireguardConfig{
|
||||||
"x: wgtypes: failed to parse base64-encoded key: " +
|
PreSharedKey: ptrTo("YJ680VN+dGrdsWNjSFqZ6vvwuiNhbq502ZL3G7Q3o3g="),
|
||||||
"illegal base64 data at input byte 0",
|
},
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
fileContent: `
|
fileContent: `
|
||||||
@@ -69,12 +63,10 @@ DNS = 193.138.218.74
|
|||||||
[Peer]
|
[Peer]
|
||||||
PresharedKey = YJ680VN+dGrdsWNjSFqZ6vvwuiNhbq502ZL3G7Q3o3g=
|
PresharedKey = YJ680VN+dGrdsWNjSFqZ6vvwuiNhbq502ZL3G7Q3o3g=
|
||||||
`,
|
`,
|
||||||
wireguard: settings.Wireguard{
|
wireguard: WireguardConfig{
|
||||||
PrivateKey: ptrTo("QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8="),
|
PrivateKey: ptrTo("QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8="),
|
||||||
PreSharedKey: ptrTo("YJ680VN+dGrdsWNjSFqZ6vvwuiNhbq502ZL3G7Q3o3g="),
|
PreSharedKey: ptrTo("YJ680VN+dGrdsWNjSFqZ6vvwuiNhbq502ZL3G7Q3o3g="),
|
||||||
Addresses: []netip.Prefix{
|
Addresses: ptrTo("10.38.22.35/32"),
|
||||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{10, 38, 22, 35}), 32),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -88,11 +80,7 @@ PresharedKey = YJ680VN+dGrdsWNjSFqZ6vvwuiNhbq502ZL3G7Q3o3g=
|
|||||||
err := os.WriteFile(configFile, []byte(testCase.fileContent), 0600)
|
err := os.WriteFile(configFile, []byte(testCase.fileContent), 0600)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
source := &Source{
|
wireguard, err := ParseWireguardConf(configFile)
|
||||||
wireguardConfigPath: configFile,
|
|
||||||
}
|
|
||||||
|
|
||||||
wireguard, err := source.readWireguard()
|
|
||||||
|
|
||||||
assert.Equal(t, testCase.wireguard, wireguard)
|
assert.Equal(t, testCase.wireguard, wireguard)
|
||||||
if testCase.errMessage != "" {
|
if testCase.errMessage != "" {
|
||||||
@@ -109,34 +97,26 @@ func Test_parseWireguardInterfaceSection(t *testing.T) {
|
|||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
iniData string
|
iniData string
|
||||||
wireguard settings.Wireguard
|
privateKey *string
|
||||||
errMessage string
|
addresses *string
|
||||||
}{
|
}{
|
||||||
"private key error": {
|
"no_fields": {
|
||||||
iniData: `[Interface]
|
iniData: `[Interface]`,
|
||||||
PrivateKey = x`,
|
|
||||||
errMessage: "parsing PrivateKey: x: " +
|
|
||||||
"wgtypes: failed to parse base64-encoded key: " +
|
|
||||||
"illegal base64 data at input byte 0",
|
|
||||||
},
|
},
|
||||||
"address error": {
|
"only_private_key": {
|
||||||
iniData: `[Interface]
|
iniData: `[Interface]
|
||||||
Address = x
|
PrivateKey = x
|
||||||
`,
|
`,
|
||||||
errMessage: "parsing address: netip.ParsePrefix(\"x/32\"): ParseAddr(\"x\"): unable to parse IP",
|
privateKey: ptrTo("x"),
|
||||||
},
|
},
|
||||||
"success": {
|
"all_fields": {
|
||||||
iniData: `
|
iniData: `
|
||||||
[Interface]
|
[Interface]
|
||||||
PrivateKey = QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8=
|
PrivateKey = QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8=
|
||||||
Address = 10.38.22.35/32
|
Address = 10.38.22.35/32
|
||||||
`,
|
`,
|
||||||
wireguard: settings.Wireguard{
|
privateKey: ptrTo("QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8="),
|
||||||
PrivateKey: ptrTo("QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8="),
|
addresses: ptrTo("10.38.22.35/32"),
|
||||||
Addresses: []netip.Prefix{
|
|
||||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{10, 38, 22, 35}), 32),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,109 +130,74 @@ Address = 10.38.22.35/32
|
|||||||
iniSection, err := iniFile.GetSection("Interface")
|
iniSection, err := iniFile.GetSection("Interface")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var wireguard settings.Wireguard
|
privateKey, addresses := parseWireguardInterfaceSection(iniSection)
|
||||||
err = parseWireguardInterfaceSection(iniSection, &wireguard)
|
|
||||||
|
|
||||||
assert.Equal(t, testCase.wireguard, wireguard)
|
|
||||||
if testCase.errMessage != "" {
|
|
||||||
assert.EqualError(t, err, testCase.errMessage)
|
|
||||||
} else {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_parseINIWireguardKey(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
fileContent string
|
|
||||||
keyName string
|
|
||||||
key *string
|
|
||||||
errMessage string
|
|
||||||
}{
|
|
||||||
"key does not exist": {
|
|
||||||
fileContent: `[Interface]`,
|
|
||||||
keyName: "PrivateKey",
|
|
||||||
},
|
|
||||||
"bad Wireguard key": {
|
|
||||||
fileContent: `[Interface]
|
|
||||||
PrivateKey = x`,
|
|
||||||
keyName: "PrivateKey",
|
|
||||||
errMessage: "parsing PrivateKey: x: " +
|
|
||||||
"wgtypes: failed to parse base64-encoded key: " +
|
|
||||||
"illegal base64 data at input byte 0",
|
|
||||||
},
|
|
||||||
"success": {
|
|
||||||
fileContent: `[Interface]
|
|
||||||
PrivateKey = QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8=`,
|
|
||||||
keyName: "PrivateKey",
|
|
||||||
key: ptrTo("QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8="),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for testName, testCase := range testCases {
|
|
||||||
testCase := testCase
|
|
||||||
t.Run(testName, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
iniFile, err := ini.Load([]byte(testCase.fileContent))
|
|
||||||
require.NoError(t, err)
|
|
||||||
iniSection, err := iniFile.GetSection("Interface")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
key, err := parseINIWireguardKey(iniSection, testCase.keyName)
|
|
||||||
|
|
||||||
assert.Equal(t, testCase.key, key)
|
|
||||||
if testCase.errMessage != "" {
|
|
||||||
assert.EqualError(t, err, testCase.errMessage)
|
|
||||||
} else {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_parseINIWireguardAddress(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
fileContent string
|
|
||||||
addresses []netip.Prefix
|
|
||||||
errMessage string
|
|
||||||
}{
|
|
||||||
"key does not exist": {
|
|
||||||
fileContent: `[Interface]`,
|
|
||||||
},
|
|
||||||
"bad address": {
|
|
||||||
fileContent: `[Interface]
|
|
||||||
Address = x`,
|
|
||||||
errMessage: "parsing address: netip.ParsePrefix(\"x/32\"): ParseAddr(\"x\"): unable to parse IP",
|
|
||||||
},
|
|
||||||
"success": {
|
|
||||||
fileContent: `[Interface]
|
|
||||||
Address = 1.2.3.4/32, 5.6.7.8/32`,
|
|
||||||
addresses: []netip.Prefix{
|
|
||||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{1, 2, 3, 4}), 32),
|
|
||||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{5, 6, 7, 8}), 32),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for testName, testCase := range testCases {
|
|
||||||
testCase := testCase
|
|
||||||
t.Run(testName, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
iniFile, err := ini.Load([]byte(testCase.fileContent))
|
|
||||||
require.NoError(t, err)
|
|
||||||
iniSection, err := iniFile.GetSection("Interface")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
addresses, err := parseINIWireguardAddress(iniSection)
|
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.privateKey, privateKey)
|
||||||
assert.Equal(t, testCase.addresses, addresses)
|
assert.Equal(t, testCase.addresses, addresses)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_parseWireguardPeerSection(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
iniData string
|
||||||
|
preSharedKey *string
|
||||||
|
publicKey *string
|
||||||
|
endpointIP *string
|
||||||
|
endpointPort *string
|
||||||
|
errMessage string
|
||||||
|
}{
|
||||||
|
"public key set": {
|
||||||
|
iniData: `[Peer]
|
||||||
|
PublicKey = QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8=`,
|
||||||
|
publicKey: ptrTo("QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8="),
|
||||||
|
},
|
||||||
|
"endpoint_only_host": {
|
||||||
|
iniData: `[Peer]
|
||||||
|
Endpoint = x`,
|
||||||
|
endpointIP: ptrTo("x"),
|
||||||
|
},
|
||||||
|
"endpoint_no_port": {
|
||||||
|
iniData: `[Peer]
|
||||||
|
Endpoint = x:`,
|
||||||
|
endpointIP: ptrTo("x"),
|
||||||
|
endpointPort: ptrTo(""),
|
||||||
|
},
|
||||||
|
"valid_endpoint": {
|
||||||
|
iniData: `[Peer]
|
||||||
|
Endpoint = 1.2.3.4:51820`,
|
||||||
|
endpointIP: ptrTo("1.2.3.4"),
|
||||||
|
endpointPort: ptrTo("51820"),
|
||||||
|
},
|
||||||
|
"all_set": {
|
||||||
|
iniData: `[Peer]
|
||||||
|
PublicKey = QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8=
|
||||||
|
Endpoint = 1.2.3.4:51820`,
|
||||||
|
publicKey: ptrTo("QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8="),
|
||||||
|
endpointIP: ptrTo("1.2.3.4"),
|
||||||
|
endpointPort: ptrTo("51820"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for testName, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(testName, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
iniFile, err := ini.Load([]byte(testCase.iniData))
|
||||||
|
require.NoError(t, err)
|
||||||
|
iniSection, err := iniFile.GetSection("Peer")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
preSharedKey, publicKey, endpointIP,
|
||||||
|
endpointPort := parseWireguardPeerSection(iniSection)
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.preSharedKey, preSharedKey)
|
||||||
|
assert.Equal(t, testCase.publicKey, publicKey)
|
||||||
|
assert.Equal(t, testCase.endpointIP, endpointIP)
|
||||||
|
assert.Equal(t, testCase.endpointPort, endpointPort)
|
||||||
if testCase.errMessage != "" {
|
if testCase.errMessage != "" {
|
||||||
assert.EqualError(t, err, testCase.errMessage)
|
assert.EqualError(t, err, testCase.errMessage)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
package files
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/govalid/port"
|
|
||||||
"gopkg.in/ini.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrEndpointHostNotIP = errors.New("endpoint host is not an IP")
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readWireguardSelection() (selection settings.WireguardSelection, err error) {
|
|
||||||
fileStringPtr, err := ReadFromFile(s.wireguardConfigPath)
|
|
||||||
if err != nil {
|
|
||||||
return selection, fmt.Errorf("reading file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if fileStringPtr == nil {
|
|
||||||
return selection, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rawData := []byte(*fileStringPtr)
|
|
||||||
iniFile, err := ini.Load(rawData)
|
|
||||||
if err != nil {
|
|
||||||
return selection, fmt.Errorf("loading ini from reader: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
peerSection, err := iniFile.GetSection("Peer")
|
|
||||||
if err == nil {
|
|
||||||
err = parseWireguardPeerSection(peerSection, &selection)
|
|
||||||
if err != nil {
|
|
||||||
return selection, fmt.Errorf("parsing peer section: %w", err)
|
|
||||||
}
|
|
||||||
} else if !regexINISectionNotExist.MatchString(err.Error()) {
|
|
||||||
// can never happen
|
|
||||||
return selection, fmt.Errorf("getting peer section: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return selection, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseWireguardPeerSection(peerSection *ini.Section,
|
|
||||||
selection *settings.WireguardSelection) (err error) {
|
|
||||||
publicKeyPtr, err := parseINIWireguardKey(peerSection, "PublicKey")
|
|
||||||
if err != nil {
|
|
||||||
return err // error is already wrapped correctly
|
|
||||||
} else if publicKeyPtr != nil {
|
|
||||||
selection.PublicKey = *publicKeyPtr
|
|
||||||
}
|
|
||||||
|
|
||||||
endpointKey, err := peerSection.GetKey("Endpoint")
|
|
||||||
if err == nil {
|
|
||||||
endpoint := endpointKey.String()
|
|
||||||
host, portString, err := net.SplitHostPort(endpoint)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("splitting endpoint: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ip, err := netip.ParseAddr(host)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%w: %w", ErrEndpointHostNotIP, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
endpointPort, err := port.Validate(portString)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("port from Endpoint key: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
selection.EndpointIP = ip
|
|
||||||
selection.EndpointPort = &endpointPort
|
|
||||||
} else if !regexINIKeyNotExist.MatchString(err.Error()) {
|
|
||||||
// can never happen
|
|
||||||
return fmt.Errorf("getting endpoint key: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
package files
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/netip"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"gopkg.in/ini.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func uint16Ptr(n uint16) *uint16 { return &n }
|
|
||||||
|
|
||||||
func Test_Source_readWireguardSelection(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
t.Run("fail reading from file", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dirPath := t.TempDir()
|
|
||||||
source := &Source{
|
|
||||||
wireguardConfigPath: dirPath,
|
|
||||||
}
|
|
||||||
wireguard, err := source.readWireguardSelection()
|
|
||||||
assert.Equal(t, settings.WireguardSelection{}, wireguard)
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Regexp(t, `reading file: read .+: is a directory`, err.Error())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("no file", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
noFile := filepath.Join(t.TempDir(), "doesnotexist")
|
|
||||||
source := &Source{
|
|
||||||
wireguardConfigPath: noFile,
|
|
||||||
}
|
|
||||||
wireguard, err := source.readWireguardSelection()
|
|
||||||
assert.Equal(t, settings.WireguardSelection{}, wireguard)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
fileContent string
|
|
||||||
selection settings.WireguardSelection
|
|
||||||
errMessage string
|
|
||||||
}{
|
|
||||||
"ini load error": {
|
|
||||||
fileContent: "invalid",
|
|
||||||
errMessage: "loading ini from reader: key-value delimiter not found: invalid",
|
|
||||||
},
|
|
||||||
"empty file": {},
|
|
||||||
"peer section parsing error": {
|
|
||||||
fileContent: `
|
|
||||||
[Peer]
|
|
||||||
PublicKey = x
|
|
||||||
`,
|
|
||||||
errMessage: "parsing peer section: parsing PublicKey: " +
|
|
||||||
"x: wgtypes: failed to parse base64-encoded key: " +
|
|
||||||
"illegal base64 data at input byte 0",
|
|
||||||
},
|
|
||||||
"success": {
|
|
||||||
fileContent: `
|
|
||||||
[Peer]
|
|
||||||
PublicKey = QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8=
|
|
||||||
Endpoint = 1.2.3.4:51820
|
|
||||||
`,
|
|
||||||
selection: settings.WireguardSelection{
|
|
||||||
PublicKey: "QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8=",
|
|
||||||
EndpointIP: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
|
|
||||||
EndpointPort: uint16Ptr(51820),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for testName, testCase := range testCases {
|
|
||||||
testCase := testCase
|
|
||||||
t.Run(testName, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
configFile := filepath.Join(t.TempDir(), "wg.conf")
|
|
||||||
err := os.WriteFile(configFile, []byte(testCase.fileContent), 0600)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
source := &Source{
|
|
||||||
wireguardConfigPath: configFile,
|
|
||||||
}
|
|
||||||
|
|
||||||
wireguard, err := source.readWireguardSelection()
|
|
||||||
|
|
||||||
assert.Equal(t, testCase.selection, wireguard)
|
|
||||||
if testCase.errMessage != "" {
|
|
||||||
assert.EqualError(t, err, testCase.errMessage)
|
|
||||||
} else {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_parseWireguardPeerSection(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
iniData string
|
|
||||||
selection settings.WireguardSelection
|
|
||||||
errMessage string
|
|
||||||
}{
|
|
||||||
"public key error": {
|
|
||||||
iniData: `[Peer]
|
|
||||||
PublicKey = x`,
|
|
||||||
errMessage: "parsing PublicKey: x: " +
|
|
||||||
"wgtypes: failed to parse base64-encoded key: " +
|
|
||||||
"illegal base64 data at input byte 0",
|
|
||||||
},
|
|
||||||
"public key set": {
|
|
||||||
iniData: `[Peer]
|
|
||||||
PublicKey = QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8=`,
|
|
||||||
selection: settings.WireguardSelection{
|
|
||||||
PublicKey: "QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8=",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"missing port in endpoint": {
|
|
||||||
iniData: `[Peer]
|
|
||||||
Endpoint = x`,
|
|
||||||
errMessage: "splitting endpoint: address x: missing port in address",
|
|
||||||
},
|
|
||||||
"endpoint host is not IP": {
|
|
||||||
iniData: `[Peer]
|
|
||||||
Endpoint = website.com:51820`,
|
|
||||||
errMessage: "endpoint host is not an IP: ParseAddr(\"website.com\"): unexpected character (at \"website.com\")",
|
|
||||||
},
|
|
||||||
"endpoint port is not valid": {
|
|
||||||
iniData: `[Peer]
|
|
||||||
Endpoint = 1.2.3.4:518299`,
|
|
||||||
errMessage: "port from Endpoint key: port cannot be higher than 65535: 518299",
|
|
||||||
},
|
|
||||||
"valid endpoint": {
|
|
||||||
iniData: `[Peer]
|
|
||||||
Endpoint = 1.2.3.4:51820`,
|
|
||||||
selection: settings.WireguardSelection{
|
|
||||||
EndpointIP: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
|
|
||||||
EndpointPort: uint16Ptr(51820),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"all set": {
|
|
||||||
iniData: `[Peer]
|
|
||||||
PublicKey = QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8=
|
|
||||||
Endpoint = 1.2.3.4:51820`,
|
|
||||||
selection: settings.WireguardSelection{
|
|
||||||
PublicKey: "QOlCgyA/Sn/c/+YNTIEohrjm8IZV+OZ2AUFIoX20sk8=",
|
|
||||||
EndpointIP: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
|
|
||||||
EndpointPort: uint16Ptr(51820),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for testName, testCase := range testCases {
|
|
||||||
testCase := testCase
|
|
||||||
t.Run(testName, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
iniFile, err := ini.Load([]byte(testCase.iniData))
|
|
||||||
require.NoError(t, err)
|
|
||||||
iniSection, err := iniFile.GetSection("Peer")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var selection settings.WireguardSelection
|
|
||||||
err = parseWireguardPeerSection(iniSection, &selection)
|
|
||||||
|
|
||||||
assert.Equal(t, testCase.selection, selection)
|
|
||||||
if testCase.errMessage != "" {
|
|
||||||
assert.EqualError(t, err, testCase.errMessage)
|
|
||||||
} else {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
package merge
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConfigSource interface {
|
|
||||||
Read() (settings settings.Settings, err error)
|
|
||||||
ReadHealth() (settings settings.Health, err error)
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Source struct {
|
|
||||||
sources []ConfigSource
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(sources ...ConfigSource) *Source {
|
|
||||||
return &Source{
|
|
||||||
sources: sources,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Source) String() string {
|
|
||||||
sources := make([]string, len(s.sources))
|
|
||||||
for i := range s.sources {
|
|
||||||
sources[i] = s.sources[i].String()
|
|
||||||
}
|
|
||||||
return strings.Join(sources, ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read reads the settings for each source, merging unset fields
|
|
||||||
// with field set by the next source.
|
|
||||||
// It then set defaults to remaining unset fields.
|
|
||||||
func (s *Source) Read() (settings settings.Settings, err error) {
|
|
||||||
for _, source := range s.sources {
|
|
||||||
settingsFromSource, err := source.Read()
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading from %s: %w", source, err)
|
|
||||||
}
|
|
||||||
settings.MergeWith(settingsFromSource)
|
|
||||||
}
|
|
||||||
settings.SetDefaults()
|
|
||||||
return settings, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadHealth reads the health settings for each source, merging unset fields
|
|
||||||
// with field set by the next source.
|
|
||||||
// It then set defaults to remaining unset fields, and validate
|
|
||||||
// all the fields.
|
|
||||||
func (s *Source) ReadHealth() (settings settings.Health, err error) {
|
|
||||||
for _, source := range s.sources {
|
|
||||||
settingsFromSource, err := source.ReadHealth()
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading from %s: %w", source, err)
|
|
||||||
}
|
|
||||||
settings.MergeWith(settingsFromSource)
|
|
||||||
}
|
|
||||||
settings.SetDefaults()
|
|
||||||
|
|
||||||
err = settings.Validate()
|
|
||||||
if err != nil {
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return settings, nil
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package secrets
|
|
||||||
|
|
||||||
import "github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
|
|
||||||
func (s *Source) ReadHealth() (settings settings.Health, err error) { return settings, nil }
|
|
||||||
@@ -1,58 +1,8 @@
|
|||||||
package secrets
|
package secrets
|
||||||
|
|
||||||
import (
|
func strPtrToStringIsSet(ptr *string) (s string, isSet bool) {
|
||||||
"fmt"
|
if ptr == nil {
|
||||||
"net/netip"
|
return "", false
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/sources/files"
|
|
||||||
"github.com/qdm12/gluetun/internal/openvpn/extract"
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readSecretFileAsStringPtr(secretPathEnvKey, defaultSecretPath string) (
|
|
||||||
stringPtr *string, err error) {
|
|
||||||
path := s.env.String(secretPathEnvKey, env.ForceLowercase(false))
|
|
||||||
if path == "" {
|
|
||||||
path = defaultSecretPath
|
|
||||||
}
|
}
|
||||||
return files.ReadFromFile(path)
|
return *ptr, true
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Source) readPEMSecretFile(secretPathEnvKey, defaultSecretPath string) (
|
|
||||||
base64Ptr *string, err error) {
|
|
||||||
pemData, err := s.readSecretFileAsStringPtr(secretPathEnvKey, defaultSecretPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("reading secret file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if pemData == nil {
|
|
||||||
return nil, nil //nolint:nilnil
|
|
||||||
}
|
|
||||||
|
|
||||||
base64Data, err := extract.PEM([]byte(*pemData))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("extracting base64 encoded data from PEM content: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &base64Data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseAddresses(addressesCSV string) (addresses []netip.Prefix, err error) {
|
|
||||||
if addressesCSV == "" {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
addressStrings := strings.Split(addressesCSV, ",")
|
|
||||||
addresses = make([]netip.Prefix, len(addressStrings))
|
|
||||||
for i, addressString := range addressStrings {
|
|
||||||
addressString = strings.TrimSpace(addressString)
|
|
||||||
addresses[i], err = netip.ParsePrefix(addressString)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("parsing address %d of %d: %w",
|
|
||||||
i+1, len(addressStrings), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return addresses, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
package secrets
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ptrTo[T any](value T) *T { return &value }
|
|
||||||
|
|
||||||
func Test_readSecretFileAsStringPtr(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
source func(tempDir string) Source
|
|
||||||
secretPathEnvKey string
|
|
||||||
defaultSecretFileName string
|
|
||||||
setupFile func(tempDir string) error
|
|
||||||
stringPtr *string
|
|
||||||
errWrapped error
|
|
||||||
errMessage string
|
|
||||||
}{
|
|
||||||
"no_secret_file": {
|
|
||||||
defaultSecretFileName: "default_secret_file",
|
|
||||||
secretPathEnvKey: "SECRET_FILE",
|
|
||||||
},
|
|
||||||
"empty_secret_file": {
|
|
||||||
defaultSecretFileName: "default_secret_file",
|
|
||||||
secretPathEnvKey: "SECRET_FILE",
|
|
||||||
setupFile: func(tempDir string) error {
|
|
||||||
secretFilepath := filepath.Join(tempDir, "default_secret_file")
|
|
||||||
return os.WriteFile(secretFilepath, nil, os.ModePerm)
|
|
||||||
},
|
|
||||||
stringPtr: ptrTo(""),
|
|
||||||
},
|
|
||||||
"default_secret_file": {
|
|
||||||
defaultSecretFileName: "default_secret_file",
|
|
||||||
secretPathEnvKey: "SECRET_FILE",
|
|
||||||
setupFile: func(tempDir string) error {
|
|
||||||
secretFilepath := filepath.Join(tempDir, "default_secret_file")
|
|
||||||
return os.WriteFile(secretFilepath, []byte("A"), os.ModePerm)
|
|
||||||
},
|
|
||||||
stringPtr: ptrTo("A"),
|
|
||||||
},
|
|
||||||
"env_specified_secret_file": {
|
|
||||||
source: func(tempDir string) Source {
|
|
||||||
secretFilepath := filepath.Join(tempDir, "secret_file")
|
|
||||||
environ := []string{"SECRET_FILE=" + secretFilepath}
|
|
||||||
return Source{env: *env.New(environ, nil)}
|
|
||||||
},
|
|
||||||
defaultSecretFileName: "default_secret_file",
|
|
||||||
secretPathEnvKey: "SECRET_FILE",
|
|
||||||
setupFile: func(tempDir string) error {
|
|
||||||
secretFilepath := filepath.Join(tempDir, "secret_file")
|
|
||||||
return os.WriteFile(secretFilepath, []byte("B"), os.ModePerm)
|
|
||||||
},
|
|
||||||
stringPtr: ptrTo("B"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, testCase := range testCases {
|
|
||||||
testCase := testCase
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
tempDir := t.TempDir()
|
|
||||||
|
|
||||||
var source Source
|
|
||||||
if testCase.source != nil {
|
|
||||||
source = testCase.source(tempDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultSecretPath := filepath.Join(tempDir, testCase.defaultSecretFileName)
|
|
||||||
if testCase.setupFile != nil {
|
|
||||||
err := testCase.setupFile(tempDir)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stringPtr, err := source.readSecretFileAsStringPtr(
|
|
||||||
testCase.secretPathEnvKey, defaultSecretPath)
|
|
||||||
|
|
||||||
assert.Equal(t, testCase.stringPtr, stringPtr)
|
|
||||||
assert.ErrorIs(t, err, testCase.errWrapped)
|
|
||||||
if testCase.errWrapped != nil {
|
|
||||||
assert.EqualError(t, err, testCase.errMessage)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package secrets
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readHTTPProxy() (settings settings.HTTPProxy, err error) {
|
|
||||||
settings.User, err = s.readSecretFileAsStringPtr(
|
|
||||||
"HTTPPROXY_USER_SECRETFILE",
|
|
||||||
"/run/secrets/httpproxy_user",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading HTTP proxy user secret file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.Password, err = s.readSecretFileAsStringPtr(
|
|
||||||
"HTTPPROXY_PASSWORD_SECRETFILE",
|
|
||||||
"/run/secrets/httpproxy_password",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading HTTP proxy password secret file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return settings, nil
|
|
||||||
}
|
|
||||||
5
internal/configuration/sources/secrets/interfaces.go
Normal file
5
internal/configuration/sources/secrets/interfaces.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package secrets
|
||||||
|
|
||||||
|
type Warner interface {
|
||||||
|
Warnf(format string, a ...interface{})
|
||||||
|
}
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
package secrets
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readOpenVPN() (
|
|
||||||
settings settings.OpenVPN, err error) {
|
|
||||||
settings.User, err = s.readSecretFileAsStringPtr(
|
|
||||||
"OPENVPN_USER_SECRETFILE",
|
|
||||||
"/run/secrets/openvpn_user",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading user file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.Password, err = s.readSecretFileAsStringPtr(
|
|
||||||
"OPENVPN_PASSWORD_SECRETFILE",
|
|
||||||
"/run/secrets/openvpn_password",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading password file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.Key, err = s.readPEMSecretFile(
|
|
||||||
"OPENVPN_CLIENTKEY_SECRETFILE",
|
|
||||||
"/run/secrets/openvpn_clientkey",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading client key file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.EncryptedKey, err = s.readPEMSecretFile(
|
|
||||||
"OPENVPN_ENCRYPTED_KEY_SECRETFILE",
|
|
||||||
"/run/secrets/openvpn_encrypted_key",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading encrypted key file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.KeyPassphrase, err = s.readSecretFileAsStringPtr(
|
|
||||||
"OPENVPN_KEY_PASSPHRASE_SECRETFILE",
|
|
||||||
"/run/secrets/openvpn_key_passphrase",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading key passphrase file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.Cert, err = s.readPEMSecretFile(
|
|
||||||
"OPENVPN_CLIENTCRT_SECRETFILE",
|
|
||||||
"/run/secrets/openvpn_clientcrt",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading client certificate file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return settings, nil
|
|
||||||
}
|
|
||||||
@@ -1,46 +1,104 @@
|
|||||||
package secrets
|
package secrets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/sources/files"
|
||||||
"github.com/qdm12/gosettings/sources/env"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Source struct {
|
type Source struct {
|
||||||
env env.Env
|
rootDirectory string
|
||||||
|
environ map[string]string
|
||||||
|
warner Warner
|
||||||
|
cached struct {
|
||||||
|
wireguardLoaded bool
|
||||||
|
wireguardConf files.WireguardConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(warner Warner) (source *Source) {
|
||||||
|
const rootDirectory = "/run/secrets"
|
||||||
|
osEnviron := os.Environ()
|
||||||
|
environ := make(map[string]string, len(osEnviron))
|
||||||
|
for _, pair := range osEnviron {
|
||||||
|
const maxSplit = 2
|
||||||
|
split := strings.SplitN(pair, "=", maxSplit)
|
||||||
|
environ[split[0]] = split[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Source {
|
|
||||||
handleDeprecatedKey := (func(deprecatedKey, newKey string))(nil)
|
|
||||||
return &Source{
|
return &Source{
|
||||||
env: *env.New(os.Environ(), handleDeprecatedKey),
|
rootDirectory: rootDirectory,
|
||||||
|
environ: environ,
|
||||||
|
warner: warner,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) String() string { return "secret files" }
|
func (s *Source) String() string { return "secret files" }
|
||||||
|
|
||||||
func (s *Source) Read() (settings settings.Settings, err error) {
|
func (s *Source) Get(key string) (value string, isSet bool) {
|
||||||
settings.VPN, err = s.readVPN()
|
if key == "" {
|
||||||
if err != nil {
|
return "", false
|
||||||
return settings, err
|
}
|
||||||
|
// TODO v4 custom environment variable to set the secrets parent directory
|
||||||
|
// and not to set each secret file to a specific path
|
||||||
|
envKey := strings.ToUpper(key)
|
||||||
|
envKey = strings.ReplaceAll(envKey, "-", "_")
|
||||||
|
envKey += "_SECRETFILE" // TODO v4 change _SECRETFILE to _FILE
|
||||||
|
path := s.environ[envKey]
|
||||||
|
if path == "" {
|
||||||
|
path = filepath.Join(s.rootDirectory, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.HTTPProxy, err = s.readHTTPProxy()
|
// Special file parsing
|
||||||
|
switch key {
|
||||||
|
// TODO timezone from /etc/localtime
|
||||||
|
case "openvpn_clientcrt", "openvpn_clientkey":
|
||||||
|
value, isSet, err := files.ReadPEMFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, err
|
s.warner.Warnf("skipping %s: parsing PEM: %s", path, err)
|
||||||
|
}
|
||||||
|
return value, isSet
|
||||||
|
case "wireguard_private_key":
|
||||||
|
privateKey := s.lazyLoadWireguardConf().PrivateKey
|
||||||
|
if privateKey != nil {
|
||||||
|
return *privateKey, true
|
||||||
|
} // else continue to read from individual secret file
|
||||||
|
case "wireguard_preshared_key":
|
||||||
|
preSharedKey := s.lazyLoadWireguardConf().PreSharedKey
|
||||||
|
if preSharedKey != nil {
|
||||||
|
return *preSharedKey, true
|
||||||
|
} // else continue to read from individual secret file
|
||||||
|
case "wireguard_addresses":
|
||||||
|
addresses := s.lazyLoadWireguardConf().Addresses
|
||||||
|
if addresses != nil {
|
||||||
|
return *addresses, true
|
||||||
|
} // else continue to read from individual secret file
|
||||||
|
case "wireguard_public_key":
|
||||||
|
return strPtrToStringIsSet(s.lazyLoadWireguardConf().PublicKey)
|
||||||
|
case "vpn_endpoint_ip":
|
||||||
|
return strPtrToStringIsSet(s.lazyLoadWireguardConf().EndpointIP)
|
||||||
|
case "vpn_endpoint_port":
|
||||||
|
return strPtrToStringIsSet(s.lazyLoadWireguardConf().EndpointPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Shadowsocks, err = s.readShadowsocks()
|
value, isSet, err := files.ReadFromFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, err
|
s.warner.Warnf("skipping %s: reading file: %s", path, err)
|
||||||
|
}
|
||||||
|
return value, isSet
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.VPN.Wireguard, err = s.readWireguard()
|
func (s *Source) KeyTransform(key string) string {
|
||||||
if err != nil {
|
switch key {
|
||||||
return settings, fmt.Errorf("reading Wireguard: %w", err)
|
// TODO v4 remove these irregular cases
|
||||||
|
case "OPENVPN_KEY":
|
||||||
|
return "openvpn_clientkey"
|
||||||
|
case "OPENVPN_CERT":
|
||||||
|
return "openvpn_clientcrt"
|
||||||
|
default:
|
||||||
|
key = strings.ToLower(key) // HTTPROXY_USER -> httpproxy_user
|
||||||
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
102
internal/configuration/sources/secrets/reader_test.go
Normal file
102
internal/configuration/sources/secrets/reader_test.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package secrets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Source_Get(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
makeSource func(tempDir string) (source *Source, err error)
|
||||||
|
key string
|
||||||
|
value string
|
||||||
|
isSet bool
|
||||||
|
}{
|
||||||
|
"empty_key": {
|
||||||
|
makeSource: func(tempDir string) (source *Source, err error) {
|
||||||
|
return &Source{
|
||||||
|
rootDirectory: tempDir,
|
||||||
|
environ: map[string]string{},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"no_secret_file": {
|
||||||
|
makeSource: func(tempDir string) (source *Source, err error) {
|
||||||
|
return &Source{
|
||||||
|
rootDirectory: tempDir,
|
||||||
|
environ: map[string]string{},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
key: "test_file",
|
||||||
|
},
|
||||||
|
"empty_secret_file": {
|
||||||
|
makeSource: func(tempDir string) (source *Source, err error) {
|
||||||
|
secretFilepath := filepath.Join(tempDir, "test_file")
|
||||||
|
err = os.WriteFile(secretFilepath, nil, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Source{
|
||||||
|
rootDirectory: tempDir,
|
||||||
|
environ: map[string]string{},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
key: "test_file",
|
||||||
|
isSet: true,
|
||||||
|
},
|
||||||
|
"default_secret_file": {
|
||||||
|
makeSource: func(tempDir string) (source *Source, err error) {
|
||||||
|
secretFilepath := filepath.Join(tempDir, "test_file")
|
||||||
|
err = os.WriteFile(secretFilepath, []byte{'A'}, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Source{
|
||||||
|
rootDirectory: tempDir,
|
||||||
|
environ: map[string]string{},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
key: "test_file",
|
||||||
|
value: "A",
|
||||||
|
isSet: true,
|
||||||
|
},
|
||||||
|
"env_specified_secret_file": {
|
||||||
|
makeSource: func(tempDir string) (source *Source, err error) {
|
||||||
|
secretFilepath := filepath.Join(tempDir, "test_file_custom")
|
||||||
|
err = os.WriteFile(secretFilepath, []byte{'A'}, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Source{
|
||||||
|
rootDirectory: tempDir,
|
||||||
|
environ: map[string]string{
|
||||||
|
"TEST_FILE_SECRETFILE": secretFilepath,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
key: "test_file",
|
||||||
|
value: "A",
|
||||||
|
isSet: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
source, err := testCase.makeSource(t.TempDir())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
value, isSet := source.Get(testCase.key)
|
||||||
|
assert.Equal(t, testCase.value, value)
|
||||||
|
assert.Equal(t, testCase.isSet, isSet)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package secrets
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readShadowsocks() (settings settings.Shadowsocks, err error) {
|
|
||||||
settings.Password, err = s.readSecretFileAsStringPtr(
|
|
||||||
"SHADOWSOCKS_PASSWORD_SECRETFILE",
|
|
||||||
"/run/secrets/shadowsocks_password",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading Shadowsocks password secret file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return settings, nil
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package secrets
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Source) readVPN() (vpn settings.VPN, err error) {
|
|
||||||
vpn.OpenVPN, err = s.readOpenVPN()
|
|
||||||
if err != nil {
|
|
||||||
return vpn, fmt.Errorf("reading OpenVPN settings: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
vpn.Wireguard, err = s.readWireguard()
|
|
||||||
if err != nil {
|
|
||||||
return vpn, fmt.Errorf("reading Wireguard settings: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return vpn, nil
|
|
||||||
}
|
|
||||||
@@ -1,52 +1,27 @@
|
|||||||
package secrets
|
package secrets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/sources/files"
|
"github.com/qdm12/gluetun/internal/configuration/sources/files"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Source) readWireguard() (settings settings.Wireguard, err error) {
|
func (s *Source) lazyLoadWireguardConf() files.WireguardConfig {
|
||||||
wireguardConf, err := s.readSecretFileAsStringPtr(
|
if s.cached.wireguardLoaded {
|
||||||
"WIREGUARD_CONF_SECRETFILE",
|
return s.cached.wireguardConf
|
||||||
"/run/secrets/wg0.conf",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading Wireguard conf secret file: %w", err)
|
|
||||||
} else if wireguardConf != nil {
|
|
||||||
// Wireguard ini config file takes precedence over individual secrets
|
|
||||||
return files.ParseWireguardConf([]byte(*wireguardConf))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.PrivateKey, err = s.readSecretFileAsStringPtr(
|
path := os.Getenv("WIREGUARD_CONF_SECRETFILE")
|
||||||
"WIREGUARD_PRIVATE_KEY_SECRETFILE",
|
if path == "" {
|
||||||
"/run/secrets/wireguard_private_key",
|
path = filepath.Join(s.rootDirectory, "wg0.conf")
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading private key file: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.PreSharedKey, err = s.readSecretFileAsStringPtr(
|
s.cached.wireguardLoaded = true
|
||||||
"WIREGUARD_PRESHARED_KEY_SECRETFILE",
|
var err error
|
||||||
"/run/secrets/wireguard_preshared_key",
|
s.cached.wireguardConf, err = files.ParseWireguardConf(path)
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return settings, fmt.Errorf("reading preshared key file: %w", err)
|
s.warner.Warnf("skipping Wireguard config: %s", err)
|
||||||
}
|
}
|
||||||
|
return s.cached.wireguardConf
|
||||||
wireguardAddressesCSV, err := s.readSecretFileAsStringPtr(
|
|
||||||
"WIREGUARD_ADDRESSES_SECRETFILE",
|
|
||||||
"/run/secrets/wireguard_addresses",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("reading addresses file: %w", err)
|
|
||||||
} else if wireguardAddressesCSV != nil {
|
|
||||||
settings.Addresses, err = parseAddresses(*wireguardAddressesCSV)
|
|
||||||
if err != nil {
|
|
||||||
return settings, fmt.Errorf("parsing addresses: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return settings, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/validate"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
"github.com/qdm12/govalid/address"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Settings struct {
|
type Settings struct {
|
||||||
@@ -34,12 +34,12 @@ type Settings struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Settings) SetDefaults() {
|
func (s *Settings) SetDefaults() {
|
||||||
s.Address = gosettings.DefaultString(s.Address, ":8000")
|
s.Address = gosettings.DefaultComparable(s.Address, ":8000")
|
||||||
const defaultReadTimeout = 3 * time.Second
|
const defaultReadTimeout = 3 * time.Second
|
||||||
s.ReadHeaderTimeout = gosettings.DefaultNumber(s.ReadHeaderTimeout, defaultReadTimeout)
|
s.ReadHeaderTimeout = gosettings.DefaultComparable(s.ReadHeaderTimeout, defaultReadTimeout)
|
||||||
s.ReadTimeout = gosettings.DefaultNumber(s.ReadTimeout, defaultReadTimeout)
|
s.ReadTimeout = gosettings.DefaultComparable(s.ReadTimeout, defaultReadTimeout)
|
||||||
const defaultShutdownTimeout = 3 * time.Second
|
const defaultShutdownTimeout = 3 * time.Second
|
||||||
s.ShutdownTimeout = gosettings.DefaultNumber(s.ShutdownTimeout, defaultShutdownTimeout)
|
s.ShutdownTimeout = gosettings.DefaultComparable(s.ShutdownTimeout, defaultShutdownTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Settings) Copy() Settings {
|
func (s Settings) Copy() Settings {
|
||||||
@@ -53,26 +53,15 @@ func (s Settings) Copy() Settings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Settings) MergeWith(other Settings) {
|
|
||||||
s.Address = gosettings.MergeWithString(s.Address, other.Address)
|
|
||||||
s.Handler = gosettings.MergeWithInterface(s.Handler, other.Handler)
|
|
||||||
if s.Logger == nil {
|
|
||||||
s.Logger = other.Logger
|
|
||||||
}
|
|
||||||
s.ReadHeaderTimeout = gosettings.MergeWithNumber(s.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
|
||||||
s.ReadTimeout = gosettings.MergeWithNumber(s.ReadTimeout, other.ReadTimeout)
|
|
||||||
s.ShutdownTimeout = gosettings.MergeWithNumber(s.ShutdownTimeout, other.ShutdownTimeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Settings) OverrideWith(other Settings) {
|
func (s *Settings) OverrideWith(other Settings) {
|
||||||
s.Address = gosettings.OverrideWithString(s.Address, other.Address)
|
s.Address = gosettings.OverrideWithComparable(s.Address, other.Address)
|
||||||
s.Handler = gosettings.OverrideWithInterface(s.Handler, other.Handler)
|
s.Handler = gosettings.OverrideWithComparable(s.Handler, other.Handler)
|
||||||
if other.Logger != nil {
|
if other.Logger != nil {
|
||||||
s.Logger = other.Logger
|
s.Logger = other.Logger
|
||||||
}
|
}
|
||||||
s.ReadHeaderTimeout = gosettings.OverrideWithNumber(s.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
s.ReadHeaderTimeout = gosettings.OverrideWithComparable(s.ReadHeaderTimeout, other.ReadHeaderTimeout)
|
||||||
s.ReadTimeout = gosettings.OverrideWithNumber(s.ReadTimeout, other.ReadTimeout)
|
s.ReadTimeout = gosettings.OverrideWithComparable(s.ReadTimeout, other.ReadTimeout)
|
||||||
s.ShutdownTimeout = gosettings.OverrideWithNumber(s.ShutdownTimeout, other.ShutdownTimeout)
|
s.ShutdownTimeout = gosettings.OverrideWithComparable(s.ShutdownTimeout, other.ShutdownTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -84,8 +73,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (s Settings) Validate() (err error) {
|
func (s Settings) Validate() (err error) {
|
||||||
uid := os.Getuid()
|
err = validate.ListeningAddress(s.Address, os.Getuid())
|
||||||
err = address.Validate(s.Address, address.OptionListening(uid))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/govalid/address"
|
"github.com/qdm12/gosettings/validate"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -98,68 +98,6 @@ func Test_Settings_Copy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Settings_MergeWith(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
someHandler := http.NewServeMux()
|
|
||||||
someLogger := &testLogger{}
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
settings Settings
|
|
||||||
other Settings
|
|
||||||
expected Settings
|
|
||||||
}{
|
|
||||||
"merge empty with empty": {},
|
|
||||||
"merge empty with filled": {
|
|
||||||
other: Settings{
|
|
||||||
Address: ":8001",
|
|
||||||
Handler: someHandler,
|
|
||||||
Logger: someLogger,
|
|
||||||
ReadHeaderTimeout: time.Second,
|
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
|
||||||
expected: Settings{
|
|
||||||
Address: ":8001",
|
|
||||||
Handler: someHandler,
|
|
||||||
Logger: someLogger,
|
|
||||||
ReadHeaderTimeout: time.Second,
|
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"merge filled with empty": {
|
|
||||||
settings: Settings{
|
|
||||||
Address: ":8001",
|
|
||||||
Handler: someHandler,
|
|
||||||
Logger: someLogger,
|
|
||||||
ReadHeaderTimeout: time.Second,
|
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
|
||||||
expected: Settings{
|
|
||||||
Address: ":8001",
|
|
||||||
Handler: someHandler,
|
|
||||||
Logger: someLogger,
|
|
||||||
ReadHeaderTimeout: time.Second,
|
|
||||||
ReadTimeout: time.Second,
|
|
||||||
ShutdownTimeout: time.Second,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, testCase := range testCases {
|
|
||||||
testCase := testCase
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
testCase.settings.MergeWith(testCase.other)
|
|
||||||
|
|
||||||
assert.Equal(t, testCase.expected, testCase.settings)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Settings_OverrideWith(t *testing.T) {
|
func Test_Settings_OverrideWith(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -257,12 +195,12 @@ func Test_Settings_Validate(t *testing.T) {
|
|||||||
errWrapped error
|
errWrapped error
|
||||||
errMessage string
|
errMessage string
|
||||||
}{
|
}{
|
||||||
"bad address": {
|
"bad_address": {
|
||||||
settings: Settings{
|
settings: Settings{
|
||||||
Address: "noport",
|
Address: "address:notanint",
|
||||||
},
|
},
|
||||||
errWrapped: address.ErrValueNotValid,
|
errWrapped: validate.ErrPortNotAnInteger,
|
||||||
errMessage: "value is not valid: address noport: missing port in address",
|
errMessage: "port value is not an integer: notanint",
|
||||||
},
|
},
|
||||||
"nil handler": {
|
"nil handler": {
|
||||||
settings: Settings{
|
settings: Settings{
|
||||||
@@ -332,7 +270,7 @@ func Test_Settings_Validate(t *testing.T) {
|
|||||||
err := testCase.settings.Validate()
|
err := testCase.settings.Validate()
|
||||||
|
|
||||||
assert.ErrorIs(t, err, testCase.errWrapped)
|
assert.ErrorIs(t, err, testCase.errWrapped)
|
||||||
if err != nil {
|
if testCase.errMessage != "" {
|
||||||
assert.EqualError(t, err, testCase.errMessage)
|
assert.EqualError(t, err, testCase.errMessage)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -29,11 +29,11 @@ func (s Settings) Copy() (copied Settings) {
|
|||||||
|
|
||||||
func (s *Settings) OverrideWith(update Settings) {
|
func (s *Settings) OverrideWith(update Settings) {
|
||||||
s.Enabled = gosettings.OverrideWithPointer(s.Enabled, update.Enabled)
|
s.Enabled = gosettings.OverrideWithPointer(s.Enabled, update.Enabled)
|
||||||
s.PortForwarder = gosettings.OverrideWithInterface(s.PortForwarder, update.PortForwarder)
|
s.PortForwarder = gosettings.OverrideWithComparable(s.PortForwarder, update.PortForwarder)
|
||||||
s.Filepath = gosettings.OverrideWithString(s.Filepath, update.Filepath)
|
s.Filepath = gosettings.OverrideWithComparable(s.Filepath, update.Filepath)
|
||||||
s.Interface = gosettings.OverrideWithString(s.Interface, update.Interface)
|
s.Interface = gosettings.OverrideWithComparable(s.Interface, update.Interface)
|
||||||
s.ServerName = gosettings.OverrideWithString(s.ServerName, update.ServerName)
|
s.ServerName = gosettings.OverrideWithComparable(s.ServerName, update.ServerName)
|
||||||
s.ListeningPort = gosettings.OverrideWithNumber(s.ListeningPort, update.ListeningPort)
|
s.ListeningPort = gosettings.OverrideWithComparable(s.ListeningPort, update.ListeningPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/httpserver"
|
"github.com/qdm12/gluetun/internal/httpserver"
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
|
"github.com/qdm12/gosettings/reader"
|
||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,9 +29,9 @@ type Settings struct {
|
|||||||
|
|
||||||
func (s *Settings) SetDefaults() {
|
func (s *Settings) SetDefaults() {
|
||||||
s.Enabled = gosettings.DefaultPointer(s.Enabled, false)
|
s.Enabled = gosettings.DefaultPointer(s.Enabled, false)
|
||||||
s.HTTPServer.Address = gosettings.DefaultString(s.HTTPServer.Address, "localhost:6060")
|
s.HTTPServer.Address = gosettings.DefaultComparable(s.HTTPServer.Address, "localhost:6060")
|
||||||
const defaultReadTimeout = 5 * time.Minute // for CPU profiling
|
const defaultReadTimeout = 5 * time.Minute // for CPU profiling
|
||||||
s.HTTPServer.ReadTimeout = gosettings.DefaultNumber(s.HTTPServer.ReadTimeout, defaultReadTimeout)
|
s.HTTPServer.ReadTimeout = gosettings.DefaultComparable(s.HTTPServer.ReadTimeout, defaultReadTimeout)
|
||||||
s.HTTPServer.SetDefaults()
|
s.HTTPServer.SetDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,13 +44,6 @@ func (s Settings) Copy() (copied Settings) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Settings) MergeWith(other Settings) {
|
|
||||||
s.Enabled = gosettings.MergeWithPointer(s.Enabled, other.Enabled)
|
|
||||||
s.BlockProfileRate = gosettings.MergeWithPointer(s.BlockProfileRate, other.BlockProfileRate)
|
|
||||||
s.MutexProfileRate = gosettings.MergeWithPointer(s.MutexProfileRate, other.MutexProfileRate)
|
|
||||||
s.HTTPServer.MergeWith(other.HTTPServer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Settings) OverrideWith(other Settings) {
|
func (s *Settings) OverrideWith(other Settings) {
|
||||||
s.Enabled = gosettings.OverrideWithPointer(s.Enabled, other.Enabled)
|
s.Enabled = gosettings.OverrideWithPointer(s.Enabled, other.Enabled)
|
||||||
s.BlockProfileRate = gosettings.OverrideWithPointer(s.BlockProfileRate, other.BlockProfileRate)
|
s.BlockProfileRate = gosettings.OverrideWithPointer(s.BlockProfileRate, other.BlockProfileRate)
|
||||||
@@ -97,3 +91,24 @@ func (s Settings) ToLinesNode() (node *gotree.Node) {
|
|||||||
func (s Settings) String() string {
|
func (s Settings) String() string {
|
||||||
return s.ToLinesNode().String()
|
return s.ToLinesNode().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Settings) Read(r *reader.Reader) (err error) {
|
||||||
|
s.Enabled, err = r.BoolPtr("PPROF_ENABLED")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.BlockProfileRate, err = r.IntPtr("PPROF_BLOCK_PROFILE_RATE")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.MutexProfileRate, err = r.IntPtr("PPROF_MUTEX_PROFILE_RATE")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.HTTPServer.Address = r.String("PPROF_HTTP_SERVER_ADDRESS")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/httpserver"
|
"github.com/qdm12/gluetun/internal/httpserver"
|
||||||
"github.com/qdm12/govalid/address"
|
"github.com/qdm12/gosettings/validate"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -108,65 +108,6 @@ func Test_Settings_Copy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Settings_MergeWith(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
settings Settings
|
|
||||||
other Settings
|
|
||||||
expected Settings
|
|
||||||
}{
|
|
||||||
"merge empty with empty": {},
|
|
||||||
"merge empty with filled": {
|
|
||||||
other: Settings{
|
|
||||||
Enabled: boolPtr(true),
|
|
||||||
BlockProfileRate: intPtr(1),
|
|
||||||
MutexProfileRate: intPtr(1),
|
|
||||||
HTTPServer: httpserver.Settings{
|
|
||||||
Address: ":8001",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: Settings{
|
|
||||||
Enabled: boolPtr(true),
|
|
||||||
BlockProfileRate: intPtr(1),
|
|
||||||
MutexProfileRate: intPtr(1),
|
|
||||||
HTTPServer: httpserver.Settings{
|
|
||||||
Address: ":8001",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"merge filled with empty": {
|
|
||||||
settings: Settings{
|
|
||||||
Enabled: boolPtr(true),
|
|
||||||
BlockProfileRate: intPtr(1),
|
|
||||||
MutexProfileRate: intPtr(1),
|
|
||||||
HTTPServer: httpserver.Settings{
|
|
||||||
Address: ":8001",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: Settings{
|
|
||||||
Enabled: boolPtr(true),
|
|
||||||
BlockProfileRate: intPtr(1),
|
|
||||||
MutexProfileRate: intPtr(1),
|
|
||||||
HTTPServer: httpserver.Settings{
|
|
||||||
Address: ":8001",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, testCase := range testCases {
|
|
||||||
testCase := testCase
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
testCase.settings.MergeWith(testCase.other)
|
|
||||||
|
|
||||||
assert.Equal(t, testCase.expected, testCase.settings)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Settings_OverrideWith(t *testing.T) {
|
func Test_Settings_OverrideWith(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -280,10 +221,12 @@ func Test_Settings_Validate(t *testing.T) {
|
|||||||
settings: Settings{
|
settings: Settings{
|
||||||
BlockProfileRate: intPtr(0),
|
BlockProfileRate: intPtr(0),
|
||||||
MutexProfileRate: intPtr(0),
|
MutexProfileRate: intPtr(0),
|
||||||
HTTPServer: httpserver.Settings{},
|
HTTPServer: httpserver.Settings{
|
||||||
|
Address: ":x",
|
||||||
},
|
},
|
||||||
errWrapped: address.ErrValueNotValid,
|
},
|
||||||
errMessage: "value is not valid: missing port in address",
|
errWrapped: validate.ErrPortNotAnInteger,
|
||||||
|
errMessage: "port value is not an integer: x",
|
||||||
},
|
},
|
||||||
"valid settings": {
|
"valid settings": {
|
||||||
settings: Settings{
|
settings: Settings{
|
||||||
@@ -309,7 +252,7 @@ func Test_Settings_Validate(t *testing.T) {
|
|||||||
err := testCase.settings.Validate()
|
err := testCase.settings.Validate()
|
||||||
|
|
||||||
assert.ErrorIs(t, err, testCase.errWrapped)
|
assert.ErrorIs(t, err, testCase.errWrapped)
|
||||||
if err != nil {
|
if testCase.errMessage != "" {
|
||||||
assert.EqualError(t, err, testCase.errMessage)
|
assert.EqualError(t, err, testCase.errMessage)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user