-
Notifications
You must be signed in to change notification settings - Fork 875
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
280 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# snyn-container-scan-policy | ||
|
||
Scan Pulumi-managed Docker containers with Snyk and Pulumi Policy as Code: | ||
|
||
- The code in the `infra` directory creates two `docker.Image` resources: | ||
1. An image sourced from the `alpine` image, which does not have critical vulnerabilities. | ||
1. An image sourced from the `debian` image, which has critical vulnerabilities. | ||
- The code in the `policy` directory contains a Pulumi Policy Pack which calls the Snyk CLI. If the Snyk CLI fails (e.g. because it detects vulnerabilities), the resource will be considered in violation and the `pulumi preview` (or `pulumi up`) operation will fail. | ||
|
||
To run the demo: | ||
|
||
```bash | ||
cd infra | ||
pulumi preview --policy-pack ../policy | ||
``` | ||
|
||
## Configuration Options | ||
|
||
The `snyk-container-scan` policy has the following configurable options. These can be set by altering values in `policy-config.json` and calling Pulumi CLI with the `--policy-config` option, e.g.: | ||
|
||
```bash | ||
cd infra | ||
pulumi preview --policy-pack ../policy --policy-pack-config policy-config.json | ||
``` | ||
|
||
- `dockerfileScanning` (boolean): If set to `true`, Snyk will scan the Dockerfile of each image in the stack to scan for vulnerabilities in the upstream image. If set to `true`, `pulumiProgramAbsPath` must also be set to the absolute path on disk of the Pulumi program that contains the images so that the Snyk CLI can locate the Dockerfile. | ||
- `excludeBaseImageVulns` (boolean): If true, do not show vulnerabilities introduced only by the base image. Defaults to `false`. | ||
- `failOn`: Valid values: `all`, `upgradable`. Defaults to `all`. | ||
- `pulumiProgramAbsPath` (string, optional): The absolute path on disk to the Pulumi (IaC) program. Used by Snyk to scan Dockerfiles for vulnerabilities. Only used (and required) if `dockerfileScanning` is set to `true`. | ||
- `severityThreshold`: The minimum severity of found issues to report. If any issues are found at or above the minimum severity, the stack will contain violations. Valid values: `low`, `medium`, `high`, `critical`. Defaults to `critical`. | ||
|
||
For additional information on Snyk CLI options, see: <https://docs.snyk.io/snyk-cli/commands/container-test> | ||
|
||
## Enabling Dockerfile Scanning | ||
|
||
Snyk can scan Dockerfiles for vulnerabilities. Because there's no direct relationship between the location on disk of a Pulumi program and any policy packs that might be running, we need to configure the Snyk policy to know where the Pulumi program is running. | ||
|
||
The following script will add the needed absolute path configuration: | ||
|
||
```bash | ||
cd infra | ||
./add-dockerfile-scanning.sh | ||
``` | ||
|
||
Because Dockerfile scanning requires the absolute path to the Pulumi program to be supplied via policy configuration, server-side policy enforcement requires that the Pulumi program be run from a known location on disk (i.e. whatever the path on disk is that the policy is configured with in the Pulumi Cloud console) if Dockerfile scanning is desired. If <https://github.com/pulumi/pulumi-policy/issues/333> is implemented, this restriction can be lifted and the configuration value can be removed. | ||
|
||
## Troubleshooting | ||
|
||
### Failed to connect to Docker Daemon | ||
|
||
If Pulumi gives the following error: | ||
|
||
```text | ||
Docker native provider returned an unexpected error from Configure: failed to connect to any docker daemon | ||
``` | ||
|
||
Start Docker Desktop on your machine. | ||
|
||
### Snyk Unable to find Docker Socket | ||
|
||
If the Snyk CLI gives you an error similar to the following: | ||
|
||
```text | ||
connect ENOENT /var/run/docker.sock | ||
``` | ||
|
||
You may need to set the `DOCKER_HOST` environment variable. At the time of writing, the Snyk CLI appears to assume that the Docker socket is running in the older (privileged) location. The newer version of Docker use a socket placed in the current user's home directory, e.g.: | ||
|
||
```bash | ||
export DOCKER_HOST=unix:///Users/jkodroff/.docker/run/docker.sock | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/bin/ | ||
/node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
FROM alpine:3.19.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
FROM debian:12.5 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
name: snyk-container-scan-policy-ts | ||
runtime: nodejs | ||
description: A minimal TypeScript Pulumi program | ||
config: | ||
pulumi:tags: | ||
value: | ||
pulumi:template: typescript |
2 changes: 2 additions & 0 deletions
2
snyk-container-scan-policy-ts/infra/add-dockerfile-scanning.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#!/bin/bash | ||
echo "$(jq --arg pwd "$(pwd)" '.["snyk-container-scan"] += {"pulumiProgramAbsPath": $pwd}' policy-config.json)" > policy-config.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import * as docker from "@pulumi/docker"; | ||
|
||
// This image does not have critical issues: | ||
new docker.Image("alpine", { | ||
imageName: "docker.io/joshkodroff/snyk-policy-alpine", | ||
buildOnPreview: true, | ||
build: { | ||
dockerfile: "AlpineDockerfile", | ||
platform: "linux/amd64", | ||
}, | ||
skipPush: true, | ||
}); | ||
|
||
// This image has critical issues: | ||
new docker.Image("debian", { | ||
imageName: "docker.io/joshkodroff/snyk-policy-debian", | ||
buildOnPreview: true, | ||
build: { | ||
dockerfile: "DebianDockerfile", | ||
platform: "linux/amd64", | ||
}, | ||
skipPush: true, | ||
}); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"name": "snyk-container-scan-policy-ts", | ||
"main": "index.ts", | ||
"devDependencies": { | ||
"@types/node": "^18" | ||
}, | ||
"dependencies": { | ||
"@pulumi/docker": "^4.5.1", | ||
"@pulumi/pulumi": "^3.0.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"snyk-container-scan": { | ||
"enforcementLevel": "mandatory", | ||
"dockerfileScanning": false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"compilerOptions": { | ||
"strict": true, | ||
"outDir": "bin", | ||
"target": "es2016", | ||
"module": "commonjs", | ||
"moduleResolution": "node", | ||
"sourceMap": true, | ||
"experimentalDecorators": true, | ||
"pretty": true, | ||
"noFallthroughCasesInSwitch": true, | ||
"noImplicitReturns": true, | ||
"forceConsistentCasingInFileNames": true | ||
}, | ||
"files": [ | ||
"index.ts" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/bin/ | ||
/node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
runtime: nodejs | ||
description: A minimal Policy Pack for AWS using TypeScript. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { PolicyPack, PolicyResource, ReportViolation, StackValidationArgs } from "@pulumi/policy"; | ||
const awaitSpawn = require("await-spawn"); | ||
const fs = require("fs"); | ||
|
||
interface SnykPolicyConfig { | ||
dockerfileScanning: boolean, | ||
excludeBaseImageVulns: boolean, | ||
failOn: string, | ||
pulumiProgramAbsPath: string, | ||
severityThreshold: string, | ||
} | ||
|
||
const validateStack = async (args: StackValidationArgs, reportViolation: ReportViolation) => { | ||
const config = args.getConfig<SnykPolicyConfig>(); | ||
|
||
if (config.dockerfileScanning && !config.pulumiProgramAbsPath) { | ||
throw new Error("If `dockerfileScanning` is configured to be `true`, `pulumiProgramAbsPath` must be set to the absolute path of the Pulumi program this policy is evaluating."); | ||
} | ||
|
||
const dockerImages = args.resources.filter(x => x.type === "docker:index/image:Image"); | ||
for (const image of dockerImages) { | ||
await validateStackImage(config, image, reportViolation); | ||
} | ||
}; | ||
|
||
const validateStackImage = async (config: SnykPolicyConfig, image: PolicyResource, reportViolation: ReportViolation) => { | ||
const commandArgs = [ | ||
"container", | ||
"test", | ||
image.props["imageName"], | ||
]; | ||
|
||
if (config.dockerfileScanning) { | ||
const dockerfileAbsPath = `${config.pulumiProgramAbsPath}/${image.props.dockerfile}`; | ||
|
||
if (!fs.existsSync(dockerfileAbsPath)) { | ||
const msg = `dockerfileScanning is set to 'true', but the Dockerfile at path '${dockerfileAbsPath}' could not be found. Either reconfigure the policy to turn off Dockerfile scanning, or set the value of docker.Image.snyk.dockerfileAbsPath resource to the absolute path of the Dockerfile in a resource transform.`; | ||
reportViolation(msg); | ||
return; | ||
} | ||
|
||
commandArgs.push(`--file=${dockerfileAbsPath}`); | ||
} | ||
|
||
if (config.excludeBaseImageVulns) { | ||
commandArgs.push("--exclude-base-image-vulns"); | ||
} | ||
|
||
commandArgs.push(`--severity-threshold=${config.severityThreshold}`); | ||
|
||
try { | ||
await awaitSpawn("snyk", commandArgs); | ||
} catch (e) { | ||
let errorMessage = `Snyk validation failed.`; | ||
|
||
if (e.stdout && e.stdout.toString()) { | ||
errorMessage += `\n${e.stdout.toString()}`; | ||
} | ||
|
||
if (e.stderr && e.stderr.toString()) { | ||
errorMessage += `\n${e.stderr.toString()}`; | ||
} | ||
|
||
reportViolation(errorMessage); | ||
} | ||
}; | ||
|
||
new PolicyPack("snyk-container-scanning", { | ||
policies: [{ | ||
name: "snyk-container-scan", | ||
configSchema: { | ||
properties: { | ||
"dockerfileScanning": { | ||
default: true, | ||
type: "boolean", | ||
}, | ||
"excludeBaseImageVulns": { | ||
default: false, | ||
type: "boolean" | ||
}, | ||
"failOn": { | ||
default: "all", | ||
enum: ["all", "upgradable"] | ||
}, | ||
"pulumiProgramAbsPath": { | ||
type: "string" | ||
}, | ||
"severityThreshold": { | ||
default: "critical", | ||
enum: ["low", "medium", "high", "critical"] | ||
}, | ||
}, | ||
}, | ||
enforcementLevel: "mandatory", | ||
description: "Scans Docker Images with Snyk", | ||
validateStack: validateStack, | ||
}], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"name": "synk-container-scan", | ||
"version": "0.0.1", | ||
"main": "index.ts", | ||
"devDependencies": { | ||
"@types/node": "^18" | ||
}, | ||
"dependencies": { | ||
"@pulumi/docker": "^4.5.1", | ||
"@pulumi/policy": "^1.10.0", | ||
"@pulumi/pulumi": "^3.0.0", | ||
"await-spawn": "^4.0.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"compilerOptions": { | ||
"outDir": "bin", | ||
"target": "es6", | ||
"module": "commonjs", | ||
"moduleResolution": "node", | ||
"declaration": true, | ||
"sourceMap": false, | ||
"stripInternal": true, | ||
"experimentalDecorators": true, | ||
"pretty": true, | ||
"noFallthroughCasesInSwitch": true, | ||
"noImplicitAny": true, | ||
"noImplicitReturns": true, | ||
"forceConsistentCasingInFileNames": true, | ||
"strictNullChecks": true, | ||
}, | ||
"files": [ | ||
"index.ts" | ||
] | ||
} |