Commit Graph

1952 Commits

Author SHA1 Message Date
fuomag9 89a6a71d3e fix: serve world-atlas topojson locally to satisfy CSP connect-src
Fetching from cdn.jsdelivr.net was blocked by connect-src 'self'.
Copy countries-110m.json from world-atlas npm package into public/geo/
and reference it as /geo/countries-110m.json instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 21:38:01 +01:00
fuomag9 8be69d2774 feat: add analytics dashboard with traffic monitoring
- Parse Caddy access logs every 30s into traffic_events SQLite table
- GeoIP country lookup via maxmind (GeoLite2-Country.mmdb)
- 90-day retention with automatic purge
- Analytics page with interval (24h/7d/30d) and per-host filtering:
  - Stats cards: total requests, unique IPs, blocked count, block rate
  - Requests-over-time area chart (ApexCharts)
  - SVG world choropleth map (d3-geo + topojson-client, React 19 compatible)
  - Top countries table with flag emojis
  - HTTP protocol donut chart
  - Top user agents horizontal bar chart
  - Recent blocked requests table with pagination
- Traffic (24h) summary card on Overview page linking to analytics
- 7 authenticated API routes under /api/analytics/
- Share caddy-logs volume with web container (read-only)
- group_add caddy GID to web container for log file read access

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 20:43:23 +01:00
fuomag9 674e06e3c9 feat: replace country/continent TagInputs with visual flag pickers
Countries:
- Searchable chip grid (flag emoji + name + code) with 249 countries
- Instant search by name or ISO code prefix
- Select matching / Clear matching when search is active
- Select all / Clear all when no search
- Selected-count indicator in toolbar
- Summary strip showing all selected when search is active
- Custom thin scrollbar, 220px viewport

Continents:
- 7 clickable tiles with emoji + name + code
- Select all / Clear all toolbar
- Warning/success color theming per block/allow tab

Both pickers:
- accentColor prop (warning=orange for block, success=green for allow)
- Hidden form input for server compatibility
- Smooth 120ms transitions

Also simplified TagInput to a plain TextField with inline chips
(removes Autocomplete dependency for freeform fields like ASNs/CIDRs/IPs)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 01:31:12 +01:00
fuomag9 3442beba19 fix: expand private_ranges to CIDRs before passing to caddy-blocker-plugin
The blocker plugin only accepts literal IP/CIDR strings; Caddy's built-in
'private_ranges' shorthand is not understood by third-party modules.
Expand it to the equivalent CIDR list at config-build time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 01:25:08 +01:00
fuomag9 f3679f7f45 fix: add statement-breakpoint separators to multi-statement migrations
Drizzle's better-sqlite3 migrator splits on '--> statement-breakpoint'.
Without it, the entire file is passed to db.prepare() as a single
statement, which better-sqlite3 rejects with 'more than one statement'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 00:54:56 +01:00
fuomag9 75044c8d9b fix: harden security post-review (JWT exposure, rate limiter, token expiry, timing)
- Raw JWT never sent to browser: page.tsx uses peekLinkingToken (read-only),
  client sends opaque linkingId, API calls retrieveLinkingToken server-side
- link-account rate limiter now uses isRateLimited/registerFailedAttempt/
  resetAttempts correctly (count only failures, reset on success)
- linking_tokens gains expiresAt column (indexed) + opportunistic expiry
  purge on insert to prevent unbounded table growth
- secureTokenCompare fixed: pad+slice to expected length so timing is
  constant regardless of submitted token length (no length leak)
- autoLinkOAuth uses config.oauth.allowAutoLinking (boolean) instead of
  process.env truthy check that mishandles OAUTH_ALLOW_AUTO_LINKING=false
- Add Permissions-Policy header; restore X-Frame-Options for legacy UAs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 20:58:21 +01:00
fuomag9 b2238f3101 fix: gate unsafe-eval to dev, drop redundant X-Frame-Options, document PKCE+state
- CSP script-src 'unsafe-eval' is now dev-only; Next.js HMR needs it in
  development but the production standalone build does not
