diff --git a/.github/workflows/verify-k8s.yml b/.github/workflows/verify-k8s.yml index a27efbb5c..e86859d01 100644 --- a/.github/workflows/verify-k8s.yml +++ b/.github/workflows/verify-k8s.yml @@ -52,12 +52,11 @@ jobs: include: - issuer: "OIDC Issuer" - issuer-config: | - "OIDCIssuers": {"https://kubernetes.default.svc": {"IssuerURL": "https://kubernetes.default.svc","ClientID": "sigstore","Type": "kubernetes"}} + issuer-config: + "oidc-issuers:\n https://kubernetes.default.svc:\n issuer-url: \"https://kubernetes.default.svc\"\n client-id: \"sigstore\"\n type: \"kubernetes\"" - issuer: "Meta Issuer" - issuer-config: | - "MetaIssuers": {"https://kubernetes.*.svc": {"ClientID": "sigstore","Type": "kubernetes"}} - + issuer-config: + "meta-issuers:\n https://kubernetes.*.svc: \n client-id: \"sigstore\"\n type: \"kubernetes\"" env: # https://github.com/google/go-containerregistry/pull/125 allows insecure registry for # '*.local' hostnames. This works both for `ko` and our own tag-to-digest resolution logic, @@ -124,10 +123,8 @@ jobs: name: fulcio-config namespace: fulcio-system data: - config.json: |- - { - ${{ matrix.issuer-config }} - } + config.yaml: |- + ${{ matrix.issuer-config }} server.yaml: |- host: 0.0.0.0 port: 5555 @@ -139,7 +136,6 @@ jobs: ct-log-url: "" log_type: prod EOF - # Create secret needed to use fileca cat < config/fulcio-secret.yaml apiVersion: v1 diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index b3683b9eb..14f6b0a53 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -91,5 +91,4 @@ jobs: - name: check-config run: | set -e - go run federation/main.go - git diff --exit-code + go test -timeout 30s -run ^TestLoadFulcioConfig$ github.com/sigstore/fulcio/pkg/config diff --git a/cmd/app/serve.go b/cmd/app/serve.go index de0b28194..016e42ba0 100644 --- a/cmd/app/serve.go +++ b/cmd/app/serve.go @@ -87,7 +87,7 @@ func newServeCmd() *cobra.Command { cmd.Flags().String("hsm-caroot-id", "", "HSM ID for Root CA (only used with --ca pkcs11ca)") cmd.Flags().String("ct-log-url", "http://localhost:6962/test", "host and path (with log prefix at the end) to the ct log") cmd.Flags().String("ct-log-public-key-path", "", "Path to a PEM-encoded public key of the CT log, used to verify SCTs") - cmd.Flags().String("config-path", "/etc/fulcio-config/config.json", "path to fulcio config json") + cmd.Flags().String("config-path", "/etc/fulcio-config/config.yaml", "path to fulcio config yaml") cmd.Flags().String("pkcs11-config-path", "config/crypto11.conf", "path to fulcio pkcs11 config file") cmd.Flags().String("fileca-cert", "", "Path to CA certificate") cmd.Flags().String("fileca-key", "", "Path to CA encrypted private key") diff --git a/config/config.jsn b/config/config.jsn deleted file mode 100644 index 27fae5423..000000000 --- a/config/config.jsn +++ /dev/null @@ -1,35 +0,0 @@ -{ - "OIDCIssuers": { - "https://accounts.google.com": { - "IssuerURL": "https://accounts.google.com", - "ClientID": "sigstore", - "Type": "email" - }, - "https://oauth2.sigstore.dev/auth": { - "IssuerURL": "https://oauth2.sigstore.dev/auth", - "ClientID": "sigstore", - "Type": "email" - }, - "http://dex-idp:8888/auth": { - "IssuerURL": "http://dex-idp:8888/auth", - "ClientID": "fulcio", - "IssuerClaim": "$.federated_claims.connector_id", - "Type": "email" - }, - "https://token.actions.githubusercontent.com": { - "IssuerURL": "https://token.actions.githubusercontent.com", - "ClientID": "sigstore", - "Type": "github-workflow" - }, - "https://oidc.codefresh.io": { - "IssuerURL": "https://oidc.codefresh.io", - "ClientID": "sigstore", - "Type": "codefresh-workflow" - }, - "https://issuer.enforce.dev": { - "IssuerURL": "https://issuer.enforce.dev", - "ClientID": "sigstore", - "Type": "chainguard-identity" - } - } -} diff --git a/config/fulcio-config.yaml b/config/fulcio-config.yaml deleted file mode 100644 index 0f7a0aded..000000000 --- a/config/fulcio-config.yaml +++ /dev/null @@ -1,121 +0,0 @@ -# -# Copyright 2021 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: v1 -data: - config.json: |- - { - "OIDCIssuers": { - "https://accounts.google.com": { - "IssuerURL": "https://accounts.google.com", - "ClientID": "sigstore", - "Type": "email" - }, - "https://agent.buildkite.com": { - "IssuerURL": "https://agent.buildkite.com", - "ClientID": "sigstore", - "Type": "buildkite-job" - }, - "https://allow.pub": { - "IssuerURL": "https://allow.pub", - "ClientID": "sigstore", - "Type": "spiffe", - "SPIFFETrustDomain": "allow.pub" - }, - "https://auth-staging.eclipse.org/realms/sigstore": { - "IssuerURL": "https://auth-staging.eclipse.org/realms/sigstore", - "ClientID": "sigstore", - "Type": "email" - }, - "https://auth.eclipse.org/auth/realms/sigstore": { - "IssuerURL": "https://auth.eclipse.org/auth/realms/sigstore", - "ClientID": "sigstore", - "Type": "email" - }, - "https://dev.gitlab.org": { - "IssuerURL": "https://dev.gitlab.org", - "ClientID": "sigstore", - "Type": "gitlab-pipeline" - }, - "https://gitlab.archlinux.org": { - "IssuerURL": "https://gitlab.archlinux.org", - "ClientID": "sigstore", - "Type": "gitlab-pipeline" - }, - "https://gitlab.com": { - "IssuerURL": "https://gitlab.com", - "ClientID": "sigstore", - "Type": "gitlab-pipeline" - }, - "https://issuer.enforce.dev": { - "IssuerURL": "https://issuer.enforce.dev", - "ClientID": "sigstore", - "Type": "chainguard-identity" - }, - "https://oauth2.sigstore.dev/auth": { - "IssuerURL": "https://oauth2.sigstore.dev/auth", - "ClientID": "sigstore", - "Type": "email", - "IssuerClaim": "$.federated_claims.connector_id" - }, - "https://oidc.codefresh.io": { - "IssuerURL": "https://oidc.codefresh.io", - "ClientID": "sigstore", - "Type": "codefresh-workflow" - }, - "https://ops.gitlab.net": { - "IssuerURL": "https://ops.gitlab.net", - "ClientID": "sigstore", - "Type": "gitlab-pipeline" - }, - "https://token.actions.githubusercontent.com": { - "IssuerURL": "https://token.actions.githubusercontent.com", - "ClientID": "sigstore", - "Type": "github-workflow" - } - }, - "MetaIssuers": { - "https://*.oic.prod-aks.azure.com/*": { - "ClientID": "sigstore", - "Type": "kubernetes" - }, - "https://container.googleapis.com/v1/projects/*/locations/*/clusters/*": { - "ClientID": "sigstore", - "Type": "kubernetes" - }, - "https://oidc.eks.*.amazonaws.com/id/*": { - "ClientID": "sigstore", - "Type": "kubernetes" - }, - "https://oidc.prod-aks.azure.com/*": { - "ClientID": "sigstore", - "Type": "kubernetes" - }, - "https://token.actions.githubusercontent.com/*": { - "ClientID": "sigstore", - "Type": "github-workflow" - } - } - } - server.yaml: |- - host: 0.0.0.0 - port: 5555 - grpc-port: 5554 - ca: googleca - ct-log-url: http://ct-log/test - log_type: prod -kind: ConfigMap -metadata: - name: fulcio-config - namespace: fulcio-system diff --git a/config/identity/config.yaml b/config/identity/config.yaml new file mode 100644 index 000000000..298d89c20 --- /dev/null +++ b/config/identity/config.yaml @@ -0,0 +1,81 @@ +# Copyright 2024 The Sigstore Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +oidc-issuers: + https://accounts.google.com: + issuer-url: https://accounts.google.com + client-id: sigstore + type: email + https://agent.buildkite.com: + issuer-url: https://agent.buildkite.com + client-id: sigstore + type: buildkite-job + https://allow.pub: + issuer-url: https://allow.pub + client-id: sigstore + type: spiffe + spiffe-trust-domain: allow.pub + https://auth.eclipse.org/auth/realms/sigstore: + issuer-url: https://auth.eclipse.org/auth/realms/sigstore + client-id: sigstore + type: email + https://dev.gitlab.org: + issuer-url: https://dev.gitlab.org + client-id: sigstore + type: gitlab-pipeline + https://gitlab.archlinux.org: + issuer-url: https://gitlab.archlinux.org + client-id: sigstore + type: gitlab-pipeline + https://gitlab.com: + issuer-url: https://gitlab.com + client-id: sigstore + type: gitlab-pipeline + https://issuer.enforce.dev: + issuer-url: https://issuer.enforce.dev + client-id: sigstore + type: chainguard-identity + https://oauth2.sigstore.dev/auth: + issuer-url: https://oauth2.sigstore.dev/auth + client-id: sigstore + type: email + issuer-claim: $.federated_claims.connector_id + https://oidc.codefresh.io: + issuer-url: https://oidc.codefresh.io + client-id: sigstore + type: codefresh-workflow + https://ops.gitlab.net: + issuer-url: https://ops.gitlab.net + client-id: sigstore + type: gitlab-pipeline + https://token.actions.githubusercontent.com: + issuer-url: https://token.actions.githubusercontent.com + client-id: sigstore + type: github-workflow +meta-issuers: + https://*.oic.prod-aks.azure.com/*: + client-id: sigstore + type: kubernetes + https://container.googleapis.com/v1/projects/*/locations/*/clusters/*: + client-id: sigstore + type: kubernetes + https://oidc.eks.*.amazonaws.com/id/*: + client-id: sigstore + type: kubernetes + https://oidc.prod-aks.azure.com/*: + client-id: sigstore + type: kubernetes + https://token.actions.githubusercontent.com/*: + client-id: sigstore + type: github-workflow diff --git a/docker-compose.yml b/docker-compose.yml index cee2a6410..a720943c9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -37,7 +37,7 @@ services: - "${FULCIO_METRICS_PORT:-2112}:2112" volumes: - ~/.config/gcloud:/root/.config/gcloud/:z # for GCP authentication - - ${FULCIO_CONFIG:-./config/config.jsn}:/etc/fulcio-config/config.json:z + - ${FULCIO_CONFIG:-./config/identity/config.yaml}:/etc/fulcio-config/config.yaml:z healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5555/healthz"] interval: 10s diff --git a/docs/oidc.md b/docs/oidc.md index d0907bfc9..a58bc4aad 100644 --- a/docs/oidc.md +++ b/docs/oidc.md @@ -10,9 +10,7 @@ Sigstore runs a federated OIDC identity provider, Dex. Users authenticate to the To add a new OIDC issuer: -* Add a file under the [`federation` folder](https://github.com/sigstore/fulcio/tree/main/federation) with the URL, new issuer type name, and contact ([example](https://github.com/sigstore/fulcio/blob/8975dfd/federation/agent.buildkite.com/config.yaml)) -* Add the new issuer to the [configuration](https://github.com/sigstore/fulcio/blob/main/config/fulcio-config.yaml) by running `go run federation/main.go` -* Add the new issuer to the [`identity` folder](https://github.com/sigstore/fulcio/tree/main/pkg/identity) ([example](https://github.com/sigstore/fulcio/tree/main/pkg/identity/buildkite)). You will define an `Issuer` type and a way to map the token to the certificate extensions. +* Add the new issuer to the [configuration](https://github.com/sigstore/fulcio/blob/main/config/identity/config.yaml) and to the [`identity` folder](https://github.com/sigstore/fulcio/tree/main/pkg/identity) ([example](https://github.com/sigstore/fulcio/tree/main/pkg/identity/buildkite)). You will define an `Issuer` type and a way to map the token to the certificate extensions. * Define a constant with the issuer type name in the [configuration](https://github.com/sigstore/fulcio/blob/afeadb3b7d11f704489637cabc4e150dea3e00ed/pkg/config/config.go#L213-L221), add update the [tests](https://github.com/sigstore/fulcio/blob/afeadb3b7d11f704489637cabc4e150dea3e00ed/pkg/config/config_test.go#L473-L503) * Map the issuer type to the token claim that will be signed over when requesting a token [here](https://github.com/sigstore/fulcio/blob/afeadb3b7d11f704489637cabc4e150dea3e00ed/pkg/config/config.go#L464-L486). You can likely just use `sub`. * Add a case statement to map the issuer constant to the issuer type you created [here](https://github.com/sigstore/fulcio/blob/4d9d96a/pkg/server/issuer_pool.go#L40-L62) diff --git a/federation/main.go b/federation/main.go deleted file mode 100644 index 7926f772a..000000000 --- a/federation/main.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2021 The Sigstore Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -package main - -import ( - "encoding/json" - "os" - "path/filepath" - - "github.com/sigstore/fulcio/pkg/config" - "gopkg.in/yaml.v3" -) - -var rootPaths = []string{"federation", "federation/external"} -var boilerPlate = `# -# Copyright 2021 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -` - -type federationConfig struct { - URL string - Type string - IssuerClaim string - SpiffeTrustDomain string -} - -func main() { - matches := []string{} - for _, rp := range rootPaths { - glob := filepath.Join(rp, "*/config.yaml") - globs, err := filepath.Glob(glob) - if err != nil { - panic(err) - } - matches = append(matches, globs...) - } - fulcioConfig := &config.FulcioConfig{ - OIDCIssuers: map[string]config.OIDCIssuer{}, - MetaIssuers: map[string]config.OIDCIssuer{ - // EKS Cluster OIDC issuers - "https://oidc.eks.*.amazonaws.com/id/*": { - ClientID: "sigstore", - Type: "kubernetes", - }, - // GKE Cluster OIDC issuers - "https://container.googleapis.com/v1/projects/*/locations/*/clusters/*": { - ClientID: "sigstore", - Type: "kubernetes", - }, - // AKS Cluster OIDC issuers - "https://oidc.prod-aks.azure.com/*": { - ClientID: "sigstore", - Type: "kubernetes", - }, - "https://*.oic.prod-aks.azure.com/*": { - ClientID: "sigstore", - Type: "kubernetes", - }, - // GitHub Actions OIDC unique enterprise issuers - "https://token.actions.githubusercontent.com/*": { - ClientID: "sigstore", - Type: "github-workflow", - }, - }, - } - for _, m := range matches { - b, err := os.ReadFile(m) - if err != nil { - panic(err) - } - cfg := federationConfig{} - if err := yaml.Unmarshal(b, &cfg); err != nil { - panic(err) - } - - fulcioCfg := config.OIDCIssuer{ - IssuerURL: cfg.URL, - ClientID: "sigstore", - Type: config.IssuerType(cfg.Type), - IssuerClaim: cfg.IssuerClaim, - } - if fulcioCfg.Type == config.IssuerTypeSpiffe { - fulcioCfg.SPIFFETrustDomain = cfg.SpiffeTrustDomain - } - fulcioConfig.OIDCIssuers[cfg.URL] = fulcioCfg - } - - m, err := json.MarshalIndent(fulcioConfig, "", " ") - if err != nil { - panic(err) - } - - // Update the yaml - yb, err := os.ReadFile("config/fulcio-config.yaml") - if err != nil { - panic(err) - } - - cm := map[string]interface{}{} - if err := yaml.Unmarshal(yb, &cm); err != nil { - panic(err) - } - data := cm["data"].(map[string]interface{}) - data["config.json"] = string(m) - - newYaml, err := yaml.Marshal(cm) - if err != nil { - panic(err) - } - - yamlWithBoilerplate := boilerPlate + string(newYaml) - - if err := os.WriteFile("config/fulcio-config.yaml", []byte(yamlWithBoilerplate), 0600); err != nil { - panic(err) - } -} diff --git a/pkg/config/config_network_test.go b/pkg/config/config_network_test.go index 4e00720bf..00f139824 100644 --- a/pkg/config/config_network_test.go +++ b/pkg/config/config_network_test.go @@ -28,10 +28,51 @@ import ( "github.com/sigstore/fulcio/pkg/certificate" ) -func TestLoad(t *testing.T) { +func TestLoadYamlConfig(t *testing.T) { + td := t.TempDir() + cfgPath := filepath.Join(td, "config.yaml") + if err := os.WriteFile(cfgPath, []byte(validYamlCfg), 0644); err != nil { + t.Fatal(err) + } + + cfg, err := Load(cfgPath) + if err != nil { + t.Fatal(err) + } + got, ok := cfg.GetIssuer("https://accounts.google.com") + if !ok { + t.Error("expected true, got false") + } + if got.ClientID != "foo" { + t.Errorf("expected foo, got %s", got.ClientID) + } + if got.IssuerURL != "https://accounts.google.com" { + t.Errorf("expected https://accounts.google.com, got %s", got.IssuerURL) + } + if got := len(cfg.OIDCIssuers); got != 1 { + t.Errorf("expected 1 issuer, got %d", got) + } + + got, ok = cfg.GetIssuer("https://oidc.eks.fantasy-land.amazonaws.com/id/CLUSTERIDENTIFIER") + if !ok { + t.Error("expected true, got false") + } + if got.ClientID != "bar" { + t.Errorf("expected bar, got %s", got.ClientID) + } + if got.IssuerURL != "https://oidc.eks.fantasy-land.amazonaws.com/id/CLUSTERIDENTIFIER" { + t.Errorf("expected https://oidc.eks.fantasy-land.amazonaws.com/id/CLUSTERIDENTIFIER, got %s", got.IssuerURL) + } + + if _, ok := cfg.GetIssuer("not_an_issuer"); ok { + t.Error("no error returned from an unconfigured issuer") + } +} + +func TestLoadJsonConfig(t *testing.T) { td := t.TempDir() cfgPath := filepath.Join(td, "config.json") - if err := os.WriteFile(cfgPath, []byte(validCfg), 0644); err != nil { + if err := os.WriteFile(cfgPath, []byte(validJSONCfg), 0644); err != nil { t.Fatal(err) } @@ -128,7 +169,7 @@ func TestLoadDefaults(t *testing.T) { td := t.TempDir() // Don't put anything here! - cfgPath := filepath.Join(td, "config.json") + cfgPath := filepath.Join(td, "config.yaml") cfg, err := Load(cfgPath) if err != nil { t.Fatal(err) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 390bd6f6b..042fe9b8f 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -27,7 +27,20 @@ import ( "github.com/sigstore/fulcio/pkg/generated/protobuf" ) -var validCfg = ` +var validYamlCfg = ` +oidc-issuers: + https://accounts.google.com: + issuer-url: https://accounts.google.com + client-id: foo + type: email + challenge-claim: email +meta-issuers: + https://oidc.eks.*.amazonaws.com/id/*: + client-id: bar + type: kubernetes +` + +var validJSONCfg = ` { "OIDCIssuers": { "https://accounts.google.com": { diff --git a/pkg/config/fulcio_config_test.go b/pkg/config/fulcio_config_test.go new file mode 100644 index 000000000..c0c464523 --- /dev/null +++ b/pkg/config/fulcio_config_test.go @@ -0,0 +1,73 @@ +// Copyright 2024 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +//go:build !hermetic + +package config + +import ( + "os" + "path/filepath" + "runtime" + "testing" +) + +// The config/identity/config.yaml is a config file that is reflected directly +// to the public good instance. +// This test checks that the config.yaml is valid and can be properly used +// on the public good instance. +func TestLoadFulcioConfig(t *testing.T) { + _, path, _, _ := runtime.Caller(0) + basepath := filepath.Dir(path) + b, err := os.ReadFile(basepath + "/../../config/identity/config.yaml") + if err != nil { + t.Errorf("read file: %v", err) + } + + fulcioConfig, err := Read(b) + if err != nil { + t.Fatal(err) + } + + for issuerURL := range fulcioConfig.OIDCIssuers { + got, ok := fulcioConfig.GetIssuer(issuerURL) + if !ok { + t.Error("expected true, got false") + } + if got.ClientID != "sigstore" { + t.Errorf("expected sigstore, got %s", got.ClientID) + } + if got.IssuerURL != issuerURL { + t.Errorf("expected %s, got %s", issuerURL, got.IssuerURL) + } + if string(got.Type) == "" { + t.Errorf("Issuer Type should not be empty") + } + if got.Type == IssuerTypeCIProvider { + if got.CIProvider == "" { + t.Errorf("Issuer CIProvider should not be empty when Type is ci-provider") + } + } + if _, ok := fulcioConfig.GetIssuer("not_an_issuer"); ok { + t.Error("no error returned from an unconfigured issuer") + } + } + + for _, metaIssuer := range fulcioConfig.MetaIssuers { + if metaIssuer.ClientID != "sigstore" { + t.Errorf("expected sigstore, got %s", metaIssuer.ClientID) + } + } +} diff --git a/tools/loadtest/README.md b/tools/loadtest/README.md index 0e2af9930..1a5d7f014 100644 --- a/tools/loadtest/README.md +++ b/tools/loadtest/README.md @@ -24,7 +24,7 @@ Confirm a successful install with `locust -V`, which should print the version. Y ### Fetching identity token -To fetch a certificate, you will need an OIDC token from one of the [OIDC issuers](https://github.com/sigstore/fulcio/blob/main/config/fulcio-config.yaml). One way is to fetch a token from Google. Note that you will need to install [`gcloud`](https://cloud.google.com/sdk/gcloud) and create a service account. A service account is necessary for the `--include-email` flag, which is needed to get an OIDC token with the correct format for Fulcio. +To fetch a certificate, you will need an OIDC token from one of the [OIDC issuers](https://github.com/sigstore/fulcio/blob/main/config/identity/config.yaml). One way is to fetch a token from Google. Note that you will need to install [`gcloud`](https://cloud.google.com/sdk/gcloud) and create a service account. A service account is necessary for the `--include-email` flag, which is needed to get an OIDC token with the correct format for Fulcio. Run the following command, and record the output: