Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v13] Fix headless server access requests #27241

Merged
merged 2 commits into from
Jun 1, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 16 additions & 6 deletions lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -585,25 +585,35 @@ func IsErrorResolvableWithRelogin(err error) bool {
trace.IsBadParameter(err) || trace.IsTrustError(err) || keys.IsPrivateKeyPolicyError(err) || trace.IsNotFound(err)
}

// LoadProfile populates Config with the values stored in the given
// profiles directory. If profileDir is an empty string, the default profile
// directory ~/.tsh is used.
func (c *Config) LoadProfile(ps ProfileStore, proxyAddr string) error {
// GetProfile gets the profile for the specified proxy address, or
// the current profile if no proxy is specified.
func (c *Config) GetProfile(ps ProfileStore, proxyAddr string) (*profile.Profile, error) {
var proxyHost string
var err error
if proxyAddr == "" {
proxyHost, err = ps.CurrentProfile()
if err != nil {
return trace.Wrap(err)
return nil, trace.Wrap(err)
}
} else {
proxyHost, err = utils.Host(proxyAddr)
if err != nil {
return trace.Wrap(err)
return nil, trace.Wrap(err)
}
}

profile, err := ps.GetProfile(proxyHost)
if err != nil {
return nil, trace.Wrap(err)
}
return profile, nil
}

// LoadProfile populates Config with the values stored in the given
// profiles directory. If profileDir is an empty string, the default profile
// directory ~/.tsh is used.
func (c *Config) LoadProfile(ps ProfileStore, proxyAddr string) error {
profile, err := c.GetProfile(ps, proxyAddr)
if err != nil {
return trace.Wrap(err)
}
Expand Down
121 changes: 67 additions & 54 deletions tool/tsh/tsh.go
Original file line number Diff line number Diff line change
Expand Up @@ -3275,6 +3275,61 @@ func makeClient(cf *CLIConf) (*client.TeleportClient, error) {
// makeClient takes the command-line configuration and a proxy address and constructs & returns
// a fully configured TeleportClient object
func makeClientForProxy(cf *CLIConf, proxy string) (*client.TeleportClient, error) {
c, err := loadClientConfigFromCLIConf(cf, proxy)
if err != nil {
return nil, trace.Wrap(err)
}

ctx, span := c.Tracer.Start(cf.Context, "makeClientForProxy/init")
defer span.End()

tc, err := client.NewClient(c)
if err != nil {
return nil, trace.Wrap(err)
}

// Load SSH key for the cluster indicated in the profile.
// Handle gracefully if the profile is empty, the key cannot
// be found, or the key isn't supported as an agent key.
profile, profileError := c.GetProfile(c.ClientStore, proxy)
if profileError == nil {
if err := tc.LoadKeyForCluster(ctx, profile.SiteName); err != nil {
if !trace.IsNotFound(err) && !trace.IsConnectionProblem(err) {
return nil, trace.Wrap(err)
}
log.WithError(err).Infof("Could not load key for %s into the local agent.", cf.SiteName)
}
}

// If we are missing client profile information, ping the webproxy
// for proxy info and load it into the client config.
if profileError != nil || cf.IdentityFileIn != "" {
log.Debug("Pinging the proxy to fetch listening addresses for non-web ports.")
_, err := tc.Ping(cf.Context)
if err != nil {
return nil, trace.Wrap(err)
}

// Identityfile uses a placeholder profile. Save missing profile info.
if cf.IdentityFileIn != "" {
if err := tc.SaveProfile(true); err != nil {
return nil, trace.Wrap(err)
}
}
}

return tc, nil
}

func loadClientConfigFromCLIConf(cf *CLIConf, proxy string) (*client.Config, error) {
if cf.TracingProvider == nil {
cf.TracingProvider = tracing.NoopProvider()
}

cf.tracer = cf.TracingProvider.Tracer(teleport.ComponentTSH)
ctx, span := cf.tracer.Start(cf.Context, "loadClientConfigFromCLIConf/init")
defer span.End()

// Parse OpenSSH style options.
options, err := parseOptions(cf.Options)
if err != nil {
Expand Down Expand Up @@ -3329,13 +3384,8 @@ func makeClientForProxy(cf *CLIConf, proxy string) (*client.TeleportClient, erro

// 1: start with the defaults
c := client.MakeDefaultConfig()
if cf.TracingProvider == nil {
cf.TracingProvider = tracing.NoopProvider()
}
c.Tracer = cf.TracingProvider.Tracer(teleport.ComponentTSH)
cf.tracer = c.Tracer
ctx, span := c.Tracer.Start(cf.Context, "makeClientForProxy/init")
defer span.End()

c.Tracer = cf.tracer

// Force the use of proxy template below.
useProxyTemplate := strings.Contains(cf.ProxyJump, "{{proxy}}")
Expand Down Expand Up @@ -3400,9 +3450,10 @@ func makeClientForProxy(cf *CLIConf, proxy string) (*client.TeleportClient, erro
return nil, trace.BadParameter("either --headless or --auth can be specified, not both")
}
cf.AuthConnector = constants.HeadlessConnector
if !cf.ExplicitUsername {
return nil, trace.BadParameter("user must be set explicitly for headless login with the --user flag or $TELEPORT_USER env variable")
}
}

if cf.AuthConnector == constants.HeadlessConnector && !cf.ExplicitUsername {
return nil, trace.BadParameter("user must be set explicitly for headless login with the --user flag or $TELEPORT_USER env variable")
}

if err := tryLockMemory(cf); err != nil {
Expand Down Expand Up @@ -3457,7 +3508,6 @@ func makeClientForProxy(cf *CLIConf, proxy string) (*client.TeleportClient, erro
if len(dPorts) > 0 {
c.DynamicForwardedPorts = dPorts
}
profileSiteName := c.SiteName
if cf.SiteName != "" {
c.SiteName = cf.SiteName
}
Expand Down Expand Up @@ -3561,47 +3611,13 @@ func makeClientForProxy(cf *CLIConf, proxy string) (*client.TeleportClient, erro
c.NonInteractive = true
}

tc, err := client.NewClient(c)
if err != nil {
return nil, trace.Wrap(err)
}
c.Stderr = cf.Stderr()
c.Stdout = cf.Stdout()

// Load SSH key for the cluster indicated in the profile.
// Handle gracefully if the profile is empty, the key cannot
// be found, or the key isn't supported as an agent key.
if profileSiteName != "" {
if err := tc.LoadKeyForCluster(ctx, profileSiteName); err != nil {
if !trace.IsNotFound(err) && !trace.IsConnectionProblem(err) {
return nil, trace.Wrap(err)
}
log.WithError(err).Infof("Could not load key for %s into the local agent.", profileSiteName)
}
}

// If we are missing client profile information, ping the webproxy
// for proxy info and load it into the client config.
if profileErr != nil || cf.IdentityFileIn != "" {
log.Debug("Pinging the proxy to fetch listening addresses for non-web ports.")
_, err := tc.Ping(cf.Context)
if err != nil {
return nil, trace.Wrap(err)
}

// Identityfile uses a placeholder profile. Save missing profile info.
if cf.IdentityFileIn != "" {
if err := tc.SaveProfile(true); err != nil {
return nil, trace.Wrap(err)
}
}
}

tc.Config.Stderr = cf.Stderr()
tc.Config.Stdout = cf.Stdout()

tc.Config.Reason = cf.Reason
tc.Config.Invited = cf.Invited
tc.Config.DisplayParticipantRequirements = cf.displayParticipantRequirements
return tc, nil
c.Reason = cf.Reason
c.Invited = cf.Invited
c.DisplayParticipantRequirements = cf.displayParticipantRequirements
return c, nil
}

func initClientStore(cf *CLIConf, proxy string) (*client.Store, error) {
Expand Down Expand Up @@ -4339,9 +4355,6 @@ func reissueWithRequests(cf *CLIConf, tc *client.TeleportClient, newRequests []s
if err != nil {
return trace.Wrap(err)
}
if profile.IsVirtual {
return trace.BadParameter("cannot reissue certificates while using an identity file (-i)")
}
params := client.ReissueParams{
AccessRequests: newRequests,
DropAccessRequests: dropRequests,
Expand Down