awslabs / amazon-ecr-credential-helper

Automatically gets credentials for Amazon ECR on docker push/docker pull
Apache License 2.0
2.48k stars 336 forks source link

Support for providing registry region and id from other source #94

Open ojundt opened 6 years ago

ojundt commented 6 years ago

Hi there,

We are using ECR in combination with a proxy with a custom domain (e.g. registry.foobar.com). We use the custom domain to make migration from and to ECR easier.

However, the custom domain currently prevents us from using the ECR credential helper. It requires the ECR domain format for extracting the registry id and region: https://github.com/awslabs/amazon-ecr-credential-helper/blob/3eac0af2021820739ca704166494c1f45a4e8f72/ecr-login/api/client.go#L33-L55

It would be nice if there are alternative means to provide the region and account id information, maybe with environment variables or config files for the helper with a simple mapping such as:

{  
   "registry.foobar.com":{  
      "region":"eu-central-1",
      "id":"0176235112345"
   },
   "images.somethingelse.net":{  
      "region":"us-west-1",
      "id":"0182738917123"
   }
}
beamerblvd commented 5 years ago

This hit me, too. It's a big bummer. I was looking forward to shortening the <horrendous ID>.dkr.ecr.<region name>.amazonaws.com URIs. I, apparently, successfully got a proxy in front of it, and then had to throw it all away when I had no way to authenticate (neither ecr-login nor aws ecr get-login returns an auth that Docker can use with my custom URL). Would really love to see this fixed. In my opinion (which, of course, is just that—an opinion), this is a defect, not a feature. The internet is simply full of people trying to shorten these ridiculous URIs with one proxy or another, usually with success, but it's all moot if the login tools intentionally prevent us from doing so. :-(

alexmac commented 4 years ago

I'm surprised there isn't an easier way to front ECR behind a CNAME, it's definitely a usability failure in my opinion. Nevertheless I was able to cobble together workaround which others hitting this might be interested in.

First you need to setup some kind of proxy that can rewrite both request and response headers. I used haproxy. The relevant part of the config that reqwrites both the Host(request) and Location(Response) headers looks like this:

frontend fe_router
    bind 0.0.0.0:9550 ssl crt /tmp/cert.pem
    option forwardfor
    use_backend be_external_host

backend be_external_host
    http-request set-header Host 1234567890.dkr.ecr.us-east-1.amazonaws.com
    http-response replace-value Location ^(https://1234567890.dkr.ecr.us-east-1.amazonaws.com)(.*)$ https://example.com\2
    server server_external 1234567890.dkr.ecr.us-east-1.amazonaws.com:443 check ssl

Then I implemented my own dumb credential helper (with caching of the token)

#!/bin/bash

LOCAL_DOCKER_DIR=~/.docker
DOCKER_CONFIG="${LOCAL_DOCKER_DIR}/config.json"
ECR_CREDENTIAL="${LOCAL_DOCKER_DIR}/example_ecr_credential.json"
ECR_ENDPOINT=1234567890.dkr.ecr.us-east-1.amazonaws.com

mkdir -p "${LOCAL_DOCKER_DIR}"

install_config="false"

while [[ $# -gt 0 ]]
do
key="$1"
case $key in
    install)
        install_config="true"
        shift
    ;;
    *)
        shift
    ;;
esac
done

if [[ "${install_config}" == "true" ]]; then
    original_config=$(cat ${DOCKER_CONFIG} 2>/dev/null)
    if [ -z "${original_config}" ];
    then
        original_config="{}"
    fi
    cred_helpers="{\"credHelpers\": {\"${ECR_ENDPOINT}\": \"ecr-login\",\"example.com\": \"cname-ecr-login\"}}"
    echo "${original_config} ${cred_helpers}" | \
        jq -Scs '.[0] * .[1]' > "${DOCKER_CONFIG}"
    echo "Config installed in ${DOCKER_CONFIG}"
    exit 0
fi

# Docker provides the server url on stdin, but we don't use it
read line

# AWS docs claim the token lasts 12h, I cache it for 11
recent_credential=$(find "${ECR_CREDENTIAL}" -mmin -660 2>/dev/null)
if [ -z "${recent_credential}" ]
then
    credential=$( \
        aws ecr get-authorization-token \
        | jq -r '.authorizationData[0].authorizationToken' \
        | base64 -d \
        | sed -e 's/^[^:]*://' \
    )
    echo "{\"ServerURL\":\"${ECR_ENDPOINT}\",\"Username\":\"AWS\",\"Secret\":\"${credential}\"}" | jq -Sc > "${ECR_CREDENTIAL}"
fi

cat "${ECR_CREDENTIAL}"

In this case if the script was named "docker-credential-cname-ecr-login" and somewhere on your path it would be used (I've redacted the above scripts a bit and might have typoed things, but you get the idea)

samuelkarp commented 4 years ago

https://github.com/aws/containers-roadmap/issues/299 is the issue in the roadmap tracking general support for custom domains in ECR.

nicks commented 3 months ago

One fun way to fix this would be to use the distribution protocol itself -- https://distribution.github.io/distribution/spec/auth/token/#how-to-authenticate

If the URL doesn't match the expected pattern, we could send a HEAD to registry.foobar.com/v2/ and grab the ECR authentication URL off the Www-Authenticate response headers, then infer account id and region from that.