Supply Chain

Assess Dockerfile Security with cnspec

Scan Dockerfiles for security misconfigurations using cnspec.

Enforce container image hygiene at the source by scanning Dockerfiles for security misconfigurations. cnspec checks for issues like running as root, missing health checks, and pinned base image versions, so you can block bad patterns in CI before an image is ever built.

Prerequisites

To scan Dockerfiles with cnspec, you must have:

Scan a Dockerfile

Scan a single Dockerfile, substituting the path and name of the file for FILEPATH:

cnspec scan docker file FILEPATH

Find nested Dockerfiles within a directory, substituting the directory path for PATH:

cnspec scan docker file PATH

Explore Dockerfiles

Run cnspec shell docker file FILEPATH to open the interactive shell and explore your Dockerfiles.

List stages in a multi-stage build

cnspec> docker.file.stages
docker.file.stages: [
  0: docker.file.stage
  1: docker.file.stage
]

Retrieve base images

Get the base image for a specific stage:

cnspec> docker.file.stages[0].from
docker.file.stages[0].from: {
  image: "ubuntu"
  tag: "22.04"
}

Get all FROM instructions across stages:

cnspec> docker.file.stages { from }
docker.file.stages: [
  0: {
    from: {
      image: "node"
      tag: "18-alpine"
    }
  }
  1: {
    from: {
      image: "nginx"
      tag: "alpine"
    }
  }
]

Retrieve RUN instructions

cnspec> docker.file.stages[0].run
docker.file.stages[0].run: [
  0: "apt-get update"
  1: "apt-get install -y curl"
]

Retrieve USER configuration

cnspec> docker.file.stages[0].user
docker.file.stages[0].user: {
  user: "appuser"
}

Retrieve exposed ports

cnspec> docker.file.stages[0].expose
docker.file.stages[0].expose: [
  0: {
    port: 8080
    protocol: "tcp"
  }
]

Retrieve environment variables

cnspec> docker.file.stages[0].env
docker.file.stages[0].env: {
  NODE_ENV: "production"
  APP_PORT: "8080"
}

Retrieve COPY instructions

cnspec> docker.file.stages[0].copy { src dst chown chmod }
docker.file.stages[0].copy: [
  0: {
    src: ["package.json"]
    dst: "/app/"
    chown: "appuser:appuser"
    chmod: ""
  }
]

Retrieve ADD instructions

cnspec> docker.file.stages[0].add { src dst }
docker.file.stages[0].add: [
  0: {
    src: ["https://example.com/config.tar.gz"]
    dst: "/opt/"
  }
]

Retrieve build arguments

cnspec> docker.file.stages[0].arg { name default }
docker.file.stages[0].arg: [
  0: {
    name: "NODE_VERSION"
    default: "18"
  }
]

Retrieve image labels

cnspec> docker.file.stages[0].labels
docker.file.stages[0].labels: {
  maintainer: "team@example.com"
  version: "1.0"
}

Retrieve entrypoint and CMD

cnspec> docker.file.stages[0].entrypoint
docker.file.stages[0].entrypoint: {
  script: "node server.js"
}
cnspec> docker.file.stages[0].cmd
docker.file.stages[0].cmd: {
  script: "npm start"
}

List all instructions

cnspec> docker.file.instructions
docker.file.instructions: {
  FROM: ["node:18-alpine"]
  RUN: ["apt-get update", "apt-get install -y curl"]
  COPY: ["package.json /app/"]
  ...
}

Example security checks

Ensure the Dockerfile does not use the root user

cnspec> docker.file.user.user != "root"
[ok] value: true

Ensure a HEALTHCHECK instruction is defined

cnspec> docker.file.healthcheck.test.length > 0
[ok] value: true

Verify no stage uses the latest tag

cnspec> docker.file.stages.all(from.tag != "latest")
docker.file.stages.all: true

Verify COPY is used instead of ADD

cnspec> docker.file.stages.all(add == empty)
docker.file.stages.all: true

Verify no RUN instructions use sudo

cnspec> docker.file.stages.all(run.none(script.contains("sudo")))
docker.file.stages.all: true

Scan options

OptionDescription
--asset-nameOverride the asset name
--annotationAdd an annotation to the asset (key=value)
--incognitoRun in incognito mode (do not report results to Mondoo Platform)
-o, --outputSet the output format (compact, full, json, junit, summary, yaml)
-f, --policy-bundlePath to a policy file (local path, s3:// URI, or http(s):// URL)
--policySpecify policies to execute (requires --policy-bundle)
--risk-thresholdExit with status 1 if any risk meets or exceeds this value (0-100)

Learn more

On this page