Linux: fix potential deadlock/segfault when calling into libnss for user mgmt (#295)

Export-Source-Commit: 12f28f2b1aa27c84df9f608557b8dc6f0de6cbb2
This commit is contained in:
Luke Okraszewski
2026-08-25 11:35:10 -05:00
committed by Gus Rivera
parent 6d906dc57b
commit 6dbccdd8ad
34 changed files with 325 additions and 150 deletions
+9
View File
@@ -393,6 +393,9 @@ linters:
- pattern: ^ssh\.(Dial|NewClient|NewClientConn)$ - pattern: ^ssh\.(Dial|NewClient|NewClientConn)$
pkg: ^golang\.org/x/crypto/ssh$ pkg: ^golang\.org/x/crypto/ssh$
msg: Use api/ssh package to construct SSH clients with proper defaults msg: Use api/ssh package to construct SSH clients with proper defaults
- pkg: '^os/user$'
pattern: '^user\.(Lookup|LookupId|LookupGroup|LookupGroupId|Current|User\.GroupIds)$'
msg: 'os/user lookup APIs potentially unsafe; use the session/host/user wrapper instead'
- pattern: ^ssh\.(Dial|NewClientWithTimeout|NewClientConnWithTimeout)$ - pattern: ^ssh\.(Dial|NewClientWithTimeout|NewClientConnWithTimeout)$
pkg: ^github\.com/gravitational/teleport/api/observability/tracing/ssh$ pkg: ^github\.com/gravitational/teleport/api/observability/tracing/ssh$
msg: Use api/ssh package to construct SSH clients with proper defaults msg: Use api/ssh package to construct SSH clients with proper defaults
@@ -567,6 +570,12 @@ linters:
path: ^api/ssh/ path: ^api/ssh/
text: tracessh.NewClientConnWithTimeout text: tracessh.NewClientConnWithTimeout
# Allow session/host/user and tests to use os/user functions directly.
- path: '(_test\.go$|^api/|^integration/helpers/|^lib/utils/testutils|^session/host/user/)'
text: "session/host/user"
linters:
- forbidigo
# TODO: Fix forced type assertions in the rest of the codebase and remove this exclusion so that the linter can # TODO: Fix forced type assertions in the rest of the codebase and remove this exclusion so that the linter can
# enforce this rule everywhere. # enforce this rule everywhere.
- linters: - linters:
+2 -2
View File
@@ -32,7 +32,6 @@ import (
"net" "net"
"net/url" "net/url"
"os" "os"
"os/user"
"path/filepath" "path/filepath"
"runtime" "runtime"
"slices" "slices"
@@ -103,6 +102,7 @@ import (
"github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils"
logutils "github.com/gravitational/teleport/lib/utils/log" logutils "github.com/gravitational/teleport/lib/utils/log"
"github.com/gravitational/teleport/lib/utils/set" "github.com/gravitational/teleport/lib/utils/set"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
const ( const (
@@ -5012,7 +5012,7 @@ func loopbackPool(proxyAddr string) *x509.CertPool {
// Username returns the current user's username // Username returns the current user's username
func Username() (string, error) { func Username() (string, error) {
u, err := user.Current() u, err := hostuser.Current()
if err != nil { if err != nil {
return "", trace.Wrap(err) return "", trace.Wrap(err)
} }
+2 -2
View File
@@ -20,7 +20,6 @@ package mysql
import ( import (
"os" "os"
"os/user"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
@@ -29,6 +28,7 @@ import (
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
"github.com/gravitational/teleport/lib/client/db/profile" "github.com/gravitational/teleport/lib/client/db/profile"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
func init() { func init() {
@@ -51,7 +51,7 @@ type OptionFile struct {
func DefaultConfigPath() (string, error) { func DefaultConfigPath() (string, error) {
home, err := os.UserHomeDir() home, err := os.UserHomeDir()
if err != nil || home == "" { if err != nil || home == "" {
usr, err := user.Current() usr, err := hostuser.Current()
if err != nil { if err != nil {
return "", trace.ConvertSystemError(err) return "", trace.ConvertSystemError(err)
} }
+2 -2
View File
@@ -20,7 +20,6 @@ package postgres
import ( import (
"os" "os"
"os/user"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
@@ -29,6 +28,7 @@ import (
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
"github.com/gravitational/teleport/lib/client/db/profile" "github.com/gravitational/teleport/lib/client/db/profile"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
func init() { func init() {
@@ -52,7 +52,7 @@ func defaultConfigPath() (string, error) {
// TODO(r0mant): Check PGSERVICEFILE and PGSYSCONFDIR env vars as well. // TODO(r0mant): Check PGSERVICEFILE and PGSYSCONFDIR env vars as well.
home, err := os.UserHomeDir() home, err := os.UserHomeDir()
if err != nil || home == "" { if err != nil || home == "" {
usr, err := user.Current() usr, err := hostuser.Current()
if err != nil { if err != nil {
return "", trace.ConvertSystemError(err) return "", trace.ConvertSystemError(err)
} }
+2 -2
View File
@@ -35,7 +35,6 @@ import (
"io/fs" "io/fs"
"log/slog" "log/slog"
"os/exec" "os/exec"
"os/user"
"strings" "strings"
"sync" "sync"
"unsafe" "unsafe"
@@ -46,6 +45,7 @@ import (
devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1"
"github.com/gravitational/teleport/lib/darwin" "github.com/gravitational/teleport/lib/darwin"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
func enrollDeviceInit() (*devicepb.EnrollDeviceInit, error) { func enrollDeviceInit() (*devicepb.EnrollDeviceInit, error) {
@@ -115,7 +115,7 @@ func collectDeviceData(_ CollectDataMode) (*devicepb.DeviceCollectedData, error)
return nil, trace.Wrap(statusErrorFromC(res)) return nil, trace.Wrap(statusErrorFromC(res))
} }
osUser, err := user.Current() osUser, err := hostuser.Current()
if err != nil { if err != nil {
return nil, trace.Wrap(err, "reading current user") return nil, trace.Wrap(err, "reading current user")
} }
+2 -2
View File
@@ -29,7 +29,6 @@ import (
"log/slog" "log/slog"
"os" "os"
"os/exec" "os/exec"
"os/user"
"time" "time"
"github.com/google/go-attestation/attest" "github.com/google/go-attestation/attest"
@@ -39,6 +38,7 @@ import (
"github.com/gravitational/teleport" "github.com/gravitational/teleport"
devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1"
"github.com/gravitational/teleport/lib/linux" "github.com/gravitational/teleport/lib/linux"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
// deviceStateFolderName starts without a "." on Linux systems. // deviceStateFolderName starts without a "." on Linux systems.
@@ -167,7 +167,7 @@ func collectDeviceData(mode CollectDataMode) (*devicepb.DeviceCollectedData, err
baseBoardSerialNumber = dmiInfo.BoardSerial baseBoardSerialNumber = dmiInfo.BoardSerial
} }
u, err := user.Current() u, err := hostuser.Current()
if err != nil { if err != nil {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
+2 -2
View File
@@ -25,7 +25,6 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"os/user"
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -34,6 +33,7 @@ import (
"github.com/gravitational/trace" "github.com/gravitational/trace"
"github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/defaults"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
type msalTokenCache struct { type msalTokenCache struct {
@@ -51,7 +51,7 @@ type exchangeResponse struct {
// getRefreshTokens returns all current refresh tokens from the Azure CLI token cache. // getRefreshTokens returns all current refresh tokens from the Azure CLI token cache.
func getRefreshTokens() ([]msalToken, error) { func getRefreshTokens() ([]msalToken, error) {
usr, err := user.Current() usr, err := hostuser.Current()
if err != nil { if err != nil {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
+2 -2
View File
@@ -23,13 +23,13 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"os/user"
"path/filepath" "path/filepath"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/gravitational/trace" "github.com/gravitational/trace"
"github.com/gravitational/teleport/lib/msgraph" "github.com/gravitational/teleport/lib/msgraph"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
// createGraphClient creates a new graph client from ambient credentials (Azure CLI credentials cache). // createGraphClient creates a new graph client from ambient credentials (Azure CLI credentials cache).
@@ -60,7 +60,7 @@ func EnsureAZLogin(ctx context.Context) error {
} }
func getAzureDir() (string, error) { func getAzureDir() (string, error) {
usr, err := user.Current() usr, err := hostuser.Current()
if err != nil { if err != nil {
return "", trace.Wrap(err) return "", trace.Wrap(err)
} }
@@ -43,11 +43,7 @@ import (
clusterconfigpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/clusterconfig/v1" clusterconfigpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/clusterconfig/v1"
"github.com/gravitational/teleport/api/types/accessgraph" "github.com/gravitational/teleport/api/types/accessgraph"
"github.com/gravitational/teleport/api/utils/retryutils" "github.com/gravitational/teleport/api/utils/retryutils"
) hostuser "github.com/gravitational/teleport/session/host/user"
var (
// ErrUnsupportedPlatform is returned when the operating system is not supported.
ErrUnsupportedPlatform = errors.New("unsupported platform")
) )
// Watcher watches for changes to authorized_keys files // Watcher watches for changes to authorized_keys files
@@ -96,7 +92,7 @@ func NewWatcher(ctx context.Context, config WatcherConfig) (*Watcher, error) {
switch platform := getOS(config); platform { switch platform := getOS(config); platform {
case constants.LinuxOS, constants.DarwinOS: case constants.LinuxOS, constants.DarwinOS:
default: default:
return nil, trace.Wrap(ErrUnsupportedPlatform) return nil, trace.Wrap(hostuser.ErrUnsupportedPlatform)
} }
if config.HostID == "" { if config.HostID == "" {
@@ -112,7 +108,7 @@ func NewWatcher(ctx context.Context, config WatcherConfig) (*Watcher, error) {
config.Clock = clockwork.NewRealClock() config.Clock = clockwork.NewRealClock()
} }
if config.getHostUsers == nil { if config.getHostUsers == nil {
config.getHostUsers = getHostUsers config.getHostUsers = hostuser.GetHostUsers
} }
w := &Watcher{ w := &Watcher{
@@ -1,74 +0,0 @@
/*
* Teleport
* Copyright (C) 2024 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package authorizedkeys
/*
#define _XOPEN_SOURCE 500
#include <pwd.h>
*/
import "C"
import (
"os/user"
"strconv"
"sync"
"github.com/gravitational/trace"
)
// pwentLock should be acquired when using MT-Unsafe race:pwent functions (i.e.
// setpwent/getpwent/endpwent).
var pwentLock sync.Mutex
// getHostUsers returns the list of all users on the host from the user
// directory (depending on system configuration this can be /etc/passwd,
// LDAP...).
func getHostUsers() ([]user.User, error) {
pwentLock.Lock()
defer pwentLock.Unlock()
C.setpwent()
defer C.endpwent()
var results []user.User
for {
result, err := C.getpwent()
// cgo error convention, check the return value before errno
if result != nil {
results = append(results, passwdC2Go(result))
continue
}
if err != nil {
return nil, trace.Wrap(err)
}
return results, nil
}
}
// passwdC2Go converts `passwd` struct from C to golang native struct
func passwdC2Go(passwdC *C.struct_passwd) user.User {
name := C.GoString(passwdC.pw_name)
return user.User{
Name: name,
Username: name,
Uid: strconv.FormatUint(uint64(passwdC.pw_uid), 10),
Gid: strconv.FormatUint(uint64(passwdC.pw_gid), 10),
HomeDir: C.GoString(passwdC.pw_dir),
}
}
+3 -3
View File
@@ -26,7 +26,6 @@ import (
"maps" "maps"
"net" "net"
"os" "os"
"os/user"
"regexp" "regexp"
"slices" "slices"
"strconv" "strconv"
@@ -65,6 +64,7 @@ import (
"github.com/gravitational/teleport/lib/tlsca" "github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils"
logutils "github.com/gravitational/teleport/lib/utils/log" logutils "github.com/gravitational/teleport/lib/utils/log"
hostuser "github.com/gravitational/teleport/session/host/user"
"github.com/gravitational/teleport/session/reexec" "github.com/gravitational/teleport/session/reexec"
) )
@@ -749,11 +749,11 @@ func (sess *linuxSession) handleClientHello(m *tdpb.ClientHello) error {
} }
func (sess *linuxSession) changeAuthorityFileOwnership(m *tdpb.ClientHello) error { func (sess *linuxSession) changeAuthorityFileOwnership(m *tdpb.ClientHello) error {
currentUser, err := user.Current() currentUser, err := hostuser.Current()
if err != nil { if err != nil {
return trace.Wrap(err, "failed to get current user") return trace.Wrap(err, "failed to get current user")
} }
targetUser, err := user.Lookup(m.Username) targetUser, err := hostuser.Lookup(m.Username)
if err != nil { if err != nil {
return trace.Wrap(err, "failed to lookup user %s", m.Username) return trace.Wrap(err, "failed to lookup user %s", m.Username)
} }
+2 -2
View File
@@ -23,7 +23,6 @@ import (
"log/slog" "log/slog"
"os" "os"
"os/exec" "os/exec"
"os/user"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
@@ -36,6 +35,7 @@ import (
reexecutils "github.com/gravitational/teleport/lib/sshutils/reexec" reexecutils "github.com/gravitational/teleport/lib/sshutils/reexec"
logutils "github.com/gravitational/teleport/lib/utils/log" logutils "github.com/gravitational/teleport/lib/utils/log"
"github.com/gravitational/teleport/session/envutils" "github.com/gravitational/teleport/session/envutils"
hostuser "github.com/gravitational/teleport/session/host/user"
"github.com/gravitational/teleport/session/reexec" "github.com/gravitational/teleport/session/reexec"
"github.com/gravitational/teleport/session/reexec/reexecconstants" "github.com/gravitational/teleport/session/reexec/reexecconstants"
) )
@@ -126,7 +126,7 @@ func StartTeleportExecXSession(ctx context.Context, cfg *XSessionConfig) (*reexe
return nil, trace.BadParameter("missing parameter ChildLogConfig") return nil, trace.BadParameter("missing parameter ChildLogConfig")
} }
if _, err := user.Lookup(cfg.Login); err != nil { if _, err := hostuser.Lookup(cfg.Login); err != nil {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
+2 -2
View File
@@ -26,7 +26,6 @@ import (
"net" "net"
"os" "os"
"os/exec" "os/exec"
"os/user"
"sync" "sync"
"syscall" "syscall"
"time" "time"
@@ -36,6 +35,7 @@ import (
"github.com/gravitational/teleport/lib/utils/mcputils" "github.com/gravitational/teleport/lib/utils/mcputils"
hostutils "github.com/gravitational/teleport/session/host" hostutils "github.com/gravitational/teleport/session/host"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
// handleAuthErrStdio starts a stdio message reader and replies with the auth // handleAuthErrStdio starts a stdio message reader and replies with the auth
@@ -238,7 +238,7 @@ func makeExecServerRunner(ctx context.Context, session *sessionHandler) (stdioSe
} }
// Set host user. // Set host user.
hostUser, err := user.Lookup(mcpSpec.RunAsHostUser) hostUser, err := hostuser.Lookup(mcpSpec.RunAsHostUser)
if err != nil { if err != nil {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
+2 -1
View File
@@ -75,6 +75,7 @@ import (
"github.com/gravitational/teleport/lib/sshutils" "github.com/gravitational/teleport/lib/sshutils"
reexecutils "github.com/gravitational/teleport/lib/sshutils/reexec" reexecutils "github.com/gravitational/teleport/lib/sshutils/reexec"
"github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils"
hostuser "github.com/gravitational/teleport/session/host/user"
"github.com/gravitational/teleport/session/networking" "github.com/gravitational/teleport/session/networking"
"github.com/gravitational/teleport/session/networking/x11" "github.com/gravitational/teleport/session/networking/x11"
"github.com/gravitational/teleport/session/pam/pamcfg" "github.com/gravitational/teleport/session/pam/pamcfg"
@@ -1103,7 +1104,7 @@ func (s *Server) startAuthorizedKeysManager(ctx context.Context, auth authclient
Clock: s.clock, Clock: s.clock,
}, },
) )
if errors.Is(err, authorizedkeysreporter.ErrUnsupportedPlatform) { if errors.Is(err, hostuser.ErrUnsupportedPlatform) {
return nil return nil
} else if err != nil { } else if err != nil {
return trace.Wrap(err) return trace.Wrap(err)
+2 -1
View File
@@ -41,6 +41,7 @@ import (
tracessh "github.com/gravitational/teleport/api/observability/tracing/ssh" tracessh "github.com/gravitational/teleport/api/observability/tracing/ssh"
rsession "github.com/gravitational/teleport/lib/session" rsession "github.com/gravitational/teleport/lib/session"
reexecutils "github.com/gravitational/teleport/lib/sshutils/reexec" reexecutils "github.com/gravitational/teleport/lib/sshutils/reexec"
hostuser "github.com/gravitational/teleport/session/host/user"
"github.com/gravitational/teleport/session/reexec" "github.com/gravitational/teleport/session/reexec"
"github.com/gravitational/teleport/session/reexec/reexecconstants" "github.com/gravitational/teleport/session/reexec/reexecconstants"
) )
@@ -535,7 +536,7 @@ func getOwner(login string, lookupUser LookupUser, lookupGroup LookupGroup) (int
// setOwner changes the owner and mode of the TTY. // setOwner changes the owner and mode of the TTY.
func (t *terminal) setOwner() error { func (t *terminal) setOwner() error {
uid, gid, mode, err := getOwner(t.serverContext.Identity.Login, user.Lookup, user.LookupGroup) uid, gid, mode, err := getOwner(t.serverContext.Identity.Login, hostuser.Lookup, hostuser.LookupGroup)
if err != nil { if err != nil {
return trace.Wrap(err) return trace.Wrap(err)
} }
+5 -4
View File
@@ -35,6 +35,7 @@ import (
"github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/teleport/session/host" "github.com/gravitational/teleport/session/host"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
// HostUsersProvisioningBackend is used to implement HostUsersBackend // HostUsersProvisioningBackend is used to implement HostUsersBackend
@@ -74,7 +75,7 @@ func newHostSudoersBackend(uuid string) (HostSudoersBackend, error) {
// Lookup implements host user information lookup // Lookup implements host user information lookup
func (*HostUsersProvisioningBackend) Lookup(username string) (*user.User, error) { func (*HostUsersProvisioningBackend) Lookup(username string) (*user.User, error) {
usr, err := user.Lookup(username) usr, err := hostuser.Lookup(username)
if err != nil { if err != nil {
if !errors.Is(err, user.UnknownUserError(username)) && strings.Contains(err.Error(), "no such file or directory") { if !errors.Is(err, user.UnknownUserError(username)) && strings.Contains(err.Error(), "no such file or directory") {
return nil, trace.Wrap(err, "looking up user %q, sources configured for passwd in host's /etc/nsswitch.conf may be misconfigured", username) return nil, trace.Wrap(err, "looking up user %q, sources configured for passwd in host's /etc/nsswitch.conf may be misconfigured", username)
@@ -88,17 +89,17 @@ func (*HostUsersProvisioningBackend) Lookup(username string) (*user.User, error)
// UserGIDs returns the list of group IDs for a user // UserGIDs returns the list of group IDs for a user
func (*HostUsersProvisioningBackend) UserGIDs(u *user.User) ([]string, error) { func (*HostUsersProvisioningBackend) UserGIDs(u *user.User) ([]string, error) {
return u.GroupIds() return hostuser.GroupIds(u)
} }
// LookupGroup host group information lookup // LookupGroup host group information lookup
func (*HostUsersProvisioningBackend) LookupGroup(name string) (*user.Group, error) { func (*HostUsersProvisioningBackend) LookupGroup(name string) (*user.Group, error) {
return user.LookupGroup(name) return hostuser.LookupGroup(name)
} }
// LookupGroup host group information lookup by GID // LookupGroup host group information lookup by GID
func (*HostUsersProvisioningBackend) LookupGroupByID(gid string) (*user.Group, error) { func (*HostUsersProvisioningBackend) LookupGroupByID(gid string) (*user.Group, error) {
return user.LookupGroupId(gid) return hostuser.LookupGroupId(gid)
} }
// UpdateUser sets a user's groups and default shell, replacing their existing groups. // UpdateUser sets a user's groups and default shell, replacing their existing groups.
+2 -1
View File
@@ -29,6 +29,7 @@ import (
"github.com/gravitational/trace" "github.com/gravitational/trace"
decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
// ErrorContext contains context used to enrich child process launch errors. // ErrorContext contains context used to enrich child process launch errors.
@@ -65,7 +66,7 @@ func ChildErrorWithContext(errMsg string, context *ErrorContext) string {
unknownUserError := user.UnknownUserError(context.Login) unknownUserError := user.UnknownUserError(context.Login)
switch { switch {
case strings.Contains(errMsg, "failed to open PAM context"): // PAM errors are often cause by an unknown user. case strings.Contains(errMsg, "failed to open PAM context"): // PAM errors are often cause by an unknown user.
if _, err := user.Lookup(context.Login); errors.Is(err, unknownUserError) { if _, err := hostuser.Lookup(context.Login); errors.Is(err, unknownUserError) {
if ambiguousHostUserDenial { if ambiguousHostUserDenial {
return ambiguousHostUserError() return ambiguousHostUserError()
} }
+2 -2
View File
@@ -24,7 +24,6 @@ import (
"fmt" "fmt"
"io/fs" "io/fs"
"os" "os"
"os/user"
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -37,6 +36,7 @@ import (
"github.com/gravitational/teleport/lib/tbot/botfs" "github.com/gravitational/teleport/lib/tbot/botfs"
"github.com/gravitational/teleport/lib/tbot/internal/encoding" "github.com/gravitational/teleport/lib/tbot/internal/encoding"
"github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
const DirectoryType = "directory" const DirectoryType = "directory"
@@ -233,7 +233,7 @@ func (dd *Directory) ACLsEnabled() bool {
// This will not attempt to correct any issues, but will cause a hard failure if // This will not attempt to correct any issues, but will cause a hard failure if
// `acls: required` is configured and issues are detected. // `acls: required` is configured and issues are detected.
func (dd *Directory) verifyLegacyACLs(keys []string) error { func (dd *Directory) verifyLegacyACLs(keys []string) error {
currentUser, err := user.Current() currentUser, err := hostuser.Current()
if err != nil { if err != nil {
// user.Current will fail if the user id does not exist in /etc/passwd // user.Current will fail if the user id does not exist in /etc/passwd
// as is the case with some containerized environments. // as is the case with some containerized environments.
+4 -3
View File
@@ -39,6 +39,7 @@ import (
"github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/teleport/lib/utils/set" "github.com/gravitational/teleport/lib/utils/set"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
// mostACLRead is a permission mode granting readonly access to a file. // mostACLRead is a permission mode granting readonly access to a file.
@@ -485,7 +486,7 @@ func resolveACLReaderSelector(s *ACLSelector, dir bool) (acl.Entry, error) {
}, nil }, nil
} }
user, err := user.Lookup(s.User) user, err := hostuser.Lookup(s.User)
if err != nil { if err != nil {
return acl.Entry{}, trace.Wrap(err) return acl.Entry{}, trace.Wrap(err)
} }
@@ -506,7 +507,7 @@ func resolveACLReaderSelector(s *ACLSelector, dir bool) (acl.Entry, error) {
}, nil }, nil
} }
group, err := user.LookupGroup(s.Group) group, err := hostuser.LookupGroup(s.Group)
if err != nil { if err != nil {
return acl.Entry{}, trace.Wrap(err) return acl.Entry{}, trace.Wrap(err)
} }
@@ -711,7 +712,7 @@ func GetOwner(fileInfo fs.FileInfo) (*user.User, error) {
return nil, trace.NotImplemented("Cannot verify file ownership on this platform.") return nil, trace.NotImplemented("Cannot verify file ownership on this platform.")
} }
user, err := user.LookupId(strconv.Itoa(int(info.Uid))) user, err := hostuser.LookupId(strconv.Itoa(int(info.Uid)))
if err != nil { if err != nil {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
@@ -23,7 +23,6 @@ import (
"fmt" "fmt"
"log/slog" "log/slog"
"os" "os"
"os/user"
"path/filepath" "path/filepath"
"slices" "slices"
"strings" "strings"
@@ -42,6 +41,7 @@ import (
"github.com/gravitational/teleport/lib/teleterm/clusters" "github.com/gravitational/teleport/lib/teleterm/clusters"
"github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/teleport/lib/utils/hostid" "github.com/gravitational/teleport/lib/utils/hostid"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
type RoleSetup struct { type RoleSetup struct {
@@ -98,7 +98,7 @@ func (s *RoleSetup) Run(ctx context.Context, accessAndIdentity AccessAndIdentity
} }
} }
systemUser, err := user.Current() systemUser, err := hostuser.Current()
if err != nil { if err != nil {
return noCertsReloaded, trace.Wrap(err) return noCertsReloaded, trace.Wrap(err)
} }
+5 -3
View File
@@ -30,6 +30,8 @@ import (
"syscall" "syscall"
"github.com/gravitational/trace" "github.com/gravitational/trace"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
// PercentUsed returns percentage of disk space used. The percentage of disk // PercentUsed returns percentage of disk space used. The percentage of disk
@@ -93,12 +95,12 @@ func CanUserWriteTo(path string) (bool, error) {
var usr *user.User var usr *user.User
if ogUser := os.Getenv("SUDO_USER"); ogUser != "" { if ogUser := os.Getenv("SUDO_USER"); ogUser != "" {
usr, err = user.Lookup(ogUser) usr, err = hostuser.Lookup(ogUser)
if err != nil { if err != nil {
return false, trace.NotFound("could not determine original user: %+v", err) return false, trace.NotFound("could not determine original user: %+v", err)
} }
} else { } else {
usr, err = user.Current() usr, err = hostuser.Current()
if err != nil { if err != nil {
return false, trace.NotFound("could not determine current user: %+v", err) return false, trace.NotFound("could not determine current user: %+v", err)
} }
@@ -116,7 +118,7 @@ func CanUserWriteTo(path string) (bool, error) {
} }
// file and user have a group in common // file and user have a group in common
groupIDs, err := usr.GroupIds() groupIDs, err := hostuser.GroupIds(usr)
if err != nil { if err != nil {
return false, trace.NotFound("could not determine current user group ids: %+v", err) return false, trace.NotFound("could not determine current user group ids: %+v", err)
} }
+4 -2
View File
@@ -34,6 +34,8 @@ import (
"syscall" "syscall"
"github.com/gravitational/trace" "github.com/gravitational/trace"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
// man GROUPADD(8), exit codes section // man GROUPADD(8), exit codes section
@@ -181,7 +183,7 @@ func UserDel(username string) (exitCode int, err error) {
if err != nil { if err != nil {
return -1, trace.NotFound("cant find userdel binary: %s", err) return -1, trace.NotFound("cant find userdel binary: %s", err)
} }
u, err := user.Lookup(username) u, err := hostuser.Lookup(username)
if err != nil { if err != nil {
return -1, trace.Wrap(err) return -1, trace.Wrap(err)
} }
@@ -390,7 +392,7 @@ func GetHostUserCredential(localUser *user.User) (*syscall.Credential, error) {
} }
// Lookup supplementary groups for the user. // Lookup supplementary groups for the user.
userGroups, err := localUser.GroupIds() userGroups, err := hostuser.GroupIds(localUser)
if err != nil { if err != nil {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
+22
View File
@@ -0,0 +1,22 @@
// Teleport
// Copyright (C) 2026 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package user
import "errors"
// ErrUnsupportedPlatform is returned when the operating system is not supported.
var ErrUnsupportedPlatform = errors.New("unsupported platform")
+42
View File
@@ -0,0 +1,42 @@
// Teleport
// Copyright (C) 2026 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// Package user provides a thin concurrency safe wrapper around the
// `os/user` APIs for Linux when CGO is enabled.
//
// On Linux, the `os/user` package may call into C-based NSS backends via cgo.
// Some NSS implementations (or configurations) are not safe for concurrent
// use and can deadlock or crash when lookups are performed concurrently.
//
// To avoid such failures, this package serializes calls to a small set of `os/user`
// APIs on linux+cgo builds.
// Non-Linux or non-cgo builds forward calls to the stdlib without locking.
//
// References:
//
// - Debian bug report: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=831390
// - Teleport issue: https://github.com/gravitational/teleport/issues/69662
//
// Note that this package is placed in `session/host` specifically because the `session` tree
// is effectively a submodule within Teleport which guards against any imports from Teleport itself.
// This was done to optimize the session helper load times and avoid bleeding runtime dependencies into the
// session helper which would incease the load time and degrade latency. Since Teleport still calls
// into the session helper code in the same process for which a mutex must be shared, the user package
// is placed here despite less than ideal code organization.
package user
// TODO(okraport): revisit this module placement and investigate moving it to lib/utils/user if possible.
// TODO(okraport): revisit this module to consider replacement with getent process shell out to avoid the mutex.
@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package authorizedkeys package user
/* /*
#include <pwd.h> #include <pwd.h>
@@ -31,10 +31,10 @@ import (
"github.com/gravitational/trace" "github.com/gravitational/trace"
) )
// getHostUsers returns the list of all users on the host from the user // GetHostUsers returns the list of all users on the host from the user
// directory (depending on system configuration this can be /etc/passwd, // directory (depending on system configuration this can be /etc/passwd,
// LDAP...). // LDAP...).
func getHostUsers() ([]user.User, error) { func GetHostUsers() ([]user.User, error) {
// on darwin the setpwent/getpwent/endpwent functions use thread-local // on darwin the setpwent/getpwent/endpwent functions use thread-local
// storage so there's no need for a global lock but we must call the whole // storage so there's no need for a global lock but we must call the whole
// sequence from the same thread // sequence from the same thread
+48
View File
@@ -0,0 +1,48 @@
//go:build !linux || !cgo
// Teleport
// Copyright (C) 2026 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package user
import (
osu "os/user"
"github.com/gravitational/trace"
)
// Lookup wraps [os/user.Lookup].
func Lookup(username string) (*osu.User, error) { return osu.Lookup(username) }
// LookupId wraps [os/user.LookupId].
func LookupId(id string) (*osu.User, error) { return osu.LookupId(id) }
// LookupGroup wraps [os/user.LookupGroup].
func LookupGroup(name string) (*osu.Group, error) { return osu.LookupGroup(name) }
// LookupGroupId wraps [os/user.LookupGroupId].
func LookupGroupId(id string) (*osu.Group, error) { return osu.LookupGroupId(id) }
// Current wraps [os/user.Current].
func Current() (*osu.User, error) { return osu.Current() }
// GroupIds wraps [os/user.User.GroupIds].
func GroupIds(u *osu.User) ([]string, error) {
if u == nil {
return nil, trace.BadParameter("user cannot be nil")
}
return u.GroupIds()
}
+120
View File
@@ -0,0 +1,120 @@
//go:build linux && cgo
// Teleport
// Copyright (C) 2026 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package user
/*
#define _XOPEN_SOURCE 500
#include <pwd.h>
*/
import "C"
import (
osuser "os/user"
"strconv"
"sync"
"github.com/gravitational/trace"
)
var packageMU sync.Mutex
// Lookup wraps [os/user.Lookup] with process-wide serialization.
func Lookup(username string) (*osuser.User, error) {
packageMU.Lock()
defer packageMU.Unlock()
return osuser.Lookup(username)
}
// LookupId wraps [os/user.LookupId] with process-wide serialization.
func LookupId(id string) (*osuser.User, error) {
packageMU.Lock()
defer packageMU.Unlock()
return osuser.LookupId(id)
}
// LookupGroup wraps [os/user.LookupGroup] with process-wide serialization.
func LookupGroup(name string) (*osuser.Group, error) {
packageMU.Lock()
defer packageMU.Unlock()
return osuser.LookupGroup(name)
}
// LookupGroupId wraps [os/user.LookupGroupId] with process-wide serialization.
func LookupGroupId(id string) (*osuser.Group, error) {
packageMU.Lock()
defer packageMU.Unlock()
return osuser.LookupGroupId(id)
}
// Current wraps [os/user.Current] with process-wide serialization.
func Current() (*osuser.User, error) {
packageMU.Lock()
defer packageMU.Unlock()
return osuser.Current()
}
// LookGroupIdsup wraps [os/user.User.GroupIds] with process-wide serialization.
func GroupIds(u *osuser.User) ([]string, error) {
if u == nil {
return nil, trace.BadParameter("user cannot be nil")
}
packageMU.Lock()
defer packageMU.Unlock()
return u.GroupIds()
}
// GetHostUsers returns the list of all users on the host from the user
// directory (depending on system configuration this can be /etc/passwd,
// LDAP...).
func GetHostUsers() ([]osuser.User, error) {
// A lock should be acquired when using MT-Unsafe race:pwent functions (i.e.
// setpwent/getpwent/endpwent). Since `getpwent` can follow the same path
// into libnss we take the package lock.
packageMU.Lock()
defer packageMU.Unlock()
C.setpwent()
defer C.endpwent()
var results []osuser.User
for {
result, err := C.getpwent()
// cgo error convention, check the return value before errno
if result != nil {
results = append(results, passwdC2Go(result))
continue
}
if err != nil {
return nil, trace.Wrap(err)
}
return results, nil
}
}
// passwdC2Go converts `passwd` struct from C to golang native struct
func passwdC2Go(passwdC *C.struct_passwd) osuser.User {
name := C.GoString(passwdC.pw_name)
return osuser.User{
Name: name,
Username: name,
Uid: strconv.FormatUint(uint64(passwdC.pw_uid), 10),
Gid: strconv.FormatUint(uint64(passwdC.pw_gid), 10),
HomeDir: C.GoString(passwdC.pw_dir),
}
}
@@ -16,7 +16,7 @@
//go:build !darwin && !linux //go:build !darwin && !linux
package authorizedkeys package user
import ( import (
"os/user" "os/user"
@@ -29,7 +29,7 @@ var alwaysFalse bool
// getHostUsers returns ErrUnsupportedPlatform because this platform is not // getHostUsers returns ErrUnsupportedPlatform because this platform is not
// supported. On supported platforms, it returns the list of all users on the // supported. On supported platforms, it returns the list of all users on the
// host from the user directory. // host from the user directory.
func getHostUsers() ([]user.User, error) { func GetHostUsers() ([]user.User, error) {
if alwaysFalse { if alwaysFalse {
// thwart the well-meaning intentions of staticcheck // thwart the well-meaning intentions of staticcheck
return nil, nil return nil, nil
+9 -8
View File
@@ -48,6 +48,7 @@ import (
"github.com/gravitational/teleport/session/auditd" "github.com/gravitational/teleport/session/auditd"
"github.com/gravitational/teleport/session/envutils" "github.com/gravitational/teleport/session/envutils"
"github.com/gravitational/teleport/session/host" "github.com/gravitational/teleport/session/host"
hostuser "github.com/gravitational/teleport/session/host/user"
"github.com/gravitational/teleport/session/logconstants" "github.com/gravitational/teleport/session/logconstants"
"github.com/gravitational/teleport/session/loginuid" "github.com/gravitational/teleport/session/loginuid"
"github.com/gravitational/teleport/session/networking" "github.com/gravitational/teleport/session/networking"
@@ -468,7 +469,7 @@ func RunCommand() (exitErr error, err error) {
WtmpdbFile: c.UaccMetadata.WtmpdbPath, WtmpdbFile: c.UaccMetadata.WtmpdbPath,
}) })
localUser, err := user.Lookup(c.Login) localUser, err := hostuser.Lookup(c.Login)
if err != nil { if err != nil {
if uaccErr := uaccHandler.FailedLogin(c.Login, &c.UaccMetadata.RemoteAddr); uaccErr != nil { if uaccErr := uaccHandler.FailedLogin(c.Login, &c.UaccMetadata.RemoteAddr); uaccErr != nil {
slog.DebugContext(ctx, "unable to write failed login attempt to uacc", "error", uaccErr) slog.DebugContext(ctx, "unable to write failed login attempt to uacc", "error", uaccErr)
@@ -671,8 +672,8 @@ type osWrapper struct {
func newOsWrapper() *osWrapper { func newOsWrapper() *osWrapper {
return &osWrapper{ return &osWrapper{
LookupGroup: user.LookupGroup, LookupGroup: hostuser.LookupGroup,
LookupUser: user.Lookup, LookupUser: hostuser.Lookup,
CommandContext: exec.CommandContext, CommandContext: exec.CommandContext,
} }
} }
@@ -698,7 +699,7 @@ func (s *systemUser) UID() string {
} }
func (s *systemUser) GroupIds() ([]string, error) { func (s *systemUser) GroupIds() ([]string, error) {
return s.u.GroupIds() return hostuser.GroupIds(s.u)
} }
// startNewParker starts a new parker process only if the requested user has been created // startNewParker starts a new parker process only if the requested user has been created
@@ -810,7 +811,7 @@ func RunNetworking() (code int, err error) {
// Once the PAM stack is called with parent process permissions, set the process uid // Once the PAM stack is called with parent process permissions, set the process uid
// and gid to the requested user. This way, the user's networking requests will be // and gid to the requested user. This way, the user's networking requests will be
// done with the user's permissions. // done with the user's permissions.
localUser, err := user.Lookup(c.Login) localUser, err := hostuser.Lookup(c.Login)
if err != nil { if err != nil {
return reexecconstants.RemoteCommandFailure, trace.NotFound("%s", err) return reexecconstants.RemoteCommandFailure, trace.NotFound("%s", err)
} }
@@ -1231,7 +1232,7 @@ func openFileAsUser(localUser *user.User, path string) (file *os.File, err error
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
strIDs, err := localUser.GroupIds() strIDs, err := hostuser.GroupIds(localUser)
if err != nil { if err != nil {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
@@ -1480,7 +1481,7 @@ var accessibleHomeDirMu sync.Mutex
// hasAccessibleHomeDir checks if the current user has access to an existing home directory. // hasAccessibleHomeDir checks if the current user has access to an existing home directory.
func hasAccessibleHomeDir() error { func hasAccessibleHomeDir() error {
// this should usually be fetching a cached value // this should usually be fetching a cached value
currentUser, err := user.Current() currentUser, err := hostuser.Current()
if err != nil { if err != nil {
return trace.Wrap(err) return trace.Wrap(err)
} }
@@ -1522,7 +1523,7 @@ func hasAccessibleHomeDir() error {
// errors will be returned, which means a missing, inaccessible, or otherwise invalid home directory will result // errors will be returned, which means a missing, inaccessible, or otherwise invalid home directory will result
// in a return of (false, nil) // in a return of (false, nil)
func checkHomeDir(localUser *user.User) (bool, error) { func checkHomeDir(localUser *user.User) (bool, error) {
currentUser, err := user.Current() currentUser, err := hostuser.Current()
if err != nil { if err != nil {
return false, trace.Wrap(err) return false, trace.Wrap(err)
} }
+2 -2
View File
@@ -26,7 +26,6 @@ import (
"io" "io"
"log/slog" "log/slog"
"os" "os"
"os/user"
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -38,6 +37,7 @@ import (
"github.com/pkg/sftp" "github.com/pkg/sftp"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
hostuser "github.com/gravitational/teleport/session/host/user"
"github.com/gravitational/teleport/session/reexec/safefile" "github.com/gravitational/teleport/session/reexec/safefile"
"github.com/gravitational/teleport/session/sftputils" "github.com/gravitational/teleport/session/sftputils"
) )
@@ -305,7 +305,7 @@ func RunSFTP(logger *slog.Logger) error {
} }
defer auditFile.Close() defer auditFile.Close()
currentUser, err := user.Current() currentUser, err := hostuser.Current()
if err != nil { if err != nil {
return trace.Wrap(err) return trace.Wrap(err)
} }
+3 -2
View File
@@ -37,18 +37,19 @@ import "C"
import ( import (
"context" "context"
"log/slog" "log/slog"
"os/user"
"strings" "strings"
"syscall" "syscall"
"unsafe" "unsafe"
"github.com/gravitational/trace" "github.com/gravitational/trace"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
// getLoginShell determines the login shell for a given username // getLoginShell determines the login shell for a given username
func getLoginShell(username string) (string, error) { func getLoginShell(username string) (string, error) {
// See if the username is valid. // See if the username is valid.
_, err := user.Lookup(username) _, err := hostuser.Lookup(username)
if err != nil { if err != nil {
return "", trace.Wrap(err) return "", trace.Wrap(err)
} }
+9 -8
View File
@@ -37,6 +37,7 @@ import (
"github.com/gravitational/teleport/lib/tbot/cli" "github.com/gravitational/teleport/lib/tbot/cli"
"github.com/gravitational/teleport/lib/tbot/config" "github.com/gravitational/teleport/lib/tbot/config"
"github.com/gravitational/teleport/lib/tbot/identity" "github.com/gravitational/teleport/lib/tbot/identity"
hostuser "github.com/gravitational/teleport/session/host/user"
) )
// RootUID is the UID of the root user // RootUID is the UID of the root user
@@ -207,7 +208,7 @@ func ensurePermissions(
return trace.BadParameter("File %s is expected to be a file but is a directory", path) return trace.BadParameter("File %s is expected to be a file but is a directory", path)
} }
currentUser, err := user.Current() currentUser, err := hostuser.Current()
if err != nil { if err != nil {
return trace.Wrap(err) return trace.Wrap(err)
} }
@@ -319,12 +320,12 @@ func parseOwnerString(owner string) (*user.User, *user.Group, error) {
return nil, nil, trace.BadParameter("invalid owner string: %q", owner) return nil, nil, trace.BadParameter("invalid owner string: %q", owner)
} }
ownerUser, err := user.Lookup(ownerParts[0]) ownerUser, err := hostuser.Lookup(ownerParts[0])
if err != nil { if err != nil {
return nil, nil, trace.Wrap(err) return nil, nil, trace.Wrap(err)
} }
ownerGroup, err := user.LookupGroup(ownerParts[1]) ownerGroup, err := hostuser.LookupGroup(ownerParts[1])
if err != nil { if err != nil {
return nil, nil, trace.Wrap(err) return nil, nil, trace.Wrap(err)
} }
@@ -358,12 +359,12 @@ func getOwner(cliOwner, defaultOwner string) (*user.User, *user.Group, error) {
log.DebugContext(context.TODO(), "Will use current user as owner") log.DebugContext(context.TODO(), "Will use current user as owner")
// Otherwise, return the current user and group // Otherwise, return the current user and group
currentUser, err := user.Current() currentUser, err := hostuser.Current()
if err != nil { if err != nil {
return nil, nil, trace.Wrap(err) return nil, nil, trace.Wrap(err)
} }
currentGroup, err := user.LookupGroupId(currentUser.Gid) currentGroup, err := hostuser.LookupGroupId(currentUser.Gid)
if err != nil { if err != nil {
return nil, nil, trace.Wrap(err) return nil, nil, trace.Wrap(err)
} }
@@ -383,17 +384,17 @@ func getAndTestACLOptions(initCmd *cli.InitCommand, destDir string) (*user.User,
return nil, nil, nil, trace.BadParameter("--reader-user must be set") return nil, nil, nil, trace.BadParameter("--reader-user must be set")
} }
botUser, err := user.Lookup(initCmd.BotUser) botUser, err := hostuser.Lookup(initCmd.BotUser)
if err != nil { if err != nil {
return nil, nil, nil, trace.Wrap(err) return nil, nil, nil, trace.Wrap(err)
} }
botGroup, err := user.LookupGroupId(botUser.Gid) botGroup, err := hostuser.LookupGroupId(botUser.Gid)
if err != nil { if err != nil {
return nil, nil, nil, trace.Wrap(err) return nil, nil, nil, trace.Wrap(err)
} }
readerUser, err := user.Lookup(initCmd.ReaderUser) readerUser, err := hostuser.Lookup(initCmd.ReaderUser)
if err != nil { if err != nil {
return nil, nil, nil, trace.Wrap(err) return nil, nil, nil, trace.Wrap(err)
} }
+2 -2
View File
@@ -27,7 +27,6 @@ import (
"maps" "maps"
"net/url" "net/url"
"os" "os"
"os/user"
"path" "path"
"path/filepath" "path/filepath"
"runtime" "runtime"
@@ -61,6 +60,7 @@ import (
"github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils"
logutils "github.com/gravitational/teleport/lib/utils/log" logutils "github.com/gravitational/teleport/lib/utils/log"
"github.com/gravitational/teleport/lib/versioncontrol" "github.com/gravitational/teleport/lib/versioncontrol"
hostuser "github.com/gravitational/teleport/session/host/user"
"github.com/gravitational/teleport/session/reexec" "github.com/gravitational/teleport/session/reexec"
"github.com/gravitational/teleport/session/reexec/reexecconstants" "github.com/gravitational/teleport/session/reexec/reexecconstants"
"github.com/gravitational/teleport/session/selinux" "github.com/gravitational/teleport/session/selinux"
@@ -1230,7 +1230,7 @@ func onSCP(scpFlags *scp.Flags) error {
} }
// get user's home dir (it serves as a default destination) // get user's home dir (it serves as a default destination)
user, err := user.Current() user, err := hostuser.Current()
if err != nil { if err != nil {
return trace.Wrap(err) return trace.Wrap(err)
} }
+2 -2
View File
@@ -33,7 +33,6 @@ import (
"os" "os"
"os/exec" "os/exec"
"os/signal" "os/signal"
"os/user"
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime" "runtime"
@@ -106,6 +105,7 @@ import (
"github.com/gravitational/teleport/lib/utils/mlock" "github.com/gravitational/teleport/lib/utils/mlock"
"github.com/gravitational/teleport/lib/utils/parse" "github.com/gravitational/teleport/lib/utils/parse"
stacksignal "github.com/gravitational/teleport/lib/utils/signal" stacksignal "github.com/gravitational/teleport/lib/utils/signal"
hostuser "github.com/gravitational/teleport/session/host/user"
"github.com/gravitational/teleport/session/networking/x11" "github.com/gravitational/teleport/session/networking/x11"
"github.com/gravitational/teleport/session/shell" "github.com/gravitational/teleport/session/shell"
"github.com/gravitational/teleport/tool/common" "github.com/gravitational/teleport/tool/common"
@@ -4453,7 +4453,7 @@ func onSSHLatency(cf *CLIConf) error {
func runLocalCommand(hostLogin string, command []string) error { func runLocalCommand(hostLogin string, command []string) error {
if len(command) == 0 { if len(command) == 0 {
if hostLogin == "" { if hostLogin == "" {
user, err := user.Current() user, err := hostuser.Current()
if err != nil { if err != nil {
return trace.Wrap(err) return trace.Wrap(err)
} }