65741644a3
- dashboard.html: Fix zones, leases, wg_status, cert key names, add services var
- server.py: Pass services to dashboard template via _get_service_status()
- lib/acme.py: Fix dead third date format (%Y%m%d%H%M%z) using astimezone(UTC)
- lib/wireguard.py: Add -- separator to cp command to match sudoers rule
- lib/nginx.py: Replace shallow dict.copy() with {**...} for DEFAULT_SSL
- AGENTS.md: Update test count 149 -> 154
- docs/api.md: Rename cert field expiry -> expires_at
271 lines
11 KiB
Markdown
271 lines
11 KiB
Markdown
# Vacuum Wall Deployment Guide
|
|
|
|
This guide walks through deploying Vacuum Wall on a real appliance or server. Vacuum Wall is an SSL proxy firewall appliance that combines edge proxying, firewall management, DHCP, DNS, and WireGuard in a single device.
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
- **OS**: Clean Debian 13 (Trixie) system. Also works on Debian 12 with backports for firewalld.
|
|
- **git**: Required for cloning the repository.
|
|
- **Access**: Root access to the machine.
|
|
- **Networking**:
|
|
- One public-facing network interface (external/edge). This receives inbound traffic and serves the management UI.
|
|
- At least one LAN network interface (internal). This connects to your downstream network and will serve DHCP/DNS.
|
|
- **DNS**: A DNS record pointing to the appliance's public IP for the management domain (e.g., `wall.example.com`).
|
|
- **Minimum hardware**: 1 CPU, 512 MB RAM, 4 GB disk.
|
|
|
|
---
|
|
|
|
## Installation
|
|
|
|
Download the Vacuum Wall repository onto the target machine, then run the installer with the required environment variables:
|
|
|
|
```bash
|
|
MGMT_DOMAIN=wall.example.com \
|
|
MGMT_PASS="strongpassword" \
|
|
MGMT_USER="admin" \
|
|
ACME_EMAIL="admin@example.com" \
|
|
bash install.sh
|
|
```
|
|
|
|
### Environment Variables
|
|
|
|
| Variable | Required | Description |
|
|
|---|---|---|
|
|
| `MGMT_DOMAIN` | Yes | The public-facing domain for the management WebUI. A DNS A record must point to the appliance's IP. |
|
|
| `MGMT_PASS` | Yes | The password for HTTP basic auth protecting the WebUI. Use a strong, randomly generated password. |
|
|
| `MGMT_USER` | No | The username for WebUI access. Defaults to `admin`. |
|
|
| `ACME_EMAIL` | Yes | The email address registered with Let's Encrypt for certificate issuance and expiry notifications. |
|
|
|
|
---
|
|
|
|
## What install.sh Does
|
|
|
|
The installer performs the following steps automatically:
|
|
|
|
- **Package installation**: Installs firewalld, nginx, dnsmasq, wireguard-tools, python3, Flask, pip, jq, curl, iptables, nftables, and apache2-utils.
|
|
- **acme.sh installation**: Downloads and installs the acme.sh client to the project user's home directory for Let's Encrypt certificate management.
|
|
- **Flask installation**: Ensures the Flask Python package is available via pip for the WebUI backend.
|
|
- **System user creation**: Creates a dedicated `vacuum-wall` system user (nologin shell) that owns the project data and runs the WebUI service.
|
|
- **Directory setup**: Creates data directories under `/home/wall/vacuum-wall/data/` for nginx sites, dnsmasq config, firewall rules, and WireGuard config. Sets ownership to the `vacuum-wall` user.
|
|
- **Sudoers whitelist**: Installs a restrictive sudoers file at `/etc/sudoers.d/vacuum-wall` allowing the `vacuum-wall` user to run only the specific privileged commands needed for firewall, nginx, and dnsmasq management. Validates syntax with `visudo -cf`.
|
|
- **IP forwarding**: Enables `net.ipv4.ip_forward=1` in sysctl.conf and applies it at runtime, required for routing traffic between zones.
|
|
- **Firewalld initialization**: Starts and enables firewalld. Opens HTTP, HTTPS, and SSH services on the public zone for management access.
|
|
- **Dnsmasq initialization**: Starts and enables dnsmasq for future DHCP/DNS serving on internal interfaces.
|
|
- **Self-signed certificate**: Generates a temporary self-signed X.509 certificate for the management domain with the correct CN and SAN, placed where acme.sh would store a real cert.
|
|
- **Management proxy configuration**: Configures nginx as a reverse proxy that forward-proxies to the WebUI at `127.0.0.1:9090`, with HTTP-to-HTTPS redirect, basic auth, and WebSocket upgrade support.
|
|
- **Credentials**: Generates an htpasswd file using `apache2-utils` (with a Python fallback) for the management proxy's basic auth. Copies it to both `$USER_HOME/vacuum-wall/.htpasswd` (used by install.sh's initial nginx config) and `data/nginx/.htpasswd` (used by the running app).
|
|
- **Systemd units**: Installs three units:
|
|
- `vacuum-wall.service` — the Flask WebUI backend.
|
|
- `vacuum-wall-acme.service` — the certificate renewal oneshot.
|
|
- `vacuum-wall-acme.timer` — periodic timer that triggers cert renewals.
|
|
- **Firewalld zones**: Creates initial zones:
|
|
- `internal` — trusted LAN zone with DHCP, DNS, and NTP services allowed.
|
|
- `vpn` — WireGuard tunnel zone.
|
|
- **Service startup**: Enables and starts nginx, the vacuum-wall WebUI, and the ACME renewal timer.
|
|
- **ACME registration**: Registers the Let's Encrypt account with the provided email via acme.sh.
|
|
|
|
---
|
|
|
|
## Post-Installation
|
|
|
|
### Verify Services
|
|
|
|
After the installer completes, confirm all services are running:
|
|
|
|
```bash
|
|
systemctl status vacuum-wall nginx firewalld dnsmasq
|
|
```
|
|
|
|
Each should be active (running). The `vacuum-wall-acme.timer` should also be active (waiting).
|
|
|
|
### Access the WebUI
|
|
|
|
Open a browser and navigate to:
|
|
|
|
```
|
|
https://wall.example.com
|
|
```
|
|
|
|
Log in with the username and password you provided during installation.
|
|
|
|
### Certificate Note
|
|
|
|
The initial certificate is **self-signed** and generated during installation. Your browser will show a security warning. This is expected. Once DNS is pointing to the appliance and port 80 is accessible from the internet, use the **Certs** tab in the WebUI to issue a real Let's Encrypt certificate for the management domain. After issuance, go to the **Proxy** tab and click **Apply** to reload nginx with the new cert.
|
|
|
|
---
|
|
|
|
## Configuring Your First Network
|
|
|
|
After installation, the appliance has no interfaces assigned to zones and no DHCP ranges configured. Use the WebUI to set up your LAN.
|
|
|
|
### 1. Assign a LAN Interface to the Internal Zone
|
|
|
|
1. Navigate to the **Interfaces** tab.
|
|
2. From the interface list, select your LAN interface (e.g., `eth1`).
|
|
3. Assign it to the `internal` zone.
|
|
4. Click **Apply** to update the firewall configuration.
|
|
|
|
### 2. Enable NAT/Masquerade
|
|
|
|
1. Go to the **NAT** tab.
|
|
2. Enable masquerade on the `internal` zone. This allows devices on your LAN to reach the internet through the appliance's external interface.
|
|
3. Click **Apply**.
|
|
|
|
### 3. Configure DHCP
|
|
|
|
1. Go to the **DHCP** tab.
|
|
2. Click **Add Range**.
|
|
3. Specify:
|
|
- Address range: e.g., `192.168.2.100-192.168.2.200`
|
|
- Lease time: e.g., `12h`
|
|
- Interface: `eth1` (or whichever interface you assigned to internal)
|
|
4. Click **Apply**. This writes the dnsmasq configuration and reloads the service.
|
|
|
|
DNS resolution will also be provided on this interface by dnsmasq, which forwards queries upstream.
|
|
|
|
---
|
|
|
|
## Adding a Proxy Domain
|
|
|
|
Vacuum Wall's primary function is proxying incoming HTTPS traffic to internal backend services.
|
|
|
|
### 1. Add the Domain
|
|
|
|
1. Navigate to the **Proxy** tab.
|
|
2. Click **Add Domain**.
|
|
3. Fill in:
|
|
- **Domain**: The public domain name (e.g., `app.example.com`).
|
|
- **Backend Host**: The internal IP address of the service (e.g., `192.168.2.50`).
|
|
- **Backend Port**: The port the service listens on (e.g., `8080`).
|
|
|
|
### 2. Issue a Certificate
|
|
|
|
1. Go to the **Certs** tab.
|
|
2. Click **Issue Certificate** and enter the domain name.
|
|
3. ACME validation requires that port 80 on the appliance is reachable from the internet and that the domain's DNS A record points to the appliance's public IP.
|
|
|
|
### 3. Reload Nginx
|
|
|
|
1. Return to the **Proxy** tab.
|
|
2. Click **Apply** to write the nginx configuration and reload the service.
|
|
|
|
The proxied domain is now accessible via HTTPS at the configured domain name.
|
|
|
|
---
|
|
|
|
## Setting up WireGuard
|
|
|
|
Vacuum Wall includes integrated WireGuard server support for VPN access.
|
|
|
|
### 1. Initialize the Server
|
|
|
|
1. Navigate to the **WireGuard** tab.
|
|
2. Click **Initialize**. This generates the server's private and public keys and creates the `wg0` interface configuration.
|
|
|
|
### 2. Add a Peer
|
|
|
|
1. Click **Add Peer**.
|
|
2. Enter a peer name (e.g., `alice`).
|
|
3. Optionally set a specific AllowedIPs range for this peer (defaults to `0.0.0.0/0`).
|
|
4. Optionally set an **Endpoint** if you know the peer's static public IP (restricts incoming connections to that IP).
|
|
5. Click **Add**. The peer's public key and preshared key are generated automatically.
|
|
|
|
### 3. Activate the Tunnel
|
|
|
|
1. Click **Apply** to write the WireGuard configuration and bring up the `wg0` interface.
|
|
|
|
### 4. Download Client Configuration
|
|
|
|
1. In the peer list, use the peer actions menu to download the client configuration file for the peer.
|
|
2. Install this configuration on the client device.
|
|
|
|
### 5. Assign WireGuard to a Firewall Zone
|
|
|
|
1. Navigate to the **Interfaces** tab.
|
|
2. Assign `wg0` to the `vpn` zone.
|
|
3. The `vpn` zone allows all traffic by default (target ACCEPT). Adjust firewall rules as needed to restrict VPN access to specific services.
|
|
|
|
### 6. Configure Firewall Rules for VPN Traffic
|
|
|
|
1. Go to the **Firewall** tab or use the **NAT** tab.
|
|
2. Add rules as needed to control what VPN peers can access. For example, you can restrict VPN peers to only reach specific internal services rather than the entire LAN.
|
|
3. Optionally enable masquerade on the `vpn` zone to allow VPN clients to reach the internet through the appliance.
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Services Won't Start
|
|
|
|
Check service logs and configuration:
|
|
|
|
```bash
|
|
journalctl -u vacuum-wall --no-pager -n 50
|
|
journalctl -u nginx --no-pager -n 50
|
|
nginx -t
|
|
```
|
|
|
|
Common causes include port conflicts (another service on port 80/443), missing dependencies, or file permission issues on `/home/wall/vacuum-wall/data/`.
|
|
|
|
### Firewall Rules Not Applying
|
|
|
|
Verify that firewalld is running:
|
|
|
|
```bash
|
|
firewall-cmd --state
|
|
systemctl status firewalld
|
|
```
|
|
|
|
If firewalld is not running, start it with `systemctl start firewalld`. Check that the sudoers whitelist is valid:
|
|
|
|
```bash
|
|
visudo -cf /etc/sudoers.d/vacuum-wall
|
|
```
|
|
|
|
### Certificate Issuance Fails
|
|
|
|
Let's Encrypt ACME validation requires:
|
|
|
|
- The domain's DNS A record points to the appliance's public IP.
|
|
- Port 80 (HTTP-01 challenge) is accessible from the internet on the external interface.
|
|
- The ACME email was registered correctly. Check with:
|
|
|
|
```bash
|
|
su -s /bin/bash vacuum-wall -c "~/.acme.sh/acme.sh --list"
|
|
```
|
|
|
|
If port 80 is blocked or the DNS record hasn't propagated yet, wait and retry. The ACME timer will also attempt renewal automatically.
|
|
|
|
### DHCP Not Working
|
|
|
|
Verify that:
|
|
|
|
- The LAN interface is assigned to a firewalld zone (check the **Interfaces** tab or `firewall-cmd --get-active-zones`).
|
|
- Dnsmasq is running: `systemctl status dnsmasq`.
|
|
- A DHCP range is configured for the correct interface. Check dnsmasq config at `/home/wall/vacuum-wall/data/dnsmasq/`.
|
|
- The firewall allows DHCP traffic on the internal zone: `firewall-cmd --zone=internal --list-services` should include `dhcp` and `dns`.
|
|
|
|
### WebUI Not Accessible
|
|
|
|
1. Verify nginx is running: `systemctl status nginx`.
|
|
2. Test nginx configuration: `nginx -t`.
|
|
2. Check the management proxy configuration at `/etc/nginx/conf.d/vacuum-wall-mgmt.conf` (initial) or via the WebUI Proxy tab (after first apply).
|
|
4. Ensure the `vacuum-wall` WebUI service is listening on port 9090: `ss -tlnp | grep 9090`.
|
|
5. If using the self-signed cert, confirm your browser trusts it or use the WebUI to issue a real Let's Encrypt certificate.
|
|
|
|
---
|
|
|
|
## Reference
|
|
|
|
| Component | Service | Config Location |
|
|
|---|---|---|
|
|
| WebUI backend | `vacuum-wall.service` | `/home/wall/vacuum-wall/webui/` |
|
|
| Reverse proxy | `nginx` | `/etc/nginx/conf.d/vacuum-wall-mgmt.conf` |
|
|
| Firewall | `firewalld` | Managed via WebUI and `firewall-cmd` |
|
|
| DHCP/DNS | `dnsmasq` | `/home/wall/vacuum-wall/data/dnsmasq/` |
|
|
| VPN | wireguard-tools | `/home/wall/vacuum-wall/data/wireguard/` |
|
|
| Certificates | `vacuum-wall-acme.timer` | `/home/vacuum-wall/.acme.sh/` |
|
|
| Sudoers | — | `/etc/sudoers.d/vacuum-wall` |
|