dockergradleamazon-ecrjibaws-credentials

Jib: how to use amazon-ecr-credential-helper without installing it?


When using jib-gradle-plugin to build and push to AWS ECR, it requires me to install aws ecr credential helper otherwise the build complains "The system does not have docker-credential-ecr-login CLI".

I am wondering if there is a way to push to AWS ECR without installing the credential helper, or if it is possible to bundle a portable version of the credential helper in the repo?

The issues with installing the helper are:

  1. it requires the helper to be installed on every machine where the project needs to be built, hence making the build flow not as automated as I would like
  2. To install the aws ecr credential helper, it requires Docker to be installed. This feels a bit ironic because a large part of the point of Jib is that no Docker is needed on the host where the build happens, hence the build can be self-contained and portable.

I know this is not a Jib issue but I am just hoping whoever using Jib might have run into similar challenges and therefore can offer some insights of how to work around it.


Solution

  • In the end, authenticating with a registry all boils down to providing a simple username/password string pair to Jib. Once Jib retrieves the pair, Jib just passes the username and password string literals to a server as-is without any processing at all. (BTW, this mechanism isn't specific to Jib; every Docker registry works this way.) It is that simple: a username and password pair is all that matters.

    Using a Docker credential helper is no different than providing this string pair through a CLI. Any credential helper will output a username and a password with the "get" command. For example with Google Container Registry,

    $ docker-credential-gcr get <<<gcr.io
    {"ServerURL":"","Username":"... this is the username ...","Secret":"... this is the password ..."}
    

    So, theoretically you can write a dumb script or binary that always outputs some username/password, name the file docker-credential-my-dumb-script, and configure jib.{from|to}.credHelper='my-dumb-script'. I wouldn't do it though; this is just to highlight that registry auth is only a matter of providing a username and password pair to Jib.

    However, note that many credential helpers dynamically generate short-lived credentials that expire soon, which are much more secure than using static and permanent credentials. This is one of the reasons we generally recommend using a credential helper whenever possible. It could also be that some cloud registries accept only these short-lived credentials generated by their credential helpers.

    Another example is docker login. For example, successfully logging in with docker login chanseoktest.azurecr.io -u my-username -p my-password simply results in recording my-username and my-password in ~/.docker/config.json:

        "auths": {
            "chanseoktest.azurecr.io": {
                # <username>:<password> in PLAIN string in base64 encoded form
                "auth": "bXktdXNlcm5hbWU6bXktcGFzc3dvcmQ="
            },
    
    

    (If you do base64-decode on bXktdXNlcm5hbWU6bXktcGFzc3dvcmQ=, it results in my-username:my-password in plain string.) This means that, if you can make docker pull/push work on some system, Jib will also work (as Jib looks into ~/.docker/config.json). Therefore, another way to provide credentials to Jib would be to create a working ~/.docker/config.json on the system (or you could copy it from another system where you successfully ran docker login). This approach, I wouldn't do either unless this can be done securely.

    For yet another example, instead of the dumb credential helper or ~/.docker/config indirection, you can also directly pass your credentials to Jib via jib.{from|to}.auth.{username|password} (which can also be set through the corresponding system properties with, e.g., -Djib.from.auth.username=...). We don't recommend this either as long as you can use a credential helper. Be aware that if you pass credentials on the command-line, other users on the same system can see the command (including the credentials), not to mention that commands can be logged or stored in a shell history. In certain environments, this command-line risk may be mitigated if you store these credentials in some environment variables and you modify your build.gradle or pom.xml to read jib.{from|to}.auth.{username|password} from the environment variables.

    For the complete list of ways you can provide a username/password pair, you can consult the official FAQ.

    Also do note that what you believe a correct username and password pair may not be the one that your registry actually accepts. For example, this AWS ECR user mistakenly assumed that they could use an "AWS ECR key user" (whatever it is) as a username, whereas in reality, docker-credential-ecr-login returned AWS as a username. (Not that you always have to use AWS as a username; ECR may (or may not) have multiple forms of acceptable credentials.)

    Lastly, I would confirm with the AWS ECR community or the community of the platform where you're using Jib to figure out which credential form would be best to use as a username and password pair to "login to Docker" if you cannot use a credential helper. For example, for GitHub Actions, previously I successfully used aws-actions/amazon-ecr-login.