Add a new RPC ConnectWithCreds to allow gofer to connect to a unix domain socket with application's credentials

This commit is contained in:
xianzhe-databricks
2025-01-03 17:50:06 +01:00
parent bd0cbf8071
commit c4f686f4e1
12 changed files with 263 additions and 53 deletions
+28 -3
View File
@@ -53,6 +53,11 @@ var caps = []string{
"CAP_SYS_CHROOT",
}
var udsOpenCaps = []string{
"CAP_SETUID",
"CAP_SETGID",
}
// goferCaps is the minimal set of capabilities needed by the Gofer to operate
// on files.
var goferCaps = &specs.LinuxCapabilities{
@@ -61,6 +66,12 @@ var goferCaps = &specs.LinuxCapabilities{
Permitted: caps,
}
var goferUdsOpenCaps = &specs.LinuxCapabilities{
Bounding: udsOpenCaps,
Effective: udsOpenCaps,
Permitted: udsOpenCaps,
}
// goferSyncFDs contains file descriptors that are used for synchronization
// of the Gofer startup process against other processes.
type goferSyncFDs struct {
@@ -180,7 +191,11 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomm
overrides["apply-caps"] = "false"
overrides["setup-root"] = "false"
args := prepareArgs(g.Name(), f, overrides)
util.Fatalf("setCapsAndCallSelf(%v, %v): %v", args, goferCaps, setCapsAndCallSelf(args, goferCaps))
capsToApply := goferCaps
if conf.GetHostUDS().AllowOpen() {
capsToApply = specutils.MergeCapabilities(capsToApply, goferUdsOpenCaps)
}
util.Fatalf("setCapsAndCallSelf(%v, %v): %v", args, capsToApply, setCapsAndCallSelf(args, capsToApply))
panic("unreachable")
}
@@ -252,6 +267,12 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomm
}
log.Infof("Process chroot'd to %q", root)
ruid := unix.Getuid()
euid := unix.Geteuid()
rgid := unix.Getgid()
egid := unix.Getegid()
log.Debugf("Process running as uid=%d euid=%d gid=%d egid=%d", ruid, euid, rgid, egid)
// Initialize filters.
opts := filter.Options{
UDSOpenEnabled: conf.GetHostUDS().AllowOpen(),
@@ -264,7 +285,7 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomm
util.Fatalf("installing seccomp filters: %v", err)
}
return g.serve(spec, conf, root)
return g.serve(spec, conf, root, ruid, euid, rgid, egid)
}
func newSocket(ioFD int) *unet.Socket {
@@ -275,7 +296,7 @@ func newSocket(ioFD int) *unet.Socket {
return socket
}
func (g *Gofer) serve(spec *specs.Spec, conf *config.Config, root string) subcommands.ExitStatus {
func (g *Gofer) serve(spec *specs.Spec, conf *config.Config, root string, ruid int, euid int, rgid int, egid int) subcommands.ExitStatus {
type connectionConfig struct {
sock *unet.Socket
mountPath string
@@ -288,6 +309,10 @@ func (g *Gofer) serve(spec *specs.Spec, conf *config.Config, root string) subcom
HostUDS: conf.GetHostUDS(),
HostFifo: conf.HostFifo,
DonateMountPointFD: conf.DirectFS,
RUID: ruid,
EUID: euid,
RGID: rgid,
EGID: egid,
})
ioFDs := g.ioFDs
+3 -1
View File
@@ -208,7 +208,9 @@ var udsCommonSyscalls = seccomp.MakeSyscallRules(map[uintptr]seccomp.SyscallRule
})
var udsOpenSyscalls = seccomp.MakeSyscallRules(map[uintptr]seccomp.SyscallRule{
unix.SYS_CONNECT: seccomp.MatchAll{},
unix.SYS_CONNECT: seccomp.MatchAll{},
unix.SYS_SETREUID: seccomp.MatchAll{},
unix.SYS_SETREGID: seccomp.MatchAll{},
})
var udsCreateSyscalls = seccomp.MakeSyscallRules(map[uintptr]seccomp.SyscallRule{
+60
View File
@@ -17,14 +17,17 @@
package fsgofer
import (
"errors"
"fmt"
"io"
"math"
"os"
"path"
"path/filepath"
"runtime"
"strconv"
"sync"
"syscall"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
@@ -64,6 +67,18 @@ type Config struct {
// DonateMountPointFD indicates whether a host FD to the mount point should
// be donated to the client on Mount RPC.
DonateMountPointFD bool
// Gofer process's RUID.
RUID int
// Gofer process's EUID.
EUID int
// Gofer process's RGID.
RGID int
// Gofer process's EGID.
EGID int
}
var procSelfFD *rwfd.FD
@@ -178,6 +193,7 @@ func (s *LisafsServer) SupportedMessages() []lisafs.MID {
lisafs.BindAt,
lisafs.Listen,
lisafs.Accept,
lisafs.ConnectWithCreds,
}
}
@@ -823,6 +839,50 @@ func (fd *controlFDLisa) Connect(sockType uint32) (int, error) {
return sock, nil
}
// ConnectWithCreds implements lisafs.ControlFDImpl.ConnectWithCreds.
func (fd *controlFDLisa) ConnectWithCreds(sockType uint32, uid lisafs.UID, gid lisafs.GID) (int, error) {
serverConfig := fd.Conn().ServerImpl().(*LisafsServer).config
if !serverConfig.HostUDS.AllowOpen() {
logRejectedUdsConnectOnce.Do(func() {
log.Warningf("Rejecting attempt to connect to unix domain socket from host filesystem: %q. If you want to allow this, set flag --host-uds=open", fd.ControlFD.Node().FilePath())
})
return -1, unix.EPERM
}
runtime.LockOSThread()
defer runtime.UnlockOSThread()
_, _, err := unix.Syscall(unix.SYS_SETREGID, uintptr(serverConfig.RGID), uintptr(gid), 0)
if !errors.Is(err, syscall.Errno(0)) {
log.Warningf("Failed to set egid; err: %v", err)
} else {
log.Debugf("Successfully set egid to %d", gid)
}
_, _, err = unix.Syscall(unix.SYS_SETREUID, uintptr(serverConfig.RUID), uintptr(uid), 0)
if !errors.Is(err, syscall.Errno(0)) {
log.Warningf("Failed to set euid; err: %v", err)
} else {
log.Debugf("Successfully set euid to %d", uid)
}
defer func() {
_, _, err := unix.Syscall(unix.SYS_SETREUID, uintptr(serverConfig.RUID), uintptr(serverConfig.EUID), 0)
if !errors.Is(err, unix.Errno(0)) {
log.Warningf("Failed to restore euid; err: %v", err)
} else {
log.Debugf("Successfully restored euid to %d", serverConfig.EUID)
}
_, _, err = unix.Syscall(unix.SYS_SETREGID, uintptr(serverConfig.RGID), uintptr(serverConfig.EGID), 0)
if !errors.Is(err, unix.Errno(0)) {
log.Warningf("Failed to restore egid; err: %v", err)
} else {
log.Debugf("Successfully restored egid to %d", serverConfig.EGID)
}
}()
return fd.Connect(sockType)
}
// BindAt implements lisafs.ControlFDImpl.BindAt.
func (fd *controlFDLisa) BindAt(name string, sockType uint32, mode linux.FileMode, uid lisafs.UID, gid lisafs.GID) (*lisafs.ControlFD, linux.Statx, *lisafs.BoundSocketFD, int, error) {
if !fd.Conn().ServerImpl().(*LisafsServer).config.HostUDS.AllowCreate() {