Enhance configuration and documentation for CIDR and IP handling. Updated config.example.yaml to include additional whitelisted IPs and new server entries. Improved GATEWAY_RUN.md to clarify CIDR and single IP usage. Implemented parseCIDROrIP function in config.go for better validation of IP formats in whitelist and trusted proxies.
This commit is contained in:
+15
-3
@@ -9,8 +9,10 @@ listen: ":8080"
|
||||
allow_all: false
|
||||
|
||||
# CIDR allowlist when allow_all is false. Empty list denies all clients.
|
||||
# Можно указывать и одиночный IP (будет трактован как /32 или /128), и CIDR.
|
||||
whitelist_cidrs:
|
||||
- "127.0.0.1/32"
|
||||
- "203.0.113.5"
|
||||
- "::1/128"
|
||||
# Docker bridge (adjust to your environment):
|
||||
# - "172.16.0.0/12"
|
||||
@@ -24,8 +26,18 @@ trusted_proxies: []
|
||||
servers:
|
||||
- alias: main_srv
|
||||
base_url: http://127.0.0.1:9091
|
||||
# Default path prefix on upstream (Telemt Control API uses /v1).
|
||||
path_prefix: /v1
|
||||
# Optional: name of environment variable whose value is sent as Authorization
|
||||
# to this upstream (exact string, Telemt auth_header semantics).
|
||||
# authorization_env: TELEMT_API_AUTH
|
||||
|
||||
# ivx: порт 9091 как у типичного Telemt API; при необходимости — https и другой порт.
|
||||
- alias: gt1
|
||||
base_url: http://gt1.ivx.su:9091
|
||||
path_prefix: /v1
|
||||
|
||||
- alias: gt2
|
||||
base_url: http://gt2.ivx.su:9091
|
||||
path_prefix: /v1
|
||||
|
||||
- alias: gt3
|
||||
base_url: http://gt3.ivx.su:9091
|
||||
path_prefix: /v1
|
||||
|
||||
@@ -60,6 +60,7 @@ servers:
|
||||
Правила:
|
||||
|
||||
- При `allow_all: false` и **пустом** `whitelist_cidrs` доступ будет **закрыт для всех** (кроме `GET /health`).
|
||||
- В `whitelist_cidrs` и `trusted_proxies` допустимы **CIDR** (`10.0.0.0/8`) и **одиночный IPv4/IPv6** без маски (`87.103.241.8` эквивалентно `87.103.241.8/32`).
|
||||
- `GET /health` на шлюзе **не** проверяется по whitelist — так проще настроить Docker `HEALTHCHECK` и оркестраторы.
|
||||
- Поле `path_prefix` по умолчанию равно `/v1` (префикс Telemt Control API).
|
||||
|
||||
|
||||
@@ -83,18 +83,34 @@ func (c *Config) Validate() error {
|
||||
return fmt.Errorf("at least one server entry is required")
|
||||
}
|
||||
for i, s := range c.WhitelistCIDRs {
|
||||
if _, err := netip.ParsePrefix(strings.TrimSpace(s)); err != nil {
|
||||
if _, err := parseCIDROrIP(s); err != nil {
|
||||
return fmt.Errorf("whitelist_cidrs[%d]: %w", i, err)
|
||||
}
|
||||
}
|
||||
for i, s := range c.TrustedProxies {
|
||||
if _, err := netip.ParsePrefix(strings.TrimSpace(s)); err != nil {
|
||||
if _, err := parseCIDROrIP(s); err != nil {
|
||||
return fmt.Errorf("trusted_proxies[%d]: %w", i, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseCIDROrIP accepts a CIDR ("10.0.0.0/8") or a single IP ("87.103.241.8" → /32 or /128).
|
||||
func parseCIDROrIP(s string) (netip.Prefix, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
return netip.Prefix{}, fmt.Errorf("empty")
|
||||
}
|
||||
if p, err := netip.ParsePrefix(s); err == nil {
|
||||
return p, nil
|
||||
}
|
||||
addr, err := netip.ParseAddr(s)
|
||||
if err != nil {
|
||||
return netip.Prefix{}, fmt.Errorf("%w (use CIDR like %s/32 for IPv4)", err, s)
|
||||
}
|
||||
return addr.Prefix(addr.BitLen())
|
||||
}
|
||||
|
||||
// Parsed holds compiled CIDR lists and server map.
|
||||
type Parsed struct {
|
||||
Config *Config
|
||||
@@ -108,7 +124,7 @@ type Parsed struct {
|
||||
func (c *Config) Parse() (*Parsed, error) {
|
||||
var wl []netip.Prefix
|
||||
for _, s := range c.WhitelistCIDRs {
|
||||
p, err := netip.ParsePrefix(strings.TrimSpace(s))
|
||||
p, err := parseCIDROrIP(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -116,7 +132,7 @@ func (c *Config) Parse() (*Parsed, error) {
|
||||
}
|
||||
var tr []netip.Prefix
|
||||
for _, s := range c.TrustedProxies {
|
||||
p, err := netip.ParsePrefix(strings.TrimSpace(s))
|
||||
p, err := parseCIDROrIP(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -31,6 +31,26 @@ servers:
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCIDRorIP(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{"87.103.241.8", "87.103.241.8/32"},
|
||||
{"87.103.241.8/32", "87.103.241.8/32"},
|
||||
{"10.0.0.0/8", "10.0.0.0/8"},
|
||||
{"2001:db8::1", "2001:db8::1/128"},
|
||||
} {
|
||||
p, err := parseCIDROrIP(tc.in)
|
||||
if err != nil {
|
||||
t.Fatalf("%q: %v", tc.in, err)
|
||||
}
|
||||
if p.String() != tc.want {
|
||||
t.Fatalf("%q: got %s want %s", tc.in, p, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDuplicateAlias(t *testing.T) {
|
||||
c := &Config{
|
||||
Servers: []Server{
|
||||
|
||||
Reference in New Issue
Block a user