Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/reference/docker_auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ docker_auth:
password: "${REGISTRY_PASSWORD}"
```

On each `up` run, dokku-compose runs `dokku registry:login <server> <username> <password>` for every entry. Docker stores credentials in `~/.docker/config.json` and overwrites in place, so re-running is idempotent and rotation is just an `up` away.
On each `up` run, dokku-compose runs `dokku registry:login --global <server> <username> <password>` for every entry. Docker stores credentials in `~/.docker/config.json` and overwrites in place, so re-running is idempotent and rotation is just an `up` away.

| Field | Required | Description |
|-------|----------|-------------|
Expand All @@ -34,7 +34,7 @@ This top-level `docker_auth:` is host-global pull authentication. It is **not**

## Removal Semantics

Removing an entry from `docker_auth:` does **not** automatically log out — host credentials persist. To revoke, run `dokku registry:logout <server>` manually on the host. Same posture as `plugins:` (installed plugins aren't auto-removed).
Removing an entry from `docker_auth:` does **not** automatically log out — host credentials persist. To revoke, run `dokku registry:logout --global <server>` manually on the host. Same posture as `plugins:` (installed plugins aren't auto-removed).

## Secrets

Expand Down
17 changes: 11 additions & 6 deletions src/core/mask.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,19 @@ describe('maskSensitiveArgs', () => {
.toBe('apps:create myapp')
})

it('masks positional password in registry:login', () => {
expect(maskSensitiveArgs('registry:login ghcr.io octocat ghp_abcdefgh'))
.toBe('registry:login ghcr.io octocat ****efgh')
it('masks positional password in registry:login --global', () => {
expect(maskSensitiveArgs('registry:login --global ghcr.io octocat ghp_abcdefgh'))
.toBe('registry:login --global ghcr.io octocat ****efgh')
})

it('masks short positional password in registry:login --global', () => {
expect(maskSensitiveArgs('registry:login --global ghcr.io octocat pw'))
.toBe('registry:login --global ghcr.io octocat ****')
})

it('masks short positional password in registry:login', () => {
expect(maskSensitiveArgs('registry:login ghcr.io octocat pw'))
.toBe('registry:login ghcr.io octocat ****')
it('masks positional password in registry:login without --global', () => {
expect(maskSensitiveArgs('registry:login ghcr.io octocat ghp_abcdefgh'))
.toBe('registry:login ghcr.io octocat ****efgh')
})

it('does not affect other registry: subcommands', () => {
Expand Down
6 changes: 3 additions & 3 deletions src/core/mask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ export function maskSensitiveArgs(cmd: string): string {
return `${key}=${maskValue(value)}`
})

// Special-case `registry:login <server> <username> <password>` — password is
// a positional arg, not KEY=VALUE, so the regex above can't catch it.
// Special-case `registry:login [--global] <server> <username> <password>` —
// password is a positional arg, not KEY=VALUE, so the regex above can't catch it.
masked = masked.replace(
/^(registry:login\s+\S+\s+\S+\s+)(\S+)/,
/^(registry:login\s+(?:--global\s+)?\S+\s+\S+\s+)(\S+)/,
(_, prefix, password) => `${prefix}${maskValue(password)}`
)

Expand Down
4 changes: 2 additions & 2 deletions src/modules/docker-auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ describe('ensureDockerAuth', () => {
'registry.example.com': { username: 'svc', password: 'pw' },
})
expect(runner.run).toHaveBeenCalledWith(
'registry:login', 'ghcr.io', 'octocat', 'ghp_secret'
'registry:login', '--global', 'ghcr.io', 'octocat', 'ghp_secret'
)
expect(runner.run).toHaveBeenCalledWith(
'registry:login', 'registry.example.com', 'svc', 'pw'
'registry:login', '--global', 'registry.example.com', 'svc', 'pw'
)
expect(runner.run).toHaveBeenCalledTimes(2)
})
Expand Down
2 changes: 1 addition & 1 deletion src/modules/docker-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export async function ensureDockerAuth(
): Promise<void> {
for (const [server, creds] of Object.entries(config)) {
logAction('docker-auth', `Logging in to ${server} as ${creds.username}`)
await ctx.run('registry:login', server, creds.username, creds.password)
await ctx.run('registry:login', '--global', server, creds.username, creds.password)
logDone()
}
}
Loading