name: inverse layout: true class: center, middle, inverse


class: center, middle, title-slide

Your CI’s Mistaken Identity

Task-Scoped Trust in Cloud-Native Pipelines

Andrew McNamara (Red Hat) • Maia Iyer (IBM Research)

Tekton logo + in-toto logo + SPIFFE / SPIRE + Kyverno

.footnote[ KubeCon + CloudNativeCon North America · November 10, 2026 ]

???

Andrew and Maia introduce themselves. Andrew: “I’m Andrew McNamara, SLSA maintainer, Konflux/Tekton contributor.” Maia: “I’m Maia Iyer from IBM Research, SPIFFE maintainer and Tornjak contributor.” “Today we’re talking about why workload identity in CI/CD currently has a major identity crisis.”


layout: false

The Hook: A Simple Question

Maia · Identity & Auth

“You wouldn’t ask a plumber to sign off on your electrical work.”

–

Yet in nearly every Kubernetes CI/CD pipeline running today:

–

The Problem: Workload identities encode location (where something ran), not authorization (what it was permitted to do).

???

Maia opens the talk. “Think about what happens when a pipeline runs in Kubernetes. Tekton spawns a series of pods for tasks. All of them run in the same namespace, usually under the same service account. If you give that service account an IAM role or a Cosign key, any task that gets compromised can produce any attestation it wants.”


The Identity Perspective: Why Location Fails Us

Maia · Identity & Auth

In the cloud-native identity world, we solved machine-to-machine authentication with Workload Identity (SPIFFE/SPIRE).

We said: “No more hardcoded API keys or static credentials in Kubernetes Secrets.”

–

The Standard SPIFFE Deployment Model:

Workload Attestors inspect the running container:

spiffe://company.org/ns/default-tenant/sa/pipeline-runner

–

The Blind Spot:

???

Maia: “From the identity community’s perspective, SPIFFE was a huge leap forward. We eliminated static credentials. But we brought microservice-era assumptions into CI/CD. In a microservice, ServiceAccount ≈ Application. In CI/CD, ServiceAccount ≈ The entire factory, including untrusted PR checkouts, third-party package downloaders, compiler toolchains, security scanners, and release signers. Location alone no longer tells you who is calling.”


The Supply Chain Threat: Confused Deputies in CI

Maia & Andrew

When identity is coarse, every pipeline step becomes a potential Confused Deputy:

PipelineRun: build-and-test
  ├── Task 1: git-clone         [SA: runner]  ◄── Untrusted code / PRs
  ├── Task 2: fetch-deps        [SA: runner]  ◄── Dynamic package downloads
  ├── Task 3: build-container   [SA: runner]  ──► Needs builder authority
  └── Task 4: trivy-scan        [SA: runner]  ──► Needs scanner authority

–

What Actually Happens During an Attack:

  1. Malicious PR / Typosquatted Dependency: Injects code during git-clone or fetch-deps.
  2. Ambient Authority Exploitation: The malicious code accesses the pod’s ambient token (regcred or projected SA token).
  3. Identity Impersonation: Because all tasks share sa/runner, the compromised step can overwrite registry tags or sign false claims.
  4. Forged Integrity: The attacker pushes backdoored images or signs an attestation claiming: “Vulnerabilities: 0; SBOM: Clean”.

–

Andrew: Let's stop talking in theory. Let's see what actually happens in a real Kubernetes cluster right now.

???

Maia: “This isn’t hypothetical. Most modern supply chain compromises don’t break crypto; they abuse legitimate credentials running in the wrong context.” Andrew: “Let’s pull up our live cluster and demonstrate how an untrusted step silently hijacks ambient authority to overwrite a production container tag.”


layout: false

Live Demo: Act 1 — The Breakdown (Ambient Authority & Secret Hijack)

🖥️ Live Demo: Act 1

ttyd offline
Ambient Authority Flaw & Registry Tag Hijack
• Standard projected ServiceAccount token exposes namespace-wide ambient identity (sub: system:serviceaccount:...).
• An untrusted pod mounts ambient regcred credentials and silently overwrites slsa-e2e-test:latest with a backdoor payload.
• Verification proves the tag was corrupted without triggering cluster authorization alarms.
./demo/run-demo.sh --act 1

???

Andrew runs Act 1 live in the embedded terminal. Walk the audience through:

  1. Inspecting the projected ServiceAccount token: issuer is Kubernetes, subject is coarse-grained to default-tenant:default.
  2. Running rogue-ambient-push: pod mounts the namespace’s regcred and uses oras to push a backdoor payload over slsa-e2e-test:latest.
  3. Verification: pulls the tag back down and prints “[VERIFICATION] Tag contents: MALICIOUS BACKDOOR EXECUTED”. Hit Escape or click Next to return focus to the slide deck.

