In this blog post, you will learn everything you need to secure your CI/CD pipeline by notarizing all of your artifacts with a GitHub Action that uses Codenotary.
When your users, clients, or customers download an asset from you, they may want to validate that the file has not been tampered with. With Codenotary, you can notarize all your assets so that their exact signatures can be compared with the artifacts produced by your CI/CD process.
While you can use vcn
to manually notarize your assets after creation, GitHub Actions let you encapsulate and reuse certain logic automatically. Creating a GitHub Action allows you to automate running the VCN command-line tool against your build artifact or Dockerfile.
You can also find a great tutorial video here:
GitHub Actions are composed of a few core concepts:
In other words, an event triggers a workflow of jobs, with steps performing different actions.
Workflows are defined in .github/workflows
by a YAML file. To understand the syntax of workflows, let’s look at a sample workflow provided by GitHub in Introduction to GitHub Actions:
name: learn-github-actions
on: [push]
jobs:
check-bats-version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- run: npm install -g bats
- run: bats -v
A full list of available syntax is available at Workflow syntax for GitHub Actions, but here we have enough to understand the basics of a workflow.
push
to any branch. You can set more advanced parameters here as well.on:
event. Multiple jobs can be defined, but this example only defines one job, which has been named check-bats-version
. The job runs-on:
the latest Ubuntu image.uses:
two different predefined Actions and run:
s two commands directly.
uses:
the action hosted at the actions/checkout
repo on the v2
branch or tag. This action checks out the repository so that further actions can work with your code.uses:
the action hosted at the actions/setup-node
repo on the v1
branch or tag. This action installs and sets up Node.js on the working image.run:
a command that will install bats, and the fourth step will run:
a command that checks the version of bats.You can also set up a workflow graphically by using an available template. Navigate to the Actions tab of your repository and pick a template you would like to use. Click set up this workflow. You can search for actions from the marketplace while defining your workflow.
GitHub Actions give you the tools to automate all your software workflows, like building, testing, or deploying your code. Many predefined actions exist for you to find and use in your custom workflows. You can read more about actions at About actions.
You can use Docker to build a custom image that can be used to run your workflow. See Dockerfile support for GitHub Actions for more information.
GitHub Actions require an action.yml
file to define the inputs, outputs, and main entrypoint for your action. See Metadata syntax for GitHub Actions for more information.
Let’s try making one of our own GitHub Actions right now!
Note: Before you continue, you will need a Codenotary account. You can make one at https://dashboard.Codenotary.io/auth/signup for free.
GitHub Actions are defined as a repository containing specific files, so let’s create a repository to host our GitHub Action.
Now that we have a repository for our GitHub Action, the first step is to create a Dockerfile. This will define a container image that will provide us with a clean workspace to run scripts against, as the usual way to distribute pre-made actions. To do so, we use the latest Alpine Linux image, adding bash and curl for later use in the script. We create a work directory and copy our repository to it. Finally, we call the entrypoint script, which will notarize our assets.
Place the following file at /Dockerfile
:
FROM alpine:latest
RUN apk update && apk add bash
RUN apk add --update
curl
&& rm -rf /var/cache/apk/*
WORKDIR /workspace
COPY . /workspace
ENTRYPOINT [ “/workspace/entrypoint.sh” ]
Note: Before you continue, you will need a Codenotary account. You can make one at https://dashboard.Codenotary.io/auth/signup for free.
This script will obtain the Codenotary CLI tool and then notarize your file.
Earlier, we created a Dockerfile for an image with bash and curl. We will now write this Bash script. GitHub Actions can be written in any language, but using bash and curl provides us with all we need for this simple script.
For more advanced information on workflow commands, visit Workflow commands for GitHub Actions.
The Bash script below first sets some flags that will improve error handling:
-e
flag will exit immediately if there is an error.-u
flag will treat unset variables as an error during substitution.-x
flag will print commands and their arguments as they are executed.-o pipefail
flag will return any error codes from piped commands.The setup
subroutine will download v0.9.4 of the VCN command line tool, which is currently the latest version. It will then verify the checksum to ensure that it has downloaded correctly, and then it will mark the binary as executable.
The main
subroutine will login to Codenotary using environment variables, then publicly notarize the file passed to it.
Place the following file at /entrypoint.sh
:
#!/bin/bash
set -euxo pipefail
setup() {
curl -L -o /tmp/vcn https://github.com/Codenotary/vcn/releases/download/v0.9.4/vcn-v0.9.4-linux-amd64-static
CHECKSUM=$(sha256sum /tmp/vcn | cut -d " " -f 1)
curl -s https://api.Codenotary.io/authenticate/$CHECKSUM?org=Codenotary.com | grep -q :0
chmod +x /tmp/vcn
}
main() {
echo | /tmp/vcn login
/tmp/vcn n -p $1
}
if [ $# -eq 0 ];
then
exit 0
fi
setup
main $1
Make sure that you mark this script as executable, so that it can be executed by the Docker image instead of giving an error:
chmod +x entrypoint.sh
When creating the entrypoint script, we mentioned that VCN will login using environment variables.
The GitHub Action .yml file we define will allow us to declare those environment variables, as well as the inputs to be used with the script that will be run in the Docker image we defined above and the outputs we wish to have.
Essentially, the action.yml is like our API that will be exposed to anyone using the action. The syntax available to us is documented at Metadata syntax for GitHub Actions.
There are three inputs we will need:
This action will output a signed hash generated by the VCN notarization process. It will run using Docker, and specifically with the Dockerfile we created.
The appropriate environment variables will be set by the inputs we will provide. Some optional branding is added to display a blue checkmark when viewing the results of the GitHub Action.
Place the following file at /action.yml
:
name: ‘VCN Notarization’
description: ‘An action that allows you to notarize build artifacts using Codenotary VCN’
inputs:
file:
description: ‘The file we wish to notarize’
required: true
vcn_username:
description: ‘The username for your account’
required: true
vcn_password:
description: ‘The password for your account’
required: true
outputs:
signed-hash:
description: ‘The VCN signed hash generated from the file’
runs:
using: docker
image: ‘Dockerfile’
env:
VCN_USER: $
VCN_PASSWORD: $
VCN_NOTARIZATION_PASSWORD: $
args:
* $
branding:
icon: ‘check’
color: blue
We are now ready to create a GitHub Workflow that will test our GitHub Action. When a commit is pushed to the specified branch, a test file will be created and notarized using the username and password provided in your repository secrets.
Place the following file at .github/workflows/test.yml
, making sure to edit the branch name and the uses:
repository as appropriate (i.e., with the identifier of the repository you created for this action, and the branch you would like to use for triggering the test workflow):
name: test
on:
push:
branches:
- testing
jobs:
test-usage:
runs-on: ubuntu-latest
steps:
- name: Create a test file
run: |
echo "Hello world" > temp.txt
- name: Notarize file
uses: username/repository@testing
with:
vcn_username: $
vcn_password: $
file: temp.txt
As defined, this “test” workflow will trigger when you push to the testing
branch. It will run the “test-usage” job on the latest Ubuntu image, which contains two steps: the first step will create a temporary text file, and the second step will notarize it using the GitHub Action we just created.
On GitHub, navigate to the main page of the repository and click Settings. In the left sidebar, click Secrets. You will need to add two new repository secrets:
You are now ready to see your very own GitHub Action in action! Just make a commit to the specified branch, and then check the Workflows tab of your repository. You should see the results of your GitHub Action there. Congratulations!
Little reminder, you can also find a great tutorial video here:
By following this article, we have learned about GitHub Actions, including their core concepts, how workflows are configured, and how actions can be written. We have also created our own GitHub Action which can be used to notarize a file.
If you want to check a major Open Source project Home Assistant making use of GitHub actions and Codenotary, check this PR
In summary, this process involved writing a Dockerfile to generate a clean working environment which calls an entrypoint script. Our action.yml file defined the inputs and outputs as well as the environment variables to be passed to our Docker image, and our sample Github Workflow utilizes all of this to create a test file and notarize it using the action we wrote.
You can get started in minutes with your Codenotary Cloud account