Using Fortanix Data Security Manager with GitHub

Prev Next

1.0 Introduction

This article explains how to integrate Fortanix-Data-Security-Manager (DSM) with GitHub Actions to securely retrieve secrets during a build pipeline.

It also explains how to:

  • Authenticate GitHub Actions workflows with Fortanix DSM.

  • Retrieve secrets securely during pipeline execution.

  • Use those secrets in build steps (for example, code signing).

2.0 Prerequisites

Ensure the following:

  • Access to a Fortanix DSM account with appropriate administrative privileges.

  • Knowledge of how to create and manage security objects (secrets) in Fortanix DSM, including generating or importing secrets.

  • A GitHub repository with permission to manage:

    • Repository Secrets

    • Repository Variables

    • GitHub Actions workflows

  • Basic understanding of GitHub Actions workflows and YAML configuration

3.0 Configure Fortanix DSM

This section describes the steps to configure Fortanix DSM to generate and import a security object.

3.1 Signing Up

To get started with the Fortanix DSM cloud service, you must register an account at <Your_DSM_Service_URL>. For example, https://eu.smartkey.io.

On-premises customers use the KMS URL, and the SaaS customers can use the URLs as listed  here  based on the application region.

For detailed steps on how to set up the Fortanix DSM, refer to the User's Guide: Sign Up for Fortanix Data Security Manager SaaS.

3.2 Logging In

Access <Your_DSM_Service_URL> in a web browser and enter your credentials to log in to Fortanix DSM.

A screenshot of a login screen  AI-generated content may be incorrect.

Figure 1: Logging In

For more information on how to set up an account in Fortanix DSM, refer to the User's Guide: Getting Started with Fortanix Data Security Manager - UI.

3.3 Creating a Group

Perform the following steps to create a group in the Fortanix DSM:

  1. In the DSM left navigation panel, click the Groups menu item, and then click ADD GROUP to create a new group.

    Figure 2: Add Groups

  2. On the Adding new group page, do the following:

    1. Title: Enter a name for your group.

    2. Description (optional): Enter a short description of the group.

  3. Click SAVE to create the new group.

The new group is added to Fortanix DSM successfully.

3.4 Creating an Application

Perform the following steps to create an application (app) in the Fortanix DSM:

  1. In the DSM left navigation panel, click the Apps menu item, and then click ADD APP to create a new app.

    Figure 3: Add Application

  2. On the Adding new app page, do the following:

    1. App name: Enter the name for your application.

    2. ADD DESCRIPTION (optional): Enter a short description of the application.

    3. Authentication method: Select API Key or Certificate as the authentication method, depending on your security and automation requirements, from the dropdown menu. For more information on these authentication methods, refer to the User's Guide: Authentication.

    4. Assigning the new app to groups: Select the group created in Section 3.3: Creating a Group from the list.

  3. Click SAVE to add the new application.

The new application is added to Fortanix DSM successfully.

3.5 Copying the API Key

Perform the following steps to copy the API key from the Fortanix DSM:

  1. In the DSM left navigation panel, click the Apps menu item, and then click the app created in Section 3.4: Creating an Application to go to the detailed view of the app

  2. On the INFO tab, click VIEW API KEY DETAILS.

  3. From the API Key Details dialog box, copy the API Key of the app to use it later.

3.6 Creating a Security Object

Perform the following steps to generate a Secret security object in Fortanix DSM:

  1. In the DSM left navigation panel, click the Security Objects menu item, and then click ADD SECURITY OBJECT to create a new security object.

    Figure 4: Add Security Object

  1. On the Add new Security Object page, do the following:

    1. Security Object name: Enter the name of your security object. For example, Github_secret.

    2. Group: Select the group as created in Section 3.3: Creating a Group.

    3. Select the IMPORT radio button.

    4. In the Choose a type section, select the Secret key type.

    5. In the Place value here or import from file section, select the value format type as Hex, Base64, or Raw, and click UPLOAD A FILE to upload the key file.

    6. In the Key operations permitted section, select the required operations to define the actions that can be performed with the cryptographic keys, such as encryption, decryption, signing, and verifying.

      NOTE

      Ensure that the Export permission is selected.

    7. Click IMPORT to generate the security object.

  2. Open the newly created Security Object and copy the Security Object ID. This ID is the KID and will be used in the GitHub Actions workflow to retrieve the secret value.

4.0 Integration Steps

