create-user: passwordless sudo user (NOPASSWD, !tty_tickets, 7d cache) + retarget config; --create-user/--username flags
This commit is contained in:
parent
a9fff32255
commit
8a516f039c
155
modules/05-create-user.sh
Executable file
155
modules/05-create-user.sh
Executable file
@ -0,0 +1,155 @@
|
|||||||
|
#!/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/<user>: 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" <<EOF
|
||||||
|
# managed by bootstrap — passwordless sudo for $user
|
||||||
|
Defaults:$user !tty_tickets
|
||||||
|
Defaults:$user timestamp_timeout=10080
|
||||||
|
$user ALL=(ALL) NOPASSWD:ALL
|
||||||
|
EOF
|
||||||
|
# visudo must see proper perms (0440) to be happy
|
||||||
|
chmod 0440 "$tmp"
|
||||||
|
if visudo -cf "$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"
|
||||||
|
}
|
||||||
32
setup.sh
32
setup.sh
@ -4,13 +4,20 @@
|
|||||||
# Detects distro/arch, lists modular steps, asks y/N per step, runs them.
|
# Detects distro/arch, lists modular steps, asks y/N per step, runs them.
|
||||||
#
|
#
|
||||||
# Flags:
|
# Flags:
|
||||||
# --yes run all steps without prompting
|
# --yes run all steps without prompting
|
||||||
# --only NAME run only the step matching NAME
|
# --only NAME run only the step matching NAME
|
||||||
# --skip NAME run all steps except NAME (repeatable in future)
|
# --skip NAME run all steps except NAME
|
||||||
# -h|--help usage
|
# --create-user force creating a sudo user (otherwise Arch auto-creates, Debian asks)
|
||||||
|
# --username NAME override the sudo user name (default: mk)
|
||||||
|
# -h|--help usage
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Default config target = whoever is running this. The create-user step
|
||||||
|
# (05-create-user) may override these to point at a freshly-created sudoer.
|
||||||
|
export TARGET_USER="$USER"
|
||||||
|
export TARGET_HOME="$HOME"
|
||||||
|
|
||||||
BOOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
BOOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
# shellcheck disable=SC1091
|
# shellcheck disable=SC1091
|
||||||
@ -18,18 +25,25 @@ source "$BOOT_DIR/lib/detect.sh"
|
|||||||
source "$BOOT_DIR/lib/log.sh"
|
source "$BOOT_DIR/lib/log.sh"
|
||||||
source "$BOOT_DIR/lib/prompt.sh"
|
source "$BOOT_DIR/lib/prompt.sh"
|
||||||
|
|
||||||
ALL=0; ONLY=""; SKIP=""
|
# helper: resolve the target user's real home even when we're root operating
|
||||||
|
# on a created user (whose home isn't $HOME).
|
||||||
|
target_home() { getent passwd "${TARGET_USER:-$USER}" | cut -d: -f6; }
|
||||||
|
|
||||||
|
ALL=0; ONLY=""; SKIP=""; CREATE_USER=0; USERNAME_ARG=""
|
||||||
while (( $# )); do
|
while (( $# )); do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--yes|-y) ALL=1; shift ;;
|
--yes|-y) ALL=1; shift ;;
|
||||||
--only) ONLY="${2:-}"; shift 2 ;;
|
--only) ONLY="${2:-}"; shift 2 ;;
|
||||||
--skip) SKIP="${2:-}"; shift 2 ;;
|
--skip) SKIP="${2:-}"; shift 2 ;;
|
||||||
|
--create-user) CREATE_USER=1; shift ;;
|
||||||
|
--username) USERNAME_ARG="${2:-}"; shift 2 ;;
|
||||||
-h|--help)
|
-h|--help)
|
||||||
echo "usage: setup.sh [--yes] [--only NAME] [--skip NAME]"
|
grep -E '^# --' "$0" | sed 's/^# //'
|
||||||
exit 0 ;;
|
exit 0 ;;
|
||||||
*) echo "unknown arg: $1" >&2; shift ;;
|
*) echo "unknown arg: $1" >&2; shift ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
export CREATE_USER USERNAME_ARG
|
||||||
|
|
||||||
# ---------------------------------------------------------------- collect steps
|
# ---------------------------------------------------------------- collect steps
|
||||||
NAMES=(); DESCS=(); RUNS=()
|
NAMES=(); DESCS=(); RUNS=()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user