Skip to content

feat: admin-controlled policy for self-hosted mail server connections#144

Open
saiththerobo wants to merge 3 commits into
maathimself:mainfrom
saiththerobo:feat/allow-private-host
Open

feat: admin-controlled policy for self-hosted mail server connections#144
saiththerobo wants to merge 3 commits into
maathimself:mainfrom
saiththerobo:feat/allow-private-host

Conversation

@saiththerobo

@saiththerobo saiththerobo commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #126. Also addresses the use case raised in discussion #116.

Adds three admin-only toggles under Settings → Security → Mail Server Connection Policy, allowing self-hosted deployments (protonmail-bridge, Dovecot on localhost, etc.) to connect to local IMAP/SMTP servers while keeping the existing SSRF protection intact for internet-facing instances.

All three flags default to off, so existing deployments are unaffected.

Changes

  • backend/migrations/0013_account_allow_private_host.sql — adds allow_private_hosts, allow_insecure_tls, allow_nonstandard_ports to system_settings
  • backend/src/services/connectionPolicy.js — new module that reads the three flags from the DB
  • backend/src/services/hostValidation.js — all exports accept { allowPrivate } option; DNS rebinding protection is preserved
  • backend/src/services/imapManager.js — reads policy on every connection; enforces TLS requirement; policy errors surface to the UI via sync_error and WebSocket broadcast
  • backend/src/routes/accounts.js — validates host and port against policy on account create/update
  • backend/src/routes/admin.js — PATCH /settings persists the three flags
  • backend/src/routes/send.js — enforces policy for outgoing SMTP
  • frontend/src/components/AdminPanel.jsx — policy toggles in Security tab (auto-save on click); account form shows skip-TLS toggle only when allowInsecureTls is enabled
  • Locale files updated for en, de, es, fr, it, ru, zhCN

Testing

  • All existing tests pass (165 passing)
  • New unit tests for makeClientCfg TLS enforcement and rejectUnauthorized under all policy/account combinations
  • New allowPrivate tests for all three hostValidation exports
  • Manually tested end-to-end with a local IMAP server: private host + non-standard port connect when policy allows, fail with a UI-visible error when revoked
  • Plain-text IMAP correctly blocked when "Allow insecure TLS" is off
  • IMAPS with self-signed cert: fails when policy is off, succeeds when on

Contributor License Agreement

By submitting this pull request I confirm that:

  • I have read and agree to the Contributor License Agreement.
  • My contribution is my own original work (or I have identified any third-party material and confirmed it is compatible with the CLA).
  • I have the right to submit this contribution under the terms of the CLA.

Replaces the per-account allow_private_host toggle with three
server-level policy flags set by admins in Settings → Security:

- allow_private_hosts: permits IMAP/SMTP on private/local addresses
- allow_insecure_tls: exposes per-account TLS verify skip toggle
- allow_nonstandard_ports: unlocks free-form port input

Migration 0013 seeds the three keys in system_settings (default false)
and drops the account-level column added in the earlier draft.
A new connectionPolicy service reads the flags for use in validation
and connection code. The account form shows TLS and port options only
when the admin has enabled them.
- makeClientCfg throws when imap_tls=false and allowInsecureTls policy is off,
  ensuring plain-text IMAP is rejected when admin requires secure connections
- Move makeClientCfg call inside try/catch so policy errors set sync_error
  and broadcast account_error to the UI instead of being silently swallowed
- Block plain-text SMTP when allowInsecureTls is off (STARTTLS and SSL pass)
- Replace Save button on mail policy toggles with auto-save on click,
  matching the existing pattern used by other boolean admin settings
- Export makeClientCfg and add tests covering TLS enforcement and
  rejectUnauthorized behaviour under all policy combinations
@maathimself

Copy link
Copy Markdown
Owner

Hey @saiththerobo — two things to fix before merging:

  1. admin.js dead codeaccount.allow_private_host doesn't exist in the schema, so allowPrivate is always false in the test-email-send path. Replace with the system policy like the rest of the PR.

  2. Breaking change for imap_skip_tls_verify = true accounts — these accounts will lose connectivity on deployment until an admin enables "Allow insecure TLS". Needs a callout in the migration guide or release notes.

Also consider caching getConnectionPolicy() — it hits the DB on every IMAP connection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IMAP: Host cannot be a private or reserved IP address

2 participants