Exploring Container Security Tools
Snyk
Snyk is a container security tool, developed by Snyk Ltd. It can be used to analyze container images for known vulnerabilities. Snyk was integrated into Dock...
Snyk is a container security tool, developed by Snyk Ltd. It can be used to analyze container images for known
vulnerabilities. Snyk was integrated into Docker CLI as a subcommand, docker scan, in 2020 until replaced by Docker Scout in 2023. Snyk is now available as a Docker Desktop extension and a standalone CLI tool.
Unlike Trivy, Snyk is not open-source, but is free for public repositories.
My Snyk fun fact is that I've been a Snyk Ambassador since 2022. So, I have a hoodie from Snyk with my Twitter handle written on it. But now Elon Musk is destroying Twitter, and all my other social media accounts have different handles.
Installation
To use Snyk, one needs to create an account on Snyk.io and get an API token. The API token can be obtained from the
account settings page. The token can be set as an environment variable, e.g. SNYK_TOKEN.
Snyk is available as a Docker Desktop extension, and can be installed from the Docker Desktop UI. I'll skip the UI stuff as I'm more of a CLI person. To use Snyk CLI, there are two ways:
- Use the official Docker image,
- Install the CLI binary.
Using the Official Docker Image
Using the official Snyk Docker image is a bit more verbose than using Trivy:
docker run --rm -it \
--env SNYK_TOKEN=${SNYK_TOKEN} \
-v /var/run/docker.sock:/var/run/docker.sock \
snyk/snyk:docker snyk container test alpine:3.12
A few points:
- There are multiple Docker images based on the things you want to scan. In this example, we use the
snyk/snyk:dockerimage, which is used to scan Docker images. There is e.g.snyk/snyk:nodefor Node.js projects. - The
SNYK_TOKENenvironment variable is set to the API token obtained from Snyk.io. - The Docker socket is mounted to the container, so that the container can access the Docker daemon. This is needed because Snyk will pull the image to be scanned, and analyze it locally.
Since the command is quite verbose, you can define an alias to simplify it:
alias snyky='docker run --rm -it \
--env SNYK_TOKEN=${SNYK_TOKEN} \
-v /var/run/docker.sock:/var/run/docker.sock \
snyk/snyk:docker snyk'
I called it snyky because it's a sneaky way to use Snyk and I don't want it to clash with snyk in case I install
the CLI binary later.
snyky container test alpine:3.12
Installing the CLI Binary
There are multiple ways to install the Snyk CLI binary. The easiest way is to use NPM, if you happen to have Node.js installed on your system:
$ npm install -g snyk
If not, please confer the official installation instructions.
After the installation, you need to authenticate with Snyk using the API token:
$ snyk auth ${SNYK_TOKEN}
Analyzing Container Images
From now on, I'll use the command snyk, but you'll be able to do all the stuff with snyky as well.
To check a Docker image, say alpine:3.12, for known vulnerabilities, run the following command:
$ snyk container test alpine:3.12
The output of the command will be similar to the following:
Testing alpine:3.12...
✗ Critical severity vulnerability found in zlib/zlib
Description: Out-of-bounds Write
Info: https://security.snyk.io/vuln/SNYK-ALPINE312-ZLIB-2977082
Introduced through: zlib/zlib@1.2.12-r0, apk-tools/apk-tools@2.10.8-r1
From: zlib/zlib@1.2.12-r0
From: apk-tools/apk-tools@2.10.8-r1 > zlib/zlib@1.2.12-r0
Fixed in: 1.2.12-r2
Organization: aerabi
Package manager: apk
Project name: docker-image|alpine
Docker image: alpine:3.12
Platform: linux/arm64
Licenses: enabled
Tested 14 dependencies for known issues, found 1 issue.
Alpine 3.12.12 is no longer supported by the Alpine maintainers.
The output of the command shows that the image has one critical vulnerability, in the zlib library. There is also a link to Snyk's vulnerability database: SNYK-ALPINE312-ZLIB-2977082.
Opening the link will show the details of the vulnerability, including the CVE, the reason for the vulnerability, and the affected versions.
Container Image Scan Output Formats
Snyk, like Trivy, supports JSON and SARIF output formats. The following is an example of the JSON output:
$ snyk container test alpine:3.12 --json
Like Trivy, the output here is huge, and has a list of vulnerabilities among other things. Here is the beginning of the JSON output:
{
"vulnerabilities": [
{
"id": "SNYK-ALPINE312-ZLIB-2977082",
"cpes": [],
"title": "Out-of-bounds Write",
"CVSSv3": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"credit": [
""
],
"semver": {
"vulnerable": [
"<1.2.12-r2"
]
},
"exploit": "Not Defined",
"patches": [],
"insights": {
"triageAdvice": null
},
"language": "linux",
"severity": "critical",
"cvssScore": 9.8,
"malicious": false,
"isDisputed": false,
"references": [
{
"url": "https://github.com/ivd38/zlib_overflow",
"title": "cve@mitre.org"
},
The SARIF output is similar to the one of Trivy, and can be used to upload the results to the GitHub Security tab.
$ snyk container test alpine:3.12 --sarif
To store the SARIF file, you can use the following command:
$ snyk container test alpine:3.12 --sarif-file-output=sarif.json
This is essentially what we’ll replicate in the CI/CD pipeline.
GitHub Actions
Snyk can be used as a GitHub Action, and the results of the scan can be uploaded to the GitHub Security tab. The only difference here is that the token is set as a secret, so that the GitHub Actions workflow can access it.
The following is an example of a GitHub Actions workflow that uses Snyk to scan a Docker image:
name: "Scan Docker Application for Vulnerabilities"
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}-backend
defaults:
run:
working-directory: backend
jobs:
docker_build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: backend
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
docker_scan_snyk:
runs-on: ubuntu-latest
needs:
- docker_build
steps:
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Run Snyk to check Docker image for vulnerabilities
uses: snyk/actions/docker@master
continue-on-error: true
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
image: ${{ steps.meta.outputs.tags }}
sarif: true
args: --file=backend/Dockerfile
- name: Upload Snyk scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: snyk.sarif
In this example, we assume there is a Dockerfile in the backend directory. The Docker build part is similar to the
example we covered with Trivy. The CI pipeline builds the Docker image and pushes it to the GitHub Container Registry.
Then, it uses Snyk to scan the image for known vulnerabilities, and uploads the results to the GitHub Security tab.
Snyk GitHub Integration
Snyk also has a GitHub integration, which can be used to scan the repository for known vulnerabilities without the need for a CI pipeline. List of vulnerabilities then appears on Snyk's website and Snyk automatically creates pull requests to fix the vulnerabilities.
Exercises
- Create a GitHub Actions workflow that uses Snyk to scan a Docker image for known vulnerabilities, and uploads the results to the GitHub Security tab. Add it to your project of choice.
- Integrate Snyk with your GitHub repository, and scan it for known vulnerabilities.
- Create a SARIF report from Snyk and compare it with the one from Trivy.