#!/usr/bin/env bash set -euo pipefail # Run this in the customer cluster AFTER copying the complete evidence directory # to a customer-owned PVC. This script has not been executed against a cluster. # Requires kubectl, jq, the installed product's runner image pull entitlement, # and permission to read the source Job/Pod/PVC and create a Job in its namespace. NAMESPACE="${NAMESPACE:-infernosim-system}" SIMULATION_RUN="${SIMULATION_RUN:-first-verification}" EVIDENCE_PVC="${EVIDENCE_PVC:?Set EVIDENCE_PVC to the claim holding the downloaded evidence}" EVIDENCE_SUBPATH="${EVIDENCE_SUBPATH:-run}" VERIFIER_JOB="${VERIFIER_JOB:-evidence-verifier}" case "$EVIDENCE_SUBPATH" in ''|/*|..|../*|*/../*|*/..) printf 'EVIDENCE_SUBPATH must stay inside the PVC\n' >&2; exit 2 ;; esac command -v kubectl >/dev/null command -v jq >/dev/null kubectl get pvc "$EVIDENCE_PVC" -n "$NAMESPACE" >/dev/null SOURCE_RUN_JSON=$(kubectl get simulationrun "$SIMULATION_RUN" -n "$NAMESPACE" -o json) jq -e '.spec.mode == "verify" and (.spec.workload == null)' <<<"$SOURCE_RUN_JSON" >/dev/null SOURCE_JOB=$(jq -er '.status.jobName | select(length > 0)' <<<"$SOURCE_RUN_JSON") kubectl get job "$SOURCE_JOB" -n "$NAMESPACE" >/dev/null SOURCE_POD_JSON=$(kubectl get pods -n "$NAMESPACE" -l "job-name=$SOURCE_JOB" -o json | jq -ce ' [.items[] | select(any(.spec.containers[]; .name == "runner"))] | if length == 0 then error("The source run Pod no longer exists; run a new synthetic verification") else .[0] end') VERIFIER_JSON=$(mktemp) # Preserve namespace, runner service account, injected imagePullSecrets and the # extension usage label. No private registry password or credential is read. # No Workload Identity token or incident passphrase is needed for file hashing. jq -n --argjson source "$SOURCE_POD_JSON" --arg ns "$NAMESPACE" \ --arg job "$VERIFIER_JOB" --arg claim "$EVIDENCE_PVC" \ --arg path "/evidence/$EVIDENCE_SUBPATH" ' ($source.spec.containers[] | select(.name == "runner") | .image) as $image | { apiVersion: "batch/v1", kind: "Job", metadata: {name: $job, namespace: $ns}, spec: { backoffLimit: 0, activeDeadlineSeconds: 120, ttlSecondsAfterFinished: 3600, template: { metadata: {labels: ($source.metadata.labels | with_entries(select(.key == "azure-extensions-usage-release-identifier")))}, spec: { restartPolicy: "Never", serviceAccountName: $source.spec.serviceAccountName, automountServiceAccountToken: false, imagePullSecrets: ($source.spec.imagePullSecrets // []), nodeSelector: {"kubernetes.io/os": "linux", "kubernetes.io/arch": "amd64"}, securityContext: {runAsNonRoot: true, runAsUser: 65532, runAsGroup: 65532, seccompProfile: {type: "RuntimeDefault"}}, containers: [{ name: "verifier", image: $image, imagePullPolicy: "IfNotPresent", command: ["/usr/local/bin/infernosim-azurectl"], args: ["evidence", "verify", $path], securityContext: {allowPrivilegeEscalation: false, readOnlyRootFilesystem: true, capabilities: {drop: ["ALL"]}}, resources: {requests: {cpu: "50m", memory: "64Mi"}, limits: {cpu: "500m", memory: "256Mi"}}, volumeMounts: [{name: "evidence", mountPath: "/evidence", readOnly: true}] }], volumes: [{name: "evidence", persistentVolumeClaim: {claimName: $claim, readOnly: true}}] } } } }' > "$VERIFIER_JSON" kubectl create -f "$VERIFIER_JSON" if ! kubectl wait -n "$NAMESPACE" --for=condition=complete "job/$VERIFIER_JOB" --timeout=150s; then kubectl logs -n "$NAMESPACE" "job/$VERIFIER_JOB" --all-containers=true || true kubectl describe job "$VERIFIER_JOB" -n "$NAMESPACE" exit 1 fi kubectl logs -n "$NAMESPACE" "job/$VERIFIER_JOB" printf 'Verifier Job completed. Rendered manifest: %s\n' "$VERIFIER_JSON"