69 lines
2.3 KiB
Go
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
|
|
}
|