GitHub Actions enables you to automate workflows for your GitHub-hosted repositories. With the Github Actions plugin, you can fetch secrets directly from Akeyless into your workflows. This guide describes how to use our various Authentication Methods to fetch Static, Dynamic, and Rotated secrets, as well as SSH and PKI certificates, from Akeyless.
⚠️ Important: SettingACTIONS_RUNNER_DEBUG
totrue
can expose sensitive information in your error logs. Use with caution.
Name | Required | Type | Value |
---|---|---|---|
access-id | No | string |
The access id for your auth method. If token is not provided this field is required. |
access-type | No | string |
Default: jwt . The login method to use. Valid options are jwt /access_key /universal_identity /aws_iam /azure_ad /gcp /k8s . |
token | No | string |
A valid Akeyless token. |
api-url | No | string |
Default: https://api.akeyless.io . The API endpoint to use. |
static-secrets | No | string |
A YAML list representing static secrets to fetch. |
dynamic-secrets | No | string |
A YAML list representing dynamic secrets to fetch. |
rotated-secrets | No | string |
A YAML list representing rotated secrets to fetch. |
ssh-certificates | No | string |
A YAML list representing ssh certificates to fetch. |
pki-certificates | No | string |
A YAML list representing pki certificates to fetch. |
export-secrets-to-outputs | No | boolean |
Default: true . True/False to denote if secrets should be exported as environment variables. |
export-secrets-to-environment | No | boolean |
Default: true . True/False to denote if secrets should be exported as action outputs. |
access-key | No | string |
Access key (relevant only for access-type=access_key ). |
gcp-audience | No | string |
GCP audience to use in signed JWT (relevant only for access-type=gcp ). |
gateway-url | No | string |
Gateway URL for the K8s authenticated (relevant only for access-type=k8s /oauth2 ). |
k8s-auth-config-name | No | string |
The K8s Auth config name (relevant only for access-type=k8s ). |
uid_token | No | string |
The Universal Identity token (relevant only for access-type=universal_identity). |
The job outputs are determined by the values set in your static-secrets
/dynamic-secrets
/rotated-secrets
/ssh-certificates
/pki-certificates
inputs.
The default behavior will create a single output/env variable that uses the name you set for the output.
Name | Value |
---|---|
outputs | use ${{ steps.JOB_NAME.outputs.SECRET_NAME }} |
environment variables | use ${{ env.SECRET_NAME }} |
For each Akeyless secret you can extract a specific field out of the JSON by adding the field key name.
For example, for the following static secret value name "github-static-secret-json":
{
"imp": "needed",
"no": "no_need"
}
We can use:
- name: "/akeyless-github-action/github-static-secret-json"
output-name: "my_first_secret"
key: "imp"
and in "steps.output.my_first_secret" the value will be "needed".
There is an option to get more detailed logs from the Akeyless Github Action by setting the ACTIONS_RUNNER_DEBUG
secret or variable to true
in the repository that contains the workflow.
⚠️ Important: SettingACTIONS_RUNNER_DEBUG
totrue
can expose sensitive information in your error logs. Use with caution.
Although this repository's workflows use placeholder values, it is still a real Akeyless account and real providers. The approaches demonstrated are still valid as-is for real implementations. Use these to your advantage!
Static secrets are the easiest to use. Just define the secret's path and the secret's output.
jobs:
fetch_secrets:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Fetch static secrets from Akeyless
uses: akeyless-community/akeyless-github-action@v1.0.1
id: fetch-secrets
with:
access-id: ${{ vars.AKEYLESS_ACCESS_ID }}
access-type: jwt
static-secrets: |
- name: "/akeyless-github-action/github-static-secret-json"
output-name: "my_first_secret"
key: "imp"
- name: "/akeyless-github-action/github-static-secret"
output-name: "my_second_secret"
- name: Use Akeyless secret
run: |
echo "Step Outputs"
echo "my_first_secret: ${{ steps.fetch-secrets.outputs.my_first_secret }}" >> secrets.txt
echo "my_second_secret: ${{ steps.fetch-secrets.outputs.my_second_secret }}" >> secrets.txt
echo "Environment Variables"
echo "my_first_secret: ${{ env.my_first_secret }}" >> secrets.txt
echo "my_second_secret: ${{ env.my_second_secret }}" >> secrets.txt
This example demonstrates fetching an AWS Dynamic Secret from Akeyless.
fetch_aws_dynamic_secrets:
runs-on: ubuntu-latest
name: Fetch AWS dynamic secrets
permissions:
id-token: write
contents: read
steps:
- name: Fetch dynamic secrets from Akeyless
id: fetch-dynamic-secrets
uses: akeyless-community/akeyless-github-action@v1.0.1
with:
access-id: ${{vars.AKEYLESS_ACCESS_ID}}
dynamic-secrets: |
- name: "/path/to/dynamic/aws/secret"
output-name: "aws_dynamic_secrets"
access-type: jwt
# ********* KEY TAKEAWAY ********* #
# STEP 1 - Export Dynamic Secret's keys to env vars
- name: Export Secrets to Environment
run: |
echo '${{ steps.fetch-dynamic-secrets.outputs.aws_dynamic_secrets }}' | jq -r 'to_entries|map("AWS_\(.key|ascii_upcase)=\(.value|tostring)")|.[]' >> $GITHUB_ENV
# STEP 2 - You can now access each secret separately as environment variables
- name: Verify Vars
run: |
echo "access_key_id: ${{ env.AWS_ACCESS_KEY_ID }}" >> secrets.txt
echo "id: ${{ env.AWS_ID }}" >> secrets.txt
echo "secret_access_key: ${{ env.AWS_SECRET_ACCESS_KEY }}" >> secrets.txt
echo "security_token: ${{ env.AWS_SECURITY_TOKEN }}" >> secrets.txt
echo "ttl_in_minutes: ${{ env.AWS_TTL_IN_MINUTES }}" >> secrets.txt
echo "type: ${{ env.AWS_TYPE }}" >> secrets.txt
echo "user: ${{ env.AWS_USER }}" >> secrets.txt
This example demonstrates fetching an AWS Rotated Secret from Akeyless.
fetch_aws_rotated_secrets:
runs-on: ubuntu-latest
name: Fetch AWS rotated secrets
permissions:
id-token: write
contents: read
steps:
- name: Fetch rotated secrets from Akeyless
id: fetch-rotated-secrets
uses: akeyless-community/akeyless-github-action@v1.0.1
with:
access-id: ${{ vars.AKEYLESS_ACCESS_ID }}
access-type: jwt
rotated-secrets: |
- name: "/path/to/rotated/aws/secret"
output-name: "aws_rotated_secrets"
fetch_ssh_secrets:
runs-on: ubuntu-latest
name: Fetch ssh certificate
permissions:
id-token: write
contents: read
steps:
- name: Fetch ssh certificates from Akeyless
id: fetch-ssh-certificate
uses: akeyless-community/akeyless-github-action@v1.0.1
with:
access-type: jwt
ssh-certificate-secrets: |
- name: "/path/to/ssh/secret1"
output-name: "ssh_secret"
cert-username: "ubuntu",
public-key-data: "public_key_data",
- name: "/path/to/ssh/secret2"
output-name: "ssh_secret2"
cert-username: "ubuntu",
public-key-data: "public_key_data",
fetch_pki_secrets:
runs-on: ubuntu-latest
name: Fetch pki certificate
permissions:
id-token: write
contents: read
steps:
- name: Fetch pki certificates from Akeyless
id: fetch-pki-certificates
uses: akeyless-community/akeyless-github-action@v1.0.1
with:
access-type: jwt
pki-certificate-secrets: |
- name: "/path/to/pki/secret1"
output-name: "pki_secret"
csr-data-base64: "csr_data_base64"
- name: "/path/to/pki/secret2"
output-name: "pki_secret2"
csr-data-base64: "csr_data_base64"
By default, the action sets the environment variable value to the entire JSON string in the secret value. Set parse-json-secrets
to true
to create environment variables for each key/value pair in the secret JSON.
- If the JSON uses case-sensitive keys such as "name" and "Name", the action will have duplicate name conflicts. In this case, set
parse-json-secrets
to false and parse the JSON secret value separately. - You can still use the
key
andoutput-name
for extracting a specific key with a specific name. - The default env name will be the path to the secret. If your secret name is
/dev/test
, the default name will beenv.DEV_TEST_{key}
.
For example, for a secret with JSON values:
{
"key1":"val1",
"key2":"val2"
}
with:
access-id: ${{ vars.AKEYLESS_ACCESS_ID }}
access-type: jwt
static-secrets: |
- name: "/path/to-secret"
- name: "/path/to-secret"
key: "key1"
output-name: "SECRET"
parse-json-secrets: true
- name: Use Akeyless secret
run: |
echo "key1: ${{ env.PATH_TO-SECRET_KEY1 }} >> secrets.txt
echo "key2: ${{ env.PATH_TO-SECRET_KEY2 }}" >> secrets.txt
echo "key1: ${{ env.SECRET }}" >> secrets.txt
If you don't want the prefix to be the secret name you can add prefix-json-secrets
with the prefix you would like:
with:
access-id: ${{ vars.AKEYLESS_ACCESS_ID }}
access-type: jwt
static-secrets: |
- name: "/path/to-secret"
prefix-json-secrets: "Mysql"
parse-json-secrets: true
- name: Use Akeyless secret
run: |
echo "val1 == ${{ env.MYSQL_KEY1 }}" >> secrets.txt
echo "val2 == ${{ env.MYSQL_KEY2 }}" >> secrets.txt
This action supports the following authentication methods. Pay close attention as for some authentication methods there are extra inputs needed aside from the access-type
.
with:
access-id: ${{ vars.AKEYLESS_ACCESS_ID }}
access-type: jwt
static-secrets: |
- name: "/akeyless-github-action/github-static-secret-json"
output-name: "my_first_secret"
key: "imp"
The default usage relies on using the GitHub JWT to login to Akeyless. To make this available, you have to configure it in your job workflow:
jobs:
my_job:
#---------Required---------#
permissions:
id-token: write
contents: read
#--------------------------#
⚠️ If this is not present, the akeyless-action step will fail with the following error:Failed to login to Akeyless: Error: Failed to fetch Github JWT: Error message: Unable to get ACTIONS\_ID\_TOKEN\_REQUEST\_URL env variable
.
with:
access-id: ${{ vars.AKEYLESS_ACCESS_ID }}
access-type: aws_iam
static-secrets: |
- name: "/akeyless-github-action/github-static-secret-json"
output-name: "my_first_secret"
key: "imp"
with:
access-id: ${{ vars.AKEYLESS_ACCESS_ID }}
access-type: azure_ad
static-secrets: |
- name: "/akeyless-github-action/github-static-secret-json"
output-name: "my_first_secret"
key: "imp"
with:
access-id: ${{ vars.AKEYLESS_ACCESS_ID }}
access-type: gcp
gcp-audience: "gcp-audience"
static-secrets: |
- name: "/akeyless-github-action/github-static-secret-json"
output-name: "my_first_secret"
key: "imp"
with:
access-id: ${{ vars.AKEYLESS_ACCESS_ID }}
access-type: k8s
k8s-auth-config-name: "k8s-auth-config-name"
gateway-url: "gateway-url"
static-secrets: |
- name: "/akeyless-github-action/github-static-secret-json"
output-name: "my_first_secret"
key: "imp"
with:
access-id: ${{ vars.AKEYLESS_ACCESS_ID }}
access-type: universal_identity
uid_token: "uid_token"
static-secrets: |
- name: "/akeyless-github-action/github-static-secret-json"
output-name: "my_first_secret"
key: "imp"
with:
access-id: ${{ vars.AKEYLESS_ACCESS_ID }}
access-key: ${{ secrets.AKEYLESS_ACCESS_KEY }}
access-type: access_key
static-secrets: |
- name: "/akeyless-github-action/github-static-secret-json"
output-name: "my_first_secret"
key: "imp"
with:
token: ${{ steps.JOB_NAME.outputs.token }}
static-secrets: |
- name: "/akeyless-github-action/github-static-secret-json"
output-name: "my_first_secret"
key: "imp"
When TLS is configured on the Akeyless Gateway, you need to also pass the CA certificate (the CA certificate should be in PEM format):
with:
access-id: ${{ vars.AKEYLESS_ACCESS_ID }}
access-type: universal_identity
uid_token: "uid_token"
ca-certificate: ${{ secrets.AKEYLESS_CA_CERTIFICATE }}
static-secrets: |
- name: "/akeyless-github-action/github-static-secret-json"
output-name: "my_first_secret"
key: "imp"
To configure Akeyless and grant your repositories the necessary permissions to execute this action:
- Create a GitHub JWT Auth method in Akeyless if you don't already have one (you can safely share the auth method between repositories):
- In Akeyless go to "Auth Methods" -> "+ New" -> "OAuth 2.0/JWT".
- Specify a name (e.g. "GitHub JWT Auth") and location of your choice.
- For the JWKS Url, specify
https://token.actions.githubusercontent.com/.well-known/jwks
- For the unique identifier, use
repository
. See note (1) below for more details. - You MUST click "Require Sub Claim on role association". This will prevent you from attaching this to a role without any additional checks. If you accidentally forgot to set sub-claim checks, then any GitHub runner owned by anyone would be able to authenticate to Akeyless and access your resources... that make this a critical checkbox. See the GitHub docs for more details.
- Create an appropriate access role (if you don't already have one):
- In Akeyless go to "Access Roles" -> "+ New"
- Give it a name and location, and create it.
- Find your new access role and click on it to edit it.
- On the right side, under "Secrets & Keys", click the "Add" button to configure
read
access to any static or dynamic secrets you will fetch from your pipeline.
- Attach your GitHub JWT Auth method to your role:
- Once again, find the access role you created in step #2 above and click on it to edit it.
- Hit the "+ Associate" button to associate your "GitHub JWT Auth" method with the role.
- In the list, find the auth method you created in Step #1 above.
- Add an appropriate sub-claim, based on the claims available in the JWT. See note (2) below for more details.
- Save!
After following these steps, you'll be ready to use JWT Auth from your GitHub runners!
(1) Note: The unique identifier is mainly used for auditing/billing purposes, so there isn't one correct answer here. repository
is a sensible default but if you are uncertain, contact support for more details.
(2) Note: Sub-claim checks allow Akeyless to grant access to specific workflows based on the claims that GitHub provides in the JWT. Using the example JWT from the documentation, you could set a sub-claim check in Akeyless, using the example below, to limit access to workflows that were triggered from the main branch in the octo-org/octo-repo
repository:
repository=octo-org/octo-repo
ref=refs/heads/main