mirror of
https://github.com/gravitational/teleport
synced 2024-10-21 01:34:01 +00:00
Certificate TTL improvements
1. Server now always uses UTC timestamps for certificates it ussues 2. Client doesn't store cert validBefore time in separate files, it parses the cert itself. Fixes #370
This commit is contained in:
parent
b349ea9340
commit
e28f21922c
|
@ -230,10 +230,9 @@ func (this *TeleInstance) Create(trustedSecrets []*InstanceSecrets, enableSSH bo
|
|||
return err
|
||||
}
|
||||
user.Key = &client.Key{
|
||||
Priv: priv,
|
||||
Pub: pub,
|
||||
Cert: cert,
|
||||
Deadline: time.Now().Add(ttl),
|
||||
Priv: priv,
|
||||
Pub: pub,
|
||||
Cert: cert,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -143,7 +143,7 @@ func (n *nauth) GenerateHostCert(privateSigningKey, publicKey []byte, hostname,
|
|||
}
|
||||
validBefore := uint64(ssh.CertTimeInfinity)
|
||||
if ttl != 0 {
|
||||
b := time.Now().Add(ttl)
|
||||
b := time.Now().UTC().Add(ttl)
|
||||
validBefore = uint64(b.UnixNano())
|
||||
}
|
||||
cert := &ssh.Certificate{
|
||||
|
@ -179,7 +179,7 @@ func (n *nauth) GenerateUserCert(pkey, key []byte, teleportUsername string, allo
|
|||
}
|
||||
validBefore := uint64(ssh.CertTimeInfinity)
|
||||
if ttl != 0 {
|
||||
b := time.Now().Add(ttl)
|
||||
b := time.Now().UTC().Add(ttl)
|
||||
validBefore = uint64(b.Unix())
|
||||
}
|
||||
// we do not use any extensions in users certs because of this:
|
||||
|
|
|
@ -78,7 +78,7 @@ func (n *Keygen) GenerateHostCert(pkey, key []byte, hostname, authDomain string,
|
|||
}
|
||||
validBefore := uint64(ssh.CertTimeInfinity)
|
||||
if ttl != 0 {
|
||||
b := time.Now().Add(ttl)
|
||||
b := time.Now().UTC().Add(ttl)
|
||||
validBefore = uint64(b.UnixNano())
|
||||
}
|
||||
cert := &ssh.Certificate{
|
||||
|
@ -107,7 +107,7 @@ func (n *Keygen) GenerateUserCert(pkey, key []byte, teleportUsername string, all
|
|||
}
|
||||
validBefore := uint64(ssh.CertTimeInfinity)
|
||||
if ttl != 0 {
|
||||
b := time.Now().Add(ttl)
|
||||
b := time.Now().UTC().Add(ttl)
|
||||
validBefore = uint64(b.UnixNano())
|
||||
}
|
||||
cert := &ssh.Certificate{
|
||||
|
|
|
@ -781,9 +781,7 @@ func (tc *TeleportClient) AddTrustedCA(ca *services.CertAuthority) error {
|
|||
// MakeKey generates a new unsigned key. It's useless by itself until a
|
||||
// trusted CA signs it
|
||||
func (tc *TeleportClient) MakeKey() (key *Key, err error) {
|
||||
key = &Key{
|
||||
Deadline: time.Now().Add(tc.KeyTTL),
|
||||
}
|
||||
key = &Key{}
|
||||
keygen := native.New()
|
||||
defer keygen.Close()
|
||||
key.Priv, key.Pub, err = keygen.GenerateKeyPair("")
|
||||
|
|
|
@ -8,15 +8,11 @@ import (
|
|||
"golang.org/x/crypto/ssh/agent"
|
||||
)
|
||||
|
||||
// Key describes a stored client key
|
||||
// Key describes a complete (signed) client key
|
||||
type Key struct {
|
||||
Priv []byte `json:"Priv,omitempty"`
|
||||
Pub []byte `json:"Pub,omitempty"`
|
||||
Cert []byte `json:"Cert,omitempty"`
|
||||
|
||||
// Deadline AKA TTL is the time when this key is safe to be discarded
|
||||
// for garbage collection purposes
|
||||
Deadline time.Time `json:"Deadline,omitempty"`
|
||||
}
|
||||
|
||||
// LocalKeyStore interface allows for different storage back-ends for TSH to load/save its keys
|
||||
|
@ -50,3 +46,13 @@ func (k *Key) AsAgentKey() (*agent.AddedKey, error) {
|
|||
ConfirmBeforeUse: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CertValidBefore returns UTC time of the cert expiration
|
||||
func (k *Key) CertValidBefore() (time.Time, error) {
|
||||
pcert, _, _, _, err := ssh.ParseAuthorizedKey(k.Cert)
|
||||
if err != nil {
|
||||
return time.Now().UTC(), trace.Wrap(err)
|
||||
}
|
||||
cert := pcert.(*ssh.Certificate)
|
||||
return time.Unix(0, int64(cert.ValidBefore)).UTC(), nil
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@ const (
|
|||
fileNameCert = "cert"
|
||||
fileNameKey = "key"
|
||||
fileNamePub = "pub"
|
||||
fileNameTTL = ".ttl"
|
||||
fileNameKnownHosts = "known_hosts"
|
||||
)
|
||||
|
||||
|
@ -129,10 +128,6 @@ func (fs *FSLocalKeyStore) AddKey(host string, key *Key) error {
|
|||
if err = writeBytes(fileNameKey, key.Priv); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
ttl, _ := key.Deadline.UTC().MarshalJSON()
|
||||
if err = writeBytes(fileNameTTL, ttl); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
log.Infof("keystore.AddKey(%s)", host)
|
||||
return nil
|
||||
}
|
||||
|
@ -144,23 +139,8 @@ func (fs *FSLocalKeyStore) GetKey(host string) (*Key, error) {
|
|||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
ttl, err := ioutil.ReadFile(filepath.Join(dirPath, fileNameTTL))
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
var deadline time.Time
|
||||
if err = deadline.UnmarshalJSON(ttl); err != nil {
|
||||
log.Error(err)
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
// this session key is expired
|
||||
if deadline.Before(time.Now().UTC()) {
|
||||
os.RemoveAll(dirPath)
|
||||
log.Infof("TTL expired for session key %v", dirPath)
|
||||
return nil, trace.NotFound("session keys for %s are not found", host)
|
||||
}
|
||||
cert, err := ioutil.ReadFile(filepath.Join(dirPath, fileNameCert))
|
||||
certFile := filepath.Join(dirPath, fileNameCert)
|
||||
cert, err := ioutil.ReadFile(certFile)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil, trace.Wrap(err)
|
||||
|
@ -175,13 +155,21 @@ func (fs *FSLocalKeyStore) GetKey(host string) (*Key, error) {
|
|||
log.Error(err)
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
log.Infof("keystore.Get(%v)", host)
|
||||
return &Key{
|
||||
Pub: pub,
|
||||
Priv: priv,
|
||||
Cert: cert,
|
||||
Deadline: deadline,
|
||||
}, nil
|
||||
|
||||
key := &Key{Pub: pub, Priv: priv, Cert: cert}
|
||||
|
||||
// expired certificate? this key won't be accepted anymore, lets delete it:
|
||||
certExpiration, err := key.CertValidBefore()
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
log.Infof("returning cert %v valid until %v", certFile, certExpiration)
|
||||
if certExpiration.Before(time.Now().UTC()) {
|
||||
os.RemoveAll(dirPath)
|
||||
log.Infof("TTL expired for session key %v", dirPath)
|
||||
return nil, trace.NotFound("session keys for %s are not found", host)
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// AddKnownHost adds a new entry to 'known_CAs' file
|
||||
|
|
|
@ -60,7 +60,7 @@ func (s *KeyStoreTestSuite) TestListKeys(c *check.C) {
|
|||
// add 5 keys:
|
||||
keys := make([]Key, keyNum)
|
||||
for i := 0; i < keyNum; i++ {
|
||||
key := s.makeSignedKey(c)
|
||||
key := s.makeSignedKey(c, false)
|
||||
s.store.AddKey(fmt.Sprintf("host-%v", i), key)
|
||||
keys[i] = *key
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ func (s *KeyStoreTestSuite) TestListKeys(c *check.C) {
|
|||
}
|
||||
|
||||
func (s *KeyStoreTestSuite) TestKeySaveLoad(c *check.C) {
|
||||
key := s.makeSignedKey(c)
|
||||
key := s.makeSignedKey(c, false)
|
||||
|
||||
// add key:
|
||||
err := s.store.AddKey("host.a", key)
|
||||
|
@ -85,9 +85,8 @@ func (s *KeyStoreTestSuite) TestKeySaveLoad(c *check.C) {
|
|||
|
||||
func (s *KeyStoreTestSuite) TestKeyExpiration(c *check.C) {
|
||||
// make two keys: one is current, and the expire one
|
||||
good := s.makeSignedKey(c)
|
||||
expired := s.makeSignedKey(c)
|
||||
expired.Deadline = time.Now().Add(-time.Hour)
|
||||
good := s.makeSignedKey(c, false)
|
||||
expired := s.makeSignedKey(c, true)
|
||||
|
||||
s.store.AddKey("good.host", good)
|
||||
s.store.AddKey("expired.host", expired)
|
||||
|
@ -120,7 +119,7 @@ func (s *KeyStoreTestSuite) TestKnownHosts(c *check.C) {
|
|||
}
|
||||
|
||||
// makeSIgnedKey helper returns all 3 components of a user key (signed by CAPriv key)
|
||||
func (s *KeyStoreTestSuite) makeSignedKey(c *check.C) *Key {
|
||||
func (s *KeyStoreTestSuite) makeSignedKey(c *check.C, makeExpired bool) *Key {
|
||||
var (
|
||||
err error
|
||||
priv, pub, cert []byte
|
||||
|
@ -129,13 +128,15 @@ func (s *KeyStoreTestSuite) makeSignedKey(c *check.C) *Key {
|
|||
username := "vincento"
|
||||
allowedLogins := []string{username, "root"}
|
||||
ttl := time.Duration(time.Minute * 20)
|
||||
if makeExpired {
|
||||
ttl = -ttl
|
||||
}
|
||||
cert, err = s.keygen.GenerateUserCert(CAPriv, pub, username, allowedLogins, ttl)
|
||||
c.Assert(err, check.IsNil)
|
||||
return &Key{
|
||||
Priv: priv,
|
||||
Pub: pub,
|
||||
Cert: cert,
|
||||
Deadline: time.Now().UTC().Add(ttl),
|
||||
Priv: priv,
|
||||
Pub: pub,
|
||||
Cert: cert,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package utils
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Clock interface {
|
||||
Now() time.Time
|
||||
}
|
||||
|
||||
type TestClock struct {
|
||||
N time.Time
|
||||
}
|
||||
|
||||
func (t *TestClock) Now() time.Time {
|
||||
return t.N
|
||||
}
|
||||
|
||||
func (t *TestClock) Advance(d time.Duration) {
|
||||
t.N = t.N.Add(d)
|
||||
}
|
||||
|
||||
type WallClock struct {
|
||||
}
|
||||
|
||||
func (*WallClock) Now() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
var RealTime = &WallClock{}
|
Loading…
Reference in a new issue