Remote resolution
who does what
stage 1/6kubectl create -f taskrun.yaml. The spec has a taskRef with a resolver, not a Task name, so there is nothing to run yet.
apiVersion: tekton.dev/v1kind: TaskRunmetadata: { name: clone-run }spec:taskRef:resolver: gitparams:- { name: url, value: github.com/tektoncd/catalog.git }- { name: revision, value: main }- { name: pathInRepo, value: task/git-clone/0.9/git-clone.yaml }status:conditions:- type: Succeededstatus: "Unknown"reason: Pending
# no ResolutionRequest yet
# waiting for status.data
Try: step through once and watch the work pass from the Pipelines controller to the resolver and back through the ResolutionRequest. Then inject “repo not found”: the error stops at the request, the TaskRun fails, and no Pod is ever created.
taskRef:resolver: gitparams:- { name: url, value: https://github.com/tektoncd/catalog.git }- { name: revision, value: main }- { name: pathInRepo, value: task/git-clone/0.9/git-clone.yaml }
resolution path
resolved- git resolver
- →git clone (anonymous, shallow)
- →checkout main
- →read git-clone.yaml
resolved; refSource.digest.sha1 = a339fc519093…
defaults from ConfigMap git-resolver-config: default-revision, fetch-timeout, default-url, scm-type, server-url, api-token-secret-name, default-org
about the git resolver
- Anonymous mode does a shallow clone of url and reads one file at revision. Simple, but every resolution is a clone.
- SCM API mode (org + repo) reads the file through the provider API with a token from a Secret. Required for private repos without clone credentials, and subject to API rate limits.
- revision can be a branch, a tag or a commit SHA. Only the SHA is immutable; status.provenance.refSource always records the SHA that was actually used.
Try: in bundles, ask for name “ci” and see that a Pipeline layer doesn’t satisfy a Task request. In cluster, pick kube-system: the resolver refuses it because of blocked-namespaces. In git, flip to the SCM API mode and note the token Secret it now needs.
upstream repo: tektoncd-style task on main
HEAD = e0bad11A (3f2a9c1): the version you tested · B (81d07be): new optional param · C (c45e210): param renamed: runs fail · M (e0bad11): step now exfiltrates secrets
what each weekly PipelineRun actually ran
Try: play the weeks with “upstream compromised” on. revision: main runs every change the moment it lands, including the malicious one. Then turn on “tag force-moved”: tags look pinned but are just movable pointers. Only the commit SHA and the bundle digest stay on version A.
apiVersion: tekton.dev/v1kind: Taskmetadata:name: buildannotations:tekton.dev/signature: ZjI3ZDg1M2Q3ODY1MjNhMDhjNzAzMTgzN2I2ZTE1ZDZkspec:steps:- name: buildimage: gcr.io/kaniko-project/executor:v1.23.2args: ["--destination=$(params.IMAGE)"]
apiVersion: tekton.dev/v1alpha1kind: VerificationPolicymetadata: { name: acme-tasks, namespace: ci }spec:resources:- pattern: "https://github.com/acme/tasks.git"authorities:- name: acme-release-keykey:secretRef: { name: acme-pubkey, namespace: ci }hashAlgorithm: sha256mode: enforce
Pipelines controller verifies before running
- ✓policy matches source git+https://github.com/acme/tasks.git
- ✓signature annotation present; strip it, then hash the rest (sha256)
- ✓signature verifies with the policy's public key
Try: tamper with the Task. The signature no longer matches and enforce mode fails the TaskRun before a Pod exists. Switch to warn and it runs, flagged with TrustedResourcesVerified=False. Then make the pattern not match: now the no-match feature flag decides, and its default (ignore) lets the unverified Task run.
# ns tekton-pipelines-resolverskind: ConfigMapmetadata: { name: resolvers-feature-flags }data:enable-git-resolver: "true"enable-bundles-resolver: "true"enable-hub-resolver: "true"enable-cluster-resolver: "true"enable-http-resolver: "true"---kind: ConfigMapmetadata: { name: git-resolver-config }data:default-revision: mainfetch-timeout: 1mscm-type: githubapi-token-secret-name: ""api-token-secret-key: tokendefault-org: acme
taskRef:resolver: gitparams:- { name: repo, value: tasks } # org from default-org- { name: pathInRepo, value: build.yaml }# revision omitted
effective request the resolver sees
| org/repo | acme/tasks ← default-org |
| revision | main ← default-revision |
| pathInRepo | build.yaml |
| deadline | 60s ← fetch-timeout |
load on the remote: every run resolves every remote ref
Try: in “API, no token” mode push PipelineRuns/hour past ~15 with 4 refs and watch runs fail on the rate limit; adding a token Secret fixes it. Turn latency up and fetch-timeout down to see resolution timeouts. Pin to SHAs and enable the cache: fetches drop to one per distinct ref.
tektoncd/catalog
- git-clone.yamlREADME.mdsamples/ · tests/
git-clone 0.9
GitClones a repo into the output workspace and reports the commit it checked out.
- params
- urlrevisionrefspecsubmodulesdepthsslVerifysubdirectorydeleteExisting
- workspaces
- outputssh-directory?basic-auth?ssl-ca-directory?
- results
- commiturlcommitter-date
apiVersion: tekton.dev/v1kind: Pipelinemetadata: { name: ci }spec:tasks:- name: fetchtaskRef:resolver: hubparams:- { name: catalog, value: tekton-catalog-tasks }- { name: type, value: artifact }- { name: kind, value: task }- { name: name, value: git-clone }- { name: version, value: "0.9" }params:- { name: url, value: $(params.repo-url) }workspaces:- { name: output, workspace: shared }
Try: pick a task and switch the reference method. Hub is the shortest; git with a commit SHA and bundles by digest are the ones you can pin. Each catalog version is its own directory, so upgrading is an explicit change in your Pipeline, never a surprise.
# pick files, then push
ghcr.io/acme/ci-bundle:v1
Not pushed yet.
registry: every pushed digest
empty
Layer digest of build: sha256:537d768135adc1e615a…
Try: push to :v1, then edit build.yaml and push to :v1 again. The tag moves to a new manifest digest, but the old digest is still in the registry. A pipeline that references the bundle by @sha256 keeps getting the old version. Uncheck everything and push to see the validation error.
- takeawayA remote ref is a request, not a lookup. The controller creates a ResolutionRequest, a resolver fills status.data, and only after decoding and validation does a Pod exist. Fetch failures show up as TaskRunResolutionFailed before anything runs.
- takeawayThe reference decides reproducibility. Branches and tags drift; commit SHAs and bundle digests don't. status.provenance.refSource records what was actually used either way.
- takeawayPin for sameness and sign for authorship. A VerificationPolicy in enforce mode rejects a Task that was modified or signed by the wrong key, but sources that match no policy follow the no-match flag, which defaults to ignore.
Key concepts · 19
book chapter 9- Remote resolution
Fetching a Task or Pipeline definition from outside the cluster at run time, by naming a resolver and its params in taskRef/pipelineRef.
It lets teams share versioned Tasks without copying YAML into every namespace. The controller stays generic, and each source type is a pluggable resolver.
taskRef.resolverpipelineRef.resolvertaskRef.params↑ see it in the visualization - Resolver
A controller that watches ResolutionRequests carrying its type label, fetches the requested resource and writes it back into the request's status.
Resolvers are decoupled from the Pipelines controller: built-in ones ship in the tekton-pipelines-resolvers namespace, and you can write your own with the resolver framework.
tekton-pipelines-remote-resolversresolution.tekton.dev/type↑ see it in the visualization - ResolutionRequest
A CRD (resolution.tekton.dev/v1beta1) the Pipelines controller creates per remote ref: spec.params in, status.data (base64) and status.refSource out.
It is the contract between controller and resolver, and it is inspectable: kubectl get resolutionrequests shows what was asked, what came back, and why a resolution failed.
status.datastatus.refSourcekubectl get resolutionrequests↑ see it in the visualization - refSource provenance
The uri, digest and entryPoint of the fetched resource, copied into TaskRun/PipelineRun status.provenance.refSource.
Even when you reference a moving branch, the run records the exact commit it used. That is how you audit drift afterwards, and what Tekton Chains puts into provenance.
status.provenance.refSource.digest.sha1↑ see it in the visualization - Git resolver
Fetches one file (pathInRepo) at a revision, either by anonymous clone of url or through the SCM provider API using org/repo and a token Secret.
The most common way to keep Tasks next to the code that uses them. Clone mode is simple; API mode works with private repos but counts against provider rate limits.
url / revision / pathInRepoorg / repo / token / scmTypegit-resolver-config↑ see it in the visualization - Bundles resolver
Pulls an OCI image (bundle param) and returns the layer whose annotations match the requested kind and name.
Registries give you immutable digests, replication and access control for free, so bundles are the easiest way to pin and distribute Tasks.
bundle / name / kind / secretbundleresolver-config↑ see it in the visualization - Tekton Bundle
An OCI image in which each layer holds one Tekton resource, identified by the annotations dev.tekton.image.kind, dev.tekton.image.name and dev.tekton.image.apiVersion.
Packing a Pipeline and its Tasks into one image versions them together; referencing it by @sha256 digest gives you exactly the bytes you tested.
dev.tekton.image.kinddev.tekton.image.name↑ see it in the visualization - tkn bundle push / list
CLI commands that build a Tekton Bundle from YAML files and push it to a registry, and list the resources inside an existing bundle.
They make bundles a normal artifact in your release process. Pushing to the same tag moves the tag, so consumers should use the printed digest.
tkn bundle push ghcr.io/acme/b:v1 -f task.yamltkn bundle list ghcr.io/acme/b:v1↑ see it in the visualization - Hub resolver
Fetches a catalog Task or Pipeline by catalog, kind, name and version from Artifact Hub (type: artifact) or the older Tekton Hub API (type: tekton).
The shortest way to use community Tasks such as git-clone or buildah, at the price of trusting the hub and the publisher at fetch time.
type / catalog / kind / name / versionhubresolver-config↑ see it in the visualization - Artifact Hub
The CNCF package index (artifacthub.io) where the Tekton catalog is now published, as tekton-task and tekton-pipeline packages.
Tekton Hub (hub.tekton.dev) is deprecated; the hub resolver defaults to type artifact and the tekton-catalog-tasks catalog.
artifacthub.iotekton-catalog-tasks↑ see it in the visualization - Cluster resolver
Fetches a Task or Pipeline from another namespace of the same cluster by kind, name and namespace, subject to allowed and blocked namespace lists.
It replaces the deprecated ClusterTask: a platform team keeps shared Tasks in one namespace, and the resolver config controls who can read from where.
kind / name / namespaceblocked-namespacesClusterTask (deprecated)↑ see it in the visualization - HTTP resolver
Fetches a resource from any HTTPS URL, with optional basic auth from a Secret.
Handy for internal file servers, but a URL is not a version: unless the URL itself is immutable, the content can change under you.
urlhttp-usernamehttp-password-secret↑ see it in the visualization - Pinning vs drift
Referencing an immutable identifier (commit SHA, image digest) instead of a moving one (branch, tag) so every run resolves the same bytes.
Branches change on every merge and tags can be force-moved, so unpinned refs silently pick up breaking or malicious changes. Pinning makes runs reproducible; upgrades become explicit commits.
revision: <sha>bundle: img@sha256:…↑ see it in the visualization - Trusted resources
A Pipelines feature that verifies a signature on remotely resolved Tasks and Pipelines before running them.
Pinning tells you which bytes you got; a signature tells you who published them. Together they close the gap between “the file I resolved” and “the file my release team approved”.
tekton.dev/signaturetrusted-resources-verification-no-match-policy↑ see it in the visualization - VerificationPolicy
A namespaced CRD mapping source patterns (regexes over the resource's source URI) to authorities: public keys from inline data, a Secret or KMS, with mode enforce or warn.
enforce fails the run on a bad or missing signature; warn runs it but sets TrustedResourcesVerified=False. Sources that match no policy fall back to the no-match feature flag (ignore, warn or fail).
spec.resources[].patternspec.authorities[].keyspec.mode↑ see it in the visualization - Resolver ConfigMaps
Per-resolver defaults and limits in the resolvers namespace (git-resolver-config, bundleresolver-config, hubresolver-config, cluster-resolver-config, http-resolver-config).
Defaults such as default-revision or default-org keep TaskRuns short, and fetch-timeout bounds how long a slow remote can hold up a run.
default-revisionfetch-timeoutdefault-kind↑ see it in the visualization - resolvers-feature-flags
The ConfigMap with enable-git-resolver, enable-bundles-resolver, enable-hub-resolver, enable-cluster-resolver and enable-http-resolver.
Operators can switch off resolvers they don't want users to reach; a disabled resolver makes every request of that type fail.
enable-git-resolverenable-http-resolver↑ see it in the visualization - Tekton Catalog
The community collection of reusable Tasks and Pipelines (tektoncd/catalog), one directory per name and version, each with a README, samples and tests.
Versions are directories, so a new version never changes an old one. The tekton.dev/pipelines.minVersion annotation says which Pipelines release a version needs.
task/<name>/<version>/<name>.yamltekton.dev/pipelines.minVersionbuildahkaniko↑ see it in the visualization - git-clone Task
The most used catalog Task: clones url at revision into the output workspace and emits commit and url results.
Almost every pipeline starts with it, and its commit result is what downstream tasks and provenance use to say exactly which source was built.
workspaces.outputresults.commitparams.revision↑ see it in the visualization