docs(auth): simplify authentication setup
- commit
- 18f31a95de86150ec8a07791afdc1b56f288a2df
- author
- bnema · · 2026-01-30 20:58 UTC
- parent
- 5498bc4
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +92 -222
M docs/config/auth.md +42 -189
| @@ -2,47 +2,33 @@ | |||
| 2 | 2 | ||
| 3 | 3 | Configure authentication for the Gordon registry and Admin API. | |
| 4 | 4 | ||
| 5 | - | ## Quick Start | |
| 5 | + | Authentication is enabled by default. Gordon will not start without a valid `token_secret` (or `GORDON_AUTH_TOKEN_SECRET`). | |
| 6 | 6 | ||
| 7 | - | Authentication is **enabled by default** for security. Gordon will not start without authentication configured. | |
| 7 | + | ## Pick a Mode | |
| 8 | 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 | |
| 9 | + | ### Token-only (recommended for CI/CD) | |
| 14 | 10 | ||
| 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 | 11 | ```toml | |
| 28 | 12 | [auth] | |
| 29 | - | enabled = false | |
| 13 | + | enabled = true | |
| 14 | + | secrets_backend = "pass" | |
| 15 | + | token_secret = "gordon/auth/token_secret" | |
| 16 | + | token_expiry = "30d" | |
| 30 | 17 | ``` | |
| 31 | 18 | ||
| 32 | - | ## Configuration | |
| 19 | + | ### Password + Token (interactive + CI/CD) | |
| 33 | 20 | ||
| 34 | 21 | ```toml | |
| 35 | 22 | [auth] | |
| 36 | 23 | enabled = true | |
| 37 | 24 | secrets_backend = "pass" | |
| 38 | - | token_secret = "gordon/auth/token_secret" | |
| 39 | - | token_expiry = "30d" | |
| 40 | - | ||
| 41 | - | # Optional: enable password authentication | |
| 42 | 25 | username = "deploy" | |
| 43 | 26 | password_hash = "gordon/auth/password_hash" | |
| 27 | + | token_secret = "gordon/auth/token_secret" | |
| 44 | 28 | ``` | |
| 45 | 29 | ||
| 30 | + | Token auth always works. Adding `username` + `password_hash` enables interactive login. | |
| 31 | + | ||
| 46 | 32 | ## Options | |
| 47 | 33 | ||
| 48 | 34 | | Option | Type | Default | Description | | |
| @@ -54,35 +40,9 @@ password_hash = "gordon/auth/password_hash" | |||
| 54 | 40 | | `username` | string | - | Username for password auth (optional) | | |
| 55 | 41 | | `password_hash` | string | - | Path to bcrypt hash in secrets backend (optional) | | |
| 56 | 42 | ||
| 57 | - | ## Authentication Modes | |
| 58 | - | ||
| 59 | - | Gordon infers the authentication mode from your configuration: | |
| 60 | - | ||
| 61 | - | | Config | Mode | Description | | |
| 62 | - | |--------|------|-------------| | |
| 63 | - | | Only `token_secret` | Token-only | JWT tokens for all access | | |
| 64 | - | | `token_secret` + `username` + `password_hash` | Password + Token | Password for interactive login, tokens for CI/CD | | |
| 65 | - | ||
| 66 | - | Tokens always work regardless of mode. The difference is whether password authentication is available. | |
| 67 | - | ||
| 68 | - | ## Environment Variables | |
| 69 | - | ||
| 70 | - | Gordon supports environment variables for sensitive configuration: | |
| 71 | - | ||
| 72 | - | | Variable | Description | | |
| 73 | - | |----------|-------------| | |
| 74 | - | | `GORDON_AUTH_TOKEN_SECRET` | JWT signing secret (takes priority over config file) | | |
| 75 | - | ||
| 76 | - | Using environment variables is recommended for production as it avoids storing secrets on disk: | |
| 77 | - | ||
| 78 | - | ```bash | |
| 79 | - | export GORDON_AUTH_TOKEN_SECRET="your-32-character-secret-here" | |
| 80 | - | gordon serve | |
| 81 | - | ``` | |
| 82 | - | ||
| 83 | - | ## Secrets Backend | |
| 43 | + | ## Secrets Backends | |
| 84 | 44 | ||
| 85 | - | The `secrets_backend` option determines how Gordon retrieves sensitive values like `token_secret` and `password_hash`: | |
| 45 | + | `token_secret` and `password_hash` are read through the configured backend: | |
| 86 | 46 | ||
| 87 | 47 | | Backend | Description | | |
| 88 | 48 | |---------|-------------| | |
| @@ -90,23 +50,21 @@ The `secrets_backend` option determines how Gordon retrieves sensitive values li | |||
| 90 | 50 | | `sops` | Mozilla SOPS encrypted files | | |
| 91 | 51 | | `unsafe` | Plain text files (development only) | | |
| 92 | 52 | ||
| 93 | - | See [Secret Providers](./secrets.md) for detailed configuration of each backend. | |
| 94 | - | ||
| 95 | - | ## Authentication Types | |
| 53 | + | See [Secret Providers](./secrets.md) for setup details. | |
| 96 | 54 | ||
| 97 | - | ### Token-Only Mode | |
| 55 | + | ## Environment Variable | |
| 98 | 56 | ||
| 99 | - | JWT-based authentication, ideal for CI/CD pipelines when you don't need interactive login: | |
| 57 | + | `GORDON_AUTH_TOKEN_SECRET` overrides `token_secret` in the config. | |
| 100 | 58 | ||
| 101 | - | ```toml | |
| 102 | - | [auth] | |
| 103 | - | enabled = true | |
| 104 | - | secrets_backend = "pass" | |
| 105 | - | token_secret = "gordon/auth/token_secret" | |
| 106 | - | token_expiry = "30d" | |
| 59 | + | ```bash | |
| 60 | + | export GORDON_AUTH_TOKEN_SECRET="your-32-character-secret-here" | |
| 61 | + | gordon serve | |
| 107 | 62 | ``` | |
| 108 | 63 | ||
| 109 | - | **Setup:** | |
| 64 | + | ## Setup | |
| 65 | + | ||
| 66 | + | ### Token-only setup | |
| 67 | + | ||
| 110 | 68 | ```bash | |
| 111 | 69 | # Create token secret (random 32+ characters) | |
| 112 | 70 | pass insert gordon/auth/token_secret | |
| @@ -115,83 +73,43 @@ pass insert gordon/auth/token_secret | |||
| 115 | 73 | gordon auth token generate --subject ci-bot --scopes push,pull --expiry 0 | |
| 116 | 74 | ``` | |
| 117 | 75 | ||
| 118 | - | **Usage:** | |
| 119 | 76 | ```bash | |
| 120 | 77 | # Docker login with token | |
| 121 | 78 | docker login -u ci-bot -p <token> registry.mydomain.com | |
| 122 | 79 | ``` | |
| 123 | 80 | ||
| 124 | - | **Token Management:** | |
| 125 | - | ```bash | |
| 126 | - | # List all tokens | |
| 127 | - | gordon auth token list | |
| 128 | - | ||
| 129 | - | # Revoke a specific token | |
| 130 | - | gordon auth token revoke <token-id> | |
| 131 | - | ||
| 132 | - | # Revoke all tokens | |
| 133 | - | gordon auth token revoke --all | |
| 134 | - | ``` | |
| 135 | - | ||
| 136 | - | ### Password + Token Mode | |
| 81 | + | ### Password + token setup | |
| 137 | 82 | ||
| 138 | - | Password authentication for interactive login, plus tokens for CI/CD: | |
| 139 | - | ||
| 140 | - | ```toml | |
| 141 | - | [auth] | |
| 142 | - | enabled = true | |
| 143 | - | secrets_backend = "pass" | |
| 144 | - | username = "deploy" | |
| 145 | - | password_hash = "gordon/auth/password_hash" | |
| 146 | - | token_secret = "gordon/auth/token_secret" | |
| 147 | - | ``` | |
| 148 | - | ||
| 149 | - | When `username` and `password_hash` are configured, both password and token authentication work. | |
| 150 | - | ||
| 151 | - | **Setup:** | |
| 152 | 83 | ```bash | |
| 153 | 84 | # Generate bcrypt hash | |
| 154 | 85 | gordon auth password hash | |
| 155 | - | # Enter password when prompted | |
| 156 | 86 | ||
| 157 | - | # Store hash in secrets backend | |
| 87 | + | # Store hash and token secret | |
| 158 | 88 | pass insert gordon/auth/password_hash | |
| 159 | - | # Paste the bcrypt hash | |
| 160 | - | ||
| 161 | - | # Create token secret (for CI/CD token generation) | |
| 162 | 89 | pass insert gordon/auth/token_secret | |
| 163 | - | # Enter a random 32+ character string | |
| 164 | 90 | ``` | |
| 165 | 91 | ||
| 166 | - | **Usage:** | |
| 167 | 92 | ```bash | |
| 168 | 93 | # Docker registry access | |
| 169 | 94 | docker login -u deploy -p <password> registry.mydomain.com | |
| 170 | 95 | ||
| 171 | - | # Remote CLI authentication | |
| 96 | + | # Remote CLI login (stores a token for you) | |
| 172 | 97 | gordon auth login --remote prod | |
| 173 | - | # Enter username and password when prompted | |
| 174 | 98 | ``` | |
| 175 | 99 | ||
| 176 | - | **Remote CLI Authentication:** | |
| 100 | + | ## Remote CLI Tokens | |
| 177 | 101 | ||
| 178 | - | With password authentication enabled, you can use `gordon auth login` to obtain a token for remote CLI operations: | |
| 102 | + | If you already have a token (token-only servers), store it directly: | |
| 179 | 103 | ||
| 180 | 104 | ```bash | |
| 181 | - | gordon auth login --remote prod | |
| 182 | - | # Username: deploy | |
| 183 | - | # Password: ******** | |
| 184 | - | # ✓ Authentication successful! | |
| 185 | - | # Token stored for remote 'prod' | |
| 105 | + | gordon auth login --token <token> | |
| 106 | + | # or | |
| 107 | + | gordon remotes set-token prod <token> | |
| 186 | 108 | ``` | |
| 187 | 109 | ||
| 188 | - | The returned token is stored automatically and used for subsequent CLI commands. | |
| 189 | - | ||
| 190 | 110 | ## Token Scopes | |
| 191 | 111 | ||
| 192 | - | Tokens can have different permission levels: | |
| 193 | - | ||
| 194 | - | ### Registry Scopes | |
| 112 | + | Registry scopes: | |
| 195 | 113 | ||
| 196 | 114 | | Scope | Permission | | |
| 197 | 115 | |-------|------------| | |
| @@ -199,9 +117,7 @@ Tokens can have different permission levels: | |||
| 199 | 117 | | `pull` | Pull images from registry | | |
| 200 | 118 | | `push,pull` | Both push and pull (default) | | |
| 201 | 119 | ||
| 202 | - | ### Admin Scopes | |
| 203 | - | ||
| 204 | - | For remote CLI access via the Admin API: | |
| 120 | + | Admin scopes (for remote CLI): | |
| 205 | 121 | ||
| 206 | 122 | | Scope | Permission | | |
| 207 | 123 | |-------|------------| | |
| @@ -214,108 +130,45 @@ For remote CLI access via the Admin API: | |||
| 214 | 130 | | `admin:secrets:read` | List secret keys | | |
| 215 | 131 | | `admin:secrets:write` | Set/delete secrets | | |
| 216 | 132 | ||
| 217 | - | **Examples:** | |
| 133 | + | Examples: | |
| 134 | + | ||
| 218 | 135 | ```bash | |
| 219 | - | # Pull-only token (for read-only access) | |
| 220 | 136 | gordon auth token generate --subject reader --scopes pull --expiry 30d | |
| 221 | - | ||
| 222 | - | # Push-only token (for CI builds) | |
| 223 | 137 | gordon auth token generate --subject builder --scopes push --expiry 0 | |
| 224 | - | ||
| 225 | - | # Full admin access (for remote CLI) | |
| 226 | 138 | gordon auth token generate --subject admin --scopes "push,pull,admin:*:*" --expiry 0 | |
| 227 | 139 | ``` | |
| 228 | 140 | ||
| 229 | 141 | ## Token Expiry | |
| 230 | 142 | ||
| 231 | - | Control how long tokens remain valid. Supports human-friendly units: | |
| 232 | - | ||
| 233 | - | | Unit | Description | | |
| 234 | - | |------|-------------| | |
| 235 | - | | `d` | days (24 hours) | | |
| 236 | - | | `w` | weeks (7 days) | | |
| 237 | - | | `M` | months (30 days) | | |
| 238 | - | | `y` | years (365 days) | | |
| 239 | - | ||
| 240 | - | Compound durations work too: `1y6M`, `2w3d` | |
| 143 | + | Durations support `d`, `w`, `M`, `y` and combinations like `1y6M` or `2w3d`. | |
| 241 | 144 | ||
| 242 | 145 | ```bash | |
| 243 | - | # Never expires (for CI/CD) | |
| 244 | 146 | gordon auth token generate --subject ci --expiry 0 | |
| 245 | - | ||
| 246 | - | # Expires in 1 year | |
| 247 | 147 | gordon auth token generate --subject deploy --expiry 1y | |
| 248 | - | ||
| 249 | - | # Expires in 30 days | |
| 250 | 148 | gordon auth token generate --subject temp --expiry 30d | |
| 251 | 149 | ``` | |
| 252 | 150 | ||
| 253 | - | ## Instance-Specific Tokens | |
| 254 | - | ||
| 255 | - | Tokens are signed with the `token_secret`, which is unique to each Gordon instance: | |
| 151 | + | ## Instance-specific Tokens | |
| 256 | 152 | ||
| 153 | + | - Tokens are signed with `token_secret` | |
| 257 | 154 | - Tokens from one Gordon instance won't work on another | |
| 258 | - | - Changing the `token_secret` invalidates all existing tokens | |
| 259 | - | - Each environment (dev, staging, prod) should have different secrets | |
| 260 | - | ||
| 261 | - | ## Development Setup | |
| 155 | + | - Changing `token_secret` invalidates all existing tokens | |
| 262 | 156 | ||
| 263 | - | For local development without authentication: | |
| 157 | + | ## Disable Auth (development only) | |
| 264 | 158 | ||
| 265 | 159 | ```toml | |
| 266 | 160 | [auth] | |
| 267 | 161 | enabled = false | |
| 268 | 162 | ``` | |
| 269 | 163 | ||
| 270 | - | ## Examples | |
| 271 | - | ||
| 272 | - | ### CI/CD Setup (Token-Only) | |
| 273 | - | ||
| 274 | - | ```toml | |
| 275 | - | [auth] | |
| 276 | - | enabled = true | |
| 277 | - | secrets_backend = "pass" | |
| 278 | - | token_secret = "gordon/auth/token_secret" | |
| 279 | - | ``` | |
| 280 | - | ||
| 281 | - | ```bash | |
| 282 | - | # Generate CI token | |
| 283 | - | gordon auth token generate --subject github-actions --scopes push,pull --expiry 0 | |
| 284 | - | ``` | |
| 285 | - | ||
| 286 | - | In GitHub Actions: | |
| 287 | - | ```yaml | |
| 288 | - | - name: Login to Gordon Registry | |
| 289 | - | run: docker login -u github-actions -p ${{ secrets.GORDON_TOKEN }} ${{ secrets.GORDON_REGISTRY }} | |
| 290 | - | ``` | |
| 291 | - | ||
| 292 | - | ### Production with Password + Token | |
| 293 | - | ||
| 294 | - | ```toml | |
| 295 | - | [auth] | |
| 296 | - | enabled = true | |
| 297 | - | secrets_backend = "pass" | |
| 298 | - | username = "deploy" | |
| 299 | - | password_hash = "gordon/auth/password_hash" | |
| 300 | - | token_secret = "gordon/auth/token_secret" | |
| 301 | - | ``` | |
| 302 | - | ||
| 303 | - | ### Enterprise with SOPS | |
| 304 | - | ||
| 305 | - | ```toml | |
| 306 | - | [auth] | |
| 307 | - | enabled = true | |
| 308 | - | secrets_backend = "sops" | |
| 309 | - | token_secret = "gordon/auth/token_secret" | |
| 310 | - | ``` | |
| 311 | - | ||
| 312 | 164 | ## Internal Registry Auth | |
| 313 | 165 | ||
| 314 | - | Gordon generates internal credentials automatically when auth is enabled. These are used for loopback pulls when deploying containers and are regenerated on each restart. | |
| 166 | + | Gordon generates internal credentials when auth is enabled. These are used for loopback pulls during deploys and are regenerated on each restart. | |
| 315 | 167 | ||
| 316 | 168 | Gordon also generates a separate service token for its own registry-domain pulls. This token is managed internally and is not exposed via configuration or CLI. | |
| 317 | 169 | ||
| 318 | 170 | To view internal credentials (for troubleshooting): | |
| 171 | + | ||
| 319 | 172 | ```bash | |
| 320 | 173 | gordon auth internal | |
| 321 | 174 | ``` | |
| @@ -2,47 +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 | 5 | + | Authentication is enabled by default. Gordon will not start without a valid `token_secret` (or `GORDON_AUTH_TOKEN_SECRET`). |
| 6 | 6 | ||||
| 7 | - | Authentication is **enabled by default** for security. Gordon will not start without authentication configured. | 7 | + | ## Pick a Mode |
| 8 | 8 | ||||
| 9 | - | **Option 1: Password Authentication (simplest)** | 9 | + | ### Token-only (recommended for CI/CD) |
| 10 | - | ```bash | |||
| 11 | - | # 1. Generate password hash | |||
| 12 | - | gordon auth password hash | |||
| 13 | - | # Enter your password, copy the bcrypt hash | |||
| 14 | 10 | ||||
| 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 | 11 | ```toml | ||
| 28 | [auth] | 12 | [auth] | ||
| 29 | - | enabled = false | 13 | + | enabled = true |
| 14 | + | secrets_backend = "pass" | |||
| 15 | + | token_secret = "gordon/auth/token_secret" | |||
| 16 | + | token_expiry = "30d" | |||
| 30 | ``` | 17 | ``` | ||
| 31 | 18 | ||||
| 32 | - | ## Configuration | 19 | + | ### Password + Token (interactive + CI/CD) |
| 33 | 20 | ||||
| 34 | ```toml | 21 | ```toml | ||
| 35 | [auth] | 22 | [auth] | ||
| 36 | enabled = true | 23 | enabled = true | ||
| 37 | secrets_backend = "pass" | 24 | secrets_backend = "pass" | ||
| 38 | - | token_secret = "gordon/auth/token_secret" | |||
| 39 | - | token_expiry = "30d" | |||
| 40 | - | ||||
| 41 | - | # Optional: enable password authentication | |||
| 42 | username = "deploy" | 25 | username = "deploy" | ||
| 43 | password_hash = "gordon/auth/password_hash" | 26 | password_hash = "gordon/auth/password_hash" | ||
| 27 | + | token_secret = "gordon/auth/token_secret" | |||
| 44 | ``` | 28 | ``` | ||
| 45 | 29 | ||||
| 30 | + | Token auth always works. Adding `username` + `password_hash` enables interactive login. | |||
| 31 | + | ||||
| 46 | ## Options | 32 | ## Options | ||
| 47 | 33 | ||||
| 48 | | Option | Type | Default | Description | | 34 | | Option | Type | Default | Description | | ||
| @@ -54,35 +40,9 @@ password_hash = "gordon/auth/password_hash" | |||||
| 54 | | `username` | string | - | Username for password auth (optional) | | 40 | | `username` | string | - | Username for password auth (optional) | | ||
| 55 | | `password_hash` | string | - | Path to bcrypt hash in secrets backend (optional) | | 41 | | `password_hash` | string | - | Path to bcrypt hash in secrets backend (optional) | | ||
| 56 | 42 | ||||
| 57 | - | ## Authentication Modes | 43 | + | ## Secrets Backends |
| 58 | - | ||||
| 59 | - | Gordon infers the authentication mode from your configuration: | |||
| 60 | - | ||||
| 61 | - | | Config | Mode | Description | | |||
| 62 | - | |--------|------|-------------| | |||
| 63 | - | | Only `token_secret` | Token-only | JWT tokens for all access | | |||
| 64 | - | | `token_secret` + `username` + `password_hash` | Password + Token | Password for interactive login, tokens for CI/CD | | |||
| 65 | - | ||||
| 66 | - | Tokens always work regardless of mode. The difference is whether password authentication is available. | |||
| 67 | - | ||||
| 68 | - | ## Environment Variables | |||
| 69 | - | ||||
| 70 | - | Gordon supports environment variables for sensitive configuration: | |||
| 71 | - | ||||
| 72 | - | | Variable | Description | | |||
| 73 | - | |----------|-------------| | |||
| 74 | - | | `GORDON_AUTH_TOKEN_SECRET` | JWT signing secret (takes priority over config file) | | |||
| 75 | - | ||||
| 76 | - | Using environment variables is recommended for production as it avoids storing secrets on disk: | |||
| 77 | - | ||||
| 78 | - | ```bash | |||
| 79 | - | export GORDON_AUTH_TOKEN_SECRET="your-32-character-secret-here" | |||
| 80 | - | gordon serve | |||
| 81 | - | ``` | |||
| 82 | - | ||||
| 83 | - | ## Secrets Backend | |||
| 84 | 44 | ||||
| 85 | - | The `secrets_backend` option determines how Gordon retrieves sensitive values like `token_secret` and `password_hash`: | 45 | + | `token_secret` and `password_hash` are read through the configured backend: |
| 86 | 46 | ||||
| 87 | | Backend | Description | | 47 | | Backend | Description | | ||
| 88 | |---------|-------------| | 48 | |---------|-------------| | ||
| @@ -90,23 +50,21 @@ The `secrets_backend` option determines how Gordon retrieves sensitive values li | |||||
| 90 | | `sops` | Mozilla SOPS encrypted files | | 50 | | `sops` | Mozilla SOPS encrypted files | | ||
| 91 | | `unsafe` | Plain text files (development only) | | 51 | | `unsafe` | Plain text files (development only) | | ||
| 92 | 52 | ||||
| 93 | - | See [Secret Providers](./secrets.md) for detailed configuration of each backend. | 53 | + | See [Secret Providers](./secrets.md) for setup details. |
| 94 | - | ||||
| 95 | - | ## Authentication Types | |||
| 96 | 54 | ||||
| 97 | - | ### Token-Only Mode | 55 | + | ## Environment Variable |
| 98 | 56 | ||||
| 99 | - | JWT-based authentication, ideal for CI/CD pipelines when you don't need interactive login: | 57 | + | `GORDON_AUTH_TOKEN_SECRET` overrides `token_secret` in the config. |
| 100 | 58 | ||||
| 101 | - | ```toml | 59 | + | ```bash |
| 102 | - | [auth] | 60 | + | export GORDON_AUTH_TOKEN_SECRET="your-32-character-secret-here" |
| 103 | - | enabled = true | 61 | + | gordon serve |
| 104 | - | secrets_backend = "pass" | |||
| 105 | - | token_secret = "gordon/auth/token_secret" | |||
| 106 | - | token_expiry = "30d" | |||
| 107 | ``` | 62 | ``` | ||
| 108 | 63 | ||||
| 109 | - | **Setup:** | 64 | + | ## Setup |
| 65 | + | ||||
| 66 | + | ### Token-only setup | |||
| 67 | + | ||||
| 110 | ```bash | 68 | ```bash | ||
| 111 | # Create token secret (random 32+ characters) | 69 | # Create token secret (random 32+ characters) | ||
| 112 | pass insert gordon/auth/token_secret | 70 | pass insert gordon/auth/token_secret | ||
| @@ -115,83 +73,43 @@ pass insert gordon/auth/token_secret | |||||
| 115 | gordon auth token generate --subject ci-bot --scopes push,pull --expiry 0 | 73 | gordon auth token generate --subject ci-bot --scopes push,pull --expiry 0 | ||
| 116 | ``` | 74 | ``` | ||
| 117 | 75 | ||||
| 118 | - | **Usage:** | |||
| 119 | ```bash | 76 | ```bash | ||
| 120 | # Docker login with token | 77 | # Docker login with token | ||
| 121 | docker login -u ci-bot -p <token> registry.mydomain.com | 78 | docker login -u ci-bot -p <token> registry.mydomain.com | ||
| 122 | ``` | 79 | ``` | ||
| 123 | 80 | ||||
| 124 | - | **Token Management:** | 81 | + | ### Password + token setup |
| 125 | - | ```bash | |||
| 126 | - | # List all tokens | |||
| 127 | - | gordon auth token list | |||
| 128 | - | ||||
| 129 | - | # Revoke a specific token | |||
| 130 | - | gordon auth token revoke <token-id> | |||
| 131 | - | ||||
| 132 | - | # Revoke all tokens | |||
| 133 | - | gordon auth token revoke --all | |||
| 134 | - | ``` | |||
| 135 | - | ||||
| 136 | - | ### Password + Token Mode | |||
| 137 | 82 | ||||
| 138 | - | Password authentication for interactive login, plus tokens for CI/CD: | |||
| 139 | - | ||||
| 140 | - | ```toml | |||
| 141 | - | [auth] | |||
| 142 | - | enabled = true | |||
| 143 | - | secrets_backend = "pass" | |||
| 144 | - | username = "deploy" | |||
| 145 | - | password_hash = "gordon/auth/password_hash" | |||
| 146 | - | token_secret = "gordon/auth/token_secret" | |||
| 147 | - | ``` | |||
| 148 | - | ||||
| 149 | - | When `username` and `password_hash` are configured, both password and token authentication work. | |||
| 150 | - | ||||
| 151 | - | **Setup:** | |||
| 152 | ```bash | 83 | ```bash | ||
| 153 | # Generate bcrypt hash | 84 | # Generate bcrypt hash | ||
| 154 | gordon auth password hash | 85 | gordon auth password hash | ||
| 155 | - | # Enter password when prompted | |||
| 156 | 86 | ||||
| 157 | - | # Store hash in secrets backend | 87 | + | # Store hash and token secret |
| 158 | pass insert gordon/auth/password_hash | 88 | pass insert gordon/auth/password_hash | ||
| 159 | - | # Paste the bcrypt hash | |||
| 160 | - | ||||
| 161 | - | # Create token secret (for CI/CD token generation) | |||
| 162 | pass insert gordon/auth/token_secret | 89 | pass insert gordon/auth/token_secret | ||
| 163 | - | # Enter a random 32+ character string | |||
| 164 | ``` | 90 | ``` | ||
| 165 | 91 | ||||
| 166 | - | **Usage:** | |||
| 167 | ```bash | 92 | ```bash | ||
| 168 | # Docker registry access | 93 | # Docker registry access | ||
| 169 | docker login -u deploy -p <password> registry.mydomain.com | 94 | docker login -u deploy -p <password> registry.mydomain.com | ||
| 170 | 95 | ||||
| 171 | - | # Remote CLI authentication | 96 | + | # Remote CLI login (stores a token for you) |
| 172 | gordon auth login --remote prod | 97 | gordon auth login --remote prod | ||
| 173 | - | # Enter username and password when prompted | |||
| 174 | ``` | 98 | ``` | ||
| 175 | 99 | ||||
| 176 | - | **Remote CLI Authentication:** | 100 | + | ## Remote CLI Tokens |
| 177 | 101 | ||||
| 178 | - | With password authentication enabled, you can use `gordon auth login` to obtain a token for remote CLI operations: | 102 | + | If you already have a token (token-only servers), store it directly: |
| 179 | 103 | ||||
| 180 | ```bash | 104 | ```bash | ||
| 181 | - | gordon auth login --remote prod | 105 | + | gordon auth login --token <token> |
| 182 | - | # Username: deploy | 106 | + | # or |
| 183 | - | # Password: ******** | 107 | + | gordon remotes set-token prod <token> |
| 184 | - | # ✓ Authentication successful! | |||
| 185 | - | # Token stored for remote 'prod' | |||
| 186 | ``` | 108 | ``` | ||
| 187 | 109 | ||||
| 188 | - | The returned token is stored automatically and used for subsequent CLI commands. | |||
| 189 | - | ||||
| 190 | ## Token Scopes | 110 | ## Token Scopes | ||
| 191 | 111 | ||||
| 192 | - | Tokens can have different permission levels: | 112 | + | Registry scopes: |
| 193 | - | ||||
| 194 | - | ### Registry Scopes | |||
| 195 | 113 | ||||
| 196 | | Scope | Permission | | 114 | | Scope | Permission | | ||
| 197 | |-------|------------| | 115 | |-------|------------| | ||
| @@ -199,9 +117,7 @@ Tokens can have different permission levels: | |||||
| 199 | | `pull` | Pull images from registry | | 117 | | `pull` | Pull images from registry | | ||
| 200 | | `push,pull` | Both push and pull (default) | | 118 | | `push,pull` | Both push and pull (default) | | ||
| 201 | 119 | ||||
| 202 | - | ### Admin Scopes | 120 | + | Admin scopes (for remote CLI): |
| 203 | - | ||||
| 204 | - | For remote CLI access via the Admin API: | |||
| 205 | 121 | ||||
| 206 | | Scope | Permission | | 122 | | Scope | Permission | | ||
| 207 | |-------|------------| | 123 | |-------|------------| | ||
| @@ -214,108 +130,45 @@ For remote CLI access via the Admin API: | |||||
| 214 | | `admin:secrets:read` | List secret keys | | 130 | | `admin:secrets:read` | List secret keys | | ||
| 215 | | `admin:secrets:write` | Set/delete secrets | | 131 | | `admin:secrets:write` | Set/delete secrets | | ||
| 216 | 132 | ||||
| 217 | - | **Examples:** | 133 | + | Examples: |
| 134 | + | ||||
| 218 | ```bash | 135 | ```bash | ||
| 219 | - | # Pull-only token (for read-only access) | |||
| 220 | gordon auth token generate --subject reader --scopes pull --expiry 30d | 136 | gordon auth token generate --subject reader --scopes pull --expiry 30d | ||
| 221 | - | ||||
| 222 | - | # Push-only token (for CI builds) | |||
| 223 | gordon auth token generate --subject builder --scopes push --expiry 0 | 137 | gordon auth token generate --subject builder --scopes push --expiry 0 | ||
| 224 | - | ||||
| 225 | - | # Full admin access (for remote CLI) | |||
| 226 | gordon auth token generate --subject admin --scopes "push,pull,admin:*:*" --expiry 0 | 138 | gordon auth token generate --subject admin --scopes "push,pull,admin:*:*" --expiry 0 | ||
| 227 | ``` | 139 | ``` | ||
| 228 | 140 | ||||
| 229 | ## Token Expiry | 141 | ## Token Expiry | ||
| 230 | 142 | ||||
| 231 | - | Control how long tokens remain valid. Supports human-friendly units: | 143 | + | Durations support `d`, `w`, `M`, `y` and combinations like `1y6M` or `2w3d`. |
| 232 | - | ||||
| 233 | - | | Unit | Description | | |||
| 234 | - | |------|-------------| | |||
| 235 | - | | `d` | days (24 hours) | | |||
| 236 | - | | `w` | weeks (7 days) | | |||
| 237 | - | | `M` | months (30 days) | | |||
| 238 | - | | `y` | years (365 days) | | |||
| 239 | - | ||||
| 240 | - | Compound durations work too: `1y6M`, `2w3d` | |||
| 241 | 144 | ||||
| 242 | ```bash | 145 | ```bash | ||
| 243 | - | # Never expires (for CI/CD) | |||
| 244 | gordon auth token generate --subject ci --expiry 0 | 146 | gordon auth token generate --subject ci --expiry 0 | ||
| 245 | - | ||||
| 246 | - | # Expires in 1 year | |||
| 247 | gordon auth token generate --subject deploy --expiry 1y | 147 | gordon auth token generate --subject deploy --expiry 1y | ||
| 248 | - | ||||
| 249 | - | # Expires in 30 days | |||
| 250 | gordon auth token generate --subject temp --expiry 30d | 148 | gordon auth token generate --subject temp --expiry 30d | ||
| 251 | ``` | 149 | ``` | ||
| 252 | 150 | ||||
| 253 | - | ## Instance-Specific Tokens | 151 | + | ## Instance-specific Tokens |
| 254 | - | ||||
| 255 | - | Tokens are signed with the `token_secret`, which is unique to each Gordon instance: | |||
| 256 | 152 | ||||
| 153 | + | - Tokens are signed with `token_secret` | |||
| 257 | - Tokens from one Gordon instance won't work on another | 154 | - Tokens from one Gordon instance won't work on another | ||
| 258 | - | - Changing the `token_secret` invalidates all existing tokens | 155 | + | - Changing `token_secret` invalidates all existing tokens |
| 259 | - | - Each environment (dev, staging, prod) should have different secrets | |||
| 260 | - | ||||
| 261 | - | ## Development Setup | |||
| 262 | 156 | ||||
| 263 | - | For local development without authentication: | 157 | + | ## Disable Auth (development only) |
| 264 | 158 | ||||
| 265 | ```toml | 159 | ```toml | ||
| 266 | [auth] | 160 | [auth] | ||
| 267 | enabled = false | 161 | enabled = false | ||
| 268 | ``` | 162 | ``` | ||
| 269 | 163 | ||||
| 270 | - | ## Examples | |||
| 271 | - | ||||
| 272 | - | ### CI/CD Setup (Token-Only) | |||
| 273 | - | ||||
| 274 | - | ```toml | |||
| 275 | - | [auth] | |||
| 276 | - | enabled = true | |||
| 277 | - | secrets_backend = "pass" | |||
| 278 | - | token_secret = "gordon/auth/token_secret" | |||
| 279 | - | ``` | |||
| 280 | - | ||||
| 281 | - | ```bash | |||
| 282 | - | # Generate CI token | |||
| 283 | - | gordon auth token generate --subject github-actions --scopes push,pull --expiry 0 | |||
| 284 | - | ``` | |||
| 285 | - | ||||
| 286 | - | In GitHub Actions: | |||
| 287 | - | ```yaml | |||
| 288 | - | - name: Login to Gordon Registry | |||
| 289 | - | run: docker login -u github-actions -p ${{ secrets.GORDON_TOKEN }} ${{ secrets.GORDON_REGISTRY }} | |||
| 290 | - | ``` | |||
| 291 | - | ||||
| 292 | - | ### Production with Password + Token | |||
| 293 | - | ||||
| 294 | - | ```toml | |||
| 295 | - | [auth] | |||
| 296 | - | enabled = true | |||
| 297 | - | secrets_backend = "pass" | |||
| 298 | - | username = "deploy" | |||
| 299 | - | password_hash = "gordon/auth/password_hash" | |||
| 300 | - | token_secret = "gordon/auth/token_secret" | |||
| 301 | - | ``` | |||
| 302 | - | ||||
| 303 | - | ### Enterprise with SOPS | |||
| 304 | - | ||||
| 305 | - | ```toml | |||
| 306 | - | [auth] | |||
| 307 | - | enabled = true | |||
| 308 | - | secrets_backend = "sops" | |||
| 309 | - | token_secret = "gordon/auth/token_secret" | |||
| 310 | - | ``` | |||
| 311 | - | ||||
| 312 | ## Internal Registry Auth | 164 | ## Internal Registry Auth | ||
| 313 | 165 | ||||
| 314 | - | Gordon generates internal credentials automatically when auth is enabled. These are used for loopback pulls when deploying containers and are regenerated on each restart. | 166 | + | Gordon generates internal credentials when auth is enabled. These are used for loopback pulls during deploys and are regenerated on each restart. |
| 315 | 167 | ||||
| 316 | Gordon also generates a separate service token for its own registry-domain pulls. This token is managed internally and is not exposed via configuration or CLI. | 168 | Gordon also generates a separate service token for its own registry-domain pulls. This token is managed internally and is not exposed via configuration or CLI. | ||
| 317 | 169 | ||||
| 318 | To view internal credentials (for troubleshooting): | 170 | To view internal credentials (for troubleshooting): | ||
| 171 | + | ||||
| 319 | ```bash | 172 | ```bash | ||
| 320 | gordon auth internal | 173 | gordon auth internal | ||
| 321 | ``` | 174 | ``` | ||
M internal/app/run.go +50 -33
| @@ -266,19 +266,8 @@ func createServices(ctx context.Context, v *viper.Viper, cfg Config, log zerowra | |||
| 266 | 266 | return nil, err | |
| 267 | 267 | } | |
| 268 | 268 | ||
| 269 | - | // Generate internal registry credentials for loopback-only pulls | |
| 270 | - | if cfg.Auth.Enabled { | |
| 271 | - | svc.internalRegUser, svc.internalRegPass, err = generateInternalRegistryAuth() | |
| 272 | - | if err != nil { | |
| 273 | - | return nil, log.WrapErr(err, "failed to generate internal registry credentials") | |
| 274 | - | } | |
| 275 | - | ||
| 276 | - | // Persist credentials to file for CLI access (gordon auth internal) | |
| 277 | - | if err := persistInternalCredentials(svc.internalRegUser, svc.internalRegPass); err != nil { | |
| 278 | - | log.Warn().Err(err).Msg("failed to persist internal credentials for CLI access") | |
| 279 | - | } | |
| 280 | - | ||
| 281 | - | log.Debug().Msg("internal registry auth generated for loopback pulls") | |
| 269 | + | if err := setupInternalRegistryAuth(cfg, svc, log); err != nil { | |
| 270 | + | return nil, err | |
| 282 | 271 | } | |
| 283 | 272 | ||
| 284 | 273 | // Create use case services | |
| @@ -287,28 +276,13 @@ func createServices(ctx context.Context, v *viper.Viper, cfg Config, log zerowra | |||
| 287 | 276 | return nil, log.WrapErr(err, "failed to load configuration") | |
| 288 | 277 | } | |
| 289 | 278 | ||
| 290 | - | // Determine env directory for admin API | |
| 291 | - | svc.envDir = resolveEnvDir(cfg) | |
| 292 | - | ||
| 293 | - | backend := resolveSecretsBackend(cfg.Auth.SecretsBackend) | |
| 294 | - | var domainSecretStore out.DomainSecretStore | |
| 279 | + | var backend domain.SecretsBackend | |
| 295 | 280 | var passStore *domainsecrets.PassStore | |
| 281 | + | var domainSecretStore out.DomainSecretStore | |
| 296 | 282 | ||
| 297 | - | switch backend { | |
| 298 | - | case domain.SecretsBackendPass: | |
| 299 | - | passStore, err = domainsecrets.NewPassStore(log) | |
| 300 | - | if err != nil { | |
| 301 | - | return nil, log.WrapErr(err, "failed to create pass domain secret store") | |
| 302 | - | } | |
| 303 | - | if err := migrateEnvFilesToPass(svc.envDir, passStore, log); err != nil { | |
| 304 | - | return nil, log.WrapErr(err, "failed to migrate env files to pass") | |
| 305 | - | } | |
| 306 | - | domainSecretStore = passStore | |
| 307 | - | default: | |
| 308 | - | domainSecretStore, err = domainsecrets.NewFileStore(svc.envDir, log) | |
| 309 | - | if err != nil { | |
| 310 | - | return nil, log.WrapErr(err, "failed to create domain secret store") | |
| 311 | - | } | |
| 283 | + | svc.envDir, backend, passStore, domainSecretStore, err = createDomainSecretStore(cfg, log) | |
| 284 | + | if err != nil { | |
| 285 | + | return nil, err | |
| 312 | 286 | } | |
| 313 | 287 | ||
| 314 | 288 | if svc.envLoader, err = createEnvLoader(backend, svc.envDir, passStore, log); err != nil { | |
| @@ -348,6 +322,49 @@ func createServices(ctx context.Context, v *viper.Viper, cfg Config, log zerowra | |||
| 348 | 322 | return svc, nil | |
| 349 | 323 | } | |
| 350 | 324 | ||
| 325 | + | func setupInternalRegistryAuth(cfg Config, svc *services, log zerowrap.Logger) error { | |
| 326 | + | if !cfg.Auth.Enabled { | |
| 327 | + | return nil | |
| 328 | + | } | |
| 329 | + | ||
| 330 | + | var err error | |
| 331 | + | svc.internalRegUser, svc.internalRegPass, err = generateInternalRegistryAuth() | |
| 332 | + | if err != nil { | |
| 333 | + | return log.WrapErr(err, "failed to generate internal registry credentials") | |
| 334 | + | } | |
| 335 | + | ||
| 336 | + | // Persist credentials to file for CLI access (gordon auth internal) | |
| 337 | + | if err := persistInternalCredentials(svc.internalRegUser, svc.internalRegPass); err != nil { | |
| 338 | + | log.Warn().Err(err).Msg("failed to persist internal credentials for CLI access") | |
| 339 | + | } | |
| 340 | + | ||
| 341 | + | log.Debug().Msg("internal registry auth generated for loopback pulls") | |
| 342 | + | return nil | |
| 343 | + | } | |
| 344 | + | ||
| 345 | + | func createDomainSecretStore(cfg Config, log zerowrap.Logger) (string, domain.SecretsBackend, *domainsecrets.PassStore, out.DomainSecretStore, error) { | |
| 346 | + | envDir := resolveEnvDir(cfg) | |
| 347 | + | backend := resolveSecretsBackend(cfg.Auth.SecretsBackend) | |
| 348 | + | ||
| 349 | + | switch backend { | |
| 350 | + | case domain.SecretsBackendPass: | |
| 351 | + | passStore, err := domainsecrets.NewPassStore(log) | |
| 352 | + | if err != nil { | |
| 353 | + | return "", backend, nil, nil, log.WrapErr(err, "failed to create pass domain secret store") | |
| 354 | + | } | |
| 355 | + | if err := migrateEnvFilesToPass(envDir, passStore, log); err != nil { | |
| 356 | + | return "", backend, nil, nil, log.WrapErr(err, "failed to migrate env files to pass") | |
| 357 | + | } | |
| 358 | + | return envDir, backend, passStore, passStore, nil | |
| 359 | + | default: | |
| 360 | + | store, err := domainsecrets.NewFileStore(envDir, log) | |
| 361 | + | if err != nil { | |
| 362 | + | return "", backend, nil, nil, log.WrapErr(err, "failed to create domain secret store") | |
| 363 | + | } | |
| 364 | + | return envDir, backend, nil, store, nil | |
| 365 | + | } | |
| 366 | + | } | |
| 367 | + | ||
| 351 | 368 | // resolveLogFilePath returns the configured log file path or a default. | |
| 352 | 369 | func resolveLogFilePath(cfg Config) string { | |
| 353 | 370 | if cfg.Logging.File.Path != "" { | |
| @@ -266,19 +266,8 @@ func createServices(ctx context.Context, v *viper.Viper, cfg Config, log zerowra | |||||
| 266 | return nil, err | 266 | return nil, err | ||
| 267 | } | 267 | } | ||
| 268 | 268 | ||||
| 269 | - | // Generate internal registry credentials for loopback-only pulls | 269 | + | if err := setupInternalRegistryAuth(cfg, svc, log); err != nil { |
| 270 | - | if cfg.Auth.Enabled { | 270 | + | return nil, err |
| 271 | - | svc.internalRegUser, svc.internalRegPass, err = generateInternalRegistryAuth() | |||
| 272 | - | if err != nil { | |||
| 273 | - | return nil, log.WrapErr(err, "failed to generate internal registry credentials") | |||
| 274 | - | } | |||
| 275 | - | ||||
| 276 | - | // Persist credentials to file for CLI access (gordon auth internal) | |||
| 277 | - | if err := persistInternalCredentials(svc.internalRegUser, svc.internalRegPass); err != nil { | |||
| 278 | - | log.Warn().Err(err).Msg("failed to persist internal credentials for CLI access") | |||
| 279 | - | } | |||
| 280 | - | ||||
| 281 | - | log.Debug().Msg("internal registry auth generated for loopback pulls") | |||
| 282 | } | 271 | } | ||
| 283 | 272 | ||||
| 284 | // Create use case services | 273 | // Create use case services | ||
| @@ -287,28 +276,13 @@ func createServices(ctx context.Context, v *viper.Viper, cfg Config, log zerowra | |||||
| 287 | return nil, log.WrapErr(err, "failed to load configuration") | 276 | return nil, log.WrapErr(err, "failed to load configuration") | ||
| 288 | } | 277 | } | ||
| 289 | 278 | ||||
| 290 | - | // Determine env directory for admin API | 279 | + | var backend domain.SecretsBackend |
| 291 | - | svc.envDir = resolveEnvDir(cfg) | |||
| 292 | - | ||||
| 293 | - | backend := resolveSecretsBackend(cfg.Auth.SecretsBackend) | |||
| 294 | - | var domainSecretStore out.DomainSecretStore | |||
| 295 | var passStore *domainsecrets.PassStore | 280 | var passStore *domainsecrets.PassStore | ||
| 281 | + | var domainSecretStore out.DomainSecretStore | |||
| 296 | 282 | ||||
| 297 | - | switch backend { | 283 | + | svc.envDir, backend, passStore, domainSecretStore, err = createDomainSecretStore(cfg, log) |
| 298 | - | case domain.SecretsBackendPass: | 284 | + | if err != nil { |
| 299 | - | passStore, err = domainsecrets.NewPassStore(log) | 285 | + | return nil, err |
| 300 | - | if err != nil { | |||
| 301 | - | return nil, log.WrapErr(err, "failed to create pass domain secret store") | |||
| 302 | - | } | |||
| 303 | - | if err := migrateEnvFilesToPass(svc.envDir, passStore, log); err != nil { | |||
| 304 | - | return nil, log.WrapErr(err, "failed to migrate env files to pass") | |||
| 305 | - | } | |||
| 306 | - | domainSecretStore = passStore | |||
| 307 | - | default: | |||
| 308 | - | domainSecretStore, err = domainsecrets.NewFileStore(svc.envDir, log) | |||
| 309 | - | if err != nil { | |||
| 310 | - | return nil, log.WrapErr(err, "failed to create domain secret store") | |||
| 311 | - | } | |||
| 312 | } | 286 | } | ||
| 313 | 287 | ||||
| 314 | if svc.envLoader, err = createEnvLoader(backend, svc.envDir, passStore, log); err != nil { | 288 | if svc.envLoader, err = createEnvLoader(backend, svc.envDir, passStore, log); err != nil { | ||
| @@ -348,6 +322,49 @@ func createServices(ctx context.Context, v *viper.Viper, cfg Config, log zerowra | |||||
| 348 | return svc, nil | 322 | return svc, nil | ||
| 349 | } | 323 | } | ||
| 350 | 324 | ||||
| 325 | + | func setupInternalRegistryAuth(cfg Config, svc *services, log zerowrap.Logger) error { | |||
| 326 | + | if !cfg.Auth.Enabled { | |||
| 327 | + | return nil | |||
| 328 | + | } | |||
| 329 | + | ||||
| 330 | + | var err error | |||
| 331 | + | svc.internalRegUser, svc.internalRegPass, err = generateInternalRegistryAuth() | |||
| 332 | + | if err != nil { | |||
| 333 | + | return log.WrapErr(err, "failed to generate internal registry credentials") | |||
| 334 | + | } | |||
| 335 | + | ||||
| 336 | + | // Persist credentials to file for CLI access (gordon auth internal) | |||
| 337 | + | if err := persistInternalCredentials(svc.internalRegUser, svc.internalRegPass); err != nil { | |||
| 338 | + | log.Warn().Err(err).Msg("failed to persist internal credentials for CLI access") | |||
| 339 | + | } | |||
| 340 | + | ||||
| 341 | + | log.Debug().Msg("internal registry auth generated for loopback pulls") | |||
| 342 | + | return nil | |||
| 343 | + | } | |||
| 344 | + | ||||
| 345 | + | func createDomainSecretStore(cfg Config, log zerowrap.Logger) (string, domain.SecretsBackend, *domainsecrets.PassStore, out.DomainSecretStore, error) { | |||
| 346 | + | envDir := resolveEnvDir(cfg) | |||
| 347 | + | backend := resolveSecretsBackend(cfg.Auth.SecretsBackend) | |||
| 348 | + | ||||
| 349 | + | switch backend { | |||
| 350 | + | case domain.SecretsBackendPass: | |||
| 351 | + | passStore, err := domainsecrets.NewPassStore(log) | |||
| 352 | + | if err != nil { | |||
| 353 | + | return "", backend, nil, nil, log.WrapErr(err, "failed to create pass domain secret store") | |||
| 354 | + | } | |||
| 355 | + | if err := migrateEnvFilesToPass(envDir, passStore, log); err != nil { | |||
| 356 | + | return "", backend, nil, nil, log.WrapErr(err, "failed to migrate env files to pass") | |||
| 357 | + | } | |||
| 358 | + | return envDir, backend, passStore, passStore, nil | |||
| 359 | + | default: | |||
| 360 | + | store, err := domainsecrets.NewFileStore(envDir, log) | |||
| 361 | + | if err != nil { | |||
| 362 | + | return "", backend, nil, nil, log.WrapErr(err, "failed to create domain secret store") | |||
| 363 | + | } | |||
| 364 | + | return envDir, backend, nil, store, nil | |||
| 365 | + | } | |||
| 366 | + | } | |||
| 367 | + | ||||
| 351 | // resolveLogFilePath returns the configured log file path or a default. | 368 | // resolveLogFilePath returns the configured log file path or a default. | ||
| 352 | func resolveLogFilePath(cfg Config) string { | 369 | func resolveLogFilePath(cfg Config) string { | ||
| 353 | if cfg.Logging.File.Path != "" { | 370 | if cfg.Logging.File.Path != "" { | ||