Skip to content

Commit

Permalink
Add some more details about SPIRE support
Browse files Browse the repository at this point in the history
  • Loading branch information
Priya Wadhwa committed Nov 18, 2021
1 parent 2302e71 commit 0c33dda
Showing 1 changed file with 77 additions and 37 deletions.
114 changes: 77 additions & 37 deletions teps/0089-spire-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
status: proposed
title: SPIRE Support
creation-date: '2021-10-04'
last-updated: '2021-10-04'
last-updated: '2021-11-18'
authors:
- '@priyawadhwa'
---
Expand Down Expand Up @@ -36,17 +36,34 @@ tags, and then generate with `hack/update-toc.sh`.

## Summary

A zero-trust supply chain for Tekton requires collaboration between Tekton Pipelines, Tekton Chains, and [SPIRE](https://spiffe.io/).
A detailed explanation of why this is necessary can be found in [Zero Trust Supply Chains](https://docs.google.com/document/d/1CRvANkYu0fxJjEZO4KTyyk_1uZm2Q9Nr0ibxplakODg/edit?resourcekey=0-nGnWnCni8IpiXim-WreYMg#heading=h.fyy27kd27z1r) by dlorenc@.
This TEP covers integrating Tekton and Tekton Chains with [SPIFFE/SPIRE](https://spiffe.io/), which would provide a more secure supply chain for Tekton users. It would also guarantee [non-falsifiable provenance](https://slsa.dev/requirements#non-falsifiable), which is a requirement for [SLSA Level 3](https://slsa.dev/levels).
With this integration, Tekton will be one step closer to SLSA Level 3 compliance.

To summarize, we can't rely on only Tekton Pipelines and Tekton Chains to secure our pipeline because Chains relies on Pipelines to tell it what the given state of a TaskRun is.
For example, Chains expects Pipelines to tell it when the TaskRun is complete, what happened in the TaskRun, and what artifacts need to be signed.
This is insecure because any cluster administrator can edit a TaskRun to say anything they want, and Chains will just believe it.
Chains has no way to verify that the TaskRun it gets was actually run as it says it was.
Currently, Tekton Chains observes Tekton and waits for TaskRuns to complete.
Once it sees that a TaskRun has completed it tries to sign any artifacts that were built and also generates provenance.

This is where SPIRE comes in!
SPIRE can be used to attest workloads on a cluster, and in this case it will be used to attest TaskRuns.
Together, Tekton Pipelines, Tekton Chains, and SPIRE can provide a secure zero-trust supply chain on Kubernetes.
There are a couple issues with this:
1. Tekton Chains has no way to verify that a TaskRun in the cluster was created by Tekton
2. Tekton Chains has no way to verify that the TaskRun it received wasn't modified by anybody other than Tekton

This is where SPIRE comes in!
SPIRE can be used to request signatures and certificates (SVIDs) for a given workload (this workload could be a result, or in our case an entire TaskRun yaml).
SPIRE can be configured to only issue SVIDs to certain k8s workloads (e.g., you can set up SPIRE to only issue SVIDs to the Tekton controller) with a SPIRE [k8s Workload Attestor](https://github.com/spiffe/spire/blob/main/doc/plugin_agent_workloadattestor_k8s.md).
This property is a critical feature, as it ensures that a TaskRun was created by Tekton.

The basic flow would look like this:
1. Tekton Pipelines receives a TaskRun config, and generates the Pod for it as usual
1. Tekton Pipelines runs the TaskRun, and then requests a signature over the completed TaskRun yaml from SPIRE
1. SPIRE has been set up to only issue signatures and SVIDs to the Tekton controller, which it does
1. Tekton Pipelines stores the SVID and signature received from SPIRE as annotations on the TaskRun
1. Tekton Chains verifies that SVID and signature against the TaskRun yaml
1. If verification is successful, Chains proceeds normally. Otherwise, it stops and doesn't sign anything!

With the SPIRE integration, we successfully mitigate the two issues mentioned above.
Tekton Chains can be confident the TaskRun was created by Tekton, since only Tekton workloads can have SPIRE issued SVIDs.
Tekton Chains can also verify that the TaskRun yaml wasn't modified by anybody other than the Tekton controller, because the signature exists over the entire TaskRun yaml.

_NOTE: A cluster administrator could still potentially threaten this model by disabling SPIRE or changing settings on the k8s Workload Attestor. At SLSA 3, it is still allowed that some trusted administrators will have root access to the build system. This will eventually need to be addressed for SLSA 4_.


## Motivation
Expand All @@ -55,49 +72,54 @@ Security! =)

We'll also need this feature for Tekton to achieve [SLSA 3](https://slsa.dev/levels), which requires Non-falsifiable Provenance.

### Requirements
* SPIRE is configured to only issue SVIDs to the Tekton controller via a [k8s Workload Attestor](https://github.com/spiffe/spire/blob/main/doc/plugin_agent_workloadattestor_k8s.md)

### Goals

* Pipelines should attest a TaskRun against SPIRE before it completes
* Pipelines should store info necessary for verification somewhere
* Chains should be able to verify a TaskRun wasn't modified before signing
* Once a TaskRun completes, Pipelines can request a signature over the TaskRun yaml from SPIRE
* Pipelines can store the signature and SVID on the TaskRun itself as annotaitons
* Chains should be able to verify a TaskRun wasn't modified after execution via the signature and SVID provided

### Non-Goals
* Protect against a malicious cluster admin (this will be a goal for SLSA 4)

## Proposal
The basic design looks like this:
## SPIRE Concepts
The Tekton Controller will send the TaskRun yaml (this is the `payload`) to SPIRE for signing.
SPIRE will make sure that the request came from the Tekton controller, and once it has confirmed that it will sign the `payload`.
SPIRE will return the following to the Tekton Controller:
1. The `signature` over the `payload`
1. A SPIFFE Verifiable Identity Document, or [SVID](https://spiffe.io/docs/latest/spiffe-about/spiffe-concepts/#spiffe-verifiable-identity-document-svid), an X509 certificate containing the public key used to verify the `signature` against the `payload`

1. User installs SPIRE capabilities into Tekton (details discussed below)
1. Pipelines controller creates a Pod for a new TaskRun per usual
1. Pipelines requests a signature from SPIRE over the TaskRun yaml
1. SPIRE returns signature and SVID x509 Certificate for the TaskRun
1. Pipelines stores the signature and SVID on the TaskRun as an annotation
## Proposal
As mentioned above, the basic design looks like this:
1. Tekton Pipelines receives a TaskRun config, and generates the Pod for it
1. Tekton Pipelines runs the TaskRun, and then requests a signature and SVID over the completed TaskRun yaml from SPIRE
1. Tekton Pipelines stores the SVID and signature as annotations on the TaskRun
1. Tekton Chains observes the TaskRun, and verifies that SVID and signature against the TaskRun yaml
1. If verification is successful, Chains proceeds normally. Otherwise, it stops and doesn't sign anything!


Then, Chains gets the TaskRun.
Chains will:
1. Verify the signature against the SPIRE x509 certificate
1. Verify the signature against the SPIRE SVID
1. If verification fails, Chains will mark the TaskRun as "failed" and move on
1. Otherwise, continue signing stuff!


<!--
This is where we get down to the specifics of what the proposal actually is.
This should have enough detail that reviewers can understand exactly what
you're proposing, but should not include things like API designs or
implementation. The "Design Details" section below is for the real
nitty-gritty.
-->

### Risks and Mitigations

Annotations might not be big enough to store the info we want to send.
None that I can think of for now.

## Design Details

### Requesting an SVID/Signature from the Controller Image

We could add a flag to the controller image, `--validate-spire=true`, which would tell the controller image to request an SVID & signature from SPIRE.
The Tekton Controller image will need some way of knowing SPIRE has been enabled, at that it should request SVIDs and signatures for all TaskRuns.

We could add a flag to the controller image, `--validate-spire=true`, which would enable the SPIRE functionality.


### Enabling SPIRE in Tekton
### Mounting SPIRE into the Tekton Controller
SPIRE runs as a Unix domain socket on the k8s node.
For Tekton to interact with SPIRE, we would need to make the following changes to the controller deployment:
1. Mount in the SPIRE socket as a volume of type `hostPath`
Expand All @@ -119,26 +141,44 @@ volumes:

We could write a TaskRun that would be responsible for making the above changes to the controller.
The code for this TaskRun could live in the Chains repo, and be released as part of Chains releases.
Or, Tekton could start publishing an alternate `release.yaml` with this configuration and the `--validate-spire=true` flag set by default (suggested by bobcatfish@).

## Test Plan
Tests should cover the entire flow mentioned above including:
1. Enabling SPIRE in Tekton with the installation `TaskRun`
1. Requesting an SVID & signature for a TaskRun in the controller
1. Storing those as annotations on the TaskRun
1. Verification of SPIRE with Chains
1. Verification of SPIRE with Chains
1. Verify that a TaskRun that isn't created by Tekton isn't signed Chains
1. Verify that a TaskRun that's been modified after execution isn't signed by Chains



## Alternatives

We could also look into other ways of preventing tampering of Tasks.
As of now I don't know of any other alternatives would work.
### Kubernertes Service Account Token Volume Projection
Instead of SPIRE, we could potentially use [Kubernertes Service Account Token Volume Projection](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-token-volume-projection) for signing.
This is a form of keyless signing, which is described in detail in [Zero-friction “keyless signing” with Kubernetes](https://chainguard.dev/posts/2021-11-03-zero-friction-keyless-signing) by mattmoor@.

Instead of requesting an SVID and signature from SPIRE, the Tekton Controller would use this keyless signing to request a certificate from [Fulcio](https://github.com/sigstore/fulcio).
Chains would verify the signature against this certificate instead.

Pros:
* Much easier to set up, since it wouldn't require installation of a new tool
* Wouldn't require any changes to the `release.yaml` for Tekton Chains

Cons:
* The cert is tied to a service account rather than to a specific workload (so we could prove the certificate was requested by something running under the Tekton controller service account, but not the controller itself)
* SPIRE has much more control around the policy for granting SVIDs



## Infrastructure Needed (optional)
We'll probably need a persistent k8s cluster with SPIRE installed to run tests against.


Test clusters will also need Pipelines and Chains installed (currently they only have Pipeliens installed).

## References (optional)

* [Zero-Trust Supply Chains](https://docs.google.com/document/d/1CRvANkYu0fxJjEZO4KTyyk_1uZm2Q9Nr0ibxplakODg/edit?resourcekey=0-nGnWnCni8IpiXim-WreYMg#heading=h.fyy27kd27z1r) by dlorenc@
* [Zero-friction “keyless signing” with Kubernetes](https://chainguard.dev/posts/2021-11-03-zero-friction-keyless-signing) by mattmoor@

0 comments on commit 0c33dda

Please sign in to comment.