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

CiProvider as a new OIDCIssuer type #1679

Merged
merged 47 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
486397e
drafting generic issuer implementation
javanlacerda May 27, 2024
f74f6a6
adding license o yaml file
javanlacerda May 27, 2024
cc5a602
remove yaml2.0
javanlacerda May 28, 2024
9c2c0f8
start removing IssuerType usage
javanlacerda May 28, 2024
1261f88
update license year
javanlacerda May 28, 2024
73c988c
log
javanlacerda Jun 4, 2024
be6acf8
adding ci provider flag
javanlacerda Jun 6, 2024
ee93df8
removing drafting logic for generic principal, to be done in another pr
javanlacerda Jun 6, 2024
f7cd58a
fixes
javanlacerda Jun 6, 2024
ba9e42e
generate config
javanlacerda Jun 6, 2024
e0e6c53
set ci provider as a new type instead of a flag
javanlacerda Jun 11, 2024
4930187
rollback isserToChallengeC
javanlacerda Jun 11, 2024
cafe1db
change module name to ci_provider
javanlacerda Jun 11, 2024
c365153
remove IsCiProvider from test
javanlacerda Jun 11, 2024
39bdc22
removing underscore
javanlacerda Jun 11, 2024
af67958
logging
javanlacerda Jun 10, 2024
3c66a3a
implementing ci provider principal logic and test for Name function
javanlacerda Jun 12, 2024
2cbe6da
moving embed logic to embed function and test
javanlacerda Jun 13, 2024
aa257ed
adding test for ci provider WorkflowPrincipalFromIDToken
javanlacerda Jun 14, 2024
4a86f57
adding test issuer for ci provider
javanlacerda Jun 14, 2024
342f8ad
change providers config file name
javanlacerda Jun 14, 2024
2b1ea3f
refactoring
javanlacerda Jun 19, 2024
c50cd30
comment applyTemplate
javanlacerda Jun 19, 2024
cedc9cf
removing github for now, we should add it futher for rollouting
javanlacerda Jun 19, 2024
31f8e11
Adding option to check the required claim exist
javanlacerda Jun 20, 2024
8eff532
omit subytype for json
javanlacerda Jun 24, 2024
b480032
rename issuers metadata
javanlacerda Jun 24, 2024
6558b31
update the templates data order to prioritize default data over claim…
javanlacerda Jun 24, 2024
5c7ee85
adding error handling for applyTemplateOrReplace
javanlacerda Jun 24, 2024
54c4e6d
fixes and refactoring
javanlacerda Jun 25, 2024
8f54c6a
set token.issuer as extension issuer
javanlacerda Jun 25, 2024
a35b6cc
remove load for providers, merge with fulcio config
javanlacerda Jun 25, 2024
66a07a0
remove providers config file
javanlacerda Jun 26, 2024
e7cd08f
adding check for parsing templates
javanlacerda Jun 26, 2024
13e7059
remove structs usage, using mapstructure instead
javanlacerda Jun 26, 2024
397eee3
renamme IssuerMetadata
javanlacerda Jun 30, 2024
04ef3c7
update SubjectAlternativeNameTemplate comment
javanlacerda Jun 30, 2024
96a6663
several fixes
javanlacerda Jun 30, 2024
611d5c4
update for using reflect instead of mapstructure
javanlacerda Jun 30, 2024
1a481eb
adding test for ciprovider API
javanlacerda Jul 1, 2024
8ab3f2a
adding json tags for Extensions
javanlacerda Jul 1, 2024
31f0318
adding CIIssuerMetadata json and yaml mapping
javanlacerda Jul 2, 2024
b9cc94e
fixes
javanlacerda Jul 3, 2024
42f803c
adding test for applyTemplateOrReplace
javanlacerda Jul 3, 2024
478c37a
omit CIIIssuerMetadata
javanlacerda Jul 3, 2024
7455b87
adding tests case for ApplyTemplateOrReplace
javanlacerda Jul 4, 2024
e248f43
update code for using Elem() instead of indirect
javanlacerda Jul 8, 2024
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
38 changes: 19 additions & 19 deletions pkg/certificate/extensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,69 +69,69 @@ type Extensions struct {
// Deprecated
// Triggering event of the Github Workflow. Matches the `event_name` claim of ID
// tokens from Github Actions
GithubWorkflowTrigger string // OID 1.3.6.1.4.1.57264.1.2
GithubWorkflowTrigger string `json:"GithubWorkflowTrigger,omitempty" yaml:"github-workflow-trigger,omitempty"` // OID 1.3.6.1.4.1.57264.1.2

// Deprecated
// SHA of git commit being built in Github Actions. Matches the `sha` claim of ID
// tokens from Github Actions
GithubWorkflowSHA string // OID 1.3.6.1.4.1.57264.1.3
GithubWorkflowSHA string `json:"GithubWorkflowSHA,omitempty" yaml:"github-workflow-sha,omitempty"` // OID 1.3.6.1.4.1.57264.1.3

// Deprecated
// Name of Github Actions Workflow. Matches the `workflow` claim of the ID
// tokens from Github Actions
GithubWorkflowName string // OID 1.3.6.1.4.1.57264.1.4
GithubWorkflowName string `json:"GithubWorkflowName,omitempty" yaml:"github-workflow-name,omitempty"` // OID 1.3.6.1.4.1.57264.1.4

// Deprecated
// Repository of the Github Actions Workflow. Matches the `repository` claim of the ID
// tokens from Github Actions
GithubWorkflowRepository string // OID 1.3.6.1.4.1.57264.1.5
GithubWorkflowRepository string `json:"GithubWorkflowRepository,omitempty" yaml:"github-workflow-repository,omitempty"` // OID 1.3.6.1.4.1.57264.1.5

// Deprecated
// Git Ref of the Github Actions Workflow. Matches the `ref` claim of the ID tokens
// from Github Actions
GithubWorkflowRef string // 1.3.6.1.4.1.57264.1.6
GithubWorkflowRef string `json:"GithubWorkflowRef,omitempty" yaml:"github-workflow-ref,omitempty"` // 1.3.6.1.4.1.57264.1.6

// Reference to specific build instructions that are responsible for signing.
BuildSignerURI string // 1.3.6.1.4.1.57264.1.9
BuildSignerURI string `json:"BuildSignerURI,omitempty" yaml:"build-signer-uri,omitempty"` // 1.3.6.1.4.1.57264.1.9

// Immutable reference to the specific version of the build instructions that is responsible for signing.
BuildSignerDigest string // 1.3.6.1.4.1.57264.1.10
BuildSignerDigest string `json:"BuildSignerDigest,omitempty" yaml:"build-signer-digest,omitempty"` // 1.3.6.1.4.1.57264.1.10

// Specifies whether the build took place in platform-hosted cloud infrastructure or customer/self-hosted infrastructure.
RunnerEnvironment string // 1.3.6.1.4.1.57264.1.11
RunnerEnvironment string `json:"RunnerEnvironment,omitempty" yaml:"runner-environment,omitempty"` // 1.3.6.1.4.1.57264.1.11

// Source repository URL that the build was based on.
SourceRepositoryURI string // 1.3.6.1.4.1.57264.1.12
SourceRepositoryURI string `json:"SourceRepositoryURI,omitempty" yaml:"source-repository-uri,omitempty"` // 1.3.6.1.4.1.57264.1.12

// Immutable reference to a specific version of the source code that the build was based upon.
SourceRepositoryDigest string // 1.3.6.1.4.1.57264.1.13
SourceRepositoryDigest string `json:"SourceRepositoryDigest,omitempty" yaml:"source-repository-digest,omitempty"` // 1.3.6.1.4.1.57264.1.13

// Source Repository Ref that the build run was based upon.
SourceRepositoryRef string // 1.3.6.1.4.1.57264.1.14
SourceRepositoryRef string `json:"SourceRepositoryRef,omitempty" yaml:"source-repository-ref,omitempty"` // 1.3.6.1.4.1.57264.1.14

// Immutable identifier for the source repository the workflow was based upon.
SourceRepositoryIdentifier string // 1.3.6.1.4.1.57264.1.15
SourceRepositoryIdentifier string `json:"SourceRepositoryIdentifier,omitempty" yaml:"source-repository-identifier,omitempty"` // 1.3.6.1.4.1.57264.1.15

// Source repository owner URL of the owner of the source repository that the build was based on.
SourceRepositoryOwnerURI string // 1.3.6.1.4.1.57264.1.16
SourceRepositoryOwnerURI string `json:"SourceRepositoryOwnerURI,omitempty" yaml:"source-repository-owner-uri,omitempty"` // 1.3.6.1.4.1.57264.1.16

// Immutable identifier for the owner of the source repository that the workflow was based upon.
SourceRepositoryOwnerIdentifier string // 1.3.6.1.4.1.57264.1.17
SourceRepositoryOwnerIdentifier string `json:"SourceRepositoryOwnerIdentifier,omitempty" yaml:"source-repository-owner-identifier,omitempty"` // 1.3.6.1.4.1.57264.1.17

// Build Config URL to the top-level/initiating build instructions.
BuildConfigURI string // 1.3.6.1.4.1.57264.1.18
BuildConfigURI string `json:"BuildConfigURI,omitempty" yaml:"build-config-uri,omitempty"` // 1.3.6.1.4.1.57264.1.18

// Immutable reference to the specific version of the top-level/initiating build instructions.
BuildConfigDigest string // 1.3.6.1.4.1.57264.1.19
BuildConfigDigest string `json:"BuildConfigDigest,omitempty" yaml:"build-config-digest,omitempty"` // 1.3.6.1.4.1.57264.1.19

// Event or action that initiated the build.
BuildTrigger string // 1.3.6.1.4.1.57264.1.20
BuildTrigger string `json:"BuildTrigger,omitempty" yaml:"build-trigger,omitempty"` // 1.3.6.1.4.1.57264.1.20

// Run Invocation URL to uniquely identify the build execution.
RunInvocationURI string // 1.3.6.1.4.1.57264.1.21
RunInvocationURI string `json:"RunInvocationURI,omitempty" yaml:"run-invocation-uri,omitempty"` // 1.3.6.1.4.1.57264.1.21

// Source repository visibility at the time of signing the certificate.
SourceRepositoryVisibilityAtSigning string // 1.3.6.1.4.1.57264.1.22
SourceRepositoryVisibilityAtSigning string `json:"SourceRepositoryVisibilityAtSigning,omitempty" yaml:"source-repository-visibility-at-signing,omitempty"` // 1.3.6.1.4.1.57264.1.22
}