class: center, middle, inverse

Part 2: Building Task Identity

Tekton Resolvers, Admission Control, and SPIRE


layout: false

Tekton Architecture: Tasks, TaskRuns, and Resolvers

Andrew · Pipelines & Architecture

How do pipelines execute in cloud-native Kubernetes?

–

The Tekton Execution Hierarchy:

–

The Problem: Where Do Tasks Come From?

???

Andrew explains Tekton primitives. “To solve the identity problem, we have to know what code is scheduled before the pod starts running. Tekton Resolvers give us exactly that hook.”


Tekton Resolvers: Pinned OCI Bundles as Trust Anchors

Andrew · Pipelines & Architecture

A Tekton Bundle is a Tekton Task packaged inside an OCI container image layer:

apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
  generateName: demo-signed-task-
  namespace: default-tenant
spec:
  taskRef:
    resolver: bundles
    params:
    - name: bundle
      value: registry-service.kind-registry/tekton-catalog/demo-catalog-task@sha256:ce7492...
    - name: name
      value: demo-catalog-task
    - name: kind
      value: task

–

Why This Changes the Game:

  1. Content-Addressable & Immutable: Pinned by cryptographic digest (@sha256:...).
  2. Registry-Native: Stored alongside application images; supports Sigstore signatures.
  3. Admission-Time Visibility: When a TaskRun is submitted to the Kubernetes API, taskRef.params.bundle is immediately inspectable before any Pod is scheduled!

???

Andrew: “Because the bundle is an OCI artifact pinned by digest, we can treat task definitions the same way we treat container images: we can sign them with Cosign and verify them with Kyverno at admission time.”


Bridging Tekton to SPIRE: Kyverno at the Gate

Andrew · Pipelines & Architecture

Kyverno intercepts the TaskRun at admission time using a CEL-based ImageValidatingPolicy:

apiVersion: policies.kyverno.io/v1beta1
kind: ImageValidatingPolicy
metadata:
  name: verify-bundle-signatures
spec:
  validationActions: [Deny]
  matchConstraints:
    resourceRules:
    - apiGroups: ["tekton.dev"]
      resources: ["taskruns"]
  images:
  - name: taskBundle
    expression: >-
      object.spec.taskRef.params
        .filter(p, p.name == "bundle")
        .map(p, p.value)
  validations:
  - expression: >-
      images.taskBundle.map(img,
        verifyImageSignatures(img, [attestors.catalogKey])
      ).all(valid, valid > 0)

–

The Trust Promotion:

???

Andrew: “Notice what’s happening here. Kyverno inspects the TaskRun before scheduling. If the bundle is signed by our trusted catalog key, Kyverno promotes the TaskRun label to ‘prod’. An anti-spoofing policy ensures pods cannot self-assign this label.”


Step 2: SPIRE Mints the Role SVID

Andrew · Pipelines & Architecture

ClusterSPIFFEID evaluates the Kyverno-verified pod labels:

apiVersion: spire.spiffe.io/v1alpha1
kind: ClusterSPIFFEID
metadata:
  name: konflux-trusted-prod
spec:
  spiffeIDTemplate: >-
    spiffe://konflux-ci.dev/trusted/cluster-01//
  podSelector:
    matchLabels:
      trusted-task-role: "prod"

–

The Resulting Distinct Identities:

–

Key Architectural Leap: All three tasks share sa/runner, but receive completely different cryptographic identities based on admission-verified task code!

???

Andrew: “Even though all three tasks share the exact same ServiceAccount, they no longer share the same cryptographic identity. The identity is bound to the verified catalog code.”


layout: false

Live Demo: Act 2 — Task Admission & Cryptographic Identity

🖥️ Live Demo: Act 2

ttyd offline
Task Admission Classification & SPIRE Identity Minting
• Inline untrusted tasks are classified into the unprivileged dev role.
• Attacker attempting to spoof catalog namespace with unsigned task is denied at admission.
• Cosign-signed catalog bundle verified by Kyverno, promoted to prod, and minted production SVID.
./demo/run-demo.sh --act 2

???

Andrew runs Act 2 live:

  1. Shows classify-taskrun and prevent-pod-label-spoofing policies.
  2. Submits untrusted inline task: Kyverno sets trusted-task-role: dev, SPIRE mints spiffe://konflux-ci.dev/dev/…
  3. Submits spoofed unsigned task: Kyverno’s ImageValidatingPolicy blocks admission at the API boundary!
  4. Submits Cosign-signed catalog task: Kyverno admits with trusted-task-role: prod, SPIRE mints vetted production SVID. Hit Escape or click Next to return to slides.

