feat(server): HTTP_CONTROL_SERVER_PORT to HTTP_CONTROL_SERVER_ADDRESS

This commit is contained in:
Quentin McGaw
2022-01-27 23:15:08 +00:00
parent f3692cd47f
commit 1b585159d1
7 changed files with 49 additions and 31 deletions

View File

@@ -160,6 +160,8 @@ ENV VPNSP=pia \
SHADOWSOCKS_PASSWORD= \ SHADOWSOCKS_PASSWORD= \
SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password \ SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password \
SHADOWSOCKS_CIPHER=chacha20-ietf-poly1305 \ SHADOWSOCKS_CIPHER=chacha20-ietf-poly1305 \
# Control server
HTTP_CONTROL_SERVER_ADDRESS=":8000" \
# Server data updater # Server data updater
UPDATER_PERIOD=0 \ UPDATER_PERIOD=0 \
UPDATER_VPN_SERVICE_PROVIDERS= \ UPDATER_VPN_SERVICE_PROVIDERS= \

View File

@@ -419,7 +419,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
go shadowsocksLooper.Run(shadowsocksCtx, shadowsocksDone) go shadowsocksLooper.Run(shadowsocksCtx, shadowsocksDone)
otherGroupHandler.Add(shadowsocksHandler) otherGroupHandler.Add(shadowsocksHandler)
controlServerAddress := fmt.Sprintf(":%d", *allSettings.ControlServer.Port) controlServerAddress := *allSettings.ControlServer.Address
controlServerLogging := *allSettings.ControlServer.Log controlServerLogging := *allSettings.ControlServer.Log
httpServerHandler, httpServerCtx, httpServerDone := goshutdown.NewGoRoutineHandler( httpServerHandler, httpServerCtx, httpServerDone := goshutdown.NewGoRoutineHandler(
"http server", goroutine.OptionTimeout(defaultShutdownTimeout)) "http server", goroutine.OptionTimeout(defaultShutdownTimeout))

View File

