Skip to content

Commit

Permalink
Merge pull request #468 from hectorj2f/add_tsa_support
Browse files Browse the repository at this point in the history
feature: add TSA support when verifying authorities
  • Loading branch information
hectorj2f authored Dec 29, 2022
2 parents b7e168e + b5766e5 commit d6ef1f3
Show file tree
Hide file tree
Showing 27 changed files with 695 additions and 31 deletions.
136 changes: 136 additions & 0 deletions .github/workflows/kind-cluster-image-policy-tsa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Copyright 2022 The Sigstore Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Test policy-controller with TSA

on:
pull_request:
branches: [ 'main', 'release-*' ]

defaults:
run:
shell: bash

permissions: read-all

jobs:
cip-test-trustroot-bring-your-own-keys:
name: ClusterImagePolicy e2e tests with TrustRoot - Bring Your Own Keys
runs-on: ubuntu-latest

strategy:
fail-fast: false # Keep running if one leg fails.
matrix:
k8s-version:
- v1.23.x
- v1.24.x
- v1.25.x

env:
KO_DOCKER_REPO: "registry.local:5000/policy-controller"
SCAFFOLDING_RELEASE_VERSION: "v0.5.4"
GO111MODULE: on
GOFLAGS: -ldflags=-s -ldflags=-w
KOCACHE: ~/ko
COSIGN_EXPERIMENTAL: true

steps:
- uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
- uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
with:
go-version: '1.19'
check-latest: true

# will use the latest release available for ko
- uses: imjasonh/setup-ko@ace48d793556083a76f1e3e6068850c1f4a369aa # v0.6

- uses: imranismail/setup-kustomize@a76db1c6419124d51470b1e388c4b29476f495f1 # v1.6.1

- name: Install yq
uses: mikefarah/yq@87cba2ecbeaecf860efcceb66deab46ae030ce1e # v4.27.3

- name: Setup mirror
uses: chainguard-dev/actions/setup-mirror@main
with:
mirror: mirror.gcr.io

- uses: hectorj2f/cosign-installer@c5ac9ce01eb4b0048c02123cb3721624c8f4dc55 # v2
with:
cosign-release: 'v2.0.0-rc.0'

- name: Install cluster + sigstore
uses: sigstore/scaffolding/actions/[email protected]
env:
INSTALL_TSA: true
with:
k8s-version: ${{ matrix.k8s-version}}
version: ${{ env.SCAFFOLDING_RELEASE_VERSION }}

# Install policy-controller that does not have TUF embedded or installed.
- name: Install policy-controller
env:
GIT_HASH: ${{ github.sha }}
GIT_VERSION: ci
LDFLAGS: ""
POLICY_CONTROLLER_YAML: test/kustomize-no-tuf/policy-controller-e2e.yaml
KO_PREFIX: registry.local:5000/policy-controller
POLICY_CONTROLLER_ARCHS: linux/amd64
run: |
make ko-policy-controller
kustomize build test/kustomize-no-tuf | kubectl apply -f -
# Wait for the webhook to come up and become Ready
kubectl rollout status --timeout 5m --namespace cosign-system deployments/webhook
echo "TUF_ROOT_FILE=./root.json" >> $GITHUB_ENV
- name: Checkout TSA for testing.
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.0.2
with:
repository: sigstore/timestamp-authority
path: ./src/github.com/sigstore/timestamp-authority

