chore(settings): refactor settings processing (#756)
- Better settings tree structure logged using `qdm12/gotree` - Read settings from environment variables, then files, then secret files - Settings methods to default them, merge them and override them - `DNS_PLAINTEXT_ADDRESS` default changed to `127.0.0.1` to use DoT. Warning added if set to something else. - `HTTPPROXY_LISTENING_ADDRESS` instead of `HTTPPROXY_PORT` (with retro-compatibility)
This commit is contained in:
51
internal/configuration/sources/env/dns.go
vendored
Normal file
51
internal/configuration/sources/env/dns.go
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func (r *Reader) readDNS() (dns settings.DNS, err error) {
|
||||
dns.ServerAddress, err = r.readDNSServerAddress()
|
||||
if err != nil {
|
||||
return dns, err
|
||||
}
|
||||
|
||||
dns.KeepNameserver, err = envToBoolPtr("DNS_KEEP_NAMESERVER")
|
||||
if err != nil {
|
||||
return dns, fmt.Errorf("environment variable DNS_KEEP_NAMESERVER: %w", err)
|
||||
}
|
||||
|
||||
dns.DoT, err = r.readDoT()
|
||||
if err != nil {
|
||||
return dns, fmt.Errorf("cannot read DoT settings: %w", err)
|
||||
}
|
||||
|
||||
return dns, nil
|
||||
}
|
||||
|
||||
func (r *Reader) readDNSServerAddress() (address net.IP, err error) {
|
||||
s := os.Getenv("DNS_PLAINTEXT_ADDRESS")
|
||||
if s == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
address = net.ParseIP(s)
|
||||
if address == nil {
|
||||
return nil, fmt.Errorf("environment variable DNS_PLAINTEXT_ADDRESS: %w: %s", ErrIPAddressParse, s)
|
||||
}
|
||||
|
||||
// TODO remove in v4
|
||||
if !address.Equal(net.IPv4(127, 0, 0, 1)) { //nolint:gomnd
|
||||
r.warner.Warn("DNS_PLAINTEXT_ADDRESS is set to " + s +
|
||||
" 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 server." +
|
||||
" 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
|
||||
}
|
||||
90
internal/configuration/sources/env/dnsblacklist.go
vendored
Normal file
90
internal/configuration/sources/env/dnsblacklist.go
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"inet.af/netaddr"
|
||||
)
|
||||
|
||||
func (r *Reader) readDNSBlacklist() (blacklist settings.DNSBlacklist, err error) {
|
||||
blacklist.BlockMalicious, err = envToBoolPtr("BLOCK_MALICIOUS")
|
||||
if err != nil {
|
||||
return blacklist, fmt.Errorf("environment variable BLOCK_MALICIOUS: %w", err)
|
||||
}
|
||||
|
||||
blacklist.BlockSurveillance, err = r.readBlockSurveillance()
|
||||
if err != nil {
|
||||
return blacklist, fmt.Errorf("environment variable BLOCK_MALICIOUS: %w", err)
|
||||
}
|
||||
|
||||
blacklist.BlockAds, err = envToBoolPtr("BLOCK_ADS")
|
||||
if err != nil {
|
||||
return blacklist, fmt.Errorf("environment variable BLOCK_ADS: %w", err)
|
||||
}
|
||||
|
||||
blacklist.AddBlockedIPs, blacklist.AddBlockedIPPrefixes,
|
||||
err = readDoTPrivateAddresses() // TODO v4 split in 2
|
||||
if err != nil {
|
||||
return blacklist, err
|
||||
}
|
||||
|
||||
blacklist.AllowedHosts = envToCSV("UNBLOCK") // TODO v4 change name
|
||||
|
||||
return blacklist, nil
|
||||
}
|
||||
|
||||
func (r *Reader) readBlockSurveillance() (blocked *bool, err error) {
|
||||
blocked, err = envToBoolPtr("BLOCK_SURVEILLANCE")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("environment variable BLOCK_SURVEILLANCE: %w", err)
|
||||
} else if blocked != nil {
|
||||
return blocked, nil
|
||||
}
|
||||
|
||||
blocked, err = envToBoolPtr("BLOCK_NSA")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("environment variable BLOCK_NSA: %w", err)
|
||||
} else if blocked != nil {
|
||||
r.onRetroActive("BLOCK_NSA", "BLOCK_SURVEILLANCE")
|
||||
return blocked, nil
|
||||
}
|
||||
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
var (
|
||||
ErrPrivateAddressNotValid = errors.New("private address is not a valid IP or CIDR range")
|
||||
)
|
||||
|
||||
func readDoTPrivateAddresses() (ips []netaddr.IP,
|
||||
ipPrefixes []netaddr.IPPrefix, err error) {
|
||||
privateAddresses := envToCSV("DOT_PRIVATE_ADDRESS")
|
||||
if len(privateAddresses) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
ips = make([]netaddr.IP, 0, len(privateAddresses))
|
||||
ipPrefixes = make([]netaddr.IPPrefix, 0, len(privateAddresses))
|
||||
|
||||
for _, privateAddress := range privateAddresses {
|
||||
ip, err := netaddr.ParseIP(privateAddress)
|
||||
if err == nil {
|
||||
ips = append(ips, ip)
|
||||
continue
|
||||
}
|
||||
|
||||
ipPrefix, err := netaddr.ParseIPPrefix(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
|
||||
}
|
||||
31
internal/configuration/sources/env/dot.go
vendored
Normal file
31
internal/configuration/sources/env/dot.go
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func (r *Reader) readDoT() (dot settings.DoT, err error) {
|
||||
dot.Enabled, err = envToBoolPtr("DOT")
|
||||
if err != nil {
|
||||
return dot, fmt.Errorf("environment variable DOT: %w", err)
|
||||
}
|
||||
|
||||
dot.UpdatePeriod, err = envToDurationPtr("DNS_UPDATE_PERIOD")
|
||||
if err != nil {
|
||||
return dot, fmt.Errorf("environment variable DNS_UPDATE_PERIOD: %w", err)
|
||||
}
|
||||
|
||||
dot.Unbound, err = readUnbound()
|
||||
if err != nil {
|
||||
return dot, err
|
||||
}
|
||||
|
||||
dot.Blacklist, err = r.readDNSBlacklist()
|
||||
if err != nil {
|
||||
return dot, err
|
||||
}
|
||||
|
||||
return dot, nil
|
||||
}
|
||||
96
internal/configuration/sources/env/firewall.go
vendored
Normal file
96
internal/configuration/sources/env/firewall.go
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func (r *Reader) readFirewall() (firewall settings.Firewall, err error) {
|
||||
vpnInputPortStrings := envToCSV("FIREWALL_VPN_INPUT_PORTS")
|
||||
firewall.VPNInputPorts, err = stringsToPorts(vpnInputPortStrings)
|
||||
if err != nil {
|
||||
return firewall, fmt.Errorf("environment variable FIREWALL_VPN_INPUT_PORTS: %w", err)
|
||||
}
|
||||
|
||||
inputPortStrings := envToCSV("FIREWALL_INPUT_PORTS")
|
||||
firewall.InputPorts, err = stringsToPorts(inputPortStrings)
|
||||
if err != nil {
|
||||
return firewall, fmt.Errorf("environment variable FIREWALL_INPUT_PORTS: %w", err)
|
||||
}
|
||||
|
||||
outboundSubnetsKey := "FIREWALL_OUTBOUND_SUBNETS"
|
||||
outboundSubnetStrings := envToCSV(outboundSubnetsKey)
|
||||
if len(outboundSubnetStrings) == 0 {
|
||||
// Retro-compatibility
|
||||
outboundSubnetStrings = envToCSV("EXTRA_SUBNETS")
|
||||
if len(outboundSubnetStrings) > 0 {
|
||||
outboundSubnetsKey = "EXTRA_SUBNETS"
|
||||
r.onRetroActive("EXTRA_SUBNETS", "FIREWALL_OUTBOUND_SUBNETS")
|
||||
}
|
||||
}
|
||||
firewall.OutboundSubnets, err = stringsToIPNets(outboundSubnetStrings)
|
||||
if err != nil {
|
||||
return firewall, fmt.Errorf("environment variable %s: %w", outboundSubnetsKey, err)
|
||||
}
|
||||
|
||||
firewall.Enabled, err = envToBoolPtr("FIREWALL")
|
||||
if err != nil {
|
||||
return firewall, fmt.Errorf("environment variable FIREWALL: %w", err)
|
||||
}
|
||||
|
||||
firewall.Debug, err = envToBoolPtr("FIREWALL_DEBUG")
|
||||
if err != nil {
|
||||
return firewall, fmt.Errorf("environment variable FIREWALL_DEBUG: %w", err)
|
||||
}
|
||||
|
||||
return firewall, nil
|
||||
}
|
||||
|
||||
var (
|
||||
ErrPortParsing = errors.New("cannot parse port")
|
||||
ErrPortValue = errors.New("port value is not valid")
|
||||
)
|
||||
|
||||
func stringsToPorts(ss []string) (ports []uint16, err error) {
|
||||
if len(ss) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
ports = make([]uint16, len(ss))
|
||||
for i, s := range ss {
|
||||
port, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %s: %s",
|
||||
ErrPortParsing, s, err)
|
||||
} else if port < 1 || port > 2^16 {
|
||||
return nil, fmt.Errorf("%w: must be between 1 and 65535: %d",
|
||||
ErrPortValue, port)
|
||||
}
|
||||
ports[i] = uint16(port)
|
||||
}
|
||||
return ports, nil
|
||||
}
|
||||
|
||||
var (
|
||||
ErrIPNetParsing = errors.New("cannot parse IP network")
|
||||
)
|
||||
|
||||
func stringsToIPNets(ss []string) (ipNets []net.IPNet, err error) {
|
||||
if len(ss) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
ipNets = make([]net.IPNet, len(ss))
|
||||
for i, s := range ss {
|
||||
ip, ipNet, err := net.ParseCIDR(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %s: %s",
|
||||
ErrIPNetParsing, s, err)
|
||||
}
|
||||
ipNet.IP = ip
|
||||
ipNets[i] = *ipNet
|
||||
}
|
||||
return ipNets, nil
|
||||
}
|
||||
52
internal/configuration/sources/env/health.go
vendored
Normal file
52
internal/configuration/sources/env/health.go
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func (r *Reader) ReadHealth() (health settings.Health, err error) {
|
||||
health.ServerAddress = os.Getenv("HEALTH_SERVER_ADDRESS")
|
||||
health.AddressToPing = os.Getenv("HEALTH_ADDRESS_TO_PING")
|
||||
|
||||
health.VPN.Initial, err = r.readDurationWithRetro(
|
||||
"HEALTH_VPN_DURATION_INITIAL",
|
||||
"HEALTH_OPENVPN_DURATION_INITIAL")
|
||||
if err != nil {
|
||||
return health, err
|
||||
}
|
||||
|
||||
health.VPN.Initial, err = r.readDurationWithRetro(
|
||||
"HEALTH_VPN_DURATION_ADDITION",
|
||||
"HEALTH_OPENVPN_DURATION_ADDITION")
|
||||
if err != nil {
|
||||
return health, err
|
||||
}
|
||||
|
||||
return health, nil
|
||||
}
|
||||
|
||||
func (r *Reader) readDurationWithRetro(envKey, retroEnvKey string) (d *time.Duration, err error) {
|
||||
s := os.Getenv(envKey)
|
||||
if s == "" {
|
||||
s = os.Getenv(retroEnvKey)
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
r.onRetroActive(envKey, retroEnvKey)
|
||||
envKey = retroEnvKey
|
||||
}
|
||||
|
||||
d = new(time.Duration)
|
||||
*d, err = time.ParseDuration(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"environment variable %s: %w",
|
||||
envKey, err)
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
134
internal/configuration/sources/env/helpers.go
vendored
Normal file
134
internal/configuration/sources/env/helpers.go
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/govalid/binary"
|
||||
"github.com/qdm12/govalid/integer"
|
||||
)
|
||||
|
||||
func envToCSV(envKey string) (values []string) {
|
||||
csv := os.Getenv(envKey)
|
||||
if csv == "" {
|
||||
return nil
|
||||
}
|
||||
return lowerAndSplit(csv)
|
||||
}
|
||||
|
||||
func envToStringPtr(envKey string) (stringPtr *string) {
|
||||
s := os.Getenv(envKey)
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
return &s
|
||||
}
|
||||
|
||||
func envToBoolPtr(envKey string) (boolPtr *bool, err error) {
|
||||
s := os.Getenv(envKey)
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
value, err := binary.Validate(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &value, nil
|
||||
}
|
||||
|
||||
func envToIntPtr(envKey string) (intPtr *int, err error) {
|
||||
s := os.Getenv(envKey)
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
value, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &value, nil
|
||||
}
|
||||
|
||||
func envToUint8Ptr(envKey string) (uint8Ptr *uint8, err error) {
|
||||
s := os.Getenv(envKey)
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
const min, max = 0, 255
|
||||
value, err := integer.Validate(s, integer.OptionRange(min, max))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uint8Ptr = new(uint8)
|
||||
*uint8Ptr = uint8(value)
|
||||
return uint8Ptr, nil
|
||||
}
|
||||
|
||||
func envToUint16Ptr(envKey string) (uint16Ptr *uint16, err error) {
|
||||
s := os.Getenv(envKey)
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
const min, max = 0, 65535
|
||||
value, err := integer.Validate(s, integer.OptionRange(min, max))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uint16Ptr = new(uint16)
|
||||
*uint16Ptr = uint16(value)
|
||||
return uint16Ptr, nil
|
||||
}
|
||||
|
||||
func envToDurationPtr(envKey string) (durationPtr *time.Duration, err error) {
|
||||
s := os.Getenv(envKey)
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
durationPtr = new(time.Duration)
|
||||
*durationPtr, err = time.ParseDuration(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return durationPtr, nil
|
||||
}
|
||||
|
||||
func lowerAndSplit(csv string) (values []string) {
|
||||
csv = strings.ToLower(csv)
|
||||
return strings.Split(csv, ",")
|
||||
}
|
||||
|
||||
var ErrDecodeBase64 = errors.New("cannot decode base64 string")
|
||||
|
||||
func decodeBase64(b64String string) (decoded string, err error) {
|
||||
b, err := base64.StdEncoding.DecodeString(b64String)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%w: %s: %s",
|
||||
ErrDecodeBase64, b64String, err)
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
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("cannot unset environment variable %s: %w", envKey, unsetErr)
|
||||
}
|
||||
}
|
||||
return newErr
|
||||
}
|
||||
|
||||
func stringPtr(s string) *string { return &s }
|
||||
func uint16Ptr(n uint16) *uint16 { return &n }
|
||||
func boolPtr(b bool) *bool { return &b }
|
||||
186
internal/configuration/sources/env/httproxy.go
vendored
Normal file
186
internal/configuration/sources/env/httproxy.go
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/govalid/binary"
|
||||
)
|
||||
|
||||
func (r *Reader) readHTTPProxy() (httpProxy settings.HTTPProxy, err error) {
|
||||
httpProxy.User = r.readHTTProxyUser()
|
||||
httpProxy.Password = r.readHTTProxyPassword()
|
||||
httpProxy.ListeningAddress = r.readHTTProxyListeningAddress()
|
||||
|
||||
httpProxy.Enabled, err = r.readHTTProxyEnabled()
|
||||
if err != nil {
|
||||
return httpProxy, err
|
||||
}
|
||||
|
||||
httpProxy.Stealth, err = envToBoolPtr("HTTPPROXY_STEALTH")
|
||||
if err != nil {
|
||||
return httpProxy, fmt.Errorf("environment variable HTTPPROXY_STEALTH: %w", err)
|
||||
}
|
||||
|
||||
httpProxy.Log, err = r.readHTTProxyLog()
|
||||
if err != nil {
|
||||
return httpProxy, err
|
||||
}
|
||||
|
||||
return httpProxy, nil
|
||||
}
|
||||
|
||||
func (r *Reader) readHTTProxyUser() (user *string) {
|
||||
s := os.Getenv("HTTPPROXY_USER")
|
||||
if s != "" {
|
||||
return &s
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
s = os.Getenv("TINYPROXY_USER")
|
||||
if s != "" {
|
||||
r.onRetroActive("TINYPROXY_USER", "HTTPPROXY_USER")
|
||||
return &s
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
s = os.Getenv("PROXY_USER")
|
||||
if s != "" {
|
||||
r.onRetroActive("PROXY_USER", "HTTPPROXY_USER")
|
||||
return &s
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reader) readHTTProxyPassword() (user *string) {
|
||||
s := os.Getenv("HTTPPROXY_PASSWORD")
|
||||
if s != "" {
|
||||
return &s
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
s = os.Getenv("TINYPROXY_PASSWORD")
|
||||
if s != "" {
|
||||
r.onRetroActive("TINYPROXY_PASSWORD", "HTTPPROXY_PASSWORD")
|
||||
return &s
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
s = os.Getenv("PROXY_PASSWORD")
|
||||
if s != "" {
|
||||
r.onRetroActive("PROXY_PASSWORD", "HTTPPROXY_PASSWORD")
|
||||
return &s
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reader) readHTTProxyListeningAddress() (listeningAddress string) {
|
||||
s := os.Getenv("HTTPPROXY_LISTENING_ADDRESS")
|
||||
if s != "" {
|
||||
return s
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
s = os.Getenv("HTTPPROXY_PORT")
|
||||
if s != "" {
|
||||
r.onRetroActive("HTTPPROXY_PORT", "HTTPPROXY_LISTENING_ADDRESS")
|
||||
return ":" + s
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
s = os.Getenv("TINYPROXY_PORT")
|
||||
if s != "" {
|
||||
r.onRetroActive("TINYPROXY_PORT", "HTTPPROXY_LISTENING_ADDRESS")
|
||||
return ":" + s
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
s = os.Getenv("PROXY_PORT")
|
||||
if s != "" {
|
||||
r.onRetroActive("PROXY_PORT", "HTTPPROXY_LISTENING_ADDRESS")
|
||||
return ":" + s
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *Reader) readHTTProxyEnabled() (enabled *bool, err error) {
|
||||
s := strings.ToLower(os.Getenv("HTTPPROXY"))
|
||||
if s != "" {
|
||||
enabled = new(bool)
|
||||
*enabled, err = binary.Validate(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("environment variable HTTPPROXY: %w", err)
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
s = strings.ToLower(os.Getenv("TINYPROXY"))
|
||||
if s != "" {
|
||||
r.onRetroActive("TINYPROXY", "HTTPPROXY")
|
||||
enabled = new(bool)
|
||||
*enabled, err = binary.Validate(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("environment variable TINYPROXY: %w", err)
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
s = strings.ToLower(os.Getenv("PROXY"))
|
||||
if s != "" {
|
||||
r.onRetroActive("PROXY", "HTTPPROXY")
|
||||
enabled = new(bool)
|
||||
*enabled, err = binary.Validate(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("environment variable PROXY: %w", err)
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
func (r *Reader) readHTTProxyLog() (enabled *bool, err error) {
|
||||
s := strings.ToLower(os.Getenv("HTTPPROXY_LOG"))
|
||||
if s != "" {
|
||||
enabled = new(bool)
|
||||
*enabled, err = binary.Validate(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("environment variable HTTPPROXY_LOG: %w", err)
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
retroOption := binary.OptionEnabled("on", "info", "connect", "notice")
|
||||
s = strings.ToLower(os.Getenv("TINYPROXY_LOG"))
|
||||
if s != "" {
|
||||
r.onRetroActive("TINYPROXY_LOG", "HTTPPROXY_LOG")
|
||||
enabled = new(bool)
|
||||
*enabled, err = binary.Validate(s, retroOption)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("environment variable TINYPROXY_LOG: %w", err)
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
s = strings.ToLower(os.Getenv("PROXY_LOG_LEVEL"))
|
||||
if s != "" {
|
||||
r.onRetroActive("PROXY_LOG_LEVEL", "HTTPPROXY_LOG")
|
||||
enabled = new(bool)
|
||||
*enabled, err = binary.Validate(s, retroOption)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("environment variable PROXY_LOG_LEVEL: %w", err)
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
54
internal/configuration/sources/env/log.go
vendored
Normal file
54
internal/configuration/sources/env/log.go
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
)
|
||||
|
||||
func readLog() (log settings.Log, err error) {
|
||||
log.Level, err = readLogLevel()
|
||||
if err != nil {
|
||||
return log, err
|
||||
}
|
||||
|
||||
return log, nil
|
||||
}
|
||||
|
||||
func readLogLevel() (level *logging.Level, err error) {
|
||||
s := os.Getenv("LOG_LEVEL")
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
level = new(logging.Level)
|
||||
*level, err = parseLogLevel(s)
|
||||
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 logging.Level, err error) {
|
||||
switch strings.ToLower(s) {
|
||||
case "debug":
|
||||
return logging.LevelDebug, nil
|
||||
case "info":
|
||||
return logging.LevelInfo, nil
|
||||
case "warning":
|
||||
return logging.LevelWarn, nil
|
||||
case "error":
|
||||
return logging.LevelError, nil
|
||||
default:
|
||||
return level, fmt.Errorf(
|
||||
"%w: %s: can be one of: debug, info, warning or error",
|
||||
ErrLogLevelUnknown, s)
|
||||
}
|
||||
}
|
||||
125
internal/configuration/sources/env/openvpn.go
vendored
Normal file
125
internal/configuration/sources/env/openvpn.go
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func (r *Reader) readOpenVPN() (
|
||||
openVPN settings.OpenVPN, err error) {
|
||||
defer func() {
|
||||
err = unsetEnvKeys([]string{"OPENVPN_CLIENTKEY", "OPENVPN_CLIENTCRT"}, err)
|
||||
}()
|
||||
|
||||
openVPN.Version = os.Getenv("OPENVPN_VERSION")
|
||||
openVPN.User = r.readOpenVPNUser()
|
||||
openVPN.Password = r.readOpenVPNPassword()
|
||||
confFile := os.Getenv("OPENVPN_CUSTOM_CONFIG")
|
||||
if confFile != "" {
|
||||
openVPN.ConfFile = &confFile
|
||||
}
|
||||
|
||||
openVPN.Ciphers = envToCSV("OPENVPN_CIPHER")
|
||||
auth := os.Getenv("OPENVPN_AUTH")
|
||||
if auth != "" {
|
||||
openVPN.Auth = &auth
|
||||
}
|
||||
|
||||
openVPN.ClientCrt, err = readBase64OrNil("OPENVPN_CLIENTCRT")
|
||||
if err != nil {
|
||||
return openVPN, fmt.Errorf("environment variable OPENVPN_CLIENTCRT: %w", err)
|
||||
}
|
||||
|
||||
openVPN.ClientKey, err = readBase64OrNil("OPENVPN_CLIENTKEY")
|
||||
if err != nil {
|
||||
return openVPN, fmt.Errorf("environment variable OPENVPN_CLIENTKEY: %w", err)
|
||||
}
|
||||
|
||||
openVPN.PIAEncPreset = r.readPIAEncryptionPreset()
|
||||
|
||||
openVPN.IPv6, err = envToBoolPtr("OPENVPN_IPV6")
|
||||
if err != nil {
|
||||
return openVPN, fmt.Errorf("environment variable OPENVPN_IPV6: %w", err)
|
||||
}
|
||||
|
||||
openVPN.MSSFix, err = envToUint16Ptr("OPENVPN_MSSFIX")
|
||||
if err != nil {
|
||||
return openVPN, fmt.Errorf("environment variable OPENVPN_MSSFIX: %w", err)
|
||||
}
|
||||
|
||||
openVPN.Interface = os.Getenv("OPENVPN_INTERFACE")
|
||||
|
||||
openVPN.Root, err = envToBoolPtr("OPENVPN_ROOT")
|
||||
if err != nil {
|
||||
return openVPN, fmt.Errorf("environment variable OPENVPN_ROOT: %w", err)
|
||||
}
|
||||
|
||||
// TODO ProcUser once Root is deprecated.
|
||||
|
||||
openVPN.Verbosity, err = envToIntPtr("OPENVPN_VERBOSITY")
|
||||
if err != nil {
|
||||
return openVPN, fmt.Errorf("environment variable OPENVPN_VERBOSITY: %w", err)
|
||||
}
|
||||
|
||||
return openVPN, nil
|
||||
}
|
||||
|
||||
func (r *Reader) readOpenVPNUser() (user string) {
|
||||
user = os.Getenv("OPENVPN_USER")
|
||||
if user == "" {
|
||||
// Retro-compatibility
|
||||
user = os.Getenv("USER")
|
||||
if user != "" {
|
||||
r.onRetroActive("USER", "OPENVPN_USER")
|
||||
}
|
||||
}
|
||||
// Remove spaces in user ID to simplify user's life, thanks @JeordyR
|
||||
return strings.ReplaceAll(user, " ", "")
|
||||
}
|
||||
|
||||
func (r *Reader) readOpenVPNPassword() (password string) {
|
||||
password = os.Getenv("OPENVPN_PASSWORD")
|
||||
if password != "" {
|
||||
return password
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
password = os.Getenv("PASSWORD")
|
||||
if password != "" {
|
||||
r.onRetroActive("PASSWORD", "OPENVPN_PASSWORD")
|
||||
}
|
||||
return password
|
||||
}
|
||||
|
||||
func readBase64OrNil(envKey string) (valueOrNil *string, err error) {
|
||||
value := os.Getenv(envKey)
|
||||
if value == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
decoded, err := decodeBase64(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &decoded, nil
|
||||
}
|
||||
|
||||
func (r *Reader) readPIAEncryptionPreset() (presetPtr *string) {
|
||||
preset := strings.ToLower(os.Getenv("PIA_ENCRYPTION"))
|
||||
if preset != "" {
|
||||
return &preset
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
preset = strings.ToLower(os.Getenv("ENCRYPTION"))
|
||||
if preset != "" {
|
||||
r.onRetroActive("ENCRYPTION", "PIA_ENCRYPTION")
|
||||
return &preset
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
78
internal/configuration/sources/env/openvpnselection.go
vendored
Normal file
78
internal/configuration/sources/env/openvpnselection.go
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/govalid/port"
|
||||
)
|
||||
|
||||
func (r *Reader) readOpenVPNSelection() (
|
||||
selection settings.OpenVPNSelection, err error) {
|
||||
selection.TCP, err = r.readOpenVPNProtocol()
|
||||
if err != nil {
|
||||
return selection, err
|
||||
}
|
||||
|
||||
selection.CustomPort, err = r.readOpenVPNCustomPort()
|
||||
if err != nil {
|
||||
return selection, err
|
||||
}
|
||||
|
||||
selection.PIAEncPreset = r.readPIAEncryptionPreset()
|
||||
|
||||
return selection, nil
|
||||
}
|
||||
|
||||
var ErrOpenVPNProtocolNotValid = errors.New("OpenVPN protocol is not valid")
|
||||
|
||||
func (r *Reader) readOpenVPNProtocol() (tcp *bool, err error) {
|
||||
envKey := "OPENVPN_PROTOCOL"
|
||||
protocol := strings.ToLower(os.Getenv("OPENVPN_PROTOCOL"))
|
||||
if protocol == "" {
|
||||
// Retro-compatibility
|
||||
protocol = strings.ToLower(os.Getenv("PROTOCOL"))
|
||||
if protocol != "" {
|
||||
envKey = "PROTOCOL"
|
||||
r.onRetroActive("PROTOCOL", "OPENVPN_PROTOCOL")
|
||||
}
|
||||
}
|
||||
|
||||
switch protocol {
|
||||
case "":
|
||||
return nil, nil //nolint:nilnil
|
||||
case constants.UDP:
|
||||
return boolPtr(false), nil
|
||||
case constants.TCP:
|
||||
return boolPtr(true), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("environment variable %s: %w: %s",
|
||||
envKey, ErrOpenVPNProtocolNotValid, protocol)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reader) readOpenVPNCustomPort() (customPort *uint16, err error) {
|
||||
key := "OPENVPN_PORT"
|
||||
s := os.Getenv(key)
|
||||
if s == "" {
|
||||
// Retro-compatibility
|
||||
key = "PORT"
|
||||
s = os.Getenv(key)
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
r.onRetroActive("PORT", "OPENVPN_PORT")
|
||||
}
|
||||
|
||||
customPort = new(uint16)
|
||||
*customPort, err = port.Validate(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("environment variable %s: %w", key, err)
|
||||
}
|
||||
|
||||
return customPort, nil
|
||||
}
|
||||
19
internal/configuration/sources/env/portforward.go
vendored
Normal file
19
internal/configuration/sources/env/portforward.go
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func readPortForward() (
|
||||
portForwarding settings.PortForwarding, err error) {
|
||||
portForwarding.Enabled, err = envToBoolPtr("PORT_FORWARDING")
|
||||
if err != nil {
|
||||
return portForwarding, fmt.Errorf("environment variable PORT_FORWARDING: %w", err)
|
||||
}
|
||||
|
||||
portForwarding.Filepath = envToStringPtr("PORT_FORWARDING_STATUS_FILE")
|
||||
|
||||
return portForwarding, nil
|
||||
}
|
||||
40
internal/configuration/sources/env/provider.go
vendored
Normal file
40
internal/configuration/sources/env/provider.go
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
func (r *Reader) readProvider(vpnType string) (provider settings.Provider, err error) {
|
||||
provider.Name = readVPNServiceProvider(vpnType)
|
||||
|
||||
provider.ServerSelection, err = r.readServerSelection(*provider.Name, vpnType)
|
||||
if err != nil {
|
||||
return provider, fmt.Errorf("cannot read server selection settings: %w", err)
|
||||
}
|
||||
|
||||
provider.PortForwarding, err = readPortForward()
|
||||
if err != nil {
|
||||
return provider, fmt.Errorf("cannot read port forwarding settings: %w", err)
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
func readVPNServiceProvider(vpnType string) (vpnProviderPtr *string) {
|
||||
s := strings.ToLower(os.Getenv("VPNSP"))
|
||||
switch {
|
||||
case vpnType == constants.OpenVPN &&
|
||||
os.Getenv("OPENVPN_CUSTOM_CONFIG") != "": // retro compatibility
|
||||
return stringPtr(constants.Custom)
|
||||
case s == "":
|
||||
return nil
|
||||
case s == "pia": // retro compatibility
|
||||
return stringPtr(constants.PrivateInternetAccess)
|
||||
}
|
||||
return stringPtr(s)
|
||||
}
|
||||
51
internal/configuration/sources/env/publicip.go
vendored
Normal file
51
internal/configuration/sources/env/publicip.go
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func (r *Reader) readPublicIP() (publicIP settings.PublicIP, err error) {
|
||||
publicIP.Period, err = readPublicIPPeriod()
|
||||
if err != nil {
|
||||
return publicIP, err
|
||||
}
|
||||
|
||||
publicIP.IPFilepath = r.readPublicIPFilepath()
|
||||
|
||||
return publicIP, nil
|
||||
}
|
||||
|
||||
func readPublicIPPeriod() (period *time.Duration, err error) {
|
||||
s := os.Getenv("PUBLICIP_PERIOD")
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
period = new(time.Duration)
|
||||
*period, err = time.ParseDuration(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("environment variable PUBLICIP_PERIOD: %w", err)
|
||||
}
|
||||
|
||||
return period, nil
|
||||
}
|
||||
|
||||
func (r *Reader) readPublicIPFilepath() (filepath *string) {
|
||||
s := os.Getenv("PUBLICIP_FILE")
|
||||
if s != "" {
|
||||
return &s
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
s = os.Getenv("IP_STATUS_FILE")
|
||||
if s != "" {
|
||||
r.onRetroActive("IP_STATUS_FILE", "PUBLICIP_FILE")
|
||||
return &s
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
87
internal/configuration/sources/env/reader.go
vendored
Normal file
87
internal/configuration/sources/env/reader.go
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/configuration/sources"
|
||||
)
|
||||
|
||||
var _ sources.Source = (*Reader)(nil)
|
||||
|
||||
type Reader struct {
|
||||
warner Warner
|
||||
}
|
||||
|
||||
type Warner interface {
|
||||
Warn(s string)
|
||||
}
|
||||
|
||||
func New(warner Warner) *Reader {
|
||||
return &Reader{
|
||||
warner: warner,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reader) Read() (settings settings.Settings, err error) {
|
||||
settings.VPN, err = r.readVPN()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
settings.Firewall, err = r.readFirewall()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
settings.System, err = r.readSystem()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
settings.Health, err = r.ReadHealth()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
settings.HTTPProxy, err = r.readHTTPProxy()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
settings.Log, err = readLog()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
settings.PublicIP, err = r.readPublicIP()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
settings.Updater, err = readUpdater()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
settings.Version, err = readVersion()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
settings.Shadowsocks, err = r.readShadowsocks()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
settings.DNS, err = r.readDNS()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
func (r *Reader) onRetroActive(oldKey, newKey string) {
|
||||
r.warner.Warn(
|
||||
"You are using the old environment variable " + oldKey +
|
||||
", please consider changing it to " + newKey)
|
||||
}
|
||||
115
internal/configuration/sources/env/serverselection.go
vendored
Normal file
115
internal/configuration/sources/env/serverselection.go
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrServerNumberNotValid = errors.New("server number is not valid")
|
||||
)
|
||||
|
||||
func (r *Reader) readServerSelection(vpnProvider, vpnType string) (
|
||||
ss settings.ServerSelection, err error) {
|
||||
ss.VPN = vpnType
|
||||
|
||||
ss.TargetIP, err = readOpenVPNTargetIP()
|
||||
if err != nil {
|
||||
return ss, err
|
||||
}
|
||||
|
||||
countriesCSV := os.Getenv("COUNTRY")
|
||||
if vpnProvider == constants.Cyberghost && countriesCSV == "" {
|
||||
// Retro-compatibility
|
||||
r.onRetroActive("REGION", "COUNTRY")
|
||||
countriesCSV = os.Getenv("REGION")
|
||||
}
|
||||
if countriesCSV != "" {
|
||||
ss.Countries = lowerAndSplit(countriesCSV)
|
||||
}
|
||||
|
||||
ss.Regions = envToCSV("REGION")
|
||||
ss.Cities = envToCSV("CITY")
|
||||
ss.ISPs = envToCSV("ISP")
|
||||
ss.Hostnames = envToCSV("SERVER_HOSTNAME")
|
||||
ss.Names = envToCSV("SERVER_NAME")
|
||||
|
||||
if csv := os.Getenv("SERVER_NUMBER"); csv != "" {
|
||||
numbersStrings := strings.Split(csv, ",")
|
||||
numbers := make([]uint16, len(numbersStrings))
|
||||
for i, numberString := range numbersStrings {
|
||||
number, err := strconv.Atoi(numberString)
|
||||
if err != nil {
|
||||
return ss, fmt.Errorf("%w: %s",
|
||||
ErrServerNumberNotValid, numberString)
|
||||
} else if number < 0 || number > 2^16 {
|
||||
return ss, fmt.Errorf("%w: %d must be between 0 and 2^16",
|
||||
ErrServerNumberNotValid, number)
|
||||
}
|
||||
numbers[i] = uint16(number)
|
||||
}
|
||||
ss.Numbers = numbers
|
||||
}
|
||||
|
||||
// Mullvad only
|
||||
ss.OwnedOnly, err = envToBoolPtr("OWNED")
|
||||
if err != nil {
|
||||
return ss, fmt.Errorf("environment variable OWNED: %w", err)
|
||||
}
|
||||
|
||||
// VPNUnlimited only
|
||||
ss.FreeOnly, err = envToBoolPtr("FREE_ONLY")
|
||||
if err != nil {
|
||||
return ss, fmt.Errorf("environment variable FREE_ONLY: %w", err)
|
||||
}
|
||||
|
||||
// VPNUnlimited only
|
||||
ss.MultiHopOnly, err = envToBoolPtr("MULTIHOP_ONLY")
|
||||
if err != nil {
|
||||
return ss, fmt.Errorf("environment variable MULTIHOP_ONLY: %w", err)
|
||||
}
|
||||
|
||||
// VPNUnlimited only
|
||||
ss.MultiHopOnly, err = envToBoolPtr("STREAM_ONLY")
|
||||
if err != nil {
|
||||
return ss, fmt.Errorf("environment variable STREAM_ONLY: %w", err)
|
||||
}
|
||||
|
||||
ss.OpenVPN, err = r.readOpenVPNSelection()
|
||||
if err != nil {
|
||||
return ss, err
|
||||
}
|
||||
|
||||
ss.Wireguard, err = r.readWireguardSelection()
|
||||
if err != nil {
|
||||
return ss, err
|
||||
}
|
||||
|
||||
return ss, nil
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidIP = errors.New("invalid IP address")
|
||||
)
|
||||
|
||||
func readOpenVPNTargetIP() (ip net.IP, err error) {
|
||||
s := os.Getenv("OPENVPN_TARGET_IP")
|
||||
if s == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
ip = net.ParseIP(s)
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("environment variable OPENVPN_TARGET_IP: %w: %s",
|
||||
ErrInvalidIP, s)
|
||||
}
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
54
internal/configuration/sources/env/shadowsocks.go
vendored
Normal file
54
internal/configuration/sources/env/shadowsocks.go
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func (r *Reader) readShadowsocks() (shadowsocks settings.Shadowsocks, err error) {
|
||||
shadowsocks.Enabled, err = envToBoolPtr("SHADOWSOCKS")
|
||||
if err != nil {
|
||||
return shadowsocks, fmt.Errorf("environment variable SHADOWSOCKS: %w", err)
|
||||
}
|
||||
|
||||
shadowsocks.Address = r.readShadowsocksAddress()
|
||||
shadowsocks.LogAddresses, err = envToBoolPtr("SHADOWSOCKS_LOG")
|
||||
if err != nil {
|
||||
return shadowsocks, fmt.Errorf("environment variable SHADOWSOCKS_LOG: %w", err)
|
||||
}
|
||||
shadowsocks.CipherName = r.readShadowsocksCipher()
|
||||
shadowsocks.Password = envToStringPtr("SHADOWSOCKS_PASSWORD")
|
||||
|
||||
return shadowsocks, nil
|
||||
}
|
||||
|
||||
func (r *Reader) readShadowsocksAddress() (address string) {
|
||||
address = os.Getenv("SHADOWSOCKS_LISTENING_ADDRESS")
|
||||
if address != "" {
|
||||
return address
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
portString := os.Getenv("SHADOWSOCKS_PORT")
|
||||
if portString != "" {
|
||||
r.onRetroActive("SHADOWSOCKS_PORT", "SHADOWSOCKS_LISTENING_ADDRESS")
|
||||
return ":" + portString
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *Reader) readShadowsocksCipher() (cipher string) {
|
||||
cipher = os.Getenv("SHADOWSOCKS_CIPHER")
|
||||
if cipher != "" {
|
||||
return cipher
|
||||
}
|
||||
// Retro-compatibility
|
||||
cipher = os.Getenv("SHADOWSOCKS_METHOD")
|
||||
if cipher != "" {
|
||||
r.onRetroActive("SHADOWSOCKS_METHOD", "SHADOWSOCKS_CIPHER")
|
||||
}
|
||||
return cipher
|
||||
}
|
||||
63
internal/configuration/sources/env/system.go
vendored
Normal file
63
internal/configuration/sources/env/system.go
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrSystemPUIDNotValid = errors.New("PUID is not valid")
|
||||
ErrSystemPGIDNotValid = errors.New("PGID is not valid")
|
||||
ErrSystemTimezoneNotValid = errors.New("timezone is not valid")
|
||||
)
|
||||
|
||||
func (r *Reader) readSystem() (system settings.System, err error) {
|
||||
system.PUID, err = r.readID("PUID", "UID")
|
||||
if err != nil {
|
||||
return system, err
|
||||
}
|
||||
|
||||
system.PGID, err = r.readID("PGID", "GID")
|
||||
if err != nil {
|
||||
return system, err
|
||||
}
|
||||
|
||||
system.Timezone = os.Getenv("TZ")
|
||||
|
||||
return system, nil
|
||||
}
|
||||
|
||||
var ErrSystemIDNotValid = errors.New("system ID is not valid")
|
||||
|
||||
func (r *Reader) readID(key, retroKey string) (
|
||||
id *uint16, err error) {
|
||||
idEnvKey := key
|
||||
idString := os.Getenv(key)
|
||||
if idString == "" {
|
||||
// retro-compatibility
|
||||
idString = os.Getenv(retroKey)
|
||||
if idString != "" {
|
||||
idEnvKey = retroKey
|
||||
r.onRetroActive(retroKey, key)
|
||||
}
|
||||
}
|
||||
|
||||
if idString == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
idInt, err := strconv.Atoi(idString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("environment variable %s: %w: %s: %s",
|
||||
idEnvKey, ErrSystemIDNotValid, idString, err)
|
||||
} else if idInt < 0 || idInt > 2^16 {
|
||||
return nil, fmt.Errorf("environment variable %s: %w: %d: must be between 0 and 65535",
|
||||
idEnvKey, ErrSystemIDNotValid, idInt)
|
||||
}
|
||||
|
||||
return uint16Ptr(uint16(idInt)), nil
|
||||
}
|
||||
38
internal/configuration/sources/env/unbound.go
vendored
Normal file
38
internal/configuration/sources/env/unbound.go
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func readUnbound() (unbound settings.Unbound, err error) {
|
||||
unbound.Providers = envToCSV("DOT_PROVIDERS")
|
||||
|
||||
unbound.Caching, err = envToBoolPtr("DOT_CACHING")
|
||||
if err != nil {
|
||||
return unbound, fmt.Errorf("environment variable DOT_CACHING: %w", err)
|
||||
}
|
||||
|
||||
unbound.IPv6, err = envToBoolPtr("DOT_IPV6")
|
||||
if err != nil {
|
||||
return unbound, fmt.Errorf("environment variable DOT_IPV6: %w", err)
|
||||
}
|
||||
|
||||
unbound.VerbosityLevel, err = envToUint8Ptr("DOT_VERBOSITY")
|
||||
if err != nil {
|
||||
return unbound, fmt.Errorf("environment variable DOT_VERBOSITY: %w", err)
|
||||
}
|
||||
|
||||
unbound.VerbosityDetailsLevel, err = envToUint8Ptr("DOT_VERBOSITY_DETAILS")
|
||||
if err != nil {
|
||||
return unbound, fmt.Errorf("environment variable DOT_VERBOSITY_DETAILS: %w", err)
|
||||
}
|
||||
|
||||
unbound.ValidationLogLevel, err = envToUint8Ptr("DOT_VALIDATION_LOGLEVEL")
|
||||
if err != nil {
|
||||
return unbound, fmt.Errorf("environment variable DOT_VALIDATION_LOGLEVEL: %w", err)
|
||||
}
|
||||
|
||||
return unbound, nil
|
||||
}
|
||||
50
internal/configuration/sources/env/updater.go
vendored
Normal file
50
internal/configuration/sources/env/updater.go
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
func readUpdater() (updater settings.Updater, err error) {
|
||||
updater.Period, err = readUpdaterPeriod()
|
||||
if err != nil {
|
||||
return updater, err
|
||||
}
|
||||
|
||||
updater.DNSAddress, err = readUpdaterDNSAddress()
|
||||
if err != nil {
|
||||
return updater, err
|
||||
}
|
||||
|
||||
// TODO use current provider being used
|
||||
updater.Providers = constants.AllProviders()
|
||||
|
||||
return updater, nil
|
||||
}
|
||||
|
||||
func readUpdaterPeriod() (period *time.Duration, err error) {
|
||||
s := os.Getenv("UPDATER_PERIOD")
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
period = new(time.Duration)
|
||||
*period, err = time.ParseDuration(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("environment variable UPDATER_PERIOD: %w", err)
|
||||
}
|
||||
return period, nil
|
||||
}
|
||||
|
||||
func readUpdaterDNSAddress() (ip net.IP, 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, nil
|
||||
}
|
||||
33
internal/configuration/sources/env/version.go
vendored
Normal file
33
internal/configuration/sources/env/version.go
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/govalid/binary"
|
||||
)
|
||||
|
||||
func readVersion() (version settings.Version, err error) {
|
||||
version.Enabled, err = readVersionEnabled()
|
||||
if err != nil {
|
||||
return version, err
|
||||
}
|
||||
|
||||
return version, nil
|
||||
}
|
||||
|
||||
func readVersionEnabled() (enabled *bool, err error) {
|
||||
s := os.Getenv("VERSION_INFORMATION")
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
enabled = new(bool)
|
||||
*enabled, err = binary.Validate(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("environment variable VERSION_INFORMATION: %w", err)
|
||||
}
|
||||
|
||||
return enabled, nil
|
||||
}
|
||||
29
internal/configuration/sources/env/vpn.go
vendored
Normal file
29
internal/configuration/sources/env/vpn.go
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func (r *Reader) readVPN() (vpn settings.VPN, err error) {
|
||||
vpn.Type = os.Getenv("VPN_TYPE")
|
||||
|
||||
vpn.Provider, err = r.readProvider(vpn.Type)
|
||||
if err != nil {
|
||||
return vpn, fmt.Errorf("cannot read provider settings: %w", err)
|
||||
}
|
||||
|
||||
vpn.OpenVPN, err = r.readOpenVPN()
|
||||
if err != nil {
|
||||
return vpn, fmt.Errorf("cannot read OpenVPN settings: %w", err)
|
||||
}
|
||||
|
||||
vpn.Wireguard, err = readWireguard()
|
||||
if err != nil {
|
||||
return vpn, fmt.Errorf("cannot read Wireguard settings: %w", err)
|
||||
}
|
||||
|
||||
return vpn, nil
|
||||
}
|
||||
44
internal/configuration/sources/env/wireguard.go
vendored
Normal file
44
internal/configuration/sources/env/wireguard.go
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func readWireguard() (wireguard settings.Wireguard, err error) {
|
||||
defer func() {
|
||||
err = unsetEnvKeys([]string{"WIREGUARD_PRIVATE_KEY", "WIREGUARD_PRESHARED_KEY"}, err)
|
||||
}()
|
||||
wireguard.PrivateKey = envToStringPtr("WIREGUARD_PRIVATE_KEY")
|
||||
wireguard.PreSharedKey = envToStringPtr("WIREGUARD_PRESHARED_KEY")
|
||||
wireguard.Interface = os.Getenv("WIREGUARD_INTERFACE")
|
||||
wireguard.Addresses, err = readWireguardAddresses()
|
||||
if err != nil {
|
||||
return wireguard, err // already wrapped
|
||||
}
|
||||
return wireguard, nil
|
||||
}
|
||||
|
||||
func readWireguardAddresses() (addresses []net.IPNet, err error) {
|
||||
addressesCSV := os.Getenv("WIREGUARD_ADDRESS")
|
||||
if addressesCSV == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
addressStrings := strings.Split(addressesCSV, ",")
|
||||
addresses = make([]net.IPNet, len(addressStrings))
|
||||
for i, addressString := range addressStrings {
|
||||
ip, ipNet, err := net.ParseCIDR(addressString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("environment variable WIREGUARD_ADDRESS: %w", err)
|
||||
}
|
||||
ipNet.IP = ip
|
||||
addresses[i] = *ipNet
|
||||
}
|
||||
|
||||
return addresses, nil
|
||||
}
|
||||
65
internal/configuration/sources/env/wireguardselection.go
vendored
Normal file
65
internal/configuration/sources/env/wireguardselection.go
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/govalid/port"
|
||||
)
|
||||
|
||||
func (r *Reader) readWireguardSelection() (
|
||||
selection settings.WireguardSelection, err error) {
|
||||
selection.EndpointIP, err = readWireguardEndpointIP()
|
||||
if err != nil {
|
||||
return selection, err
|
||||
}
|
||||
|
||||
selection.EndpointPort, err = r.readWireguardCustomPort()
|
||||
if err != nil {
|
||||
return selection, err
|
||||
}
|
||||
|
||||
selection.PublicKey = os.Getenv("WIREGUARD_PUBLIC_KEY")
|
||||
|
||||
return selection, nil
|
||||
}
|
||||
|
||||
var ErrIPAddressParse = errors.New("cannot parse IP address")
|
||||
|
||||
func readWireguardEndpointIP() (endpointIP net.IP, err error) {
|
||||
s := os.Getenv("WIREGUARD_ENDPOINT_IP")
|
||||
if s == "" {
|
||||
return nil, nil
|
||||
}
|
||||
endpointIP = net.ParseIP(s)
|
||||
if endpointIP == nil {
|
||||
return nil, fmt.Errorf("environment variable WIREGUARD_ENDPOINT_IP: %w: %s",
|
||||
ErrIPAddressParse, s)
|
||||
}
|
||||
return endpointIP, nil
|
||||
}
|
||||
|
||||
func (r *Reader) readWireguardCustomPort() (customPort *uint16, err error) {
|
||||
key := "WIREGUARD_ENDPOINT_PORT"
|
||||
s := os.Getenv(key)
|
||||
if s == "" {
|
||||
// Retro-compatibility
|
||||
key = "WIREGUARD_PORT"
|
||||
s = os.Getenv(key)
|
||||
if s == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
r.onRetroActive("WIREGUARD_PORT", "WIREGUARD_ENDPOINT_PORT")
|
||||
}
|
||||
|
||||
customPort = new(uint16)
|
||||
*customPort, err = port.Validate(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("environment variable %s: %w", key, err)
|
||||
}
|
||||
|
||||
return customPort, nil
|
||||
}
|
||||
5
internal/configuration/sources/files/health.go
Normal file
5
internal/configuration/sources/files/health.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package files
|
||||
|
||||
import "github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
|
||||
func (r *Reader) ReadHealth() (settings settings.Health, err error) { return settings, nil }
|
||||
31
internal/configuration/sources/files/helpers.go
Normal file
31
internal/configuration/sources/files/helpers.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ReadFromFile reads the content of the file as a string.
|
||||
// It returns a nil string pointer if the file does not exist.
|
||||
func ReadFromFile(filepath string) (s *string, err error) {
|
||||
file, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
_ = file.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := file.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
content := string(b)
|
||||
return &content, nil
|
||||
}
|
||||
22
internal/configuration/sources/files/openvpn.go
Normal file
22
internal/configuration/sources/files/openvpn.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
func (r *Reader) readOpenVPN() (settings settings.OpenVPN, err error) {
|
||||
settings.ClientKey, err = ReadFromFile(constants.ClientKey)
|
||||
if err != nil {
|
||||
return settings, fmt.Errorf("cannot read client key: %w", err)
|
||||
}
|
||||
|
||||
settings.ClientCrt, err = ReadFromFile(constants.ClientCertificate)
|
||||
if err != nil {
|
||||
return settings, fmt.Errorf("cannot read client certificate: %w", err)
|
||||
}
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
28
internal/configuration/sources/files/reader.go
Normal file
28
internal/configuration/sources/files/reader.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/configuration/sources"
|
||||
)
|
||||
|
||||
var _ sources.Source = (*Reader)(nil)
|
||||
|
||||
type Reader struct{}
|
||||
|
||||
func New() *Reader {
|
||||
return &Reader{}
|
||||
}
|
||||
|
||||
func (r *Reader) Read() (settings settings.Settings, err error) {
|
||||
settings.VPN, err = r.readVPN()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
settings.System, err = r.readSystem()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
10
internal/configuration/sources/files/system.go
Normal file
10
internal/configuration/sources/files/system.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func (r *Reader) readSystem() (system settings.System, err error) {
|
||||
// TODO timezone from /etc/localtime
|
||||
return system, nil
|
||||
}
|
||||
16
internal/configuration/sources/files/vpn.go
Normal file
16
internal/configuration/sources/files/vpn.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func (r *Reader) readVPN() (vpn settings.VPN, err error) {
|
||||
vpn.OpenVPN, err = r.readOpenVPN()
|
||||
if err != nil {
|
||||
return vpn, fmt.Errorf("cannot read OpenVPN settings: %w", err)
|
||||
}
|
||||
|
||||
return vpn, nil
|
||||
}
|
||||
57
internal/configuration/sources/mux/reader.go
Normal file
57
internal/configuration/sources/mux/reader.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package mux
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/configuration/sources"
|
||||
)
|
||||
|
||||
var _ sources.Source = (*Reader)(nil)
|
||||
|
||||
type Reader struct {
|
||||
sources []sources.Source
|
||||
}
|
||||
|
||||
func New(sources ...sources.Source) *Reader {
|
||||
return &Reader{
|
||||
sources: 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 (r *Reader) Read() (settings settings.Settings, err error) {
|
||||
for _, source := range r.sources {
|
||||
settingsFromSource, err := source.Read()
|
||||
if err != nil {
|
||||
return settings, fmt.Errorf("cannot read from source %T: %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 (r *Reader) ReadHealth() (settings settings.Health, err error) {
|
||||
for _, source := range r.sources {
|
||||
settingsFromSource, err := source.ReadHealth()
|
||||
if err != nil {
|
||||
return settings, fmt.Errorf("cannot read from source %T: %w", source, err)
|
||||
}
|
||||
settings.MergeWith(settingsFromSource)
|
||||
}
|
||||
settings.SetDefaults()
|
||||
|
||||
err = settings.Validate()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
5
internal/configuration/sources/secrets/health.go
Normal file
5
internal/configuration/sources/secrets/health.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package secrets
|
||||
|
||||
import "github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
|
||||
func (r *Reader) ReadHealth() (settings settings.Health, err error) { return settings, nil }
|
||||
31
internal/configuration/sources/secrets/helpers.go
Normal file
31
internal/configuration/sources/secrets/helpers.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package secrets
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/sources/files"
|
||||
)
|
||||
|
||||
func readSecretFileAsStringPtr(secretPathEnvKey, defaultSecretPath string) (
|
||||
stringPtr *string, err error) {
|
||||
path := os.Getenv(secretPathEnvKey)
|
||||
if path == "" {
|
||||
path = defaultSecretPath
|
||||
}
|
||||
return files.ReadFromFile(path)
|
||||
}
|
||||
|
||||
func readSecretFileAsString(secretPathEnvKey, defaultSecretPath string) (
|
||||
s string, err error) {
|
||||
path := os.Getenv(secretPathEnvKey)
|
||||
if path == "" {
|
||||
path = defaultSecretPath
|
||||
}
|
||||
stringPtr, err := files.ReadFromFile(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else if stringPtr == nil {
|
||||
return "", nil
|
||||
}
|
||||
return *stringPtr, nil
|
||||
}
|
||||
27
internal/configuration/sources/secrets/httpproxy.go
Normal file
27
internal/configuration/sources/secrets/httpproxy.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package secrets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func readHTTPProxy() (settings settings.HTTPProxy, err error) {
|
||||
settings.User, err = readSecretFileAsStringPtr(
|
||||
"HTTPPROXY_USER_SECRETFILE",
|
||||
"/run/secrets/httpproxy_user",
|
||||
)
|
||||
if err != nil {
|
||||
return settings, fmt.Errorf("cannot read HTTP proxy user secret file: %w", err)
|
||||
}
|
||||
|
||||
settings.Password, err = readSecretFileAsStringPtr(
|
||||
"HTTPPROXY_PASSWORD_SECRETFILE",
|
||||
"/run/secrets/httpproxy_password",
|
||||
)
|
||||
if err != nil {
|
||||
return settings, fmt.Errorf("cannot read OpenVPN password secret file: %w", err)
|
||||
}
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
44
internal/configuration/sources/secrets/openvpn.go
Normal file
44
internal/configuration/sources/secrets/openvpn.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package secrets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func readOpenVPN() (
|
||||
settings settings.OpenVPN, err error) {
|
||||
settings.User, err = readSecretFileAsString(
|
||||
"OPENVPN_USER_SECRETFILE",
|
||||
"/run/secrets/openvpn_user",
|
||||
)
|
||||
if err != nil {
|
||||
return settings, fmt.Errorf("cannot read user file: %w", err)
|
||||
}
|
||||
|
||||
settings.Password, err = readSecretFileAsString(
|
||||
"OPENVPN_PASSWORD_SECRETFILE",
|
||||
"/run/secrets/openvpn_password",
|
||||
)
|
||||
if err != nil {
|
||||
return settings, fmt.Errorf("cannot read password file: %w", err)
|
||||
}
|
||||
|
||||
settings.ClientKey, err = readSecretFileAsStringPtr(
|
||||
"OPENVPN_CLIENTKEY_SECRETFILE",
|
||||
"/run/secrets/openvpn_clientkey",
|
||||
)
|
||||
if err != nil {
|
||||
return settings, fmt.Errorf("cannot read client key file: %w", err)
|
||||
}
|
||||
|
||||
settings.ClientCrt, err = readSecretFileAsStringPtr(
|
||||
"OPENVPN_CLIENTCRT_SECRETFILE",
|
||||
"/run/secrets/openvpn_clientcrt",
|
||||
)
|
||||
if err != nil {
|
||||
return settings, fmt.Errorf("cannot read client certificate file: %w", err)
|
||||
}
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
34
internal/configuration/sources/secrets/reader.go
Normal file
34
internal/configuration/sources/secrets/reader.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package secrets
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/configuration/sources"
|
||||
)
|
||||
|
||||
var _ sources.Source = (*Reader)(nil)
|
||||
|
||||
type Reader struct {
|
||||
}
|
||||
|
||||
func New() *Reader {
|
||||
return &Reader{}
|
||||
}
|
||||
|
||||
func (r *Reader) Read() (settings settings.Settings, err error) {
|
||||
settings.VPN, err = readVPN()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
settings.HTTPProxy, err = readHTTPProxy()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
settings.Shadowsocks, err = readShadowsocks()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
19
internal/configuration/sources/secrets/shadowsocks.go
Normal file
19
internal/configuration/sources/secrets/shadowsocks.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package secrets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func readShadowsocks() (settings settings.Shadowsocks, err error) {
|
||||
settings.Password, err = readSecretFileAsStringPtr(
|
||||
"SHADOWSOCKS_PASSWORD_SECRETFILE",
|
||||
"/run/secrets/shadowsocks_password",
|
||||
)
|
||||
if err != nil {
|
||||
return settings, fmt.Errorf("cannot read Shadowsocks password secret file: %w", err)
|
||||
}
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
16
internal/configuration/sources/secrets/vpn.go
Normal file
16
internal/configuration/sources/secrets/vpn.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package secrets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
)
|
||||
|
||||
func readVPN() (vpn settings.VPN, err error) {
|
||||
vpn.OpenVPN, err = readOpenVPN()
|
||||
if err != nil {
|
||||
return vpn, fmt.Errorf("cannot read OpenVPN settings: %w", err)
|
||||
}
|
||||
|
||||
return vpn, nil
|
||||
}
|
||||
8
internal/configuration/sources/source.go
Normal file
8
internal/configuration/sources/source.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package sources
|
||||
|
||||
import "github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
|
||||
type Source interface {
|
||||
Read() (settings settings.Settings, err error)
|
||||
ReadHealth() (settings settings.Health, err error)
|
||||
}
|
||||
Reference in New Issue
Block a user