Skip to content

Instantly share code, notes, and snippets.

@psturc
Last active July 22, 2024 06:06
Show Gist options
  • Save psturc/0416344288f1dfc9a2b42556eab63a99 to your computer and use it in GitHub Desktop.
Save psturc/0416344288f1dfc9a2b42556eab63a99 to your computer and use it in GitHub Desktop.
my custom clair-scan task
---
apiVersion: tekton.dev/v1
kind: Task
metadata:
annotations:
tekton.dev/pipelines.minVersion: 0.12.1
tekton.dev/tags: appstudio, hacbs
creationTimestamp: null
labels:
app.kubernetes.io/version: "0.1"
test: "clair-scan"
name: clair-scan
spec:
description: Scans container images for vulnerabilities using Clair, by comparing
the components of container image against Clair's vulnerability databases.
params:
- description: Image digest to scan.
name: image-digest
- description: Image URL.
name: image-url
- default: ""
description: unused, should be removed in next task version.
name: docker-auth
results:
- description: Tekton task test output.
name: TEST_OUTPUT
- description: Clair scan result.
name: CLAIR_SCAN_RESULT
- description: Images processed in the task.
name: IMAGES_PROCESSED
steps:
- computeResources: {}
env:
- name: IMAGE_URL
value: $(params.image-url)
- name: IMAGE_DIGEST
value: $(params.image-digest)
image: quay.io/redhat-appstudio/konflux-test:v1.4.0@sha256:54d49b37c9a2e280d42961a57e4f7a16c171d6b065559f1329b548db85300bea
name: get-image-manifests
script: |
#!/usr/bin/env bash
set -euo pipefail
set -x
. /utils.sh
imagewithouttag=$(echo -n $IMAGE_URL | sed "s/\(.*\):.*/\1/")
# strip new-line escape symbol from parameter and save it to variable
imageanddigest=$(echo $imagewithouttag@$IMAGE_DIGEST)
echo "Inspecting raw image manifest $imageanddigest."
# Get the arch and image manifests by inspecting the image. This is mainly for identifying image indexes
image_manifests=$(get_image_manifests -i ${imageanddigest})
if [ -n "$image_manifests" ]; then
echo "$image_manifests" | jq -r 'to_entries[] | "\(.key) \(.value)"' | while read -r arch arch_sha; do
echo "$arch_sha" > /tekton/home/image-manifest-$arch.sha
done
fi
securityContext:
capabilities:
add:
- SETFCAP
- computeResources: {}
env:
- name: IMAGE_URL
value: $(params.image-url)
- name: IMAGE_DIGEST
value: $(params.image-digest)
image: quay.io/redhat-appstudio/clair-in-ci:v1
imagePullPolicy: Always
name: get-vulnerabilities
script: |
#!/usr/bin/env bash
set -x
imagewithouttag=$(echo -n $IMAGE_URL | sed "s/\(.*\):.*/\1/")
images_processed_template='{"image": {"pullspec": "'"$IMAGE_URL"'", "digests": [%s]}}'
digests_processed=()
for sha_file in /tekton/home/image-manifest-*.sha; do
if [ -e "$sha_file" ]; then
arch_sha=$(cat "$sha_file")
arch=$(basename "$sha_file" | sed 's/image-manifest-//;s/.sha//')
arch_specific_digest="$imagewithouttag@$arch_sha"
echo "Running clair-action on $arch image manifest."
# run the scan for each image manifest in the image index
echo "BEFORE CLAIR ACTION"
clair-action -l debug report --image-ref=$arch_specific_digest --db-path=/tmp/matcher.db --format=quay | tee /tekton/home/clair-result-$arch.json || true
echo "AFTER CLAIR ACTION"
digests_processed+=("\"$arch_sha\"")
fi
done
digests_processed_string=$(IFS=,; echo "${digests_processed[*]}")
# add the image_index to the processed digests list and store the result in a file
images_processed=$(echo "${images_processed_template/\[%s]/[$digests_processed_string]}")
echo "$images_processed" > /tekton/home/images-processed.json
- computeResources: {}
image: quay.io/redhat-appstudio/konflux-test:v1.4.0@sha256:54d49b37c9a2e280d42961a57e4f7a16c171d6b065559f1329b548db85300bea
name: conftest-vulnerabilities
script: |
#!/usr/bin/env bash
set -euo pipefail
set -x
. /utils.sh
trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT
clair_result_files=$(ls /tekton/home/clair-result-*.json)
if [ -z "$clair_result_files" ]; then
echo "Previous step [get-vulnerabilities] failed: No clair-result files found in /tekton/home."
fi
missing_vulnerabilities_files=""
for file in $clair_result_files; do
file_suffix=$(basename "$file" | sed 's/clair-result-//;s/.json//')
if [ ! -s "$file" ]; then
echo "Previous step [get-vulnerabilities] failed: $file is empty."
else
/usr/bin/conftest test --no-fail $file \
--policy /project/clair/vulnerabilities-check.rego --namespace required_checks \
--output=json | tee /tekton/home/clair-vulnerabilities-$file_suffix.json || true
fi
#check for missing "clair-vulnerabilities-<arch>/image-index" file and create a string
if [ ! -f "/tekton/home/clair-vulnerabilities-$file_suffix.json" ]; then
missing_vulnerabilities_files+="${missing_vulnerabilities_files:+, }/tekton/home/clair-vulnerabilities-$file_suffix.json"
fi
done
if [ -n "$missing_vulnerabilities_files" ]; then
note="Task $(context.task.name) failed: $missing_vulnerabilities_files did not generate. For details, check Tekton task log."
TEST_OUTPUT=$(make_result_json -r "ERROR" -t "$note")
echo "$missing_vulnerabilities_files did not generate correctly. For details, check conftest command in Tekton task log."
echo "${TEST_OUTPUT}" | tee $(results.TEST_OUTPUT.path)
exit 0
fi
scan_result='{"vulnerabilities":{"critical":0, "high":0, "medium":0, "low":0, "unknown":0}, "unpatched_vulnerabilities":{"critical":0, "high":0, "medium":0, "low":0, "unknown":0}}'
for file in /tekton/home/clair-vulnerabilities-*.json; do
result=$(jq -rce \
'{
vulnerabilities:{
critical: (.[] | .warnings? // [] | map(select(.metadata.details.name=="clair_critical_vulnerabilities").metadata."vulnerabilities_number" // 0)| add // 0),
high: (.[] | .warnings? // [] | map(select(.metadata.details.name=="clair_high_vulnerabilities").metadata."vulnerabilities_number" // 0)| add // 0),
medium: (.[] | .warnings? // [] | map(select(.metadata.details.name=="clair_medium_vulnerabilities").metadata."vulnerabilities_number" // 0)| add // 0),
low: (.[] | .warnings? // [] | map(select(.metadata.details.name=="clair_low_vulnerabilities").metadata."vulnerabilities_number" // 0)| add // 0),
unknown: (.[] | .warnings? // [] | map(select(.metadata.details.name=="clair_unknown_vulnerabilities").metadata."vulnerabilities_number" // 0)| add // 0)
},
unpatched_vulnerabilities:{
critical: (.[] | .warnings? // [] | map(select(.metadata.details.name=="clair_unpatched_critical_vulnerabilities").metadata."vulnerabilities_number" // 0)| add // 0),
high: (.[] | .warnings? // [] | map(select(.metadata.details.name=="clair_unpatched_high_vulnerabilities").metadata."vulnerabilities_number" // 0)| add // 0),
medium: (.[] | .warnings? // [] | map(select(.metadata.details.name=="clair_unpatched_medium_vulnerabilities").metadata."vulnerabilities_number" // 0)| add // 0),
low: (.[] | .warnings? // [] | map(select(.metadata.details.name=="clair_unpatched_low_vulnerabilities").metadata."vulnerabilities_number" // 0)| add // 0),
unknown: (.[] | .warnings? // [] | map(select(.metadata.details.name=="clair_unpatched_unknown_vulnerabilities").metadata."vulnerabilities_number" // 0)| add // 0)
}
}' "$file")
scan_result=$(jq -s -rce \
'.[0].vulnerabilities.critical += .[1].vulnerabilities.critical |
.[0].vulnerabilities.high += .[1].vulnerabilities.high |
.[0].vulnerabilities.medium += .[1].vulnerabilities.medium |
.[0].vulnerabilities.low += .[1].vulnerabilities.low |
.[0].vulnerabilities.unknown += .[1].vulnerabilities.unknown |
.[0].unpatched_vulnerabilities.critical += .[1].unpatched_vulnerabilities.critical |
.[0].unpatched_vulnerabilities.high += .[1].unpatched_vulnerabilities.high |
.[0].unpatched_vulnerabilities.medium += .[1].unpatched_vulnerabilities.medium |
.[0].unpatched_vulnerabilities.low += .[1].unpatched_vulnerabilities.low |
.[0].unpatched_vulnerabilities.unknown += .[1].unpatched_vulnerabilities.unknown |
.[0]' <<<"$scan_result $result")
done
echo "$scan_result" | tee $(results.CLAIR_SCAN_RESULT.path)
cat /tekton/home/images-processed.json | tee $(results.IMAGES_PROCESSED.path)
note="Task $(context.task.name) completed: Refer to Tekton task result CLAIR_SCAN_RESULT for vulnerabilities scanned by Clair."
TEST_OUTPUT=$(make_result_json -r "SUCCESS" -t "$note")
echo "${TEST_OUTPUT}" | tee $(results.TEST_OUTPUT.path)
securityContext:
capabilities:
add:
- SETFCAP
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
name: test-clair-scan
spec:
params:
- name: image-digest
value: sha256:83440566304234a445f2815db6a17930fa4cf21fbe04a69cdfbc423492f3ba19
- name: image-url
value: quay.io/psturc/test-images:for-clair-scan
taskRef:
name: clair-scan
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment