File 80-vulnerability-scan-neuvector of Package post-build-checks-containers.41016
#!/bin/bash
#
# Perform vulnerability scan of a container image built by OBS using NeuVector.
# Strict mode
set -euo pipefail
TOPDIR="/usr/src/packages"
OUTPUTDIR="$TOPDIR/DOCKER"
# Helper functions
log() {
echo "$@" >&2
}
check_required_environment_variables() {
local required_environment_variable
for required_environment_variable in "$@"; do
if [[ -z ${!required_environment_variable:-} ]]; then
log "Missing environment variable '$required_environment_variable'"
return 1
fi
done
}
containerinfos() {
find "$OUTPUTDIR" -type f -name '*.containerinfo'
}
check_required_environment_variables BUILD_DIR BUILD_ROOT
# Obtain versions
NV_VERSION="$(rpm -qa | grep -E '^neuvector-scanner-[a-f0-9]+-' |
sed -E 's/.*-([a-f0-9]+)-.*/\1/g')"
NV_DB_VERSION="$(scanner -d /etc/neuvector/db/ -v | grep -Eo '[0-9.]+')"
log "Using NeuVector version $NV_VERSION (db version: $NV_DB_VERSION)"
log "Retrieving container image metadata"
CONTAINERINFO="$(find "$OUTPUTDIR" -name '*.containerinfo' -print -quit)"
CONTAINER_NAME="$(basename "$CONTAINERINFO" | sed 's/\.containerinfo$//')"
CONTAINER_TAG="$(python3 -c \
'import json, sys; print(json.load(sys.stdin)["tags"][0])' \
<"$CONTAINERINFO")"
log "Container image tag to check: $CONTAINER_TAG"
log "Starting Docker"
"$BUILD_DIR/startdockerd" \
--root "$BUILD_ROOT" \
--webserver "$TOPDIR/SOURCES/repos" \
--webserver-upload "$TOPDIR/SOURCES/repos/UPLOAD"
# Load the final built image into Docker, this is based on build-recipe-docker
# https://github.com/openSUSE/obs-build/blob/master/build-recipe-docker#L152-L170
DOCKER_CMD="chroot $BUILD_ROOT docker"
if [[ -z "$($DOCKER_CMD images -q "$CONTAINER_TAG")" ]]; then
log "Importing container image to Docker daemon"
for image_path in $(find "$OUTPUTDIR" -regextype egrep \
-regex ".*\.(tgz|tar|tar\.xz|tar\.gz)$" -print); do
echo "Loading container image ${image_path##*/}"
if test -L "$image_path"; then
# copy into build root
cp -L "$image_path" "$image_path.lnk"
mv "$image_path.lnk" "$image_path"
fi
# Inspect the content of the image to decide if this is a layered image
# or a filesystem one. We need to know if we will "docker load" it or
# "docker import" it.
if tar -tf $image_path | grep -q "^manifest.json"; then
$DOCKER_CMD load --input $image_path
else
echo "Layered images not supported, FS-based image required"
exit 1
fi
done
fi
# In some environments, the image is tagged with the `localhost/` prefix
# But when it is tagged with this prefix, we cannot reference images without it
if [[ -z "$($DOCKER_CMD images -q "$CONTAINER_TAG")" &&
-n "$($DOCKER_CMD images -q "localhost/$CONTAINER_TAG")" ]]; then
log "Tagging localhost/$CONTAINER_TAG as $CONTAINER_TAG"
$DOCKER_CMD tag "localhost/$CONTAINER_TAG" "$CONTAINER_TAG"
fi
log "Scanning image with NeuVector"
export SCANNER_ON_DEMAND=true
export SCANNER_REPOSITORY="${CONTAINER_TAG%%:*}"
export SCANNER_TAG="${CONTAINER_TAG#*:}"
monitor
log "Stopping Docker"
"$BUILD_DIR/startdockerd" \
--root "$BUILD_ROOT" \
--kill
# Convert file to the Cosign Vulnerability Scan Record Attestation Spec format
# https://github.com/sigstore/cosign/blob/main/specs/COSIGN_VULN_ATTESTATION_SPEC.md
# NOTE: The subject will be filled by OBS during publishing
# This command merges the files, avoid --argjson due to error using large files
NV_SCAN_RESULT="/var/neuvector/scan_result.json"
OUTPUT_FILE="$OUTPUTDIR/$CONTAINER_NAME.neuvector_vuln.intoto.json"
SCAN_TIMESTAMP="$(date '+%Y-%m-%dT%H:%M:%S.%N%:z')"
jq -s '.[1].predicate.scanner.result = .[0] | .[1]' "$NV_SCAN_RESULT" - \
>"$OUTPUT_FILE" <<EOF
{
"_type": "https://in-toto.io/Statement/v0.1",
"predicateType": "https://cosign.sigstore.dev/attestation/vuln/v1",
"subject": [
{
"name": "",
"digest": {
"sha256": ""
}
}
],
"predicate": {
"invocation": {
"parameters": null,
"uri": "",
"event_id": "",
"builder.id": ""
},
"scanner": {
"uri": "pkg:github.com/neuvector/scanner@$NV_VERSION",
"version": "$NV_VERSION",
"db": {
"uri": "pkg:github.com/neuvector/vul-dbgen@$NV_DB_VERSION",
"version": "$NV_DB_VERSION"
},
"result": {}
},
"metadata": {
"scanStartedOn": "$SCAN_TIMESTAMP",
"scanFinishedOn": "$SCAN_TIMESTAMP"
}
}
}
EOF
log "Cleaning up"
rm "$NV_SCAN_RESULT"