feat: add auth subsystem with WebAuthn passkeys support

New modules: lib/auth, lib/auth_users, lib/db, lib/db_sqlite, lib/password,
lib/webauthn, daemon/handlers/auth, scripts/bootstrap_auth, tests/test_auth

Frontend: webui/api/auth, hoover/components/auth, pages/login, passkeys, users

Updates: daemon/iface and server, lib/common and nginx, pyproject.toml deps,
install script, server.py, app.js, and websocket/api clients
This commit is contained in:
2026-07-24 01:21:39 +00:00
parent 04417cf05c
commit 56b200d233
28 changed files with 4900 additions and 82 deletions
+22 -24
View File
@@ -41,8 +41,8 @@ while [[ $# -gt 0 ]]; do
" --user, -u USER WebUI user (created if it does not exist, required for non-dev mode)" \
" --path, -p DIR Install directory (default: repo root)" \
" --dev Dev mode: auto-detect repo owner, skip safety warning" \
" --mgmt-pass PASS WebUI basic auth password (required)" \
" --mgmt-user USER WebUI basic auth username (default: admin)" \
" --mgmt-pass PASS Initial admin password (required)" \
" --mgmt-user USER Initial admin username (default: admin)" \
" --mgmt-domain DOMAIN Management domain (auto-detected)" \
" --wan-iface IFACE WAN interface name (auto-detected)" \
" --lan-ifaces IFC,... LAN interface names, comma-separated (auto-detected)" \
@@ -69,9 +69,9 @@ done
# --- Resolve config: CLI flag > env var > default ---
REPO_DIR="$(cd "$(dirname "$0")/../" && pwd)"
# Required settings (no defaults — must be provided)
MGMT_PASS="${_cli_mgmt_pass:-${MGMT_PASS:-}}"
# Optional settings with defaults
# MGMT_PASS is strictly required — admin user is created at install time
MGMT_PASS="${_cli_mgmt_pass:-${MGMT_PASS:-}}"
MGMT_USER="${_cli_mgmt_user:-${MGMT_USER:-admin}}"
# MGMT_DOMAIN — CLI > env > auto-detect from hostname
@@ -104,19 +104,7 @@ LAN_IFACES="${_cli_lan_ifaces:-${LAN_IFACES:-}}"
[[ -f /etc/debian_version ]] || warn "This script is designed for Debian/Ubuntu."
# --- Validate required settings ---
missing=()
[[ -z "$MGMT_PASS" ]] && missing+=("MGMT_PASS (--mgmt-pass)")
if (( ${#missing[@]} )); then
echo -e "${RED}[!!]${NC} Missing required settings:"
for v in "${missing[@]}"; do
case "$v" in
"MGMT_PASS (--mgmt-pass)") echo ' export MGMT_PASS="your-password" # or --mgmt-pass';;
esac
done
printf '\nTo run: MGMT_PASS=pass ./scripts/install.sh\n'
exit 1
fi
[[ -n "$MGMT_PASS" ]] || err "MGMT_PASS is required (set --mgmt-pass or MGMT_PASS env var)"
ACME_HOME="$PROJECT_DIR/data/acme"
# Dev mode: auto-detect repo owner as service user
@@ -356,7 +344,20 @@ else
echo ""
echo " Setting up initial management configuration..."
# htpasswd is created by the daemon via /nginx/domains/add (writes to data/.htpasswd)
# Bootstrap auth: generate config + seed admin user
if [[ ! -f "${PROJECT_DIR}/config/auth/config.json" ]]; then
echo ""
echo " Bootstrapping auth (creating admin user: $MGMT_USER)..."
"${PROJECT_DIR}/.venv/bin/python3" "${PROJECT_DIR}/scripts/bootstrap_auth.py" \
--project-dir "$PROJECT_DIR" \
--username "$MGMT_USER" \
--password "$MGMT_PASS" \
--domain "$DOMAIN"
chown -R "$USER_DAEMON_NAME:$USER_GROUP" "${PROJECT_DIR}/config/auth"
chown -R "$USER_DAEMON_NAME:$USER_GROUP" "${PROJECT_DIR}/data/auth.db" 2>/dev/null || true
fi
# Helper: POST JSON to daemon API over Unix socket
_daemon_post() {
@@ -379,18 +380,15 @@ else
# 1A. Self-signed certificate for management domain
_daemon_post "/acme/self-signed" "{\"domain\":\"$DOMAIN\"}" "Self-signed certificate"
# 1C. Management proxy domain — try idempotent update first, fall back to add
# 1C. Management proxy domain (no auth — JWT auth is handled by Flask)
local mgmt_json
mgmt_json="$(jq -n \
--arg domain "$DOMAIN" \
--arg user "$MGMT_USER" \
--arg pass "$MGMT_PASS" \
'{
domain: $domain,
backend: "webui",
cert: "selfsigned",
force_ssl: true,
auth: {user: $user, pass: $pass}
force_ssl: true
}')"
_daemon_post "/nginx/domains/update" "$mgmt_json" "Management domain configured" || \
_daemon_post "/nginx/domains/add" "$mgmt_json" "Management domain configured"
@@ -430,7 +428,7 @@ echo -e " ${GREEN}Vacuum Wall installed successfully!${NC}"
echo "============================================"
echo ""
echo " Management UI: https://$DOMAIN"
echo " User: $MGMT_USER"
echo " Admin user: $MGMT_USER"
echo " Daemon service: vacuum-walld.service"
echo " WebUI service: vacuum-wall.service"
echo " ACME renewal: vacuum-wall-acme.timer"