Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(CLDX-82): add checksum signing step #593

Merged
merged 1 commit into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions pipelines/push-binaries-to-dev-portal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ Tekton pipeline to sign and release Red Hat binaries to the Red Hat Developer Po
| taskGitUrl | The url to the git repo where the release-service-catalog tasks to be used are stored | Yes | https://github.com/konflux-ci/release-service-catalog.git |
| taskGitRevision | The revision in the taskGitUrl repo to be used | No | - |

## Changes in 1.3.0
* Add more params for `sign-binaries` task

## Changes in 1.2.0
* Add new reduce-snapshot task

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ kind: Pipeline
metadata:
name: push-binaries-to-dev-portal
labels:
app.kubernetes.io/version: "1.2.0"
app.kubernetes.io/version: "1.3.0"
annotations:
tekton.dev/pipelines.minVersion: "0.12.1"
tekton.dev/tags: release
Expand Down Expand Up @@ -213,6 +213,16 @@ spec:
value: mac-signing-credentials
- name: pipelineRunUid
value: $(context.pipelineRun.uid)
- name: releasePath
value: "$(tasks.collect-data.results.release)"
- name: contentDir
value: "$(tasks.extract-binaries-from-image.results.binaries_path)"
- name: checksumUser
value: konflux-release-signing-sa
- name: checksumHost
value: etera-worker.hosted.upshift.rdu2.redhat.com
- name: kerberosRealm
value: IPA.REDHAT.COM
runAfter:
- extract-binaries-from-image
- name: prepare-exodus-params
Expand Down
10 changes: 10 additions & 0 deletions tasks/sign-binaries/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,25 @@ Tekton task to sign windows and mac binaries before they are pushed to the Red H

| Name | Description | Optional | Default value |
|------|-------------|----------|---------------|
| relasePath | Path to the release data JSON file | No | |
| quayUrl | Quay URL of the repo where content will be shared | No | |
| quaySecret | Secret to interact with Quay | No | |
| windowsCredentials | Secret to interact with the Windows signing host | No | |
| windowsSSHKey | Secret containing private key and fingerprint for Windows signing host | Yes | windows-ssh-key |
| macHostCredentials | Secret to interact with the Mac signing host | No | |
| macSigningCredentials | Secret to interact with the Mac signing utils | No | |
| macSSHKey | Secret containing SSH private key for the Mac signing host | Yes | mac-ssh-key |
| checksumUser | User to interact with the checksum host | No | |
| checksumHost | Hostname of the checksum host | No | |
| checksumKeytab | Secret containing the keytab for the checksum host | Yes | checksum-keytab |
| checksumFingerprint | Secret containing the fingerprint for the checksum host | Yes | checksum-fingerprint |
| kerberosRealm | Kerberos realm for the checksum host | No | |
| contentDir | Path where the content to push is stored in the workspace | No | |
| pipelineRunUid | Unique ID of the pipelineRun | No | |


## Changes in 2.0.0
* Add checksum signing step

## Changes in 1.0.0
* Added parameters for mac signing steps
158 changes: 147 additions & 11 deletions tasks/sign-binaries/sign-binaries.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ kind: Task
metadata:
name: sign-binaries
labels:
app.kubernetes.io/version: "1.0.0"
app.kubernetes.io/version: "2.0.0"
annotations:
tekton.dev/pipelines.minVersion: "0.12.1"
tekton.dev/tags: release
spec:
description: >-
Tekton task to sign windows and mac binaries before they are pushed to the Red Hat Developer Portal
params:
- name: releasePath
type: string
description: Path to the release data JSON file
- name: quayURL
type: string
description: quay URL of the repo where content will be shared
Expand All @@ -38,10 +41,36 @@ spec:
- name: pipelineRunUid
type: string
description: Unique identifier for the pipeline run
- name: checksumUser
type: string
description: User to interact with the checksum host
- name: checksumHost
type: string
description: Hostname of the checksum host
- name: checksumFingerprint
type: string
description: Secret containing the fingerprint for the checksum host
default: checksum-fingerprint
- name: checksumKeytab
type: string
description: Secret containing the keytab for the checksum host
default: checksum-keytab
- name: kerberosRealm
type: string
description: Kerberos realm for the checksum host
- name: contentDir
type: string
description: Path where the content to push is stored in the workspace
volumes:
- name: windows-ssh-key-vol
secret:
secretName: $(params.windowsSSHKey)
- name: checksum-fingerprint-vol
secret:
secretName: $(params.checksumFingerprint)
- name: checksum-keytab-vol
secret:
secretName: $(params.checksumKeytab)
workspaces:
- name: data
description: Workspace to save the results to
Expand All @@ -54,6 +83,14 @@ spec:
type: string
description: |
Digest used to pull signed content back to pipeline via ORAS
- name: unsignedMacDigest
type: string
description: |
Digest used by signing host to pull unsignged content via ORAS
- name: signedMacDigest
type: string
description: |
Digest used to pull signed content back to pipeline via ORAS
steps:
- name: push-unsigned-using-oras
image: quay.io/konflux-ci/release-service-utils:e633d51cd41d73e4b3310face21bb980af7a662f
Expand Down Expand Up @@ -170,13 +207,112 @@ spec:
# shellcheck disable=SC2029
ssh "$SSH_OPTS" "rmdir /s /q C:\\Users\\Administrator\\AppData\\Local\\Temp\\$(params.pipelineRunUid)"

