Skip to content

Commit 1be8abc

Browse files
feat: add fips-operator-check task
Refers to CVP-4333. This task uses the check-payload tool to verify if an operator bundle image is FIPS compliant.It utilizes Tekton stepAction because the code will be reused for checking FBC fragments in the fbc-validation check. Signed-off-by: Yashvardhan Nanavati <[email protected]>
1 parent 609f834 commit 1be8abc

File tree

6 files changed

+327
-0
lines changed

6 files changed

+327
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
## fips-operator-check-step-action
2+
3+
This stepAction scans relatedImages of operator bundle image builds for FIPS compliance using the check-payload tool.
4+
5+
## Params:
6+
7+
| name | description | default |
8+
|-----------------------------------|------------------------------------------------------------------------|---------------|
9+
| unique_related_images | Images to scan using the check-payload tool. | None |
10+
| images_processed | Image from which the relatedImages were extracted. | None |
11+
12+
## Results:
13+
14+
| name | description |
15+
|--------------------|--------------------------------------|
16+
| TEST_OUTPUT | Tekton task test output. |
17+
| IMAGES_PROCESSED | Images processed. |
18+
19+
20+
## Additional links:
21+
https://github.com/openshift/check-payload
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
---
2+
apiVersion: tekton.dev/v1beta1
3+
kind: StepAction
4+
metadata:
5+
labels:
6+
app.kubernetes.io/version: "0.1"
7+
annotations:
8+
tekton.dev/pipelines.minVersion: "0.12.1"
9+
tekton.dev/tags: "konflux"
10+
name: fips-operator-check-step-action
11+
spec:
12+
description: >-
13+
This stepAction scans relatedImages of operator bundle image builds for FIPS compliance using the check-payload tool.
14+
params:
15+
- name: unique_related_images
16+
description: Images to scan using check-payload.
17+
- name: images_processed
18+
description: Images that were processed as a part of the scan.
19+
results:
20+
- name: TEST_OUTPUT
21+
description: Tekton task test output.
22+
- name: IMAGES_PROCESSED
23+
description: Images processed in the task.
24+
image: quay.io/redhat-appstudio/konflux-test:v1.4.9@sha256:eee855e60b437d9a55a30e63f2eb7f95d9fd6d3b111c32cac8730c9b7a071394
25+
env:
26+
- name: unique_related_images
27+
value: $(params.unique_related_images)
28+
- name: images_processed
29+
value: $(params.images_processed)
30+
securityContext:
31+
capabilities:
32+
add:
33+
- SETFCAP
34+
script: |
35+
#!/usr/bin/env bash
36+
set -euo pipefail
37+
# shellcheck source=/dev/null
38+
. /utils.sh
39+
40+
success_counter=0
41+
warnings_counter=0
42+
error_counter=0
43+
failure_counter=0
44+
45+
# shellcheck disable=SC2154
46+
read -r -a related_images <<< "${unique_related_images}"
47+
48+
if [[ -z "${related_images[*]}" ]]; then
49+
echo "No relatedImages to process"
50+
exit 0
51+
else
52+
for related_image in "${related_images[@]}"; do
53+
echo "Processing related image : ${related_image}"
54+
55+
image_labels=$(skopeo inspect docker://"${related_image}" --config | jq -r '.config.Labels // {} | to_entries[] | "\(.key)=\(.value)"')
56+
component_label=$(echo "${image_labels}" | grep 'com.redhat.component=' | cut -d= -f2 || true)
57+
echo "Component label is ${component_label}"
58+
59+
if [ -z "${component_label}" ]; then
60+
echo "Error: Could not get com.redhat.component label for ${related_image}"
61+
error_counter=$((error_counter + 1))
62+
continue
63+
fi
64+
65+
# Convert image to OCI format since umoci can only handle the OCI format
66+
if ! skopeo copy --remove-signatures "docker://${related_image}" "oci:///tekton/home/${component_label}:latest"; then
67+
echo "Error: Could not convert image ${related_image} to OCI format"
68+
error_counter=$((error_counter + 1))
69+
continue
70+
fi
71+
72+
# Unpack OCI image
73+
if ! umoci raw unpack --rootless \
74+
--image "/tekton/home/${component_label}:latest" \
75+
"/tekton/home/unpacked-${component_label}"; then
76+
echo "Error: Could not unpack OCI image ${related_image}"
77+
error_counter=$((error_counter + 1))
78+
continue
79+
fi
80+
81+
echo "Now RUNNING SCAN ON THE IMAGE ${related_image}"
82+
83+
# Run check-payload on the unpacked image
84+
# The check-payload command fails with exit 1 when the scan for an image is unsuccessful
85+
# or when the image is not FIPS compliant. Hence, count those as failures and not errors
86+
if ! check-payload scan local \
87+
--path="/tekton/home/unpacked-${component_label}" \
88+
--components="${component_label}" \
89+
--output-format=csv \
90+
--output-file="/tekton/home/report-${component_label}.csv"; then
91+
echo "check-payload scan failed for ${related_image}"
92+
failure_counter=$((failure_counter + 1))
93+
continue
94+
fi
95+
96+
if [ -f "/tekton/home/report-${component_label}.csv" ]; then
97+
if grep -q -- "---- Successful run" "/tekton/home/report-${component_label}.csv"; then
98+
echo "check-payload scan was successful for ${related_image}"
99+
success_counter=$((success_counter + 1))
100+
elif grep -q -- "---- Successful run with warnings" "/tekton/home/report-${component_label}.csv"; then
101+
echo "check-payload scan was successful with warnings for ${related_image}"
102+
warnings_counter=$((warnings_counter + 1))
103+
fi
104+
fi
105+
106+
done
107+
fi
108+
109+
note="Task $(context.task.name) failed: Some images could not be scanned. For details, check Tekton task log."
110+
ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note")
111+
112+
note="Task $(context.task.name) completed: Check result for task result."
113+
if [[ "$error_counter" == 0 ]];
114+
then
115+
if [[ "${failure_counter}" -gt 0 ]]; then
116+
RES="FAILURE"
117+
elif [[ "${warnings_counter}" -gt 0 ]]; then
118+
RES="WARNING"
119+
else
120+
RES="SUCCESS"
121+
fi
122+
TEST_OUTPUT=$(make_result_json \
123+
-r "${RES}" \
124+
-s "${success_counter}" -f "${failure_counter}" -w "${warnings_counter}" -t "$note")
125+
fi
126+
echo "${TEST_OUTPUT:-${ERROR_OUTPUT}}" | tee "$(step.results.TEST_OUTPUT.path)"
127+
128+
if [ -n "${images_processed}" ]; then
129+
echo "${images_processed}" | tee "$(step.results.IMAGES_PROCESSED.path)"
130+
else
131+
echo "Could not generate IMAGES_PROCESSED result. images_processed parameter is empty"
132+
exit 1
133+
fi
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
approvers:
2+
- integration-team
3+
- yashvardhannanavati
4+
reviewers:
5+
- integration-team
6+
- yashvardhannanavati
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# fips-operator-bundle-check task
2+
3+
## Description:
4+
The fips-operator-bundle-check task uses the check-payload tool to verify if an operator bundle image is FIPS compliant.
5+
It only scans operator bundle images which either claim to be FIPS compliant by setting the `features.operators.openshift.io/fips-compliant`
6+
label to `"true"` on the bundle image or require one of `OpenShift Kubernetes Engine, OpenShift Platform Plus or OpenShift Container Platform`
7+
subscriptions to run the operator on an Openshift cluster.
8+
9+
## Params:
10+
11+
| name | description | default |
12+
|--------------------------|------------------------------------------------------------------------|---------------|
13+
| image-digest | Image digest to scan. | None |
14+
| image-url | Image URL. | None |
15+
16+
## Results:
17+
18+
| name | description |
19+
|--------------------|------------------------------|
20+
| TEST_OUTPUT | Tekton task test output. |
21+
| IMAGES_PROCESSED | Images processed in the task.|
22+
23+
24+
## Additional links:
25+
https://github.com/openshift/check-payload
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
---
2+
apiVersion: tekton.dev/v1
3+
kind: Task
4+
metadata:
5+
labels:
6+
app.kubernetes.io/version: "0.1"
7+
annotations:
8+
tekton.dev/pipelines.minVersion: "0.12.1"
9+
tekton.dev/tags: "konflux"
10+
name: fips-operator-bundle-check
11+
spec:
12+
description: >-
13+
Checks operator bundle image builds for FIPS compliance using the check-payload tool.
14+
params:
15+
- name: image-digest
16+
description: Image digest to scan.
17+
- name: image-url
18+
description: Image URL.
19+
results:
20+
- name: TEST_OUTPUT
21+
description: Tekton task test output.
22+
value: $(steps.fips-operator-check-step-action.results.TEST_OUTPUT)
23+
- name: IMAGES_PROCESSED
24+
description: Images processed in the task.
25+
value: $(steps.fips-operator-check-step-action.results.IMAGES_PROCESSED)
26+
steps:
27+
- name: get-unique-related-images
28+
image: quay.io/redhat-appstudio/konflux-test:v1.4.9@sha256:eee855e60b437d9a55a30e63f2eb7f95d9fd6d3b111c32cac8730c9b7a071394
29+
computeResources:
30+
limits:
31+
memory: 512Mi
32+
cpu: 200m
33+
requests:
34+
memory: 256Mi
35+
cpu: 100m
36+
env:
37+
- name: IMAGE_URL
38+
value: $(params.image-url)
39+
- name: IMAGE_DIGEST
40+
value: $(params.image-digest)
41+
results:
42+
- name: unique_related_images
43+
description: Unique relatedImages from input operator bundle image
44+
- name: images_processed
45+
description: Images processed in the task.
46+
securityContext:
47+
capabilities:
48+
add:
49+
- SETFCAP
50+
script: |
51+
#!/usr/bin/env bash
52+
set -euo pipefail
53+
# shellcheck source=/dev/null
54+
. /utils.sh
55+
56+
imagewithouttag=$(echo -n "${IMAGE_URL}" | sed "s/\(.*\):.*/\1/")
57+
# strip new-line escape symbol from parameter and save it to variable
58+
imageanddigest="${imagewithouttag}@${IMAGE_DIGEST}"
59+
60+
imageanddigest_labels=$(skopeo inspect docker://"${imageanddigest}" --config | jq -r '.config.Labels // {} | to_entries[] | "\(.key)=\(.value)"')
61+
if ! echo "${imageanddigest_labels}" | grep -q 'operators.operatorframework.io.bundle.mediatype.v1='; then
62+
echo "The image $imageanddigest is not an operator bundle. Skipping FIPS static check..."
63+
echo "" | tee "$(step.results.images_processed.path)" | tee "$(step.results.unique_related_images.path)"
64+
exit 0
65+
fi
66+
67+
# Run the FIPS check only if the bundle is part of the Openshift Subscription or has the fips label set
68+
imageanddigest_render_out=$(opm render "$imageanddigest")
69+
subscription_label=$(echo "${imageanddigest_render_out}" | jq -r '.properties[] | select(.value.annotations["operators.openshift.io/valid-subscription"] != null) | (.value.annotations["operators.openshift.io/valid-subscription"] | fromjson)[]')
70+
fips_label=$(echo "${imageanddigest_labels}" | grep 'features.operators.openshift.io/fips-compliant=' | cut -d= -f2 || true)
71+
72+
if ! echo "${subscription_label}" | grep -e "OpenShift Kubernetes Engine" -e "OpenShift Container Platform" -e "OpenShift Platform Plus"; then
73+
echo "OpenShift Kubernetes Engine, OpenShift Platform Plus or OpenShift Container Platform are not present in operators.openshift.io/valid-subscription."
74+
echo "Subscription labels are : $subscription_label"
75+
if [ -z "${fips_label}" ] || [ "${fips_label}" != "true" ]; then
76+
echo "The label features.operators.openshift.io/fips-compliant is also not set to true. Skipping the FIPS static check..."
77+
echo "" | tee "$(step.results.images_processed.path)" | tee "$(step.results.unique_related_images.path)"
78+
exit 0
79+
else
80+
echo "The label features.operators.openshift.io/fips-compliant is set to true. Running the FIPS static check..."
81+
fi
82+
else
83+
echo "OpenShift Kubernetes Engine, OpenShift Platform Plus or OpenShift Container Platform are present in operators.openshift.io/valid-subscription. Running the FIPS static check..."
84+
fi
85+
86+
unique_related_images=()
87+
digests_processed=()
88+
images_processed_template='{"image": {"pullspec": "'"$IMAGE_URL"'", "digests": [%s]}}'
89+
90+
echo "Inspecting raw image manifest $imageanddigest."
91+
# Get the arch and image manifests by inspecting the image. This is mainly for identifying image indexes
92+
image_manifests=$(get_image_manifests -i "${imageanddigest}")
93+
echo "Image manifests are $image_manifests"
94+
95+
declare -A seen_related_images
96+
# Extract relatedImages from the bundle image
97+
while read -r _ arch_sha; do
98+
digests_processed+=("\"$arch_sha\"")
99+
bundle_render_out=$(opm render "$imagewithouttag@$arch_sha")
100+
manifest_related_images=$(echo "${bundle_render_out}" | jq -r '.relatedImages[]?.image')
101+
if [ -n "$manifest_related_images" ]; then
102+
for img in $manifest_related_images; do
103+
if [ -z "${seen_related_images["$img"]:-}" ]; then
104+
unique_related_images+=("$img")
105+
seen_related_images["$img"]=1
106+
fi
107+
done
108+
fi
109+
done < <(echo "$image_manifests" | jq -r 'to_entries[] | "\(.key) \(.value)"')
110+
111+
echo "Unique related images: ${unique_related_images[*]}"
112+
echo "${unique_related_images[*]}" | tee "$(step.results.unique_related_images.path)"
113+
114+
# If the image is an Image Index, also add the Image Index digest to the list.
115+
if [[ "${digests_processed[*]}" != *"$IMAGE_DIGEST"* ]]; then
116+
digests_processed+=("\"$IMAGE_DIGEST\"")
117+
fi
118+
digests_processed_string=$(IFS=,; echo "${digests_processed[*]}")
119+
120+
echo "${images_processed_template/\[%s]/[$digests_processed_string]}" | tee "$(step.results.images_processed.path)"
121+
122+
- name: fips-operator-check-step-action
123+
computeResources:
124+
limits:
125+
memory: 512Mi
126+
cpu: 200m
127+
requests:
128+
memory: 256Mi
129+
cpu: 100m
130+
ref:
131+
name: fips-operator-check-step-action
132+
params:
133+
- name: unique_related_images
134+
value: $(steps.get-unique-related-images.results.unique_related_images)
135+
- name: images_processed
136+
value: $(steps.get-unique-related-images.results.images_processed)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
approvers:
2+
- integration-team
3+
- yashvardhannanavati
4+
reviewers:
5+
- integration-team
6+
- yashvardhannanavati

0 commit comments

Comments
 (0)