Skip to main content
How to Use Secrets in GitHub Actions Workflows
6 min read

How to Use Secrets in GitHub Actions Workflows

CICUBE ANALYTICS INSIGHTS
Engineering Velocity: 25% Team Time Lost to CI Issues
View Platform →
3.5h
Time Saved/Dev/Week
40%
Faster Releases
Click for next insight

Introduction

I would like to go through some thoughts on how you can better maintain and make use of secrets with your GitHub Actions workflows. Once I had read through the GitHub documentation, I did a simple breakdown of the core activities before giving a number of recommendations:

Steps we will cover in this article:

Overview of Secrets

In GitHub, these are called secrets, which are stored-sensitive data at the organization, repository, or environment level. Secrets might be used within workflows but would remain accessible only when they have explicitly been mentioned in a workflow file to protect API tokens, credentials, and other sensitive data.

How to Define Secrets in GitHub

First of all, to get started with secrets in GitHub Actions, you have to define them at organization, repository, or environment levels. Here is how you can create and manage them:

A Secret Creation for a Repository:

  1. On GitHub, navigate to the homepage of your repository.
  2. Under the repository name, click Settings.
  3. In the left sidebar, click Secrets and variables, then select Actions.
  4. Click the Secrets tab.
  5. Click New repository secret.
  6. In the Name field give your secret a name like API_KEY.
  7. Under Secret, fill in the value of the secret, e.g., the actual API key or token.
  8. Click Add secret.

Once added, your secret will be stored securely and can be utilized within your workflows.

Creating Secrets for an Organization:

  1. On GitHub, navigate to your organization's main page.
  2. Click Settings under the organization name.
  3. In the left sidebar, click Secrets and variables, then select Actions.
  4. Click the Secrets tab.
  5. Click New organization secret.
  6. Type an appropriate name for your secret, and fill in its value.
  7. In the Repository access dropdown, select which repositories can access this secret.
  8. Click Add secret.

This allows you to share secrets between multiple repositories and reduces the duplication of it.

Creating Secrets for an Environment:

  1. Navigate to the main page of the repository on GitHub.
  2. Under the repository name, click Settings.
  3. Click Environments in the left sidebar.
  4. From the dropdown list, select the environment you would like to create a secret in.
  5. Under Environment secrets, click Add secret.
  6. Name your secret, and add its value, then click Add secret.

The environment-to-secrets support an extra layer of approval in that they will be covered under approval before access is granted to workflows.

Examples of Secrets:

Here are some robust pragmatic ways to apply secrets within a GitHub Actions workflow:

Example 1: Using Secrets as Environment Variables

The following example uses a secret as an environment variable in a step in a workflow:

name: Use secret as environment variable
on: push

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run a command with a secret
env:
API_KEY: ${{ secrets.API_KEY }} # Use your secret here
run: echo "Using secret in a command"

In this example, environment variable API_KEY secret was used in the job. The secret is securely passed to the worflow and can be set in the script without showing sensitive data.

Example 2: Using Secrets as Workflow Inputs

This is how you can perform it in case you want to pass a secret as an input for an action:

name: Use secret as input
on: push

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Use secret in an action
uses: some-action/with-secrets@v1
with:
api_key: ${{ secrets.API_KEY }} # Set secret as input

For this action, api_key is a required input; hence, we safely pass the API_KEY secret to satisfy this requirement.

Example 3: Decrypting Large Secrets

Another alternative, especially for large secrets bigger than 48KB, is the encryption of a file and its storage in a repository, then using a passphrase stored as a secret from GitHub to decrypt that particular file during workflow execution.

  1. encrypt the secret file:
    gpg --symmetric --cipher-algo AES256 my_secret.json
    This command will create the encrypted file called my_secret.json.gpg.
  2. Store the passphrase as secret in GitHub. For example, LARGE_SECRET_PASSPHRASE.
  3. Add a workflow step to decrypt the file:
name: Decrypt large secret
on: push

jobs:
decrypt-job:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Decrypt large secret
// higliht-next-line
run: ./decrypt_secret.sh
env:
LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }}

This step uses that passphrase as stored in the secret LARGE_SECRET_PASSPHRASE to decrypt the my_secret.json.gpg file.

#!/bin/sh
gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" \
--output $HOME/secrets/my_secret.json my_secret.json.gpg

Example 4: Base64 Encoding a Binary Blob

We can store binary blobs, such as certificates, as Base64-encoded strings. Here is how it's done :

  1. Encode your file to Base64:

    base64 -i cert.der -o cert.base64

    This command takes the cert.der binary file and transforms it into a base64-encoded string, placing the output into cert.base64:.

  2. Store the Base64 string as a secret in GitHub.

  3. Decode a Base64 secret in a workflow:

name: Retrieve Base64 secret
on: push

jobs:
decode-secret:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Decode Base64 secret
env:
CERTIFICATE_BASE64: ${{ secrets.CERTIFICATE_BASE64 }}
run: |
echo $CERTIFICATE_BASE64 | base64 --decode > cert.der

In this example, a Base64-encoded certificate is stored as a secret and decoded back to its original binary form inside the workflow.

Example 5: Conditional Step with Secrets

Secrets cannot be used directly within conditional if statements. However, we can make a secret a job-level environment variable and then use that value for conditional logic:

name: Conditional secret usage
on: push

jobs:
build:
runs-on: ubuntu-latest
env:
SECRET_FLAG: ${{ secrets.FLAG_SECRET }}
steps:
- name: Conditional step
if: env.SECRET_FLAG == 'enabled'
run: echo "The secret flag is enabled!"

Besides the above example here the secret FLAG_SECRET is assigned to an environment variable SECRET_FLAG controlling via its existence whether or not a conditional step will be executed:

Conclusion

Secret usage within GitHub Actions workflows offers a layer of protection to sensitive data while efficiently automating. Setting these secrets across higher levels of granularity-organization, repository, and environment-will give you the flexibility of access control. Be it API keys, credentials, or encrypted files, this should give one a good basis for security and reliability within the workflow.