Skip to content

Commit

Permalink
feat: add fips-operator-check task
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
yashvardhannanavati committed Dec 17, 2024
1 parent 7d7e52e commit c94a207
Show file tree
Hide file tree
Showing 9 changed files with 563 additions and 8 deletions.
19 changes: 11 additions & 8 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,17 @@
/.tekton/tasks/ec-checks.yaml @konflux-ci/ec

# renovate groupName=integration
/task/clair-scan @konflux-ci/integration-service-maintainers
/task/clamav-scan @konflux-ci/integration-service-maintainers
/task/deprecated-image-check @konflux-ci/integration-service-maintainers
/task/fbc-related-image-check @konflux-ci/integration-service-maintainers
/task/fbc-validation @konflux-ci/integration-service-maintainers
/task/inspect-image @konflux-ci/integration-service-maintainers
/task/sbom-json-check @konflux-ci/integration-service-maintainers
/task/validate-fbc @konflux-ci/integration-service-maintainers
/task/clair-scan @konflux-ci/integration-service-maintainers
/task/clamav-scan @konflux-ci/integration-service-maintainers
/task/deprecated-image-check @konflux-ci/integration-service-maintainers
/task/fbc-related-image-check @konflux-ci/integration-service-maintainers
/task/fbc-validation @konflux-ci/integration-service-maintainers
/task/inspect-image @konflux-ci/integration-service-maintainers
/task/sbom-json-check @konflux-ci/integration-service-maintainers
/task/validate-fbc @konflux-ci/integration-service-maintainers
/task/fips-operator-bundle-check @konflux-ci/integration-service-maintainers
/task/fips-operator-bundle-check-oci-ta @konflux-ci/integration-service-maintainers
/stepactions/fips-operator-check-step-action @konflux-ci/integration-service-maintainers

