Page
Prerequisites and step-by-step guide
Prerequisites
- Learning exercise on Signing and Verifying Artifacts with GitHub identity provider
- Red Hat OpenShift cluster
- A running instance of Red Hat Trusted Artifact Signer (RHTAS) on OpenShift
- Access to a container registry, for cosign to work with
Step-by-step guide
Please ensure you are logged into your OpenShift cluster and have the cosign
and gitsign
binaries installed and available in your $PATH, as per the first part of this learning exercise.
1. Workflow setup
GitHub Actions is a continuous integration and delivery (CI/CD) platform that allows you to execute custom workflows directly from your repository. A workflow can do any task you'd like, from securing your CI/CD pipeline to automating your tests.
You can also have multiple workflows. The key to setting up a workflow in your GitHub CI/CD pipeline is to create a YAML file for it. Workflow files must go in the .github/workflows
directory in the root of your project. Since we don't have one, we'll have to create it.
1.1 Creating your first workflow
We'll create a simple workflow that builds, signs, and logs metadata to Rekor, and then verifies and deploys the software artifacts. This could be particularly helpful for developers who want to follow best secure software practices in their organization by signing their commits and ensuring they are verified prior to building the container image and deployment.
Following on from the previous learning exercise, let's navigate to the project previously created in part 1 of the learning exercise and create the directory for our GitHub workflows. Run the following commands in your terminal:
cd tas-demo
mkdir .github .github/workflows dist
We'll also go ahead and make our dummy artifact, a simple text file in this case, as an example of a build artifact:
echo "Example artifact" > dist/artifact.txt
Next, create a YAML file called verify-signatures.yml in the .github/workflows directory within your project.
Let's take a look at an example of a workflow that monitors your repo for pushes to the main branch, checks out your code, and builds the project.
name: Verify Signatures with Gitsign
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main
- name: Build project
run: |
rm -rf dist & mkdir -p dist
echo "Example artifact" > dist/artifact.txt
Add the above to the verify-signatures.yml file.
1.2 Point to the cluster
Just as we set up our environment for signing locally, we need to do so for our workflow as well. With GitHub Actions, we can provide custom variables by adding the env key as follows.
name: Verify Signatures with Gitsign
on:
push:
branches:
- main
env:
IMAGE_REGISTRY: quay.io
IMAGE_REPO: lucarval/festoji
IMAGE_TAGS: latest
APPS_DOMAIN: apps.rosa.p1.openshiftapps.com
TUF_URL: https://tuf-openshift-operators.$APPS_DOMAIN
OIDC_ISSUER_URL: https://token.actions.githubusercontent.com
COSIGN_FULCIO_URL: https://fulcio-server-openshift-operators.$APPS_DOMAIN
COSIGN_REKOR_URL: https://rekor-server-openshift-operators.$APPS_DOMAIN
COSIGN_MIRROR: https://tuf-openshift-operators.$APPS_DOMAIN
COSIGN_ROOT: https://tuf-openshift-operators.$APPS_DOMAIN/root.json
COSIGN_OIDC_ISSUER: https://token.actions.githubusercontent.com
COSIGN_CERTIFICATE_OIDC_ISSUER: https://token.actions.githubusercontent.com
REKOR_REKOR_SERVER: https://rekor-server-openshift-operators.$APPS_DOMAIN
GITSIGN_OIDC_ISSUER: https://keycloak-keycloak-system.$APPS_DOMAIN/auth/realms/trusted-artifact-signer
TAS_IMAGE: quay.io/kahboom/test-image:latest
COSIGN_YES: "true"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main
- name: Build project
run: |
rm -rf dist & mkdir -p dist
echo "Example artifact" > dist/artifact.txt
Be sure to replace the initial APPS_DOMAIN
declaration with your own cluster and TAS_IMAGE
with your own container image.
Important: By default, variables are displayed unmasked in build outputs. You can use secrets (discussed later in this tutorial) to prevent exposing sensitive information by mistake.
1.3 Setup Gitsign
Before proceeding in the workflow, we'll want to verify the signature of the author proposing the changes, so we'll create a dedicated step to do just that. Generally, it's best practice to break up the flow into smaller chunks, where possible.
Because these workflows run on GitHub's servers, we don't have access to the Gitsign CLI, so we'll also need to add a step that downloads the CLI binary from our TAS cluster for verifying the signature, and for any future signing within this workflow.
Below the Build project job, we'll add another job to check the installation was successful and to configure our git settings for signing.
- name: Install Gitsign
shell: bash
run: |
set -ex
curl -fsL https://cli-server-trusted-artifact-signer.$APPS_DOMAIN/clients/linux/gitsign-amd64.gz -o /usr/local/bin/gitsign-amd64.gz
gunzip /usr/local/bin/gitsign-amd64.gz
chmod +x /usr/local/bin/gitsign-amd64
mv /usr/local/bin/gitsign-amd64 /usr/local/bin/gitsign
- name: Verify Gitsign Installation
run: |
which gitsign
ls -l /usr/local/bin/gitsign
gitsign version
shell: bash
- name: Initialize Gitsign
shell: bash
run: gitsign initialize --mirror=$TUF_URL --root=$TUF_URL/root.json
1.4 Verify author signatures
With Gitsign installed and git configured, we can now add a step to verify the signature.
- name: Verify Commit Signatures
run: |
gitsign verify --certificate-identity=${{ secrets.IDENTITY_EMAIL }} --certificate-oidc-issuer=$GITSIGN_OIDC_ISSUER HEAD
shell: bash
Note: For now, don't worry about variables prefixed with a $, as we will declare them as GitHub Secrets in the following section.
2. Set up GitHub Secrets
Until now, we've been entering values that include personal information directly into our commands. In real-world scenarios, it's better to use environment variables or similar methods to protect sensitive data. With GitHub Actions, we can use GitHub Secrets, which are variables you create that can be easily referenced from within a workflow.
Secrets can be added at the organization, environment, or repository level so that you don't have to create duplicate secrets. Note that if you are adding secrets at an organization level (i.e. an organization repository), you will need admin access.
Let's create the secrets we've been referring to in our workflow.
2.1 Certificate identity email secret
- In your browser, navigate to the project repository on GitHub
- Under the repository name, click on the "Settings" tab
- In the Security section of the sidebar, click on Secrets and Variables
- Click "Actions"
From the "Secrets" tab, press the "New repository secret" button
- Fill in the name with and the secret with your identity email address
- Secret name: "IDENTITY_EMAIL"
- Secret value: Provide your identity email address
- Click "Add secret"
2.2 OIDC, cluster, and container registry secret
Repeat the same process for the remaining secrets for accessing the cluster and container registry.
Note that while the name of the secret must remain the same, you should replace the value with your own:
3. Security hardening with OpenID Connect
Because GitHub Actions does not have access to our cluster, the workflow we've created so far can only work if we create credentials beforehand and then duplicate them in GitHub as a secret.
With OpenID Connect (OIDC), we can configure the workflow to request a short-lived access token directly from the cloud provider, which must support OIDC. This eliminates the need to create and supply credentials.
In Part 1 of this learning exercise, we configured a trust relationship between Keycloak and GitHub that will allow our workflow to request the access token. Let's see how we can modify our workflow to reflect this change.
3.1 Permissions
The first thing we'll need to do is give special permissions to our workflow, so that it can request the required OIDC token and run the checkout action. We can do this by adding a permissions key under the build metadata near the top of our workflow file:
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write # required to request the OIDC token
contents: read # required for actions/checkout
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main
3.2 OIDC token steps
Below the Verify Commit Signatures job, we'll create a step to request the OIDC token from our cluster by specifying the request URL and token:
- name: Request OIDC token
id: oidc-token
run: |
echo "Requesting OIDC token..."
echo "Token: ${{ steps.auth.outputs.id_token }}"
env:
ACTIONS_ID_TOKEN_REQUEST_URL: ${{ secrets.ACTIONS_ID_TOKEN_REQUEST_URL }}
ACTIONS_ID_TOKEN_REQUEST_TOKEN: ${{ secrets.ACTIONS_ID_TOKEN_REQUEST_TOKEN }}
And, finally, our last step is to use the token with TAS:
- name: Use OIDC token with TAS
run: |
cosign login ${{ secrets.CI_REGISTRY }} -u ${{ secrets.CI_REGISTRY_USER }} -p ${{ secrets.CI_REGISTRY_PASSWORD }}
cosign initialize --mirror=$TUF_URL --root=$TUF_URL/root.json
cosign sign --oidc-issuer ${{ secrets.ACTIONS_ID_TOKEN_REQUEST_URL }} --identity-token ${{ steps.oidc-token.outputs.id_token }} $TAS_IMAGE
cosign verify --certificate-oidc-issuer='https://token.actions.githubusercontent.com' --certificate-identity-regexp='https://github.com/kahboom' $TAS_IMAGE
Be sure to replace the value of –-certificate-identify-regexp
with your own.
4. Workflow deployment
The hard work is done, and the only thing left is to deploy our workflow and let GitHub do the heavy lifting. Back in our terminal, we'll push up our code with a signed commit.
git add .
git commit -S -m "feat(ci): add github workflow"
git push
The push will then trigger a build on GitHub. Magic!
We can verify it in the browser by navigating to our repository on GitHub and clicking on the Actions tab to view all recent runs from all workflows.
You should see a list of commits that have triggered the build, and the name of the workflow it ran, just below it.
Clicking on any of the commits will take you to a summary view with a nice visualization of the build, and any artifacts that have resulted from it.
Let's take a look at the build step. Click on the "build" button from the Summary above.
You should see a breakdown of the workflow steps and the output of each, including how long it took to run each step.
Feel free to check out the repository here if you'd like to see the source code for this workflow.
Summary
This learning exercise presented the integration of Red Hat Trusted Artifact Signer with GitHub Actions. We covered the setup process for adding a new workflow and explored techniques for verifying signatures throughout the application lifecycle. These activities included setting up GitHub Actions by creating a workflow, adding a job to verify author commits and artifact signatures, and triggering the workflow build.