Integrating IaC custom rules within a pipeline
Using a CI/CD such as GitHub Actions is ideal for managing, distributing, and enforcing your custom rules.
Overview of IaC custom rules in a pipeline
This example shows how a security team can:
Store their rules in a GitHub repository
Use GitHub Actions to add different development-time steps to their pipelines
Configure a different GitHub repository to run a GitHub Action pipeline that uses the custom rules to gate changes.
We use the snyk/custom-rules-example repository for the example; this repo contains all the custom rules written while getting started with the SDK.
Aims: We want to configure our pipeline to:
Verify that new rules or changes to the existing rules don't break existing functionality.
Publish the rules in
main
to an OCI registry.Enforce the usage of custom rules in other pipelines.
(Optionally) Configure the custom rules using environment variables.
Adding PR checks using GitHub Action
An example of a PR check can be seen in https://github.com/snyk/custom-rules-example/pull/5 where we attempt to add a new rule called my_rule.
This is the same rule we showed for learning how to write a rule.
To verify that this rule works as expected, we have implemented unit tests. To run the unit tests as part of PR checks, we previously configured a GitHub Action under .github/workflows
called test.yml
:
name: Test Custom Rules
on:
push:
branches:
- '**' # matches every branch
- '!main' # excludes main
jobs:
unit_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 15
- name: Install snyk-iac-rules
run: npm i -g snyk-iac-rules
- name: Run unit tests
run: snyk-iac-rules test
A few things to note about this workflow:
We configured it to run on all non-
main
branches, so that it runs when PRs are open.We added steps to setup a Node.js environment so we can install the
snyk-iac-rules
SDK using npm.We added a step to run
snyk-iac-rules test
, which will cause the PR check to fail if any of the tests fail.
Snyk IaC GitHub Action
Another way to test the rules is by testing the contract with the Snyk CLI by using the Snyk IaC GitHub Action, making sure the generated bundle can be read by the CLI.
To do this, you will need a step for installing the Snyk CLI and a SNYK_TOKEN
, which can be found in your Snyk Account Settings.
jobs:
contract_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 15
- name: Install snyk-iac-rules
run: npm i -g snyk-iac-rules
- name: Build bundle
run: snyk-iac-rules build .
- name: Run contract with Snyk to check Infrastructure as Code files for issues
continue-on-error: true
uses: snyk/actions/iac@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --rules=bundle.tar.gz
You can also expand these tests to use Shellspec and verify that the desired vulnerabilities get triggered, but we recommend using the unit tests for this.
Publishing the custom rules
Once a PR passes its checks from the previous section and gets merged into the main
branch, you can publish our rules to an OCI registry. This allows you to configure a separate pipeline, to download the custom rules bundle from this location, and run the custom rules in order to catch misconfigurations.
For this, we will add another workflow under .github/workflows
called publish.yml
:
name: Publish Custom Rules
on:
push:
branches:
- 'main'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 15
- name: Install snyk-iac-rules
run: npm i -g snyk-iac-rules
- name: Build bundle
run: snyk-iac-rules build .
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.OCI_REGISTRY_USERNAME }}
password: ${{ secrets.OCI_REGISTRY_PASSWORD }}
- name: Publish rules
run: snyk-iac-rules push --registry $OCI_REGISTRY_URL bundle.tar.gz
env:
OCI_REGISTRY_URL: "${{ secrets.OCI_REGISTRY_NAME }}:v1"
It looks similar to the previous workflow, but there are a few things to note about this one:
We configured it to run only on
main
branches, so that it runs when PRs are merged.We added a step to authenticate with Docker Hub, our chosen OCI registry. For a list of supported registries read about pushing bundles. Use the docker/login-action GitHub Action to do that and make sure to configure the GitHub secrets under
Settings
->Secrets
.We added a step to run
snyk-iac-rules build
followed bysnyk-iac-rules push
, which will publish our generated custom rules bundle to an OCI registry.
Versioning rules
If we want to release an experimental version of the custom rules without affecting all our CI/CD pipelines, we can use tagging to version our bundles.
So, we can start trialing bundle v2-beta
while still using v1
in most of our services:
- name: Publish experimental rules
run: snyk-iac-rules push --registry $OCI_REGISTRY_URL bundle.tar.gz
env:
OCI_REGISTRY_URL: "${{ secrets.OCI_REGISTRY_NAME }}:v1"
- name: Publish rules
run: snyk-iac-rules push --registry $OCI_REGISTRY_URL bundle.tar.gz
env:
OCI_REGISTRY_URL: "${{ secrets.OCI_REGISTRY_NAME }}:v2-beta"
Enforcing the custom rules
After publishing the custom rules to an OCI registry, you can configure a separate pipeline to use these rules. One way to do this is by using the public Group IaC Settings API.
This means configuring the GitHub Action above with another job for updating Snyk to use the configured custom rules bundle:
- name: Update Snyk
run: |
curl --location --request PATCH 'https://api.snyk.io/rest/groups/<group id>/settings/iac/?version=2021-11-03~beta' \
--header 'Content-Type: application/vnd.api+json' \
--header 'Authorization: token ${{ secrets.SNYK_TOKEN }}' \
--data-raw '{
"data": {
"type": "iac_settings",
"attributes": {
"custom_rules": {
"oci_registry_url": "registry-1.${{ secrets.OCI_REGISTRY_NAME }}",
"oci_registry_tag": "v1",
"is_enabled": true
}
}
}
}'
This API call will update the chosen Snyk group and all the organizations underneath it to use the configured custom rules bundle.
In a different repository, all you have to do is authenticate with one of the organizations underneath this group and add the Snyk IaC GitHub Action to a workflow:
name: Snyk Infrastructure as Code Custom Rules
on:
push:
jobs:
snyk-iac-security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run Snyk to check Infrastructure as Code files for issues
continue-on-error: false
uses: snyk/actions/iac@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
The result is that the GitHub action will fail until the generated misconfigurations have been resolved:
Testing example.tf...
Infrastructure as code issues:
✗ IAM Role missing one of the required tags: owner, description or type [Medium Severity] [CUSTOM-RULE-8]
introduced by input > resource > aws_iam_role[new_role] > tags
✗ Vendor or Service must have either owneralternate or ticketgroup or both tags. [Medium Severity] [CUSTOM-RULE-9]
introduced by input > resource > aws_iam_role[new_role] > tags
Configuring the custom rules
Additionally, if using an API or the Snyk Settings page seem too restrictive, we also provide a way to configure the custom rules by using the environment variables.
You can use the Snyk IaC GitHub Action with the SNYK_CFG_OCI_REGISTRY_URL
, SNYK_CFG_OCI_REGISTRY_USERNAME
, and SNYK_CFG_OCI_REGISTRY_PASSWORD
environment variables to scan your configuration files for any custom rules which may have been breached.
The GitHub Action reads these environment variables and pulls down the bundle pushed in the previous step to the configured OCI registry. The GitHub action will look similar to this:
name: Snyk Infrastructure as Code Custom Rules
on:
push:
jobs:
snyk-iac-security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run Snyk to check Infrastructure as Code files for issues
continue-on-error: false
uses: snyk/actions/iac@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
SNYK_CFG_OCI_REGISTRY_URL: ${{ secrets.OCI_REGISTRY_URL }}
SNYK_CFG_OCI_REGISTRY_USERNAME: ${{ secrets.OCI_REGISTRY_USERNAME }}
SNYK_CFG_OCI_REGISTRY_PASSWORD: ${{ secrets.OCI_REGISTRY_PASSWORD }}
Last updated
Was this helpful?