From dc686920d035d4b8ecf3080902a81318e58560e1 Mon Sep 17 00:00:00 2001 From: Andrew Block Date: Thu, 27 Jun 2024 23:28:31 -0500 Subject: [PATCH] Support for Client Secret File Signed-off-by: Andrew Block --- README.md | 13 +++++++------ internal/config/config.go | 24 ++++++++++++++++++++++++ internal/config/config_test.go | 2 +- internal/fulcio/identity.go | 15 +++++++++++---- 4 files changed, 43 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index d1469d72..06c90806 100644 --- a/README.md +++ b/README.md @@ -83,12 +83,13 @@ The following config options are supported: | Environment Variable | Sigstore
Prefix | Default | Description | | ---------------------------- | ------------------ | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | GITSIGN_CREDENTIAL_CACHE | | | Optional path to [gitsign-credential-cache](cmd/gitsign-credential-cache/README.md) socket. | -| GITSIGN_CONNECTOR_ID | ✅ | | 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` | -| GITSIGN_TOKEN_PROVIDER | ✅ | | 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` | -| GITSIGN_FULCIO_URL | ✅ | https://fulcio.sigstore.dev | Address of Fulcio server | -| GITSIGN_LOG | ❌ | | Path to log status output. Helpful for debugging when no TTY is available in the environment. | -| GITSIGN_OIDC_CLIENT_ID | ✅ | sigstore | OIDC client ID for application | -| GITSIGN_OIDC_ISSUER | ✅ | https://oauth2.sigstore.dev/auth | OIDC provider to be used to issue ID token | +| GITSIGN_CONNECTOR_ID | ✅ | | 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` | +| GITSIGN_TOKEN_PROVIDER | ✅ | | 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` | +| GITSIGN_FULCIO_URL | ✅ | https://fulcio.sigstore.dev | Address of Fulcio server | +| GITSIGN_LOG | ❌ | | Path to log status output. Helpful for debugging when no TTY is available in the environment. | +| GITSIGN_OIDC_CLIENT_ID | ✅ | sigstore | OIDC client ID for application +| GITSIGN_OIDC_CLIENT_SECRET_FILE | ✅ | | Path to the file containing the OIDC client secret for the application. | | +| GITSIGN_OIDC_ISSUER | ✅ | https://oauth2.sigstore.dev/auth | OIDC provider to be used to issue ID token | | GITSIGN_OIDC_REDIRECT_URL | ✅ | | OIDC Redirect URL | | GITSIGN_REKOR_URL | ✅ | https://rekor.sigstore.dev | Address of Rekor server | | GITSIGN_TIMESTAMP_SERVER_URL | ✅ | | Address of timestamping authority. If set, a trusted timestamp will be included in the signature. | diff --git a/internal/config/config.go b/internal/config/config.go index 62d81028..2ee62b70 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -24,6 +24,7 @@ import ( "os/exec" "strconv" "strings" + "unicode/utf8" ) type RekorVerificationMode int @@ -57,6 +58,9 @@ type Config struct { // OIDC client ID for application ClientID string + // File containing the OIDC Client Secret + clientSecretFile string + // OIDC Redirect URL RedirectURL string // OIDC provider to be used to issue ID token @@ -89,6 +93,23 @@ type Config struct { AutocloseTimeout int } +// CLientSecret retrieves the OIDC client secret from the file provided +func (o *Config) ClientSecret() (string, error) { + if o.clientSecretFile != "" { + clientSecretBytes, err := os.ReadFile(o.clientSecretFile) + if err != nil { + return "", fmt.Errorf("reading OIDC client secret: %w", err) + } + if !utf8.Valid(clientSecretBytes) { + return "", fmt.Errorf("OIDC client secret in file %s not valid utf8", o.clientSecretFile) + } + clientSecretString := string(clientSecretBytes) + clientSecretString = strings.TrimSpace(clientSecretString) + return clientSecretString, nil + } + return "", nil +} + // Get fetches the gitsign config options for the repo in the current working // directory. func Get() (*Config, error) { @@ -127,6 +148,7 @@ func Get() (*Config, error) { out.FulcioRoot = envOrValue(fmt.Sprintf("%s_FULCIO_ROOT", prefix), out.FulcioRoot) out.Rekor = envOrValue(fmt.Sprintf("%s_REKOR_URL", prefix), out.Rekor) out.ClientID = envOrValue(fmt.Sprintf("%s_OIDC_CLIENT_ID", prefix), out.ClientID) + out.clientSecretFile = envOrValue(fmt.Sprintf("%s_OIDC_CLIENT_SECRET_FILE", prefix), out.clientSecretFile) 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) @@ -198,6 +220,8 @@ func applyGitOptions(out *Config, cfg map[string]string) { out.RekorMode = v case strings.EqualFold(k, "gitsign.clientID"): out.ClientID = v + case strings.EqualFold(k, "gitsign.clientSecretFile"): + out.clientSecretFile = v case strings.EqualFold(k, "gitsign.redirectURL"): out.RedirectURL = v case strings.EqualFold(k, "gitsign.issuer"): diff --git a/internal/config/config_test.go b/internal/config/config_test.go index b63bcaca..f0e3c2c8 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -99,7 +99,7 @@ func TestGet(t *testing.T) { t.Fatal(err) } - if diff := cmp.Diff(want, got); diff != "" { + if diff := cmp.Diff(want, got, cmp.AllowUnexported(Config{})); diff != "" { t.Error(diff) } } diff --git a/internal/fulcio/identity.go b/internal/fulcio/identity.go index fbc901b7..79748c87 100644 --- a/internal/fulcio/identity.go +++ b/internal/fulcio/identity.go @@ -195,6 +195,12 @@ func NewIdentityFactory(in io.Reader, out io.Writer) *IdentityFactory { func (f *IdentityFactory) NewIdentity(ctx context.Context, cfg *config.Config) (*Identity, error) { clientID := cfg.ClientID + clientSecret, err := cfg.ClientSecret() + + if err != nil { + return nil, err + } + // Autoclose only works if we don't go through the identity selection page // (otherwise it'll show a countdown timer that doesn't work) if cfg.ConnectorID == "" { @@ -247,10 +253,11 @@ func (f *IdentityFactory) NewIdentity(ctx context.Context, cfg *config.Config) ( client, err := fulcio.NewClient(cfg.Fulcio, fulcio.OIDCOptions{ - Issuer: cfg.Issuer, - ClientID: clientID, - RedirectURL: cfg.RedirectURL, - TokenGetter: authFlow, + Issuer: cfg.Issuer, + ClientID: clientID, + ClientSecret: clientSecret, + RedirectURL: cfg.RedirectURL, + TokenGetter: authFlow, }) if err != nil { return nil, fmt.Errorf("error creating Fulcio client: %w", err)