62 lines
3.5 KiB
Markdown
62 lines
3.5 KiB
Markdown
# 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`
|
|
```ini
|
|
# /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 `href`s 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.
|