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
6 changes: 6 additions & 0 deletions api/observability/tracing/tracing.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
2 changes: 1 addition & 1 deletion integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions lib/auth/webauthncli/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down
184 changes: 133 additions & 51 deletions lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand All @@ -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")
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.")
Expand Down Expand Up @@ -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 := ""
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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(
Comment thread
rosstimothy marked this conversation as resolved.
Outdated
ctx,
"teleportClient/activateKey",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
)
defer span.End()
Expand All @@ -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
}

Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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")
Expand All @@ -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")
Expand Down
Loading