Last active
July 22, 2024 06:06
-
-
Save psturc/0416344288f1dfc9a2b42556eab63a99 to your computer and use it in GitHub Desktop.
my custom clair-scan task
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
--- | |
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 |
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
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