Skip to content

cli/command/registry: fix "docker logout docker.io" leaving credentials behind - #7213

Open
c879873067877881111 wants to merge 2 commits into
docker:masterfrom
c879873067877881111:fix/logout-docker-io-leaves-credentials
Open

cli/command/registry: fix "docker logout docker.io" leaving credentials behind#7213
c879873067877881111 wants to merge 2 commits into
docker:masterfrom
c879873067877881111:fix/logout-docker-io-leaves-credentials

Conversation

@c879873067877881111

Copy link
Copy Markdown

- What I did

Fixed docker logout docker.io silently leaving the credentials in config.json. It printed Removing login credentials for docker.io, returned no error and exited 0, but nothing was removed.

docker.io is the name the docs use for Docker Hub, so this is the spelling a user is most likely to reach for. docker logout with no argument, and docker logout https://index.docker.io/v1/, both work — only the namespace form is affected.

- How I did it

runLogin resolves the default registry before storing:

if opts.serverAddress != "" && opts.serverAddress != registry.DefaultNamespace {
	serverAddress = opts.serverAddress
} else {
	serverAddress = registry.IndexServer
}

so docker login docker.io stores under https://index.docker.io/v1/.

runLogout only treats an empty server address as the default registry, so for docker.io it takes the !isDefaultRegistry branch and builds regsToLogout as docker.io, http://docker.io, https://docker.io. None of those keys exist, and fileStore.Erase returns nil for a key that is not present:

func (c *fileStore) Erase(serverAddress string) error {
	if _, exists := c.file.GetAuthConfigs()[serverAddress]; !exists {
		// nothing to do; no credentials found for the given serverAddress
		return nil
	}
	...

so every removal "succeeds", errs stays empty, and the success message is printed.

This patch appends registry.IndexServer to regsToLogout when logging out of the default registry by namespace or index hostname. The existing lookups are left in place, so credentials stored under a legacy key are still removed.

- How to verify it

cli/command/registry/logout.go had no test file at all. This PR adds one that drives the real runLogin / runLogout round-trip for the spellings a user can pass:

$ go test ./cli/command/registry/ -run TestLogoutRemovesCredentialsStoredByLogin -v

On master exactly one subtest fails:

--- FAIL: TestLogoutRemovesCredentialsStoredByLogin/docker.io
    logout_test.go:46: assertion failed: expected
    map[https://index.docker.io/v1/:{my-username my-password  https://index.docker.io/v1/  }]
    (length 1) to have length 0
--- PASS: TestLogoutRemovesCredentialsStoredByLogin/no_server_address
--- PASS: TestLogoutRemovesCredentialsStoredByLogin/index.docker.io
--- PASS: TestLogoutRemovesCredentialsStoredByLogin/https://index.docker.io/v1/
--- PASS: TestLogoutRemovesCredentialsStoredByLogin/myreg.example.com

With the fix all five pass.

End to end, with a config file holding the entry docker login docker.io writes and no credential helper configured:

$ export DOCKER_CONFIG=$(mktemp -d)
$ echo '{"auths":{"https://index.docker.io/v1/":{"auth":"bXl1c2VyOm15cGFzc3dvcmQ="}}}' > $DOCKER_CONFIG/config.json
$ docker logout docker.io
Removing login credentials for docker.io
$ echo $?
0
$ jq -r '.auths | keys[]' $DOCKER_CONFIG/config.json
https://index.docker.io/v1/

The same sequence built from this branch leaves .auths empty. In both cases the output and the exit status are identical, which is what makes this one easy to miss.

- One thing I deliberately did not change

There is a second, larger reading of this asymmetry, and I would rather ask than guess.

runLogin treats docker.io as the default registry for everything, including the OAuth device-code flow. runLogout does not, so isDefaultRegistry stays false and this block is skipped:

if isDefaultRegistry {
	store := dockerCLI.ConfigFile().GetCredentialsStore(registry.IndexServer)
	if err := manager.NewManager(store).Logout(ctx); err != nil {
		...

That means docker logout docker.io also never reaches OAuthManager.Logout, so the refresh token is neither erased nor revoked with the tenant. Aligning the condition in runLogout with the one in runLogin would fix both at once:

if serverAddress == "" || serverAddress == registry.DefaultNamespace {
	serverAddress = registry.IndexServer
	isDefaultRegistry = true
}

I kept this PR to the narrow fix because that version also changes the address printed in the output message and drops the legacy http:// / https:// lookups for this input. Happy to switch to it if you prefer the symmetry — the test above covers either shape.

- Human readable description for the release notes

Fix `docker logout docker.io` reporting success without removing the stored credentials

…ls behind

"docker login docker.io" resolves the default registry to
registry.IndexServer ("https://index.docker.io/v1/") and stores the
credentials under that key. "docker logout docker.io" only treated an
empty server address as the default registry, so it looked for
"docker.io", "http://docker.io" and "https://docker.io" instead. None of
those exist, and fileStore.Erase returns nil for a key that is not
present, so every removal "succeeded": the credentials stayed in
config.json, "Removing login credentials for docker.io" was printed, and
the command exited 0.

Look for the full index address as well when logging out of the default
registry by its namespace ("docker.io") or index hostname
("index.docker.io"). The existing lookups are kept, so credentials stored
under a legacy key are still removed.

Add tests covering the login/logout round-trip; logout had no test
coverage.

Signed-off-by: c879873067877881111 <c879873067877881111@gmail.com>
Point at getAuthConfigKey in cli/config/configfile as the source of the
"docker.io" / "index.docker.io" pairing, so the condition does not read
as an ad-hoc choice.

Signed-off-by: c879873067877881111 <c879873067877881111@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant