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.
Publish telemt-api gateway Docker image / test (push) Successful in 39s
Publish telemt-api gateway Docker image / build-and-push (push) Successful in 1m45s

This commit is contained in:
Denozordec
2026-03-30 00:00:49 +07:00
parent 073d2b2c09
commit e58f504395
4 changed files with 56 additions and 7 deletions
+15 -3
View File
@@ -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
+1
View File
@@ -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).
+20 -4
View File
@@ -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
}
+20
View File
@@ -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{