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 returns first PEM encoded TLS cert
GetTLSKeyPairs() []TLSKeyPair GetTLSKeyPairs() []TLSKeyPair
// JWTSigner returns the active JWT key used to sign tokens. // 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 gets all JWT key pairs.
GetJWTKeyPairs() []JWTKeyPair GetJWTKeyPairs() []JWTKeyPair
// SetJWTKeyPairs sets all JWT key pairs. // 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. // 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 { if len(ca.Spec.JWTKeyPairs) == 0 {
return nil, trace.BadParameter("no JWT keypairs found") return nil, trace.BadParameter("no JWT keypairs found")
} }
@ -229,11 +229,10 @@ func (ca *CertAuthorityV2) JWTSigner() (*jwt.Key, error) {
if err != nil { if err != nil {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
key, err := jwt.New(&jwt.Config{ config.Algorithm = defaults.ApplicationTokenAlgorithm
Algorithm: defaults.ApplicationTokenAlgorithm, config.ClusterName = ca.Spec.ClusterName
ClusterName: ca.Spec.ClusterName, config.PrivateKey = privateKey
PrivateKey: privateKey, key, err := jwt.New(&config)
})
if err != nil { if err != nil {
return nil, trace.Wrap(err) 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/hashicorp/golang-lru v0.5.4
github.com/iovisor/gobpf v0.0.1 github.com/iovisor/gobpf v0.0.1
github.com/johannesboyne/gofakes3 v0.0.0-20191228161223-9aee1c78a252 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/json-iterator/go v1.1.10
github.com/julienschmidt/httprouter v1.2.0 github.com/julienschmidt/httprouter v1.2.0
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 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.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.0 h1:J2SLSdy7HgElq8ekSl2Mxh6vrRNFxqbXGenYH2I02Vs= 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.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/jonboulle/clockwork v0.2.1 h1:S/EaQvW6FpWMYAvYvY+OBDvpaM+izu0oiwo5y0MH7U0= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.1/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= 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.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.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= 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/tlsca"
"github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/teleport/lib/wrappers" "github.com/gravitational/teleport/lib/wrappers"
"github.com/pborman/uuid"
"github.com/coreos/go-oidc/oauth2" "github.com/coreos/go-oidc/oauth2"
"github.com/coreos/go-oidc/oidc" "github.com/coreos/go-oidc/oidc"
"github.com/gravitational/trace" "github.com/gravitational/trace"
"github.com/jonboulle/clockwork" "github.com/jonboulle/clockwork"
"github.com/pborman/uuid"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
saml2 "github.com/russellhaering/gosaml2" saml2 "github.com/russellhaering/gosaml2"
"github.com/tstranex/u2f" "github.com/tstranex/u2f"
@ -1842,6 +1842,13 @@ func (a *Server) GetAppSession(ctx context.Context, req services.GetAppSessionRe
return a.GetCache().GetAppSession(ctx, req) 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 // authKeepAliver is a keep aliver using auth server directly
type authKeepAliver struct { type authKeepAliver struct {
sync.RWMutex sync.RWMutex

View file

@ -67,7 +67,7 @@ func (cfg *TestAuthServerConfig) CheckAndSetDefaults() error {
return trace.BadParameter("missing parameter Dir") return trace.BadParameter("missing parameter Dir")
} }
if cfg.Clock == nil { if cfg.Clock == nil {
cfg.Clock = clockwork.NewFakeClockAt(time.Now()) cfg.Clock = clockwork.NewFakeClock()
} }
if len(cfg.CipherSuites) == 0 { if len(cfg.CipherSuites) == 0 {
cfg.CipherSuites = utils.DefaultCipherSuites() cfg.CipherSuites = utils.DefaultCipherSuites()
@ -160,13 +160,13 @@ func NewTestAuthServer(cfg TestAuthServerConfig) (*TestAuthServer, error) {
srv.AuthServer, err = NewServer(&InitConfig{ srv.AuthServer, err = NewServer(&InitConfig{
Backend: srv.Backend, Backend: srv.Backend,
Authority: authority.New(), Authority: authority.NewWithClock(cfg.Clock),
Access: access, Access: access,
Identity: identity, Identity: identity,
AuditLog: srv.AuditLog, AuditLog: srv.AuditLog,
SkipPeriodicOperations: true, SkipPeriodicOperations: true,
Emitter: localLog, Emitter: localLog,
}) }, WithClock(cfg.Clock))
if err != nil { if err != nil {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
@ -214,13 +214,25 @@ func NewTestAuthServer(cfg TestAuthServerConfig) (*TestAuthServer, error) {
} }
// Setup certificate and signing authorities. // 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) 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) 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) 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.Certificates = []tls.Certificate{*cert}
tlsConfig.RootCAs = pool tlsConfig.RootCAs = pool
tlsConfig.ServerName = EncodeClusterName(a.ClusterName) tlsConfig.ServerName = EncodeClusterName(a.ClusterName)
tlsConfig.Time = a.AuthServer.clock.Now
addrs := []string{addr.String()} addrs := []string{addr.String()}
return NewClient(client.Config{Addrs: addrs, TLS: tlsConfig}) return NewClient(client.Config{Addrs: addrs, TLS: tlsConfig})
} }
@ -461,6 +474,7 @@ func NewTestTLSServer(cfg TestTLSServerConfig) (*TestTLSServer, error) {
if err != nil { if err != nil {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
tlsConfig.Time = cfg.AuthServer.Clock().Now
accessPoint, err := NewAdminAuthServer(srv.AuthServer.AuthServer, srv.AuthServer.SessionServer, srv.AuthServer.AuditLog) accessPoint, err := NewAdminAuthServer(srv.AuthServer.AuthServer, srv.AuthServer.SessionServer, srv.AuthServer.AuditLog)
if err != nil { 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") return nil, trace.Wrap(err, "failed to parse TLS cert and key")
} }
tlsConfig.Certificates = []tls.Certificate{tlsCert} tlsConfig.Certificates = []tls.Certificate{tlsCert}
tlsConfig.Time = t.AuthServer.AuthServer.clock.Now
addrs := []string{t.Addr().String()} addrs := []string{t.Addr().String()}
return NewClient(client.Config{Addrs: addrs, TLS: tlsConfig}) 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 // server should apply Nop builtin role
tlsConfig.Certificates = nil tlsConfig.Certificates = nil
} }
tlsConfig.Time = t.AuthServer.AuthServer.clock.Now
return tlsConfig, nil return tlsConfig, nil
} }

View file

@ -29,6 +29,7 @@ import (
"github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/trace" "github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
) )
// LocalRegister is used to generate host keys when a node or proxy is running // LocalRegister is used to generate host keys when a node or proxy is running
@ -91,6 +92,16 @@ type RegisterParams struct {
CAPath string CAPath string
// GetHostCredentials is a client that can fetch host credentials. // GetHostCredentials is a client that can fetch host credentials.
GetHostCredentials HostCredentials 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 // 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 // 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. // as well as a method for the node to validate the auth server.
func Register(params RegisterParams) (*Identity, error) { func Register(params RegisterParams) (*Identity, error) {
params.setDefaults()
// Read in the token. The token can either be passed in or come from a file // Read in the token. The token can either be passed in or come from a file
// on disk. // on disk.
token, err := utils.ReadToken(params.Token) token, err := utils.ReadToken(params.Token)
@ -219,6 +231,7 @@ func registerThroughAuth(token string, params RegisterParams) (*Identity, error)
// Server it is connecting to. // Server it is connecting to.
func insecureRegisterClient(params RegisterParams) (*Client, error) { func insecureRegisterClient(params RegisterParams) (*Client, error) {
tlsConfig := utils.TLSConfig(params.CipherSuites) tlsConfig := utils.TLSConfig(params.CipherSuites)
tlsConfig.Time = params.Clock.Now
cert, err := readCA(params) cert, err := readCA(params)
if err != nil && !trace.IsNotFound(err) { 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. // an attacker were to MITM this connection the CA pin will not match below.
tlsConfig := utils.TLSConfig(params.CipherSuites) tlsConfig := utils.TLSConfig(params.CipherSuites)
tlsConfig.InsecureSkipVerify = true tlsConfig.InsecureSkipVerify = true
tlsConfig.Time = params.Clock.Now
authClient, err := NewClient(client.Config{Addrs: utils.NetAddrsToStrings(params.Servers), TLS: tlsConfig}) authClient, err := NewClient(client.Config{Addrs: utils.NetAddrsToStrings(params.Servers), TLS: tlsConfig})
if err != nil { if err != nil {
return nil, trace.Wrap(err) 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 // Create another client, but this time with the CA provided to validate
// that the Auth Server was issued a certificate by the same CA. // that the Auth Server was issued a certificate by the same CA.
tlsConfig = utils.TLSConfig(params.CipherSuites) tlsConfig = utils.TLSConfig(params.CipherSuites)
tlsConfig.Time = params.Clock.Now
certPool := x509.NewCertPool() certPool := x509.NewCertPool()
certPool.AddCert(tlsCA) certPool.AddCert(tlsCA)
tlsConfig.RootCAs = certPool tlsConfig.RootCAs = certPool

View file

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

View file

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

View file

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

View file

@ -88,7 +88,7 @@ func (f *processState) update(event Event) {
s, ok := f.states[component] s, ok := f.states[component]
if !ok { if !ok {
// Register a new component. // 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 f.states[component] = s
} }
@ -109,10 +109,10 @@ func (f *processState) update(event Event) {
f.process.log.Debugf("Teleport component %q has started.", component) f.process.log.Debugf("Teleport component %q has started.", component)
case stateDegraded: case stateDegraded:
s.state = stateRecovering 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) f.process.log.Infof("Teleport component %q is recovering from a degraded state.", component)
case stateRecovering: 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 s.state = stateOK
f.process.log.Infof("Teleport component %q has recovered from a degraded state.", component) 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 ActiveRequests RequestIDs
} }
// Check checks the user cert parameters // Check checks the user certificate parameters
func (c UserCertParams) Check() error { func (c UserCertParams) Check() error {
if len(c.PrivateCASigningKey) == 0 || c.CASigningAlg == "" { if len(c.PrivateCASigningKey) == 0 || c.CASigningAlg == "" {
return trace.BadParameter("PrivateCASigningKey and CASigningAlg are required") 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) { func (s *ServicesSuite) SetUpTest(c *check.C) {
var err error var err error
clock := clockwork.NewFakeClockAt(time.Now()) clock := clockwork.NewFakeClock()
s.bk, err = lite.NewWithConfig(context.TODO(), lite.Config{ s.bk, err = lite.NewWithConfig(context.TODO(), lite.Config{
Path: c.MkDir(), 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 // NewTestCA returns new test authority with a test key as a public and
// signing key // signing key
func NewTestCA(caType services.CertAuthType, clusterName string, privateKeys ...[]byte) *services.CertAuthorityV2 { 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 // privateKeys is to specify another RSA private key
if len(privateKeys) == 0 { if len(config.PrivateKeys) == 0 {
privateKeys = [][]byte{fixtures.PEMBytes["rsa"]} config.PrivateKeys = [][]byte{fixtures.PEMBytes["rsa"]}
} }
keyBytes := privateKeys[0] keyBytes := config.PrivateKeys[0]
rsaKey, err := ssh.ParseRawPrivateKey(keyBytes) rsaKey, err := ssh.ParseRawPrivateKey(keyBytes)
if err != nil { if err != nil {
panic(err) panic(err)
@ -68,10 +88,15 @@ func NewTestCA(caType services.CertAuthType, clusterName string, privateKeys ...
panic(err) panic(err)
} }
key, cert, err := tlsca.GenerateSelfSignedCAWithPrivateKey(rsaKey.(*rsa.PrivateKey), pkix.Name{ key, cert, err := tlsca.GenerateSelfSignedCAWithConfig(tlsca.GenerateCAConfig{
CommonName: clusterName, PrivateKey: rsaKey.(*rsa.PrivateKey),
Organization: []string{clusterName}, Entity: pkix.Name{
}, nil, defaults.CATTL) CommonName: config.ClusterName,
Organization: []string{config.ClusterName},
},
TTL: defaults.CATTL,
Clock: config.Clock,
})
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -83,15 +108,15 @@ func NewTestCA(caType services.CertAuthType, clusterName string, privateKeys ...
return &services.CertAuthorityV2{ return &services.CertAuthorityV2{
Kind: services.KindCertAuthority, Kind: services.KindCertAuthority,
SubKind: string(caType), SubKind: string(config.Type),
Version: services.V2, Version: services.V2,
Metadata: services.Metadata{ Metadata: services.Metadata{
Name: clusterName, Name: config.ClusterName,
Namespace: defaults.Namespace, Namespace: defaults.Namespace,
}, },
Spec: services.CertAuthoritySpecV2{ Spec: services.CertAuthoritySpecV2{
Type: caType, Type: config.Type,
ClusterName: clusterName, ClusterName: config.ClusterName,
CheckingKeys: [][]byte{ssh.MarshalAuthorizedKey(signer.PublicKey())}, CheckingKeys: [][]byte{ssh.MarshalAuthorizedKey(signer.PublicKey())},
SigningKeys: [][]byte{keyBytes}, SigningKeys: [][]byte{keyBytes},
TLSKeyPairs: []services.TLSKeyPair{{Cert: cert, Key: key}}, TLSKeyPairs: []services.TLSKeyPair{{Cert: cert, Key: key}},
@ -528,7 +553,7 @@ func (s *ServicesTestSuite) WebSessionCRUD(c *check.C) {
_, err := s.WebS.GetWebSession("user1", "sid1") _, err := s.WebS.GetWebSession("user1", "sid1")
c.Assert(trace.IsNotFound(err), check.Equals, true, check.Commentf("%#v", err)) 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, ws := services.NewWebSession("sid1", services.KindWebSession, services.KindWebSession,
services.WebSessionSpecV2{ services.WebSessionSpecV2{
Pub: []byte("pub123"), 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.RoleAuth), check.Equals, true)
c.Assert(token.GetRoles().Include(teleport.RoleNode), check.Equals, true) c.Assert(token.GetRoles().Include(teleport.RoleNode), check.Equals, true)
c.Assert(token.GetRoles().Include(teleport.RoleProxy), check.Equals, false) 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 { if diff > 1 {
c.Fatalf("expected diff to be within one second, got %v instead", diff) 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(err, check.IsNil)
c.Assert(len(out), check.Equals, 0) 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{ conn, err := services.NewTunnelConnection("conn1", services.TunnelConnectionSpecV2{
ClusterName: clusterName, ClusterName: clusterName,
ProxyName: "p1", ProxyName: "p1",

View file

@ -46,7 +46,7 @@ import (
) )
type Suite struct { type Suite struct {
clock clockwork.Clock clock clockwork.FakeClock
dataDir string dataDir string
authServer *auth.TestAuthServer authServer *auth.TestAuthServer
tlsServer *auth.TestTLSServer tlsServer *auth.TestTLSServer
@ -74,7 +74,7 @@ func TestApp(t *testing.T) { check.TestingT(t) }
func (s *Suite) SetUpSuite(c *check.C) { func (s *Suite) SetUpSuite(c *check.C) {
utils.InitLoggerForTests(testing.Verbose()) utils.InitLoggerForTests(testing.Verbose())
s.clock = clockwork.NewFakeClockAt(time.Now()) s.clock = clockwork.NewFakeClock()
s.dataDir = c.MkDir() s.dataDir = c.MkDir()
var err error var err error
@ -82,6 +82,7 @@ func (s *Suite) SetUpSuite(c *check.C) {
s.authServer, err = auth.NewTestAuthServer(auth.TestAuthServerConfig{ s.authServer, err = auth.NewTestAuthServer(auth.TestAuthServerConfig{
ClusterName: "root.example.com", ClusterName: "root.example.com",
Dir: s.dataDir, Dir: s.dataDir,
Clock: s.clock,
}) })
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
s.tlsServer, err = s.authServer.NewTestTLSServer() 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 // 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. // will be checked in the client later to ensure a connection was made.
s.message = uuid.New() 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) fmt.Fprintln(w, s.message)
s.closeFunc() 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. // Extract the hostport that the in-memory HTTP server is running on.
u, err := url.Parse(s.testhttp.URL) u, err := url.Parse(s.testhttp.URL)
@ -147,7 +151,7 @@ func (s *Suite) SetUpTest(c *check.C) {
Spec: services.ServerSpecV2{ Spec: services.ServerSpecV2{
Version: teleport.Version, Version: teleport.Version,
Apps: []*services.App{ Apps: []*services.App{
&services.App{ {
Name: "foo", Name: "foo",
URI: s.testhttp.URL, URI: s.testhttp.URL,
PublicAddr: "foo.example.com", PublicAddr: "foo.example.com",
@ -166,6 +170,7 @@ func (s *Suite) SetUpTest(c *check.C) {
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
tlsConfig, err := serverIdentity.TLSConfig(nil) tlsConfig, err := serverIdentity.TLSConfig(nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
tlsConfig.Time = s.clock.Now
// Generate certificate for user. // Generate certificate for user.
privateKey, publicKey, err := s.tlsServer.Auth().GenerateKeyPair("") privateKey, publicKey, err := s.tlsServer.Auth().GenerateKeyPair("")
@ -286,6 +291,8 @@ func (s *Suite) TestHandleConnection(c *check.C) {
RootCAs: s.hostCertPool, RootCAs: s.hostCertPool,
// Certificates is the user's application specific certificate. // Certificates is the user's application specific certificate.
Certificates: []tls.Certificate{s.clientCertificate}, 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/services"
"github.com/gravitational/teleport/lib/sshutils" "github.com/gravitational/teleport/lib/sshutils"
"github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/trace" "github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@ -54,6 +56,11 @@ type AuthHandlers struct {
// FIPS mode means Teleport started in a FedRAMP/FIPS 140-2 compliant // FIPS mode means Teleport started in a FedRAMP/FIPS 140-2 compliant
// configuration. // configuration.
FIPS bool 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 // 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 // Check that the user certificate uses supported public key algorithms, was
// issued by Teleport, and check the certificate metadata (principals, // issued by Teleport, and check the certificate metadata (principals,
// timestamp, etc). Fallback to keys is not supported. // timestamp, etc). Fallback to keys is not supported.
clock := time.Now
if h.Clock != nil {
clock = h.Clock.Now
}
certChecker := utils.CertChecker{ certChecker := utils.CertChecker{
CertChecker: ssh.CertChecker{ CertChecker: ssh.CertChecker{
IsUserAuthority: h.IsUserAuthority, IsUserAuthority: h.IsUserAuthority,
Clock: clock,
}, },
FIPS: h.FIPS, FIPS: h.FIPS,
} }

View file

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

View file

@ -297,6 +297,15 @@ func (s *Server) HandleConnection(conn net.Conn) {
// RotationGetter returns rotation state // RotationGetter returns rotation state
type RotationGetter func(role teleport.Role) (*services.Rotation, error) 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 // SetRotationGetter sets rotation state getter
func SetRotationGetter(getter RotationGetter) ServerOption { func SetRotationGetter(getter RotationGetter) ServerOption {
return func(s *Server) error { return func(s *Server) error {
@ -542,6 +551,7 @@ func New(addr utils.NetAddr,
AccessPoint: s.authService, AccessPoint: s.authService,
FIPS: s.fips, FIPS: s.fips,
Emitter: s.StreamEmitter, Emitter: s.StreamEmitter,
Clock: s.clock,
} }
// common term handlers // common term handlers

View file

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

View file

@ -30,6 +30,7 @@ import (
"github.com/gravitational/teleport" "github.com/gravitational/teleport"
"github.com/gravitational/trace" "github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
) )
// ClusterName returns cluster name from organization // 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 // 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) { func GenerateSelfSignedCAWithPrivateKey(priv *rsa.PrivateKey, entity pkix.Name, dnsNames []string, ttl time.Duration) ([]byte, []byte, error) {
notBefore := time.Now() return GenerateSelfSignedCAWithConfig(GenerateCAConfig{
notAfter := notBefore.Add(ttl) 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) serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 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 // this is important, otherwise go will accept certificate authorities
// signed by the same private key and having the same subject (happens in tests) // 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{ template := x509.Certificate{
SerialNumber: serialNumber, SerialNumber: serialNumber,
Issuer: entity, Issuer: config.Entity,
Subject: entity, Subject: config.Entity,
NotBefore: notBefore, NotBefore: notBefore,
NotAfter: notAfter, NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true, BasicConstraintsValid: true,
IsCA: 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 { if err != nil {
return nil, nil, trace.Wrap(err) return nil, nil, trace.Wrap(err)
} }
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) keyPEM = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(config.PrivateKey)})
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) certPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
return keyPEM, certPEM, nil 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 // Config represents web handler configuration parameters
type Config struct { type Config struct {
// Proxy is a reverse tunnel proxy that handles connections // 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 // NewHandler returns a new instance of web proxy handler
func NewHandler(cfg Config, opts ...HandlerOption) (*RewritingHandler, error) { func NewHandler(cfg Config, opts ...HandlerOption) (*RewritingHandler, error) {
const apiPrefix = "/" + teleport.WebAPIVersion 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{ h := &Handler{
cfg: cfg, cfg: cfg,
auth: lauth, log: newPackageLogger(),
log: newPackageLogger(), clock: clockwork.NewRealClock(),
} }
for _, o := range opts { 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()) _, sshPort, err := net.SplitHostPort(cfg.ProxySSHAddr.String())
if err != nil { if err != nil {
h.log.WithError(err).Warnf("Invalid SSH proxy address %q, will use default port %v.", 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 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 // ping endpoint is used to check if the server is up. the /webapi/ping
// endpoint returns the default authentication method and configuration that // endpoint returns the default authentication method and configuration that
// the server supports. the /webapi/ping/:connector endpoint can be used to // the server supports. the /webapi/ping/:connector endpoint can be used to
@ -1190,7 +1200,7 @@ func NewSessionResponse(ctx *SessionContext) (*CreateSessionResponse, error) {
return &CreateSessionResponse{ return &CreateSessionResponse{
Type: roundtrip.AuthBearer, Type: roundtrip.AuthBearer,
Token: webSession.GetBearerToken(), Token: webSession.GetBearerToken(),
ExpiresIn: int(time.Until(webSession.GetBearerTokenExpiryTime()) / time.Second), ExpiresIn: int(webSession.GetBearerTokenExpiryTime().Sub(ctx.parent.clock.Now()) / time.Second),
}, nil }, nil
} }

View file

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

View file

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

View file

@ -34,33 +34,39 @@ func (ft *fakeTicker) Stop() {
ft.stop <- true ft.stop <- true
} }
// tick sends the tick time to the ticker channel after every period. // runTickThread initializes a background goroutine to send the tick time to the ticker channel
// Tick events are discarded if the underlying ticker channel does // after every period. Tick events are discarded if the underlying ticker channel does not have
// not have enough capacity. // enough capacity.
func (ft *fakeTicker) tick() { func (ft *fakeTicker) runTickThread() {
tick := ft.clock.Now() nextTick := ft.clock.Now().Add(ft.period)
for { next := ft.clock.After(ft.period)
tick = tick.Add(ft.period) go func() {
remaining := tick.Sub(ft.clock.Now()) for {
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.
select { select {
case ft.c <- tick: case <-ft.stop:
default: return
} case <-next:
continue // 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.
select { now := ft.clock.Now()
case <-ft.stop: // First, figure out how many periods there have been between "now" and the time we were
return // supposed to have trigged, then advance over all of those.
case <-ft.clock.After(remaining): skipTicks := (now.Sub(tick) + ft.period - 1) / ft.period
select { nextTick = nextTick.Add(skipTicks * ft.period)
case ft.c <- tick: // Now, keep advancing until we are past now. This should happen at most once.
default: 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/backend/s3mem
github.com/johannesboyne/gofakes3/internal/goskipiter github.com/johannesboyne/gofakes3/internal/goskipiter
github.com/johannesboyne/gofakes3/internal/s3io github.com/johannesboyne/gofakes3/internal/s3io
# github.com/jonboulle/clockwork v0.2.1 # github.com/jonboulle/clockwork v0.2.2
## explicit ## explicit
github.com/jonboulle/clockwork github.com/jonboulle/clockwork
# github.com/json-iterator/go v1.1.10 # github.com/json-iterator/go v1.1.10