#!/usr/bin/env bash # Vacuum Wall - SSL Proxy Firewall Appliance Installer # Run as root on a fresh Debian 13 (trixie) system set -euo pipefail RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' log() { echo -e "${GREEN}[OK]${NC} $*"; } warn() { echo -e "${YELLOW}[!!]${NC} $*"; } err() { echo -e "${RED}[!!]${NC} $*"; exit 1; } PROJECT_DIR="/home/wall/vacuum-wall" USER_NAME="vacuum-wall" USER_HOME="/home/$USER_NAME" DOMAIN="${MGMT_DOMAIN:?ERROR: Set MGMT_DOMAIN env var (e.g., wall.lan)}" MGMT_USER="${MGMT_USER:-admin}" MGMT_PASS="${MGMT_PASS:?ERROR: Set MGMT_PASS env var for WebUI basic auth}" ACME_EMAIL="${ACME_EMAIL:?ERROR: Set ACME_EMAIL env var for Let's Encrypt}" # --- Pre-flight checks --- [[ $EUID -eq 0 ]] || err "This script must be run as root." [[ -f /etc/debian_version ]] || warn "This script is designed for Debian/Ubuntu." echo "============================================" echo " Vacuum Wall Appliance Installer" echo " Management domain: $DOMAIN" echo "============================================" # --- 1. Install packages --- log "Installing system packages..." apt-get update -qq apt-get install -y -qq \ firewalld \ nginx \ dnsmasq \ wireguard-tools \ python3 \ python3-pip \ jq \ curl \ iptables \ nftables \ apache2-utils # Install acme.sh under the project user's home if [[ ! -d "$USER_HOME/.acme.sh" ]]; then log "Installing acme.sh..." mkdir -p "$USER_HOME" ACME_HOME="$USER_HOME/.acme.sh" curl -sS https://get.acme.sh | sh else log "acme.sh already installed." fi # Setup Python venv with project dependencies log "Setting up Python virtual environment..." python3 -m venv "${PROJECT_DIR}/.venv" "${PROJECT_DIR}/.venv/bin/pip" install -q "${PROJECT_DIR}" chown -R "$USER_NAME:$USER_NAME" "${PROJECT_DIR}/.venv" # --- 2. Create system user --- if ! id "$USER_NAME" &>/dev/null; then log "Creating system user $USER_NAME..." useradd --system --home-dir "$USER_HOME" --shell /usr/sbin/nologin "$USER_NAME" else log "User $USER_NAME already exists." fi # --- 3. Setup directories --- log "Creating data directories..." mkdir -p "${PROJECT_DIR}/data"/{nginx/sites-enabled,dnsmasq,firewall,wireguard} mkdir -p "$USER_HOME/vacuum-wall" mkdir -p /etc/wireguard mkdir -p /etc/dnsmasq chown -R "$USER_NAME:$USER_NAME" "${PROJECT_DIR}/data" chown -R "$USER_NAME:$USER_NAME" "${PROJECT_DIR}/lib" chown -R "$USER_NAME:$USER_NAME" "${PROJECT_DIR}/webui" chown -R "$USER_NAME:$USER_NAME" "$USER_HOME/vacuum-wall" # --- 4. Install sudoers --- log "Installing sudoers whitelist..." install -m 0440 "${PROJECT_DIR}/system/sudoers.d/vacuum-wall" /etc/sudoers.d/vacuum-wall # Validate sudoers syntax visudo -cf /etc/sudoers.d/vacuum-wall || err "Invalid sudoers file!" # --- 5. Enable IP forwarding --- log "Enabling IP forwarding..." if ! grep -q "^net.ipv4.ip_forward=1" /etc/sysctl.conf 2>/dev/null; then echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf fi sysctl -w net.ipv4.ip_forward=1 2>/dev/null || warn "Could not enable IP forwarding (may need kernel access)" # --- 6. Start and configure firewalld --- log "Enabling firewalld..." systemctl enable firewalld || warn "Could not enable firewalld (already running?)" systemctl start firewalld || warn "Could not start firewalld (may need D-Bus)" # Allow management access (HTTP/HTTPS for the proxy) firewall-cmd --permanent --add-service=http 2>/dev/null || true firewall-cmd --permanent --add-service=https 2>/dev/null || true firewall-cmd --permanent --add-service=ssh 2>/dev/null || true firewall-cmd --reload 2>/dev/null || true # --- 7. Configure dnsmasq --- log "Configuring dnsmasq..." systemctl enable dnsmasq || true systemctl start dnsmasq || warn "Could not start dnsmasq (no interfaces configured yet)" # --- 8. Setup nginx management proxy --- log "Generating self-signed certificate for management domain..." mkdir -p "$USER_HOME/.acme.sh/$DOMAIN" openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout "$USER_HOME/.acme.sh/$DOMAIN/$DOMAIN.key" \ -out "$USER_HOME/.acme.sh/$DOMAIN/fullchain.cer" \ -subj "/CN=$DOMAIN" \ -addext "subjectAltName=DNS:$DOMAIN" chown -R "$USER_NAME:$USER_NAME" "$USER_HOME/.acme.sh" # Generate htpasswd (credentials passed via environment to avoid shell injection) mkdir -p "$USER_HOME/vacuum-wall" htpasswd -cb "$USER_HOME/vacuum-wall/.htpasswd" "$MGMT_USER" "$MGMT_PASS" 2>/dev/null || \ MGMT_USER="$MGMT_USER" MGMT_PASS="$MGMT_PASS" python3 -c " import os, crypt, base64 password = os.environ['MGMT_PASS'] user = os.environ['MGMT_USER'] salt = '\$6\$' + base64.b64encode(os.urandom(16)).decode().rstrip('=')[:16] hashed = crypt.crypt(password, salt) with open('$USER_HOME/vacuum-wall/.htpasswd', 'w') as f: f.write(user + ':' + hashed + '\n') " 2>/dev/null || \ warn "Could not generate htpasswd (install apache2-utils or python3-crypt)" chown "$USER_NAME:$USER_NAME" "$USER_HOME/vacuum-wall/.htpasswd" 2>/dev/null # Write WebSocket upgrade map (nginx conf.d/ is already inside http {} context) cat > /etc/nginx/conf.d/vacuum-wall-map.conf <<'MAPEOF' # Vacuum Wall - WebSocket upgrade map map $http_upgrade $connection_upgrade { default upgrade; '' close; } MAPEOF # Write the management site block (conf.d/ is inside http {}, no extra http {} needed) cat > /etc/nginx/conf.d/vacuum-wall-mgmt.conf </dev/null || true firewall-cmd --permanent --zone=internal --set-target=ACCEPT 2>/dev/null || true firewall-cmd --permanent --zone=internal --add-service=dhcp 2>/dev/null || true firewall-cmd --permanent --zone=internal --add-service=dns 2>/dev/null || true firewall-cmd --permanent --zone=internal --add-service=ntp 2>/dev/null || true # VPN zone firewall-cmd --permanent --new-zone=vpn 2>/dev/null || true firewall-cmd --permanent --zone=vpn --set-target=ACCEPT 2>/dev/null || true # Public/external zone defaults are fine firewall-cmd --reload 2>/dev/null || true # --- 11. Enable and start services --- log "Enabling services..." systemctl enable nginx systemctl enable vacuum-wall systemctl enable vacuum-wall-acme.timer systemctl start nginx 2>/dev/null || warn "Could not start nginx (check config)" systemctl start vacuum-wall 2>/dev/null || warn "Could not start vacuum-wall WebUI" # --- 12. Configure acme.sh default email --- log "Configuring acme.sh default email..." "$USER_HOME/.acme.sh/acme.sh" --register-account -m "$ACME_EMAIL" 2>/dev/null || \ warn "Could not register acme.sh account (will be done from WebUI)" # --- Done --- echo "" echo "============================================" echo -e " ${GREEN}Vacuum Wall installed successfully!${NC}" echo "============================================" echo "" echo " Management UI: https://$DOMAIN" echo " User: $MGMT_USER" echo " WebUI service: vacuum-wall.service" echo " ACME renewal: vacuum-wall-acme.timer" echo "" echo " Next steps:" echo " 1. Assign interfaces to zones from the WebUI" echo " 2. Configure DHCP ranges for your LAN" echo " 3. Add proxy domains with Let's Encrypt certs" echo " 4. Set up WireGuard tunnel (optional)" echo "" echo " NOTE: A self-signed certificate was generated." echo " From the WebUI, issue a real certificate for $DOMAIN" echo " when DNS points to this appliance." echo ""