Skip to content

Commit 0b46102

Browse files
committed
Add Archivista Storage Backend
Adds the ability to store signed TaskRun and PipelineRun results in Archivista. Archivista currently only supports payloads that are wrapped in a DSSE envelope, so any signatures that are not DSSE envelopes will not be stored.
1 parent 7824cdc commit 0b46102

39 files changed

+4155
-12
lines changed

docs/config.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Supported keys include:
2020
| Key | Description | Supported Values | Default |
2121
| :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------- | :-------- |
2222
| `artifacts.taskrun.format` | The format to store `TaskRun` payloads in. | `in-toto`, `slsa/v1`, `slsa/v2alpha3`, `slsa/v2alpha4` | `in-toto` |
23-
| `artifacts.taskrun.storage` | The storage backend to store `TaskRun` signatures in. Multiple backends can be specified with comma-separated list ("tekton,oci"). To disable the `TaskRun` artifact input an empty string (""). | `tekton`, `oci`, `gcs`, `docdb`, `grafeas` | `tekton` |
23+
| `artifacts.taskrun.storage` | The storage backend to store `TaskRun` signatures in. Multiple backends can be specified with comma-separated list ("tekton,oci"). To disable the `TaskRun` artifact input an empty string (""). | `tekton`, `oci`, `gcs`, `docdb`, `grafeas`, `archivista` | `tekton` |
2424
| `artifacts.taskrun.signer` | The signature backend to sign `TaskRun` payloads with. | `x509`, `kms` | `x509` |
2525

2626
> NOTE:
@@ -34,7 +34,7 @@ Supported keys include:
3434
| Key | Description | Supported Values | Default |
3535
| :--------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------- | :-------- |
3636
| `artifacts.pipelinerun.format` | The format to store `PipelineRun` payloads in. | `in-toto`, `slsa/v1`, `slsa/v2alpha3`, `slsa/v2alpha4` | `in-toto` |
37-
| `artifacts.pipelinerun.storage` | The storage backend to store `PipelineRun` signatures in. Multiple backends can be specified with comma-separated list ("tekton,oci"). To disable the `PipelineRun` artifact input an empty string (""). | `tekton`, `oci`, `gcs`, `docdb`, `grafeas` | `tekton` |
37+
| `artifacts.pipelinerun.storage` | The storage backend to store `PipelineRun` signatures in. Multiple backends can be specified with comma-separated list ("tekton,oci"). To disable the `PipelineRun` artifact input an empty string (""). | `tekton`, `oci`, `gcs`, `docdb`, `grafeas`, `archivista` | `tekton` |
3838
| `artifacts.pipelinerun.signer` | The signature backend to sign `PipelineRun` payloads with. | `x509`, `kms` | `x509` |
3939
| `artifacts.pipelinerun.enable-deep-inspection` | This boolean option will configure whether Chains should inspect child taskruns in order to capture inputs/outputs within a pipelinerun. `"false"` means that Chains only checks pipeline level results, whereas `"true"` means Chains inspects both pipeline level and task level results. | `"true"`, `"false"` | `"false"` |
4040

