Skip to content

Commit

Permalink
Add in code review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
Priya Wadhwa committed Dec 15, 2021
1 parent f89d048 commit 3b01fcd
Showing 1 changed file with 119 additions and 77 deletions.
196 changes: 119 additions & 77 deletions teps/0089-spire-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,24 @@ tags, and then generate with `hack/update-toc.sh`.

<!-- toc -->
- [Summary](#summary)
- [Background](#background)
- [SPIRE Concepts](#spire-concepts)
- [Installing SPIRE](#installing-spire)
- [Proposed Solution](#proposed-solution)
- [1. Tekton Chains has no way to verify that a TaskRun in the cluster was created by Tekton](#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, during or after execution](#2-tekton-chains-has-no-way-to-verify-that-the-taskrun-it-received-wasnt-modified-by-anybody-other-than-tekton-during-or-after-execution)
- [3. Tekton Pipelines can't verify that the results it reads weren't modified](#3-tekton-pipelines-cant-verify-that-the-results-it-reads-werent-modified)
- [Motivation](#motivation)
- [Requirements](#requirements)
- [Goals](#goals)
- [Non-Goals](#non-goals)
- [SPIRE Concepts](#spire-concepts)
- [Proposal](#proposal)
- [Implementation Plan](#implementation-plan)
- [Risks and Mitigations](#risks-and-mitigations)
- [Design Details](#design-details)
- [Requesting an SVID/Signature from the Controller Image](#requesting-an-svidsignature-from-the-controller-image)
- [Mounting SPIRE into the Tekton Controller](#mounting-spire-into-the-tekton-controller)
- [Enabling SPIRE on the Entrypointer Image](#enabling-spire-on-the-entrypointer-image)
- [Verification in Chains](#verification-in-chains)
- [Test Plan](#test-plan)
- [Implementation Plan](#implementation-plan)
- [Alternatives](#alternatives)
- [Kubernertes Service Account Token Volume Projection](#kubernertes-service-account-token-volume-projection)
- [Infrastructure Needed (optional)](#infrastructure-needed-optional)
Expand All @@ -44,6 +50,8 @@ tags, and then generate with `hack/update-toc.sh`.
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.

## Background

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.

Expand All @@ -53,32 +61,84 @@ There are a couple issues with this:
3. Tekton Pipelines can't verify that the results it reads weren't modified

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 with a SPIRE [k8s Workload Attestor](https://github.com/spiffe/spire/blob/main/doc/plugin_agent_workloadattestor_k8s.md).
In thise case, we'll set up SPIRE to only issue SVIDs to:
SPIRE can be used to request signatures and certificates (SVIDs) for a given workload.
This workload can be anything, including a Result, a Pod spec, or an entire TaskRun yaml.

We're going to use SPIRE to mitigate all three of the issues mentioned above.

## SPIRE Concepts
Before discussing how SPIRE is going to fix these issues, here's a very basic overview of how it works:

Pods running in the cluster can interact with SPIRE.
A pod will send some `payload` to SPIRE for signing.
The SPIRE socket will confirm that the request came from an approved workload (in our case, the Tekton controller or Pods created by the controller) and then it will sign the `payload`.
SPIRE will return:
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`

For our use case, both the Tekton entrypointer image (running in Pods) and the Tekton Controller will interact with SPIRE.

### Installing SPIRE
SPIRE runs as a Unix domain socket on the k8s node.
We can use [spiffe-csi](https://github.com/spiffe/spiffe-csi) to mount the SPIRE socket into Pods as a `csi` type Volume so that we don't have to rely on the `hostPath` volume.
Users will be responsible for installing this themselves.
When creating Pods, we would automatically mount this volume in as appropriate.

This volume mount would look something like this on an arbitrary Pod created by the controller:

```yaml
containers:
- name: my-image
volumeMounts:
- name: spiffe-workload-api
mountPath: /spiffe-workload-api
readOnly: true
env:
- name: SPIFFE_ENDPOINT_SOCKET
value: unix:///spiffe-workload-api/spire-agent.sock
volumes:
- name: spiffe-workload-api
csi:
driver: "csi.spiffe.io"
```


## Proposed Solution

### 1. Tekton Chains has no way to verify that a TaskRun in the cluster was created by Tekton
The solution to this relies on the SPIRE [k8s Workload Attestor](https://github.com/spiffe/spire/blob/main/doc/plugin_agent_workloadattestor_k8s.md).
SPIRE can be configured to only issue SVIDs to certain k8s workloads.
That means we can set up SPIRE to only issue signatures for:
* The Tekton controller
* Pods created by the Tekton controller (e.g. pods running the entrypointer image)

This configuration will let us ensure that all Tasks were created by Tekton and that Task results weren't modified.

The basic flow would look like this:
1. Tekton Pipelines receives a TaskRun config, and generates the Pod for with SPIRE mounted in to the Pod
1. Tekton Pipelines stores the state of the TaskRun in memory, and every time the status updates it follows this loop:
* Compare the current TaskRun to the state stored in memory
* If the TaskRun has been changed, don't request an SVID from SPIRE
* If the TaskRun has not been changed, then update the TaskRun status and store the new state in memory
1. The entrypointer image requests an SVID and signature for all results in the Pod
1. Tekton Pipelines verifies the results and stops if verification fails
1. If successful, it requests a signature over the completed TaskRun yaml from SPIRE
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!
We can be certain that all signed Tasks were created by Tekton itself, since no other Pods can request signatures.

### 2. Tekton Chains has no way to verify that the TaskRun it received wasn't modified by anybody other than Tekton, during or after execution

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.
The solution to this is Signed TaskRuns, where the TaskRun has to be signed and verified every time it has been modified.
That way, we can prove that the TaskRun hasn't been tampered with during or after execution.

_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_.
There could potentially be a performance implication here, if we need to request signatures every time the TaskRun is modified during execution.
This is something we will need to look into further before implementing this feature.

The Tekton Controller will need to request a siganture from SPIRE each time the TaskRun is modified to prove that it hasn't been tampered with during execution.
Roughly, this will look something like this:
* Controller initiates a TaskRun, and stores a signature over its contents as an annotation

Each time the TaskRun is modified,
* Controller verifies the TaskRun hasn't been modified
* Controller requests a new SPIRE SVID and signature over the new modified TaskRun
* Controller stores the new SVID and signature on the TaskRun as an annotation

Right now, the design details around this are a little fuzzy!
The plan is to first implement signed Results, and then if this design no longer seems like the correct fit for signed TaskRuns then it will be revisited in this TEP :)


### 3. Tekton Pipelines can't verify that the results it reads weren't modified
The solution to this is Signed Results.
We will modifiy the entrypointer image to sign results with SPIRE once they're available.
The signature and SVID provided by SPIRE will be stored on the TaskRun itself as annotations for a client to verify (this could be Pipelines or Chains).


## Motivation
Expand All @@ -92,31 +152,21 @@ We'll also need this feature for Tekton to achieve [SLSA 3](https://slsa.dev/lev

### Goals

* Once a TaskRun completes, Pipelines can request a signature over the TaskRun yaml from SPIRE
* The entrypointer image can request an SVID and signature over results
* Pipelines can store the signature and SVID on the TaskRun itself as annotations
* Chains should be able to verify a TaskRun wasn't modified after execution via the signature and SVID provided
* Results can be verified as non-falsifiabe
* A TaskRun yaml can be verified as non-falsifiable
* Clients are confident that TaskRuns were initiated and monitored by the Tekton controller

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

## SPIRE Concepts
Both the Tekton entrypointer image (running in Pods) and the Tekton Controller will interact with SPIRE.
The entrypointer image will use SPIRE to secure results, and the Tekton Controller will use it to secure the entire TaskRun yaml.

The entrypointer image will request a signature and an SVID for each result, and store them as annotations on the TaskRun.

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`

## Proposal
As mentioned above, the basic design looks like this:
As mentioned above, the basic design looks like this (this is meant to be high level and still needs to be fleshed out a bit):
1. Tekton Pipelines receives a TaskRun config, and generates the Pod for it with SPIRE mounted in
1. The Pod executes, and the entrypointer requests an SVID and signature over the Results
1. Tekton Pipelines verifies the Results
1. Meanwhile, Tekton Pipelines has been verifying that the TaskRun hasn't been modified during execution
1. Tekton Pipelines 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
Expand All @@ -129,60 +179,52 @@ Chains will:
1. If verification fails, Chains will mark the TaskRun as "failed" and move on
1. Otherwise, continue signing stuff!

## Implementation Plan
The plan to achieve non-falsifiable provenance will be implemented in phases.

### Risks and Mitigations
We'll need to depend on the [github.com/spiffe/go-spiffe](https://github.com/spiffe/go-spiffe) library to interact with the SPIRE agent, request SVIDs and signatures.
**Phase 1**
* Add support for Signed Results with SPIRE (this will primarily involve modifications to the entrypointer image)
* Add support for Chains verifying Signed Results

**Phase 2**
* Implement Signed TaskRuns with SPIRE (requires further design)
* Determine an alternate release process for the SPIRE feature (since the Pipelines Controller will need the SPIRE volume mounted in)
* Add support for Chains verifying Signed TaskRuns

## Design Details
In parallel with this work, we should:
* Confirm that this meets the SLSA defintion of "non-falsifiable", which might require a security audit

### Requesting an SVID/Signature from the Controller Image
### Risks and Mitigations
We'll need to depend on the [github.com/spiffe/go-spiffe](https://github.com/spiffe/go-spiffe) library to interact with the SPIRE agent, request SVIDs and signatures.

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.
## Design Details

For now, this design section will focus on Part 1 of the implementation plan: Signed Results.
This TEP will be updated as we flesh out the design for Signed Taskruns.

### 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`
1. Pass in the `--validate-spire=true` flag to the controller image
### Enabling SPIRE on the Entrypointer Image
We can add a feature flag `--enable-spire=true` as described in [Customizing the Pipelines Controller behavior](https://github.com/tektoncd/pipeline/blob/main/docs/install.md#customizing-the-pipelines-controller-behavior) as an alpha feature.
If the feature is enabled, then Pipelines would mount in the `csi` Volume into all Pods.

This volume mount would look something like this on the Tekton controller:
The entrypointer image should also be able to see that this flag is set and accordingly sign Results.

```
containers:
- name: tekton-pipelines-controller
volumeMounts:
- name: spire
mountPath: /run/spire/sockets/agent.sock
volumes:
- name: spire
hostPath:
path: /run/spire/sockets/agent.sock
```
### Verification in Chains
Once Signed Results are available, we'll add verification of Signed Results to Chains.
If verification fails for a TaskRun then Chains will not sign it.

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`
Tests for Signed Results:
1. Enabling the alpha feature for SPIRE in Tekton
1. Requesting an SVID & signature over Results for a 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. 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

## Implementation Plan
1. Add support for signing Results with SPIRE (this will primarily involve modifications to the entrypointer image)
1. Add support for signing TaskRuns with SPIRE
1. Figure out an alternate release process for the SPIRE feature
1. Add support in Chains for verifying SPIRE signatures (this exists in an alternate branch, but we would need to merge it into main)
1. Add the in-memory comparison feature to prevent modifications to the TaskRun during execution
Tests for Signed TaskRuns:
1. Verify that a TaskRun that's been modified during execution isn't verified
1. Verify that a TaskRun that's been modified after execution isn't verified



## Alternatives
Expand Down

0 comments on commit 3b01fcd

Please sign in to comment.