BastionGuard/webui/scripts/install-webui-helpers.sh
specialworld83 f0f913a209 Release 2.0
2026-07-15 10:52:22 +02:00

197 lines
9 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
ACTION=install
case "${1:-}" in
uninstall|--uninstall|remove|--remove) ACTION=uninstall; shift ;;
purge|--purge) ACTION=purge; shift ;;
install|--install) ACTION=install; shift ;;
esac
TARGET_USER="${1:-}"
if [[ -z "$TARGET_USER" ]]; then
if [[ -n "${SUDO_USER:-}" && "$SUDO_USER" != "root" ]]; then TARGET_USER="$SUDO_USER"; else TARGET_USER="$(logname 2>/dev/null || true)"; fi
fi
if [[ -z "$TARGET_USER" || ! "$TARGET_USER" =~ ^[A-Za-z_][A-Za-z0-9_-]*[$]?$ ]] || ! id "$TARGET_USER" >/dev/null 2>&1; then
echo "Specify the desktop user: sudo bash scripts/install-webui-helpers.sh \"$USER\"" >&2
exit 2
fi
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
uninstall_webui_helpers() {
local user="$1" home="" user_unit=""
echo "Uninstalling BastionGuard WebUI helpers for user: $user"
systemctl disable --now bastionguard-webui-inotify.service 2>/dev/null || true
rm -f /etc/systemd/system/bastionguard-webui-inotify.service
systemctl daemon-reload 2>/dev/null || true
systemctl reset-failed bastionguard-webui-inotify.service 2>/dev/null || true
home="$(getent passwd "$user" | cut -d: -f6)"
if [[ -n "$home" && -d "$home" ]]; then
user_unit="$home/.config/systemd/user/bastionguard-webui-inotify.service"
runuser -u "$user" -- systemctl --user disable --now bastionguard-webui-inotify.service 2>/dev/null || true
rm -f "$user_unit" "$user_unit.disabled-by-root-service" 2>/dev/null || true
fi
rm -f /usr/local/sbin/bastionguard-webui-systemctl
rm -f /usr/local/sbin/bastionguard-webui-admin
rm -f /usr/local/sbin/bastionguard-webui-wizard
rm -f /usr/local/libexec/bastionguard-webui-inotify-runner
rm -f /etc/sudoers.d/bastionguard-webui
rm -f "/run/bastionguard-webui-inotify-$user.sh" "/run/bastionguard-webui-inotify-$user.paths" "/run/bastionguard-webui-inotify-$user.pid" 2>/dev/null || true
rm -rf /run/bastionguard-webui 2>/dev/null || true
if [[ "$ACTION" == "purge" ]]; then
rm -rf /var/lib/bastionguard-webui
echo "Persistent data removed: /var/lib/bastionguard-webui"
else
echo "Persistent data kept: /var/lib/bastionguard-webui"
fi
echo "BastionGuard WebUI helpers uninstalled."
}
if [[ "$ACTION" == "uninstall" || "$ACTION" == "purge" ]]; then
uninstall_webui_helpers "$TARGET_USER"
exit 0
fi
# Font policy: the WebUI template uses a vendored Cantarell asset only.
# Expected path: webui/assets/fonts/Cantarell-VF.otf
if [[ ! -f "$SCRIPT_DIR/../assets/fonts/Cantarell-VF.otf" ]]; then
if [[ -x "$SCRIPT_DIR/vendor-cantarell-font.sh" ]]; then
bash "$SCRIPT_DIR/vendor-cantarell-font.sh" || echo "Warning: vendored Cantarell font missing: webui/assets/fonts/Cantarell-VF.otf" >&2
else
echo "Warning: vendored Cantarell font missing: webui/assets/fonts/Cantarell-VF.otf" >&2
fi
fi
install -o root -g root -m 0755 "$SCRIPT_DIR/bastionguard-webui-systemctl" /usr/local/sbin/bastionguard-webui-systemctl
install -o root -g root -m 0755 "$SCRIPT_DIR/bastionguard-webui-admin" /usr/local/sbin/bastionguard-webui-admin
install -o root -g root -m 0755 "$SCRIPT_DIR/bastionguard-webui-wizard" /usr/local/sbin/bastionguard-webui-wizard
install -d -o root -g root -m 0755 /usr/local/libexec
install -o root -g root -m 0755 "$SCRIPT_DIR/bastionguard-webui-inotify-runner" /usr/local/libexec/bastionguard-webui-inotify-runner
WEB_USERS=()
for u in www-data http apache nginx caddy; do
if id "$u" >/dev/null 2>&1; then WEB_USERS+=("$u"); fi
done
if [[ ${#WEB_USERS[@]} -eq 0 ]]; then
echo "No known web-server user found. Add sudoers manually for your PHP-FPM user." >&2
exit 3
fi
# Data dir persistente usata dalla WebUI per opzioni, cache e stato locale.
DATA_DIR=/var/lib/bastionguard-webui
mkdir -p "$DATA_DIR"
chown "${WEB_USERS[0]}:${WEB_USERS[0]}" "$DATA_DIR" 2>/dev/null || chown "${WEB_USERS[0]}" "$DATA_DIR" 2>/dev/null || true
chmod 0750 "$DATA_DIR" || true
# WebUI language/theme are session-only. Remove older persisted values if present.
if command -v python3 >/dev/null 2>&1 && [[ -f "$DATA_DIR/web_options.json" ]]; then
python3 - "$DATA_DIR/web_options.json" <<'PYWEBOPTS' || true
import json, os, sys
path = sys.argv[1]
try:
with open(path, 'r', encoding='utf-8') as fh:
data = json.load(fh)
if isinstance(data, dict):
changed = False
for key in ('language', 'theme'):
if key in data:
data.pop(key, None)
changed = True
if changed:
tmp = path + '.tmp'
with open(tmp, 'w', encoding='utf-8') as fh:
json.dump(data, fh, ensure_ascii=False, indent=2)
fh.write('\n')
os.replace(tmp, path)
except Exception:
pass
PYWEBOPTS
fi
# Persistent root/system service for the realtime WebPanel monitor.
# This replaces the old --user unit so the monitor can inspect root-owned paths
# allowed by the WebUI policy (/var/www, /srv, /tmp, /home, mounted media, ...).
TARGET_HOME="$(getent passwd "$TARGET_USER" | cut -d: -f6)"
SYSTEM_UNIT=/etc/systemd/system/bastionguard-webui-inotify.service
cat > "$SYSTEM_UNIT" <<EOFUNIT
[Unit]
Description=BastionGuard WebPanel realtime malware and ransomware monitor (root)
Documentation=file:///srv/http/webui/README.md
After=network.target clamav-daemon.service
Wants=clamav-daemon.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/bastionguard-webui-admin inotify-start-saved $TARGET_USER
ExecStartPost=/usr/local/sbin/bastionguard-webui-admin ransomware-realtime-start $TARGET_USER
ExecStop=/usr/local/sbin/bastionguard-webui-admin inotify-stop $TARGET_USER
TimeoutStartSec=60
TimeoutStopSec=30
[Install]
WantedBy=multi-user.target
EOFUNIT
chmod 0644 "$SYSTEM_UNIT"
systemctl daemon-reload 2>/dev/null || true
# Disable the legacy per-user unit if an older WebUI package installed it.
if [[ -n "${TARGET_HOME:-}" && -d "$TARGET_HOME" ]]; then
USER_UNIT="$TARGET_HOME/.config/systemd/user/bastionguard-webui-inotify.service"
if [[ -f "$USER_UNIT" ]]; then
runuser -u "$TARGET_USER" -- systemctl --user disable --now bastionguard-webui-inotify.service 2>/dev/null || true
mv -f "$USER_UNIT" "$USER_UNIT.disabled-by-root-service" 2>/dev/null || true
fi
fi
# Remove legacy dynamic runtime scripts generated by older WebUI builds.
# The realtime worker is now a static root-owned helper under /usr/local/libexec.
rm -f "/run/bastionguard-webui-inotify-$TARGET_USER.sh" "/run/bastionguard-webui-inotify-$TARGET_USER.paths" 2>/dev/null || true
install -d -o root -g root -m 0755 /run/bastionguard-webui 2>/dev/null || true
SUDOERS="/etc/sudoers.d/bastionguard-webui"
TMP="$(mktemp)"
{
echo "# BastionGuard WebUI: controlled helpers for $TARGET_USER"
echo "Cmnd_Alias BASTIONGUARD_WEBUI_SYSTEMCTL = /usr/local/sbin/bastionguard-webui-systemctl user $TARGET_USER *"
echo "Cmnd_Alias BASTIONGUARD_WEBUI_ADMIN = /usr/local/sbin/bastionguard-webui-admin *"
echo "Cmnd_Alias BASTIONGUARD_WEBUI_INOTIFY = /usr/local/sbin/bastionguard-webui-admin inotify-start-saved $TARGET_USER, /usr/local/sbin/bastionguard-webui-admin inotify-stop $TARGET_USER, /usr/local/sbin/bastionguard-webui-admin inotify-status $TARGET_USER, /usr/local/sbin/bastionguard-webui-admin inotify-service-stop $TARGET_USER, /usr/local/sbin/bastionguard-webui-admin inotify-service-status $TARGET_USER, /usr/local/sbin/bastionguard-webui-admin ransomware-realtime-start $TARGET_USER"
echo "Cmnd_Alias BASTIONGUARD_WEBUI_WIZARD = /usr/local/sbin/bastionguard-webui-wizard *"
for u in "${WEB_USERS[@]}"; do
echo "$u ALL=(root) NOPASSWD: BASTIONGUARD_WEBUI_SYSTEMCTL, BASTIONGUARD_WEBUI_ADMIN, BASTIONGUARD_WEBUI_WIZARD"
done
echo "$TARGET_USER ALL=(root) NOPASSWD: BASTIONGUARD_WEBUI_INOTIFY"
} > "$TMP"
chmod 0440 "$TMP"
if command -v visudo >/dev/null 2>&1; then visudo -cf "$TMP" >/dev/null; fi
install -o root -g root -m 0440 "$TMP" "$SUDOERS"
rm -f "$TMP"
cat <<EOF
BastionGuard WebUI helpers installed.
Desktop user: $TARGET_USER
Authorized web users: ${WEB_USERS[*]}
Files:
/usr/local/sbin/bastionguard-webui-systemctl
/usr/local/sbin/bastionguard-webui-admin
/usr/local/sbin/bastionguard-webui-wizard
/usr/local/libexec/bastionguard-webui-inotify-runner
$SUDOERS
Optional runtime tools for realtime/Samba features: inotifywait from inotify-tools, smbclient from Samba. Ransomware realtime uses BastionGuard-ransomware-realtime.service when installed.
Persistent realtime service: sudo systemctl enable --now bastionguard-webui-inotify.service
Test:
sudo -u ${WEB_USERS[0]} sudo -n /usr/local/sbin/bastionguard-webui-admin ensure-configs $TARGET_USER
sudo -u ${WEB_USERS[0]} sudo -n /usr/local/sbin/bastionguard-webui-admin scan-path-b64 $(printf /tmp | base64 -w0)
sudo -u ${WEB_USERS[0]} sudo -n /usr/local/sbin/bastionguard-webui-admin inotify-status $TARGET_USER
sudo -u ${WEB_USERS[0]} sudo -n /usr/local/sbin/bastionguard-webui-admin samba-scan-b64 $(printf /tmp | base64 -w0) $TARGET_USER
sudo -u ${WEB_USERS[0]} sudo -n /usr/local/sbin/bastionguard-webui-admin journal system - 20 clamav-freshclam.service
sudo -u ${WEB_USERS[0]} sudo -n /usr/local/sbin/bastionguard-webui-wizard update-banks $TARGET_USER
EOF