Files
mesh-host/internal/system/identities.go
T
jschoubben 2a2be687f3 Keep libraries under groups of their own, and give modules users in them (hq ADR 0328, issue 567)
ADR 0328 supersedes ADR 0325: shared data is a library with one home, kept usable by
its group rather than by ACL entries. The engine now makes each library's group and
each module's user in its libraries' groups; keeps a library's root and requested
sub-paths in its group with group write and setgid on its home, making missing
sub-paths as the root is; waits for the operator's word before touching a root that
holds data, and on the take act walks it by descriptor (no link, same filesystem,
bounded and resumed, files with other names left); holds the containers of a library
whose disk or mount is not there; and puts the tool runner's account in the group.
The ACL keeping, mask repair and watch are gone; an access is again as before ADR 0325.
2026-10-12 22:11:09 +02:00

113 lines
4.5 KiB
Go

package system
import (
"context"
"fmt"
"strconv"
"strings"
)
// A module's identity as a system account (novox/hq ADR 0328 §3, §4): an account and a group with exactly the numbers
// the controller allocated, no login shell, no home and no password. Optional beside System: a system that has
// none (android) makes no module identity, and the engine says so.
// CreateSystemGroup makes a system group with gid.
func (arch) CreateSystemGroup(ctx context.Context, run Runner, name string, gid int) error {
if _, err := run(ctx, "groupadd", "--system", "--gid", strconv.Itoa(gid), name); err != nil {
return fmt.Errorf("cannot create the group %q: %w", name, err)
}
return nil
}
// CreateSystemAccount makes a system account with uid and the group gid as its own: no home made, `/` as the
// home field, nologin as its shell, and the password useradd leaves locked.
func (arch) CreateSystemAccount(ctx context.Context, run Runner, name string, uid, gid int, groups []string) error {
args := []string{"--system", "--uid", strconv.Itoa(uid), "--gid", strconv.Itoa(gid)}
if len(groups) > 0 {
args = append(args, "--groups", strings.Join(groups, ","))
}
args = append(args, "--no-create-home", "--home-dir", "/", "--shell", "/usr/bin/nologin", name)
if _, err := run(ctx, "useradd", args...); err != nil {
return fmt.Errorf("cannot create the account %q: %w", name, err)
}
return nil
}
// ModifyAccount gives an account the mesh made its primary group gid and exactly groups as its added groups.
func (arch) ModifyAccount(ctx context.Context, run Runner, name string, gid int, groups []string) error {
if _, err := run(ctx, "usermod", "--gid", strconv.Itoa(gid), "--groups", strings.Join(groups, ","),
name); err != nil {
return fmt.Errorf("cannot give the account %q its groups: %w", name, err)
}
return nil
}
// RemoveAccount removes an account, leaving every file it owns where it is.
func (arch) RemoveAccount(ctx context.Context, run Runner, name string) error {
if _, err := run(ctx, "userdel", name); err != nil {
return fmt.Errorf("cannot remove the account %q: %w", name, err)
}
return nil
}
// RemoveGroup removes a group.
func (arch) RemoveGroup(ctx context.Context, run Runner, name string) error {
if _, err := run(ctx, "groupdel", name); err != nil {
return fmt.Errorf("cannot remove the group %q: %w", name, err)
}
return nil
}
// CreateSystemGroup makes a system group with gid, with busybox's addgroup.
func (alpine) CreateSystemGroup(ctx context.Context, run Runner, name string, gid int) error {
if _, err := run(ctx, "addgroup", "-S", "-g", strconv.Itoa(gid), name); err != nil {
return fmt.Errorf("cannot create the group %q: %w", name, err)
}
return nil
}
// CreateSystemAccount makes a system account with busybox's adduser: -S system, -D no password, -H no home made.
func (alpine) CreateSystemAccount(ctx context.Context, run Runner, name string, uid, gid int, groups []string) error {
group, err := run(ctx, "getent", "group", strconv.Itoa(gid))
if err != nil {
return fmt.Errorf("cannot create the account %q: its group gid %d is not here: %w", name, gid, err)
}
groupName := name
if n, _, ok := strings.Cut(group, ":"); ok {
groupName = n
}
if _, err := run(ctx, "adduser", "-S", "-D", "-H", "-h", "/", "-s", "/sbin/nologin", "-u", strconv.Itoa(uid),
"-G", groupName, name); err != nil {
return fmt.Errorf("cannot create the account %q: %w", name, err)
}
for _, g := range groups {
if _, err := run(ctx, "addgroup", name, g); err != nil {
return fmt.Errorf("cannot put the account %q in %q: %w", name, g, err)
}
}
return nil
}
// ModifyAccount is not one busybox can do: it has no usermod to change an account's primary group, and removing
// an added group is a hand edit of /etc/group. Refused, saying so: the account is left as it is.
func (alpine) ModifyAccount(context.Context, Runner, string, int, []string) error {
return fmt.Errorf("this system cannot change an account's groups once it is made (busybox has no usermod): %w",
ErrUnsupported)
}
// RemoveAccount removes an account with busybox's deluser.
func (alpine) RemoveAccount(ctx context.Context, run Runner, name string) error {
if _, err := run(ctx, "deluser", name); err != nil {
return fmt.Errorf("cannot remove the account %q: %w", name, err)
}
return nil
}
// RemoveGroup removes a group with busybox's delgroup.
func (alpine) RemoveGroup(ctx context.Context, run Runner, name string) error {
if _, err := run(ctx, "delgroup", name); err != nil {
return fmt.Errorf("cannot remove the group %q: %w", name, err)
}
return nil
}