# - name: pull-signed-using-oras
# image: quay.io/konflux-ci/release-service-utils:e633d51cd41d73e4b3310face21bb980af7a662f
# script: |
# #!/usr/bin/env bash
# # TODO CLDX-79
# - name: generate-checksums
# image: quay.io/konflux-ci/release-service-utils:e633d51cd41d73e4b3310face21bb980af7a662f
# script: |
# #!/usr/bin/env bash
# # TODO CLDX-82
- name: generate-checksums
image: quay.io/konflux-ci/release-service-utils:e633d51cd41d73e4b3310face21bb980af7a662f
volumeMounts:
- name: checksum-fingerprint-vol
mountPath: "/etc/secrets_fingerprint"
readOnly: true
- name: checksum-keytab-vol
mountPath: "/etc/secrets_keytab"
readOnly: true
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

#---------------------------------------------------------------------------------------
# This step generates checksums for all of the binaries in the content directory and
# signs them using the checksum host.
# The general workflow is that the binaries are extracted from the image(previous task),
# signed on remote hosts (windows and mac) and then a sha256sum is generated for each
# binary. The shasums are collected in a sha256sum.txt file which is then transferred to
# the checksum host for signing with Red Hat's GPG key.
# The detached signatures are returned to the workspace for inclusion in the later tasks
# to be pushed to CDN and the Red Hat Developer Portal.
#---------------------------------------------------------------------------------------

AUTHOR=$(jq -r '.status.attribution.author' "$(workspaces.data.path)/$(params.releasePath)")
if [[ ${AUTHOR} == "" ]] ; then exit 1 ; fi

SSH_OPTS="-o UserKnownHostsFile=/root/.ssh/known_hosts \
-o GSSAPIAuthentication=yes \
-o GSSAPIDelegateCredentials=yes"

sign_file() {
sign_method=$1 # The signing method: --clearsign or --gpgsign
checksum_user=$(params.checksumUser)
checksum_host=$(params.checksumHost)
pipeline_run_uid=$(params.pipelineRunUid)
output_path="/home/$checksum_user/$pipeline_run_uid/checksum/sha256sum.txt.$2"
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" \
"rpm-sign --nat $sign_method --key redhatrelease2 --onbehalfof=$AUTHOR \
--output $output_path $input_file"
}

# Generate a kerberos ticket to ssh to the checksum host.
# The ticket is required for interacting with rpm-sign as well,
# so we use GSSAPI Delegate (in ssh opts) to transfer the ticket to the checksum host
KRB5CCNAME=FILE:/tmp/krb5cc_$(id -u)
export KRB5CCNAME
kinit -kt /etc/secrets_keytab/keytab "$(params.checksumUser)@$(params.kerberosRealm)"

mkdir -p /root/.ssh
chmod 700 /root/.ssh
cp "/etc/secrets_fingerprint/fingerprint" /root/.ssh/known_hosts
chmod 600 root/.ssh/known_hosts

