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 16, 2024
1 parent 8842690 commit 82b0df6
Show file tree
Hide file tree
Showing 9 changed files with 551 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,144 @@
---
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/yashn/konflux-test-yashn:latest-amd64-dec17
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]')
echo "${reg_and_repo} and ${first_mirror}"
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

Checks operator bundle image builds for FIPS compliance using the check-payload tool.

## 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,162 @@
---
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: Checks operator bundle image builds for FIPS compliance using
the check-payload tool.
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/yashn/konflux-test-yashn:latest-amd64-dec17
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/related-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.9@sha256:eee855e60b437d9a55a30e63f2eb7f95d9fd6d3b111c32cac8730c9b7a071394
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
9 changes: 9 additions & 0 deletions task/fips-operator-bundle-check-oci-ta/0.1/recipe.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
base: ../../fips-operator-bundle-check/0.1/fips-operator-bundle-check.yaml
add:
- use-source
preferStepTemplate: true
removeWorkspaces:
- workspace
replacements:
workspaces.workspace.path: /var/workdir
Loading

0 comments on commit 82b0df6

Please sign in to comment.