0.3.49
This commit is contained in:
@@ -4,6 +4,7 @@ package config
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -85,6 +86,17 @@ type Config struct {
|
||||
|
||||
UpstreamTimeout time.Duration
|
||||
|
||||
// TrustedProxies are the reverse proxies whose X-Forwarded-For and X-Real-IP
|
||||
// headers the gateway will believe when working out where a request originated.
|
||||
// A forwarded header is only honoured when the immediate peer is one of these, so
|
||||
// a client reaching the gateway directly cannot spoof its address by setting one.
|
||||
//
|
||||
// Empty (MEMBY_TRUSTED_PROXIES unset) trusts loopback and the private/unique-local
|
||||
// ranges — every reverse proxy a home deployment puts in front of Memby sits in one
|
||||
// of those. Set it to a comma-separated list of addresses or CIDR ranges to narrow
|
||||
// or widen that, or to "none" to trust no proxy at all.
|
||||
TrustedProxies []netip.Prefix
|
||||
|
||||
// SlowRequestThreshold is how long a request has to take before its log line
|
||||
// carries a stage breakdown. Fast requests deliberately carry none: it is the one
|
||||
// field on the line that varies in width, and a column of them on every /v1/status
|
||||
@@ -225,6 +237,10 @@ func Load() (Config, error) {
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
trusted, err := trustedProxies(os.Getenv("MEMBY_TRUSTED_PROXIES"))
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("MEMBY_TRUSTED_PROXIES: %w", err)
|
||||
}
|
||||
c := Config{
|
||||
ListenAddr: env("MEMBY_LISTEN_ADDR", ":8080"),
|
||||
EmbyURL: strings.TrimRight(os.Getenv("MEMBY_EMBY_URL"), "/"),
|
||||
@@ -261,6 +277,7 @@ func Load() (Config, error) {
|
||||
SyncAPIKey: strings.TrimSpace(os.Getenv("MEMBY_SYNC_API_KEY")),
|
||||
AnalyticsRetention: duration("MEMBY_ANALYTICS_RETENTION", 90*24*time.Hour),
|
||||
UpstreamTimeout: duration("MEMBY_UPSTREAM_TIMEOUT", 20*time.Second),
|
||||
TrustedProxies: trusted,
|
||||
SlowRequestThreshold: duration("MEMBY_SLOW_REQUEST_THRESHOLD", 500*time.Millisecond),
|
||||
EmbyHealthInterval: duration("MEMBY_EMBY_HEALTH_INTERVAL", 60*time.Second),
|
||||
SonarrURL: strings.TrimRight(strings.TrimSpace(os.Getenv("MEMBY_SONARR_URL")), "/"),
|
||||
@@ -408,6 +425,61 @@ func duration(key string, fallback time.Duration) time.Duration {
|
||||
return fallback
|
||||
}
|
||||
|
||||
// DefaultTrustedProxyRanges is what MEMBY_TRUSTED_PROXIES falls back to: loopback and
|
||||
// the private and unique-local ranges. It is exported so the request layer can use the
|
||||
// same set when it is handed a nil list.
|
||||
func DefaultTrustedProxyRanges() []netip.Prefix {
|
||||
return parsePrefixes(
|
||||
"127.0.0.0/8", "::1/128",
|
||||
"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16",
|
||||
"169.254.0.0/16", "fe80::/10", "fc00::/7",
|
||||
)
|
||||
}
|
||||
|
||||
func parsePrefixes(values ...string) []netip.Prefix {
|
||||
out := make([]netip.Prefix, 0, len(values))
|
||||
for _, value := range values {
|
||||
if prefix, err := netip.ParsePrefix(value); err == nil {
|
||||
out = append(out, prefix.Masked())
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// trustedProxies reads the MEMBY_TRUSTED_PROXIES list. Blank means the defaults, the
|
||||
// literal "none" means an empty (but non-nil) set, and every other token is an address
|
||||
// or a CIDR range — with "private" as a shorthand for the default ranges.
|
||||
func trustedProxies(raw string) ([]netip.Prefix, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
switch {
|
||||
case raw == "":
|
||||
return DefaultTrustedProxyRanges(), nil
|
||||
case strings.EqualFold(raw, "none"):
|
||||
return []netip.Prefix{}, nil
|
||||
}
|
||||
out := []netip.Prefix{}
|
||||
for _, token := range strings.Split(raw, ",") {
|
||||
token = strings.TrimSpace(token)
|
||||
if token == "" {
|
||||
continue
|
||||
}
|
||||
if strings.EqualFold(token, "private") {
|
||||
out = append(out, DefaultTrustedProxyRanges()...)
|
||||
continue
|
||||
}
|
||||
if prefix, err := netip.ParsePrefix(token); err == nil {
|
||||
out = append(out, prefix.Masked())
|
||||
continue
|
||||
}
|
||||
addr, err := netip.ParseAddr(token)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%q is not an IP address or CIDR range", token)
|
||||
}
|
||||
out = append(out, netip.PrefixFrom(addr, addr.BitLen()))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func integer(key string, fallback int) int {
|
||||
raw := strings.TrimSpace(os.Getenv(key))
|
||||
if raw == "" {
|
||||
|
||||
Reference in New Issue
Block a user