From 1b585159d15b6cf8cdf8dc3a5bc88244b800cacb Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Thu, 27 Jan 2022 23:15:08 +0000 Subject: [PATCH] feat(server): `HTTP_CONTROL_SERVER_PORT` to `HTTP_CONTROL_SERVER_ADDRESS` --- Dockerfile | 2 + cmd/gluetun/main.go | 2 +- internal/configuration/settings/errors.go | 5 ++- internal/configuration/settings/server.go | 39 ++++++++++++------- .../configuration/settings/settings_test.go | 2 +- internal/configuration/sources/env/reader.go | 2 +- internal/configuration/sources/env/server.go | 28 +++++++------ 7 files changed, 49 insertions(+), 31 deletions(-) diff --git a/Dockerfile b/Dockerfile index d0034aec..e9d6d624 100644 --- a/Dockerfile +++ b/Dockerfile @@ -160,6 +160,8 @@ ENV VPNSP=pia \ SHADOWSOCKS_PASSWORD= \ SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password \ SHADOWSOCKS_CIPHER=chacha20-ietf-poly1305 \ + # Control server + HTTP_CONTROL_SERVER_ADDRESS=":8000" \ # Server data updater UPDATER_PERIOD=0 \ UPDATER_VPN_SERVICE_PROVIDERS= \ diff --git a/cmd/gluetun/main.go b/cmd/gluetun/main.go index 78a29e42..de9ff2b8 100644 --- a/cmd/gluetun/main.go +++ b/cmd/gluetun/main.go @@ -419,7 +419,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation, go shadowsocksLooper.Run(shadowsocksCtx, shadowsocksDone) otherGroupHandler.Add(shadowsocksHandler) - controlServerAddress := fmt.Sprintf(":%d", *allSettings.ControlServer.Port) + controlServerAddress := *allSettings.ControlServer.Address controlServerLogging := *allSettings.ControlServer.Log httpServerHandler, httpServerCtx, httpServerDone := goshutdown.NewGoRoutineHandler( "http server", goroutine.OptionTimeout(defaultShutdownTimeout)) diff --git a/internal/configuration/settings/errors.go b/internal/configuration/settings/errors.go index e93b2889..88879d0a 100644 --- a/internal/configuration/settings/errors.go +++ b/internal/configuration/settings/errors.go @@ -4,6 +4,8 @@ import "errors" var ( ErrCityNotValid = errors.New("the city specified is not valid") + ErrControlServerAddress = errors.New("listening address it not valid") + ErrControlServerPort = errors.New("listening port it not valid") ErrControlServerPrivilegedPort = errors.New("cannot use privileged port without running as root") ErrCountryNotValid = errors.New("the country specified is not valid") ErrFirewallZeroPort = errors.New("cannot have a zero port to block") @@ -33,6 +35,7 @@ var ( ErrSystemPGIDNotValid = errors.New("process group id is not valid") ErrSystemPUIDNotValid = errors.New("process user id is not valid") ErrSystemTimezoneNotValid = errors.New("timezone is not valid") + ErrUpdaterPeriodTooSmall = errors.New("VPN server data updater period is too small") ErrVPNProviderNameNotValid = errors.New("VPN provider name is not valid") ErrVPNTypeNotValid = errors.New("VPN type is not valid") ErrWireguardEndpointIPNotSet = errors.New("endpoint IP is not set") @@ -46,6 +49,4 @@ var ( ErrWireguardPrivateKeyNotValid = errors.New("private key is not valid") ErrWireguardPublicKeyNotSet = errors.New("public key is not set") ErrWireguardPublicKeyNotValid = errors.New("public key is not valid") - - ErrUpdaterPeriodTooSmall = errors.New("VPN server data updater period is too small") ) diff --git a/internal/configuration/settings/server.go b/internal/configuration/settings/server.go index 9b7f488f..bb39602e 100644 --- a/internal/configuration/settings/server.go +++ b/internal/configuration/settings/server.go @@ -2,7 +2,9 @@ package settings import ( "fmt" + "net" "os" + "strconv" "github.com/qdm12/gluetun/internal/configuration/settings/helpers" "github.com/qdm12/gotree" @@ -10,22 +12,30 @@ import ( // ControlServer contains settings to customize the control server operation. type ControlServer struct { - // Port is the listening port to use. - // It can be set to 0 to bind to a random port. + // Address is the listening address to use. // It cannot be nil in the internal state. - // TODO change to address - Port *uint16 + Address *string // Log can be true or false to enable logging on requests. // It cannot be nil in the internal state. Log *bool } func (c ControlServer) validate() (err error) { + _, portStr, err := net.SplitHostPort(*c.Address) + if err != nil { + return fmt.Errorf("%w: %s", ErrControlServerAddress, err) + } + + port, err := strconv.Atoi(portStr) + if err != nil { + return fmt.Errorf("%w: %s", ErrControlServerPort, err) + } + uid := os.Getuid() - const maxPrivilegedPort uint16 = 1023 - if uid != 0 && *c.Port <= maxPrivilegedPort { + const maxPrivilegedPort = 1023 + if uid != 0 && port <= maxPrivilegedPort { return fmt.Errorf("%w: %d when running with user ID %d", - ErrControlServerPrivilegedPort, *c.Port, uid) + ErrControlServerPrivilegedPort, port, uid) } return nil @@ -33,15 +43,15 @@ func (c ControlServer) validate() (err error) { func (c *ControlServer) copy() (copied ControlServer) { return ControlServer{ - Port: helpers.CopyUint16Ptr(c.Port), - Log: helpers.CopyBoolPtr(c.Log), + Address: helpers.CopyStringPtr(c.Address), + Log: helpers.CopyBoolPtr(c.Log), } } // mergeWith merges the other settings into any // unset field of the receiver settings object. func (c *ControlServer) mergeWith(other ControlServer) { - c.Port = helpers.MergeWithUint16(c.Port, other.Port) + c.Address = helpers.MergeWithStringPtr(c.Address, other.Address) c.Log = helpers.MergeWithBool(c.Log, other.Log) } @@ -49,13 +59,12 @@ func (c *ControlServer) mergeWith(other ControlServer) { // settings object with any field set in the other // settings. func (c *ControlServer) overrideWith(other ControlServer) { - c.Port = helpers.MergeWithUint16(c.Port, other.Port) - c.Log = helpers.MergeWithBool(c.Log, other.Log) + c.Address = helpers.OverrideWithStringPtr(c.Address, other.Address) + c.Log = helpers.OverrideWithBool(c.Log, other.Log) } func (c *ControlServer) setDefaults() { - const defaultPort = 8000 - c.Port = helpers.DefaultUint16(c.Port, defaultPort) + c.Address = helpers.DefaultStringPtr(c.Address, ":8000") c.Log = helpers.DefaultBool(c.Log, true) } @@ -65,7 +74,7 @@ func (c ControlServer) String() string { func (c ControlServer) toLinesNode() (node *gotree.Node) { node = gotree.New("Control server settings:") - node.Appendf("Listening port: %d", *c.Port) + node.Appendf("Listening address: %s", *c.Address) node.Appendf("Logging: %s", helpers.BoolPtrToYesNo(c.Log)) return node } diff --git a/internal/configuration/settings/settings_test.go b/internal/configuration/settings/settings_test.go index 1462606b..d64a2f0a 100644 --- a/internal/configuration/settings/settings_test.go +++ b/internal/configuration/settings/settings_test.go @@ -75,7 +75,7 @@ func Test_Settings_String(t *testing.T) { ├── HTTP proxy settings: | └── Enabled: no ├── Control server settings: -| ├── Listening port: 8000 +| ├── Listening address: :8000 | └── Logging: yes ├── OS Alpine settings: | ├── Process UID: 1000 diff --git a/internal/configuration/sources/env/reader.go b/internal/configuration/sources/env/reader.go index 87f686ca..e6c787a9 100644 --- a/internal/configuration/sources/env/reader.go +++ b/internal/configuration/sources/env/reader.go @@ -77,7 +77,7 @@ func (r *Reader) Read() (settings settings.Settings, err error) { return settings, err } - settings.ControlServer, err = readControlServer() + settings.ControlServer, err = r.readControlServer() if err != nil { return settings, err } diff --git a/internal/configuration/sources/env/server.go b/internal/configuration/sources/env/server.go index 51cdee96..e51b30b6 100644 --- a/internal/configuration/sources/env/server.go +++ b/internal/configuration/sources/env/server.go @@ -9,13 +9,13 @@ import ( "github.com/qdm12/govalid/port" ) -func readControlServer() (controlServer settings.ControlServer, err error) { +func (r *Reader) readControlServer() (controlServer settings.ControlServer, err error) { controlServer.Log, err = readControlServerLog() if err != nil { return controlServer, err } - controlServer.Port, err = readControlServerPort() + controlServer.Address, err = r.readControlServerAddress() if err != nil { return controlServer, err } @@ -37,17 +37,23 @@ func readControlServerLog() (enabled *bool, err error) { return &log, nil } -func readControlServerPort() (p *uint16, err error) { +func (r *Reader) readControlServerAddress() (address *string, err error) { + // Retro-compatibility s := os.Getenv("HTTP_CONTROL_SERVER_PORT") + if s != "" { + r.onRetroActive("HTTP_CONTROL_SERVER_PORT", "HTTP_CONTROL_SERVER_ADDRESS") + port, err := port.Validate(s) + if err != nil { + return nil, fmt.Errorf("environment variable HTTP_CONTROL_SERVER_PORT: %w", err) + } + address = new(string) + *address = ":" + fmt.Sprint(port) + return address, nil + } + + s = os.Getenv("HTTP_CONTROL_SERVER_ADDRESS") if s == "" { return nil, nil //nolint:nilnil } - - p = new(uint16) - *p, err = port.Validate(s, port.OptionPortListening(os.Geteuid())) - if err != nil { - return nil, fmt.Errorf("environment variable HTTP_CONTROL_SERVER_PORT: %w", err) - } - - return p, nil + return &s, nil }