5.2 Attest + Conforma

Overview

You copy the too-permissive Conforma seed and tighten your copy. Unsigned builder images must fail. The signed app digest from 5.1 must pass. You run that gate as Task conforma-policy in namespace lightwell-tasks. The Task is not in the seeded pipeline.

This is not SLSA L3. There is no Tekton Chains on this cluster and no cosign attest step. Leave skip-att-sig-check=true on the ConfigMap. Showroom ec is 0.7 (ec-rhel9:0.7). It has no --skip-att-sig-check, and --ignore-rekor breaks Fulcio timestamps on the pass path. The listing and Task ignore only builtin.attestation.signature_check in the report. Konflux Conforma maps to this Task. Chains and in-toto are a callout. Full table is Konflux mapping.

The seeded ConfigMap skips signature checks, uses identity .*, and sets CVE 999. Argo reverts edits to that object. Copy it to lw-poc-build and tighten the copy.

Why it matters

Policy must be able to fail. A pipeline that always passes is not a gate. You own the policy copy. Do not fetch a bundle from quay.io or github.com.

You copy and tighten the ConfigMap because Argo owns the seed. A wildcard identity would accept anything. Task order is after cosign-sign-keyless.

Lightwell Network and Clearinghouse Premier do not replace Conforma. Library VEX is Track 7. Conforma here gates image signature and identity. A Premier GAV inside an unsigned image still fails.

What does it solve

  • Policy that never fails

  • Unsigned work that still ships

  • Wildcard identity that accepts any signer

  • Editing the Argo-owned seed instead of a copy you own

Why Conforma

Too-permissive Conforma seed versus learner-tightened copy in lw-poc-build

The seed is too permissive. Your copy in lw-poc-build is the gate. Do not fetch a bundle from quay.io or github.com.

Discover the seed (do not paste the example)

oc -n lightwell-tasks get task conforma-policy
oc -n lightwell-tasks get configmap conforma-policy \
  -o jsonpath='{.data.skip-image-sig-check}{"\n"}{.data.certificate-identity-regexp}{"\n"}'
oc -n lightwell-tasks get configmap conforma-policy-docs \
  -o jsonpath='{.data.example-pipeline-snippet\.yaml}'

Worked example (not the Check)

Named verify-enterprise-contract, runAfter: [clone] (before sign), fake example.invalid identity, skip left on, image docker.io/library/java:latest. Paste-identical copy fails.

# Worked example ONLY — wrong Task name, runs before sign, public image.
- name: verify-enterprise-contract
  runAfter: [clone]
  params:
    - name: POLICY_CONFIGURATION
      value: example.invalid/not-tightened
    - name: IMAGES
      value: docker.io/library/java:latest

Your change: copy, tighten, wire

  1. Copy ConfigMap conforma-policy from lightwell-tasks into lw-poc-build. Do not edit the Argo-owned original.

BUILD_NS="$(oc -n gitea get configmap demo-userinfo-gitea -o jsonpath='{.data.student_build_namespace}')"
oc -n lightwell-tasks get configmap conforma-policy -o yaml \
  | sed -e '/^  resourceVersion:/d' -e '/^  uid:/d' -e '/^  creationTimestamp:/d' \
        -e "s/namespace: lightwell-tasks/namespace: ${BUILD_NS}/" \
  | oc apply -f -
  1. On the copy in lw-poc-build, set skip-image-sig-check to false. Leave skip-att-sig-check true (no Chains / no cosign attest on this claim). Set certificate-identity-regexp to the same pipeline SA regexp you used in 5.1 (not a wildcard identity, not a Hummingbird placeholder). Set the OIDC issuer regexp to https://kubernetes.default.svc. In policy.yaml / data.yaml, drop the catch-all exclude (the list that is a single asterisk). Do not add an exclude for attestation_signature_checkec 0.7 builtins ignore that list. Lower restrict_max_cve_score below 999, and pin allowed_registry_prefixes to the internal registry (image-registry.openshift-image-registry.svc).

  2. In Gitea .tekton/pipeline.yaml, add Task conforma-policy (cluster resolver, namespace: lightwell-tasks) with runAfter: [cosign-sign-keyless]. Point policy-namespace / policy-configmap at your copy in lw-poc-build, not lightwell-tasks. Pass tuf-url: $(params.cosign-tuf-url) and rekor-url: $(params.cosign-rekor-url) (in-cluster *.trusted-artifact-signer.svc from 4.3 / 5.1 — not HTTPS Route hints). Commit, push, apply the Pipeline.

Check: fail on the builder; pass on the signed app

Fail path is the in-cluster ImageStream openjdk-21-builder you imported in 4.1 — it is not signed with the RHTAS pipeline SA. It can pull (internal registry is allowed). Pass path is the 5.1 app digest. Do not use docker.io/library/java:latest (worked example; hermetic egress also blocks it from the build namespace).

ec is on Showroom PATH (do not curl github.com). Initialize TUF and log in to the internal registry the same way as 5.1. Do not pass --ignore-rekor (Fulcio certs need Rekor timestamps) and do not pass --skip-att-sig-check (unknown on ec 0.7). --strict=false writes a report even when the attestation builtin fails; judge the files, not the exit code.

