diff --git a/README.md b/README.md index 734aa53b..cc158d4d 100644 --- a/README.md +++ b/README.md @@ -62,18 +62,19 @@ $ git config --local gitsign.fulcio https://fulcio.example.com The following config options are supported: -| Option | Default | Description | -| ------------------ | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| fulcio | https://fulcio.sigstore.dev | Address of Fulcio server | -| logPath | | Path to log status output. Helpful for debugging when no TTY is available in the environment. | -| clientID | sigstore | OIDC client ID for application | -| issuer | https://oauth2.sigstore.dev/auth | OIDC provider to be used to issue ID token | -| matchCommitter | false | If true, verify that the committer matches certificate user identity. See [docs/committer-verification.md](./docs/committer-verification.md) for more details. | -| redirectURL | | OIDC Redirect URL | -| rekor | https://rekor.sigstore.dev | Address of Rekor server | -| connectorID | | Optional Connector ID to auto-select to pre-select auth flow to use. For the public sigstore instance, valid values are:
- `https://github.com/login/oauth`
- `https://accounts.google.com`
- `https://login.microsoftonline.com` | -| timestampServerURL | | Address of timestamping authority. If set, a trusted timestamp will be included in the signature. | -| timestampCertChain | | Path to PEM encoded certificate chain for RFC3161 Timestamp Authority verification. | +| Option | Default | Description | +| ------------------ | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| fulcio | https://fulcio.sigstore.dev | Address of Fulcio server | +| logPath | | Path to log status output. Helpful for debugging when no TTY is available in the environment. | +| clientID | sigstore | OIDC client ID for application | +| issuer | https://oauth2.sigstore.dev/auth | OIDC provider to be used to issue ID token | +| matchCommitter | false | If true, verify that the committer matches certificate user identity. See [docs/committer-verification.md](./docs/committer-verification.md) for more details. | +| redirectURL | | OIDC Redirect URL | +| rekor | https://rekor.sigstore.dev | Address of Rekor server | +| connectorID | | Optional Connector ID to auto-select to pre-select auth flow to use. For the public sigstore instance, valid values are:
- `https://github.com/login/oauth`
- `https://accounts.google.com`
- `https://login.microsoftonline.com` | +| tokenProvider | | Optional OIDC token provider to use to fetch tokens. If not set, any available providers are used. valid values are:
- `interactive`
- `spiffe`
- `google-workload-identity`
- `google-impersonation`
- `github-actions`
- `filesystem`
- `buildkite-agent` | +| timestampServerURL | | Address of timestamping authority. If set, a trusted timestamp will be included in the signature. | +| timestampCertChain | | Path to PEM encoded certificate chain for RFC3161 Timestamp Authority verification. | ### Environment Variables @@ -153,10 +154,10 @@ Validated Certificate claims: true **NOTE**: `gitsign verify` is preferred over [`git verify-commit`](https://git-scm.com/docs/git-verify-commit) and -[`git verify-tag`](https://git-scm.com/docs/git-verify-tag). The git commands -do not pass through any expected identity information to the signing tools, so -they only verify cryptographic integrity and that the data exists on Rekor, but -not **who** put the data there. +[`git verify-tag`](https://git-scm.com/docs/git-verify-tag). The git commands do +not pass through any expected identity information to the signing tools, so they +only verify cryptographic integrity and that the data exists on Rekor, but not +**who** put the data there. Using these commands will still work, but a warning being displayed. @@ -293,7 +294,8 @@ Gitsign stores data in 2 places: - If `rekorMode = offline` - Note: offline verification is new, and should be considered experimental for now. + Note: offline verification is new, and should be considered experimental for + now. By default, data is written to the [public Rekor instance](https://docs.sigstore.dev/rekor/public-instance). In diff --git a/internal/config/config.go b/internal/config/config.go index ab11208b..84490093 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -63,6 +63,10 @@ type Config struct { // See https://github.com/sigstore/sigstore/blob/c645ceb9d075499f3a4b3f183d3a6864640fa956/pkg/oauthflow/flow.go#L49-L53 // for more details. ConnectorID string + // TokenProviders select a OIDC token provider to use to fetch tokens. If not set, all providers are attempted. + // See https://github.com/sigstore/cosign/tree/main/pkg/providers for more details. + // Valid values are: [interactive, spiffe, google-workload-identity, google-impersonation, github-actions, filesystem, buildkite-agent] + TokenProvider string // Timestamp Authority address to use to get a trusted timestamp TimestampURL string @@ -117,6 +121,7 @@ func Get() (*Config, error) { out.RedirectURL = envOrValue(fmt.Sprintf("%s_OIDC_REDIRECT_URL", prefix), out.RedirectURL) out.Issuer = envOrValue(fmt.Sprintf("%s_OIDC_ISSUER", prefix), out.Issuer) out.ConnectorID = envOrValue(fmt.Sprintf("%s_CONNECTOR_ID", prefix), out.ConnectorID) + out.TokenProvider = envOrValue(fmt.Sprintf("%s_TOKEN_PROVIDER", prefix), out.TokenProvider) out.TimestampURL = envOrValue(fmt.Sprintf("%s_TIMESTAMP_SERVER_URL", prefix), out.TimestampURL) out.TimestampCert = envOrValue(fmt.Sprintf("%s_TIMESTAMP_CERT_CHAIN", prefix), out.TimestampCert) } @@ -190,6 +195,8 @@ func applyGitOptions(out *Config, cfg map[string]string) { out.LogPath = v case strings.EqualFold(k, "gitsign.connectorID"): out.ConnectorID = v + case strings.EqualFold(k, "gitsign.tokenProvider"): + out.TokenProvider = v case strings.EqualFold(k, "gitsign.timestampServerURL"): out.TimestampURL = v case strings.EqualFold(k, "gitsign.timestampCertChain"): diff --git a/internal/fulcio/identity.go b/internal/fulcio/identity.go index ac5bc278..c64e9240 100644 --- a/internal/fulcio/identity.go +++ b/internal/fulcio/identity.go @@ -203,10 +203,27 @@ func (f *IdentityFactory) NewIdentity(ctx context.Context, cfg *config.Config) ( } var authFlow oauthflow.TokenGetter = defaultFlow - if providers.Enabled(ctx) { - idToken, err := providers.Provide(ctx, clientID) + // If enabled, try OIDC token providers to get a token. unless the token provider is "interactive" (in which case always do default interactive flow). + var provider providers.Interface + if cfg.TokenProvider == "" && providers.Enabled(ctx) { + // If no token provider is set, look for any available provider to use. + provider = defaultFlowProvider{} + } else if cfg.TokenProvider != "" && cfg.TokenProvider != "interactive" { + fmt.Fprintln(f.out, "using token provider", cfg.TokenProvider) + + // If a token provider is explicitly set always use it, unless it's "interactive", + // which means always use the default interactive flow. + p, err := providers.ProvideFrom(ctx, cfg.TokenProvider) + if err != nil { + return nil, fmt.Errorf("error getting token provider %q: %w", cfg.TokenProvider, err) + } + provider = p + } + if provider != nil { + idToken, err := provider.Provide(ctx, clientID) if err != nil { fmt.Fprintln(f.out, "error getting id token:", err) + return nil, fmt.Errorf("error getting id token: %w", err) } authFlow = &oauthflow.StaticTokenGetter{RawToken: idToken} } @@ -239,3 +256,13 @@ func (f *IdentityFactory) NewIdentity(ctx context.Context, cfg *config.Config) ( ChainPEM: cert.ChainPEM, }, nil } + +type defaultFlowProvider struct{} + +func (defaultFlowProvider) Enabled(ctx context.Context) bool { + return providers.Enabled(ctx) +} + +func (defaultFlowProvider) Provide(ctx context.Context, audience string) (string, error) { + return providers.Provide(ctx, audience) +}