mesh-cli: follow a line to its answer, bound each account, refuse other namespaces (review of hq ADR 0272)
- A request that timed out was said as "the controller was not reached, nothing ran", though the line may have run. The engine now follows a line the controller answers as running, by its call, until it ends; when no answer comes in time it says the line may have run and names the call. "Nothing ran" is said only of a request no controller took. - One account could hold the socket: accepting waited for a free slot, and a connection could stall before its deadline was set. The deadline is set at accept, before any lookup; each user id has two lines at once and the machine eight, and one over is answered busy at once. - A container running with the operator's user id shares the number, not the machine: a peer whose mount or user namespace is not the engine's is refused, and nothing is asked. SDK pinned at the go/cli branch's newest commit until go/v0.1.9 is tagged.
This commit is contained in:
@@ -12,25 +12,44 @@ import (
|
||||
|
||||
// peerCredentials is the user id of the process at the other end, as the kernel recorded it when it connected
|
||||
// (SO_PEERCRED). Nothing the process says is read.
|
||||
func peerCredentials(conn *net.UnixConn) (uint32, error) {
|
||||
func peerCredentials(conn *net.UnixConn) (uint32, int32, error) {
|
||||
raw, err := conn.SyscallConn()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, 0, err
|
||||
}
|
||||
var cred *syscall.Ucred
|
||||
var inner error
|
||||
if err := raw.Control(func(fd uintptr) {
|
||||
cred, inner = syscall.GetsockoptUcred(int(fd), syscall.SOL_SOCKET, syscall.SO_PEERCRED)
|
||||
}); err != nil {
|
||||
return 0, err
|
||||
return 0, 0, err
|
||||
}
|
||||
if inner != nil {
|
||||
return 0, inner
|
||||
return 0, 0, inner
|
||||
}
|
||||
if cred == nil {
|
||||
return 0, errors.New("the kernel gave no credentials")
|
||||
return 0, 0, errors.New("the kernel gave no credentials")
|
||||
}
|
||||
return cred.Uid, nil
|
||||
return cred.Uid, cred.Pid, nil
|
||||
}
|
||||
|
||||
// namespacesOf refuses a process whose mount or user namespace is not this one's, read from the kernel's own
|
||||
// names for them. One that cannot be read is refused, never assumed the same.
|
||||
func namespacesOf(pid int32) error {
|
||||
for _, ns := range []string{"mnt", "user"} {
|
||||
mine, err := os.Readlink("/proc/self/ns/" + ns)
|
||||
if err != nil {
|
||||
return fmt.Errorf("the engine's own %s namespace cannot be read: %w", ns, err)
|
||||
}
|
||||
theirs, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/%s", pid, ns))
|
||||
if err != nil {
|
||||
return fmt.Errorf("process %d's %s namespace cannot be read: %w", pid, ns, err)
|
||||
}
|
||||
if theirs != mine {
|
||||
return fmt.Errorf("process %d is in %s namespace %s, the engine in %s", pid, ns, theirs, mine)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// notOwnDirectory says what is wrong with the socket's directory, or nothing: it must be a directory, not a
|
||||
|
||||
Reference in New Issue
Block a user