BUILD_NS="$(oc -n gitea get configmap demo-userinfo-gitea -o jsonpath='{.data.student_build_namespace}')"
TUF="$(oc -n trusted-artifact-signer get configmap demo-userinfo-rhtas -o jsonpath='{.data.tuf_url_hint}')"
REKOR="$(oc -n trusted-artifact-signer get configmap demo-userinfo-rhtas -o jsonpath='{.data.rekor_url_hint}')"
mkdir -p /tmp/ec /tmp/lab-home
export HOME=/tmp/lab-home
export SSL_CERT_FILE=/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt
cosign initialize --mirror="${TUF}" --root="${TUF}/root.json"
cosign login -u unused -p "$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
  image-registry.openshift-image-registry.svc:5000
oc -n "${BUILD_NS}" extract configmap/conforma-policy --to=/tmp/ec --confirm
FAIL_IMG="image-registry.openshift-image-registry.svc:5000/${BUILD_NS}/openjdk-21-builder:latest"
PASS_IMG="image-registry.openshift-image-registry.svc:5000/${BUILD_NS}/spring-boot-lw-poc:latest"
IDENTITY="$(tr -d '[:space:]' < /tmp/ec/certificate-identity-regexp)"
ISSUER="$(tr -d '[:space:]' < /tmp/ec/certificate-oidc-issuer-regexp)"
# Fail path: unsigned builder must report builtin.image.signature_check
ec validate image --image="${FAIL_IMG}" --policy=/tmp/ec/policy.yaml --strict=false \
  --show-successes --info --output=text=/tmp/ec-fail.txt \
  --certificate-identity-regexp="${IDENTITY}" \
  --certificate-oidc-issuer-regexp="${ISSUER}" \
  --rekor-url="${REKOR}" || true
grep '\[Violation\]' /tmp/ec-fail.txt | grep -q 'builtin.image.signature_check' \
  || { echo 'FAIL PATH UNEXPECTEDLY PASSED'; cat /tmp/ec-fail.txt; exit 1; }
# Pass path: ignore only the attestation builtin (no cosign attest on this claim)
ec validate image --image="${PASS_IMG}" --policy=/tmp/ec/policy.yaml --strict=false \
  --show-successes --info --output=text=/tmp/ec-pass.txt \
  --certificate-identity-regexp="${IDENTITY}" \
  --certificate-oidc-issuer-regexp="${ISSUER}" \
  --rekor-url="${REKOR}" || true
grep '\[Violation\]' /tmp/ec-pass.txt | grep -q 'builtin.image.signature_check' \
  && { echo 'PASS PATH: image signature still failing'; cat /tmp/ec-pass.txt; exit 1; }
leftover="$(grep '\[Violation\]' /tmp/ec-pass.txt | grep -v 'builtin.attestation.signature_check' || true)"
[[ -z "${leftover}" ]] || { echo "PASS PATH leftover: ${leftover}"; cat /tmp/ec-pass.txt; exit 1; }
oc -n "${BUILD_NS}" get pipeline spring-boot-lw-poc-build-sign -o yaml \
  | grep -A30 'name: conforma-policy' | head -40

Pass when the builder report has builtin.image.signature_check as a [Violation], the signed app leftover [Violation] lines are empty after dropping builtin.attestation.signature_check, the live Pipeline includes conforma-policy after cosign-sign-keyless with in-cluster tuf-url / rekor-url, and policy-namespace is lw-poc-build.

Conforma fail on openjdk-21-builder then pass on signed app digest
Fill the report

The Job also grades ConfigMap report-13-attest-conforma key policy_owner. Set it to learner-copy. Empty or REPLACE_ME fails even if cluster state is correct. Do not copy validate-docs.

oc -n lw-poc-validate edit configmap report-13-attest-conforma

The scored Check is Job validate-13-attest-conforma in lw-poc-validate. Direct nav URLs still work (honor system). Unlimited retries. There is no Solve.

Run the Validate Job
oc -n lw-poc-validate delete job validate-13-attest-conforma --ignore-not-found
TPL='job-13.yaml'
oc -n lw-poc-validate get configmap validate-job-templates \
  -o go-template="{{ index .data \"${TPL}\" }}" | oc create -f -
oc -n lw-poc-validate logs -f job/validate-13-attest-conforma

Expect CHECK PASSED: before you continue. CHECK FAILED: names the object to fix. Delete the Job and create it again as often as you need.

Continue to 5.3 when you are ready. Track 6 still opens.

Official documentation

Callout

SLSA L3 / Tekton Chains is a mapping sentence, not this Check. Full table is Konflux mapping.

Key takeaways

  • You own the Conforma policy copy. A passing default is not a gate.

  • Lightwell Network and Clearinghouse Premier do not skip this gate.

  • This lab is not SLSA L3. Leave skip-att-sig-check=true and ignore builtin.attestation.signature_check in the ec report. Do not use --ignore-rekor.

cd ~/ocp5-rhacs-showroom
bash setup/lab-cleanup.sh --module tssc-13