fix(app,test): align auth config semantics and stabilize resolver tests
Use cfg.Auth.Enabled when building auth.Config, update fail-closed allowlist log wording, and add regression coverage. Also restore package-level remote flags in resolver tests and document expected 503 fail-closed admin behavior in handler tests.
- commit
- 340fd86d623223e7f013f91bbecd23b1e5edb2b5
- author
- brice · · 2026-02-09 06:43 UTC
- parent
- 9b0b996
- signature
- unsigned
4 files · +42 -2
M internal/adapters/in/cli/controlplane_resolver_test.go +12 -0
| @@ -12,6 +12,12 @@ func TestResolveControlPlane_LocalDeniedWhenAuthEnabled(t *testing.T) { | |||
| 12 | 12 | t.Setenv("XDG_CONFIG_HOME", filepath.Join(tmpDir, "xdg")) | |
| 13 | 13 | t.Setenv("GORDON_REMOTE", "") | |
| 14 | 14 | t.Setenv("GORDON_TOKEN", "") | |
| 15 | + | originalRemoteFlag, originalTokenFlag, originalInsecureTLSFlag := remoteFlag, tokenFlag, insecureTLSFlag | |
| 16 | + | t.Cleanup(func() { | |
| 17 | + | remoteFlag = originalRemoteFlag | |
| 18 | + | tokenFlag = originalTokenFlag | |
| 19 | + | insecureTLSFlag = originalInsecureTLSFlag | |
| 20 | + | }) | |
| 15 | 21 | ||
| 16 | 22 | remoteFlag = "" | |
| 17 | 23 | tokenFlag = "" | |
| @@ -46,6 +52,12 @@ func TestResolveControlPlane_LocalAllowedWhenAuthDisabled(t *testing.T) { | |||
| 46 | 52 | t.Setenv("XDG_CONFIG_HOME", filepath.Join(tmpDir, "xdg")) | |
| 47 | 53 | t.Setenv("GORDON_REMOTE", "") | |
| 48 | 54 | t.Setenv("GORDON_TOKEN", "") | |
| 55 | + | originalRemoteFlag, originalTokenFlag, originalInsecureTLSFlag := remoteFlag, tokenFlag, insecureTLSFlag | |
| 56 | + | t.Cleanup(func() { | |
| 57 | + | remoteFlag = originalRemoteFlag | |
| 58 | + | tokenFlag = originalTokenFlag | |
| 59 | + | insecureTLSFlag = originalInsecureTLSFlag | |
| 60 | + | }) | |
| 49 | 61 | ||
| 50 | 62 | remoteFlag = "" | |
| 51 | 63 | tokenFlag = "" | |
| @@ -12,6 +12,12 @@ func TestResolveControlPlane_LocalDeniedWhenAuthEnabled(t *testing.T) { | |||||
| 12 | t.Setenv("XDG_CONFIG_HOME", filepath.Join(tmpDir, "xdg")) | 12 | t.Setenv("XDG_CONFIG_HOME", filepath.Join(tmpDir, "xdg")) | ||
| 13 | t.Setenv("GORDON_REMOTE", "") | 13 | t.Setenv("GORDON_REMOTE", "") | ||
| 14 | t.Setenv("GORDON_TOKEN", "") | 14 | t.Setenv("GORDON_TOKEN", "") | ||
| 15 | + | originalRemoteFlag, originalTokenFlag, originalInsecureTLSFlag := remoteFlag, tokenFlag, insecureTLSFlag | |||
| 16 | + | t.Cleanup(func() { | |||
| 17 | + | remoteFlag = originalRemoteFlag | |||
| 18 | + | tokenFlag = originalTokenFlag | |||
| 19 | + | insecureTLSFlag = originalInsecureTLSFlag | |||
| 20 | + | }) | |||
| 15 | 21 | ||||
| 16 | remoteFlag = "" | 22 | remoteFlag = "" | ||
| 17 | tokenFlag = "" | 23 | tokenFlag = "" | ||
| @@ -46,6 +52,12 @@ func TestResolveControlPlane_LocalAllowedWhenAuthDisabled(t *testing.T) { | |||||
| 46 | t.Setenv("XDG_CONFIG_HOME", filepath.Join(tmpDir, "xdg")) | 52 | t.Setenv("XDG_CONFIG_HOME", filepath.Join(tmpDir, "xdg")) | ||
| 47 | t.Setenv("GORDON_REMOTE", "") | 53 | t.Setenv("GORDON_REMOTE", "") | ||
| 48 | t.Setenv("GORDON_TOKEN", "") | 54 | t.Setenv("GORDON_TOKEN", "") | ||
| 55 | + | originalRemoteFlag, originalTokenFlag, originalInsecureTLSFlag := remoteFlag, tokenFlag, insecureTLSFlag | |||
| 56 | + | t.Cleanup(func() { | |||
| 57 | + | remoteFlag = originalRemoteFlag | |||
| 58 | + | tokenFlag = originalTokenFlag | |||
| 59 | + | insecureTLSFlag = originalInsecureTLSFlag | |||
| 60 | + | }) | |||
| 49 | 61 | ||||
| 50 | remoteFlag = "" | 62 | remoteFlag = "" | ||
| 51 | tokenFlag = "" | 63 | tokenFlag = "" | ||
M internal/app/run.go +2 -2
| @@ -876,7 +876,7 @@ func createTokenStore(backend domain.SecretsBackend, dataDir string, log zerowra | |||
| 876 | 876 | ||
| 877 | 877 | func buildAuthConfig(ctx context.Context, cfg Config, authType domain.AuthType, backend domain.SecretsBackend, dataDir string, log zerowrap.Logger) (auth.Config, error) { | |
| 878 | 878 | authConfig := auth.Config{ | |
| 879 | - | Enabled: true, | |
| 879 | + | Enabled: cfg.Auth.Enabled, | |
| 880 | 880 | AuthType: authType, | |
| 881 | 881 | Username: cfg.Auth.Username, | |
| 882 | 882 | } | |
| @@ -1380,7 +1380,7 @@ func buildRegistryCIDRAllowlistMiddleware(cfg Config, trustedNets []*net.IPNet, | |||
| 1380 | 1380 | if len(allowedNets) == 0 { | |
| 1381 | 1381 | log.Error(). | |
| 1382 | 1382 | Strs("registry_allowed_ips", cfg.Server.RegistryAllowedIPs). | |
| 1383 | - | Msg("registry_allowed_ips is set but no valid entries were parsed; registry will allow all traffic") | |
| 1383 | + | Msg("registry_allowed_ips is set but no valid entries were parsed; registry will deny all traffic (fail-closed)") | |
| 1384 | 1384 | return func(next http.Handler) http.Handler { | |
| 1385 | 1385 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| 1386 | 1386 | log.Warn(). | |
| @@ -876,7 +876,7 @@ func createTokenStore(backend domain.SecretsBackend, dataDir string, log zerowra | |||||
| 876 | 876 | ||||
| 877 | func buildAuthConfig(ctx context.Context, cfg Config, authType domain.AuthType, backend domain.SecretsBackend, dataDir string, log zerowrap.Logger) (auth.Config, error) { | 877 | func buildAuthConfig(ctx context.Context, cfg Config, authType domain.AuthType, backend domain.SecretsBackend, dataDir string, log zerowrap.Logger) (auth.Config, error) { | ||
| 878 | authConfig := auth.Config{ | 878 | authConfig := auth.Config{ | ||
| 879 | - | Enabled: true, | 879 | + | Enabled: cfg.Auth.Enabled, |
| 880 | AuthType: authType, | 880 | AuthType: authType, | ||
| 881 | Username: cfg.Auth.Username, | 881 | Username: cfg.Auth.Username, | ||
| 882 | } | 882 | } | ||
| @@ -1380,7 +1380,7 @@ func buildRegistryCIDRAllowlistMiddleware(cfg Config, trustedNets []*net.IPNet, | |||||
| 1380 | if len(allowedNets) == 0 { | 1380 | if len(allowedNets) == 0 { | ||
| 1381 | log.Error(). | 1381 | log.Error(). | ||
| 1382 | Strs("registry_allowed_ips", cfg.Server.RegistryAllowedIPs). | 1382 | Strs("registry_allowed_ips", cfg.Server.RegistryAllowedIPs). | ||
| 1383 | - | Msg("registry_allowed_ips is set but no valid entries were parsed; registry will allow all traffic") | 1383 | + | Msg("registry_allowed_ips is set but no valid entries were parsed; registry will deny all traffic (fail-closed)") |
| 1384 | return func(next http.Handler) http.Handler { | 1384 | return func(next http.Handler) http.Handler { | ||
| 1385 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 1385 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| 1386 | log.Warn(). | 1386 | log.Warn(). | ||
A internal/app/run_auth_config_test.go +25 -0
| @@ -0,0 +1,25 @@ | |||
| 1 | + | package app | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "context" | |
| 5 | + | "testing" | |
| 6 | + | ||
| 7 | + | "github.com/bnema/gordon/internal/domain" | |
| 8 | + | "github.com/bnema/zerowrap" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | func TestBuildAuthConfig_UsesConfigEnabledFlag(t *testing.T) { | |
| 12 | + | t.Setenv(TokenSecretEnvVar, "test-token-secret") | |
| 13 | + | ||
| 14 | + | cfg := Config{} | |
| 15 | + | cfg.Auth.Enabled = false | |
| 16 | + | cfg.Auth.Type = "token" | |
| 17 | + | ||
| 18 | + | authCfg, err := buildAuthConfig(context.Background(), cfg, domain.AuthTypeToken, domain.SecretsBackendUnsafe, t.TempDir(), zerowrap.Default()) | |
| 19 | + | if err != nil { | |
| 20 | + | t.Fatalf("expected no error, got %v", err) | |
| 21 | + | } | |
| 22 | + | if authCfg.Enabled { | |
| 23 | + | t.Fatalf("expected auth config enabled=false, got true") | |
| 24 | + | } | |
| 25 | + | } | |
| @@ -0,0 +1,25 @@ | |||||
| 1 | + | package app | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "context" | |||
| 5 | + | "testing" | |||
| 6 | + | ||||
| 7 | + | "github.com/bnema/gordon/internal/domain" | |||
| 8 | + | "github.com/bnema/zerowrap" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | func TestBuildAuthConfig_UsesConfigEnabledFlag(t *testing.T) { | |||
| 12 | + | t.Setenv(TokenSecretEnvVar, "test-token-secret") | |||
| 13 | + | ||||
| 14 | + | cfg := Config{} | |||
| 15 | + | cfg.Auth.Enabled = false | |||
| 16 | + | cfg.Auth.Type = "token" | |||
| 17 | + | ||||
| 18 | + | authCfg, err := buildAuthConfig(context.Background(), cfg, domain.AuthTypeToken, domain.SecretsBackendUnsafe, t.TempDir(), zerowrap.Default()) | |||
| 19 | + | if err != nil { | |||
| 20 | + | t.Fatalf("expected no error, got %v", err) | |||
| 21 | + | } | |||
| 22 | + | if authCfg.Enabled { | |||
| 23 | + | t.Fatalf("expected auth config enabled=false, got true") | |||
| 24 | + | } | |||
| 25 | + | } | |||
M internal/app/run_http_handlers_test.go +3 -0
| @@ -76,6 +76,9 @@ func TestCreateHTTPHandlers_AuthEnabled_ExposesAdminOnProxyForRegistryHost(t *te | |||
| 76 | 76 | ||
| 77 | 77 | svc := &services{adminHandler: &adminhttp.Handler{}} | |
| 78 | 78 | _, proxyHandler := createHTTPHandlers(svc, cfg, zerowrap.Default()) | |
| 79 | + | // services uses an adminHandler stub with authSvc left nil. In createHTTPHandlers, | |
| 80 | + | // registerAdminRoutes installs fail-closed middleware for that case, so admin | |
| 81 | + | // requests on the allowed host return 503 ServiceUnavailable. | |
| 79 | 82 | ||
| 80 | 83 | req := httptest.NewRequest(http.MethodGet, "/admin/status", nil) | |
| 81 | 84 | req.Host = "gordon.local" | |
| @@ -76,6 +76,9 @@ func TestCreateHTTPHandlers_AuthEnabled_ExposesAdminOnProxyForRegistryHost(t *te | |||||
| 76 | 76 | ||||
| 77 | svc := &services{adminHandler: &adminhttp.Handler{}} | 77 | svc := &services{adminHandler: &adminhttp.Handler{}} | ||
| 78 | _, proxyHandler := createHTTPHandlers(svc, cfg, zerowrap.Default()) | 78 | _, proxyHandler := createHTTPHandlers(svc, cfg, zerowrap.Default()) | ||
| 79 | + | // services uses an adminHandler stub with authSvc left nil. In createHTTPHandlers, | |||
| 80 | + | // registerAdminRoutes installs fail-closed middleware for that case, so admin | |||
| 81 | + | // requests on the allowed host return 503 ServiceUnavailable. | |||
| 79 | 82 | ||||
| 80 | req := httptest.NewRequest(http.MethodGet, "/admin/status", nil) | 83 | req := httptest.NewRequest(http.MethodGet, "/admin/status", nil) | ||
| 81 | req.Host = "gordon.local" | 84 | req.Host = "gordon.local" | ||