From 56c35b00e61578acbc916e0016935e162edc1e5b Mon Sep 17 00:00:00 2001 From: gru <1+gru@gitea.linuxiarz.pl> Date: Wed, 4 Nov 2026 15:13:35 +0100 Subject: [PATCH] Update packaging/rustbox.toml --- packaging/rustbox.toml | 245 +++++++++++++++++------------------------ 1 file changed, 102 insertions(+), 143 deletions(-) diff --git a/packaging/rustbox.toml b/packaging/rustbox.toml index 69e3480..a9cef57 100644 --- a/packaging/rustbox.toml +++ b/packaging/rustbox.toml @@ -10,6 +10,12 @@ host = "0.0.0.0" port = 3001 environment = "production" +# Secure attribute of session cookies: auto | always | never. +# auto (default) sets Secure only when the browser reached RustBox over HTTPS: +# X-Forwarded-Proto / Forwarded: proto= / X-Forwarded-Ssl from a trusted proxy, +# otherwise the scheme of the browser's Origin/Referer. Plain-HTTP installations +# therefore keep working. Use "always" behind HTTPS if your proxy strips headers. +cookie_secure = "auto" data_dir = "/var/lib/rustbox" log_dir = "/var/log/rustbox" @@ -39,25 +45,8 @@ default_interface_skin = "apple-mail" sieve_enabled = true diagnostics_enabled = true about_enabled = true - -# Allow multiple IMAP accounts in one browser session. -# When disabled, RustBox hides multi-account UI, rejects adding another account, -# and realtime notifications/watchers are limited to the active account. -multi_account_enabled = true - -# Security headers added by RustBox. Off by default for compatibility. -# -# When enabled: -# - HTML documents receive the full browser security policy -# (CSP, frame protection, referrer/permissions policy, COOP, nosniff). -# - CSS receives only headers that are meaningful for stylesheets. -# - JS/static/API responses receive only headers that are meaningful for -# non-document resources; private API responses use no-store. -# - HSTS is intentionally NOT emitted here. Configure HSTS at the HTTPS -# terminator/reverse proxy, which knows whether the request is really HTTPS. +# Content-type-aware browser security headers. Off by default for compatibility. security_headers_enabled = false - - # ----------------------------------------------------------------------------- # Database # ----------------------------------------------------------------------------- @@ -144,6 +133,18 @@ max_attachment_size_mb = 50 # Maximum complete RFC822/MIME message size in MiB. max_message_size_mb = 100 +# Memory guard for messages with attachments. Sending a message or saving an IMAP +# draft snapshot loads every attachment into RAM and the MIME builder keeps several +# copies (about 3-4x the message size). Attachment sets that cannot fit +# max_message_size_mb are rejected with 413 before any file is read, and at most +# max_concurrent_message_builds requests may hold attachments in memory at once; +# the others wait up to message_build_wait_secs and then get 503. +# Rough peak RAM = max_concurrent_message_builds x 4 x max_message_size_mb. +max_concurrent_message_builds = 4 +message_build_wait_secs = 30 + +# "Undo Send" window in seconds (0 disables; max 120). See config.example.toml. +undo_send_window_secs = 8 # ----------------------------------------------------------------------------- @@ -168,8 +169,6 @@ sieve_timeout_secs = 10 # Reverse proxy # ----------------------------------------------------------------------------- -# Also used by the administrative diagnostics endpoint to validate forwarded -# client addresses. Do not trust arbitrary Internet-facing proxy ranges. trusted_proxies = "127.0.0.1,::1" @@ -231,17 +230,38 @@ pgp_allow_private_export = true # ----------------------------------------------------------------------------- -# Blob storage +# Login brute-force protection # ----------------------------------------------------------------------------- -# -# Blob storage is independent from database_backend. -# -# PostgreSQL/MySQL can use a local filesystem in single-node mode. -# -# Cluster deployments must use: -# - S3-compatible object storage, or -# - a genuinely shared POSIX filesystem such as NFS/CephFS/GlusterFS. -# + +# Login brute-force protection (POST /api/auth/login). Enabled by default. +# Environment overrides use a double underscore, e.g. +# LOGIN_RATE_LIMIT__ACCOUNT_MAX_FAILURES=10 or LOGIN_RATE_LIMIT__ENABLED=false. +# Client IPs are taken from X-Forwarded-For only when the peer is in trusted_proxies. +# State is kept in memory on each node: behind a load balancer every node counts +# separately, so use sticky routing or lower thresholds in cluster mode. +[login_rate_limit] +enabled = true +# Per-IP token bucket: up to ip_burst attempts at once, then one new attempt +# every ip_replenish_secs seconds. Requests over the limit get HTTP 429. +ip_burst = 5 +ip_replenish_secs = 12 +# Failed logins counted inside failure_window_secs before a lockout. +# account_max_failures: per mailbox (case-insensitive), from any IP. +# ip_max_failures: per client IP, across all mailboxes (password spraying). +# 0 disables that lockout type. +account_max_failures = 5 +ip_max_failures = 20 +failure_window_secs = 900 +# First lockout length; every consecutive lockout of the same key doubles it +# up to max_lockout_secs. Locked attempts are refused before contacting IMAP. +# Trade-off: an attacker can keep a known mailbox locked for webmail logins +# (IMAP/SMTP clients are unaffected), so keep max_lockout_secs moderate. +lockout_secs = 300 +max_lockout_secs = 3600 +# IPs/CIDRs exempt from the per-IP lockout (account lockouts still apply). +exempt_networks = [] +# Memory cap per tracking table (accounts, IPs). +max_tracked_keys = 100000 [storage] @@ -302,23 +322,19 @@ retry_timeout_secs = 60 # ----------------------------------------------------------------------------- -# IMAP synchronization / body cache +# IMAP synchronization / automatic body cache # ----------------------------------------------------------------------------- -# -# Periodic synchronization complements IMAP IDLE. The currently watched folder -# can update immediately through IDLE, while this interval is used to reconcile -# counters/flags and to make progress on folders that are not actively open. -# -# Lower values make folder counters converge faster but increase IMAP traffic. -# + [sync] -# Periodic synchronization interval in seconds. +# Periodic fallback/background IMAP reconciliation. Active folders use IMAP IDLE, +# while this pass refreshes counters/flags/headers in the rest of the mailbox and +# also runs automatic deep indexing when enabled. Lower values increase IMAP load. interval_secs = 30 -# Maximum number of complete message bodies fetched from IMAP during one -# deep-cache/index pass. Increasing this speeds initial catch-up but increases -# IMAP traffic, disk writes and indexing CPU usage. +# Maximum full message bodies fetched from IMAP per periodic pass when deep index is +# effective. Larger batches catch up faster but increase bandwidth, application database/ +# search-storage usage and indexing CPU/I/O bursts. deep_index_batch_size = 10 @@ -344,21 +360,6 @@ deep_index_batch_size = 10 # disabled | local_tantivy | opensearch backend = "local_tantivy" -# Global deep body cache/index policy. -# -# false (recommended production default): -# - RustBox does not force full-message caching for every account. -# - An account may still opt in through its own deep_index setting. -# -# true: -# - RustBox automatically downloads complete message bodies from IMAP and keeps -# them in the local/shared cache so the search backend can fully index content. -# - This can significantly increase IMAP bandwidth, local/S3 storage usage, -# indexing CPU and initial synchronization time for large mailboxes. -# -# This does not control IMAP IDLE itself. -deep_index = false - # Local Tantivy path. path = "/var/lib/rustbox/search" @@ -380,6 +381,13 @@ allow_insecure_http = false request_timeout_secs = 10 +# Server-wide automatic full-body cache/index switch. Production default is false +# because enabling it proactively downloads and stores full message bodies. When +# true, all accounts are deep-indexed; when false, an account may still opt in via +# its existing deep_index preference. +deep_index = false + +# Search outbox worker cadence/batch; these do NOT control IMAP body downloads. worker_interval_secs = 2 worker_batch_size = 100 worker_max_attempts = 12 @@ -390,86 +398,6 @@ worker_max_attempts = 12 # password = "change-me" -# ----------------------------------------------------------------------------- -# Administrative system diagnostics endpoint -# ----------------------------------------------------------------------------- -# -# This endpoint is intended for local/cluster troubleshooting and monitoring. -# It is independent from the normal signed-in-user diagnostics UI. -# -# Security model: -# - disabled by default; -# - source IP must match allowed_networks; -# - authentication is additionally required (token or Basic Auth); -# - untrusted source network -> HTTP 403; -# - missing/invalid endpoint credentials -> HTTP 401; -# - backend health can make the diagnostic response report degraded/error. -# -# Behind a reverse proxy, configure trusted_proxies correctly. If forwarded -# client-IP headers are present but the proxy is not trusted, access fails -# closed instead of treating the proxy connection as localhost. -# -# The endpoint NEVER returns configured passwords, access keys, session keys, -# bearer tokens or full credential-bearing DSNs. Secret fields are exposed only -# as configured/not-configured state. -# -# Runtime statistics are intentionally limited to the RustBox process and -# application state. RustBox does not inspect host CPU/RAM/load or /proc for -# this endpoint, keeping it lightweight and portable across supported systems. -# -[diagnostics_endpoint] - -enabled = false - -# Must start with "/api/". -path = "/api/system/diagnostics" - -# Default: localhost only. -# Add explicit internal management networks when required, for example: -# allowed_networks = ["127.0.0.1/32", "::1/128", "10.20.30.0/24"] -allowed_networks = ["127.0.0.1/32", "::1/128"] - -# token | basic -auth = "token" - -# For token authentication uncomment and set a long random secret. -# Keep it outside source control in real deployments, preferably via environment -# configuration/secrets management. -# -# token = "minimum-24-random-characters" - -# For Basic authentication: -# -# auth = "basic" -# basic_username = "rustbox-monitor" -# basic_password = "minimum-16-random-characters" - -# When false, session/account diagnostics avoid exposing user identifiers. -include_user_identifiers = false - -# Timeout used by lightweight dependency probes. -probe_timeout_secs = 5 - -# Sliding security-observation window used to aggregate login failures, -# 401/403/429 responses, 5xx responses and suspicious access patterns. -security_window_secs = 900 - -# The report can include: -# - RustBox version, PID, start time and uptime; -# - effective configuration with secrets redacted; -# - SQL backend/version/pool/schema migration state; -# - Redis/Valkey state when configured; -# - filesystem/S3 blob backend state; -# - Tantivy/OpenSearch state and index health; -# - active sessions and IMAP IDLE/synchronization state; -# - cache/index backlog and worker state; -# - aggregate security signals such as failed logins, 401/403/429/5xx, -# brute-force-like patterns and rejected diagnostic endpoint access. -# -# It intentionally does NOT expose message bodies, passwords, auth headers, -# session secrets, S3 credentials or host-wide resource statistics. - - # ============================================================================= # CLUSTER EXAMPLES # ============================================================================= @@ -491,10 +419,6 @@ security_window_secs = 900 # # pgp_enabled = false # -# [sync] -# interval_secs = 30 -# deep_index_batch_size = 10 -# # [storage] # backend = "s3" # bucket = "rustbox" @@ -510,12 +434,12 @@ security_window_secs = 900 # # [search] # backend = "opensearch" -# deep_index = false # urls = ["https://opensearch.example.net:9200"] # index_prefix = "rustbox" # required = true # allow_insecure_http = false # request_timeout_secs = 10 +# deep_index = false # worker_interval_secs = 2 # worker_batch_size = 100 # worker_max_attempts = 12 @@ -545,7 +469,6 @@ security_window_secs = 900 # # [search] # backend = "opensearch" -# deep_index = false # urls = ["https://opensearch.example.net:9200"] # index_prefix = "rustbox" # required = true @@ -570,3 +493,39 @@ security_window_secs = 900 # redis_sentinel_master = "mymaster" # redis_sentinel_password = "sentinel-secret" # redis_password = "secret" + + +# ----------------------------------------------------------------------------- +# Blob storage +# ----------------------------------------------------------------------------- +# +# Blob storage is independent from database_backend. +# +# PostgreSQL/MySQL can use a local filesystem in single-node mode. +# +# Cluster deployments must use: +# - S3-compatible object storage, or +# - a genuinely shared POSIX filesystem such as NFS/CephFS/GlusterFS. +# + +# ----------------------------------------------------------------------------- +# Administrator system diagnostics endpoint +# ----------------------------------------------------------------------------- +# Disabled by default. When enabled, the endpoint performs live probes of SQL, +# session persistence/Redis, filesystem/S3, Tantivy/OpenSearch and reports +# process/runtime/IMAP-IDLE/security telemetry. Returned configuration is redacted: +# passwords, auth tokens, S3 secrets, session keys and URL credentials are never emitted. +# Access is an AND condition: source IP/CIDR must be allowed and token/basic auth must pass. +[diagnostics_endpoint] +enabled = false +path = "/api/system/diagnostics" +# Fail-safe default. Add only monitoring/admin networks that really need access. +allowed_networks = ["127.0.0.1/32", "::1/128"] +auth = "token" # token | basic +# Prefer injecting these with a secret manager/environment rather than storing here. +# token = "at-least-24-random-characters" +# basic_username = "rustbox-monitor" +# basic_password = "long-random-password" +include_user_identifiers = false +probe_timeout_secs = 5 +security_window_secs = 900