@@ -4,6 +4,8 @@ import "errors"
var ( var (
ErrCityNotValid = errors.New("the city specified is not valid") 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") ErrControlServerPrivilegedPort = errors.New("cannot use privileged port without running as root")
ErrCountryNotValid = errors.New("the country specified is not valid") ErrCountryNotValid = errors.New("the country specified is not valid")
ErrFirewallZeroPort = errors.New("cannot have a zero port to block") ErrFirewallZeroPort = errors.New("cannot have a zero port to block")
@@ -33,6 +35,7 @@ var (
ErrSystemPGIDNotValid = errors.New("process group id is not valid") ErrSystemPGIDNotValid = errors.New("process group id is not valid")
ErrSystemPUIDNotValid = errors.New("process user id is not valid") ErrSystemPUIDNotValid = errors.New("process user id is not valid")
ErrSystemTimezoneNotValid = errors.New("timezone 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") ErrVPNProviderNameNotValid = errors.New("VPN provider name is not valid")
ErrVPNTypeNotValid = errors.New("VPN type is not valid") ErrVPNTypeNotValid = errors.New("VPN type is not valid")
ErrWireguardEndpointIPNotSet = errors.New("endpoint IP is not set") ErrWireguardEndpointIPNotSet = errors.New("endpoint IP is not set")
@@ -46,6 +49,4 @@ var (
ErrWireguardPrivateKeyNotValid = errors.New("private key is not valid") ErrWireguardPrivateKeyNotValid = errors.New("private key is not valid")
ErrWireguardPublicKeyNotSet = errors.New("public key is not set") ErrWireguardPublicKeyNotSet = errors.New("public key is not set")
ErrWireguardPublicKeyNotValid = errors.New("public key is not valid") ErrWireguardPublicKeyNotValid = errors.New("public key is not valid")
ErrUpdaterPeriodTooSmall = errors.New("VPN server data updater period is too small")
) )

View File

@@ -2,7 +2,9 @@ package settings
import ( import (
"fmt" "fmt"
"net"
"os" "os"
"strconv"
"github.com/qdm12/gluetun/internal/configuration/settings/helpers" "github.com/qdm12/gluetun/internal/configuration/settings/helpers"
"github.com/qdm12/gotree" "github.com/qdm12/gotree"
@@ -10,22 +12,30 @@ import (
// ControlServer contains settings to customize the control server operation. // ControlServer contains settings to customize the control server operation.
type ControlServer struct { type ControlServer struct {
// Port is the listening port to use. // Address is the listening address to use.
// It can be set to 0 to bind to a random port.
// It cannot be nil in the internal state. // It cannot be nil in the internal state.
// TODO change to address Address *string
Port *uint16
// Log can be true or false to enable logging on requests. // Log can be true or false to enable logging on requests.
// It cannot be nil in the internal state. // It cannot be nil in the internal state.
Log *bool Log *bool
} }
func (c ControlServer) validate() (err error) { 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() uid := os.Getuid()
const maxPrivilegedPort uint16 = 1023 const maxPrivilegedPort = 1023
if uid != 0 && *c.Port <= maxPrivilegedPort { if uid != 0 && port <= maxPrivilegedPort {
return fmt.Errorf("%w: %d when running with user ID %d", return fmt.Errorf("%w: %d when running with user ID %d",
ErrControlServerPrivilegedPort, *c.Port, uid) ErrControlServerPrivilegedPort, port, uid)
} }
return nil return nil
@@ -33,7 +43,7 @@ func (c ControlServer) validate() (err error) {
func (c *ControlServer) copy() (copied ControlServer) { func (c *ControlServer) copy() (copied ControlServer) {
return ControlServer{ return ControlServer{
Port: helpers.CopyUint16Ptr(c.Port), Address: helpers.CopyStringPtr(c.Address),
Log: helpers.CopyBoolPtr(c.Log), Log: helpers.CopyBoolPtr(c.Log),
} }
} }
@@ -41,7 +51,7 @@ func (c *ControlServer) copy() (copied ControlServer) {
// mergeWith merges the other settings into any // mergeWith merges the other settings into any
// unset field of the receiver settings object. // unset field of the receiver settings object.
func (c *ControlServer) mergeWith(other ControlServer) { func (c *ControlServer) mergeWith(other ControlServer) {
c.Port = helpers.MergeWithUint16(c.Port, other.Port) c.Address = helpers.MergeWithStringPtr(c.Address, other.Address)
c.Log = helpers.MergeWithBool(c.Log, other.Log) 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 object with any field set in the other
// settings. // settings.
func (c *ControlServer) overrideWith(other ControlServer) { func (c *ControlServer) overrideWith(other ControlServer) {
c.Port = helpers.MergeWithUint16(c.Port, other.Port) c.Address = helpers.OverrideWithStringPtr(c.Address, other.Address)
c.Log = helpers.MergeWithBool(c.Log, other.Log) c.Log = helpers.OverrideWithBool(c.Log, other.Log)
} }
func (c *ControlServer) setDefaults() { func (c *ControlServer) setDefaults() {
const defaultPort = 8000 c.Address = helpers.DefaultStringPtr(c.Address, ":8000")
c.Port = helpers.DefaultUint16(c.Port, defaultPort)
c.Log = helpers.DefaultBool(c.Log, true) c.Log = helpers.DefaultBool(c.Log, true)
} }
@@ -65,7 +74,7 @@ func (c ControlServer) String() string {
func (c ControlServer) toLinesNode() (node *gotree.Node) { func (c ControlServer) toLinesNode() (node *gotree.Node) {
node = gotree.New("Control server settings:") 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)) node.Appendf("Logging: %s", helpers.BoolPtrToYesNo(c.Log))
return node return node
} }

View File

@@ -75,7 +75,7 @@ func Test_Settings_String(t *testing.T) {
├── HTTP proxy settings: ├── HTTP proxy settings:
| └── Enabled: no | └── Enabled: no
├── Control server settings: ├── Control server settings:
| ├── Listening port: 8000 | ├── Listening address: :8000
| └── Logging: yes | └── Logging: yes
├── OS Alpine settings: ├── OS Alpine settings:
| ├── Process UID: 1000 | ├── Process UID: 1000

View File

@@ -77,7 +77,7 @@ func (r *Reader) Read() (settings settings.Settings, err error) {
return settings, err return settings, err
} }
settings.ControlServer, err = readControlServer() settings.ControlServer, err = r.readControlServer()
if err != nil { if err != nil {
return settings, err return settings, err
} }

View File

@@ -9,13 +9,13 @@ import (
"github.com/qdm12/govalid/port" "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() controlServer.Log, err = readControlServerLog()
if err != nil { if err != nil {
return controlServer, err return controlServer, err
} }
controlServer.Port, err = readControlServerPort() controlServer.Address, err = r.readControlServerAddress()
if err != nil { if err != nil {
return controlServer, err return controlServer, err
} }
@@ -37,17 +37,23 @@ func readControlServerLog() (enabled *bool, err error) {
return &log, nil 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") s := os.Getenv("HTTP_CONTROL_SERVER_PORT")
if s == "" { if s != "" {
return nil, nil //nolint:nilnil r.onRetroActive("HTTP_CONTROL_SERVER_PORT", "HTTP_CONTROL_SERVER_ADDRESS")
} port, err := port.Validate(s)
p = new(uint16)
*p, err = port.Validate(s, port.OptionPortListening(os.Geteuid()))
if err != nil { if err != nil {
return nil, fmt.Errorf("environment variable HTTP_CONTROL_SERVER_PORT: %w", err) return nil, fmt.Errorf("environment variable HTTP_CONTROL_SERVER_PORT: %w", err)
} }
address = new(string)
*address = ":" + fmt.Sprint(port)
return address, nil
}
return p, nil s = os.Getenv("HTTP_CONTROL_SERVER_ADDRESS")
if s == "" {
return nil, nil //nolint:nilnil
}
return &s, nil
} }