If you use Artifact Registry to store private Java/Node/Python packages and Cloud Build to compile your code before deployment, you'll quickly discover that the Docker container it creates can't access the Artifact Registry by default.
This solution was discovered after a day of trial and error so I hope it saves you from the same forehead-to-desk frustration.
Step by Step
Deploy your private packages to the Artifact Registry.
Create a Service Account that only has access to read from the Artifact Registry. In my case I created
artifact-registry-reader@<PROJECT>.iam.gserviceaccount.com
and gave it access to the Artifact Registry repository as an "Artifact Registry Reader."Edit the newly created
artifact-registry-reader@<PROJECT>.iam.gserviceaccount.com
Service Account and under permissions add your Cloud Builder Service Account (<PROJECT_ID>@cloudbuild.gserviceaccount.com
) as a Principal and grant it the "Service Account Token Creator" role. [Note, this works even if you use Cloud Build in a separate project.]Next, your cloudbuild.yaml file should look something like this:
steps: # Step 1: Generate an Access Token and save it # # Here we call `gcloud auth print-access-token` to impersonate the service account # we created above and to output a short-lived access token to the default volume # `/workspace/access_token`. This is accessible in subsequent steps. # - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk:slim' args: - '-c' - > gcloud auth print-access-token --impersonate-service-account artifact-registry-reader@<PROJECT>.iam.gserviceaccount.com > /workspace/access_token entrypoint: sh # Step 2: Build our Docker container # # We build the Docker container passing the access token we generated in Step 1 as # the `--build-arg` `TOKEN`. It's then accessible within the Dockerfile using # `ARG TOKEN` # - name: gcr.io/cloud-builders/docker args: - '-c' - > docker build -t us-docker.pkg.dev/<PROJECT>/services/frontend:latest --build-arg TOKEN=$(cat /workspace/access_token) -f ./docker/prod/Dockerfile . && docker push us-docker.pkg.dev/<PROJECT>/services/frontend entrypoint: sh
FIVE. This next step is specific to private npm packages in the Artifact Registry, but my app has a partial .npmrc file with the following content:
@<NAMESPACE>:registry=https://us-npm.pkg.dev/<PROJECT>/npm/ //us-npm.pkg.dev/<PROJECT>/npm/:username=oauth2accesstoken //us-npm.pkg.dev/<PROJECT>/npm/:email=artifact-registry-reader@<PROJECT>.iam.gserviceaccount.com //us-npm.pkg.dev/<PROJECT>/npm/:always-auth=true
[Note: All that's missing is the :_authToken
line]
SIX. Finally my Dockerfile uses the minted token to update my .npmrc file, giving it access to pull private npm packages from the Artifact Registry.
ARG NODE_IMAGE=node:17.2-alpine FROM ${NODE_IMAGE} as base ENV APP_PORT=8080 ENV WORKDIR=/usr/src/app ENV NODE_ENV=production FROM base AS builder # Create our WORKDIR RUN mkdir -p ${WORKDIR} # Set the current working directory WORKDIR ${WORKDIR} # Copy the files we need COPY --chown=node:node package.json ./ COPY --chown=node:node ts*.json ./ COPY --chown=node:node .npmrc ./ COPY --chown=node:node src ./src ####################### # MAGIC HAPPENS HERE # Append our access token to the .npmrc file and the container will now be # authorized to download packages from the Artifact Registry # # IMPORTANT! Declare the TOKEN build arg so that it's accessible ####################### ARG TOKEN RUN echo "//us-npm.pkg.dev/<PROJECT>/npm/:_authToken=\"$TOKEN\"" >> .npmrc RUN npm install RUN npm run build EXPOSE ${APP_PORT}/tcp CMD ["cd", "${WORKDIR}"] ENTRYPOINT ["npm", "run", "start"]
This is NPM specific but you can transfer these concepts to any other GCP resource to give your Docker build containers secure access with short-lived tokens to any resource in your project(s).
Top comments (0)