Docker Community Forums

Share and learn in the Docker community.

RegistryLogin responds with "Login Succeeded" but private registry is unaccessible

Hello folks, I’m trying to log into a private AWS ECR repository using client · github.com/docker/docker/client · pkg.go.dev (version github.com/docker/docker v20.10.8+incompatible)

This is the function:

// Log into default AWS ECR registry using `profileName` as AWS_PROFILE value
// NOTE: The authorization token is valid for 12 hours
func DefaultRegistryLogin(profileName string) error {
	log.Infof("Log into default ECR registry using AWS `%s` profile's credentials", profileName)
	url, err := aws.ECRRegistryURL(profileName)
	if err != nil {
		return err
	}
	cli, ctx, err := getDockerCliAndCtx()
	if err != nil {
		return err
	}
	cfg, err := aws.GetConfig(profileName)
	if err != nil {
		return err
	}
	client := ecr.New(ecr.Options{Region: cfg.Region, Credentials: cfg.Credentials})

	log.Debugf("Run ECR GetAuthorizationToken in region %s", cfg.Region)
	tokens, err := client.GetAuthorizationToken(ctx, &ecr.GetAuthorizationTokenInput{})
	if err != nil {
		return err
	}
	if len(tokens.AuthorizationData) != 1 {
		return fmt.Errorf("Unexpected token list length. Expected 1, got %d", len(tokens.AuthorizationData))
	}

	// Parse token in a format digestible by Docker APIs
	token := tokens.AuthorizationData[0]
	decoded, err := base64.StdEncoding.DecodeString(*token.AuthorizationToken)
	if err != nil {
		return err
	}
	log.Debugf("Decoded base64 token: %s", decoded)
	splitted := bytes.Split(decoded, []byte(":"))
	if len(splitted) != 2 {
		return fmt.Errorf("Unexpected token format: %s", decoded)
	}
	password := splitted[1]

	// Attempt login (will expire in 12H)
	log.Debugf("Attempt login against ECR registry URL: `%s`", url)
	resp, err := cli.RegistryLogin(ctx, types.AuthConfig{
		Username:      "AWS",
		Password:      string(password),
		ServerAddress: url,
	})
	if err != nil {
		return err
	}
	log.Debugf("Response status: %s", resp.Status)
	log.Infof("Login successful, ECR token will expire at %s", token.ExpiresAt)

	return nil
}

which seems to execute fine according to the logs:

INFO[2021-08-18T09:29:58+02:00] Log into default ECR registry using AWS `user` profile's credentials
DEBU[2021-08-18T09:29:58+02:00] Run ECR GetAuthorizationToken in region us-west-2
DEBU[2021-08-18T09:29:59+02:00] Decoded base64 token: AWS:eyJwYXlsb2FkIjoidmc2d0htNDhoVj[cut...]
DEBU[2021-08-18T09:29:59+02:00] Attempt login against ECR registry URL: `https://xxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com`
DEBU[2021-08-18T09:30:01+02:00] Response status: Login Succeeded
INFO[2021-08-18T09:30:01+02:00] Login successful, ECR token will expire at 2021-08-18 19:29:59.777 +0000 UTC
DEBU[2021-08-18T09:30:01+02:00] Total execution time: 5.137222335s

but unfortunately it seems the login credentials are not actually stored so I can’t pull images for example:

docker pull xxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/testservice
# Using default tag: latest
# Error response from daemon: Head https://xxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/v2/testservice/manifests/latest: no basic auth credentials

Note that copy&pasting the AWS login token printed in the logs and piping it to docker login does seem to work. Any ideas?

For the records, we worked around the issue calling docker login from within:

func DefaultRegistryLogin(profileName string) error {
    // [ CUT... ]

	// FIXME: the following doesn't work as expected, see https://forums.docker.com/t/registrylogin-responds-with-login-succeeded-but-private-registry-is-unaccessible/113974
	// resp, err := cli.RegistryLogin(ctx, types.AuthConfig{
	// 	Username:      "AWS",
	// 	Password:      string(password),
	// 	ServerAddress: url,
	// })
	// if err != nil {
	// 	return err
	// }
	// log.Debugf("Response status: %s", resp.Status)

	// ... so for the moment we fall back to using CLI for login
	args := []string{"login", "-u", "AWS", "-p", string(password), url}
	if err := exec.Command("docker", args...).Run(); err != nil {
		return err
	}

	log.Infof("Login successful, ECR token will expire at %s", token.ExpiresAt)
	return nil
}