diff --git a/api/profile/profile.go b/api/profile/profile.go index b1449eda148..e652fe0153a 100644 --- a/api/profile/profile.go +++ b/api/profile/profile.go @@ -23,6 +23,7 @@ import ( "io/fs" "net" "os" + "os/user" "path/filepath" "strings" "time" @@ -32,7 +33,6 @@ import ( "gopkg.in/yaml.v2" "github.com/gravitational/teleport/api/defaults" - "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/api/utils/keypaths" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/api/utils/sshutils" @@ -359,7 +359,7 @@ func defaultProfilePath() string { } home = os.TempDir() - if u, err := utils.CurrentUser(); err == nil && u.HomeDir != "" { + if u, err := user.Current(); err == nil && u.HomeDir != "" { home = u.HomeDir } return filepath.Join(home, profileDir) diff --git a/api/utils/user.go b/api/utils/user.go deleted file mode 100644 index da317e654ba..00000000000 --- a/api/utils/user.go +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright 2023 Gravitational, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package utils - -import ( - "os/user" - "time" - - "github.com/gravitational/trace" -) - -const lookupTimeout = 10 * time.Second - -type userGetter func() (*user.User, error) - -type userResult struct { - user *user.User - err error -} - -// CurrentUser is just like [user.Current], except an error is returned -// if the user lookup exceeds 10 seconds. This is because if -// [user.Current] is called on a domain joined host, the user lookup -// will contact potentially multiple domain controllers which can hang. -func CurrentUser() (*user.User, error) { - return currentUser(user.Current, lookupTimeout) -} - -func currentUser(getUser userGetter, timeout time.Duration) (*user.User, error) { - result := make(chan userResult, 1) - go func() { - u, err := getUser() - result <- userResult{ - user: u, - err: err, - } - }() - - timer := time.NewTimer(timeout) - defer timer.Stop() - - select { - case u := <-result: - return u.user, trace.Wrap(u.err) - case <-timer.C: - return nil, trace.LimitExceeded( - "unexpected host user lookup timeout, please explicitly specify the Teleport user with \"--user\" and the host user \"--login\" to skip host user lookup", - ) - } -} diff --git a/api/utils/user_test.go b/api/utils/user_test.go deleted file mode 100644 index df3f244a14c..00000000000 --- a/api/utils/user_test.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright 2023 Gravitational, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package utils - -import ( - "os/user" - "testing" - "time" - - "github.com/gravitational/trace" - "github.com/stretchr/testify/require" -) - -func TestCurrentUser(t *testing.T) { - t.Parallel() - - u, err := currentUser(func() (*user.User, error) { - time.Sleep(100 * time.Millisecond) - return nil, nil - }, 10*time.Millisecond) - require.Nil(t, u) - require.Error(t, err) - require.True(t, trace.IsLimitExceeded(err)) - - u, err = currentUser(func() (*user.User, error) { - return nil, nil - }, lookupTimeout) - require.Nil(t, u) - require.NoError(t, err) -} diff --git a/docs/pages/faq.mdx b/docs/pages/faq.mdx index 85060347567..d86545744ef 100644 --- a/docs/pages/faq.mdx +++ b/docs/pages/faq.mdx @@ -78,11 +78,14 @@ are not logged in to Teleport or may not have access to a browser. ## `tsh` is very slow on Windows, what to do? -If your host machine is joined to an Active Directory domain, you might find user lookups take a -lot longer than you expect. The number of Active Directory accounts that must be scanned to -perform a user lookup can cause tsh to hang waiting to get information about the current user. -To fix this issue, you can use environment variables to set default account information for your -Teleport user. If you are experiencing long lookup times on Windows, do the following: +If your host machine is joined to an Active Directory domain, you might find +user lookups take a lot longer than you expect. If possible, we recommend +updating `tsh` to v18 or later, which contains an optimized user lookup +algorithm. + +If upgrading is not possible, you can use environment variables to set default +account information for your Teleport user. If you are experiencing long lookup +times on Windows, do the following: - Either set the `TELEPORT_USER` environment variable or set the `--user` flag to the name of your Teleport user. - Either set the `TELEPORT_LOGIN` environment variable or set the `--login` flag to the name of current host user. This setting can be overridden if you open a new SSH session on a machine as a different user. diff --git a/lib/client/api.go b/lib/client/api.go index c0524be3157..da2382162d6 100644 --- a/lib/client/api.go +++ b/lib/client/api.go @@ -31,6 +31,7 @@ import ( "net" "net/url" "os" + "os/user" "path/filepath" "runtime" "slices" @@ -4866,7 +4867,7 @@ func connectToSSHAgent() agent.ExtendedAgent { // Username returns the current user's username func Username() (string, error) { - u, err := apiutils.CurrentUser() + u, err := user.Current() if err != nil { return "", trace.Wrap(err) } diff --git a/lib/client/db/mysql/optionfile.go b/lib/client/db/mysql/optionfile.go index 52db8d3ab3f..4efa27a300f 100644 --- a/lib/client/db/mysql/optionfile.go +++ b/lib/client/db/mysql/optionfile.go @@ -20,6 +20,7 @@ package mysql import ( "os" + "os/user" "path/filepath" "strconv" "strings" @@ -27,7 +28,6 @@ import ( "github.com/gravitational/trace" "gopkg.in/ini.v1" - "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/lib/client/db/profile" ) @@ -51,7 +51,7 @@ type OptionFile struct { func DefaultConfigPath() (string, error) { home, err := os.UserHomeDir() if err != nil || home == "" { - usr, err := utils.CurrentUser() + usr, err := user.Current() if err != nil { return "", trace.ConvertSystemError(err) } diff --git a/lib/client/db/postgres/servicefile.go b/lib/client/db/postgres/servicefile.go index 6d52c3e80b1..369b0a54cc1 100644 --- a/lib/client/db/postgres/servicefile.go +++ b/lib/client/db/postgres/servicefile.go @@ -20,6 +20,7 @@ package postgres import ( "os" + "os/user" "path/filepath" "strconv" "strings" @@ -27,7 +28,6 @@ import ( "github.com/gravitational/trace" "gopkg.in/ini.v1" - "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/lib/client/db/profile" ) @@ -52,7 +52,7 @@ func defaultConfigPath() (string, error) { // TODO(r0mant): Check PGSERVICEFILE and PGSYSCONFDIR env vars as well. home, err := os.UserHomeDir() if err != nil || home == "" { - usr, err := utils.CurrentUser() + usr, err := user.Current() if err != nil { return "", trace.ConvertSystemError(err) } diff --git a/lib/utils/agentconn/agent_windows.go b/lib/utils/agentconn/agent_windows.go index 850f791eb04..5778f780fc5 100644 --- a/lib/utils/agentconn/agent_windows.go +++ b/lib/utils/agentconn/agent_windows.go @@ -29,14 +29,13 @@ import ( "net" "os" "os/exec" + "os/user" "regexp" "strconv" "strings" "github.com/Microsoft/go-winio" "github.com/gravitational/trace" - - apiutils "github.com/gravitational/teleport/api/utils" ) const namedPipe = `\\.\pipe\openssh-ssh-agent` @@ -104,7 +103,7 @@ func dialCygwin(socket string) (net.Conn, error) { } key := sockMatches[3] - u, err := apiutils.CurrentUser() + u, err := user.Current() if err != nil { return nil, trace.Wrap(err) } diff --git a/tool/tsh/common/tsh.go b/tool/tsh/common/tsh.go index 2599600679d..a3171c6bbdf 100644 --- a/tool/tsh/common/tsh.go +++ b/tool/tsh/common/tsh.go @@ -31,6 +31,7 @@ import ( "os" "os/exec" "os/signal" + "os/user" "path/filepath" "regexp" "runtime" @@ -66,7 +67,6 @@ import ( "github.com/gravitational/teleport/api/types/accesslist" apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/api/types/wrappers" - apiutils "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/api/utils/prompt" "github.com/gravitational/teleport/lib/asciitable" @@ -3813,7 +3813,7 @@ func onSSHLatency(cf *CLIConf) error { func runLocalCommand(hostLogin string, command []string) error { if len(command) == 0 { if hostLogin == "" { - user, err := apiutils.CurrentUser() + user, err := user.Current() if err != nil { return trace.Wrap(err) }