Files
2026-11-05 12:52:32 +01:00

3.5 KiB

Security

Model

  • Devices authenticate with Basic Auth (required by the protocol) - always behind TLS (reverse proxy). The password is verified by logging in to IMAP and is passed to SMTP/DAV only within the same request. It is never stored. The successful login cache keeps only HMAC-SHA256(login, password) with a random key generated at process start.
  • The IMAP pool keeps authenticated connections keyed by the same HMAC - a connection is only reused for a request with identical credentials.
  • The panel is a separate listener (loopback by default; another address requires explicit consent). Administrator passwords: argon2id. Session: random 256-bit token in an HttpOnly; SameSite=Strict cookie (Secure over HTTPS); only its SHA-256 is stored. Every modifying request needs the CSRF header. The first administrator can only be created with a one-time token from the CLI (valid 60 minutes).
  • Panel headers: strict CSP (script-src 'self', no inline code), X-Frame-Options: DENY, nosniff, Referrer-Policy: no-referrer, COOP.

Abuse protection

  • Lockout after auth_fail_max failures per account and 3x per IP within the window; HTTP 429 with Retry-After. Other accounts from the same IP keep working until the IP limit is reached.
  • fail2ban friendly log line: authentication failed user=jan@example.com ip=203.0.113.7 service=activesync
    # /etc/fail2ban/filter.d/active-push.conf
    [Definition]
    failregex = authentication failed user=\S+ ip=<HOST> service=
    journalmatch = _SYSTEMD_UNIT=active-push.service
    
  • Panel: 5 failed logins in 10 minutes lock the IP for 15 minutes; constant verification cost even for unknown accounts.
  • Limits: request size (sync.max_request_mb), attachments, messages, WBXML depth and element count, IMAP response size, timeouts on every backend operation, HTTP header read timeout.
  • Unknown ActiveSync command names are counted as Unknown (no unbounded metric labels).
  • XML (Autodiscover, WebDAV) is parsed without DTD/entities (XXE, billion laughs).
  • The DAV client never follows hrefs to another host (SSRF).
  • Logins, device ids and device types are validated; SQL parameters are always bound.
  • smtp.enforce_from rewrites From to the account address; Bcc is stripped from the transmitted copy.

Device control

  • Provisioning policies (password, encryption, camera, maximum mail age ...). Changing a policy invalidates keys - devices must accept the policy again.
  • Remote wipe (confirmed by typing the device id), device block, approval of new devices, mailbox block, blocked device types/User-Agents.
  • Every administrative action is written to the audit log (who, what, when, from where).

systemd

The service runs as active-push without privileges or capabilities, with ProtectSystem=strict, ProtectHome, PrivateTmp, PrivateDevices, MemoryDenyWriteExecute, a system call filter and write access only to /var/lib/active-push. The database is 0600, the directory 0700, UMask=0077.

Recommendations

  1. TLS only at the reverse proxy, with HSTS; ActivePush on 127.0.0.1.
  2. Panel through an SSH tunnel or VPN. If exposed - TLS + panel.allowed_networks.
  3. SMTP submission with reject_sender_login_mismatch.
  4. Enable security.require_device_approval in company environments.
  5. Set a Prometheus token only if you scrape metrics; the endpoint then lives on the panel port.
  6. logging.log_wbxml contains message content - only for short diagnosis.