@@ -73,6 +73,7 @@ Supported keys include:
7373
| `storage.grafeas.projectid` | The project of where grafeas server is located for storing occurrences | | |
7474
| `storage.grafeas.noteid` (optional) | This field will be used as the prefix part of the note name that will be created. The value of this field must be a string without spaces. (See more details [below](#grafeas).) | | |
7575
| `storage.grafeas.notehint` (optional) | This field is used to set the [human_readable_name](https://github.com/grafeas/grafeas/blob/cd23d4dc1bef740d6d6d90d5007db5c9a2431c41/proto/v1/attestation.proto#L49) field in the Grafeas ATTESTATION note. If it is not provided, the default `This attestation note was generated by Tekton Chains` will be used. | | |
76+
| `storage.archivista.url` | The URL endpoint for the Archivista service. | A valid HTTPS URL pointing to your Archivista instance (e.g. `https://archivista.testifysec.io`). | None |
7677

7778
#### docstore
7879

go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ require (
1616
github.com/google/go-licenses v1.6.0
1717
github.com/grafeas/grafeas v0.2.3
1818
github.com/hashicorp/go-multierror v1.1.1
19+
github.com/in-toto/archivista v0.9.0
1920
github.com/in-toto/attestation v1.1.1
21+
github.com/in-toto/go-witness v0.7.0
2022
github.com/in-toto/in-toto-golang v0.9.1-0.20240317085821-8e2966059a09
2123
github.com/opencontainers/go-digest v1.0.0
2224
github.com/pkg/errors v0.9.1
@@ -183,6 +185,7 @@ require (
183185
github.com/eapache/go-resiliency v1.7.0 // indirect
184186
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect
185187
github.com/eapache/queue v1.1.0 // indirect
188+
github.com/edwarnicke/gitoid v0.0.0-20220710194850-1be5bfda1f9d // indirect
186189
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
187190
github.com/emirpasic/gods v1.18.1 // indirect
188191
github.com/envoyproxy/go-control-plane v0.13.1 // indirect

go.sum

+6
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,8 @@ github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4A
463463
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0=
464464
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
465465
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
466+
github.com/edwarnicke/gitoid v0.0.0-20220710194850-1be5bfda1f9d h1:4l+Uq5zFWSagXgGFaKRRVWJrnlzeathyagWgYUltCgY=
467+
github.com/edwarnicke/gitoid v0.0.0-20220710194850-1be5bfda1f9d/go.mod h1:WxWwA3EYuCQjlR5EBUX3uaTS8bh9BOa7BcqVREHQ0uQ=
466468
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
467469
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
468470
github.com/emicklei/proto v1.13.4 h1:myn1fyf8t7tAqIzV91Tj9qXpvyXXGXk8OS2H6IBSc9g=
@@ -863,8 +865,12 @@ github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJ
863865
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
864866
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
865867
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
868+
github.com/in-toto/archivista v0.9.0 h1:XlS+jkrcFjmwSMhp6BZbP5y8FOvFPXM1h23WvCDT8bQ=
869+
github.com/in-toto/archivista v0.9.0/go.mod h1:cLhrICj86j+8wJZmrUzDbNQdcwdc2lqX+v1SKV4tXpE=
866870
github.com/in-toto/attestation v1.1.1 h1:QD3d+oATQ0dFsWoNh5oT0udQ3tUrOsZZ0Fc3tSgWbzI=
867871
github.com/in-toto/attestation v1.1.1/go.mod h1:Dcq1zVwA2V7Qin8I7rgOi+i837wEf/mOZwRm047Sjys=
872+
github.com/in-toto/go-witness v0.7.0 h1:I48FUCLfyos0uCSlHJoqCJO6HjtxF2f/y65TQVpxd8k=
873+
github.com/in-toto/go-witness v0.7.0/go.mod h1:WZQY96yHqPPYkRcQU7dXl0d3saMKAg9DepWbUVL586E=
868874
github.com/in-toto/in-toto-golang v0.9.1-0.20240317085821-8e2966059a09 h1:cwCITdi9pF50CF8uh40qDbkJ/VrEVzx5AoaHP7OPdEo=
869875
github.com/in-toto/in-toto-golang v0.9.1-0.20240317085821-8e2966059a09/go.mod h1:yGCBn2JKF1m26FX8GmkcLSOFVjB6khWRxFsHwWIg7hw=
870876
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
Copyright 2025 The Tekton Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package archivista
15+
16+
import (
17+
"context"
18+
"encoding/json"
19+
"errors"
20+
"fmt"
21+
"net/http"
22+
"strings"
23+
24+
archivistaClient "github.com/in-toto/archivista/pkg/http-client"
25+
"github.com/in-toto/go-witness/dsse"
26+
"github.com/tektoncd/chains/pkg/chains/objects"
27+
"github.com/tektoncd/chains/pkg/config"
28+
"knative.dev/pkg/logging"
29+
)
30+
31+
const (
32+
// StorageBackendArchivista is the name of the Archivista storage backend
33+
StorageBackendArchivista = "archivista"
34+
)
35+
36+
// Backend is a storage backend that is capable of storing Payloaders that are signed and wrapped
37+
// with a DSSE envelope. Archivista is an in-toto attestation storage service.
38+
type Backend struct {
39+
client *archivistaClient.ArchivistaClient
40+
url string
41+
cfg config.ArchivistaStorageConfig
42+
}
43+
44+
// NewStorageBackend returns a new Archivista StorageBackend that can store Payloaders that are signed
45+
// and wrapped in a DSSE envelope
46+
func NewStorageBackend(cfg config.Config) (*Backend, error) {
47+
archCfg := cfg.Storage.Archivista
48+
if strings.TrimSpace(archCfg.URL) == "" {
49+
return nil, fmt.Errorf("missing archivista URL in storage configuration")
50+
}
51+
52+
client, err := archivistaClient.CreateArchivistaClient(&http.Client{}, archCfg.URL)
53+
if err != nil {
54+
return nil, fmt.Errorf("failed to create Archivista client: %w", err)
55+
}
56+
57+
return &Backend{
58+
client: client,
59+
url: archCfg.URL,
60+
cfg: archCfg,
61+
}, nil
62+
}
63+
64+
// StorePayload attempts to parse `signature` as a DSSE envelope, and if successful
65+
// sends it to an Archivista server for storage.
66+
func (b *Backend) StorePayload(ctx context.Context, _ objects.TektonObject, _ []byte, signature string, _ config.StorageOpts) error {
67+
logger := logging.FromContext(ctx)
68+
var env dsse.Envelope
69+
if err := json.Unmarshal([]byte(signature), &env); err != nil {
70+
logger.Errorf("Failed to parse DSSE envelope: %w", err)
71+
return errors.Join(errors.New("Failed to parse DSSE envelope"), err)
72+
}
73+
74+
uploadResp, err := b.client.Store(ctx, env)
75+
if err != nil {
76+
logger.Errorw("Failed to upload DSSE envelope to Archivista", "error", err)
77+
return err
78+
}
79+
logger.Infof("Successfully uploaded DSSE envelope to Archivista, response: %+v", uploadResp)
80+
return nil
81+
}
82+
83+
// RetrievePayload is not implemented for Archivista.
84+
func (b *Backend) RetrievePayload(_ context.Context, _ string) ([]byte, []byte, error) {
85+
return nil, nil, fmt.Errorf("RetrievePayload not implemented for Archivista")
86+
}
87+
88+
// RetrievePayloads is not implemented for Archivista.
89+
func (b *Backend) RetrievePayloads(_ context.Context, _ objects.TektonObject, _ config.StorageOpts) (map[string]string, error) {
90+
return nil, fmt.Errorf("RetrievePayloads not implemented for Archivista")
91+
}
92+
93+
// RetrieveSignatures is not implemented for Archivista.
94+
func (b *Backend) RetrieveSignatures(_ context.Context, _ objects.TektonObject, _ config.StorageOpts) (map[string][]string, error) {
95+
return nil, fmt.Errorf("RetrieveSignatures not implemented for Archivista")
96+
}
97+
98+
// Type returns the name of the storage backend
99+
func (b *Backend) Type() string {
100+
return StorageBackendArchivista
101+
}

0 commit comments

Comments
 (0)