Skip to content

Commit

Permalink
Move spiffe principal to package
Browse files Browse the repository at this point in the history
Signed-off-by: Nathan Smith <nathan@chainguard.dev>
  • Loading branch information
Nathan Smith committed May 23, 2022
1 parent 6bf9d30 commit 5a2a1c3
Show file tree
Hide file tree
Showing 4 changed files with 359 additions and 186 deletions.
47 changes: 2 additions & 45 deletions pkg/challenges/challenges.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
"github.com/sigstore/fulcio/pkg/ca/x509ca"
"github.com/sigstore/fulcio/pkg/config"
"github.com/sigstore/fulcio/pkg/identity"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/sigstore/fulcio/pkg/identity/spiffe"

"github.com/coreos/go-oidc/v3/oidc"
"github.com/sigstore/fulcio/pkg/oauthflow"
Expand All @@ -40,7 +40,6 @@ type ChallengeType int

const (
EmailValue ChallengeType = iota
SpiffeValue
GithubWorkflowValue
KubernetesValue
URIValue
Expand Down Expand Up @@ -82,12 +81,6 @@ func (cr *ChallengeResult) Embed(ctx context.Context, cert *x509.Certificate) er
switch cr.TypeVal {
case EmailValue:
cert.EmailAddresses = []string{cr.Value}
case SpiffeValue:
challengeURL, err := url.Parse(cr.Value)
if err != nil {
return err
}
cert.URIs = []*url.URL{challengeURL}
case GithubWorkflowValue:
jobWorkflowURI, err := url.Parse(cr.Value)
if err != nil {
Expand Down Expand Up @@ -183,42 +176,6 @@ func email(ctx context.Context, principal *oidc.IDToken) (identity.Principal, er
}, nil
}

func spiffe(ctx context.Context, principal *oidc.IDToken) (identity.Principal, error) {
spiffeID := principal.Subject

cfg, ok := config.FromContext(ctx).GetIssuer(principal.Issuer)
if !ok {
return nil, errors.New("invalid configuration for OIDC ID Token issuer")
}

trustDomain, err := spiffeid.TrustDomainFromString(cfg.SPIFFETrustDomain)
if err != nil {
return nil, fmt.Errorf("unable to parse trust domain from configuration: %s", cfg.SPIFFETrustDomain)
}

parsedSpiffeID, err := spiffeid.FromString(spiffeID)
if err != nil {
return nil, fmt.Errorf("invalid spiffe ID provided: %s", spiffeID)
}

if parsedSpiffeID.TrustDomain().Compare(trustDomain) != 0 {
return nil, fmt.Errorf("spiffe ID trust domain %s doesn't match configured trust domain %s", parsedSpiffeID.TrustDomain(), trustDomain)
}

issuer, err := oauthflow.IssuerFromIDToken(principal, cfg.IssuerClaim)
if err != nil {
return nil, err
}

// Now issue cert!
return &ChallengeResult{
Issuer: issuer,
TypeVal: SpiffeValue,
Value: spiffeID,
subject: spiffeID,
}, nil
}

func kubernetes(ctx context.Context, principal *oidc.IDToken) (identity.Principal, error) {
k8sURI, err := kubernetesToken(principal)
if err != nil {
Expand Down Expand Up @@ -420,7 +377,7 @@ func PrincipalFromIDToken(ctx context.Context, tok *oidc.IDToken) (identity.Prin
case config.IssuerTypeEmail:
principal, err = email(ctx, tok)
case config.IssuerTypeSpiffe:
principal, err = spiffe(ctx, tok)
principal, err = spiffe.PrincipalFromIDToken(ctx, tok)
case config.IssuerTypeGithubWorkflow:
principal, err = githubWorkflow(ctx, tok)
case config.IssuerTypeKubernetes:
Expand Down
143 changes: 2 additions & 141 deletions pkg/challenges/challenges_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,38 +103,6 @@ func TestEmbedChallengeResult(t *testing.T) {
`Certificate should have issuer extension set`: factIssuerIs("example.com"),
},
},
`Good spiffe challenge`: {
Challenge: ChallengeResult{
Issuer: `example.com`,
TypeVal: SpiffeValue,
Value: `spiffe://example.com/foo/bar`,
},
WantErr: false,
WantFacts: map[string]func(x509.Certificate) error{
`Issuer is example.com`: factIssuerIs(`example.com`),
`SAN is spiffe://example.com/foo/bar`: func(cert x509.Certificate) error {
WantURI, err := url.Parse("spiffe://example.com/foo/bar")
if err != nil {
return err
}
if len(cert.URIs) != 1 {
return errors.New("no URI SAN set")
}
if diff := cmp.Diff(cert.URIs[0], WantURI); diff != "" {
return errors.New(diff)
}
return nil
},
},
},
`Spiffe value with bad URL fails`: {
Challenge: ChallengeResult{
Issuer: `example.com`,
TypeVal: SpiffeValue,
Value: "\nbadurl",
},
WantErr: true,
},
`Good Kubernetes value`: {
Challenge: ChallengeResult{
Issuer: `k8s.example.com`,
Expand Down Expand Up @@ -222,8 +190,8 @@ func TestEmbedChallengeResult(t *testing.T) {
`No issuer should fail to render extensions`: {
Challenge: ChallengeResult{
Issuer: ``,
TypeVal: SpiffeValue,
Value: "spiffe://foo.example.com/foo/bar",
TypeVal: URIValue,
Value: "https://foo.example.com/foo/bar",
},
WantErr: true,
},
Expand Down Expand Up @@ -436,113 +404,6 @@ func TestEmailWithClaims(t *testing.T) {
}
}

func TestSpiffe(t *testing.T) {
tests := map[string]struct {
Token *oidc.IDToken
Config *config.FulcioConfig
WantErr bool
}{
"good token": {
Token: &oidc.IDToken{
Subject: "spiffe://foo.com/bar",
Issuer: "id.foo.com",
},
Config: &config.FulcioConfig{
OIDCIssuers: map[string]config.OIDCIssuer{
"id.foo.com": {
IssuerURL: "id.foo.com",
ClientID: "sigstore",
Type: config.IssuerTypeSpiffe,
SPIFFETrustDomain: "foo.com",
},
},
},
WantErr: false,
},
"spiffe id wrong trust domain": {
Token: &oidc.IDToken{
Subject: "spiffe://baz.com/bar",
Issuer: "id.foo.com",
},
Config: &config.FulcioConfig{
OIDCIssuers: map[string]config.OIDCIssuer{
"id.foo.com": {
IssuerURL: "id.foo.com",
ClientID: "sigstore",
Type: config.IssuerTypeSpiffe,
SPIFFETrustDomain: "foo.com",
},
},
},
WantErr: true,
},
"spiffe id no issuer configured": {
Token: &oidc.IDToken{
Subject: "spiffe://foo.com/bar",
Issuer: "id.foo.com",
},
Config: &config.FulcioConfig{
OIDCIssuers: map[string]config.OIDCIssuer{
"id.bar.com": {
IssuerURL: "id.bar.com",
ClientID: "sigstore",
Type: config.IssuerTypeSpiffe,
SPIFFETrustDomain: "foo.com",
},
},
},
WantErr: true,
},
"invalid spiffe id": {
Token: &oidc.IDToken{
Subject: "spiffe://foo#com/bar",
Issuer: "id.foo.com",
},
Config: &config.FulcioConfig{
OIDCIssuers: map[string]config.OIDCIssuer{
"id.foo.com": {
IssuerURL: "id.foo.com",
ClientID: "sigstore",
Type: config.IssuerTypeSpiffe,
SPIFFETrustDomain: "foo.com",
},
},
},
WantErr: true,
},
"invalid configured trust domain": {
Token: &oidc.IDToken{
Subject: "spiffe://foo.com/bar",
Issuer: "id.foo.com",
},
Config: &config.FulcioConfig{
OIDCIssuers: map[string]config.OIDCIssuer{
"id.foo.com": {
IssuerURL: "id.foo.com",
ClientID: "sigstore",
Type: config.IssuerTypeSpiffe,
SPIFFETrustDomain: "foo#com",
},
},
},
WantErr: true,
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
ctx := config.With(context.Background(), test.Config)
_, err := spiffe(ctx, test.Token)
if err != nil && !test.WantErr {
t.Errorf("%s: %v", name, err)
}
if err == nil && test.WantErr {
t.Errorf("%s: expected error", name)
}
})
}
}

func failErr(t *testing.T, err error) {
if err != nil {
t.Fatal(err)
Expand Down
94 changes: 94 additions & 0 deletions pkg/identity/spiffe/principal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// 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 spiffe

import (
"context"
"crypto/x509"
"errors"
"fmt"
"net/url"

"github.com/coreos/go-oidc/v3/oidc"
"github.com/sigstore/fulcio/pkg/ca/x509ca"
"github.com/sigstore/fulcio/pkg/config"
"github.com/sigstore/fulcio/pkg/identity"
"github.com/spiffe/go-spiffe/v2/spiffeid"
)

type principal struct {
// spiffe ID
id string

// OIDC issuer url
issuer string
}

func PrincipalFromIDToken(ctx context.Context, token *oidc.IDToken) (identity.Principal, error) {
cfg, ok := config.FromContext(ctx).GetIssuer(token.Issuer)
if !ok {
return nil, errors.New("invalid configuration for OIDC ID Token issuer")
}

if err := validSpiffeID(token.Subject, cfg.SPIFFETrustDomain); err != nil {
return nil, err
}

// Now issue cert!
return principal{
id: token.Subject,
issuer: token.Issuer,
}, nil

}

func validSpiffeID(id, trustDomain string) error {
parsedTrustDomain, err := spiffeid.TrustDomainFromString(trustDomain)
if err != nil {
return fmt.Errorf("unable to parse trust domain from configuration %s: %w", trustDomain, err)
}

parsedID, err := spiffeid.FromString(id)
if err != nil {
return fmt.Errorf("invalid spiffe ID provided: %s", id)
}

if parsedID.TrustDomain().Compare(parsedTrustDomain) != 0 {
return fmt.Errorf("spiffe ID trust domain %s doesn't match configured trust domain %s", parsedID.TrustDomain(), trustDomain)
}

return nil
}

func (p principal) Name(context.Context) string {
return p.id
}

func (p principal) Embed(ctx context.Context, cert *x509.Certificate) error {
parsed, err := url.Parse(p.id)
if err != nil {
return err
}
cert.URIs = []*url.URL{parsed}

cert.ExtraExtensions, err = x509ca.Extensions{
Issuer: p.issuer,
}.Render()
if err != nil {
return err
}

return nil
}
Loading

0 comments on commit 5a2a1c3

Please sign in to comment.