Skip to content

Security

ollikurki edited this page May 20, 2026 · 1 revision

Security

Hardening guide for HomePilot Agent infrastructure — Zabbix, Proxmox, Docker, and network security.

Defense in Depth

┌──────────────────────────────────────────┐
│              VPN / Firewall              │  ← Network perimeter
├──────────────────────────────────────────┤
│          TLS (Caddy reverse proxy)       │  ← Transport encryption
├──────────────────────────────────────────┤
│     Docker network isolation + secrets   │  ← Container security
├──────────────────────────────────────────┤
│  Least-privilege PVE tokens + PSK TLS    │  ← Monitoring auth
├──────────────────────────────────────────┤
│    Zabbix role separation + audit log    │  ← Access control
├──────────────────────────────────────────┤
│      Artifact approval lifecycle          │  ← Change control
└──────────────────────────────────────────┘

Zabbix Hardening

1. Rotate Default Passwords

The default Zabbix PostgreSQL password is insecure. Rotate immediately:

# Generate strong password
NEW_PW=$(openssl rand -base64 24 | tr -d '/+=' | head -c 28)

# Update secret file
echo "$NEW_PW" > monitoring/zabbix/secrets/zabbix_pg_password.txt

# Update .env
sed -i "s/^ZABBIX_DB_PASSWORD=.*/ZABBIX_DB_PASSWORD=$NEW_PW/" .env

# Update PostgreSQL (in running container)
docker exec zabbix-postgres psql -U zabbix -c \
  "ALTER USER zabbix WITH PASSWORD '$NEW_PW';"

# Restart Zabbix to pick up new secret
docker compose -f docker-compose.yml \
  -f docker-compose.monitoring.yml restart zabbix-server zabbix-web

2. Change Zabbix Admin Password

After first login, immediately change the default Admin / zabbix credentials:

  1. Log in to Zabbix Web UI
  2. User settings → Change password
  3. Use a strong, unique password

3. TLS-PSK for Agent Connections

All Zabbix agent connections should use TLS with Pre-Shared Key (PSK):

# Generate PSK on each monitored host
openssl rand -hex 32 > /etc/zabbix/psk/<hostname>.psk
chmod 600 /etc/zabbix/psk/<hostname>.psk

Configure in Zabbix UI for each host:

  • Encryption → Connections from host: PSK
  • PSK identity: descriptive name (e.g., PSK-pve)
  • PSK: paste from the generated .psk file

4. Network Isolation

Zabbix services should only be accessible through the Caddy reverse proxy:

# In docker-compose.monitoring.yml
zabbix-web:
  networks:
    - monitoring-net
    - caddy-net  # Only if using Caddy
  # Do NOT publish ports directly

Caddy handles TLS termination and restricts access:

homepilot.example.com {
    reverse_proxy zabbix-web:8084
}

5. Principle of Least Privilege

Create separate Zabbix users for different roles:

Role Access Use Case
Super Admin Full access Initial setup only
Admin Read/write monitoring Day-to-day management
User Read-only dashboards NOC / status viewing

Proxmox VE Least Privilege

Monitoring Token (Read-Only)

Create a dedicated monitoring user and token with minimal permissions:

  1. Create user: monitor@pam in PVE web UI
  2. Set permissions: / → PVEAuditor (read-only)
  3. Generate API token: monitor@pam!monitoring
  4. Copy the secret — store in n8n credential store, never in .env or git

The token grants:

  • Read node status, VM/LXC configs, storage info
  • List APT updates and changelogs
  • NO write operations

Elevated Token (Restricted)

If you need proxmox_refresh_apt_updates or similar write operations:

  1. Create monitor-ops@pam with PVEPowerUser on /nodes only
  2. Generate token: monitor-ops@pam!ops
  3. Scope permissions to the minimum required path
  4. Never use root@pam tokens in n8n workflows

Docker Security

Secrets Management

All sensitive values use Docker secrets (file-based, mounted at /run/secrets/), not environment variables:

