-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
client: protect user lookups with global lock
This PR updates Nomad client to always do user lookups while holding a global process lock. This is to prevent concurrency unsafe implementations of NSS, but still enabling NSS lookups of users (i.e. cannot not use osusergo).
- Loading branch information
Showing
7 changed files
with
101 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package users | ||
|
||
import ( | ||
"fmt" | ||
"os/user" | ||
"sync" | ||
) | ||
|
||
// lock is used to serialize all user lookup at the process level, because | ||
// some NSS implementations are not concurrency safe | ||
var lock *sync.Mutex | ||
|
||
// nobody is a cached copy of the nobody user, which is going to be looked-up | ||
// frequently and is unlikely to be modified on the underlying system. | ||
var nobody user.User | ||
|
||
// Nobody returns User data for the "nobody" user on the system, bypassing the | ||
// locking / file read / NSS lookup. | ||
func Nobody() user.User { | ||
// original is immutable via copy by value | ||
return nobody | ||
} | ||
|
||
func init() { | ||
lock = new(sync.Mutex) | ||
u, err := Lookup("nobody") | ||
if err != nil { | ||
panic(fmt.Sprintf("unable to lookup the nobody user: %v", err)) | ||
} | ||
nobody = *u | ||
} | ||
|
||
// Lookup username while holding a global process lock. | ||
func Lookup(username string) (*user.User, error) { | ||
lock.Lock() | ||
defer lock.Unlock() | ||
return user.Lookup(username) | ||
} | ||
|
||
// LookupGroupId while holding a global process lock. | ||
func LookupGroupId(gid string) (*user.Group, error) { | ||
lock.Lock() | ||
defer lock.Unlock() | ||
return user.LookupGroupId(gid) | ||
} | ||
|
||
// Current returns the current user, acquired while holding a global process | ||
// lock. | ||
func Current() (*user.User, error) { | ||
lock.Lock() | ||
defer lock.Unlock() | ||
return user.Current() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
//go:build linux | ||
|
||
package users | ||
|
||
import ( | ||
"errors" | ||
"os/user" | ||
"testing" | ||
|
||
"github.com/shoenig/test/must" | ||
) | ||
|
||
func TestLookup(t *testing.T) { | ||
cases := []struct { | ||
username string | ||
|
||
expErr error | ||
expUser *user.User | ||
}{ | ||
{username: "nobody", expUser: &user.User{Username: "nobody", Uid: "65534", Gid: "65534", Name: "nobody", HomeDir: "/nonexistent"}}, // ubuntu | ||
{username: "root", expUser: &user.User{Username: "root", Uid: "0", Gid: "0", Name: "root", HomeDir: "/root"}}, | ||
{username: "doesnotexist", expErr: errors.New("user: unknown user doesnotexist")}, | ||
} | ||
|
||
for _, tc := range cases { | ||
t.Run(tc.username, func(t *testing.T) { | ||
u, err := Lookup(tc.username) | ||
if tc.expErr != nil { | ||
must.EqError(t, tc.expErr, err.Error()) | ||
} else { | ||
must.Eq(t, tc.expUser, u) | ||
} | ||
}) | ||
} | ||
} |