# get all of the signed binaries into a common directory
CONTENT_DIR="$(workspaces.data.path)/$(params.contentDir)"
SIGNED_DIR="$CONTENT_DIR/signed"
mkdir -p "$SIGNED_DIR"
mkdir -p "$CONTENT_DIR"/linux
cp -r "$CONTENT_DIR"/linux/* "$SIGNED_DIR"
cd "$SIGNED_DIR" || exit
oras login quay.io -u "$QUAY_USER" -p "$QUAY_PASS"
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}"

# generate checksums for all of the binaries
SHA_SUM_PATH="${CONTENT_DIR}/sha256sum.txt"
touch "$SHA_SUM_PATH"
for file in *; do
if [ -f "$file" ]; then
checksum=$(sha256sum "$file" | awk '{ print $1 }')
echo "$checksum $file" >> "$SHA_SUM_PATH"
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}" \
"$(params.checksumUser)@$(params.checksumHost):~/$(params.pipelineRunUid)/checksum"

sign_file --clearsign sig
sign_file --gpgsign gpg

# scp the two files back to the content directory
scp "$SSH_OPTS" \
"$(params.checksumUser)@$(params.checksumHost):~/$(params.pipelineRunUid)/checksum/sha256sum.txt.sig" \
"${SIGNED_DIR}/sha256sum.txt.sig"

scp "$SSH_OPTS" \
"$(params.checksumUser)@$(params.checksumHost):~/$(params.pipelineRunUid)/checksum/sha256sum.txt.gpg" \
"${SIGNED_DIR}/sha256sum.txt.gpg"

mv "$SHA_SUM_PATH" "${SIGNED_DIR}/sha256sum.txt"
46 changes: 46 additions & 0 deletions tasks/sign-binaries/tests/checksum_mocks.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/bash


count_file="/tmp/ssh_count_checksum.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_checksum.txt
}

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))
echo "$scp_call_count" > "$scp_count_file"
if [[ "$scp_call_count" -eq 1 ]]; then
echo "$@" > $(workspaces.data.path)/mock_scp_1_checksum.txt
fi


if [[ "$scp_call_count" -eq 2 ]]; then
echo "$@" > "$(workspaces.data.path)/mock_scp_2_checksum.txt"
echo -n "sha256:0c4355ee4ef8d9d3875d5421972aed405ce6d8f5262983eeea3f6cbf5740c6e2" | \
tee "$(results.signedWindowsDigest.path)"
fi

}

function kinit() {
echo "kinit $@"
}

function oras() {
echo "oras $@"
}
13 changes: 6 additions & 7 deletions tasks/sign-binaries/tests/mocks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,7 @@ function ssh() {
ssh_call_count=$((ssh_call_count + 1))
echo "$ssh_call_count" > "$count_file"

echo "$ssh_call_count" > $(workspaces.data.path)/ssh_calls.txt
if [[ "$*" == *"digest.txt"* ]]; then
# this is mocking oras via Windows ssh returning the signed digest
echo "mocked ssh $@" > $(workspaces.data.path)/mock_ssh_second_call.txt
echo "sha256:0c4355ee4ef8d9d3875d5421972aed405ce6d8f5262983eeea3f6cbf5740c6e2"
fi
echo "$ssh_call_count" > "$(workspaces.data.path)/ssh_calls.txt"
}

scp_count_file="/tmp/scp_count.txt"
Expand All @@ -30,7 +25,7 @@ function scp() {
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
echo "$@" > "$(workspaces.data.path)/mock_scp_1.txt"
fi


Expand All @@ -46,3 +41,7 @@ function oras() {
# this is mocking the oras command to push unsigned binaries to the registry
echo "Digest: sha256:5ce6d8f5262983eeea3f6cbf5740c6e20c4355ee4ef8d9d3875d5421972aed40"
}

function kinit() {
echo "kinit $*"
}
14 changes: 14 additions & 0 deletions tasks/sign-binaries/tests/pre-apply-task-hook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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"

# Delete existing secrets if they exist
kubectl delete secret windows-credentials --ignore-not-found
Expand All @@ -13,6 +14,9 @@ kubectl delete secret mac-signing-credentials --ignore-not-found
kubectl delete secret mac-host-credentials --ignore-not-found
kubectl delete secret mac-ssh-key --ignore-not-found
kubectl delete secret quay-secret --ignore-not-found
kubectl delete secret checksum-fingerprint --ignore-not-found
kubectl delete secret checksum-keytab --ignore-not-found

# Create the windows-credentials secret
kubectl create secret generic windows-credentials \
--from-literal=username=myusername \
Expand Down Expand Up @@ -52,3 +56,13 @@ kubectl create secret generic mac-ssh-key \
--from-literal=id_rsa="some private key" \
--from-literal=fingerprint="some fingerprint" \
--namespace=default

# Create the checksum fingerprint
kubectl create secret generic checksum-fingerprint \
--from-literal=fingerprint="some fingerprint" \
--namespace=default

# Create the checksum keytab
kubectl create secret generic checksum-keytab \
--from-literal=keytab="some keytab" \
--namespace=default
Loading
Loading