Perform the following steps:

  1. Navigate to GitHub and select the repository where you want to set up the integration.

  2. Navigate to Settings → Secrets and variables → Actions → Secrets.

  3. Click New repository secret and add the required secrets based on your authentication method:

    • API Key-based Authentication:

    • Certificate-based Authentication:

      • DSM_CERT: The Base64-encoded client certificate (public certificate) used for Certificate-based authentication with Fortanix DSM. This certificate must correspond to the DSM Application configured for certificate authentication.

      • DSM_KEY: The Base64-encoded private key associated with the client certificate (DSM_CERT). This private key is used during mutual TLS (mTLS) authentication when GitHub Actions connects to Fortanix DSM.

    Figure 5: Add repository secrets

  4. Navigate to Settings → Secrets and variables → Actions → Variables.

  5. Click New repository variable to add the following variables:

    Figure 6: Add variables

  6. Create a new workflow file (for example, fetch-secret.yml) in your repository at the following path: .github/workflows/fetch-secret.yml.

  7. Edit the fetch-secret.yml file as follows to configure the integration as a manually triggered workflow using workflow_dispatch.

    • API Key-based Authentication

      name: Fetch Secret from DSM
      on: 
        workflow_dispatch:
      jobs:
        build:
          runs-on: ubuntu-latest
          steps:
            - name: Fetch Secret from DSM
              id: kms
              run: |
                RESPONSE=$(curl -s -X POST https://${{ vars.DSM_FQDN }}/crypto/v1/keys/export \
                  -H "Authorization: Basic ${{ secrets.API_KEY }}" \
                  -H "Content-Type: application/json" \
                  -d '{"kid":"${{ vars.KID }}"}')
                SECRET=$(echo "$RESPONSE" | jq -r '.value')
                if [ -z "$SECRET" ] || [ "$SECRET" = "null" ]; then
                  echo "KMS did not return a secret value"
                  exit 1
                fi
                DECODED=$(echo "$SECRET" | base64 -d)
                echo "::add-mask::$SECRET"
                echo "MY_SECRET=$DECODED" >> $GITHUB_ENV
            - name: Use Secret
              run: echo "Secret retrieved successfully"
    • Certificate-based Authentication

      name: Fetch Secret from DSM
      on: 
        workflow_dispatch:
      jobs:
        build:
          runs-on: ubuntu-latest
          steps:
            - name: Get certificates
              run: |
                set -euo pipefail
                echo "${{ secrets.DSM_CERT }}" | base64 -d > client.crt
                echo "${{ secrets.DSM_KEY }}" | base64 -d > client.key
                chmod 600 client.key         
      
            - name: Fetch Secret from DSM
              id: kms
              run: |
                RESPONSE=$(curl -s -X POST https://${{ vars.DSM_FQDN }}/crypto/v1/keys/export \
                  --cert client.crt \
                  --key client.key \
                  -H "Content-Type: application/json" \
                  -d '{"kid":"${{ vars.KID }}"}')
                SECRET=$(echo "$RESPONSE" | jq -r '.value')
                if [ -z "$SECRET" ] || [ "$SECRET" = "null" ]; then
                  echo "KMS did not return a secret value"
                  exit 1
                fi
                DECODED=$(echo "$SECRET" | base64 -d)
                echo "::add-mask::$SECRET"
                echo "MY_SECRET=$DECODED" >> $GITHUB_ENV
            - name: Use Secret
              run: echo "Secret retrieved successfully"

    NOTE

    If you want to run the workflow automatically, you must replace the workflow_dispatch under the on: section with the appropriate trigger (for example, push, pull_request, or schedule).

    For detailed steps on how to configure the trigger, refer to Triggering a workflow.

  8. After editing the file, run the workflow.

    Figure 7: Run the workflow

  9. Verify that the workflow completes successfully by navigating to the Actions tab in your GitHub repository and confirming that the job (For example, Fetch Secret from External KMS) runs without errors and retrieves the secret successfully.

    Figure 8: Verify the integration

5.0 Appendix

5.1 Optional: Generate and Import a Secret Using the DSM Plugin

This section describes how to use a Fortanix DSM plugin to generate a random secret and optionally import it as a SECRET security object.

NOTE

The GitHub Actions workflow does not invoke this plugin. This plugin is only used to generate and optionally store a secret in Fortanix DSM before integration.

Perform the following steps to generate a plugin in Fortanix DSM to generate and store a secret in Fortanix DSM:

  1. Add the following plugin code while generating a new plugin in Fortanix DSM:

    For more information, refer to the User’s Guide: Plugin Library.

    numericAlphabet = "0123456789"
    alphanumericAlphabet = numericAlphabet .. "abcdefghijklmnopqrstuvwxyz"
    alphanumericCapsAlphabet = alphanumericAlphabet .. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    alphanumericCapsSymbolsAlphabets = alphanumericCapsAlphabet .. "!@#$&*_%="
    
    function genPass(alphabet, len, name, import)
    local alphabetSize = #alphabet
    local password = ''
    
    for i = 1, len, 1 do
    local random_char = math.random(alphabetSize)
    password = password .. string.sub(alphabet, random_char, random_char)
    end
    
    local pass = Blob.from_bytes(password)
    
    if import == "yes" then
    local sobject = assert(Sobject.import {
    name = name,
    obj_type = "SECRET",
    value = pass,
    key_ops = {'APPMANAGEABLE', 'EXPORT'}
    })
    
    -- Return both password and KID
    return {
    password = password,
    kid = sobject.id
    }
    end
    
    -- If not importing, return only generated password
    return {
    password = password
    }
    end
    
    function run(input)
    if input.type == "numeric" then
    return genPass(numericAlphabet, input.length, input.name, input.import)
    end
    
    if input.type == "alphanumeric" then
    return genPass(alphanumericAlphabet, input.length, input.name, input.import)
    end 
    
    if input.type == "alphanumeric_caps" then
    return genPass(alphanumericCapsAlphabet, input.length, input.name, input.import)
    end 
    
    if input.type == "alphanumeric_caps_symbols" then
    return genPass(alphanumericCapsSymbolsAlphabets, input.length, input.name, input.import)
    end
    end
  • Set the import option to yes if you want to store the secret in Fortanix DSM.

    {
        "type": "numeric",
        "length": 64,
        "name": "GitHub-Secret",
        "import": "yes"
    }

    Output:

    {
    "password": " 839274938274...",
    "kid": "b3f1c2a4-xxxx-xxxx-xxxx-xxxxxxxx"
    }
  • Set the import option to no if you only want a new value generated for rotation.

    {
        "type": "numeric",
        "length": 64,
        "name": "GitHub-Secret",
        "import": "no"
    }

    Output:

    {
    "password": " 839274938274..."
    }