From 08b2acc920acd1de73091ddab3154fe3f00b3c51 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Tue, 20 Oct 2026 16:59:08 +0200 Subject: [PATCH] remotecache: honor gha cache transparency log thresholds Pass explicit cache log thresholds to policy-helpers during signature verification. Preserve the distinction between an omitted threshold and zero. Omission uses the bundle-derived default, while zero skips log verification and requires a verified TSA timestamp. Remove the local log threshold check, which counted log timestamps rather than verified logs and could reject valid inclusion-proof-only entries. Keep timestamp and certificate policy checks in place. Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- cache/remotecache/gha/gha.go | 17 ++++++----------- cache/remotecache/gha/gha_test.go | 20 ++++++++++++++++++++ cache/remotecache/gha/ghatypes/config.go | 4 ++-- docs/buildkitd.toml.md | 2 ++ 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/cache/remotecache/gha/gha.go b/cache/remotecache/gha/gha.go index a340d378e..1798e6d66 100644 --- a/cache/remotecache/gha/gha.go +++ b/cache/remotecache/gha/gha.go @@ -364,7 +364,12 @@ func verifySignature(ctx context.Context, dgst digest.Digest, bundle []byte, con return errors.New("no verifier available for signed github actions cache") } - sig, err := v.VerifyArtifact(ctx, dgst, bundle, policy.WithSLSANotRequired()) + policyRules := config.Verify.Policy + opts := []policy.ArtifactVerifyOpt{policy.WithSLSANotRequired()} + if policyRules.TlogThreshold != nil { + opts = append(opts, policy.WithTlogThreshold(*policyRules.TlogThreshold)) + } + sig, err := v.VerifyArtifact(ctx, dgst, bundle, opts...) if err != nil { return err } @@ -372,19 +377,9 @@ func verifySignature(ctx context.Context, dgst digest.Digest, bundle []byte, con return errors.New("signature verification failed: no signer found") } numTimestamps := len(sig.Timestamps) - numTlog := 0 - for _, t := range sig.Timestamps { - if t.Type == "Tlog" { - numTlog++ - } - } - policyRules := config.Verify.Policy if policyRules.TimestampThreshold > numTimestamps { return errors.Errorf("signature verification failed: not enough timestamp authorities: have %d, need %d", numTimestamps, policyRules.TimestampThreshold) } - if policyRules.TlogThreshold > numTlog { - return errors.Errorf("signature verification failed: not enough tlog authorities: have %d, need %d", numTlog, policyRules.TlogThreshold) - } certRules, err := certToStringMap(&config.Verify.Policy.Summary) if err != nil { diff --git a/cache/remotecache/gha/gha_test.go b/cache/remotecache/gha/gha_test.go index 5ed02f479..d63e2295b 100644 --- a/cache/remotecache/gha/gha_test.go +++ b/cache/remotecache/gha/gha_test.go @@ -9,13 +9,33 @@ import ( "testing" "time" + "github.com/moby/buildkit/cache/remotecache/gha/ghatypes" "github.com/moby/buildkit/client" "github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/util/testutil/integration" "github.com/moby/buildkit/util/testutil/workers" + policy "github.com/moby/policy-helpers" + digest "github.com/opencontainers/go-digest" "github.com/stretchr/testify/require" ) +func TestVerifySignatureNegativeTlogThreshold(t *testing.T) { + config := &Config{ + CacheConfig: &ghatypes.CacheConfig{ + Verify: ghatypes.VerifyConfig{ + Policy: ghatypes.VerifyPolicy{ + TlogThreshold: new(-1), + }, + }, + }, + verifier: func() (*policy.Verifier, error) { + return &policy.Verifier{}, nil + }, + } + err := verifySignature(t.Context(), digest.FromString("cache index"), nil, config) + require.EqualError(t, err, "transparency log threshold must not be negative") +} + func init() { if workers.IsTestDockerd() { workers.InitDockerdWorker() diff --git a/cache/remotecache/gha/ghatypes/config.go b/cache/remotecache/gha/ghatypes/config.go index 770ef624e..a138d9662 100644 --- a/cache/remotecache/gha/ghatypes/config.go +++ b/cache/remotecache/gha/ghatypes/config.go @@ -17,7 +17,7 @@ type VerifyConfig struct { } type VerifyPolicy struct { - TimestampThreshold int `toml:"timestampThreshold"` - TlogThreshold int `toml:"tlogThreshold"` + TimestampThreshold int `toml:"timestampThreshold"` + TlogThreshold *int `toml:"tlogThreshold"` certificate.Summary } diff --git a/docs/buildkitd.toml.md b/docs/buildkitd.toml.md index bba3961d2..6a42a80e3 100644 --- a/docs/buildkitd.toml.md +++ b/docs/buildkitd.toml.md @@ -267,6 +267,8 @@ provenanceEnvDir = "/etc/buildkit/provenance.d" # # [cache.gha.verify.policy] # timestampThreshold = 1 +# # If omitted, require all distinct transparency logs present in the bundle. +# # Zero skips log verification and requires a verified TSA timestamp instead. # tlogThreshold = 1 # # certificate properties that need to match. Simple wildcards (*) are supported. # certificateIssuer = ""