Commit Graph

3 Commits

Author SHA1 Message Date
Paul Nothaft 8017370271 feat(settings): expose the API rate limiter in the Security tab (#1338)
* feat(settings): expose the API rate limiter in the Security tab

The general per-IP limiter had six settings in app_settings and a
backend route to write them, and no screen. Installs ran on the code
fallback — 300 requests per 15 minutes per IP — with no way to see it,
which is how issue 1287 played out: a 546-photo gallery exhausted the
budget for one viewer and the operator learned about the setting from
a grep of the backend log.

Security tab: a card with the six settings, the validation ranges the
route enforces, a one-line explanation per field, and a note that the
unit is the client IP — an office or household behind one NAT shares
a budget, and behind a proxy TRUST_PROXY has to cover the proxy or
every visitor shares its address. The tab's Save button saves the
limiter through its own route. The limiter values are checked against
the route's ranges before anything is written and the limiter is
written first, so a rejected value cannot leave the password/session
settings half-saved behind a failure toast.

Backend, three things the screen needed:

- The settings read fills the six keys with the code defaults when
  they have no row, so the form shows the budget in force rather than
  an empty field; the defaults live in one exported constant the
  limiter itself reads.
- The write route upserts instead of updating: on a fresh install,
  which has no rows, the old UPDATE matched nothing and the route
  answered 200 while changing nothing.
- The live limiter instances move into rateLimitService and the
  write route rebuilds them. express-rate-limit fixes windowMs when an
  instance is built — max and skip re-read the settings per request,
  the window does not — so a saved window used to apply only after a
  restart. The gates in server.js resolve the instance per request
  through the service's getters. A rebuild starts fresh counters,
  which on a settings change is acceptable. The limiters get explicit
  MemoryStores and a rebuild shuts the superseded ones down, because a
  store keeps a cleanup interval alive for as long as it exists and
  dropping the reference alone would leak one timer per save.

Tests: the read surfaces defaults and honours the key filter; the
write creates rows on a fresh database, the limiter sees the values
immediately and hands the gates a fresh instance; existing rows are
updated not duplicated; out-of-range values are rejected. The tab
renders the values, edits through the hook state, carries the ranges,
saves with the tab's button, and the pre-write validation accepts the
bounds and rejects outside them and cleared fields.

Relates to issue 1337

* docs(security): point the rate limiter doc at the Security tab and the upsert

---------

Co-authored-by: Paul Nothaft <paul@MacStudio-von-Paul.local>
2026-09-07 20:02:40 +02:00
Paul Nothaft 9bbdca9fc5 docs(security): correct the rate limiter defaults and how they are set (#1336)
* docs(security): correct the rate limiter defaults and how they are set

SECURITY_LOGGING.md said rate_limit_max_requests defaults to 1000 and
that the settings are configurable via the admin panel. The code
fallback when no app_settings row exists is 300 (19e125d8), a fresh
install has no row, and there is no admin screen: the settings are
written by PUT /api/admin/settings/security/rate-limit, which nothing
in the frontend calls. The reporter of issue 1287 ran on the 300
default without any way to see it.

The table now carries the real defaults, what the auth budget counts,
the exemptions including the gallery-image one from v3.127.0-beta.0,
and the TRUST_PROXY and shared-NAT caveats.

Relates to issue 1287

* docs(security): note the rate-limit route only updates existing rows

---------

Co-authored-by: Paul Nothaft <paul@MacStudio-von-Paul.local>
2026-09-07 16:35:13 +02:00
paul 1773ed5f95 Initial commit - Project start (July 17, 2025)
Mirror to GitHub / mirror (push) Successful in 26s
Test and Lint / backend-test (push) Successful in 1m11s
continuous-integration/drone/push Build is passing
Test and Lint / frontend-test (push) Successful in 2m28s
Version and Release / version-bump (push) Successful in 32s
Version and Release / trigger-drone (push) Has been skipped
Original: feat: enhance security logging and ensure rate limit blocks are properly tracked

- Add comprehensive logging for rate limit blocks with full request details
  - IP address (with proper proxy detection), user agent, headers, timestamps
  - Rate limit info (current count, limit, remaining, reset time)
  - Separate tracking for auth vs general endpoints

- Enhance authentication failure logging
  - JWT validation failures with detailed error info
  - Admin auth attempts without token
  - Failed token validation with user context
  - All events include IP, path, method, user agent

- Improve Winston logger configuration for production
  - Add automatic log rotation (10MB errors, 50MB combined)
  - Create separate security.log for auth/rate limit events
  - Ensure logs directory exists automatically
  - Add structured JSON format for log aggregation
  - Support container logging with LOG_TO_CONSOLE env var

- Create comprehensive documentation
  - Security logging guide with examples
  - Monitoring recommendations
  - Configuration reference

- Add test script to verify logging functionality

All rate limit settings remain configurable via admin panel:
- Window duration, max requests, auth limits
- Skip authenticated requests option
- Public endpoints only option

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-24 16:57:07 +02:00