open-nomad/client/allocdir/fs_unix.go
Seth Hoenig ba1e337f8b
helpers: lockfree lookup of nobody user on unix systems (#14866)
* helpers: lockfree lookup of nobody user on linux and darwin

This PR continues the nobody user lookup saga, by making the nobody
user lookup lock-free on linux and darwin.

By doing the lookup in an init block this originally broke on Windows,
where we must avoid doing the lookup at all. We can get around that
breakage by only doing the lookup on linux/darwin where the nobody
user is going to exist.

Also return the nobody user by value so that a copy is created that
cannot be modified by callers of Nobody().

* helper: move nobody code into unix file
2022-10-11 08:38:05 -05:00

106 lines
2.4 KiB
Go

//go:build unix
package allocdir
import (
"fmt"
"os"
"os/user"
"path/filepath"
"strconv"
"syscall"
"github.com/hashicorp/nomad/helper/users"
"golang.org/x/sys/unix"
)
var (
// SharedAllocContainerPath is the path inside container for mounted
// directory shared across tasks in a task group.
SharedAllocContainerPath = filepath.Join("/", SharedAllocName)
// TaskLocalContainerPath is the path inside a container for mounted directory
// for local storage.
TaskLocalContainerPath = filepath.Join("/", TaskLocal)
// TaskSecretsContainerPath is the path inside a container for mounted
// secrets directory
TaskSecretsContainerPath = filepath.Join("/", TaskSecrets)
)
// dropDirPermissions gives full access to a directory to all users and sets
// the owner to nobody.
func dropDirPermissions(path string, desired os.FileMode) error {
if err := os.Chmod(path, desired|0777); err != nil {
return fmt.Errorf("Chmod(%v) failed: %v", path, err)
}
// Can't change owner if not root.
if unix.Geteuid() != 0 {
return nil
}
nobody := users.Nobody()
uid, err := getUid(&nobody)
if err != nil {
return err
}
gid, err := getGid(&nobody)
if err != nil {
return err
}
if err := os.Chown(path, uid, gid); err != nil {
return fmt.Errorf("Couldn't change owner/group of %v to (uid: %v, gid: %v): %v", path, uid, gid, err)
}
return nil
}
// getUid for a user
func getUid(u *user.User) (int, error) {
uid, err := strconv.Atoi(u.Uid)
if err != nil {
return 0, fmt.Errorf("Unable to convert Uid to an int: %v", err)
}
return uid, nil
}
// getGid for a user
func getGid(u *user.User) (int, error) {
gid, err := strconv.Atoi(u.Gid)
if err != nil {
return 0, fmt.Errorf("Unable to convert Gid to an int: %v", err)
}
return gid, nil
}
// linkOrCopy attempts to hardlink dst to src and fallsback to copying if the
// hardlink fails.
func linkOrCopy(src, dst string, uid, gid int, perm os.FileMode) error {
// Avoid link/copy if the file already exists in the chroot
// TODO 0.6 clean this up. This was needed because chroot creation fails
// when a process restarts.
if fileInfo, _ := os.Stat(dst); fileInfo != nil {
return nil
}
// Attempt to hardlink.
if err := os.Link(src, dst); err == nil {
return nil
}
return fileCopy(src, dst, uid, gid, perm)
}
func getOwner(fi os.FileInfo) (int, int) {
stat, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return -1, -1
}
return int(stat.Uid), int(stat.Gid)
}