fix client IP handling behind proxies
This commit is contained in:
@@ -1,3 +1,10 @@
|
|||||||
|
set_real_ip_from 127.0.0.1;
|
||||||
|
set_real_ip_from 10.42.0.0/16;
|
||||||
|
set_real_ip_from 10.43.0.0/16;
|
||||||
|
set_real_ip_from 172.16.0.0/12;
|
||||||
|
real_ip_header X-Forwarded-For;
|
||||||
|
real_ip_recursive on;
|
||||||
|
|
||||||
map $http_upgrade $connection_upgrade {
|
map $http_upgrade $connection_upgrade {
|
||||||
default upgrade;
|
default upgrade;
|
||||||
'' close;
|
'' close;
|
||||||
@@ -21,7 +28,7 @@ server {
|
|||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection $connection_upgrade;
|
proxy_set_header Connection $connection_upgrade;
|
||||||
@@ -35,7 +42,7 @@ server {
|
|||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection $connection_upgrade;
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
set_real_ip_from 127.0.0.1;
|
||||||
|
set_real_ip_from 10.42.0.0/16;
|
||||||
|
set_real_ip_from 10.43.0.0/16;
|
||||||
|
set_real_ip_from 172.16.0.0/12;
|
||||||
|
real_ip_header X-Forwarded-For;
|
||||||
|
real_ip_recursive on;
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name _;
|
server_name _;
|
||||||
@@ -14,7 +21,7 @@ server {
|
|||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_read_timeout 120s;
|
proxy_read_timeout 120s;
|
||||||
proxy_send_timeout 120s;
|
proxy_send_timeout 120s;
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
server:
|
server:
|
||||||
port: 8080
|
port: 8080
|
||||||
mode: release
|
mode: release
|
||||||
|
trusted_proxies:
|
||||||
|
- 127.0.0.1
|
||||||
|
- ::1
|
||||||
|
- 10.42.0.0/16
|
||||||
|
- 10.43.0.0/16
|
||||||
|
- 172.16.0.0/12
|
||||||
|
|
||||||
database:
|
database:
|
||||||
host: pgbouncer
|
host: pgbouncer
|
||||||
|
|||||||
@@ -87,6 +87,16 @@ k3s 默认同时支持两种访问方式。
|
|||||||
- tenant admin: `http://<node-ip>:30080`
|
- tenant admin: `http://<node-ip>:30080`
|
||||||
- ops admin: `http://<node-ip>:30081`
|
- ops admin: `http://<node-ip>:30081`
|
||||||
|
|
||||||
|
这两个 NodePort Service 使用 `externalTrafficPolicy: Local`,用于保留客户端源 IP,后端日志和登录审计会读取规范化后的 `X-Forwarded-For` / `X-Real-IP`。
|
||||||
|
|
||||||
|
如果通过 k3s 内置 Traefik Ingress 对外提供服务,也需要把 Traefik Service 调整为保留源地址:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl -n kube-system patch svc traefik -p '{"spec":{"externalTrafficPolicy":"Local"}}'
|
||||||
|
```
|
||||||
|
|
||||||
|
临时用 frpc/frps 做 TCP 隧道时,用户源 IP 通常不会穿透到 k3s;这类链路需要 proxy protocol 或 HTTP 反代写入可信 `X-Forwarded-For`,否则日志只能记录隧道出口/NAS 本地转发地址。
|
||||||
|
|
||||||
例如 NAS IP 是 `192.168.100.19`:
|
例如 NAS IP 是 `192.168.100.19`:
|
||||||
|
|
||||||
- tenant admin: `http://192.168.100.19:30080`
|
- tenant admin: `http://192.168.100.19:30080`
|
||||||
|
|||||||
@@ -471,6 +471,7 @@ metadata:
|
|||||||
app.kubernetes.io/name: frontend
|
app.kubernetes.io/name: frontend
|
||||||
spec:
|
spec:
|
||||||
type: NodePort
|
type: NodePort
|
||||||
|
externalTrafficPolicy: Local
|
||||||
selector:
|
selector:
|
||||||
app.kubernetes.io/name: frontend
|
app.kubernetes.io/name: frontend
|
||||||
ports:
|
ports:
|
||||||
@@ -530,6 +531,7 @@ metadata:
|
|||||||
app.kubernetes.io/name: ops-web
|
app.kubernetes.io/name: ops-web
|
||||||
spec:
|
spec:
|
||||||
type: NodePort
|
type: NodePort
|
||||||
|
externalTrafficPolicy: Local
|
||||||
selector:
|
selector:
|
||||||
app.kubernetes.io/name: ops-web
|
app.kubernetes.io/name: ops-web
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
server:
|
server:
|
||||||
port: 8080
|
port: 8080
|
||||||
mode: release
|
mode: release
|
||||||
|
trusted_proxies:
|
||||||
|
- 127.0.0.1
|
||||||
|
- ::1
|
||||||
|
- 10.42.0.0/16
|
||||||
|
- 10.43.0.0/16
|
||||||
|
- 172.16.0.0/12
|
||||||
|
|
||||||
database:
|
database:
|
||||||
host: pgbouncer
|
host: pgbouncer
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
server:
|
server:
|
||||||
port: 8090
|
port: 8090
|
||||||
mode: release
|
mode: release
|
||||||
|
trusted_proxies:
|
||||||
|
- 127.0.0.1
|
||||||
|
- ::1
|
||||||
|
- 10.42.0.0/16
|
||||||
|
- 10.43.0.0/16
|
||||||
|
- 172.16.0.0/12
|
||||||
|
|
||||||
database:
|
database:
|
||||||
host: pgbouncer
|
host: pgbouncer
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
server:
|
server:
|
||||||
port: 8090
|
port: 8090
|
||||||
mode: release
|
mode: release
|
||||||
|
trusted_proxies:
|
||||||
|
- 127.0.0.1
|
||||||
|
- ::1
|
||||||
|
- 10.42.0.0/16
|
||||||
|
- 10.43.0.0/16
|
||||||
|
- 172.16.0.0/12
|
||||||
|
|
||||||
database:
|
database:
|
||||||
host: pgbouncer
|
host: pgbouncer
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/geo-platform/tenant-api/internal/ops/app"
|
"github.com/geo-platform/tenant-api/internal/ops/app"
|
||||||
@@ -18,6 +17,7 @@ import (
|
|||||||
"github.com/geo-platform/tenant-api/internal/ops/repository"
|
"github.com/geo-platform/tenant-api/internal/ops/repository"
|
||||||
"github.com/geo-platform/tenant-api/internal/ops/transport"
|
"github.com/geo-platform/tenant-api/internal/ops/transport"
|
||||||
sharedauth "github.com/geo-platform/tenant-api/internal/shared/auth"
|
sharedauth "github.com/geo-platform/tenant-api/internal/shared/auth"
|
||||||
|
sharedbootstrap "github.com/geo-platform/tenant-api/internal/shared/bootstrap"
|
||||||
"github.com/geo-platform/tenant-api/internal/shared/cache"
|
"github.com/geo-platform/tenant-api/internal/shared/cache"
|
||||||
"github.com/geo-platform/tenant-api/internal/shared/observability"
|
"github.com/geo-platform/tenant-api/internal/shared/observability"
|
||||||
"github.com/geo-platform/tenant-api/internal/shared/repository/postgres"
|
"github.com/geo-platform/tenant-api/internal/shared/repository/postgres"
|
||||||
@@ -113,10 +113,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if cfg.Server.Mode == "release" {
|
engine, err := sharedbootstrap.NewEngine(cfg.Server)
|
||||||
gin.SetMode(gin.ReleaseMode)
|
if err != nil {
|
||||||
|
logger.Sugar().Fatalf("ops-api init http engine: %v", err)
|
||||||
}
|
}
|
||||||
engine := gin.New()
|
|
||||||
|
|
||||||
transport.RegisterRoutes(transport.Deps{
|
transport.RegisterRoutes(transport.Deps{
|
||||||
Engine: engine,
|
Engine: engine,
|
||||||
|
|||||||
@@ -4,6 +4,12 @@
|
|||||||
server:
|
server:
|
||||||
port: 8080
|
port: 8080
|
||||||
mode: debug
|
mode: debug
|
||||||
|
trusted_proxies:
|
||||||
|
- 127.0.0.1
|
||||||
|
- ::1
|
||||||
|
- 10.42.0.0/16
|
||||||
|
- 10.43.0.0/16
|
||||||
|
- 172.16.0.0/12
|
||||||
|
|
||||||
database:
|
database:
|
||||||
host: localhost
|
host: localhost
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
server:
|
server:
|
||||||
port: 8080
|
port: 8080
|
||||||
mode: debug
|
mode: debug
|
||||||
|
trusted_proxies:
|
||||||
|
- 127.0.0.1
|
||||||
|
- ::1
|
||||||
|
- 10.42.0.0/16
|
||||||
|
- 10.43.0.0/16
|
||||||
|
- 172.16.0.0/12
|
||||||
|
|
||||||
database:
|
database:
|
||||||
host: localhost
|
host: localhost
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
server:
|
server:
|
||||||
port: 8090
|
port: 8090
|
||||||
mode: debug
|
mode: debug
|
||||||
|
trusted_proxies:
|
||||||
|
- 127.0.0.1
|
||||||
|
- ::1
|
||||||
|
- 10.42.0.0/16
|
||||||
|
- 10.43.0.0/16
|
||||||
|
- 172.16.0.0/12
|
||||||
|
|
||||||
database:
|
database:
|
||||||
host: localhost
|
host: localhost
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/geo-platform/tenant-api/internal/shared/auditlog"
|
"github.com/geo-platform/tenant-api/internal/shared/auditlog"
|
||||||
"github.com/geo-platform/tenant-api/internal/shared/auth"
|
"github.com/geo-platform/tenant-api/internal/shared/auth"
|
||||||
|
sharedbootstrap "github.com/geo-platform/tenant-api/internal/shared/bootstrap"
|
||||||
"github.com/geo-platform/tenant-api/internal/shared/cache"
|
"github.com/geo-platform/tenant-api/internal/shared/cache"
|
||||||
"github.com/geo-platform/tenant-api/internal/shared/config"
|
"github.com/geo-platform/tenant-api/internal/shared/config"
|
||||||
"github.com/geo-platform/tenant-api/internal/shared/llm"
|
"github.com/geo-platform/tenant-api/internal/shared/llm"
|
||||||
@@ -148,10 +149,10 @@ func New(configPath string) (*App, error) {
|
|||||||
kolAssists := repository.NewKolAssistRepository(pool)
|
kolAssists := repository.NewKolAssistRepository(pool)
|
||||||
kolPromptAsset := tenantapp.NewKolPromptAsset(objectStorageClient)
|
kolPromptAsset := tenantapp.NewKolPromptAsset(objectStorageClient)
|
||||||
|
|
||||||
if cfg.Server.Mode == "release" {
|
engine, err := sharedbootstrap.NewEngine(cfg.Server)
|
||||||
gin.SetMode(gin.ReleaseMode)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("init http engine: %w", err)
|
||||||
}
|
}
|
||||||
engine := gin.New()
|
|
||||||
|
|
||||||
engine.Use(
|
engine.Use(
|
||||||
middleware.Recovery(logger),
|
middleware.Recovery(logger),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -216,7 +217,7 @@ func Diff(previous, current *Config) []FieldChange {
|
|||||||
changes = append(changes, FieldChange{Path: path, Hot: hot})
|
changes = append(changes, FieldChange{Path: path, Hot: hot})
|
||||||
}
|
}
|
||||||
|
|
||||||
if previous.Server != current.Server {
|
if !reflect.DeepEqual(previous.Server, current.Server) {
|
||||||
add("server", false)
|
add("server", false)
|
||||||
}
|
}
|
||||||
if previous.Database != current.Database {
|
if previous.Database != current.Database {
|
||||||
@@ -330,6 +331,7 @@ func normalizeConfig(cfg *Config) {
|
|||||||
if strings.TrimSpace(cfg.Server.Mode) == "" {
|
if strings.TrimSpace(cfg.Server.Mode) == "" {
|
||||||
cfg.Server.Mode = "debug"
|
cfg.Server.Mode = "debug"
|
||||||
}
|
}
|
||||||
|
sharedconfig.NormalizeServerConfig(&cfg.Server)
|
||||||
if strings.TrimSpace(cfg.Redis.Addr) == "" {
|
if strings.TrimSpace(cfg.Redis.Addr) == "" {
|
||||||
cfg.Redis.Addr = "localhost:6379"
|
cfg.Redis.Addr = "localhost:6379"
|
||||||
}
|
}
|
||||||
@@ -361,8 +363,9 @@ func normalizeConfig(cfg *Config) {
|
|||||||
func defaultSettings() map[string]any {
|
func defaultSettings() map[string]any {
|
||||||
return map[string]any{
|
return map[string]any{
|
||||||
"server": map[string]any{
|
"server": map[string]any{
|
||||||
"port": 8090,
|
"port": 8090,
|
||||||
"mode": "debug",
|
"mode": "debug",
|
||||||
|
"trusted_proxies": sharedconfig.DefaultTrustedProxies(),
|
||||||
},
|
},
|
||||||
"redis": map[string]any{
|
"redis": map[string]any{
|
||||||
"addr": "localhost:6379",
|
"addr": "localhost:6379",
|
||||||
@@ -417,6 +420,9 @@ func applyEnvOverrides(cfg *Config) error {
|
|||||||
if v := os.Getenv("OPS_IP_REGION_V6_XDB_PATH"); v != "" {
|
if v := os.Getenv("OPS_IP_REGION_V6_XDB_PATH"); v != "" {
|
||||||
cfg.IPRegion.V6XDBPath = v
|
cfg.IPRegion.V6XDBPath = v
|
||||||
}
|
}
|
||||||
|
if v := os.Getenv("OPS_SERVER_TRUSTED_PROXIES"); v != "" {
|
||||||
|
cfg.Server.TrustedProxies = strings.Split(v, ",")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package bootstrap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"github.com/geo-platform/tenant-api/internal/shared/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewEngine(server config.ServerConfig) (*gin.Engine, error) {
|
||||||
|
config.NormalizeServerConfig(&server)
|
||||||
|
if server.Mode == "release" {
|
||||||
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
}
|
||||||
|
engine := gin.New()
|
||||||
|
engine.ForwardedByClientIP = true
|
||||||
|
engine.RemoteIPHeaders = []string{"X-Forwarded-For", "X-Real-IP"}
|
||||||
|
if err := engine.SetTrustedProxies(server.TrustedProxies); err != nil {
|
||||||
|
return nil, fmt.Errorf("set trusted proxies: %w", err)
|
||||||
|
}
|
||||||
|
return engine, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package bootstrap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/geo-platform/tenant-api/internal/shared/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewEngineUsesForwardedClientIPFromTrustedProxy(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
|
engine, err := NewEngine(config.ServerConfig{
|
||||||
|
TrustedProxies: []string{"10.42.0.0/16"},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
engine.GET("/ip", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, c.ClientIP())
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/ip", nil)
|
||||||
|
req.RemoteAddr = "10.42.0.128:51234"
|
||||||
|
req.Header.Set("X-Forwarded-For", "106.14.89.27")
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
|
||||||
|
engine.ServeHTTP(resp, req)
|
||||||
|
|
||||||
|
require.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
require.Equal(t, "106.14.89.27", resp.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewEngineRejectsInvalidTrustedProxy(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
|
_, err := NewEngine(config.ServerConfig{
|
||||||
|
TrustedProxies: []string{"not-a-cidr"},
|
||||||
|
})
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
@@ -41,8 +41,9 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
Port int `mapstructure:"port"`
|
Port int `mapstructure:"port"`
|
||||||
Mode string `mapstructure:"mode"`
|
Mode string `mapstructure:"mode"`
|
||||||
|
TrustedProxies []string `mapstructure:"trusted_proxies"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DatabaseConfig struct {
|
type DatabaseConfig struct {
|
||||||
@@ -366,6 +367,7 @@ func loadWithFiles(configPath string) (*Config, []string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyEnvOverrides(cfg)
|
applyEnvOverrides(cfg)
|
||||||
|
NormalizeServerConfig(&cfg.Server)
|
||||||
NormalizeRedisConfig(&cfg.Redis)
|
NormalizeRedisConfig(&cfg.Redis)
|
||||||
NormalizeCacheConfig(&cfg.Cache)
|
NormalizeCacheConfig(&cfg.Cache)
|
||||||
normalizeRabbitMQConfig(&cfg.RabbitMQ)
|
normalizeRabbitMQConfig(&cfg.RabbitMQ)
|
||||||
@@ -469,6 +471,42 @@ func ensureMapSetting(settings map[string]any, key string) map[string]any {
|
|||||||
return next
|
return next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DefaultTrustedProxies() []string {
|
||||||
|
return []string{
|
||||||
|
"127.0.0.1",
|
||||||
|
"::1",
|
||||||
|
"10.42.0.0/16",
|
||||||
|
"10.43.0.0/16",
|
||||||
|
"172.16.0.0/12",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NormalizeServerConfig(cfg *ServerConfig) {
|
||||||
|
if cfg == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cfg.Mode = strings.TrimSpace(cfg.Mode)
|
||||||
|
if cfg.TrustedProxies == nil {
|
||||||
|
cfg.TrustedProxies = DefaultTrustedProxies()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cfg.TrustedProxies = compactStringSlice(cfg.TrustedProxies)
|
||||||
|
}
|
||||||
|
|
||||||
|
func compactStringSlice(values []string) []string {
|
||||||
|
if values == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := make([]string, 0, len(values))
|
||||||
|
for _, value := range values {
|
||||||
|
value = strings.TrimSpace(value)
|
||||||
|
if value != "" {
|
||||||
|
out = append(out, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
func normalizeMonitoringDispatchConfig(cfg *MonitoringDispatchConfig) {
|
func normalizeMonitoringDispatchConfig(cfg *MonitoringDispatchConfig) {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return
|
return
|
||||||
@@ -747,6 +785,9 @@ func applyEnvOverrides(cfg *Config) {
|
|||||||
if token, ok := lookupFirstNonEmptyEnv("SCHEDULER_INTERNAL_METRICS_TOKEN", "SCHEDULER_METRICS_TOKEN"); ok {
|
if token, ok := lookupFirstNonEmptyEnv("SCHEDULER_INTERNAL_METRICS_TOKEN", "SCHEDULER_METRICS_TOKEN"); ok {
|
||||||
cfg.Scheduler.InternalMetricsToken = token
|
cfg.Scheduler.InternalMetricsToken = token
|
||||||
}
|
}
|
||||||
|
if trustedProxies, ok := lookupNonEmptyEnv("SERVER_TRUSTED_PROXIES"); ok {
|
||||||
|
cfg.Server.TrustedProxies = strings.Split(trustedProxies, ",")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeRabbitMQConfig(cfg *RabbitMQConfig) {
|
func normalizeRabbitMQConfig(cfg *RabbitMQConfig) {
|
||||||
|
|||||||
@@ -132,6 +132,42 @@ brand_library: {}
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoadAppliesServerTrustedProxyDefaultsAndOverrides(t *testing.T) {
|
||||||
|
defaultConfigPath := writeTestConfig(t, `
|
||||||
|
server: {}
|
||||||
|
`)
|
||||||
|
|
||||||
|
cfg, err := Load(defaultConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Load() default error = %v", err)
|
||||||
|
}
|
||||||
|
if got := cfg.Server.TrustedProxies; len(got) != len(DefaultTrustedProxies()) {
|
||||||
|
t.Fatalf("expected default trusted proxies, got %#v", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
overrideConfigPath := writeTestConfig(t, `
|
||||||
|
server:
|
||||||
|
trusted_proxies:
|
||||||
|
- " 10.42.0.0/16 "
|
||||||
|
- ""
|
||||||
|
- "192.168.1.10"
|
||||||
|
`)
|
||||||
|
|
||||||
|
cfg, err = Load(overrideConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Load() override error = %v", err)
|
||||||
|
}
|
||||||
|
want := []string{"10.42.0.0/16", "192.168.1.10"}
|
||||||
|
if len(cfg.Server.TrustedProxies) != len(want) {
|
||||||
|
t.Fatalf("trusted proxies = %#v, want %#v", cfg.Server.TrustedProxies, want)
|
||||||
|
}
|
||||||
|
for i := range want {
|
||||||
|
if cfg.Server.TrustedProxies[i] != want[i] {
|
||||||
|
t.Fatalf("trusted proxies = %#v, want %#v", cfg.Server.TrustedProxies, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLoadAppliesSchedulerHTTPDefaultsAndDisableSentinel(t *testing.T) {
|
func TestLoadAppliesSchedulerHTTPDefaultsAndDisableSentinel(t *testing.T) {
|
||||||
defaultConfigPath := writeTestConfig(t, `
|
defaultConfigPath := writeTestConfig(t, `
|
||||||
scheduler: {}
|
scheduler: {}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func Diff(previous, current *Config) []FieldChange {
|
|||||||
changes = append(changes, FieldChange{Path: path, Hot: hot})
|
changes = append(changes, FieldChange{Path: path, Hot: hot})
|
||||||
}
|
}
|
||||||
|
|
||||||
if previous.Server != current.Server {
|
if !reflect.DeepEqual(previous.Server, current.Server) {
|
||||||
addChange("server", false)
|
addChange("server", false)
|
||||||
}
|
}
|
||||||
if previous.Database != current.Database {
|
if previous.Database != current.Database {
|
||||||
|
|||||||
@@ -72,3 +72,36 @@ func TestLoggerIncludesDetailedAuthFailureContext(t *testing.T) {
|
|||||||
require.Contains(t, logLine, `"auth_parse_error":"token has invalid claims: token is expired"`)
|
require.Contains(t, logLine, `"auth_parse_error":"token has invalid claims: token is expired"`)
|
||||||
require.Contains(t, logLine, `"auth_token_fingerprint":"`)
|
require.Contains(t, logLine, `"auth_token_fingerprint":"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoggerUsesForwardedClientIPFromTrustedProxy(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
|
var logBuffer bytes.Buffer
|
||||||
|
encoderConfig := zap.NewProductionEncoderConfig()
|
||||||
|
encoderConfig.TimeKey = ""
|
||||||
|
logger := zap.New(zapcore.NewCore(
|
||||||
|
zapcore.NewJSONEncoder(encoderConfig),
|
||||||
|
zapcore.AddSync(&logBuffer),
|
||||||
|
zapcore.DebugLevel,
|
||||||
|
))
|
||||||
|
|
||||||
|
router := gin.New()
|
||||||
|
require.NoError(t, router.SetTrustedProxies([]string{"10.42.0.0/16"}))
|
||||||
|
router.Use(RequestID())
|
||||||
|
router.Use(Logger(logger))
|
||||||
|
router.GET("/test", func(c *gin.Context) {
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||||
|
req.RemoteAddr = "10.42.0.128:51234"
|
||||||
|
req.Header.Set("X-Forwarded-For", "106.14.89.27")
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
|
||||||
|
router.ServeHTTP(resp, req)
|
||||||
|
|
||||||
|
require.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
require.Contains(t, logBuffer.String(), `"client_ip":"106.14.89.27"`)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user