Resolve real client IP behind trusted proxies (closes #94) #127
+35
-19
@@ -44,6 +44,7 @@ const (
|
||||
keyUpstreamConnectionsPerHost = "upstream_connections_per_host"
|
||||
keyCacheMaxBytes = "cache_max_bytes"
|
||||
keyBlockedNetworks = "blocked_networks"
|
||||
keyTrustedProxies = "trusted_proxies"
|
||||
)
|
||||
|
||||
// placeholderSigningKey is the dummy signing_key shipped in
|
||||
@@ -117,6 +118,14 @@ type Config struct {
|
||||
// fetcher's dialer; the built-in ranges always apply.
|
||||
BlockedNetworks []netip.Prefix
|
||||
|
||||
// TrustedProxies are the CIDR ranges of reverse proxies whose
|
||||
// forwarding headers may be believed. Forwarded headers are honored
|
||||
// only when the immediate peer falls inside one of these ranges;
|
||||
// otherwise the peer address is used and the headers are ignored, so
|
||||
// an untrusted client cannot spoof its address. Empty means trust
|
||||
// nothing and always use the peer address.
|
||||
TrustedProxies []netip.Prefix
|
||||
|
||||
// CacheMaxBytes is the disk cache size limit in bytes. Zero
|
||||
// disables the disk cache entirely. When cache_max_bytes is
|
||||
// omitted from the configuration, this holds the computed default
|
||||
@@ -185,7 +194,12 @@ func newFromSmartConfig(sc *smartconfig.Config) (*Config, error) {
|
||||
}
|
||||
}
|
||||
|
||||
blockedNetworks, err := getBlockedNetworks(sc)
|
||||
blockedNetworks, err := parseCIDRList(sc, keyBlockedNetworks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trustedProxies, err := parseCIDRList(sc, keyTrustedProxies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -207,6 +221,7 @@ func newFromSmartConfig(sc *smartconfig.Config) (*Config, error) {
|
||||
keyUpstreamConnectionsPerHost, DefaultUpstreamConnectionsPerHost),
|
||||
CacheMaxBytes: loader.int64Val(keyCacheMaxBytes, 0),
|
||||
BlockedNetworks: blockedNetworks,
|
||||
TrustedProxies: trustedProxies,
|
||||
}
|
||||
|
||||
// The computed default for cache_max_bytes needs a validated
|
||||
@@ -322,7 +337,8 @@ func isKnownConfigKey(key string) bool {
|
||||
switch key {
|
||||
case keyDebug, keyMaintenanceMode, keyPort, keyStateDir, keySentryDSN,
|
||||
keyDBURL, keyMetrics, keySigningKey, keyAllowlistHosts, keyAllowHTTP,
|
||||
keyUpstreamConnectionsPerHost, keyCacheMaxBytes, keyBlockedNetworks, "env":
|
||||
keyUpstreamConnectionsPerHost, keyCacheMaxBytes, keyBlockedNetworks,
|
||||
keyTrustedProxies, "env":
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -817,27 +833,27 @@ func getStringSlice(sc *smartconfig.Config) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// getBlockedNetworks parses the blocked_networks value into CIDR prefixes,
|
||||
// or returns nil if the key is omitted. It accepts a YAML list of strings
|
||||
// or a comma-separated string. An explicitly null value, a wrong type, an
|
||||
// empty entry, a non-string entry, or an unparseable CIDR aborts startup
|
||||
// naming the key and the offending value; a default (the built-in
|
||||
// blocklist alone) applies only to an omitted key.
|
||||
func getBlockedNetworks(sc *smartconfig.Config) ([]netip.Prefix, error) {
|
||||
// parseCIDRList parses the value of the named config key into CIDR
|
||||
// prefixes, or returns nil if the key is omitted. It accepts a YAML list
|
||||
// of strings or a comma-separated string. An explicitly null value, a
|
||||
// wrong type, an empty entry, a non-string entry, or an unparseable CIDR
|
||||
// aborts startup naming the key and the offending value; the default
|
||||
// (an empty list) applies only to an omitted key.
|
||||
func parseCIDRList(sc *smartconfig.Config, key string) ([]netip.Prefix, error) {
|
||||
if sc == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
raw, ok := sc.Get(keyBlockedNetworks)
|
||||
raw, ok := sc.Get(key)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if raw == nil {
|
||||
return nil, errNullConfigValue(keyBlockedNetworks)
|
||||
return nil, errNullConfigValue(key)
|
||||
}
|
||||
|
||||
entries, err := blockedNetworkEntries(raw)
|
||||
entries, err := cidrListEntries(raw, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -848,7 +864,7 @@ func getBlockedNetworks(sc *smartconfig.Config) ([]netip.Prefix, error) {
|
||||
prefix, err := netip.ParsePrefix(entry)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("config key %q: value %q is %w",
|
||||
keyBlockedNetworks, entry, errNotAValidCIDR)
|
||||
key, entry, errNotAValidCIDR)
|
||||
}
|
||||
|
||||
prefixes = append(prefixes, prefix)
|
||||
@@ -857,10 +873,10 @@ func getBlockedNetworks(sc *smartconfig.Config) ([]netip.Prefix, error) {
|
||||
return prefixes, nil
|
||||
}
|
||||
|
||||
// blockedNetworkEntries extracts the raw blocked_networks entries as
|
||||
// cidrListEntries extracts the raw entries of the named CIDR-list key as
|
||||
// trimmed, non-empty strings, from either a YAML list of strings or a
|
||||
// comma-separated string. Any other shape is a configuration error.
|
||||
func blockedNetworkEntries(raw any) ([]string, error) {
|
||||
func cidrListEntries(raw any, key string) ([]string, error) {
|
||||
switch val := raw.(type) {
|
||||
case []any:
|
||||
entries := make([]string, 0, len(val))
|
||||
@@ -869,12 +885,12 @@ func blockedNetworkEntries(raw any) ([]string, error) {
|
||||
str, ok := item.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("config key %q: list entry %v (%T) is %w",
|
||||
keyBlockedNetworks, item, item, errNotAString)
|
||||
key, item, item, errNotAString)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(str) == "" {
|
||||
return nil, fmt.Errorf("config key %q: %w",
|
||||
keyBlockedNetworks, errEmptyListEntry)
|
||||
key, errEmptyListEntry)
|
||||
}
|
||||
|
||||
entries = append(entries, strings.TrimSpace(str))
|
||||
@@ -888,7 +904,7 @@ func blockedNetworkEntries(raw any) ([]string, error) {
|
||||
trimmed := strings.TrimSpace(part)
|
||||
if trimmed == "" {
|
||||
return nil, fmt.Errorf("config key %q: value %q %w",
|
||||
keyBlockedNetworks, val, errEmptyEntry)
|
||||
key, val, errEmptyEntry)
|
||||
}
|
||||
|
||||
entries = append(entries, trimmed)
|
||||
@@ -897,6 +913,6 @@ func blockedNetworkEntries(raw any) ([]string, error) {
|
||||
return entries, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("config key %q: value %v (%T) is %w",
|
||||
keyBlockedNetworks, raw, raw, errNotAStringList)
|
||||
key, raw, raw, errNotAStringList)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user