Add kiosk setup and deployment scripts (Phases 5 + 9)

Phase 5 — RPi Kiosk:
- setup_kiosk.sh: full RPi OS Lite setup (X11, Chromium kiosk mode,
  auto-login, DPMS disabled, GPU memory split, screen rotation)
- kiosk.service: systemd unit for reliable auto-start
- update_kiosk.sh: reconfigure URL/rotation/resolution without re-setup
- Handles both Bullseye and Bookworm RPi OS versions

Phase 9 — Hardening + Deployment:
- install.sh: full server setup (apt/pacman, vigilar user, venv,
  directories, permissions, mosquitto config, systemd units)
- gen_cert.sh: TLS cert via mkcert or openssl fallback
- gen_vapid_keys.sh: VAPID keys for Web Push notifications
- setup_nut.sh: NUT configuration with USB UPS auto-detection
- backup.sh: SQLite snapshot + config archive, cron-ready
- uninstall.sh: clean removal with data preservation option
- vigilar.service: hardened systemd unit (ProtectSystem, NoNewPrivileges,
  PrivateTmp, syscall filtering)
- vigilar-mosquitto.conf: localhost-only MQTT broker config

All scripts idempotent, bash -n validated, support Debian + Arch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aaron D. Lee
2026-04-02 23:25:03 -04:00
parent 10b0cf4d0e
commit ebcc49b474
13 changed files with 1704 additions and 0 deletions

93
scripts/backup.sh Executable file
View File

