Introduction
An increasing and often overlooked risk posed to organisations is the “Insider Threat”
These can take two forms:
- Malicious Insiders
- Unintentional Insiders
When it comes to pipelines, a common mistake is to give every developer full access to edit pipeline yml definitions. So that could be your .gitlab-ci.yml
, .github/workflows/*.yml
or .circle/config.yml
file depending on your CICD tool.
This could potentially lead to data theft, sabotage, espionage, fraud or even just accidental damage.
During this short article we will explore the high level ideas behind using OPA (Open Policy Agent) policies within CircleCI to help mitigate the risk of insider threats.
Prerequisites
- Access to the Scale Plan on CircleCI - Unfortunately this is an enterprise only feature currently!
OPA Configuration Policies
CircleCI’s config policies feature lets you create OPA (Open Policy Agent) policies to govern project configurations.
OPA provides a high-level declarative language that lets you specify policy as code. These allow you to enforce fine-grained controls against the .circle/config.yml
file.
Example CircleCI Config
For the sake of this example, here is an example of a very bare bones .circle/config.yml
file:
version: 2.1
jobs:
build:
docker:
- image: alpine:3.15
steps:
- run:
name: The First Step
command: |
echo 'Hello World!'
echo 'This is the delivery pipeline'
Note the version number, currently it would be possible for a developer to switch this out for any value they like. What if an old version was unsecure?
We need a way to stop developers from intentionally or unintentionally changing key parts of the config file without blocking them from doing their day job.
Example OPA Policy
This is where OPA policies come into play. To give you a better idea on how OPA policies work, here is a basic example, ./config-policies/check-version.rego
:
package org
policy_name["check_version_is_set"]
# enable_hard will block any pipeline from running that fails this step
enable_hard["check_version"]
# define the version check
check_version = reason {
not input.version
reason := "version must be defined"
} {
not input.version >= 2.1
reason := sprintf("version must be at least 2.1 but got %v", [input.version])
}
This is a simple check, but would enforce that the company will only allow pipelines to run if they are using the correct version number.
These customizable, code-based rules could also be written to:
- Restrict access to production environments
- Require specific approval jobs
- Limit the use of untrusted tools or processes
- Restrict SSH access to production pipelines
- Restrict unauthorised custom bash scripts from runnning
Infact, this same idea can be taken further to validate nearly any facet of our .circle/config.yml
file.
Example Test Case
If you’re going to move fast with the development of OPA policies it is integral that you develop these using a test driven development approach.
CircleCI has an excellent wrapper around OPA policies to allow these to be easily tested.
Here is an shortened example CircleCI provide to show tests working, ./config-policies/check-version-test.yml
:
# Top level tests must have keys prefixed with `test_`
test_version_check:
# The default values our tests are built on
input:
version: 2.1
decision: &root
status: PASS
enabled_rules: [check_version]
# The overrides for each specific test, along with the decision we expect to see
cases:
absent_version:
input:
version: null
decision:
<<: *root
status: HARD_FAIL
hard_failures:
- rule: check_version
reason: version must be defined
inferior_version:
input:
version: 1.0
decision:
<<: *root
status: HARD_FAIL
hard_failures:
- rule: check_version
reason: version must be at least 2.1 but got 1
This can be as simple as running this locally:
circleci policy test ./config-policies --verbose
Using TDD lets us move fast, test locally and help confirm our policies will work as intended before deploying them to CircleCI.
Getting Started
Before you get started, please refer to the CircleCI documentation. This is an in-depth topic that can not be condensed into a single blog post unfortunately.
Once you have exhausted the CircleCI documentation, then checkout the Open Policy Agent site. They have tonnes of examples to help you progress further.
Summary
If insider threats are a risk to your business, then OPA policies can be an essential control for your pipelines.
A key tip is to use test driven development to make your life a lot easier implementing these restrictions.
OPA policies are not limited to CircleCI configuration files, and can also be used to protect all sorts of configuration files including in Kubernets and Envoy.