mirror of
https://github.com/gravitational/teleport
synced 2024-10-19 00:33:50 +00:00
Reduce login latency (#28499)
Reuse the root cluster auth client during the login process to reduce latency. Closes #26712. Partially addresses #26712.
This commit is contained in:
parent
15d3564382
commit
119dc7a3a3
|
@ -47,3 +47,9 @@ func WithPropagationContext(ctx context.Context, pc PropagationContext, opts ...
|
|||
func DefaultProvider() oteltrace.TracerProvider {
|
||||
return otel.GetTracerProvider()
|
||||
}
|
||||
|
||||
// NewTracer creates a new [oteltrace.Tracer] from the global default
|
||||
// [oteltrace.TracerProvider] with the provided name.
|
||||
func NewTracer(name string) oteltrace.Tracer {
|
||||
return DefaultProvider().Tracer(name)
|
||||
}
|
||||
|
|
|
@ -2392,7 +2392,7 @@ func twoClustersTunnel(t *testing.T, suite *integrationTestSuite, now time.Time,
|
|||
require.Equal(t, "hello world\n", outputA.String())
|
||||
|
||||
// Update trusted CAs.
|
||||
err = tc.UpdateTrustedCA(ctx, a.Secrets.SiteName)
|
||||
err = tc.UpdateTrustedCA(ctx, a.GetSiteAPI(a.Secrets.SiteName))
|
||||
require.NoError(t, err)
|
||||
|
||||
// The known_hosts file should have two certificates, the way bytes.Split
|
||||
|
|
|
@ -21,8 +21,10 @@ import (
|
|||
|
||||
"github.com/gravitational/trace"
|
||||
log "github.com/sirupsen/logrus"
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/gravitational/teleport/api/client/proto"
|
||||
"github.com/gravitational/teleport/api/observability/tracing"
|
||||
"github.com/gravitational/teleport/lib/auth/touchid"
|
||||
wanlib "github.com/gravitational/teleport/lib/auth/webauthn"
|
||||
"github.com/gravitational/teleport/lib/auth/webauthnwin"
|
||||
|
@ -119,6 +121,13 @@ func Login(
|
|||
ctx context.Context,
|
||||
origin string, assertion *wanlib.CredentialAssertion, prompt LoginPrompt, opts *LoginOpts,
|
||||
) (*proto.MFAAuthenticateResponse, string, error) {
|
||||
ctx, span := tracing.NewTracer("mfa").Start(
|
||||
ctx,
|
||||
"webauthncli/Login",
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
// origin vs RPID sanity check.
|
||||
// Doesn't necessarily means a failure, but it's likely to be one.
|
||||
switch {
|
||||
|
|
|
@ -579,12 +579,18 @@ func RetryWithRelogin(ctx context.Context, tc *TeleportClient, fn func() error)
|
|||
}
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
if err := tc.ActivateKey(ctx, key); err != nil {
|
||||
|
||||
proxyClient, rootAuthClient, err := tc.ConnectToRootCluster(ctx, key)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
defer func() {
|
||||
rootAuthClient.Close()
|
||||
proxyClient.Close()
|
||||
}()
|
||||
|
||||
// Attempt device login. This activates a fresh key if successful.
|
||||
if err := tc.AttemptDeviceLogin(ctx, key); err != nil {
|
||||
if err := tc.AttemptDeviceLogin(ctx, key, rootAuthClient); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
|
@ -3243,8 +3249,9 @@ func (tc *TeleportClient) GetWebConfig(ctx context.Context) (*webclient.WebConfi
|
|||
|
||||
// Login logs the user into a Teleport cluster by talking to a Teleport proxy.
|
||||
//
|
||||
// The returned Key should typically be passed to ActivateKey in order to
|
||||
// update local agent state.
|
||||
// The returned Key should typically be passed to ConnectToRootCluster in order to
|
||||
//
|
||||
// update the local agent state and create an initial connection to the cluster.
|
||||
//
|
||||
// If the initial login fails due to a private key policy not being met, Login
|
||||
// will automatically retry login with a private key that meets the required policy.
|
||||
|
@ -3331,14 +3338,20 @@ func (tc *TeleportClient) LoginWeb(ctx context.Context) (*WebClient, types.WebSe
|
|||
// [TeleportClient.Login], and augments the certificates within the key with
|
||||
// device extensions.
|
||||
//
|
||||
// If successful, the new device certificates are automatically activated (using
|
||||
// [TeleportClient.ActivateKey].)
|
||||
// If successful, the new device certificates are automatically activated.
|
||||
//
|
||||
// A nil response from this method doesn't mean that device authentication was
|
||||
// successful, as skipping the ceremony is valid for various reasons (Teleport
|
||||
// cluster doesn't support device authn, device wasn't enrolled, etc).
|
||||
// Use [TeleportClient.DeviceLogin] if you want more control over process.
|
||||
func (tc *TeleportClient) AttemptDeviceLogin(ctx context.Context, key *Key) error {
|
||||
func (tc *TeleportClient) AttemptDeviceLogin(ctx context.Context, key *Key, rootAuthClient auth.ClientI) error {
|
||||
ctx, span := tc.Tracer.Start(
|
||||
ctx,
|
||||
"teleportClient/AttemptDeviceLogin",
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
pingResp, err := tc.Ping(ctx)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
|
@ -3349,11 +3362,14 @@ func (tc *TeleportClient) AttemptDeviceLogin(ctx context.Context, key *Key) erro
|
|||
return nil
|
||||
}
|
||||
|
||||
newCerts, err := tc.DeviceLogin(ctx, &devicepb.UserCertificates{
|
||||
// Augment the SSH certificate.
|
||||
// The TLS certificate is already part of the connection.
|
||||
SshAuthorizedKey: key.Cert,
|
||||
})
|
||||
newCerts, err := tc.DeviceLogin(
|
||||
ctx,
|
||||
rootAuthClient,
|
||||
&devicepb.UserCertificates{
|
||||
// Augment the SSH certificate.
|
||||
// The TLS certificate is already part of the connection.
|
||||
SshAuthorizedKey: key.Cert,
|
||||
})
|
||||
switch {
|
||||
case errors.Is(err, devicetrust.ErrDeviceKeyNotFound):
|
||||
log.Debug("Device Trust: Skipping device authentication, device key not found")
|
||||
|
@ -3376,7 +3392,16 @@ func (tc *TeleportClient) AttemptDeviceLogin(ctx context.Context, key *Key) erro
|
|||
Type: "CERTIFICATE",
|
||||
Bytes: newCerts.X509Der,
|
||||
})
|
||||
return trace.Wrap(tc.ActivateKey(ctx, &cp))
|
||||
|
||||
// Get the list of host certificates that this cluster knows about.
|
||||
hostCerts, err := rootAuthClient.GetCertAuthorities(ctx, types.HostCA, false)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
trustedCerts := auth.AuthoritiesToTrustedCerts(hostCerts)
|
||||
|
||||
// Update the CA pool and known hosts for all CAs the cluster knows about.
|
||||
return trace.Wrap(tc.localAgent.SaveTrustedCerts(trustedCerts))
|
||||
}
|
||||
|
||||
// DeviceLogin attempts to authenticate the current device with Teleport.
|
||||
|
@ -3392,18 +3417,13 @@ func (tc *TeleportClient) AttemptDeviceLogin(ctx context.Context, key *Key) erro
|
|||
// `tsh login`).
|
||||
//
|
||||
// Device Trust is a Teleport Enterprise feature.
|
||||
func (tc *TeleportClient) DeviceLogin(ctx context.Context, certs *devicepb.UserCertificates) (*devicepb.UserCertificates, error) {
|
||||
proxyClient, err := tc.ConnectToProxy(ctx)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
defer proxyClient.Close()
|
||||
|
||||
authClient, err := proxyClient.ConnectToRootCluster(ctx)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
defer authClient.Close()
|
||||
func (tc *TeleportClient) DeviceLogin(ctx context.Context, rootAuthClient auth.ClientI, certs *devicepb.UserCertificates) (*devicepb.UserCertificates, error) {
|
||||
ctx, span := tc.Tracer.Start(
|
||||
ctx,
|
||||
"teleportClient/DeviceLogin",
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
// Allow tests to override the default authn function.
|
||||
runCeremony := tc.DTAuthnRunCeremony
|
||||
|
@ -3412,7 +3432,7 @@ func (tc *TeleportClient) DeviceLogin(ctx context.Context, certs *devicepb.UserC
|
|||
}
|
||||
|
||||
// Login without a previous auto-enroll attempt.
|
||||
devicesClient := authClient.DevicesClient()
|
||||
devicesClient := rootAuthClient.DevicesClient()
|
||||
newCerts, loginErr := runCeremony(ctx, devicesClient, certs)
|
||||
// Success or auto-enroll impossible.
|
||||
if loginErr == nil || errors.Is(loginErr, devicetrust.ErrPlatformNotSupported) || trace.IsNotImplemented(loginErr) {
|
||||
|
@ -3669,6 +3689,13 @@ type SSHLoginFunc func(context.Context, *keys.PrivateKey) (*auth.SSHLoginRespons
|
|||
// SSHLogin uses the given login function to login the client. This function handles
|
||||
// private key logic and parsing the resulting auth response.
|
||||
func (tc *TeleportClient) SSHLogin(ctx context.Context, sshLoginFunc SSHLoginFunc) (*Key, error) {
|
||||
ctx, span := tc.Tracer.Start(
|
||||
ctx,
|
||||
"teleportClient/SSHLogin",
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
priv, err := tc.GetNewLoginKey(ctx)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
|
@ -3762,6 +3789,13 @@ func (tc *TeleportClient) webLogin(ctx context.Context, webLoginFunc WebLoginFun
|
|||
|
||||
// GetNewLoginKey gets a new private key for login.
|
||||
func (tc *TeleportClient) GetNewLoginKey(ctx context.Context) (priv *keys.PrivateKey, err error) {
|
||||
_, span := tc.Tracer.Start(
|
||||
ctx,
|
||||
"teleportClient/GetNewLoginKey",
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
switch tc.PrivateKeyPolicy {
|
||||
case keys.PrivateKeyPolicyHardwareKey:
|
||||
log.Debugf("Attempting to login with YubiKey private key.")
|
||||
|
@ -3802,6 +3836,13 @@ func (tc *TeleportClient) newSSHLogin(priv *keys.PrivateKey) (SSHLogin, error) {
|
|||
}
|
||||
|
||||
func (tc *TeleportClient) pwdlessLogin(ctx context.Context, priv *keys.PrivateKey) (*auth.SSHLoginResponse, error) {
|
||||
ctx, span := tc.Tracer.Start(
|
||||
ctx,
|
||||
"teleportClient/pwdlessLogin",
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
// Only pass on the user if explicitly set, otherwise let the credential
|
||||
// picker kick in.
|
||||
user := ""
|
||||
|
@ -3853,6 +3894,13 @@ func (tc *TeleportClient) localLogin(ctx context.Context, priv *keys.PrivateKey,
|
|||
|
||||
// directLogin asks for a password + OTP token, makes a request to CA via proxy
|
||||
func (tc *TeleportClient) directLogin(ctx context.Context, secondFactorType constants.SecondFactorType, priv *keys.PrivateKey) (*auth.SSHLoginResponse, error) {
|
||||
ctx, span := tc.Tracer.Start(
|
||||
ctx,
|
||||
"teleportClient/directLogin",
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
password, err := tc.AskPassword(ctx)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
|
@ -3885,6 +3933,13 @@ func (tc *TeleportClient) directLogin(ctx context.Context, secondFactorType cons
|
|||
|
||||
// mfaLocalLogin asks for a password and performs the challenge-response authentication
|
||||
func (tc *TeleportClient) mfaLocalLogin(ctx context.Context, priv *keys.PrivateKey) (*auth.SSHLoginResponse, error) {
|
||||
ctx, span := tc.Tracer.Start(
|
||||
ctx,
|
||||
"teleportClient/mfaLocalLogin",
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
password, err := tc.AskPassword(ctx)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
|
@ -3968,12 +4023,43 @@ func (tc *TeleportClient) ssoLogin(ctx context.Context, priv *keys.PrivateKey, c
|
|||
return response, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// ActivateKey saves the target session cert into the local
|
||||
// keystore (and into the ssh-agent) for future use.
|
||||
func (tc *TeleportClient) ActivateKey(ctx context.Context, key *Key) error {
|
||||
// ConnectToRootCluster activates the provided key and connects to the
|
||||
// root cluster with its credentials.
|
||||
func (tc *TeleportClient) ConnectToRootCluster(ctx context.Context, key *Key) (*ProxyClient, auth.ClientI, error) {
|
||||
ctx, span := tc.Tracer.Start(
|
||||
ctx,
|
||||
"teleportClient/ActivateKey",
|
||||
"teleportClient/ConnectToRootCluster",
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
if err := tc.activateKey(ctx, key); err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
proxyClient, err := tc.ConnectToProxy(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
rootAuthClient, err := proxyClient.ConnectToRootCluster(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, trace.NewAggregate(err, proxyClient.Close())
|
||||
}
|
||||
|
||||
if err := tc.UpdateTrustedCA(ctx, rootAuthClient); err != nil {
|
||||
return nil, nil, trace.NewAggregate(err, rootAuthClient.Close(), proxyClient.Close())
|
||||
}
|
||||
|
||||
return proxyClient, rootAuthClient, nil
|
||||
}
|
||||
|
||||
// activateKey saves the target session cert into the local
|
||||
// keystore (and into the ssh-agent) for future use.
|
||||
func (tc *TeleportClient) activateKey(ctx context.Context, key *Key) error {
|
||||
_, span := tc.Tracer.Start(
|
||||
ctx,
|
||||
"teleportClient/activateKey",
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
)
|
||||
defer span.End()
|
||||
|
@ -3988,23 +4074,6 @@ func (tc *TeleportClient) ActivateKey(ctx context.Context, key *Key) error {
|
|||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// Connect to the Auth Server of the root cluster and fetch the known hosts.
|
||||
rootClusterName := key.TrustedCerts[0].ClusterName
|
||||
if err := tc.UpdateTrustedCA(ctx, rootClusterName); err != nil {
|
||||
if len(tc.JumpHosts) == 0 {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
errViaJumphost := err
|
||||
// If JumpHosts was pointing at the leaf cluster (e.g. during 'tsh ssh
|
||||
// -J leaf.example.com'), this could've caused the above error. Try to
|
||||
// fetch CAs without JumpHosts to force it to use the root cluster.
|
||||
if err := tc.WithoutJumpHosts(func(tc *TeleportClient) error {
|
||||
return tc.UpdateTrustedCA(ctx, rootClusterName)
|
||||
}); err != nil {
|
||||
return trace.NewAggregate(errViaJumphost, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -4140,20 +4209,19 @@ func (tc *TeleportClient) GetTrustedCA(ctx context.Context, clusterName string)
|
|||
|
||||
// UpdateTrustedCA connects to the Auth Server and fetches all host certificates
|
||||
// and updates ~/.tsh/keys/proxy/certs.pem and ~/.tsh/known_hosts.
|
||||
func (tc *TeleportClient) UpdateTrustedCA(ctx context.Context, clusterName string) error {
|
||||
func (tc *TeleportClient) UpdateTrustedCA(ctx context.Context, getter services.AuthorityGetter) error {
|
||||
ctx, span := tc.Tracer.Start(
|
||||
ctx,
|
||||
"teleportClient/UpdateTrustedCA",
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
oteltrace.WithAttributes(attribute.String("cluster", clusterName)),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
if tc.localAgent == nil {
|
||||
return trace.BadParameter("TeleportClient.UpdateTrustedCA called on a client without localAgent")
|
||||
return trace.BadParameter("UpdateTrustedCA called on a client without localAgent")
|
||||
}
|
||||
// Get the list of host certificates that this cluster knows about.
|
||||
hostCerts, err := tc.GetTrustedCA(ctx, clusterName)
|
||||
hostCerts, err := getter.GetCertAuthorities(ctx, types.HostCA, false)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
@ -4457,6 +4525,13 @@ func Username() (string, error) {
|
|||
|
||||
// AskOTP prompts the user to enter the OTP token.
|
||||
func (tc *TeleportClient) AskOTP(ctx context.Context) (token string, err error) {
|
||||
ctx, span := tc.Tracer.Start(
|
||||
ctx,
|
||||
"teleportClient/AskOTP",
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
stdin := prompt.Stdin()
|
||||
if !stdin.IsTerminal() {
|
||||
return "", trace.Wrap(prompt.ErrNotTerminal, "cannot perform OTP login without a terminal")
|
||||
|
@ -4466,6 +4541,13 @@ func (tc *TeleportClient) AskOTP(ctx context.Context) (token string, err error)
|
|||
|
||||
// AskPassword prompts the user to enter the password
|
||||
func (tc *TeleportClient) AskPassword(ctx context.Context) (pwd string, err error) {
|
||||
ctx, span := tc.Tracer.Start(
|
||||
ctx,
|
||||
"teleportClient/AskPassword",
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
stdin := prompt.Stdin()
|
||||
if !stdin.IsTerminal() {
|
||||
return "", trace.Wrap(prompt.ErrNotTerminal, "cannot perform password login without a terminal")
|
||||
|
|
|
@ -443,9 +443,13 @@ func TestTeleportClient_DeviceLogin(t *testing.T) {
|
|||
// Login the current user and fetch a valid pair of certificates.
|
||||
key, err := teleportClient.Login(ctx)
|
||||
require.NoError(t, err, "Login failed")
|
||||
require.NoError(t,
|
||||
teleportClient.ActivateKey(ctx, key),
|
||||
"ActivateKey failed")
|
||||
|
||||
proxyClient, rootAuthClient, err := teleportClient.ConnectToRootCluster(ctx, key)
|
||||
require.NoError(t, err, "Connecting to the root cluster failed")
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, rootAuthClient.Close())
|
||||
require.NoError(t, proxyClient.Close())
|
||||
})
|
||||
|
||||
// Prepare "device aware" certificates from key.
|
||||
// In a real scenario these would be augmented certs.
|
||||
|
@ -487,16 +491,18 @@ func TestTeleportClient_DeviceLogin(t *testing.T) {
|
|||
require.NoError(t, authenticatedAction(), "Authenticated action failed *before* AttemptDeviceLogin")
|
||||
|
||||
// Test! Exercise DeviceLogin.
|
||||
got, err := teleportClient.DeviceLogin(ctx, &devicepb.UserCertificates{
|
||||
SshAuthorizedKey: key.Cert,
|
||||
})
|
||||
got, err := teleportClient.DeviceLogin(ctx,
|
||||
rootAuthClient,
|
||||
&devicepb.UserCertificates{
|
||||
SshAuthorizedKey: key.Cert,
|
||||
})
|
||||
require.NoError(t, err, "DeviceLogin failed")
|
||||
require.Equal(t, validCerts, got, "DeviceLogin mismatch")
|
||||
assert.Equal(t, 1, runCeremonyCalls, `DeviceLogin didn't call dtAuthnRunCeremony()`)
|
||||
|
||||
// Test! Exercise AttemptDeviceLogin.
|
||||
require.NoError(t,
|
||||
teleportClient.AttemptDeviceLogin(ctx, key),
|
||||
teleportClient.AttemptDeviceLogin(ctx, key, rootAuthClient),
|
||||
"AttemptDeviceLogin failed")
|
||||
assert.Equal(t, 2, runCeremonyCalls, `AttemptDeviceLogin didn't call dtAuthnRunCeremony()`)
|
||||
|
||||
|
@ -521,7 +527,7 @@ func TestTeleportClient_DeviceLogin(t *testing.T) {
|
|||
// Test!
|
||||
// AttemptDeviceLogin should obey Ping and not attempt the ceremony.
|
||||
require.NoError(t,
|
||||
teleportClient.AttemptDeviceLogin(ctx, key),
|
||||
teleportClient.AttemptDeviceLogin(ctx, key, rootAuthClient),
|
||||
"AttemptDeviceLogin failed")
|
||||
assert.False(t, runCeremonyCalled, "AttemptDeviceLogin called DeviceLogin/dtAuthnRunCeremony, despite the Ping response")
|
||||
})
|
||||
|
@ -550,10 +556,21 @@ func TestTeleportClient_DeviceLogin(t *testing.T) {
|
|||
}, nil
|
||||
})
|
||||
|
||||
proxyClient, err := teleportClient.ConnectToProxy(ctx)
|
||||
require.NoError(t, err)
|
||||
defer proxyClient.Close()
|
||||
|
||||
rootAuthClient, err := proxyClient.ConnectToRootCluster(ctx)
|
||||
require.NoError(t, err)
|
||||
defer rootAuthClient.Close()
|
||||
|
||||
// Test!
|
||||
got, err := teleportClient.DeviceLogin(ctx, &devicepb.UserCertificates{
|
||||
SshAuthorizedKey: key.Cert,
|
||||
})
|
||||
got, err := teleportClient.DeviceLogin(
|
||||
ctx,
|
||||
rootAuthClient,
|
||||
&devicepb.UserCertificates{
|
||||
SshAuthorizedKey: key.Cert,
|
||||
})
|
||||
require.NoError(t, err, "DeviceLogin failed")
|
||||
assert.Equal(t, got, validCerts, "DeviceLogin mismatch")
|
||||
assert.Equal(t, 2, runCeremonyCalls, "RunCeremony called an unexpected number of times")
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/gravitational/teleport/api/client/proto"
|
||||
"github.com/gravitational/teleport/api/observability/tracing"
|
||||
wanlib "github.com/gravitational/teleport/lib/auth/webauthn"
|
||||
wancli "github.com/gravitational/teleport/lib/auth/webauthncli"
|
||||
"github.com/gravitational/teleport/lib/utils/prompt"
|
||||
|
@ -117,6 +118,13 @@ func (tc *TeleportClient) PromptMFAChallenge(ctx context.Context, proxyAddr stri
|
|||
// PromptMFAChallenge prompts the user to complete MFA authentication
|
||||
// challenges.
|
||||
func PromptMFAChallenge(ctx context.Context, c *proto.MFAAuthenticateChallenge, proxyAddr string, opts *PromptMFAChallengeOpts) (*proto.MFAAuthenticateResponse, error) {
|
||||
ctx, span := tracing.NewTracer("mfa").Start(
|
||||
ctx,
|
||||
"PromptMFAChallenge",
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
// Is there a challenge present?
|
||||
if c.TOTP == nil && c.WebauthnChallenge == nil {
|
||||
return &proto.MFAAuthenticateResponse{}, nil
|
||||
|
|
|
@ -190,12 +190,17 @@ func (c *Cluster) login(ctx context.Context, sshLoginFunc client.SSHLoginFunc) e
|
|||
c.clusterClient.LocalAgent().UpdateUsername(key.Username)
|
||||
c.clusterClient.Username = key.Username
|
||||
|
||||
if err := c.clusterClient.ActivateKey(ctx, key); err != nil {
|
||||
proxyClient, rootAuthClient, err := c.clusterClient.ConnectToRootCluster(ctx, key)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
defer func() {
|
||||
rootAuthClient.Close()
|
||||
proxyClient.Close()
|
||||
}()
|
||||
|
||||
// Attempt device login. This activates a fresh key if successful.
|
||||
if err := c.clusterClient.AttemptDeviceLogin(ctx, key); err != nil {
|
||||
if err := c.clusterClient.AttemptDeviceLogin(ctx, key, rootAuthClient); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ import (
|
|||
"github.com/gravitational/trace"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/api/breaker"
|
||||
"github.com/gravitational/teleport/api/constants"
|
||||
apidefaults "github.com/gravitational/teleport/api/defaults"
|
||||
|
@ -315,6 +316,7 @@ func TestLocalProxyRequirement(t *testing.T) {
|
|||
Context: ctx,
|
||||
TracingProvider: tracing.NoopProvider(),
|
||||
HomePath: tmpHomePath,
|
||||
tracer: tracing.NoopTracer(teleport.ComponentTSH),
|
||||
}
|
||||
tc, err := makeClient(cf)
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -50,6 +50,7 @@ import (
|
|||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/lib/client"
|
||||
"github.com/gravitational/teleport/lib/kube/kubeconfig"
|
||||
"github.com/gravitational/teleport/lib/observability/tracing"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -151,14 +152,13 @@ func wrapConfigFn(cf *CLIConf) func(c *rest.Config) *rest.Config {
|
|||
// paths.
|
||||
func runKubectlCode(cf *CLIConf, args []string) {
|
||||
closeTracer := func() {}
|
||||
cf.TracingProvider = tracing.NoopProvider()
|
||||
cf.tracer = cf.TracingProvider.Tracer(teleport.ComponentTSH)
|
||||
if cf.SampleTraces {
|
||||
provider, err := newTraceProvider(cf, "", nil)
|
||||
if err != nil {
|
||||
log.WithError(err).Debug("Failed to set up span forwarding")
|
||||
} else {
|
||||
// only update the provider if we successfully set it up
|
||||
cf.TracingProvider = provider
|
||||
|
||||
// ensure that the provider is shutdown on exit to flush any spans
|
||||
// that haven't been forwarded yet.
|
||||
closeTracer = func() {
|
||||
|
|
|
@ -1102,6 +1102,8 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error {
|
|||
// because the data that we would be tracing would be the tsh kubectl command.
|
||||
// Instead, we want to enable tracing for the re-executed kubectl command and
|
||||
// we do that in the kubectl command handler.
|
||||
cf.TracingProvider = tracing.NoopProvider()
|
||||
cf.tracer = cf.TracingProvider.Tracer(teleport.ComponentTSH)
|
||||
if cf.SampleTraces && cf.command != kubectl.FullCommand() {
|
||||
// login only needs to be ignored if forwarding to auth
|
||||
var ignored []string
|
||||
|
@ -1112,16 +1114,13 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error {
|
|||
if err != nil {
|
||||
log.WithError(err).Debug("failed to set up span forwarding.")
|
||||
} else {
|
||||
// only update the provider if we successfully set it up
|
||||
cf.TracingProvider = provider
|
||||
|
||||
// ensure that the provider is shutdown on exit to flush any spans
|
||||
// that haven't been forwarded yet.
|
||||
defer func() {
|
||||
shutdownCtx, cancel := context.WithTimeout(cf.Context, 1*time.Second)
|
||||
defer cancel()
|
||||
err := provider.Shutdown(shutdownCtx)
|
||||
if err != nil && !strings.Contains(err.Error(), context.DeadlineExceeded.Error()) {
|
||||
if err != nil && !errors.Is(err, context.DeadlineExceeded) {
|
||||
log.WithError(err).Debugf("failed to shutdown trace provider")
|
||||
}
|
||||
}()
|
||||
|
@ -1130,7 +1129,7 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error {
|
|||
|
||||
// start the span for the command and update the config context so that all spans created
|
||||
// in the future will be rooted at this span.
|
||||
ctx, span := cf.TracingProvider.Tracer(teleport.ComponentTSH).Start(cf.Context, command)
|
||||
ctx, span := cf.tracer.Start(cf.Context, command)
|
||||
cf.Context = ctx
|
||||
defer span.End()
|
||||
|
||||
|
@ -1405,6 +1404,8 @@ func newTraceProvider(cf *CLIConf, command string, ignored []string) (*tracing.P
|
|||
SamplingRate: 1.0,
|
||||
})
|
||||
|
||||
cf.TracingProvider = provider
|
||||
cf.tracer = provider.Tracer(teleport.ComponentTSH)
|
||||
return provider, trace.Wrap(err)
|
||||
}
|
||||
|
||||
|
@ -1441,6 +1442,8 @@ func newTraceProvider(cf *CLIConf, command string, ignored []string) (*tracing.P
|
|||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
cf.TracingProvider = provider
|
||||
cf.tracer = provider.Tracer(teleport.ComponentTSH)
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
|
@ -1721,7 +1724,13 @@ func onLogin(cf *CLIConf) error {
|
|||
// Try updating kube config. If it fails, then we may have
|
||||
// switched to an inactive profile. Continue to normal login.
|
||||
if err := updateKubeConfigOnLogin(cf, tc, updateKubeConfigOption); err == nil {
|
||||
return trace.Wrap(onStatus(cf))
|
||||
profile, profiles, err = cf.FullProfileStatus()
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// Print status to show information of the logged in user.
|
||||
return trace.Wrap(printProfiles(cf, profile, profiles))
|
||||
}
|
||||
|
||||
// proxy is unspecified or the same as the currently provided proxy,
|
||||
|
@ -1747,7 +1756,8 @@ func onLogin(cf *CLIConf) error {
|
|||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
return trace.Wrap(onStatus(cf))
|
||||
// Print status to show information of the logged in user.
|
||||
return trace.Wrap(printProfiles(cf, profile, profiles))
|
||||
// proxy is unspecified or the same as the currently provided proxy,
|
||||
// but desired roles or request ID is specified, treat this as a
|
||||
// privilege escalation request for the same login session.
|
||||
|
@ -1762,7 +1772,8 @@ func onLogin(cf *CLIConf) error {
|
|||
if err := updateKubeConfigOnLogin(cf, tc, updateKubeConfigOption); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
return trace.Wrap(onStatus(cf))
|
||||
// Print status to show information of the logged in user.
|
||||
return trace.Wrap(printProfiles(cf, profile, profiles))
|
||||
|
||||
// otherwise just pass through to standard login
|
||||
default:
|
||||
|
@ -1793,9 +1804,14 @@ func onLogin(cf *CLIConf) error {
|
|||
// "authoritative" source.
|
||||
cf.Username = tc.Username
|
||||
|
||||
if err := tc.ActivateKey(cf.Context, key); err != nil {
|
||||
proxyClient, rootAuthClient, err := tc.ConnectToRootCluster(cf.Context, key)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
defer func() {
|
||||
rootAuthClient.Close()
|
||||
proxyClient.Close()
|
||||
}()
|
||||
|
||||
// TODO(fspmarshall): Refactor access request & cert reissue logic to allow
|
||||
// access requests to be applied to identity files.
|
||||
|
@ -1803,8 +1819,7 @@ func onLogin(cf *CLIConf) error {
|
|||
// key.TrustedCA at this point only has the CA of the root cluster we
|
||||
// logged into. We need to fetch all the CAs for leaf clusters too, to
|
||||
// make them available in the identity file.
|
||||
rootClusterName := key.TrustedCerts[0].ClusterName
|
||||
authorities, err := tc.GetTrustedCA(cf.Context, rootClusterName)
|
||||
authorities, err := rootAuthClient.GetCertAuthorities(cf.Context, types.HostCA, false)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
@ -1836,7 +1851,7 @@ func onLogin(cf *CLIConf) error {
|
|||
// Attempt device login. This activates a fresh key if successful.
|
||||
// We do not save the resulting in the identity file above on purpose, as this
|
||||
// certificate is bound to the present device.
|
||||
if err := tc.AttemptDeviceLogin(cf.Context, key); err != nil {
|
||||
if err := tc.AttemptDeviceLogin(cf.Context, key, rootAuthClient); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
|
@ -1853,33 +1868,24 @@ func onLogin(cf *CLIConf) error {
|
|||
}
|
||||
|
||||
if autoRequest && cf.DesiredRoles == "" && cf.RequestID == "" {
|
||||
var capabailities *types.AccessCapabilities
|
||||
err = tc.WithRootClusterClient(cf.Context, func(clt auth.ClientI) error {
|
||||
cap, err := clt.GetAccessCapabilities(cf.Context, types.AccessCapabilitiesRequest{
|
||||
User: cf.Username,
|
||||
})
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
capabailities = cap
|
||||
|
||||
return nil
|
||||
capabilities, err := rootAuthClient.GetAccessCapabilities(cf.Context, types.AccessCapabilitiesRequest{
|
||||
User: cf.Username,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logoutErr := tc.Logout()
|
||||
return trace.NewAggregate(err, logoutErr)
|
||||
}
|
||||
if capabailities.RequireReason && cf.RequestReason == "" {
|
||||
if capabilities.RequireReason && cf.RequestReason == "" {
|
||||
msg := "--request-reason must be specified"
|
||||
if capabailities.RequestPrompt != "" {
|
||||
msg = msg + ", prompt=" + capabailities.RequestPrompt
|
||||
if capabilities.RequestPrompt != "" {
|
||||
msg = msg + ", prompt=" + capabilities.RequestPrompt
|
||||
}
|
||||
err := trace.BadParameter(msg)
|
||||
logoutErr := tc.Logout()
|
||||
return trace.NewAggregate(err, logoutErr)
|
||||
}
|
||||
if capabailities.AutoRequest {
|
||||
if capabilities.AutoRequest {
|
||||
cf.DesiredRoles = "*"
|
||||
}
|
||||
}
|
||||
|
@ -1915,16 +1921,15 @@ func onLogin(cf *CLIConf) error {
|
|||
alertSeverityMax = types.AlertSeverity_HIGH
|
||||
}
|
||||
|
||||
if err := common.ShowClusterAlerts(cf.Context, tc, os.Stderr, map[string]string{
|
||||
types.AlertOnLogin: "yes",
|
||||
}, types.AlertSeverity_LOW, alertSeverityMax); err != nil {
|
||||
log.WithError(err).Warn("Failed to display cluster alerts.")
|
||||
}
|
||||
|
||||
// NOTE: we currently print all alerts that are marked as `on-login`, because we
|
||||
// don't use the alert API very heavily. If we start to make more use of it, we
|
||||
// could probably add a separate `tsh alerts ls` command, and truncate the list
|
||||
// with a message like "run 'tsh alerts ls' to view N additional alerts".
|
||||
if err := common.ShowClusterAlerts(cf.Context, proxyClient.CurrentCluster(), os.Stderr, map[string]string{
|
||||
types.AlertOnLogin: "yes",
|
||||
}, types.AlertSeverity_LOW, alertSeverityMax); err != nil {
|
||||
log.WithError(err).Warn("Failed to display cluster alerts.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -3343,10 +3348,10 @@ func makeClientForProxy(cf *CLIConf, proxy string) (*client.TeleportClient, erro
|
|||
func loadClientConfigFromCLIConf(cf *CLIConf, proxy string) (*client.Config, error) {
|
||||
if cf.TracingProvider == nil {
|
||||
cf.TracingProvider = tracing.NoopProvider()
|
||||
cf.tracer = cf.TracingProvider.Tracer(teleport.ComponentTSH)
|
||||
}
|
||||
|
||||
cf.tracer = cf.TracingProvider.Tracer(teleport.ComponentTSH)
|
||||
ctx, span := cf.tracer.Start(cf.Context, "loadClientConfigFromCLIConf/init")
|
||||
ctx, span := cf.tracer.Start(cf.Context, "loadClientConfigFromCLIConf")
|
||||
defer span.End()
|
||||
|
||||
// Parse OpenSSH style options.
|
||||
|
@ -3809,8 +3814,6 @@ func proxyHostsErrorMsgDefault(proxyAddress string, ports []int) string {
|
|||
//
|
||||
// If successful, setClientWebProxyAddr will modify the client Config in-place.
|
||||
func setClientWebProxyAddr(ctx context.Context, cf *CLIConf, c *client.Config) error {
|
||||
ctx, span := cf.tracer.Start(ctx, "makeClientForProxy/setClientWebProxyAddr")
|
||||
defer span.End()
|
||||
// If the user has specified a proxy on the command line, and one has not
|
||||
// already been specified from configuration...
|
||||
|
||||
|
|
Loading…
Reference in a new issue