@@ -0,0 +1,93 @@
#!/usr/bin/env bash
set -euo pipefail
# Vigilar — Backup script
# Backs up SQLite database, config, and secrets to a dated tar.gz archive.
# Suitable for cron: 0 3 * * * /opt/vigilar/scripts/backup.sh
DATA_DIR="/var/vigilar/data"
CONFIG_DIR="/etc/vigilar"
BACKUP_DEST="${VIGILAR_BACKUP_DIR:-/var/vigilar/backups}"
RETENTION_DAYS="${VIGILAR_BACKUP_RETENTION_DAYS:-30}"
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
ARCHIVE_NAME="vigilar-backup-${TIMESTAMP}.tar.gz"
info() { printf '\033[1;34m[INFO]\033[0m %s\n' "$*"; }
ok() { printf '\033[1;32m[ OK ]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[WARN]\033[0m %s\n' "$*"; }
fail() { printf '\033[1;31m[FAIL]\033[0m %s\n' "$*" >&2; exit 1; }
main() {
info "=== Vigilar Backup ==="
# Create backup destination
sudo mkdir -p "$BACKUP_DEST"
# Collect files to back up
local items=()
# SQLite database
if [[ -d "$DATA_DIR" ]]; then
# Use sqlite3 .backup for a consistent snapshot if sqlite3 is available
local db_file="${DATA_DIR}/vigilar.db"
if [[ -f "$db_file" ]] && command -v sqlite3 &>/dev/null; then
local db_snapshot="/tmp/vigilar-backup-${TIMESTAMP}.db"
info "Creating consistent database snapshot"
sqlite3 "$db_file" ".backup '${db_snapshot}'"
items+=("$db_snapshot")
elif [[ -f "$db_file" ]]; then
warn "sqlite3 not found, copying database file directly (may be inconsistent if running)"
items+=("$db_file")
fi
# Also back up any WAL/SHM files
for f in "${db_file}-wal" "${db_file}-shm"; do
if [[ -f "$f" ]]; then
items+=("$f")
fi
done
else
warn "Data directory ${DATA_DIR} not found, skipping database"
fi
# Config directory (includes secrets, certs, toml)
if [[ -d "$CONFIG_DIR" ]]; then
items+=("$CONFIG_DIR")
else
warn "Config directory ${CONFIG_DIR} not found, skipping config"
fi
if [[ ${#items[@]} -eq 0 ]]; then
fail "Nothing to back up"
fi
# Create archive
local archive_path="${BACKUP_DEST}/${ARCHIVE_NAME}"
info "Creating archive: ${archive_path}"
sudo tar -czf "$archive_path" "${items[@]}" 2>/dev/null
# Secure the backup (contains secrets)
sudo chmod 0600 "$archive_path"
sudo chown root:root "$archive_path"
# Clean up temp db snapshot
if [[ -f "/tmp/vigilar-backup-${TIMESTAMP}.db" ]]; then
rm -f "/tmp/vigilar-backup-${TIMESTAMP}.db"
fi
local size
size="$(du -h "$archive_path" | cut -f1)"
ok "Backup complete: ${archive_path} (${size})"
# Prune old backups
if [[ "$RETENTION_DAYS" -gt 0 ]]; then
info "Pruning backups older than ${RETENTION_DAYS} days"
local pruned
pruned="$(sudo find "$BACKUP_DEST" -name 'vigilar-backup-*.tar.gz' -mtime +"$RETENTION_DAYS" -delete -print | wc -l)"
if [[ "$pruned" -gt 0 ]]; then
ok "Pruned ${pruned} old backup(s)"
fi
fi
}
main "$@"

126
scripts/gen_cert.sh Executable file
View File

@@ -0,0 +1,126 @@
#!/usr/bin/env bash
set -euo pipefail
# Vigilar — Self-signed TLS certificate generator
# Uses mkcert if available, otherwise falls back to openssl.
CONFIG_DIR="/etc/vigilar"
CERT_DIR="${CONFIG_DIR}/certs"
CERT_FILE="${CERT_DIR}/cert.pem"
KEY_FILE="${CERT_DIR}/key.pem"
CONFIG_FILE="${CONFIG_DIR}/vigilar.toml"
VIGILAR_GROUP="vigilar"
info() { printf '\033[1;34m[INFO]\033[0m %s\n' "$*"; }
ok() { printf '\033[1;32m[ OK ]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[WARN]\033[0m %s\n' "$*"; }
fail() { printf '\033[1;31m[FAIL]\033[0m %s\n' "$*" >&2; exit 1; }
get_lan_ip() {
# Try to detect the primary LAN IP
ip -4 route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src") print $(i+1)}' | head -1
}
generate_with_mkcert() {
local lan_ip="$1"
info "Using mkcert to generate certificate"
local san_args=("vigilar.local" "localhost" "127.0.0.1")
if [[ -n "$lan_ip" ]]; then
san_args+=("$lan_ip")
fi
mkcert -cert-file "$CERT_FILE" -key-file "$KEY_FILE" "${san_args[@]}"
ok "Certificate generated with mkcert"
}
generate_with_openssl() {
local lan_ip="$1"
info "Using openssl to generate self-signed certificate"
local san="DNS:vigilar.local,DNS:localhost,IP:127.0.0.1"
if [[ -n "$lan_ip" ]]; then
san="${san},IP:${lan_ip}"
fi
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
-keyout "$KEY_FILE" \
-out "$CERT_FILE" \
-sha256 -days 3650 -nodes \
-subj "/CN=vigilar.local" \
-addext "subjectAltName=${san}" \
2>/dev/null
ok "Self-signed certificate generated with openssl"
}
update_config() {
if [[ ! -f "$CONFIG_FILE" ]]; then
warn "Config file not found at ${CONFIG_FILE}, skipping config update"
return
fi
# Uncomment the tls_cert and tls_key lines if they are commented out
if grep -q '^# *tls_cert' "$CONFIG_FILE"; then
sudo sed -i 's|^# *tls_cert *=.*|tls_cert = "/etc/vigilar/certs/cert.pem"|' "$CONFIG_FILE"
sudo sed -i 's|^# *tls_key *=.*|tls_key = "/etc/vigilar/certs/key.pem"|' "$CONFIG_FILE"
ok "Config updated with TLS cert paths"
elif grep -q '^tls_cert' "$CONFIG_FILE"; then
ok "Config already has TLS cert paths"
else
# Append after the [web] section port line
sudo sed -i '/^\[web\]/,/^$/{/^port/a\tls_cert = "/etc/vigilar/certs/cert.pem"\ntls_key = "/etc/vigilar/certs/key.pem"
}' "$CONFIG_FILE"
ok "Config updated with TLS cert paths"
fi
}
main() {
info "=== Vigilar TLS Certificate Generator ==="
sudo mkdir -p "$CERT_DIR"
if [[ -f "$CERT_FILE" && -f "$KEY_FILE" ]]; then
warn "Certificates already exist at ${CERT_DIR}/"
read -rp "Overwrite? [y/N] " answer
if [[ ! "$answer" =~ ^[Yy]$ ]]; then
info "Keeping existing certificates"
exit 0
fi
fi
local lan_ip
lan_ip="$(get_lan_ip)" || lan_ip=""
if [[ -n "$lan_ip" ]]; then
info "Detected LAN IP: ${lan_ip}"
else
warn "Could not detect LAN IP, skipping IP SAN"
fi
if command -v mkcert &>/dev/null; then
generate_with_mkcert "$lan_ip"
elif command -v openssl &>/dev/null; then
generate_with_openssl "$lan_ip"
else
fail "Neither mkcert nor openssl found. Install one and retry."
fi
# Set permissions — readable by vigilar group
sudo chown root:"${VIGILAR_GROUP}" "$CERT_FILE" "$KEY_FILE"
sudo chmod 0640 "$KEY_FILE"
sudo chmod 0644 "$CERT_FILE"
update_config
echo
ok "TLS certificate ready"
info " Cert: ${CERT_FILE}"
info " Key: ${KEY_FILE}"
if [[ -n "$lan_ip" ]]; then
info " SANs: vigilar.local, localhost, 127.0.0.1, ${lan_ip}"
else
info " SANs: vigilar.local, localhost, 127.0.0.1"
fi
}
main "$@"

100
scripts/gen_vapid_keys.sh Executable file
View File

@@ -0,0 +1,100 @@
#!/usr/bin/env bash
set -euo pipefail
# Vigilar — VAPID key generator for Web Push notifications
# Uses py-vapid (from the vigilar venv) or falls back to openssl.
VENV_DIR="/opt/vigilar/venv"
SECRETS_DIR="/etc/vigilar/secrets"
PRIVATE_KEY_FILE="${SECRETS_DIR}/vapid_private.pem"
PUBLIC_KEY_FILE="${SECRETS_DIR}/vapid_public.txt"
VIGILAR_GROUP="vigilar"
info() { printf '\033[1;34m[INFO]\033[0m %s\n' "$*"; }
ok() { printf '\033[1;32m[ OK ]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[WARN]\033[0m %s\n' "$*"; }
fail() { printf '\033[1;31m[FAIL]\033[0m %s\n' "$*" >&2; exit 1; }
generate_with_py_vapid() {
info "Generating VAPID keys with py-vapid"
local python="${VENV_DIR}/bin/python"
"$python" -c "
from py_vapid import Vapid
import base64
v = Vapid()
v.generate_keys()
v.save_key('${PRIVATE_KEY_FILE}')
raw = v.public_key.public_bytes(
encoding=__import__('cryptography.hazmat.primitives.serialization', fromlist=['Encoding']).Encoding.X962,
format=__import__('cryptography.hazmat.primitives.serialization', fromlist=['PublicFormat']).PublicFormat.UncompressedPoint,
)
pub_b64 = base64.urlsafe_b64encode(raw).rstrip(b'=').decode()
print(pub_b64)
" | tee "$PUBLIC_KEY_FILE"
}
generate_with_openssl() {
info "Generating VAPID keys with openssl"
# Generate ECDSA P-256 private key in PEM format
openssl ecparam -name prime256v1 -genkey -noout -out "$PRIVATE_KEY_FILE" 2>/dev/null
# Extract the public key in uncompressed point format, base64url-encode it
local pub_b64
pub_b64="$(openssl ec -in "$PRIVATE_KEY_FILE" -pubout -outform DER 2>/dev/null \
| tail -c 65 \
| base64 -w 0 \
| tr '+/' '-_' \
| tr -d '=')"
echo "$pub_b64" | tee "$PUBLIC_KEY_FILE"
}
main() {
info "=== Vigilar VAPID Key Generator ==="
sudo mkdir -p "$SECRETS_DIR"
if [[ -f "$PRIVATE_KEY_FILE" ]]; then
warn "VAPID private key already exists at ${PRIVATE_KEY_FILE}"
read -rp "Overwrite? [y/N] " answer
if [[ ! "$answer" =~ ^[Yy]$ ]]; then
info "Keeping existing key"
if [[ -f "$PUBLIC_KEY_FILE" ]]; then
info "Public key (base64url):"
cat "$PUBLIC_KEY_FILE"
fi
exit 0
fi
fi
local public_key
if [[ -x "${VENV_DIR}/bin/python" ]] && "${VENV_DIR}/bin/python" -c "import py_vapid" 2>/dev/null; then
public_key="$(generate_with_py_vapid)"
elif command -v openssl &>/dev/null; then
public_key="$(generate_with_openssl)"
else
fail "Neither py-vapid nor openssl found."
fi
# Secure the private key
sudo chown root:root "$PRIVATE_KEY_FILE"
sudo chmod 0600 "$PRIVATE_KEY_FILE"
# Public key file is not sensitive
sudo chown root:"${VIGILAR_GROUP}" "$PUBLIC_KEY_FILE"
sudo chmod 0644 "$PUBLIC_KEY_FILE"
echo
ok "VAPID keys generated"
info " Private key: ${PRIVATE_KEY_FILE}"
info " Public key (base64url):"
echo " ${public_key}"
echo
info "Use the public key above in your web app's push subscription config."
}
main "$@"

232
scripts/install.sh Executable file
View File

@@ -0,0 +1,232 @@
#!/usr/bin/env bash
set -euo pipefail
# Vigilar Home Security — Installation Script
# Supports Debian/Ubuntu (apt) and Arch Linux (pacman).
VIGILAR_USER="vigilar"
VIGILAR_GROUP="vigilar"
INSTALL_DIR="/opt/vigilar"
VENV_DIR="${INSTALL_DIR}/venv"
CONFIG_DIR="/etc/vigilar"
DATA_DIR="/var/vigilar"
SYSTEMD_DIR="/etc/systemd/system"
MOSQUITTO_CONF_DIR="/etc/mosquitto/conf.d"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
info() { printf '\033[1;34m[INFO]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[WARN]\033[0m %s\n' "$*"; }
ok() { printf '\033[1;32m[ OK ]\033[0m %s\n' "$*"; }
fail() { printf '\033[1;31m[FAIL]\033[0m %s\n' "$*" >&2; exit 1; }
need_cmd() {
command -v "$1" &>/dev/null || fail "Required command not found: $1"
}
detect_pkg_manager() {
if command -v apt-get &>/dev/null; then
echo "apt"
elif command -v pacman &>/dev/null; then
echo "pacman"
else
fail "Unsupported package manager. This script supports apt (Debian/Ubuntu) and pacman (Arch)."
fi
}
# ---------------------------------------------------------------------------
# 1. System dependencies
# ---------------------------------------------------------------------------
install_system_deps() {
local pkg_mgr
pkg_mgr="$(detect_pkg_manager)"
info "Detected package manager: ${pkg_mgr}"
case "$pkg_mgr" in
apt)
sudo apt-get update -qq
sudo apt-get install -y -qq \
ffmpeg mosquitto python3 python3-venv python3-pip nut-client
;;
pacman)
sudo pacman -Sy --needed --noconfirm \
ffmpeg mosquitto python python-virtualenv nut
;;
esac
ok "System dependencies installed"
}
# ---------------------------------------------------------------------------
# 2. System user & group
# ---------------------------------------------------------------------------
create_user() {
if id "$VIGILAR_USER" &>/dev/null; then
ok "User '${VIGILAR_USER}' already exists"
return
fi
info "Creating system user '${VIGILAR_USER}'"
sudo useradd --system --home-dir "$INSTALL_DIR" --shell /usr/sbin/nologin \
--create-home "$VIGILAR_USER"
ok "User '${VIGILAR_USER}' created"
}
# ---------------------------------------------------------------------------
# 3. Directories & permissions
# ---------------------------------------------------------------------------
create_directories() {
info "Creating directories"
# Data directories — owned by vigilar
sudo mkdir -p "${DATA_DIR}/data" "${DATA_DIR}/recordings" "${DATA_DIR}/hls"
sudo chown -R "${VIGILAR_USER}:${VIGILAR_GROUP}" "$DATA_DIR"
sudo chmod -R 0750 "$DATA_DIR"
# Config directories
sudo mkdir -p "${CONFIG_DIR}/secrets" "${CONFIG_DIR}/certs"
sudo chown root:root "${CONFIG_DIR}"
sudo chmod 0755 "${CONFIG_DIR}"
# Secrets — root-owned, restricted
sudo chown root:root "${CONFIG_DIR}/secrets"
sudo chmod 0700 "${CONFIG_DIR}/secrets"
# Certs — readable by vigilar
sudo chown root:"${VIGILAR_GROUP}" "${CONFIG_DIR}/certs"
sudo chmod 0750 "${CONFIG_DIR}/certs"
# Install dir
sudo mkdir -p "$INSTALL_DIR"
sudo chown "${VIGILAR_USER}:${VIGILAR_GROUP}" "$INSTALL_DIR"
ok "Directories created"
}
# ---------------------------------------------------------------------------
# 4. Python venv & package
# ---------------------------------------------------------------------------
install_venv() {
if [[ -d "$VENV_DIR" ]]; then
info "Venv already exists at ${VENV_DIR}, upgrading"
else
info "Creating Python venv at ${VENV_DIR}"
sudo -u "$VIGILAR_USER" python3 -m venv "$VENV_DIR"
fi
info "Installing vigilar package into venv"
sudo -u "$VIGILAR_USER" "${VENV_DIR}/bin/pip" install --upgrade pip setuptools wheel -q
sudo -u "$VIGILAR_USER" "${VENV_DIR}/bin/pip" install "${PROJECT_DIR}" -q
ok "Vigilar installed into ${VENV_DIR}"
}
# ---------------------------------------------------------------------------
# 5. Storage encryption key
# ---------------------------------------------------------------------------
generate_storage_key() {
local key_file="${CONFIG_DIR}/secrets/storage.key"
if [[ -f "$key_file" ]]; then
ok "Storage encryption key already exists"
return
fi
info "Generating storage encryption key"
sudo dd if=/dev/urandom bs=32 count=1 2>/dev/null | sudo tee "$key_file" > /dev/null
sudo chmod 0600 "$key_file"
sudo chown root:root "$key_file"
ok "Storage key written to ${key_file}"
}
# ---------------------------------------------------------------------------
# 6. Sample config
# ---------------------------------------------------------------------------
install_config() {
local dest="${CONFIG_DIR}/vigilar.toml"
if [[ -f "$dest" ]]; then
ok "Config already exists at ${dest}"
return
fi
info "Copying sample config"
sudo cp "${PROJECT_DIR}/config/vigilar.toml" "$dest"
sudo chmod 0644 "$dest"
sudo chown root:"${VIGILAR_GROUP}" "$dest"
ok "Config installed to ${dest}"
}
# ---------------------------------------------------------------------------
# 7. Systemd units
# ---------------------------------------------------------------------------
install_systemd() {
info "Installing systemd service"
sudo cp "${PROJECT_DIR}/systemd/vigilar.service" "${SYSTEMD_DIR}/vigilar.service"
sudo chmod 0644 "${SYSTEMD_DIR}/vigilar.service"
sudo systemctl daemon-reload
sudo systemctl enable vigilar.service
ok "vigilar.service enabled"
}
# ---------------------------------------------------------------------------
# 8. Mosquitto configuration
# ---------------------------------------------------------------------------
configure_mosquitto() {
local conf="${MOSQUITTO_CONF_DIR}/vigilar.conf"
info "Configuring mosquitto for localhost-only"
sudo mkdir -p "$MOSQUITTO_CONF_DIR"
sudo cp "${PROJECT_DIR}/systemd/vigilar-mosquitto.conf" "$conf"
sudo chmod 0644 "$conf"
sudo systemctl enable mosquitto.service
sudo systemctl restart mosquitto.service
ok "Mosquitto configured and restarted"
}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
main() {
info "=== Vigilar Home Security — Installer ==="
info "Project dir: ${PROJECT_DIR}"
echo
install_system_deps
create_user
create_directories
install_venv
generate_storage_key
install_config
install_systemd
configure_mosquitto
echo
ok "=== Installation complete ==="
echo
info "Summary:"
info " Service user: ${VIGILAR_USER}"
info " Venv: ${VENV_DIR}"
info " Config: ${CONFIG_DIR}/vigilar.toml"
info " Data: ${DATA_DIR}/"
info " Secrets: ${CONFIG_DIR}/secrets/"
info " Systemd unit: ${SYSTEMD_DIR}/vigilar.service"
echo
info "Next steps:"
info " 1. Edit /etc/vigilar/vigilar.toml — set camera RTSP URLs, passwords, etc."
info " 2. Run: sudo ${SCRIPT_DIR}/gen_cert.sh — generate TLS certs"
info " 3. Run: sudo ${SCRIPT_DIR}/gen_vapid_keys.sh — generate VAPID keys for push"
info " 4. Run: sudo ${SCRIPT_DIR}/setup_nut.sh — configure UPS monitoring"
info " 5. Start: sudo systemctl start vigilar"
info " 6. Open: https://vigilar.local:49735"
}
main "$@"

