diff --git a/docs/spire.md b/docs/spire.md index 620928deb98..b19eb726232 100644 --- a/docs/spire.md +++ b/docs/spire.md @@ -6,13 +6,58 @@ weight: 1660 --> ⚠️ This is a work in progress: SPIRE support is not yet functional -TaskRun result attestations is currently an alpha experimental feature. Currently all that is implemented is support for configuring Tekton to connect to SPIRE. See TEP-0089 for details on the overall design and feature set. +TaskRun result attestations is currently an alpha experimental feature. Currently all that is implemented is support for configuring Tekton to connect to SPIRE and enabling TaskRun to sign and verify the TaskRunStatus. See [TEP-0089](https://github.com/tektoncd/community/blob/main/teps/0089-nonfalsifiable-provenance-support.md) for details on the overall design and feature set. This being a large feature, this will be implemented in the following phases. This document will be updated as we implement new phases. 1. Add a client for SPIRE (done). -2. Add a configMap which initializes SPIRE (in progress). -3. Modify TaskRun to sign and verify TaskRun Results using SPIRE. -4. Modify Tekton Chains to verify the TaskRun Results. +2. Add a configMap which initializes SPIRE (done). +3. Modify TaskRun to sign and verify TaskRunStatus using SPIRE (done). +4. Enabling Chains to verify the TaskRun Results. + +When the TaskRun result attestations feature is [enabled](./spire.md#enabling-taskrun-result-attestations) all TaskRuns will produce a signature alongside its results, which can then be used to validate its provenance. For example, a TaskRun result that creates user-specified results `commit` and `url` would look like the following. `SVID`, `RESULT_MANIFEST`, `RESULT_MANIFEST.sig`, `commit.sig` and `url.sig` are generated attestations by the integration of SPIRE and Tekton Controller. + +Parsed, the fields would be: +``` +... + +... +📝 Results + + NAME VALUE + ∙ RESULT_MANIFEST commit,url,SVID,commit.sig,url.sig + ∙ RESULT_MANIFEST.sig MEUCIQD55MMII9SEk/esQvwNLGC43y7efNGZ+7fsTdq+9vXYFAIgNoRW7cV9WKriZkcHETIaAKqfcZVJfsKbEmaDyohDSm4= + ∙ SVID -----BEGIN CERTIFICATE----- +MIICGzCCAcGgAwIBAgIQH9VkLxKkYMidPIsofckRQTAKBggqhkjOPQQDAjAeMQsw +CQYDVQQGEwJVUzEPMA0GA1UEChMGU1BJRkZFMB4XDTIyMDIxMTE2MzM1MFoXDTIy +MDIxMTE3MzQwMFowHTELMAkGA1UEBhMCVVMxDjAMBgNVBAoTBVNQSVJFMFkwEwYH +KoZIzj0CAQYIKoZIzj0DAQcDQgAEBRdg3LdxVAELeH+lq8wzdEJd4Gnt+m9G0Qhy +NyWoPmFUaj9vPpvOyRgzxChYnW0xpcDWihJBkq/EbusPvQB8CKOB4TCB3jAOBgNV +HQ8BAf8EBAMCA6gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1Ud +EwEB/wQCMAAwHQYDVR0OBBYEFID7ARM5+vwzvnLPMO7Icfnj7l7hMB8GA1UdIwQY +MBaAFES3IzpGDqgV3QcQNgX8b/MBwyAtMF8GA1UdEQRYMFaGVHNwaWZmZTovL2V4 +YW1wbGUub3JnL25zL2RlZmF1bHQvdGFza3J1bi9jYWNoZS1pbWFnZS1waXBlbGlu +ZXJ1bi04ZHE5Yy1mZXRjaC1mcm9tLWdpdDAKBggqhkjOPQQDAgNIADBFAiEAi+LR +JkrZn93PZPslaFmcrQw3rVcEa4xKmPleSvQaBoACIF1QB+q1uwH6cNvWdbLK9g+W +T9Np18bK0xc6p5SuTM2C +-----END CERTIFICATE----- + ∙ commit aa79de59c4bae24e32f15fda467d02ae9cd94b01 + ∙ commit.sig MEQCIEJHk+8B+mCFozp0F52TQ1AadlhEo1lZNOiOnb/ht71aAiBCE0otKB1R0BktlPvweFPldfZfjG0F+NUSc2gPzhErzg== + ∙ url https://github.com/buildpacks/samples + ∙ url.sig MEUCIF0Fuxr6lv1MmkreqDKcPH3m+eXp+gY++VcxWgGCx7T1AiEA9U/tROrKuCGfKApLq2A9EModbdoGXyQXFOpAa0aMpOg= +``` + +However, the verification materials are removed from the final results as part of the TaskRun status. It is stored in the termination messages (more details below): + +``` +$ tkn tr describe cache-image-pipelinerun-8dq9c-fetch-from-git +... + +... +📝 Results + NAME VALUE + ∙ commit aa79de59c4bae24e32f15fda467d02ae9cd94b01 + ∙ url https://github.com/buildpacks/samples +``` ## Architecture Overview @@ -127,3 +172,204 @@ To enable TaskRun attestations: # spire-node-alias-prefix specifies the SPIRE node alias prefix to use. spire-node-alias-prefix: "/tekton-node/" ``` + +## Sample TaskRun attestation + +The following example shows how this feature works: + +```yaml +kind: TaskRun +apiVersion: tekton.dev/v1beta1 +metadata: + name: non-falsifiable-provenance +spec: + timeout: 60s + taskSpec: + steps: + - name: non-falsifiable + image: ubuntu + script: | + #!/usr/bin/env bash + printf "%s" "hello" > "$(results.foo.path)" + printf "%s" "world" > "$(results.bar.path)" + results: + - name: foo + - name: bar +``` + + +The termination message is: +``` +message: '[{"key":"RESULT_MANIFEST","value":"foo,bar","type":1},{"key":"RESULT_MANIFEST.sig","value":"MEQCIB4grfqBkcsGuVyoQd9KUVzNZaFGN6jQOKK90p5HWHqeAiB7yZerDA+YE3Af/ALG43DQzygiBpKhTt8gzWGmpvXJFw==","type":1},{"key":"SVID","value":"-----BEGIN + CERTIFICATE-----\nMIICCjCCAbCgAwIBAgIRALH94zAZZXdtPg97O5vG5M0wCgYIKoZIzj0EAwIwHjEL\nMAkGA1UEBhMCVVMxDzANBgNVBAoTBlNQSUZGRTAeFw0yMjAzMTQxNTUzNTlaFw0y\nMjAzMTQxNjU0MDlaMB0xCzAJBgNVBAYTAlVTMQ4wDAYDVQQKEwVTUElSRTBZMBMG\nByqGSM49AgEGCCqGSM49AwEHA0IABPLzFTDY0RDpjKb+eZCIWgUw9DViu8/pM8q7\nHMTKCzlyGqhaU80sASZfpkZvmi72w+gLszzwVI1ZNU5e7aCzbtSjgc8wgcwwDgYD\nVR0PAQH/BAQDAgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNV\nHRMBAf8EAjAAMB0GA1UdDgQWBBSsUvspy+/Dl24pA1f+JuNVJrjgmTAfBgNVHSME\nGDAWgBSOMyOHnyLLGxPSD9RRFL+Yhm/6qzBNBgNVHREERjBEhkJzcGlmZmU6Ly9l\neGFtcGxlLm9yZy9ucy9kZWZhdWx0L3Rhc2tydW4vbm9uLWZhbHNpZmlhYmxlLXBy\nb3ZlbmFuY2UwCgYIKoZIzj0EAwIDSAAwRQIhAM4/bPAH9dyhBEj3DbwtJKMyEI56\n4DVrP97ps9QYQb23AiBiXWrQkvRYl0h4CX0lveND2yfqLrGdVL405O5NzCcUrA==\n-----END + CERTIFICATE-----\n","type":1},{"key":"bar","value":"world","type":1},{"key":"bar.sig","value":"MEUCIQDOtg+aEP1FCr6/FsHX+bY1d5abSQn2kTiUMg4Uic2lVQIgTVF5bbT/O77VxESSMtQlpBreMyw2GmKX2hYJlaOEH1M=","type":1},{"key":"foo","value":"hello","type":1},{"key":"foo.sig","value":"MEQCIBr+k0i7SRSyb4h96vQE9hhxBZiZb/2PXQqReOKJDl/rAiBrjgSsalwOvN0zgQay0xQ7PRbm5YSmI8tvKseLR8Ryww==","type":1}]' +``` + +Parsed, the fields are: +- `RESULT_MANIFEST`: List of results that should be present, to prevent pick and choose attacks +- `RESULT_MANIFEST.sig`: The signature of the result manifest +- `SVID`: The x509 certificate that will be used to verify the signature trust chain to the authority +- `*.sig`: The signature of each individual result output +``` + ∙ RESULT_MANIFEST foo,bar + ∙ RESULT_MANIFEST.sig MEQCIB4grfqBkcsGuVyoQd9KUVzNZaFGN6jQOKK90p5HWHqeAiB7yZerDA+YE3Af/ALG43DQzygiBpKhTt8gzWGmpvXJFw== + ∙ SVID -----BEGIN CERTIFICATE----- +MIICCjCCAbCgAwIBAgIRALH94zAZZXdtPg97O5vG5M0wCgYIKoZIzj0EAwIwHjEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBlNQSUZGRTAeFw0yMjAzMTQxNTUzNTlaFw0y +MjAzMTQxNjU0MDlaMB0xCzAJBgNVBAYTAlVTMQ4wDAYDVQQKEwVTUElSRTBZMBMG +ByqGSM49AgEGCCqGSM49AwEHA0IABPLzFTDY0RDpjKb+eZCIWgUw9DViu8/pM8q7 +HMTKCzlyGqhaU80sASZfpkZvmi72w+gLszzwVI1ZNU5e7aCzbtSjgc8wgcwwDgYD +VR0PAQH/BAQDAgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNV +HRMBAf8EAjAAMB0GA1UdDgQWBBSsUvspy+/Dl24pA1f+JuNVJrjgmTAfBgNVHSME +GDAWgBSOMyOHnyLLGxPSD9RRFL+Yhm/6qzBNBgNVHREERjBEhkJzcGlmZmU6Ly9l +eGFtcGxlLm9yZy9ucy9kZWZhdWx0L3Rhc2tydW4vbm9uLWZhbHNpZmlhYmxlLXBy +b3ZlbmFuY2UwCgYIKoZIzj0EAwIDSAAwRQIhAM4/bPAH9dyhBEj3DbwtJKMyEI56 +4DVrP97ps9QYQb23AiBiXWrQkvRYl0h4CX0lveND2yfqLrGdVL405O5NzCcUrA== +-----END CERTIFICATE----- + ∙ bar world + ∙ bar.sig MEUCIQDOtg+aEP1FCr6/FsHX+bY1d5abSQn2kTiUMg4Uic2lVQIgTVF5bbT/O77VxESSMtQlpBreMyw2GmKX2hYJlaOEH1M= + ∙ foo hello + ∙ foo.sig MEQCIBr+k0i7SRSyb4h96vQE9hhxBZiZb/2PXQqReOKJDl/rAiBrjgSsalwOvN0zgQay0xQ7PRbm5YSmI8tvKseLR8Ryww== +``` + + +However, the verification materials are removed from the results as part of the TaskRun status: +```console +$ tkn tr describe non-falsifiable-provenance +Name: non-falsifiable-provenance +Namespace: default +Service Account: default +Timeout: 1m0s +Labels: + app.kubernetes.io/managed-by=tekton-pipelines + +🌡️ Status + +STARTED DURATION STATUS +38 seconds ago 36 seconds Succeeded + +📝 Results + + NAME VALUE + ∙ bar world + ∙ foo hello + +🦶 Steps + + NAME STATUS + ∙ non-falsifiable Completed +``` + +## How is the result being verified + +The signatures are being verified by the Tekton controller, the process of verification is as follows: + +- Verifying the SVID + - Obtain the trust bundle from the SPIRE server + - Verify the SVID with the trust bundle + - Verify that the SVID spiffe ID is for the correct TaskRun +- Verifying the result manifest + - Verify the content of `RESULT_MANIFEST` with the field `RESULT_MANIFEST.sig` with the SVID public key + - Verify that there is a corresponding field for all items listed in `RESULT_MANIFEST` (besides SVID and `*.sig` fields) +- Verify individual result fields + - For each of the items in the results, verify its content against its associated `.sig` field + + +# TaskRun Status attestations + +Each TaskRun status that is written by the tekton-pipelines-controller will be signed to ensure that there is no external +tampering of the TaskRun status. Upon each retrieval of the TaskRun, the tekton-pipelines-controller checks if the status is initialized, +and that the signature validates the current status. +The signature and SVID will be stored as annotations on the TaskRun Status field, and can be verified by a client. + +The verification is done on every consumption of the TaskRun except when the TaskRun is uninitialized. When uninitialized, the +tekton-pipelines-controller is not influenced by fields in the status and thus will not sign incorrect reflections of the TaskRun. + +The spec and TaskRun annotations/labels are not signed when there are valid interactions from other controllers or users (i.e. cancelling taskrun). +Editing the object annotations/labels or spec will not result in any unverifiable outcome of the status field. + +As the TaskRun progresses, the Pipeline Controller will reconcile the TaskRun object and continually verify the current hash against the `tekton.dev/status-hash-sig` before updating the hash to match the new status and creating a new signature. + +An example TaskRun annotations would be: + +```console +$ tkn tr describe non-falsifiable-provenance -oyaml +apiVersion: tekton.dev/v1beta1 +kind: TaskRun +metadata: + annotations: + pipeline.tekton.dev/release: 3ee99ec + creationTimestamp: "2022-03-04T19:10:46Z" + generation: 1 + labels: + app.kubernetes.io/managed-by: tekton-pipelines + name: non-falsifiable-provenance + namespace: default + resourceVersion: "23088242" + uid: 548ebe99-d40b-4580-a9bc-afe80915e22e +spec: + serviceAccountName: default + taskSpec: + results: + - description: "" + name: foo + - description: "" + name: bar + steps: + - image: ubuntu + name: non-falsifiable + resources: {} + script: | + #!/usr/bin/env bash + sleep 30 + printf "%s" "hello" > "$(results.foo.path)" + printf "%s" "world" > "$(results.bar.path)" + timeout: 1m0s +status: + annotations: + tekton.dev/controller-svid: | + -----BEGIN CERTIFICATE----- + MIIB7jCCAZSgAwIBAgIRAI8/08uXSn9tyv7cRN87uvgwCgYIKoZIzj0EAwIwHjEL + MAkGA1UEBhMCVVMxDzANBgNVBAoTBlNQSUZGRTAeFw0yMjAzMDQxODU0NTlaFw0y + MjAzMDQxOTU1MDlaMB0xCzAJBgNVBAYTAlVTMQ4wDAYDVQQKEwVTUElSRTBZMBMG + ByqGSM49AgEGCCqGSM49AwEHA0IABL+e9OjkMv+7XgMWYtrzq0ESzJi+znA/Pm8D + nvApAHg3/rEcNS8c5LgFFRzDfcs9fxGSSkL1JrELzoYul1Q13XejgbMwgbAwDgYD + VR0PAQH/BAQDAgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNV + HRMBAf8EAjAAMB0GA1UdDgQWBBR+ma+yZfo092FKIM4F3yhEY8jgDDAfBgNVHSME + GDAWgBRKiCg5+YdTaQ+5gJmvt2QcDkQ6KjAxBgNVHREEKjAohiZzcGlmZmU6Ly9l + eGFtcGxlLm9yZy90ZWt0b24vY29udHJvbGxlcjAKBggqhkjOPQQDAgNIADBFAiEA + 8xVWrQr8+i6yMLDm9IUjtvTbz9ofjSsWL6c/+rxmmRYCIBTiJ/HW7di3inSfxwqK + 5DKyPrKoR8sq8Ne7flkhgbkg + -----END CERTIFICATE----- + tekton.dev/status-hash: 76692c9dcd362f8a6e4bda8ccb4c0937ad16b0d23149ae256049433192892511 + tekton.dev/status-hash-sig: MEQCIFv2bW0k4g0Azx+qaeZjUulPD8Ma3uCUn0tXQuuR1FaEAiBHQwN4XobOXmC2nddYm04AZ74YubUyNl49/vnbnR/HcQ== + completionTime: "2022-03-04T19:11:22Z" + conditions: + - lastTransitionTime: "2022-03-04T19:11:22Z" + message: All Steps have completed executing + reason: Succeeded + status: "True" + type: Succeeded + - lastTransitionTime: "2022-03-04T19:11:22Z" + message: Spire verified + reason: TaskRunResultsVerified + status: "True" + type: SignedResultsVerified + podName: non-falsifiable-provenance-pod + startTime: "2022-03-04T19:10:46Z" + steps: + ... + +``` + +## How is the status being verified + +The signature are being verified by the Tekton controller, the process of verification is as follows: + +- Verify status-hash fields + - verify `tekton.dev/status-hash` content against its associated `tekton.dev/status-hash-sig` field. If status hash does + not match invalidate the `tekton.dev/verified = no` annotation will be added + +## Further Details + +To learn more about SPIRE attestations, check out the [TEP](https://github.com/tektoncd/community/blob/main/teps/0089-nonfalsifiable-provenance-support.md). diff --git a/pkg/apis/pipeline/v1/taskrun_types.go b/pkg/apis/pipeline/v1/taskrun_types.go index fedf6e33b2b..7098dcba97b 100644 --- a/pkg/apis/pipeline/v1/taskrun_types.go +++ b/pkg/apis/pipeline/v1/taskrun_types.go @@ -131,6 +131,19 @@ type TaskRunStatus struct { // reasons that emerge from underlying resources are not included here type TaskRunReason string +// TaskRunConditionType is an enum used to store TaskRun custom +// conditions such as one used in spire results verification +type TaskRunConditionType string + +const ( + // TaskRunConditionResultsVerified is a Condition Type that indicates that the results were verified by spire + TaskRunConditionResultsVerified TaskRunConditionType = "SignedResultsVerified" +) + +func (t TaskRunConditionType) String() string { + return string(t) +} + const ( // TaskRunReasonStarted is the reason set when the TaskRun has just started TaskRunReasonStarted TaskRunReason = "Started" @@ -155,6 +168,12 @@ const ( TaskRunReasonResultLargerThanAllowedLimit TaskRunReason = "TaskRunResultLargerThanAllowedLimit" // TaskRunReasonStopSidecarFailed indicates that the sidecar is not properly stopped. TaskRunReasonStopSidecarFailed = "TaskRunStopSidecarFailed" + // TaskRunReasonResultsVerified is the reason set when the TaskRun results are verified by spire + TaskRunReasonResultsVerified TaskRunReason = "TaskRunResultsVerified" + // TaskRunReasonsResultsVerificationFailed is the reason set when the TaskRun results are failed to verify by spire + TaskRunReasonsResultsVerificationFailed TaskRunReason = "TaskRunResultsVerificationFailed" + // AwaitingTaskRunResults is the reason set when waiting upon `TaskRun` results and signatures to verify + AwaitingTaskRunResults TaskRunReason = "AwaitingTaskRunResults" ) func (t TaskRunReason) String() string { @@ -434,3 +453,13 @@ func (tr *TaskRun) HasVolumeClaimTemplate() bool { } return false } + +// IsTaskRunResultVerified returns true if the TaskRun's results have been validated by spire. +func (tr *TaskRun) IsTaskRunResultVerified() bool { + return tr.Status.GetCondition(apis.ConditionType(TaskRunConditionResultsVerified.String())).IsTrue() +} + +// IsTaskRunResultDone returns true if the TaskRun's results are available for verification +func (tr *TaskRun) IsTaskRunResultDone() bool { + return !tr.Status.GetCondition(apis.ConditionType(TaskRunConditionResultsVerified.String())).IsUnknown() +} diff --git a/pkg/entrypoint/entrypointer_test.go b/pkg/entrypoint/entrypointer_test.go index f451593f839..32489c83c97 100644 --- a/pkg/entrypoint/entrypointer_test.go +++ b/pkg/entrypoint/entrypointer_test.go @@ -31,6 +31,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/tektoncd/pipeline/pkg/apis/config" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" "github.com/tektoncd/pipeline/pkg/result" "github.com/tektoncd/pipeline/pkg/spire" @@ -697,14 +698,14 @@ func createTmpDir(t *testing.T, name string) string { return tmpDir } -func getMockSpireClient(ctx context.Context) (spire.EntrypointerAPIClient, spire.ControllerAPIClient, *v1beta1.TaskRun) { - tr := &v1beta1.TaskRun{ +func getMockSpireClient(ctx context.Context) (spire.EntrypointerAPIClient, spire.ControllerAPIClient, *v1.TaskRun) { + tr := &v1.TaskRun{ ObjectMeta: metav1.ObjectMeta{ Name: "taskrun-example", Namespace: "foo", }, - Spec: v1beta1.TaskRunSpec{ - TaskRef: &v1beta1.TaskRef{ + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{ Name: "taskname", APIVersion: "a1", }, diff --git a/pkg/pod/status.go b/pkg/pod/status.go index 9bbc5598ff2..b049910cfb5 100644 --- a/pkg/pod/status.go +++ b/pkg/pod/status.go @@ -29,6 +29,7 @@ import ( "github.com/tektoncd/pipeline/pkg/apis/config" "github.com/tektoncd/pipeline/pkg/apis/pipeline" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" "github.com/tektoncd/pipeline/pkg/result" "github.com/tektoncd/pipeline/pkg/spire" "github.com/tektoncd/pipeline/pkg/termination" @@ -165,10 +166,10 @@ func MakeTaskRunStatus(ctx context.Context, logger *zap.SugaredLogger, tr v1.Tas } func setTaskRunStatusBasedOnSpireVerification(ctx context.Context, logger *zap.SugaredLogger, tr *v1.TaskRun, trs *v1.TaskRunStatus, - filteredResults []v1beta1.PipelineResourceResult, spireAPI spire.ControllerAPIClient) { + filteredResults []result.RunResult, spireAPI spire.ControllerAPIClient) { if tr.IsSuccessful() && spireAPI != nil && ((tr.Status.TaskSpec != nil && len(tr.Status.TaskSpec.Results) >= 1) || len(filteredResults) >= 1) { - logger.Info("validating signed results with spire: ", trs.TaskRunResults) + logger.Info("validating signed results with spire: ", trs.Results) if err := spireAPI.VerifyTaskRunResults(ctx, filteredResults, tr); err != nil { logger.Errorf("failed to verify signed results with spire: %w", err) markStatusSignedResultsFailure(trs, err.Error()) @@ -238,7 +239,7 @@ func setTaskRunStatusBasedOnStepStatus(ctx context.Context, logger *zap.SugaredL taskResults, filteredResults := filterResults(results, specResults, spireEnabled) if tr.IsDone() { - trs.Results = append(trs.TaskRunResults, taskResults...) + trs.Results = append(trs.Results, taskResults...) if spireEnabled { setTaskRunStatusBasedOnSpireVerification(ctx, logger, tr, trs, filteredResults, spireAPI) } @@ -641,31 +642,31 @@ func markStatusSuccess(trs *v1.TaskRunStatus) { } // markStatusResultsVerified sets taskrun status to verified -func markStatusSignedResultsVerified(trs *v1beta1.TaskRunStatus) { +func markStatusSignedResultsVerified(trs *v1.TaskRunStatus) { trs.SetCondition(&apis.Condition{ Type: apis.ConditionType(v1beta1.TaskRunConditionResultsVerified.String()), Status: corev1.ConditionTrue, - Reason: v1beta1.TaskRunReasonResultsVerified.String(), + Reason: v1.TaskRunReasonResultsVerified.String(), Message: "Successfully verified all spire signed taskrun results", }) } // markStatusFailure sets taskrun status to failure with specified reason -func markStatusSignedResultsFailure(trs *v1beta1.TaskRunStatus, message string) { +func markStatusSignedResultsFailure(trs *v1.TaskRunStatus, message string) { trs.SetCondition(&apis.Condition{ - Type: apis.ConditionType(v1beta1.TaskRunConditionResultsVerified.String()), + Type: apis.ConditionType(v1.TaskRunConditionResultsVerified.String()), Status: corev1.ConditionFalse, - Reason: v1beta1.TaskRunReasonsResultsVerificationFailed.String(), + Reason: v1.TaskRunReasonsResultsVerificationFailed.String(), Message: message, }) } // markStatusRunning sets taskrun status to running -func markStatusSignedResultsRunning(trs *v1beta1.TaskRunStatus) { +func markStatusSignedResultsRunning(trs *v1.TaskRunStatus) { trs.SetCondition(&apis.Condition{ - Type: apis.ConditionType(v1beta1.TaskRunConditionResultsVerified.String()), + Type: apis.ConditionType(v1.TaskRunConditionResultsVerified.String()), Status: corev1.ConditionUnknown, - Reason: v1beta1.AwaitingTaskRunResults.String(), + Reason: v1.AwaitingTaskRunResults.String(), Message: "Waiting upon TaskRun results and signatures to verify", }) } diff --git a/pkg/pod/status_test.go b/pkg/pod/status_test.go index b01a8128cc1..98c0190ce0a 100644 --- a/pkg/pod/status_test.go +++ b/pkg/pod/status_test.go @@ -232,9 +232,9 @@ func TestMakeTaskRunStatusVerify(t *testing.T) { statusSRVUnknown := func() duckv1.Status { status := statusRunning() status.Conditions = append(status.Conditions, apis.Condition{ - Type: apis.ConditionType(v1beta1.TaskRunConditionResultsVerified.String()), + Type: apis.ConditionType(v1.TaskRunConditionResultsVerified.String()), Status: corev1.ConditionUnknown, - Reason: v1beta1.AwaitingTaskRunResults.String(), + Reason: v1.AwaitingTaskRunResults.String(), Message: "Waiting upon TaskRun results and signatures to verify", }) return status @@ -242,9 +242,9 @@ func TestMakeTaskRunStatusVerify(t *testing.T) { statusSRVVerified := func() duckv1.Status { status := statusSuccess() status.Conditions = append(status.Conditions, apis.Condition{ - Type: apis.ConditionType(v1beta1.TaskRunConditionResultsVerified.String()), + Type: apis.ConditionType(v1.TaskRunConditionResultsVerified.String()), Status: corev1.ConditionTrue, - Reason: v1beta1.TaskRunReasonResultsVerified.String(), + Reason: v1.TaskRunReasonResultsVerified.String(), Message: "Successfully verified all spire signed taskrun results", }) return status @@ -252,9 +252,9 @@ func TestMakeTaskRunStatusVerify(t *testing.T) { statusSRVUnverified := func() duckv1.Status { status := statusSuccess() status.Conditions = append(status.Conditions, apis.Condition{ - Type: apis.ConditionType(v1beta1.TaskRunConditionResultsVerified.String()), + Type: apis.ConditionType(v1.TaskRunConditionResultsVerified.String()), Status: corev1.ConditionFalse, - Reason: v1beta1.TaskRunReasonsResultsVerificationFailed.String(), + Reason: v1.TaskRunReasonsResultsVerificationFailed.String(), Message: "", }) return status @@ -262,20 +262,20 @@ func TestMakeTaskRunStatusVerify(t *testing.T) { for _, c := range []struct { desc string specifyTaskRunResult bool - resultOut []v1beta1.PipelineResourceResult + resultOut []result.RunResult podStatus corev1.PodStatus pod corev1.Pod - want v1beta1.TaskRunStatus + want v1.TaskRunStatus }{{ // test awaiting results desc: "running pod awaiting results", podStatus: corev1.PodStatus{}, - want: v1beta1.TaskRunStatus{ + want: v1.TaskRunStatus{ Status: statusSRVUnknown(), - TaskRunStatusFields: v1beta1.TaskRunStatusFields{ - Steps: []v1beta1.StepState{}, - Sidecars: []v1beta1.SidecarState{}, + TaskRunStatusFields: v1.TaskRunStatusFields{ + Steps: []v1.StepState{}, + Sidecars: []v1.SidecarState{}, }, }, }, { @@ -291,22 +291,22 @@ func TestMakeTaskRunStatusVerify(t *testing.T) { }, }}, }, - want: v1beta1.TaskRunStatus{ + want: v1.TaskRunStatus{ Status: statusSRVUnverified(), - TaskRunStatusFields: v1beta1.TaskRunStatusFields{ - Steps: []v1beta1.StepState{{ + TaskRunStatusFields: v1.TaskRunStatusFields{ + Steps: []v1.StepState{{ ContainerState: corev1.ContainerState{ Terminated: &corev1.ContainerStateTerminated{ Message: `[{"key":"digest","value":"sha256:1234","type":1}]`, }}, - Name: "bar", - ContainerName: "step-bar", + Name: "bar", + Container: "step-bar", }}, - Sidecars: []v1beta1.SidecarState{}, - TaskRunResults: []v1beta1.TaskRunResult{{ + Sidecars: []v1.SidecarState{}, + Results: []v1.TaskRunResult{{ Name: "digest", - Type: v1beta1.ResultsTypeString, - Value: *v1beta1.NewStructuredValues("sha256:1234"), + Type: v1.ResultsTypeString, + Value: *v1.NewStructuredValues("sha256:1234"), }}, // We don't actually care about the time, just that it's not nil CompletionTime: &metav1.Time{Time: time.Now()}, @@ -332,22 +332,22 @@ func TestMakeTaskRunStatusVerify(t *testing.T) { }, }}, }, - want: v1beta1.TaskRunStatus{ + want: v1.TaskRunStatus{ Status: statusSRVVerified(), - TaskRunStatusFields: v1beta1.TaskRunStatusFields{ - Steps: []v1beta1.StepState{{ + TaskRunStatusFields: v1.TaskRunStatusFields{ + Steps: []v1.StepState{{ ContainerState: corev1.ContainerState{ Terminated: &corev1.ContainerStateTerminated{ Message: `to be overridden by signing`, }}, - Name: "bar", - ContainerName: "step-bar", + Name: "bar", + Container: "step-bar", }}, - Sidecars: []v1beta1.SidecarState{}, - TaskRunResults: []v1beta1.TaskRunResult{{ + Sidecars: []v1.SidecarState{}, + Results: []v1.TaskRunResult{{ Name: "resultName", - Type: v1beta1.ResultsTypeString, - Value: *v1beta1.NewStructuredValues("resultValue"), + Type: v1.ResultsTypeString, + Value: *v1.NewStructuredValues("resultValue"), }}, // We don't actually care about the time, just that it's not nil CompletionTime: &metav1.Time{Time: time.Now()}, @@ -373,22 +373,22 @@ func TestMakeTaskRunStatusVerify(t *testing.T) { }, }}, }, - want: v1beta1.TaskRunStatus{ + want: v1.TaskRunStatus{ Status: statusSRVVerified(), - TaskRunStatusFields: v1beta1.TaskRunStatusFields{ - Steps: []v1beta1.StepState{{ + TaskRunStatusFields: v1.TaskRunStatusFields{ + Steps: []v1.StepState{{ ContainerState: corev1.ContainerState{ Terminated: &corev1.ContainerStateTerminated{ Message: `to be overridden by signing`, }}, - Name: "bar", - ContainerName: "step-bar", + Name: "bar", + Container: "step-bar", }}, - Sidecars: []v1beta1.SidecarState{}, - TaskRunResults: []v1beta1.TaskRunResult{{ + Sidecars: []v1.SidecarState{}, + Results: []v1.TaskRunResult{{ Name: "resultName", - Type: v1beta1.ResultsTypeArray, - Value: *v1beta1.NewStructuredValues("hello", "world"), + Type: v1.ResultsTypeArray, + Value: *v1.NewStructuredValues("hello", "world"), }}, // We don't actually care about the time, just that it's not nil CompletionTime: &metav1.Time{Time: time.Now()}, @@ -396,7 +396,7 @@ func TestMakeTaskRunStatusVerify(t *testing.T) { }, }, { desc: "test result with no result with signed termination message", - resultOut: []v1beta1.PipelineResourceResult{}, + resultOut: []result.RunResult{}, podStatus: corev1.PodStatus{ Phase: corev1.PodSucceeded, ContainerStatuses: []corev1.ContainerStatus{{ @@ -408,18 +408,18 @@ func TestMakeTaskRunStatusVerify(t *testing.T) { }, }}, }, - want: v1beta1.TaskRunStatus{ + want: v1.TaskRunStatus{ Status: statusSRVVerified(), - TaskRunStatusFields: v1beta1.TaskRunStatusFields{ - Steps: []v1beta1.StepState{{ + TaskRunStatusFields: v1.TaskRunStatusFields{ + Steps: []v1.StepState{{ ContainerState: corev1.ContainerState{ Terminated: &corev1.ContainerStateTerminated{ Message: `to be overridden by signing`, }}, - Name: "bar", - ContainerName: "step-bar", + Name: "bar", + Container: "step-bar", }}, - Sidecars: []v1beta1.SidecarState{}, + Sidecars: []v1.SidecarState{}, // We don't actually care about the time, just that it's not nil CompletionTime: &metav1.Time{Time: time.Now()}, }, @@ -437,18 +437,18 @@ func TestMakeTaskRunStatusVerify(t *testing.T) { }, }}, }, - want: v1beta1.TaskRunStatus{ + want: v1.TaskRunStatus{ Status: statusSRVVerified(), - TaskRunStatusFields: v1beta1.TaskRunStatusFields{ - Steps: []v1beta1.StepState{{ + TaskRunStatusFields: v1.TaskRunStatusFields{ + Steps: []v1.StepState{{ ContainerState: corev1.ContainerState{ Terminated: &corev1.ContainerStateTerminated{ Message: "[]", }}, - Name: "bar", - ContainerName: "step-bar", + Name: "bar", + Container: "step-bar", }}, - Sidecars: []v1beta1.SidecarState{}, + Sidecars: []v1.SidecarState{}, // We don't actually care about the time, just that it's not nil CompletionTime: &metav1.Time{Time: time.Now()}, }, @@ -467,18 +467,18 @@ func TestMakeTaskRunStatusVerify(t *testing.T) { }, }}, }, - want: v1beta1.TaskRunStatus{ + want: v1.TaskRunStatus{ Status: statusSRVUnverified(), - TaskRunStatusFields: v1beta1.TaskRunStatusFields{ - Steps: []v1beta1.StepState{{ + TaskRunStatusFields: v1.TaskRunStatusFields{ + Steps: []v1.StepState{{ ContainerState: corev1.ContainerState{ Terminated: &corev1.ContainerStateTerminated{ Message: "[]", }}, - Name: "bar", - ContainerName: "step-bar", + Name: "bar", + Container: "step-bar", }}, - Sidecars: []v1beta1.SidecarState{}, + Sidecars: []v1.SidecarState{}, // We don't actually care about the time, just that it's not nil CompletionTime: &metav1.Time{Time: time.Now()}, }, @@ -503,21 +503,21 @@ func TestMakeTaskRunStatusVerify(t *testing.T) { } startTime := time.Date(2010, 1, 1, 1, 1, 1, 1, time.UTC) - tr := v1beta1.TaskRun{ + tr := v1.TaskRun{ ObjectMeta: metav1.ObjectMeta{ Name: "task-run", Namespace: "foo", }, - Status: v1beta1.TaskRunStatus{ - TaskRunStatusFields: v1beta1.TaskRunStatusFields{ + Status: v1.TaskRunStatus{ + TaskRunStatusFields: v1.TaskRunStatusFields{ StartTime: &metav1.Time{Time: startTime}, }, }, } if c.specifyTaskRunResult { // Specify result - tr.Status.TaskSpec = &v1beta1.TaskSpec{ - Results: []v1beta1.TaskResult{{ + tr.Status.TaskSpec = &v1.TaskSpec{ + Results: []v1.TaskResult{{ Name: "some-task-result", }}, } @@ -550,7 +550,7 @@ func TestMakeTaskRunStatusVerify(t *testing.T) { logger, _ := logging.NewLogger("", "status") kubeclient := fakek8s.NewSimpleClientset() - got, err := MakeTaskRunStatus(ctx, logger, tr, &c.pod, kubeclient, &v1beta1.TaskSpec{}, sc) + got, err := MakeTaskRunStatus(ctx, logger, tr, &c.pod, kubeclient, &v1.TaskSpec{}, sc) if err != nil { t.Errorf("MakeTaskRunResult: %s", err) } diff --git a/pkg/reconciler/taskrun/taskrun_test.go b/pkg/reconciler/taskrun/taskrun_test.go index 224a06cbe63..9123295b986 100644 --- a/pkg/reconciler/taskrun/taskrun_test.go +++ b/pkg/reconciler/taskrun/taskrun_test.go @@ -1479,7 +1479,7 @@ spec: t.Errorf("expected no error. Got error %v", err) } - client := testAssets.Clients.ResolutionRequests.ResolutionV1().ResolutionRequests("default") + client := testAssets.Clients.ResolutionRequests.ResolutionV1beta1().ResolutionRequests("default") resolutionrequests, err := client.List(testAssets.Ctx, metav1.ListOptions{}) if err != nil { t.Fatalf("unexpected error listing resource requests: %v", err) @@ -1579,7 +1579,7 @@ spec: t.Errorf("expected no error. Got error %v", err) } - client := testAssets.Clients.ResolutionRequests.ResolutionV1().ResolutionRequests("default") + client := testAssets.Clients.ResolutionRequests.ResolutionV1beta1().ResolutionRequests("default") resolutionrequests, err := client.List(testAssets.Ctx, metav1.ListOptions{}) if err != nil { t.Fatalf("unexpected error listing resource requests: %v", err) diff --git a/pkg/spire/controller.go b/pkg/spire/controller.go index e26275491ff..538f70fd8ff 100644 --- a/pkg/spire/controller.go +++ b/pkg/spire/controller.go @@ -28,7 +28,7 @@ import ( entryv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/entry/v1" spiffetypes "github.com/spiffe/spire-api-sdk/proto/spire/api/types" "github.com/tektoncd/pipeline/pkg/apis/config" - "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" spireconfig "github.com/tektoncd/pipeline/pkg/spire/config" "go.uber.org/zap" "google.golang.org/grpc" @@ -173,7 +173,7 @@ func (sc *spireControllerAPIClient) nodeEntry(nodeName string) *spiffetypes.Entr } } -func (sc *spireControllerAPIClient) workloadEntry(tr *v1beta1.TaskRun, pod *corev1.Pod, expiry int64) *spiffetypes.Entry { +func (sc *spireControllerAPIClient) workloadEntry(tr *v1.TaskRun, pod *corev1.Pod, expiry int64) *spiffetypes.Entry { // Note: We can potentially add attestation on the container images as well since // the information is available here. selectors := []*spiffetypes.Selector{ @@ -202,7 +202,7 @@ func (sc *spireControllerAPIClient) workloadEntry(tr *v1beta1.TaskRun, pod *core } // ttl is the TTL for the SPIRE entry in seconds, not the SVID TTL -func (sc *spireControllerAPIClient) CreateEntries(ctx context.Context, tr *v1beta1.TaskRun, pod *corev1.Pod, ttl time.Duration) error { +func (sc *spireControllerAPIClient) CreateEntries(ctx context.Context, tr *v1.TaskRun, pod *corev1.Pod, ttl time.Duration) error { err := sc.setupClient(ctx) if err != nil { return err @@ -244,7 +244,7 @@ func (sc *spireControllerAPIClient) CreateEntries(ctx context.Context, tr *v1bet return nil } -func (sc *spireControllerAPIClient) getEntries(ctx context.Context, tr *v1beta1.TaskRun, pod *corev1.Pod) ([]*spiffetypes.Entry, error) { +func (sc *spireControllerAPIClient) getEntries(ctx context.Context, tr *v1.TaskRun, pod *corev1.Pod) ([]*spiffetypes.Entry, error) { req := &entryv1.ListEntriesRequest{ Filter: &entryv1.ListEntriesRequest_Filter{ BySpiffeId: &spiffetypes.SPIFFEID{ @@ -273,7 +273,7 @@ func (sc *spireControllerAPIClient) getEntries(ctx context.Context, tr *v1beta1. return entries, nil } -func (sc *spireControllerAPIClient) DeleteEntry(ctx context.Context, tr *v1beta1.TaskRun, pod *corev1.Pod) error { +func (sc *spireControllerAPIClient) DeleteEntry(ctx context.Context, tr *v1.TaskRun, pod *corev1.Pod) error { entries, err := sc.getEntries(ctx, tr, pod) if err != nil { return err diff --git a/pkg/spire/sign.go b/pkg/spire/sign.go index 798eefbe805..9a4bc10d1f2 100644 --- a/pkg/spire/sign.go +++ b/pkg/spire/sign.go @@ -27,7 +27,7 @@ import ( "strings" "github.com/spiffe/go-spiffe/v2/svid/x509svid" - "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/result" ) @@ -116,7 +116,7 @@ func getManifest(results []result.RunResult) string { } // AppendStatusInternalAnnotation creates the status annotations which are used by the controller to verify the status hash -func (sc *spireControllerAPIClient) AppendStatusInternalAnnotation(ctx context.Context, tr *v1beta1.TaskRun) error { +func (sc *spireControllerAPIClient) AppendStatusInternalAnnotation(ctx context.Context, tr *v1.TaskRun) error { err := sc.setupClient(ctx) if err != nil { return err diff --git a/pkg/spire/spire.go b/pkg/spire/spire.go index 7ad3942aaa7..09b92b208f4 100644 --- a/pkg/spire/spire.go +++ b/pkg/spire/spire.go @@ -28,7 +28,7 @@ import ( "context" "time" - "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/result" spireconfig "github.com/tektoncd/pipeline/pkg/spire/config" "go.uber.org/zap" @@ -59,13 +59,13 @@ const ( // ControllerAPIClient interface maps to the spire controller API to interact with spire type ControllerAPIClient interface { - AppendStatusInternalAnnotation(ctx context.Context, tr *v1beta1.TaskRun) error - CheckSpireVerifiedFlag(tr *v1beta1.TaskRun) bool + AppendStatusInternalAnnotation(ctx context.Context, tr *v1.TaskRun) error + CheckSpireVerifiedFlag(tr *v1.TaskRun) bool Close() error - CreateEntries(ctx context.Context, tr *v1beta1.TaskRun, pod *corev1.Pod, ttl time.Duration) error - DeleteEntry(ctx context.Context, tr *v1beta1.TaskRun, pod *corev1.Pod) error - VerifyStatusInternalAnnotation(ctx context.Context, tr *v1beta1.TaskRun, logger *zap.SugaredLogger) error - VerifyTaskRunResults(ctx context.Context, prs []result.RunResult, tr *v1beta1.TaskRun) error + CreateEntries(ctx context.Context, tr *v1.TaskRun, pod *corev1.Pod, ttl time.Duration) error + DeleteEntry(ctx context.Context, tr *v1.TaskRun, pod *corev1.Pod) error + VerifyStatusInternalAnnotation(ctx context.Context, tr *v1.TaskRun, logger *zap.SugaredLogger) error + VerifyTaskRunResults(ctx context.Context, prs []result.RunResult, tr *v1.TaskRun) error SetConfig(c spireconfig.SpireConfig) } diff --git a/pkg/spire/spire_mock.go b/pkg/spire/spire_mock.go index 6c771584819..46ab9a05264 100644 --- a/pkg/spire/spire_mock.go +++ b/pkg/spire/spire_mock.go @@ -24,7 +24,7 @@ import ( "time" "github.com/pkg/errors" - "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/result" spireconfig "github.com/tektoncd/pipeline/pkg/spire/config" "go.uber.org/zap" @@ -67,16 +67,16 @@ type MockClient struct { VerifyAlwaysReturns *bool // VerifyStatusInternalAnnotationOverride contains the function to overwrite a call to VerifyStatusInternalAnnotation - VerifyStatusInternalAnnotationOverride func(ctx context.Context, tr *v1beta1.TaskRun, logger *zap.SugaredLogger) error + VerifyStatusInternalAnnotationOverride func(ctx context.Context, tr *v1.TaskRun, logger *zap.SugaredLogger) error // VerifyTaskRunResultsOverride contains the function to overwrite a call to VerifyTaskRunResults - VerifyTaskRunResultsOverride func(ctx context.Context, prs []result.RunResult, tr *v1beta1.TaskRun) error + VerifyTaskRunResultsOverride func(ctx context.Context, prs []result.RunResult, tr *v1.TaskRun) error // AppendStatusInternalAnnotationOverride contains the function to overwrite a call to AppendStatusInternalAnnotation - AppendStatusInternalAnnotationOverride func(ctx context.Context, tr *v1beta1.TaskRun) error + AppendStatusInternalAnnotationOverride func(ctx context.Context, tr *v1.TaskRun) error // CheckSpireVerifiedFlagOverride contains the function to overwrite a call to CheckSpireVerifiedFlag - CheckSpireVerifiedFlagOverride func(tr *v1beta1.TaskRun) bool + CheckSpireVerifiedFlagOverride func(tr *v1.TaskRun) bool // SignOverride contains the function to overwrite a call to Sign SignOverride func(ctx context.Context, results []result.RunResult) ([]result.RunResult, error) @@ -96,12 +96,12 @@ func (sc *MockClient) mockVerify(content, sig, signedBy string) bool { } // GetIdentity get the taskrun namespace and taskrun name that is used for signing and verifying in mocked spire -func (*MockClient) GetIdentity(tr *v1beta1.TaskRun) string { +func (*MockClient) GetIdentity(tr *v1.TaskRun) string { return fmt.Sprintf("/ns/%v/taskrun/%v", tr.Namespace, tr.Name) } // AppendStatusInternalAnnotation creates the status annotations which are used by the controller to verify the status hash -func (sc *MockClient) AppendStatusInternalAnnotation(ctx context.Context, tr *v1beta1.TaskRun) error { +func (sc *MockClient) AppendStatusInternalAnnotation(ctx context.Context, tr *v1.TaskRun) error { if sc.AppendStatusInternalAnnotationOverride != nil { return sc.AppendStatusInternalAnnotationOverride(ctx, tr) } @@ -121,7 +121,7 @@ func (sc *MockClient) AppendStatusInternalAnnotation(ctx context.Context, tr *v1 } // CheckSpireVerifiedFlag checks if the verified status annotation is set which would result in spire verification failed -func (sc *MockClient) CheckSpireVerifiedFlag(tr *v1beta1.TaskRun) bool { +func (sc *MockClient) CheckSpireVerifiedFlag(tr *v1.TaskRun) bool { if sc.CheckSpireVerifiedFlagOverride != nil { return sc.CheckSpireVerifiedFlagOverride(tr) } @@ -131,7 +131,7 @@ func (sc *MockClient) CheckSpireVerifiedFlag(tr *v1beta1.TaskRun) bool { } // CreateEntries adds entries to the dictionary of entries that mock the SPIRE server datastore -func (sc *MockClient) CreateEntries(ctx context.Context, tr *v1beta1.TaskRun, pod *corev1.Pod, ttl time.Duration) error { +func (sc *MockClient) CreateEntries(ctx context.Context, tr *v1.TaskRun, pod *corev1.Pod, ttl time.Duration) error { id := fmt.Sprintf("/ns/%v/taskrun/%v", tr.Namespace, tr.Name) if sc.Entries == nil { sc.Entries = map[string]bool{} @@ -141,7 +141,7 @@ func (sc *MockClient) CreateEntries(ctx context.Context, tr *v1beta1.TaskRun, po } // DeleteEntry removes the entry from the dictionary of entries that mock the SPIRE server datastore -func (sc *MockClient) DeleteEntry(ctx context.Context, tr *v1beta1.TaskRun, pod *corev1.Pod) error { +func (sc *MockClient) DeleteEntry(ctx context.Context, tr *v1.TaskRun, pod *corev1.Pod) error { id := fmt.Sprintf("/ns/%v/taskrun/%v", tr.Namespace, tr.Name) if sc.Entries != nil { delete(sc.Entries, id) @@ -150,7 +150,7 @@ func (sc *MockClient) DeleteEntry(ctx context.Context, tr *v1beta1.TaskRun, pod } // VerifyStatusInternalAnnotation checks that the internal status annotations are valid by the mocked spire client -func (sc *MockClient) VerifyStatusInternalAnnotation(ctx context.Context, tr *v1beta1.TaskRun, logger *zap.SugaredLogger) error { +func (sc *MockClient) VerifyStatusInternalAnnotation(ctx context.Context, tr *v1.TaskRun, logger *zap.SugaredLogger) error { if sc.VerifyStatusInternalAnnotationOverride != nil { return sc.VerifyStatusInternalAnnotationOverride(ctx, tr, logger) } @@ -187,7 +187,7 @@ func (sc *MockClient) VerifyStatusInternalAnnotation(ctx context.Context, tr *v1 } // VerifyTaskRunResults checks that all the TaskRun results are valid by the mocked spire client -func (sc *MockClient) VerifyTaskRunResults(ctx context.Context, prs []result.RunResult, tr *v1beta1.TaskRun) error { +func (sc *MockClient) VerifyTaskRunResults(ctx context.Context, prs []result.RunResult, tr *v1.TaskRun) error { if sc.VerifyTaskRunResultsOverride != nil { return sc.VerifyTaskRunResultsOverride(ctx, prs, tr) } diff --git a/pkg/spire/spire_mock_test.go b/pkg/spire/spire_mock_test.go index 73d9dc7b1d9..81fc29577c0 100644 --- a/pkg/spire/spire_mock_test.go +++ b/pkg/spire/spire_mock_test.go @@ -23,7 +23,7 @@ import ( "github.com/google/uuid" "github.com/tektoncd/pipeline/pkg/apis/config" - "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/result" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -55,7 +55,7 @@ func TestMock_TaskRunSign(t *testing.T) { } } -// test CheckSpireVerifiedFlag(tr *v1beta1.TaskRun) bool +// test CheckSpireVerifiedFlag(tr *v1.TaskRun) bool func TestMock_CheckSpireVerifiedFlag(t *testing.T) { spireMockClient := &MockClient{} var ( @@ -95,7 +95,7 @@ func TestMock_CheckHashSimilarities(t *testing.T) { tr2c.Status.Status.Annotations = map[string]string{"new": "value"} - signTrs := []*v1beta1.TaskRun{tr1, tr1c, tr2, tr2c} + signTrs := []*v1.TaskRun{tr1, tr1c, tr2, tr2c} for _, tr := range signTrs { err := cc.AppendStatusInternalAnnotation(ctx, tr) @@ -220,7 +220,7 @@ func TestMock_CheckTamper(t *testing.T) { } if tt.modifyStatus { - tr.Status.TaskRunStatusFields.Steps = append(tr.Status.TaskRunStatusFields.Steps, v1beta1.StepState{ + tr.Status.TaskRunStatusFields.Steps = append(tr.Status.TaskRunStatusFields.Steps, v1.StepState{ ContainerState: corev1.ContainerState{ Terminated: &corev1.ContainerStateTerminated{ExitCode: int32(54321)}, }}) @@ -545,13 +545,13 @@ func objectMeta(name, ns string) metav1.ObjectMeta { } } -func testTaskRuns() []*v1beta1.TaskRun { - return []*v1beta1.TaskRun{ +func testTaskRuns() []*v1.TaskRun { + return []*v1.TaskRun{ // taskRun 1 { ObjectMeta: objectMeta("taskrun-example", "foo"), - Spec: v1beta1.TaskRunSpec{ - TaskRef: &v1beta1.TaskRef{ + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{ Name: "taskname", APIVersion: "a1", }, @@ -561,14 +561,14 @@ func testTaskRuns() []*v1beta1.TaskRun { // taskRun 2 { ObjectMeta: objectMeta("taskrun-example-populated", "foo"), - Spec: v1beta1.TaskRunSpec{ - TaskRef: &v1beta1.TaskRef{Name: "unit-test-task"}, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "unit-test-task"}, ServiceAccountName: "test-sa", Timeout: &metav1.Duration{Duration: config.DefaultTimeoutMinutes * time.Minute}, }, - Status: v1beta1.TaskRunStatus{ - TaskRunStatusFields: v1beta1.TaskRunStatusFields{ - Steps: []v1beta1.StepState{{ + Status: v1.TaskRunStatus{ + TaskRunStatusFields: v1.TaskRunStatusFields{ + Steps: []v1.StepState{{ ContainerState: corev1.ContainerState{ Terminated: &corev1.ContainerStateTerminated{ExitCode: int32(0)}, }, @@ -579,12 +579,12 @@ func testTaskRuns() []*v1beta1.TaskRun { // taskRun 3 { ObjectMeta: objectMeta("taskrun-example-with-objmeta", "foo"), - Spec: v1beta1.TaskRunSpec{ - TaskRef: &v1beta1.TaskRef{Name: "unit-test-task"}, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "unit-test-task"}, ServiceAccountName: "test-sa", Timeout: &metav1.Duration{Duration: config.DefaultTimeoutMinutes * time.Minute}, }, - Status: v1beta1.TaskRunStatus{ + Status: v1.TaskRunStatus{ Status: duckv1.Status{ Conditions: duckv1.Conditions{ apis.Condition{ @@ -592,8 +592,8 @@ func testTaskRuns() []*v1beta1.TaskRun { }, }, }, - TaskRunStatusFields: v1beta1.TaskRunStatusFields{ - Steps: []v1beta1.StepState{{ + TaskRunStatusFields: v1.TaskRunStatusFields{ + Steps: []v1.StepState{{ ContainerState: corev1.ContainerState{ Terminated: &corev1.ContainerStateTerminated{ExitCode: int32(0)}, }, @@ -603,12 +603,12 @@ func testTaskRuns() []*v1beta1.TaskRun { }, { ObjectMeta: objectMeta("taskrun-example-with-objmeta-annotations", "foo"), - Spec: v1beta1.TaskRunSpec{ - TaskRef: &v1beta1.TaskRef{Name: "unit-test-task"}, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "unit-test-task"}, ServiceAccountName: "test-sa", Timeout: &metav1.Duration{Duration: config.DefaultTimeoutMinutes * time.Minute}, }, - Status: v1beta1.TaskRunStatus{ + Status: v1.TaskRunStatus{ Status: duckv1.Status{ Conditions: duckv1.Conditions{ apis.Condition{ @@ -620,8 +620,8 @@ func testTaskRuns() []*v1beta1.TaskRun { "annotation2": "a2value", }, }, - TaskRunStatusFields: v1beta1.TaskRunStatusFields{ - Steps: []v1beta1.StepState{{ + TaskRunStatusFields: v1.TaskRunStatusFields{ + Steps: []v1.StepState{{ ContainerState: corev1.ContainerState{ Terminated: &corev1.ContainerStateTerminated{ExitCode: int32(0)}, }, @@ -716,11 +716,11 @@ func testRunResults() [][]result.RunResult { } } -func getHash(tr *v1beta1.TaskRun) string { +func getHash(tr *v1.TaskRun) string { return tr.Status.Status.Annotations[TaskRunStatusHashAnnotation] } -func genPodObj(tr *v1beta1.TaskRun, uid string) *corev1.Pod { +func genPodObj(tr *v1.TaskRun, uid string) *corev1.Pod { if uid == "" { uid = uuid.NewString() } diff --git a/pkg/spire/spire_test.go b/pkg/spire/spire_test.go index aa1d94bb97b..bd9ee71ac72 100644 --- a/pkg/spire/spire_test.go +++ b/pkg/spire/spire_test.go @@ -27,7 +27,7 @@ import ( "github.com/spiffe/go-spiffe/v2/spiffeid" "github.com/spiffe/go-spiffe/v2/svid/x509svid" pconf "github.com/tektoncd/pipeline/pkg/apis/config" - "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" ttesting "github.com/tektoncd/pipeline/pkg/reconciler/testing" "github.com/tektoncd/pipeline/pkg/result" "github.com/tektoncd/pipeline/pkg/spire/config" @@ -169,7 +169,7 @@ func TestCheckHashSimilarities(t *testing.T) { tr2c.Status.Status.Annotations = map[string]string{"new": "value"} - signTrs := []*v1beta1.TaskRun{tr1, tr1c, tr2, tr2c} + signTrs := []*v1.TaskRun{tr1, tr1c, tr2, tr2c} for _, tr := range signTrs { err := cc.AppendStatusInternalAnnotation(ctx, tr) @@ -329,7 +329,7 @@ func TestCheckTamper(t *testing.T) { } if tt.modifyStatus { - tr.Status.TaskRunStatusFields.Steps = append(tr.Status.TaskRunStatusFields.Steps, v1beta1.StepState{ + tr.Status.TaskRunStatusFields.Steps = append(tr.Status.TaskRunStatusFields.Steps, v1.StepState{ ContainerState: corev1.ContainerState{ Terminated: &corev1.ContainerStateTerminated{ExitCode: int32(54321)}, }}) @@ -701,6 +701,6 @@ func x509svids(ca *test.CA, ids ...spiffeid.ID) []*x509svid.SVID { return svids } -func taskrunPath(tr *v1beta1.TaskRun) string { +func taskrunPath(tr *v1.TaskRun) string { return fmt.Sprintf("/ns/%v/taskrun/%v", tr.Namespace, tr.Name) } diff --git a/pkg/spire/verify.go b/pkg/spire/verify.go index fab083608fb..2fa046aecfd 100644 --- a/pkg/spire/verify.go +++ b/pkg/spire/verify.go @@ -33,13 +33,13 @@ import ( "github.com/pkg/errors" "github.com/spiffe/go-spiffe/v2/workloadapi" - "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/result" "go.uber.org/zap" ) // VerifyTaskRunResults ensures that the TaskRun results are valid and have not been tampered with -func (sc *spireControllerAPIClient) VerifyTaskRunResults(ctx context.Context, prs []result.RunResult, tr *v1beta1.TaskRun) error { +func (sc *spireControllerAPIClient) VerifyTaskRunResults(ctx context.Context, prs []result.RunResult, tr *v1.TaskRun) error { err := sc.setupClient(ctx) if err != nil { return err @@ -90,7 +90,7 @@ func (sc *spireControllerAPIClient) VerifyTaskRunResults(ctx context.Context, pr } // VerifyStatusInternalAnnotation run multuple verification steps to ensure that the spire status annotations are valid -func (sc *spireControllerAPIClient) VerifyStatusInternalAnnotation(ctx context.Context, tr *v1beta1.TaskRun, logger *zap.SugaredLogger) error { +func (sc *spireControllerAPIClient) VerifyStatusInternalAnnotation(ctx context.Context, tr *v1.TaskRun, logger *zap.SugaredLogger) error { err := sc.setupClient(ctx) if err != nil { return err @@ -143,14 +143,14 @@ func (sc *spireControllerAPIClient) VerifyStatusInternalAnnotation(ctx context.C } // CheckSpireVerifiedFlag checks if the verified status annotation is set which would result in spire verification failed -func (sc *spireControllerAPIClient) CheckSpireVerifiedFlag(tr *v1beta1.TaskRun) bool { +func (sc *spireControllerAPIClient) CheckSpireVerifiedFlag(tr *v1.TaskRun) bool { if _, ok := tr.Status.Annotations[VerifiedAnnotation]; !ok { return true } return false } -func hashTaskrunStatusInternal(tr *v1beta1.TaskRun) (string, error) { +func hashTaskrunStatusInternal(tr *v1.TaskRun) (string, error) { s, err := json.Marshal(tr.Status.TaskRunStatusFields) if err != nil { return "", err @@ -159,7 +159,7 @@ func hashTaskrunStatusInternal(tr *v1beta1.TaskRun) (string, error) { } // CheckStatusInternalAnnotation ensures that the internal status annotation hash and current status hash match -func CheckStatusInternalAnnotation(tr *v1beta1.TaskRun) error { +func CheckStatusInternalAnnotation(tr *v1.TaskRun) error { // get stored hash of status annotations := tr.Status.Annotations hash, ok := annotations[TaskRunStatusHashAnnotation] @@ -219,12 +219,12 @@ func getTrustBundle(ctx context.Context, client *workloadapi.Client) (*x509.Cert return nil, errors.Wrap(err, "trust domain bundle empty") } -func getFullPath(tr *v1beta1.TaskRun) string { +func getFullPath(tr *v1.TaskRun) string { // URI:spiffe://example.org/ns/default/taskrun/cache-image-pipelinerun-r4r22-fetch-from-git return fmt.Sprintf("/ns/%s/taskrun/%s", tr.Namespace, tr.Name) } -func verifyCertURI(cert *x509.Certificate, tr *v1beta1.TaskRun, trustDomain string) error { +func verifyCertURI(cert *x509.Certificate, tr *v1.TaskRun, trustDomain string) error { path := getFullPath(tr) switch { case len(cert.URIs) == 0: @@ -326,27 +326,27 @@ func verifySignature(pub crypto.PublicKey, signature string, value string) error } func getResultValue(result result.RunResult) (string, error) { - aos := v1beta1.ArrayOrString{} - err := aos.UnmarshalJSON([]byte(result.Value)) + pv := v1.ParamValue{} + err := pv.UnmarshalJSON([]byte(result.Value)) valList := []string{} if err != nil { return "", fmt.Errorf("unmarshal error for key: %s", result.Key) } - switch aos.Type { - case v1beta1.ParamTypeString: - return aos.StringVal, nil - case v1beta1.ParamTypeArray: - valList = append(valList, aos.ArrayVal...) + switch pv.Type { + case v1.ParamTypeString: + return pv.StringVal, nil + case v1.ParamTypeArray: + valList = append(valList, pv.ArrayVal...) return strings.Join(valList, ","), nil - case v1beta1.ParamTypeObject: - keys := make([]string, 0, len(aos.ObjectVal)) - for k := range aos.ObjectVal { + case v1.ParamTypeObject: + keys := make([]string, 0, len(pv.ObjectVal)) + for k := range pv.ObjectVal { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { valList = append(valList, k) - valList = append(valList, aos.ObjectVal[k]) + valList = append(valList, pv.ObjectVal[k]) } return strings.Join(valList, ","), nil } diff --git a/test/featureflags.go b/test/featureflags.go index 071a45ee083..bcdef1b8d4b 100644 --- a/test/featureflags.go +++ b/test/featureflags.go @@ -29,6 +29,39 @@ import ( "knative.dev/pkg/system" ) +var spireFeatureGates = map[string]string{ + "enable-nonfalsifiability": "spire", + "enable-api-fields": "alpha", +} + +func isSpireEnabled(ctx context.Context, t *testing.T, c *clients, namespace string) bool { + t.Helper() + isSpireEnabled, _ := hasAllFeatureGates(ctx, t, spireFeatureGates, c, namespace) + return isSpireEnabled +} + +func hasAllFeatureGates(ctx context.Context, t *testing.T, gates map[string]string, c *clients, namespace string) (bool, string) { + t.Helper() + featureFlagsCM, err := c.KubeClient.CoreV1().ConfigMaps(system.Namespace()).Get(ctx, config.GetFeatureFlagsConfigName(), metav1.GetOptions{}) + if err != nil { + t.Fatalf("Failed to get ConfigMap `%s`: %s", config.GetFeatureFlagsConfigName(), err) + } + pairs := []string{} + for name, value := range gates { + actual, ok := featureFlagsCM.Data[name] + if !ok || value != actual { + pairs = append(pairs, fmt.Sprintf("%q is %q, want %s", name, actual, value)) + } + } + if len(pairs) > 0 { + status := fmt.Sprintf( + "Some feature flags in namespace %q not matching %s\nExisting feature flag: %#v\n", + system.Namespace(), strings.Join(pairs, " and "), featureFlagsCM.Data) + return false, status + } + return true, "" +} + // requireAnyGate returns a setup func that will skip the current // test if none of the feature-flags in the given map match // what's in the feature-flags ConfigMap. It will fatally fail diff --git a/test/init_test.go b/test/init_test.go index b95fe444d6d..aaa79c384fc 100644 --- a/test/init_test.go +++ b/test/init_test.go @@ -31,7 +31,9 @@ import ( "testing" "github.com/tektoncd/pipeline/pkg/apis/config" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" "github.com/tektoncd/pipeline/pkg/names" + "github.com/tektoncd/pipeline/pkg/spire" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -315,3 +317,43 @@ func getCRDYaml(ctx context.Context, cs *clients, ns string) ([]byte, error) { return output, nil } + +// Verifies if the taskrun results should not be verified by spire +func spireShouldFailTaskRunResultsVerify(t *testing.T, tr *v1.TaskRun) { + t.Helper() + if tr.IsTaskRunResultVerified() { + t.Errorf("Taskrun `%s` status condition should not be verified as taskrun failed", tr.Name) + } + t.Logf("Taskrun `%s` status results condition verified by spire as false, which is valid", tr.Name) +} + +// Verifies if the taskrun results are verified by spire +func spireShouldPassTaskRunResultsVerify(t *testing.T, tr *v1.TaskRun) { + t.Helper() + if !tr.IsTaskRunResultVerified() { + t.Errorf("Taskrun `%s` status condition not verified. Spire taskrun results verification failure", tr.Name) + } else { + t.Logf("Taskrun `%s` status results condition verified by spire as true, which is valid", tr.Name) + } + t.Logf("Taskrun `%s` status results condition verified by spire as true, which is valid", tr.Name) +} + +// Verifies if the taskrun status annotation does not contain "not-verified" +func spireShouldPassSpireAnnotation(t *testing.T, tr *v1.TaskRun) { + t.Helper() + if _, notVerified := tr.Status.Annotations[spire.VerifiedAnnotation]; notVerified { + t.Errorf("Taskrun `%s` status not verified. Spire annotation tekton.dev/spire-verified = no. Failed spire verification", tr.Name) + } + t.Logf("Taskrun `%s` status spire annotation verified", tr.Name) +} + +// Verifies if the taskrun status annotation does contain "not-verified" +func spireShouldFailSpireAnnotation(t *testing.T, tr *v1.TaskRun) { + t.Helper() + _, notVerified := tr.Status.Annotations[spire.VerifiedAnnotation] + _, hash := tr.Status.Annotations[spire.TaskRunStatusHashAnnotation] + if !notVerified && hash { + t.Errorf("Taskrun `%s` status should be not verified missing spire Annotation tekton.dev/not-verified = yes", tr.Name) + } + t.Logf("Taskrun `%s` status spire annotation not verified, which is valid", tr.Name) +}