Skip to content

Commit

Permalink
Update go-jose to 4.0.1 (#5017)
Browse files Browse the repository at this point in the history
* Update go-jose to 4.0.1

There are two breaking changes from the 3.x versions that affect SPIRE code:
- JWT parsing methods now require accepted signature algorithms (`alg`
  header parameter) to be passed as input. If the token contains a
  signature algorithm not in the list of accepted signature algorithms,
  token parsing fails. This is to prevent against signature algorithm
  confusion attacks.
- CompactSerialize() method has been removed in favor of
  Serialize(). These methods are functionally equivalent.

Signed-off-by: Ryan Turner <[email protected]>
  • Loading branch information
rturner3 authored Apr 12, 2024
1 parent fd6e51a commit acdbd50
Show file tree
Hide file tree
Showing 50 changed files with 214 additions and 147 deletions.
16 changes: 10 additions & 6 deletions cmd/spire-server/cli/jwt/mint.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"fmt"
"time"

"github.com/go-jose/go-jose/v3/jwt"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/mitchellh/cli"
"github.com/spiffe/go-spiffe/v2/spiffeid"
svidv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/svid/v1"
Expand All @@ -16,6 +16,7 @@ import (
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
"github.com/spiffe/spire/pkg/common/diskutil"
"github.com/spiffe/spire/pkg/common/jwtsvid"
)

func NewMintCommand() cli.Command {
Expand All @@ -38,6 +39,7 @@ type mintCommand struct {
func (c *mintCommand) Name() string {
return "jwt mint"
}

func (c *mintCommand) Synopsis() string {
return "Mints a JWT-SVID"
}
Expand All @@ -63,10 +65,11 @@ func (c *mintCommand) Run(ctx context.Context, env *commoncli.Env, serverClient
}

client := serverClient.NewSVIDClient()
resp, err := client.MintJWTSVID(ctx, &svidv1.MintJWTSVIDRequest{Id: &types.SPIFFEID{
TrustDomain: spiffeID.TrustDomain().Name(),
Path: spiffeID.Path(),
},
resp, err := client.MintJWTSVID(ctx, &svidv1.MintJWTSVIDRequest{
Id: &types.SPIFFEID{
TrustDomain: spiffeID.TrustDomain().Name(),
Path: spiffeID.Path(),
},
Ttl: ttlToSeconds(c.ttl),
Audience: c.audience,
})
Expand Down Expand Up @@ -108,8 +111,9 @@ func (c *mintCommand) validateToken(token string, env *commoncli.Env) error {

return nil
}

func getJWTSVIDEndOfLife(token string) (time.Time, error) {
t, err := jwt.ParseSigned(token)
t, err := jwt.ParseSigned(token, jwtsvid.AllowedSignatureAlgorithms)
if err != nil {
return time.Time{}, err
}
Expand Down
11 changes: 6 additions & 5 deletions cmd/spire-server/cli/jwt/mint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import (
"testing"
"time"

"github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v3/jwt"
"github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jwt"
svidv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/svid/v1"
"github.com/spiffe/spire-api-sdk/proto/spire/api/types"
"github.com/spiffe/spire/cmd/spire-server/cli/common"
Expand Down Expand Up @@ -81,15 +81,15 @@ func TestMintRun(t *testing.T) {
builder := jwt.Signed(signer).Claims(jwt.Claims{
Expiry: jwt.NewNumericDate(expiry),
})
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
require.NoError(t, err)

// Create expired token
expiredAt := time.Now().Add(-30 * time.Second)
builder = jwt.Signed(signer).Claims(jwt.Claims{
Expiry: jwt.NewNumericDate(expiredAt),
})
expiredToken, err := builder.CompactSerialize()
expiredToken, err := builder.Serialize()
require.NoError(t, err)

testCases := []struct {
Expand Down Expand Up @@ -205,7 +205,8 @@ func TestMintRun(t *testing.T) {
"hint": "",
"issued_at": "1628500000"
}
}`, token)},
}`, token),
},

{
name: "write on invalid path",
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ require (
github.com/docker/docker v26.0.1+incompatible
github.com/envoyproxy/go-control-plane v0.12.0
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa
github.com/go-jose/go-jose/v3 v3.0.3
github.com/go-jose/go-jose/v4 v4.0.1
github.com/go-sql-driver/mysql v1.8.1
github.com/godbus/dbus/v5 v5.1.0
github.com/gofrs/uuid/v5 v5.0.0
Expand Down Expand Up @@ -178,7 +178,7 @@ require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
Expand Down
4 changes: 2 additions & 2 deletions pkg/agent/endpoints/workload/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1266,7 +1266,7 @@ func TestValidateJWTSVID(t *testing.T) {
svid: "BAD",
audience: "AUDIENCE",
expectCode: codes.InvalidArgument,
expectMsg: "unable to parse JWT token",
expectMsg: "unable to parse JWT token: go-jose/go-jose: compact JWS format must have three parts",
expectLogs: []spiretest.LogEntry{
{
Level: logrus.WarnLevel,
Expand All @@ -1275,7 +1275,7 @@ func TestValidateJWTSVID(t *testing.T) {
"audience": "AUDIENCE",
"service": "WorkloadAPI",
"method": "ValidateJWTSVID",
logrus.ErrorKey: "unable to parse JWT token",
logrus.ErrorKey: "unable to parse JWT token: go-jose/go-jose: compact JWS format must have three parts",
},
},
},
Expand Down
9 changes: 5 additions & 4 deletions pkg/agent/plugin/nodeattestor/azuremsi/msi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"net/http"
"testing"

jose "github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v3/jwt"
jose "github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/spiffe/spire/pkg/agent/plugin/nodeattestor"
nodeattestortest "github.com/spiffe/spire/pkg/agent/plugin/nodeattestor/test"
"github.com/spiffe/spire/pkg/common/plugin/azure"
Expand Down Expand Up @@ -105,11 +105,12 @@ func (s *MSIAttestorSuite) makeAccessToken(principalID, tenantID string) string
TenantID: tenantID,
}

signingKey := jose.SigningKey{Algorithm: jose.HS256, Key: []byte("KEY")}
key := make([]byte, 256)
signingKey := jose.SigningKey{Algorithm: jose.HS256, Key: key}
signer, err := jose.NewSigner(signingKey, nil)
s.Require().NoError(err)

token, err := jwt.Signed(signer).Claims(claims).CompactSerialize()
token, err := jwt.Signed(signer).Claims(claims).Serialize()
s.Require().NoError(err)
return token
}
8 changes: 4 additions & 4 deletions pkg/agent/plugin/nodeattestor/gcpiit/iit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import (
"net/http/httptest"
"testing"

"github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v3/cryptosigner"
"github.com/go-jose/go-jose/v3/jwt"
"github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/cryptosigner"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/spiffe/spire/pkg/agent/plugin/nodeattestor"
nodeattestortest "github.com/spiffe/spire/pkg/agent/plugin/nodeattestor/test"
"github.com/spiffe/spire/pkg/common/plugin/gcp"
Expand Down Expand Up @@ -176,7 +176,7 @@ func signToken(t *testing.T, key crypto.Signer, kid string, claims any) string {
}, nil)
require.NoError(t, err)

token, err := jwt.Signed(signer).Claims(claims).CompactSerialize()
token, err := jwt.Signed(signer).Claims(claims).Serialize()
require.NoError(t, err)
return token
}
11 changes: 5 additions & 6 deletions pkg/agent/plugin/nodeattestor/k8spsat/psat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"path/filepath"
"testing"

jose "github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v3/jwt"
jose "github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/spiffe/spire/pkg/agent/plugin/nodeattestor"
nodeattestortest "github.com/spiffe/spire/pkg/agent/plugin/nodeattestor/test"
"github.com/spiffe/spire/pkg/common/pemutil"
Expand Down Expand Up @@ -112,9 +112,9 @@ func (s *AttestorSuite) joinPath(path string) string {

func (s *AttestorSuite) writeValue(path, data string) string {
valuePath := s.joinPath(path)
err := os.MkdirAll(filepath.Dir(valuePath), 0755)
err := os.MkdirAll(filepath.Dir(valuePath), 0o755)
s.Require().NoError(err)
err = os.WriteFile(valuePath, []byte(data), 0600)
err = os.WriteFile(valuePath, []byte(data), 0o600)
s.Require().NoError(err)
return valuePath
}
Expand All @@ -136,7 +136,7 @@ func createPSAT(namespace, podName string) (string, error) {
builder = builder.Claims(claims)

// Serialize and return token
token, err := builder.CompactSerialize()
token, err := builder.Serialize()
if err != nil {
return "", err
}
Expand All @@ -154,7 +154,6 @@ func createSigner() (jose.Signer, error) {
Algorithm: jose.RS256,
Key: sampleKey,
}, nil)

if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/common/bundleutil/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"encoding/json"
"time"

"github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v4"
"github.com/spiffe/go-spiffe/v2/bundle/spiffebundle"
)

Expand Down
2 changes: 1 addition & 1 deletion pkg/common/bundleutil/types.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package bundleutil

import (
"github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v4"
)

const (
Expand Down
2 changes: 1 addition & 1 deletion pkg/common/cryptoutil/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"crypto/rsa"
"fmt"

"github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v4"
"github.com/zeebo/errs"
)

Expand Down
2 changes: 1 addition & 1 deletion pkg/common/cryptoutil/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"crypto/rsa"
"testing"

"github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v4"
"github.com/spiffe/spire/test/testkey"
"github.com/stretchr/testify/require"
)
Expand Down
4 changes: 2 additions & 2 deletions pkg/common/jwtsvid/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import (
"errors"
"time"

"github.com/go-jose/go-jose/v3/jwt"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/zeebo/errs"
)

func GetTokenExpiry(token string) (time.Time, time.Time, error) {
tok, err := jwt.ParseSigned(token)
tok, err := jwt.ParseSigned(token, AllowedSignatureAlgorithms)
if err != nil {
return time.Time{}, time.Time{}, errs.Wrap(err)
}
Expand Down
15 changes: 15 additions & 0 deletions pkg/common/jwtsvid/signature.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package jwtsvid

import "github.com/go-jose/go-jose/v4"

var AllowedSignatureAlgorithms = []jose.SignatureAlgorithm{
jose.ES256,
jose.ES384,
jose.ES512,
jose.RS256,
jose.RS384,
jose.RS512,
jose.PS256,
jose.PS384,
jose.PS512,
}
21 changes: 5 additions & 16 deletions pkg/common/jwtsvid/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import (
"fmt"
"time"

"github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v3/jwt"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/zeebo/errs"
)
Expand Down Expand Up @@ -40,25 +39,15 @@ func (t *keyStore) FindPublicKey(_ context.Context, td spiffeid.TrustDomain, key
}

func ValidateToken(ctx context.Context, token string, keyStore KeyStore, audience []string) (spiffeid.ID, map[string]any, error) {
tok, err := jwt.ParseSigned(token)
tok, err := jwt.ParseSigned(token, AllowedSignatureAlgorithms)
if err != nil {
return spiffeid.ID{}, nil, errs.New("unable to parse JWT token")
return spiffeid.ID{}, nil, errs.New("unable to parse JWT token: %v", err)
}

if len(tok.Headers) != 1 {
return spiffeid.ID{}, nil, errs.New("expected a single token header; got %d", len(tok.Headers))
}

// Make sure it has an algorithm supported by JWT-SVID
alg := tok.Headers[0].Algorithm
switch jose.SignatureAlgorithm(alg) {
case jose.RS256, jose.RS384, jose.RS512,
jose.ES256, jose.ES384, jose.ES512,
jose.PS256, jose.PS384, jose.PS512:
default:
return spiffeid.ID{}, nil, errs.New("unsupported token signature algorithm %q", alg)
}

// Obtain the key ID from the header
keyID := tok.Headers[0].KeyID
if keyID == "" {
Expand Down Expand Up @@ -95,8 +84,8 @@ func ValidateToken(ctx context.Context, token string, keyStore KeyStore, audienc
// Now that the signature over the claims has been verified, validate the
// standard claims.
if err := claims.Validate(jwt.Expected{
Audience: audience,
Time: time.Now(),
AnyAudience: audience,
Time: time.Now(),
}); err != nil {
// Convert expected validation errors for pretty errors
switch {
Expand Down
15 changes: 8 additions & 7 deletions pkg/common/jwtsvid/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (
"testing"
"time"

"github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v3/cryptosigner"
"github.com/go-jose/go-jose/v3/jwt"
"github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/cryptosigner"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/spire/pkg/common/cryptoutil"
"github.com/spiffe/spire/test/clock"
Expand Down Expand Up @@ -98,10 +98,11 @@ func (s *TokenSuite) TestValidateWithAudienceList() {
}

func (s *TokenSuite) TestValidateBadAlgorithm() {
token := s.signToken(jose.HS256, []byte("BLAH"), jwt.Claims{})
key := make([]byte, 256)
token := s.signToken(jose.HS256, key, jwt.Claims{})

spiffeID, claims, err := ValidateToken(ctx, token, s.bundle, fakeAudience[0:1])
s.Require().EqualError(err, `unsupported token signature algorithm "HS256"`)
s.Require().EqualError(err, `unable to parse JWT token: go-jose/go-jose: unexpected signature algorithm "HS256"; expected ["ES256" "ES384" "ES512" "RS256" "RS384" "RS512" "PS256" "PS384" "PS512"]`)
s.Require().Empty(spiffeID)
s.Require().Nil(claims)
}
Expand Down Expand Up @@ -193,7 +194,7 @@ func (s *TokenSuite) signToken(alg jose.SignatureAlgorithm, key any, claims jwt.
}, nil)
s.Require().NoError(err)

token, err := jwt.Signed(signer).Claims(claims).CompactSerialize()
token, err := jwt.Signed(signer).Claims(claims).Serialize()
s.Require().NoError(err)
return token
}
Expand Down Expand Up @@ -221,7 +222,7 @@ func (s *TokenSuite) signJWTSVID(id spiffeid.ID, audience []string, expires time
)
s.Require().NoError(err)

signedToken, err := jwt.Signed(jwtSigner).Claims(claims).CompactSerialize()
signedToken, err := jwt.Signed(jwtSigner).Claims(claims).Serialize()
s.Require().NoError(err)
return signedToken
}
2 changes: 1 addition & 1 deletion pkg/common/jwtutil/keyset.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"sync"
"time"

"github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v4"
"github.com/sirupsen/logrus"
"github.com/zeebo/errs"
)
Expand Down
2 changes: 1 addition & 1 deletion pkg/common/jwtutil/keyset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"testing"
"time"

jose "github.com/go-jose/go-jose/v3"
jose "github.com/go-jose/go-jose/v4"
"github.com/stretchr/testify/require"
)

Expand Down
Loading

0 comments on commit acdbd50

Please sign in to comment.