203
scripts/setup_nut.sh Executable file
View File

@@ -0,0 +1,203 @@
#!/usr/bin/env bash
set -euo pipefail
# Vigilar — NUT (Network UPS Tools) configuration helper
# Detects USB UPS devices and configures NUT for standalone monitoring.
info() { printf '\033[1;34m[INFO]\033[0m %s\n' "$*"; }
ok() { printf '\033[1;32m[ OK ]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[WARN]\033[0m %s\n' "$*"; }
fail() { printf '\033[1;31m[FAIL]\033[0m %s\n' "$*" >&2; exit 1; }
detect_pkg_manager() {
if command -v apt-get &>/dev/null; then
echo "apt"
elif command -v pacman &>/dev/null; then
echo "pacman"
else
fail "Unsupported package manager."
fi
}
install_nut() {
if command -v upsc &>/dev/null; then
ok "NUT already installed"
return
fi
info "Installing NUT"
local pkg_mgr
pkg_mgr="$(detect_pkg_manager)"
case "$pkg_mgr" in
apt)
sudo apt-get update -qq
sudo apt-get install -y -qq nut nut-client nut-server
;;
pacman)
sudo pacman -Sy --needed --noconfirm nut
;;
esac
ok "NUT installed"
}
detect_ups() {
info "Scanning for USB UPS devices..."
local driver=""
local port=""
local desc=""
# Try nut-scanner first
if command -v nut-scanner &>/dev/null; then
local scan_output
scan_output="$(sudo nut-scanner -U 2>/dev/null)" || true
if [[ -n "$scan_output" ]]; then
driver="$(echo "$scan_output" | grep -oP 'driver\s*=\s*"\K[^"]+' | head -1)" || true
port="$(echo "$scan_output" | grep -oP 'port\s*=\s*"\K[^"]+' | head -1)" || true
desc="$(echo "$scan_output" | grep -oP 'desc\s*=\s*"\K[^"]+' | head -1)" || true
fi
fi
# Fallback: check for common USB UPS vendor IDs
if [[ -z "$driver" ]]; then
if lsusb 2>/dev/null | grep -qi "051d"; then
driver="usbhid-ups"
port="auto"
desc="APC UPS (auto-detected via lsusb)"
elif lsusb 2>/dev/null | grep -qi "0764"; then
driver="usbhid-ups"
port="auto"
desc="CyberPower UPS (auto-detected via lsusb)"
elif lsusb 2>/dev/null | grep -qi "0463"; then
driver="usbhid-ups"
port="auto"
desc="Eaton UPS (auto-detected via lsusb)"
elif lsusb 2>/dev/null | grep -qi "06da"; then
driver="usbhid-ups"
port="auto"
desc="Phoenixtec/Tripp Lite UPS (auto-detected via lsusb)"
fi
fi
if [[ -z "$driver" ]]; then
warn "No USB UPS detected. Using generic usbhid-ups driver with auto port."
warn "You may need to edit /etc/nut/ups.conf manually."
driver="usbhid-ups"
port="auto"
desc="UPS (not auto-detected)"
else
ok "Detected UPS: ${desc:-unknown}"
fi
# Export for use in config generation
UPS_DRIVER="$driver"
UPS_PORT="$port"
UPS_DESC="${desc:-UPS}"
}
generate_configs() {
info "Generating NUT configuration"
# /etc/nut/nut.conf — standalone mode
sudo tee /etc/nut/nut.conf > /dev/null <<'NUTCONF'
# Vigilar NUT configuration — standalone mode
MODE=standalone
NUTCONF
# /etc/nut/ups.conf — UPS definition
sudo tee /etc/nut/ups.conf > /dev/null <<UPSCONF
# Vigilar UPS configuration
# Generated by setup_nut.sh
[ups]
driver = ${UPS_DRIVER}
port = ${UPS_PORT}
desc = "${UPS_DESC}"
pollinterval = 15
UPSCONF
# /etc/nut/upsd.conf — daemon config (localhost only)
sudo tee /etc/nut/upsd.conf > /dev/null <<'UPSDCONF'
# Vigilar upsd configuration
LISTEN 127.0.0.1 3493
UPSDCONF
# /etc/nut/upsd.users — local monitoring user
sudo tee /etc/nut/upsd.users > /dev/null <<'USERSCONF'
[vigilar]
password = vigilar_local
upsmon master
USERSCONF
# /etc/nut/upsmon.conf — monitoring config
sudo tee /etc/nut/upsmon.conf > /dev/null <<'MONCONF'
# Vigilar upsmon configuration
MONITOR ups@localhost 1 vigilar vigilar_local master
MINSUPPLIES 1
SHUTDOWNCMD "/sbin/shutdown -h +0"
POLLFREQ 15
POLLFREQALERT 5
HOSTSYNC 15
DEADTIME 45
RBWARNTIME 43200
NOCOMMWARNTIME 600
FINALDELAY 5
MONCONF
# Secure the config files
sudo chmod 0640 /etc/nut/ups.conf /etc/nut/upsd.conf /etc/nut/upsd.users /etc/nut/upsmon.conf
sudo chown root:nut /etc/nut/ups.conf /etc/nut/upsd.conf /etc/nut/upsd.users /etc/nut/upsmon.conf 2>/dev/null || true
ok "NUT configuration files written"
}
enable_services() {
info "Enabling and starting NUT services"
# Service names vary by distro
local driver_svc="nut-driver"
local server_svc="nut-server"
local monitor_svc="nut-monitor"
# On Arch, the services may be named differently
if ! systemctl list-unit-files "${server_svc}.service" &>/dev/null; then
if systemctl list-unit-files "upsd.service" &>/dev/null; then
server_svc="upsd"
monitor_svc="upsmon"
driver_svc="nut-driver-enumerator"
fi
fi
# Enable and start
for svc in "$driver_svc" "$server_svc" "$monitor_svc"; do
if systemctl list-unit-files "${svc}.service" &>/dev/null; then
sudo systemctl enable "${svc}.service" 2>/dev/null || true
sudo systemctl restart "${svc}.service" 2>/dev/null || warn "Could not start ${svc}.service — check UPS connection"
fi
done
ok "NUT services enabled"
}
main() {
info "=== Vigilar NUT Setup ==="
echo
install_nut
UPS_DRIVER=""
UPS_PORT=""
UPS_DESC=""
detect_ups
generate_configs
enable_services
echo
ok "=== NUT setup complete ==="
info "Test with: upsc ups@localhost"
info "Vigilar will monitor UPS at 127.0.0.1:3493 (ups name: 'ups')"
}
main "$@"

130
scripts/uninstall.sh Executable file
View File

@@ -0,0 +1,130 @@
#!/usr/bin/env bash
set -euo pipefail
# Vigilar — Uninstall script
# Stops services, removes venv, systemd units, and mosquitto config.
# Data and config are preserved by default.
VIGILAR_USER="vigilar"
INSTALL_DIR="/opt/vigilar"
VENV_DIR="${INSTALL_DIR}/venv"
CONFIG_DIR="/etc/vigilar"
DATA_DIR="/var/vigilar"
SYSTEMD_DIR="/etc/systemd/system"
MOSQUITTO_CONF="/etc/mosquitto/conf.d/vigilar.conf"
info() { printf '\033[1;34m[INFO]\033[0m %s\n' "$*"; }
ok() { printf '\033[1;32m[ OK ]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[WARN]\033[0m %s\n' "$*"; }
# ---------------------------------------------------------------------------
# 1. Stop and disable services
# ---------------------------------------------------------------------------
stop_services() {
info "Stopping and disabling services"
if systemctl is-active vigilar.service &>/dev/null; then
sudo systemctl stop vigilar.service
fi
if systemctl is-enabled vigilar.service &>/dev/null; then
sudo systemctl disable vigilar.service
fi
ok "Services stopped"
}
# ---------------------------------------------------------------------------
# 2. Remove systemd units
# ---------------------------------------------------------------------------
remove_systemd() {
info "Removing systemd unit"
if [[ -f "${SYSTEMD_DIR}/vigilar.service" ]]; then
sudo rm -f "${SYSTEMD_DIR}/vigilar.service"
sudo systemctl daemon-reload
fi
ok "Systemd unit removed"
}
# ---------------------------------------------------------------------------
# 3. Remove mosquitto config
# ---------------------------------------------------------------------------
remove_mosquitto_conf() {
info "Removing Vigilar mosquitto config"
if [[ -f "$MOSQUITTO_CONF" ]]; then
sudo rm -f "$MOSQUITTO_CONF"
sudo systemctl restart mosquitto.service 2>/dev/null || true
fi
ok "Mosquitto config removed"
}
# ---------------------------------------------------------------------------
# 4. Remove venv and install dir
# ---------------------------------------------------------------------------
remove_venv() {
info "Removing venv at ${VENV_DIR}"
if [[ -d "$VENV_DIR" ]]; then
sudo rm -rf "$VENV_DIR"
fi
if [[ -d "$INSTALL_DIR" ]]; then
sudo rm -rf "$INSTALL_DIR"
fi
ok "Venv removed"
}
# ---------------------------------------------------------------------------
# 5. Remove system user
# ---------------------------------------------------------------------------
remove_user() {
if id "$VIGILAR_USER" &>/dev/null; then
info "Removing system user '${VIGILAR_USER}'"
sudo userdel "$VIGILAR_USER" 2>/dev/null || true
ok "User removed"
fi
}
# ---------------------------------------------------------------------------
# 6. Optionally remove data
# ---------------------------------------------------------------------------
remove_data() {
if [[ -d "$DATA_DIR" ]]; then
echo
warn "Data directory exists: ${DATA_DIR}"
warn "This contains recordings, database, and HLS segments."
read -rp "Delete data directory? [y/N] " answer
if [[ "$answer" =~ ^[Yy]$ ]]; then
sudo rm -rf "$DATA_DIR"
ok "Data directory removed"
else
info "Data preserved at ${DATA_DIR}"
fi
fi
}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
main() {
info "=== Vigilar Uninstaller ==="
echo
stop_services
remove_systemd
remove_mosquitto_conf
remove_venv
remove_user
remove_data
echo
ok "=== Uninstall complete ==="
info "Config and secrets preserved at ${CONFIG_DIR}/"
info "To remove config/secrets too: sudo rm -rf ${CONFIG_DIR}"
}
main "$@"