#!/usr/bin/env bash # 05-create-user — create a daily-driver sudo user when running as direct root. # # Why this exists: `makepkg` (yay / AUR) refuses to run as root. On a fresh VPS # that lands you in as root with no SUDO_USER, we create a non-root sudoer, # copy root's authorized_keys to it, configure passwordless sudo with # !tty_tickets + a long timestamp_timeout, and retarget the config steps # (zsh, nvim, nvm, docker group) at that user's $HOME. # # Behaviour: # - Not root -> skip (you're already a normal user). # - Root + SUDO_USER set -> skip (you sudoed in; that user is the target). # - Root + no SUDO_USER + Arch -> auto-create (AUR needs it), confirm name. # - Root + no SUDO_USER + Debian/Ubuntu -> ask y/N (optional, apt works as root). # - --create-user flag -> force prompt regardless. # - --username NAME -> override default name, no prompt. # # Passwordless, no user password (NOPASSWD). Idempotent: re-runs re-sync # authorized_keys + sudoers rather than failing. step_name="create-user" step_desc="create a sudo user (passwordless) + copy root's ssh keys + retarget config" step_run="run_create_user" run_create_user() { log "create-user step" # not root -> nothing to do, target stays as current user if (( EUID != 0 )); then echo " not root — target stays $USER ($HOME)" export TARGET_USER="$USER" TARGET_HOME="$HOME" return fi # root but came in via sudo -> use that invoking user, don't create a new one if [[ -z "${SUDO_USER:-}" ]]; then _handle_direct_root; else _handle_sudoed_root; fi } # ── direct root (logged in as root; the case that actually needs a new user) ─ _handle_direct_root() { local should_create=0 if (( CREATE_USER )); then should_create=1 elif [[ "$DISTRO" == "arch" ]]; then # AUR/makepkg cannot run as root — we genuinely need a non-root sudoer. echo " running as direct root on Arch: a non-root sudoer is needed for yay/AUR." yn "Create a sudo user now? (recommended)" y && should_create=1 else # Debian/Ubuntu: apt works as root, so this is convenience (key-first login). yn "Create a daily-driver sudo user? (optional — apt works as root)" n && should_create=1 fi if (( ! should_create )); then echo " skipping user creation — config will target root ($HOME)" warn "on Arch you won't be able to build AUR packages (yay/angie) as root" export TARGET_USER="root" TARGET_HOME="$HOME" return fi local user="${USERNAME_ARG:-}" if [[ -z "$user" ]]; then local default="mk" read -rp " username [$default]: " user [[ -z "$user" ]] && user="$default" fi _create_or_sync_user "$user" } # ── root via sudo: use the invoking user, don't create another ─────────────── _handle_sudoed_root() { local user="$SUDO_USER" local home; home="$(getent passwd "$user" | cut -d: -f6)" log "running as root via sudo — using invoking user '$user' as target" # ensure they have passwordless sudo + the ssh key + sudo group, then retarget _create_or_sync_user "$user" skip_create export TARGET_USER="$user" TARGET_HOME="$home" } # ── the meat: create if missing, sync sudoers, sync authorized_keys ────────── _create_or_sync_user() { # username [skip_create] local user="$1" local skip_create="${2:-}" if [[ "$skip_create" != "skip_create" ]]; then if id "$user" >/dev/null 2>&1; then ok "user '$user' already exists — syncing config only" else local shell_bin; shell_bin="$(command -v zsh 2>/dev/null || echo /bin/bash)" local groups case "$DISTRO" in arch) groups="wheel" ;; debian) groups="sudo,adm" ;; esac log "creating user '$user' (shell=$shell_bin, groups=$groups)" useradd -m -s "$shell_bin" -G "$groups" "$user" # passwordless account: clear the password field entirely passwd -d "$user" >/dev/null 2>&1 || true # unlock the account so SSH key login still works after passwd -d usermod -p "" "$user" 2>/dev/null || true ok "user '$user' created (no password set; ssh-key login only)" fi fi local home; home="$(getent passwd "$user" | cut -d: -f6)" _sync_authorized_keys "$user" "$home" _install_sudoers "$user" export TARGET_USER="$user" TARGET_HOME="$home" ok "config target retargeted: user=$user home=$home" } # ── copy root's authorized_keys to the target user (idempotent) ────────────── _sync_authorized_keys() { # user home local user="$1" home="$2" local src="/root/.ssh/authorized_keys" if [[ ! -f "$src" ]]; then warn "no /root/.ssh/authorized_keys — add your key manually:" echo " install -d -m 700 -o $user -g $user $home/.ssh" echo " # then: paste your pubkey into $home/.ssh/authorized_keys (chmod 600)" return fi install -d -m 700 -o "$user" -g "$user" "$home/.ssh" install -m 600 -o "$user" -g "$user" "$src" "$home/.ssh/authorized_keys" local n; n=$(grep -cE '^[^#]' "$src" 2>/dev/null || echo 0) ok "copied $n authorized key(s) to $home/.ssh/authorized_keys" warn "sanity-check these are YOUR keys (fingerprints):" ssh-keygen -lf "$src" 2>/dev/null | sed 's/^/ /' || true } # ── write /etc/sudoers.d/: NOPASSWD, !tty_tickets, 7-day cache ───────── _install_sudoers() { # user local user="$1" file="/etc/sudoers.d/${user}" log "configuring sudoers for '$user' (passwordless, !tty_tickets, 7d timeout)" # write to a temp, validate with visudo -c, then move into place atomically local tmp; tmp="$(mktemp)" cat > "$tmp" </dev/null 2>&1; then install -m 0440 -o root -g root "$tmp" "$file" ok "sudoers installed: $file" else err "visudo -c rejected the sudoers snippet — NOT installed, inspect aborted" cat "$tmp" | sed 's/^/ /' fi rm -f "$tmp" }