detached 2b91abc

feat!: require explicit auth credentials when auth.enabled=true

BREAKING: Gordon now requires authentication credentials to be configured
when auth is enabled (which is the default). Server will fail to start
with a helpful error message explaining setup options.

This prevents accidentally running an unauthenticated registry in production.
commit
2b91abcad3da13ea0b201de1a1543258e125d40a
author
brice · · 2026-01-17 15:57 UTC
committer
brice · · 2026-01-17 16:01 UTC
parent
30433bf
signature
unsigned
3 files · +39 -6
M docs/config/auth.md +27 -2
@@ -2,6 +2,33 @@
22
33 Configure authentication for the Gordon registry and Admin API.
44
5+## Quick Start
6+
7+Authentication is **enabled by default** for security. Gordon will not start without authentication configured.
8+
9+**Option 1: Password Authentication (simplest)**
10+```bash
11+# 1. Generate password hash
12+gordon auth password hash
13+# Enter your password, copy the bcrypt hash
14+
15+# 2. Save hash to secrets file
16+mkdir -p ~/.gordon/secrets
17+echo '$2a$10$...' > ~/.gordon/secrets/password_hash
18+
19+# 3. Add to config.toml
20+cat >> ~/.gordon/config.toml << 'EOF'
21+[auth]
22+password_hash = "password_hash"
23+EOF
24+```
25+
26+**Option 2: Disable Authentication (development only)**
27+```toml
28+[auth]
29+enabled = false
30+```
31+
532 ## Configuration
633
734 ```toml
@@ -13,8 +40,6 @@ token_secret = "gordon/auth/token_secret"
1340 token_expiry = "30d"
1441 ```
1542
16-> **Note:** Authentication is enabled by default for security. For local development without auth, explicitly set `enabled = false`.
17-
1843 ## Options
1944
2045 | Option | Type | Default | Description |
@@ -2,6 +2,33 @@
2 2
3 Configure authentication for the Gordon registry and Admin API.3 Configure authentication for the Gordon registry and Admin API.
4 4
5+## Quick Start
6+
7+Authentication is **enabled by default** for security. Gordon will not start without authentication configured.
8+
9+**Option 1: Password Authentication (simplest)**
10+```bash
11+# 1. Generate password hash
12+gordon auth password hash
13+# Enter your password, copy the bcrypt hash
14+
15+# 2. Save hash to secrets file
16+mkdir -p ~/.gordon/secrets
17+echo '$2a$10$...' > ~/.gordon/secrets/password_hash
18+
19+# 3. Add to config.toml
20+cat >> ~/.gordon/config.toml << 'EOF'
21+[auth]
22+password_hash = "password_hash"
23+EOF
24+```
25+
26+**Option 2: Disable Authentication (development only)**
27+```toml
28+[auth]
29+enabled = false
30+```
31+
5 ## Configuration32 ## Configuration
6 33
7 ```toml34 ```toml
@@ -13,8 +40,6 @@ token_secret = "gordon/auth/token_secret"
13 token_expiry = "30d"40 token_expiry = "30d"
14 ```41 ```
15 42
16-> **Note:** Authentication is enabled by default for security. For local development without auth, explicitly set `enabled = false`.
17-
18 ## Options43 ## Options
19 44
20 | Option | Type | Default | Description |45 | Option | Type | Default | Description |
M docs/config/reference.md +4 -4
@@ -15,15 +15,15 @@ gordon_domain = "" # Required: Gordon domain (registry
1515 data_dir = "~/.gordon" # Data directory (varies by install type)
1616
1717 # =============================================================================
18-# AUTHENTICATION
18+# AUTHENTICATION (required - Gordon won't start without credentials configured)
1919 # =============================================================================
2020 [auth]
21-enabled = true # Enable registry authentication
21+enabled = true # Enable registry authentication (default: true)
2222 type = "password" # "password" or "token"
2323 secrets_backend = "unsafe" # "pass", "sops", or "unsafe"
2424 username = "" # Username for password auth
25-password_hash = "" # Path in secrets backend to bcrypt hash
26-token_secret = "" # Path in secrets backend to JWT signing key
25+password_hash = "" # Path in secrets backend to bcrypt hash (REQUIRED for password type)
26+token_secret = "" # Path in secrets backend to JWT signing key (REQUIRED for token type)
2727 token_expiry = "720h" # Token expiry duration (720h = 30 days)
2828
2929 # =============================================================================
@@ -15,15 +15,15 @@ gordon_domain = "" # Required: Gordon domain (registry
15 data_dir = "~/.gordon" # Data directory (varies by install type)15 data_dir = "~/.gordon" # Data directory (varies by install type)
16 16
17 # =============================================================================17 # =============================================================================
18-# AUTHENTICATION18+# AUTHENTICATION (required - Gordon won't start without credentials configured)
19 # =============================================================================19 # =============================================================================
20 [auth]20 [auth]
21-enabled = true # Enable registry authentication21+enabled = true # Enable registry authentication (default: true)
22 type = "password" # "password" or "token"22 type = "password" # "password" or "token"
23 secrets_backend = "unsafe" # "pass", "sops", or "unsafe"23 secrets_backend = "unsafe" # "pass", "sops", or "unsafe"
24 username = "" # Username for password auth24 username = "" # Username for password auth
25-password_hash = "" # Path in secrets backend to bcrypt hash25+password_hash = "" # Path in secrets backend to bcrypt hash (REQUIRED for password type)
26-token_secret = "" # Path in secrets backend to JWT signing key26+token_secret = "" # Path in secrets backend to JWT signing key (REQUIRED for token type)
27 token_expiry = "720h" # Token expiry duration (720h = 30 days)27 token_expiry = "720h" # Token expiry duration (720h = 30 days)
28 28
29 # =============================================================================29 # =============================================================================
M internal/app/run.go +8 -0
@@ -594,6 +594,9 @@ func buildAuthConfig(ctx context.Context, cfg Config, authType domain.AuthType,
594594 if err != nil {
595595 return auth.Config{}, err
596596 }
597+ if hash == "" {
598+ return auth.Config{}, errAuthNotConfigured()
599+ }
597600 authConfig.PasswordHash = hash
598601 case domain.AuthTypeToken:
599602 secret, expiry, err := loadTokenConfig(ctx, cfg, backend, dataDir, log)
@@ -607,6 +610,11 @@ func buildAuthConfig(ctx context.Context, cfg Config, authType domain.AuthType,
607610 return authConfig, nil
608611 }
609612
613+// errAuthNotConfigured returns an error when auth is enabled but credentials are not configured.
614+func errAuthNotConfigured() error {
615+ return fmt.Errorf("auth is enabled by default, configure auth type and secrets backend. See: https://gordon.bnema.dev/docs/config/auth")
616+}
617+
610618 func loadPasswordHash(ctx context.Context, cfg Config, backend domain.SecretsBackend, dataDir string, log zerowrap.Logger) (string, error) {
611619 if cfg.Auth.PasswordHash != "" {
612620 hash, err := loadSecret(ctx, backend, cfg.Auth.PasswordHash, dataDir, log)
@@ -594,6 +594,9 @@ func buildAuthConfig(ctx context.Context, cfg Config, authType domain.AuthType,
594 if err != nil {594 if err != nil {
595 return auth.Config{}, err595 return auth.Config{}, err
596 }596 }
597+ if hash == "" {
598+ return auth.Config{}, errAuthNotConfigured()
599+ }
597 authConfig.PasswordHash = hash600 authConfig.PasswordHash = hash
598 case domain.AuthTypeToken:601 case domain.AuthTypeToken:
599 secret, expiry, err := loadTokenConfig(ctx, cfg, backend, dataDir, log)602 secret, expiry, err := loadTokenConfig(ctx, cfg, backend, dataDir, log)
@@ -607,6 +610,11 @@ func buildAuthConfig(ctx context.Context, cfg Config, authType domain.AuthType,
607 return authConfig, nil610 return authConfig, nil
608 }611 }
609 612
613+// errAuthNotConfigured returns an error when auth is enabled but credentials are not configured.
614+func errAuthNotConfigured() error {
615+ return fmt.Errorf("auth is enabled by default, configure auth type and secrets backend. See: https://gordon.bnema.dev/docs/config/auth")
616+}
617+
610 func loadPasswordHash(ctx context.Context, cfg Config, backend domain.SecretsBackend, dataDir string, log zerowrap.Logger) (string, error) {618 func loadPasswordHash(ctx context.Context, cfg Config, backend domain.SecretsBackend, dataDir string, log zerowrap.Logger) (string, error) {
611 if cfg.Auth.PasswordHash != "" {619 if cfg.Auth.PasswordHash != "" {
612 hash, err := loadSecret(ctx, backend, cfg.Auth.PasswordHash, dataDir, log)620 hash, err := loadSecret(ctx, backend, cfg.Auth.PasswordHash, dataDir, log)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close