183 Commits

Author SHA1 Message Date
fuomag9
7aeaaded5e feat: scaffold tailwind + shadcn foundation, swap to next-themes
Installs Tailwind CSS v3, postcss, autoprefixer, next-themes, lucide-react,
clsx, tailwind-merge, class-variance-authority, sonner, and tailwindcss-animate.
Creates tailwind.config.ts, postcss.config.mjs, components.json, src/lib/utils.ts
(cn helper), replaces globals.css with CSS variable theme, adds
suppressHydrationWarning to html element, and replaces MUI ThemeProvider
with next-themes ThemeProvider + sonner Toaster. MUI remains installed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 11:14:09 +01:00
fuomag9
9b9076d115 fix: use NonNullable for optional l4ProxyHosts field in sync route validator
SyncPayload["data"]["l4ProxyHosts"] is optional (Array | undefined),
so indexing with [number] fails tsc. NonNullable<...>[number] resolves
the correct element type.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 00:27:37 +01:00
fuomag9
00c9bff8b4 feat: instant banner refresh on L4 mutations + master-slave L4 sync
Banner (L4PortsApplyBanner):
- Accept refreshSignal prop; re-fetch /api/l4-ports when it changes
- Signal fires immediately after create/edit/delete/toggle in L4ProxyHostsClient
  without waiting for a page reload

