Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for configuration from yaml file #1687

Merged
merged 2 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
fulciogrpc "github.com/sigstore/fulcio/pkg/generated/protobuf"
"github.com/sigstore/fulcio/pkg/log"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"gopkg.in/yaml.v3"
)

const defaultOIDCDiscoveryTimeout = 10 * time.Second
Expand All @@ -48,7 +49,7 @@ type verifierWithConfig struct {
}

type FulcioConfig struct {
OIDCIssuers map[string]OIDCIssuer `json:"OIDCIssuers,omitempty"`
OIDCIssuers map[string]OIDCIssuer `json:"OIDCIssuers,omitempty" yaml:"oidc-issuers,omitempty"`

// A meta issuer has a templated URL of the form:
// https://oidc.eks.*.amazonaws.com/id/*
Expand All @@ -57,7 +58,7 @@ type FulcioConfig struct {
// other special characters) Some examples we want to match:
// * https://oidc.eks.us-west-2.amazonaws.com/id/B02C93B6A2D30341AD01E1B6D48164CB
// * https://container.googleapis.com/v1/projects/mattmoor-credit/locations/us-west1-b/clusters/tenant-cluster
MetaIssuers map[string]OIDCIssuer `json:"MetaIssuers,omitempty"`
MetaIssuers map[string]OIDCIssuer `json:"MetaIssuers,omitempty" yaml:"meta-issuers,omitempty"`

// verifiers is a fixed mapping from our OIDCIssuers to their OIDC verifiers.
verifiers map[string][]*verifierWithConfig
Expand All @@ -67,24 +68,24 @@ type FulcioConfig struct {

type OIDCIssuer struct {
// The expected issuer of an OIDC token
IssuerURL string `json:"IssuerURL,omitempty"`
IssuerURL string `json:"IssuerURL,omitempty" yaml:"issuer-url,omitempty"`
// The expected client ID of the OIDC token
ClientID string `json:"ClientID"`
ClientID string `json:"ClientID" yaml:"client-id,omitempty"`
// Used to determine the subject of the certificate and if additional
// certificate values are needed
Type IssuerType `json:"Type"`
Type IssuerType `json:"Type" yaml:"type,omitempty"`
// Optional, if the issuer is in a different claim in the OIDC token
IssuerClaim string `json:"IssuerClaim,omitempty"`
IssuerClaim string `json:"IssuerClaim,omitempty" yaml:"issuer-claim,omitempty"`
// The domain that must be present in the subject for 'uri' issuer types
// Also used to create an email for 'username' issuer types
SubjectDomain string `json:"SubjectDomain,omitempty"`
SubjectDomain string `json:"SubjectDomain,omitempty" yaml:"subject-domain,omitempty"`
// SPIFFETrustDomain specifies the trust domain that 'spiffe' issuer types
// issue ID tokens for. Tokens with a different trust domain will be
// rejected.
SPIFFETrustDomain string `json:"SPIFFETrustDomain,omitempty"`
SPIFFETrustDomain string `json:"SPIFFETrustDomain,omitempty" yaml:"spiffe-trust-domain,omitempty"`
// Optional, the challenge claim expected for the issuer
// Set if using a custom issuer
ChallengeClaim string `json:"ChallengeClaim,omitempty"`
ChallengeClaim string `json:"ChallengeClaim,omitempty" yaml:"challenge-claim,omitempty"`
}

func metaRegex(issuer string) (*regexp.Regexp, error) {
Expand Down Expand Up @@ -287,7 +288,9 @@ const (
func parseConfig(b []byte) (cfg *FulcioConfig, err error) {
cfg = &FulcioConfig{}
if err := json.Unmarshal(b, cfg); err != nil {
return nil, fmt.Errorf("unmarshal: %w", err)
if err = yaml.Unmarshal(b, cfg); err != nil {
return nil, fmt.Errorf("unmarshal: %w", err)
}
}

return cfg, nil
Expand Down
140 changes: 140 additions & 0 deletions pkg/server/grpc_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,146 @@ func TestGetConfiguration(t *testing.T) {
}
}

// Tests GetConfigurationFromYaml API
func TestGetConfigurationFromYaml(t *testing.T) {
_, emailIssuer := newOIDCIssuer(t)
_, spiffeIssuer := newOIDCIssuer(t)
_, uriIssuer := newOIDCIssuer(t)
_, usernameIssuer := newOIDCIssuer(t)
_, k8sIssuer := newOIDCIssuer(t)
_, buildkiteIssuer := newOIDCIssuer(t)
_, gitHubIssuer := newOIDCIssuer(t)
_, gitLabIssuer := newOIDCIssuer(t)
_, codefreshIssuer := newOIDCIssuer(t)

issuerDomain, err := url.Parse(usernameIssuer)
if err != nil {
t.Fatal("issuer URL could not be parsed", err)
}

yamlBytes := []byte(fmt.Sprintf(`
oidc-issuers:
%v:
issuer-url: %q
client-id: sigstore
type: spiffe
spiffe-trust-domain: example.com
%v:
issuer-url: %q
client-id: sigstore
type: uri
subject-domain: %q
%v:
issuer-url: %q
client-id: sigstore
type: email
%v:
issuer-url: %q
client-id: sigstore
type: username
subject-domain: %q
%v:
issuer-url: %q
client-id: sigstore
type: buildkite-job
%v:
issuer-url: %q
client-id: sigstore
type: github-workflow
%v:
issuer-url: %q
client-id: sigstore
type: gitlab-pipeline
%v:
issuer-url: %q
client-id: sigstore
type: codefresh-workflow
meta-issuers:
%v:
client-id: sigstore
type: kubernetes`,
spiffeIssuer, spiffeIssuer,
uriIssuer, uriIssuer, uriIssuer,
emailIssuer, emailIssuer,
usernameIssuer, usernameIssuer, issuerDomain.Hostname(),
buildkiteIssuer, buildkiteIssuer,
gitHubIssuer, gitHubIssuer,
gitLabIssuer, gitLabIssuer,
codefreshIssuer, codefreshIssuer,
k8sIssuer))

cfg, err := config.Read(yamlBytes)
if err != nil {
t.Fatalf("config.Read() = %v", err)
}

ctClient, eca := createCA(cfg, t)
ctx := context.Background()
server, conn := setupGRPCForTest(ctx, t, cfg, ctClient, eca)
defer func() {
server.Stop()
conn.Close()
}()

client := protobuf.NewCAClient(conn)

config, err := client.GetConfiguration(ctx, &protobuf.GetConfigurationRequest{})
if err != nil {
t.Fatal("GetConfiguration failed", err)
}

if len(config.Issuers) != 9 {
t.Fatalf("expected 9 issuers, got %v", len(config.Issuers))
}

expectedIssuers := map[string]bool{
emailIssuer: true, spiffeIssuer: true, uriIssuer: true,
usernameIssuer: true, k8sIssuer: true, gitHubIssuer: true,
buildkiteIssuer: true, gitLabIssuer: true, codefreshIssuer: true,
}
for _, iss := range config.Issuers {
var issURL string
switch {
case expectedIssuers[iss.GetIssuerUrl()]:
delete(expectedIssuers, iss.GetIssuerUrl())
issURL = iss.GetIssuerUrl()
case expectedIssuers[iss.GetWildcardIssuerUrl()]:
delete(expectedIssuers, iss.GetWildcardIssuerUrl())
issURL = iss.GetWildcardIssuerUrl()
default:
t.Fatal("issuer missing from expected issuers")
}

if iss.Audience != "sigstore" {
t.Fatalf("expected audience to be sigstore, got %v", iss.Audience)
}

if issURL == emailIssuer {
if iss.ChallengeClaim != "email" {
t.Fatalf("expected email claim for email PoP challenge, got %v", iss.ChallengeClaim)
}
} else {
if iss.ChallengeClaim != "sub" {
t.Fatalf("expected sub claim for non-email PoP challenge, got %v", iss.ChallengeClaim)
}
}

if issURL == spiffeIssuer {
if iss.SpiffeTrustDomain != "example.com" {
t.Fatalf("expected SPIFFE trust domain example.com, got %v", iss.SpiffeTrustDomain)
}
} else {
if iss.SpiffeTrustDomain != "" {
t.Fatalf("expected no SPIFFE trust domain, got %v", iss.SpiffeTrustDomain)
}
}
}

if len(expectedIssuers) != 0 {
t.Fatal("not all issuers were found in configuration")
}
}

// oidcTestContainer holds values needed for each API test invocation
type oidcTestContainer struct {
Signer jose.Signer
Expand Down
Loading