Files
mesh-tools/node-tools/internal/launch/runas.go
T

69 lines
2.3 KiB
Go

package launch
import (
"fmt"
"os/exec"
"os/user"
"strconv"
"strings"
"syscall"
)
// RunAs is the word in a bundle's environment naming the account it runs as (novox/hq ADR 0259 §8), and
// OperatorAccount the word naming the operator's account on this machine, which the runtime is given.
const (
RunAs = "MESH_RUN_AS"
OperatorAccount = "MESH_OPERATOR_ACCOUNT"
)
// lookupUser is user.Lookup, a variable so a test can name accounts this machine does not have.
var lookupUser = user.Lookup
func word(env []string, name string) string {
for _, kv := range env {
if k, v, ok := strings.Cut(kv, "="); ok && k == name {
return v
}
}
return ""
}
// runAs starts cmd as the account its environment names, with that account's home, or leaves it as the
// runtime's own when none is named. It refuses root and the operator's account: a module asks for an
// account of its own to keep what it holds from the agents, and every agent runs as the operator.
func runAs(cmd *exec.Cmd, env []string) error {
name := word(env, RunAs)
if name == "" {
return nil
}
if operator := word(env, OperatorAccount); operator != "" && name == operator {
return fmt.Errorf("%s names the operator's account %s; a module runs as an account of its own, never "+
"the one every agent runs as (novox/hq ADR 0259)", RunAs, name)
}
u, err := lookupUser(name)
if err != nil {
return fmt.Errorf("%s names the account %s, which this machine does not have: the module makes it with "+
"a user resource (novox/hq ADR 0259): %w", RunAs, name, err)
}
uid, err := strconv.ParseUint(u.Uid, 10, 32)
if err != nil {
return fmt.Errorf("the account %s has no usable uid %q", name, u.Uid)
}
gid, err := strconv.ParseUint(u.Gid, 10, 32)
if err != nil {
return fmt.Errorf("the account %s has no usable gid %q", name, u.Gid)
}
if uid == 0 || name == "root" {
return fmt.Errorf("%s names root; a module that asks for an account of its own is not given root", RunAs)
}
cmd.SysProcAttr = &syscall.SysProcAttr{Credential: &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}}
var kept []string
for _, kv := range cmd.Env {
if !strings.HasPrefix(kv, "HOME=") && !strings.HasPrefix(kv, "USER=") && !strings.HasPrefix(kv, "LOGNAME=") {
kept = append(kept, kv)
}
}
cmd.Env = append(kept, "HOME="+u.HomeDir, "USER="+name, "LOGNAME="+name)
return nil
}