GitHub Actions Plugin

Akeyless Official GitHub Actions plugin 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.

Prerequisites

  • Job permissions requirement: (Relevant for OAuth 2.0 / JWT Authentication only)

The default usage relies on using the GitHub JWT (JSON Web Token) to authenticate to Akeyless. To make this available, you must configure it inside your job workflow.

jobs:
  my_job:
    #---------Required---------#
    permissions: 
      id-token: write
      contents: read
    #--------------------------#

Runner Configuration

Configure a self-hosted-runner:

  • In GitHub - navigate to the main page of the repository and select Settings > Actions > Runners > New self-hosted runner.
  • Select the operating system and architecture of your self-hosted-runner machine.
  • Follow the instructions in the Download section to prepare a directory for the GitHub runner, and then download the runner.
  • Follow the instructions in the Configure section to configure the runner to connect to GitHub with a token GitHub generates for the runner.

Authentication

This Action plugin supports the following Authentication Methods:

GitHub Repository Variable

You can store the Access ID as a GitHub variable inside the repository to use in your workflow.

In the following examples, instead of explicitly specifying the Access ID of the Authentication Method inside the workflow, we store it as a variable in the repository called AKEYLESS_ACCESS_ID.

  • On GitHub, navigate to the main page of the repository, and select Settings > Secrets and variables > Actions > Variables tab > New repository variable.
  • Enter the name for the variable (for example, AKEYLESS_ACCESS_ID ) and set the value to your Auth Method Access ID.
  • Select Add Variable.

This is only part of the YAML action. More complete examples are given in the next section.

        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"
        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: "<https://Your-Akelyess-Gateway-URL:8000>"
          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-type: access_key
          access-key: ${{ secrets.AKEYLESS_ACCESS_KEY }}
          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"
          ca-certificate: ${{ secrets.AKEYLESS_CA_CERTIFICATE }}
          static-secrets: |
            - name: "/akeyless-github-action/github-static-secret-json"
              output-name: "my_first_secret"
              key: "imp"

🚧

Warning

For JWT authentication, it is required to add appropriate Sub-Claims based on the claims available in the JWT to prevent access by unauthorized users.

Sub-Claim configuration allows Akeyless to grant access to specific workflows, based on the claims that GitHub provides in the JWT.

For example, Create and Associate your Authentication Method with an Access Role to grant the relevant permissions within Akeyless.

akeyless create-auth-method-oauth2 --name /Dev/GitHubAuth  \
--jwks-uri https://token.actions.githubusercontent.com/.well-known/jwks \
--unique-identifier repository \
--force-sub-claims

akeyless assoc-role-am --role-name /Dev/GitHubRole
--am-name /Dev/GitHubAuth
--sub-claims <your-sub-claims> 

For example: repository=octo-org/octo-repo where octo-org = {GitHub Account} and octo-repo = {GitHub Repository}.

Usage

Although this repository's workflows use placeholder values, it is still a real Akeyless account and a real provider. The approaches demonstrated in these examples are still valid as-is for real implementations. Use these to your advantage!

Static Secrets Example

In this example, you will fetch two static secrets from Akeyless, my_first_secret and my_second_secret. Just define each secret's path and output name. The secret values can be found in the secrets.txt file created in that directory (note the "key" is only relevant for JSON formatted secrets, see below).

jobs:
  fetch_secrets:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read

    steps:
      - name: Fetch static secrets from Akeyless
        uses: akeyless-community/[email protected]
        id: fetch-secrets
        with:
          access-id: ${{ vars.AKEYLESS_ACCESS_ID }}
          access-type: jwt
          api-url: https://api.akeyless.io
          static-secrets: |
            - name: "/path/to/secret"
              output-name: "my_first_secret"
              key: "imp"
            - name: "/path/to/another/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

Dynamic Secrets Example

In this example, you will fetch an AWS Dynamic Secret from Akeyless, called aws_dynamic_secret. Just define the secret path and output name. The secret's values can be found in the secrets.txt file created in that directory.

  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/[email protected]
      with:
        access-id: ${{ vars.AKEYLESS_ACCESS_ID }}
        access-type: jwt        
        dynamic-secrets: |
          - name: "/path/to/dynamic/aws/secret"
            output-name: "aws_dynamic_secret"
        
# ********* 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_secret }}' | 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

Rotated Secrets Example

In this example, you will fetch an AWS Rotated Secret from Akeyless, called aws_rotated_secret.

  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/[email protected]
      with:
        access-id: ${{ vars.AKEYLESS_ACCESS_ID }}
        access-type: jwt
      rotated-secrets: |
        - name: "/path/to/rotated/aws/secret"
          output-name: "aws_rotated_secret"

SSH Certificates Example

In this example, you will fetch two SSH Certificates from Akeyless, called ssh_secret1 and ssh_secret2.

  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/[email protected]
        with:
          access-type: jwt
          ssh-certificate-secrets: |
            - name: "/path/to/ssh/secret1"
              output-name: "ssh_secret1"
              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",

PKI Certificates Example

In this example, you will fetch two PKI Certificates from Akeyless, called pki_secret1 and pki_secret2.

  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/[email protected]
        with:
          access-type: jwt
          pki-certificate-secrets: |
            - name: "/path/to/pki/secret1"
              output-name: "pki_secret1"
              csr-data-base64: "csr_data_base64"
            - name: "/path/to/pki/secret2"
              output-name: "pki_secret2"
              csr-data-base64: "csr_data_base64"

Parsing JSON Secrets Examples

By default, the action sets the environment variable value to the entire JSON string in the secret value. You can 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 and output-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 be env.DEV_TEST_{key}.

For a secret with JSON values:

{
  "key1":"val1",
  "key2":"val2"
}

Using the following inside your yaml:

   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

In this example, the output in secrets.txt will be:

key1:val1
key2:val2
key1:val1

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 "key1 == ${{ env.MYSQL_KEY1 }}" >> secrets.txt
     echo "key2 == ${{ env.MYSQL_KEY2 }}" >> secrets.txt

In this example, the output in secrets.txt will be:

key1 == val1 
key2 == val2

Extracting from JSON by field 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": "value",
  "no": "no_value"
}

We can use the following example:

    - 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 value.