yeti logo icon
Close Icon
contact us
Yeti postage stamp
We'll reply within 24 hours.
Thank you! Your message has been received!
A yeti hand giving a thumb's up
Oops! Something went wrong while submitting the form.

Authorizing Google Cloud Platform Service Accounts from a Docker Container Running in Heroku

By
Will Harris
-
July 28, 2021

In this post I'm going to cover how we dealt with authorizing a Google Cloud Platform service account from a Docker image running in Heroku's cloud platform.

Recently we implemented a system using Google Cloud's Pub/Sub messaging service and My Business API to capture updates to Google Places businesses with a NodeJS server. Google Cloud requires applications to authenticate as service accounts in order to interact with their APIs. Service accounts allow a non-human user to authenticate and access Google's APIs.

The Google Pub/Sub client library loads service account credentials into a GOOGLE_APPLICATION_CREDENTIALS environment variable from an absolute path to a .json file in the filesystem. This meant that we needed access to a service-account.json file in our application's environment in order to authenticate and authorize our service account.

One of our other main requirements for this application was that we deploy the app to Heroku. We also deploy most of our projects in Docker containers, so we needed to figure out how to add the service account's credentials file to the Docker container's filesystem during Heroku's build process.

First (Unsuccessful) Attempts

The first solution we tried was adding the contents of the service-account.json file as a Heroku environment variable. Heroku accepted and saved the value, but it was not available within our Docker container. When we tried to read the value, we got a parsing error at the first " in the file. It seemed as though Heroku was unable to parse raw JSON stored in an environment variable.

Another solution we tried was using one of the "off-the-shelf" Heroku buildpacks that claim to help with this issue. They're both essentially just shell scripts that echo the value stored in a GOOGLE_CREDENTIALS environment variable into a file in the .profile.d/* directory. This directory is similar to /etc/profile.d in standard Unix filesystems, where shell startup files are typically stored.

Unfortunately, one of the current limitations of Heroku's "container" stack is that it does not run .profile or .profile.d/* scripts when booting a dyno. So neither of the Heroku buildpacks worked for us.

Solution

We're still dealing with a Linux filesystem in Docker though, and the filesystem has access to Heroku environment variables during the build process. So the question became: how can we output the contents of a Heroku environment variable to a .json file in the container's filesystem during the build process?

We ended up writing a small shell script that executes in our production Dockerfile to do just this.

First we added the contents of the service-account.json file to a GOOGLE_CREDENTIALS environment variable in Heroku.

We then added the following script ( add-google-credentials.sh ) to the root of our project:

#!/bin/sh

echo "Generating google-credentials.json from Heroku environment variable"

echo $GOOGLE_CREDENTIALS > google-credentials.json

exec "$@"

This script takes the value stored in the GOOGLE_CREDENTIALS environment variable and echoes it into a new .json file in the Docker container's filesystem, creating a fresh copy each time we build the container.

The exec "$@" line replaces the parent process with the current child process. This is important in Docker containers for signals to be proxied correctly, otherwise we might end up with data loss or orphan processes. More information about this can be found in this interesting Unix Stack Overflow discussion.

Finally we needed to actually call this script during the Docker build process, so we added the following two commands to our Dockerfile :

COPY add-google-credentials.sh /app/add-google-credentials.sh
ENTRYPOINT ["sh", "/app/add-google-credentials.sh"]

The first command copies the shell script from our project's root directory into the container's root directory.

The second command basically says "every time this container builds, run the add-google-credentials.sh script."

These changes allowed us to store a GOOGLE_APPLICATION_CREDENTIALS environment variable in Heroku with the value google-credentials.json, which is an absolute path to the service account credentials inside our container. After putting this together we were finally able to interact with the Google Cloud APIs that we needed for this project! 🎉

‍

Will is a software developer at Yeti. In his free time you can find him riding bikes, hiking in the hills and searching  Bay Area restaurants for delicious vegetarian food.

You Might also like...

code on a computerManaging Perisistent Browser Data with useSyncExternalStore

Struggling to keep React state in sync across tabs and sessions? Learn how to use useSyncExternalStore to manage state persistence with localStorage and sessionStorage—without complex state management libraries. Improve performance and streamline your app’s state logic.

software developerReact Hooks 102: When to Avoid useEffect

Overusing useEffect in React can lead to inefficient components and performance issues. In this post, learn when to avoid useEffect and discover better alternatives for managing state, calculations, and user events. Optimize your React code with best practices for cleaner, faster applications.

software developer codingFintech Security with GraphQL Shield

Securing fintech applications is crucial, and GraphQL’s flexibility can be a security risk if not properly managed. GraphQL Shield helps enforce fine-grained authorization using role-based (RBAC) and attribute-based (ABAC) access control. In this post, we’ll explore why authorization matters, how to implement secure, composable rules, and walk through a demo app showcasing best practices.

Browse all Blog Articles

Ready for your new product adventure?

Let's Get Started