From a0d50ff38a398cca9e16c77393b5a4ad507431ea Mon Sep 17 00:00:00 2001 From: Parthey Khanderia <60154758+pkhander@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:37:56 -0400 Subject: [PATCH] feat(CLDX-79): add sign-mac-binaries step to sign-binaries task (#566) This commit adds sign-mac-binaries step to the sign-binaries task. Signed-off-by: Parthey Khanderia --- tasks/publish-to-cgw/README.md | 4 + tasks/publish-to-cgw/publish-to-cgw.yaml | 34 ++- tasks/sign-binaries/README.md | 8 + tasks/sign-binaries/sign-binaries.yaml | 286 +++++++++++++++--- tasks/sign-binaries/tests/checksum_mocks.sh | 18 +- tasks/sign-binaries/tests/mac_mocks.sh | 53 ++++ tasks/sign-binaries/tests/mocks.sh | 21 +- .../tests/pre-apply-task-hook.sh | 35 +-- .../tests/test-sign-binaries-checksum.yaml | 21 +- .../tests/test-sign-binaries.yaml | 94 +++++- 10 files changed, 493 insertions(+), 81 deletions(-) create mode 100644 tasks/sign-binaries/tests/mac_mocks.sh diff --git a/tasks/publish-to-cgw/README.md b/tasks/publish-to-cgw/README.md index e98ecc7cb..20b433de5 100644 --- a/tasks/publish-to-cgw/README.md +++ b/tasks/publish-to-cgw/README.md @@ -16,6 +16,10 @@ Tekton task to publish content to Red Hat's Developer portal using pubtools-cont | cgwHostname | The hostname of the content-gateway to publish the metadata to | yes | https://developers.redhat.com/content-gateway/rest/admin | | cgwSecret | The kubernetes secret to use to authenticate to content-gateway | yes | publish-to-cgw-secret | +## Changes in 0.2.3 +* Added logic to handle checksum files +* Fix bug in computing shortUrl + ## Changes in 0.2.2 * Update the base image used in this task to provide jsonschema module diff --git a/tasks/publish-to-cgw/publish-to-cgw.yaml b/tasks/publish-to-cgw/publish-to-cgw.yaml index c2993be03..9ea85c8ee 100644 --- a/tasks/publish-to-cgw/publish-to-cgw.yaml +++ b/tasks/publish-to-cgw/publish-to-cgw.yaml @@ -4,7 +4,7 @@ kind: Task metadata: name: publish-to-cgw labels: - app.kubernetes.io/version: "0.2.2" + app.kubernetes.io/version: "0.2.3" annotations: tekton.dev/pipelines.minVersion: "0.12.1" tekton.dev/tags: release @@ -92,7 +92,6 @@ spec: # values from DATA_FILE takes presedence over these default_values_per_component = { 'type': "FILE", - 'shortURL': f"/cgw/{productCode}", "hidden": False, "invisible": False } @@ -131,17 +130,44 @@ spec: 'productCode': productCode, 'productVersionName': productVersionName, 'downloadURL': generate_download_url(file), + 'shortURL': f"/cgw/{productCode}/{file}", 'label': file, }) del component['name'] - default_values_per_component['shortURL'] += f"/{file}" metadata.append({ 'type': 'file', 'action': 'create', 'metadata': {**default_values_per_component, **component} }) + else: - print(f"Skipping file: {file} as it does not start with any component name") + if file.startswith('sha256'): + if file.endswith(".gpg"): + label = "Checksum - GPG" + elif file.endswith(".sig"): + label = "Checksum - Signature" + elif file.endswith(".txt"): + label = "Checksum" + + metadata.append({ + 'type': 'file', + 'action': 'create', + 'metadata': { + 'productName': productName, + 'productCode': productCode, + 'productVersionName': productVersionName, + 'downloadURL': generate_download_url(file), + 'shortURL': f"/cgw/{productCode}/{file}", + 'label': label, + **default_values_per_component + } + }) + else: + # Skip files that do not start with any component name or + # sha256 + print(f"Skipping file: {file} as it does not start with any \ + component name") + continue return metadata diff --git a/tasks/sign-binaries/README.md b/tasks/sign-binaries/README.md index 5d3a08fbf..13be545d6 100644 --- a/tasks/sign-binaries/README.md +++ b/tasks/sign-binaries/README.md @@ -2,6 +2,8 @@ Tekton task to sign windows and mac binaries before they are pushed to the Red Hat Developer Portal +If any help is needed with this task, please ping in [#clouddst](https://redhat.enterprise.slack.com/archives/C04QRCD4SQZ) slack channel. + ## Parameters | Name | Description | Optional | Default value | @@ -23,6 +25,12 @@ Tekton task to sign windows and mac binaries before they are pushed to the Red H | pipelineRunUid | Unique ID of the pipelineRun | No | | +## Changes in 2.1.0 +* Added sign-mac-binaries step +* Added push-unsigned-using-oras step +* Added a new task result named 'binaries_path' that can be utilized by subsequent tasks. +* Fix sign-windows-binaries step + ## Changes in 2.0.0 * Add checksum signing step diff --git a/tasks/sign-binaries/sign-binaries.yaml b/tasks/sign-binaries/sign-binaries.yaml index 78414b59f..f8a1de5d1 100644 --- a/tasks/sign-binaries/sign-binaries.yaml +++ b/tasks/sign-binaries/sign-binaries.yaml @@ -4,7 +4,7 @@ kind: Task metadata: name: sign-binaries labels: - app.kubernetes.io/version: "2.0.0" + app.kubernetes.io/version: "2.1.0" annotations: tekton.dev/pipelines.minVersion: "0.12.1" tekton.dev/tags: release @@ -71,14 +71,25 @@ spec: - name: checksum-keytab-vol secret: secretName: $(params.checksumKeytab) + - name: mac-ssh-key-vol + secret: + secretName: $(params.macSSHKey) workspaces: - name: data description: Workspace to save the results to results: + - name: unsignedMacDigest + type: string + description: | + Digest used by Mac signing host to pull unsigned content via ORAS + - name: signedMacDigest + type: string + description: | + Digest used to pull signed Mac content back to pipeline via ORAS - name: unsignedWindowsDigest type: string description: | - Digest used by signing host to pull unsignged content via ORAS + Digest used by Windows signing host to pull unsigned content via ORAS - name: signedWindowsDigest type: string description: | @@ -86,29 +97,213 @@ spec: - name: unsignedMacDigest type: string description: | - Digest used by signing host to pull unsignged content via ORAS + Digest used by signing host to pull unsigned content via ORAS - name: signedMacDigest type: string description: | Digest used to pull signed content back to pipeline via ORAS + - name: binaries_path + type: string + description: | + Path where the final signed content is stored in the workspace steps: - name: push-unsigned-using-oras image: quay.io/konflux-ci/release-service-utils:e633d51cd41d73e4b3310face21bb980af7a662f + env: + - name: QUAY_USER + valueFrom: + secretKeyRef: + name: $(params.quaySecret) + key: username + - name: QUAY_PASS + valueFrom: + secretKeyRef: + name: $(params.quaySecret) + key: password script: | #!/usr/bin/env bash + set -eux - # TODO CLDX-134 + CONTENT_DIR=$(workspaces.data.path)/$(params.contentDir) + UNSIGNED_DIR=$CONTENT_DIR/unsigned + MAC_CONTENT=$UNSIGNED_DIR/macos + WINDOWS_CONTENT=$UNSIGNED_DIR/windows + LINUX_CONTENT=$CONTENT_DIR/linux + + mkdir -p "$MAC_CONTENT" "$WINDOWS_CONTENT" "$LINUX_CONTENT" + cd "$CONTENT_DIR" + + # Loop through each gz file and move them to the appropriate directory + for file in *.gz; do + case "$file" in + (*darwin*) + mv "$file" unsigned/macos/ + ;; + (*windows*) + mv "$file" unsigned/windows/ + ;; + (*linux*) + mv "$file" linux/ + ;; + esac + done + + # Unzip files in each directory + for dir in unsigned/macos unsigned/windows linux; do + for file in "$dir"/*.gz; do + gunzip "$file" + done + done - output=$(oras push "$(params.quayURL)/unsigned" .) + cd "$UNSIGNED_DIR" + + echo "Logging into Quay..." + set +x + oras login quay.io -u "${QUAY_USER}" -p "${QUAY_PASS}" > /dev/null 2>&1 + set -x + echo "Pushing unsigned Macos content to $(params.quayURL)..." + output=$(oras push "$(params.quayURL)/unsigned" macos) + mac_digest=$(echo "$output" | grep 'Digest:' | awk '{print $2}') + echo "Digest for mac content: $mac_digest" + echo -n "$mac_digest" > "$(results.unsignedMacDigest.path)" + + echo "Pushing unsigned Windows content to $(params.quayURL)..." + output=$(oras push "$(params.quayURL)/unsigned" windows) windows_digest=$(echo "$output" | grep 'Digest:' | awk '{print $2}') echo "Digest for windows content: $windows_digest" echo -n "$windows_digest" > "$(results.unsignedWindowsDigest.path)" + - name: sign-mac-binaries + image: quay.io/konflux-ci/release-service-utils:e633d51cd41d73e4b3310face21bb980af7a662f + volumeMounts: + - name: mac-ssh-key-vol + mountPath: "/etc/secrets" + readOnly: true + env: + - name: MAC_USER + valueFrom: + secretKeyRef: + name: $(params.macHostCredentials) + key: username + - name: MAC_HOST + valueFrom: + secretKeyRef: + name: $(params.macHostCredentials) + key: host + - name: KEYCHAIN_PASSWORD + valueFrom: + secretKeyRef: + name: $(params.macSigningCredentials) + key: keychain_password + - name: SIGNING_IDENTITY + valueFrom: + secretKeyRef: + name: $(params.macSigningCredentials) + key: signing_identity + - name: APPLE_ID + valueFrom: + secretKeyRef: + name: $(params.macSigningCredentials) + key: apple_id + - name: TEAM_ID + valueFrom: + secretKeyRef: + name: $(params.macSigningCredentials) + key: team_id + - name: APP_SPECIFIC_PASSWORD + valueFrom: + secretKeyRef: + name: $(params.macSigningCredentials) + key: app_specific_password + - name: QUAY_USER + valueFrom: + secretKeyRef: + name: $(params.quaySecret) + key: username + - name: QUAY_PASS + valueFrom: + secretKeyRef: + name: $(params.quaySecret) + key: password + - name: QUAY_URL + value: $(params.quayURL) + - name: PIPELINE_UID + value: $(params.pipelineRunUid) + script: | + #!/usr/bin/env bash + set -eux + + mkdir -p /root/.ssh + chmod 700 /root/.ssh + cp "/etc/secrets/mac_id_rsa" /root/.ssh/id_rsa + cp "/etc/secrets/mac_fingerprint" /root/.ssh/known_hosts + chmod 600 /root/.ssh/id_rsa /root/.ssh/known_hosts + + SSH_OPTS=(-i /root/.ssh/id_rsa -o UserKnownHostsFile=/root/.ssh/known_hosts) + + # shell check complains about the variable (unsigned_digest) not being used but it is used in the script + # shellcheck disable=SC2034 + unsigned_digest=$(cat "$(results.unsignedMacDigest.path)") + mac_signing_script="/tmp/mac_signing_script.sh" + + TEMP_DIR="/tmp/$(params.pipelineRunUid)" + BINARY_PATH="$TEMP_DIR/unsigned/macos" + ZIP_PATH="$TEMP_DIR/signed_content.zip" + DIGEST_FILE="$TEMP_DIR/push_digest.txt" + + cat << EOF > "$mac_signing_script" + #!/bin/bash + set -eux + + mkdir -p "$TEMP_DIR" + mkdir -p "$BINARY_PATH" + + cd "$TEMP_DIR" + /usr/local/bin/oras login quay.io -u ${QUAY_USER} -p ${QUAY_PASS} + /usr/local/bin/oras pull $(params.quayURL)/unsigned@$unsigned_digest -o "$BINARY_PATH" + # This is the directory where the content was extracted + CONTENT_DIR=\$(find "$BINARY_PATH" -maxdepth 1 -type d | tail -n 1) - # - name: sign-mac-binaries - # image: quay.io/konflux-ci/release-service-utils:e633d51cd41d73e4b3310face21bb980af7a662f - # script: | - # #!/usr/bin/env bash - # # TODO CLDX-79 + security unlock-keychain -p $KEYCHAIN_PASSWORD login.keychain + + echo "Signing files in the \$CONTENT_DIR directory..." + find "\$CONTENT_DIR" -type f | while read file; do + echo "Signing: \$file" + if ! xcrun codesign --sign "Developer ID Application: $SIGNING_IDENTITY" \ + --options runtime --timestamp --force "\$file"; then + echo "Failed to sign file: \$file" + exit 1 + fi + done + + cd "$BINARY_PATH" + NEW_CONTENT_DIR=\$(basename "\$CONTENT_DIR") + zip -r "$ZIP_PATH" "\$NEW_CONTENT_DIR" + + xcrun notarytool submit "$ZIP_PATH" \ + --wait \ + --apple-id "$APPLE_ID" \ + --team-id "$TEAM_ID" \ + --password "$APP_SPECIFIC_PASSWORD" + + PUSH_OUTPUT=\$(/usr/local/bin/oras push "$QUAY_URL/signed:$(params.pipelineRunUid)-mac" "\$NEW_CONTENT_DIR") + SIGNED_DIGEST=\$(echo "\$PUSH_OUTPUT" | grep 'Digest:' | awk '{print \$2}') + echo -n "\$SIGNED_DIGEST" >> "$DIGEST_FILE" + echo "Process completed successfully." + EOF + # Copy the script to the Mac host + scp "${SSH_OPTS[@]}" "$mac_signing_script" "${MAC_USER}@${MAC_HOST}:/tmp/mac_signing_script.sh" + + # Execute the script on the Mac host + ssh "${SSH_OPTS[@]}" "${MAC_USER}@${MAC_HOST}" bash /tmp/mac_signing_script.sh + + # Copy the signed digest back to the pipeline + scp "${SSH_OPTS[@]}" "${MAC_USER}@${MAC_HOST}:/tmp/$(params.pipelineRunUid)/push_digest.txt" \ + "$(results.signedMacDigest.path)" + + # Clean up the Mac host now that we are done + # shell check complains about the variable (params.pipelineRunUid) being evaluated on the client side + # shellcheck disable=SC2029 + ssh "${SSH_OPTS[@]}" "${MAC_USER}@${MAC_HOST}" "rm -rf /tmp/$(params.pipelineRunUid)" - name: sign-windows-binaries image: quay.io/konflux-ci/release-service-utils:e633d51cd41d73e4b3310face21bb980af7a662f volumeMounts: @@ -147,9 +342,9 @@ spec: mkdir -p /root/.ssh chmod 700 /root/.ssh - cp "/etc/secrets/id_rsa" /root/.ssh/id_rsa - cp "/etc/secrets/fingerprint" /root/.ssh/known_hosts - chmod 600 root/.ssh/known_hosts /root/.ssh/id_rsa + cp "/etc/secrets/windows_id_rsa" /root/.ssh/id_rsa + cp "/etc/secrets/windows_fingerprint" /root/.ssh/known_hosts + chmod 600 /root/.ssh/known_hosts /root/.ssh/id_rsa SSH_OPTS="-i /root/.ssh/id_rsa -o UserKnownHostsFile=/root/.ssh/known_hosts -p ${WINDOWS_PORT} \ ${WINDOWS_USER}@${WINDOWS_HOST}" @@ -157,9 +352,9 @@ spec: unsigned_digest=$(cat "$(results.unsignedWindowsDigest.path)") # Create the batch script - signing_script_file="/tmp/signing_script.bat" + windows_signing_script_file="/tmp/windows_signing_script_file.bat" set +x - cat << EOF > "$signing_script_file" + cat << EOF > "$windows_signing_script_file" mkdir %TEMP%\$(params.pipelineRunUid) && cd /d %TEMP%\$(params.pipelineRunUid) @echo off @@ -168,14 +363,14 @@ spec: oras pull $(params.quayURL)/unsigned@${unsigned_digest} signtool sign /v /n "Red Hat" /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 ^ - %TEMP%\$(params.pipelineRunUid)\* + %TEMP%\$(params.pipelineRunUid)\windows\* if errorlevel 1 ( echo Signing of binaries failed exit /B %ERRORLEVEL% ) - signtool verify /v /pa %TEMP%\$(params.pipelineRunUid)\* + signtool verify /v /pa %TEMP%\$(params.pipelineRunUid)\windows\* if errorlevel 1 ( echo Verification of binaries failed @@ -184,29 +379,33 @@ spec: echo [%DATE% %TIME%] Signing of Windows binaries completed successfully - oras push $(params.quayURL)/signed:$(params.pipelineRunUid) %TEMP%\$(params.pipelineRunUid) \ + oras push $(params.quayURL)/signed:$(params.pipelineRunUid)-windows windows \ > oras_push_output.txt 2>&1 for /f "tokens=2,3 delims=: " %%a in ('findstr "Digest:" oras_push_output.txt') do @echo %%a:%%b > digest.txt EOF set -x - scp "$SCP_OPTS" "$signing_script_file" \ - "${WINDOWS_USER}@${WINDOWS_HOST}:C:/Users/Administrator/AppData/Local/Temp/signing_script.bat" + # shellcheck disable=SC2086 + scp $SCP_OPTS "$windows_signing_script_file" \ + "${WINDOWS_USER}@${WINDOWS_HOST}:C:/Users/Administrator/AppData/Local/Temp/windows_signing_script_file.bat" # Execute the script on the Windows host - ssh "$SSH_OPTS" "C:/Users/Administrator/AppData/Local/Temp/signing_script.bat" + # shellcheck disable=SC2086 + ssh $SSH_OPTS "C:/Users/Administrator/AppData/Local/Temp/windows_signing_script_file.bat" # disable shellcheck for escaping the pipelineRunUid as we want that evaluated on client side - # shellcheck disable=SC2029 - - scp "$SCP_OPTS" "${WINDOWS_USER}@${WINDOWS_HOST}:\ - C:\\Users\\Administrator\\AppData\\Local\\Temp\\$(params.pipelineRunUid)\\digest.txt" \ + # shellcheck disable=SC2029,SC2086 + scp $SCP_OPTS "${WINDOWS_USER}@${WINDOWS_HOST}:\ + C:/Users/Administrator/AppData/Local/Temp/$(params.pipelineRunUid)/digest.txt" \ "$(results.signedWindowsDigest.path)" - # Clean up the windows host now that we are done - # shellcheck disable=SC2029 - ssh "$SSH_OPTS" "rmdir /s /q C:\\Users\\Administrator\\AppData\\Local\\Temp\\$(params.pipelineRunUid)" + # Remove trailing spaces, carriage returns, newlines + sed -i 's/[[:space:]]*$//; s/\r//g; :a;N;$!ba;s/\n//g' "$(results.signedWindowsDigest.path)" + # Clean up the windows host now that we are done + # disable shellcheck for escaping the pipelineRunUid as we want that evaluated on client side + # shellcheck disable=SC2029,SC2086 + ssh $SSH_OPTS "rmdir /s /q C:\\Users\\Administrator\\AppData\\Local\\Temp\\$(params.pipelineRunUid)" - name: generate-checksums image: quay.io/konflux-ci/release-service-utils:e633d51cd41d73e4b3310face21bb980af7a662f volumeMounts: @@ -229,6 +428,7 @@ spec: key: password script: | #!/usr/bin/env bash + set -eux #--------------------------------------------------------------------------------------- # This step generates checksums for all of the binaries in the content directory and @@ -257,8 +457,8 @@ spec: input_file="/home/$checksum_user/$pipeline_run_uid/checksum/sha256sum.txt" echo "Executing SSH command with sign method: $sign_method" - # shellcheck disable=SC2029 - ssh "$SSH_OPTS" "$checksum_user@$checksum_host" \ + # shellcheck disable=SC2029,SC2086 + ssh $SSH_OPTS "$checksum_user@$checksum_host" \ "rpm-sign --nat $sign_method --key redhatrelease2 --onbehalfof=$AUTHOR \ --output $output_path $input_file" } @@ -281,12 +481,22 @@ spec: mkdir -p "$SIGNED_DIR" mkdir -p "$CONTENT_DIR"/linux cp -r "$CONTENT_DIR"/linux/* "$SIGNED_DIR" - cd "$SIGNED_DIR" || exit + cd "$SIGNED_DIR" + set +x oras login quay.io -u "$QUAY_USER" -p "$QUAY_PASS" + set -x signed_mac_digest=$(cat "$(results.signedMacDigest.path)") signed_windows_digest=$(cat "$(results.signedWindowsDigest.path)") - oras pull "$(params.quayURL)/signed@${signed_mac_digest}" - oras pull "$(params.quayURL)/signed@${signed_windows_digest}" + signed_windows_digest=${signed_windows_digest//[[:space:]]/} + # shellcheck disable=SC2086,SC2046 + oras pull $(params.quayURL)/signed@${signed_mac_digest} + # shellcheck disable=SC2086,SC2046 + oras pull $(params.quayURL)/signed@${signed_windows_digest} + + # Copy everything to SIGNED_DIR and remove mac,windows dirs + cp macos/* . + cp windows/* . + rm -r macos/ windows/ # generate checksums for all of the binaries SHA_SUM_PATH="${CONTENT_DIR}/sha256sum.txt" @@ -298,9 +508,10 @@ spec: fi done # Send sha256sum.txt to the checksum host for signing - # shellcheck disable=SC2029 - ssh "$SSH_OPTS" "$(params.checksumUser)@$(params.checksumHost)" "mkdir -p ~/$(params.pipelineRunUid)/checksum" - scp "$SSH_OPTS" "${SHA_SUM_PATH}" \ + # shellcheck disable=SC2029,SC2086 + ssh $SSH_OPTS "$(params.checksumUser)@$(params.checksumHost)" "mkdir -p ~/$(params.pipelineRunUid)/checksum" + # shellcheck disable=SC2086 + scp $SSH_OPTS "${SHA_SUM_PATH}" \ "$(params.checksumUser)@$(params.checksumHost):~/$(params.pipelineRunUid)/checksum" sign_file --clearsign sig @@ -316,3 +527,6 @@ spec: "${SIGNED_DIR}/sha256sum.txt.gpg" mv "$SHA_SUM_PATH" "${SIGNED_DIR}/sha256sum.txt" + + # shellcheck disable=SC2086 + echo -n "$(params.contentDir)/signed" | tee "$(results.binaries_path.path)" diff --git a/tasks/sign-binaries/tests/checksum_mocks.sh b/tasks/sign-binaries/tests/checksum_mocks.sh index a59b44a18..366eb702d 100644 --- a/tasks/sign-binaries/tests/checksum_mocks.sh +++ b/tasks/sign-binaries/tests/checksum_mocks.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash count_file="/tmp/ssh_count_checksum.txt" @@ -12,7 +12,6 @@ function ssh() { ssh_call_count=$(cat "$count_file") ssh_call_count=$((ssh_call_count + 1)) echo "$ssh_call_count" > "$count_file" - echo "$ssh_call_count" > $(workspaces.data.path)/ssh_calls_checksum.txt } @@ -20,6 +19,7 @@ scp_count_file="/tmp/scp_count_checksum.txt" if [[ ! -f "$scp_count_file" ]]; then echo "0" > "$scp_count_file" fi + function scp() { scp_call_count=$(cat "$scp_count_file") scp_call_count=$((scp_call_count + 1)) @@ -42,5 +42,17 @@ function kinit() { } function oras() { - echo "oras $@" + oras_args=$@ + echo "Trying to use oras with $oras_args" + + # Check for Windows signing script + if [[ "$oras_args" == *"pull"* && "$oras_args" == *"signed"* ]]; then + echo "Trying to pull signed digests: $@" + mkdir -p "$(workspaces.data.path)/$(params.contentDir)/signed/macos/" + mkdir -p "$(workspaces.data.path)/$(params.contentDir)/signed/windows/" + echo -n "some data" | \ + tee "$(workspaces.data.path)/$(params.contentDir)/signed/macos/mac_binary.bin" + echo -n "some data" | \ + tee "$(workspaces.data.path)/$(params.contentDir)/signed/windows/windows_binary.bin" + fi } diff --git a/tasks/sign-binaries/tests/mac_mocks.sh b/tasks/sign-binaries/tests/mac_mocks.sh new file mode 100644 index 000000000..8d1959118 --- /dev/null +++ b/tasks/sign-binaries/tests/mac_mocks.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +# This script specifically is used to specifically mock mac server calls + +count_file=$(workspaces.data.path)/mac_ssh_count.txt +if [[ ! -f "$count_file" ]]; then + echo "0" > "$count_file" +fi + + +function ssh() { + # Read the current ssh_call_count from the file + ssh_call_count=$(cat "$count_file") + ssh_call_count=$((ssh_call_count + 1)) + echo "$ssh_call_count" > "$count_file" + + echo "$ssh_call_count" > $(workspaces.data.path)/ssh_calls_mac.txt +} + +scp_count_file="/tmp/scp_count_mac.txt" +if [[ ! -f "$scp_count_file" ]]; then + echo "0" > "$scp_count_file" +fi + +function scp() { + scp_call_count=$(cat "$scp_count_file") + scp_call_count=$((scp_call_count + 1)) + echo "$scp_call_count" > "$scp_count_file" + if [[ "$scp_call_count" -eq 1 ]]; then + echo "$@" > "$(workspaces.data.path)/mock_scp_1.txt" + fi + + scp_args=$@ + + if [[ "$scp_args" == *"mac_signing_script"* ]]; then + echo "MAC SCP command detected with args: $@" + echo "$@" > $(workspaces.data.path)/scp_mac_args.txt + fi + + if [[ "$scp_call_count" -eq 2 ]]; then + echo "$@" > "$(workspaces.data.path)/mock_scp_2.txt" + echo -n "sha256:0c4355ee4ef8d9d3875d5421972aed405ce6d8f5262983eeea3f6cbf5740c6e2" | \ + tee "$(results.signedWindowsDigest.path)" + fi + +} +function kinit() { + echo "kinit $@" +} + +function oras() { + echo "oras $@" +} diff --git a/tasks/sign-binaries/tests/mocks.sh b/tasks/sign-binaries/tests/mocks.sh index b77ca80eb..8aac936c5 100644 --- a/tasks/sign-binaries/tests/mocks.sh +++ b/tasks/sign-binaries/tests/mocks.sh @@ -1,12 +1,11 @@ -#!/bin/bash +#!/usr/bin/env bash -count_file="/tmp/ssh_count.txt" +count_file=$(workspaces.data.path)/win_ssh_count.txt if [[ ! -f "$count_file" ]]; then echo "0" > "$count_file" fi - function ssh() { # Read the current ssh_call_count from the file ssh_call_count=$(cat "$count_file") @@ -20,6 +19,7 @@ scp_count_file="/tmp/scp_count.txt" if [[ ! -f "$scp_count_file" ]]; then echo "0" > "$scp_count_file" fi + function scp() { scp_call_count=$(cat "$scp_count_file") scp_call_count=$((scp_call_count + 1)) @@ -28,15 +28,30 @@ function scp() { echo "$@" > "$(workspaces.data.path)/mock_scp_1.txt" fi + scp_args=$@ + + # Check for Windows signing script + if [[ "$scp_args" == *"windows_signing_script"* ]]; then + echo "Windows SCP command detected with args: $@" + echo "$@" > $(workspaces.data.path)/scp_windows_args.txt + fi if [[ "$scp_call_count" -eq 2 ]]; then echo "$@" > "$(workspaces.data.path)/mock_scp_2.txt" echo -n "sha256:0c4355ee4ef8d9d3875d5421972aed405ce6d8f5262983eeea3f6cbf5740c6e2" | \ tee "$(results.signedWindowsDigest.path)" + echo -n "sha256:0c4355ee4ef8d9d3875d5421972aed405ce6d8f5262983eeea3f6cbf5740c6e2" | \ + tee "$(results.signedMacDigest.path)" fi } +oras_count_file="/tmp/oras_count.txt" +if [[ ! -f "$count_file" ]]; then + echo "0" > "$count_file" +fi + + function oras() { # this is mocking the oras command to push unsigned binaries to the registry echo "Digest: sha256:5ce6d8f5262983eeea3f6cbf5740c6e20c4355ee4ef8d9d3875d5421972aed40" diff --git a/tasks/sign-binaries/tests/pre-apply-task-hook.sh b/tasks/sign-binaries/tests/pre-apply-task-hook.sh index 8d6e23654..72daa07e0 100755 --- a/tasks/sign-binaries/tests/pre-apply-task-hook.sh +++ b/tasks/sign-binaries/tests/pre-apply-task-hook.sh @@ -4,8 +4,9 @@ TASK_PATH="$1" SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) yq -i '.spec.steps[0].script = load_str("'$SCRIPT_DIR'/mocks.sh") + .spec.steps[0].script' "$TASK_PATH" -yq -i '.spec.steps[1].script = load_str("'$SCRIPT_DIR'/mocks.sh") + .spec.steps[1].script' "$TASK_PATH" -yq -i '.spec.steps[2].script = load_str("'$SCRIPT_DIR'/checksum_mocks.sh") + .spec.steps[2].script' "$TASK_PATH" +yq -i '.spec.steps[1].script = load_str("'$SCRIPT_DIR'/mac_mocks.sh") + .spec.steps[1].script' "$TASK_PATH" +yq -i '.spec.steps[2].script = load_str("'$SCRIPT_DIR'/mocks.sh") + .spec.steps[2].script' "$TASK_PATH" +yq -i '.spec.steps[3].script = load_str("'$SCRIPT_DIR'/checksum_mocks.sh") + .spec.steps[3].script' "$TASK_PATH" # Delete existing secrets if they exist kubectl delete secret windows-credentials --ignore-not-found @@ -19,16 +20,16 @@ kubectl delete secret checksum-keytab --ignore-not-found # Create the windows-credentials secret kubectl create secret generic windows-credentials \ - --from-literal=username=myusername \ - --from-literal=password=mypass \ + --from-literal=username=windowsusername \ + --from-literal=password=windowspass \ --from-literal=port=22 \ - --from-literal=host=myserver.com \ + --from-literal=host=windowsserver.com \ --namespace=default # Create the windows-ssh-key secret kubectl create secret generic windows-ssh-key \ - --from-literal=id_rsa="some private key" \ - --from-literal=fingerprint="some fingerprint" \ + --from-literal=windows_id_rsa="windows private key" \ + --from-literal=windows_fingerprint="windows fingerprint" \ --namespace=default # Create the quay-secret secret @@ -39,22 +40,22 @@ kubectl create secret generic quay-secret \ # Create the mac-host-credentials secret kubectl create secret generic mac-host-credentials \ - --from-literal=mac-host="some_host" \ - --from-literal=mac-user="some_user" \ - --from-literal=mac-password="some_password" \ + --from-literal=host="mac_host" \ + --from-literal=username="mac_user" \ + --from-literal=mac-password="mac_password" \ # Create the mac-signing-credentials secret kubectl create secret generic mac-signing-credentials \ - --from-literal=keychain_password="some_password" \ - --from-literal=signing_identity="some_identity" \ - --from-literal=apple_id="some_id" \ - --from-literal=app_specific_password="some_password" \ - --from-literal=team_id="some_id" \ + --from-literal=keychain_password="keychain_password" \ + --from-literal=signing_identity="signing_identity" \ + --from-literal=apple_id="apple_id" \ + --from-literal=app_specific_password="app_specific_password" \ + --from-literal=team_id="team_id" \ # Create the mac-ssh-key secret kubectl create secret generic mac-ssh-key \ - --from-literal=id_rsa="some private key" \ - --from-literal=fingerprint="some fingerprint" \ + --from-literal=mac_id_rsa="some private key" \ + --from-literal=mac_fingerprint="some fingerprint" \ --namespace=default # Create the checksum fingerprint diff --git a/tasks/sign-binaries/tests/test-sign-binaries-checksum.yaml b/tasks/sign-binaries/tests/test-sign-binaries-checksum.yaml index 689a12b51..96e5117b7 100644 --- a/tasks/sign-binaries/tests/test-sign-binaries-checksum.yaml +++ b/tasks/sign-binaries/tests/test-sign-binaries-checksum.yaml @@ -30,10 +30,23 @@ spec: } } EOF - mkdir -p "$(workspaces.data.path)/content/linux" - cat > "$(workspaces.data.path)/content/linux/linux_binary.bin" << EOF + mkdir -p "$(workspaces.data.path)/content" + cd "$(workspaces.data.path)/content" + + cat > "linux_binary.bin" << EOF + some data + EOF + gzip --keep linux_binary.bin + + cat > "darwin_binary.bin" << EOF + some data + EOF + gzip --keep darwin_binary.bin + + cat > "windows_binary.bin" << EOF some data EOF + gzip --keep windows_binary.bin # Create release.json status.attribution.author cat > "$(workspaces.data.path)/release.json" << EOF @@ -120,8 +133,8 @@ spec: # assert sha256sum.txt is generated properly expected_sha256sum="5aa03f96c77536579166fba147929626cc3a97960e994057a9d80271a736d10f linux_binary.bin - 1e26ce5588db2ef5080a3df10385a731af2a4bfd0d2515f691d05d9dd900e18a mac_binary.bin - a809cbf745ba013315fd174e0319d7159b856d1a09733cfd457636bf3898d581 windows_binary.bin" + 1307990e6ba5ca145eb35e99182a9bec46531bc54ddf656a602c780fa0240dee mac_binary.bin + 1307990e6ba5ca145eb35e99182a9bec46531bc54ddf656a602c780fa0240dee windows_binary.bin" actual_sha256sum=$(cat "$(workspaces.data.path)/content/signed/sha256sum.txt") if [[ "$actual_sha256sum" == "$expected_sha256sum" ]]; then echo "Test passed: sha256sum.txt is generated properly." diff --git a/tasks/sign-binaries/tests/test-sign-binaries.yaml b/tasks/sign-binaries/tests/test-sign-binaries.yaml index ae1c20a0b..34e4ac540 100644 --- a/tasks/sign-binaries/tests/test-sign-binaries.yaml +++ b/tasks/sign-binaries/tests/test-sign-binaries.yaml @@ -22,7 +22,7 @@ spec: script: | #!/usr/bin/env sh set -eux - cat > "$(workspaces.data.path)/data.json" << EOF + cat > "$(workspaces.data.path)/data.json" << EOF { "somekey": { "key": "value", @@ -30,10 +30,28 @@ spec: } } EOF - mkdir -p "$(workspaces.data.path)/content/linux" - cat > "$(workspaces.data.path)/content/linux/linux_binary.bin" << EOF + + # shellcheck disable=SC3037 + echo -n "sha256:0c4355ee4ef8d9d3875d5421972aed405ce6d8f5262983eeea3f6cbf5740c6e2" > \ + /tekton/results/signedWindowsDigest + + mkdir -p "$(workspaces.data.path)/content" + cd "$(workspaces.data.path)/content" + + cat > "linux_binary.bin" << EOF + some data + EOF + gzip --keep linux_binary.bin + + cat > "darwin_binary.bin" << EOF some data EOF + gzip --keep darwin_binary.bin + + cat > "windows_binary.bin" << EOF + some data + EOF + gzip --keep windows_binary.bin # Create release.json status.attribution.author cat > "$(workspaces.data.path)/release.json" << EOF @@ -63,8 +81,10 @@ spec: params: - name: windowsCredentials value: windows-credentials - - name: ssh_key_secret + - name: windows_ssh_key_secret value: windows-ssh-key + - name: mac_ssh_key_secret + value: mac-ssh-key - name: macHostCredentials value: mac-host-credentials - name: macSigningCredentials @@ -107,17 +127,62 @@ spec: script: | #!/usr/bin/env bash - expected_calls="2" - actual_content=$(cat "$(workspaces.data.path)/ssh_calls.txt") - if [[ "$actual_content" == "$expected_calls" ]]; then - echo "Test passed: SSH called two times as expected." + # Check Mac ssh calls + mac_expected_ssh_calls="2" + mac_ssh_calls=$(cat "$(workspaces.data.path)/mac_ssh_count.txt") + if [[ "$mac_ssh_calls" == "$mac_expected_ssh_calls" ]]; then + echo "Test passed: Mac SSH called as expected. Number of calls: $mac_ssh_calls" + else + echo "Test failed: Mac SSH not called expected number of times." + echo "Expected: '$mac_expected_ssh_calls'" + echo "Actual: '$mac_ssh_calls'" + exit 1 + fi + + # Check Windows ssh calls + windows_expected_ssh_calls="2" + windows_ssh_calls=$(cat "$(workspaces.data.path)/win_ssh_count.txt") + if [[ "$windows_ssh_calls" == "$windows_expected_ssh_calls" ]]; then + echo "Test passed: Windows SSH called as expected. Number of calls: $windows_ssh_calls" + else + echo "Test failed: Windows SSH not called expected number of times." + echo "Expected: '$windows_expected_ssh_calls'" + echo "Actual: '$windows_ssh_calls'" + exit 1 + fi + + # Check Mac scp calls + scp_mac_args=$(cat "$(workspaces.data.path)/scp_mac_args.txt") + expected_scp_mac_args="-i /root/.ssh/id_rsa -o UserKnownHostsFile=/root/.ssh/known_hosts " + expected_scp_mac_args+="/tmp/mac_signing_script.sh " + expected_scp_mac_args+="mac_user@mac_host:/tmp/mac_signing_script.sh" + + if [[ "$scp_mac_args" == "$expected_scp_mac_args" ]]; then + echo "Test passed: Mac scp command called with correct arguments." + else + echo "Test failed: Mac scp command called with incorrect arguments." + echo "Expected: '$expected_scp_mac_args'" + echo "Actual: '$scp_mac_args'" + exit 1 + fi + + # Check Windows scp calls + scp_windows_args=$(cat "$(workspaces.data.path)/scp_windows_args.txt") + expected_scp_windows_args="-i /root/.ssh/id_rsa -o UserKnownHostsFile=/root/.ssh/known_hosts " + expected_scp_windows_args+="-P 22 /tmp/windows_signing_script_file.bat " + expected_scp_windows_args+="windowsusername@windowsserver.com:" + expected_scp_windows_args+="C:/Users/Administrator/AppData/Local/Temp/windows_signing_script_file.bat" + + if [[ "$scp_windows_args" == "$expected_scp_windows_args" ]]; then + echo "Test passed: Windows scp command called with correct arguments." else - echo "Test failed: SSH not called expected number of times." - echo "Expected: '$expected_calls'" - echo "Actual: '$actual_content'" + echo "Test failed: Windows scp command called with incorrect arguments." + echo "Expected: '$expected_scp_windows_args'" + echo "Actual: '$scp_windows_args'" exit 1 fi + # Check signed windows digest expected_signed_digest="sha256:0c4355ee4ef8d9d3875d5421972aed405ce6d8f5262983eeea3f6cbf5740c6e2" if [ "$(params.signedWindowsDigest)" != "$expected_signed_digest" ]; then echo Error: signedWindowsDigest was expected to be $expected_signed_digest. @@ -129,8 +194,9 @@ spec: mock_scp_1=$(cat "$(workspaces.data.path)/mock_scp_1.txt") expected_scp="-i /root/.ssh/id_rsa -o UserKnownHostsFile=/root/.ssh/known_hosts -P 22 \ - /tmp/signing_script.bat \ - myusername@myserver.com:C:/Users/Administrator/AppData/Local/Temp/signing_script.bat" + /tmp/windows_signing_script_file.bat \ + windowsusername@windowsserver.com:\ + C:/Users/Administrator/AppData/Local/Temp/windows_signing_script_file.bat" if [[ "$mock_scp_1" == "$expected_scp" ]]; then echo "Test passed: First SCP command is correct." @@ -144,7 +210,7 @@ spec: # check second scp command mock_scp_2=$(cat "$(workspaces.data.path)/mock_scp_2.txt") expected_scp_2="-i /root/.ssh/id_rsa -o UserKnownHostsFile=/root/.ssh/known_hosts -P 22 \ - myusername@myserver.com:C:\Users\Administrator\AppData\Local\Temp\12345678\digest.txt \ + windowsusername@windowsserver.com:C:/Users/Administrator/AppData/Local/Temp/12345678/digest.txt \ /tekton/results/signedWindowsDigest" if [[ "$mock_scp_2" == "$expected_scp_2" ]]; then