package system import ( "context" "fmt" "strconv" "strings" ) // A module's identity as a system account (novox/hq ADR 0325 ยง3): 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) error { if _, err := run(ctx, "useradd", "--system", "--uid", strconv.Itoa(uid), "--gid", strconv.Itoa(gid), "--no-create-home", "--home-dir", "/", "--shell", "/usr/bin/nologin", name); err != nil { return fmt.Errorf("cannot create the account %q: %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) 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) } return nil } // 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 }