fix(auth): properly handle HTTP scheme and load secrets from pass
- Detect request scheme (HTTP/HTTPS) dynamically for token realm URL instead of hardcoding https:// - CLI now loads token_secret from pass backend instead of using path string
- commit
- b91fac7dae8c43cd08da0f8a3e84c7db1d103591
- author
- brice · · 2026-01-14 05:38 UTC
- parent
- ce7d45e
- signature
- unsigned
2 files · +26 -5
M internal/adapters/in/cli/auth.go +18 -4
| @@ -14,6 +14,7 @@ import ( | |||
| 14 | 14 | "github.com/spf13/viper" | |
| 15 | 15 | "golang.org/x/term" | |
| 16 | 16 | ||
| 17 | + | "gordon/internal/adapters/out/secrets" | |
| 17 | 18 | "gordon/internal/adapters/out/tokenstore" | |
| 18 | 19 | "gordon/internal/app" | |
| 19 | 20 | "gordon/internal/domain" | |
| @@ -345,12 +346,25 @@ func loadAuthConfig(configPath string) (*cliConfig, error) { | |||
| 345 | 346 | cfg.Backend = domain.SecretsBackendUnsafe | |
| 346 | 347 | } | |
| 347 | 348 | ||
| 348 | - | // Load token secret if specified | |
| 349 | + | // Load token secret from secrets backend | |
| 349 | 350 | tokenSecretPath := v.GetString("registry_auth.token_secret") | |
| 350 | 351 | if tokenSecretPath != "" { | |
| 351 | - | // For CLI, we generate a default secret if not available | |
| 352 | - | // In production, the secret should be loaded from the secrets backend | |
| 353 | - | cfg.TokenSecret = []byte(tokenSecretPath) // Use path as seed for now | |
| 352 | + | // Load actual secret from the configured backend | |
| 353 | + | ctx := context.Background() | |
| 354 | + | log := zerowrap.New(zerowrap.Config{Level: "warn"}) | |
| 355 | + | ||
| 356 | + | switch cfg.Backend { | |
| 357 | + | case domain.SecretsBackendPass: | |
| 358 | + | provider := secrets.NewPassProvider(log) | |
| 359 | + | secret, err := provider.GetSecret(ctx, tokenSecretPath) | |
| 360 | + | if err != nil { | |
| 361 | + | return nil, fmt.Errorf("failed to load token secret from pass: %w", err) | |
| 362 | + | } | |
| 363 | + | cfg.TokenSecret = []byte(secret) | |
| 364 | + | default: | |
| 365 | + | // For unsafe backend, use the path as the secret (backwards compatible) | |
| 366 | + | cfg.TokenSecret = []byte(tokenSecretPath) | |
| 367 | + | } | |
| 354 | 368 | } else { | |
| 355 | 369 | // Generate a default secret for CLI token generation | |
| 356 | 370 | cfg.TokenSecret = []byte("gordon-cli-default-secret") | |
| @@ -14,6 +14,7 @@ import ( | |||||
| 14 | "github.com/spf13/viper" | 14 | "github.com/spf13/viper" | ||
| 15 | "golang.org/x/term" | 15 | "golang.org/x/term" | ||
| 16 | 16 | ||||
| 17 | + | "gordon/internal/adapters/out/secrets" | |||
| 17 | "gordon/internal/adapters/out/tokenstore" | 18 | "gordon/internal/adapters/out/tokenstore" | ||
| 18 | "gordon/internal/app" | 19 | "gordon/internal/app" | ||
| 19 | "gordon/internal/domain" | 20 | "gordon/internal/domain" | ||
| @@ -345,12 +346,25 @@ func loadAuthConfig(configPath string) (*cliConfig, error) { | |||||
| 345 | cfg.Backend = domain.SecretsBackendUnsafe | 346 | cfg.Backend = domain.SecretsBackendUnsafe | ||
| 346 | } | 347 | } | ||
| 347 | 348 | ||||
| 348 | - | // Load token secret if specified | 349 | + | // Load token secret from secrets backend |
| 349 | tokenSecretPath := v.GetString("registry_auth.token_secret") | 350 | tokenSecretPath := v.GetString("registry_auth.token_secret") | ||
| 350 | if tokenSecretPath != "" { | 351 | if tokenSecretPath != "" { | ||
| 351 | - | // For CLI, we generate a default secret if not available | 352 | + | // Load actual secret from the configured backend |
| 352 | - | // In production, the secret should be loaded from the secrets backend | 353 | + | ctx := context.Background() |
| 353 | - | cfg.TokenSecret = []byte(tokenSecretPath) // Use path as seed for now | 354 | + | log := zerowrap.New(zerowrap.Config{Level: "warn"}) |
| 355 | + | ||||
| 356 | + | switch cfg.Backend { | |||
| 357 | + | case domain.SecretsBackendPass: | |||
| 358 | + | provider := secrets.NewPassProvider(log) | |||
| 359 | + | secret, err := provider.GetSecret(ctx, tokenSecretPath) | |||
| 360 | + | if err != nil { | |||
| 361 | + | return nil, fmt.Errorf("failed to load token secret from pass: %w", err) | |||
| 362 | + | } | |||
| 363 | + | cfg.TokenSecret = []byte(secret) | |||
| 364 | + | default: | |||
| 365 | + | // For unsafe backend, use the path as the secret (backwards compatible) | |||
| 366 | + | cfg.TokenSecret = []byte(tokenSecretPath) | |||
| 367 | + | } | |||
| 354 | } else { | 368 | } else { | ||
| 355 | // Generate a default secret for CLI token generation | 369 | // Generate a default secret for CLI token generation | ||
| 356 | cfg.TokenSecret = []byte("gordon-cli-default-secret") | 370 | cfg.TokenSecret = []byte("gordon-cli-default-secret") | ||
M internal/adapters/in/http/middleware/auth.go +8 -1
| @@ -278,7 +278,14 @@ func sendUnauthorized(w http.ResponseWriter, authType domain.AuthType, host stri | |||
| 278 | 278 | switch authType { | |
| 279 | 279 | case domain.AuthTypeToken: | |
| 280 | 280 | // For token auth, indicate the token server endpoint | |
| 281 | - | realm := "https://" + host + "/v2/token" | |
| 281 | + | // Detect scheme from request (TLS or X-Forwarded-Proto header) | |
| 282 | + | scheme := "http" | |
| 283 | + | if r.TLS != nil { | |
| 284 | + | scheme = "https" | |
| 285 | + | } else if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" { | |
| 286 | + | scheme = proto | |
| 287 | + | } | |
| 288 | + | realm := scheme + "://" + host + "/v2/token" | |
| 282 | 289 | w.Header().Set("WWW-Authenticate", `Bearer realm="`+realm+`",service="gordon-registry"`) | |
| 283 | 290 | default: | |
| 284 | 291 | w.Header().Set("WWW-Authenticate", `Basic realm="Gordon Registry"`) | |
| @@ -278,7 +278,14 @@ func sendUnauthorized(w http.ResponseWriter, authType domain.AuthType, host stri | |||||
| 278 | switch authType { | 278 | switch authType { | ||
| 279 | case domain.AuthTypeToken: | 279 | case domain.AuthTypeToken: | ||
| 280 | // For token auth, indicate the token server endpoint | 280 | // For token auth, indicate the token server endpoint | ||
| 281 | - | realm := "https://" + host + "/v2/token" | 281 | + | // Detect scheme from request (TLS or X-Forwarded-Proto header) |
| 282 | + | scheme := "http" | |||
| 283 | + | if r.TLS != nil { | |||
| 284 | + | scheme = "https" | |||
| 285 | + | } else if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" { | |||
| 286 | + | scheme = proto | |||
| 287 | + | } | |||
| 288 | + | realm := scheme + "://" + host + "/v2/token" | |||
| 282 | w.Header().Set("WWW-Authenticate", `Bearer realm="`+realm+`",service="gordon-registry"`) | 289 | w.Header().Set("WWW-Authenticate", `Bearer realm="`+realm+`",service="gordon-registry"`) | ||
| 283 | default: | 290 | default: | ||
| 284 | w.Header().Set("WWW-Authenticate", `Basic realm="Gordon Registry"`) | 291 | w.Header().Set("WWW-Authenticate", `Basic realm="Gordon Registry"`) | ||