# renovate groupName=integration
/task/coverity-availability-check @konflux-ci/integration-service-maintainers @kdudka
Expand Down
3 changes: 3 additions & 0 deletions renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,16 @@
{
"groupName": "integration",
"matchFileNames": [
"stepactions/fips-operator-check-step-action/**",
"task/clair-scan/**",
"task/clamav-scan/**",
"task/coverity-availability-check-oci-ta/**",
"task/coverity-availability-check/**",
"task/deprecated-image-check/**",
"task/fbc-related-image-check/**",
"task/fbc-validation/**",
"task/fips-operator-bundle-check-oci-ta/**",
"task/fips-operator-bundle-check/**",
"task/inspect-image/**",
"task/sast-coverity-check-oci-ta/**",
"task/sast-coverity-check/**",
Expand Down
16 changes: 16 additions & 0 deletions stepactions/fips-operator-check-step-action/0.1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## fips-operator-check-step-action

This stepAction scans relatedImages of operator bundle image builds for FIPS compliance using the check-payload tool.
* The relatedImages are expected to be in a file located at `/tekton/home/unique_related_images.txt`.
* If the check-payload scan is desired to be run with the built-in exception list, the target OCP version (`v4.x`) should be in a file located at `/tekton/home/target_ocp_version.txt`.
* It also supports replacing relatedImages pullspecs with their first mirror. In order to use that, a mapping like {"source_registry_and_repo": ["mirror_registry_and_repo"]} should be stored in a file located at `/tekton/home/related-images-map.txt`

## Results:

| name | description |
|--------------------|--------------------------------------|
| TEST_OUTPUT | Tekton task test output. |


## Additional links:
https://github.com/openshift/check-payload
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
---
apiVersion: tekton.dev/v1beta1
kind: StepAction
metadata:
labels:
app.kubernetes.io/version: "0.1"
annotations:
tekton.dev/pipelines.minVersion: "0.12.1"
tekton.dev/tags: "konflux"
name: fips-operator-check-step-action
spec:
description: >-
This stepAction scans relatedImages of operator bundle image builds for FIPS compliance using the check-payload tool.
results:
- name: TEST_OUTPUT
description: Tekton task test output.
image: quay.io/redhat-appstudio/konflux-test:v1.4.10@sha256:a9c8deb7582ac15ce0f0df0c7c7f017c33d8f12113c7efa3ed6811fd65e4706f
securityContext:
capabilities:
add:
- SETFCAP
script: |
#!/usr/bin/env bash
set -euo pipefail
# shellcheck source=/dev/null
. /utils.sh
success_counter=0
warnings_counter=0
error_counter=0
failure_counter=0
if [ ! -e "/tekton/home/unique_related_images.txt" ]; then
echo "No relatedImages to process"
exit 0
fi
related_images=$(cat /tekton/home/unique_related_images.txt)
echo "Related images are : ${related_images}"
# If target OCP version is found, use it to apply the exception list when running check-payload
check_payload_version=""
if [ -f "/tekton/home/target_ocp_version.txt" ]; then
version=$(cat "/tekton/home/target_ocp_version.txt")
check_payload_version="-V=${version}"
echo "Target OCP version found: ${check_payload_version}"
fi
# Check if an image to mirror map is defined for unreleased images
image_mirror_map=""
if [ -f "/tekton/home/related-images-map.txt" ]; then
image_mirror_map=$(cat "/tekton/home/related-images-map.txt")
echo "Image Mirror Map found: ${image_mirror_map}"
fi
for related_image in ${related_images}; do
echo "Processing related image : ${related_image}"
# Replace original pullspec with mirror, if present
if [ -n "${image_mirror_map}" ]; then
reg_and_repo=$(echo "${related_image}" | sed -E 's/^([^:@]+).*$/\1/')
first_mirror=$(echo "$image_mirror_map" | jq -r --arg image "$reg_and_repo" '.[$image][0]')
if [ "$first_mirror" != "null" ]; then
replaced_image=$(replace_image_pullspec "$related_image" "$first_mirror")
echo "Replacing $related_image with $replaced_image"
related_image="$replaced_image"
fi
fi
if ! image_labels=$(get_image_labels "${related_image}"); then
echo "Error: Could not inspect image ${related_image} for labels"
error_counter=$((error_counter + 1))
continue
fi
component_label=$(echo "${image_labels}" | grep 'com.redhat.component=' | cut -d= -f2 || true)
echo "Component label is ${component_label}"
if [ -z "${component_label}" ]; then
echo "Error: Could not get com.redhat.component label for ${related_image}"
error_counter=$((error_counter + 1))
continue
fi
# Convert image to OCI format since umoci can only handle the OCI format
if ! skopeo copy --remove-signatures "docker://${related_image}" "oci:///tekton/home/${component_label}:latest"; then
echo "Error: Could not convert image ${related_image} to OCI format"
error_counter=$((error_counter + 1))
continue
fi
# Unpack OCI image
if ! umoci raw unpack --rootless \
--image "/tekton/home/${component_label}:latest" \
"/tekton/home/unpacked-${component_label}"; then
echo "Error: Could not unpack OCI image ${related_image}"
error_counter=$((error_counter + 1))
continue
fi
# Run check-payload on the unpacked image
# The check-payload command fails with exit 1 when the scan for an image is unsuccessful
# or when the image is not FIPS compliant. Hence, count those as failures and not errors
if ! check-payload scan local \
--path="/tekton/home/unpacked-${component_label}" \
"${check_payload_version}" \
--components="${component_label}" \
--output-format=csv \
--output-file="/tekton/home/report-${component_label}.csv"; then
echo "check-payload scan failed for ${related_image}"
failure_counter=$((failure_counter + 1))
continue
fi
if [ -f "/tekton/home/report-${component_label}.csv" ]; then
if grep -q -- "---- Successful run" "/tekton/home/report-${component_label}.csv"; then
echo "check-payload scan was successful for ${related_image}"
success_counter=$((success_counter + 1))
elif grep -q -- "---- Successful run with warnings" "/tekton/home/report-${component_label}.csv"; then
echo "check-payload scan was successful with warnings for ${related_image}"
warnings_counter=$((warnings_counter + 1))
fi
fi
done
note="Task $(context.task.name) failed: Some images could not be scanned. For details, check Tekton task log."
ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note")
note="Task $(context.task.name) completed: Check result for task result."
if [[ "$error_counter" == 0 ]];
then
if [[ "${failure_counter}" -gt 0 ]]; then
RES="FAILURE"
elif [[ "${warnings_counter}" -gt 0 ]]; then
RES="WARNING"
else
RES="SUCCESS"
fi
TEST_OUTPUT=$(make_result_json \
-r "${RES}" \
-s "${success_counter}" -f "${failure_counter}" -w "${warnings_counter}" -t "$note")
fi
echo "${TEST_OUTPUT:-${ERROR_OUTPUT}}" | tee "$(step.results.TEST_OUTPUT.path)"
17 changes: 17 additions & 0 deletions task/fips-operator-bundle-check-oci-ta/0.1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# fips-operator-bundle-check-oci-ta task

The fips-operator-bundle-check task uses the check-payload tool to verify if an operator bundle image is FIPS compliant. It only scans operator bundle images which either claim to be FIPS compliant by setting the `features.operators.openshift.io/fips-compliant` label to `"true"` on the bundle image or require one of `OpenShift Kubernetes Engine, OpenShift Platform Plus or OpenShift Container Platform` subscriptions to run the operator on an Openshift cluster. This task extracts relatedImages from the operator bundle image and scans them. Hence, it is necessary for relatedImages pullspecs to be pullable at build time. In order to resolve them, this task expects a `imageDigestMirrorSet` file located at `.tekton/images-mirror-set.yaml` of your operator bundle git repo. It should map unreleased `registry.redhat.io` pullspecs of relatedImages to their valid `quay.io` pullspecs.

## Parameters
|name|description|default value|required|
|---|---|---|---|
|SOURCE_ARTIFACT|The Trusted Artifact URI pointing to the artifact with the application source code.||true|
|image-digest|Image digest to scan.||true|
|image-url|Image URL.||true|

## Results
|name|description|
|---|---|
|IMAGES_PROCESSED|Images processed in the task.|
|TEST_OUTPUT|Tekton task test output.|

Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
---
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: fips-operator-bundle-check-oci-ta
annotations:
tekton.dev/pipelines.minVersion: 0.12.1
tekton.dev/tags: konflux
labels:
app.kubernetes.io/version: "0.1"
spec:
description: The fips-operator-bundle-check task uses the check-payload
tool to verify if an operator bundle image is FIPS compliant. It only
scans operator bundle images which either claim to be FIPS compliant by
setting the `features.operators.openshift.io/fips-compliant` label to
`"true"` on the bundle image or require one of `OpenShift Kubernetes Engine,
OpenShift Platform Plus or OpenShift Container Platform` subscriptions
to run the operator on an Openshift cluster. This task extracts relatedImages
from the operator bundle image and scans them. Hence, it is necessary
for relatedImages pullspecs to be pullable at build time. In order to
resolve them, this task expects a `imageDigestMirrorSet` file located
at `.tekton/images-mirror-set.yaml` of your operator bundle git repo.
It should map unreleased `registry.redhat.io` pullspecs of relatedImages
to their valid `quay.io` pullspecs.
params:
- name: SOURCE_ARTIFACT
description: The Trusted Artifact URI pointing to the artifact with
the application source code.
type: string
- name: image-digest
description: Image digest to scan.
- name: image-url
description: Image URL.
results:
- name: IMAGES_PROCESSED
description: Images processed in the task.
- name: TEST_OUTPUT
description: Tekton task test output.
value: $(steps.fips-operator-check-step-action.results.TEST_OUTPUT)
volumes:
- name: workdir
emptyDir: {}
stepTemplate:
volumeMounts:
- mountPath: /var/workdir
name: workdir
steps:
- name: use-trusted-artifact
image: quay.io/redhat-appstudio/build-trusted-artifacts:latest@sha256:b31dc501d5068e30621e51681a2921d4e43f5a030ab78c8991f83a5e774534a3
args:
- use
- $(params.SOURCE_ARTIFACT)=/var/workdir/source
- name: get-unique-related-images
image: quay.io/redhat-appstudio/konflux-test:v1.4.10@sha256:a9c8deb7582ac15ce0f0df0c7c7f017c33d8f12113c7efa3ed6811fd65e4706f
env:
- name: IMAGE_URL
value: $(params.image-url)
- name: IMAGE_DIGEST
value: $(params.image-digest)
- name: SOURCE_CODE_DIR
value: /var/workdir
script: |
#!/usr/bin/env bash
set -euo pipefail
# shellcheck source=/dev/null
. /utils.sh
image_without_tag=$(echo -n "${IMAGE_URL}" | sed "s/\(.*\):.*/\1/")
# strip new-line escape symbol from parameter and save it to variable
image_and_digest="${image_without_tag}@${IMAGE_DIGEST}"
image_and_digest_labels=$(get_image_labels "${image_and_digest}")
if ! echo "${image_and_digest_labels}" | grep -q '^operators.operatorframework.io.bundle.mediatype.v1='; then
echo "The image $image_and_digest is not an operator bundle. Skipping FIPS static check..."
exit 0
fi
# Run the FIPS check only if the bundle is part of the Openshift Subscription or has the fips label set
image_and_digest_render_out=$(opm render "$image_and_digest")
subscription_label=$(echo "${image_and_digest_render_out}" | jq -r '.properties[] | select(.value.annotations["operators.openshift.io/valid-subscription"] != null) | (.value.annotations["operators.openshift.io/valid-subscription"] | fromjson)[]')
fips_label=$(echo "${image_and_digest_labels}" | grep '^features.operators.openshift.io/fips-compliant=' | cut -d= -f2 || true)
if ! echo "${subscription_label}" | grep -e "OpenShift Kubernetes Engine" -e "OpenShift Container Platform" -e "OpenShift Platform Plus"; then
echo "OpenShift Kubernetes Engine, OpenShift Platform Plus or OpenShift Container Platform are not present in operators.openshift.io/valid-subscription."
echo "Subscription labels are : $subscription_label"
if [ -z "${fips_label}" ] || [ "${fips_label}" != "true" ]; then
echo "The label features.operators.openshift.io/fips-compliant is also not set to true. Skipping the FIPS static check..."
exit 0
else
echo "The label features.operators.openshift.io/fips-compliant is set to true. Running the FIPS static check..."
fi
else
echo "OpenShift Kubernetes Engine, OpenShift Platform Plus or OpenShift Container Platform are present in operators.openshift.io/valid-subscription. Running the FIPS static check..."
fi
mirror_set="${SOURCE_CODE_DIR}/source/.tekton/images-mirror-set.yaml"
if [[ -f "${mirror_set}" ]]; then
mirror_set_yaml=$(cat "${mirror_set}")
process_image_digest_mirror_set "${mirror_set_yaml}" >"/tekton/home/related-images-map.txt"
else
echo "Could not find Related Images mirror set at ${mirror_set}. Unreleased relatedImages will fail the scan."
fi
unique_related_images=()
digests_processed=()
images_processed_template='{"image": {"pullspec": "'"$IMAGE_URL"'", "digests": [%s]}}'
echo "Inspecting raw image manifest $image_and_digest."
# Get the arch and image manifests by inspecting the image. This is mainly for identifying image indexes
image_manifests=$(get_image_manifests -i "${image_and_digest}")
echo "Image manifests are $image_manifests"
declare -A seen_related_images
# Extract relatedImages from the bundle image
while read -r _ arch_sha; do
digests_processed+=("\"$arch_sha\"")
manifest_related_images=$(extract_related_images_from_bundle "$image_without_tag@$arch_sha")
if [ -n "$manifest_related_images" ]; then
for img in $manifest_related_images; do
if [ -z "${seen_related_images["$img"]:-}" ]; then
unique_related_images+=("$img")
seen_related_images["$img"]=1
fi
done
fi
done < <(echo "$image_manifests" | jq -r 'to_entries[] | "\(.key) \(.value)"')
echo "Unique related images: ${unique_related_images[*]}"
echo "${unique_related_images[*]}" >/tekton/home/unique_related_images.txt
# If the image is an Image Index, also add the Image Index digest to the list.
if [[ "${digests_processed[*]}" != *"$IMAGE_DIGEST"* ]]; then
digests_processed+=("\"$IMAGE_DIGEST\"")
fi
digests_processed_string=$(
IFS=,
echo "${digests_processed[*]}"
)
echo "${images_processed_template/\[%s]/[$digests_processed_string]}" >/tekton/home/images_processed.txt
computeResources:
limits:
cpu: 200m
memory: 512Mi
requests:
cpu: 100m
memory: 256Mi
securityContext:
capabilities:
add:
- SETFCAP
- name: fips-operator-check-step-action
computeResources:
limits:
cpu: 200m
memory: 512Mi
requests:
cpu: 100m
memory: 256Mi
ref:
name: fips-operator-check-step-action
- name: parse-images-processed-result
image: quay.io/redhat-appstudio/konflux-test:v1.4.10@sha256:a9c8deb7582ac15ce0f0df0c7c7f017c33d8f12113c7efa3ed6811fd65e4706f
script: |
#!/usr/bin/env bash
set -euo pipefail
if [ -e "/tekton/home/images_processed.txt" ]; then
tee "$(results.IMAGES_PROCESSED.path)" </tekton/home/images_processed.txt
else
echo "Task was skipped. Exiting"
exit 0
fi
Loading

0 comments on commit c94a207

Please sign in to comment.