fix(security): trusted-proxy-scoped login rate limit
This commit is contained in:
@@ -5,20 +5,22 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Addr string
|
||||
DatabaseURL string
|
||||
RedisURL string
|
||||
JWTSecret []byte
|
||||
AdminUser string
|
||||
AdminPassword string
|
||||
BooksDir string
|
||||
CacheDir string
|
||||
ScanInterval time.Duration
|
||||
UploadMaxMB int64
|
||||
Addr string
|
||||
DatabaseURL string
|
||||
RedisURL string
|
||||
JWTSecret []byte
|
||||
AdminUser string
|
||||
AdminPassword string
|
||||
BooksDir string
|
||||
CacheDir string
|
||||
ScanInterval time.Duration
|
||||
UploadMaxMB int64
|
||||
TrustedProxies []string
|
||||
}
|
||||
|
||||
func Load() (*Config, error) {
|
||||
@@ -28,6 +30,16 @@ func Load() (*Config, error) {
|
||||
}
|
||||
return def
|
||||
}
|
||||
envList := func(k, def string) []string {
|
||||
v := env(k, def)
|
||||
out := make([]string, 0, len(strings.Split(v, ",")))
|
||||
for _, s := range strings.Split(v, ",") {
|
||||
if s = strings.TrimSpace(s); s != "" {
|
||||
out = append(out, s)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
scanSec, err := strconv.Atoi(env("SCAN_INTERVAL_SEC", "60"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("SCAN_INTERVAL_SEC: %w", err)
|
||||
@@ -49,15 +61,16 @@ func Load() (*Config, error) {
|
||||
return dir
|
||||
}
|
||||
return &Config{
|
||||
Addr: env("ADDR", ":8080"),
|
||||
DatabaseURL: env("DATABASE_URL", ""),
|
||||
RedisURL: env("REDIS_URL", ""),
|
||||
JWTSecret: []byte(secret),
|
||||
AdminUser: env("ADMIN_USER", ""),
|
||||
AdminPassword: env("ADMIN_PASSWORD", ""),
|
||||
BooksDir: resolveDir(env("BOOKS_DIR", "/data/books")),
|
||||
CacheDir: resolveDir(env("CACHE_DIR", "/data/cache")),
|
||||
ScanInterval: time.Duration(scanSec) * time.Second,
|
||||
UploadMaxMB: uploadMB,
|
||||
Addr: env("ADDR", ":8080"),
|
||||
DatabaseURL: env("DATABASE_URL", ""),
|
||||
RedisURL: env("REDIS_URL", ""),
|
||||
JWTSecret: []byte(secret),
|
||||
AdminUser: env("ADMIN_USER", ""),
|
||||
AdminPassword: env("ADMIN_PASSWORD", ""),
|
||||
BooksDir: resolveDir(env("BOOKS_DIR", "/data/books")),
|
||||
CacheDir: resolveDir(env("CACHE_DIR", "/data/cache")),
|
||||
ScanInterval: time.Duration(scanSec) * time.Second,
|
||||
UploadMaxMB: uploadMB,
|
||||
TrustedProxies: envList("TRUSTED_PROXY_CIDRS", "172.16.0.0/12"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ func TestLoad(t *testing.T) {
|
||||
}
|
||||
t.Setenv("SCAN_INTERVAL_SEC", "30")
|
||||
t.Setenv("DATABASE_URL", "postgres://x")
|
||||
t.Setenv("TRUSTED_PROXY_CIDRS", "")
|
||||
c, err := Load()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -34,4 +35,15 @@ func TestLoad(t *testing.T) {
|
||||
if c.ScanInterval != 30*time.Second || c.BooksDir != wantBooks || c.Addr != ":8080" {
|
||||
t.Fatalf("%+v", c)
|
||||
}
|
||||
if len(c.TrustedProxies) != 1 || c.TrustedProxies[0] != "172.16.0.0/12" {
|
||||
t.Fatalf("trusted proxies default: %+v", c.TrustedProxies)
|
||||
}
|
||||
t.Setenv("TRUSTED_PROXY_CIDRS", "10.0.0.0/8, 1.2.3.4")
|
||||
c, err = Load()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(c.TrustedProxies) != 2 || c.TrustedProxies[0] != "10.0.0.0/8" || c.TrustedProxies[1] != "1.2.3.4" {
|
||||
t.Fatalf("trusted proxies override: %+v", c.TrustedProxies)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user