Skip to content

Commit

Permalink
Merge branch 'master' into fixing-agent-reconnect-issue
Browse files Browse the repository at this point in the history
  • Loading branch information
marcosy committed Mar 15, 2019
2 parents 4f9a5df + 05c696a commit 05c837e
Show file tree
Hide file tree
Showing 13 changed files with 139 additions and 115 deletions.
4 changes: 3 additions & 1 deletion pkg/agent/endpoints/workload/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,9 @@ func (s *HandlerTestSuite) TestValidateJWTSVID() {
})
s.Require().NoError(err)

svid, err := jwtsvid.SignToken(
jwtSigner := jwtsvid.NewSigner(jwtsvid.SignerConfig{})

svid, err := jwtSigner.SignToken(
"spiffe://example.org/blog",
[]string{"audience"},
time.Now().Add(time.Minute),
Expand Down
22 changes: 20 additions & 2 deletions pkg/common/jwtsvid/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"time"

"github.com/andres-erbsen/clock"
jwt "github.com/dgrijalva/jwt-go"
"github.com/spiffe/spire/pkg/common/idutil"
)
Expand All @@ -13,7 +14,24 @@ const (
keyIDHeader = "kid"
)

func SignToken(spiffeID string, audience []string, expires time.Time, signer crypto.Signer, kid string) (string, error) {
type SignerConfig struct {
Clock clock.Clock
}

type Signer struct {
c SignerConfig
}

func NewSigner(config SignerConfig) *Signer {
if config.Clock == nil {
config.Clock = clock.New()
}
return &Signer{
c: config,
}
}

func (s *Signer) SignToken(spiffeID string, audience []string, expires time.Time, signer crypto.Signer, kid string) (string, error) {
if err := idutil.ValidateSpiffeID(spiffeID, idutil.AllowAnyTrustDomainWorkload()); err != nil {
return "", err
}
Expand All @@ -34,7 +52,7 @@ func SignToken(spiffeID string, audience []string, expires time.Time, signer cry
"sub": spiffeID,
"exp": expires.Unix(),
"aud": audienceClaim(audience),
"iat": time.Now().Unix(),
"iat": s.c.Clock.Now().Unix(),
}

token := jwt.NewWithClaims(signingMethodES256, claims)
Expand Down
27 changes: 16 additions & 11 deletions pkg/common/jwtsvid/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"time"

jwt "github.com/dgrijalva/jwt-go"
"github.com/spiffe/spire/test/clock"
"github.com/stretchr/testify/suite"
)

Expand Down Expand Up @@ -39,6 +40,7 @@ type TokenSuite struct {

key *ecdsa.PrivateKey
bundle KeyStore
signer *Signer
}

func (s *TokenSuite) SetupTest() {
Expand All @@ -48,6 +50,9 @@ func (s *TokenSuite) SetupTest() {
"kid": s.key.Public(),
},
})
s.signer = NewSigner(SignerConfig{
Clock: clock.NewMock(s.T()),
})
}

