From e62b977b371530c566ae0b3a7fafdc9264d06981 Mon Sep 17 00:00:00 2001 From: thesayyn Date: Tue, 28 May 2024 15:29:05 -0700 Subject: [PATCH] credential-helpers --- .bazelrc | 7 ++++-- .github/workflows/ci.yaml | 20 +++++++++++++++++- examples/credential_helper/auth.sh | 34 ++++++++++++++++++++++++------ oci/private/pull.bzl | 12 +++++++++-- 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/.bazelrc b/.bazelrc index 6ab87247..3474ab97 100644 --- a/.bazelrc +++ b/.bazelrc @@ -9,8 +9,11 @@ test --test_env=DOCKER_HOST # Disable bzlmod lockfile common --lockfile_mode=off -# TODO(2.0): enable once we drop support for Bazel 5. -# common --credential_helper=public.ecr.aws=%workspace%/examples/credential_helper/auth.sh +# On bazel 6.4.0 these are needed to successfully fetch images. +common:needs_credential_helpers --credential_helper=public.ecr.aws=%workspace%/examples/credential_helper/auth.sh +common:needs_credential_helpers --credential_helper=index.docker.io=%workspace%/examples/credential_helper/auth.sh +common:needs_credential_helpers --credential_helper=docker.elastic.co=%workspace%/examples/credential_helper/auth.sh +common:needs_credential_helpers --credential_helper_cache_duration=0 # Load any settings specific to the current user. # .bazelrc.user should appear in .gitignore so that settings are not shared with team members diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index cffcb60c..cea3045f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -103,6 +103,18 @@ jobs: if: matrix.bzlmodEnabled run: echo "bzlmod_flag=--enable_bzlmod" >> $GITHUB_OUTPUT + - name: Set credential helpers flag + # Add --config needs_credential_helpers to add additional credential helpers + # to fetch from registries with HTTP headers set by credential helpers. + id: set_credential_helper_flag + if: matrix.bazelversion == '6.4.0' + run: echo "credential_helper_flag=--config=needs_credential_helpers" >> $GITHUB_OUTPUT + + - name: Setup credential helpers + uses: imjasonh/setup-crane@v0.3 + with: + version: "v0.19.1" + - name: Configure Bazel version working-directory: ${{ matrix.folder }} run: echo "${{ matrix.bazelversion }}" > .bazelversion @@ -146,4 +158,10 @@ jobs: env: # Bazelisk will download bazel to here, ensure it is cached between runs. XDG_CACHE_HOME: ~/.cache/bazel-repo - run: bazel --bazelrc=$GITHUB_WORKSPACE/.github/workflows/ci.bazelrc --bazelrc=.bazelrc test ${{ steps.set_bzlmod_flag.outputs.bzlmod_flag }} //... + run: | + bazel \ + --bazelrc=$GITHUB_WORKSPACE/.github/workflows/ci.bazelrc \ + --bazelrc=.bazelrc \ + test //... \ + ${{ steps.set_bzlmod_flag.outputs.bzlmod_flag }} \ + ${{ steps.set_credential_helper_flag.outputs.credential_helper_flag }} diff --git a/examples/credential_helper/auth.sh b/examples/credential_helper/auth.sh index 2bdd8f2b..531c1f0d 100755 --- a/examples/credential_helper/auth.sh +++ b/examples/credential_helper/auth.sh @@ -1,14 +1,15 @@ #!/usr/bin/env bash # Requirements -# - curl +# - crane # - jq # - awk # # ./examples/credential_helper/auth.sh <<< '{"uri":"https://public.ecr.aws/token/?scope\u003drepository:lambda/python:pull\u0026service\u003dpublic.ecr.aws"}' # ./examples/credential_helper/auth.sh <<< '{"uri":"https://public.ecr.aws/v2/lambda/python/manifests/3.11.2024.01.25.10"}' + function log () { - echo "$1" >> /tmp/oci_auth.log + echo $@ >> "/tmp/oci_auth.log" } log "" @@ -20,18 +21,39 @@ log "Payload: $input" uri=$(jq -r ".uri" <<< $input) log "URI: $uri" -host="$(echo $uri | awk -F[/:] '{print $4}')" +host="$(awk -F[/:] '{print $4}' <<< $uri)" log "Host: $host" + if [[ $input == *"/token"* ]]; then log "Auth: None" echo "{}" - exit 0 + exit 1 fi +repository=$(awk -F'^https?://|v2/|/manifests|/blobs' '{print $2 $3}' <<< "$uri") +log "Repository: $repository" + + +ACCEPTED_MEDIA_TYPES='[ + "application/vnd.docker.distribution.manifest.v2+json", + "application/vnd.docker.distribution.manifest.list.v2+json", + "application/vnd.oci.image.manifest.v1+json", + "application/vnd.oci.image.index.v1+json" +]' + + # This will write the response to stdout in a format that Bazels credential helper protocol understands. # Since this is called by Bazel, users won't bee seeing output of this. -curl -fsSL https://$host/token | jq '{headers:{"Authorization": [("Bearer " + .token)]}}' +crane auth token "$repository" | +jq --argjson accept "$ACCEPTED_MEDIA_TYPES" \ +'{headers: {Authorization: [("Bearer " + .token)], Accept: [($accept | join(", "))], "Docker-Distribution-API-Version": ["registry/2.0"] }}' + +if [[ $? != 0 ]]; then + log "Auth: Failed" + exit 1 +fi log "Auth: Complete" -# Alternatively you can call an external program such as `docker-credential-ecr-login` to perform the token exchange. \ No newline at end of file + +# Alternatively you can call an external program such as `docker-credential-ecr-login` to perform the token exchange. diff --git a/oci/private/pull.bzl b/oci/private/pull.bzl index afb78dcf..cc826dfd 100644 --- a/oci/private/pull.bzl +++ b/oci/private/pull.bzl @@ -38,10 +38,16 @@ _IMAGE_REFERENCE_ATTRS = { SCHEMA1_ERROR = """\ The registry sent a manifest with schemaVersion=1. This commonly occurs when fetching from a registry that needs the Docker-Distribution-API-Version header to be set. +See: https://github.com/bazel-contrib/rules_oci/blob/main/docs/pull.md#authentication-using-credential-helpers """ OCI_MEDIA_TYPE_OR_AUTHN_ERROR = """\ -Unable to retrieve the manifest. This could be due to authentication problems or an attempt to fetch an image with OCI image media types. +Unable to retrieve the image manifest. This could be due to authentication problems or an attempt to fetch an image with OCI image media types. +See: https://github.com/bazel-contrib/rules_oci/blob/main/docs/pull.md#authentication-using-credential-helpers +""" + +OCI_MEDIA_TYPE_OR_AUTHN_ERROR_BAZEL7 = """\ +Unable to retrieve the image manifest. This could be due to authentication problems. """ # Supported media types @@ -142,7 +148,9 @@ def _download_manifest(rctx, authn, identifier, output): explanation = authn.explain() if explanation: util.warning(rctx, explanation) - fail(OCI_MEDIA_TYPE_OR_AUTHN_ERROR) + fail( + OCI_MEDIA_TYPE_OR_AUTHN_ERROR_BAZEL7 if versions.is_at_least("7.1.0", versions.get()) else OCI_MEDIA_TYPE_OR_AUTHN_ERROR, + ) return manifest, len(bytes), digest