Master-slave replication (instance-sync):
- Add l4ProxyHosts to SyncPayload.data (optional for backward compat
  with older master instances that don't include it)
- buildSyncPayload: query and include l4ProxyHosts, sanitize ownerUserId
- applySyncPayload: clear and re-insert l4ProxyHosts in transaction;
  call applyL4Ports() if port diff requires it so the slave's sidecar
  recreates caddy with the correct ports
- Sync route: add isL4ProxyHost validator; backfill missing field from
  old masters; validate array when present

Tests (25 new tests):
- instance-sync.test.ts: buildSyncPayload includes L4 data, sanitizes ownerUserId;
  applySyncPayload replaces L4 hosts, handles missing field, writes trigger
  when ports differ, skips trigger when ports already match
- l4-ports-apply-banner.test.ts: banner refreshSignal contract + client
  increments counter on all mutation paths

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 00:22:44 +01:00
fuomag9
3a4a4d51cf feat: add L4 (TCP/UDP) proxy host support via caddy-l4
- New l4_proxy_hosts table and Drizzle migration (0015)
- Full CRUD model layer with validation, audit logging, and Caddy config
  generation (buildL4Servers integrating into buildCaddyDocument)
- Server actions, paginated list page, create/edit/delete dialogs
- L4 port manager sidecar (docker/l4-port-manager) that auto-recreates
  the caddy container when port mappings change via a trigger file
- Auto-detects Docker Compose project name from caddy container labels
- Supports both named-volume and bind-mount (COMPOSE_HOST_DIR) deployments
- getL4PortsStatus simplified: status file is sole source of truth,
  trigger files deleted after processing to prevent stuck 'Waiting' banner
- Navigation entry added (CableIcon)
- Tests: unit (entrypoint.sh invariants + validation), integration (ports
  lifecycle + caddy config), E2E (CRUD + functional routing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 00:11:16 +01:00
fuomag9
b5625e5a96 feat: migrate from npm to bun and fix analytics map height
Switch package manager and runtime from Node.js/npm to Bun across
Docker, CI, and scripts. The SQLite driver remains better-sqlite3
due to Next.js Turbopack being unable to resolve bun:sqlite during
build-time page pre-rendering.

Also fix the world map not rendering in the analytics page — the
overflowX wrapper added for mobile broke the flex height chain,
collapsing the map to 0px.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 01:48:21 +01:00
fuomag9
4b5323a7bf feat: add structured redirects and path prefix rewrite for proxy hosts
Adds two new UI-configurable Caddy patterns that previously required raw JSON:
- Per-path redirect rules (from/to/status) emitted as a subroute handler before
  auth so .well-known paths work without login; supports full URLs, cross-domain
  targets, and wildcard path patterns (e.g. /.well-known/*)
- Path prefix rewrite that prepends a segment to every request before proxying
  (e.g. /recipes → upstream sees /recipes/original/path)

Config is stored in the existing meta JSON column (no schema migration). Includes
integration tests for meta serialization and E2E functional tests against a real
Caddy instance covering relative/absolute destinations, all 3xx status codes, and
various wildcard combinations. Adds traefik/whoami to the test stack to verify
rewritten paths actually reach the upstream.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 17:53:33 +01:00
fuomag9
73c90894b1 Handle wildcard proxy hosts and stabilize test coverage
- accept wildcard proxy host domains like *.example.com with validation and normalization
- make exact hosts win over overlapping wildcards in generated routes and TLS policies
- add unit coverage for host-pattern priority and wildcard domain handling
- add a single test:all entry point and clean up lint/typecheck issues so the suite runs cleanly
- run mobile layout Playwright checks under both chromium and mobile-iphone
2026-03-14 01:03:34 +01:00
fuomag9
cf7eb7757e fix: address code review — responsive overflow, map minWidth, DataTable mobile click handler
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 09:58:45 +01:00
fuomag9
6e8db4ec39 test: add mobile layout E2E tests for iPhone 15
- Create tests/e2e/mobile/mobile-layout.spec.ts with 8 tests covering
  AppBar/hamburger visibility, drawer open/close, mobile card rendering,
  PageHeader button layout, dialog width, card actions, and analytics overflow.
- Fix AnalyticsClient: make Autocomplete full-width on mobile, add
  overflow:hidden to outer Stack to prevent body scrollWidth growth.
- Fix WorldMapInner: remove hard-coded minWidth:400 that caused 73px
  horizontal overflow on 393px iPhone 15 viewport.
- Fix DashboardLayoutClient: add overflowX:hidden to main content area
  to contain chart library elements that exceed viewport width.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 09:04:58 +01:00
fuomag9
599479befa feat: make analytics charts mobile-safe with overflow wrappers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 01:31:34 +01:00
fuomag9
222da9c731 feat: add mobile card views to ImportedTab and CaTab
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 01:28:09 +01:00
fuomag9
b97e41ed07 feat: add mobile card view to AcmeTab
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 01:16:19 +01:00
fuomag9
3c47bbf110 feat: add mobile card view to AuditLogClient
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 01:13:37 +01:00
fuomag9
4245688129 feat: add mobile card view to WafEventsClient
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 01:11:20 +01:00
fuomag9
adda0df2ef feat: add mobile card view to ProxyHostsClient
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 00:50:44 +01:00
fuomag9
a75642bc7f feat: replace floating hamburger with proper mobile AppBar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 00:26:00 +01:00
fuomag9
e5ba3e1ed9 refractor code to allow more tests 2026-03-07 16:53:36 +01:00
fuomag9
264e80ed73 consolidate WAF into unified page, reorder sidebar nav
- Move WAF config (enable, CRS, custom directives, templates) from
  Settings page into a new Settings tab on the WAF page
- WAF page now has three tabs: Events | Suppressed Rules | Settings
- Rename nav item from "WAF Events" to "WAF", route /waf-events → /waf
- Fix excluded_rule_ids preservation: no longer wiped when form field
  is absent (Settings tab omits the hidden field intentionally)
- Allow pre-adding suppressed rules even when WAF is disabled
- Reorder sidebar: Overview, Proxy Hosts, Access Lists, Certificates,
  WAF, Analytics, Audit Log, Settings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 23:58:02 +01:00
fuomag9
41c6db3a3f waf suppressed rules: add by ID with lookup, search filter
- Add lookupWafRuleMessageAction server action — queries WAF event
  history for a known message for any rule ID
- Suppressed Rules tab: type a rule ID, look it up to see its
  description (or a "not triggered yet" note), confirm to suppress
- Duplicate-guard: looking up an already-suppressed rule shows an error
- Search field filters the suppressed list by rule ID or message text
- Newly added rules show their message immediately without page reload

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 22:50:21 +01:00
fuomag9
9a82ad9033 hide revoked client certs by default, add show-revoked toggle
- IssuedCertsPanel preview: only show active (non-revoked) certs
- ManageIssuedClientCertsDialog: filter out revoked by default; show
  "Show revoked (N)" toggle when revoked certs exist

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 22:38:18 +01:00
fuomag9
6ecd195073 redesign certificates page: tabs, drawers, relative expiry, status bar
- Split ACME / Imported / CA-mTLS into tabs with count badges
- Add clickable status summary bar (expired / expiring soon / healthy)
- Per-tab search filter by name and domain
- Replace accordion cards with DataTable for imported certs
- Slide-in Drawers (480 px) for add/edit imported and CA certs
- File upload + show/hide toggle for private key in ImportCertDrawer
- CaCertDrawer: Generate / Import PEM tabs for add, simple form for edit
- CA tab: expandable rows showing issued client certs inline
- RelativeTime component: "in 45 days" / "EXPIRED 3 days ago" with date tooltip
- Remove CreateCaCertDialog and EditCaCertDialog (replaced by CaCertDrawer)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 22:36:46 +01:00
fuomag9
9fa57bcf28 fix mTLS: use trusted_leaf_certs for issued certs, surface CA delete errors
Two bugs fixed:

1. buildClientAuthentication was placing issued leaf cert PEMs into
   trusted_ca_certs. Caddy uses that field for CA chain validation, not
   leaf pinning — putting leaf certs there made chain verification fail
   for every presented client cert, causing the browser to be asked
   repeatedly. Fixed by using trusted_leaf_certs for managed CAs.

2. If all issued certs for a CA were revoked, the active cert map would
   be empty and the code fell back to trusting the CA cert directly,
   effectively un-revoking everything. Fixed by tracking which CAs have
   ever had issued certs (including revoked) and keeping them in
   trusted_leaf_certs mode permanently (empty list = no one trusted).

Also fix CA certificate delete action not surfacing the error message
to the user in production (Next.js strips thrown error messages in
server actions). Changed to return { success, error } and updated the
client dialog to check the result instead of using try/catch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 18:21:48 +01:00
fuomag9
9834fe20c9 simplify global WAF settings: replace toggle+radio with single switch
With DetectionOnly removed, the global WAF had two redundant controls:
an Enable toggle and an Off/On radio, both doing the same thing. Collapse
them into a single labelled switch. Mode is now derived from the enabled
state in the action rather than being a separate form field.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 17:30:13 +01:00
fuomag9
b348dae4be remove DetectionOnly WAF mode
DetectionOnly was fundamentally broken in coraza-caddy (actually blocks
requests via anomaly scoring), caused massive audit log flooding, and the
threshold workaround had several issues:
- t:none is meaningless in a SecAction (no target to transform)
- SecRuleEngine directive ordering relative to SecAction is implementation-
  defined, making the override fragile
- host.mode ?? 'DetectionOnly' fallbacks silently gave any host without an
  explicit mode the broken DetectionOnly behaviour

Changes:
- Remove DetectionOnly from UI (global settings radio, per-host engine mode)
- Coerce legacy DB values of 'DetectionOnly' to 'On' in buildWafHandler
- Fix fallback defaults: host.mode ?? 'DetectionOnly' → host.mode ?? 'On'
- Fix action parsers: unknown mode defaults to 'On' (was 'DetectionOnly')
- Fix global settings defaultValue: ?? 'DetectionOnly' → ?? 'On' (or 'Off')
- Remove the fragile threshold SecAction workaround
- Update types: mode is now 'Off' | 'On' throughout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 17:27:08 +01:00
fuomag9
e06b41b604 fix WAF detection mode and payload logging
- DetectionOnly mode: add SecAction to set anomaly score thresholds to
  9999999 so rule 949110/980130 never fires; works around coraza-caddy
  bug where is_interrupted=true still causes a 403 in detection mode
- Switch SecAuditEngine back to On (from RelevantOnly) so DetectionOnly
  hits are captured, now safe because body parts are excluded
- SecAuditLogParts: ABIJDEFHZ → ABFHZ, dropping request body (I),
  multipart files (J), intermediate response headers (D), and response
  body (E) — prevents multi-MB payloads being written to audit log
- Parser: store both blocked and detected events; filter on rule matched
  OR is_interrupted instead of is_interrupted only
- Add blocked column to waf_events (migration 0014); existing rows
  default to blocked=true
- WAF Events UI: Blocked/Detected chip in table and drawer header
- Fix misleading help text that said to use Detection Only to observe
  traffic before blocking

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 15:32:56 +01:00
fuomag9
044f012dd0 Added issued-client-cert tracking and revocation for mTLS 2026-03-06 14:53:17 +01:00
fuomag9
6acd51b578 export as .p12 2026-03-06 13:25:06 +01:00
fuomag9
333f7d4733 Revert "fix authentik not being removed when toggle is disabled"
This reverts commit 641ab9ef34.
2026-03-06 09:56:59 +01:00
fuomag9
641ab9ef34 fix authentik not being removed when toggle is disabled 2026-03-06 09:34:26 +01:00
fuomag9
c76004f8ac better pki 2026-03-06 00:22:30 +01:00
fuomag9
f3358c20cd feat: add mTLS support for proxy hosts
- New `ca_certificates` table for reusable CA certs (migration 0011)
- CA cert CRUD model, server actions, and UI dialogs
- Proxy host create/edit dialogs include mTLS toggle + CA cert selection
- Caddy config generates `client_authentication` TLS policy blocks with
  `require_and_verify` mode for hosts with mTLS enabled
- CA certs sync to slave instances via instance-sync payload
- Certificates page shows CA Certificates section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 20:29:55 +01:00
fuomag9
bdd3019214 security: add same-origin CSRF check to state-changing user API routes
Adds checkSameOrigin() helper in auth.ts that validates the Origin header
against the Host header. If Origin is present and mismatched, returns 403.
Applied to all 5 custom POST routes flagged in CPM-003 (NEXT-CSRF-001):
  - change-password, link-oauth-start, unlink-oauth, update-avatar, logout

SameSite=Lax (NextAuth default) already blocks standard cross-site CSRF;
this adds defense-in-depth against subdomain and misconfiguration scenarios.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 01:04:18 +01:00
fuomag9
bd4220c74c feat: show WAF count in blocked stat card; clean up proxy hosts table
- Analytics: show "X from WAF" sub-stat under Blocked Requests card
- Proxy hosts: remove WAF column and redundant Status column (toggle already shows enabled state)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 22:44:52 +01:00
fuomag9
c20ba54b4c feat: analytics WAF improvements — bar chart, host chips, country column
- Add getTopWafRulesWithHosts() and getWafEventCountries() model queries
- WAF stats API now returns topRules with per-host breakdown and byCountry
- Analytics: replace WAF rules table with bar chart + host chip details
- Analytics: add WAF column (amber) to Top Countries table

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 22:42:10 +01:00
fuomag9
7ceeb84fc2 improve UX 2026-03-04 22:26:11 +01:00
fuomag9
d959fdf836 if waf for a host is not configured, suppressing a rule for a host should automatically set it to "merge with global" and enabled. 2026-03-04 21:27:15 +01:00
fuomag9
7341070c0d Fix rule parsing for single reverse proxies 2026-03-04 21:16:11 +01:00
fuomag9
77d3e35c63 feat: clickable WAF event rows with detail drawer
- WafEvent model: expose rawData field from DB
- DataTable: add optional onRowClick prop with hover cursor
- WafEventsClient: clicking a row opens a right-side drawer showing
  all event fields plus the raw Coraza audit JSON (pretty-printed)

Safety: rawData is rendered via JSON.stringify into a <pre> element,
never via dangerouslySetInnerHTML, so attack payloads are displayed
as inert text.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 18:21:08 +01:00
fuomag9
edd4e6879f fix: make WAF events table fit in viewport
- DataTable: add overflowX auto to TableContainer + minWidth 600
- WAF events: tighten column widths (Time 150, Host 150, IP 140,
  Method 60), add ellipsis+tooltip on Host column, let Rule Message
  expand to fill remaining space

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 08:26:36 +01:00
fuomag9
0dad675c6d feat: integrate Coraza WAF with full UI and event logging
- Add coraza-caddy/v2 to Caddy Docker build
- Add waf_events + waf_log_parse_state DB tables (migration 0010)
- Add WafSettings type and get/save functions to settings
- Add WafHostConfig/WafMode types to proxy-hosts model
- Add resolveEffectiveWaf + buildWafHandler to caddy config generation
- Create waf-log-parser.ts: parse Coraza JSON audit log → waf_events
- Add WafFields.tsx per-host WAF UI (accordion, mode, CRS, directives)
- Add global WAF settings card to SettingsClient
- Add WAF Events dashboard page with search, pagination, severity chips
- Add WAF Events nav link to sidebar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 22:16:34 +01:00
fuomag9
a45a156068 fix pages viewing 2026-02-27 19:49:11 +01:00
fuomag9
3d88f565ca fix: await searchParams Promise in all paginated pages (Next.js 15+) 2026-02-27 18:40:56 +01:00
fuomag9
f64eb2c8d0 fix: sync search state on navigation; move pagination before create form in access-lists
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 18:05:15 +01:00
fuomag9
f7a092f1e4 feat: add pagination to certificates ACME table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 18:01:27 +01:00
fuomag9
5126273101 feat: add server-side pagination to access-lists page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 18:00:36 +01:00
fuomag9
6bd7fe92d8 feat: add server-side pagination to proxy-hosts page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 18:00:26 +01:00
fuomag9
ce042d968d feat: add server-side pagination and search to audit-log page 2026-02-27 18:00:23 +01:00
fuomag9
28f9fc1d8a feat: show ACME cert expiry and issuer in certificates page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 17:51:14 +01:00
fuomag9
296d17205f fix: count ACME-issued certs in overview (not just imported ones)
Previously only counted rows in the certificates table (0 for most setups).
Now counts: proxy hosts with certificate_id=NULL (each gets one Caddy ACME cert
via HTTP-01/DNS-01) + imported certs with actual PEM data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 16:19:24 +01:00
fuomag9
7e5626f328 fix: align nav icons with overview card icons
SwapHorizIcon→Proxy Hosts, VpnKeyIcon→Access Lists, SecurityIcon→Certificates,
HistoryIcon→Audit Log. Nav now uses the exact same icons as the overview stat
cards so identical sections share identical icons.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 16:03:26 +01:00