feat(go-services): tracker-go — auth gate (itsdangerous + internal-trust)
Replicates main.py's AuthMiddleware so /go/ can be exposed safely:
- internal-trust: private source IP AND no X-Forwarded-For => skip auth
(loopback/compose callers; nginx adds XFF to all internet traffic).
- session cookie: byte-compatible itsdangerous URLSafeTimedSerializer verify
(HMAC-SHA1, django-concat key derivation sha1("itsdangerous"+"signer"+key),
Unix-epoch timestamp, urlsafe-b64 no pad, optional zlib payload), keyed on the
same SECRET_KEY. 30-day max-age. Public allowlist (/login,/logout,login assets,
/icons/,/health); 302->/login for html, 401 JSON otherwise.
Validated on the server: internal-trust loopback 200; external no-cookie 401;
html 302; valid cookie 200; tampered 401; /health public 200; and the SAME
Python-issued cookie authenticates BOTH services (cross-compat proof).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c4e8190656
commit
bf15d4a2f7
3 changed files with 180 additions and 1 deletions
|
|
@ -38,6 +38,7 @@ type Server struct {
|
|||
totals *totalsCache
|
||||
invProxy *httputil.ReverseProxy
|
||||
staticDir string
|
||||
secretKey string
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
|
|
@ -55,8 +56,14 @@ func main() {
|
|||
cache: newLiveCache(),
|
||||
totals: newTotalsCache(),
|
||||
staticDir: cfg.StaticDir,
|
||||
secretKey: cfg.SecretKey,
|
||||
log: logger,
|
||||
}
|
||||
if cfg.SecretKey == "" {
|
||||
// Fail closed like the Python service: with no key, no external cookie
|
||||
// can verify, so only internal-trust (loopback/compose) requests pass.
|
||||
logger.Warn("SECRET_KEY unset — external (nginx-proxied) requests will all be rejected")
|
||||
}
|
||||
|
||||
// Inventory-service reverse proxy (independent of the DB).
|
||||
if err := srv.initInvProxy(cfg.InventoryURL); err != nil {
|
||||
|
|
@ -88,7 +95,7 @@ func main() {
|
|||
|
||||
httpSrv := &http.Server{
|
||||
Addr: cfg.Addr,
|
||||
Handler: withRequestLogging(mux),
|
||||
Handler: withRequestLogging(srv.authMiddleware(mux)),
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
|
|
@ -117,6 +124,7 @@ type config struct {
|
|||
DatabaseURL string // dereth TimescaleDB DSN (read-only use)
|
||||
InventoryURL string // inventory-service base URL
|
||||
StaticDir string // directory for static assets / openissues.json
|
||||
SecretKey string // session-cookie signing key (must match the Python service)
|
||||
}
|
||||
|
||||
func loadConfig() config {
|
||||
|
|
@ -125,6 +133,7 @@ func loadConfig() config {
|
|||
DatabaseURL: os.Getenv("DATABASE_URL"),
|
||||
InventoryURL: envOr("INVENTORY_SERVICE_URL", "http://inventory-service:8000"),
|
||||
StaticDir: envOr("STATIC_DIR", "static"),
|
||||
SecretKey: os.Getenv("SECRET_KEY"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue