Commit Graph

41 Commits

Author SHA1 Message Date
mteehan 04417cf05c WireGuard access classes, firewall nftables fixes, network sync event refactor
- WireGuard: refactor to multi-interface 'access classes' model; extract config
  generation and helpers into lib/wireguard.py; add per-class up/down endpoints
  and API routes; update UI with class management pages and QR code component
- Firewall: fix zone creation with --new-zone before --set-target; skip
  masquerade on public zone; add masquerade propagation for nftables backend
  so NAT works when internal zones exit via public
- Network: rename sync event subsystem 'network' -> 'networkd'; always stamp
  config hash even when deployment fails (fixes pending-changes detection)
- DHCP: add new API endpoint and update frontend page
- State/Sync: update state collectors and sync buses for new subsystems
- Docs: update API and config documentation for new endpoints and schemas
2026-07-20 03:57:16 +00:00
mteehan dadabd7954 feat: add system metrics dashboard with resource monitoring
- Add system metrics endpoint (CPU load, memory, swap, network traffic)
- Collect metrics from /proc and /sys (no subprocess required)
- Overhaul dashboard to pull from per-subsystem models
- Remove deprecated /status/all monolithic endpoint
- Improve networkd import to handle optional priority prefix
- Fix CSS duplicate .grid-4 rule and unused dashboard imports
2026-07-15 00:24:43 +00:00
mteehan 2e49dec633 feat: auto-generate self-signed certs, hoover modal processing guard, refactor backends/proxy pages 2026-07-13 14:30:35 +00:00
mteehan 05524f3756 fix: critical bugs + security hardening
Phase 1 (critical bugs):
- Fix firewall import string-to-list bug (system_import.py)
- Add rich rules removal in firewall config apply (handlers/firewall.py)

Phase 2 (security hardening):
- Restrict sudo wildcards to specific paths (sudoers.d/vacuum-walld)
- Fix TOCTOU: use /run/vacuum-wall/ for temp files (nginx, dnsmasq, network handlers)
- Remove unnecessary sudo from wg genkey/pubkey (handlers/wireguard.py)

Phase 3 (validation):
- Validate poll intervals > 0 (daemon/server.py)
- Restrict sysctl to whitelisted parameters (handlers/network.py)

Phase 4 (defensive programming):
- Enforce shell=False in run() and run_proc() (lib/common.py)
- Track issuance tasks for graceful shutdown (handlers/acme.py)
- Add nginx template marker consistency tests (tests/test_system_import.py)
2026-07-11 12:21:36 +00:00
mteehan 803258cf18 dhcp: auto-populate gateway from interface IP for DHCP ranges
Add get_interface_ip() helper to resolve an interface's IPv4 address
via 'ip -o addr show'.  Use it to back-propagate gateway into DHCP
ranges so clients receive their default route.

