feat(pods/admin): root admin alias, default-caps cleanup, pod rebuild script

- auth: add `root` as an admin-console route alias (alongside admin/sysop);
  still gated by $AGENTBBS_ADMINS — the name confers nothing on its own.
- pods: drop the now-redundant tuneApt apt-sandbox hack. Rootless podman keeps
  its default capability set, so apt/chown/su work without disabling the
  download sandbox.
- scripts/rebuild-pods.sh: recreate all member pods (keeps home volumes) so
  they pick up the current container profile on next `ssh pod@`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Anthony Ettinger 2026-06-15 13:57:09 +00:00
parent 06bc5631d8
commit a8cb7e3182
5 changed files with 69 additions and 24 deletions

View file

@ -43,8 +43,9 @@ var DomainNames = map[string]bool{"domain": true, "domains": true}
// AdminNames are usernames that route to the privileged admin console (PRD §6).
// The route only opens for accounts whose name is in the operator allowlist
// (see IsAdmin); the name itself confers nothing.
var AdminNames = map[string]bool{"admin": true, "sysop": true}
// (see IsAdmin); the name itself confers nothing — so "root" is just a familiar
// alias here, not a backdoor.
var AdminNames = map[string]bool{"admin": true, "sysop": true, "root": true}
// TorURLNames route to the one-shot "fetch a URL over Tor" command (premium).
var TorURLNames = map[string]bool{"tor-url": true}

View file

@ -3,7 +3,7 @@ package auth
import "testing"
func TestIsAdminName(t *testing.T) {
for _, name := range []string{"admin", "ADMIN", "sysop"} {
for _, name := range []string{"admin", "ADMIN", "sysop", "root"} {
if !IsAdminName(name) {
t.Errorf("IsAdminName(%q) = false, want true", name)
}

View file

@ -107,7 +107,6 @@ func (m *Manager) ensure(user string) (string, error) {
// Already exists?
if err := exec.Command(m.engine, "container", "inspect", name).Run(); err == nil {
_ = exec.Command(m.engine, "start", name).Run() // no-op if running
m.tuneApt(name)
return name, nil
}
args := []string{
@ -149,28 +148,9 @@ func (m *Manager) ensure(user string) (string, error) {
if err != nil {
return "", fmt.Errorf("pods: create failed: %v: %s", err, strings.TrimSpace(string(out)))
}
m.tuneApt(name)
return name, nil
}
// tuneApt makes apt usable inside the hardened pod. apt drops privileges to
// the _apt user for downloads (setgroups/setegid/seteuid), which needs
// CAP_SETUID/CAP_SETGID/CAP_CHOWN — caps we intentionally drop (cap-drop ALL).
// Rather than re-grant those to the whole container, disable apt's download
// sandbox so package management runs as the pod's (rootless-mapped) root.
//
// Only applies to the podman/container-root path; under docker the pod runs as
// uid 1000 and can't write /etc/apt (apt isn't usable there by design). Failure
// is non-fatal: a missing config just means the user sees the old apt errors.
func (m *Manager) tuneApt(name string) {
if m.engine == "docker" {
return
}
_ = exec.Command(m.engine, "exec", "--user", "root", name,
"sh", "-c", `printf 'APT::Sandbox::User "root";\n' > /etc/apt/apt.conf.d/00no-sandbox`,
).Run()
}
// Attach provisions the pod and wires the SSH session to a shell inside it.
// Blocks until the shell exits or the session closes.
func (m *Manager) Attach(s ssh.Session, user string) error {