- Remove X-Frame-Options: DENY since frame-ancestors 'none' in CSP supersedes
  it in all modern browsers; keeping both creates a maintenance hazard
- Add comment explaining why state check is added alongside PKCE default

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 20:36:43 +01:00
fuomag9 b5b15c2496 fix: use explicit empty Buffer as HKDF salt and log legacy key fallback
- Replace empty string "" salt with Buffer.alloc(0) for explicit intent
  in security-critical HKDF call
- Add console.warn when legacy SHA-256 decryption path is taken so
  operators can track when all secrets have been re-encrypted

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 20:33:45 +01:00
fuomag9 48385684f9 fix: add PKCE to OAuth checks and HTTP security response headers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 18:43:00 +01:00
fuomag9 a1c18cf09c fix: derive AES key with HKDF for key separation from JWT signing key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 18:42:46 +01:00
fuomag9 66ad3e9431 fix: enforce unique provider+subject constraint and harden sync route
- Change providerSubjectIdx from index to uniqueIndex in schema.ts to
  prevent multiple users sharing the same (provider, subject) pair,
  which caused non-deterministic sign-in resolution via findFirst.
- Add migration 0008_unique_provider_subject.sql: DROP the existing
  non-unique index and CREATE UNIQUE INDEX in its place.
- Validate INSTANCE_SYNC_MAX_BYTES env var in sync route: fall back to
  10 MB default when the value is non-numeric (e.g. 'off') or
  non-positive, preventing NaN comparisons that silently disabled the
  size limit.
- Return a generic error message to callers on applySyncPayload /
  applyCaddyConfig failure instead of leaking the raw error string;
  the original message is still stored internally via setSlaveLastSync.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 18:41:12 +01:00
fuomag9 cb3c0a1536 fix: detect auth failure by response URL not status code to fix rate limiter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 18:38:25 +01:00
fuomag9 618982484c fix: verify OAuth provider email against pending link to prevent account takeover
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 18:36:41 +01:00
fuomag9 1348c0b4cd fix: add server-side validation for geoblock_mode, access_list_id, redirect_url, response_status, and response_headers keys
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 09:35:06 +01:00
fuomag9 9a189ea342 fix: store OAuth linking token server-side, remove JWT from URL and audit log
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 09:31:27 +01:00
fuomag9 5d219095b3 fix: use rightmost XFF entry in rate limiter to prevent IP spoofing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 09:25:34 +01:00
fuomag9 0758e5b27a feat: support fail_closed option from caddy-blocker-plugin
New field from upstream plugin: when the real client IP is
indeterminate (trusted proxy present but no usable XFF entry),
fail_closed=true blocks the request instead of passing it through.