class: center, middle, inverse

Part 3: What This Unlocks

Location vs. Authorization & Same-Namespace API Gating


layout: false

The Implications: Cryptographic Proof of Code, Not Just Location

Maia · Identity & Auth

What did we just establish?

–

1. We Broke the “Location = Authorization” Trap

–

2. We Preserved SPIFFE’s Core Tenets

–

The Principle: Authorization must follow Role, not Location.

???

Maia reflects on the implications. “This is the critical conceptual pivot. In the identity world, we always say: Authentication is identity; Authorization is policy. But if the identity only says ‘Kubernetes Pod in Namespace X’, downstream services have no signal to authorize. By feeding admission-verified task roles into the SPIFFE ID hierarchy, we give downstream services the exact signal they need.”


Generalizing Within the Namespace: Three Core Patterns

Maia · Identity & Auth

Once tasks possess fine-grained cryptographic identities, how do we use them inside the tenant namespace?

–

Pattern 1: Federated OCI Push Gating
Eliminate ambient registry secrets (regcred). Configure the OCI registry with OIDC Bearer auth matching SPIFFE SVIDs—only vetted builder tasks can initiate push sessions.
Pattern 2: Portable Secretless Service Access
Internal microservices (CVE databases, KMS, artifact stores) validate caller SVIDs directly against SPIRE's /keys JWKS endpoint without mounting static Kubernetes Secrets.
Pattern 3: Separation of Duties Attestations
Tasks sign in-toto claims via Sigstore/Fulcio. Policy engines (Conforma / OPA) verify that the certificate SAN matches the authorized role (scanners sign CVEs, builders sign SBOMs).

???

Maia outlines the three same-namespace patterns. “We now have three immediate use cases inside the same namespace that completely eliminate ambient secrets.”


Patterns 1 & 2: Push Gating and Secretless Services

Maia & Andrew

Pattern 1: Zot OCI Push Gating

Zot validates bearer JWTs against SPIRE’s OIDC discovery endpoint (/keys):

{
  "repositories": {
    "slsa-e2e-test": {
      "policies": [{
        "users": ["spiffe://konflux-ci.dev/trusted/cluster-01/runner/buildah-oci-ta"],
        "actions": ["read", "create"]
      }]
    }
  }
}

–

Pattern 2: Secretless Internal CVE Database

???

Andrew explains Zot’s access control policy: “Notice that we don’t need dockerconfigjson secrets in the namespace anymore. Zot accepts the SPIFFE JWT as an OIDC bearer token and checks if the subject is the approved builder task.”


Pattern 3: Scoped Attestations & Separation of Duties

Andrew · Pipelines & Architecture

Each task produces attestations matching its specific domain:

–

Conforma Policy Enforcement (OPA Rego):

package policy.cve

# Rule: Vulnerability reports MUST be signed by an authorized scanner
deny[msg] {
  attestation := input.attestations[_]
  attestation.predicateType == "https://aquasecurity.github.io/trivy/report/v1"
  
  # Check certificate identity
  not startswith(attestation.certificate.uri, 
                 "spiffe://konflux-ci.dev/trusted/cluster-01/runner/trivy-sbom-scan")
  
  msg := sprintf("CVE scan signed by unauthorized role: %s", [attestation.certificate.uri])
}

–

The Guarantee: Even if a builder task signs an attestation claiming 0 vulnerabilities, Conforma rejects it because the builder is not an authorized scanner!

???

Andrew: “This is the separation of duties punchline. A signature from the cluster isn’t enough. Conforma verifies that the certificate identity matches the role authorized to make that claim.”


layout: false

Live Demo: Act 3 — Same-Namespace API Gating & Separation of Duties

🖥️ Live Demo: Act 3

ttyd offline
OCI Push Gating, Secretless Services & Separation of Duties
• Zot OCI Push Gating: Rogue task rejected with 403 Forbidden; vetted builder accepted with 202 Accepted.
• Secretless CVE DB: Zero secrets in namespace; untrusted caller gets 403; scanner gets 200 OK.
• Separation of Duties: OPA tests prove builder forgery of scanner reports is blocked.
./demo/run-demo.sh --act 3

???

Andrew runs Act 3 live:

  1. Zot push gating: rogue dev task attempts upload handshake -> 403 Forbidden. Vetted builder task attempts upload handshake -> 202 Accepted.
  2. Secretless CVE database: dev task queries internal service -> 403 Forbidden. Scanner task queries internal service -> 200 OK with vulnerability feed.
  3. Separation of duties: runs opa test proving builder forgery is blocked. Hit Escape or click Next to return to slides.