# docker-compose.monitoring.yml
zabbix-server:
  secrets:
    - zabbix_pg_password
    - zabbix_agent_psk

Docker secret files are stored in monitoring/zabbix/secrets/ (gitignored) and mounted at runtime.

Network Isolation

Each service group uses its own Docker network:

Network Services Purpose
default n8n, searxng, radicale, llm Core application
monitoring-net zabbix-server, postgres, web, agent Monitoring stack
caddy-net caddy TLS termination

Containers on different networks cannot communicate unless explicitly connected.

Container Hardening

# Example: read-only filesystem, no new privileges
services:
  zabbix-server:
    read_only: true
    tmpfs:
      - /tmp
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE

Firewall Recommendations

Host Firewall (iptables/nftables)

# Allow only necessary inbound
# SSH, HTTPS (Caddy), monitoring
iptables -A INPUT -p tcp --dport 22 -j ACCEPT    # SSH
iptables -A INPUT -p tcp --dport 443 -j ACCEPT   # HTTPS (Caddy)
iptables -A INPUT -p tcp --dport 10051 -j ACCEPT # Zabbix trapper (internal only)

# Default deny
iptables -A INPUT -j DROP

Docker Network Policies

Docker handles inter-container networking. Only publish ports that need external access:

  • 443 — Caddy TLS (Zabbix, n8n web UIs)
  • 5678 — n8n (consider restricting to VPN only)
  • Everything else stays on internal Docker networks

VPN Recommendations

  • WireGuard or Tailscale for remote access to management interfaces
  • Route all admin traffic (n8n UI, Zabbix UI, PVE UI) through VPN only
  • Do not expose management ports to the public internet
  • Use VPN for SSH access to all servers

Example Tailscale network policy:

# Only allow VPN clients to reach management ports
homepilot-agent: 5678, 8084  # n8n, Zabbix
pve: 8006                     # PVE web UI
zabbix: 10051                 # Zabbix trapper (internal)

Agent Security

n8n Credential Hygiene

  • All tokens stored in n8n credential store (encrypted with N8N_ENCRYPTION_KEY)
  • Never commit credentials to git
  • Use MCP read-only token by default; read-write only for explicit mutations
  • Rotate MCP tokens periodically via HomePilot admin UI

Webhook Validation

The Artifact Notification workflow validates:

  1. X-HP-Webhook-Secret header matches HP Webhook Secret credential
  2. Payload structure matches expected schema (event, artifact.id, artifact.intent, etc.)
  3. Invalid requests return 401 and are logged

LLM Security

  • All inference runs locally on your hardware — no data sent to external APIs
  • LLM has no direct database access — it goes through MCP (scoped by token)
  • Read-write mutations require propose_artifact (human approval required)

Audit and Incident Response

Zabbix Audit Log

Enable audit logging in Zabbix:

  1. Administration → General → Housekeeping → Enable audit logging
  2. Set retention period (e.g., 365 days)
  3. Monitor Audit log report for unauthorized access attempts

Incident Response Checklist

  1. Revoke compromised tokens immediately in HomePilot and PVE
  2. Rotate passwords — Zabbix admin, PostgreSQL, Docker secrets
  3. Review audit logs — Zabbix, PVE, Docker
  4. Check agent health — verify no unexpected Zabbix host registrations
  5. Isolate if needed — disconnect from VPN, disable webhooks
  6. Document — record timeline, affected systems, root cause

Security Checklist

  • Zabbix admin password changed from default
  • PostgreSQL password rotated from zabbix_pw
  • All Zabbix agent connections use TLS-PSK
  • PVE monitoring uses read-only monitor@pam token (not root@pam)
  • No credentials in .env — all secrets in n8n credential store or Docker secrets
  • Caddy TLS reverse proxy active for all web UIs
  • Docker networks isolated by service group
  • Firewall blocks all non-essential inbound traffic
  • VPN configured for remote admin access
  • Zabbix audit logging enabled with retention ≥ 90 days
  • Webhook secrets validated on every incoming request