Use fake clock consistently in units tests. (#5263)

Use fake clock consistently in units tests.
This commit is contained in:
a-palchikov 2021-01-12 12:10:00 +01:00 committed by GitHub
parent a9829381e8
commit 6684c37103
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 332 additions and 167 deletions

View file

@ -90,7 +90,7 @@ type CertAuthority interface {
// GetTLSKeyPairs returns first PEM encoded TLS cert
GetTLSKeyPairs() []TLSKeyPair
// JWTSigner returns the active JWT key used to sign tokens.
JWTSigner() (*jwt.Key, error)
JWTSigner(jwt.Config) (*jwt.Key, error)
// GetJWTKeyPairs gets all JWT key pairs.
GetJWTKeyPairs() []JWTKeyPair
// SetJWTKeyPairs sets all JWT key pairs.
@ -221,7 +221,7 @@ func (ca *CertAuthorityV2) GetTLSKeyPairs() []TLSKeyPair {
}
// JWTSigner returns the active JWT key used to sign tokens.
func (ca *CertAuthorityV2) JWTSigner() (*jwt.Key, error) {
func (ca *CertAuthorityV2) JWTSigner(config jwt.Config) (*jwt.Key, error) {
if len(ca.Spec.JWTKeyPairs) == 0 {
return nil, trace.BadParameter("no JWT keypairs found")
}
@ -229,11 +229,10 @@ func (ca *CertAuthorityV2) JWTSigner() (*jwt.Key, error) {
if err != nil {
return nil, trace.Wrap(err)
}
key, err := jwt.New(&jwt.Config{
Algorithm: defaults.ApplicationTokenAlgorithm,
ClusterName: ca.Spec.ClusterName,
PrivateKey: privateKey,
})
config.Algorithm = defaults.ApplicationTokenAlgorithm
config.ClusterName = ca.Spec.ClusterName
config.PrivateKey = privateKey
key, err := jwt.New(&config)
if err != nil {
return nil, trace.Wrap(err)
}

2
go.mod
View file

@ -44,7 +44,7 @@ require (
github.com/hashicorp/golang-lru v0.5.4
github.com/iovisor/gobpf v0.0.1
github.com/johannesboyne/gofakes3 v0.0.0-20191228161223-9aee1c78a252
github.com/jonboulle/clockwork v0.2.1
github.com/jonboulle/clockwork v0.2.2
github.com/json-iterator/go v1.1.10
github.com/julienschmidt/httprouter v1.2.0
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0

4
go.sum
View file

@ -355,8 +355,8 @@ github.com/johannesboyne/gofakes3 v0.0.0-20191228161223-9aee1c78a252/go.mod h1:c
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.0 h1:J2SLSdy7HgElq8ekSl2Mxh6vrRNFxqbXGenYH2I02Vs=
github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/jonboulle/clockwork v0.2.1 h1:S/EaQvW6FpWMYAvYvY+OBDvpaM+izu0oiwo5y0MH7U0=
github.com/jonboulle/clockwork v0.2.1/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=

View file

@ -49,12 +49,12 @@ import (
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/teleport/lib/wrappers"
"github.com/pborman/uuid"
"github.com/coreos/go-oidc/oauth2"
"github.com/coreos/go-oidc/oidc"
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
"github.com/pborman/uuid"
"github.com/prometheus/client_golang/prometheus"
saml2 "github.com/russellhaering/gosaml2"
"github.com/tstranex/u2f"
@ -1842,6 +1842,13 @@ func (a *Server) GetAppSession(ctx context.Context, req services.GetAppSessionRe
return a.GetCache().GetAppSession(ctx, req)
}
// WithClock is a functional server option that sets the server's clock
func WithClock(clock clockwork.Clock) func(*Server) {
return func(s *Server) {
s.clock = clock
}
}
// authKeepAliver is a keep aliver using auth server directly
type authKeepAliver struct {
sync.RWMutex

View file

@ -67,7 +67,7 @@ func (cfg *TestAuthServerConfig) CheckAndSetDefaults() error {
return trace.BadParameter("missing parameter Dir")
}
if cfg.Clock == nil {
cfg.Clock = clockwork.NewFakeClockAt(time.Now())
cfg.Clock = clockwork.NewFakeClock()
}
if len(cfg.CipherSuites) == 0 {
cfg.CipherSuites = utils.DefaultCipherSuites()
@ -160,13 +160,13 @@ func NewTestAuthServer(cfg TestAuthServerConfig) (*TestAuthServer, error) {
srv.AuthServer, err = NewServer(&InitConfig{
Backend: srv.Backend,
Authority: authority.New(),
Authority: authority.NewWithClock(cfg.Clock),
Access: access,
Identity: identity,
AuditLog: srv.AuditLog,
SkipPeriodicOperations: true,
Emitter: localLog,
})
}, WithClock(cfg.Clock))
if err != nil {
return nil, trace.Wrap(err)
}
@ -214,13 +214,25 @@ func NewTestAuthServer(cfg TestAuthServerConfig) (*TestAuthServer, error) {
}
// Setup certificate and signing authorities.
if err = srv.AuthServer.UpsertCertAuthority(suite.NewTestCA(services.HostCA, srv.ClusterName)); err != nil {
if err = srv.AuthServer.UpsertCertAuthority(suite.NewTestCAWithConfig(suite.TestCAConfig{
Type: services.HostCA,
ClusterName: srv.ClusterName,
Clock: cfg.Clock,
})); err != nil {
return nil, trace.Wrap(err)
}
if err = srv.AuthServer.UpsertCertAuthority(suite.NewTestCA(services.UserCA, srv.ClusterName)); err != nil {
if err = srv.AuthServer.UpsertCertAuthority(suite.NewTestCAWithConfig(suite.TestCAConfig{
Type: services.UserCA,
ClusterName: srv.ClusterName,
Clock: cfg.Clock,
})); err != nil {
return nil, trace.Wrap(err)
}
if err = srv.AuthServer.UpsertCertAuthority(suite.NewTestCA(services.JWTSigner, srv.ClusterName)); err != nil {
if err = srv.AuthServer.UpsertCertAuthority(suite.NewTestCAWithConfig(suite.TestCAConfig{
Type: services.JWTSigner,
ClusterName: srv.ClusterName,
Clock: cfg.Clock,
})); err != nil {
return nil, trace.Wrap(err)
}
@ -381,6 +393,7 @@ func (a *TestAuthServer) NewRemoteClient(identity TestIdentity, addr net.Addr, p
tlsConfig.Certificates = []tls.Certificate{*cert}
tlsConfig.RootCAs = pool
tlsConfig.ServerName = EncodeClusterName(a.ClusterName)
tlsConfig.Time = a.AuthServer.clock.Now
addrs := []string{addr.String()}
return NewClient(client.Config{Addrs: addrs, TLS: tlsConfig})
}
@ -461,6 +474,7 @@ func NewTestTLSServer(cfg TestTLSServerConfig) (*TestTLSServer, error) {
if err != nil {
return nil, trace.Wrap(err)
}
tlsConfig.Time = cfg.AuthServer.Clock().Now
accessPoint, err := NewAdminAuthServer(srv.AuthServer.AuthServer, srv.AuthServer.SessionServer, srv.AuthServer.AuditLog)
if err != nil {
@ -549,6 +563,7 @@ func (t *TestTLSServer) NewClientFromWebSession(sess services.WebSession) (*Clie
return nil, trace.Wrap(err, "failed to parse TLS cert and key")
}
tlsConfig.Certificates = []tls.Certificate{tlsCert}
tlsConfig.Time = t.AuthServer.AuthServer.clock.Now
addrs := []string{t.Addr().String()}
return NewClient(client.Config{Addrs: addrs, TLS: tlsConfig})
}
@ -579,6 +594,7 @@ func (t *TestTLSServer) ClientTLSConfig(identity TestIdentity) (*tls.Config, err
// server should apply Nop builtin role
tlsConfig.Certificates = nil
}
tlsConfig.Time = t.AuthServer.AuthServer.clock.Now
return tlsConfig, nil
}

View file

@ -29,6 +29,7 @@ import (
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
)
// LocalRegister is used to generate host keys when a node or proxy is running
@ -91,6 +92,16 @@ type RegisterParams struct {
CAPath string
// GetHostCredentials is a client that can fetch host credentials.
GetHostCredentials HostCredentials
// Clock specifies the time provider. Will be used to override the time anchor
// for TLS certificate verification.
// Defaults to real clock if unspecified
Clock clockwork.Clock
}
func (r *RegisterParams) setDefaults() {
if r.Clock == nil {
r.Clock = clockwork.NewRealClock()
}
}
// CredGetter is an interface for a client that can be used to get host
@ -103,6 +114,7 @@ type HostCredentials func(context.Context, string, bool, RegisterUsingTokenReque
// tokens to prove a valid auth server was used to issue the joining request
// as well as a method for the node to validate the auth server.
func Register(params RegisterParams) (*Identity, error) {
params.setDefaults()
// Read in the token. The token can either be passed in or come from a file
// on disk.
token, err := utils.ReadToken(params.Token)
@ -219,6 +231,7 @@ func registerThroughAuth(token string, params RegisterParams) (*Identity, error)
// Server it is connecting to.
func insecureRegisterClient(params RegisterParams) (*Client, error) {
tlsConfig := utils.TLSConfig(params.CipherSuites)
tlsConfig.Time = params.Clock.Now
cert, err := readCA(params)
if err != nil && !trace.IsNotFound(err) {
@ -275,6 +288,7 @@ func pinRegisterClient(params RegisterParams) (*Client, error) {
// an attacker were to MITM this connection the CA pin will not match below.
tlsConfig := utils.TLSConfig(params.CipherSuites)
tlsConfig.InsecureSkipVerify = true
tlsConfig.Time = params.Clock.Now
authClient, err := NewClient(client.Config{Addrs: utils.NetAddrsToStrings(params.Servers), TLS: tlsConfig})
if err != nil {
return nil, trace.Wrap(err)
@ -305,6 +319,7 @@ func pinRegisterClient(params RegisterParams) (*Client, error) {
// Create another client, but this time with the CA provided to validate
// that the Auth Server was issued a certificate by the same CA.
tlsConfig = utils.TLSConfig(params.CipherSuites)
tlsConfig.Time = params.Clock.Now
certPool := x509.NewCertPool()
certPool.AddCert(tlsCA)
tlsConfig.RootCAs = certPool

View file

@ -497,10 +497,15 @@ func startNewRotation(req rotationReq, ca services.CertAuthority) error {
sshPublicKey = ssh.MarshalAuthorizedKey(signer.PublicKey())
sshPrivateKey = req.privateKey
tlsPrivateKey, tlsPublicKey, err = tlsca.GenerateSelfSignedCAWithPrivateKey(rsaKey.(*rsa.PrivateKey), pkix.Name{
CommonName: ca.GetClusterName(),
Organization: []string{ca.GetClusterName()},
}, nil, defaults.CATTL)
tlsPrivateKey, tlsPublicKey, err = tlsca.GenerateSelfSignedCAWithConfig(tlsca.GenerateCAConfig{
PrivateKey: rsaKey.(*rsa.PrivateKey),
Entity: pkix.Name{
CommonName: ca.GetClusterName(),
Organization: []string{ca.GetClusterName()},
},
TTL: defaults.CATTL,
Clock: req.clock,
})
if err != nil {
return trace.Wrap(err)
}

View file

@ -107,7 +107,7 @@ func (s *Server) generateAppToken(username string, roles []string, uri string, e
}
// Extract the JWT signing key and sign the claims.
privateKey, err := ca.JWTSigner()
privateKey, err := ca.JWTSigner(jwt.Config{Clock: s.clock})
if err != nil {
return "", trace.Wrap(err)
}

View file

@ -19,7 +19,6 @@ package testauthority
import (
"crypto/rand"
random "math/rand"
"time"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/lib/auth/native"
@ -29,14 +28,22 @@ import (
"github.com/gravitational/teleport/lib/wrappers"
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
"golang.org/x/crypto/ssh"
)
type Keygen struct {
clock clockwork.Clock
}
// New creates a new key generator with defaults
func New() *Keygen {
return &Keygen{}
return &Keygen{clock: clockwork.NewRealClock()}
}
// NewWithClock creates a new key generator with the specified configuration
func NewWithClock(clock clockwork.Clock) *Keygen {
return &Keygen{clock: clock}
}
func (n *Keygen) Close() {
@ -57,7 +64,7 @@ func (n *Keygen) GenerateHostCert(c services.HostCertParams) ([]byte, error) {
}
validBefore := uint64(ssh.CertTimeInfinity)
if c.TTL != 0 {
b := time.Now().Add(c.TTL)
b := n.clock.Now().Add(c.TTL)
validBefore = uint64(b.Unix())
}
principals := native.BuildPrincipals(c.HostID, c.NodeName, c.ClusterName, c.Roles)
@ -89,7 +96,7 @@ func (n *Keygen) GenerateUserCert(c services.UserCertParams) ([]byte, error) {
}
validBefore := uint64(ssh.CertTimeInfinity)
if c.TTL != 0 {
b := time.Now().Add(c.TTL)
b := n.clock.Now().Add(c.TTL)
validBefore = uint64(b.Unix())
}
cert := &ssh.Certificate{

View file

@ -57,6 +57,7 @@ import (
type TLSSuite struct {
dataDir string
server *TestTLSServer
clock clockwork.FakeClock
}
var _ = check.Suite(&TLSSuite{})
@ -69,9 +70,11 @@ func (s *TLSSuite) SetUpSuite(c *check.C) {
func (s *TLSSuite) SetUpTest(c *check.C) {
s.dataDir = c.MkDir()
s.clock = clockwork.NewFakeClock()
testAuthServer, err := NewTestAuthServer(TestAuthServerConfig{
Dir: s.dataDir,
Dir: s.dataDir,
Clock: s.clock,
})
c.Assert(err, check.IsNil)
s.server, err = testAuthServer.NewTestTLSServer()
@ -90,6 +93,7 @@ func (s *TLSSuite) TestRemoteBuiltinRole(c *check.C) {
remoteServer, err := NewTestAuthServer(TestAuthServerConfig{
Dir: c.MkDir(),
ClusterName: "remote",
Clock: s.clock,
})
c.Assert(err, check.IsNil)
@ -131,6 +135,7 @@ func (s *TLSSuite) TestAcceptedUsage(c *check.C) {
Dir: c.MkDir(),
ClusterName: "remote",
AcceptedUsage: []string{"usage:k8s"},
Clock: s.clock,
})
c.Assert(err, check.IsNil)
@ -188,6 +193,7 @@ func (s *TLSSuite) TestRemoteRotation(c *check.C) {
remoteServer, err := NewTestAuthServer(TestAuthServerConfig{
Dir: c.MkDir(),
ClusterName: "remote",
Clock: s.clock,
})
c.Assert(err, check.IsNil)
@ -287,6 +293,7 @@ func (s *TLSSuite) TestLocalProxyPermissions(c *check.C) {
remoteServer, err := NewTestAuthServer(TestAuthServerConfig{
Dir: c.MkDir(),
ClusterName: "remote",
Clock: s.clock,
})
c.Assert(err, check.IsNil)
@ -322,9 +329,6 @@ func (s *TLSSuite) TestLocalProxyPermissions(c *check.C) {
func (s *TLSSuite) TestAutoRotation(c *check.C) {
var ok bool
clock := clockwork.NewFakeClockAt(time.Now().Add(-2 * time.Hour))
s.server.Auth().SetClock(clock)
// create proxy client
proxy, err := s.server.NewClient(TestBuiltin(teleport.RoleProxy))
c.Assert(err, check.IsNil)
@ -345,7 +349,7 @@ func (s *TLSSuite) TestAutoRotation(c *check.C) {
c.Assert(err, check.IsNil)
// advance rotation by clock
clock.Advance(gracePeriod/3 + time.Minute)
s.clock.Advance(gracePeriod/3 + time.Minute)
err = s.server.Auth().autoRotateCertAuthorities()
c.Assert(err, check.IsNil)
@ -365,7 +369,7 @@ func (s *TLSSuite) TestAutoRotation(c *check.C) {
c.Assert(err, check.IsNil)
// advance rotation by clock
clock.Advance((gracePeriod*2)/3 + time.Minute)
s.clock.Advance((gracePeriod*2)/3 + time.Minute)
err = s.server.Auth().autoRotateCertAuthorities()
c.Assert(err, check.IsNil)
@ -388,7 +392,7 @@ func (s *TLSSuite) TestAutoRotation(c *check.C) {
c.Assert(err, check.IsNil)
// complete rotation - advance rotation by clock
clock.Advance(gracePeriod/3 + time.Minute)
s.clock.Advance(gracePeriod/3 + time.Minute)
err = s.server.Auth().autoRotateCertAuthorities()
c.Assert(err, check.IsNil)
ca, err = s.server.Auth().GetCertAuthority(services.CertAuthID{
@ -418,9 +422,6 @@ func (s *TLSSuite) TestAutoRotation(c *check.C) {
func (s *TLSSuite) TestAutoFallback(c *check.C) {
var ok bool
clock := clockwork.NewFakeClockAt(time.Now().Add(-2 * time.Hour))
s.server.Auth().SetClock(clock)
// create proxy client just for test purposes
proxy, err := s.server.NewClient(TestBuiltin(teleport.RoleProxy))
c.Assert(err, check.IsNil)
@ -441,7 +442,7 @@ func (s *TLSSuite) TestAutoFallback(c *check.C) {
c.Assert(err, check.IsNil)
// advance rotation by clock
clock.Advance(gracePeriod/3 + time.Minute)
s.clock.Advance(gracePeriod/3 + time.Minute)
err = s.server.Auth().autoRotateCertAuthorities()
c.Assert(err, check.IsNil)
@ -670,9 +671,6 @@ func (s *TLSSuite) TestRollback(c *check.C) {
// TestAppTokenRotation checks that JWT tokens can be rotated and tokens can or
// can not be validated at the appropriate phase.
func (s *TLSSuite) TestAppTokenRotation(c *check.C) {
clock := clockwork.NewFakeClockAt(time.Now())
s.server.Auth().SetClock(clock)
client, err := s.server.NewClient(TestBuiltin(teleport.RoleApp))
c.Assert(err, check.IsNil)
@ -683,7 +681,7 @@ func (s *TLSSuite) TestAppTokenRotation(c *check.C) {
Username: "foo",
Roles: []string{"bar", "baz"},
URI: "http://localhost:8080",
Expires: clock.Now().Add(1 * time.Minute),
Expires: s.clock.Now().Add(1 * time.Minute),
})
c.Assert(err, check.IsNil)
@ -696,7 +694,7 @@ func (s *TLSSuite) TestAppTokenRotation(c *check.C) {
c.Assert(oldCA.GetJWTKeyPairs(), check.HasLen, 1)
// Verify that the JWT token validates with the JWT authority.
_, err = s.verifyJWT(clock, s.server.ClusterName(), oldCA.GetJWTKeyPairs(), oldJWT)
_, err = s.verifyJWT(s.clock, s.server.ClusterName(), oldCA.GetJWTKeyPairs(), oldJWT)
c.Assert(err, check.IsNil)
// Start rotation and move to initial phase. A new CA will be added (for
@ -720,7 +718,7 @@ func (s *TLSSuite) TestAppTokenRotation(c *check.C) {
c.Assert(oldCA.GetJWTKeyPairs(), check.HasLen, 2)
// Verify that the JWT token validates with the JWT authority.
_, err = s.verifyJWT(clock, s.server.ClusterName(), oldCA.GetJWTKeyPairs(), oldJWT)
_, err = s.verifyJWT(s.clock, s.server.ClusterName(), oldCA.GetJWTKeyPairs(), oldJWT)
c.Assert(err, check.IsNil)
// Move rotation into the update client phase. In this phase, requests will
@ -739,7 +737,7 @@ func (s *TLSSuite) TestAppTokenRotation(c *check.C) {
Username: "foo",
Roles: []string{"bar", "baz"},
URI: "http://localhost:8080",
Expires: clock.Now().Add(1 * time.Minute),
Expires: s.clock.Now().Add(1 * time.Minute),
})
c.Assert(err, check.IsNil)
@ -753,9 +751,9 @@ func (s *TLSSuite) TestAppTokenRotation(c *check.C) {
c.Assert(newCA.GetRotation().Phase, check.Equals, services.RotationPhaseUpdateClients)
// Both JWT should now validate.
_, err = s.verifyJWT(clock, s.server.ClusterName(), newCA.GetJWTKeyPairs(), oldJWT)
_, err = s.verifyJWT(s.clock, s.server.ClusterName(), newCA.GetJWTKeyPairs(), oldJWT)
c.Assert(err, check.IsNil)
_, err = s.verifyJWT(clock, s.server.ClusterName(), newCA.GetJWTKeyPairs(), newJWT)
_, err = s.verifyJWT(s.clock, s.server.ClusterName(), newCA.GetJWTKeyPairs(), newJWT)
c.Assert(err, check.IsNil)
// Move rotation into update servers phase.
@ -777,9 +775,9 @@ func (s *TLSSuite) TestAppTokenRotation(c *check.C) {
c.Assert(newCA.GetRotation().Phase, check.Equals, services.RotationPhaseUpdateServers)
// Both JWT should continue to validate.
_, err = s.verifyJWT(clock, s.server.ClusterName(), newCA.GetJWTKeyPairs(), oldJWT)
_, err = s.verifyJWT(s.clock, s.server.ClusterName(), newCA.GetJWTKeyPairs(), oldJWT)
c.Assert(err, check.IsNil)
_, err = s.verifyJWT(clock, s.server.ClusterName(), newCA.GetJWTKeyPairs(), newJWT)
_, err = s.verifyJWT(s.clock, s.server.ClusterName(), newCA.GetJWTKeyPairs(), newJWT)
c.Assert(err, check.IsNil)
// Complete rotation. The old CA will be removed.
@ -801,9 +799,9 @@ func (s *TLSSuite) TestAppTokenRotation(c *check.C) {
c.Assert(newCA.GetRotation().Phase, check.Equals, services.RotationPhaseStandby)
// Old token should no longer validate.
_, err = s.verifyJWT(clock, s.server.ClusterName(), newCA.GetJWTKeyPairs(), oldJWT)
_, err = s.verifyJWT(s.clock, s.server.ClusterName(), newCA.GetJWTKeyPairs(), oldJWT)
c.Assert(err, check.NotNil)
_, err = s.verifyJWT(clock, s.server.ClusterName(), newCA.GetJWTKeyPairs(), newJWT)
_, err = s.verifyJWT(s.clock, s.server.ClusterName(), newCA.GetJWTKeyPairs(), newJWT)
c.Assert(err, check.IsNil)
}
@ -813,6 +811,7 @@ func (s *TLSSuite) TestRemoteUser(c *check.C) {
remoteServer, err := NewTestAuthServer(TestAuthServerConfig{
Dir: c.MkDir(),
ClusterName: "remote",
Clock: s.clock,
})
c.Assert(err, check.IsNil)
@ -924,6 +923,7 @@ func (s *TLSSuite) TestTunnelConnectionsCRUD(c *check.C) {
suite := &suite.ServicesTestSuite{
PresenceS: clt,
Clock: clockwork.NewFakeClock(),
}
suite.TunnelConnectionsCRUD(c)
}
@ -1178,7 +1178,7 @@ func (s *TLSSuite) TestValidatePostSessionSlice(c *check.C) {
Namespace: defaults.Namespace,
SessionID: string(sess.ID),
Chunks: []*events.SessionChunk{
&events.SessionChunk{
{
Time: time.Now().UTC().UnixNano(),
EventIndex: 0,
EventType: events.SessionStartEvent,
@ -1244,13 +1244,13 @@ func (s *TLSSuite) TestSharedSessions(c *check.C) {
Namespace: defaults.Namespace,
SessionID: string(sess.ID),
Chunks: []*events.SessionChunk{
&events.SessionChunk{
{
Time: time.Now().UTC().UnixNano(),
EventIndex: 0,
EventType: events.SessionStartEvent,
Data: marshal(events.EventFields{events.EventLogin: "bob", "val": "one"}),
},
&events.SessionChunk{
{
Time: time.Now().UTC().UnixNano(),
EventIndex: 1,
EventType: events.SessionEndEvent,
@ -1276,13 +1276,13 @@ func (s *TLSSuite) TestSharedSessions(c *check.C) {
Namespace: defaults.Namespace,
SessionID: string(anotherSessionID),
Chunks: []*events.SessionChunk{
&events.SessionChunk{
{
Time: time.Now().UTC().UnixNano(),
EventIndex: 0,
EventType: events.SessionStartEvent,
Data: marshal(events.EventFields{events.EventLogin: "alice", "val": "three"}),
},
&events.SessionChunk{
{
Time: time.Now().UTC().UnixNano(),
EventIndex: 1,
EventType: events.SessionEndEvent,
@ -2008,9 +2008,6 @@ func (s *TLSSuite) TestGenerateCerts(c *check.C) {
// TestGenerateAppToken checks the identity of the caller and makes sure only
// certain roles can request JWT tokens.
func (s *TLSSuite) TestGenerateAppToken(c *check.C) {
clock := clockwork.NewFakeClockAt(time.Now())
s.server.Auth().SetClock(clock)
authClient, err := s.server.NewClient(TestBuiltin(teleport.RoleAdmin))
c.Assert(err, check.IsNil)
@ -2020,7 +2017,7 @@ func (s *TLSSuite) TestGenerateAppToken(c *check.C) {
}, true)
c.Assert(err, check.IsNil)
key, err := ca.JWTSigner()
key, err := ca.JWTSigner(jwt.Config{Clock: s.clock})
c.Assert(err, check.IsNil)
var tests = []struct {
@ -2054,7 +2051,7 @@ func (s *TLSSuite) TestGenerateAppToken(c *check.C) {
Username: "foo@example.com",
Roles: []string{"bar", "baz"},
URI: "http://localhost:8080",
Expires: clock.Now().Add(1 * time.Minute),
Expires: s.clock.Now().Add(1 * time.Minute),
})
c.Assert(err != nil, check.Equals, tt.outError, tt.inComment)
if !tt.outError {
@ -2195,7 +2192,7 @@ func (s *TLSSuite) TestAuthenticateWebUserOTP(c *check.C) {
c.Assert(err, check.IsNil)
// create a valid otp token
validToken, err := totp.GenerateCode(otpSecret, s.server.Clock().Now())
validToken, err := totp.GenerateCode(otpSecret, s.clock.Now())
c.Assert(err, check.IsNil)
proxy, err := s.server.NewClient(TestBuiltin(teleport.RoleProxy))
@ -2496,6 +2493,7 @@ func (s *TLSSuite) TestRegisterCAPin(c *check.C) {
PublicSSHKey: pub,
PublicTLSKey: pubTLS,
CAPin: caPin,
Clock: s.clock,
})
c.Assert(err, check.IsNil)
@ -2513,6 +2511,7 @@ func (s *TLSSuite) TestRegisterCAPin(c *check.C) {
PublicSSHKey: pub,
PublicTLSKey: pubTLS,
CAPin: "sha256:123",
Clock: s.clock,
})
c.Assert(err, check.NotNil)
}
@ -2551,6 +2550,7 @@ func (s *TLSSuite) TestRegisterCAPath(c *check.C) {
PrivateKey: priv,
PublicSSHKey: pub,
PublicTLSKey: pubTLS,
Clock: s.clock,
})
c.Assert(err, check.IsNil)
@ -2582,6 +2582,7 @@ func (s *TLSSuite) TestRegisterCAPath(c *check.C) {
PublicSSHKey: pub,
PublicTLSKey: pubTLS,
CAPath: caPath,
Clock: s.clock,
})
c.Assert(err, check.IsNil)
}

View file

@ -226,10 +226,11 @@ func (c *Connector) Close() error {
// TeleportProcess structure holds the state of the Teleport daemon, controlling
// execution and configuration of the teleport services: ssh, auth and proxy.
type TeleportProcess struct {
clockwork.Clock
Clock clockwork.Clock
sync.Mutex
Supervisor
Config *Config
// localAuth has local auth server listed in case if this process
// has started with auth server role enabled
localAuth *auth.Server
@ -1314,7 +1315,7 @@ func (process *TeleportProcess) initAuthService() error {
} else {
srv.Spec.Rotation = state.Spec.Rotation
}
srv.SetTTL(process, defaults.ServerAnnounceTTL)
srv.SetTTL(process.Clock, defaults.ServerAnnounceTTL)
return &srv, nil
},
KeepAlivePeriod: defaults.ServerKeepAliveTTL,

View file

@ -88,7 +88,7 @@ func (f *processState) update(event Event) {
s, ok := f.states[component]
if !ok {
// Register a new component.
s = &componentState{recoveryTime: f.process.Now(), state: stateStarting}
s = &componentState{recoveryTime: f.process.Clock.Now(), state: stateStarting}
f.states[component] = s
}
@ -109,10 +109,10 @@ func (f *processState) update(event Event) {
f.process.log.Debugf("Teleport component %q has started.", component)
case stateDegraded:
s.state = stateRecovering
s.recoveryTime = f.process.Now()
s.recoveryTime = f.process.Clock.Now()
f.process.log.Infof("Teleport component %q is recovering from a degraded state.", component)
case stateRecovering:
if f.process.Now().Sub(s.recoveryTime) > defaults.HeartbeatCheckPeriod*2 {
if f.process.Clock.Now().Sub(s.recoveryTime) > defaults.HeartbeatCheckPeriod*2 {
s.state = stateOK
f.process.log.Infof("Teleport component %q has recovered from a degraded state.", component)
}

View file

@ -141,7 +141,7 @@ type UserCertParams struct {
ActiveRequests RequestIDs
}
// Check checks the user cert parameters
// Check checks the user certificate parameters
func (c UserCertParams) Check() error {
if len(c.PrivateCASigningKey) == 0 || c.CASigningAlg == "" {
return trace.BadParameter("PrivateCASigningKey and CASigningAlg are required")

View file

@ -47,7 +47,7 @@ func (s *ServicesSuite) SetUpSuite(c *check.C) {
func (s *ServicesSuite) SetUpTest(c *check.C) {
var err error
clock := clockwork.NewFakeClockAt(time.Now())
clock := clockwork.NewFakeClock()
s.bk, err = lite.NewWithConfig(context.TODO(), lite.Config{
Path: c.MkDir(),

View file

@ -53,11 +53,31 @@ var _ = fmt.Printf
// NewTestCA returns new test authority with a test key as a public and
// signing key
func NewTestCA(caType services.CertAuthType, clusterName string, privateKeys ...[]byte) *services.CertAuthorityV2 {
return NewTestCAWithConfig(TestCAConfig{
Type: caType,
ClusterName: clusterName,
PrivateKeys: privateKeys,
Clock: clockwork.NewRealClock(),
})
}
// TestCAConfig defines the configuration for generating
// a test certificate authority
type TestCAConfig struct {
Type services.CertAuthType
ClusterName string
PrivateKeys [][]byte
Clock clockwork.Clock
}
// NewTestCAWithConfig generates a new certificate authority with the specified
// configuration
func NewTestCAWithConfig(config TestCAConfig) *services.CertAuthorityV2 {
// privateKeys is to specify another RSA private key
if len(privateKeys) == 0 {
privateKeys = [][]byte{fixtures.PEMBytes["rsa"]}
if len(config.PrivateKeys) == 0 {
config.PrivateKeys = [][]byte{fixtures.PEMBytes["rsa"]}
}
keyBytes := privateKeys[0]
keyBytes := config.PrivateKeys[0]
rsaKey, err := ssh.ParseRawPrivateKey(keyBytes)
if err != nil {
panic(err)
@ -68,10 +88,15 @@ func NewTestCA(caType services.CertAuthType, clusterName string, privateKeys ...
panic(err)
}
key, cert, err := tlsca.GenerateSelfSignedCAWithPrivateKey(rsaKey.(*rsa.PrivateKey), pkix.Name{
CommonName: clusterName,
Organization: []string{clusterName},
}, nil, defaults.CATTL)
key, cert, err := tlsca.GenerateSelfSignedCAWithConfig(tlsca.GenerateCAConfig{
PrivateKey: rsaKey.(*rsa.PrivateKey),
Entity: pkix.Name{
CommonName: config.ClusterName,
Organization: []string{config.ClusterName},
},
TTL: defaults.CATTL,
Clock: config.Clock,
})
if err != nil {
panic(err)
}
@ -83,15 +108,15 @@ func NewTestCA(caType services.CertAuthType, clusterName string, privateKeys ...
return &services.CertAuthorityV2{
Kind: services.KindCertAuthority,
SubKind: string(caType),
SubKind: string(config.Type),
Version: services.V2,
Metadata: services.Metadata{
Name: clusterName,
Name: config.ClusterName,
Namespace: defaults.Namespace,
},
Spec: services.CertAuthoritySpecV2{
Type: caType,
ClusterName: clusterName,
Type: config.Type,
ClusterName: config.ClusterName,
CheckingKeys: [][]byte{ssh.MarshalAuthorizedKey(signer.PublicKey())},
SigningKeys: [][]byte{keyBytes},
TLSKeyPairs: []services.TLSKeyPair{{Cert: cert, Key: key}},
@ -528,7 +553,7 @@ func (s *ServicesTestSuite) WebSessionCRUD(c *check.C) {
_, err := s.WebS.GetWebSession("user1", "sid1")
c.Assert(trace.IsNotFound(err), check.Equals, true, check.Commentf("%#v", err))
dt := time.Date(2015, 6, 5, 4, 3, 2, 1, time.UTC).UTC()
dt := s.Clock.Now().Add(1 * time.Minute)
ws := services.NewWebSession("sid1", services.KindWebSession, services.KindWebSession,
services.WebSessionSpecV2{
Pub: []byte("pub123"),
@ -575,7 +600,7 @@ func (s *ServicesTestSuite) TokenCRUD(c *check.C) {
c.Assert(token.GetRoles().Include(teleport.RoleAuth), check.Equals, true)
c.Assert(token.GetRoles().Include(teleport.RoleNode), check.Equals, true)
c.Assert(token.GetRoles().Include(teleport.RoleProxy), check.Equals, false)
diff := time.Now().UTC().Add(defaults.ProvisioningTokenTTL).Second() - token.Expiry().Second()
diff := s.Clock.Now().UTC().Add(defaults.ProvisioningTokenTTL).Second() - token.Expiry().Second()
if diff > 1 {
c.Fatalf("expected diff to be within one second, got %v instead", diff)
}
@ -823,7 +848,7 @@ func (s *ServicesTestSuite) TunnelConnectionsCRUD(c *check.C) {
c.Assert(err, check.IsNil)
c.Assert(len(out), check.Equals, 0)
dt := time.Date(2015, 6, 5, 4, 3, 2, 1, time.UTC).UTC()
dt := s.Clock.Now()
conn, err := services.NewTunnelConnection("conn1", services.TunnelConnectionSpecV2{
ClusterName: clusterName,
ProxyName: "p1",

View file

@ -46,7 +46,7 @@ import (
)
type Suite struct {
clock clockwork.Clock
clock clockwork.FakeClock
dataDir string
authServer *auth.TestAuthServer
tlsServer *auth.TestTLSServer
@ -74,7 +74,7 @@ func TestApp(t *testing.T) { check.TestingT(t) }
func (s *Suite) SetUpSuite(c *check.C) {
utils.InitLoggerForTests(testing.Verbose())
s.clock = clockwork.NewFakeClockAt(time.Now())
s.clock = clockwork.NewFakeClock()
s.dataDir = c.MkDir()
var err error
@ -82,6 +82,7 @@ func (s *Suite) SetUpSuite(c *check.C) {
s.authServer, err = auth.NewTestAuthServer(auth.TestAuthServerConfig{
ClusterName: "root.example.com",
Dir: s.dataDir,
Clock: s.clock,
})
c.Assert(err, check.IsNil)
s.tlsServer, err = s.authServer.NewTestTLSServer()
@ -116,10 +117,13 @@ func (s *Suite) SetUpTest(c *check.C) {
// Create a in-memory HTTP server that will respond with a UUID. This value
// will be checked in the client later to ensure a connection was made.
s.message = uuid.New()
s.testhttp = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
s.testhttp = httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, s.message)
s.closeFunc()
}))
s.testhttp.Config.TLSConfig = &tls.Config{Time: s.clock.Now}
s.testhttp.Start()
// Extract the hostport that the in-memory HTTP server is running on.
u, err := url.Parse(s.testhttp.URL)
@ -147,7 +151,7 @@ func (s *Suite) SetUpTest(c *check.C) {
Spec: services.ServerSpecV2{
Version: teleport.Version,
Apps: []*services.App{
&services.App{
{
Name: "foo",
URI: s.testhttp.URL,
PublicAddr: "foo.example.com",
@ -166,6 +170,7 @@ func (s *Suite) SetUpTest(c *check.C) {
c.Assert(err, check.IsNil)
tlsConfig, err := serverIdentity.TLSConfig(nil)
c.Assert(err, check.IsNil)
tlsConfig.Time = s.clock.Now
// Generate certificate for user.
privateKey, publicKey, err := s.tlsServer.Auth().GenerateKeyPair("")
@ -286,6 +291,8 @@ func (s *Suite) TestHandleConnection(c *check.C) {
RootCAs: s.hostCertPool,
// Certificates is the user's application specific certificate.
Certificates: []tls.Certificate{s.clientCertificate},
// Time defines the time anchor for certificate validation
Time: s.clock.Now,
},
},
}

View file

@ -29,8 +29,10 @@ import (
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/sshutils"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
log "github.com/sirupsen/logrus"
)
@ -54,6 +56,11 @@ type AuthHandlers struct {
// FIPS mode means Teleport started in a FedRAMP/FIPS 140-2 compliant
// configuration.
FIPS bool
// Clock specifies the time provider. Will be used to override the time anchor
// for TLS certificate verification.
// Defaults to real clock if unspecified
Clock clockwork.Clock
}
// CreateIdentityContext returns an IdentityContext populated with information
@ -198,9 +205,14 @@ func (h *AuthHandlers) UserKeyAuth(conn ssh.ConnMetadata, key ssh.PublicKey) (*s
// Check that the user certificate uses supported public key algorithms, was
// issued by Teleport, and check the certificate metadata (principals,
// timestamp, etc). Fallback to keys is not supported.
clock := time.Now
if h.Clock != nil {
clock = h.Clock.Now
}
certChecker := utils.CertChecker{
CertChecker: ssh.CertChecker{
IsUserAuthority: h.IsUserAuthority,
Clock: clock,
},
FIPS: h.FIPS,
}

View file

@ -299,6 +299,7 @@ func New(c ServerConfig) (*Server, error) {
AccessPoint: c.AuthClient,
FIPS: c.FIPS,
Emitter: c.Emitter,
Clock: c.Clock,
}
// Common term handlers.

View file

@ -297,6 +297,15 @@ func (s *Server) HandleConnection(conn net.Conn) {
// RotationGetter returns rotation state
type RotationGetter func(role teleport.Role) (*services.Rotation, error)
// SetClock is a functional server option to override the internal
// clock
func SetClock(clock clockwork.Clock) ServerOption {
return func(s *Server) error {
s.clock = clock
return nil
}
}
// SetRotationGetter sets rotation state getter
func SetRotationGetter(getter RotationGetter) ServerOption {
return func(s *Server) error {
@ -542,6 +551,7 @@ func New(addr utils.NetAddr,
AccessPoint: s.authService,
FIPS: s.fips,
Emitter: s.StreamEmitter,
Clock: s.clock,
}
// common term handlers

View file

@ -52,6 +52,7 @@ import (
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
"github.com/pborman/uuid"
"github.com/sirupsen/logrus"
. "gopkg.in/check.v1"
@ -76,6 +77,7 @@ type SrvSuite struct {
nodeID string
adminClient *auth.Client
testServer *auth.TestAuthServer
clock clockwork.FakeClock
}
// teleportTestUser is additional user used for tests
@ -112,9 +114,12 @@ func (s *SrvSuite) SetUpTest(c *C) {
c.Assert(err, IsNil)
s.user = u.Username
s.clock = clockwork.NewFakeClock()
authServer, err := auth.NewTestAuthServer(auth.TestAuthServerConfig{
ClusterName: "localhost",
Dir: c.MkDir(),
Clock: s.clock,
})
c.Assert(err, IsNil)
s.server, err = authServer.NewTestTLSServer()
@ -184,6 +189,7 @@ func (s *SrvSuite) SetUpTest(c *C) {
},
),
SetBPF(&bpf.NOP{}),
SetClock(s.clock),
)
c.Assert(err, IsNil)
s.srv = srv
@ -215,7 +221,6 @@ func (s *SrvSuite) SetUpTest(c *C) {
c.Assert(err, IsNil)
c.Assert(agent.ForwardToAgent(client, keyring), IsNil)
s.clt = client
}
func (s *SrvSuite) TearDownTest(c *C) {
@ -736,6 +741,7 @@ func (s *SrvSuite) TestProxyReverseTunnel(c *C) {
SetNamespace(defaults.Namespace),
SetPAMConfig(&pam.Config{Enabled: false}),
SetBPF(&bpf.NOP{}),
SetClock(s.clock),
)
c.Assert(err, IsNil)
c.Assert(proxy.Start(), IsNil)
@ -813,9 +819,9 @@ func (s *SrvSuite) TestProxyReverseTunnel(c *C) {
SetPAMConfig(&pam.Config{Enabled: false}),
SetBPF(&bpf.NOP{}),
SetEmitter(s.nodeClient),
SetClock(s.clock),
)
c.Assert(err, IsNil)
c.Assert(err, IsNil)
c.Assert(srv2.Start(), IsNil)
c.Assert(srv2.heartbeat.ForceSend(time.Second), IsNil)
defer srv2.Close()
@ -903,6 +909,7 @@ func (s *SrvSuite) TestProxyRoundRobin(c *C) {
SetNamespace(defaults.Namespace),
SetPAMConfig(&pam.Config{Enabled: false}),
SetBPF(&bpf.NOP{}),
SetClock(s.clock),
)
c.Assert(err, IsNil)
c.Assert(proxy.Start(), IsNil)
@ -1008,6 +1015,7 @@ func (s *SrvSuite) TestProxyDirectAccess(c *C) {
SetNamespace(defaults.Namespace),
SetPAMConfig(&pam.Config{Enabled: false}),
SetBPF(&bpf.NOP{}),
SetClock(s.clock),
)
c.Assert(err, IsNil)
c.Assert(proxy.Start(), IsNil)
@ -1087,12 +1095,12 @@ func (s *SrvSuite) TestLimiter(c *C) {
limiter.Config{
MaxConnections: 2,
Rates: []limiter.Rate{
limiter.Rate{
{
Period: 10 * time.Second,
Average: 1,
Burst: 3,
},
limiter.Rate{
{
Period: 40 * time.Millisecond,
Average: 10,
Burst: 30,
@ -1118,6 +1126,7 @@ func (s *SrvSuite) TestLimiter(c *C) {
SetNamespace(defaults.Namespace),
SetPAMConfig(&pam.Config{Enabled: false}),
SetBPF(&bpf.NOP{}),
SetClock(s.clock),
)
c.Assert(err, IsNil)
c.Assert(srv.Start(), IsNil)

View file

@ -30,6 +30,7 @@ import (
"github.com/gravitational/teleport"
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
)
// ClusterName returns cluster name from organization
@ -42,8 +43,39 @@ func ClusterName(subject pkix.Name) (string, error) {
// GenerateSelfSignedCA generates self-signed certificate authority used for internal inter-node communications
func GenerateSelfSignedCAWithPrivateKey(priv *rsa.PrivateKey, entity pkix.Name, dnsNames []string, ttl time.Duration) ([]byte, []byte, error) {
notBefore := time.Now()
notAfter := notBefore.Add(ttl)
return GenerateSelfSignedCAWithConfig(GenerateCAConfig{
PrivateKey: priv,
Entity: entity,
DNSNames: dnsNames,
TTL: ttl,
Clock: clockwork.NewRealClock(),
})
}
// GenerateCAConfig defines the configuration for generating
// self-signed CA certificates
type GenerateCAConfig struct {
PrivateKey *rsa.PrivateKey
Entity pkix.Name
DNSNames []string
TTL time.Duration
Clock clockwork.Clock
}
// setDefaults imposes defaults on this configuration
func (r *GenerateCAConfig) setDefaults() {
if r.Clock == nil {
r.Clock = clockwork.NewRealClock()
}
}
// GenerateSelfSignedCAWithConfig generates a new CA certificate from the specified
// configuration.
// Returns PEM-encoded private key/certificate payloads upon success
func GenerateSelfSignedCAWithConfig(config GenerateCAConfig) (keyPEM []byte, certPEM []byte, err error) {
config.setDefaults()
notBefore := config.Clock.Now()
notAfter := notBefore.Add(config.TTL)
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
@ -52,27 +84,27 @@ func GenerateSelfSignedCAWithPrivateKey(priv *rsa.PrivateKey, entity pkix.Name,
}
// this is important, otherwise go will accept certificate authorities
// signed by the same private key and having the same subject (happens in tests)
entity.SerialNumber = serialNumber.String()
config.Entity.SerialNumber = serialNumber.String()
template := x509.Certificate{
SerialNumber: serialNumber,
Issuer: entity,
Subject: entity,
Issuer: config.Entity,
Subject: config.Entity,
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
IsCA: true,
DNSNames: dnsNames,
DNSNames: config.DNSNames,
}
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &config.PrivateKey.PublicKey, config.PrivateKey)
if err != nil {
return nil, nil, trace.Wrap(err)
}
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
keyPEM = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(config.PrivateKey)})
certPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
return keyPEM, certPEM, nil
}

View file

@ -92,6 +92,14 @@ func SetSessionStreamPollPeriod(period time.Duration) HandlerOption {
}
}
// SetClock sets the clock on a handler
func SetClock(clock clockwork.Clock) HandlerOption {
return func(h *Handler) error {
h.clock = clock
return nil
}
}
// Config represents web handler configuration parameters
type Config struct {
// Proxy is a reverse tunnel proxy that handles connections
@ -171,15 +179,10 @@ func (h *RewritingHandler) Close() error {
// NewHandler returns a new instance of web proxy handler
func NewHandler(cfg Config, opts ...HandlerOption) (*RewritingHandler, error) {
const apiPrefix = "/" + teleport.WebAPIVersion
lauth, err := newSessionCache(cfg.ProxyClient, []utils.NetAddr{cfg.AuthServers}, cfg.CipherSuites)
if err != nil {
return nil, trace.Wrap(err)
}
h := &Handler{
cfg: cfg,
auth: lauth,
log: newPackageLogger(),
cfg: cfg,
log: newPackageLogger(),
clock: clockwork.NewRealClock(),
}
for _, o := range opts {
@ -188,6 +191,17 @@ func NewHandler(cfg Config, opts ...HandlerOption) (*RewritingHandler, error) {
}
}
auth, err := newSessionCache(&sessionCache{
proxyClient: cfg.ProxyClient,
authServers: []utils.NetAddr{cfg.AuthServers},
cipherSuites: cfg.CipherSuites,
clock: h.clock,
})
if err != nil {
return nil, trace.Wrap(err)
}
h.auth = auth
_, sshPort, err := net.SplitHostPort(cfg.ProxySSHAddr.String())
if err != nil {
h.log.WithError(err).Warnf("Invalid SSH proxy address %q, will use default port %v.",
@ -196,10 +210,6 @@ func NewHandler(cfg Config, opts ...HandlerOption) (*RewritingHandler, error) {
}
h.sshPort = sshPort
if h.clock == nil {
h.clock = clockwork.NewRealClock()
}
// ping endpoint is used to check if the server is up. the /webapi/ping
// endpoint returns the default authentication method and configuration that
// the server supports. the /webapi/ping/:connector endpoint can be used to
@ -1190,7 +1200,7 @@ func NewSessionResponse(ctx *SessionContext) (*CreateSessionResponse, error) {
return &CreateSessionResponse{
Type: roundtrip.AuthBearer,
Token: webSession.GetBearerToken(),
ExpiresIn: int(time.Until(webSession.GetBearerTokenExpiryTime()) / time.Second),
ExpiresIn: int(webSession.GetBearerTokenExpiryTime().Sub(ctx.parent.clock.Now()) / time.Second),
}, nil
}

View file

@ -99,12 +99,10 @@ type WebSuite struct {
mockU2F *mocku2f.Key
server *auth.TestTLSServer
proxyClient *auth.Client
clock clockwork.Clock
clock clockwork.FakeClock
}
var _ = Suite(&WebSuite{
clock: clockwork.NewFakeClock(),
})
var _ = Suite(&WebSuite{})
// TestMain will re-execute Teleport to run a command if "exec" is passed to
// it as an argument. Otherwise it will run tests as normal.
@ -144,11 +142,12 @@ func (s *WebSuite) SetUpTest(c *C) {
u, err := user.Current()
c.Assert(err, IsNil)
s.user = u.Username
s.clock = clockwork.NewFakeClock()
authServer, err := auth.NewTestAuthServer(auth.TestAuthServerConfig{
ClusterName: "localhost",
Dir: c.MkDir(),
Clock: clockwork.NewFakeClockAt(time.Date(2017, 05, 10, 18, 53, 0, 0, time.UTC)),
Clock: s.clock,
})
c.Assert(err, IsNil)
s.server, err = authServer.NewTestTLSServer()
@ -191,6 +190,7 @@ func (s *WebSuite) SetUpTest(c *C) {
regular.SetEmitter(nodeClient),
regular.SetPAMConfig(&pam.Config{Enabled: false}),
regular.SetBPF(&bpf.NOP{}),
regular.SetClock(s.clock),
)
c.Assert(err, IsNil)
s.node = node
@ -243,6 +243,7 @@ func (s *WebSuite) SetUpTest(c *C) {
regular.SetEmitter(s.proxyClient),
regular.SetNamespace(defaults.Namespace),
regular.SetBPF(&bpf.NOP{}),
regular.SetClock(s.clock),
)
c.Assert(err, IsNil)
@ -256,7 +257,7 @@ func (s *WebSuite) SetUpTest(c *C) {
Context: context.Background(),
HostUUID: proxyID,
Emitter: s.proxyClient,
}, SetSessionStreamPollPeriod(200*time.Millisecond))
}, SetSessionStreamPollPeriod(200*time.Millisecond), SetClock(s.clock))
c.Assert(err, IsNil)
s.webServer = httptest.NewUnstartedServer(handler)
@ -314,9 +315,7 @@ func (s *WebSuite) authPackFromResponse(c *C, re *roundtrip.Response) *authPack
jar.SetCookies(s.url(), re.Cookies())
session, err := sess.response()
if err != nil {
panic(err)
}
c.Assert(err, IsNil)
if session.ExpiresIn < 0 {
c.Errorf("expected expiry time to be in the future but got %v", session.ExpiresIn)
}
@ -346,7 +345,7 @@ func (s *WebSuite) authPack(c *C, user string) *authPack {
s.createUser(c, user, login, pass, otpSecret)
// create a valid otp token
validToken, err := totp.GenerateCode(otpSecret, time.Now())
validToken, err := totp.GenerateCode(otpSecret, s.clock.Now())
c.Assert(err, IsNil)
clt := s.client()
@ -590,10 +589,10 @@ func (s *WebSuite) TestCSRF(c *C) {
func (s *WebSuite) TestPasswordChange(c *C) {
pack := s.authPack(c, "foo")
fakeClock := clockwork.NewFakeClock()
s.server.AuthServer.AuthServer.SetClock(fakeClock)
validToken, err := totp.GenerateCode(pack.otpSecret, fakeClock.Now())
// invalidate the token
s.clock.Advance(1 * time.Minute)
validToken, err := totp.GenerateCode(pack.otpSecret, s.clock.Now())
c.Assert(err, IsNil)
req := changePasswordReq{
@ -1345,7 +1344,9 @@ func (s *WebSuite) TestChangePasswordWithTokenOTP(c *C) {
secrets, err := s.server.Auth().RotateResetPasswordTokenSecrets(context.TODO(), token.GetName())
c.Assert(err, IsNil)
secondFactorToken, err := totp.GenerateCode(secrets.GetOTPKey(), time.Now())
// Advance the clock to invalidate the TOTP token
s.clock.Advance(1 * time.Minute)
secondFactorToken, err := totp.GenerateCode(secrets.GetOTPKey(), s.clock.Now())
c.Assert(err, IsNil)
data, err := json.Marshal(auth.ChangePasswordWithTokenRequest{
@ -1815,7 +1816,7 @@ func (s *WebSuite) TestCreateAppSession(c *C) {
Spec: services.ServerSpecV2{
Version: teleport.Version,
Apps: []*services.App{
&services.App{
{
Name: "panel",
PublicAddr: "panel.example.com",
URI: "http://127.0.0.1:8080",
@ -1902,11 +1903,6 @@ func (s *WebSuite) TestCreateAppSession(c *C) {
}
}
// TestAppRouting verifies requests get routed correctly: either to the Web UI
// or an application.
func (s *WebSuite) TestRouting(c *C) {
}
type authProviderMock struct {
server services.ServerV2
}

View file

@ -314,23 +314,27 @@ func (c *SessionContext) Close() error {
}
// newSessionCache returns new instance of the session cache
func newSessionCache(proxyClient auth.ClientI, servers []utils.NetAddr, cipherSuites []uint16) (*sessionCache, error) {
clusterName, err := proxyClient.GetClusterName()
func newSessionCache(config *sessionCache) (*sessionCache, error) {
clusterName, err := config.proxyClient.GetClusterName()
if err != nil {
return nil, trace.Wrap(err)
}
m, err := ttlmap.New(1024, ttlmap.CallOnExpire(closeContext))
if config.clock == nil {
config.clock = clockwork.NewRealClock()
}
m, err := ttlmap.New(1024, ttlmap.CallOnExpire(closeContext), ttlmap.Clock(config.clock))
if err != nil {
return nil, trace.Wrap(err)
}
cache := &sessionCache{
clusterName: clusterName.GetClusterName(),
proxyClient: proxyClient,
proxyClient: config.proxyClient,
contexts: m,
authServers: servers,
authServers: config.authServers,
closer: utils.NewCloseBroadcaster(),
cipherSuites: cipherSuites,
cipherSuites: config.cipherSuites,
log: newPackageLogger(),
clock: config.clock,
}
// periodically close expired and unused sessions
go cache.expireSessions()
@ -347,6 +351,7 @@ type sessionCache struct {
authServers []utils.NetAddr
closer *utils.CloseBroadcaster
clusterName string
clock clockwork.Clock
// cipherSuites is the list of supported TLS cipher suites.
cipherSuites []uint16
@ -576,6 +581,7 @@ func (s *sessionCache) ValidateSession(user, sid string) (*SessionContext, error
tlsConfig.Certificates = []tls.Certificate{tlsCert}
tlsConfig.RootCAs = certPool
tlsConfig.ServerName = auth.EncodeClusterName(s.clusterName)
tlsConfig.Time = s.clock.Now
userClient, err := auth.NewClient(apiclient.Config{
Addrs: utils.NetAddrsToStrings(s.authServers),
@ -597,7 +603,7 @@ func (s *sessionCache) ValidateSession(user, sid string) (*SessionContext, error
}),
}
ttl := utils.ToTTL(clockwork.NewRealClock(), sess.GetBearerTokenExpiryTime())
ttl := utils.ToTTL(s.clock, sess.GetBearerTokenExpiryTime())
out, err := s.insertContext(user, sid, c, ttl)
if err != nil {
// this means that someone has just inserted the context, so

View file

@ -152,7 +152,7 @@ func (fc *fakeClock) NewTicker(d time.Duration) Ticker {
clock: fc,
period: d,
}
go ft.tick()
ft.runTickThread()
return ft
}

View file

@ -34,33 +34,39 @@ func (ft *fakeTicker) Stop() {
ft.stop <- true
}
// tick sends the tick time to the ticker channel after every period.
// Tick events are discarded if the underlying ticker channel does
// not have enough capacity.
func (ft *fakeTicker) tick() {
tick := ft.clock.Now()
for {
tick = tick.Add(ft.period)
remaining := tick.Sub(ft.clock.Now())
if remaining <= 0 {
// The tick should have already happened. This can happen when
// Advance() is called on the fake clock with a duration larger
// than this ticker's period.
// runTickThread initializes a background goroutine to send the tick time to the ticker channel
// after every period. Tick events are discarded if the underlying ticker channel does not have
// enough capacity.
func (ft *fakeTicker) runTickThread() {
nextTick := ft.clock.Now().Add(ft.period)
next := ft.clock.After(ft.period)
go func() {
for {
select {
case ft.c <- tick:
default:
}
continue
}
select {
case <-ft.stop:
return
case <-ft.clock.After(remaining):
select {
case ft.c <- tick:
default:
case <-ft.stop:
return
case <-next:
// We send the time that the tick was supposed to occur at.
tick := nextTick
// Before sending the tick, we'll compute the next tick time and star the clock.After call.
now := ft.clock.Now()
// First, figure out how many periods there have been between "now" and the time we were
// supposed to have trigged, then advance over all of those.
skipTicks := (now.Sub(tick) + ft.period - 1) / ft.period
nextTick = nextTick.Add(skipTicks * ft.period)
// Now, keep advancing until we are past now. This should happen at most once.
for !nextTick.After(now) {
nextTick = nextTick.Add(ft.period)
}
// Figure out how long between now and the next scheduled tick, then wait that long.
remaining := nextTick.Sub(now)
next = ft.clock.After(remaining)
// Finally, we can actually send the tick.
select {
case ft.c <- tick:
default:
}
}
}
}
}()
}

2
vendor/modules.txt vendored
View file

@ -274,7 +274,7 @@ github.com/johannesboyne/gofakes3
github.com/johannesboyne/gofakes3/backend/s3mem
github.com/johannesboyne/gofakes3/internal/goskipiter
github.com/johannesboyne/gofakes3/internal/s3io
# github.com/jonboulle/clockwork v0.2.1
# github.com/jonboulle/clockwork v0.2.2
## explicit
github.com/jonboulle/clockwork
# github.com/json-iterator/go v1.1.10