Skip to content

Commit

Permalink
Surface the right Name() from our principal. (#1726)
Browse files Browse the repository at this point in the history
The cosign logic for interacting with Fulcio treats identity tokens as *largely* opaque, and most of the logic for how issuers and subjects and whatnot is handled happens server-side.  However, for the "proof of possession" `cosign` has some logic (from `sigstore/sigstore`) that fumbles with `email` and `sub` claims in ways that have (until now) been compatible with Fulcio principals.

The Chainguard provider is the first provider that optionally includes an `email` claim, but we always want the subject we use to be our opaque identifier string (from `sub`).  This creates a tear in the fulcio/cosign continuum, and so we must surface what `cosign` is signing as `Name()` even though that isn't necessarily what we embed in the certificate.

The only correct way to implement `Name()` today is to match what this function does, and current implementations happen to align, but unfortunately because of how this abstraction is formulated it is challenging to actually change how we confirm the proof of possession to use this directly in place of the principal itself.

Fixes: sigstore/cosign#3777

Signed-off-by: Matt Moore <mattmoor@chainguard.dev>
  • Loading branch information
mattmoor committed Jul 10, 2024
1 parent 9f49a8b commit d16be8d
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 1 deletion.
14 changes: 13 additions & 1 deletion pkg/identity/chainguard/principal.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ import (
"github.com/coreos/go-oidc/v3/oidc"
"github.com/sigstore/fulcio/pkg/certificate"
"github.com/sigstore/fulcio/pkg/identity"
"github.com/sigstore/sigstore/pkg/oauthflow"
)

type workflowPrincipal struct {
issuer string
subject string
name string

actor map[string]string
servicePrincipal string
Expand All @@ -35,7 +37,7 @@ type workflowPrincipal struct {
var _ identity.Principal = (*workflowPrincipal)(nil)

func (w workflowPrincipal) Name(_ context.Context) string {
return w.subject
return w.name
}

func PrincipalFromIDToken(_ context.Context, token *oidc.IDToken) (identity.Principal, error) {
Expand All @@ -50,9 +52,19 @@ func PrincipalFromIDToken(_ context.Context, token *oidc.IDToken) (identity.Prin
return nil, err
}

// This is the exact function that cosign uses to extract the "subject"
// (misnomer) from the token in order to establish "proof of possession".
// We MUST use this to implement Name() or tokens that embed an email claim
// will fail to sign because of this divergent logic.
name, err := oauthflow.SubjectFromToken(token)
if err != nil {
return nil, err
}

return &workflowPrincipal{
issuer: token.Issuer,
subject: token.Subject,
name: name,
actor: claims.Actor,
servicePrincipal: claims.Internal.ServicePrincipal,
}, nil
Expand Down
29 changes: 29 additions & 0 deletions pkg/identity/chainguard/principal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func TestJobPrincipalFromIDToken(t *testing.T) {
ExpectPrincipal: workflowPrincipal{
issuer: "https://issuer.enforce.dev",
subject: id.String(),
name: id.String(),
actor: map[string]string{
"iss": "https://iss.example.com/",
"sub": fmt.Sprintf("catalog-syncer:%s", group.String()),
Expand All @@ -85,6 +86,34 @@ func TestJobPrincipalFromIDToken(t *testing.T) {
ExpectPrincipal: workflowPrincipal{
issuer: "https://issuer.enforce.dev",
subject: group.String(),
name: group.String(),
actor: map[string]string{
"iss": "https://auth.chainguard.dev/",
"sub": "google-oauth2|1234567890",
"aud": "fdsaldfkjhasldf",
},
},
WantErr: false,
},
`Human SSO token (with email)`: {
Claims: map[string]interface{}{
"iss": "https://issuer.enforce.dev",
"sub": group.String(),
"email": "jane@doe.dev",
"email_verified": true,
// Actor claims track the identity that was used to assume the
// Chainguard identity. In this case, it is the Catalog Syncer
// service principal.
"act": map[string]string{
"iss": "https://auth.chainguard.dev/",
"sub": "google-oauth2|1234567890",
"aud": "fdsaldfkjhasldf",
},
},
ExpectPrincipal: workflowPrincipal{
issuer: "https://issuer.enforce.dev",
subject: group.String(),
name: "jane@doe.dev",
actor: map[string]string{
"iss": "https://auth.chainguard.dev/",
"sub": "google-oauth2|1234567890",
Expand Down

0 comments on commit d16be8d

Please sign in to comment.