class: center, middle, inverse

Part 4: The Next Frontier

Cross-Task Artifacts and the Managed Release Boundary


layout: false

Cross-Task Data Plane: Why PVCs Undermine Task Trust

Andrew · Pipelines & Architecture

Does securing task identities protect the entire pipeline? Not if tasks share a disk.

–

The Data Plane Loophole: Shared PersistentVolumes

Task A (build) ──────► [ Shared PVC Workspace ] ◄────── Task B (untrusted test)
                             │
                             ▼ (tampered files)
                       Task C (package & sign) ──► Cryptographically valid MALWARE!

–

The Solution: OCI Trusted Artifacts

???

Andrew: “Identity on the control plane is useless if your data plane is compromised. If tasks share a PVC, any task can alter the binaries before they are packaged. Trusted Artifacts replace shared PVCs with immutable OCI storage.”


The Managed Release Boundary: Dual-Gated Release Authority

Maia & Andrew

Build-time tasks in default-tenant must never possess release authority.

–

The Release Separation:

–

Model 2 Dual-Gated Release Authority:

Downstream consumers verifying a Verification Summary Attestation (VSA) need proof that the entire release pipeline was governed:

  1. Gate 1 (PipelineRun Classification): Kyverno validates the managed PipelineRun definition and mutates: trusted-pipeline-role: release-authority
  2. Gate 2 (Task Selector Conjunction): SPIRE issues the release SVID only if both the pipeline label AND the specific task selector (attach-summary-attestations) match!
spiffe://konflux-ci.dev/release/demo-app/slsa-e2e-release-dual-gated

???

Maia and Andrew explain dual-gating: “In the release namespace, we don’t just ask if the task is attach-summary-attestations. We ask: is this task running inside an authorized, policy-governed release pipeline? Both conditions must be met simultaneously.”


layout: false

Live Demo: Act 4 — Dual-Gated Managed Release Authority

🖥️ Live Demo: Act 4

ttyd offline
Managed Release Boundary & Dual-Control Signing
• Ambient ServiceAccount release authority is prohibited in managed-tenant.
• Dual-gating requires Kyverno PipelineRun validation AND SPIRE attachment task selector matching.
• Attaches Verification Summary Attestation (VSA) keylessly with Cosign into Rekor and verifies the transparency log.
./demo/run-demo.sh --act 4

???

Andrew runs Act 4 live:

  1. Submits AppStudio Release CR in default-tenant referencing demo-app snapshot.
  2. Watches managed-tenant release pipeline execute verify-conforma, push-snapshot, and attach-summary-attestations.
  3. Shows Rekor query: extracts log entry, parses X.509 certificate SAN, verifies it matches spiffe://konflux-ci.dev/release/demo-app/… Hit Escape or click Next to advance to conclusions.

Key Takeaways

Maia & Andrew

  1. Location ≠ Authorization: Stop treating Kubernetes namespaces and generic ServiceAccounts as authorization boundaries.
  2. Shift Verification to Admission: Intercepting task bundles at admission with Kyverno prevents untrusted code from ever gaining production identities.
  3. Task-Scoped Identity is Real Today: Combining Tekton, Kyverno, SPIFFE/SPIRE, and Sigstore brings least-privilege cryptographic identity to every CI step.
  4. Policy Closes the Loop: In-toto attestations signed by role-scoped SVIDs allow policy engines like Conforma to verify who was authorized to make each claim.

–

Working Code & Helm Charts:
github.com/arewm/slsa-konflux-example
(Branch: kubecon-na-2026-your-cis-mistaken-identity)

???

Maia and Andrew deliver the closing thoughts.


class: center, middle, inverse

Thank You!

Questions & Discussion

Andrew McNamara (amcnamar@redhat.com) • Maia Iyer (miyer@redhat.com)

Presentation & Code
github.com/arewm/presentations
SLSA & Tekton Demo
github.com/arewm/slsa-konflux-example

layout: false

Appendix: Full End-to-End Demonstration Arc

🖥️ Live Demo: Complete Arc

ttyd offline
Complete Walkthrough (Acts 0 through 4)
Runs the entire end-to-end demonstration arc from pre-flight cluster baseline through ambient breakdown, Kyverno admission, same-namespace API gating, and dual-gated release authority.
./demo/run-demo.sh

???

Full uninterrupted demonstration arc from pre-flight baseline to final Rekor verification.