- name: Build timestamp-cli
working-directory: ./src/github.com/sigstore/timestamp-authority
run: |
go build -o ./timestamp-cli ./cmd/timestamp-cli
- name: Exercise our local TSA
working-directory: ./src/github.com/sigstore/timestamp-authority
run: |
TSA_URL=$(kubectl -n tsa-system get ksvc tsa -ojsonpath='{.status.url}')
echo "TSA_URL=$TSA_URL" >> $GITHUB_ENV
curl $TSA_URL/api/v1/timestamp/certchain > ts_chain.pem
echo "myblob" > myblob
if ! ./timestamp-cli --timestamp_server $TSA_URL timestamp --hash sha256 --artifact myblob --out response.tsr ; then
echo "failed to timestamp artifact"
exit -1
fi
if ! ./timestamp-cli verify --timestamp response.tsr --artifact "myblob" --certificate-chain ts_chain.pem ; then
echo "failed to verify timestamp"
exit -1
fi
if ! ./timestamp-cli inspect --timestamp response.tsr --format json ; then
echo "failed to inspect the timestamp"
exit -1
fi
- name: Run TSA Tests
timeout-minutes: 5
run: |
./test/e2e_test_cluster_image_policy_with_tsa.sh
- name: Collect diagnostics
if: ${{ failure() }}
uses: chainguard-dev/actions/kind-diag@84c993eaf02da1c325854fb272a4df9184bd80fc # main
14 changes: 14 additions & 0 deletions config/300-clusterimagepolicy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,13 @@ spec:
name:
description: Name is the name for this authority. Used by the CIP Policy validator to be able to reference matching signature or attestation verifications. If not specified, the name will be authority-<index in array>
type: string
rfc3161timestamp:
description: RFC3161Timestamp sets the configuration to verify the signature timestamp against a RFC3161 time-stamping instance.
type: object
properties:
trustRootRef:
description: Use the Certificate Chain from the referred TrustRoot.TimeStampAuthorities
type: string
source:
description: Sources sets the configuration to specify the sources from where to consume the signatures.
type: array
Expand Down Expand Up @@ -455,6 +462,13 @@ spec:
name:
description: Name is the name for this authority. Used by the CIP Policy validator to be able to reference matching signature or attestation verifications. If not specified, the name will be authority-<index in array>
type: string
rfc3161timestamp:
description: RFC3161Timestamp sets the configuration to verify the signature timestamp against a RFC3161 time-stamping instance.
type: object
properties:
trustRootRef:
description: Use the Certificate Chain from the referred TrustRoot.TimeStampAuthorities
type: string
source:
description: Sources sets the configuration to specify the sources from where to consume the signatures.
type: array
Expand Down
12 changes: 12 additions & 0 deletions docs/api-types/index-v1alpha1.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
* [KeylessRef](#keylessref)
* [MatchResource](#matchresource)
* [Policy](#policy)
* [RFC3161Timestamp](#rfc3161timestamp)
* [Source](#source)
* [StaticRef](#staticref)
* [TLog](#tlog)
Expand Down Expand Up @@ -161,6 +162,7 @@ Attestation defines the type of attestation to validate and optionally apply a p
| source | Sources sets the configuration to specify the sources from where to consume the signatures. | [][Source](#source) | false |
| ctlog | CTLog sets the configuration to verify the authority against a Rekor instance. | [TLog](#tlog) | false |
| attestations | Attestations is a list of individual attestations for this authority, once the signature for this authority has been verified. | [][Attestation](#attestation) | false |
| rfc3161timestamp | RFC3161Timestamp sets the configuration to verify the signature timestamp against a RFC3161 time-stamping instance. | [RFC3161Timestamp](#rfc3161timestamp) | false |

[Back to TOC](#table-of-contents)

Expand Down Expand Up @@ -288,6 +290,16 @@ Policy specifies a policy to use for Attestation or the CIP validation (iff at l

[Back to TOC](#table-of-contents)

## RFC3161Timestamp

RFC3161Timestamp specifies the URL to a RFC3161 time-stamping server that holds the time-stamped verification for the signature

| Field | Description | Scheme | Required |
| ----- | ----------- | ------ | -------- |
| trustRootRef | Use the Certificate Chain from the referred TrustRoot.TimeStampAuthorities | string | false |

[Back to TOC](#table-of-contents)

## Source

Source specifies the location of the signature
Expand Down
12 changes: 12 additions & 0 deletions docs/api-types/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* [KeylessRef](#keylessref)
* [MatchResource](#matchresource)
* [Policy](#policy)
* [RFC3161Timestamp](#rfc3161timestamp)
* [Source](#source)
* [StaticRef](#staticref)
* [TLog](#tlog)
Expand Down Expand Up @@ -46,6 +47,7 @@ The authorities block defines the rules for discovering and validating signature
| source | Sources sets the configuration to specify the sources from where to consume the signatures. | [][Source](#source) | false |
| ctlog | CTLog sets the configuration to verify the authority against a Rekor instance. | [TLog](#tlog) | false |
| attestations | Attestations is a list of individual attestations for this authority, once the signature for this authority has been verified. | [][Attestation](#attestation) | false |
| rfc3161timestamp | RFC3161Timestamp sets the configuration to verify the signature timestamp against a RFC3161 time-stamping instance. | [RFC3161Timestamp](#rfc3161timestamp) | false |

[Back to TOC](#table-of-contents)

Expand Down Expand Up @@ -173,6 +175,16 @@ Policy specifies a policy to use for Attestation or the CIP validation (iff at l

[Back to TOC](#table-of-contents)

## RFC3161Timestamp

RFC3161Timestamp specifies the URL to a RFC3161 time-stamping server that holds the time-stamped verification for the signature

| Field | Description | Scheme | Required |
| ----- | ----------- | ------ | -------- |
| trustRootRef | Use the Certificate Chain from the referred TrustRoot.TimeStampAuthorities | string | false |

[Back to TOC](#table-of-contents)

## Source

Source specifies the location of the signature
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ require (
github.com/sassoftware/relic v0.0.0-20210427151427-dfb082b79b74 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/sigstore/timestamp-authority v0.2.0 // indirect
github.com/sigstore/timestamp-authority v0.2.1-0.20221222150103-0c33cf516254 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/spf13/afero v1.9.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -838,8 +838,8 @@ github.com/sigstore/rekor v1.0.1 h1:rcESXSNkAPRWFYZel9rarspdvneET60F2ngNkadi89c=
github.com/sigstore/rekor v1.0.1/go.mod h1:ecTKdZWGWqE1pl3U1m1JebQJLU/hSjD9vYHOmHQ7w4g=
github.com/sigstore/sigstore v1.5.0 h1:NqstQ6SwwhQsp6Ll0wgk/d9g5MlfmEppo14aquUjJ/8=
github.com/sigstore/sigstore v1.5.0/go.mod h1:fRAaZ9xXh7ZQ0GJqZdpmNJ3pemuHBu2PgIAngmzIFSI=
github.com/sigstore/timestamp-authority v0.2.0 h1:Z9VxRYL1fdd4R+Rp01fPyhqQw13PgbarTosBO/hmvio=
github.com/sigstore/timestamp-authority v0.2.0/go.mod h1:bGOkcgpe7CeRXOoJXtWX8c3PZBY6cCBBZvxs3t4ZGbY=
github.com/sigstore/timestamp-authority v0.2.1-0.20221222150103-0c33cf516254 h1:dicyQw3lnc59gyo4dEMTm6aRnytEqkEgaclIccWHv5s=
github.com/sigstore/timestamp-authority v0.2.1-0.20221222150103-0c33cf516254/go.mod h1:bGOkcgpe7CeRXOoJXtWX8c3PZBY6cCBBZvxs3t4ZGbY=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/config/image_policies_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ func TestGetAuthorities(t *testing.T) {
if got := getAuthority(t, c, matchedPolicy).Keyless.Identities[0].Subject; got != want {
t.Errorf("Did not get what I wanted %q, got %+v", want, got)
}
want = "trustroot-tsa-ref"
got := getAuthority(t, c, matchedPolicy)
if got.RFC3161Timestamp.TrustRootRef != want {
t.Errorf("Did not get the tsa what I wanted %q, got %+v", want, got)
}
// Make sure UID and ResourceVersion are unserialized properly
checkUIDAndResourceVersion(t, matchedPolicy, c[matchedPolicy])

Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/config/testdata/config-image-policies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ data:
- glob: rando3
authorities:
- name: attestation-0
rfc3161timestamp:
trustRootRef: trustroot-tsa-ref
keyless:
ca-cert:
data: |-
Expand Down
8 changes: 8 additions & 0 deletions pkg/apis/policy/v1alpha1/clusterimagepolicy_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ func (authority *Authority) ConvertTo(ctx context.Context, sink *v1beta1.Authori
TrustRootRef: authority.CTLog.TrustRootRef,
}
}
if authority.RFC3161Timestamp != nil && authority.RFC3161Timestamp.TrustRootRef != "" {
sink.RFC3161Timestamp = &v1beta1.RFC3161Timestamp{}
sink.RFC3161Timestamp.TrustRootRef = authority.RFC3161Timestamp.TrustRootRef
}
for _, source := range authority.Sources {
v1beta1Source := v1beta1.Source{}
v1beta1Source.OCI = source.OCI
Expand Down Expand Up @@ -233,6 +237,10 @@ func (authority *Authority) ConvertFrom(ctx context.Context, source *v1beta1.Aut
TrustRootRef: source.CTLog.TrustRootRef,
}
}
if source.RFC3161Timestamp != nil && source.RFC3161Timestamp.TrustRootRef != "" {
authority.RFC3161Timestamp = &RFC3161Timestamp{}
authority.RFC3161Timestamp.TrustRootRef = source.RFC3161Timestamp.TrustRootRef
}
for _, s := range source.Sources {
src := Source{}
src.OCI = s.OCI
Expand Down
19 changes: 19 additions & 0 deletions pkg/apis/policy/v1alpha1/clusterimagepolicy_conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,25 @@ func TestConversionRoundTripV1beta1(t *testing.T) {
},
},
},
}, {name: "key, keyless, and rfc3161timestamp, regexp",
in: &v1beta1.ClusterImagePolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cip",
},
Spec: v1beta1.ClusterImagePolicySpec{
Images: []v1beta1.ImagePattern{{Glob: "*"}},
Authorities: []v1beta1.Authority{
{Key: &v1beta1.KeyRef{
SecretRef: &v1.SecretReference{Name: "mysecret"}}},
{Keyless: &v1beta1.KeylessRef{
Identities: []v1beta1.Identity{{SubjectRegExp: "subjectregexp", IssuerRegExp: "issuerregexp"}},
CACert: &v1beta1.KeyRef{KMS: "kms", Data: "data", SecretRef: &v1.SecretReference{Name: "secret"}},
},
RFC3161Timestamp: &v1beta1.RFC3161Timestamp{TrustRootRef: "trust-root-tsa-ref"},
},
},
},
},
}}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
Expand Down
11 changes: 11 additions & 0 deletions pkg/apis/policy/v1alpha1/clusterimagepolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ type Authority struct {
// once the signature for this authority has been verified.
// +optional
Attestations []Attestation `json:"attestations,omitempty"`
// RFC3161Timestamp sets the configuration to verify the signature timestamp against a RFC3161 time-stamping instance.
// +optional
RFC3161Timestamp *RFC3161Timestamp `json:"rfc3161timestamp,omitempty"`
}

// This references a public verification key stored in
Expand Down Expand Up @@ -286,6 +289,14 @@ type Identity struct {
SubjectRegExp string `json:"subjectRegExp,omitempty"`
}

// RFC3161Timestamp specifies the URL to a RFC3161 time-stamping server that holds
// the time-stamped verification for the signature
type RFC3161Timestamp struct {
// Use the Certificate Chain from the referred TrustRoot.TimeStampAuthorities
// +optional
TrustRootRef string `json:"trustRootRef,omitempty"`
}

// ClusterImagePolicyList is a list of ClusterImagePolicy resources
//
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
Expand Down
10 changes: 7 additions & 3 deletions pkg/apis/policy/v1alpha1/clusterimagepolicy_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,17 @@ func (image *ImagePattern) Validate(ctx context.Context) *apis.FieldError {

func (authority *Authority) Validate(ctx context.Context) *apis.FieldError {
var errs *apis.FieldError
if authority.Key == nil && authority.Keyless == nil && authority.Static == nil {
errs = errs.Also(apis.ErrMissingOneOf("key", "keyless", "static"))
if authority.Key == nil && authority.Keyless == nil && authority.RFC3161Timestamp == nil && authority.Static == nil {
errs = errs.Also(apis.ErrMissingOneOf("key", "keyless", "rfc3161timestamp", "static"))
// Instead of returning all the missing subfields, just return here
// to give a more concise and arguably a more meaningful error message.
return errs
}
if (authority.Key != nil && authority.Keyless != nil) ||
(authority.RFC3161Timestamp != nil && authority.Static != nil) ||
(authority.Key != nil && authority.Static != nil) ||
(authority.Keyless != nil && authority.Static != nil) {
errs = errs.Also(apis.ErrMultipleOneOf("key", "keyless", "static"))
errs = errs.Also(apis.ErrMultipleOneOf("key", "keyless", "rfc3161timestamp", "static"))
// Instead of returning all the missing subfields, just return here
// to give a more concise and arguably a more meaningful error message.
return errs
Expand All @@ -126,6 +127,9 @@ func (authority *Authority) Validate(ctx context.Context) *apis.FieldError {
if authority.CTLog != nil {
errs = errs.Also(apis.ErrMultipleOneOf("static", "ctlog"))
}
if authority.RFC3161Timestamp != nil {
errs = errs.Also(apis.ErrMultipleOneOf("static", "rfc3161timestamp"))
}
}

for i, source := range authority.Sources {
Expand Down
Loading

0 comments on commit d6ef1f3

Please sign in to comment.