Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions lib/tbot/identity/identity_facade.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (
"github.com/gravitational/teleport/api/client"
apidefaults "github.com/gravitational/teleport/api/defaults"
apiutils "github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/api/utils/sshutils"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/utils"
Expand Down Expand Up @@ -212,7 +211,7 @@ func (f *Facade) Expiry() (time.Time, bool) {
if len(f.identity.TLSCert.Certificate) == 0 {
return time.Time{}, false
}
cert, _, err := keys.X509Certificate(f.identity.TLSCert.Certificate[0])
cert, err := x509.ParseCertificate(f.identity.TLSCert.Certificate[0])
if err != nil {
return time.Time{}, false
}
Expand Down
49 changes: 48 additions & 1 deletion lib/tbot/service_bot_identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,24 @@ func (s *identityService) unblockWaiters() {
s.initializedOnce.Do(func() { close(s.initialized) })
}

// renewIdentity attempts to renew an existing bot identity. "Renewal" in this
// case means one of two things:
//
// 1. If using an explicitly renewable identity (i.e. `token` joining),
// certificates will be renewed directly via Auth using the formal renewal
// process.
//
// If the existing identity is expired, this will fail and cannot be
// recovered.
//
// 2. For all other join methods, a "lightweight renewal" is performed. The
// existing client is used to authenticate the request and prove ownership
// of the existing bot instance ID, but otherwise the delegated joining
// ceremony is performed as usual.
//
// If the existing identity appears to be expired (`time.Now()` >
// `NotAfter`), the existing auth client will be discarded and the bot will
// try to join without it. This will result in a new bot instance ID.
func renewIdentity(
ctx context.Context,
log *slog.Logger,
Expand Down Expand Up @@ -452,9 +470,38 @@ func renewIdentity(
return newIdentity, nil
}

// Note: This simple expiration check is probably not the best possible
// solution to determine when to discard an existing identity: the client
// could have severe clock drift, or there could be non-expiry related
// reasons that an identity should be thrown out. We may improve this
// discard logic in the future if we determine we're still creating excess
// bot instances.
now := time.Now()
if expiry, ok := facade.Expiry(); !ok || now.After(expiry) {
slog.WarnContext(
ctx,
"The bot identity appears to be expired and will not be used to "+
"authenticate the identity renewal. If it is possible to "+
"rejoin, a new bot instance will be created. If this occurs "+
"repeatedly, ensure the local machine's clock is properly "+
"synchronized, the certificate TTL is adjusted to your "+
"environment, and that no external issues will prevent "+
"the bot from renewing its identity on schedule.",
"now", now,
"expiry", expiry,
"credential_lifetime", botCfg.CredentialLifetime,
)

newIdentity, err := botIdentityFromToken(ctx, log, botCfg, nil)
if err != nil {
return nil, trace.Wrap(err, "renewing identity using Register without existing auth client")
}
return newIdentity, nil
}

newIdentity, err := botIdentityFromToken(ctx, log, botCfg, authClient)
if err != nil {
return nil, trace.Wrap(err, "renewing identity using Register")
return nil, trace.Wrap(err, "renewing identity using Register with existing auth client")
}
return newIdentity, nil
}
Expand Down
Loading