func (e Extensions) Render() ([]pkix.Extension, error) {
Expand Down
3 changes: 3 additions & 0 deletions pkg/challenges/challenges.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/sigstore/fulcio/pkg/config"
"github.com/sigstore/fulcio/pkg/identity"
"github.com/sigstore/fulcio/pkg/identity/buildkite"
"github.com/sigstore/fulcio/pkg/identity/ciprovider"
"github.com/sigstore/fulcio/pkg/identity/email"
"github.com/sigstore/fulcio/pkg/identity/github"
"github.com/sigstore/fulcio/pkg/identity/gitlabcom"
Expand Down Expand Up @@ -75,6 +76,8 @@ func PrincipalFromIDToken(ctx context.Context, tok *oidc.IDToken) (identity.Prin
principal, err = uri.PrincipalFromIDToken(ctx, tok)
case config.IssuerTypeUsername:
principal, err = username.PrincipalFromIDToken(ctx, tok)
case config.IssuerTypeCIProvider:
principal, err = ciprovider.WorkflowPrincipalFromIDToken(ctx, tok)
default:
return nil, fmt.Errorf("unsupported issuer: %s", iss.Type)
}
Expand Down
58 changes: 57 additions & 1 deletion pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"encoding/json"
"errors"
"fmt"
"html/template"
"net/http"
"net/url"
"os"
Expand All @@ -31,6 +32,7 @@ import (

"github.com/coreos/go-oidc/v3/oidc"
lru "github.com/hashicorp/golang-lru"
"github.com/sigstore/fulcio/pkg/certificate"
fulciogrpc "github.com/sigstore/fulcio/pkg/generated/protobuf"
"github.com/sigstore/fulcio/pkg/log"
"github.com/spiffe/go-spiffe/v2/spiffeid"
Expand Down Expand Up @@ -60,12 +62,33 @@ type FulcioConfig struct {
// * https://container.googleapis.com/v1/projects/mattmoor-credit/locations/us-west1-b/clusters/tenant-cluster
MetaIssuers map[string]OIDCIssuer `json:"MetaIssuers,omitempty" yaml:"meta-issuers,omitempty"`

// It defines metadata to be used for the CIProvider identity provider principal.
// The CI provider has a generic logic for ci providers, this metadata is used
// to define the right behavior for each ci provider that is defined
// on the configuration file
CIIssuerMetadata map[string]IssuerMetadata `json:"CIIssuerMetadata,omitempty" yaml:"ci-issuer-metadata,omitempty"`

// verifiers is a fixed mapping from our OIDCIssuers to their OIDC verifiers.
verifiers map[string][]*verifierWithConfig
// lru is an LRU cache of recently used verifiers for our meta issuers.
lru *lru.TwoQueueCache
}

type IssuerMetadata struct {
// Defaults contains key-value pairs that can be used for filling the templates from ExtensionTemplates
// If a key cannot be found on the token claims, the template will use the defaults
DefaultTemplateValues map[string]string `json:"DefaultTemplateValues,omitempty" yaml:"default-template-values,omitempty"`
// ExtensionTemplates contains a mapping between certificate extension and token claim
// Provide either strings following https://pkg.go.dev/text/template syntax,
// e.g "{{ .url }}/{{ .repository }}"
// or non-templated strings with token claim keys to be replaced,
// e.g "job_workflow_sha"
ExtensionTemplates certificate.Extensions `json:"ExtensionTemplates,omitempty" yaml:"extension-templates,omitempty"`
// Template for the Subject Alternative Name extension
// It's typically the same value as Build Signer URI
SubjectAlternativeNameTemplate string `json:"SubjectAlternativeNameTemplate,omitempty" yaml:"subject-alternative-name-template,omitempty"`
}

type OIDCIssuer struct {
// The expected issuer of an OIDC token
IssuerURL string `json:"IssuerURL,omitempty" yaml:"issuer-url,omitempty"`
Expand All @@ -74,6 +97,8 @@ type OIDCIssuer struct {
// Used to determine the subject of the certificate and if additional
// certificate values are needed
Type IssuerType `json:"Type" yaml:"type,omitempty"`
// CIProvider is an optional configuration to map token claims to extensions for CI workflows
CIProvider string `json:"CIProvider,omitempty" yaml:"ci-provider,omitempty"`
// Optional, if the issuer is in a different claim in the OIDC token
IssuerClaim string `json:"IssuerClaim,omitempty" yaml:"issuer-claim,omitempty"`
// The domain that must be present in the subject for 'uri' issuer types
Expand Down Expand Up @@ -284,6 +309,7 @@ const (
IssuerTypeSpiffe = "spiffe"
IssuerTypeURI = "uri"
IssuerTypeUsername = "username"
IssuerTypeCIProvider = "ci-provider"
)

func parseConfig(b []byte) (cfg *FulcioConfig, err error) {
Expand Down Expand Up @@ -391,7 +417,7 @@ func validateConfig(conf *FulcioConfig) error {
}
}

return nil
return validateCIIssuerMetadata(conf)
}

var DefaultConfig = &FulcioConfig{
Expand Down Expand Up @@ -432,6 +458,34 @@ func FromContext(ctx context.Context) *FulcioConfig {
return untyped.(*FulcioConfig)
}

// It checks that the templates defined are parseable
// We should check it during the service bootstrap to avoid errors further
func validateCIIssuerMetadata(fulcioConfig *FulcioConfig) error {

checkParse := func(temp string) error {
t := template.New("").Option("missingkey=error")
_, err := t.Parse(temp)
return err
}

for _, ciIssuerMetadata := range fulcioConfig.CIIssuerMetadata {
v := reflect.ValueOf(ciIssuerMetadata.ExtensionTemplates)
for i := 0; i < v.NumField(); i++ {
s := v.Field(i).String()
err := checkParse(s)
if err != nil {
return err
}
}

err := checkParse(ciIssuerMetadata.SubjectAlternativeNameTemplate)
if err != nil {
return err
}
}
return nil
}

// Load a config from disk, or use defaults
func Load(configPath string) (*FulcioConfig, error) {
if _, err := os.Stat(configPath); os.IsNotExist(err) {
Expand Down Expand Up @@ -516,6 +570,8 @@ func issuerToChallengeClaim(issType IssuerType, challengeClaim string) string {
return "email"
case IssuerTypeGithubWorkflow:
return "sub"
case IssuerTypeCIProvider:
return "sub"
case IssuerTypeCodefreshWorkflow:
return "sub"
case IssuerTypeChainguard:
Expand Down
56 changes: 56 additions & 0 deletions pkg/config/config_network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/sigstore/fulcio/pkg/certificate"
)

func TestLoad(t *testing.T) {
Expand Down Expand Up @@ -68,6 +69,61 @@ func TestLoad(t *testing.T) {
}
}

func TestParseTemplate(t *testing.T) {

validTemplate := "{{.foobar}}"
invalidTemplate := "{{.foobar}"
ciissuerMetadata := make(map[string]IssuerMetadata)
ciissuerMetadata["github"] = IssuerMetadata{
ExtensionTemplates: certificate.Extensions{
BuildTrigger: invalidTemplate,
},
}
fulcioConfig := &FulcioConfig{
CIIssuerMetadata: ciissuerMetadata,
}
// BuildTrigger as a invalid template should raise an error
err := validateCIIssuerMetadata(fulcioConfig)
if err == nil {
t.Error("invalid template should raise an error")
}
ciissuerMetadata["github"] = IssuerMetadata{
ExtensionTemplates: certificate.Extensions{
BuildTrigger: validTemplate,
},
}
fulcioConfig = &FulcioConfig{
CIIssuerMetadata: ciissuerMetadata,
}
// BuildTrigger as a valid template shouldn't raise an error
err = validateCIIssuerMetadata(fulcioConfig)
if err != nil {
t.Error("valid template shouldn't raise an error, error: %w", err)
}
ciissuerMetadata["github"] = IssuerMetadata{
SubjectAlternativeNameTemplate: invalidTemplate,
}
fulcioConfig = &FulcioConfig{
CIIssuerMetadata: ciissuerMetadata,
}
// A SAN as a invalid template should raise an error
err = validateCIIssuerMetadata(fulcioConfig)
if err == nil {
t.Error("invalid SAN should raise an error")
}
ciissuerMetadata["github"] = IssuerMetadata{
SubjectAlternativeNameTemplate: invalidTemplate,
}
fulcioConfig = &FulcioConfig{
CIIssuerMetadata: ciissuerMetadata,
}
// A SAN as a valid template should raise an error
err = validateCIIssuerMetadata(fulcioConfig)
if err == nil {
t.Error("valid SAN shouldn't raise an error")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you want to test for a valid SAN template as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep! just did. thanks!!

}

func TestLoadDefaults(t *testing.T) {
td := t.TempDir()

Expand Down
3 changes: 3 additions & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,9 @@ func Test_issuerToChallengeClaim(t *testing.T) {
if claim := issuerToChallengeClaim(IssuerTypeGithubWorkflow, ""); claim != "sub" {
t.Fatalf("expected sub subject claim for GitHub issuer, got %s", claim)
}
if claim := issuerToChallengeClaim(IssuerTypeCIProvider, ""); claim != "sub" {
t.Fatalf("expected sub subject claim for CI issuer, got %s", claim)
}
if claim := issuerToChallengeClaim(IssuerTypeGitLabPipeline, ""); claim != "sub" {
t.Fatalf("expected sub subject claim for GitLab issuer, got %s", claim)
}
Expand Down
39 changes: 39 additions & 0 deletions pkg/identity/ciprovider/issuer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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.

package ciprovider

import (
"context"

"github.com/sigstore/fulcio/pkg/config"
"github.com/sigstore/fulcio/pkg/identity"
"github.com/sigstore/fulcio/pkg/identity/base"
)

type ciProviderIssuer struct {
identity.Issuer
}

func Issuer(issuerURL string) identity.Issuer {
return &ciProviderIssuer{base.Issuer(issuerURL)}
}

func (e *ciProviderIssuer) Authenticate(ctx context.Context, token string, opts ...config.InsecureOIDCConfigOption) (identity.Principal, error) {
idtoken, err := identity.Authorize(ctx, token, opts...)
if err != nil {
return nil, err
}
return WorkflowPrincipalFromIDToken(ctx, idtoken)
}
Loading
Loading