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:
rosstimothy 2023-07-05 11:51:56 -04:00 committed by GitHub
parent 15d3564382
commit 119dc7a3a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 238 additions and 106 deletions

View file

@ -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)
}

View file

@ -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

View file

@ -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 {

View file

@ -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")

View file

@ -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")

View file

@ -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

View file

@ -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)
}

View file

@ -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)

View file

@ -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() {

View file

@ -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...