func (s *TokenSuite) loadKey(pemBytes []byte) *ecdsa.PrivateKey {
Expand All @@ -62,7 +67,7 @@ func (s *TokenSuite) loadKey(pemBytes []byte) *ecdsa.PrivateKey {
}

func (s *TokenSuite) TestSignAndValidate() {
token, err := SignToken(fakeSpiffeID, fakeAudience, time.Now().Add(time.Hour), s.key, "kid")
token, err := s.signer.SignToken(fakeSpiffeID, fakeAudience, time.Now().Add(time.Hour), s.key, "kid")
s.Require().NoError(err)
s.Require().NotEmpty(token)

Expand All @@ -73,7 +78,7 @@ func (s *TokenSuite) TestSignAndValidate() {
}

func (s *TokenSuite) TestSignAndValidateWithAudienceList() {
token, err := SignToken(fakeSpiffeID, fakeAudiences, time.Now().Add(time.Hour), s.key, "kid")
token, err := s.signer.SignToken(fakeSpiffeID, fakeAudiences, time.Now().Add(time.Hour), s.key, "kid")
s.Require().NoError(err)
s.Require().NotEmpty(token)

Expand All @@ -84,27 +89,27 @@ func (s *TokenSuite) TestSignAndValidateWithAudienceList() {
}

func (s *TokenSuite) TestSignWithNoExpiration() {
_, err := SignToken(fakeSpiffeID, fakeAudience, time.Time{}, s.key, "kid")
_, err := s.signer.SignToken(fakeSpiffeID, fakeAudience, time.Time{}, s.key, "kid")
s.Require().EqualError(err, "expiration is required")
}

func (s *TokenSuite) TestSignInvalidSpiffeID() {
// missing ID
_, err := SignToken("", fakeAudience, time.Now(), s.key, "kid")
_, err := s.signer.SignToken("", fakeAudience, time.Now(), s.key, "kid")
s.requireErrorContains(err, "is not a valid workload SPIFFE ID: SPIFFE ID is empty")

// not a spiffe ID
_, err = SignToken("sparfe://example.org", fakeAudience, time.Now(), s.key, "kid")
_, err = s.signer.SignToken("sparfe://example.org", fakeAudience, time.Now(), s.key, "kid")
s.requireErrorContains(err, "is not a valid workload SPIFFE ID: invalid scheme")
}

func (s *TokenSuite) TestSignNoAudience() {
_, err := SignToken(fakeSpiffeID, nil, time.Now().Add(time.Hour), s.key, "kid")
_, err := s.signer.SignToken(fakeSpiffeID, nil, time.Now().Add(time.Hour), s.key, "kid")
s.Require().EqualError(err, "audience is required")
}

func (s *TokenSuite) TestSignEmptyAudience() {
_, err := SignToken(fakeSpiffeID, []string{""}, time.Now().Add(time.Hour), s.key, "kid")
_, err := s.signer.SignToken(fakeSpiffeID, []string{""}, time.Now().Add(time.Hour), s.key, "kid")
s.Require().EqualError(err, "audience is required")
}

Expand All @@ -131,7 +136,7 @@ func (s *TokenSuite) TestValidateMissingThumbprint() {
}

func (s *TokenSuite) TestValidateExpiredToken() {
token, err := SignToken(fakeSpiffeID, fakeAudience, time.Now().Add(-time.Hour), s.key, "kid")
token, err := s.signer.SignToken(fakeSpiffeID, fakeAudience, time.Now().Add(-time.Hour), s.key, "kid")
s.Require().NoError(err)
s.Require().NotEmpty(token)

Expand Down Expand Up @@ -184,7 +189,7 @@ func (s *TokenSuite) TestValidateNoAudience() {
}

func (s *TokenSuite) TestValidateUnexpectedAudience() {
token, err := SignToken(fakeSpiffeID, fakeAudience, time.Now().Add(time.Hour), s.key, "kid")
token, err := s.signer.SignToken(fakeSpiffeID, fakeAudience, time.Now().Add(time.Hour), s.key, "kid")
s.Require().NoError(err)
s.Require().NotEmpty(token)

Expand All @@ -195,7 +200,7 @@ func (s *TokenSuite) TestValidateUnexpectedAudience() {
}

func (s *TokenSuite) TestValidateUnexpectedAudienceList() {
token, err := SignToken(fakeSpiffeID, fakeAudiences, time.Now().Add(time.Hour), s.key, "kid")
token, err := s.signer.SignToken(fakeSpiffeID, fakeAudiences, time.Now().Add(time.Hour), s.key, "kid")
s.Require().NoError(err)
s.Require().NotEmpty(token)

Expand All @@ -206,7 +211,7 @@ func (s *TokenSuite) TestValidateUnexpectedAudienceList() {
}

func (s *TokenSuite) TestValidateKeyNotFound() {
token, err := SignToken(fakeSpiffeID, fakeAudience, time.Now().Add(time.Hour), s.key, "whatever")
token, err := s.signer.SignToken(fakeSpiffeID, fakeAudience, time.Now().Add(time.Hour), s.key, "whatever")
s.Require().NoError(err)
s.Require().NotEmpty(token)

Expand Down
24 changes: 14 additions & 10 deletions pkg/server/ca/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"sync/atomic"
"time"

"github.com/andres-erbsen/clock"
"github.com/sirupsen/logrus"
"github.com/spiffe/spire/pkg/common/cryptoutil"
"github.com/spiffe/spire/pkg/common/idutil"
Expand All @@ -33,6 +34,7 @@ type serverCAConfig struct {
TrustDomain url.URL
DefaultTTL time.Duration
CASubject pkix.Name
Clock clock.Clock
}

type ServerCA interface {
Expand All @@ -48,17 +50,19 @@ type serverCA struct {
mu sync.RWMutex
kp *keypairSet

hooks struct {
now func() time.Time
}
jwtSigner *jwtsvid.Signer
}

func newServerCA(config serverCAConfig) *serverCA {
out := &serverCA{
if config.Clock == nil {
config.Clock = clock.New()
}
return &serverCA{
c: config,
jwtSigner: jwtsvid.NewSigner(jwtsvid.SignerConfig{
Clock: config.Clock,
}),
}
out.hooks.now = time.Now
return out
}

func (ca *serverCA) setKeypairSet(kp keypairSet) {
Expand All @@ -79,7 +83,7 @@ func (ca *serverCA) SignX509SVID(ctx context.Context, csrDER []byte, ttl time.Du
return nil, errors.New("no X509-SVID keypair available")
}

now := ca.hooks.now()
now := ca.c.Clock.Now()
if ttl <= 0 {
ttl = ca.c.DefaultTTL
}
Expand Down Expand Up @@ -124,7 +128,7 @@ func (ca *serverCA) SignX509CASVID(ctx context.Context, csrDER []byte, ttl time.
return nil, errors.New("no X509-SVID keypair available")
}

now := ca.hooks.now()
now := ca.c.Clock.Now()
if ttl <= 0 {
ttl = ca.c.DefaultTTL
}
Expand Down Expand Up @@ -181,14 +185,14 @@ func (ca *serverCA) SignJWTSVID(ctx context.Context, jsr *node.JSR) (string, err
if ttl <= 0 {
ttl = DefaultJWTSVIDTTL
}
expiresAt := ca.hooks.now().Add(ttl)
expiresAt := ca.c.Clock.Now().Add(ttl)
if expiresAt.After(kp.jwtSigningKey.notAfter) {
expiresAt = kp.jwtSigningKey.notAfter
}

km := ca.c.Catalog.KeyManagers()[0]
signer := cryptoutil.NewKeyManagerSigner(km, kp.JWTSignerKeyID(), kp.jwtSigningKey.publicKey)
token, err := jwtsvid.SignToken(jsr.SpiffeId, jsr.Audience, expiresAt, signer, kp.jwtSigningKey.Kid)
token, err := ca.jwtSigner.SignToken(jsr.SpiffeId, jsr.Audience, expiresAt, signer, kp.jwtSigningKey.Kid)
if err != nil {
return "", fmt.Errorf("unable to sign JWT-SVID: %v", err)
}
Expand Down
39 changes: 19 additions & 20 deletions pkg/server/ca/ca_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/spiffe/spire/proto/api/node"
"github.com/spiffe/spire/proto/common"
"github.com/spiffe/spire/proto/server/keymanager"
"github.com/spiffe/spire/test/clock"
"github.com/spiffe/spire/test/fakes/fakeservercatalog"
"github.com/stretchr/testify/suite"
)
Expand All @@ -40,21 +41,22 @@ type CATestSuite struct {
suite.Suite

signer crypto.Signer
now time.Time
clock *clock.Mock
ca *serverCA
}

func (s *CATestSuite) SetupTest() {
key, err := pemutil.ParseECPrivateKey(keyPEM)
s.Require().NoError(err)
s.signer = key
s.now = time.Now().Truncate(time.Second).UTC()
s.clock = clock.NewMock(s.T())
s.clock.Set(time.Now().Truncate(time.Second).UTC())

km := memory.New()
x509CASigner, err := cryptoutil.GenerateKeyAndSigner(ctx, km, "x509-CA-FOO", keymanager.KeyType_EC_P256)
s.Require().NoError(err)

cert, err := SelfSignServerCACertificate(x509CASigner, "example.org", pkix.Name{}, s.now, s.now.Add(time.Minute*10))
cert, err := SelfSignServerCACertificate(x509CASigner, "example.org", pkix.Name{}, s.clock.Now(), s.clock.Now().Add(time.Minute*10))
s.Require().NoError(err)

jwtSigningKeyPKIX, err := cryptoutil.GenerateKeyRaw(ctx, km, "JWT-Signer-FOO", keymanager.KeyType_EC_P256)
Expand Down Expand Up @@ -94,9 +96,6 @@ func (s *CATestSuite) SetupTest() {
},
jwtSigningKey: jwtSigningKey,
})
s.ca.hooks.now = func() time.Time {
return s.now
}
}

func (s *CATestSuite) TestNoX509KeypairSet() {
Expand All @@ -109,8 +108,8 @@ func (s *CATestSuite) TestSignX509SVIDUsesDefaultTTLIfTTLUnspecified() {
svid, err := s.ca.SignX509SVID(ctx, s.generateCSR("example.org"), 0)
s.Require().NoError(err)
s.Require().Len(svid, 2)
s.Require().Equal(s.now.Add(-backdate), svid[0].NotBefore)
s.Require().Equal(s.now.Add(time.Minute), svid[0].NotAfter)
s.Require().Equal(s.clock.Now().Add(-backdate), svid[0].NotBefore)
s.Require().Equal(s.clock.Now().Add(time.Minute), svid[0].NotAfter)
}

func (s *CATestSuite) TestSignX509SVIDReturnsEmptyIntermediatesIfServerCASelfSigned() {
Expand All @@ -134,16 +133,16 @@ func (s *CATestSuite) TestSignX509SVIDUsesTTLIfSpecified() {
svid, err := s.ca.SignX509SVID(ctx, s.generateCSR("example.org"), time.Minute+time.Second)
s.Require().NoError(err)
s.Require().Len(svid, 2)
s.Require().Equal(s.now.Add(-backdate), svid[0].NotBefore)
s.Require().Equal(s.now.Add(time.Minute+time.Second), svid[0].NotAfter)
s.Require().Equal(s.clock.Now().Add(-backdate), svid[0].NotBefore)
s.Require().Equal(s.clock.Now().Add(time.Minute+time.Second), svid[0].NotAfter)
}

func (s *CATestSuite) TestSignX509SVIDCapsTTLToKeypairTTL() {
svid, err := s.ca.SignX509SVID(ctx, s.generateCSR("example.org"), time.Hour)
s.Require().NoError(err)
s.Require().Len(svid, 2)
s.Require().Equal(s.now.Add(-backdate), svid[0].NotBefore)
s.Require().Equal(s.now.Add(10*time.Minute), svid[0].NotAfter)
s.Require().Equal(s.clock.Now().Add(-backdate), svid[0].NotBefore)
s.Require().Equal(s.clock.Now().Add(10*time.Minute), svid[0].NotAfter)
}

func (s *CATestSuite) TestSignX509SVIDValidatesCSR() {
Expand Down Expand Up @@ -185,26 +184,26 @@ func (s *CATestSuite) TestSignJWTSVIDUsesDefaultTTLIfTTLUnspecified() {
s.Require().NoError(err)
issuedAt, expiresAt, err := jwtsvid.GetTokenExpiry(token)
s.Require().NoError(err)
s.Require().Equal(s.now, issuedAt)
s.Require().Equal(s.now.Add(DefaultJWTSVIDTTL), expiresAt)
s.Require().Equal(s.clock.Now(), issuedAt)
s.Require().Equal(s.clock.Now().Add(DefaultJWTSVIDTTL), expiresAt)
}

func (s *CATestSuite) TestSignJWTSVIDUsesTTLIfSpecified() {
token, err := s.ca.SignJWTSVID(ctx, s.generateJSR("example.org", time.Minute+time.Second))
s.Require().NoError(err)
issuedAt, expiresAt, err := jwtsvid.GetTokenExpiry(token)
s.Require().NoError(err)
s.Require().Equal(s.now, issuedAt)
s.Require().Equal(s.now.Add(time.Minute+time.Second), expiresAt)
s.Require().Equal(s.clock.Now(), issuedAt)
s.Require().Equal(s.clock.Now().Add(time.Minute+time.Second), expiresAt)
}

func (s *CATestSuite) TestSignJWTSVIDCapsTTLToKeypairTTL() {
token, err := s.ca.SignJWTSVID(ctx, s.generateJSR("example.org", time.Hour))
s.Require().NoError(err)
issuedAt, expiresAt, err := jwtsvid.GetTokenExpiry(token)
s.Require().NoError(err)
s.Require().Equal(s.now, issuedAt)
s.Require().Equal(s.now.Add(10*time.Minute), expiresAt)
s.Require().Equal(s.clock.Now(), issuedAt)
s.Require().Equal(s.clock.Now().Add(10*time.Minute), expiresAt)
}

func (s *CATestSuite) TestSignJWTSVIDValidatesJSR() {
Expand All @@ -229,8 +228,8 @@ func (s *CATestSuite) TestSignX509CASVIDUsesDefaultTTLIfTTLUnspecified() {
svid, err := s.ca.SignX509CASVID(ctx, s.generateCSR("example.org"), 0)
s.Require().NoError(err)
s.Require().Len(svid, 2)
s.Require().Equal(s.now.Add(-backdate), svid[0].NotBefore)
s.Require().Equal(s.now.Add(time.Minute), svid[0].NotAfter)
s.Require().Equal(s.clock.Now().Add(-backdate), svid[0].NotBefore)
s.Require().Equal(s.clock.Now().Add(time.Minute), svid[0].NotAfter)
}

func (s *CATestSuite) TestSignX509CASVIDWithDifferentSubject() {
Expand Down
Loading

0 comments on commit 05c837e

Please sign in to comment.