Skip to content

Commit 1460f33

Browse files
committed
oci: sort remaining quirks in cosign verify logic
This commit properly sets `IgnoreTlog` to `true` when a public key is provided to check the signature against, which matches the (silent) default behavior from cosign v1. However, during this exercise it has become apparant that this assumption isn't necessarily true. As you can theoretically have a custom key and a tlog entry. Given this, we should inventarise the possible configuration options and the potential value they have to users (e.g. defining a custom Rekor URL seems to be valuable as well), and extend our API to facilitate these needs. In addition to the above, the CTLog public keys are now properly retrieved to avoid a `none of the CTFE keys have been found` error. Signed-off-by: Hidde Beydals <[email protected]>
1 parent 0c95ab5 commit 1460f33

File tree

4 files changed

+41
-36
lines changed

4 files changed

+41
-36
lines changed

internal/controller/helmchart_controller_test.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"errors"
2424
"fmt"
2525
"io"
26-
"io/ioutil"
2726
"net/http"
2827
"os"
2928
"path"
@@ -1058,7 +1057,8 @@ func TestHelmChartReconciler_buildFromOCIHelmRepository(t *testing.T) {
10581057
)
10591058

10601059
// Load a test chart
1061-
chartData, err := ioutil.ReadFile(chartPath)
1060+
chartData, err := os.ReadFile(chartPath)
1061+
g.Expect(err).NotTo(HaveOccurred())
10621062

