Source in Github to Firebase: No secrets, no keys, no tricks

minherz
Google Cloud - Community
4 min readJul 1, 2024

Do you manage your content in Github and host in Firebase? Then this post is for you. Feel free to skip it if you are not.

In spring 2024 I decided to start my own blog, with the original name leoy.blog. Following advice from my colleagues I decided to host in Firebase. And where many engineers place sources for free? Yes, in Github. Being lazy I wanted to automate uploading of all changes to the blog source and content to Firebase hosting. And I came across Deploy to Firebase Hosting. It has everything that I needed except one thing: it required providing a Google Cloud service account key value as an input parameter. Using service account keys is a known security concern. In addition to key procurement and further copying the key to Github public repo, there are challenges with key refreshing. Luckily Google took care of it a couple of years ago describing the keyless authentication method for Github actions. All I had to do is to fork the repo and to make a change allowing it to accept a path to the service account key file instead of providing the value itself. Below I describe all the steps that I did to configure the deployment pipeline for my blog repository.

1. Adding authentication step

To enable keyless authentication the Github workflow has to have the auth step. This step is responsible for creating temporary credentials that will be used for authentication. If you use the FirebaseExtended/action-hosting-deploy action, it is coded in the workflow like the following (or similar):

- uses: FirebaseExtended/action-hosting-deploy@v0
with:
channelId: live
firebaseServiceAccount: "${{ secrets.SA_KEY_JSON }}"
projectId: ${{ secrets.FIREBASE_PROJECT_ID }}

I hope you use Github secrets to store these values. Anyway, replace this step with the following two:

- id: 'auth'
uses: google-github-actions/auth@v2
with:
token_format: 'access_token'
workload_identity_provider: ${{ secrets.FIREBASE_IDENTITY_PROVIDER }}
service_account: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
create_credentials_file: true
- uses: minherz/firebase-hosting-deploy@v1
with:
channelId: live
firebaseServiceAccount: "${{ steps.auth.outputs.credentials_file_path }}"
projectId: ${{ secrets.FIREBASE_PROJECT_ID }}

I will explain what these new secrets mean and how to configure them in the following steps.

Oh, and do not forget to upgrade your “checkout step” to version 4:

- uses: 'actions/checkout@v4'

The change replaced the use of service account key with the use of service account name. Next you will have to configure Google Cloud identity provider that will handle keys for you. You will need to have gcloud CLI to perform these steps.

2. Configure Workload identity provider

You can create the workload identity provider in any project. Having all things tidy has its benefits and I defined my identity provider in the same Firebase project where I host my blog. The following commands assume that the project ID of that project is defined in the PROJECT_ID environment variable. To execute the commands you have to have the role of Workload Identity Pool Admin on PROJECT_ID project or ask your Google Cloud organization administrator to grant you the roles/iam.workloadIdentityPoolAdmin IAM role.

Create a new workload identity pool:

gcloud iam workload-identity-pools create "github-actions-pool" \
- project="${PROJECT_ID}" \
- location="global" \
- display-name="Workload identity pool for keyless authentication"

Next, create a new OIDC provider in this pool:

gcloud iam workload-identity-pools providers create-oidc "github-actions-oidc" \
- project="${PROJECT_ID}" \
- location="global" \
- workload-identity-pool="github-actions-pool" \
- display-name="Github actions OIDC provider" \
- issuer-uri="https://token.actions.githubusercontent.com" \
- attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository,attribute.actor=assertion.actor,attribute.aud=assertion.aud" \
- attribute-condition="assertion.repository_owner=='YOUR_ORG'"

The OIDC provider uses basic attribute mapping and recommended conditions. Replace YOUR_ORG with the name of the organization that owns your repository. You can add more conditions to further secure your OIDC provider.

3. Configure IAM and set up Github secrets

In this step we first get the name of the identity provider. Then we grant the identity provider permission to authenticate as our service account (a.k.a. impersonate). And finally configure the identity provider and the service account in our Github repo as secrets that we used in Step 1.

If you don’t have the Google Cloud service account that can be used to deploy to Firebase hosting, follow these instructions to create one. Then set its full email address as the FIREBASE_SA_NAME environment variable.

Run the following command to get the fully qualified name of the identity provider.

PRINCIPAL=$(gcloud iam workload-identity-pools providers describe \
github-actions-oidc \
- workload-identity-pool="github-actions-pool" \
- location="global" \
- project=${PROJECT_ID} \
- format="value(name)")

Now grant the provider with permissions to impersonate the service account:

gcloud iam service-accounts add-iam-policy-binding \
"${FIREBASE_SA_NAME}" \
- project="${PROJECT_ID}" \
- role="roles/iam.workloadIdentityUser" \
- member="principalSet://iam.googleapis.com/${PRINCIPAL}"

What is left is to follow the Github manual to configure two secrets in the Github repository.

Set up the secret FIREBASE_IDENTITY_PROVIDER to the value of PRINCIPAL and FIREBASE_SERVICE_ACCOUNT to the value of FIREBASE_SA_NAME. Your secure deployment from Github to Firebase hosting is ready to go.

* * *

In Google I/O 2024, Google announced the public preview launch of Firebase App Hosting. App Hosting offers painless GitHub integration powered by Developer Connect and the new Firebase App Hosting GitHub app. You may want to use this pre-release functionality instead of the Github action I described. There are three different between these two methods:

  1. The new method is still in preview and its functionality and pricing model can change before it is generally available
  2. The new method is focused on complex server-rendering Web applications that utilize additional Firebase functionality that may be overhead to what you need, especially if, like me, you are looking to host a static Website
  3. The new method does not support Firebase hosting channels which prevent you from staging your changes before making them public.

You can read more about my experience with Firebase hosting in my series of posts about Hugo.

Read the following materials for more information:

--

--

minherz
Google Cloud - Community

DevRel Engineer at Google Cloud. The opinions posted here are my own, and not those of my company.