- set_dhcp_range() resolves gateway: explicit > existing range > iface IP
- DnsToFirewallSync and FirewallToDhcpSync sync ensure gateways are set
- Remove automatic masquerade toggle from DnsToFirewallSync
- Fix dnsmasq lease file path to /var/lib/misc/dnsmasq.leases
- Rename lease state field expires_at -> expires (ISO string)
- Add 'ip -o addr show' to sudo whitelist
2026-07-09 00:58:09 +00:00
mteehan 5135de0921 feat: add system config import, refactor install script and nginx auth
- lib/system_import: new module to import system configs into JSON at daemon startup
- daemon/server.py: call import_all() during startup for config reconciliation
- daemon/handlers/nginx.py: simplify add_domain auth handling, remove duplicate code
- scripts/install.sh: replace inline Python setup with curl-based daemon API calls; apply IP forwarding at runtime
- hoover: bump internal asset versions to v=8
- pages: bump asset versions to v=9
2026-07-08 02:20:28 +00:00
mteehan fb39af126a docs: update documentation and project structure
- Update AGENTS.md, README.md, and docs/* with revisions
- Refactor lib/acme.py and lib/state.py
- Add tests for acme module
- Remove install.sh and restart-services.sh (moved to scripts/)
- Normalize vendor files (acme.sh, htm.js)
2026-07-02 14:41:02 +00:00
mteehan 8c13ad55ce Add update-vendor.sh symlink support, unify install.sh vendor flow
- update-vendor.sh now creates webui/vendor symlinks (htm.js)
- install.sh calls update-vendor.sh after package install
- Add vendor/.empty and webui/vendor/.empty as directory placeholders in git
2026-07-01 00:55:03 +00:00
mteehan 575cf06a4b hoover: move ToastContainer to separate module, add reactive re-render, bump module cache v8 2026-06-30 04:01:41 +00:00
mteehan 9088f34345 sync: add cross-subsystem event bus for config consistency
Add EventBus with loop guards to keep firewall, dnsmasq, wireguard,
and network configs consistent. Handlers emit SyncEvent after mutations;
subscribers compute diffs and write JSON without manual cascade loops.
2026-06-30 01:18:44 +00:00
mteehan 348bbfbca6 dhcp: track pending config changes with hash, update UI button 2026-06-28 16:26:32 +00:00
mteehan baa441fa13 dhcp: auto-populate IP range when selecting interface
- QuickModal: add postRender callback for DOM-level initialization
- dhcp: derive start/end from interface CIDR on change
2026-06-28 13:04:51 +00:00
mteehan 391466664e Update status checks and add optgroup support to form modals
- modal.js: add optgroup support for select options in formModal
- dhcp.js: use zone-based interface selector for DHCP ranges
- dashboard.js, wireguard.js: use status.up for state checks
- proxy.js: use inner.querySelector for modal field lookup
2026-06-28 12:47:59 +00:00
mteehan 25a1943fce Optimize firewall state collection and improve daemon shutdown
- Replace per-zone --list-all calls with single --list-all-zones in _collect_firewall
- Add _parse_all_zones_output() parser with rich rules/rich-rules normalization
- Convert daemon shutdown to async with proper runner cleanup and socket unlink
- Add TimeoutStopSec=15 to vacuum-walld.service for graceful stop
- Fix exception handling in _collect_dnsmasq
- Remove management badge from proxy path rows
2026-06-28 00:54:01 +00:00
mteehan 80dd4e3272 proxy: refactor modals and add cert selector options 2026-06-27 23:59:12 +00:00
mteehan 835326311b Refactor nginx to path-based domain model with config migration
Replace the legacy top-level management key with a unified paths-based
model. Each domain now contains a paths map where each entry defines its
own backend, auth, headers, and flags (is_management, is_websocket).

- Add _migrate_config() to auto-migrate legacy formats on first load
- Remove set_management_proxy() and POST_NGINX_MANAGEMENT endpoint
- Update server_block.conf template to iterate paths with per-location auth
- Update daemon handler, API blueprint, state collector, and install script
- Add server config generation tests for paths, WebSocket, auth inheritance
- Update frontend proxy page to display per-path rows with flags
2026-06-27 23:34:06 +00:00
mteehan 8feb56faf6 fix: ECC cert support, ACME deploy hook path, NAT detection, and account config fallback
- Add find_cert_dir() to resolve both RSA and ECC (domain_ecc/) cert dirs
- Copy acme deploy hook to /deploy/ where acme.sh resolves it
- _parse_account_conf checks both legacy .account.conf and declarative config
- Skip public DNS check when all local IPs are private (NAT)
- Improve check message strings for validity and expiry status
- Support timezone-aware date formats in _days_until parsing
- Filter out "no" SAN domains in cert listing
- Bump frontend asset version cache keys
- Fix DOMContentLoaded race condition in app.js boot
- Fix spread operator in certs.js modal template
2026-06-27 14:23:40 +00:00
mteehan 398831b6e2 Refactor ACME module and add cert issuance conflict handling
- Move acme.sh utilities (_run_acme, _find_acme, etc.) from lib/state to lib/acme
- Rewrite _parse_list_output to support pipe, tab, and column-separated formats
- Add ConflictError (409) to block issuing when cert already exists
- Move _find_issuance helper to detect in-progress issuance per domain
- Update issue_cert to check existing certs and return issuance status
- Fix start_polling to accept event loop explicitly
- Add sudoers entry for chown on vacuum-wall.conf
- Extend systemd ReadWritePaths for /run/nginx.pid and /var/log/nginx
- Update frontend to handle 'existing' issuance status
2026-06-27 00:38:49 +00:00
mteehan feaf253403 Remove SPA catch-all, add vendor route and JSON 404 handler
The SPA uses hash-based routing, so the catch-all route was dead code.
Replace with explicit routes for / and /vendor/<path>, plus a 404
handler that returns JSON for /api/ paths and empty HTML otherwise.
2026-06-23 23:20:35 +00:00
mteehan e74f0a5ffb Remove unused htmx/json-enc vendor libs; fix missing htm.js symlink
- Remove vendor/htmx-2.0.4.min.js and vendor/json-enc-2.0.0.js (unused)
- Remove webui/static/htmx.min.js and webui/static/json-enc.js symlinks
- Add webui/static/vendor/htm.js symlink pointing to project root
  vendor/htm.js, fixing the module script MIME type error caused by
  hoover/html.js importing from a non-existent path
2026-06-23 21:34:57 +00:00
mteehan 5ba0f31767 Add state management, WebSocket polling, html.js templating, and refactor pages
- lib/state.py: per-subsystem collectors with versioned state store
- daemon/server.py: state refresh on request, batch routing updates
- webui/static/hoover/html.js: new html tag template helper via htm.js
- webui/static/hoover/websocket.js: real-time state change notifications
- webui/static/hoover/vdom.js: VDOM improvements for keyed diff
- All frontend pages refactored to use html templates
- Add tests for state management and polling
- Update docs and AGENTS.md
2026-06-23 21:12:56 +00:00
mteehan 5025dfaf30 feat: add ACME account management with validation pipeline
- Register, view, and deactivate ACME accounts via API and UI
- 16-check validation framework for certificate issuance readiness
- DNS resolution, port, nginx, and firewall pre-flight checks
- External IP detection with NAT support and fallback providers
- Account card and settings modal in certificates page
- Guard certificate issuance behind account registration
- Update modal CSS to overlay-based approach
- 1000+ lines of tests for validation and account handlers
2026-06-23 14:24:19 +00:00
mteehan 75aa6fb885 fix: wrap model data in named objects and fix renderGuardMulti empty check 2026-06-23 00:08:40 +00:00
mteehan b673e87c9b refactor: introduce model layer for centralized data synchronization
Add hoover model.js as a central reactive store per subsystem, replacing
per-component data fetching with a single source of truth.

- Add hoover/model.js with modelRegister, modelFetch, and WS invalidation
- Refactor websocket.js to route messages to model refresh (drop per-component
  subscribe/unsubscribe)
- Simplify component.js by removing WS subscription management
- Add refresh option to apiSubmit, deprecate refactorLoad and checkAbort
- Rewrite all pages to use getModel() instead of inline data fetching
- Bootstrap model registrations in app.js
- Add GET /api/firewall/state endpoint
- Fix restart-services.sh restart order and add service health verification
- Update hoover.md docs with model layer architecture
2026-06-22 22:54:29 +00:00
mteehan 633505e7dc refactor: modernize frontend with hoover framework components and docs
- Add quick modal, table, service status, and confirmation dialog components
- Refactor all pages (certs, dhcp, proxy, etc.) to use new component patterns
- Introduce refactor load utility and render guard for consistent UX
- Add hoover documentation and update AGENTS.md, architecture, overview
2026-06-21 04:29:27 +00:00
mteehan 687fa8f52f pre-refactor 2026-06-17 03:41:08 +00:00
mteehan 708b8b5d15 refactor: update all API blueprints (certs, dhcp, firewall, logs, network, proxy, wireguard) 2026-06-16 03:50:00 +00:00
mteehan 593dece92b refactor: replace Jinja templates with static frontend pages 2026-06-16 03:50:00 +00:00
mteehan bc72db903c feat: add networkd subsystem and fix code review issues
Phase 1-4: Networkd subsystem
- lib/network.py: systemd-networkd config renderer (.network INI files)
  with full schema support: [Match], [Link], [Network], [Address], [Route],
  [DHCPv4], [DHCPv6] sections. One Address/=DNS= line per value per spec.
  Route sections use #N suffix per systemd.syntax(7).
- lib/network.py: generate_network_files() with 50-<name>.network prefix
  and stale file cleanup
- lib/network.py: collect_upstream_dns() filters local/private DNS
- lib/network.py: infer_dhcp_ranges() and infer_zones() helpers
- daemon/handlers/network.py: routes for GET/POST /network/interfaces
  and full apply with DNS upstream sync to dnsmasq
- webui/api/network.py: Flask blueprint for /api/network/* endpoints
- webui/api: interfaces page updated with IP config inline editing
- lib/state.py: networkd collector using parse_networkctl_status()
- system/sudoers.d/vacuum-walld: networkctl + systemd-network rules
- system/systemd/vacuum-walld.service: ReadWritePaths for /etc/systemd/network
- install.sh: ACME email now optional, configured from WebUI
- lib/acme.py: get_email() falls back to declarative config

Phase 5: Code review fixes
- daemon/server.py: path params now win over JSON body and query params
  in request body merge (prevents config save name override)
- daemon/server.py: remove dead 'import re'
- daemon/handlers/network.py: replace Path.mkdir() with sudo mkdir
  for /etc/systemd/network (ProtectSystem=strict compatibility)
- system/sudoers.d/vacuum-walld: pin systemctl to specific commands
  (reload/is-active dnsmasq instead of wildcard)
- system/sudoers.d/vacuum-walld: restore !requiretty and section comment
- lib/network.py: remove unused _MANAGEMENT_PORTS constant
- webui/api/network.py: remove redundant body[\name\] = name in save_interface

Tests: 332 passing (110 new/updated), ruff clean
2026-06-01 03:15:50 +00:00
mteehan 2f215793e9 docs: add docstrings to all API endpoints and daemon handlers
Add comprehensive docstrings to firewall, DHCP, proxy, wireguard, certs,
and logs API endpoints. Document parameters, return values, and error cases
for the documentation system.
2026-05-30 16:15:45 +00:00
mteehan dc96e15643 feat: pre-computed state store and async ACME issuance (fixes timeout mismatch)
- Add lib/state.py: in-memory state store with subsystem collectors
  (firewall, dnsmasq, nginx, acme, wireguard)
- Refactor all handlers: read from state on GET, call refresh_state()
  after mutations instead of invoking subprocesses per request
- daemon/server.py: add refresh_state(), /status/all, /status/refresh;
  populate state at startup
- webui/api/certs.py: async step-by-step ACME issuance (validate,
  issue with request_id, poll status) replacing blocking endpoint
- webui/server.py: render pages from state instead of direct lib calls
- Update templates, JS for async cert issuance with polling UI
- Update tests for state-based mocking; add test_state.py
- Fix SIM105 lint issue (contextlib.suppress)
- Add TODO.md with certificate issuance issue tracking

Resolves: WebUI 30s timeout freeze during cert issuance (Problem 1)
2026-05-30 05:46:09 +00:00
mteehan c091063248 fix: two-user model bug fixes and docs 2026-05-29 22:29:59 +00:00
mteehan 200e078bc5 refactor: introduce two-user daemon architecture with socket-based communication
- Add daemon/ module with aiohttp server, sync client, and handler registry
- Add daemon/handlers/ for privileged operations (acme, dnsmasq, firewall, logs, nginx, wireguard)
- Add system/acme-deploy.py, vacuum-walld sudoers and systemd service
- Update API routes to use daemon client instead of lib/ directly
- Update lib/, tests/, and webui/ for new architecture
- Update docs and deployment scripts
2026-05-27 23:39:33 +00:00
mteehan 5ac69dfa7e refactor: move JS vendors to vendor/ with versioned filenames
Vendor JS files to vendor/ with versioned names, symlinks in
webui/static/ select active version. Update update-vendor.sh and
AGENTS.md.
2026-05-25 01:20:37 +00:00
mteehan f81be96e59 add: track shared modules, vendor scripts, and update gitignore
- lib/common.py: shared run/load_json/save_json utilities
- webui/api/common.py: _ok/_error response helpers
- webui/static/htmx.min.js, json-enc.js: vendored frontend deps
- scripts/update-vendor.sh: frontend vendor updater
- vendor/acme.sh: bundled ACME client
- .gitignore: add .playwright-mcp/ and opencode.json.pwenv
2026-05-25 01:13:34 +00:00
mteehan d1ab717c0f refactor: unify project structure, improve security, and enhance deployment
- Fix WireGuard private key leak in API responses and config updates
- Update systemd service to serve from repo root with adjusted sandbox
- Add CLI flags, idempotency, and dev mode to install.sh
- Extract common utilities to lib/common.py and webui/api/common.py
- Migrate frontend to htmx for simpler, more maintainable UI
- Update docs to reflect current architecture and deployment model
- Vendor htmx dependencies per project requirements
2026-05-25 00:53:32 +00:00
mteehan 8829ac579d fix: strip private_key from WireGuard PATCH endpoint and add acme-challenge template 2026-05-24 13:12:40 +00:00
mteehan 37039351be fix htmx refactor route mismatches and remaining TODO items
- wireguard: POST /peers with JSON encoding (was /add-peer)
- rules: delete by rule_id in URL path (was JSON body); pass rule objects with id from server; add hx-disable to initial render
- nat: port forward delete uses URL path params to match blueprint
- nat: masquerade toggle uses native hx-post/hx-vals (was inline fetch)
- app.js renderers updated to use URL path deletes for rules and forwards
- remove TODO.md
2026-05-17 01:17:12 +00:00
mteehan 6106c1434d Add declarative firewall config with save-then-apply workflow
New two-step config flow: POST /config saves desired state to
config/firewall/config.json, GET /config/pending diffs against live
firewalld state, POST /config/apply synchronizes live state.  Adds target
normalization helpers and full test coverage for config CRUD and pending
diff logic.
2026-05-14 03:31:49 +00:00
mteehan 65741644a3 Fix dashboard template bugs, acme date parsing, wireguard sudoers match, and stale docs
- 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
2026-05-08 19:11:54 +00:00
mteehan e2f56b8cc8 Initial commit: SSL proxy / firewall appliance
Flask WebUI behind nginx reverse proxy with zone-based firewall, DHCP,
WireGuard, and ACME certificate management.
2026-05-07 22:24:24 +00:00