diff --git a/Makefile b/Makefile index 584c5682..26a89e0e 100644 --- a/Makefile +++ b/Makefile @@ -4,11 +4,11 @@ include Makefile-common -##@ Firmware Reference Values +##@ Reference Value Collection .PHONY: collect-firmware-refvals -collect-firmware-refvals: ## Collect firmware reference values from bare metal cluster +collect-firmware-refvals: ## Collect firmware reference values (bare metal, default) @scripts/collect-firmware-refvals.sh -.PHONY: collect-firmware-refvals-merge -collect-firmware-refvals-merge: ## Collect and merge with existing firmware refvals - @scripts/collect-firmware-refvals.sh --merge +.PHONY: collect-azure-refvals +collect-azure-refvals: ## Collect PCR reference values (Azure) + @scripts/collect-firmware-refvals.sh --platform azure diff --git a/charts/all/coco-kyverno-policies/templates/inject-coco-initdata.yaml b/charts/all/coco-kyverno-policies/templates/inject-coco-initdata.yaml index 84c06855..d7b220a7 100644 --- a/charts/all/coco-kyverno-policies/templates/inject-coco-initdata.yaml +++ b/charts/all/coco-kyverno-policies/templates/inject-coco-initdata.yaml @@ -24,6 +24,12 @@ spec: - Pod operations: - CREATE + exclude: + any: + - resources: + selector: + matchLabels: + coco.io/skip-initdata: "true" preconditions: all: - key: "{{ "{{" }}request.object.spec.runtimeClassName || '' {{ "}}" }}" diff --git a/rhdp/README.md b/rhdp/README.md index e5240b00..0e0e980e 100644 --- a/rhdp/README.md +++ b/rhdp/README.md @@ -3,6 +3,27 @@ Red Hat demo platform is a system for employees and red hat partners to generate test infrastructure. The scripts in this directory help users of that platform automate deployments. +## Prerequisites + +- `podman` installed and running (used for reference value collection) +- `yq`, `jq` installed +- OpenShift pull secret at `~/pull-secret.json` +- SSH key at `~/.ssh/id_rsa` (RSA) +- RHDP environment variables loaded (see below) + +## Environment variables + +Provided by your RHDP Azure Open Environment: + +```shell +export GUID= +export CLIENT_ID= +export PASSWORD= +export TENANT= +export SUBSCRIPTION= +export RESOURCEGROUP= +``` + ## To deploy 1. Stand up the 'Azure Subscription Based Blank Open Environment' @@ -12,13 +33,23 @@ The scripts in this directory help users of that platform automate deployments. ### Single Cluster Deployment - 1. `bash ./rhdp/wrapper.sh eastasia` - 2. The wrapper script **requires** an azure region code this code SHOULD be the same as what was selected in RHDP. + 1. Set `main.clusterGroupName: simple` in `values-global.yaml` + 2. `bash ./rhdp/wrapper.sh eastasia` + 3. The wrapper script **requires** an azure region code. This code SHOULD be the same as what was selected in RHDP. + 4. Optionally use `--prefix` for custom cluster naming: `bash ./rhdp/wrapper.sh --prefix dev1 eastasia` + +The wrapper handles: cluster provisioning, secret generation, PCR reference value collection (via veritas), and pattern installation. ### Multi-Cluster Deployment (Hub and Spoke) - 1. `bash ./rhdp/wrapper-multicluster.sh eastasia` - 2. This creates two clusters: `coco-hub` and `coco-spoke` in the same region - 3. The pattern is deployed only on the hub cluster - 4. Hub cluster kubeconfig: `./openshift-install-hub/auth/kubeconfig` - 5. Spoke cluster kubeconfig: `./openshift-install-spoke/auth/kubeconfig` + 1. Set `main.clusterGroupName: trusted-hub` in `values-global.yaml` + 2. `bash ./rhdp/wrapper-multicluster.sh eastasia` + 3. This creates two clusters: `coco-hub` and `coco-spoke` in the same region + 4. The pattern is deployed on the hub cluster; the spoke is imported into ACM + 5. Hub cluster kubeconfig: `./openshift-install-hub/auth/kubeconfig` + 6. Spoke cluster kubeconfig: `./openshift-install-spoke/auth/kubeconfig` + +### Cluster Only (no pattern install) + + 1. `bash ./rhdp/wrapper-cluster-only.sh eastasia` + 2. Provisions the cluster without installing secrets or the pattern diff --git a/rhdp/wrapper-multicluster.sh b/rhdp/wrapper-multicluster.sh index 26bb726d..08843f02 100755 --- a/rhdp/wrapper-multicluster.sh +++ b/rhdp/wrapper-multicluster.sh @@ -144,6 +144,11 @@ echo "---------------------" echo "setting up secrets" bash ./scripts/gen-secrets.sh +echo "---------------------" +echo "retrieving PCR measurements" +echo "---------------------" +bash ./scripts/collect-firmware-refvals.sh --platform azure + echo "---------------------" echo "starting pattern install on hub cluster" echo "---------------------" diff --git a/rhdp/wrapper.sh b/rhdp/wrapper.sh index 18763936..6c6cc539 100755 --- a/rhdp/wrapper.sh +++ b/rhdp/wrapper.sh @@ -189,7 +189,7 @@ bash ./scripts/gen-secrets.sh echo "---------------------" echo "retrieving PCR measurements" echo "---------------------" -bash ./scripts/get-pcr.sh +bash ./scripts/collect-firmware-refvals.sh --platform azure sleep 60 echo "---------------------" diff --git a/scripts/collect-firmware-refvals.sh b/scripts/collect-firmware-refvals.sh index ac3ecdc5..708cf40d 100755 --- a/scripts/collect-firmware-refvals.sh +++ b/scripts/collect-firmware-refvals.sh @@ -1,60 +1,64 @@ #!/usr/bin/env bash -# Collect firmware reference values from a bare metal confidential VM +# Collect firmware reference values using veritas container (runs locally, no cluster pods) # -# This script automates the full lifecycle: -# 1. Launch a kata pod with the specified RuntimeClass -# 2. Install veritas inside the pod -# 3. Collect firmware measurements (TDX/SNP) -# 4. Copy output locally and transform to RVPS format -# 5. Save to ~/.coco-pattern/firmware-reference-values.json -# 6. Clean up the pod +# This script: +# 1. Runs veritas via podman container to compute firmware measurements +# 2. Extracts reference values from OCP release artifacts (baremetal) or +# dm-verity image (azure) +# 3. Saves to ~/.coco-pattern/ for loading into Vault via 'make load-secrets' # # Usage: # ./scripts/collect-firmware-refvals.sh [OPTIONS] # # Options: -# -m, --merge Merge with existing file instead of overwriting -# -n, --namespace Namespace for collection pod (default: default) -# -o, --output Override output path (default: ~/.coco-pattern/firmware-reference-values.json) -# -r, --runtime-class Override RuntimeClass (default: kata-cc) -# -i, --pod-image Override pod base image (default: registry.access.redhat.com/ubi9/ubi:latest) +# --platform Platform: baremetal (default) or azure +# -o, --output Override output path +# -p, --pull-secret Pull secret file (default: ~/pull-secret.json) +# -v, --ocp-version OCP version (baremetal; default: auto-detect) +# --osc-version OSC operator version (azure; default: auto-detect) +# -t, --tee TEE type (default: tdx) # -h, --help Show this help message set -euo pipefail # Defaults -NAMESPACE="default" -OUTPUT_FILE="${HOME}/.coco-pattern/firmware-reference-values.json" -RUNTIME_CLASS="kata-cc" -POD_IMAGE="registry.access.redhat.com/ubi9/ubi:latest" -MERGE_MODE=false -POD_NAME="firmware-collector-$(date +%s)" +PLATFORM="baremetal" +OUTPUT_FILE="" +PULL_SECRET="${HOME}/pull-secret.json" +OCP_VERSION="" +OSC_VERSION="" +TEE="tdx" +CONTAINER_IMAGE="quay.io/openshift_sandboxed_containers/coco-tools:1.12" # Parse arguments while [[ $# -gt 0 ]]; do case $1 in - -m|--merge) - MERGE_MODE=true - shift - ;; - -n|--namespace) - NAMESPACE="$2" + --platform) + PLATFORM="$2" shift 2 ;; -o|--output) OUTPUT_FILE="$2" shift 2 ;; - -r|--runtime-class) - RUNTIME_CLASS="$2" + -p|--pull-secret) + PULL_SECRET="$2" + shift 2 + ;; + -v|--ocp-version) + OCP_VERSION="$2" + shift 2 + ;; + --osc-version) + OSC_VERSION="$2" shift 2 ;; - -i|--pod-image) - POD_IMAGE="$2" + -t|--tee) + TEE="$2" shift 2 ;; -h|--help) - sed -n '2,17p' "$0" | sed 's/^# //' + sed -n '2,18p' "$0" | sed 's/^# //' exit 0 ;; *) @@ -65,179 +69,130 @@ while [[ $# -gt 0 ]]; do esac done +# Validate platform +if [[ "$PLATFORM" != "baremetal" && "$PLATFORM" != "azure" ]]; then + echo "Error: --platform must be 'baremetal' or 'azure'" >&2 + exit 1 +fi + +# Set default output file based on platform +if [ -z "$OUTPUT_FILE" ]; then + if [ "$PLATFORM" = "azure" ]; then + OUTPUT_FILE="${HOME}/.coco-pattern/measurements.json" + else + OUTPUT_FILE="${HOME}/.coco-pattern/firmware-reference-values.json" + fi +fi + # Prerequisites check -command -v oc >/dev/null 2>&1 || { echo "Error: oc CLI is required but not installed." >&2; exit 1; } +command -v podman >/dev/null 2>&1 || { echo "Error: podman is required but not installed." >&2; exit 1; } +command -v yq >/dev/null 2>&1 || { echo "Error: yq is required but not installed." >&2; exit 1; } command -v jq >/dev/null 2>&1 || { echo "Error: jq is required but not installed." >&2; exit 1; } -# Check oc login -if ! oc whoami >/dev/null 2>&1; then - echo "Error: Not logged in to OpenShift. Run 'oc login' first." >&2 +# Check pull secret exists +if [ ! -f "$PULL_SECRET" ]; then + echo "Error: Pull secret not found at $PULL_SECRET" >&2 + echo "Provide path via --pull-secret or create ~/pull-secret.json" >&2 exit 1 fi +# Build version args and resolve version for display +VERSION_ARGS="" +VERSION_DISPLAY="" +if [ "$PLATFORM" = "azure" ]; then + if [ -z "$OSC_VERSION" ]; then + # Auto-detect from the cluster's sandbox subscription CSV + if command -v oc >/dev/null 2>&1 && oc whoami >/dev/null 2>&1; then + echo "Detecting OSC version from cluster..." + CSV=$(oc get subscription sandboxed-containers-operator \ + -n openshift-sandboxed-containers-operator \ + -o jsonpath='{.status.installedCSV}' 2>/dev/null || echo "") + if [ -n "$CSV" ]; then + OSC_VERSION="${CSV##*.v}" + echo "Detected OSC version: $OSC_VERSION" + fi + fi + if [ -z "$OSC_VERSION" ]; then + echo "Could not auto-detect OSC version, using 'latest'" >&2 + OSC_VERSION="latest" + fi + fi + VERSION_ARGS="--osc-version $OSC_VERSION" + VERSION_DISPLAY="OSC $OSC_VERSION" +else + if [ -z "$OCP_VERSION" ]; then + if command -v oc >/dev/null 2>&1 && oc whoami >/dev/null 2>&1; then + echo "Detecting OCP version from cluster..." + OCP_VERSION=$(oc version -o json | yq -r '.openshiftVersion' 2>/dev/null || echo "") + fi + if [ -z "$OCP_VERSION" ]; then + echo "Error: Could not auto-detect OCP version. Specify with --ocp-version" >&2 + exit 1 + fi + echo "Detected OCP version: $OCP_VERSION" + fi + VERSION_ARGS="--ocp-version $OCP_VERSION" + VERSION_DISPLAY="OCP $OCP_VERSION" +fi + echo "==========================================" echo "Firmware Reference Value Collection" echo "==========================================" -echo "Namespace: $NAMESPACE" -echo "RuntimeClass: $RUNTIME_CLASS" -echo "Pod image: $POD_IMAGE" +echo "Platform: $PLATFORM" +echo "Version: $VERSION_DISPLAY" +echo "TEE Type: $TEE" echo "Output file: $OUTPUT_FILE" -echo "Merge mode: $MERGE_MODE" echo "" -# Cleanup function (called via trap) -cleanup() { - local exit_code=$? - echo "" - if [[ $exit_code -ne 0 ]]; then - echo "⚠ Collection failed or was interrupted" - fi - - if oc get pod "$POD_NAME" -n "$NAMESPACE" &>/dev/null; then - echo "Cleaning up pod $POD_NAME..." - oc delete pod "$POD_NAME" -n "$NAMESPACE" --ignore-not-found=true - fi - - exit $exit_code -} +# Create temp directory for output +TEMP_DIR=$(mktemp -d) +trap "rm -rf $TEMP_DIR" EXIT -# Register cleanup on exit, error, or interrupt -trap cleanup EXIT ERR SIGINT SIGTERM +# Build veritas command +VERITAS_CMD="veritas --platform $PLATFORM --tee $TEE $VERSION_ARGS --authfile /pull-secret.json" -# Check for existing pod with same name and clean it up -if oc get pod "$POD_NAME" -n "$NAMESPACE" &>/dev/null; then - echo "Found existing pod $POD_NAME, cleaning up..." - oc delete pod "$POD_NAME" -n "$NAMESPACE" --wait=false - sleep 2 +# Add baremetal-specific flags +if [ "$PLATFORM" = "baremetal" ]; then + VERITAS_CMD="$VERITAS_CMD --hw-xfam-allow x87 --hw-xfam-allow sse --hw-xfam-allow avx" fi -# Create kata pod -echo "Creating kata pod with RuntimeClass $RUNTIME_CLASS..." -cat <&2 - oc describe pod/$POD_NAME -n $NAMESPACE >&2 - exit 1 -fi - -echo "Pod is Ready" - -# Install pip and veritas -echo "Installing pip and veritas inside pod..." -oc exec $POD_NAME -n $NAMESPACE -- bash -c "dnf install -y python3-pip > /dev/null 2>&1" || { - echo "Error: Failed to install pip" >&2 - exit 1 -} - -oc exec $POD_NAME -n $NAMESPACE -- bash -c "pip install --quiet veritas-collectd" || { - echo "Error: Failed to install veritas" >&2 - exit 1 -} +VERITAS_CMD="$VERITAS_CMD -o /output" -# Run veritas collection -echo "Running veritas collection (this may take 30-60 seconds)..." -if ! oc exec $POD_NAME -n $NAMESPACE -- veritas collect --output /tmp/refvals.json; then - echo "Error: Veritas collection failed" >&2 - echo "Check that the pod is running on hardware with TDX or SNP support" >&2 - exit 1 -fi +echo "Running veritas to compute firmware measurements..." +echo "(This may take 2-3 minutes to download and process artifacts)" +echo "" -# Copy output locally -echo "Copying veritas output locally..." -TEMP_RAW="/tmp/refvals-raw-$$.json" -oc cp $NAMESPACE/$POD_NAME:/tmp/refvals.json $TEMP_RAW || { - echo "Error: Failed to copy veritas output from pod" >&2 - exit 1 -} - -# Transform to RVPS format -echo "Transforming to RVPS format..." -TEMP_RVPS="/tmp/refvals-rvps-$$.json" - -jq -n \ - --arg mr_td "$(jq -r '.tdx.mr_td // empty' "$TEMP_RAW" 2>/dev/null || echo "")" \ - --arg rtmr_1 "$(jq -r '.tdx.rtmr[1] // empty' "$TEMP_RAW" 2>/dev/null || echo "")" \ - --arg rtmr_2 "$(jq -r '.tdx.rtmr[2] // empty' "$TEMP_RAW" 2>/dev/null || echo "")" \ - --arg xfam "$(jq -r '.tdx.xfam // empty' "$TEMP_RAW" 2>/dev/null || echo "")" \ - --arg snp_launch "$(jq -r '.snp.launch_measurement // empty' "$TEMP_RAW" 2>/dev/null || echo "")" \ - '{ - mr_td: (if $mr_td != "" then [$mr_td] else [] end), - rtmr_1: (if $rtmr_1 != "" then [$rtmr_1] else [] end), - rtmr_2: (if $rtmr_2 != "" then [$rtmr_2] else [] end), - xfam: (if $xfam != "" then [$xfam] else [] end), - snp_launch_measurement: (if $snp_launch != "" then [$snp_launch] else [] end) - }' > "$TEMP_RVPS" - -# Check if any values were extracted -VALUE_COUNT=$(jq '[.[] | select(length > 0)] | length' "$TEMP_RVPS") -if [ "$VALUE_COUNT" -eq 0 ]; then - echo "Error: No firmware measurements found in veritas output" >&2 - echo "Veritas may not support this hardware or the output format changed" >&2 - rm -f "$TEMP_RAW" "$TEMP_RVPS" - exit 1 -fi +podman run --rm \ + -v "${PULL_SECRET}:/pull-secret.json:ro,z" \ + -v "${TEMP_DIR}:/output:z" \ + "$CONTAINER_IMAGE" \ + $VERITAS_CMD -echo "Extracted firmware values:" -jq . "$TEMP_RVPS" - -# Merge with existing file if requested -if [ "$MERGE_MODE" = true ] && [ -f "$OUTPUT_FILE" ]; then - echo "Merging with existing file..." - EXISTING_DATA=$(cat "$OUTPUT_FILE") - - MERGED_DATA=$(jq -n \ - --argjson existing "$EXISTING_DATA" \ - --argjson new "$(cat "$TEMP_RVPS")" \ - '$existing * $new | - to_entries | - map({ - key: .key, - value: (.value | if type == "array" then (. + ($new[.key] // [])) | unique else . end) - }) | - from_entries' - ) - - echo "Merged firmware values:" - echo "$MERGED_DATA" | jq . - echo "$MERGED_DATA" > "$TEMP_RVPS" -fi - -# Save to output file +# Extract reference-values.json from ConfigMap and transform array -> object +echo "" +echo "Extracting reference values..." +# -r ensures the embedded JSON string is output raw (not quoted), +# which is required for yq v3 (kislyuk/yq) compatibility. +# yq v4 (mikefarah/yq) outputs raw scalars by default but -r is harmless. +yq -r '.data["reference-values.json"]' "$TEMP_DIR/rvps-reference-values.yaml" | \ + jq '[.[] | {(.name): .value}] | add' > "$OUTPUT_FILE" + +# Save output mkdir -p "$(dirname "$OUTPUT_FILE")" -cp "$TEMP_RVPS" "$OUTPUT_FILE" - -# Cleanup temp files -rm -f "$TEMP_RAW" "$TEMP_RVPS" echo "" -echo "✓ Successfully collected firmware reference values" +echo "Collected firmware reference values:" +jq . "$OUTPUT_FILE" echo "" echo "Saved to: $OUTPUT_FILE" echo "" +if [ "$PLATFORM" = "azure" ]; then + VAULT_KEY="pcrStash" +else + VAULT_KEY="firmwareReferenceValues" +fi echo "Next steps:" echo "1. Review the collected values: cat $OUTPUT_FILE" -echo "2. For bare metal deployments:" -echo " - Uncomment 'firmwareReferenceValues' in ~/values-secret-coco-pattern.yaml" -echo " - Run: make load-secrets" -echo "3. Verify the secret was synced to Vault:" -echo " vault kv get secret/hub/firmwareReferenceValues" -echo "4. Force ExternalSecret sync on the KBS cluster (if needed):" -echo " oc delete externalsecret firmware-refvals-eso -n trustee-operator-system" +echo "2. Ensure '$VAULT_KEY' is configured in ~/values-secret-coco-pattern.yaml" +echo "3. Run: make load-secrets" echo ""