install: two-user model ownership and deployment
This commit is contained in:
+32
-5
@@ -26,7 +26,7 @@ Download the Vacuum Wall repository onto the target machine, then run the instal
|
||||
MGMT_DOMAIN=wall.example.com \
|
||||
MGMT_PASS="strongpassword" \
|
||||
ACME_EMAIL="admin@example.com" \
|
||||
bash install.sh
|
||||
./install.sh --user vacuum-wall
|
||||
|
||||
# Dev mode: CLI flags, auto-detects repo owner
|
||||
./install.sh --dev --mgmt-pass strongpassword --acme-email "admin@example.com"
|
||||
@@ -46,7 +46,7 @@ All settings that can be passed as an environment variable also have a CLI flag
|
||||
| `--mgmt-pass` | `MGMT_PASS` | Yes | Password for HTTP basic auth protecting the WebUI. |
|
||||
| `--mgmt-user` | `MGMT_USER` | No | Username for WebUI access. Defaults to `admin`. |
|
||||
| `--acme-email` | `ACME_EMAIL` | Yes | Email for ACME provider (ZeroSSL by default). |
|
||||
| `--user, -u` | `USER_NAME` | No | System user for the WebUI service. Defaults to `vacuum-wall`. |
|
||||
| `--user, -u` | `USER_NAME` | Yes* | WebUI service user (created if it does not exist). Required for non-dev mode. In `--dev` mode, auto-detected from repo owner. |
|
||||
| `--path, -p` | `INSTALL_DIR` | No | Install directory. Defaults to repo root. Set to deploy from a custom path (e.g., `/opt/vacuum-wall`). |
|
||||
| `--dev` | -- | No | Development mode: auto-detects repo owner as service user, skips safety warning. |
|
||||
| `--wan-iface` | `WAN_IFACE` | No | WAN interface name. Auto-detected from default gateway. |
|
||||
@@ -57,6 +57,33 @@ Run `./install.sh --help` for full usage.
|
||||
|
||||
---
|
||||
|
||||
## Dev Mode
|
||||
|
||||
The `--dev` flag is designed for developers working in a git clone. It auto-detects the repo owner and uses that user as the WebUI service user.
|
||||
|
||||
### Ownership Model
|
||||
|
||||
In dev mode, the ownership model preserves the developer's ability to work with the repository:
|
||||
|
||||
- **Project directory**: Owned by the repo owner (e.g., `wall`), group is the repo owner's primary group (e.g., `wall`). The developer retains full control — `git add`, `git commit`, editing code and config files all work normally.
|
||||
- **Daemon access**: The daemon user (`vacuum-walld`) has the repo owner's primary group as its own primary group, granting read access to all project files. The project directory has the setgid bit (`g+s`) on all subdirectories, ensuring new files inherit the group.
|
||||
- **`.venv/` and `data/`**: Owned by the repo owner, group is the repo owner's primary group. The developer can run `pip install`, inspect logs, and manage runtime artifacts. The daemon reads `.venv/` (Python interpreter) and writes to `data/` (runtime files) via group permissions.
|
||||
- **Daemon socket** (`data/daemon.sock`): Owned by `vacuum-walld:<group>` (mode `0660`). The repo owner accesses it via primary group membership.
|
||||
|
||||
### Running the Installer in Dev Mode
|
||||
|
||||
```bash
|
||||
./install.sh --dev --mgmt-pass strongpassword --acme-email "dev@example.com"
|
||||
```
|
||||
|
||||
The script detects the repo owner (e.g., `wall`), creates the `vacuum-walld` daemon user with the repo owner's primary group, and sets up the ownership model described above.
|
||||
|
||||
### Idempotent Re-Runs
|
||||
|
||||
Running `--dev` again is safe. The ownership is idempotent (`chown -R` to the same owner), supplementary group membership is deduplicated by the OS, and setgid bits are applied recursively each time.
|
||||
|
||||
---
|
||||
|
||||
## Container / Custom Deployment
|
||||
|
||||
You can deploy Vacuum Wall in a container or at any custom path. Use `--path` (or `INSTALL_DIR`) for the mount or bind path, and `--user` (or `USER_NAME`) for whatever system user exists:
|
||||
@@ -77,14 +104,14 @@ The systemd service unit files and sudoers whitelist are rendered from Jinja2 te
|
||||
The installer performs the following steps automatically:
|
||||
|
||||
- **Package installation**: Installs firewalld, nginx, dnsmasq, avahi-daemon, wireguard-tools, python3, python3-pip, jq, curl, iptables, nftables, and apache2-utils.
|
||||
- **Shared group creation**: Creates a shared system group (`vacuum-wall`) both service users belong to.
|
||||
- **WebUI user creation**: Creates the WebUI user (from `--user`) as a system user if it does not exist.
|
||||
- **Shared group**: Uses the WebUI user's primary group as the shared group between both service users.
|
||||
- **Daemon user creation**: Creates `vacuum-walld` (derived from WebUI user name) — a system user with `NOPASSWD` sudo access for privileged operations. Owns the project directory and daemon socket.
|
||||
- **WebUI user creation**: Creates a dedicated system user (default: `vacuum-wall`, configurable via `USER_NAME`) with zero sudo access. Communicates with the daemon via Unix socket.
|
||||
- **Python venv**: Creates the Python virtual environment and installs project dependencies. Skips if already present (use `--force-venv` to recreate).
|
||||
- **acme.sh installation**: Copies the vendored acme.sh client to the data directory for ACME certificate management. Skips if already installed.
|
||||
- **Directory setup**: Creates config directories under `config/` for each subsystem's declarative JSON, and data directories under `data/` for generated files (nginx sites, dnsmasq fragments, firewall backup, WireGuard config).
|
||||
- **Template rendering**: Renders system template files (`systemd/*.service`, `sudoers.d/`) via Jinja2, substituting `USER_NAME`, `INSTALL_DIR`, and `ACME_HOME`. Installed systemd and sudoers files contain no hardcoded values.
|
||||
- **Sudoers whitelist**: Installs a restrictive sudoers file at `/etc/sudoers.d/vacuum-walld` granting the daemon user `NOPASSWD` sudo for only the specific privileged commands needed for firewall, nginx, dnsmasq, and acme.sh management. Validates syntax with `visudo -cf`. The WebUI user's sudoers file (`/etc/sudoers.d/vacuum-wall`) is empty — it has no sudo access.
|
||||
- **Sudoers whitelist**: Installs a restrictive sudoers file at `/etc/sudoers.d/vacuum-walld` granting the daemon user `NOPASSWD` sudo for only the specific privileged commands needed for firewall, nginx, dnsmasq, and acme.sh 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. Appends only if not already present.
|
||||
- **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.
|
||||
|
||||
+90
-78
@@ -40,7 +40,7 @@ while [[ $# -gt 0 ]]; do
|
||||
"Usage: install.sh [OPTIONS]" \
|
||||
"" \
|
||||
"Options:" \
|
||||
" --user, -u USER System user for service (default: vacuum-wall)" \
|
||||
" --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)" \
|
||||
@@ -60,7 +60,7 @@ while [[ $# -gt 0 ]]; do
|
||||
" ./install.sh --dev --mgmt-pass pass --acme-email me@example.com" \
|
||||
"" \
|
||||
"Example (prod):" \
|
||||
" MGMT_PASS=pass ACME_EMAIL=me@example.com ./install.sh"
|
||||
" MGMT_PASS=pass ACME_EMAIL=me@example.com ./install.sh --user vacuum-wall"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
@@ -137,25 +137,31 @@ if [[ "$_cli_is_dev" == true ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Optional settings with defaults
|
||||
USER_NAME="${_cli_user:-${USER_NAME:-vacuum-wall}}"
|
||||
# Resolve USER_NAME: dev mode auto-detects, non-dev requires --user
|
||||
USER_NAME="${_cli_user:-${USER_NAME:-}}"
|
||||
if [[ -z "$USER_NAME" ]]; then
|
||||
err "WebUI user is required. Use --dev to auto-detect repo owner, or set --user / USER_NAME."
|
||||
fi
|
||||
# Daemon user name (derived from web UI user name)
|
||||
USER_DAEMON_NAME="${USER_NAME}d"
|
||||
|
||||
# --- Safety check: running service as a non-system regular user ---
|
||||
if [[ "$_cli_is_dev" != true ]] && [[ "$USER_NAME" != "vacuum-wall" ]] && id "$USER_NAME" &>/dev/null; then
|
||||
# --- Safety check: running service as a regular user ---
|
||||
if [[ "$_cli_is_dev" != true ]] && id "$USER_NAME" &>/dev/null; then
|
||||
_uid=$(id -u "$USER_NAME")
|
||||
_gid=$(id -g "$USER_NAME")
|
||||
_shell=$(getent passwd "$USER_NAME" | cut -d: -f7)
|
||||
if [[ "$_uid" -ge 1000 ]] && [[ "$_shell" != "/usr/sbin/nologin" && "$_shell" != "/bin/false" ]]; then
|
||||
warn "USER_NAME='$USER_NAME' is a regular user (UID=$_uid, shell=$_shell)!"
|
||||
warn "This runs the web service as your login account."
|
||||
warn "Sudo access is held only by the daemon user ($USER_DAEMON_NAME)."
|
||||
warn "Only use for development. For production, use --user vacuum-wall."
|
||||
fi
|
||||
fi
|
||||
# Shared group for both users to access project files and socket
|
||||
USER_GROUP="vacuum-wall"
|
||||
# Create WebUI user if it does not exist
|
||||
if ! id "$USER_NAME" &>/dev/null; then
|
||||
log "Creating system user $USER_NAME..."
|
||||
useradd --system --home-dir "$PROJECT_DIR" --no-create-home --shell /usr/sbin/nologin "$USER_NAME"
|
||||
fi
|
||||
# Shared group: use the WebUI user's primary group
|
||||
USER_GROUP=$(id -gn "$USER_NAME")
|
||||
|
||||
echo "============================================"
|
||||
echo " Vacuum Wall Appliance Installer"
|
||||
@@ -183,13 +189,8 @@ apt-get install -y -qq \
|
||||
apache2-utils \
|
||||
avahi-daemon
|
||||
|
||||
# --- 2. Create shared group ---
|
||||
if ! getent group "$USER_GROUP" &>/dev/null; then
|
||||
log "Creating shared group $USER_GROUP..."
|
||||
groupadd --system "$USER_GROUP"
|
||||
else
|
||||
log "Group $USER_GROUP already exists."
|
||||
fi
|
||||
# --- 2. Setup users ---
|
||||
log "WebUI user: $USER_NAME (group: $USER_GROUP)"
|
||||
|
||||
# --- 2a. Create daemon user (has sudo for privileged operations) ---
|
||||
if ! id "$USER_DAEMON_NAME" &>/dev/null; then
|
||||
@@ -201,17 +202,7 @@ else
|
||||
usermod -g "$USER_GROUP" "$USER_DAEMON_NAME" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# --- 2b. Create web UI user (no sudo, communicates with daemon) ---
|
||||
if ! id "$USER_NAME" &>/dev/null; then
|
||||
log "Creating system user $USER_NAME..."
|
||||
useradd --system --home-dir "$PROJECT_DIR" --no-create-home --shell /usr/sbin/nologin \
|
||||
--gid "$USER_GROUP" "$USER_NAME"
|
||||
else
|
||||
log "User $USER_NAME already exists."
|
||||
usermod -g "$USER_GROUP" "$USER_NAME" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# --- 2c. Setup Python venv ---
|
||||
# --- 2b. Setup Python venv ---
|
||||
if [[ -x "${PROJECT_DIR}/.venv/bin/python3" ]] && [[ "$_cli_force_venv" != true ]]; then
|
||||
log "Python venv already exists, skipping (use --force-venv to recreate)."
|
||||
else
|
||||
@@ -220,9 +211,10 @@ else
|
||||
python3 -m venv "${PROJECT_DIR}/.venv"
|
||||
"${PROJECT_DIR}/.venv/bin/pip" install -qe "${PROJECT_DIR}"
|
||||
chown -R "$USER_DAEMON_NAME:$USER_GROUP" "${PROJECT_DIR}/.venv"
|
||||
chmod -R g+x "${PROJECT_DIR}/.venv"
|
||||
fi
|
||||
|
||||
# --- 2d. Install acme.sh (vendored) ---
|
||||
# --- 2c. Install acme.sh (vendored) ---
|
||||
if [[ ! -x "$ACME_HOME/acme.sh" ]]; then
|
||||
log "Installing acme.sh (vendored)..."
|
||||
mkdir -p "$ACME_HOME"
|
||||
@@ -242,11 +234,19 @@ mkdir -p "${PROJECT_DIR}/config"/{dnsmasq,nginx,wireguard,firewall}
|
||||
mkdir -p "${PROJECT_DIR}/data"/{nginx/sites-enabled,dnsmasq,firewall,wireguard,acme}
|
||||
mkdir -p /etc/wireguard
|
||||
mkdir -p /etc/dnsmasq
|
||||
# Set ownership: daemon owns project dir, web UI user is group member
|
||||
chown -R "$USER_DAEMON_NAME:$USER_GROUP" "$PROJECT_DIR"
|
||||
chmod -R g+rX "$PROJECT_DIR"
|
||||
# Set ownership: daemon owns project dir in prod, repo owner keeps ownership in dev
|
||||
if [[ "$_cli_is_dev" == true ]]; then
|
||||
_dev_owner="$USER_NAME"
|
||||
else
|
||||
_dev_owner="$USER_DAEMON_NAME"
|
||||
fi
|
||||
chown -R "$_dev_owner:$USER_GROUP" "$PROJECT_DIR"
|
||||
chmod -R g+rwX "$PROJECT_DIR"
|
||||
find "$PROJECT_DIR" -type d -exec chmod g+s '{}' +
|
||||
|
||||
# --- 4. Template rendering function ---
|
||||
# Renders Jinja2 templates by injecting env vars as template context.
|
||||
# Used for systemd units and sudoers files.
|
||||
render_template() {
|
||||
export USER_NAME USER_DAEMON_NAME USER_GROUP PROJECT_DIR ACME_HOME
|
||||
"${PROJECT_DIR}/.venv/bin/python3" -c "
|
||||
@@ -272,12 +272,6 @@ render_template "${PROJECT_DIR}/system/sudoers.d/vacuum-walld" \
|
||||
|
||||
visudo -cf /etc/sudoers.d/vacuum-walld || err "Invalid sudoers file!"
|
||||
|
||||
# WebUI user no longer has sudo — write minimal sudoers file
|
||||
install -m 0440 /dev/null /etc/sudoers.d/vacuum-wall 2>/dev/null || true
|
||||
echo "# WebUI user ($USER_NAME) has no sudo access." > /etc/sudoers.d/vacuum-wall
|
||||
echo "# Privileged operations are handled by $USER_DAEMON_NAME via the daemon API." >> /etc/sudoers.d/vacuum-wall
|
||||
visudo -cf /etc/sudoers.d/vacuum-wall || true
|
||||
|
||||
# --- 6. Install systemd units ---
|
||||
log "Installing systemd units..."
|
||||
export USER_GROUP
|
||||
@@ -305,18 +299,22 @@ log "Enabling firewalld..."
|
||||
systemctl enable firewalld >/dev/null 2>&1 || warn "Could not enable firewalld (already running?)"
|
||||
systemctl start firewalld >/dev/null 2>&1 || warn "Could not start firewalld (may need D-Bus)"
|
||||
|
||||
firewall-cmd --permanent --add-service=http >/dev/null 2>&1 || true
|
||||
log "Added service http to public zone"
|
||||
firewall-cmd --permanent --add-service=https >/dev/null 2>&1 || true
|
||||
log "Added service https to public zone"
|
||||
firewall-cmd --permanent --add-service=ssh >/dev/null 2>&1 || true
|
||||
log "Added service ssh to public zone"
|
||||
firewall-cmd --reload >/dev/null 2>&1 || true
|
||||
log "Firewalld rules reloaded"
|
||||
firewall-cmd --permanent --add-service=http >/dev/null 2>&1 && \
|
||||
log "Added service http to public zone" || \
|
||||
warn "Could not add service http to public zone (already exists?)"
|
||||
firewall-cmd --permanent --add-service=https >/dev/null 2>&1 && \
|
||||
log "Added service https to public zone" || \
|
||||
warn "Could not add service https to public zone (already exists?)"
|
||||
firewall-cmd --permanent --add-service=ssh >/dev/null 2>&1 && \
|
||||
log "Added service ssh to public zone" || \
|
||||
warn "Could not add service ssh to public zone (already exists?)"
|
||||
firewall-cmd --reload >/dev/null 2>&1 && \
|
||||
log "Firewalld rules reloaded" || \
|
||||
warn "Could not reload firewalld rules"
|
||||
|
||||
# --- 9. Configure dnsmasq ---
|
||||
log "Configuring dnsmasq..."
|
||||
systemctl enable dnsmasq >/dev/null 2>&1 || true
|
||||
systemctl enable dnsmasq >/dev/null 2>&1 && log "Enabled dnsmasq" || warn "Could not enable dnsmasq"
|
||||
systemctl start dnsmasq >/dev/null 2>&1 || warn "Could not start dnsmasq (no interfaces configured yet)"
|
||||
log "dnsmasq configured (will fully start after DHCP ranges are set)"
|
||||
|
||||
@@ -360,6 +358,10 @@ chown "$USER_NAME:$USER_GROUP" "${PROJECT_DIR}/data/nginx/.htpasswd"
|
||||
# Remove default nginx site so vacuum-wall management config takes precedence
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Write initial management proxy config directly to /etc/nginx/conf.d/.
|
||||
# This bootstrap config is needed before the WebUI is running. Once the
|
||||
# WebUI is up, it manages proxy configs from config/nginx/config.json
|
||||
# and renders them to data/nginx/sites-enabled/.
|
||||
# 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
|
||||
@@ -455,7 +457,8 @@ log "Detecting network interfaces..."
|
||||
# Auto-detect WAN (interface with default gateway)
|
||||
WAN_IFACE="${WAN_IFACE:-}"
|
||||
if [[ -z "$WAN_IFACE" ]]; then
|
||||
DETECTED_WAN=$(ip route show default 2>/dev/null | awk '/default/ {print $5; exit}')
|
||||
# Strip @if<port> suffix — physical port index can change on reboot
|
||||
DETECTED_WAN=$(ip route show default 2>/dev/null | awk '/default/ {print $5; exit}' | cut -d'@' -f1)
|
||||
if [[ -n "$DETECTED_WAN" ]]; then
|
||||
WAN_IFACE="$DETECTED_WAN"
|
||||
log "Auto-detected WAN interface: $WAN_IFACE"
|
||||
@@ -526,26 +529,35 @@ with open(os.path.join(p, 'config/firewall/config.json'), 'w') as f:
|
||||
fi
|
||||
|
||||
# Apply zones via firewall-cmd (Python venv not yet fully available for apply_config)
|
||||
firewall-cmd --permanent --new-zone=internal >/dev/null 2>&1 || true
|
||||
log "Created firewalld zone: internal"
|
||||
firewall-cmd --permanent --zone=internal --set-target=ACCEPT >/dev/null 2>&1 || true
|
||||
firewall-cmd --permanent --zone=internal --add-service=dhcp >/dev/null 2>&1 || true
|
||||
log "Added service dhcp to internal zone"
|
||||
firewall-cmd --permanent --zone=internal --add-service=dns >/dev/null 2>&1 || true
|
||||
log "Added service dns to internal zone"
|
||||
firewall-cmd --permanent --zone=internal --add-service=ntp >/dev/null 2>&1 || true
|
||||
log "Added service ntp to internal zone"
|
||||
firewall-cmd --permanent --new-zone=internal >/dev/null 2>&1 && \
|
||||
log "Created firewalld zone: internal" || \
|
||||
warn "firewalld zone 'internal' may already exist"
|
||||
firewall-cmd --permanent --zone=internal --set-target=ACCEPT >/dev/null 2>&1 || \
|
||||
warn "Could not set target ACCEPT on internal zone"
|
||||
firewall-cmd --permanent --zone=internal --add-service=dhcp >/dev/null 2>&1 && \
|
||||
log "Added service dhcp to internal zone" || \
|
||||
warn "Could not add service dhcp to internal zone"
|
||||
firewall-cmd --permanent --zone=internal --add-service=dns >/dev/null 2>&1 && \
|
||||
log "Added service dns to internal zone" || \
|
||||
warn "Could not add service dns to internal zone"
|
||||
firewall-cmd --permanent --zone=internal --add-service=ntp >/dev/null 2>&1 && \
|
||||
log "Added service ntp to internal zone" || \
|
||||
warn "Could not add service ntp to internal zone"
|
||||
|
||||
firewall-cmd --permanent --new-zone=vpn >/dev/null 2>&1 || true
|
||||
log "Created firewalld zone: vpn"
|
||||
firewall-cmd --permanent --zone=vpn --set-target=ACCEPT >/dev/null 2>&1 || true
|
||||
firewall-cmd --permanent --new-zone=vpn >/dev/null 2>&1 && \
|
||||
log "Created firewalld zone: vpn" || \
|
||||
warn "firewalld zone 'vpn' may already exist"
|
||||
firewall-cmd --permanent --zone=vpn --set-target=ACCEPT >/dev/null 2>&1 || \
|
||||
warn "Could not set target ACCEPT on vpn zone"
|
||||
|
||||
# Apply masquerade on public/WAN
|
||||
if [[ -n "$WAN_IFACE" ]]; then
|
||||
firewall-cmd --permanent --zone=public --add-masquerade >/dev/null 2>&1 || true
|
||||
log "Enabled masquerade on public zone ($WAN_IFACE)"
|
||||
firewall-cmd --permanent --zone=public --add-interface="$WAN_IFACE" >/dev/null 2>&1 || true
|
||||
log "Assigned $WAN_IFACE to public zone"
|
||||
firewall-cmd --permanent --zone=public --add-masquerade >/dev/null 2>&1 && \
|
||||
log "Enabled masquerade on public zone ($WAN_IFACE)" || \
|
||||
warn "Could not enable masquerade on public zone"
|
||||
firewall-cmd --permanent --zone=public --add-interface="$WAN_IFACE" >/dev/null 2>&1 && \
|
||||
log "Assigned $WAN_IFACE to public zone" || \
|
||||
warn "Could not assign $WAN_IFACE to public zone"
|
||||
fi
|
||||
|
||||
# Assign LAN interfaces to internal zone
|
||||
@@ -554,27 +566,24 @@ if [[ -n "$LAN_IFACES" ]]; then
|
||||
for iface in "${LAN_ARRAY[@]}"; do
|
||||
iface=$(echo "$iface" | xargs)
|
||||
[[ -z "$iface" ]] && continue
|
||||
firewall-cmd --permanent --zone=internal --add-interface="$iface" >/dev/null 2>&1 || true
|
||||
log "Assigned $iface to internal zone"
|
||||
firewall-cmd --permanent --zone=internal --add-interface="$iface" >/dev/null 2>&1 && \
|
||||
log "Assigned $iface to internal zone" || \
|
||||
warn "Could not assign $iface to internal zone"
|
||||
done
|
||||
fi
|
||||
|
||||
firewall-cmd --reload >/dev/null 2>&1 || true
|
||||
log "Firewalld rules reloaded"
|
||||
firewall-cmd --reload >/dev/null 2>&1 && \
|
||||
log "Firewalld rules reloaded" || \
|
||||
warn "Could not reload firewalld rules"
|
||||
|
||||
# --- 13. Enable and start services ---
|
||||
log "Enabling services..."
|
||||
systemctl enable nginx >/dev/null 2>&1 || true
|
||||
log "Enabled nginx"
|
||||
systemctl enable vacuum-walld >/dev/null 2>&1 || true
|
||||
log "Enabled vacuum-walld"
|
||||
systemctl enable vacuum-wall >/dev/null 2>&1 || true
|
||||
log "Enabled vacuum-wall"
|
||||
systemctl enable vacuum-wall-acme.timer >/dev/null 2>&1 || true
|
||||
log "Enabled vacuum-wall-acme.timer"
|
||||
systemctl enable nginx >/dev/null 2>&1 && log "Enabled nginx" || warn "Could not enable nginx"
|
||||
systemctl enable vacuum-walld >/dev/null 2>&1 && log "Enabled vacuum-walld" || warn "Could not enable vacuum-walld"
|
||||
systemctl enable vacuum-wall >/dev/null 2>&1 && log "Enabled vacuum-wall" || warn "Could not enable vacuum-wall"
|
||||
systemctl enable vacuum-wall-acme.timer >/dev/null 2>&1 && log "Enabled vacuum-wall-acme.timer" || warn "Could not enable vacuum-wall-acme.timer"
|
||||
|
||||
systemctl enable avahi-daemon >/dev/null 2>&1 || true
|
||||
log "Enabled avahi-daemon"
|
||||
systemctl enable avahi-daemon >/dev/null 2>&1 && log "Enabled avahi-daemon" || warn "Could not enable avahi-daemon"
|
||||
systemctl start avahi-daemon >/dev/null 2>&1 && log "Started avahi-daemon" || warn "Could not start avahi-daemon"
|
||||
|
||||
# Start daemon first, then web UI
|
||||
@@ -611,6 +620,9 @@ nginx -t 2>/dev/null && nginx -s reload 2>/dev/null && log "Reloaded nginx" || \
|
||||
if [[ -f "$ACME_HOME/account.conf" ]] && grep -q '^ACME_LEEMAIL=' "$ACME_HOME/account.conf" 2>/dev/null; then
|
||||
log "acme.sh account already registered, skipping."
|
||||
else
|
||||
# acme.sh must never run as root — always as the service user via sudo -u.
|
||||
# This prevents acme.sh from running any command as root and limits its
|
||||
# ability to modify system files.
|
||||
log "Registering acme.sh account with email $ACME_EMAIL..."
|
||||
mkdir -p "$ACME_HOME/www"
|
||||
chown "$USER_DAEMON_NAME:$USER_GROUP" "$ACME_HOME/www"
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# WebUI user ({{ USER_NAME }}) no longer has sudo access.
|
||||
# Privileged operations are handled by vacuum-walld via the daemon API.
|
||||
@@ -33,7 +33,7 @@ LockPersonality=yes
|
||||
SystemCallFilter=@system-service
|
||||
PrivateDevices=yes
|
||||
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
|
||||
IPAddressDeny=any
|
||||
IPAddressAllow=localhost
|
||||
NoNewPrivileges=yes
|
||||
|
||||
Reference in New Issue
Block a user