10631063
// Upload the test chart
10641064
metadata, err := loadTestChartToOCI(chartData, chartPath, testRegistryServer)
@@ -2333,16 +2333,16 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) {
23332333

23342334
builder := fakeclient.NewClientBuilder().WithScheme(testEnv.GetScheme())
23352335
workspaceDir := t.TempDir()
2336-
server, err := setupRegistryServer(ctx, workspaceDir, tt.registryOpts)
23372336

2337+
server, err := setupRegistryServer(ctx, workspaceDir, tt.registryOpts)
23382338
g.Expect(err).NotTo(HaveOccurred())
23392339

23402340
// Load a test chart
2341-
chartData, err := ioutil.ReadFile(chartPath)
2341+
chartData, err := os.ReadFile(chartPath)
2342+
g.Expect(err).ToNot(HaveOccurred())
23422343

23432344
// Upload the test chart
23442345
metadata, err := loadTestChartToOCI(chartData, chartPath, server)
2345-
g.Expect(err).NotTo(HaveOccurred())
23462346
g.Expect(err).ToNot(HaveOccurred())
23472347

23482348
repo := &helmv1.HelmRepository{
@@ -2452,7 +2452,8 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignature(t *testing.T
24522452
)
24532453

24542454
// Load a test chart
2455-
chartData, err := ioutil.ReadFile(chartPath)
2455+
chartData, err := os.ReadFile(chartPath)
2456+
g.Expect(err).ToNot(HaveOccurred())
24562457

24572458
// Upload the test chart
24582459
metadata, err := loadTestChartToOCI(chartData, chartPath, server)
@@ -2504,10 +2505,10 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignature(t *testing.T
25042505
},
25052506
want: sreconcile.ResultEmpty,
25062507
wantErr: true,
2507-
wantErrMsg: "chart verification error: failed to verify <url>: no matching signatures:",
2508+
wantErrMsg: "chart verification error: failed to verify <url>: no signatures found for image",
25082509
assertConditions: []metav1.Condition{
2509-
*conditions.TrueCondition(sourcev1.BuildFailedCondition, "ChartVerificationError", "chart verification error: failed to verify <url>: no matching signatures:"),
2510-
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "chart verification error: failed to verify <url>: no matching signatures:"),
2510+
*conditions.TrueCondition(sourcev1.BuildFailedCondition, "ChartVerificationError", "chart verification error: failed to verify <url>: no signatures found for image"),
2511+
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "chart verification error: failed to verify <url>: no signatures found for image"),
25112512
},
25122513
},
25132514
{
@@ -2522,8 +2523,8 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignature(t *testing.T
25222523
want: sreconcile.ResultEmpty,
25232524
wantErr: true,
25242525
assertConditions: []metav1.Condition{
2525-
*conditions.TrueCondition(sourcev1.BuildFailedCondition, "ChartVerificationError", "chart verification error: failed to verify <url>: no matching signatures:"),
2526-
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "chart verification error: failed to verify <url>: no matching signatures:"),
2526+
*conditions.TrueCondition(sourcev1.BuildFailedCondition, "ChartVerificationError", "chart verification error: failed to verify <url>: no signatures found for image"),
2527+
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "chart verification error: failed to verify <url>: no signatures found for image"),
25272528
},
25282529
},
25292530
{
@@ -2696,7 +2697,7 @@ func loadTestChartToOCI(chartData []byte, chartPath string, server *registryClie
26962697
}
26972698

26982699
// Load a test chart
2699-
chartData, err = ioutil.ReadFile(chartPath)
2700+
chartData, err = os.ReadFile(chartPath)
27002701
if err != nil {
27012702
return nil, err
27022703
}

internal/controller/ocirepository_controller_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1095,7 +1095,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
10951095
assertConditions: []metav1.Condition{
10961096
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
10971097
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
1098-
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "failed to verify the signature using provider '<provider> keyless': no matching signatures"),
1098+
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "failed to verify the signature using provider '<provider> keyless': no signatures found for image"),
10991099
},
11001100
},
11011101
{
@@ -1193,6 +1193,8 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
11931193

11941194
for _, tt := range tests {
11951195
t.Run(tt.name, func(t *testing.T) {
1196+
g := NewWithT(t)
1197+
11961198
obj := &ociv1.OCIRepository{
11971199
ObjectMeta: metav1.ObjectMeta{
11981200
GenerateName: "verify-oci-source-signature-",

internal/controller/suite_test.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"context"
2222
"fmt"
2323
"io"
24-
"io/ioutil"
2524
"math/rand"
2625
"os"
2726
"path/filepath"
@@ -164,8 +163,7 @@ func setupRegistryServer(ctx context.Context, workspaceDir string, opts registry
164163
}
165164

166165
htpasswdPath := filepath.Join(workspaceDir, testRegistryHtpasswdFileBasename)
167-
err = ioutil.WriteFile(htpasswdPath, []byte(fmt.Sprintf("%s:%s\n", testRegistryUsername, string(pwBytes))), 0644)
168-
if err != nil {
166+
if err = os.WriteFile(htpasswdPath, []byte(fmt.Sprintf("%s:%s\n", testRegistryUsername, string(pwBytes))), 0644); err != nil {
169167
return nil, fmt.Errorf("failed to create htpasswd file: %s", err)
170168
}
171169

internal/oci/verifier.go

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@ import (
2121
"crypto"
2222
"fmt"
2323

24+
"github.com/google/go-containerregistry/pkg/name"
2425
"github.com/google/go-containerregistry/pkg/v1/remote"
2526
"github.com/sigstore/cosign/v2/cmd/cosign/cli/fulcio"
27+
coptions "github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
2628
"github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor"
2729
"github.com/sigstore/cosign/v2/pkg/cosign"
28-
ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
29-
30-
"github.com/google/go-containerregistry/pkg/name"
31-
coptions "github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
3230
"github.com/sigstore/cosign/v2/pkg/oci"
31+
ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
3332
"github.com/sigstore/sigstore/pkg/cryptoutils"
3433
"github.com/sigstore/sigstore/pkg/signature"
3534
)
@@ -93,6 +92,11 @@ func NewCosignVerifier(ctx context.Context, opts ...Options) (*CosignVerifier, e
9392
// If there is no public key provided, it will try keyless verification.
9493
// https://github.com/sigstore/cosign/blob/main/KEYLESS.md.
9594
if len(o.PublicKey) > 0 {
95+
checkOpts.Offline = true
96+
// TODO(hidde): this is an oversight in our implementation. As it is
97+
// theoretically possible to have a custom PK, without disabling tlog.
98+
checkOpts.IgnoreTlog = true
99+
96100
pubKeyRaw, err := cryptoutils.UnmarshalPEMToPublicKey(o.PublicKey)
97101
if err != nil {
98102
return nil, err
@@ -102,31 +106,31 @@ func NewCosignVerifier(ctx context.Context, opts ...Options) (*CosignVerifier, e
102106
if err != nil {
103107
return nil, err
104108
}
105-
106-
checkOpts.Offline = true
107-
108109
} else {
109-
rcerts, err := fulcio.GetRoots()
110+
checkOpts.RekorClient, err = rekor.NewClient(coptions.DefaultRekorURL)
110111
if err != nil {
111-
return nil, fmt.Errorf("unable to get Fulcio root certs: %w", err)
112+
return nil, fmt.Errorf("unable to create Rekor client: %w", err)
112113
}
113-
checkOpts.RootCerts = rcerts
114114

115-
icerts, err := fulcio.GetIntermediates()
116-
if err != nil {
117-
return nil, fmt.Errorf("unable to get Fulcio intermediate certs: %w", err)
115+
// This performs an online fetch of the Rekor public keys, but this is needed
116+
// for verifying tlog entries (both online and offline).
117+
// TODO(hidde): above note is important to keep in mind when we implement
118+
// "offline" tlog above.
119+
if checkOpts.RekorPubKeys, err = cosign.GetRekorPubs(ctx); err != nil {
120+
return nil, fmt.Errorf("unable to get Rekor public keys: %w", err)
118121
}
119-
checkOpts.IntermediateCerts = icerts
120122

121-
rc, err := rekor.NewClient(coptions.DefaultRekorURL)
123+
checkOpts.CTLogPubKeys, err = cosign.GetCTLogPubs(ctx)
122124
if err != nil {
123-
return nil, fmt.Errorf("unable to create Rekor client: %w", err)
125+
return nil, fmt.Errorf("unable to get CTLog public keys: %w", err)
124126
}
125-
checkOpts.RekorClient = rc
126127

127-
checkOpts.RekorPubKeys, err = cosign.GetRekorPubs(ctx)
128-
if err != nil {
129-
return nil, fmt.Errorf("unable to get Rekor public keys: %w", err)
128+
if checkOpts.RootCerts, err = fulcio.GetRoots(); err != nil {
129+
return nil, fmt.Errorf("unable to get Fulcio root certs: %w", err)
130+
}
131+
132+
if checkOpts.IntermediateCerts, err = fulcio.GetIntermediates(); err != nil {
133+
return nil, fmt.Errorf("unable to get Fulcio intermediate certs: %w", err)
130134
}
131135
}
132136

0 commit comments

Comments
 (0)