Clair CI/CD
Project description
Clair, released by CoreOS in Nov ’16, is a very effective tool for statically analyzing docker images to determine which known vulnerabilities exist in the images. Integrating Clair into a CI/CD pipeline:
is complex (believe this is partly a documentation challenge)
creates performance problems (building the Clair required Postgres database of vulnerabilities is slow)
in and of itself is insufficient from a risk assessment point of view because once vulnerabilities are identified there’s a lack of prescriptive guidance on how to act on the identified vulnerabilities
This repo was created to address the above challenges.
Background
The roots of this repo center around the following beliefs:
when inserted into a CI/CD pipeline Clair can be a very effective foundation for the automated assessment of Docker image vulnerability risk
services should be run in Docker containers and thus CI/CD pipelines should be focused on the automated generation, assessment and ultimately deployment of Docker images
understanding and assessing the risk profile of services is important ie. security is important
Docker images should not be pushed to a Docker registry until their risk profile is understood (this is an important one)
the CI/CD pipeline has to be fast. how fast? ideally < 5 minutes between code commit and automated (CD) deployment begins rolling out a change
there should be a clear division of responsibilities between those who create a docker image (service engineer) and those who determine the risk of vulnerabilities in a docker image (security analyst)
the risk assessment process should generate evidence which can be used to understand the risk assessment decision
Key Participants
service engineer - responsible for implementing a service that is packaged in a docker image
security analyst - responsible for defining whitelists which are consumed by clair_cicd to influence Docker image risk assessment decisions
How to Use
Getting Started
To start using clair-cicd, a service engineer inserts a single line of code into a service’s CI pipeline. The single line of code runs the shell script assess-image-risk.sh. Part of the CI pipeline’s responsibility is to build the docker image and then push that docker image to a docker registry. The single line of clair-cicd code should appear after the docker image is built and tested but before the docker image is pushed to a docker registry.
In this simple case, assess-image-risk.sh returns a zero exit status if the docker image contains no known vulnerabilities above a medium severity. If the docker image contains any known vulnerabilities with a severity higher than medium, assess-image-risk.sh returns a non-zero exit status and the build fails ie. the build should fail before the docker image is pushed to a docker registry.
The example illustrates what’s described about for the alpine:3.4 docker image.
~> curl -s -L \
https://raw.githubusercontent.com/simonsdave/clair-cicd/master/bin/assess-image-risk.sh | \
bash -s -- alpine:3.4
~> echo $?
0
~>
Understanding the Risk Assessment Decision
To understand how assess-image-risk.sh is making its risk assessment decision try using the -v flag.
~> curl -s -L \
https://raw.githubusercontent.com/simonsdave/clair-cicd/master/bin/assess-image-risk.sh | \
bash -s -- -v alpine:3.4
2020-01-12 16:43:35 pulling clair database image 'simonsdave/clair-cicd-database:latest'
2020-01-12 16:44:17 successfully pulled clair database image
2020-01-12 16:44:17 starting clair database container 'clair-db-c1dbb5f93ae98755'
2020-01-12 16:44:23 waiting for database server in container 'clair-db-c1dbb5f93ae98755' to start ...........................
2020-01-12 16:44:54 successfully started clair database container
2020-01-12 16:44:54 clair configuration in '/var/folders/7x/rr443kj575s8zz54jrbrp4jc0000gn/T/tmp.ElAlhGNl'
2020-01-12 16:44:59 pulling clair image 'simonsdave/clair-cicd-clair:latest'
2020-01-12 16:45:13 successfully pulled clair image 'simonsdave/clair-cicd-clair:latest'
2020-01-12 16:45:13 starting clair container 'clair-e9573ae537134fa0'
2020-01-12 16:45:15 successfully started clair container 'clair-e9573ae537134fa0'
2020-01-12 16:45:15 saving docker image 'alpine:3.4' to '/tmp/tmp.IaNHCH'
2020-01-12 16:45:16 successfully saved docker image 'alpine:3.4'
2020-01-12 16:45:16 starting to create clair layers
2020-01-12 16:45:16 creating clair layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
2020-01-12 16:45:16 successfully created clair layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
2020-01-12 16:45:16 done creating clair layers
2020-01-12 16:45:16 starting to get vulnerabilities for clair layers
2020-01-12 16:45:16 saving vulnerabilities to directory '/tmp/tmp.MDncHN'
2020-01-12 16:45:16 getting vulnerabilities for layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
2020-01-12 16:45:16 successfully got vulnerabilities for layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
2020-01-12 16:45:16 done getting vulnerabilities for clair layers
2020-01-12 21:45:17 INFO io:89 Looking for vulnerabilities in directory '/tmp/tmp.MDncHN'
2020-01-12 21:45:17 INFO io:95 Found 1 files with vulnerabilities in directory '/tmp/tmp.MDncHN'
2020-01-12 21:45:17 INFO io:104 Looking for vulnerabilities in '/tmp/tmp.MDncHN/378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d.json'
2020-01-12 21:45:17 INFO io:122 Found 0 vulnerabilities in '/tmp/tmp.MDncHN/378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d.json'
2020-01-12 21:45:17 INFO io:133 Found 0 vulnerabilities in 1 files in directory '/tmp/tmp.MDncHN'
2020-01-12 21:45:17 INFO assessor:19 Assessment starts
2020-01-12 21:45:17 INFO assessor:26 Assessment ends - pass
~> echo $?
0
~>
Adding a Vulnerability Whitelist
In the above examples a default vulnerability whitelist was used. When specified as a JSON doc, this whitelist would be:
{
"ignoreSevertiesAtOrBelow": "medium"
}
By default, assess-image-risk.sh returns a non-zero exit status if any vulnerabilities are identified in the image with a severity higher than medium. The medium is derived from the default vulnerability whitelist.
The example below illustrate how to specify a vulnerability whitelist and with a severity other than medium. Note the use of the json:// prefix to indicate this is an inline whitelist.
~> curl -s -L \
https://raw.githubusercontent.com/simonsdave/clair-cicd/master/bin/assess-image-risk.sh | \
bash -s -- -v --whitelist 'json://{"ignoreSevertiesAtOrBelow": "negligible"}' ubuntu:18.04
2020-01-12 16:46:56 pulling clair database image 'simonsdave/clair-cicd-database:latest'
2020-01-12 16:46:58 successfully pulled clair database image
2020-01-12 16:46:58 starting clair database container 'clair-db-3b0811925f7e8bc2'
2020-01-12 16:46:59 waiting for database server in container 'clair-db-3b0811925f7e8bc2' to start .............................
2020-01-12 16:47:32 successfully started clair database container
2020-01-12 16:47:32 clair configuration in '/var/folders/7x/rr443kj575s8zz54jrbrp4jc0000gn/T/tmp.BXCs3Giy'
2020-01-12 16:47:34 pulling clair image 'simonsdave/clair-cicd-clair:latest'
2020-01-12 16:47:36 successfully pulled clair image 'simonsdave/clair-cicd-clair:latest'
2020-01-12 16:47:36 starting clair container 'clair-fc579c71e7daba57'
2020-01-12 16:47:38 successfully started clair container 'clair-fc579c71e7daba57'
2020-01-12 16:47:38 saving docker image 'ubuntu:18.04' to '/tmp/tmp.lPDhNd'
2020-01-12 16:47:43 successfully saved docker image 'ubuntu:18.04'
2020-01-12 16:47:43 starting to create clair layers
2020-01-12 16:47:43 creating clair layer 'cc59b0ca1cf21d77c81a98138703008daa167b1ab1a115849d498dba64e738dd'
2020-01-12 16:47:43 successfully created clair layer 'cc59b0ca1cf21d77c81a98138703008daa167b1ab1a115849d498dba64e738dd'
2020-01-12 16:47:43 creating clair layer '27a911bb510bf1e9458437f0f44216fd38fd08c462ed7aa026d91aab8c054e54'
2020-01-12 16:47:44 successfully created clair layer '27a911bb510bf1e9458437f0f44216fd38fd08c462ed7aa026d91aab8c054e54'
2020-01-12 16:47:44 creating clair layer 'd80735acaa72040a0a98ca3ae6891f9abb4e2f5d627b4099c4fefdc3ce1e696e'
2020-01-12 16:47:44 successfully created clair layer 'd80735acaa72040a0a98ca3ae6891f9abb4e2f5d627b4099c4fefdc3ce1e696e'
2020-01-12 16:47:44 creating clair layer '1ee34a985f7aef86436a5519f5ad83f866a74c7d9a0c22e47c4213ee9cb64e6d'
2020-01-12 16:47:44 successfully created clair layer '1ee34a985f7aef86436a5519f5ad83f866a74c7d9a0c22e47c4213ee9cb64e6d'
2020-01-12 16:47:44 done creating clair layers
2020-01-12 16:47:44 starting to get vulnerabilities for clair layers
2020-01-12 16:47:44 saving vulnerabilities to directory '/tmp/tmp.dkfgmI'
2020-01-12 16:47:44 getting vulnerabilities for layer 'cc59b0ca1cf21d77c81a98138703008daa167b1ab1a115849d498dba64e738dd'
2020-01-12 16:47:44 successfully got vulnerabilities for layer 'cc59b0ca1cf21d77c81a98138703008daa167b1ab1a115849d498dba64e738dd'
2020-01-12 16:47:44 getting vulnerabilities for layer '27a911bb510bf1e9458437f0f44216fd38fd08c462ed7aa026d91aab8c054e54'
2020-01-12 16:47:44 successfully got vulnerabilities for layer '27a911bb510bf1e9458437f0f44216fd38fd08c462ed7aa026d91aab8c054e54'
2020-01-12 16:47:44 getting vulnerabilities for layer 'd80735acaa72040a0a98ca3ae6891f9abb4e2f5d627b4099c4fefdc3ce1e696e'
2020-01-12 16:47:44 successfully got vulnerabilities for layer 'd80735acaa72040a0a98ca3ae6891f9abb4e2f5d627b4099c4fefdc3ce1e696e'
2020-01-12 16:47:44 getting vulnerabilities for layer '1ee34a985f7aef86436a5519f5ad83f866a74c7d9a0c22e47c4213ee9cb64e6d'
2020-01-12 16:47:44 successfully got vulnerabilities for layer '1ee34a985f7aef86436a5519f5ad83f866a74c7d9a0c22e47c4213ee9cb64e6d'
2020-01-12 16:47:44 done getting vulnerabilities for clair layers
2020-01-12 21:47:45 INFO io:89 Looking for vulnerabilities in directory '/tmp/tmp.dkfgmI'
2020-01-12 21:47:45 INFO io:95 Found 4 files with vulnerabilities in directory '/tmp/tmp.dkfgmI'
2020-01-12 21:47:45 INFO io:104 Looking for vulnerabilities in '/tmp/tmp.dkfgmI/27a911bb510bf1e9458437f0f44216fd38fd08c462ed7aa026d91aab8c054e54.json'
2020-01-12 21:47:45 INFO io:122 Found 33 vulnerabilities in '/tmp/tmp.dkfgmI/27a911bb510bf1e9458437f0f44216fd38fd08c462ed7aa026d91aab8c054e54.json'
2020-01-12 21:47:45 INFO io:104 Looking for vulnerabilities in '/tmp/tmp.dkfgmI/cc59b0ca1cf21d77c81a98138703008daa167b1ab1a115849d498dba64e738dd.json'
2020-01-12 21:47:45 INFO io:122 Found 33 vulnerabilities in '/tmp/tmp.dkfgmI/cc59b0ca1cf21d77c81a98138703008daa167b1ab1a115849d498dba64e738dd.json'
2020-01-12 21:47:45 INFO io:104 Looking for vulnerabilities in '/tmp/tmp.dkfgmI/1ee34a985f7aef86436a5519f5ad83f866a74c7d9a0c22e47c4213ee9cb64e6d.json'
2020-01-12 21:47:45 INFO io:122 Found 33 vulnerabilities in '/tmp/tmp.dkfgmI/1ee34a985f7aef86436a5519f5ad83f866a74c7d9a0c22e47c4213ee9cb64e6d.json'
2020-01-12 21:47:45 INFO io:104 Looking for vulnerabilities in '/tmp/tmp.dkfgmI/d80735acaa72040a0a98ca3ae6891f9abb4e2f5d627b4099c4fefdc3ce1e696e.json'
2020-01-12 21:47:45 INFO io:122 Found 33 vulnerabilities in '/tmp/tmp.dkfgmI/d80735acaa72040a0a98ca3ae6891f9abb4e2f5d627b4099c4fefdc3ce1e696e.json'
2020-01-12 21:47:45 INFO io:133 Found 33 vulnerabilities in 4 files in directory '/tmp/tmp.dkfgmI'
2020-01-12 21:47:45 INFO assessor:19 Assessment starts
2020-01-12 21:47:45 INFO assessor:34 Assessing vulnerability CVE-2018-11236 - start
2020-01-12 21:47:45 INFO assessor:52 Vulnerability CVE-2018-11236 @ severity medium greater than whitelist severity @ negligible - fail
2020-01-12 21:47:45 INFO assessor:36 Assessing vulnerability CVE-2018-11236 - finish
2020-01-12 21:47:45 INFO assessor:23 Assessment ends - fail
~> echo $?
1
~>
The above is an example of an inline whitelist. It’s also possible to specify a whitelist in a file. The example below illustrates the usage. Note use of the file:// prefix to indicate the whitelist is contained in a file.
~> cat whitelist.json
{
"ignoreSevertiesAtOrBelow": "medium"
}
~> curl -s -L \
https://raw.githubusercontent.com/simonsdave/clair-cicd/master/bin/assess-image-risk.sh | \
bash -s -- -v --whitelist file://whitelist.json alpine:3.4
2020-01-12 16:48:41 pulling clair database image 'simonsdave/clair-cicd-database:latest'
2020-01-12 16:48:42 successfully pulled clair database image
2020-01-12 16:48:42 starting clair database container 'clair-db-191152e37b864e4b'
2020-01-12 16:48:43 waiting for database server in container 'clair-db-191152e37b864e4b' to start .............................
2020-01-12 16:49:16 successfully started clair database container
2020-01-12 16:49:16 clair configuration in '/var/folders/7x/rr443kj575s8zz54jrbrp4jc0000gn/T/tmp.GdlBNmiG'
2020-01-12 16:49:19 pulling clair image 'simonsdave/clair-cicd-clair:latest'
2020-01-12 16:49:20 successfully pulled clair image 'simonsdave/clair-cicd-clair:latest'
2020-01-12 16:49:20 starting clair container 'clair-747d1c50606fba7e'
2020-01-12 16:49:21 successfully started clair container 'clair-747d1c50606fba7e'
2020-01-12 16:49:22 saving docker image 'alpine:3.4' to '/tmp/tmp.Eldkbe'
2020-01-12 16:49:23 successfully saved docker image 'alpine:3.4'
2020-01-12 16:49:23 starting to create clair layers
2020-01-12 16:49:23 creating clair layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
2020-01-12 16:49:23 successfully created clair layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
2020-01-12 16:49:23 done creating clair layers
2020-01-12 16:49:23 starting to get vulnerabilities for clair layers
2020-01-12 16:49:23 saving vulnerabilities to directory '/tmp/tmp.pCOhlL'
2020-01-12 16:49:23 getting vulnerabilities for layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
2020-01-12 16:49:23 successfully got vulnerabilities for layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
2020-01-12 16:49:23 done getting vulnerabilities for clair layers
2020-01-12 21:49:23 INFO io:89 Looking for vulnerabilities in directory '/tmp/tmp.pCOhlL'
2020-01-12 21:49:23 INFO io:95 Found 1 files with vulnerabilities in directory '/tmp/tmp.pCOhlL'
2020-01-12 21:49:23 INFO io:104 Looking for vulnerabilities in '/tmp/tmp.pCOhlL/378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d.json'
2020-01-12 21:49:23 INFO io:122 Found 0 vulnerabilities in '/tmp/tmp.pCOhlL/378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d.json'
2020-01-12 21:49:23 INFO io:133 Found 0 vulnerabilities in 1 files in directory '/tmp/tmp.pCOhlL'
2020-01-12 21:49:23 INFO assessor:19 Assessment starts
2020-01-12 21:49:23 INFO assessor:26 Assessment ends - pass
~> echo $?
0
~>
Whitelists can also contain vulnerabilities. The example below illustrates this capability. If you add the -v (verbose) flag to assess-image-risk.sh you see exactly how the vulnerabilities impact the risk assessment with statements like Vulnerability CVE-2019-13627 in whitelist - pass
~> curl -s -L \
https://raw.githubusercontent.com/simonsdave/clair-cicd/master/bin/assess-image-risk.sh | \
bash -s -- --whitelist 'json://{"ignoreSevertiesAtOrBelow":"low"}' ubuntu:18.04
~> echo $?
1
~> cat whitelist.json
{
"ignoreSevertiesAtOrBelow": "low",
"vulnerabilities": [
{ "cveId": "CVE-2018-20839", "rationale": "reason #1" },
{ "cveId": "CVE-2019-5188", "rationale": "reason #2" },
{ "cveId": "CVE-2018-11236", "rationale": "reason #3" },
{ "cveId": "CVE-2019-13627", "rationale": "reason #4" },
{ "cveId": "CVE-2019-13050", "rationale": "reason #5" },
{ "cveId": "CVE-2018-11237", "rationale": "reason #6" },
{ "cveId": "CVE-2018-19591", "rationale": "reason #7" }
]
}
~> curl -s -L \
https://raw.githubusercontent.com/simonsdave/clair-cicd/master/bin/assess-image-risk.sh | \
bash -s -- --whitelist 'file://whitelist.json' ubuntu:18.04
~> echo $?
0
~>
Responsibilities
maintained by security analyst not service engineer
checked into source code control and appropriate change management processes are used to make changes (code reviews, feature branches, etc)
How it Works + Requirements/Assumptions
There are 3 moving pieces:
assess-image-risk.sh is bash script which does the heavy lifting to co-ordinate the interaction of the 2 other moving pieces
Clair’s vulnerability database which is packaged inside the docker image simonsdave/clair-database - a CircleCI cron job is used to rebuild simonsdave/clair-database 3 days per week to ensure the vulnerability database is current
a set of Python and Bash risk assessment scripts packaged in the simonsdave/clair-cicd-clair docker image which is based on the docker image quay.io/coreos/clair which packages up Clair
From the samples at the start of this doc you’ll see the approach of curl’ing the latest release of assess-image-risk.sh into a localy run bash shell. assess-image-risk.sh then spins up a container using the simonsdave/clair-database. Another container is then run using simonsdave/clair-cicd-clair with the simonsdave/clair-cicd-clair container being able to talk with the simonsdave/clair-database container. Once the simonsdave/clair-cicd-clair container is running, assess-image-risk.sh docker exec’s this bash script which does the actual risk assessment.
Armed with the understanding of how clair-cicd works you’ll appreciate that the ability to execute assess-image-risk.sh is what defines the requirements for the execution environment. assess-image-risk.sh is a bash script used to launch the risk assessment process and as such it’s this script which defines the bulk of the assumptions/requirements for clair-cicd - the script uses docker, sed and openssl so all these need to be available in the environment running clair-cicd
References
29 Jan ’20 - What Is DevSecOps, and How Is It Different from DevOps?
8 May ’18 - DevSecOps: 7 habits of strong security organizations
18 Apr ’18 - The Cloudcast #343 - Container Vulnerability Scanning
21 Feb ’18 - Automated Compliance Testing Tool Accelerates DevSecOps
27 Nov ’17 - What is vulnerability management? Processes and software for prioritizing threats
5 Oct ’17 - How to Maintain Security when Rolling out DevOps
Five Secrets and Two Common “Gotchas” of Vulnerability Scanning
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for clair_cicd-1.0.2-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 8186fbe9cc987ac58b2960e455e9b2548dfbb643956f908434e540132658bf8a |
|
MD5 | ffbbc675b3bbd308e82ba7d6e6603f0c |
|
BLAKE2b-256 | ff9ab5012a106a3f41836fcc9fe4d85bd38ed0649bcdcb20566f603249ae49f6 |