diff --git a/README.md b/README.md index 64301ca46..d3373eb91 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ The following config options are supported: | timestampCertChain | | Path to PEM encoded certificate chain for RFC3161 Timestamp Authority verification. | | autoclose | true | If true, autoclose the browser window after `autocloseTimeout`. In order for autoclose to work you must also set `connectorID`. | | autocloseTimeout | 6 | If `autoclose` is true, this is how long to wait until the window is closed. | -| enableSigstoreGo | false | (Experimental) If true, use the sigstore-go libraries for signing and verification via the CMS↔bundle compatibility layer. Requires `rekorMode=offline`. The on-disk signature format is unchanged. See [docs/bundle-cms.md](./docs/bundle-cms.md) for more details. | +| enableSigstoreGo | true | If true (the default), use the sigstore-go libraries via the CMS↔bundle compatibility layer. Verification uses sigstore-go in all Rekor modes; signing uses it only in `rekorMode=offline` (online/legacy signing stays on the existing CMS path). Set to false to fall back to the legacy implementation. The on-disk signature format is unchanged. See [docs/bundle-cms.md](./docs/bundle-cms.md) for more details. | | urlOpener | | Optional command used to open the OIDC login URL during the interactive auth flow. If unset, the platform default browser is used. The command is split using shell-style word splitting (quote arguments containing spaces) and each token is rendered as a Go [text/template](https://pkg.go.dev/text/template) with the login URL available as `{{.URL}}` (e.g. `firefox --new-tab {{.URL}}`). No shell is invoked, so shell metacharacters are inert. See [docs/url-opener.md](./docs/url-opener.md) for examples, including opening a specific Chrome profile. | ### Environment Variables @@ -105,10 +105,10 @@ The following config options are supported: | GITSIGN_TIMESTAMP_SERVER_URL | ✅ | | Address of timestamping authority. If set, a trusted timestamp will be included in the signature. | | GITSIGN_TIMESTAMP_CERT_CHAIN | ✅ | | Path to PEM encoded certificate chain for RFC3161 Timestamp Authority verification. | | GITSIGN_FULCIO_ROOT | ✅ | | Path to PEM encoded certificate for Fulcio CA (additional alias: SIGSTORE_ROOT_FILE) | -| GITSIGN_REKOR_MODE | ❌ | online | Rekor storage mode to operate in. One of [online, offline] (default: online)
online - Commit SHAs are stored in Rekor, requiring online verification for all commit objects.
offline - Hashed commit content is stored in Rekor, with Rekor attributes necessary for offline verification being stored in the commit itself.
Note: online verification will be deprecated in favor of offline in the future. | +| GITSIGN_REKOR_MODE | ❌ | offline | Rekor storage mode to operate in. One of [online, offline] (default: offline)
online - Commit SHAs are stored in Rekor, requiring online verification for all commit objects.
offline - Hashed commit content is stored in Rekor, with Rekor attributes necessary for offline verification being stored in the commit itself.
Note: online verification will be deprecated in favor of offline in the future. | | GITSIGN_AUTOCLOSE | ❌ | true | If true, autoclose the browser window after `GITSIGN_AUTOCLOSE_TIME`. | | GITSIGN_AUTOCLOSE_TIMEOUT | ❌ | 6 | If `GITSIGN_AUTOCLOSE` is true, this is how long to wait until the window is closed. | -| GITSIGN_ENABLE_SIGSTORE_GO | ❌ | false | (Experimental) If true, use the sigstore-go libraries for signing and verification via the CMS↔bundle compatibility layer. Requires `GITSIGN_REKOR_MODE=offline`. The on-disk signature format is unchanged. See [docs/bundle-cms.md](./docs/bundle-cms.md) for more details. | +| GITSIGN_ENABLE_SIGSTORE_GO | ❌ | true | If true (the default), use the sigstore-go libraries via the CMS↔bundle compatibility layer. Verification uses sigstore-go in all Rekor modes; signing uses it only in `GITSIGN_REKOR_MODE=offline` (online/legacy signing stays on the existing CMS path). Set to false to fall back to the legacy implementation. The on-disk signature format is unchanged. See [docs/bundle-cms.md](./docs/bundle-cms.md) for more details. | | GITSIGN_URL_OPENER | ❌ | | Optional command used to open the OIDC login URL during the interactive auth flow. If unset, the platform default browser is used. The command is split using shell-style word splitting (quote arguments containing spaces) and each token is rendered as a Go text/template with the login URL available as `{{.URL}}` (e.g. `firefox --new-tab {{.URL}}`). No shell is invoked, so shell metacharacters are inert. See [docs/url-opener.md](./docs/url-opener.md) for examples, including opening a specific Chrome profile. | For environment variables that support `Sigstore Prefix`, the values may be @@ -301,7 +301,7 @@ Gitsign stores data in 2 places: time, Gitsign records commits and the code signing certificates to [Rekor](https://docs.sigstore.dev/rekor/overview/). - - If `rekorMode = online` (default) + - If `rekorMode = online` This data is a [HashedRekord](https://github.com/sigstore/rekor/blob/e375eb461cae524270889b57a249ff086bea6c05/types.md#hashed-rekord) @@ -310,10 +310,7 @@ Gitsign stores data in 2 places: [Verifying the Transparency Log](#verifying-the-transparency-log) for more details. - - If `rekorMode = offline` - - Note: offline verification is new, and should be considered experimental for - now. + - If `rekorMode = offline` (default) By default, data is written to the [public Rekor instance](https://docs.sigstore.dev/rekor/public-instance). In diff --git a/docs/bundle-cms.md b/docs/bundle-cms.md index 226207f11..373f4d097 100644 --- a/docs/bundle-cms.md +++ b/docs/bundle-cms.md @@ -14,8 +14,9 @@ explains how the two formats relate and how the conversion works. The compatibility layer lives in [`internal/sigstore/compat`](../internal/sigstore/compat). -> **Note:** This conversion is currently experimental and opt-in. See -> [Enabling the sigstore-go path](#enabling-the-sigstore-go-path) below. +> **Note:** This conversion is enabled by default. See +> [Enabling the sigstore-go path](#enabling-the-sigstore-go-path) below for how +> to disable it or how it interacts with the Rekor mode. ## The key insight: the signed artifact is the SignedAttrs @@ -104,17 +105,23 @@ Because the signature is over the `SignedAttrs` — which include the signing ti ## Enabling the sigstore-go path -Both directions are gated behind a single experimental option, off by default: +The sigstore-go path is controlled by a single option, on by default: ```sh -git config gitsign.enableSigstoreGo true -# or: GITSIGN_ENABLE_SIGSTORE_GO=true +# Disable it and fall back to the legacy CMS + Rekor implementation: +git config gitsign.enableSigstoreGo false +# or: GITSIGN_ENABLE_SIGSTORE_GO=false ``` -This requires offline Rekor mode (`gitsign.rekorMode=offline`); gitsign will -error at startup otherwise, since the bundle signing path embeds the Rekor entry -in the signature (which is meaningless in online mode). Online/legacy signing -remains on the existing CMS path. +The two directions engage differently depending on the Rekor mode: + +- **Verification** uses sigstore-go in all Rekor modes. Legacy online signatures + carry no embedded Rekor entry — sigstore-go cannot look these up from the + signature alone, so they transparently fall back to the legacy verifier. +- **Signing** uses sigstore-go only in offline Rekor mode + (`gitsign.rekorMode=offline`), where the bundle signing path embeds the Rekor + entry in the signature. Online/legacy signing remains on the existing CMS path + even when the option is enabled. The on-disk CMS signature format is identical whether or not the option is set; only the implementation of how signing and verification are performed changes. diff --git a/internal/commands/root/sign.go b/internal/commands/root/sign.go index 7efea1295..77614da0b 100644 --- a/internal/commands/root/sign.go +++ b/internal/commands/root/sign.go @@ -42,8 +42,11 @@ func commandSign(o *options, s *gsio.Streams, args ...string) error { return errors.New("specify --help, --sign, or --verify") } - if o.Config.EnableSigstoreGo { - fmt.Fprintln(s.TTYOut, "gitsign: experimental sigstore-go signing enabled") // nolint:errcheck + // The sigstore-go signing path only engages in offline Rekor mode (it embeds + // the Rekor entry in the signature); online/legacy signing stays on the CMS + // path even when sigstore-go is enabled. + if o.Config.EnableSigstoreGo && o.Config.RekorMode == "offline" { + fmt.Fprintln(s.TTYOut, "gitsign: sigstore-go signing enabled") // nolint:errcheck } userIdent, err := fulcio.NewIdentity(ctx, o.Config, s.TTYIn, s.TTYOut) diff --git a/internal/config/config.go b/internal/config/config.go index 1f3021f81..45b6f283a 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -49,17 +49,22 @@ type Config struct { // Address of Rekor server Rekor string - // Rekor storage mode to operate in. One of [online, offline] (default: online) + // Rekor storage mode to operate in. One of [online, offline] (default: offline) // online - Commit SHAs are stored in Rekor, requiring online verification for all commit objects. // offline - Hashed commit content is stored in Rekor, with Rekor attributes // necessary for offline verification being stored in the commit itself. // Note: online verification will be deprecated in favor of offline in the future. RekorMode string - // EnableSigstoreGo enables the experimental sigstore-go code paths for both - // signing and verification (via the CMS<->bundle compat layer). When false - // (the default), the legacy CMS + Rekor signing and verification are used. - // The on-disk CMS signature format is unchanged either way. + // EnableSigstoreGo enables the sigstore-go code paths (via the CMS<->bundle + // compat layer). It is on by default; set it to false to fall back to the + // legacy CMS + Rekor implementation. The on-disk CMS signature format is + // unchanged either way. + // + // Verification uses sigstore-go in all Rekor modes (legacy online signatures + // with no embedded entry fall back to the legacy verifier). Signing uses + // sigstore-go only in offline Rekor mode, where the Rekor entry is embedded + // in the signature; online/legacy signing stays on the existing CMS path. EnableSigstoreGo bool // OIDC client ID for application @@ -147,12 +152,12 @@ func Get() (*Config, error) { // Start with default config out := &Config{ - Fulcio: "https://fulcio.sigstore.dev", - Rekor: "https://rekor.sigstore.dev", - ClientID: "sigstore", - Issuer: "https://oauth2.sigstore.dev/auth", - // TODO: default to offline - RekorMode: "online", + Fulcio: "https://fulcio.sigstore.dev", + Rekor: "https://rekor.sigstore.dev", + ClientID: "sigstore", + Issuer: "https://oauth2.sigstore.dev/auth", + RekorMode: "offline", + EnableSigstoreGo: true, Autoclose: true, AutocloseTimeout: 6, } @@ -190,13 +195,6 @@ func Get() (*Config, error) { out.URLOpener = envOrValue("GITSIGN_URL_OPENER", out.URLOpener) out.EnableSigstoreGo = envOrValue("GITSIGN_ENABLE_SIGSTORE_GO", fmt.Sprintf("%t", out.EnableSigstoreGo)) == "true" - // The sigstore-go signing path embeds the Rekor entry in the signature, which - // only applies to offline Rekor mode. Fail loudly rather than silently - // ignoring the setting in online mode. - if out.EnableSigstoreGo && out.RekorMode != "offline" { - return nil, fmt.Errorf("gitsign.enableSigstoreGo requires gitsign.rekorMode=offline, got %q", out.RekorMode) - } - return out, nil } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index d7c0a6163..5129fa314 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -86,7 +86,8 @@ func TestGet(t *testing.T) { Issuer: "tacocat", RedirectURL: "example.com", ConnectorID: "bar", - RekorMode: "online", + RekorMode: "offline", + EnableSigstoreGo: true, Autoclose: true, AutocloseTimeout: 6, // From config file. @@ -107,22 +108,44 @@ func TestGet(t *testing.T) { } } -func TestEnableSigstoreGoRequiresOffline(t *testing.T) { +func TestEnableSigstoreGo(t *testing.T) { t.Cleanup(func() { execFn = realExec }) - // enableSigstoreGo with the default (online) Rekor mode must error. + // sigstore-go is opted in by default, including in the default (online) + // Rekor mode. execFn = func() (io.Reader, error) { - return strings.NewReader("gitsign.enableSigstoreGo true\n"), nil + return strings.NewReader(""), nil } - if _, err := Get(); err == nil { - t.Error("expected error when enableSigstoreGo is set without offline rekor mode") + got, err := Get() + if err != nil { + t.Fatalf("unexpected error with default config: %v", err) + } + if !got.EnableSigstoreGo { + t.Error("expected EnableSigstoreGo to default to true") } - // enableSigstoreGo with offline mode is allowed. + // It can be turned off via git config. execFn = func() (io.Reader, error) { - return strings.NewReader("gitsign.enableSigstoreGo true\ngitsign.rekorMode offline\n"), nil + return strings.NewReader("gitsign.enableSigstoreGo false\n"), nil + } + got, err = Get() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if got.EnableSigstoreGo { + t.Error("expected gitsign.enableSigstoreGo=false to disable sigstore-go") + } + + // It can be turned off via environment variable. + t.Setenv("GITSIGN_ENABLE_SIGSTORE_GO", "false") + execFn = func() (io.Reader, error) { + return strings.NewReader(""), nil + } + got, err = Get() + if err != nil { + t.Fatalf("unexpected error: %v", err) } - if _, err := Get(); err != nil { - t.Errorf("expected enableSigstoreGo + offline mode to be valid, got: %v", err) + if got.EnableSigstoreGo { + t.Error("expected GITSIGN_ENABLE_SIGSTORE_GO=false to disable sigstore-go") } }