- Add fail_closed to GeoBlockSettings type
- Include in mergeGeoBlockSettings (OR semantics: either global or host enables it)
- Emit fail_closed in buildBlockerHandler (only when true)
- Parse geoblock_fail_closed from form in both settings and proxy-host actions
- Add Checkbox UI in the Advanced accordion of GeoBlockFields

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 09:14:46 +01:00
fuomag9 95455a4e8b docs: add geo blocking docs and refresh screenshots
- Add Geo Blocking section to README with rule types and GeoIP setup
- Add Geo Blocking card to landing page (site/index.html)
- Refresh all 4 screenshots from current UI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 09:07:51 +01:00
fuomag9 c08e48a06a chore: gitignore CLAUDE.local.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 08:24:26 +01:00
fuomag9 f80b0c4735 feat: add geoip-status API route with auth
Returns whether GeoLite2-Country and GeoLite2-ASN databases are loaded,
used by the UI to show the GeoIP ready indicator in GeoBlockFields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 08:20:48 +01:00
fuomag9 98e5dbc898 fix: require auth for geoip-status endpoint
Remove /api/geoip-status from the middleware public routes allowlist so
unauthenticated requests are rejected before reaching the route handler.
The route handler already has requireUser() for defense-in-depth.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 01:47:48 +01:00
fuomag9 25e1f2acee fix: make geoip-status a public endpoint (no auth required) 2026-02-25 01:31:06 +01:00
fuomag9 e64e522929 fix: allow unauthenticated access to geoip-status endpoint 2026-02-25 01:27:19 +01:00
fuomag9 1231150550 fix: require auth on geoip-status endpoint 2026-02-25 01:24:11 +01:00
fuomag9 ccef82cca8 feat: add GeoIP status API route and improved geoblock UI 2026-02-25 01:22:04 +01:00
fuomag9 18c890bb21 feat: redesign GeoBlockFields UI with tabs, Autocomplete tag inputs, and accordion 2026-02-23 23:55:40 +01:00
fuomag9 9254d8e910 fix: use node:http for Caddy admin API calls to avoid Sec-Fetch-Mode CORS triggering 2026-02-23 23:49:05 +01:00
fuomag9 4fac5e4d50 fix: remove --resume so Caddy always starts from Caddyfile with correct admin origins 2026-02-23 23:43:28 +01:00
fuomag9 85af993c77 fix: add Origin header to Caddy admin API requests to satisfy CORS origin check 2026-02-23 22:27:31 +01:00
fuomag9 497e58db14 fix: include admin origins in generated Caddy config so they survive /load 2026-02-23 21:50:50 +01:00
fuomag9 1cfdaa061c fix: allow web container to reach Caddy admin API by adding origins 2026-02-23 21:43:50 +01:00
fuomag9 35471ec98c fix: use GOPROXY=direct in xcaddy build to bypass module proxy cache 2026-02-23 20:44:24 +01:00
fuomag9 4332e1acbc feat: make geoipupdate container opt-in via COMPOSE_PROFILES 2026-02-23 20:39:08 +01:00
fuomag9 c5a5c6b743 fix: add syncInstances to updateGeoBlockSettingsAction for consistency
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 19:56:39 +01:00
fuomag9 066c2851e4 feat: add global geoblocking section to settings page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 19:56:38 +01:00
fuomag9 b0abb407c6 feat: add GeoBlockFields to create and edit proxy host dialogs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 19:56:38 +01:00
fuomag9 7165dc4b05 fix: always render geoblock_mode hidden input regardless of showModeSelector
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 19:56:38 +01:00
fuomag9 821842b3a9 feat: add GeoBlockFields UI component
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 19:56:38 +01:00
fuomag9 c233c77bd8 fix: use consistent form parsing helpers in parseGeoBlockConfig
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 19:56:38 +01:00
fuomag9 bca740fcea feat: add parseGeoBlockConfig to proxy host actions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 19:56:38 +01:00
fuomag9 fd9aa986d9 fix: correct enabled logic in geoblock merge and route injection 2026-02-23 19:56:37 +01:00
fuomag9 80177bf067 feat: inject blocker handler into proxy routes for geoblocking
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 19:56:37 +01:00
fuomag9 f54b7db96f fix: align GeoBlock hydrators with existing patterns in proxy host model
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 19:56:37 +01:00
fuomag9 a2daedd80a Update .gitignore 2026-02-23 19:56:37 +01:00
fuomag9 e6e35646c0 feat: add GeoBlock types and hydration to proxy host model
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 19:56:37 +01:00
fuomag9 15208313a8 feat: add GeoBlockSettings type and helpers to settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 19:56:37 +01:00
fuomag9 8024c99a05 feat: add geoipupdate service and shared GeoIP volume
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 19:56:36 +01:00
fuomag9 e45507fcd7 feat: add caddy-blocker-plugin to Caddy Docker image 2026-02-23 19:56:36 +01:00
fuomag9 32f232ebee better UI for dns pinning 2026-02-22 10:00:52 +01:00
fuomag9 bb8a0d1023 implemented upstream pinning 2026-02-22 01:11:56 +01:00