Compare commits

...

267 commits

Author SHA1 Message Date
Dan Marks
69e2b56def
docs: Add warning about broken apple push notifications support (#4541)
Some checks failed
Documentation / Deploy Docs (push) Has been cancelled
Lint / lint (push) Has been cancelled
Documentation / Update `versions.json` if necessary (push) Has been cancelled
Documentation / update `latest` symlink if neccessary (push) Has been cancelled
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2025-08-05 11:30:13 +02:00
dependabot[bot]
0ffcb002f5
chore(deps): Bump docker/metadata-action from 5.7.0 to 5.8.0 (#4540)
Some checks are pending
Lint / lint (push) Waiting to run
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-04 18:29:40 +02:00
Georg Lauterbach
e448b5e53e
chore: disable F2B postfix-sasl jail (#4535)
Some checks failed
Build, Test & Deploy / Build AMD64 Image (push) Has been cancelled
Documentation / Deploy Docs (push) Has been cancelled
Lint / lint (push) Has been cancelled
Build, Test & Deploy / Test AMD64 Image (push) Has been cancelled
Build, Test & Deploy / Publish AMD64 and ARM64 Image (push) Has been cancelled
Documentation / Update `versions.json` if necessary (push) Has been cancelled
Documentation / update `latest` symlink if neccessary (push) Has been cancelled
Signed-off-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-08-03 10:37:41 +02:00
RoelSG
b6e5d34cd4
docs: Podman - Document how to setup DMS with Quadlet (#4183)
Some checks failed
Lint / lint (push) Has been cancelled
Documentation / Deploy Docs (push) Has been cancelled
Documentation / Update `versions.json` if necessary (push) Has been cancelled
Documentation / update `latest` symlink if neccessary (push) Has been cancelled
Merged in it's current state due to time constraints to revise to desired state when the revised docs would still be beneficial to users. Final revision deferred to a follow-up PR.

---------

Co-authored-by: Casper <casperklein@users.noreply.github.com>
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-08-01 12:24:55 +12:00
fim-sh
7a0b499c18
docs: Fix path in dkim helper script (rspamd) (#4531)
The path was still referring to an internal location used prior to preferring the DMS config volume, this has been corrected.

---------

Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2025-08-01 12:06:40 +12:00
dependabot[bot]
8011772feb
chore(deps): Bump anchore/scan-action from 6.4.0 to 6.5.0 (#4533)
Some checks failed
Lint / lint (push) Has been cancelled
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-28 18:22:24 +02:00
Mahdi Baghbani
e4c319b39c
docs: fix minor typo (#4532)
Some checks failed
Lint / lint (push) Has been cancelled
Documentation / Deploy Docs (push) Has been cancelled
Documentation / Update `versions.json` if necessary (push) Has been cancelled
Documentation / update `latest` symlink if neccessary (push) Has been cancelled
2025-07-27 12:10:40 +00:00
Ivan Filonenko
fcd9909841
fix(fail2ban): configure logrotate only when Fail2Ban is enabled (#4523)
Some checks failed
Build, Test & Deploy / Build AMD64 Image (push) Has been cancelled
Lint / lint (push) Has been cancelled
Build, Test & Deploy / Test AMD64 Image (push) Has been cancelled
Build, Test & Deploy / Publish AMD64 and ARM64 Image (push) Has been cancelled
Co-authored-by: Casper <casperklein@users.noreply.github.com>
2025-07-11 17:02:42 +02:00
Andreas Perhab
337ae071d2
open-dkim: use numerical uid and gid for chown (#4517)
Some checks are pending
Build, Test & Deploy / Build AMD64 Image (push) Waiting to run
Build, Test & Deploy / Test AMD64 Image (push) Blocked by required conditions
Build, Test & Deploy / Publish AMD64 and ARM64 Image (push) Blocked by required conditions
Lint / lint (push) Waiting to run
Co-authored-by: Casper <casperklein@users.noreply.github.com>
2025-07-10 22:14:39 +00:00
dependabot[bot]
20798411a3
chore(deps): Bump anchore/scan-action from 6.3.0 to 6.4.0 (#4519)
Some checks failed
Lint / lint (push) Has been cancelled
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-07 20:12:30 +02:00
Éric Veiras
6bc4d243e2
docs: fix minor typo (#4513)
Some checks failed
Documentation / Deploy Docs (push) Has been cancelled
Lint / lint (push) Has been cancelled
Documentation / Update `versions.json` if necessary (push) Has been cancelled
Documentation / update `latest` symlink if neccessary (push) Has been cancelled
Co-authored-by: Casper <casperklein@users.noreply.github.com>
2025-06-23 20:18:15 +02:00
dependabot[bot]
63f183dfc1
chore(deps): Bump docker/setup-buildx-action from 3.11.0 to 3.11.1 (#4514)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 20:16:00 +02:00
Brennan Kinney
eb87c88339
docs: Adjust PROXY Protocol port names (#4511)
Some checks failed
Documentation / Deploy Docs (push) Has been cancelled
Lint / lint (push) Has been cancelled
Documentation / Update `versions.json` if necessary (push) Has been cancelled
Documentation / update `latest` symlink if neccessary (push) Has been cancelled
2025-06-20 06:42:12 +02:00
dependabot[bot]
ade4ef528e
chore(deps): Bump docker/setup-buildx-action from 3.10.0 to 3.11.0 (#4509)
Some checks failed
Lint / lint (push) Has been cancelled
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Casper <casperklein@users.noreply.github.com>
2025-06-17 18:52:04 +02:00
dependabot[bot]
5fb87e11ac
chore(deps): Bump anchore/scan-action from 6.2.0 to 6.3.0 (#4510)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-17 18:46:01 +02:00
dependabot[bot]
0c3aff21ff
chore(deps): Bump docker/build-push-action from 6.17.0 to 6.18.0 (#4500)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-02 15:00:35 +02:00
Brennan Kinney
3c193a101e
chore: Simplify compose.yaml healthcheck (#4498) 2025-06-02 07:27:53 +00:00
s0ftcorn
e296eb4f26
docs: Revise docs for the OVERRIDE_HOSTNAME ENV (#4492)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2025-06-02 19:22:04 +12:00
Brennan Kinney
8fa6e6deba
chore: Avoid logging a warning when LOG_LEVEL is unset (#4497) 2025-06-02 17:01:00 +12:00
Brennan Kinney
ea03808c8f
fix: packages.sh - Remove Postfix hostname workaround (#4493) 2025-05-27 23:03:10 +02:00
Brennan Kinney
f6381d3bb0
fix: Ensure DMS config volume can be accessed by non-root users (#4487) 2025-05-23 16:05:20 +12:00
Ionut
61c9b21f94
docs: PROXY protocol (Traefik) - Fix config typo (#4483) 2025-05-23 10:49:50 +12:00
Brennan Kinney
e5728cf7b6
docs: Revise TLS_LEVEL ENV description (#4482) 2025-05-21 21:06:32 +12:00
Brennan Kinney
a0e0013260
chore: Typo fix for debug log (#4480) 2025-05-21 11:50:05 +12:00
dependabot[bot]
7b6f1cf7ea
chore(deps): Bump docker/build-push-action from 6.16.0 to 6.17.0 (#4477)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-19 16:19:46 +02:00
Alessio Artoni
53c36194d9
feat: Enable reading env vars from files (#4359)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-05-17 23:37:43 +02:00
worldworm
c9aac24a72
docs: mailserver.env - Remove unsupported SASL auth mechanisms (#4472) 2025-05-11 12:16:04 +12:00
dependabot[bot]
902354552f
chore(deps): Bump anchore/scan-action from 6.1.0 to 6.2.0 (#4468)
Bumps [anchore/scan-action](https://github.com/anchore/scan-action) from 6.1.0 to 6.2.0.
- [Release notes](https://github.com/anchore/scan-action/releases)
- [Changelog](https://github.com/anchore/scan-action/blob/main/RELEASE.md)
- [Commits](https://github.com/anchore/scan-action/compare/v6.1.0...v6.2.0)

---
updated-dependencies:
- dependency-name: anchore/scan-action
  dependency-version: 6.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-06 13:52:44 +02:00
Brennan Kinney
fc51996e43
docs(xapian): Add note about config compatibility (#4464) 2025-05-02 09:44:52 +12:00
dependabot[bot]
0ee8d83764
chore(deps): Bump docker/build-push-action from 6.15.0 to 6.16.0 (#4459)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.15.0 to 6.16.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.15.0...v6.16.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 6.16.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-01 11:47:21 +02:00
Brennan Kinney
491c30b194
fix: setup email list should only work with ACCOUNT_PROVISIONER=FILE (#4453)
---------

Signed-off-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-04-25 10:06:35 +12:00
Brennan Kinney
4b0e3a5002
tests: Reference the new testssl image location (#4454) 2025-04-23 22:16:36 +02:00
litetex
f2e5891b16
feat: Configurable poll rate for check-for-changes.sh (#4450)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
Co-authored-by: Casper <casperklein@users.noreply.github.com>
2025-04-23 18:43:41 +02:00
Brennan Kinney
b653d9a586
docs: Contributing (tests) - Link to guidance for TEST_TMP_CONFIG (#4451) 2025-04-21 17:29:39 +02:00
Brennan Kinney
23bb1c8e50
refactor: setup CLI open-dkim (#4375)
Refactoring this `setup` CLI command as part of the effort to unify our DKIM feature support between OpenDKIM + Rspamd:
- Adds a `main()` method similar to other setup CLI commands.
- Help text more aligned with equivalent rspamd DKIM setup CLI command.
- DRY some repetition such as hard-coded paths to use variables.
- OpenDKIM config files are created / initialized early on now with `_create_opendkim_configs()`. `while` loop only needs to append entries, so is easier to grok.
- `_create_dkim_key()` to scope just the logic (_and additional notes_) to key generation via `opendkim-genkey`
- Now overall logic with the `while` loop of the script occurs in `_generate_dkim_keys()`:
  - Ownership fixes are now applied after the `while` loop as that seems more appropriate than per iteration.
  - Temporary VHOST config is now removed since it's no longer useful after running.
- Tests adjusted for one new log for adding of default trusted hosts content.

Overall this should be nicer to grok/maintain. Some of this logic will be reused for the unified DKIM generation command in future, which is more likely to shift towards all domains using the same keypair by default with rspamd/opendkim config generated at runtime rather than reliant upon DMS config volume to provide that (_still expected for private key_).

---------

Co-authored-by: Casper <casperklein@users.noreply.github.com>
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-03-31 11:27:28 +02:00
beertje44
229ebba1b8
docs: Dovecot Solr - Add compatibility note (#4433)
Co-authored-by: Casper <casperklein@users.noreply.github.com>
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2025-03-31 11:00:57 +13:00
Christian Schmidt
df7a98ec50
chore: Fix broken README link for SRS (#4434) 2025-03-29 12:11:29 +01:00
Georg Lauterbach
5027f4f5b6
release: v15.0.2 (#4432)
* chore: prepare for release of v15.0.2

Signed-off-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>

* Update CHANGELOG.md

---------

Signed-off-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2025-03-27 00:09:31 +01:00
Moritz Poldrack
c2c48b2b83
fix: ensure message content is not modified by header filter (#4429) 2025-03-26 12:24:20 +13:00
Georg Lauterbach
70d645d863
release: v15.0.1 (#4423)
Signed-off-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-03-23 01:27:47 +01:00
Brennan Kinney
a3571a88c1
fix: DMS state volume must ensure o+x permission (#4420) 2025-03-18 23:48:12 +01:00
tranquillity-codes
8ca2bd212c
chore: Gender-neutral language (#4421)
Co-authored-by: itycodes <tranquillitycodes@proton.me>
2025-03-18 19:08:56 +01:00
Georg Lauterbach
0362fa682e
fix: include all files in change detection of Rspamd (#4418) 2025-03-18 00:08:14 +01:00
Brennan Kinney
7c680a0fbc
fix: start-mailserver.sh requires mail_state.sh to be sourced on restarts (#4417) 2025-03-16 15:34:51 +01:00
Brennan Kinney
a156c2c031
docs: Update Dovecot link in mailserver.env (#4415) 2025-03-16 21:04:32 +13:00
Lasslos
6b1a566497
docs: Fail2Ban - Add example with required ENV to enable (#4402) 2025-03-06 08:29:39 +01:00
Brennan Kinney
02f068b2b2
fix: Use correct Postfix parameter for postfix-receive-access.cf (#4399) 2025-03-05 11:00:06 +13:00
Brennan Kinney
d0629f4cb6
chore: Revise utility install scripts + add Smallstep step CLI (#4376)
Changes:
- `jaq` should probably live in `/usr/local/bin` with other third-party sourced binaries.
- `swaks` install properly with just `tar`, no `mv` + `rm` needed.
- Added Smallstep `step` CLI. This serves similar purpose to `openssl` commands, but is generally nicer for usage with generation and inspection of certs/keys. I've talked up using in DMS a few times in the past for our TLS helper and unifying DKIM support (_instead of separate OpenDKIM/Rspamd generators_).
- Including `step` for both AMD64 / ARM64 archs needs the alternate naming convention that it's published to GH releases with.
- Added commentary about the `tar` usage. The ownership is a common concern with GH release sources, technically a non-issue when running as `root`
2025-03-03 22:58:42 +01:00
Dmitry R.
1756ba04fb
fix: Support chmod on /var/log/mail/* when dir is empty (#4391)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-03-03 21:28:15 +00:00
dependabot[bot]
807f4f7118
chore(deps): Bump docker/setup-qemu-action from 3.4.0 to 3.6.0 (#4392) 2025-03-03 21:58:58 +01:00
dependabot[bot]
0fbbc44dd3
chore(deps): Bump docker/build-push-action from 6.14.0 to 6.15.0 (#4393) 2025-03-03 20:52:28 +00:00
dependabot[bot]
3c833d8ee8
chore(deps): Bump docker/setup-buildx-action from 3.9.0 to 3.10.0 (#4394) 2025-03-03 20:50:17 +00:00
dependabot[bot]
dd595e0a05
chore(deps): Bump docker/metadata-action from 5.6.1 to 5.7.0 (#4395) 2025-03-03 21:47:59 +01:00
Brennan Kinney
5686a4097a
fix: setup email restrict configs should only prepend once (#4379)
* fix: `setup email restrict` configs should only prepend once

* chore: Prepend to our custom parameter variant to retain applying to all `smtpd` ports

---------

Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-03-01 13:55:13 +01:00
dependabot[bot]
309b5a9086
chore(deps): Bump docker/build-push-action from 6.13.0 to 6.14.0 (#4389)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.13.0 to 6.14.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.13.0...v6.14.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-03-01 12:24:47 +00:00
Georg Lauterbach
ef66dd5d12
release: v15.0.0 (#4373)
Signed-off-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2025-03-01 12:14:57 +00:00
Brennan Kinney
41dd0727e4
docs(rspamd): Fix Web UI link (#4384) 2025-02-21 08:48:17 +01:00
Brennan Kinney
d2d74a29a7
fix: Ensure /var/log/mail permissions + ownership are correct (#4374) 2025-02-18 09:02:35 +13:00
Georg Lauterbach
0294294755
fix: revert __declare_readonly overcomplication (#4372)
* fix: revert `__declare_readonly` overcomplication
* chore: remove redundant checks

Signed-off-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-02-17 00:14:18 +00:00
Brennan Kinney
bcee78e2c1
docs: Revise Rspamd page (#4360) 2025-02-16 23:17:29 +01:00
Casper
0ebf820b00
Make deletion of mailbox data opt-in (#4365)
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-02-16 10:46:49 +01:00
Brennan Kinney
07e558e4be
docs: Fix broken ref links (#4366) 2025-02-16 10:22:40 +01:00
Brennan Kinney
aba92b7bb8
ci: Upgrade mkdocs-material to 9.6 (#4368) 2025-02-16 09:53:01 +01:00
dependabot[bot]
f2fedff251
chore(deps): Bump docker/setup-buildx-action from 3.8.0 to 3.9.0 (#4352) 2025-02-13 21:46:09 +01:00
Brennan Kinney
425d1162ae
chore: packages.sh - Bump versions + housekeeping (#4357) 2025-02-13 13:16:31 +01:00
Alessio Artoni
ca877999ec
docs: Fix typo in DKIM and utils.sh (#4358) 2025-02-12 20:34:22 +01:00
Brennan Kinney
83bfe72d48
chore: Migrate dovecot config from Dockerfile (#4350) 2025-02-12 11:56:51 +13:00
dependabot[bot]
c66d8ce40b
chore(deps): Bump docker/setup-qemu-action from 3.3.0 to 3.4.0 (#4353) 2025-02-11 10:36:20 +01:00
Georg Lauterbach
59a379aed7
scripts: restructure container restart behavior (#4323)
Signed-off-by: georglauterbach <44545919+georglauterbach@users.noreply.github.com>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-09 10:23:06 +13:00
Brennan Kinney
85793988d6
chore: demo-setups/relay-compose.yaml should use network alias (#4347) 2025-02-07 10:10:37 +13:00
Casper
e116920f4d
Add missing "setup debug getmail" command and documentation (#4346) 2025-02-05 23:10:57 +01:00
Zlatibor Veljkovic
0e61f170fd
docs: bind-smtp-network-interface.md - Add bridge network config advice (#4330)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2025-02-04 12:45:43 +13:00
Georg Lauterbach
3faa40bfb5
docs: add ARC example to Rspamd documentation (#4328)
Signed-off-by: georglauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-02-02 21:35:50 +00:00
dependabot[bot]
4d8a56072a
chore(deps): Bump docker/build-push-action from 6.12.0 to 6.13.0 (#4331)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.12.0 to 6.13.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.12.0...v6.13.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-01-29 11:02:25 +01:00
dependabot[bot]
7cb2fc788f
chore(deps): Bump anchore/scan-action from 6.0.0 to 6.1.0 (#4332)
Bumps [anchore/scan-action](https://github.com/anchore/scan-action) from 6.0.0 to 6.1.0.
- [Release notes](https://github.com/anchore/scan-action/releases)
- [Changelog](https://github.com/anchore/scan-action/blob/main/RELEASE.md)
- [Commits](https://github.com/anchore/scan-action/compare/v6.0.0...v6.1.0)

---
updated-dependencies:
- dependency-name: anchore/scan-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-29 10:56:37 +01:00
dependabot[bot]
2d56210c52
chore(deps): Bump docker/build-push-action from 6.11.0 to 6.12.0 (#4324)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.11.0 to 6.12.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.11.0...v6.12.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-22 21:42:25 +01:00
Georg Lauterbach
f0daa1c8ab
chore: remove VERSION file (#4321) 2025-01-19 01:27:07 +01:00
Georg Lauterbach
3ebca5daba
Revert "fix: fix incorrect link in README.md (#4184)" (#4322)
This reverts commit 84180f879e.
2025-01-19 09:26:07 +13:00
dependabot[bot]
8df1fba96e
chore(deps): Bump docker/build-push-action from 6.10.0 to 6.11.0 (#4310)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.10.0 to 6.11.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.10.0...v6.11.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2025-01-15 12:38:15 +13:00
dependabot[bot]
a302bd79e3
chore(deps): Bump docker/setup-qemu-action from 3.2.0 to 3.3.0 (#4309)
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v3.2.0...v3.3.0)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-15 12:36:44 +13:00
Brennan Kinney
e6d519b6f8
docs: TLS (Caddy) - Revise advice on tls internal (#4305) 2025-01-06 09:02:02 +13:00
Wojciech Woźniak
24fb65ce7b
docs: Environment - Update Dovecot docs URL (#4296)
Co-authored-by: Casper <casperklein@users.noreply.github.com>
2024-12-28 18:00:27 +13:00
Pooyan Khanjankhani
259f2031fc
docs: Fix typo on usage page (#4294)
Co-authored-by: Casper <casperklein@users.noreply.github.com>
2024-12-23 14:38:46 +13:00
dependabot[bot]
02415b03dc
chore(deps): Bump anchore/scan-action from 5.3.0 to 6.0.0 (#4292) 2024-12-17 08:26:14 +01:00
dependabot[bot]
a03ff8ff7c
chore(deps): Bump docker/setup-buildx-action from 3.7.1 to 3.8.0 (#4293) 2024-12-16 14:18:51 +01:00
Casper
96bffd7979
chore(compile.sh): Consistent apt-get install command 2024-12-06 21:22:37 +01:00
Brennan Kinney
cd225f1250
ci(bug_report.yml): Drop the feedback field (#4283)
This input has not provided much value to us since it's introduction, removing as redundant.
2024-12-07 02:04:13 +13:00
Brennan Kinney
9f0918c335
fix(packages.sh): swaks --help (#4282)
This command requires the `perl-doc` package to work.
2024-12-07 02:02:35 +13:00
dependabot[bot]
10882f97f2
chore(deps): Bump docker/build-push-action from 6.9.0 to 6.10.0 (#4278) 2024-12-02 21:51:52 +01:00
Brennan Kinney
d07e6d67d6
chore: Update jaq to 2.0.0 (#4277)
- Bump to [`jaq` v2 release](https://github.com/01mf02/jaq/releases/tag/v2.0.0), artifact naming convention changed.
- Tidied up the changelog a little bit unrelated to this `jaq` update.
- Fixed a typo with an `rspamd.sh` comment + minor revision to the comment.
2024-11-29 16:12:00 +13:00
dependabot[bot]
edfecbceb1
chore(deps): Bump anchore/scan-action from 5.2.1 to 5.3.0 (#4274)
Bumps [anchore/scan-action](https://github.com/anchore/scan-action) from 5.2.1 to 5.3.0.
- [Release notes](https://github.com/anchore/scan-action/releases)
- [Changelog](https://github.com/anchore/scan-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/anchore/scan-action/compare/v5.2.1...v5.3.0)

---
updated-dependencies:
- dependency-name: anchore/scan-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-11-26 09:11:35 +13:00
dependabot[bot]
ab087d28b3
chore(deps): Bump docker/metadata-action from 5.5.1 to 5.6.1 (#4273)
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5.5.1 to 5.6.1.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](https://github.com/docker/metadata-action/compare/v5.5.1...v5.6.1)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-26 09:08:10 +13:00
Brennan Kinney
c15354058f
fix: SASLAuth - Drop services for mysql, shadow, pam auth mechanisms (#4259) 2024-11-20 17:19:58 +13:00
Brennan Kinney
02f1894f74
ci(docs-preview): Acquire PR context via gh CLI (#4267) 2024-11-20 16:37:34 +13:00
Brennan Kinney
6b4627ceab
ci(docs-preview): Refactor workflows (#4262)
**Overview of changes:**
- Runner bumped from Ubuntu 22.04 => 24.04
- Revised inline documentation for maintainers.
- The output of `build-docs.sh` is now grouped in the steps action log, and now hides the noise from pulling the image via `docker run`.
- Removed the separate `tar` steps with ZSTD as there is only a directory to archive with recent changes to this workflow. The `upload` + `download` actions are sufficient.
- The `workflow_run` job has had the PR context restore step extracted to a separate job to minimize noise.
- `actions-netlify` is still effectively the same functionality.
  - `github-token` is no longer configured as it doesn't appear needed with the functions disabled.
  - Opt-out of the GH deployments feature which is not needed.
2024-11-15 13:00:40 +13:00
dependabot[bot]
b960efad74
chore(deps): Bump anchore/scan-action from 5.2.0 to 5.2.1 (#4260)
Bumps [anchore/scan-action](https://github.com/anchore/scan-action) from 5.2.0 to 5.2.1.
- [Release notes](https://github.com/anchore/scan-action/releases)
- [Changelog](https://github.com/anchore/scan-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/anchore/scan-action/compare/v5.2.0...v5.2.1)

---
updated-dependencies:
- dependency-name: anchore/scan-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-12 08:49:29 +13:00
Brennan Kinney
a599936c4b
ci: docs-preview-deploy.yml - Remove third job condition
This condition was added as an additional guard but was preventing  the workflow from running  when PRs were from forked repos.
2024-11-10 18:57:31 +13:00
Alvaro Muñoz
0ff9c0132a
ci: Revise docs-preview-deploy.yml (#4247)
- Fixes the `if` condition that was recently adjusted.
- Better documents concerns for maintainers to be aware of.
- Reference the `pull_requests` ENV at runtime instead of embedding content into the script via GHA context expression. This is a better practice which prevent exploits from untrusted inputs (_notably for context objects which might introduce new fields in future_).

---------

Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-11-05 12:50:08 +13:00
dependabot[bot]
dc0a6403b2
chore(deps): Bump anchore/scan-action from 5.1.0 to 5.2.0 (#4249)
Bumps [anchore/scan-action](https://github.com/anchore/scan-action) from 5.1.0 to 5.2.0.
- [Release notes](https://github.com/anchore/scan-action/releases)
- [Changelog](https://github.com/anchore/scan-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/anchore/scan-action/compare/v5.1.0...v5.2.0)

---
updated-dependencies:
- dependency-name: anchore/scan-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-05 09:21:53 +13:00
Brennan Kinney
e6bd0b0a09
ci(pr-docs): Acquire metadata from context (#4244)
The metadata needed is available via context, prefer this approach instead.
2024-11-01 14:11:33 +13:00
Georg Lauterbach
662afec1d1
doc: add an example for using the keytype ed25519 when using DKIM & Rspamd (#4243) 2024-10-30 13:32:17 +13:00
dependabot[bot]
ff8fc8013b
chore(deps): Bump anchore/scan-action from 4.1.2 to 5.1.0 (#4239) 2024-10-28 19:23:45 +01:00
pitilux
34eb54ac39
fix: Avoid alias being used as regex during dovecot dummy account userdb detection (#4222)
Applies alternative approach previously suggested by @polarathene and adds test cases to prevent future regressions
2024-10-12 11:34:20 +13:00
dependabot[bot]
26a44995a9
chore(deps): Bump docker/setup-buildx-action from 3.6.1 to 3.7.1 (#4216)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.6.1 to 3.7.1.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3.6.1...v3.7.1)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-08 09:01:58 +13:00
dependabot[bot]
c29fe3ff0b
chore(deps): Bump docker/build-push-action from 6.7.0 to 6.9.0 (#4205)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.7.0 to 6.9.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.7.0...v6.9.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-01 08:54:57 +13:00
Georg Lauterbach
2bcc5cf9de
Rspamd documentation: update Abusix signup link (#4204) 2024-09-29 12:53:10 +02:00
Georg Lauterbach
3937e1e719
scripts: improve DKIM path scanning in Rspamd setup (#4201) 2024-09-29 12:18:43 +02:00
Georg Lauterbach
1a938dfb15
Rspamd: update GTube patters in tests (#4191) 2024-09-28 11:27:34 +00:00
Georg Lauterbach
3bf32a6552
add dedicated feature requests to new project automatically (#4198) 2024-09-28 10:52:49 +00:00
Brennan Kinney
025a38d736
chore: Add maintenance note for LMTP (#4199) 2024-09-26 20:01:35 +12:00
Georg Lauterbach
94751e00c9
dependency: update jaq from 1.3.0 to 1.6.0 (#4190) 2024-09-21 21:04:06 +02:00
GallowsDove
84180f879e
fix: fix incorrect link in README.md (#4184) 2024-09-18 19:34:42 +02:00
Brennan Kinney
cace9c56d9
fix: Dovecot LDAP config should exist (#4175)
The config was not copied over during image build, and the associated auth config had a typo for the `mechanisms` key.
2024-09-09 19:00:53 +12:00
Brennan Kinney
4e85f799fc
fix: Dovecot LDAP config should exist 2024-09-09 09:58:12 +12:00
dependabot[bot]
3349bba1ff
chore(deps): Bump anchore/scan-action from 4.1.1 to 4.1.2 (#4166)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-26 18:30:22 +02:00
Vetu
a87d49e8f8
fix: typo in volume pathname (#4165) 2024-08-24 20:42:32 +02:00
dependabot[bot]
cb963a9a8b
chore(deps): Bump docker/build-push-action from 6.6.1 to 6.7.0 (#4163) 2024-08-19 21:44:33 +02:00
dependabot[bot]
9589d2192b
chore(deps): Bump anchore/scan-action from 4.1.0 to 4.1.1 (#4162) 2024-08-19 20:27:27 +02:00
Casper
ab2127ba67
chore: Add comments to start-mailserver.sh and stop using inherit_errexit (#4161) 2024-08-19 00:51:44 +02:00
Brennan Kinney
310786453b
chore(Dockerfile): COPY ClamAV database from debian images (#4160)
Changes ClamAV image source from DockerHub clamav/clamav (Alpine) to clamav/clamav-debian. Only the Debian variant offers multi-platform images.

This isn't too important since we are only interested in taking a copy of the database from the image. It should however resolve a CI warning.
2024-08-17 22:55:31 +12:00
Casper
b2978fd760
breaking: Refactor getmail support (#4156)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2024-08-17 12:14:59 +02:00
dependabot[bot]
fb57905aa3
chore(deps): Bump docker/build-push-action from 6.5.0 to 6.6.1 (#4158)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-13 13:26:05 +02:00
dependabot[bot]
d61909bdea
chore(deps): Bump docker/setup-buildx-action from 3.5.0 to 3.6.1 (#4152)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.5.0 to 3.6.1.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3.5.0...v3.6.1)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-06 19:15:35 +12:00
Brennan Kinney
526fd64d11
fix: Ensure main log file is tailed from the start (#4146)
Co-authored-by: Casper <casperklein@users.noreply.github.com>
2024-08-03 12:04:21 +12:00
Brennan Kinney
2f8ad142ec
fix: Prevent stderr being written to /etc/postfix/main.cf (#4147)
`stderr` is filtered by `grep` to discard unwanted (expected) log noise when appending the override `postfix-main.cf` content (_updated settings did not replace earlier defined instances_).

That `grep` filter introduced a regression into DMS v14 release, since any other `stderr` content not being excluded was now blended into `stdout` and redirected with the original `stdout` output for the `postconf -n` command.

The fix is to ensure the `grep` output is redirect to `stderr` to avoid that mishap.
2024-08-02 09:42:39 +12:00
Brennan Kinney
a338c06639
docs: Add caveat for ENV DMS_VMAIL_UID value compatibility (#4143) 2024-08-01 08:57:36 +12:00
Casper
37e5203a69
ci: Remove CONTRIBUTORS.md (#4141) 2024-07-31 09:04:51 +02:00
Casper
01194b7552
docs: Add minimal compose.yaml examples that demonstrate specific features (#4138)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-07-30 18:57:33 +02:00
github-actions[bot]
c544d770e7
docs: updated CONTRIBUTORS.md (#4139) 2024-07-30 18:34:17 +02:00
dependabot[bot]
32c0a346fc
chore(deps): Bump anchore/scan-action from 4.0.0 to 4.1.0 (#4125)
Bumps [anchore/scan-action](https://github.com/anchore/scan-action) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/anchore/scan-action/releases)
- [Changelog](https://github.com/anchore/scan-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/anchore/scan-action/compare/v4.0.0...v4.1.0)

---
updated-dependencies:
- dependency-name: anchore/scan-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-23 09:12:47 +12:00
dependabot[bot]
093e43480c
chore(deps): Bump docker/build-push-action from 6.4.0 to 6.5.0 (#4126)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.4.0 to 6.5.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.4.0...v6.5.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-23 08:46:13 +12:00
dependabot[bot]
720a4a2534
chore(deps): Bump docker/setup-qemu-action from 3.1.0 to 3.2.0 (#4127) 2024-07-22 16:53:57 +02:00
dependabot[bot]
2edd6936c4
chore(deps): Bump docker/setup-buildx-action from 3.4.0 to 3.5.0 (#4128) 2024-07-22 16:48:59 +02:00
Moritz Poldrack
009237cc26
chore: Prevent Microsoft MUAs from sending reactions (#4120)
---------

Signed-off-by: Moritz Poldrack <~git@mp.gy>
2024-07-22 23:43:11 +12:00
Brennan Kinney
0698ad9370
docs: Refactor pages for Account Management (#4122)
* docs: Relocate account / auth pages into a common section

* docs: Update references to relocated pages

* docs: Add account management overview page

Updates remaining links to account sections on this page instead (_for `accounts`, `aliases`, `quotas`_).

This page will cover the features and defer to separate pages for more specific content where relevant.

* docs: Correct relocated pages titles and links

* docs: Accounts (Dovecot Master) - Minor revisions

* docs: Fix highlighting roundcube PHP snippet in OAuth2 page

* docs: Accounts (File) - Refactor

- Manual method not necessary to document.
- Condense `setup` example guidance.
- Quotas / Aliases content migrated to Overview when not specific about file provisioner.

Some of the content is this commit is not a complete revision.

* chore: Temporary commit

* docs(refactor): Sub-addressing section

Much better docs on the sub-addressing feature supported by Postfix and Dovecot, along with the guidance with usage in Sieve.

* docs:  Revise accounts section

Add some context regarding DMS accounts and their distinction/overlap from the email address functionality, and it's relevant context for receiving/sending.

File provisioner, minor revisions to referencing associated config files and account management.

* docs: Minor adjustments

* docs: Refactor the quota section

Better documented with links and coverage over the workaround details we've implemented.

* docs: Revise the quota section

Minor revisions with phrasing, admonitions for structure and better explanation of the feature functionality/purpose.

* docs: Alias section refactor

Extensively covers known issues and technical details that have been discussed often enough.

The improvements should benefit both users and maintainers.

* docs: Refactor master accounts page

This rewrite should more clearly document the feature, along with a better example and additional links for reference.

* docs: OAuth2 revision

Minor update to this page:
- Links extracted to bottom of page as per convention.
- ENV file example converted to preferred `compose.yaml` ENV settings.

* docs: Sieve minor revisions

- Correct link to subaddressing section
- Make the config file example snippets intended filename less ambiguous.
- Minor rephrasng.

* docs: Revise accounts overview section

Revised the account section and added additional clarity for common confusion with relation to sender address and multi-domain support.

Top of the page now clarifies it's a technical reference and directs users to the related pages for configuration / caveats.

Technical Overview links to Dovecot docs were missing.

* docs: Another revision pass

File based provisioner docs:
- Sections indent with info admonitions.
- Accounts section expanded with config format and example.
- Quotas section expanded and shifted to bottom (alphabetical sort).
- Split into `setup` CLI and config reference groups.

Overview page:
- Sections indent with info admonitions.
- Revised content.

* docs(chore): Shift sub-addressing section

This is related to accounts and aliases, but not provisioners, thus extract out of the accounts parent section.

* docs: Document `postfix-accounts.cf` third column

This lacked documentation but was community contributed feature to allow further customization of a Dovecot Account.

It has caveats as DMS does not take these into consideration anywhere in scripts. Documenting officially for better awareness.

* docs: Revise and expand supplementary pages

Better outline the OAuth2 login process, the two supported login mechanisms and their docs/rfcs, along with documenting caveat with mail client compatibility.

Add a verification tip for the OAuth2 support, showing how `curl` can be used, along with caveat presently affecting the `curl` in DMS v14.

Additionally note the feature still isn't documented fully, providing the user with additional references for more information.

`ACCOUNT_PROVISIONER` ENV docs minimized. No `OIDC` provisioner plans, the OAuth2 docs page now mentions SCIM 2.0 API as the next step towards resolving that concern. The tip admonition was removed as it no longer provides value, instead we link to the Account Management overview page.

Dovecot Master Accounts docs page now lightly document the `setup` CLI and config format for the feature.

* docs: Fix broken anchor links

Some anchor links to different parts of our docs have gone stale. This branch also broke a few itself that I missed.

The build now only reports issues with anchor links to Content Tabs, which it must not be aware of during the build (_MKDocs Material specific feature?_)

* docs(lint): Fix indentation level

* chore: Add entry to `CHANGELOG.md` + corrections
2024-07-22 10:00:53 +12:00
Brennan Kinney
c5f125c973
tests: Update curl note for XOAUTH2 support (#4118) 2024-07-18 10:31:50 +12:00
dependabot[bot]
34423c2f66
chore(deps): Bump docker/build-push-action from 6.3.0 to 6.4.0 (#4113)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.3.0 to 6.4.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.3.0...v6.4.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-16 01:44:17 +00:00
dependabot[bot]
bf4ebc2a41
chore(deps): Bump anchore/scan-action from 3.6.4 to 4.0.0 (#4114)
Bumps [anchore/scan-action](https://github.com/anchore/scan-action) from 3.6.4 to 4.0.0.
- [Release notes](https://github.com/anchore/scan-action/releases)
- [Changelog](https://github.com/anchore/scan-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/anchore/scan-action/compare/v3.6.4...v4.0.0)

---
updated-dependencies:
- dependency-name: anchore/scan-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-16 13:42:20 +12:00
Brennan Kinney
755540cacf
docs: docker-build.md - Update DOVECOT_COMMUNITY_REPO default (#4111) 2024-07-14 11:24:33 +02:00
Brennan Kinney
4778f15fda
docs: TLS typo fix (#4106)
- Caddy admonition - Missing `[` for annotating a link.
- Traefik - `docker-compose` => "Docker Compose"
2024-07-09 19:44:09 +02:00
dependabot[bot]
3a40c457fc
chore(deps): Bump docker/setup-qemu-action from 3.0.0 to 3.1.0 (#4105)
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v3.0.0...v3.1.0)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 23:26:20 +00:00
dependabot[bot]
2cca907615
chore(deps): Bump docker/setup-buildx-action from 3.3.0 to 3.4.0 (#4104)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3.3.0...v3.4.0)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 23:24:48 +00:00
dependabot[bot]
19d52d9dcc
chore(deps): Bump docker/build-push-action from 6.2.0 to 6.3.0 (#4103)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.2.0 to 6.3.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.2.0...v6.3.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-09 11:23:02 +12:00
Brennan Kinney
2d12bbb7fd
docs: Update compose.yaml for dovecot-solr guide (#4099)
The `image` field is used for the default tag, if it's not specified Compose will infer one in addition to any extra `tags` provided.

Better to use `image` for the tag assignment, and a clear `pull_policy` to prevent trying to pull a remote image of the same name.
2024-07-05 23:48:42 +12:00
Brennan Kinney
9175424d0f
fix: Update dovecot-fts-xapian to 1.7.13 (#4095)
* fix: Update `dovecot-fts-xapian` to `1.7.13`

Contains a fix to a regression introduced that broke indexing

---------

Co-authored-by: casperklein <casperklein@users.noreply.github.com>
2024-07-03 22:16:02 +12:00
dependabot[bot]
b3a5e9e4e8
chore(deps): Bump docker/build-push-action from 6.1.0 to 6.2.0 (#4089)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.1.0 to 6.2.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.1.0...v6.2.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-03 12:17:19 +12:00
Georg Lauterbach
22383c28e7
CI: Remove reviewer assignment from Dependabot config (#4088) 2024-06-30 11:34:38 +02:00
github-actions[bot]
5a4a136ec5
docs: updated CONTRIBUTORS.md (#4084)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-29 12:44:00 +02:00
dependabot[bot]
ccaa02b8b5
chore(deps): Bump docker/build-push-action from 6.0.0 to 6.1.0 (#4086) 2024-06-25 22:43:10 +02:00
Casper
e370c0c96a
fail2ban install: remove -k (--insecure) from curl options (#4080) 2024-06-19 18:34:18 +02:00
Brennan Kinney
8a082be714
docs: Add info regarding DKIM key rotation and non-expiry (#4076)
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
Co-authored-by: Casper <casperklein@users.noreply.github.com>
2024-06-19 08:39:06 +00:00
Casper
d7dab2d20d
feat: Add password confirmation (#4072)
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2024-06-19 06:10:00 +00:00
dependabot[bot]
98cbcfc171
chore(deps): Bump docker/build-push-action from 5.4.0 to 6.0.0 (#4074)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Casper <casperklein@users.noreply.github.com>
2024-06-18 22:30:24 +00:00
beertje44
40aab6bd18
docs: Add tutorial for configuring Dovecot FTS with Solr (#4070)
Describe how to use Apache Solr as a Dovecot FTS backend.

---------

Co-authored-by: Casper <casperklein@users.noreply.github.com>
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-06-18 08:46:34 +02:00
github-actions[bot]
e4aff5531e
docs: updated CONTRIBUTORS.md (#4069)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-16 18:36:10 +02:00
Brennan Kinney
8e32635993
docs: Document fix for PROXY protocol with postscreen (#4066) 2024-06-15 14:26:33 +02:00
dependabot[bot]
e6713a0aec
chore(deps): Bump docker/build-push-action from 5.3.0 to 5.4.0 (#4062)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.3.0 to 5.4.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5.3.0...v5.4.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-11 14:33:57 +12:00
mmehnert
5c798e6829
Update logwatch ignore.conf to exclude Xapian messages about pending documents (#4060)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-06-09 15:12:49 +02:00
Casper
18d9d1adcc
Fail2ban 1.1.0 (#4045) 2024-06-08 13:43:25 +02:00
Brennan Kinney
f1df81a7e4
docs: mailserver.env improve description for SPAM_SUBJECT (#4050)
Co-authored-by: Casper <casperklein@users.noreply.github.com>
2024-06-07 18:42:05 +12:00
Georg Lauterbach
eb165ded65
prepare v14.0.0 release (#4013)
Co-authored-by: Casper <casperklein@users.noreply.github.com>
2024-06-05 17:14:35 +02:00
github-actions[bot]
e78d5c61ee
docs: updated CONTRIBUTORS.md (#4043) 2024-06-03 08:35:07 +02:00
Georg Lauterbach
f8b3f40276
scripts: update log format (#4035)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-05-29 12:28:51 +12:00
Guillaume VARA
95d965fb76
docs(k8s): Advise externalTrafficPolicy: Local if no PROXY protocol configured (#4039)
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-05-28 15:52:30 +00:00
Tobias Knecht
a96a4e2768
Abusix docs links update. (#4038) 2024-05-28 13:20:15 +02:00
github-actions[bot]
94bde85ac6
docs: updated CONTRIBUTORS.md (#4036) 2024-05-26 10:41:01 +02:00
Georg Lauterbach
b222035112
scripts: perform additional checks when updating/adding/deletting accounts (#4033)
* normalize accounts to lowercase
* update CHANGELOG
* add test to verify bug fix works correctly
2024-05-25 17:56:19 +00:00
Georg Lauterbach
4119849284
update: Dovecot FTS Xapian from 1.5.5 to 1.7.12 (#4034)
* update `compile.sh` and Dovecot FTS Xapian to 1.7.12
  - I updated from 1.5.5. Moreover, I adjusted the script to have what I
consider better style.
* update Dockerfile to use recent updates
* update CHANGELOG
2024-05-25 19:49:45 +02:00
Jiří Štefka
993c7b044f
breaking: Drop Dovecot support for Solr (#4025) 2024-05-21 16:49:39 +02:00
Jiří Štefka
92b06c4020
docs(rspamd): Add guidance for setting WebUI password (#4023)
* fix(docs/rspamd): Add section on how to setup WebUI password

* Apply review suggestion

* Apply suggestions from code review

* Update rspamd.md

---------

Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-05-20 15:07:42 +02:00
github-actions[bot]
03c905e6f1
docs: updated CONTRIBUTORS.md (#4021) 2024-05-19 14:06:59 +02:00
Brennan Kinney
ed669bd314
fix: /var/mail-state should not symlink non-existing directories (#4018)
Fixes an issue with the Getmail service, view PR thread for additional details.
- Log an error when the expected service state directory doesn't exist.
- The location `/var/lib/getmail/` doesn't seem like it should have been introduced. Drop it in favor of `/tmp/docker-mailserver/getmail`. It appears to be for storing remote mail that was retrieved if not configured to send to Dovecot like our docs advise. This location was never valid anyway (_as referenced issue covers_).
2024-05-19 22:32:53 +12:00
pyy
a780fb3311
docs: Add tip for disabling the default SPF service (#4019)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-05-17 21:38:02 +12:00
Georg Lauterbach
006f442cd0
Update .ecrc.json to exclude CONTRIBUTORS.md (#4020) 2024-05-17 09:57:07 +02:00
github-actions[bot]
5bd8df68eb
docs: updated CONTRIBUTORS.md (#4014)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-12 11:55:22 +02:00
mmehnert
dab3d9fe71
chore(logwatch): Add ignore.conf to ignore logs from Dovecot index-worker (#4012) 2024-05-12 09:59:22 +02:00
dependabot[bot]
016d6b5255
chore(deps): Bump akhilmhdh/contributors-readme-action (#4005) 2024-05-06 19:02:41 +02:00
F. Eber
10f72224ca
Update typo in kubernetes.md (#4003)
Added the missing "s" on "submissions", otherwise this error comes up:
The Service "mailserver" is invalid: spec.ports[2].name: Duplicate value: "submission"
2024-05-05 19:26:58 +02:00
github-actions[bot]
d52b813cd9
docs: updated CONTRIBUTORS.md (#4002) 2024-05-05 11:42:59 +02:00
Brennan Kinney
e2c2a22dcf
fix: postfix-main.cf may depend upon postfix-master.cf (#3880)
Custom parameters must be referenced to be retained when `postconf -n` is run. If those parameters are referenced by `postfix-master.cf` this needs to update `master.cf` before updating `main.cf`.
2024-05-03 11:12:34 +12:00
Brennan Kinney
7822a97430
docs(FAQ): Add advice for restricting login by IP (#3999) 2024-05-02 07:48:05 +00:00
Brennan Kinney
d00edd7209
docs: Revise fetchmail page (#3998) 2024-05-02 07:44:54 +00:00
Brennan Kinney
7dcbbd7173
fix(accounts.sh): Sync user home location for alias workaround (#3997) 2024-05-02 19:41:25 +12:00
Wael
83da191f3a
docs: Fix link for getmail6 (#3996) 2024-05-02 12:08:29 +12:00
github-actions[bot]
be8615f129
docs: updated CONTRIBUTORS.md (#3992) 2024-04-28 19:06:50 +02:00
github-actions[bot]
162e66276a
docs: updated CONTRIBUTORS.md (#3984) 2024-04-25 08:38:38 +02:00
dependabot[bot]
1051a5d921
chore(deps): Bump akhilmhdh/contributors-readme-action (#3987)
Bumps [akhilmhdh/contributors-readme-action](https://github.com/akhilmhdh/contributors-readme-action) from 2.3.6 to 2.3.8.
- [Release notes](https://github.com/akhilmhdh/contributors-readme-action/releases)
- [Commits](https://github.com/akhilmhdh/contributors-readme-action/compare/v2.3.6...v2.3.8)

---
updated-dependencies:
- dependency-name: akhilmhdh/contributors-readme-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-22 15:46:34 +02:00
Fürst
df360516ff
docs: Add config guide for relaying to and from a private DMS instance (#3973)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
Co-authored-by: Casper <casperklein@users.noreply.github.com>
2024-04-22 11:50:02 +12:00
Brennan Kinney
d739fe3785
chore: Remove base-60 port quote warning from example compose.yaml (#3982)
This should not be relevant to users of `docker compose` which is the primary demographic.
2024-04-21 23:28:11 +00:00
Brennan Kinney
ac22caf74e
docs: Updates to TLS page (Caddy, testing, etc) (#3981) 2024-04-20 11:25:02 +12:00
Tobia Bocchi
942920615c
docs: Fix typo on usage page (#3980) 2024-04-18 13:08:26 +12:00
Iztok Fister Jr
d87e4d3bfd
docs: Fix typos (#3979) 2024-04-16 20:25:45 +00:00
fanqiaojun
dc51850030
chore: remove repetitive words (#3977) 2024-04-15 19:48:55 +00:00
dependabot[bot]
f231425982
chore(deps): Bump peaceiris/actions-gh-pages from 3.9.3 to 4.0.0 (#3978) 2024-04-15 18:05:07 +02:00
github-actions[bot]
ad5d1011f8
docs: updated CONTRIBUTORS.md (#3971)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-04-11 10:18:17 +02:00
dependabot[bot]
8c5cf03203
chore(deps): Bump docker/setup-buildx-action from 3.2.0 to 3.3.0 (#3972)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-08 17:56:12 +02:00
github-actions[bot]
d502dae068
docs: updated CONTRIBUTORS.md (#3967)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-04-01 17:20:35 +02:00
Georg Lauterbach
6733a172d7
docs: add FAQ entry about DNS servers and drop feature request on custom DNS servers for Rspamd (#3966)
* add FAQ entry about DNS servers

I also opted for including a quote from @polarthene which illustrates
how DNS servers are a difficult topic and should not be DMS'
responsibility.

* link to DNS FAQ from Rspamd page & drop feature request

The feature request annotation has been removed because we decided it's
not DMS responsibility to ensure correctly working DNS servers.
2024-03-31 02:14:02 +00:00
Georg Lauterbach
4f10089c90
docs: add note about custom F2B setup with PROXY protocol (#3964) 2024-03-29 14:07:13 +13:00
Inseo Song
082e076377
docs: Add relay host config guide for Gmail (#3958)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-03-28 17:02:11 +13:00
github-actions[bot]
0dad7c49a4
docs: updated CONTRIBUTORS.md (#3944) 2024-03-21 20:53:00 +01:00
Casper
3125cad45a
Enable spamassassin only, when amavis is enabled too. (#3943) 2024-03-21 00:53:04 +01:00
dependabot[bot]
849293f88c
chore(deps): Bump docker/setup-buildx-action from 3.1.0 to 3.2.0 (#3946)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3.1.0...v3.2.0)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-03-19 20:48:35 +00:00
dependabot[bot]
7017f4c081
chore(deps): Bump docker/build-push-action from 5.2.0 to 5.3.0 (#3947)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.2.0 to 5.3.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5.2.0...v5.3.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-20 09:46:14 +13:00
Casper
066773e79f
Better support regular container restarts (#3929)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2024-03-17 16:31:55 +01:00
github-actions[bot]
910667d586
docs: updated CONTRIBUTORS.md (#3930) 2024-03-14 13:22:43 +01:00
Brennan Kinney
cdcd86420e
docs: Add IPv6 troubleshooting tip (#3938)
Sometimes a user may have a configuration error and get halfway there. This should help point them in the right direction.
2024-03-14 15:24:33 +13:00
Rahil Bhimjiani
ede95e6f7f
docs: Update links for account management in README.md (#3937) 2024-03-14 10:14:14 +13:00
Georg Lauterbach
2133b51e78
docs: rewrite Kubernetes page (#3928) 2024-03-12 09:31:44 +01:00
dependabot[bot]
a04b53f4f8
chore(deps): Bump nwtgck/actions-netlify from 2.1 to 3.0 (#3933)
Bumps [nwtgck/actions-netlify](https://github.com/nwtgck/actions-netlify) from 2.1 to 3.0.
- [Release notes](https://github.com/nwtgck/actions-netlify/releases)
- [Changelog](https://github.com/nwtgck/actions-netlify/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/nwtgck/actions-netlify/compare/v2.1...v3.0)

---
updated-dependencies:
- dependency-name: nwtgck/actions-netlify
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-12 11:46:34 +13:00
dependabot[bot]
9bc8869715
chore(deps): Bump docker/build-push-action from 5.1.0 to 5.2.0 (#3934) 2024-03-11 19:40:48 +01:00
Ikko Eltociear Ashimine
8bdda5f433
Update user-patches.sh (#3932) 2024-03-11 12:02:22 +01:00
Casper
267fc552d2
getmail: remove temp file usage (#3920) 2024-03-09 14:21:02 +01:00
Kirill Kirilenko
3649699197
fix: Move spam to mailbox associated to the \Junk special-use attribute (#3925)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-03-07 11:13:22 +13:00
Georg Lauterbach
e21e5e0490
Rspamd: update history key in Redis configuration (#3927) 2024-03-06 08:44:34 +01:00
Georg Lauterbach
b5b193ca4c
Rspamd: minor tweaks and follow-up for SPF, DKIM and DMARC symbols (#3923)
* move `policies_group.conf` to correct location

I originally assumed the file had to be placed into `scores.d`, but I
now know that `local.d` is actually correct.

* add configuration for composite symbols

See updates to #3690:

Additional Rspamd Symbols

Rspamd has so-called composite symbols that trigger when a condition
is met. Especially AUTH_NA and AUTH_NA_OR_FAIL will adjust the scores
of various lines in the table above. This needs to be taken into account.

* update CHANGELOG
2024-03-05 10:48:49 +01:00
Brennan Kinney
d227d6dc73
docs: Reference systemd timer example (cerbot renew) (#3921) 2024-03-05 20:33:04 +13:00
dependabot[bot]
899b644a04
chore(deps): Bump docker/setup-buildx-action from 3.0.0 to 3.1.0 (#3924)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-04 18:19:57 +01:00
github-actions[bot]
0c8d8f26d9
docs: updated CONTRIBUTORS.md (#3916) 2024-03-03 22:50:06 +01:00
Casper
83a48e8958
Fail2ban logrotate interval/count: substitute only when necessary (#3919) 2024-03-03 22:48:42 +01:00
Georg Lauterbach
12f5101d84
Rspamd: improve SPF, DKIM and DMARC Symbol Weights (#3913) 2024-03-02 02:42:47 +01:00
Casper
736f2e44bc
Fail2Ban: Align logrotate count & interval (#3915) 2024-03-01 01:00:23 +01:00
Casper
aa9465773c
Rename supervisor-app.conf to dms-services.conf (#3908)
* rename supervisor-app.conf to dms-services.conf

* changelog added
2024-02-28 22:08:19 +01:00
Dominic Germain
512f39c7eb
feat: Configurable number of rotated log files (#3907) 2024-02-28 15:34:30 +01:00
dependabot[bot]
2c1faa7244
chore(deps): Bump myrotvorets/set-commit-status-action (#3911) 2024-02-26 13:45:50 +01:00
github-actions[bot]
d3ccaddb70
docs: updated CONTRIBUTORS.md (#3909)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-02-25 12:54:49 +01:00
Jesse Portnoy
95dfc71b54
Fix typo and broken README link (#3906) 2024-02-25 00:06:58 +01:00
Casper
e232e43d32
fix: fetchmail environment variables (#3901) 2024-02-21 11:19:41 +01:00
Brennan Kinney
67faa95b0b
fix(setup): open-dkim log for conflicting implementations (#3899) 2024-02-20 21:33:04 +13:00
Brennan Kinney
d86c3cb159
chore: packages.sh - Remove redundant comment (#3900) 2024-02-20 21:21:22 +13:00
Robbert Klarenbeek
a815bf5ab4
fix: Apply SELinux security context after moving to mail-state (#3890)
* fix: Apply SELinux security context after moving to mail-state
* fix: Ignore failing chcon on non-SELinux systems
2024-02-16 20:24:39 +13:00
github-actions[bot]
79a9656f48
docs: update CONTRIBUTORS.md (#3883)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-02-14 09:48:35 +00:00
Brennan Kinney
22555347ed
docs: Complete rewrite of PROXY protocol guide (#3882) 2024-02-13 19:42:17 +13:00
Frugan
34654c7e20
chore: Source Postgrey whitelist_clients config from Github (#3879)
Use a more updated list for Postgrey `whitelist_clients`
2024-02-09 10:23:58 +13:00
Georg Lauterbach
4f222fe256
Rspamd: improve DKIM key generation (#3876)
* correct removal of old files with `--force`

`rm` would fail when one of the files is not present, which is quite
undesirable log (not harmful until `set -e` is introduced).

* use tmp log file

ref: https://github.com/docker-mailserver/docker-mailserver/issues/3873#issuecomment-1926736020

* correct indentation
2024-02-06 00:30:22 +01:00
Rahil Bhimjiani
51a3915257
docs: fix 404 in mailserver.env and default to RSA 2048 for TLS certs (#3875)
* fix 404: broken MTA-STS link in comment of mailserver.env

Signed-off-by: Rahil Bhimjiani <me@rahil.rocks>

* docs: recommend and default to RSA 2048 for ssl certs

Signed-off-by: Rahil Bhimjiani <me@rahil.rocks>

---------

Signed-off-by: Rahil Bhimjiani <me@rahil.rocks>
2024-02-06 11:56:47 +13:00
dependabot[bot]
d5efaf95c3
chore(deps): Bump anchore/scan-action from 3.6.1 to 3.6.4 (#3877)
Bumps [anchore/scan-action](https://github.com/anchore/scan-action) from 3.6.1 to 3.6.4.
- [Release notes](https://github.com/anchore/scan-action/releases)
- [Changelog](https://github.com/anchore/scan-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/anchore/scan-action/compare/v3.6.1...v3.6.4)

---
updated-dependencies:
- dependency-name: anchore/scan-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-02-05 12:35:57 +00:00
dependabot[bot]
9fc7f97950
chore(deps): Bump docker/metadata-action from 5.5.0 to 5.5.1 (#3878)
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5.5.0 to 5.5.1.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](https://github.com/docker/metadata-action/compare/v5.5.0...v5.5.1)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-06 01:34:20 +13:00
github-actions[bot]
32dcabe826
docs: update CONTRIBUTORS.md (#3869)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-02-04 10:05:24 +00:00
Jackson Zheng
2c02671587
Minor spelling correction (#3870) 2024-02-04 11:04:07 +01:00
Georg Lauterbach
db661bf3ac
docs: misc improvements (but mostly related to Rspamd) (#3858)
* remove leftover statement on `/etc/os-release`
* update wording on the PR template
* add section about other services to Rspamd docs
* remove more outdated information from Rspamd docs
* moved links and minor rewording in Rspamd docs

---------

Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-02-02 18:38:22 +01:00
Hans-Cees Speel
45935f5fb8
rspamd: add neural module config (#3833)
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-02-01 17:34:33 +01:00
Aaron Spettl
05fbcf6889
fix(rspamd): Add missing comma to local_networks setting (#3862)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-01-31 11:50:58 +01:00
Brennan Kinney
d426f724cd
docs: Complete rewrite of Relay Host pages (#3861)
* docs: Complete rewrite on relay host docs

- Both relay docs pages have had heavy refactor / rewrite.
- ENV docs page relay host section revised.

* docs: Revise relay host page with technical details section

* docs: Add LDAP compatibility caveat for `RELAY_HOST`
2024-01-31 23:11:19 +13:00
Casper
d65b2f35a7
chore: CHANGELOG.md - Add rsyslog breaking changes for v14 (#3854)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-01-31 11:04:42 +13:00
Brennan Kinney
5b54d1d32e
refactor: relay.sh (#3845)
* chore: `relay.sh` helper - Reference user config paths via variables

* chore: Better document postfix helper `_vhost_collect_postfix_domains()`

The functionality is effectively the same for the two configs for the most part when it comes to parsing out a domain from the target value.

Virtual aliases is more flexible in value, which may not have a domain-part present (manual user edit).

* chore: `check-for-change.sh` - Support VHOST change visibility

- Moves the "handle changes" logic into it's own scoped function, out of the main change detection loop logic.
- This will be benefit a future commit change that will rely on `VHOST_UPDATED=1`.

* chore: `relay.sh` - Minor revisions to minimize diff noise

- Better phrasing of the current logic comments.
- Regex patterns assigned to variables (easier to grok intention)
- Bulk of the logic for generating `/etc/postfix/relayhost_map` wrapped into a separate function with Postfix config setting handled separately.

* refactor: `relay.sh` opt-out logic

- Split the two distinct features that configure `/etc/postfix/relayhost_map` into separate functions (_`MATCH_VALID` var no longer needed for legacy support_).
- Instead of extracting domains from `postfix-accounts.cf` + `postfix-virtual.cf`, this has already been handled at `/etc/postfix/vhost`, sourcing from there is far less complicated.
- Rename loop var `DOMAIN_PART`to `SENDER_DOMAIN` for better context of what it represents when appended to the config file.
- Revised maintenance notes + guidance towards a future refactor of this relayhost feature support.

* docs: `relay.sh` - Additional comment revisions

* feat: `DEFAULT_RELAY_HOST` can now also use relay credentials ENV

- Remove comment regarding `smtp_sasl_password_maps = static:${RELAY_USER}:${RELAY_PASSWORD}`, it could be used but `main.cf` presently has `644` permissions vs the `sasl_passwd` file permissions of `600`, less secure at preventing leaking of secrets (ignoring the ENV exposure itself).
- Move the `main.cf` settings specific to relayhost credentials support / security into to the relevant function scope instead. This also allows for the configuration to be applied by a change detection event without container restart requirement.
- Outer functions for setup and change detection to call have a clearer config dependency guard, as does the `_legacy_support()`.
- These changes now support `DEFAULT_RELAY_HOST` to leverage the relay credentials ENV as well.
- `DATABASE_RELAYHOSTS` is available in scope to the functions called here that reference it.

* docs: Revised ENV docs on relay host config

Better quality guidance on configuring relay hosts.

* chore: Add entry to `CHANGELOG.md`

* fix: `relay.sh` - `grep` regex compatibility with `+` requires `-E`

* chore: `postfix.sh` - `FIRST_FIELD` => More descriptive field name
2024-01-31 10:24:43 +13:00
Andreas Perhab
dfd5edc000
docs: Add new local dependency (file) for running tests (#3856) 2024-01-30 23:17:58 +13:00
Casper
23705e6712
fix: abort when (jaq) curl fails (#3853) 2024-01-30 19:34:26 +13:00
Brennan Kinney
244c455ca1
fix: packages.sh - Download jaq via release tag not latest (#3852)
As the filename includes the version / tag, we cannot rely on the latest URL to be stable.
2024-01-30 11:11:45 +13:00
Georg Lauterbach
4162d608e4
Rspamd scripts: only correct permissions when directory exists (#3849) 2024-01-30 10:10:03 +13:00
dependabot[bot]
2018be7fdc
chore(deps): Bump anchore/scan-action from 3.6.0 to 3.6.1 (#3848)
Bumps [anchore/scan-action](https://github.com/anchore/scan-action) from 3.6.0 to 3.6.1.
- [Release notes](https://github.com/anchore/scan-action/releases)
- [Changelog](https://github.com/anchore/scan-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/anchore/scan-action/compare/v3.6.0...v3.6.1)

---
updated-dependencies:
- dependency-name: anchore/scan-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2024-01-29 14:08:59 +01:00
Georg Lauterbach
afb0093939
spam: use Sieve for rewriting subject with Rspamd & SA/Amavis (#3820) 2024-01-29 13:38:01 +01:00
Brennan Kinney
3b11a8305e
docs: Remove ENV ONE_DIR (#3840)
* docs: Better document DMS volumes

* docs: Remove any mention of `ONE_DIR` ENV

* chore: Remove `ONE_DIR` ENV from scripts

Only `ONE_DIR=0` has any effect. As the actual feature is now dependent upon the `/var/mail-state` location existing.

It is advised not mounting anything there instead if wanting to avoid runtime state consolidation.

* docs: Adjust link ref convention

This is more search friendly / organized to find references to all DMS volumes.

* lint: Ensure final newline is present

VSCode by default excludes this if the last line rendered is removed (rendered as a separate blank line).

A separate setting can enforce adding the final newline upon save regardless.
2024-01-29 10:35:19 +13:00
github-actions[bot]
11c508cd11
docs: update CONTRIBUTORS.md (#3844)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Casper <casperklein@users.noreply.github.com>
2024-01-28 11:15:47 +00:00
Brennan Kinney
204825fa5a
ci(fix): docs-preview-deploy.yml - Use the correct setting names (#3843) 2024-01-28 20:41:19 +13:00
Brennan Kinney
4a05d7bb7c
docs: Add Debian 12 breaking change for opendmarc package (#3841) 2024-01-28 10:23:49 +13:00
Brennan Kinney
f27629be4e
docs: Minor revisions to README.md (#3839) 2024-01-28 01:51:37 +13:00
Brennan Kinney
a8ccd54da5
ci: docs-preview-deploy.yml - Switch to official download-artifact action (#3838)
v4 of the official action now supports this use-case.
2024-01-28 01:50:01 +13:00
Andreas Perhab
9ac11021e1
setup-stack: fix error when RSPAMD_DMS_DKIM_D is not set (#3827)
* setup-stack: fix error when RSPAMD_DMS_DKIM_D is not set

prevent messages like this
  chown: cannot access '': No such file or directory
when RSPAMD_DMS_DKIM_D has no value

* Update target/scripts/startup/setup-stack.sh

---------

Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2024-01-26 14:40:29 +01:00
Georg Lauterbach
ba27edc801
Rspamd: only declare Rspamd variables when not already declared (#3837)
* only declare Rspamd vars when not already declared

* update CHANGELOG

* Update CHANGELOG.md

---------

Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-01-26 14:07:46 +01:00
Brennan Kinney
7d9eb1e4a7
docs: Add context to sender-cleanup in Postfix master.cf (#3834)
Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2024-01-26 11:32:49 +01:00
Brennan Kinney
487867285b
docs: UX Improvement - Better distinguish side nav page categories (#3835) 2024-01-26 11:32:18 +01:00
Brennan Kinney
47f8d50beb
fix: Ensure configs are sanitized for parsing (#3819)
* chore: Detect missing final newline in configs read

These lines will be not be processed by `read`, emit a warning to raise awareness.

* fix: Ensure parsed config has final newline appended (when possible)

This functionality was handled in `accounts.sh` via a similar sed command (that the linked references also offer).

`printf` is better for this, no shellcheck comment required either.

We additionally don't attempt to modify files that are read-only.

* fix: Ensure parsed configs have CRLF to LF corrected (where possible)

Likewise, this runtime fix was only covering two config files. It now applies to all callers of this method.

* fix: Sanitize `postfix-master.cf` via helper

This feature should have been using the helper to avoid user error from their config updates accidentally introducing subtle breakage implicitly (due to CRLF or missing final newline).

* tests: Add test cases for new helpers

* tests:  `rm` is redundant when using `BATS_TEST_TMPDIR`

This temporary directory is created and removed implicitly. Even after a test failure.

* chore: Remove old `postfix-virtual.cf` migration logic

This was introduced in 2018, there should be no one needing to rely on this anymore?

* tests: Remove comment on sed failure concern

* chore: Add entry to `CHANGELOG.md`

* Apply suggestions from code review

Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>

---------

Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2024-01-26 10:28:26 +13:00
Brennan Kinney
22c6daee32
chore: Revise improper restart message (#3826)
Improved guidance.
2024-01-25 12:21:24 +00:00
Brennan Kinney
303ca82fb9
docs(fix): New external link icon workaround for mkdocs-material 9.5.5 (#3823)
This is the easiest to maintain workaround now available. Upstream continues to reject the value such a feature for accessibility.
2024-01-26 01:02:19 +13:00
Georg Lauterbach
ed1e1ebbd3
tests: new sending and filtering functions (#3786)
* move log/filter functions into own file

* add ShellCheck global directives

* use new function for tracking logs

The new function, called `_send_email_with_mid`, aligns with suggestions
from @polarethene and is heavily simplified compared to its predecessor
`_send_email_and_get_id`. New helpers will be introduced to filter logs
according to the MID constructed in this function.

* new filters for searching logs with MID

* use new filters (and sending) functions

* add new helper for asserting non-existence of log message

* use new filters in tests

* Apply suggestions from code review

- `_mid` / `MID` => `_msgid` / `MSG_ID`
- Revised documentation / tooltip comments

* Apply suggestions from code review

* fix tests

* use more distinct names for MSG_ID headers

* update `_filter_service_log` to not use `-i -E`

Moreover, I added a function to print the whole mail log. Appropriate
comments were added to this function to indicate that one should only
use this function when necessary.

* adjust helpers to new helper filter

* follow-up of previous commit

* add CHANGELOG entry

* Apply suggestions from code review

* chore: Update OAuth2 to use new log helper

* Apply suggestions from code review

Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>

* added explicit `_regexp` filters for logs

* Apply suggestions from code review

---------

Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-01-25 11:06:05 +13:00
Georg Lauterbach
00018e7e2b
general: update base image to Debian 12 ("Bookworm") (#3403)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
Co-authored-by: Casper <casperklein@users.noreply.github.com>
2024-01-24 17:05:55 +01:00
164 changed files with 6694 additions and 4933 deletions

View file

@ -1,26 +0,0 @@
{
"files": [
"CONTRIBUTORS.md"
],
"imageSize": 100,
"commit": false,
"contributors": [
{
"login": "matrixes",
"name": "matrixes",
"avatar_url": "https://avatars.githubusercontent.com/u/46491408?v=4",
"profile": "https://github.com/matrixes",
"contributions": [
"blog"
]
}
],
"contributorsPerLine": 7,
"badgeTemplate": "",
"projectName": "docker-mailserver",
"projectOwner": "docker-mailserver",
"repoType": "github",
"repoHost": "https://github.com",
"skipCi": false,
"commitConvention": "none"
}

View file

@ -1,3 +1,2 @@
*
!target
!VERSION

1
.gitattributes vendored
View file

@ -28,7 +28,6 @@ Makefile
*.yaml text
## PROJECT
.all-contributorsrc text export-ignore
.editorconfig text export-ignore
.gitattributes text export-ignore
.gitignore text export-ignore

View file

@ -67,10 +67,3 @@ body:
- This field expects only plain text (_rendered as a fenced code block_).
- You can enable debug output by setting the environment variable `LOG_LEVEL` to `debug` or `trace`.
render: Text
- type: input
id: form-improvements
attributes:
label: Improvements to this form?
description: If you have criticism or general feedback about this issue form, feel free to tell us so we can enhance the experience for everyone.
validations:
required: false

View file

@ -4,6 +4,8 @@ title: 'feature request: '
labels:
- kind/new feature
- meta/needs triage
projects:
- DMS Core Backlog
body:
- type: markdown

View file

@ -4,8 +4,6 @@ updates:
directory: "/"
schedule:
interval: "weekly"
reviewers:
- "docker-mailserver/maintainers"
labels:
- "area/ci"
- "kind/update"
@ -15,8 +13,6 @@ updates:
directory: /
schedule:
interval: "weekly"
reviewers:
- "docker-mailserver/maintainers"
labels:
- "area/ci"
- "kind/update"

View file

@ -18,12 +18,12 @@ Fixes #
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
## Checklist:
## Checklist
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation (README.md or the documentation under `docs/`)
- [ ] If necessary I have added tests that prove my fix is effective or that my feature works
- [ ] If necessary, I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] **I have added information about changes made in this PR to `CHANGELOG.md`**

View file

@ -1,33 +0,0 @@
name: 'Update Contributors List'
on:
workflow_dispatch:
schedule:
- cron: 0 4 * * 0
permissions:
contents: write
pull-requests: write
statuses: write
jobs:
add-contributors:
name: 'Add Contributors'
runs-on: ubuntu-22.04
steps:
- name: 'Checkout'
uses: actions/checkout@v4
- name: 'Update CONTRIBUTORS.md'
uses: akhilmhdh/contributors-readme-action@v2.3.6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
readme_path: CONTRIBUTORS.md
collaborators: all
use_username: true
commit_message: 'docs: updated `CONTRIBUTORS.md`'
committer_username: github-actions[bot]
committer_email: 41898282+github-actions[bot]@users.noreply.github.com
pr_title_on_protected: 'docs: update `CONTRIBUTORS.md`'
auto_detect_branch_protection: true

View file

@ -1,120 +1,166 @@
name: 'Documentation (run)'
name: 'Documentation (Deploy)'
on:
# This workflow runs off the primary branch which provides access to the `secrets` context:
workflow_run:
workflows: ['Documentation (PR)']
types:
- completed
# Note: If limiting concurrency is required for this workflow:
# 1. Add an additional job prior to `preview` to get the PR number make it an output.
# 2. Assign that new job as a `needs` dependency for the `preview` job.
# It is still required for `preview` job to download the artifact so that it can access the preview build files.
permissions:
# Required by `actions/download-artifact`:
actions: read
# Required by `set-pr-context`:
contents: read
# Required by `marocchino/sticky-pull-request-comment` (write) + `set-pr-context` (read):
pull-requests: write
# Required by `myrotvorets/set-commit-status-action`:
statuses: write
# This workflow runs off the primary branch and has access to secrets as expected.
jobs:
preview:
name: 'Deploy Preview'
runs-on: ubuntu-22.04
if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }}
# NOTE: This is handled as pre-requisite job to minimize the noise from acquiring these two outputs needed for `deploy-preview` ENV:
pr-context:
name: 'Acquire PR Context'
runs-on: ubuntu-24.04
outputs:
PR_HEADSHA: ${{ steps.set-pr-context.outputs.head-sha }}
PR_NUMBER: ${{ steps.set-pr-context.outputs.number }}
if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' }}
steps:
- name: 'Get PR context'
id: set-pr-context
env:
# Token is required for the GH CLI:
GH_TOKEN: ${{ github.token }}
# Best practice for scripts is to reference via ENV at runtime. Avoid using GHA context expressions in the script content directly:
# https://github.com/docker-mailserver/docker-mailserver/pull/4247#discussion_r1827067475
PR_TARGET_REPO: ${{ github.repository }}
# If the PR is from a fork, prefix it with `<owner-login>:`, otherwise only the PR branch name is relevant:
PR_BRANCH: |-
${{
(github.event.workflow_run.head_repository.owner.login != github.event.workflow_run.repository.owner.login)
&& format('{0}:{1}', github.event.workflow_run.head_repository.owner.login, github.event.workflow_run.head_branch)
|| github.event.workflow_run.head_branch
}}
# Use the GH CLI to query the PR branch, which provides the PR number and head SHA to assign as outputs:
# (`--jq` formats JSON to `key=value` pairs and renames `headRefOid` to `head-sha`)
run: |
gh pr view --repo "${PR_TARGET_REPO}" "${PR_BRANCH}" \
--json 'number,headRefOid' \
--jq '"number=\(.number)\nhead-sha=\(.headRefOid)"' \
>> "${GITHUB_OUTPUT}"
# ======================== #
# Restore workflow context #
# ======================== #
# The official Github Action for downloading artifacts does not support multi-workflow
- name: 'Download build artifact'
uses: dawidd6/action-download-artifact@v3
deploy-preview:
name: 'Deploy Preview'
runs-on: ubuntu-24.04
needs: [pr-context]
env:
# NOTE: Keep this in sync with the equivalent ENV in `docs-preview-prepare.yml`:
BUILD_DIR: docs/site/
# PR head SHA (latest commit):
PR_HEADSHA: ${{ needs.pr-context.outputs.PR_HEADSHA }}
PR_NUMBER: ${{ needs.pr-context.outputs.PR_NUMBER }}
# Deploy URL preview prefix (the site name for this prefix is managed at Netlify):
PREVIEW_SITE_PREFIX: pullrequest-${{ needs.pr-context.outputs.PR_NUMBER }}
steps:
- name: 'Retrieve and extract the built docs preview'
uses: actions/download-artifact@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
run_id: ${{ github.event.workflow_run.id }}
workflow: docs-preview-prepare.yml
name: preview-build
- name: 'Extract build artifact'
run: tar -xf artifact.tar.zst
- name: 'Restore preserved ENV'
run: cat pr.env >> "${GITHUB_ENV}"
path: ${{ env.BUILD_DIR }}
# These are needed due this approach relying on `workflow_run`, so that it can access the build artifact:
# (uploaded from the associated `docs-preview-prepare.yml` workflow run)
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
# ==================== #
# Deploy preview build #
# ==================== #
# Manage workflow deployment status. `enable-commit-status` from `nwtgck/actions-netlify` would handle this,
# but presently does not work correctly via split workflow. It is useful in a split workflow as the 1st stage
# no longer indicates if the entire workflow/deployment was successful.
- name: 'Commit Status: Set Workflow Status as Pending'
uses: myrotvorets/set-commit-status-action@v2.0.0
# Manage workflow deployment status (Part 1/2):
# NOTE:
# - `workflow_run` trigger does not appear on the PR/commit checks status, only the initial prepare workflow triggered.
# This adds our own status check for this 2nd half of the workflow starting as `pending`, followed by `success` / `failure` at the end.
# - `enable-commit-status` from `nwtgck/actions-netlify` would have handled this,
# but the context `github.sha` that action tries to use references the primary branch commit that this workflow runs from, not the relevant PR commit.
- name: 'Commit Status (1/2) - Set Workflow Status as Pending'
uses: myrotvorets/set-commit-status-action@v2.0.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
status: pending
# Should match `env.PR_HEADSHA` when triggered by `pull_request` event workflow,
# Avoids failure of ENV being unavailable if job fails early:
sha: ${{ github.event.workflow_run.head_sha }}
sha: ${{ env.PR_HEADSHA }}
context: 'Deploy Preview (pull_request => workflow_run)'
- name: 'Send preview build to Netlify'
uses: nwtgck/actions-netlify@v2.1
id: preview
uses: nwtgck/actions-netlify@v3.0
id: preview-netlify
timeout-minutes: 1
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
# Fail the job early if credentials are missing / invalid:
# Fail the job when the required Netlify credentials are missing from ENV:
fails-without-credentials: true
# Sets/creates the Netlify deploy URL prefix.
# Uses the PR number for uniqueness:
alias: ${{ env.NETLIFY_SITE_PREFIX }}
# Set/create the Netlify deploy URL prefix:
alias: ${{ env.PREVIEW_SITE_PREFIX }}
# Only publish the contents of the build output:
publish-dir: ${{ env.BUILD_DIR }}
# Custom message for the deploy log on Netlify:
deploy-message: '${{ env.PR_TITLE }} (PR #${{ env.PR_NUMBER }} @ commit: ${{ env.PR_HEADSHA }})'
deploy-message: 'Preview Build (PR #${{ env.PR_NUMBER }} @ commit: ${{ env.PR_HEADSHA }}'
# Note: Split workflow incorrectly references latest primary branch commit for deployment.
# Assign to non-default Deployment Environment for better management:
github-deployment-environment: documentation-previews
github-deployment-description: 'Preview deploy for documentation PRs'
# Note - PR context used by this action is incorrect. These features are broken with split workflow:
# https://github.com/nwtgck/actions-netlify/issues/545
# Disable unwanted action defaults:
# Disable adding deploy comment on pre-merge commit (Github creates this for PR diff):
# This input does not fallback to the GITHUB_TOKEN taken from context, nor log that it will skip extra features of the action when this input is not set:
# https://github.com/nwtgck/actions-netlify/issues/1219
# github-token: ${{ secrets.GITHUB_TOKEN }}
# NOTE: These features won't work correctly when the triggered workflow is not run from the PR branch due to assumed `pull_request` context:
# https://github.com/nwtgck/actions-netlify/issues/545
# Disable adding a comment to the commit belonging to context `github.sha` about the successful deployment (redundant and often wrong commit):
enable-commit-comment: false
# Disable adding a "Netlify - Netlify deployment" check status:
# Disable adding a "Netlify - Netlify deployment" PR check status (workflow job status is sufficient):
enable-commit-status: false
# Disable. We provide a custom PR comment in the next action:
# Disable adding a comment about successful deployment status to the PR.
# Prefer `marocchino/sticky-pull-request-comment` instead (more flexible and allows custom message):
enable-pull-request-comment: false
# Opt-out of deployment feature:
# NOTE:
# - When affected by `nwtgck/actions-netlify/issues/545`, the deployments published reference the wrong commit and thus information.
# - While the feature creates or assigns a deployment to associate the build with, it is unrelated to the related environments feature (secrets/vars):
# https://github.com/nwtgck/actions-netlify/issues/538#issuecomment-833983970
# https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-deployments/viewing-deployment-history
# https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-deployments/managing-environments-for-deployment
enable-github-deployment: false
# Assign to non-default Deployment Environment for better management:
# github-deployment-environment: documentation-previews
# github-deployment-description: 'Preview deploy for documentation PRs'
# If a `netlify.toml` config is ever needed, enable this:
# netlify-config-path: ./docs/netlify.toml
# If ever switching from Github Pages, enable this conditionally (false by default):
# If ever switching from Github Pages, enable this only when not deploying a preview build (false by default):
# production-deploy: false
- name: 'Comment on PR: Add/Update deployment status'
- name: 'Comment on PR with preview link'
uses: marocchino/sticky-pull-request-comment@v2
with:
number: ${{ env.PR_NUMBER }}
header: preview-comment
recreate: true
message: |
[Documentation preview for this PR](${{ steps.preview.outputs.deploy-url }}) is ready! :tada:
[Documentation preview for this PR](${{ steps.preview-netlify.outputs.deploy-url }}) is ready! :tada:
Built with commit: ${{ env.PR_HEADSHA }}
- name: 'Commit Status: Update deployment status'
uses: myrotvorets/set-commit-status-action@v2.0.0
# Always run this step regardless of job failing early:
# Manage workflow deployment status (Part 2/2):
- name: 'Commit Status (2/2) - Update deployment status'
uses: myrotvorets/set-commit-status-action@v2.0.1
# Always run this step regardless of the job failing early:
if: ${{ always() }}
# Custom status descriptions:
env:
DEPLOY_SUCCESS: Successfully deployed preview.
DEPLOY_FAILURE: Failed to deploy preview.
with:
token: ${{ secrets.GITHUB_TOKEN }}
status: ${{ job.status == 'success' && 'success' || 'failure' }}
sha: ${{ github.event.workflow_run.head_sha }}
sha: ${{ env.PR_HEADSHA }}
context: 'Deploy Preview (pull_request => workflow_run)'
description: ${{ job.status == 'success' && env.DEPLOY_SUCCESS || env.DEPLOY_FAILURE }}

View file

@ -7,74 +7,68 @@ on:
- '.github/workflows/scripts/docs/build-docs.sh'
- '.github/workflows/docs-preview-prepare.yml'
# If the workflow for a PR is triggered multiple times, previous existing runs will be canceled.
# eg: Applying multiple suggestions from a review directly via the Github UI.
# Instances of the 2nd phase of this workflow (via `workflow_run`) presently lack concurrency limits due to added complexity.
# If this workflow is triggered while already running for the PR, cancel any earlier running instances:
# Instances of the 2nd phase of this workflow (via `workflow_run`) lack any concurrency limits due to added complexity.
concurrency:
group: deploypreview-pullrequest-${{ github.event.pull_request.number }}
cancel-in-progress: true
env:
# Build output directory (created by the mkdocs-material container, keep this in sync with `build-docs.sh`):
BUILD_DIR: docs/site/
# These two are only needed to construct `PREVIEW_URL`:
PREVIEW_SITE_NAME: dms-doc-previews
PREVIEW_SITE_PREFIX: pullrequest-${{ github.event.pull_request.number }}
# `pull_request` workflow is unreliable alone: Non-collaborator contributions lack access to secrets for security reasons.
# A separate workflow (docs-preview-deploy.yml) handles the deploy after the potentially untrusted code is first run in this workflow.
# See: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
permissions:
# Required by `actions/checkout` for git checkout:
contents: read
jobs:
prepare-preview:
name: 'Build Preview'
runs-on: ubuntu-22.04
env:
BUILD_DIR: docs/site
NETLIFY_SITE_PREFIX: pullrequest-${{ github.event.pull_request.number }}
NETLIFY_SITE_NAME: dms-doc-previews
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: 'Build with mkdocs-material via Docker'
working-directory: docs
env:
PREVIEW_URL: 'https://${NETLIFY_SITE_PREFIX}--${NETLIFY_SITE_NAME}.netlify.app/'
NETLIFY_BRANDING: '<a href="https://www.netlify.com/"><img alt="Deploys by Netlify" src="https://www.netlify.com/img/global/badges/netlify-color-accent.svg" style="float: right;"></a>'
run: |
# Adjust mkdocs.yml for preview build
sed -i "s|^site_url:.*|site_url: '${PREVIEW_URL}'|" mkdocs.yml
# ================== #
# Build docs preview #
# ================== #
# Insert sponsor branding into page content (Provider OSS plan requirement):
# Upstream does not provide a nicer maintainable way to do this..
# Prepends HTML to copyright text and then aligns to the right side.
- name: 'Build with mkdocs-material via Docker'
working-directory: docs/
env:
PREVIEW_URL: 'https://${{ env.PREVIEW_SITE_PREFIX }}--${{ env.PREVIEW_SITE_NAME }}.netlify.app/'
run: |
# Adjust `mkdocs.yml` for the preview build requirements:
# - Replace production `site_url` with the preview URL (only affects the canonical link: https://en.wikipedia.org/wiki/Canonical_link_element#HTML)
# - Prepend Netlify logo link to `copyright` content
sed -i "s|^site_url:.*|site_url: '${{ env.PREVIEW_URL }}'|" mkdocs.yml
# Insert branding into page content (Netlify OSS plan requirement):
# - `mkdocs-material` does not provide a better way to do this.
# - Prepends HTML to the copyright text and then aligns the logo to the right-side of the page.
NETLIFY_BRANDING='<a href="https://www.netlify.com/"><img alt="Deploys by Netlify" src="https://www.netlify.com/img/global/badges/netlify-color-accent.svg" style="float: right;"></a>'
sed -i "s|^copyright: '|copyright: '${NETLIFY_BRANDING}|" mkdocs.yml
# Need to override a CSS media query for parent element to always be full width:
# Override a CSS media query for the parent element to always be full width:
echo '.md-footer-copyright { width: 100%; }' >> content/assets/css/customizations.css
../.github/workflows/scripts/docs/build-docs.sh
# Build and prepare for upload:
echo "::group::Build (stdout)"
bash ../.github/workflows/scripts/docs/build-docs.sh
echo "::endgroup::"
# ============================== #
# Volley over to secure workflow #
# ============================== #
# Minimize risk of upload failure by bundling files to a single compressed archive (tar + zstd).
# Bundles build dir and env file into a compressed archive, nested file paths will be preserved.
- name: 'Prepare artifact for transfer'
env:
# As a precaution, reference this value by an interpolated ENV var;
# instead of interpolating user controllable input directly in the shell script..
# https://github.com/docker-mailserver/docker-mailserver/issues/2332#issuecomment-998326798
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
# Save ENV for transfer
{
echo "PR_HEADSHA=${{ github.event.pull_request.head.sha }}"
echo "PR_NUMBER=${{ github.event.pull_request.number }}"
echo "PR_TITLE=${PR_TITLE}"
echo "NETLIFY_SITE_PREFIX=${{ env.NETLIFY_SITE_PREFIX }}"
echo "BUILD_DIR=${{ env.BUILD_DIR }}"
} >> pr.env
tar --zstd -cf artifact.tar.zst pr.env ${{ env.BUILD_DIR }}
# Archives directory `path` into a ZIP file:
- name: 'Upload artifact for workflow transfer'
uses: actions/upload-artifact@v4
with:
name: preview-build
path: artifact.tar.zst
path: ${{ env.BUILD_DIR }}
retention-days: 1

View file

@ -59,7 +59,7 @@ jobs:
{} +
- name: 'Deploy to Github Pages'
uses: peaceiris/actions-gh-pages@v3.9.3
uses: peaceiris/actions-gh-pages@v4.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
# Build directory contents to publish to the `gh-pages` branch:

View file

@ -32,7 +32,7 @@ jobs:
with:
submodules: recursive
# Can potentially be replaced by: `${{ hashFiles('target/**', 'Dockerfile', 'VERSION') }}`
# Can potentially be replaced by: `${{ hashFiles('target/**', 'Dockerfile') }}`
# Must not be affected by file metadata changes and have a consistent sort order:
# https://docs.github.com/en/actions/learn-github-actions/expressions#hashfiles
# Keying by the relevant build context is more re-usable than a commit SHA.
@ -40,10 +40,7 @@ jobs:
id: derive-image-cache-key
shell: bash
run: |
ADDITIONAL_FILES=(
'Dockerfile'
'VERSION'
)
ADDITIONAL_FILES=( 'Dockerfile' )
# Recursively collect file paths from `target/` and pipe a list of
# checksums to be sorted (by hash value) and finally generate a checksum
@ -74,16 +71,16 @@ jobs:
cache-buildx-
- name: 'Set up QEMU'
uses: docker/setup-qemu-action@v3.0.0
uses: docker/setup-qemu-action@v3.6.0
with:
platforms: arm64
- name: 'Set up Docker Buildx'
uses: docker/setup-buildx-action@v3.0.0
uses: docker/setup-buildx-action@v3.11.1
# NOTE: AMD64 can build within 2 minutes
- name: 'Build images'
uses: docker/build-push-action@v5.1.0
uses: docker/build-push-action@v6.18.0
with:
context: .
# Build at least the AMD64 image (which runs against the test suite).

View file

@ -23,7 +23,7 @@ jobs:
- name: 'Prepare tags'
id: prep
uses: docker/metadata-action@v5.5.0
uses: docker/metadata-action@v5.8.0
with:
images: |
${{ secrets.DOCKER_REPOSITORY }}
@ -35,12 +35,12 @@ jobs:
type=semver,pattern={{major}}.{{minor}}.{{patch}}
- name: 'Set up QEMU'
uses: docker/setup-qemu-action@v3.0.0
uses: docker/setup-qemu-action@v3.6.0
with:
platforms: arm64
- name: 'Set up Docker Buildx'
uses: docker/setup-buildx-action@v3.0.0
uses: docker/setup-buildx-action@v3.11.1
# Try get the cached build layers from a prior `generic_build.yml` job.
# NOTE: Until adopting `type=gha` scoped cache exporter (in `docker/build-push-action`),
@ -67,7 +67,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: 'Build and publish images'
uses: docker/build-push-action@v5.1.0
uses: docker/build-push-action@v6.18.0
with:
context: .
build-args: |

View file

@ -38,12 +38,12 @@ jobs:
# Ensures consistent BuildKit version (not coupled to Docker Engine),
# and increased compatibility of the build cache vs mixing buildx drivers.
- name: 'Set up Docker Buildx'
uses: docker/setup-buildx-action@v3.0.0
uses: docker/setup-buildx-action@v3.11.1
# Importing from the cache should create the image within approx 30 seconds:
# NOTE: `qemu` step is not needed as we only test for AMD64.
- name: 'Build AMD64 image from cache'
uses: docker/build-push-action@v5.1.0
uses: docker/build-push-action@v6.18.0
with:
context: .
tags: mailserver-testing:ci

View file

@ -37,12 +37,12 @@ jobs:
# Ensures consistent BuildKit version (not coupled to Docker Engine),
# and increased compatibility of the build cache vs mixing buildx drivers.
- name: 'Set up Docker Buildx'
uses: docker/setup-buildx-action@v3.0.0
uses: docker/setup-buildx-action@v3.11.1
# Importing from the cache should create the image within approx 30 seconds:
# NOTE: `qemu` step is not needed as we only test for AMD64.
- name: 'Build AMD64 image from cache'
uses: docker/build-push-action@v5.1.0
uses: docker/build-push-action@v6.18.0
with:
context: .
tags: mailserver-testing:ci
@ -55,7 +55,7 @@ jobs:
provenance: false
- name: 'Run the Anchore Grype scan action'
uses: anchore/scan-action@v3.6.0
uses: anchore/scan-action@v6.5.0
id: scan
with:
image: mailserver-testing:ci

View file

@ -7,10 +7,11 @@ set -ex
# `build --strict` ensures the build fails when any warnings are omitted.
docker run \
--rm \
--quiet \
--user "$(id -u):$(id -g)" \
--volume "${PWD}:/docs" \
--volume "./:/docs" \
--name "build-docs" \
squidfunk/mkdocs-material:9.5 build --strict
squidfunk/mkdocs-material:9.6 build --strict
# Remove unnecessary build artifacts: https://github.com/squidfunk/mkdocs-material/issues/2519
# site/ is the build output folder.

View file

@ -2,10 +2,255 @@
All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased](https://github.com/docker-mailserver/docker-mailserver/compare/v13.3.1...HEAD)
## [Unreleased](https://github.com/docker-mailserver/docker-mailserver/compare/v15.0.2...HEAD)
> **Note**: Changes and additions listed here are contained in the `:edge` image tag. These changes may not be as stable as released changes.
### Added
- **Environment Variables:**
- [ENV can be declared with a `__FILE` suffix](https://docker-mailserver.github.io/docker-mailserver/v15.1/config/environment/) to read a value from a file during initial DMS setup scripts ([#4359](https://github.com/docker-mailserver/docker-mailserver/pull/4359))
- Improved docs for the ENV `OVERRIDE_HOSTNAME` ([#4492](https://github.com/docker-mailserver/docker-mailserver/pull/4492))
- **Internal:**
- [`DMS_CONFIG_POLL`](https://docker-mailserver.github.io/docker-mailserver/v15.0/config/environment/#dms_config_poll) supports adjusting the polling rate (seconds) for the change detection service `check-for-changes.sh` ([#4450](https://github.com/docker-mailserver/docker-mailserver/pull/4450))
### Fixes
- **DKIM**
- `setup config dkim domain subdomain.example.com` no longer throws an error if the owner of config/opendkim/keys does not exist in the container ([#4517](https://github.com/docker-mailserver/docker-mailserver/pull/4517))
- **Fail2Ban**
- Configure logrotate only when Fail2Ban is enabled ([#4493](https://github.com/docker-mailserver/docker-mailserver/pull/4523))
- **Internal:**
- The DMS _Config Volume_ (`/tmp/docker-mailserver`) will now ensure it's file tree is accessible for services when the volume was created with missing executable bit ([#4487](https://github.com/docker-mailserver/docker-mailserver/pull/4487))
- Removed the build-time hostname workaround for Postfix as Debian has since patched their post-install script ([#4493](https://github.com/docker-mailserver/docker-mailserver/pull/4493))
### Updates
- **Documentation:**
- Added a compatibility note for a Dovecot + Solr 9.8 breaking change ([#4433](https://github.com/docker-mailserver/docker-mailserver/pull/4433))
- Updated the Podman documentation with deprecation warnings and up-to-date technologies such as rootless Quadlets and Pasta networking ([#4183](https://github.com/docker-mailserver/docker-mailserver/pull/4183))
- `setup config dkim` (rspamd) - Corrected the expected path for the generated `dkim_signing.conf` file is now found in the DMS config volume ([#4521](https://github.com/docker-mailserver/docker-mailserver/issues/4521))
- **Internal:**
- Refactored `setup config dkim` (`open-dkim`) ([#4375](https://github.com/docker-mailserver/docker-mailserver/pull/4375))
- `setup email list` and the default `ENABLE_QUOTAS=1` ENV now better communicates when config is incompatible ([#4453](https://github.com/docker-mailserver/docker-mailserver/pull/4453))
### Removed
- **Fail2Ban**
- Removed `postfix-sasl` jail by default as it is covered by `postfix[mode=extra]` already ([#4535](https://github.com/docker-mailserver/docker-mailserver/pull/4535))
## [v15.0.2](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v15.0.2)
### Fixes
- **Postfix**
- Avoid modifying the message body when filtering sender headers. This regression was introduced from [#4120](https://github.com/docker-mailserver/docker-mailserver/pull/4120) as part of DMS v15.0.0 ([#4429](https://github.com/docker-mailserver/docker-mailserver/pull/4429))
## [v15.0.1](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v15.0.1)
### Added
- **Internal:**
- Added the Smallstep `step` CLI command for future internal usage ([#4376](https://github.com/docker-mailserver/docker-mailserver/pull/4376))
### Fixes
- **Postfix:**
- `setup email restrict` generated configs now only prepend to `dms_smtpd_sender_restrictions` ([#4379](https://github.com/docker-mailserver/docker-mailserver/pull/4379))
- **Rspamd:**
- Change detection support now monitors all files found within the DMS _Config Volume_ Rspamd directory ([#4418](https://github.com/docker-mailserver/docker-mailserver/pull/4418))
- **Internal:**
- A permissions fix for `/var/log/mail` that was [added in DMS v15]((https://github.com/docker-mailserver/docker-mailserver/pull/4374)) no longer encounters an error when no log files are present during a container restart, such as with a `tmpfs` volume mount ([#4391](https://github.com/docker-mailserver/docker-mailserver/pull/4391))
- The DMS _State Volume_ (`/var/mail-state`) will now ensure it's file tree is accessible for services when the volume was created with missing executable bit ([#4420](https://github.com/docker-mailserver/docker-mailserver/pull/4420))
- The DMS _Config Volume_ (`/tmp/docker-mailserver`) now correctly updates permissions on container restarts ([#4417](https://github.com/docker-mailserver/docker-mailserver/pull/4417))
### Updates
- **Internal:**
- Minor improvements to `_install_utils()` in `packages.sh` ([#4376](https://github.com/docker-mailserver/docker-mailserver/pull/4376))
## [v15.0.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v15.0.0)
### Breaking
- **saslauthd** mechanism support via ENV `SASLAUTHD_MECHANISMS` with `pam`, `shadow`, `mysql` values has been removed. Only `ldap` and `rimap` remain supported ([#4259](https://github.com/docker-mailserver/docker-mailserver/pull/4259))
- **getmail6** has been refactored: ([#4156](https://github.com/docker-mailserver/docker-mailserver/pull/4156))
- The [DMS config volume](https://docker-mailserver.github.io/docker-mailserver/v15.0/config/advanced/optional-config/#volumes) now has support for `getmailrc_general.cf` for overriding [common default settings](https://docker-mailserver.github.io/docker-mailserver/v15.0/config/advanced/mail-getmail/#common-options). If you previously mounted this config file directly to `/etc/getmailrc_general` you should switch to our config volume support.
- Generated getmail configuration files no longer set the `message_log` option. Instead of individual log files per config, the [default base settings DMS configures](https://github.com/docker-mailserver/docker-mailserver/tree/v15.0.0/target/getmail/getmailrc_general) now enables `message_log_syslog`. This aligns with how other services in DMS log to syslog where it is captured in `mail.log`.
- Getmail configurations have changed location from the base of the DMS Config Volume, to the `getmail/` subdirectory. Any existing configurations **must be migrated manually.**
- **DMS v14 mistakenly** relocated the _getmail state directory_ to the _DMS Config Volume_ as a `getmail/` subdirectory.
- This has been corrected to `/var/lib/getmail` (_if you have mounted a DMS State Volume to `/var/mail-state`, `/var/lib/getmail` will be symlinked to `/var/mail-state/lib-getmail`_).
- To preserve this state when upgrading to DMS v15, **you must manually migrate `getmail/` from the _DMS Config Volume_ to `lib-getmail/` in the _DMS State Volume_.**
- `setup email delete <EMAIL ADDRESS>` now requires explicit confirmation if the mailbox data should be deleted ([#4365](https://github.com/docker-mailserver/docker-mailserver/pull/4365)).
- **Rspamd:** Removed deprecated file path check (_DMS config volume: `./rspamd-modules.conf` => `./rspamd/custom-commands.conf`_) ([#4373](https://github.com/docker-mailserver/docker-mailserver/pull/4373))
### Added
- **Internal:**
- Add password confirmation to several `setup` CLI subcommands ([#4072](https://github.com/docker-mailserver/docker-mailserver/pull/4072))
- Added a `debug getmail` subcommand to `setup` ([#4346](https://github.com/docker-mailserver/docker-mailserver/pull/4346))
### Updates
- **Internal:**
- **Removed `VERSION` file** from the repo. Releases of DMS prior to v13 (Nov 2023) would check this to detect new releases ([#3677](https://github.com/docker-mailserver/docker-mailserver/issues/3677), [#4321](https://github.com/docker-mailserver/docker-mailserver/pull/4321))
- During image build, ensure a secure connection when downloading the `fail2ban` package ([#4080](https://github.com/docker-mailserver/docker-mailserver/pull/4080))
- **Documentation:**
- Account Management and Authentication pages have been rewritten and better organized ([#4122](https://github.com/docker-mailserver/docker-mailserver/pull/4122))
- Add a caveat for `DMS_VMAIL_UID` not being compatible with `0` / root ([#4143](https://github.com/docker-mailserver/docker-mailserver/pull/4143))
- **Getmail:** ([#4156](https://github.com/docker-mailserver/docker-mailserver/pull/4156))
- Added `getmail` as a new service for `supervisor` to manage, replacing cron for periodic polling.
- IMAP/POP3 example configs added to our [`config-examples`](https://github.com/docker-mailserver/docker-mailserver/tree/v15.0.0/config-examples/getmail).
- ENV [`GETMAIL_POLL`](https://docker-mailserver.github.io/docker-mailserver/v15.0/config/environment/#getmail_poll) now supports values above 30 minutes.
- **Postfix:**
- By default opt-out from _Microsoft reactions_ for outbound mail ([#4120](https://github.com/docker-mailserver/docker-mailserver/pull/4120))
- **Rspamd:**
- Updated GTube settings and tests ([#4191](https://github.com/docker-mailserver/docker-mailserver/pull/4191))
- Updated externally installed software ([#4357](https://github.com/docker-mailserver/docker-mailserver/pull/4357)):
- `DOVECOT_COMMUNITY_REPO=1` custom image build ARG now supports the latest Dovecot [`2.4.x`](https://github.com/dovecot/core/releases/tag/2.4.0) (_DMS provides Dovecot `2.3.19` by default_)
- Dovecot FTS Xapian module (`1.7.12` => [`1.9.0`](https://github.com/grosjo/fts-xapian/releases/tag/1.9))
- `jaq` (`1.3.0` => [`2.1.0`](https://github.com/01mf02/jaq/releases/tag/v2.1.0))
- Fail2Ban (`1.0.2-2` => [`1.1.0`](https://github.com/fail2ban/fail2ban/releases/tag/1.1.0)) ([#4045](https://github.com/docker-mailserver/docker-mailserver/pull/4045))
- Rspamd (`3.8.4` => [`3.11.0`](https://github.com/rspamd/rspamd/releases/tag/3.11.0)) - Implicitly upgraded during image build, as the third-party repo lacks version pinning support.
### Fixes
- **Dovecot:**
- The logwatch `ignore.conf` now also excludes Xapian messages about pending documents ([#4060](https://github.com/docker-mailserver/docker-mailserver/pull/4060))
- `dovecot-fts-xapian` plugin was updated, fixing a regression with indexing ([#4095](https://github.com/docker-mailserver/docker-mailserver/pull/4095))
- The "dummy account" workaround for _Dovecot Quota_ feature support no longer treats the alias as a regex when checking the Dovecot UserDB ([#4222](https://github.com/docker-mailserver/docker-mailserver/pull/4222))
- **LDAP:**
- Correctly apply a compatibility fix for OAuth2 introduced in DMS `v13.3.1` which had not been applied to the actual LDAP config changes ([#4175](https://github.com/docker-mailserver/docker-mailserver/pull/4175))
- **Internal:**
- The main `mail.log` (_which is piped to stdout via `tail`_) now correctly begins from the first log line of the active container run. Previously some daemon logs and potential warnings/errors were omitted ([#4146](https://github.com/docker-mailserver/docker-mailserver/pull/4146))
- `start-mailserver.sh` removed unused `shopt -s inherit_errexit` ([#4161](https://github.com/docker-mailserver/docker-mailserver/pull/4161))
- Fixed a regression introduced in DMS v14 where `postfix-main.cf` appended `stderr` output into `/etc/postfix/main.cf`, causing Postfix startup to fail ([#4147](https://github.com/docker-mailserver/docker-mailserver/pull/4147))
- Fixed a regression introduced in DMS v14 to better support running `start-mailserver.sh` with container restarts, which now only skip calling `_setup()` ([#4323](https://github.com/docker-mailserver/docker-mailserver/pull/4323#issuecomment-2629559254), [#4374](https://github.com/docker-mailserver/docker-mailserver/pull/4374))
- The command `swaks --help` is now functional ([#4282](https://github.com/docker-mailserver/docker-mailserver/pull/4282))
- **Rspamd:**
- DKIM private key path checking is now performed only on paths that do not contain `$` ([#4201](https://github.com/docker-mailserver/docker-mailserver/pull/4201))
### CI
- Removed `CONTRIBUTORS.md`, `.all-contributorsrc`, and workflow ([#4141](https://github.com/docker-mailserver/docker-mailserver/pull/4141))
- Refactored the workflows to be more secure for generating documentation previews on PRs ([#4267](https://github.com/docker-mailserver/docker-mailserver/pull/4267), [#4264](https://github.com/docker-mailserver/docker-mailserver/pull/4264), [#4262](https://github.com/docker-mailserver/docker-mailserver/pull/4262), [#4247](https://github.com/docker-mailserver/docker-mailserver/pull/4247), [#4244](https://github.com/docker-mailserver/docker-mailserver/pull/4244))
## [v14.0.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v14.0.0)
The most noteworthy change of this release is the update of the container's base image from Debian 11 ("Bullseye") to Debian 12 ("Bookworm"). This update alone involves breaking changes and requires a careful update!
### Breaking
- **Updated base image to Debian 12** ([#3403](https://github.com/docker-mailserver/docker-mailserver/pull/3403))
- Changed the default of `DOVECOT_COMMUNITY_REPO` to `0` (disabled) - the Dovecot community repo will (for now) not be the default when building the DMS.
- While Debian 12 (Bookworm) was released in June 2023 and the latest Dovecot `2.3.21` in Sep 2023, as of Jan 2024 there is no [Dovecot community repo available for Debian 12](https://repo.dovecot.org).
- This results in the Dovecot version being downgraded from `2.3.21` (DMS v13.3) to `2.3.19`, which [may affect functionality when you've explicitly configured for these features](https://github.com/dovecot/core/blob/30cde20f63650d8dcc4c7ad45418986f03159946/NEWS#L1-L158):
- OAuth2 (_mostly regarding JWT usage, or POST requests (`introspection_mode = post`) with `client_id` + `client_secret`_).
- Lua HTTP client (_DNS related_).
- Updated packages. For an overview, [we have a review comment on the PR that introduces Debian 12](https://github.com/docker-mailserver/docker-mailserver/pull/3403#issuecomment-1694563615)
- Notable major version bump: `openssl 3`, `clamav 1`, `spamassassin 4`, `redis-server 7`.
- Notable minor version bump: `postfix 3.5.23 => 3.7.9`
- Notable minor version bump + downgrade: `dovecot 2.3.13 => 2.3.19` (_Previous release provided `2.3.21` via community repo, `2.3.19` is now the default_)
- Updates to `packages.sh`:
- Removed custom installations of Fail2Ban, getmail6 and Rspamd
- Updated packages lists and added comments for maintainability
- OpenDMARC upgrade: `v1.4.0` => `v1.4.2` ([#3841](https://github.com/docker-mailserver/docker-mailserver/pull/3841))
- Previous versions of OpenDMARC would place incoming mail from domains announcing `p=quarantaine` (_that fail the DMARC check_) into the [Postfix "hold" queue](https://www.postfix.org/QSHAPE_README.html#hold_queue) until administrative intervention.
- [OpenDMARC v1.4.2 has disabled that feature by default](https://github.com/trusteddomainproject/OpenDMARC/issues/105), but it can be enabled again by adding the setting `HoldQuarantinedMessages true` to [`/etc/opendmarc.conf`](https://github.com/docker-mailserver/docker-mailserver/blob/v13.3.1/target/opendmarc/opendmarc.conf) (_provided from DMS_).
- [Our `user-patches.sh` feature](https://docker-mailserver.github.io/docker-mailserver/latest/config/advanced/override-defaults/user-patches/) provides a convenient approach to updating that config file.
- Please let us know if you disagree with the upstream default being carried with DMS, or the value of providing alternative configuration support within DMS.
- **Postfix:**
- Postfix upgrade from 3.5 to 3.7 ([#3403](https://github.com/docker-mailserver/docker-mailserver/pull/3403))
- `compatibility_level` was raised from `2` to `3.6`
- Postfix has deprecated the usage of `whitelist` / `blacklist` in config parameters and logging in favor of `allowlist` / `denylist` and similar variations. ([#3403](https://github.com/docker-mailserver/docker-mailserver/pull/3403/files#r1306356328))
- This [may affect monitoring / analysis of logs output from Postfix](https://www.postfix.org/COMPATIBILITY_README.html#respectful_logging) that expects to match patterns on the prior terminology used.
- DMS `main.cf` has renamed `postscreen_dnsbl_whitelist_threshold` to `postscreen_dnsbl_allowlist_threshold` as part of this change.
- `smtpd_relay_restrictions` (relay policy) is now evaluated after `smtpd_recipient_restrictions` (spam policy). Previously it was evaluated before `smtpd_recipient_restrictions`. Mail to be relayed via DMS must now pass through the spam policy first.
- The TLS fingerprint policy has changed the default from MD5 to SHA256 (_DMS does not modify this Postfix parameter, but may affect any user customizations that do_).
- **Dovecot**
- The "Junk" mailbox (folder) is now referenced by it's [special-use flag `\Junk`](https://docker-mailserver.github.io/docker-mailserver/v13.3/examples/use-cases/imap-folders/) instead of an explicit mailbox. ([#3925](https://github.com/docker-mailserver/docker-mailserver/pull/3925))
- This provides compatibility for the Junk mailbox when it's folder name differs (_eg: Renamed to "Spam"_).
- Potential breakage if your deployment modifies our `spam_to_junk.sieve` sieve script (_which is created during container startup when ENV `MOVE_SPAM_TO_JUNK=1`_) that handles storing spam mail into a users "Junk" mailbox folder.
- **Removed support for Solr integration:** ([#4025](https://github.com/docker-mailserver/docker-mailserver/pull/4025))
- This was a community contributed feature for FTS (Full Text Search), the docs advise using an image that has not been maintained for over 2 years and lacks ARM64 support. Based on user engagement over the years this feature has very niche value to continue to support, thus is being removed.
- If you use Solr, support can be restored if you're willing to contribute docs for the feature that resolves the concerns raised
- **Log:**
- The format of DMS specific logs (_from our scripts, not running services_) has been changed. The new format is `<RFC 3339 TIMESTAMP> <LOG LEVEL> <LOG EVENT SRC>: <MESSAGE>` ([#4035](https://github.com/docker-mailserver/docker-mailserver/pull/4035))
- **rsyslog:**
- Debian 12 adjusted the `rsyslog` configuration for the default file template from `RSYSLOG_TraditionalFileFormat` to `RSYSLOG_FileFormat` (_upstream default since 2012_). This change may affect you if you have any monitoring / analysis of log output (_eg: `mail.log` / `docker logs`_).
- The two formats are roughly equivalent to [RFC 3164](https://www.rfc-editor.org/rfc/rfc3164)) and [RFC 5424](https://datatracker.ietf.org/doc/html/rfc5424#section-1) respectively.
- A notable difference is the change to [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html#appendix-A) timestamps (_a strict subset of ISO 8601_). The [previous non-standardized timestamp format was defined in RFC 3164](https://www.rfc-editor.org/rfc/rfc3164.html#section-4.1.2) as `Mmm dd hh:mm:ss`.
- To revert this change you can add `sedfile -i '1i module(load="builtin:omfile" template="RSYSLOG_TraditionalFileFormat")' /etc/rsyslog.conf` via [our `user-patches.sh` feature](https://docker-mailserver.github.io/docker-mailserver/v14.0/config/advanced/override-defaults/user-patches/).
- Rsyslog now creates fewer log files:
- The files `/var/log/mail/mail.{info,warn,err}` are no longer created. These files represented `/var/log/mail.log` filtered into separate priority levels. As `/var/log/mail.log` contains all mail related messages, these files (_and their rotated counterparts_) can be deleted safely.
- `/var/log/messages`, `/var/log/debug` and several other log files not relevant to DMS were configured by default by Debian previously. These are not part of the `/var/log/mail/` volume mount, so should not impact anyone.
- **Features:**
- The relay host feature was refactored ([#3845](https://github.com/docker-mailserver/docker-mailserver/pull/3845))
- The only breaking change this should introduce is with the Change Detection service (`check-for-changes.sh`).
- When credentials are configured for relays, change events that trigger the relayhost logic now reapply the relevant Postfix settings:
- `smtp_sasl_auth_enable = yes` (_SASL auth to outbound MTA connections is enabled_)
- `smtp_sasl_security_options = noanonymous` (_credentials are mandatory for outbound mail delivery_)
- `smtp_tls_security_level = encrypt` (_the outbound MTA connection must always be secure due to credentials sent_)
- **Environment Variables:**
- `SA_SPAM_SUBJECT` has been renamed into `SPAM_SUBJECT` to become anti-spam service agnostic. ([#3820](https://github.com/docker-mailserver/docker-mailserver/pull/3820))
- As this functionality is now handled in Dovecot via a Sieve script instead of the respective anti-spam service during Postfix processing, this feature will only apply to mail stored in Dovecot. If you have relied on this feature in a different context, it will no longer be available.
- Rspamd previously handled this functionality via the `rewrite_subject` action which as now been disabled by default in favor of the new approach with `SPAM_SUBJECT`.
- `SA_SPAM_SUBJECT` is now deprecated and will log a warning if used. The value is copied as a fallback to `SPAM_SUBJECT`.
- The default has changed to not prepend any prefix to the subject unless configured to do so. If you relied on the implicit prefix, you will now need to provide one explicitly.
- `undef` was previously supported as an opt-out with `SA_SPAM_SUBJECT`. This is no longer valid, the equivalent opt-out value is now an empty value (_or rather the omission of this ENV being configured_).
- The feature to include [`_SCORE_` tag](https://spamassassin.apache.org/full/4.0.x/doc/Mail_SpamAssassin_Conf.html#rewrite_header-subject-from-to-STRING) in your value to be replaced by the associated spam score is no longer available.
- **Supervisord:**
- `supervisor-app.conf` renamed to `dms-services.conf` ([#3908](https://github.com/docker-mailserver/docker-mailserver/pull/3908))
- **Rspamd:**
- The Redis history key has been changed in order to not incorporate the hostname of the container (which is desirable in Kubernetes environments) ([#3927](https://github.com/docker-mailserver/docker-mailserver/pull/3927))
- **Account Management:**
- Addresses (accounts) are now normalized to lowercase automatically and a warning is logged in case uppercase letters are supplied ([#4033](https://github.com/docker-mailserver/docker-mailserver/pull/4033))
### Added
- **Documentation:**
- A guide for configuring a public server to relay inbound and outbound mail from DMS on a private server ([#3973](https://github.com/docker-mailserver/docker-mailserver/pull/3973))
- **Environment Variables:**
- `LOGROTATE_COUNT` defines the number of files kept by logrotate ([#3907](https://github.com/docker-mailserver/docker-mailserver/pull/3907))
- The fail2ban log file is now also taken into account by `LOGROTATE_COUNT` and `LOGROTATE_INTERVAL` ([#3915](https://github.com/docker-mailserver/docker-mailserver/pull/3915), [#3919](https://github.com/docker-mailserver/docker-mailserver/pull/3919))
- **Internal:**
- Regular container restarts are now better supported. Setup scripts that ran previously will now be skipped ([#3929](https://github.com/docker-mailserver/docker-mailserver/pull/3929))
### Updates
- **Environment Variables:**
- `ONE_DIR` has been removed (legacy ENV) ([#3840](https://github.com/docker-mailserver/docker-mailserver/pull/3840))
- It's only functionality remaining was to opt-out of run-time state consolidation with `ONE_DIR=0` (_when a volume was already mounted to `/var/mail-state`_).
- **Internal:**
- Changed the Postgrey whitelist retrieved during build to [source directly from Github](https://github.com/schweikert/postgrey/blob/master/postgrey_whitelist_clients) as the list is updated more frequently than the [author publishes at their website](https://postgrey.schweikert.ch) ([#3879](https://github.com/docker-mailserver/docker-mailserver/pull/3879))
- Enable spamassassin only, when amavis is enabled too. ([#3943](https://github.com/docker-mailserver/docker-mailserver/pull/3943))
- **Tests:**
- Refactored helper methods for sending e-mails with specific `Message-ID` headers and the helpers for retrieving + filtering logs, which together help isolate logs relevant to specific mail when multiple mails have been processed within a single test. ([#3786](https://github.com/docker-mailserver/docker-mailserver/pull/3786))
- **Rspamd:**
- The `rewrite_subject` action, is now disabled by default. It has been replaced with the new `SPAM_SUBJECT` environment variable, which implements the functionality via a Sieve script instead which is anti-spam service agnostic ([#3820](https://github.com/docker-mailserver/docker-mailserver/pull/3820))
- `RSPAMD_NEURAL` was added and is disabled by default. If switched on it will enable the experimental Rspamd "Neural network" module to add a layer of analysis to spam detection ([#3833](https://github.com/docker-mailserver/docker-mailserver/pull/3833))
- The symbol weights of SPF, DKIM and DMARC have been adjusted again. Fixes a bug and includes more appropriate combinations of symbols ([#3913](https://github.com/docker-mailserver/docker-mailserver/pull/3913), [#3923](https://github.com/docker-mailserver/docker-mailserver/pull/3923))
- **Dovecot:**
- `logwatch` now filters out non-error logs related to the status of the `index-worker` process for FTS indexing. ([#4012](https://github.com/docker-mailserver/docker-mailserver/pull/4012))
- updated FTS Xapian from version 1.5.5 to 1.7.12
### Fixes
- DMS config:
- Files that are parsed line by line are now more robust to parse by detecting and fixing line-endings ([#3819](https://github.com/docker-mailserver/docker-mailserver/pull/3819))
- The override config `postfix-main.cf` now retains custom parameters intended for use with `postfix-master.cf` ([#3880](https://github.com/docker-mailserver/docker-mailserver/pull/3880))
- Variables related to Rspamd are declared as `readonly`, which would cause warnings in the log when being re-declared; we now guard against this issue ([#3837](https://github.com/docker-mailserver/docker-mailserver/pull/3837))
- Relay host feature refactored ([#3845](https://github.com/docker-mailserver/docker-mailserver/pull/3845))
- `DEFAULT_RELAY_HOST` ENV can now also use the `RELAY_USER` + `RELAY_PASSWORD` ENV for supplying credentials.
- `RELAY_HOST` ENV no longer enforces configuring outbound SMTP to require credentials. Like `DEFAULT_RELAY_HOST` it can now configure a relay where credentials are optional.
- Restarting DMS should not be required when configuring relay hosts without these ENV, but solely via `setup relay ...`, as change detection events now apply relevant Postfix setting changes for supporting credentials too.
- Rspamd configuration: Add a missing comma in `local_networks` so that all internal IP addresses are actually considered as internal ([#3862](https://github.com/docker-mailserver/docker-mailserver/pull/3862))
- Ensure correct SELinux security context labels for files and directories moved to the mail-state volume during setup ([#3890](https://github.com/docker-mailserver/docker-mailserver/pull/3890))
- Use correct environment variable for fetchmail ([#3901](https://github.com/docker-mailserver/docker-mailserver/pull/3901))
- When using `ENABLE_GETMAIL=1` the undocumented internal location `/var/lib/getmail/` usage has been removed. Only the config volume `/tmp/docker-mailserver/getmail/` location is supported when Getmail has not been configured to deliver mail to Dovecot as advised in the DMS docs ([#4018](https://github.com/docker-mailserver/docker-mailserver/pull/4018))
- Dovecot dummy accounts (_virtual alias workaround for dovecot feature `ENABLE_QUOTAS=1`_) now correctly matches the home location of the user for that alias ([#3997](https://github.com/docker-mailserver/docker-mailserver/pull/3997))
## [v13.3.1](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.3.1)
### Fixes
@ -32,12 +277,12 @@ All notable changes to this project will be documented in this file. The format
### Added
- **Docs:**
- **Documentation:**
- An example for how to bind outbound SMTP connections to a specific network interface ([#3465](https://github.com/docker-mailserver/docker-mailserver/pull/3465))
### Updates
- **Tests**:
- **Tests:**
- Revised OAuth2 test ([#3795](https://github.com/docker-mailserver/docker-mailserver/pull/3795))
- Replace `wc -l` with `grep -c` ([#3752](https://github.com/docker-mailserver/docker-mailserver/pull/3752))
- Revised testing of service process management (supervisord) to be more robust ([#3780](https://github.com/docker-mailserver/docker-mailserver/pull/3780))
@ -49,9 +294,9 @@ All notable changes to this project will be documented in this file. The format
- `test/files/emails/existing/` files were removed similar to previous removal of SMTP auth files as they became redundant with `swaks`.
- **Internal:**
- Postfix is now configured with `smtputf8_enable = no` in our default `main.cf` config (_instead of during container startup_). ([#3750](https://github.com/docker-mailserver/docker-mailserver/pull/3750))
- **Rspamd** ([#3726](https://github.com/docker-mailserver/docker-mailserver/pull/3726)):
- **Rspamd:** ([#3726](https://github.com/docker-mailserver/docker-mailserver/pull/3726)):
- Symbol scores for SPF, DKIM & DMARC were updated to more closely align with [RFC7489](https://www.rfc-editor.org/rfc/rfc7489#page-24). Please note that complete alignment is undesirable as other symbols may be added as well, which changes the overall score calculation again, see [this issue](https://github.com/docker-mailserver/docker-mailserver/issues/3690#issuecomment-1866871996)
- **Docs:**
- **Documentation:**
- Revised the SpamAssassin ENV docs to better communicate configuration and their relation to other ENV settings. ([#3756](https://github.com/docker-mailserver/docker-mailserver/pull/3756))
- Detailed how mail received is assigned a spam score by Rspamd and processed accordingly ([#3773](https://github.com/docker-mailserver/docker-mailserver/pull/3773))
@ -100,7 +345,7 @@ DMS is now secured against the [recently published spoofing attack "SMTP Smuggli
- ENV `ENABLE_IMAP` ([#3703](https://github.com/docker-mailserver/docker-mailserver/pull/3703))
- **Tests:**
- You can now use `make run-local-instance` to run a DMS image that was built locally to test changes ([#3663](https://github.com/docker-mailserver/docker-mailserver/pull/3663))
- **Internal**:
- **Internal:**
- Log a warning when update-check is enabled, but no stable release image is used ([#3684](https://github.com/docker-mailserver/docker-mailserver/pull/3684))
### Updates
@ -115,7 +360,7 @@ DMS is now secured against the [recently published spoofing attack "SMTP Smuggli
### Fixed
- **Internal**:
- **Internal:**
- The container startup welcome log message now references `DMS_RELEASE` ([#3676](https://github.com/docker-mailserver/docker-mailserver/pull/3676))
- `VERSION` was incremented for prior releases to be notified of the v13.0.1 patch release ([#3676](https://github.com/docker-mailserver/docker-mailserver/pull/3676))
- `VERSION` is no longer included in the image ([#3711](https://github.com/docker-mailserver/docker-mailserver/pull/3711))

File diff suppressed because it is too large Load diff

View file

@ -4,10 +4,10 @@
# This is in preparation for more granular stages (eg ClamAV and Fail2Ban split into their own)
ARG DEBIAN_FRONTEND=noninteractive
ARG DOVECOT_COMMUNITY_REPO=1
ARG DOVECOT_COMMUNITY_REPO=0
ARG LOG_LEVEL=trace
FROM docker.io/debian:11-slim AS stage-base
FROM docker.io/debian:12-slim AS stage-base
ARG DEBIAN_FRONTEND
ARG DOVECOT_COMMUNITY_REPO
@ -30,8 +30,6 @@ COPY target/scripts/helpers/log.sh /usr/local/bin/helpers/log.sh
RUN /bin/bash /build/packages.sh && rm -r /build
# -----------------------------------------------
# --- Compile deb packages ----------------------
# -----------------------------------------------
@ -64,7 +62,7 @@ SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
# which would require an extra memory of 500MB+ during an image build.
# When using `COPY --link`, the `--chown` option is only compatible with numeric ID values.
# hadolint ignore=DL3021
COPY --link --chown=200 --from=docker.io/clamav/clamav:latest /var/lib/clamav /var/lib/clamav
COPY --link --chown=200 --from=docker.io/clamav/clamav-debian:latest /var/lib/clamav /var/lib/clamav
RUN <<EOF
# `COPY --link --chown=200` has a bug when built by the buildx docker-container driver.
@ -84,42 +82,32 @@ EOF
# install fts_xapian plugin
COPY --from=stage-compile dovecot-fts-xapian-1.5.5_1.5.5_*.deb /
RUN dpkg -i /dovecot-fts-xapian-1.5.5_1.5.5_*.deb && rm /dovecot-fts-xapian-1.5.5_1.5.5_*.deb
COPY --from=stage-compile dovecot-fts-xapian-*.deb /
RUN dpkg -i /dovecot-fts-xapian-*.deb && rm /dovecot-fts-xapian-*.deb
COPY target/dovecot/*.inc target/dovecot/*.conf /etc/dovecot/conf.d/
COPY target/dovecot/dovecot-purge.cron /etc/cron.d/dovecot-purge.disabled
RUN chmod 0 /etc/cron.d/dovecot-purge.disabled
WORKDIR /usr/share/dovecot
# hadolint ignore=SC2016,SC2086,SC2069
RUN <<EOF
sedfile -i -e 's/include_try \/usr\/share\/dovecot\/protocols\.d/include_try \/etc\/dovecot\/protocols\.d/g' /etc/dovecot/dovecot.conf
sedfile -i -e 's/#mail_plugins = \$mail_plugins/mail_plugins = \$mail_plugins sieve/g' /etc/dovecot/conf.d/15-lda.conf
sedfile -i -e 's/^.*lda_mailbox_autocreate.*/lda_mailbox_autocreate = yes/g' /etc/dovecot/conf.d/15-lda.conf
sedfile -i -e 's/^.*lda_mailbox_autosubscribe.*/lda_mailbox_autosubscribe = yes/g' /etc/dovecot/conf.d/15-lda.conf
sedfile -i -e 's/^.*postmaster_address.*/postmaster_address = '${POSTMASTER_ADDRESS:="postmaster@domain.com"}'/g' /etc/dovecot/conf.d/15-lda.conf
EOF
# -----------------------------------------------
# --- Rspamd ------------------------------------
# -----------------------------------------------
COPY target/rspamd/local.d/ /etc/rspamd/local.d/
COPY target/rspamd/scores.d/* /etc/rspamd/scores.d/
# -----------------------------------------------
# --- OAUTH2 ------------------------------------
# -----------------------------------------------
COPY target/dovecot/auth-oauth2.conf.ext /etc/dovecot/conf.d
COPY target/dovecot/dovecot-oauth2.conf.ext /etc/dovecot
COPY target/dovecot/auth-oauth2.conf.ext /etc/dovecot/conf.d
# -----------------------------------------------
# --- LDAP & SpamAssassin's Cron ----------------
# -----------------------------------------------
COPY target/dovecot/dovecot-ldap.conf.ext /etc/dovecot
COPY target/dovecot/auth-ldap.conf.ext /etc/dovecot/conf.d
COPY \
target/postfix/ldap-users.cf \
target/postfix/ldap-groups.cf \
@ -130,7 +118,8 @@ COPY \
# hadolint ignore=SC2016
RUN <<EOF
sedfile -i -r 's/^(CRON)=0/\1=1/g' /etc/default/spamassassin
# ref: https://github.com/docker-mailserver/docker-mailserver/pull/3403#discussion_r1306282387
echo 'CRON=1' >/etc/default/spamassassin
sedfile -i -r 's/^\$INIT restart/supervisorctl restart amavis/g' /etc/spamassassin/sa-update-hooks.d/amavisd-new
mkdir /etc/spamassassin/kam/
curl -sSfLo /etc/spamassassin/kam/kam.sa-channels.mcgrail.com.key https://mcgrail.com/downloads/kam.sa-channels.mcgrail.com.key
@ -145,7 +134,7 @@ COPY target/postgrey/postgrey /etc/default/postgrey
RUN <<EOF
mkdir /var/run/postgrey
chown postgrey:postgrey /var/run/postgrey
curl -Lsfo /etc/postgrey/whitelist_clients https://postgrey.schweikert.ch/pub/postgrey_whitelist_clients
curl -Lsfo /etc/postgrey/whitelist_clients https://raw.githubusercontent.com/schweikert/postgrey/master/postgrey_whitelist_clients
EOF
COPY target/amavis/conf.d/* /etc/amavis/conf.d/
@ -187,9 +176,6 @@ COPY target/fail2ban/fail2ban.d/fixes.local /etc/fail2ban/fail2ban.d/fixes.local
RUN <<EOF
ln -s /var/log/mail/mail.log /var/log/mail.log
ln -sf /var/log/mail/fail2ban.log /var/log/fail2ban.log
# disable sshd jail
rm /etc/fail2ban/jail.d/defaults-debian.conf
mkdir /var/run/fail2ban
EOF
COPY target/opendkim/opendkim.conf /etc/opendkim.conf
@ -217,7 +203,8 @@ EOF
RUN echo 'Reason_Message = Message {rejectdefer} due to: {spf}.' >>/etc/postfix-policyd-spf-python/policyd-spf.conf
COPY target/fetchmail/fetchmailrc /etc/fetchmailrc_general
COPY target/getmail/getmailrc /etc/getmailrc_general
COPY target/getmail/getmailrc_general /etc/getmailrc_general
COPY target/getmail/getmail-service.sh /usr/local/bin/
COPY target/postfix/main.cf target/postfix/master.cf /etc/postfix/
# DH parameters for DHE cipher suites, ffdhe4096 is the official standard 4096-bit DH params now part of TLS 1.3
@ -266,8 +253,9 @@ RUN <<EOF
sedfile -i -e 's/^\(POLICYHELPER=\).*/\1/' /usr/sbin/invoke-rc.d
# prevent syslog warning about imklog permissions
sedfile -i -e 's/^module(load=\"imklog\")/#module(load=\"imklog\")/' /etc/rsyslog.conf
# prevent email when /sbin/init or init system is not existing
sedfile -i -e 's|invoke-rc.d rsyslog rotate > /dev/null|/usr/bin/supervisorctl signal hup rsyslog >/dev/null|g' /usr/lib/rsyslog/rsyslog-rotate
# this change is for our alternative process manager rather than part of
# a fix related to the change preceding it.
echo -e '\n/usr/bin/supervisorctl signal hup rsyslog >/dev/null' >>/usr/lib/rsyslog/rsyslog-rotate
EOF
# -----------------------------------------------
@ -275,6 +263,7 @@ EOF
# -----------------------------------------------
COPY target/logwatch/maillog.conf /etc/logwatch/conf/logfiles/maillog.conf
COPY target/logwatch/ignore.conf /etc/logwatch/conf/ignore.conf
# -----------------------------------------------
# --- Supervisord & Start -----------------------

View file

@ -4,22 +4,27 @@
[ci::status]: https://img.shields.io/github/actions/workflow/status/docker-mailserver/docker-mailserver/default_on_push.yml?branch=master&color=blue&label=CI&logo=github&logoColor=white&style=for-the-badge
[ci::github]: https://github.com/docker-mailserver/docker-mailserver/actions
[docker::pulls]: https://img.shields.io/docker/pulls/mailserver/docker-mailserver.svg?style=for-the-badge&logo=docker&logoColor=white
[docker::pulls]: https://img.shields.io/docker/pulls/mailserver/docker-mailserver.svg?style=for-the-badge&logo=docker&logoColor=white&color=blue
[docker::hub]: https://hub.docker.com/r/mailserver/docker-mailserver/
[documentation::badge]: https://img.shields.io/badge/DOCUMENTATION-GH%20PAGES-0078D4?style=for-the-badge&logo=git&logoColor=white
[documentation::badge]: https://img.shields.io/badge/DOCUMENTATION-GH%20PAGES-0078D4?style=for-the-badge&logo=googledocs&logoColor=white
[documentation::web]: https://docker-mailserver.github.io/docker-mailserver/latest/
## :page_with_curl: About
A production-ready fullstack but simple containerized mail server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.). Only configuration files, no SQL database. Keep it simple and versioned. Easy to deploy and upgrade. Originally created by @tomav, this project is now maintained by volunteers since January 2021.
A production-ready fullstack but simple containerized mail server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.).
- Only configuration files, no SQL database. Keep it simple and versioned. Easy to deploy and upgrade.
- Originally created by [@tomav](https://github.com/tomav), this project is now maintained by volunteers since January 2021.
## :bulb: Documentation
## <!-- Adds a thin line break separator style -->
We provide a [dedicated documentation][documentation::web] hosted on GitHub Pages. Make sure to read it as it contains all the information necessary to set up and configure your mail server. The documentation is crafted with Markdown & [MkDocs Material](https://squidfunk.github.io/mkdocs-material/).
> [!TIP]
> Be sure to read [our documentation][documentation::web]. It provides guidance on initial setup of your mail server.
## :boom: Issues
If you have issues, please search through [the documentation][documentation::web] **for your version** before opening an issue. The issue tracker is for issues, not for personal support. Make sure the version of the documentation matches the image version you're using!
> [!IMPORTANT]
> If you have issues, please search through [the documentation][documentation::web] **for your version** before opening an issue.
>
> The issue tracker is for issues, not for personal support.
> Make sure the version of the documentation matches the image version you're using!
## :link: Links to Useful Resources
@ -33,8 +38,8 @@ If you have issues, please search through [the documentation][documentation::web
## :package: Included Services
- [Postfix](http://www.postfix.org) with SMTP or LDAP authentication and support for [extension delimiters](https://docker-mailserver.github.io/docker-mailserver/latest/config/user-management/aliases/#address-tags-extension-delimiters-an-alternative-to-aliases)
- [Dovecot](https://www.dovecot.org) with SASL, IMAP, POP3, LDAP, [basic Sieve support](https://docker-mailserver.github.io/docker-mailserver/latest/config/advanced/mail-sieve) and [quotas](https://docker-mailserver.github.io/docker-mailserver/latest/config/user-management/accounts#notes)
- [Postfix](http://www.postfix.org) with SMTP or LDAP authentication and support for [extension delimiters](https://docker-mailserver.github.io/docker-mailserver/latest/config/account-management/overview/#aliases)
- [Dovecot](https://www.dovecot.org) with SASL, IMAP, POP3, LDAP, [basic Sieve support](https://docker-mailserver.github.io/docker-mailserver/latest/config/advanced/mail-sieve) and [quotas](https://docker-mailserver.github.io/docker-mailserver/latest/config/account-management/overview/#quotas)
- [Rspamd](https://rspamd.com/)
- [Amavis](https://www.amavis.org/)
- [SpamAssassin](http://spamassassin.apache.org/) supporting custom rules

View file

@ -1 +0,0 @@
13.3.1

View file

@ -7,7 +7,6 @@ services:
env_file: mailserver.env
# More information about the mail-server ports:
# https://docker-mailserver.github.io/docker-mailserver/latest/config/security/understanding-the-ports/
# To avoid conflicts with yaml base-60 float, DO NOT remove the quotation marks.
ports:
- "25:25" # SMTP (explicit TLS => STARTTLS, Authentication is DISABLED => use port 465/587 instead)
- "143:143" # IMAP4 (explicit TLS => STARTTLS)
@ -26,6 +25,6 @@ services:
# cap_add:
# - NET_ADMIN
healthcheck:
test: "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1"
test: "ss --listening --ipv4 --tcp | grep --silent ':smtp' || exit 1"
timeout: 3s
retries: 0

View file

@ -29,9 +29,6 @@ enabled = true
# https://github.com/docker-mailserver/docker-mailserver/issues/3256#issuecomment-1511188760
mode = extra
[postfix-sasl]
enabled = true
# This jail is used for manual bans.
# To ban an IP address use: setup.sh fail2ban ban <IP>
[custom]

View file

@ -1,3 +1,5 @@
# https://getmail6.org/configuration.html#conf-options
[options]
verbose = 0
read_all = false
@ -5,3 +7,5 @@ delete = false
max_messages_per_session = 500
received = false
delivered_to = false
message_log_syslog = true

View file

@ -0,0 +1,13 @@
# https://getmail6.org/configuration.html
[retriever]
type = SimpleIMAPSSLRetriever
server = imap.gmail.com
username = alice
password = notsecure
[destination]
type = MDA_external
path = /usr/lib/dovecot/deliver
allow_root_commands = true
arguments =("-d","user1@example.com")

View file

@ -0,0 +1,13 @@
# https://getmail6.org/configuration.html
[retriever]
type = SimplePOP3SSLRetriever
server = pop3.gmail.com
username = alice
password = notsecure
[destination]
type = MDA_external
path = /usr/lib/dovecot/deliver
allow_root_commands = true
arguments =("-d","user1@example.com")

View file

@ -0,0 +1,60 @@
# Docs: https://docker-mailserver.github.io/docker-mailserver/v15.0/config/advanced/mail-fetchmail
# Additional context, with CLI commands for verification:
# https://github.com/orgs/docker-mailserver/discussions/3994#discussioncomment-9290570
services:
dms-fetch:
image: ghcr.io/docker-mailserver/docker-mailserver:latest # :15.0
hostname: mail.example.test
environment:
ENABLE_FETCHMAIL: 1
# We change this setting to 10 for quicker testing:
FETCHMAIL_POLL: 10
# Link the DNS lookup `remote.test` to resolve to the `dms-remote` container IP (for `@remote.test` address):
# This is only for this example, since no real DNS service is configured, this is a Docker internal DNS feature:
links:
- "dms-remote:remote.test"
# NOTE: Optional, You only need to publish ports if you want to verify via your own mail client.
#ports:
# - "465:465" # ESMTP (implicit TLS)
# - "993:993" # IMAP4 (implicit TLS)
# You'd normally use `volumes` here but for simplicity of the example, all config is contained within `compose.yaml`:
configs:
- source: dms-accounts-fetch
target: /tmp/docker-mailserver/postfix-accounts.cf
- source: fetchmail
target: /tmp/docker-mailserver/fetchmail.cf
dms-remote:
image: ghcr.io/docker-mailserver/docker-mailserver:latest # :15.0
hostname: mail.remote.test
environment:
# Allows for us send a test mail easily by trusting any mail client run within this container (`swaks`):
PERMIT_DOCKER: container
# Alternatively, trust and accept any mail received from clients in same subnet of dms-fetch:
#PERMIT_DOCKER: connected-networks
configs:
- source: dms-accounts-remote
target: /tmp/docker-mailserver/postfix-accounts.cf
# Using the Docker Compose `configs.content` feature instead of volume mounting separate files.
# NOTE: This feature requires Docker Compose v2.23.1 (Nov 2023) or newer:
# https://github.com/compose-spec/compose-spec/pull/446
configs:
fetchmail:
content: |
poll 'mail.remote.test' proto imap
user 'jane.doe@remote.test'
pass 'secret'
is 'john.doe@example.test'
no sslcertck
# DMS requires an account to complete setup, configure one for each instance:
# NOTE: Both accounts are configured with the same password (SHA512-CRYPT hashed), `secret`.
dms-accounts-fetch:
content: |
john.doe@example.test|{SHA512-CRYPT}$$6$$sbgFRCmQ.KWS5ryb$$EsWrlYosiadgdUOxCBHY0DQ3qFbeudDhNMqHs6jZt.8gmxUwiLVy738knqkHD4zj4amkb296HFqQ3yDq4UXt8.
dms-accounts-remote:
content: |
jane.doe@remote.test|{SHA512-CRYPT}$$6$$sbgFRCmQ.KWS5ryb$$EsWrlYosiadgdUOxCBHY0DQ3qFbeudDhNMqHs6jZt.8gmxUwiLVy738knqkHD4zj4amkb296HFqQ3yDq4UXt8.

View file

@ -0,0 +1,147 @@
# Docs: https://docker-mailserver.github.io/docker-mailserver/v15.0/config/advanced/mail-forwarding/relay-hosts/
# Additional context, with CLI commands for verification:
# https://github.com/docker-mailserver/docker-mailserver/issues/4136#issuecomment-2253693490
services:
# This would represent your actual DMS container:
dms-sender:
image: mailserver/docker-mailserver:latest # :15.0
hostname: mail.example.test
environment:
# All outbound mail will be relayed through this host
# (change the port to 587 if you do not want the postfix-main.cf override)
- DEFAULT_RELAY_HOST=[smtp.relay-service.test]:465
# Your relay host credentials.
# (since the relay in the example is DMS, the relay account username is a full email address)
- RELAY_USER=relay-user@relay-service.test
- RELAY_PASSWORD=secret
# The mail client (swaks) needs to connect with TLS:
- SSL_TYPE=manual
- SSL_KEY_PATH=/tmp/tls/key.pem
- SSL_CERT_PATH=/tmp/tls/cert.pem
# You would usually have `volumes` instead of this `configs`:
configs:
- source: dms-main
target: /tmp/docker-mailserver/postfix-main.cf
- source: dms-accounts
target: /tmp/docker-mailserver/postfix-accounts.cf
# Authenticating on port 587 or 465 enforces TLS requirement:
- source: tls-cert
target: /tmp/tls/cert.pem
- source: tls-key
target: /tmp/tls/key.pem
# This is only needed if you want to verify the TLS cert chain with swaks
# (normally with public CA providers like LetsEncrypt this file is already available to a mail client)
- source: tls-ca-cert
target: /tmp/tls/ca-cert.pem
# Pretend this is your third-party relay service:
dms-relay:
image: mailserver/docker-mailserver:latest # :15.0
hostname: smtp.relay-service.test
environment:
# WORKAROUND: Bypass security checks from the mail-client (dms-sender container)
# (avoids needing expected DNS records to run this example)
- PERMIT_DOCKER=connected-networks
# TLS is required when relaying to dms-relay via ports 587 / 465
# (dms-relay will then relay the mail to dms-destination over port 25)
- SSL_TYPE=manual
- SSL_KEY_PATH=/tmp/tls/key.pem
- SSL_CERT_PATH=/tmp/tls/cert.pem
configs:
- source: dms-accounts-relay
target: /tmp/docker-mailserver/postfix-accounts.cf
- source: tls-cert
target: /tmp/tls/cert.pem
- source: tls-key
target: /tmp/tls/key.pem
# Pretend this is another mail server that your target recipient belongs to (like Gmail):
dms-destination:
image: mailserver/docker-mailserver:latest # :15.0
hostname: mail.destination.test
# WORKAROUND: dms-relay must be able to resolve DNS for `@destination.test` to the IP of this container:
# Normally a MX record would direct mail to the MTA (eg: `mail.destination.test`)
networks:
default:
aliases:
- destination.test
environment:
# WORKAROUND: Same workaround as needed for dms-relay
- PERMIT_DOCKER=connected-networks
configs:
- source: dms-accounts-destination
target: /tmp/docker-mailserver/postfix-accounts.cf
# Using the Docker Compose `configs.content` feature instead of volume mounting separate files.
# NOTE: This feature requires Docker Compose v2.23.1 (Nov 2023) or newer:
# https://github.com/compose-spec/compose-spec/pull/446
configs:
# `postfix-main.cf`, a single line change to make all outbound SMTP connections over implicit TLS instead of the default explicit TLS (StartTLS).
# NOTE: If you need to only selectively relay mail, you would need to instead adjust this on the relay service in `/etc/postfix/master.cf`,
# However DMS presently modifies this when using the DMS Relay Host feature support, which may override `postfix-master.cf` or `user-patches.sh` due to `check-for-changes.sh`.
dms-main:
content: |
smtp_tls_wrappermode=yes
# DMS expects an account to be configured to run, this example provides accounts already created.
# Login credentials:
# user: "john.doe@example.test" password: "secret"
# user: "relay-user@relay-service.test" password: "secret"
# user: "jane.doe@destination.test" password: "secret"
dms-accounts:
# NOTE: `$` needed to be repeated to escape it,
# which opts out of the `compose.yaml` variable interpolation feature.
content: |
john.doe@example.test|{SHA512-CRYPT}$$6$$sbgFRCmQ.KWS5ryb$$EsWrlYosiadgdUOxCBHY0DQ3qFbeudDhNMqHs6jZt.8gmxUwiLVy738knqkHD4zj4amkb296HFqQ3yDq4UXt8.
dms-accounts-relay:
content: |
relay-user@relay-service.test|{SHA512-CRYPT}$$6$$o65y1ZXC4ooOPLwZ$$7TF1nYowEtNJpH6BwJBgdj2pPAxaCvhIKQA6ww5zdHm/AA7aemY9eoHC91DOgYNaKj1HLxSeWNDdvrp6mbtUY.
dms-accounts-destination:
content: |
jane.doe@destination.test|{SHA512-CRYPT}$$6$$o65y1ZXC4ooOPLwZ$$7TF1nYowEtNJpH6BwJBgdj2pPAxaCvhIKQA6ww5zdHm/AA7aemY9eoHC91DOgYNaKj1HLxSeWNDdvrp6mbtUY.
# TLS files:
# - Use an ECDSA cert that's been signed by a self-signed CA for TLS cert verification.
# - This cert is only valid for mail.example.test, mail.destination.test, smtp.relay-service.test
# `swaks` run in the container will need to reference this CA cert file for successful verficiation (optional).
tls-ca-cert:
content: |
-----BEGIN CERTIFICATE-----
MIIBfTCCASKgAwIBAgIRAMAZttlRlkcuSun0yV0z4RwwCgYIKoZIzj0EAwIwHDEa
MBgGA1UEAxMRU21hbGxzdGVwIFJvb3QgQ0EwHhcNMjEwMTAxMDAwMDAwWhcNMzEw
MTAxMDAwMDAwWjAcMRowGAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTBZMBMGByqG
SM49AgEGCCqGSM49AwEHA0IABJX2hCtoK3+bM5I3rmyApXLJ1gOcVhtoSSwM8XXR
SEl25Kkc0n6mINuMK8UrBkiBUgexf6CYayx3xVr9TmMkg4KjRTBDMA4GA1UdDwEB
/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBQD8sBrApbyYyqU
y+/TlwGynx2V5jAKBggqhkjOPQQDAgNJADBGAiEAi8N2eOETI+6hY3+G+kzNMd3K
Sd3Ke8b++/nlwr5Fb/sCIQDYAjpKp/MpTDWICeHC2tcB5ptxoTdWkTBuG4rKcktA
0w==
-----END CERTIFICATE-----
tls-key:
content: |
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIOc6wqZmSDmT336K4O26dMk1RCVc0+cmnsO2eK4P5K5yoAoGCCqGSM49
AwEHoUQDQgAEFOWNgekKKvUZE89vJ7henUYxODYIvCiHitRc2ylwttjqt1KUY1cp
q3jof2fhURHfBUH3dHPXLHig5V9Jw5gqeg==
-----END EC PRIVATE KEY-----
tls-cert:
content: |
-----BEGIN CERTIFICATE-----
MIIB9DCCAZqgAwIBAgIQE53a/y2c//YXRsz2kLm6gDAKBggqhkjOPQQDAjAcMRow
GAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTAeFw0yMTAxMDEwMDAwMDBaFw0zMTAx
MDEwMDAwMDBaMBkxFzAVBgNVBAMTDlNtYWxsc3RlcCBMZWFmMFkwEwYHKoZIzj0C
AQYIKoZIzj0DAQcDQgAEFOWNgekKKvUZE89vJ7henUYxODYIvCiHitRc2ylwttjq
t1KUY1cpq3jof2fhURHfBUH3dHPXLHig5V9Jw5gqeqOBwDCBvTAOBgNVHQ8BAf8E
BAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBSz
w74g+O6dcBbwienD70D8A9ESmDAfBgNVHSMEGDAWgBQD8sBrApbyYyqUy+/TlwGy
nx2V5jBMBgNVHREERTBDghFtYWlsLmV4YW1wbGUudGVzdIIVbWFpbC5kZXN0aW5h
dGlvbi50ZXN0ghdzbXRwLnJlbGF5LXNlcnZpY2UudGVzdDAKBggqhkjOPQQDAgNI
ADBFAiEAoety5oClZtuBMkvlUIWRmWlyg1VIOZ544LSEbplsIhcCIHb6awMwNdXP
m/xHjFkuwH1+UjDDRW53Ih7KZoLrQ6Cp
-----END CERTIFICATE-----

View file

@ -16,12 +16,16 @@ If you want to append instead, switch `::before` to `::after`.
src: url('../fonts/external-link.woff') format('woff');
}
/* Matches the two nav link classes that start with `http` `href` values, regular docs pages use relative URLs instead. */
.md-tabs__link[href^="http"]::before, .md-nav__link[href^="http"]::before {
/*
Since mkdocs-material 9.5.5 broke support in our docs from DMS v13.3.1, we now use our own class name,
which has been included for the two external nav links in mkdocs.yml via workaround (insert HTML).
*/
.icon-external-link::before {
display: inline-block; /* treat similar to text */
font-family: 'external-link';
content:'\0041'; /* represents "A" which our font renders as an icon instead of the "A" glyph */
font-size: 80%; /* icon is a little too big by default, scale it down */
margin-right: 4px;
}
/* ============================================================================================================= */
@ -98,3 +102,42 @@ div.md-content article.md-content__inner a.toclink code {
.highlight.no-copy .md-clipboard { display: none; }
/* ============================================================================================================= */
/* Make the left-sidebar nav categories better distinguished from page links (bold text) */
.md-nav__item--nested > .md-nav__link {
font-weight: 700;
}
/* ============================================================================================================= */
/*
TaskList style for a pro/con list. Presently only used for this type of list in the kubernetes docs.
Uses a custom icon for the unchecked (con) state: :octicons-x-circle-fill-24:
https://github.com/squidfunk/mkdocs-material/discussions/6811#discussioncomment-8700795
TODO: Can better scope the style under a class name when migrating to block extension syntax:
https://github.com/facelessuser/pymdown-extensions/discussions/1973
*/
:root {
--md-tasklist-icon--failed: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M1 12C1 5.925 5.925 1 12 1s11 4.925 11 11-4.925 11-11 11S1 18.075 1 12Zm8.036-4.024a.751.751 0 0 0-1.042.018.751.751 0 0 0-.018 1.042L10.939 12l-2.963 2.963a.749.749 0 0 0 .326 1.275.749.749 0 0 0 .734-.215L12 13.06l2.963 2.964a.75.75 0 0 0 1.061-1.06L13.061 12l2.963-2.964a.749.749 0 0 0-.326-1.275.749.749 0 0 0-.734.215L12 10.939Z"/></svg>');
}
.md-typeset [type="checkbox"] + .task-list-indicator::before {
background-color: rgb(216, 87, 48);
-webkit-mask-image: var(--md-tasklist-icon--failed);
mask-image: var(--md-tasklist-icon--failed);
}
/* More suitable shade of green */
.md-typeset [type=checkbox]:checked+.task-list-indicator:before {
background-color: rgb(97, 216, 42);
}
/* Tiny layout shift */
[dir=ltr] .md-typeset .task-list-indicator:before {
left: -1.6em;
top: 1px;
}
/* ============================================================================================================= */

View file

@ -0,0 +1,252 @@
# Account Management - Overview
This page provides a technical reference for account management in DMS.
!!! note "Account provisioners and alternative authentication support"
Each [`ACCOUNT_PROVISIONER`][docs::env::account-provisioner] has a separate page for configuration guidance and caveats:
- [`FILE` provisioner docs][docs::account-provisioner::file]
- [`LDAP` provisioner docs][docs::account-provisioner::ldap]
Authentication from the provisioner can be supplemented with additional methods:
- [OAuth2 / OIDC][docs::account-auth::oauth2] (_allow login from an external authentication service_)
- [Master Accounts][docs::account-auth::master-accounts] (_access the mailbox of any DMS account_)
---
For custom authentication requirements, you could [implement this with Lua][docs::examples::auth-lua].
## Accounts
!!! info
To receive or send mail, you'll need to provision user accounts into DMS (_as each provisioner page documents_).
---
A DMS account represents a user with their _login username_ + password, and optional config like aliases and quota.
- Sending mail from different addresses **does not require** aliases or separate accounts.
- Each account is configured with a _primary email address_ that a mailbox is associated to.
??? info "Primary email address"
The email address associated to an account creates a mailbox. This address is relevant:
- When DMS **receives mail** for that address as the recipient (_or an alias that resolves to it_), to identify which mailbox to deliver into.
- With **mail submission**:
- `SPOOF_PROTECTION=1` **restricts the sender address** to the DMS account email address (_unless additional sender addresses have been permitted via supported config_).
- `SPOOF_PROTECTION=0` allows DMS accounts to **use any sender address** (_only a single DMS account is necessary to send mail with different sender addresses_).
---
For more details, see the [Technical Overview](#technical-overview) section.
??? note "Support for multiple mail domains"
No extra configuration in DMS is required after provisioning an account with an email address.
- The DNS records for a domain should direct mail to DMS and allow DMS to send mail on behalf of that domain.
- DMS does not need TLS certificates for your mail domains, only for the DMS FQDN (_the `hostname` setting_).
??? warning "Choosing a compatible email address"
An email address should conform to the standard [permitted charset and format][email-syntax::valid-charset-format] (`local-part@domain-part`).
---
DMS has features that need to reserve special characters to work correctly. Ensure those characters are not present in email addresses you configure for DMS, otherwise disable / opt-out of the feature.
- [Sub-addressing](#sub-addressing) is enabled by default with `+` as the _tag delimiter_. The tag can be changed, feature opt-out when the tag is explicitly unset.
### Aliases
!!! info
Aliases allow receiving mail:
- As an alternative delivery address for a DMS account mailbox.
- To redirect / forward to an external address outside of DMS like `@gmail.com`.
??? abstract "Technical Details (_Local vs Virtual aliases_)"
Aliases are managed through Postfix which supports _local_ and _virtual_ aliases:
- **Local aliases** are for mail routed to the [`local` delivery agent][postfix::delivery-agent::local] (see [associated alias config format][postfix::config-table::local-alias])
- You rarely need to configure this. It is used internally for system unix accounts belonging to the services running in DMS (_including `root`_).
- `postmaster` may be a local alias to `root`, and `root` to a virtual alias or real email address.
- Any mail sent through the `local` delivery agent will not be delivered to an inbox managed by Dovecot (_unless you have configured a local alias to redirect mail to a valid address or alias_).
- The domain-part of an these aliases belongs to your DMS FQDN (_`hostname: mail.example.com`, thus `user@mail.example.com`_). Technically there is no domain-part at this point, that context is used when routing delivery, the local delivery agent only knows of the local-part (_an alias or unix account_).
- [**Virtual aliases**][postfix-docs::virtual-alias] are for mail routed to the [`virtual` delivery agent][postfix::delivery-agent::virtual] (see [associated alias config format][postfix::config-table::virtual-alias])
- When alias support in DMS is discussed without the context of being a local or virtual alias, it's likely the virtual kind (_but could also be agnostic_).
- The domain-part of an these aliases belongs to a mail domain managed by DMS (_like `user@example.com`_).
!!! tip "Verify alias resolves correctly"
You can run `postmap -q <alias> <table>` in the container to verify an alias resolves to the expected target. If the target is also an alias, the command will not expand that alias to resolve the actual recipient(s).
For the `FILE` provisioner, an example would be: `postmap -q alias1@example.com /etc/postfix/virtual`. For the `LDAP` provisioner you'd need to adjust the table path.
!!! info "Side effect - Dovecot Quotas (`ENABLE_QUOTAS=1`)"
As a side effect of the alias workaround for the `FILE` provisioner with this feature, aliases can be used for account login. This is not intentional.
### Quotas
!!! info
Enables mail clients with the capability to query a mailbox for disk-space used and capacity limit.
- This feature is enabled by default, opt-out via [`ENABLE_QUOTAS=0`][docs::env::enable-quotas]
- **Not implemented** for the LDAP provisioner (_PR welcome! View the [feature request for implementation advice][gh-issue::dms-feature-request::dovecot-quotas-ldap]_)
??? tip "How are quotas useful?"
Without quota limits for disk storage, a mailbox could fill up the available storage which would cause delivery failures to all mailboxes.
Quotas help by preventing that abuse, so that only a mailbox exceeding the assigned quota experiences a delivery failure instead of negatively impacting others (_provided disk space is available_).
??? abstract "Technical Details"
The [Dovecot Quotas feature][gh-pr::dms-feature::dovecot-quotas] is configured by enabling the [Dovecot `imap-quota` plugin][dovecot-docs::plugin::imap-quota] and using the [`count` quota backend][dovecot-docs::config::quota-backend-count].
---
**Dovecot workaround for Postfix aliases**
When mail is delivered to DMS, Postfix will query Dovecot with the recipient(s) to verify quota has not been exceeded.
This allows early rejection of mail arriving to DMS, preventing a spammer from taking advantage of a [backscatter][wikipedia::backscatter] source if the mail was accepted by Postfix, only to later be rejected by Dovecot for storage when the quota limit was already reached.
However, Postfix does not resolve aliases until after the incoming mail is accepted.
1. Postfix queries Dovecot (_a [`check_policy_service` restriction tied to the Dovecot `quota-status` service][dms::workaround::dovecot-quotas::notes-1]_) with the recipient (_the alias_).
2. `dovecot: auth: passwd-file(alias@example.com): unknown user` is logged, Postfix is then informed that the recipient mailbox is not full even if it actually was (_since no such user exists in the Dovecot UserDB_).
3. However, when the real mailbox address that the alias would later resolve into does have a quota that exceeded the configured limit, Dovecot will refuse the mail delivery from Postfix which introduces a backscatter source for spammers.
As a [workaround to this problem with the `ENABLE_QUOTAS=1` feature][dms::workaround::dovecot-quotas::summary], DMS will add aliases as fake users into Dovecot UserDB (_that are configured with the same data as the real address the alias would resolve to, thus sharing the same mailbox location and quota limit_). This allows Postfix to properly be aware of an aliased mailbox having exceeded the allowed quota.
**NOTE:** This workaround **only supports** aliases to a single target recipient of a real account address / mailbox.
- Additionally, aliases that resolve to another alias or to an external address would both fail the UserDB lookup, unable to determine if enough storage is available.
- A proper fix would [implement a Postfix policy service][dms::workaround::dovecot-quotas::notes-2] that could correctly resolve aliases to valid entries in the Dovecot UserDB, querying the `quota-status` service and returning that response to Postfix.
## Sub-addressing
!!! info
[Subaddressing][wikipedia::subaddressing] (_aka **Plus Addressing** or **Address Tags**_) is a feature that allows you to receive mail to an address which includes a tag appended to the `local-part` of a valid account address.
- A subaddress has a tag delimiter (_default: `+`_), followed by the tag: `<local-part>+<tag>@<domain-part>`
- The subaddress `user+github@example.com` would deliver mail to the same mailbox as `user@example.com`.
- Tags are dynamic. Anything between the `+` and `@` is understood as the tag, no additional configuration required.
- Only the first occurence of the tag delimiter is recognized. Any additional occurences become part of the tag value itself.
??? tip "When is subaddressing useful?"
A common use-case is to use a unique tag for each service you register your email address with.
- Routing delivery to different folders in your mailbox based on the tag (_via a [Sieve filter][docs::sieve::subaddressing]_).
- Data leaks or bulk sales of email addresses.
- If spam / phishing mail you receive has not removed the tag, you will have better insight into where your address was compromised from.
- When the expected tag is missing, this additionally helps identify bad actors. Especially when mail delivery is routed to subfolders by tag.
- For more use-cases, view the end of [this article][web::subaddress-use-cases].
??? tip "Changing the tag delimiter"
Add `recipient_delimiter = +` to these config override files (_replacing `+` with your preferred delimiter_):
- Postfix: `docker-data/dms/config/postfix-main.cf`
- Dovecot: `docker-data/dms/config/dovecot.cf`
??? tip "Opt-out of subaddressing"
Follow the advice to change the tag delimiter, but instead set an empty value (`recipient_delimiter =`).
??? warning "Only for receiving, not sending"
Do not attempt to send mail from these tagged addresses, they are not equivalent to aliases.
This feature is only intended to be used when a mail client sends to a DMS managed recipient address. While DMS does not restrict the sender address you choose to send mail from (_provided `SPOOF_PROTECTION` has not been enabled_), it is often [forbidden by mail services][ms-exchange-docs::limitations].
??? abstract "Technical Details"
The configured tag delimiter (`+`) allows both Postfix and Dovecot to recognize subaddresses. Without this feature configured, the subaddresses would be considered as separate mail accounts rather than routed to a common account address.
---
Internally DMS has the tag delimiter configured by:
- Applying the Postfix `main.cf` setting: [`recipient_delimiter = +`][postfix-docs::recipient-delimiter]
- Dovecot has the equivalent setting set as `+` by default: [`recipient_delimiter = +`][dovecot-docs::config::recipient-delimiter]
## Technical Overview
!!! info
This section provides insight for understanding how Postfix and Dovecot services are involved. It is intended as a reference for maintainers and contributors.
- **Postfix** - Handles when mail is delivered (inbound) to DMS, or sent (outbound) from DMS.
- **Dovecot** - Manages access and storage for mail delivered to the DMS account mailboxes of your users.
??? abstract "Technical Details - Postfix (Inbound vs Outbound)"
Postfix needs to know how to handle inbound and outbound mail by asking these queries:
=== "Inbound"
- What mail domains is DMS responsible for handling? (_for accepting mail delivered_)
- What are valid mail addresses for those mail domains? (_reject delivery for users that don't exist_)
- Are there any aliases to redirect mail to 1 or more users, or forward to externally?
=== "Outbound"
- When `SPOOF_PROTECTION=1`, how should DMS restrict the sender address? (_eg: Users may only send mail from their associated mailbox address_)
??? abstract "Technical Details - Dovecot (Authentication)"
Dovecot additionally handles authenticating user accounts for sending and retrieving mail:
- Over the ports for IMAP and POP3 connections (_110, 143, 993, 995_).
- As the default configured SASL provider, which Postfix delegates user authentication through (_for the submission(s) ports 465 & 587_). Saslauthd can be configured as an alternative SASL provider.
Dovecot splits all authentication lookups into two categories:
- A [PassDB][dovecot::docs::passdb] lookup most importantly authenticates the user. It may also provide any other necessary pre-login information.
- A [UserDB][dovecot::docs::userdb] lookup retrieves post-login information specific to a user.
[docs::env::account-provisioner]: ../environment.md#account_provisioner
[docs::account-provisioner::file]: ./provisioner/file.md
[docs::account-provisioner::ldap]: ./provisioner/ldap.md
[docs::account-auth::oauth2]: ./supplementary/oauth2.md
[docs::account-auth::master-accounts]: ./supplementary/master-accounts.md
[docs::examples::auth-lua]: ../../examples/use-cases/auth-lua.md
[email-syntax::valid-charset-format]: https://stackoverflow.com/questions/2049502/what-characters-are-allowed-in-an-email-address/2049510#2049510
[postfix-docs::virtual-alias]: http://www.postfix.org/VIRTUAL_README.html#virtual_alias
[postfix-docs::recipient-delimiter]: http://www.postfix.org/postconf.5.html#recipient_delimiter
[dovecot-docs::config::recipient-delimiter]: https://doc.dovecot.org/settings/core/#core_setting-recipient_delimiter
[postfix::delivery-agent::local]: https://www.postfix.org/local.8.html
[postfix::delivery-agent::virtual]: https://www.postfix.org/virtual.8.html
[postfix::config-table::local-alias]: https://www.postfix.org/aliases.5.html
[postfix::config-table::virtual-alias]: https://www.postfix.org/virtual.5.html
[docs::env::enable-quotas]: ../environment.md#enable_quotas
[gh-issue::dms-feature-request::dovecot-quotas-ldap]: https://github.com/docker-mailserver/docker-mailserver/issues/2957
[dovecot-docs::config::quota-backend-count]: https://doc.dovecot.org/configuration_manual/quota/quota_count/#quota-backend-count
[dovecot-docs::plugin::imap-quota]: https://doc.dovecot.org/settings/plugin/imap-quota-plugin/
[gh-pr::dms-feature::dovecot-quotas]: https://github.com/docker-mailserver/docker-mailserver/pull/1469
[wikipedia::backscatter]: https://en.wikipedia.org/wiki/Backscatter_%28email%29
[dms::workaround::dovecot-quotas::notes-1]: https://github.com/docker-mailserver/docker-mailserver/issues/2091#issuecomment-954298788
[dms::workaround::dovecot-quotas::notes-2]: https://github.com/docker-mailserver/docker-mailserver/pull/2248#issuecomment-953754532
[dms::workaround::dovecot-quotas::summary]: https://github.com/docker-mailserver/docker-mailserver/pull/2248#issuecomment-955088677
[docs::sieve::subaddressing]: ../advanced/mail-sieve.md#subaddress-mailbox-routing
[web::subaddress-use-cases]: https://www.codetwo.com/admins-blog/plus-addressing
[wikipedia::subaddressing]: https://en.wikipedia.org/wiki/Email_address#Sub-addressing
[ms-exchange-docs::limitations]: https://learn.microsoft.com/en-us/exchange/recipients-in-exchange-online/plus-addressing-in-exchange-online#using-plus-addresses
[dovecot::docs::passdb]: https://doc.dovecot.org/configuration_manual/authentication/password_databases_passdb
[dovecot::docs::userdb]: https://doc.dovecot.org/configuration_manual/authentication/user_databases_userdb

View file

@ -0,0 +1,206 @@
---
title: 'Account Management | Provisioner (File)'
---
# Provisioner - File
## Management via the `setup` CLI
The best way to manage DMS accounts and related config files is through our `setup` CLI provided within the container.
!!! example "Using the `setup` CLI"
Try the following within the DMS container (`docker exec -it <CONTAINER NAME> bash`):
- Add an account: `setup email add <EMAIL ADDRESS>`
- Add an alias: `setup alias add <FROM ALIAS> <TO TARGET ADDRESS>`
- Learn more about the available subcommands via: `setup help`
```bash
# Starts a basic DMS instance and then shells into the container to use the `setup` CLI:
docker run --rm -itd --name dms --hostname mail.example.com mailserver/docker-mailserver
docker exec -it dms bash
# Create an account:
setup email add hello@example.com your-password-here
# Create an alias:
setup alias add your-alias-here@example.com hello@example.com
# Limit the mailbox capacity to 10 MiB:
setup quota set hello@example.com 10M
```
??? tip "Secure password input"
When you don't provide a password to the command, you will be prompted for one. This avoids the password being captured in your shell history.
```bash
# As you input your password it will not update.
# Press the ENTER key to apply the hidden password input.
$ setup email add hello@example.com
Enter Password:
Confirm Password:
```
!!! note "Account removal via `setup email del`"
When you remove a DMS account with this command, it will also remove any associated aliases and quota.
The command will also prompt for deleting the account mailbox from disk, or can be forced with the `-y` flag.
## Config Reference
These config files belong to the [Config Volume][docs::volumes::config].
### Accounts
!!! info
**Config file:** `docker-data/dms/config/postfix-accounts.cf`
---
The config format is line-based with two fields separated by the delimiter `|`:
- **User:** The primary email address for the account mailbox to use.
- **Password:** A SHA512-CRYPT hash of the account password (_in this example it is `secret`_).
??? tip "Password hash without the `setup email add` command"
A compatible password hash can be generated with:
```bash
doveadm pw -s SHA512-CRYPT -u hello@example.com -p secret
```
!!! example "`postfix-accounts.cf` config file"
In this example DMS manages mail for the domain `example.com`:
```cf title="postfix-accounts.cf"
hello@example.com|{SHA512-CRYPT}$6$W4rxRQwI6HNMt9n3$riCi5/OqUxnU8eZsOlZwoCnrNgu1gBGPkJc.ER.LhJCu7sOg9i1kBrRIistlBIp938GdBgMlYuoXYUU5A4Qiv0
```
---
**Dovecot "extra fields"**
[Appending a third column will customize "extra fields"][gh-issue::provisioner-file::accounts-extra-fields] when converting account data into a Dovecot UserDB entry.
DMS is not aware of these customizations beyond carrying them over, expect potential for bugs when this feature breaks any assumed conventions used in the scripts (_such as changing the mailbox path or type_).
!!! note
Account creation will normalize the provided email address to lowercase, as DMS does not support multiple case-sensitive address variants.
The email address chosen will also represent the _login username_ credential for mail clients to authenticate with.
### Aliases
!!! info
**Config file:** `docker-data/dms/config/postfix-virtual.cf`
---
The config format is line-based with key value pairs (**alias** --> **target address**), with white-space as a delimiter.
!!! example "`postfix-virtual.cf` config file"
In this example DMS manages mail for the domain `example.com`:
```cf-extra title="postfix-virtual.cf"
# Alias delivers to an existing account:
alias1@example.com hello@example.com
# Alias forwards to an external email address:
alias2@example.com external-account@gmail.com
```
??? warning "Known Issues"
**`setup` CLI prevents an alias and account sharing an address:**
You cannot presently add a new account (`setup email add`) or alias (`setup alias add`) with an address which already exists as an alias or account in DMS.
This [restriction was enforced][gh-issue::bugs::account-alias-overlap] due to [problems it could cause][gh-issue::bugs::account-alias-overlap-problem], although there are [use-cases where you may legitimately require this functionality][gh-issue::feature-request::allow-account-alias-overlap].
For now you must manually edit the `postfix-virtual.cf` file as a workaround. There are no run-time checks outside of the `setup` CLI related to this restriction.
---
**Wildcard catch-all support (`@example.com`):**
While this type of alias without a local-part is supported, you must keep in mind that aliases in Postfix have a higher precedence than a real address associated to a DMS account.
As a result, the wildcard is matched first and will direct mail for that entire domain to the alias target address. To work around this, [you will need an alias for each non-alias address of that domain][gh-issue::bugs::wildcard-catchall].
Additionally, Postfix will read the alias config and choose the alias value that matches the recipient address first. Ensure your more specific aliases for the domain are declared above the wildcard alias in the config file.
---
**Aliasing to another alias or multiple recipients:**
[While aliasing to multiple recipients is possible][gh-discussions::no-support::alias-multiple-targets], DMS does not officially support that.
- You may experience issues when our feature integrations don't expect more than one target per alias.
- These concerns also apply to the usage of nested aliases (_where the recipient target provided is to an alias instead of a real address_). An example is the [incompatibility with `setup alias add`][gh-issue::bugs::alias-nested].
#### Configuring RegEx aliases
!!! info
**Config file:** `docker-data/dms/config/postfix-regexp.cf`
---
This config file is similar to the above `postfix-virtual.cf`, but the alias value is instead configured with a regex pattern.
There is **no `setup` CLI support** for this feature, it is config only.
!!! example "`postfix-regexp.cf` config file"
Deliver all mail for `test` users to `qa@example.com` instead:
```cf-extra title="postfix-regexp.cf"
# Remember to escape regex tokens like `.` => `\.`, otherwise
# your alias pattern may be more permissive than you intended:
/^test[0-9][0-9]*@example\.com/ qa@example.com
```
??? abstract "Technical Details"
`postfix-virtual.cf` has precedence, `postfix-regexp.cf` will only be checked if no alias match was found in `postfix-virtual.cf`.
These files are both copied internally to `/etc/postfix/` and configured in `main.cf` for the `virtual_alias_maps` setting. As `postfix-virtual.cf` is declared first for that setting, it will be processed before using `postfix-regexp.cf` as a fallback.
### Quotas
!!! info
**Config file:** `docker-data/dms/config/dovecot-quotas.cf`
----
The config format is line-based with two fields separated by the delimiter `:`:
- **Dovecot UserDB account:** The user DMS account. It should have a matching field in `postfix-accounts.cf`.
- **Quota limit:** Expressed in bytes (_binary unit suffix is supported: `M` => `MiB`, `G` => `GiB`_).
!!! example "`dovecot-quotas.cf` config file"
For the account with the mailbox address of `hello@example.com`, it may not exceed 5 GiB in storage:
```cf-extra title="dovecot-quotas.cf"
hello@example.com:5G
```
[docs::volumes::config]: ../../advanced/optional-config.md#volumes-config
[gh-issue::provisioner-file::accounts-extra-fields]: https://github.com/docker-mailserver/docker-mailserver/issues/4117
[gh-issue::feature-request::allow-account-alias-overlap]: https://github.com/docker-mailserver/docker-mailserver/issues/3528
[gh-issue::bugs::account-alias-overlap-problem]: https://github.com/docker-mailserver/docker-mailserver/issues/3350#issuecomment-1550528898
[gh-issue::bugs::account-alias-overlap]: https://github.com/docker-mailserver/docker-mailserver/issues/3022#issuecomment-1807816689
[gh-issue::bugs::wildcard-catchall]: https://github.com/docker-mailserver/docker-mailserver/issues/3022#issuecomment-1610452561
[gh-issue::bugs::alias-nested]: https://github.com/docker-mailserver/docker-mailserver/issues/3622#issuecomment-1794504849
[gh-discussions::no-support::alias-multiple-targets]: https://github.com/orgs/docker-mailserver/discussions/3805#discussioncomment-8215417

View file

@ -1,5 +1,5 @@
---
title: 'Advanced | LDAP Authentication'
title: 'Account Management | Provisioner (LDAP)'
---
## Introduction
@ -26,7 +26,7 @@ Those variables contain the LDAP lookup filters for postfix, using `%s` as the p
- Technically, there is no difference between `ALIAS` and `GROUP`, but ideally you should use `ALIAS` for personal aliases for a singular person (like `ceo@example.org`) and `GROUP` for multiple people (like `hr@example.org`).
- ...for outgoing email, the sender address is put through the `SENDERS` filter, and only if the authenticated user is one of the returned entries, the email can be sent.
- This only applies if `SPOOF_PROTECTION=1`.
- If the `SENDERS` filter is missing, the `USER`, `ALIAS` and `GROUP` filters will be used in in a disjunction (OR).
- If the `SENDERS` filter is missing, the `USER`, `ALIAS` and `GROUP` filters will be used in a disjunction (OR).
- To for example allow users from the `admin` group to spoof any sender email address, and to force everyone else to only use their personal mailbox address for outgoing email, you can use something like this: `(|(memberOf=cn=admin,*)(mail=%s))`
???+ example
@ -304,5 +304,5 @@ The changes on the configurations necessary to work with Active Directory (**onl
- NET_ADMIN
```
[docs-environment]: ../environment.md
[docs-userpatches]: ./override-defaults/user-patches.md
[docs-environment]: ../../environment.md
[docs-userpatches]: ../../advanced/override-defaults/user-patches.md

View file

@ -0,0 +1,70 @@
---
title: 'Account Management | Master Accounts (Dovecot)'
hide:
- toc # Hide Table of Contents for this page
---
This feature is useful for administrative tasks like hot backups.
!!! note
This feature is presently [not supported with `ACCOUNT_PROVISIONER=LDAP`][dms::feature::dovecot-master-accounts::caveat-ldap].
!!! info
A _Master Account_:
- Can login as any user (DMS account) and access their mailbox.
- Is not associated to a separate DMS account, nor is it a DMS account itself.
---
**`setup` CLI support**
Use the `setup dovecot-master <add|update|del|list>` commands. These are roughly equivalent to the `setup email` subcommands.
---
**Config file:** `docker-data/dms/config/dovecot-masters.cf`
The config format is the same as [`postfix-accounts.cf` for `ACCOUNT_PROVISIONER=FILE`][docs::account-management::file::accounts].
The only difference is the account field has no `@domain-part` suffix, it is only a username.
??? abstract "Technical Details"
[The _Master Accounts_ feature][dms::feature::dovecot-master-accounts] in DMS configures the [Dovecot Master Users][dovecot-docs::auth::master-users] feature with the Dovecot setting [`auth_master_user_separator`][dovecot-docs::config::auth-master-user-separator] (_where the default value is `*`_).
## Login via Master Account
!!! info
To login as another DMS account (`user@example.com`) with POP3 or IMAP, use the following credentials format:
- Username: `<LOGIN USERNAME>*<MASTER USER>` (`user@example.com*admin`)
- Password: `<MASTER PASSWORD>`
!!! example "Verify login functionality"
In the DMS container, you can verify with the `testsaslauthd` command:
```bash
# Prerequisites:
# A regular DMS account to test login through a Master Account:
setup email add user@example.com secret
# Add a new Master Account:
setup dovecot-master add admin top-secret
```
```bash
# Login with credentials format as described earlier:
testsaslauthd -u 'user@example.com*admin' -p 'top-secret'
```
Alternatively, any mail client should be able to login the equivalent credentials.
[dms::feature::dovecot-master-accounts]: https://github.com/docker-mailserver/docker-mailserver/pull/2535
[dms::feature::dovecot-master-accounts::caveat-ldap]: https://github.com/docker-mailserver/docker-mailserver/pull/2535#issuecomment-1118056745
[dovecot-docs::auth::master-users]: https://doc.dovecot.org/configuration_manual/authentication/master_users/
[dovecot-docs::config::auth-master-user-separator]: https://doc.dovecot.org/settings/core/#core_setting-auth_master_user_separator
[docs::account-management::file::accounts]: ../provisioner/file.md#accounts

View file

@ -0,0 +1,145 @@
---
title: 'Account Management | OAuth2 Support'
hide:
- toc # Hide Table of Contents for this page
---
# Authentication - OAuth2 / OIDC
This feature enables support for delegating DMS account authentication through to an external _Identity Provider_ (IdP).
!!! warning "Receiving mail requires a DMS account to exist"
If you expect DMS to receive mail, you must provision an account into DMS in advance. Otherwise DMS has no awareness of your externally manmaged users and will reject delivery.
There are [plans to implement support to provision users through a SCIM 2.0 API][dms-feature-request::scim-api]. An IdP that can operate as a SCIM Client (eg: Authentik) would then integrate with DMS for user provisioning. Until then you must keep your user accounts in sync manually via your configured [`ACCOUNT_PROVISIONER`][docs::env::account-provisioner].
??? info "How the feature works"
1. A **mail client must have support** to acquire an OAuth2 token from your IdP (_however many clients lack generic OAuth2 / OIDC provider support_).
2. The mail client then provides that token as the user password via the login mechanism `XOAUTH2` or `OAUTHBEARER`.
3. DMS (Dovecot) will then check the validity of that token against the Authentication Service it was configured with.
4. If the response returned is valid for the user account, authentication is successful.
[**XOAUTH2**][google::xoauth2-docs] (_Googles widely adopted implementation_) and **OAUTHBEARER** (_the newer variant standardized by [RFC 7628][rfc::7628] in 2015_) are supported as standards for verifying that a OAuth Bearer Token (_[RFC 6750][rfc::6750] from 2012_) is valid at the identity provider that created the token. The token itself in both cases is expected to be can an opaque _Access Token_, but it is possible to use a JWT _ID Token_ (_which encodes additional information into the token itself_).
A mail client like Thunderbird has limited OAuth2 / OIDC support. The software maintains a hard-coded list of providers supported. Roundcube is a webmail client that does have support for generic providers, allowing you to integrate with a broader range of IdP services.
---
**Documentation for this feature is WIP**
See the [initial feature support][dms-feature::oauth2-pr] and [existing issues][dms-feature::oidc-issues] for guidance that has not yet been documented officially.
??? tip "Verify authentication works"
If you have a compatible mail client you can verify login through that.
---
??? example "CLI - Verify with `curl`"
```bash
# Shell into your DMS container:
docker exec -it dms bash
# Adjust these variables for the methods below to use:
export AUTH_METHOD='OAUTHBEARER' USER_ACCOUNT='hello@example.com' ACCESS_TOKEN='DMS_YWNjZXNzX3Rva2Vu'
# Authenticate via IMAP (Dovecot):
curl --silent --url 'imap://localhost:143' \
--login-options "AUTH=${AUTH_METHOD}" --user "${USER_ACCOUNT}" --oauth2-bearer "${ACCESS_TOKEN}" \
--request 'LOGOUT' \
&& grep "dovecot: imap-login: Login: user=<${USER_ACCOUNT}>, method=${AUTH_METHOD}" /var/log/mail/mail.log
# Authenticate via SMTP (Postfix), sending a mail with the same sender(from) and recipient(to) address:
# NOTE: `curl` seems to require `--upload-file` with some mail content provided to test SMTP auth.
curl --silent --url 'smtp://localhost:587' \
--login-options "AUTH=${AUTH_METHOD}" --user "${USER_ACCOUNT}" --oauth2-bearer "${ACCESS_TOKEN}" \
--mail-from "${USER_ACCOUNT}" --mail-rcpt "${USER_ACCOUNT}" --upload-file - <<< 'RFC 5322 content - not important' \
&& grep "postfix/submission/smtpd.*, sasl_method=${AUTH_METHOD}, sasl_username=${USER_ACCOUNT}" /var/log/mail/mail.log
```
---
**Troubleshooting:**
- Add `--verbose` to the curl options. This will output the protocol exchange which includes if authentication was successful or failed.
- The above example chains the `curl` commands with `grep` on DMS logs (_for Dovecot and Postfix services_). When not running `curl` from the DMS container, ensure you check the logs correctly, or inspect the `--verbose` output instead.
!!! warning "`curl` bug with `XOAUTH2`"
[Older releases of `curl` have a bug with `XOAUTH2` support][gh-issue::curl::xoauth2-bug] since `7.80.0` (Nov 2021) but fixed from `8.6.0` (Jan 2024). It treats `XOAUTH2` as `OAUTHBEARER`.
If you use `docker exec` to run `curl` from within DMS, the current DMS v14 release (_Debian 12 with curl `7.88.1`_) is affected by this bug.
## Config Examples
### Authentik with Roundcube
This example assumes you have already set up:
- A working DMS server
- An Authentik server ([documentation][authentik::docs::install])
- A Roundcube server ([docker image][roundcube::dockerhub-image] or [bare metal install][roundcube::docs::install])
!!! example "Setup Instructions"
=== "1. Docker Mailserver"
Update your Docker Compose ENV config to include:
```env title="compose.yaml"
services:
mailserver:
env:
# Enable the feature:
- ENABLE_OAUTH2=1
# Specify the user info endpoint URL of the oauth2 server for token inspection:
- OAUTH2_INTROSPECTION_URL=https://authentik.example.com/application/o/userinfo/
```
=== "2. Authentik"
1. Create a new OAuth2 provider.
2. Note the client id and client secret. Roundcube will need this.
3. Set the allowed redirect url to the equivalent of `https://roundcube.example.com/index.php/login/oauth` for your RoundCube instance.
=== "3. Roundcube"
Add the following to `oauth2.inc.php` ([documentation][roundcube::docs::config]):
```php
$config['oauth_provider'] = 'generic';
$config['oauth_provider_name'] = 'Authentik';
$config['oauth_client_id'] = '<insert client id here>';
$config['oauth_client_secret'] = '<insert client secret here>';
$config['oauth_auth_uri'] = 'https://authentik.example.com/application/o/authorize/';
$config['oauth_token_uri'] = 'https://authentik.example.com/application/o/token/';
$config['oauth_identity_uri'] = 'https://authentik.example.com/application/o/userinfo/';
// Optional: disable SSL certificate check on HTTP requests to OAuth server. For possible values, see:
// http://docs.guzzlephp.org/en/stable/request-options.html#verify
$config['oauth_verify_peer'] = false;
$config['oauth_scope'] = 'email openid profile';
$config['oauth_identity_fields'] = ['email'];
// Boolean: automatically redirect to OAuth login when opening Roundcube without a valid session
$config['oauth_login_redirect'] = false;
```
[dms-feature::oauth2-pr]: https://github.com/docker-mailserver/docker-mailserver/pull/3480
[dms-feature::oidc-issues]: https://github.com/docker-mailserver/docker-mailserver/issues?q=label%3Afeature%2Fauth-oidc
[docs::env::account-provisioner]: ../../environment.md#account_provisioner
[dms-feature-request::scim-api]: https://github.com/docker-mailserver/docker-mailserver/issues/4090
[google::xoauth2-docs]: https://developers.google.com/gmail/imap/xoauth2-protocol#the_sasl_xoauth2_mechanism
[rfc::6750]: https://datatracker.ietf.org/doc/html/rfc6750
[rfc::7628]: https://datatracker.ietf.org/doc/html/rfc7628
[gh-issue::curl::xoauth2-bug]: https://github.com/curl/curl/issues/10259#issuecomment-1907192556
[authentik::docs::install]: https://goauthentik.io/docs/installation/
[roundcube::dockerhub-image]: https://hub.docker.com/r/roundcube/roundcubemail
[roundcube::docs::install]: https://github.com/roundcube/roundcubemail/wiki/Installation
[roundcube::docs::config]: https://github.com/roundcube/roundcubemail/wiki/Configuration

View file

@ -1,69 +0,0 @@
---
title: 'Advanced | Basic OAuth2 Authentication'
---
## Introduction
!!! warning "This is only a supplement to the existing account provisioners"
Accounts must still be managed via the configured [`ACCOUNT_PROVISIONER`][env::account-provisioner] (FILE or LDAP).
Reasoning for this can be found in [#3480][gh-pr::oauth2]. Future iterations on this feature may allow it to become a full account provisioner.
[gh-pr::oauth2]: https://github.com/docker-mailserver/docker-mailserver/pull/3480
[env::account-provisioner]: ../environment.md#account_provisioner
The present OAuth2 support provides the capability for 3rd-party applications such as Roundcube to authenticate with DMS (dovecot) by using a token obtained from an OAuth2 provider, instead of passing passwords around.
## Example (Authentik & Roundcube)
This example assumes you have:
- A working DMS server set up
- An Authentik server set up ([documentation](https://goauthentik.io/docs/installation/))
- A Roundcube server set up (either [docker](https://hub.docker.com/r/roundcube/roundcubemail/) or [bare metal](https://github.com/roundcube/roundcubemail/wiki/Installation))
!!! example "Setup Instructions"
=== "1. Docker Mailserver"
Edit the following values in `mailserver.env`:
```env
# -----------------------------------------------
# --- OAUTH2 Section ----------------------------
# -----------------------------------------------
# empty => OAUTH2 authentication is disabled
# 1 => OAUTH2 authentication is enabled
ENABLE_OAUTH2=1
# Specify the user info endpoint URL of the oauth2 provider
OAUTH2_INTROSPECTION_URL=https://authentik.example.com/application/o/userinfo/
```
=== "2. Authentik"
1. Create a new OAuth2 provider
2. Note the client id and client secret
3. Set the allowed redirect url to the equivalent of `https://roundcube.example.com/index.php/login/oauth` for your RoundCube instance.
=== "3. Roundcube"
Add the following to `oauth2.inc.php` ([documentation](https://github.com/roundcube/roundcubemail/wiki/Configuration)):
```php
$config['oauth_provider'] = 'generic';
$config['oauth_provider_name'] = 'Authentik';
$config['oauth_client_id'] = '<insert client id here>';
$config['oauth_client_secret'] = '<insert client secret here>';
$config['oauth_auth_uri'] = 'https://authentik.example.com/application/o/authorize/';
$config['oauth_token_uri'] = 'https://authentik.example.com/application/o/token/';
$config['oauth_identity_uri'] = 'https://authentik.example.com/application/o/userinfo/';
// Optional: disable SSL certificate check on HTTP requests to OAuth server. For possible values, see:
// http://docs.guzzlephp.org/en/stable/request-options.html#verify
$config['oauth_verify_peer'] = false;
$config['oauth_scope'] = 'email openid profile';
$config['oauth_identity_fields'] = ['email'];
// Boolean: automatically redirect to OAuth login when opening Roundcube without a valid session
$config['oauth_login_redirect'] = false;
```

View file

@ -1,21 +0,0 @@
---
title: 'Advanced | Dovecot master accounts'
---
## Introduction
A dovecot master account is able to login as any configured user. This is useful for administrative tasks like hot backups.
## Configuration
It is possible to create, update, delete and list dovecot master accounts using `setup.sh`. See `setup.sh help` for usage.
This feature is presently [not supported with LDAP](https://github.com/docker-mailserver/docker-mailserver/pull/2535).
## Logging in
Once a master account is configured, it is possible to connect to any users mailbox using this account. Log in over POP3/IMAP using the following credential scheme:
Username: `<EMAIL ADDRESS>*<MASTER ACCOUNT NAME>`
Password: `<MASTER ACCOUNT PASSWORD>`

View file

@ -16,7 +16,7 @@ The [dovecot-fts-xapian](https://github.com/grosjo/fts-xapian) plugin makes use
The indexes will be stored as a subfolder named `xapian-indexes` inside your local `mail-data` folder (_`/var/mail` internally_). With the default settings, 10GB of email data may generate around 4GB of indexed data.
While indexing is memory intensive, you can configure the plugin to limit the amount of memory consumed by the index workers. With Xapian being small and fast, this plugin is a good choice for low memory environments (2GB) as compared to Solr.
While indexing is memory intensive, you can configure the plugin to limit the amount of memory consumed by the index workers. With Xapian being small and fast, this plugin is a good choice for low memory environments (2GB).
#### Setup
@ -137,55 +137,11 @@ While indexing is memory intensive, you can configure the plugin to limit the am
- ./docker-data/dms/cron/fts_xapian:/etc/cron.d/fts_xapian
```
### Solr
The [dovecot-solr Plugin](https://wiki2.dovecot.org/Plugins/FTS/Solr) is used in conjunction with [Apache Solr](https://lucene.apache.org/solr/) running in a separate container. This is quite straightforward to setup using the following instructions.
Solr is a mature and fast indexing backend that runs on the JVM. The indexes are relatively compact compared to the size of your total email.
However, Solr also requires a fair bit of RAM. While Solr is [highly tuneable](https://solr.apache.org/guide/7_0/query-settings-in-solrconfig.html), it may require a bit of testing to get it right.
#### Setup
1. `compose.yaml`:
```yaml
solr:
image: lmmdock/dovecot-solr:latest
volumes:
- ./docker-data/dms/config/dovecot/solr-dovecot:/opt/solr/server/solr/dovecot
restart: always
mailserver:
depends_on:
- solr
image: ghcr.io/docker-mailserver/docker-mailserver:latest
...
volumes:
...
- ./docker-data/dms/config/dovecot/10-plugin.conf:/etc/dovecot/conf.d/10-plugin.conf:ro
...
```
2. `./docker-data/dms/config/dovecot/10-plugin.conf`:
```conf
mail_plugins = $mail_plugins fts fts_solr
plugin {
fts = solr
fts_autoindex = yes
fts_solr = url=http://solr:8983/solr/dovecot/
}
```
3. Recreate containers: `docker compose down ; docker compose up -d`
4. Flag all user mailbox FTS indexes as invalid, so they are rescanned on demand when they are next searched: `docker compose exec mailserver doveadm fts rescan -A`
#### Further Discussion
See [#905](https://github.com/docker-mailserver/docker-mailserver/issues/905)
Attempting to enable commented out features in the config example above [may not be functional][gh::xapian-decode2text].
[docs-faq-sa-learn-cron]: ../../faq.md#how-can-i-make-spamassassin-better-recognize-spam
[gh::xapian-decode2text]: https://github.com/orgs/docker-mailserver/discussions/4461#discussioncomment-13002388

View file

@ -177,6 +177,12 @@ docker run --rm -d --network dms-ipv6 -p 80:80 traefik/whoami
curl --max-time 5 http://[2001:db8::1]:80
```
!!! warning "IPv6 gateway IP"
If instead of the remote IPv6 address, you may notice the gateway IP for the IPv6 subnet your DMS container belongs to.
This will happen when DMS has an IPv6 IP address assigned, for the same reason as with IPv4, `userland-proxy: true`. It indicates that your `daemon.json` has not been configured correctly or had the updated config applied for `ip6tables :true` + `experimental: true`. Make sure you used `systemctl restart docker` after updating `daemon.json`.
!!! info "IPv6 ULA address priority"
DNS lookups that have records for both IPv4 and IPv6 addresses (_eg: `localhost`_) may prefer IPv4 over IPv6 (ULA) for private addresses, whereas for public addresses IPv6 has priority. This shouldn't be anything to worry about, but can come across as a surprise when testing your IPv6 setup on the same host instead of from a remote client.

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
title: 'Advanced | Email Gathering with Fetchmail'
---
To enable the [fetchmail][fetchmail-website] service to retrieve e-mails set the environment variable `ENABLE_FETCHMAIL` to `1`. Your `compose.yaml` file should look like following snippet:
To enable the [fetchmail][fetchmail-website] service to retrieve e-mails, set the environment variable `ENABLE_FETCHMAIL` to `1`. Your `compose.yaml` file should look like following snippet:
```yaml
environment:
@ -18,108 +18,135 @@ Generate a file called `fetchmail.cf` and place it in the `docker-data/dms/confi
│   ├── fetchmail.cf
│   ├── postfix-accounts.cf
│   └── postfix-virtual.cf
├── compose.yaml
└── README.md
└── compose.yaml
```
## Configuration
A detailed description of the configuration options can be found in the [online version of the manual page][fetchmail-docs].
Configuration options for `fetchmail.cf` are covered at the [official fetchmail docs][fetchmail-docs-config] (_see the section "The run control file" and the table with "keyword" column for all settings_).
### IMAP Configuration
!!! example "Basic `fetchmail.cf` configuration"
!!! example
Retrieve mail from `remote-user@somewhere.com` and deliver it to `dms-user@example.com`:
```fetchmailrc
poll 'imap.gmail.com' proto imap
user 'username'
pass 'secret'
is 'user1@example.com'
ssl
poll 'mail.somewhere.com'
proto imap
user 'remote-user'
pass 'secret'
is 'dms-user@example.com'
```
### POP3 Configuration
- `poll` sets the remote mail server to connect to retrieve mail from.
- `proto` lets you connect via IMAP or POP3.
- `user` and `pass` provide the login credentials for the remote mail service account to access.
- `is` configures where the fetched mail will be sent to (_eg: your local DMS account in `docker-data/dms/config/postfix-accounts.cf`_).
!!! example
---
```fetchmailrc
poll 'pop3.gmail.com' proto pop3
user 'username'
pass 'secret'
is 'user2@example.com'
ssl
??? warning "`proto imap` will still delete remote mail once fetched"
This is due to a separate default setting `no keep`. Adding the setting `keep` to your config on a new line will prevent deleting the remote copy.
??? example "Multiple users or remote servers"
The official docs [config examples][fetchmail-config-examples] show a common convention to indent settings on subsequent lines for visually grouping per server.
=== "Minimal syntax"
```fetchmailrc
poll 'mail.somewhere.com' proto imap
user 'john.doe' pass 'secret' is 'johnny@example.com'
user 'jane.doe' pass 'secret' is 'jane@example.com'
poll 'mail.somewhere-else.com' proto pop3
user 'john.doe@somewhere-else.com' pass 'secret' is 'johnny@example.com'
```
=== "With optional syntax"
- `#` for adding comments.
- The config file may include "noise" keywords to improve readability.
```fetchmailrc
# Retrieve mail for users `john.doe` and `jane.doe` via IMAP at this remote mail server:
poll 'mail.somewhere.com' with proto imap wants:
user 'john.doe' with pass 'secret', is 'johnny@example.com' here
user 'jane.doe' with pass 'secret', is 'jane@example.com' here
# Also retrieve mail from this mail server (but via POP3).
# NOTE: This could also be all on a single line, or with each key + value as a separate line.
# Notice how the remote username includes a full email address,
# Some mail servers like DMS use the full email address as the username:
poll 'mail.somewhere-else.com' with proto pop3 wants:
user 'john.doe@somewhere-else.com' with pass 'secret', is 'johnny@example.com' here
```
!!! tip "`FETCHMAIL_POLL` ENV: Override default polling interval"
By default the fetchmail service will check every 5 minutes for new mail at the configured mail accounts.
```yaml
environment:
# The fetchmail polling interval in seconds:
FETCHMAIL_POLL: 60
```
!!! caution
Dont forget the last line! (_eg: `is 'user1@example.com'`_). After `is`, you have to specify an email address from the configuration file: `docker-data/dms/config/postfix-accounts.cf`.
More details how to configure fetchmail can be found in the [fetchmail man page in the chapter “The run control file”][fetchmail-docs-run].
### Polling Interval
By default the fetchmail service searches every 5 minutes for new mails on your external mail accounts. You can override this default value by changing the ENV variable `FETCHMAIL_POLL`:
```yaml
environment:
- FETCHMAIL_POLL=60
```
You must specify a numeric argument which is a polling interval in seconds. The example above polls every minute for new mails.
## Debugging
To debug your `fetchmail.cf` configuration run this command:
To debug your `fetchmail.cf` configuration run this `setup debug` command:
```sh
./setup.sh debug fetchmail
docker exec -it dms-container-name setup debug fetchmail
```
For more information about the configuration script `setup.sh` [read the corresponding docs][docs-setup].
??? example "Sample output of `setup debug fetchmail`"
Here a sample output of `./setup.sh debug fetchmail`:
```log
fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:09 2016: poll started
Trying to connect to 132.245.48.18/995...connected.
fetchmail: Server certificate:
fetchmail: Issuer Organization: Microsoft Corporation
fetchmail: Issuer CommonName: Microsoft IT SSL SHA2
fetchmail: Subject CommonName: outlook.com
fetchmail: Subject Alternative Name: outlook.com
fetchmail: Subject Alternative Name: *.outlook.com
fetchmail: Subject Alternative Name: office365.com
fetchmail: Subject Alternative Name: *.office365.com
fetchmail: Subject Alternative Name: *.live.com
fetchmail: Subject Alternative Name: *.internal.outlook.com
fetchmail: Subject Alternative Name: *.outlook.office365.com
fetchmail: Subject Alternative Name: outlook.office.com
fetchmail: Subject Alternative Name: attachment.outlook.office.net
fetchmail: Subject Alternative Name: attachment.outlook.officeppe.net
fetchmail: Subject Alternative Name: *.office.com
fetchmail: outlook.office365.com key fingerprint: 3A:A4:58:42:56:CD:BD:11:19:5B:CF:1E:85:16:8E:4D
fetchmail: POP3< +OK The Microsoft Exchange POP3 service is ready. [SABFADEAUABSADAAMQBDAEEAMAAwADAANwAuAGUAdQByAHAAcgBkADAAMQAuAHAAcgBvAGQALgBlAHgAYwBoAGEAbgBnAGUAbABhAGIAcwAuAGMAbwBtAA==]
fetchmail: POP3> CAPA
fetchmail: POP3< +OK
fetchmail: POP3< TOP
fetchmail: POP3< UIDL
fetchmail: POP3< SASL PLAIN
fetchmail: POP3< USER
fetchmail: POP3< .
fetchmail: POP3> USER user1@outlook.com
fetchmail: POP3< +OK
fetchmail: POP3> PASS *
fetchmail: POP3< +OK User successfully logged on.
fetchmail: POP3> STAT
fetchmail: POP3< +OK 0 0
fetchmail: No mail for user1@outlook.com at outlook.office365.com
fetchmail: POP3> QUIT
fetchmail: POP3< +OK Microsoft Exchange Server 2016 POP3 server signing off.
fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:11 2016: poll completed
fetchmail: normal termination, status 1
```
```log
fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:09 2016: poll started
Trying to connect to 132.245.48.18/995...connected.
fetchmail: Server certificate:
fetchmail: Issuer Organization: Microsoft Corporation
fetchmail: Issuer CommonName: Microsoft IT SSL SHA2
fetchmail: Subject CommonName: outlook.com
fetchmail: Subject Alternative Name: outlook.com
fetchmail: Subject Alternative Name: *.outlook.com
fetchmail: Subject Alternative Name: office365.com
fetchmail: Subject Alternative Name: *.office365.com
fetchmail: Subject Alternative Name: *.live.com
fetchmail: Subject Alternative Name: *.internal.outlook.com
fetchmail: Subject Alternative Name: *.outlook.office365.com
fetchmail: Subject Alternative Name: outlook.office.com
fetchmail: Subject Alternative Name: attachment.outlook.office.net
fetchmail: Subject Alternative Name: attachment.outlook.officeppe.net
fetchmail: Subject Alternative Name: *.office.com
fetchmail: outlook.office365.com key fingerprint: 3A:A4:58:42:56:CD:BD:11:19:5B:CF:1E:85:16:8E:4D
fetchmail: POP3< +OK The Microsoft Exchange POP3 service is ready. [SABFADEAUABSADAAMQBDAEEAMAAwADAANwAuAGUAdQByAHAAcgBkADAAMQAuAHAAcgBvAGQALgBlAHgAYwBoAGEAbgBnAGUAbABhAGIAcwAuAGMAbwBtAA==]
fetchmail: POP3> CAPA
fetchmail: POP3< +OK
fetchmail: POP3< TOP
fetchmail: POP3< UIDL
fetchmail: POP3< SASL PLAIN
fetchmail: POP3< USER
fetchmail: POP3< .
fetchmail: POP3> USER user1@outlook.com
fetchmail: POP3< +OK
fetchmail: POP3> PASS *
fetchmail: POP3< +OK User successfully logged on.
fetchmail: POP3> STAT
fetchmail: POP3< +OK 0 0
fetchmail: No mail for user1@outlook.com at outlook.office365.com
fetchmail: POP3> QUIT
fetchmail: POP3< +OK Microsoft Exchange Server 2016 POP3 server signing off.
fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:11 2016: poll completed
fetchmail: normal termination, status 1
```
!!! tip "Troubleshoot with this reference `compose.yaml`"
[A minimal `compose.yaml` example][fetchmail-compose-example] demonstrates how to run two instances of DMS locally, with one instance configured with `fetchmail.cf` and the other to simulate a remote mail server to fetch from.
[docs-setup]: ../../config/setup.sh.md
[fetchmail-website]: https://www.fetchmail.info
[fetchmail-docs]: https://www.fetchmail.info/fetchmail-man.html
[fetchmail-docs-run]: https://www.fetchmail.info/fetchmail-man.html#31
[fetchmail-docs-config]: https://www.fetchmail.info/fetchmail-man.html#the-run-control-file
[fetchmail-config-examples]: https://www.fetchmail.info/fetchmail-man.html#configuration-examples
[fetchmail-compose-example]: https://github.com/orgs/docker-mailserver/discussions/3994#discussioncomment-9290570

View file

@ -2,29 +2,46 @@
title: 'Mail Forwarding | AWS SES'
---
[Amazon SES (Simple Email Service)](https://aws.amazon.com/ses/) is intended to provide a simple way for cloud based applications to send email and receive email. For the purposes of this project only sending email via SES is supported. Older versions of docker-mailserver used `AWS_SES_HOST` and `AWS_SES_USERPASS` to configure sending, this has changed and the setup is managed through [Configure Relay Hosts][docs-relay].
[Amazon SES (Simple Email Service)][aws-ses] provides a simple way for cloud based applications to send and receive email.
You will need to create some [Amazon SES SMTP credentials](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/smtp-credentials.html). The SMTP credentials you create will be used to populate the `RELAY_USER` and `RELAY_PASSWORD` environment variables.
!!! example "Configuration via ENV"
The `RELAY_HOST` should match your [AWS SES region](https://docs.aws.amazon.com/general/latest/gr/ses.html), the `RELAY_PORT` will be 587.
[Configure a relay host in DMS][docs::relay] to forward all your mail through AWS SES:
If all of your email is being forwarded through AWS SES, `DEFAULT_RELAY_HOST` should be set accordingly.
- `RELAY_HOST` should match your [AWS SES region][aws-ses::region].
- `RELAY_PORT` should be set to [one of the supported AWS SES SMTP ports][aws-ses::smtp-ports] (_eg: 587 for STARTTLS_).
- `RELAY_USER` and `RELAY_PASSWORD` should be set to your [Amazon SES SMTP credentials][aws-ses::credentials].
Example:
```
DEFAULT_RELAY_HOST=[email-smtp.us-west-2.amazonaws.com]:587
```
```env
RELAY_HOST=email-smtp.us-west-2.amazonaws.com
RELAY_PORT=587
# Alternative to RELAY_HOST + RELAY_PORT which is compatible with LDAP:
DEFAULT_RELAY_HOST=[email-smtp.us-west-2.amazonaws.com]:587
!!! note
If you set up [AWS Easy DKIM](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-email-authentication-dkim-easy.html) you can safely skip setting up DKIM as the AWS SES will take care of signing your outgoing email.
RELAY_USER=aws-user
RELAY_PASSWORD=secret
```
To verify proper operation, send an email to some external account of yours and inspect the mail headers. You will also see the connection to SES in the mail logs. For example:
!!! tip
```log
May 23 07:09:36 mail postfix/smtp[692]: Trusted TLS connection established to email-smtp.us-east-1.amazonaws.com[107.20.142.169]:25:
TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
May 23 07:09:36 mail postfix/smtp[692]: 8C82A7E7: to=<someone@example.com>, relay=email-smtp.us-east-1.amazonaws.com[107.20.142.169]:25,
delay=0.35, delays=0/0.02/0.13/0.2, dsn=2.0.0, status=sent (250 Ok 01000154dc729264-93fdd7ea-f039-43d6-91ed-653e8547867c-000000)
```
If you have set up [AWS Easy DKIM][aws-ses::easy-dkim], you can safely skip setting up DKIM as AWS SES will take care of signing your outbound mail.
[docs-relay]: ./relay-hosts.md
!!! note "Verify the relay host is configured correctly"
To verify proper operation, send an email to some external account of yours and inspect the mail headers.
You will also see the connection to SES in the mail logs:
```log
postfix/smtp[692]: Trusted TLS connection established to email-smtp.us-west-1.amazonaws.com[107.20.142.169]:25:
TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
postfix/smtp[692]: 8C82A7E7: to=<someone@example.com>, relay=email-smtp.us-west-1.amazonaws.com[107.20.142.169]:25,
delay=0.35, delays=0/0.02/0.13/0.2, dsn=2.0.0, status=sent (250 Ok 01000154dc729264-93fdd7ea-f039-43d6-91ed-653e8547867c-000000)
```
[docs::relay]: ./relay-hosts.md
[aws-ses]: https://aws.amazon.com/ses/
[aws-ses::credentials]: https://docs.aws.amazon.com/ses/latest/dg/smtp-credentials.html
[aws-ses::smtp-ports]: https://docs.aws.amazon.com/ses/latest/dg/smtp-connect.html
[aws-ses::region]: https://docs.aws.amazon.com/general/latest/gr/ses.html
[aws-ses::easy-dkim]: https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-email-authentication-dkim-easy.html

View file

@ -0,0 +1,50 @@
---
title: 'Mail Forwarding | Configure Gmail as a relay host'
---
This page provides a guide for configuring DMS to use [GMAIL as an SMTP relay host][gmail-smtp].
!!! example "Configuration via ENV"
[Configure a relay host in DMS][docs::relay]. This example shows how the related ENV settings map to the Gmail service config:
- `RELAY_HOST` should be configured as [advised by Gmail][gmail-smtp::relay-host], there are two SMTP endpoints to choose:
- `smtp.gmail.com` (_for a personal Gmail account_)
- `smtp-relay.gmail.com` (_when using Google Workspace_)
- `RELAY_PORT` should be set to [one of the supported Gmail SMTP ports][gmail-smtp::relay-port] (_eg: 587 for STARTTLS_).
- `RELAY_USER` should be your gmail address (`user@gmail.com`).
- `RELAY_PASSWORD` should be your [App Password][gmail-smtp::app-password], **not** your personal gmail account password.
```env
RELAY_HOST=smtp.gmail.com
RELAY_PORT=587
# Alternative to RELAY_HOST + RELAY_PORT which is compatible with LDAP:
DEFAULT_RELAY_HOST=[smtp.gmail.com]:587
RELAY_USER=username@gmail.com
RELAY_PASSWORD=secret
```
!!! tip
- As per our main [relay host docs page][docs::relay], you may prefer to configure your credentials via `setup relay add-auth` instead of the `RELAY_USER` + `RELAY_PASSWORD` ENV.
- If you configure for `smtp-relay.gmail.com`, the `DEFAULT_RELAY_HOST` ENV should be all you need as shown in the above example. Credentials can be optional when using Google Workspace (`smtp-relay.gmail.com`), which supports restricting connections to trusted IP addresses.
!!! note "Verify the relay host is configured correctly"
To verify proper operation, send an email to an external account of yours and inspect the mail headers.
You will also see the connection to the Gmail relay host (`smtp.gmail.com`) in the mail logs:
```log
postfix/smtp[910]: Trusted TLS connection established to smtp.gmail.com[64.233.188.109]:587:
TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
postfix/smtp[910]: 4BCB547D9D: to=<username@gmail.com>, relay=smtp.gmail.com[64.233.188.109]:587,
delay=2.9, delays=0.01/0.02/1.7/1.2, dsn=2.0.0, status=sent (250 2.0.0 OK 17... - gsmtp)
```
[docs::relay]: ./relay-hosts.md
[gmail-smtp]: https://support.google.com/a/answer/2956491
[gmail-smtp::relay-host]: https://support.google.com/a/answer/176600
[gmail-smtp::relay-port]: https://support.google.com/a/answer/2956491
[gmail-smtp::app-password]: https://support.google.com/accounts/answer/185833

View file

@ -2,82 +2,155 @@
title: 'Mail Forwarding | Relay Hosts'
---
## Introduction
## What is a Relay Host?
Rather than having Postfix deliver mail directly, you can configure Postfix to send mail via another mail relay (smarthost). Examples include [Mailgun](https://www.mailgun.com/), [Sendgrid](https://sendgrid.com/) and [AWS SES](https://aws.amazon.com/ses/).
An SMTP relay service (_aka relay host / [smarthost][wikipedia::smarthost]_) is an MTA that relays (_forwards_) mail on behalf of third-parties (_it does not manage the mail domains_).
Depending on the domain of the sender, you may want to send via a different relay, or authenticate in a different way.
- Instead of DMS handling SMTP delivery directly itself (_via Postfix_), it can be configured to delegate delivery by sending all outbound mail through a relay service.
- Examples of popular mail relay services: [AWS SES][smarthost::aws-ses], [Mailgun][smarthost::mailgun], [Mailjet][smarthost::mailjet], [SendGrid][smarthost::sendgrid]
## Basic Configuration
!!! info "When can a relay service can be helpful?"
Basic configuration is done via environment variables:
- Your network provider has blocked outbound connections on port 25 (_required for direct delivery_).
- To improve delivery success via better established reputation (trust) of a relay service.
- `RELAY_HOST`: _default host to relay mail through, `empty` (aka '', or no ENV set) will disable this feature_
- `RELAY_PORT`: _port on default relay, defaults to port 25_
- `RELAY_USER`: _username for the default relay_
- `RELAY_PASSWORD`: _password for the default user_
## Configuration
Setting these environment variables will cause mail for all sender domains to be routed via the specified host, authenticating with the user/password combination.
All mail sent outbound from DMS (_where the sender address is a DMS account or a virtual alias_) will be relayed through the configured relay host.
!!! warning
For users of the previous `AWS_SES_*` variables: please update your configuration to use these new variables, no other configuration is required.
!!! info "Configuration via ENV"
## Advanced Configuration
Configure the default relayhost with either of these ENV:
### Sender-dependent Authentication
- Preferable (_LDAP compatible_): `DEFAULT_RELAY_HOST` (eg: `[mail.relay-service.com]:25`)
- `RELAY_HOST` (eg: `mail.relay-service.com`) + `RELAY_PORT` (default: 25)
Sender dependent authentication is done in `docker-data/dms/config/postfix-sasl-password.cf`. You can create this file manually, or use:
Most relay services also require authentication configured:
```sh
setup.sh relay add-auth <domain> <username> [<password>]
```
- `RELAY_USER` + `RELAY_PASSWORD` provides credentials for authenticating with the default relayhost.
An example configuration file looks like this:
!!! warning "Providing secrets via ENV"
```txt
@domain1.com relay_user_1:password_1
@domain2.com relay_user_2:password_2
```
While ENV is convenient, the risk of exposing secrets is higher.
If there is no other configuration, this will cause Postfix to deliver email through the relay specified in `RELAY_HOST` env variable, authenticating as `relay_user_1` when sent from `domain1.com` and authenticating as `relay_user_2` when sending from `domain2.com`.
`setup relay add-auth` is a better alternative, which manages the credentials via a config file.
!!! note
To activate the configuration you must either restart the container, or you can also trigger an update by modifying a mail account.
??? tip "Excluding specific sender domains from relay"
### Sender-dependent Relay Host
You can opt-out with: `setup relay exclude-domain <domain>`
Sender dependent relay hosts are configured in `docker-data/dms/config/postfix-relaymap.cf`. You can create this file manually, or use:
Outbound mail from senders of that domain will be sent normally (_instead of through the configured `RELAY_HOST`_).
```sh
setup.sh relay add-domain <domain> <host> [<port>]
```
!!! warning "When any relay host credentials are configured"
An example configuration file looks like this:
It will still be expected that mail is sent over a secure connection with credentials provided.
```txt
@domain1.com [relay1.org]:587
@domain2.com [relay2.org]:2525
```
Thus this opt-out feature is rarely practical.
Combined with the previous configuration in `docker-data/dms/config/postfix-sasl-password.cf`, this will cause Postfix to deliver mail sent from `domain1.com` via `relay1.org:587`, authenticating as `relay_user_1`, and mail sent from `domain2.com` via `relay2.org:2525` authenticating as `relay_user_2`.
### Advanced Configuration
!!! note
You still have to define `RELAY_HOST` to activate the feature
When mail is sent, there is support to change the relay service or the credentials configured based on the sender address domain used.
### Excluding Sender Domains
We provide this support via two config files:
If you want mail sent from some domains to be delivered directly, you can exclude them from being delivered via the default relay by adding them to `docker-data/dms/config/postfix-relaymap.cf` with no destination. You can also do this via:
- Sender-dependent Relay Host: `docker-data/dms/config/postfix-relaymap.cf`
- Sender-dependent Authentication: `docker-data/dms/config/postfix-sasl-password.cf`
```sh
setup.sh relay exclude-domain <domain>
```
!!! tip "Configure with our `setup relay` commands"
Extending the configuration file from above:
While you can edit those configs directly, DMS provides these helpful config management commands:
```txt
@domain1.com [relay1.org]:587
@domain2.com [relay2.org]:2525
@domain3.com
```
```cli-syntax
# Configure a sender domain to use a specific relay host:
setup relay add-domain <domain> <host> [<port>]
This will cause email sent from `domain3.com` to be delivered directly.
# Configure relay host credentials for a sender domain to use:
setup relay add-auth <domain> <username> [<password>]
# Optionally avoid relaying from senders of this domain:
# NOTE: Only supported when configured with the `RELAY_HOST` ENV!
setup relay exclude-domain <domain>
```
!!! example "Config file: `postfix-sasl-password.cf`"
```cf-extra title="docker-data/dms/config/postfix-sasl-password.cf"
@domain1.com mailgun-user:secret
@domain2.com sendgrid-user:secret
# NOTE: This must have an exact match with the relay host in `postfix-relaymap.cf`,
# `/etc/postfix/relayhost_map`, or the `DEFAULT_RELAY_HOST` ENV.
# NOTE: Not supported via our setup CLI, but valid config for Postfix.
[email-smtp.us-west-2.amazonaws.com]:2587 aws-user:secret
```
When Postfix needs to lookup credentials for mail sent outbound, the above config will:
- Authenticate as `mailgun-user` for mail sent with a sender belonging to `@domain1.com`
- Authenticate as `sendgrid-user` for mail sent with a sender belonging to `@domain2.com`
- Authenticate as `aws-user` for mail sent through a configured AWS SES relay host (any sender domain).
!!! example "Config file: `postfix-relaymap.cf`"
```cf-extra title="docker-data/dms/config/postfix-relaymap.cf"
@domain1.com [smtp.mailgun.org]:587
@domain2.com [smtp.sendgrid.net]:2525
# Opt-out of relaying:
@domain3.com
```
When Postfix sends mail outbound from these sender domains, the above config will:
- Relay mail through `[smtp.mailgun.org]:587` when mail is sent from a sender of `@domain1.com`
- Relay mail through `[smtp.sendgrid.net]:2525` when mail is sent from a sender of `@domain1.com`
- Mail with a sender from `@domain3.com` is not sent through a relay (_**Only applicable** when using `RELAY_HOST`_)
### Technical Details
- Both the supported ENV and config files for this feature have additional details covered in our ENV docs [Relay Host section][docs::env-relay].
- For troubleshooting, a [minimal `compose.yaml` config with several DMS instances][dms-gh::relay-example] demonstrates this feature for local testing.
- [Subscribe to this tracking issue][dms-gh::pr-3607] for future improvements intended for this feature.
!!! abstract "Postfix Settings"
Internally this feature is implemented in DMS by [`relay.sh`][dms-repo::helpers-relay].
The `relay.sh` script manages configuring these Postfix settings:
```cf-extra
# Send all outbound mail through this relay service:
relayhost = [smtp.relay-service.com]:587
# Credentials to use:
smtp_sasl_password_maps = texthash:/etc/postfix/sasl_passwd
# Alternative table type examples which do not require a separate file:
#smtp_sasl_password_maps = static:john.doe@relay-service.com:secret
#smtp_sasl_password_maps = inline:{ [smtp.relay-service.com]:587=john.doe@relay-service.com:secret }
## Authentication support:
# Required to provide credentials to the relay service:
smtp_sasl_auth_enable = yes
# Enforces requiring credentials when sending mail outbound:
smtp_sasl_security_options = noanonymous
# Enforces a secure connection (TLS required) to the relay service:
smtp_tls_security_level = encrypt
## Support for advanced requirements:
# Relay service(s) to use instead of direct delivery for specific sender domains:
sender_dependent_relayhost_maps = texthash:/etc/postfix/relayhost_map
# Support credentials to a relay service(s) that vary by relay host used or sender domain:
smtp_sender_dependent_authentication = yes
```
[smarthost::mailgun]: https://www.mailgun.com/
[smarthost::mailjet]: https://www.mailjet.com
[smarthost::sendgrid]: https://sendgrid.com/
[smarthost::aws-ses]: https://aws.amazon.com/ses/
[wikipedia::smarthost]: https://en.wikipedia.org/wiki/Smart_host
[docs::env-relay]: ../../environment.md#relay-host
[dms-repo::helpers-relay]: https://github.com/docker-mailserver/docker-mailserver/blob/v15.0.0/target/scripts/helpers/relay.sh
[dms-gh::pr-3607]: https://github.com/docker-mailserver/docker-mailserver/issues/3607
[dms-gh::relay-example]: https://github.com/docker-mailserver/docker-mailserver/issues/3842#issuecomment-1913380639

View file

@ -10,14 +10,19 @@ environment:
- GETMAIL_POLL=5
```
In your DMS config volume (eg: `docker-data/dms/config/`), create a `getmail-<ID>.cf` file for each remote account that you want to retrieve mail and store into a local DMS account. `<ID>` should be replaced by you, and is just the rest of the filename (eg: `getmail-example.cf`). The contents of each file should be configuration like documented below.
In your DMS config volume (eg: `docker-data/dms/config/`), add a subdirectory `getmail/` for including your getmail config files (eg: `imap-example.cf`) for each remote account that you want to retrieve mail from and deliver to the mailbox of a DMS account.
The directory structure should similar to this:
The content of these config files is documented in the next section with an IMAP and POP3 example to reference.
The directory structure should look similar to this:
```txt
├── docker-data/dms/config
│   ├── dovecot.cf
│   ├── getmail-example.cf
│ ├── getmail
│   │ ├── getmailrc_general.cf
│   │ ├── remote-account1.cf
│   │ ├── remote-account2.cf
│   ├── postfix-accounts.cf
│   └── postfix-virtual.cf
├── docker-compose.yml
@ -42,9 +47,13 @@ received = false
delivered_to = false
```
If you want to use a different base config, mount a file to `/etc/getmailrc_general`. This file will replace the default "Common Options" base config above, that all `getmail-<ID>.cf` files will extend with their configs when used.
The DMS integration for Getmail generates a `getmailrc` config that prepends the common options of the base config to each remote account config file (`*.cf`) found in the DMS Config Volume `getmail/` directory.
??? example "IMAP Configuration"
!!! tip "Change the base options"
Add your own base config as `getmail/getmailrc_general.cf` into the DMS Config Volume. It will replace the DMS defaults shown above.
??? example "IMAP Configuration"
This example will:
@ -54,7 +63,7 @@ If you want to use a different base config, mount a file to `/etc/getmailrc_gene
```getmailrc
[retriever]
type = SimpleIMAPRetriever
type = SimpleIMAPSSLRetriever
server = imap.gmail.com
username = alice
password = notsecure
@ -71,7 +80,7 @@ If you want to use a different base config, mount a file to `/etc/getmailrc_gene
```getmailrc
[retriever]
type = SimplePOP3Retriever
type = SimplePOP3SSLRetriever
server = pop3.gmail.com
username = alice
password = notsecure
@ -84,7 +93,7 @@ If you want to use a different base config, mount a file to `/etc/getmailrc_gene
### Polling Interval
By default the `getmail` service checks external mail accounts for new mail every 5 minutes. That polling interval is configurable via the `GETMAIL_POLL` ENV variable, with a value in minutes (_default: 5, min: 1, max: 30_):
By default the `getmail` service checks external mail accounts for new mail every 5 minutes. That polling interval is configurable via the `GETMAIL_POLL` ENV variable, with a value in minutes (_default: 5, min: 1_):
```yaml
environment:
@ -95,7 +104,15 @@ environment:
It is possible to utilize the `getmail-gmail-xoauth-tokens` helper to provide authentication using `xoauth2` for [gmail (example 12)][getmail-docs-xoauth-12] or [Microsoft Office 365 (example 13)][getmail-docs-xoauth-13]
[getmail-website]: https://www.getmail.org
[getmail-website]: https://www.getmail6.org
[getmail-docs]: https://getmail6.org/configuration.html
[getmail-docs-xoauth-12]: https://github.com/getmail6/getmail6/blob/1f95606156231f1e074ba62a9baa64f892a92ef8/docs/getmailrc-examples#L286
[getmail-docs-xoauth-13]: https://github.com/getmail6/getmail6/blob/1f95606156231f1e074ba62a9baa64f892a92ef8/docs/getmailrc-examples#L351
## Debugging
To debug your `getmail` configurations, run this `setup debug` command:
```sh
docker exec -it dms-container-name setup debug getmail
```

View file

@ -81,22 +81,76 @@ For more examples or a detailed description of the Sieve language have a look at
[sieve-info::examples]: http://sieve.info/examplescripts
[third-party::sieve-examples]: https://support.tigertech.net/sieve#sieve-example-rules-jmp
## Automatic Sorting Based on Subaddresses
## Automatic Sorting Based on Sub-addresses { #subaddress-mailbox-routing }
It is possible to sort subaddresses such as `user+mailing-lists@example.com` into a corresponding folder (here: `INBOX/Mailing-lists`) automatically.
When mail is delivered to your account, it is possible to organize storing mail into folders by the [subaddress (tag)][docs::accounts-subaddressing] used.
```sieve
require ["envelope", "fileinto", "mailbox", "subaddress", "variables"];
!!! example "Example: `user+<tag>@example.com` to `INBOX/<Tag>`"
if envelope :detail :matches "to" "*" {
set :lower :upperfirst "tag" "${1}";
if mailboxexists "INBOX.${1}" {
fileinto "INBOX.${1}";
} else {
fileinto :create "INBOX.${tag}";
}
}
```
This example sorts mail into inbox folders by their tag:
```sieve title="docker-data/dms/config/user@example.com.dovecot.sieve"
require ["envelope", "fileinto", "mailbox", "subaddress", "variables"];
# Check if the mail recipient address has a tag (:detail)
if envelope :detail :matches "to" "*" {
# Create a variable `tag`, with the captured `to` value normalized (SoCIAL => Social)
set :lower :upperfirst "tag" "${1}";
# Store the mail into a folder with the tag name, nested under your inbox folder:
if mailboxexists "INBOX.${tag}" {
fileinto "INBOX.${tag}";
} else {
fileinto :create "INBOX.${tag}";
}
}
```
When receiving mail for `user+social@example.com` it would be delivered into the `INBOX/Social` folder.
??? tip "Only redirect mail for specific tags"
If you want to only handle specific tags, you could replace the envelope condition and tag assignment from the prior example with:
```sieve title="docker-data/dms/config/user@example.com.dovecot.sieve"
# Instead of `:matches`, use the default comparator `:is` (exact match)
if envelope :detail "to" "social" {
set "tag" "Social";
```
```sieve title="docker-data/dms/config/user@example.com.dovecot.sieve"
# Alternatively you can also provide a list of values to match:
if envelope :detail "to" ["azure", "aws"] {
set "tag" "Cloud";
```
```sieve title="docker-data/dms/config/user@example.com.dovecot.sieve"
# Similar to `:matches`, except `:regex` provides enhanced pattern matching.
# NOTE: This example needs you to `require` the "regex" extension
if envelope :detail :regex "to" "^cloud-(azure|aws)$" {
# Normalize the captured azure/aws tag as the resolved value is no longer fixed:
set :lower :upperfirst "vendor" "${1}";
# If a `.` exists in the tag, it will create nested folders:
set "tag" "Cloud.${vendor}";
```
**NOTE:** There is no need to lowercase the tag in the conditional as the [`to` value is a case-insensitive check][sieve-docs::envelope].
??? abstract "Technical Details"
- Dovecot supports this feature via the _Sieve subaddress extension_ ([RFC 5233][rfc::5233::sieve-subaddress]).
- Only a single tag per subaddress is supported. Any additional tag delimiters are part of the tag value itself.
- The Dovecot setting [`recipient_delimiter`][dovecot-docs::config::recipient_delimiter] (default: `+`) configures the tag delimiter. This is where the `local-part` of the recipient address will split at, providing the `:detail` (tag) value for Sieve.
---
`INBOX` is the [default namespace configured by Dovecot][dovecot-docs::namespace].
- If you omit the `INBOX.` prefix from the sieve script above, the mailbox (folder) for that tag is created at the top-level alongside your Trash and Junk folders.
- The `.` between `INBOX` and `${tag}` is important as a [separator to distinguish mailbox names][dovecot-docs::mailbox-names]. This can vary by mailbox format or configuration. DMS uses [`Maildir`][dovecot-docs::mailbox-formats::maildir] by default, which uses `.` as the separator.
- [`lmtp_save_to_detail_mailbox = yes`][dovecot-docs::config::lmtp_save_to_detail_mailbox] can be set in `/etc/dovecot/conf.d/20-lmtp.conf`:
- This implements the feature globally, except for the tag normalization and `INBOX.` prefix parts of the example script.
- However, if the sieve script is also present, the script has precedence and will handle this task instead when the condition is successful, otherwise falling back to the global feature.
## Manage Sieve
@ -104,8 +158,7 @@ The [Manage Sieve](https://doc.dovecot.org/admin_manual/pigeonhole_managesieve_s
!!! example
```yaml
# compose.yaml
```yaml title="compose.yaml"
ports:
- "4190:4190"
environment:
@ -122,3 +175,14 @@ The extension is known to work with the following ManageSieve clients:
- **[Sieve Editor](https://github.com/thsmi/sieve)** a portable standalone application based on the former Thunderbird plugin.
- **[Kmail](https://kontact.kde.org/components/kmail/)** the mail client of [KDE](https://kde.org/)'s Kontact Suite.
[docs::accounts-subaddressing]: ../account-management/overview.md#sub-addressing
[dovecot-docs::namespace]: https://doc.dovecot.org/configuration_manual/namespace/
[dovecot-docs::mailbox-names]: https://doc.dovecot.org/configuration_manual/sieve/usage/#mailbox-names
[dovecot-docs::mailbox-formats::maildir]: https://doc.dovecot.org/admin_manual/mailbox_formats/maildir/#maildir-mbox-format
[dovecot-docs::config::lmtp_save_to_detail_mailbox]: https://doc.dovecot.org/settings/core/#core_setting-lmtp_save_to_detail_mailbox
[dovecot-docs::config::recipient_delimiter]: https://doc.dovecot.org/settings/core/#core_setting-recipient_delimiter
[rfc::5233::sieve-subaddress]: https://datatracker.ietf.org/doc/html/rfc5233
[sieve-docs::envelope]: https://thsmi.github.io/sieve-reference/en/test/core/envelope.html

View file

@ -4,9 +4,61 @@ hide:
- toc # Hide Table of Contents for this page
---
This is a list of all configuration files and directories which are optional or automatically generated in your [`docker-data/dms/config/`][docs-dms-config-volume] directory.
## Volumes
## Directories
DMS has several locations in the container which may be worth persisting externally via [Docker Volumes][docker-docs::volumes].
- Often you will want to prefer [bind mount volumes][docker-docs::volumes::bind-mount] for easy access to files at a local location on your filesystem.
- As a convention for our docs and example configs, the local location has the common prefix `docker-data/dms/` for grouping these related volumes.
!!! info "Reference - Volmes for DMS"
Our docs may refer to these DMS specific volumes only by name, or the host/container path for brevity.
- [Config](#volumes-config): `docker-data/dms/config/` => `/tmp/docker-mailserver/`
- [Mail Storage](#volumes-mail): `docker-data/dms/mail-data/` => `/var/mail/`
- [State](#volumes-state): `docker-data/dms/mail-state/` => `/var/mail-state/`
- [Logs](#volumes-log): `docker-data/dms/mail-logs/` => `/var/log/mail/`
### Mail Storage Volume { #volumes-mail }
This is the location where mail is delivered to your mailboxes.
### State Volume { #volumes-state }
Run-time specific state lives here, but so does some data you may want to keep if a failure event occurs (_crash, power loss_).
!!! example "Examples of relevant data"
- The Postfix queue (eg: mail pending delivery attempt)
- Fail2Ban blocks.
- ClamAV signature updates.
- Redis storage for Rspamd.
!!! info "When a volume is mounted to `/var/mail-state/`"
- Service run-time data is [consolidated into the `/var/mail-state/` directory][mail-state-folders]. Otherwise the original locations vary and would need to be mounted individually.
- The original locations are updated with symlinks to redirect to their new path in `/var/mail-state/` (_eg: `/var/lib/redis` => `/var/mail-state/lib-redis/`_).
Supported services: Postfix, Dovecot, Fail2Ban, Amavis, PostGrey, ClamAV, SpamAssassin, Rspamd & Redis, Fetchmail, Getmail, LogRotate, PostSRSd, MTA-STS.
!!! tip
Sometimes it is helpful to disable this volume when troubleshooting to verify if the data stored here is in a bad state (_eg: caused by a failure event_).
[mail-state-folders]: https://github.com/docker-mailserver/docker-mailserver/blob/v13.3.1/target/scripts/startup/setup.d/mail_state.sh#L13-L33
### Logs Volume { #volumes-log }
This can be a useful volume to persist for troubleshooting needs for the full set of log files.
### Config Volume { #volumes-config }
Most configuration files for Postfix, Dovecot, etc. are persisted here.
This is a list of all configuration files and directories which are optional, automatically generated / updated by our `setup` CLI, or other internal scripts.
#### Directories
- **sieve-filter:** directory for sieve filter scripts. (Docs: [Sieve][docs-sieve])
- **sieve-pipe:** directory for sieve pipe scripts. (Docs: [Sieve][docs-sieve])
@ -14,7 +66,7 @@ This is a list of all configuration files and directories which are optional or
- **ssl:** SSL Certificate directory if `SSL_TYPE` is set to `self-signed` or `custom`. (Docs: [SSL][docs-ssl])
- **rspamd:** Override directory for custom settings when using Rspamd (Docs: [Rspamd][docs-rspamd-override-d])
## Files
#### Files
- **{user_email_address}.dovecot.sieve:** User specific Sieve filter file. (Docs: [Sieve][docs-sieve])
- **before.dovecot.sieve:** Global Sieve filter file, applied prior to the `${login}.dovecot.sieve` filter. (Docs: [Sieve][docs-sieve])
@ -25,8 +77,8 @@ This is a list of all configuration files and directories which are optional or
- **postfix-send-access.cf:** List of users denied sending. Modify via [`setup.sh email restrict`][docs-setupsh].
- **postfix-receive-access.cf:** List of users denied receiving. Modify via [`setup.sh email restrict`][docs-setupsh].
- **postfix-virtual.cf:** Alias configuration file. Modify via [`setup.sh alias`][docs-setupsh].
- **postfix-sasl-password.cf:** listing of relayed domains with their respective `<username>:<password>`. Modify via `setup.sh relay add-auth <domain> <username> [<password>]`. (Docs: [Relay-Hosts Auth][docs-relayhosts-senderauth])
- **postfix-relaymap.cf:** domain-specific relays and exclusions. Modify via `setup.sh relay add-domain` and `setup.sh relay exclude-domain`. (Docs: [Relay-Hosts Senders][docs-relayhosts-senderhost])
- **postfix-sasl-password.cf:** listing of relayed domains with their respective `<username>:<password>`. Modify via `setup.sh relay add-auth <domain> <username> [<password>]`. (Docs: [Relay-Hosts Auth][docs::relay-hosts::advanced])
- **postfix-relaymap.cf:** domain-specific relays and exclusions. Modify via `setup.sh relay add-domain` and `setup.sh relay exclude-domain`. (Docs: [Relay-Hosts Senders][docs::relay-hosts::advanced])
- **postfix-regexp.cf:** Regular expression alias file. (Docs: [Aliases][docs-aliases-regex])
- **ldap-users.cf:** Configuration for the virtual user mapping `virtual_mailbox_maps`. See the [`setup-stack.sh`][github-commit-setup-stack.sh-L411] script.
- **ldap-groups.cf:** Configuration for the virtual alias mapping `virtual_alias_maps`. See the [`setup-stack.sh`][github-commit-setup-stack.sh-L411] script.
@ -42,17 +94,18 @@ This is a list of all configuration files and directories which are optional or
- **user-patches.sh:** this file will be run after all configuration files are set up, but before the postfix, amavis and other daemons are started. (Docs: [FAQ - How to adjust settings with the `user-patches.sh` script][docs-faq-userpatches])
- **rspamd/custom-commands.conf:** list of simple commands to adjust Rspamd modules in an easy way (Docs: [Rspamd][docs-rspamd-commands])
[docs-dms-config-volume]: ../../faq.md#what-about-the-docker-datadmsconfig-directory
[docs-accounts-quota]: ../../config/user-management.md#quotas
[docs-aliases-regex]: ../../config/user-management.md#configuring-regexp-aliases
[docker-docs::volumes]: https://docs.docker.com/storage/volumes/
[docker-docs::volumes::bind-mount]: https://docs.docker.com/storage/bind-mounts/
[docs-accounts-quota]: ../../config/account-management/provisioner/file.md#quotas
[docs-aliases-regex]: ../../config/account-management/provisioner/file.md#configuring-regex-aliases
[docs-dkim]: ../../config/best-practices/dkim_dmarc_spf.md#dkim
[docs-fail2ban]: ../../config/security/fail2ban.md
[docs-faq-spamrules]: ../../faq.md#how-can-i-manage-my-custom-spamassassin-rules
[docs-faq-userpatches]: ../../faq.md#how-to-adjust-settings-with-the-user-patchessh-script
[docs-override-postfix]: ./override-defaults/postfix.md
[docs-override-dovecot]: ./override-defaults/dovecot.md
[docs-relayhosts-senderauth]: ./mail-forwarding/relay-hosts.md#sender-dependent-authentication
[docs-relayhosts-senderhost]: ./mail-forwarding/relay-hosts.md#sender-dependent-relay-host
[docs::relay-hosts::advanced]: ./mail-forwarding/relay-hosts.md#advanced-configuration
[docs-sieve]: ./mail-sieve.md
[docs-setupsh]: ../../config/setup.sh.md
[docs-ssl]: ../../config/security/ssl.md

View file

@ -19,7 +19,7 @@ Podman is a daemonless container engine for developing, managing, and running OC
Running podman in rootless mode requires additional modifications in order to keep your mailserver secure.
Make sure to read the related documentation.
## Installation in Rootfull Mode
## Installation in Rootful Mode
While using Podman, you can just manage docker-mailserver as what you did with Docker. Your best friend `setup.sh` includes the minimum code in order to support Podman since it's 100% compatible with the Docker CLI.
@ -45,19 +45,9 @@ docker compose ps
You should see that docker-mailserver is running now.
### Self-start in Rootfull Mode
Podman is daemonless, that means if you want docker-mailserver self-start while boot up the system, you have to generate a systemd file with Podman CLI.
```bash
podman generate systemd mailserver > /etc/systemd/system/mailserver.service
systemctl daemon-reload
systemctl enable --now mailserver.service
```
## Installation in Rootless Mode
Running rootless containers is one of Podman's major features. But due to some restrictions, deploying docker-mailserver in rootless mode is not as easy compared to rootfull mode.
Running [rootless containers][podman-docs::rootless-mode] is one of Podman's major features. But due to some restrictions, deploying docker-mailserver in rootless mode is not as easy compared to rootful mode.
- a rootless container is running in a user namespace so you cannot bind ports lower than 1024
- a rootless container's systemd file can only be placed in folder under `~/.config`
@ -67,7 +57,7 @@ Also notice that Podman's rootless mode is not about running as a non-root user
!!! warning
In order to make rootless DMS work we must modify some settings in the Linux system, it requires some basic linux server knowledge so don't follow this guide if you not sure what this guide is talking about. Podman rootfull mode and Docker are still good and security enough for normal daily usage.
In order to make rootless DMS work we must modify some settings in the Linux system, it requires some basic linux server knowledge so don't follow this guide if you not sure what this guide is talking about. Podman rootful mode and Docker are still good and security enough for normal daily usage.
First, enable `podman.socket` in systemd's userspace with a non-root user.
@ -96,6 +86,215 @@ docker compose up -d mailserver
docker compose ps
```
### Rootless Quadlet
!!! info "What is a Quadlet?"
A [Quadlet][podman::quadlet::introduction] file uses the [systemd config format][systemd-docs::config-syntax] which is similar to the INI format.
[Quadlets define your podman configuration][podman-docs::quadlet::example-configs] (_pods, volumes, networks, images, etc_) which are [adapted into the equivalent systemd service config files][podman::quadlet::generated-output-example] at [boot or when reloading the systemd daemon][podman-docs::config::quadlet-generation] (`systemctl daemon-reload` / `systemctl --user daemon-reload`).
!!! tip "Rootless compatibility"
Quadlets can [support rootless with a few differences][podman::rootless-differences]:
- `Network=pasta` configures [`pasta`][network-driver::pasta] as a rootless compatible network driver (_a popular alternative to `slirp4netns`. `pasta` is the default for rootless since Podman v5_).
- `Restart=always` will auto-start your Quadlet at login. Rootless support requires to enable [lingering][systemd-docs::loginctl::linger] for your user:
```bash
loginctl enable-linger user
```
- [Config locations between rootful vs rootless][podman-docs::quadlet::config-search-path].
#### Example Quadlet file
???+ example
1. Create your DMS Quadlet at `~/.config/containers/systemd/dms.container` with the example content shown below.
- Adjust settings like `HostName` as needed. You may prefer a different convention for your `Volume` host paths.
- Some syntax like systemd specifiers and Podman's `UIDMap` value are explained in detail after this example.
2. Run [`systemctl --user daemon-reload`][systemd-docs::systemctl::daemon-reload], which will trigger the Quadlet service generator. This command is required whenever you adjust config in `dms.container`.
3. You should now be able to start the service with `systemctl --user start dms`.
```ini title="dms.container"
[Unit]
Description="Docker Mail Server"
Documentation=https://docker-mailserver.github.io/docker-mailserver/latest
[Service]
Restart=always
# Optional - This will run before the container starts:
# - It ensures all the DMS volumes have the host directories created for you.
# - For `mkdir` command to leverage the shell brace expansion syntax, you need to run it via bash.
ExecStartPre=/usr/bin/bash -c 'mkdir -p %h/volumes/%N/{mail-data,mail-state,mail-logs,config}'
# This section enables the service at generation, avoids requiring `systemctl --user enable dms`:
# - `multi-user.target` => root
# - `default.target` => rootless
[Install]
WantedBy=default.target
[Container]
ContainerName=%N
HostName=mail.example.com
Image=docker.io/mailserver/docker-mailserver:latest
PublishPort=25:25
PublishPort=143:143
PublishPort=587:587
PublishPort=993:993
# The container UID for root will be mapped to the host UID running this Quadlet service.
# All other UIDs in the container are mapped via the sub-id range for that user from host configs `/etc/subuid` + `/etc/subgid`.
UIDMap=+0:@%U
# Volumes (Base location example: `%h/volumes/%N` => `~/volumes/dms`)
# NOTE: If your host has SELinux enabled, avoid permission errors by appending the mount option `:Z`.
Volume=%h/volumes/%N/mail-data:/var/mail
Volume=%h/volumes/%N/mail-state:/var/mail-state
Volume=%h/volumes/%N/mail-logs:/var/log/mail
Volume=%h/volumes/%N/config:/tmp/docker-mailserver
# Optional - Additional mounts:
# NOTE: For SELinux, when using the `z` or `Z` mount options:
# Take caution if choosing a host location not belonging to your user. Consider using `SecurityLabelDisable=true` instead.
# https://docs.podman.io/en/latest/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options
Volume=%h/volumes/certbot/certs:/etc/letsencrypt:ro
# Podman can create a timer (defaults to daily at midnight) to check the `registry` or `local` storage for detecting if the
# image tag points to a new digest, if so it updates the image and restarts the service (similar to `containrrr/watchtower`):
# https://docs.podman.io/en/latest/markdown/podman-auto-update.1.html
AutoUpdate=registry
# Podman Quadlet has a better alternative instead of a volume directly bind mounting `/etc/localtime` to match the host TZ:
# https://docs.podman.io/en/latest/markdown/podman-run.1.html#tz-timezone
# NOTE: Should the host modify the system TZ, neither approach will sync the change to the `/etc/localtime` inside the running container.
Timezone=local
Environment=SSL_TYPE=letsencrypt
# NOTE: You may need to adjust the default `NETWORK_INTERFACE`:
# https://docker-mailserver.github.io/docker-mailserver/latest/config/environment/#network_interface
#Environment=NETWORK_INTERFACE=enp1s0
#Environment=NETWORK_INTERFACE=tap0
```
??? info "Systemd specifiers"
Systemd has a [variety of specifiers][systemd-docs::config-specifiers] (_prefixed with `%`_) that help manage configs.
Here are the ones used in the Quadlet config example:
- **`%h`:** Location of the users home directory. Use this instead of `~` (_which would only work in a shell, not this config_).
- **`%N`:** Represents the unit service name, which is taken from the filename excluding the extension (_thus `dms.container` => `dms`_).
- **`%U`:** The UID of the user running this service. The next section details the relevance with `UIDMap`.
---
If you prefer the conventional XDG locations, you may prefer `%D` + `%E` + `%S` as part of your `Volume` host paths.
Stopping the service with systemd will result in the container being removed. Restarting will use the existing container, which is however not recommended. You do not need to enable services with Quadlet.
Start container:
`systemctl --user start dockermailserver`
Stop container:
`systemctl --user stop dockermailserver`
Using root with machinectl (used for some Ansible versions):
`machinectl -q shell yourrootlessuser@ /bin/systemctl --user start dockermailserver`
#### Mapping ownership between container and host users
Podman supports a few different approaches for this functionality. For rootless Quadlets you will likely want to use `UIDMap` (_`GIDMap` will use this same mapping by default_).
- `UIDMap` + `GIDMap` works by mapping user and group IDs from a container, to IDs associated for a user on the host [configured in `/etc/subuid` + `/etc/subgid`][podman-docs::rootless-mode] (_this isn't necessary for rootful Podman_).
- Each mapping must be unique, thus only a single container UID can map to your rootless UID on the host. Every other container UID mapped must be within the configured range from `/etc/subuid`.
- Rootless containers have one additional level of mapping involved. This is an offset from their `/etc/subuid` entry starting from `0`, but can be inferred when the intended UID on the host is prefixed with `@`
??? tip "Why should I prefer `UIDMap=+0:@%U`? How does the `@` syntax work?"
The most common case is to map the containers root user (UID `0`) to your host user ID.
For a rootless user with the UID `1000` on the host, any of the following `UIDMap` values are equivalent:
- **`UIDMap=+0:0`:** The 1st `0` is the container root ID and the 2nd `0` refers to host mapping ID. For rootless the mapping ID is an indirect offset to their user entry in `/etc/subuid` where `0` maps to their host user ID, while `1` or higher maps to the users subuid range.
- **`UIDMap=+0:@1000`:** A rootless Quadlet can also use `@` as a prefix which Podman will then instead lookup as the host ID in `/etc/subuid` to get the offset value. If the host user ID was `1000`, then `@1000` would resolve that to `0`.
- **`UIDMap=+0:@%U`:** Instead of providing the explicit rootless UID, a better approach is to leverage `%U` (_a [systemd specifier][systemd-docs::config-specifiers]_) which will resolve to the UID of your rootless user that starts the Quadlet service.
??? tip "What is the `+` syntax used with `UIDMap`?"
Prefixing the container ID with `+` is a a podman feature similar to `@`, which ensures `/etc/subuid` is mapped fully.
For example `UIDMap=+5000:@%U` is the short-hand equivalent to:
```ini
UIDMap=5000:0:1
UIDMap=0:1:5000
UIDMap=5001:5001:60536
```
The third value is the amount of IDs to map from the `container:host` pair as an offset/range. It defaults to `1`.
In addition to our explicit `5000:0` mapping, the `+` ensures:
- That we have a mapping of all container ID prior to `5000` to IDs from our rootless user entry in `/etc/subuid` on the host.
- It also adds a mapping after this value for the remainder of the range configured in `/etc/subuid` which covers the `nobody` user in the container.
Within the container you can view these mappings via `cat /proc/self/uid_map`.
??? warning "Impact on disk usage of images with Rootless"
**NOTE:** This should not usually be a concern, but is documented here to explain the impact of creating new user namespaces (_such as by running a container with settings like `UIDMap` that differ between runs_).
---
Rootless containers [perform a copy of the image with `chown`][caveat::podman::rootless::image-chown] during the first pull/run of the image.
- The larger the image to copy, the longer the initial delay on first use.
- This process will be repeated if the `UIDMap` / `GIDMap` settings are changed to a value that has not been used previously (_accumulating more disk usage with additional image layer copies_).
- Only when the original image is removed will any of these associated `chown` image copies be purged from storage.
When you specify a `UIDMap` like demonstrated in the earlier tip for the `+` syntax with `UIDMap=+0:5000`, if the `/proc/self/uid_map` shows a row with the first two columns as equivalent then no excess `chown` should be applied.
- `UIDMap=+0:@%U` is equivalent from ID 2 onwards.
- `UIDMap=+5000:@%U` is equivalent from ID 5001 onwards. This is relevant with DMS as the container UID 200 is assigned to ClamAV, the offset introduced will now incur a `chown` copy of 230MB.
## Start DMS container at boot
Unlike Docker, Podman is daemonless thus containers do not start at boot. You can create your own systemd service to schedule this or use the Podman CLI.
!!! warning "`podman generate systemd` is deprecated"
The [`podman generate systemd`][podman-docs::cli::generate-systemd] command has been deprecated [since Podman v4.7][gh::podman::release-4.7] (Sep 2023) in favor of Quadlets (_available [since Podman v4.4][gh::podman::release-4.4]_).
!!! example "Create a systemd service"
Use the Podman CLI to generate a systemd service at the rootful or rootless location.
=== "Rootful"
```bash
podman generate systemd mailserver > /etc/systemd/system/mailserver.service
systemctl daemon-reload
systemctl enable --now mailserver.service
```
=== "Rootless"
```bash
podman generate systemd mailserver > ~/.config/systemd/user/mailserver.service
systemctl --user daemon-reload
systemctl enable --user --now mailserver.service
```
A systemd user service will only start when that specific user logs in and stops when after log out. To instead allow user services to run when that user has no active session running run:
```bash
loginctl enable-linger <username>
```
### Security in Rootless Mode
In rootless mode, podman resolves all incoming IPs as localhost, which results in an open gateway in the default configuration. There are two workarounds to fix this problem, both of which have their own drawbacks.
@ -104,10 +303,22 @@ In rootless mode, podman resolves all incoming IPs as localhost, which results i
The `PERMIT_DOCKER` variable in the `mailserver.env` file allows to specify trusted networks that do not need to authenticate. If the variable is left empty, only requests from localhost and the container IP are allowed, but in the case of rootless podman any IP will be resolved as localhost. Setting `PERMIT_DOCKER=none` enforces authentication also from localhost, which prevents sending unauthenticated emails.
#### Use the slip4netns network driver
#### Use the `pasta` network driver
Since [Podman 5.0][gh::podman::release-5.0] the default rootless network driver is now `pasta` instead of `slirp4netns`. These two drivers [have some differences][rhel-docs::podman::slirp4netns-vs-pasta]:
> Notable differences of `pasta` network mode compared to `slirp4netns` include:
>
> - `pasta` supports IPv6 port forwarding.
> - `pasta` is more efficient than `slirp4netns`.
> - `pasta` copies IP addresses from the host, while `slirp4netns` uses a predefined IPv4 address.
> - `pasta` uses an interface name from the host, while `slirp4netns` uses `tap0` as an interface name.
> - `pasta` uses the gateway address from the host, while `slirp4netns` defines its own gateway address and uses NAT.
#### Use the `slip4netns` network driver
The second workaround is slightly more complicated because the `compose.yaml` has to be modified.
As shown in the [fail2ban section](../security/fail2ban.md#podman-with-slirp4netns-port-driver) the `slirp4netns` network driver has to be enabled.
As shown in the [fail2ban section][docs::fail2ban::rootless] the `slirp4netns` network driver has to be enabled.
This network driver enables podman to correctly resolve IP addresses but it is not compatible with
user defined networks which might be a problem depending on your setup.
@ -130,27 +341,9 @@ You must also add the ENV `NETWORK_INTERFACE=tap0`, because Podman uses a [hard-
`podman-compose` is not compatible with this configuration.
### Self-start in Rootless Mode
Generate a systemd file with the Podman CLI.
```bash
podman generate systemd mailserver > ~/.config/systemd/user/mailserver.service
systemctl --user daemon-reload
systemctl enable --user --now mailserver.service
```
Systemd's user space service is only started when a specific user logs in and stops when you log out. In order to make it to start with the system, we need to enable linger with `loginctl`
```bash
loginctl enable-linger <username>
```
Remember to run this command as root user.
### Port Forwarding
When it comes to forwarding ports using `firewalld`, see <https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/securing_networks/using-and-configuring-firewalld_securing-networks#port-forwarding_using-and-configuring-firewalld> for more information.
When it comes to forwarding ports using `firewalld`, see [these port forwarding docs][firewalld-port-forwarding] for more information.
```bash
firewall-cmd --permanent --add-forward-port=port=<25|143|465|587|993>:proto=<tcp>:toport=<10025|10143|10465|10587|10993>
@ -171,5 +364,30 @@ firewall-cmd --reload
Just map all the privilege port with non-privilege port you set in compose.yaml before as root user.
[docs::fail2ban::rootless]: ../security/fail2ban.md#rootless-container
[rootless::podman]: https://github.com/containers/podman/blob/v3.4.1/docs/source/markdown/podman-run.1.md#--networkmode---net
[rootless::podman::interface]: https://github.com/containers/podman/blob/v3.4.1/libpod/networking_slirp4netns.go#L264
[network-driver::pasta]: https://passt.top/passt/about/#pasta
[gh::podman::release-4.4]: https://github.com/containers/podman/releases/tag/v4.4.0
[gh::podman::release-4.7]: https://github.com/containers/podman/releases/tag/v4.7.0
[gh::podman::release-5.0]: https://github.com/containers/podman/releases/tag/v5.0.0
[rhel-docs::podman::slirp4netns-vs-pasta]: https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/building_running_and_managing_containers/assembly_communicating-among-containers_building-running-and-managing-containers#differences-between-slirp4netns-and-pasta_assembly_communicating-among-containers
[firewalld-port-forwarding]: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/securing_networks/using-and-configuring-firewalld_securing-networks#port-forwarding_using-and-configuring-firewalld
[podman::quadlet::introduction]: https://mo8it.com/blog/quadlet/
[podman::quadlet::generated-output-example]: https://blog.while-true-do.io/podman-quadlets/#writing-quadlets
[podman::rootless-differences]: https://matduggan.com/replace-compose-with-quadlet/#rootless
[podman-docs::rootless-mode]: https://docs.podman.io/en/stable/markdown/podman.1.html#rootless-mode
[podman-docs::cli::generate-systemd]: https://docs.podman.io/en/latest/markdown/podman-generate-systemd.1.html
[podman-docs::quadlet::example-configs]: https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html#examples
[podman-docs::config::quadlet-generation]: https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html#description
[podman-docs::quadlet::config-search-path]: https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html#podman-rootful-unit-search-path
[systemd-docs::config-syntax]: https://www.freedesktop.org/software/systemd/man/latest/systemd.syntax.html
[systemd-docs::config-specifiers]: https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html#Specifiers
[systemd-docs::loginctl::linger]: https://www.freedesktop.org/software/systemd/man/latest/loginctl.html#enable-linger%20USER%E2%80%A6
[systemd-docs::systemctl::daemon-reload]: https://www.freedesktop.org/software/systemd/man/latest/systemctl.html#daemon-reload
[caveat::podman::rootless::image-chown]: https://github.com/containers/podman/issues/16541#issuecomment-1352790422

View file

@ -29,17 +29,32 @@ Cloudflare has written an [article about DKIM, DMARC and SPF][cloudflare-dkim-dm
When DKIM is enabled:
1. Inbound mail will verify any included DKIM signatures
2. Outbound mail is signed (_when you're sending domain has a configured DKIM key_)
2. Outbound mail is signed (_when your sending domain has a configured DKIM key_)
DKIM requires a public/private key pair to enable **signing (_via private key_)** your outgoing mail, while the receiving end must query DNS to **verify (_via public key_)** that the signature is trustworthy.
??? info "Verification expiry"
Unlike your TLS certificate, your DKIM keypair does not have a fixed expiry associated to it.
Instead, an expiry may be included in your DKIM signature for each mail sent, where a receiver will [refuse to validate the signature for an email after that expiry date][dkim-verification-expiry-refusal]. This is an added precaution to mitigate malicious activity like "DKIM replay attacks", where an already delivered email from a third-party with a trustworthy DKIM signature is leveraged by a spammer when sending mail to an MTA which verifies the DKIM signature successfully, enabling the spammer to bypass spam protections.
Unlike a TLS handshake where you are authenticating trust with future communications, with DKIM once the mail has been received and trust of the signature has been verified, the value of verifying the signature again at a later date is less meaningful since the signature was to ensure no tampering had occurred during delivery through the network.
??? tip "DKIM key rotation"
You can rotate your DKIM keypair by switching to a new DKIM selector (_and DNS updates_), while the previous key and selector remains valid for verification until the last mail signed with that key reaches it's expiry.
DMS does not provide any automation or support for key rotation, [nor is it likely to provide a notable security benefit][gh-discussion::dkim-key-rotation-expiry] to the typical small scale DMS deployment.
### Generating Keys
You'll need to repeat this process if you add any new domains.
You should have:
- At least one [email account setup][docs-accounts-add]
- At least one [email account setup][docs-accounts]
- Attached a [volume for config][docs-volumes-config] to persist the generated files to local storage
!!! example "Creating DKIM Keys"
@ -61,7 +76,7 @@ You should have:
??? info "Changing the key size"
The keypair generated for using with DKIM presently defaults to RSA-2048. This is a good size but you can lower the security to `1024-bit`, or increase it to `4096-bit` (_discouraged as that is excessive_).
To generate a key with different size (_for RSA 1024-bit_) run:
```sh
@ -72,7 +87,7 @@ You should have:
According to [RFC 8301][rfc-8301], keys are preferably between 1024 and 2048 bits. Keys of size 4096-bit or larger may not be compatible to all systems your mail is intended for.
You [should not need a key length beyond 2048-bit][github-issue-dkimlength]. If 2048-bit does not meet your security needs, you may want to instead consider adopting key rotation or switching from RSA to ECC keys for DKIM.
You [should not need a key length beyond 2048-bit][gh-issue::dkim-length]. If 2048-bit does not meet your security needs, you may want to instead consider adopting key rotation or switching from RSA to ECC keys for DKIM.
??? note "You may need to specify mail domains explicitly"
@ -82,7 +97,7 @@ You should have:
When the DMS FQDN is `mail.example.com` or `example.com`, by default this command will generate DKIM keys for `example.com` as the primary domain for your users mail accounts (eg: `hello@example.com`).
The DKIM generation does not have support to query LDAP for additionanl mail domains it should know about. If the primary mail domain is not sufficient, then you must explicitly specify any extra domains via the `domain` option:
The DKIM generation does not have support to query LDAP for additional mail domains it should know about. If the primary mail domain is not sufficient, then you must explicitly specify any extra domains via the `domain` option:
```sh
# ENABLE_OPENDKIM=1 (default):
@ -122,7 +137,7 @@ DKIM is currently supported by either OpenDKIM or Rspamd:
If you have multiple domains, you need to:
- Create a key wth `docker exec -it <CONTAINER NAME> setup config dkim domain <DOMAIN>` for each domain DMS should sign outgoing mail for.
- Create a key with `docker exec -it <CONTAINER NAME> setup config dkim domain <DOMAIN>` for each domain DMS should sign outgoing mail for.
- Provide a custom `dkim_signing.conf` (for which an example is shown below), as the default config only supports one domain.
!!! info "About the Helper Script"
@ -133,9 +148,7 @@ DKIM is currently supported by either OpenDKIM or Rspamd:
---
In case you have not already provided a default DKIM signing configuration, the script will create one and write it to `/etc/rspamd/override.d/dkim_signing.conf`. If this file already exists, it will not be overwritten.
When you're already using [the `rspamd/override.d/` directory][docs-rspamd-config-dropin], the file is created inside your volume and therefore persisted correctly. If you are not using `rspamd/override.d/`, you will need to persist the file yourself (otherwise it is lost on container restart).
In case you have not already provided a default DKIM signing configuration, the script will create one and write it to `/tmp/docker-mailserver/rspamd/override.d/dkim_signing.conf`. If this file already exists, it will not be overwritten.
An example of what a default configuration file for DKIM signing looks like can be found by expanding the example below.
@ -302,9 +315,9 @@ The DMARC status may not be displayed instantly due to delays in DNS (caches). D
[Source][wikipedia-spf]
!!! note "Disabling `policyd-spf`?"
!!! tip "Disabling the default SPF service `policy-spf`"
As of now, `policyd-spf` cannot be disabled. This is WIP.
Set [`ENABLE_POLICYD_SPF=0`][docs-env-spf-policyd] to opt-out of the default SPF service. Advised when Rspamd is configured to handle SPF instead.
### Adding an SPF Record
@ -344,14 +357,15 @@ volumes:
- ./docker-data/dms/config/postfix-policyd-spf.conf:/etc/postfix-policyd-spf-python/policyd-spf.conf
```
[docs-accounts-add]: ../user-management.md#adding-a-new-account
[docs-volumes-config]: ../advanced/optional-config.md
[docs-accounts]: ../account-management/overview.md#accounts
[docs-volumes-config]: ../advanced/optional-config.md#volumes-config
[docs-env-opendkim]: ../environment.md#enable_opendkim
[docs-env-rspamd]: ../environment.md#enable_rspamd
[docs-rspamd-config-dropin]: ../security/rspamd.md#manually
[docs-env-spf-policyd]: ../environment.md#enable_policyd_spf
[cloudflare-dkim-dmarc-spf]: https://www.cloudflare.com/learning/email-security/dmarc-dkim-spf/
[rfc-8301]: https://datatracker.ietf.org/doc/html/rfc8301#section-3.2
[github-issue-dkimlength]: https://github.com/docker-mailserver/docker-mailserver/issues/1854#issuecomment-806280929
[gh-discussion::dkim-key-rotation-expiry]: https://github.com/orgs/docker-mailserver/discussions/4068#discussioncomment-9784263
[gh-issue::dkim-length]: https://github.com/docker-mailserver/docker-mailserver/issues/1854#issuecomment-806280929
[rspamd-docs-dkim-checks]: https://www.rspamd.com/doc/modules/dkim.html
[rspamd-docs-dkim-signing]: https://www.rspamd.com/doc/modules/dkim_signing.html
[dns::example-webui]: https://www.vultr.com/docs/introduction-to-vultr-dns/
@ -359,6 +373,7 @@ volumes:
[dns::wikipedia-zonefile]: https://en.wikipedia.org/wiki/Zone_file
[dns::webui-dkim]: https://serverfault.com/questions/763815/route-53-doesnt-allow-adding-dkim-keys-because-length-is-too-long
[dkim-ed25519-support]: https://serverfault.com/questions/1023674/is-ed25519-well-supported-for-the-dkim-validation/1074545#1074545
[dkim-verification-expiry-refusal]: https://mxtoolbox.com/problem/dkim/dkim-signature-expiration
[mxtoolbox-dkim-verifier]: https://mxtoolbox.com/dkim.aspx
[dmarc-howto-configtags]: https://github.com/internetstandards/toolbox-wiki/blob/master/DMARC-how-to.md#overview-of-dmarc-configuration-tags
[dmarc-tool-gca]: https://dmarcguide.globalcyberalliance.org

View file

@ -111,7 +111,7 @@ This could be from outdated software, or running a system that isn't able to pro
- **Container runtime:** Docker and Podman for example have subtle differences. DMS docs are primarily focused on Docker, but we try to document known issues where relevant.
- **Rootless containers:** Introduces additional differences in behavior or requirements:
- cgroup v2 is required for supporting rootless containers.
- Differences such as for container networking which may further affect support for IPv6 and preserving the client IP (Remote address). Example with Docker rootless are [binding a port to a specific interface][docker-rootless-interface] and the choice of [port forwarding driver][docs-rootless-portdriver].
- Differences such as for container networking which may further affect support for IPv6 and preserving the client IP (Remote address). Example with Docker rootless are [binding a port to a specific interface][docker-rootless-interface] and the choice of [port forwarding driver][docs::fail2ban::rootless-portdriver].
[network::docker-userlandproxy]: https://github.com/moby/moby/issues/44721
[network::docker-nftables]: https://github.com/moby/moby/issues/26824
@ -123,7 +123,7 @@ This could be from outdated software, or running a system that isn't able to pro
[docs::faq-bare-domain]: ../faq.md#can-i-use-a-nakedbare-domain-ie-no-hostname
[docs-ipv6]: ./advanced/ipv6.md
[docs-introduction]: ../introduction.md
[docs-rootless-portdriver]: ./security/fail2ban.md#running-inside-a-rootless-container
[docs::fail2ban::rootless-portdriver]: ./security/fail2ban.md#rootless-container
[docs-usage]: ../usage.md
[gh-issues]: https://github.com/docker-mailserver/docker-mailserver/issues

View file

@ -6,15 +6,31 @@ title: Environment Variables
Values in **bold** are the default values. If an option doesn't work as documented here, check if you are running the latest image. The current `master` branch corresponds to the image `ghcr.io/docker-mailserver/docker-mailserver:edge`.
!!! tip
If an environment variable `<VAR>__FILE` is set with a valid file path as the value, the content of that file will become the value for `<VAR>` (_provided `<VAR>` has not already been set_).
#### General
##### OVERRIDE_HOSTNAME
If you can't set your hostname (_eg: you're in a container platform that doesn't let you_) specify it via this environment variable. It will have priority over `docker run --hostname`, or the equivalent `hostname:` field in `compose.yaml`.
If you cannot set your DMS FQDN as your hostname (_eg: you're in a container runtime lacks the equivalent of Docker's `--hostname`_), specify it via this environment variable.
- **empty** => Uses the `hostname -f` command to get canonical hostname for DMS to use.
- **empty** => Internally uses the `hostname --fqdn` command to get the canonical hostname assigned to the DMS container.
- => Specify an FQDN (fully-qualified domain name) to serve mail for. The hostname is required for DMS to function correctly.
!!! info
`OVERRIDE_HOSTNAME` is checked early during DMS container setup. When set it will be preferred over querying the containers hostname via the `hostname --fqdn` command (_configured via `docker run --hostname` or the equivalent `hostname:` field in `compose.yaml`_).
!!! warning "Compatibility may differ"
`OVERRIDE_HOSTNAME` is not a complete replacement for adjusting the containers configured hostname. It is a best effort workaround for supporting deployment environments like Kubernetes or when using Docker with `--network=host`.
Typically this feature is only useful when software supports configuring a specific hostname to use, instead of a default fallback that infers the hostname (such as retrieving the hostname via libc / NSS). [Fetchmail is known to be incompatible][gh--issue::hostname-compatibility] with this ENV, requiring manual workarounds.
Compatibility differences are being [tracked here][gh-issue::dms-fqdn] as they become known.
##### LOG_LEVEL
Set the log level for DMS. This is mostly relevant for container startup scripts and change detection event feedback.
@ -39,37 +55,26 @@ Default: 5000
The User ID assigned to the static vmail user for `/var/mail` (_Mail storage managed by Dovecot_).
!!! warning "Incompatible UID values"
- A value of [`0` (root) is not compatible][gh-issue::vmail-uid-cannot-be-root].
- This feature will attempt to adjust the `uid` for the `docker` user (`/etc/passwd`), hence the error emitted to logs if the UID is already assigned to another user.
- The feature appears to work with other UID values that are already assigned in `/etc/passwd`, even though Dovecot by default has a setting for the minimum UID as `500`.
##### DMS_VMAIL_GID
Default: 5000
The Group ID assigned to the static vmail group for `/var/mail` (_Mail storage managed by Dovecot_).
##### ONE_DIR
- 0 => state in default directories.
- **1** => consolidate all states into a single directory (`/var/mail-state`) to allow persistence using docker volumes. See the [related FAQ entry][docs-faq-onedir] for more information.
##### ACCOUNT_PROVISIONER
Configures the provisioning source of user accounts (including aliases) for user queries and authentication by services managed by DMS (_Postfix and Dovecot_).
Configures the [provisioning source of user accounts][docs::account-management::overview] (including aliases) for user queries and authentication by services managed by DMS (_Postfix and Dovecot_).
!!! tip "OAuth2 Support"
Presently DMS supports OAuth2 only as an supplementary authentication method.
- A third-party service must provide a valid token for the user which Dovecot validates with the authentication service provider. To enable this feature reference the [OAuth2 configuration example guide][docs::auth::oauth2-config-guide].
- User accounts must be provisioned to receive mail via one of the supported `ACCOUNT_PROVISIONER` providers.
- User provisioning via OIDC is planned for the future, see [this tracking issue](https://github.com/docker-mailserver/docker-mailserver/issues/2713).
[docs::auth::oauth2-config-guide]: ./advanced/auth-oauth2.md
- **empty** => use FILE
- **FILE** => use local files
- LDAP => use LDAP authentication
- OIDC => use OIDC authentication (**not yet implemented**)
- FILE => use local files (this is used as the default)
A second container for the ldap service is necessary (e.g. [`bitnami/openldap`](https://hub.docker.com/r/bitnami/openldap/)).
LDAP requires an external service (e.g. [`bitnami/openldap`](https://hub.docker.com/r/bitnami/openldap/)).
##### PERMIT_DOCKER
@ -205,19 +210,27 @@ Please read [the SSL page in the documentation][docs-tls] for more information.
##### TLS_LEVEL
- **empty** => modern
- modern => Enables TLSv1.2 and modern ciphers only. (default)
- intermediate => Enables TLSv1, TLSv1.1 and TLSv1.2 and broad compatibility ciphers.
- `modern` => Limits the cipher suite to secure ciphers only.
- `intermediate` => Relaxes security by adding additional ciphers for broader compatibility.
!!! info
In both cases TLS v1.2 is the minimum protocol version supported.
!!! note
Prior to DMS v12.0, `TLS_LEVEL=intermediate` additionally supported TLS versions 1.0 and 1.1. If you still have legacy devices that can only use these versions of TLS, please follow [this workaround advice][gh-issue::tls-legacy-workaround].
##### SPOOF_PROTECTION
Configures the handling of creating mails with forged sender addresses.
- **0** => (not recommended) Mail address spoofing allowed. Any logged in user may create email messages with a [forged sender address](https://en.wikipedia.org/wiki/Email_spoofing).
- 1 => Mail spoofing denied. Each user may only send with his own or his alias addresses. Addresses with [extension delimiters](http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
- 1 => Mail spoofing denied. Each user may only send with their own or their alias addresses. Addresses with [extension delimiters](http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
##### ENABLE_SRS
Enables the Sender Rewriting Scheme. SRS is needed if DMS acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/master/README.md#sender-rewriting-scheme-crash-course) for further explanation.
Enables the Sender Rewriting Scheme. SRS is needed if DMS acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/main/README.rst) for further explanation.
- **0** => Disabled
- 1 => Enabled
@ -258,6 +271,12 @@ Set the mailbox size limit for all users. If set to zero, the size will be unlim
See [mailbox quota][docs-accounts-quota].
!!! info "Compatibility"
This feature is presently only compatible with `ACCOUNT_PROVISIONER=FILE`.
When using a different provisioner (or `SMTP_ONLY=1`) this ENV will instead default to `0`.
##### POSTFIX_MESSAGE_SIZE_LIMIT
Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!). Size is in bytes.
@ -306,7 +325,7 @@ Customize the update check interval. Number + Suffix. Suffix must be 's' for sec
- sdbox => (experimental) uses Dovecot high-performance mailbox format, one file contains one message
- mdbox ==> (experimental) uses Dovecot high-performance mailbox format, multiple messages per file and multiple files per box
This option has been added in November 2019. Using other format than Maildir is considered as experimental in docker-mailserver and should only be used for testing purpose. For more details, please refer to [Dovecot Documentation](https://wiki2.dovecot.org/MailboxFormat).
This option has been added in November 2019. Using other format than Maildir is considered as experimental in docker-mailserver and should only be used for testing purpose. For more details, please refer to [Dovecot Documentation](https://doc.dovecot.org/admin_manual/mailbox_formats/#mailbox-formats).
##### POSTFIX_REJECT_UNKNOWN_CLIENT_HOSTNAME
@ -333,10 +352,12 @@ Note: More information at <https://dovecot.org/doc/dovecot-example.conf>
##### MOVE_SPAM_TO_JUNK
- 0 => Spam messages will be delivered in the mailbox.
- **1** => Spam messages will be delivered in the `Junk` folder.
- 0 => Spam messages will be delivered to the inbox.
- **1** => Spam messages will be delivered to the Junk mailbox.
Routes mail identified as spam into the recipient(s) Junk folder (_via a Dovecot Sieve script_).
Routes mail identified as spam into the recipient(s) Junk mailbox (_a specialized folder for junk associated to the [special-use flag `\Junk`][docs::dovecot::special-use-flag], handled via a Dovecot sieve script internally_).
[docs::dovecot::special-use-flag]: ../examples/use-cases/imap-folders.md
!!! info
@ -359,6 +380,33 @@ Enable to treat received spam as "read" (_avoids notification to MUA client of n
- `X-Spam: Yes` (_added by Rspamd_)
- `X-Spam-Flag: YES` (_added by SpamAssassin - requires [`SPAMASSASSIN_SPAM_TO_INBOX=1`](#spamassassin_spam_to_inbox)_)
##### SPAM_SUBJECT
This variable defines a prefix for e-mails tagged with the `X-Spam: Yes` (Rspamd) or `X-Spam-Flag: YES` (SpamAssassin/Amavis) header.
Default: empty (no prefix will be added to e-mails)
??? example "Including trailing white-space"
Add trailing white-space by quote wrapping the value: `SPAM_SUBJECT='[SPAM] '`
##### DMS_CONFIG_POLL
Defines how often DMS polls [monitored config files][gh::monitored-configs] for changes in the DMS Config Volume. This also includes TLS certificates and is often relied on for applying changes managed via `setup` CLI commands.
- **`2`** => How often (in seconds) [change detection][gh::check-for-changes] is performed.
!!! note "Decreasing the frequency of polling for changes"
Raising the value will delay how soon a change is detected which may impact UX expectations for responsiveness, but reduces resource usage when changes are rare.
!!! info
When using `ACCOUNT_PROVISIONER=LDAP`, the change detection feature is presently disabled.
[gh::check-for-changes]: https://github.com/docker-mailserver/docker-mailserver/blob/v15.0.0/target/scripts/check-for-changes.sh#L37
[gh::monitored-configs]: https://github.com/docker-mailserver/docker-mailserver/blob/v15.0.0/target/scripts/helpers/change-detection.sh#L30-L42
#### Rspamd
##### ENABLE_RSPAMD
@ -446,6 +494,17 @@ Can be used to control the score when the [`HFILTER_HOSTNAME_UNKNOWN` symbol](#r
Default: 6 (which corresponds to the `add_header` action)
##### RSPAMD_NEURAL
Can be used to enable or disable the [Neural network module][rspamd-docs-neural-network]. This is an experimental anti-spam weigh method using three neural networks in the configuration added here. As far as we can tell it trains itself by using other modules to find out what spam is. It will take a while (a week or more) to train its first neural network. The config trains new networks all the time and discards old networks.
Since it is experimental, it is switched off by default.
- **0** => Disabled
- 1 => Enabled
[rspamd-docs-neural-network]: https://www.rspamd.com/doc/modules/neural.html
#### Reports
##### PFLOGSUMM_TRIGGER
@ -529,6 +588,12 @@ Changes the interval in which log files are rotated.
This variable can also determine the interval for Postfix's log summary reports, see [`PFLOGSUMM_TRIGGER`](#pflogsumm_trigger).
##### LOGROTATE_COUNT
Defines how many files are kept by logrotate.
- **4** => Number of files
#### SpamAssassin
##### ENABLE_SPAMASSASSIN
@ -567,7 +632,7 @@ Changes the interval in which log files are rotated.
- [`MOVE_SPAM_TO_JUNK=1`](#move_spam_to_junk)
- [`MARK_SPAM_AS_READ=1`](#mark_spam_as_read)
- [`SA_SPAM_SUBJECT`](#sa_spam_subject)
- [`SPAM_SUBJECT`](#spam_subject)
##### SA_TAG
@ -607,8 +672,8 @@ When a spam score is high enough, mark mail as spam (_Appends the mail header: `
!!! info "Interaction with other ENV"
- [`SA_SPAM_SUBJECT`](#sa_spam_subject) modifies the mail subject to better communicate spam mail to the user.
- [`MOVE_SPAM_TO_JUNK=1`](#move_spam_to_junk): The mail is still delivered, but to the recipient(s) junk folder instead. This feature reduces the usefulness of `SA_SPAM_SUBJECT`.
- [`SPAM_SUBJECT`](#spam_subject) modifies the mail subject to better communicate spam mail to the user.
- [`MOVE_SPAM_TO_JUNK=1`](#move_spam_to_junk): The mail is still delivered, but to the recipient(s) junk folder instead. This feature reduces the usefulness of `SPAM_SUBJECT`.
##### SA_KILL
@ -648,10 +713,10 @@ Controls the spam score threshold for triggering an action on mail that has a hi
- [It will be quarantined][amavis-docs::quarantine] regardless of the `SA_KILL` action to perform.
- With `D_PASS` the delivered mail also appends an `X-Quarantine-ID` mail header. The ID value of this header is part of the quarantined file name.
If emails are quarantined, they are compressed and stored at a location dependent on the [`ONE_DIR`](#one_dir) setting:
If emails are quarantined, they are compressed and stored at a location:
- `ONE_DIR=1` (default): `/var/mail-state/lib-amavis/virusmails/`
- `ONE_DIR=0`: `/var/lib/amavis/virusmails/`
- Default: `/var/lib/amavis/virusmails/`
- When the [`/var/mail-state/` volume][docs::dms-volumes-state] is present: `/var/mail-state/lib-amavis/virusmails/`
!!! tip
@ -664,23 +729,6 @@ Controls the spam score threshold for triggering an action on mail that has a hi
[amavis-docs::actions]: https://www.ijs.si/software/amavisd/amavisd-new-docs.html#actions
[amavis-docs::quarantine]: https://www.ijs.si/software/amavisd/amavisd-new-docs.html#quarantine
##### SA_SPAM_SUBJECT
Adds a prefix to the subject header when mail is marked as spam (_via [`SA_TAG2`](#sa_tag2)_).
- **`'***SPAM*** '`** => A string value to use as a mail subject prefix.
- `undef` => Opt-out of modifying the subject for mail marked as spam.
??? example "Including trailing white-space"
Add trailing white-space by quote wrapping the value: `SA_SPAM_SUBJECT='[SPAM] '`
??? example "Including the associated spam score"
The [`_SCORE_` tag][sa-docs::score-tag] will be substituted with the SpamAssassin score: `SA_SPAM_SUBJECT=***SPAM(_SCORE_)***`.
[sa-docs::score-tag]: https://spamassassin.apache.org/full/4.0.x/doc/Mail_SpamAssassin_Conf.html#rewrite_header-subject-from-to-STRING
##### SA_SHORTCIRCUIT_BAYES_SPAM
- **1** => will activate SpamAssassin short circuiting for bayes spam detection.
@ -731,7 +779,7 @@ Enable or disable `getmail`.
##### GETMAIL_POLL
- **5** => `getmail` The number of minutes for the interval. Min: 1; Max: 30; Default: 5.
- **5** => `getmail` The number of minutes for the interval. Min: 1; Default: 5.
#### OAUTH2
@ -909,22 +957,26 @@ Note: This postgrey setting needs `ENABLE_POSTGREY=1`
##### SASLAUTHD_MECHANISMS
- **empty** => pam
- `ldap` => authenticate against ldap server
- `shadow` => authenticate against local user db
- `mysql` => authenticate against mysql db
- `rimap` => authenticate against imap server
- NOTE: can be a list of mechanisms like pam ldap shadow
DMS only implements support for these mechanisms:
- **`ldap`** => Authenticate against an LDAP server
- `rimap` => Authenticate against an IMAP server
##### SASLAUTHD_MECH_OPTIONS
- **empty** => None
- e.g. with SASLAUTHD_MECHANISMS rimap you need to specify the ip-address/servername of the imap server ==> xxx.xxx.xxx.xxx
!!! info
With `SASLAUTHD_MECHANISMS=rimap` you need to specify the ip-address / servername of the IMAP server, such as `SASLAUTHD_MECH_OPTIONS=127.0.0.1`.
##### SASLAUTHD_LDAP_SERVER
- **empty** => same as `LDAP_SERVER_HOST`
- Note: You must include the desired URI scheme (`ldap://`, `ldaps://`, `ldapi://`).
- **empty** => Use the same value as `LDAP_SERVER_HOST`
!!! note
You must include the desired URI scheme (`ldap://`, `ldaps://`, `ldapi://`).
##### SASLAUTHD_LDAP_START_TLS
@ -1024,41 +1076,124 @@ you to replace both instead of just the envelope sender.
- **empty** => Derived from [`OVERRIDE_HOSTNAME`](#override_hostname), `$DOMAINNAME` (internal), or the container's hostname
- Set this if auto-detection fails, isn't what you want, or you wish to have a separate container handle DSNs
#### Default Relay Host
#### Relay Host
Supported ENV for the [Relay Host][docs::relay-host] feature.
!!! note "Prefer `DEFAULT_RELAY_HOST` instead of `RELAY_HOST`"
This is advised unless you need support for sender domain opt-out (via `setup relay exclude-domain`).
The implementation for `RELAY_HOST` is not compatible with LDAP.
!!! tip "Opt-in for relay host support"
Enable relaying only for specific sender domains instead by using `setup relay add-domain`.
**NOTE:** Presently there is a caveat when relay host credentials are configured (_which is incompatible with opt-in_).
##### DEFAULT_RELAY_HOST
- **empty** => don't set default relayhost setting in main.cf
- default host and port to relay all mail through.
Format: `[example.com]:587` (don't forget the brackets if you need this to
be compatible with `$RELAY_USER` and `$RELAY_PASSWORD`, explained below).
Configures a default relay host.
#### Multi-domain Relay Hosts
!!! info
- All mail sent outbound from DMS will be relayed through the configured host, unless sender-dependent relayhost maps have been configured (_which have precedence_).
- The host value may optionally be wrapped in brackets (_skips DNS query for MX record_): `[mail.example.com]:587` vs `example.com:587`
!!! abstract "Technical Details"
This ENV internally configures the Postfix `main.cf` setting: [`relayhost`][postfix-config::relayhost]
##### RELAY_HOST
- **empty** => don't configure relay host
- default host to relay mail through
Configures a default relay host.
!!! note
Expects a value like `mail.example.com`. Internally this will be wrapped to `[mail.example.com]`, so it should resolve to the MTA directly.
!!! warning "Do not use with `DEFAULT_RELAY_HOST`"
`RELAY_HOST` has precedence as it is configured with `sender_dependent_relayhost_maps`.
!!! info
- This is a legacy ENV. It is however required for the opt-out feature of `postfix-relaymap.cf` to work.
- Internal configuration however differs from `DEFAULT_RELAY_HOST`.
!!! abstract "Technical Details"
This feature is configured internally using the:
- Postfix setting with config: [`sender_dependent_relayhost_maps = texthash:/etc/postfix/relayhost_map`][postfix-config::relayhost_maps]
- DMS Config volume support via: `postfix-relaymap.cf` (_generates `/etc/postfix/relayhost_map`_)
All known mail domains managed by DMS will be configured to relay outbound mail to `RELAY_HOST` by adding them implicitly to `/etc/postfix/relayhost_map`, except for domains using the opt-out feature of `postfix-relaymap.cf`.
##### RELAY_PORT
- **empty** => 25
- default port to relay mail through
Default => 25
Support for configuring a different port than 25 for `RELAY_HOST` to use.
!!! note
Requires `RELAY_HOST`.
#### Relay Host Credentials
!!! warning "Configuring relay host credentials enforces outbound authentication"
Presently when `RELAY_USER` + `RELAY_PASSWORD` or `postfix-sasl-password.cf` are configured, all outbound mail traffic is configured to require a secure connection established and forbids the omission of credentials.
Additional feature work is required to only enforce these requirements on mail sent through a configured relay host.
##### RELAY_USER
- **empty** => no default
- default relay username (if no specific entry exists in postfix-sasl-password.cf)
##### RELAY_PASSWORD
- **empty** => no default
- password for default relay user
Provide the credentials to use with `RELAY_HOST` or `DEFAULT_RELAY_HOST`.
!!! tip "Alternative credentials config"
You may prefer to use `setup relay add-auth` to avoid risking ENV exposing secrets.
- With the CLI command, you must provide relay credentials for each of your sender domains.
- Alternatively manually edit `postfix-sasl-password.cf` with the correct relayhost entry (_`DEFAULT_RELAY_HOST` value, or as defined in `/etc/postfix/relayhost_map`_) to provide credentials per relayhost configured.
!!! abstract "Technical Details"
Credentials for relay hosts are configured internally using the:
- Postfix setting with config: [`smtp_sasl_password_maps = texthash:/etc/postfix/sasl_passwd`][postfix-config::sasl_passwd]
- DMS Config volume support via: `postfix-sasl-password.cf` (_generates `/etc/postfix/sasl_passwd`_)
---
When `postfix-sasl-password.cf` is present, DMS will copy it internally to `/etc/postfix/sasl_passwd`.
- DMS provides support for mapping credentials by sender domain:
- Explicitly via `setup relay add-auth` (_creates / updates `postfix-sasl-password.cf`_).
- Implicitly via the relay ENV support (_configures all known DMS managed domains to use the relay ENV_).
- Credentials can be explicitly configured for specific relay hosts instead of sender domains:
- Add the exact relayhost value (`host:port` / `[host]:port`) from the generated `/etc/postfix/relayhost_map`, or `main.cf:relayhost` (`DEFAULT_RELAY_HOST`).
- `setup relay ...` is missing support, you must instead add these manually to `postfix-sasl-password.cf`.
[gh-issue::vmail-uid-cannot-be-root]: https://github.com/docker-mailserver/docker-mailserver/issues/4098#issuecomment-2257201025
[docs-rspamd]: ./security/rspamd.md
[docs-faq-onedir]: ../faq.md#what-about-docker-datadmsmail-state-folder-varmail-state-internally
[docs-tls]: ./security/ssl.md
[docs-tls-letsencrypt]: ./security/ssl.md#lets-encrypt-recommended
[docs-tls-manual]: ./security/ssl.md#bring-your-own-certificates
[docs-tls-selfsigned]: ./security/ssl.md#self-signed-certificates
[docs-accounts-quota]: ./user-management.md#quotas
[docs-accounts-quota]: ./account-management/overview.md#quotas
[docs::account-management::overview]: ./account-management/overview.md
[docs::relay-host]: ./advanced/mail-forwarding/relay-hosts.md
[docs::dms-volumes-state]: ./advanced/optional-config.md#volumes-state
[postfix-config::relayhost]: https://www.postfix.org/postconf.5.html#relayhost
[postfix-config::relayhost_maps]: https://www.postfix.org/postconf.5.html#sender_dependent_relayhost_maps
[postfix-config::sasl_passwd]: https://www.postfix.org/postconf.5.html#smtp_sasl_password_maps
[gh-issue::tls-legacy-workaround]: https://github.com/docker-mailserver/docker-mailserver/pull/2945#issuecomment-1949907964
[gh-issue::hostname-compatibility]: https://github.com/docker-mailserver/docker-mailserver-helm/issues/168#issuecomment-2911782106
[gh-issue::dms-fqdn]: https://github.com/docker-mailserver/docker-mailserver/issues/3520#issuecomment-1700191973

View file

@ -14,18 +14,48 @@ hide:
## Configuration
!!! warning
Enabling Fail2Ban support can be done via ENV, but also requires granting at least the `NET_ADMIN` capability to interact with the kernel and ban IP addresses.
DMS must be launched with the `NET_ADMIN` capability in order to be able to install the NFTables rules that actually ban IP addresses. Thus, either include `--cap-add=NET_ADMIN` in the `docker run` command, or the equivalent in the `compose.yaml`:
!!! example
```yaml
cap_add:
- NET_ADMIN
```
=== "Docker Compose"
```yaml title="compose.yaml"
services:
mailserver:
environment:
- ENABLE_FAIL2BAN=1
cap_add:
- NET_ADMIN
```
=== "Docker CLI"
```bash
docker run --rm -it \
--cap-add=NET_ADMIN \
--env ENABLE_FAIL2BAN=1
```
!!! warning "Security risk of adding non-default capabilties"
DMS bundles F2B into the image for convenience to simplify integration and deployment.
The [`NET_ADMIN`][security::cap-net-admin] and [`NET_RAW`][security::cap-net-raw] capabilities are not granted by default to the container root user, as they can be used to compromise security.
If this risk concerns you, it may be wiser to instead prefer only granting these capabilities to a dedicated Fail2Ban container ([example][lsio:f2b-image]).
!!! bug "Running Fail2Ban on Older Kernels"
DMS configures F2B to use NFTables, not IPTables (legacy). We have observed that older systems, for example NAS systems, do not support the modern NFTables rules. You will need to configure F2B to use legacy IPTables again, for example with the [``fail2ban-jail.cf``][github-file-f2bjail], see the [section on configuration further down below](#custom-files).
DMS configures F2B to use [NFTables][network::nftables], not [IPTables (legacy)][network::iptables-legacy].
We have observed that older systems (for example NAS systems), do not support the modern NFTables rules. You will need to configure F2B to use legacy IPTables again, for example with the [`fail2ban-jail.cf`][github-file-f2bjail], see the [section on configuration further down below](#custom-files).
[security::cap-net-admin]: https://0xn3va.gitbook.io/cheat-sheets/container/escaping/excessive-capabilities#cap_net_admin
[security::cap-net-raw]: https://0xn3va.gitbook.io/cheat-sheets/container/escaping/excessive-capabilities#cap_net_raw
[lsio:f2b-image]: https://docs.linuxserver.io/images/docker-fail2ban
[network::nftables]: https://en.wikipedia.org/wiki/Nftables
[network::iptables-legacy]: https://developers.redhat.com/blog/2020/08/18/iptables-the-two-variants-and-their-relationship-with-nftables#two_variants_of_the_iptables_command
### DMS Defaults
@ -33,7 +63,7 @@ DMS will automatically ban IP addresses of hosts that have generated 6 failed at
### Custom Files
!!! question "What is [`docker-data/dms/config/`][docs-dms-config-volume]?"
!!! question "What is [`docker-data/dms/config/`][docs::dms-volumes-config]?"
This following configuration files inside the `docker-data/dms/config/` volume will be copied inside the container during startup
@ -44,10 +74,19 @@ This following configuration files inside the `docker-data/dms/config/` volume w
- with this file, you can adjust F2B behavior in general
- there is an example provided [in our repository on GitHub][github-file-f2bconfig]
[docs-dms-config-volume]: ../../faq.md#what-about-the-docker-datadmsconfig-directory
[docs::dms-volumes-config]: ../advanced/optional-config.md#volumes-config
[github-file-f2bjail]: https://github.com/docker-mailserver/docker-mailserver/blob/master/config-examples/fail2ban-jail.cf
[github-file-f2bconfig]: https://github.com/docker-mailserver/docker-mailserver/blob/master/config-examples/fail2ban-fail2ban.cf
### SASL
The `postfix` jail comes with `mode=extra` by default, which covers SASL login errors for our default SASL provider. Hence, the `postfix-sasl` jail has been disabled. If you switch to another SASL provider (e.g., SASLauthd), you may want to turn the `postfix-sasl` jail back on:
```ini title="docker-data/dms/config/fail2ban-jail.cf"
[postfix-sasl]
enabled = true
```
### Viewing All Bans
When just running
@ -78,7 +117,7 @@ docker exec <CONTAINER NAME> setup fail2ban [<ban|unban> <IP>]
docker exec <CONTAINER NAME> setup fail2ban log
```
## Running Inside A Rootless Container
## Running Inside A Rootless Container { #rootless-container }
[`RootlessKit`][rootless::rootless-kit] is the _fakeroot_ implementation for supporting _rootless mode_ in Docker and Podman. By default, RootlessKit uses the [`builtin` port forwarding driver][rootless::port-drivers], which does not propagate source IP addresses.

View file

@ -4,47 +4,73 @@ title: 'Security | Rspamd'
## About
Rspamd is a ["fast, free and open-source spam filtering system"][rspamd-homepage]. DMS integrates Rspamd like any other service. We provide a very simple but easy to maintain setup of Rspamd.
Rspamd is a ["fast, free and open-source spam filtering system"][rspamd-web]. DMS integrates Rspamd like any other service. We provide a basic but easy to maintain setup of Rspamd.
If you want to have a look at the default configuration files for Rspamd that DMS packs, navigate to [`target/rspamd/` inside the repository][dms-default-configuration]. Please consult the [section "The Default Configuration"](#the-default-configuration) section down below for a written overview.
If you want to take a look at the default configuration files for Rspamd that DMS adds, navigate to [`target/rspamd/` inside the repository][dms-repo::default-rspamd-configuration]. Please consult the [section "The Default Configuration"](#the-default-configuration) section down below for a written overview.
[rspamd-homepage]: https://rspamd.com/
[dms-default-configuration]: https://github.com/docker-mailserver/docker-mailserver/tree/master/target/rspamd
### Enable Rspamd
## Related Environment Variables
Rspamd is presently opt-in for DMS, but intended to become the default anti-spam service in a future release.
The following environment variables are related to Rspamd:
DMS offers two anti-spam solutions:
1. [`ENABLE_RSPAMD`](../environment.md#enable_rspamd)
2. [`ENABLE_RSPAMD_REDIS`](../environment.md#enable_rspamd_redis)
3. [`RSPAMD_CHECK_AUTHENTICATED`](../environment.md#rspamd_check_authenticated)
4. [`RSPAMD_GREYLISTING`](../environment.md#rspamd_greylisting)
5. [`RSPAMD_HFILTER`](../environment.md#rspamd_hfilter)
6. [`RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE`](../environment.md#rspamd_hfilter_hostname_unknown_score)
7. [`RSPAMD_LEARN`](../environment.md#rspamd_learn)
8. [`MOVE_SPAM_TO_JUNK`][docs-spam-to-junk]
9. [`MARK_SPAM_AS_READ`](../environment.md#mark_spam_as_read)
- Legacy (_Amavis, SpamAssassin, OpenDKIM, OpenDMARC_)
- Rspamd (_Provides equivalent features of software from the legacy solution_)
With these variables, you can enable Rspamd itself, and you can enable / disable certain features related to Rspamd.
While you could configure Rspamd to only replace some of the legacy services, it is advised to only use Rspamd with the legacy services disabled.
[docs-spam-to-junk]: ../environment.md#move_spam_to_junk
!!! example "Switch to Rspamd"
## The Default Configuration
To use Rspamd add the following ENV config changes:
```env
ENABLE_RSPAMD=1
# Rspamd replaces the functionality of all these anti-spam services, disable them:
ENABLE_OPENDKIM=0
ENABLE_OPENDMARC=0
ENABLE_POLICYD_SPF=0
ENABLE_AMAVIS=0
ENABLE_SPAMASSASSIN=0
# Greylisting is opt-in, if you had enabled Postgrey switch to the Rspamd equivalent:
ENABLE_POSTGREY=0
RSPAMD_GREYLISTING=1
# Optional: Add anti-virus support with ClamAV (compatible with Rspamd):
ENABLE_CLAMAV=1
```
!!! info "Relevant Environment Variables"
The following environment variables are related to Rspamd:
1. [`ENABLE_RSPAMD`](../environment.md#enable_rspamd)
2. [`ENABLE_RSPAMD_REDIS`](../environment.md#enable_rspamd_redis)
3. [`RSPAMD_CHECK_AUTHENTICATED`](../environment.md#rspamd_check_authenticated)
4. [`RSPAMD_GREYLISTING`](../environment.md#rspamd_greylisting)
5. [`RSPAMD_HFILTER`](../environment.md#rspamd_hfilter)
6. [`RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE`](../environment.md#rspamd_hfilter_hostname_unknown_score)
7. [`RSPAMD_LEARN`](../environment.md#rspamd_learn)
8. [`SPAM_SUBJECT`](../environment.md#spam_subject)
9. [`MOVE_SPAM_TO_JUNK`][docs::spam-to-junk]
10. [`MARK_SPAM_AS_READ`](../environment.md#mark_spam_as_read)
## Overview of Rspamd support
### Mode of Operation
!!! tip "Attention"
!!! note "Attention"
Read this section carefully if you want to understand how Rspamd is integrated into DMS and how it works (on a surface level).
Rspamd is integrated as a milter into DMS. When enabled, Postfix's `main.cf` configuration file includes the parameter `rspamd_milter = inet:localhost:11332`, which is added to `smtpd_milters`. As a milter, Rspamd can inspect incoming and outgoing e-mails.
Each mail is assigned what Rspamd calls symbols: when an e-mail matches a specific criterion, the mail receives a symbol. Afterwards, Rspamd applies a _spam score_ (as usual with anti-spam software) to the e-mail.
Each mail is assigned what Rspamd calls symbols: when an e-mail matches a specific criterion, the e-mail receives a symbol. Afterward, Rspamd applies a _spam score_ (as usual with anti-spam software) to the e-mail.
- The score itself is calculated by adding the values of the individual symbols applied earlier. The higher the spam score is, the more likely the e-mail is spam.
- Symbol values can be negative (i.e., these symbols indicate the mail is legitimate, maybe because [SPF and DKIM][docs-dkim-dmarc-spf] are verified successfully) or the symbol can be positive (i.e., these symbols indicate the e-mail is spam, maybe because the e-mail contains a lot of links).
- Symbol values can be negative (i.e., these symbols indicate the mail is legitimate, maybe because [SPF and DKIM][docs::dkim-dmarc-spf] are verified successfully). On the other hand, symbol scores can be positive (i.e., these symbols indicate the e-mail is spam, perhaps because the e-mail contains numerous links).
Rspamd then adds (a few) headers to the e-mail based on the spam score. Most important are `X-Spamd-Result`, which contains an overview of which symbols were applied. It could look like this:
Rspamd then adds (a few) headers to the e-mail based on the spam score. Most important is `X-Spamd-Result`, which contains an overview of which symbols were applied. It could look like this:
```txt
X-Spamd-Result default: False [-2.80 / 11.00]; R_SPF_NA(1.50)[no SPF record]; R_DKIM_ALLOW(-1.00)[example.com:s=dtag1]; DWL_DNSWL_LOW(-1.00)[example.com:dkim]; RWL_AMI_LASTHOP(-1.00)[192.0.2.42:from]; DMARC_POLICY_ALLOW(-1.00)[example.com,none]; RWL_MAILSPIKE_EXCELLENT(-0.40)[192.0.2.42:from]; FORGED_SENDER(0.30)[noreply@example.com,some-reply-address@bounce.example.com]; RCVD_IN_DNSWL_LOW(-0.10)[192.0.2.42:from]; MIME_GOOD(-0.10)[multipart/mixed,multipart/related,multipart/alternative,text/plain]; MIME_TRACE(0.00)[0:+,1:+,2:+,3:+,4:~,5:~,6:~]; RCVD_COUNT_THREE(0.00)[3]; RCPT_COUNT_ONE(0.00)[1]; REPLYTO_DN_EQ_FROM_DN(0.00)[]; ARC_NA(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; RCVD_TLS_LAST(0.00)[]; DKIM_TRACE(0.00)[example.com:+]; HAS_ATTACHMENT(0.00)[]; TO_DN_NONE(0.00)[]; FROM_NEQ_ENVFROM(0.00)[noreply@example.com,some-reply-address@bounce.example.com]; FROM_HAS_DN(0.00)[]; REPLYTO_DOM_NEQ_FROM_DOM(0.00)[]; PREVIOUSLY_DELIVERED(0.00)[receiver@anotherexample.com]; ASN(0.00)[asn:3320, ipnet:192.0.2.0/24, country:DE]; MID_RHS_MATCH_FROM(0.00)[]; MISSING_XM_UA(0.00)[]; HAS_REPLYTO(0.00)[some-reply-address@dms-reply.example.com]
@ -56,52 +82,74 @@ And then there is a corresponding `X-Rspamd-Action` header, which shows the over
X-Rspamd-Action no action
```
Since the score is `-2.80`, nothing will happen and the e-mail is not classified as spam. Our custom [`actions.conf`][rspamd-actions-config] defines what to do at certain scores:
Since the score is `-2.80`, nothing will happen and the e-mail is not classified as spam. Our custom [`actions.conf`][dms-repo::rspamd-actions-config] defines what to do at certain scores:
1. At a score of 4, the e-mail is to be _greylisted_;
2. At a score of 6, the e-mail is _marked with a header_ (`X-Spam: Yes`);
3. At a score of 7, the e-mail will additionally have their _subject re-written_ (appending a prefix like `[SPAM]`);
4. At a score of 11, the e-mail is outright _rejected_.
3. At a score of 11, the e-mail is outright _rejected_.
---
There is more to spam analysis than meets the eye: we have not covered the [Bayes training and filters][rspamc-docs-bayes] here, nor have we talked about [Sieve rules for e-mails that are marked as spam][docs-spam-to-junk].
There is more to spam analysis than meets the eye: we have not covered the [Bayes training and filters][rspamd-docs::bayes] here, nor have we discussed [Sieve rules for e-mails that are marked as spam][docs::spam-to-junk].
Even the calculation of the score with the individual symbols has been presented to you in a simplified manner. But with the knowledge from above, you're equipped to read on and use Rspamd confidently. Keep on reading to understand the integration even better - you will want to know about your anti-spam software, not only to keep the bad e-mail out, but also to make sure the good e-mail arrive properly!
[docs-dkim-dmarc-spf]: ../best-practices/dkim_dmarc_spf.md
[rspamd-actions-config]: https://github.com/docker-mailserver/docker-mailserver/blob/master/target/rspamd/local.d/actions.conf
[rspamc-docs-bayes]: https://rspamd.com/doc/configuration/statistic.html
### Workers
The proxy worker operates in [self-scan mode][rspamd-docs-proxy-self-scan-mode]. This simplifies the setup as we do not require a normal worker. You can easily change this though by [overriding the configuration by DMS](#providing-custom-settings-overriding-settings).
The proxy worker operates in [self-scan mode][rspamd-docs::proxy-self-scan-mode]. This simplifies the setup as we do not require a normal worker. You can easily change this though by [overriding the configuration by DMS](#providing-custom-settings-overriding-settings).
DMS does not set a default password for the controller worker. You may want to do that yourself. In setups where you already have an authentication provider in front of the Rspamd webpage, you may want to [set the `secure_ip ` option to `"0.0.0.0/0"` for the controller worker](#with-the-help-of-a-custom-file) to disable password authentication inside Rspamd completely.
[rspamd-docs-proxy-self-scan-mode]: https://rspamd.com/doc/workers/rspamd_proxy.html#self-scan-mode
### Persistence with Redis
When Rspamd is enabled, we implicitly also start an instance of Redis in the container. Redis is configured to persist its data via RDB snapshots to disk in the directory `/var/lib/redis` (_which is a symbolic link to `/var/mail-state/lib-redis/` when [`ONE_DIR=1`](../environment.md#one_dir) and a volume is mounted to `/var/mail-state/`_). With the volume mount the snapshot will restore the Redis data across container restarts, and provide a way to keep backup.
When Rspamd is enabled, we implicitly also start an instance of Redis in the container:
Redis uses `/etc/redis/redis.conf` for configuration. We adjust this file when enabling the internal Redis service. If you have an external instance of Redis to use, the internal Redis service can be opt-out via setting the ENV [`ENABLE_RSPAMD_REDIS=0`](../environment.md#enable_rspamd_redis) (_link also details required changes to the DMS Rspamd config_).
- Redis is configured to persist its data via RDB snapshots to disk in the directory `/var/lib/redis` (_or the [`/var/mail-state/`][docs::dms-volumes-state] volume when present_).
- With the volume mount, the snapshot will restore the Redis data across container updates, and provide a way to keep a backup.
- Without a volume mount a containers internal state will persist across restarts until the container is recreated due to changes like ENV or upgrading the image for the container.
Redis uses `/etc/redis/redis.conf` for configuration:
- We adjust this file when enabling the internal Redis service.
- If you have an external instance of Redis to use, the internal Redis service can be opt-out via setting the ENV [`ENABLE_RSPAMD_REDIS=0`][docs::env::enable-redis] (_link also details required changes to the DMS Rspamd config_).
If you are interested in using Valkey instead of Redis, please refer to [this guidance][gh-dms::guide::valkey].
### Web Interface
Rspamd provides a [web interface][rspamc-docs-web-interface], which contains statistics and data Rspamd collects. The interface is enabled by default and reachable on port 11334.
Rspamd provides a [web interface][rspamd-docs::web-ui], which contains statistics and data Rspamd collects. The interface is enabled by default and reachable on port 11334.
![Rspamd Web Interface](https://rspamd.com/img/webui.png)
[rspamc-docs-web-interface]: https://rspamd.com/webui/
To use the web interface you will need to configure a password, [otherwise you won't be able to log in][rspamd-docs::web-ui::password].
??? example "Set a custom password"
Add this line to [your Rspamd `custom-commands.conf` config](#with-the-help-of-a-custom-file) which sets the `password` option of the _controller worker_:
```
set-option-for-controller password "your hashed password here"
```
The password hash can be generated via the `rspamadm pw` command:
```bash
docker exec -it <CONTAINER_NAME> rspamadm pw
```
---
**Related:** A minimal Rspamd `compose.yaml` [example with a reverse-proxy for web access][gh-dms::guide::rspamd-web].
### DNS
DMS does not supply custom values for DNS servers to Rspamd. If you need to use custom DNS servers, which could be required when using [DNS-based black/whitelists](#rbls-realtime-blacklists-dnsbls-dns-based-blacklists), you need to adjust [`options.inc`][rspamd-docs-basic-options] yourself.
DMS does not supply custom values for DNS servers (to Rspamd). If you need to use custom DNS servers, which could be required when using [DNS-based deny/allowlists](#rbls-real-time-blacklists-dnsbls-dns-based-blacklists), you need to adjust [`options.inc`][rspamd-docs::config::global] yourself. Make sure to also read our [FAQ page on DNS servers][docs::faq::dns-servers].
!!! tip "Making DNS Servers Configurable"
!!! warning
If you want to see an environment variable (like `RSPAMD_DNS_SERVERS`) to support custom DNS servers for Rspamd being added to DMS, please raise a feature request issue.
Rspamd heavily relies on a properly working DNS server that it can use to resolve DNS queries. If your DNS server is misconfigured, you will encounter issues when Rspamd queries DNS to assess if mail is spam. Legitimate mail is then unintentionally marked as spam or worse, rejected entirely.
When Rspamd is deciding if mail is spam, it will check DNS records for SPF, DKIM and DMARC. Each of those has an associated symbol for DNS temporary errors with a non-zero weight assigned. That weight contributes towards the spam score assessed by Rspamd which is normally desirable - provided your network DNS is functioning correctly, otherwise when DNS is broken all mail is biased towards spam due to these failed DNS lookups.
!!! danger
@ -115,9 +163,7 @@ You can find the Rspamd logs at `/var/log/mail/rspamd.log`, and the correspondin
### Modules
You can find a list of all Rspamd modules [on their website][rspamd-docs-modules].
[rspamd-docs-modules]: https://rspamd.com/doc/modules/
You can find a list of all Rspamd modules [on their website][rspamd-docs::modules].
#### Disabled By Default
@ -129,126 +175,195 @@ You can choose to enable ClamAV, and Rspamd will then use it to check for viruse
#### RBLs (Real-time Blacklists) / DNSBLs (DNS-based Blacklists)
The [RBL module](https://rspamd.com/doc/modules/rbl.html) is enabled by default. As a consequence, Rspamd will perform DNS lookups to a variety of blacklists. Whether an RBL or a DNSBL is queried depends on where the domain name was obtained: RBL servers are queried with IP addresses extracted from message headers, DNSBL server are queried with domains and IP addresses extracted from the message body \[[source][rbl-vs-dnsbl]\].
The [RBL module][rspamd-docs::modules::rbl] is enabled by default. As a consequence, Rspamd will perform DNS lookups to various blacklists. Whether an RBL or a DNSBL is queried depends on where the domain name was obtained: RBL servers are queried with IP addresses extracted from message headers, DNSBL server are queried with domains and IP addresses extracted from the message body ([source][www::rbl-vs-dnsbl]).
!!! danger "Rspamd and DNS Block Lists"
When the RBL module is enabled, Rspamd will do a variety of DNS requests to (amongst other things) DNSBLs. There are a variety of issues involved when using DNSBLs. Rspamd will try to mitigate some of them by properly evaluating all return codes. This evaluation is a best effort though, so if the DNSBL operators change or add return codes, it may take a while for Rspamd to adjust as well.
If you want to use DNSBLs, **try to use your own DNS resolver** and make sure it is set up correctly, i.e. it should be a non-public & **recursive** resolver. Otherwise, you might not be able ([see this Spamhaus post](https://www.spamhaus.org/faq/section/DNSBL%20Usage#365)) to make use of the block lists.
[rbl-vs-dnsbl]: https://forum.eset.com/topic/25277-dnsbl-vs-rbl-mail-security/?do=findComment&comment=119818
If you want to use DNSBLs, **try to use your own DNS resolver** and make sure it is set up correctly, i.e. it should be a non-public & **recursive** resolver. Otherwise, you might not be able ([see this Spamhaus post][spamhaus::faq::dnsbl-usage]) to make use of the block lists.
## Providing Custom Settings & Overriding Settings
DMS brings sane default settings for Rspamd. They are located at `/etc/rspamd/local.d/` inside the container (or `target/rspamd/local.d/` in the repository).
!!! info "Rspamd config overriding precedence"
### Manually
Rspamd has a layered approach for configuration with [`local.d` and `override.d` config directories][rspamd-docs::config-directories].
!!! question "What is [`docker-data/dms/config/`][docs-dms-config-volume]?"
- DMS [extends the Rspamd default configs via `/etc/rspamd/local.d/`][dms-repo::default-rspamd-configuration].
- User config changes should be handled separately as overrides via the [DMS Config Volume][docs::dms-volumes-config] (`docker-data/dms/config/`) with either:
- `./rspamd/override.d/` - Config files placed here are copied to `/etc/rspamd/override.d/` during container startup.
- [`./rspamd/custom-commands.conf`](#with-the-help-of-a-custom-file) - Applied after copying any provided configs from `rspamd/override.d/` (DMS Config volume) to `/etc/rspamd/override.d/`.
If you want to overwrite the default settings and / or provide your own settings, you can place files at `docker-data/dms/config/rspamd/override.d/`. Files from this directory are copied to `/etc/rspamd/override.d/` during startup. These files [forcibly override][rspamd-docs-override-dir] Rspamd and DMS default settings.
!!! abstract "Reference docs for Rspamd config"
!!! question "What is the [`local.d` directory and how does it compare to `override.d`][rspamd-docs-config-directories]?"
- [Config Overview][rspamd-docs::config::overview], [Quickstart guide][rspamd-docs::config::quickstart], and [Config Syntax (UCL)][rspamd-docs::config::ucl-syntax]
- Global Options ([`options.inc`][rspamd-docs::config::global])
- [Workers][rspamd-docs::config::workers] ([`worker-controller.inc`][rspamd-docs::config::worker-controller], [`worker-proxy.inc`][rspamd-docs::config::worker-proxy])
- [Modules][rspamd-docs::modules] (_view each module page for their specific config options_)
!!! warning "Clashing Overrides"
!!! tip "View rendered config"
Note that when also [using the `custom-commands.conf` file](#with-the-help-of-a-custom-file), files in `override.d` may be overwritten in case you adjust them manually and with the help of the file.
`rspamadm configdump` will output the full rspamd configuration that is used should you need it for troubleshooting / inspection.
[rspamd-docs-override-dir]: https://www.rspamd.com/doc/faq.html#what-are-the-locald-and-overrided-directories
[docs-dms-config-volume]: ../../faq.md#what-about-the-docker-datadmsconfig-directory
[rspamd-docs-config-directories]: https://rspamd.com/doc/faq.html#what-are-the-locald-and-overrided-directories
- You can also see which modules are enabled / disabled via `rspamadm configdump --modules-state`
- Specific config sections like `dkim` or `worker` can also be used to filter the output to just those sections: `rspamadm configdump dkim worker`
- Use `--show-help` to include inline documentation for many settings.
### With the Help of a Custom File
### Using `custom-commands.conf` { #with-the-help-of-a-custom-file }
DMS provides the ability to do simple adjustments to Rspamd modules with the help of a single file. Just place a file called `custom-commands.conf` into `docker-data/dms/config/rspamd/`. If this file is present, DMS will evaluate it. The structure is _very_ simple. Each line in the file looks like this:
For convenience DMS provides a single config file that will directly create or modify multiple configs at `/etc/rspamd/override.d/`. This is handled as the final rspamd configuration step during container startup.
```txt
COMMAND ARGUMENT1 ARGUMENT2 ARGUMENT3
```
DMS will apply this config when you provide `rspamd/custom-commands.conf` in your DMS Config volume. Configure it with directive lines as documented below.
where `COMMAND` can be:
!!! note "Only use this feature for `option = value` changes"
1. `disable-module`: disables the module with name `ARGUMENT1`
2. `enable-module`: explicitly enables the module with name `ARGUMENT1`
3. `set-option-for-module`: sets the value for option `ARGUMENT2` to `ARGUMENT3` inside module `ARGUMENT1`
4. `set-option-for-controller`: set the value of option `ARGUMENT1` to `ARGUMENT2` for the controller worker
5. `set-option-for-proxy`: set the value of option `ARGUMENT1` to `ARGUMENT2` for the proxy worker
6. `set-common-option`: set the option `ARGUMENT1` that [defines basic Rspamd behavior][rspamd-docs-basic-options] to value `ARGUMENT2`
7. `add-line`: this will add the complete line after `ARGUMENT1` (with all characters) to the file `/etc/rspamd/override.d/<ARGUMENT1>`
`custom-commands.conf` is only suitable for adding or replacing simple `option = value` settings for configs at `/etc/rspamd/override.d/`.
- New settings are appended to the associated config file.
- When replacing an existing setting in an override config, that setting may be any matching line (_allowing for nested scopes, instead of only top-level keys_).
Any changes involving more advanced [UCL config syntax][rspamd-docs::config::ucl-syntax] should instead add UCL config files directly to `rspamd/override.d/` (_in the DMS Config volume_).
!!! example "An Example Is [Shown Down Below](#adjusting-and-extending-the-very-basic-configuration)"
!!! info "`custom-commands.conf` syntax"
!!! note "File Names & Extensions"
There are 7 directives available to manage custom Rspamd configurations. Add these directive lines into `custom-commands.conf`, they will be processed sequentially.
For command 1 - 3, we append the `.conf` suffix to the module name to get the correct file name automatically. For commands 4 - 6, the file name is fixed (you don't even need to provide it). For command 7, you will need to provide the whole file name (including the suffix) yourself!
**Directives:**
You can also have comments (the line starts with `#`) and blank lines in `custom-commands.conf` - they are properly handled and not evaluated.
```txt
# For /etc/rspamd/override.d/{options.inc,worker-controller.inc,worker-proxy}.inc
set-common-option <OPTION NAME> <OPTION VALUE>
set-option-for-controller <OPTION NAME> <OPTION VALUE>
set-option-for-proxy <OPTION NAME> <OPTION VALUE>
!!! tip "Adjusting Modules This Way"
# For /etc/rspamd/override.d/<MODULE NAME>.conf
enable-module <MODULE NAME>
disable-module <MODULE NAME>
set-option-for-module <MODULE NAME> <OPTION NAME> <OPTION VALUE>
These simple commands are meant to give users the ability to _easily_ alter modules and their options. As a consequence, they are not powerful enough to enable multi-line adjustments. If you need to do something more complex, we advise to do that [manually](#manually)!
# For /etc/rspamd/override.d/<FILENAME>
add-line <FILENAME> <CONTENT>
```
[rspamd-docs-basic-options]: https://rspamd.com/doc/configuration/options.html
**Syntax:**
## Examples & Advanced Configuration
- Blank lines are ok.
- `#` at the start of a line represents a comment for adding notes.
- `<OPTION VALUE>` and `<CONTENT>` will contain the remaining content of their line, any preceding inputs are delimited by white-space.
### A Very Basic Configuration
---
You want to start using Rspamd? Rspamd is disabled by default, so you need to set the following environment variables:
??? note "`<MODULE NAME>` can also target non-module configs"
```cf
ENABLE_RSPAMD=1
ENABLE_OPENDKIM=0
ENABLE_OPENDMARC=0
ENABLE_POLICYD_SPF=0
ENABLE_AMAVIS=0
ENABLE_SPAMASSASSIN=0
```
An example is the `statistics` module, which has config to import a separate file (`classifier-bayes.conf`) for easier overrides to this section of the module config.
This will enable Rspamd and disable services you don't need when using Rspamd.
??? example
### Adjusting and Extending The Very Basic Configuration
Rspamd is running, but you want or need to adjust it? First, create a file named `custom-commands.conf` under `docker-data/dms/config/rspamd` (which translates to `/tmp/docker-mailserver/rspamd/` inside the container). Then add you changes:
1. Say you want to be able to easily look at the frontend Rspamd provides on port 11334 (default) without the need to enter a password (maybe because you already provide authorization and authentication). You will need to adjust the controller worker: `set-option-for-controller secure_ip "0.0.0.0/0"`.
2. You additionally want to enable the auto-spam-learning for the Bayes module? No problem: `set-option-for-module classifier-bayes autolearn true`.
3. But the chartable module gets on your nerves? Easy: `disable-module chartable`.
??? example "What Does the Result Look Like?"
Here is what the file looks like in the end:
```bash
# See 1.
# ATTENTION: this disables authentication on the website - make sure you know what you're doing!
```conf title="rspamd/custom-commands.conf"
# If you're confident you've properly secured access to the rspamd web service/API (Default port: 11334)
# with your own auth layer (eg: reverse-proxy) you can bypass rspamd requiring credentials:
# https://rspamd.com/doc/workers/controller.html#controller-configuration
set-option-for-controller secure_ip "0.0.0.0/0"
# See 2.
# Some settings aren't documented well, you may find them in snippets or Rspamds default config files:
# https://rspamd.com/doc/tutorials/quickstart.html#using-of-milter-protocol-for-rspamd--16
# /etc/rspamd/worker-proxy.inc
set-option-for-proxy reject_message "Rejected - Detected as spam"
# Equivalent to the previous example, but `add-line` is more verbose:
add-line worker-proxy.inc reject_message = "Rejected - Detected as spam"
# Enable Bayes auto-learning feature to classify spam based on Rspamd action/score results:
# NOTE: The statistics module imports a separate file for classifier-bayes config
# https://rspamd.com/doc/configuration/statistic.html#autolearning
set-option-for-module classifier-bayes autolearn true
# See 3.
# Disable the `chartable` module:
# https://rspamd.com/doc/modules/chartable.html
disable-module chartable
```
## Advanced Configuration
### DKIM Signing
There is a dedicated [section for setting up DKIM with Rspamd in our documentation][docs-dkim-with-rspamd].
There is a dedicated [section for setting up DKIM with Rspamd in our documentation][docs::dkim-with-rspamd].
[docs-dkim-with-rspamd]: ../best-practices/dkim_dmarc_spf.md#dkim
### ARC (Authenticated Received Chain)
[ARC][wikipedia::arc] support in DMS is opt-in via config file. [Enable the ARC Rspamd module][rspamd-docs::arc] by creating a config file at `docker-data/dms/config/rspamd/override.d/arc.conf`.
!!! example
For each mail domain you have DMS manage, add the equivalent `example.com` sub-section to `domain` and adjust the `path` + `selector` fields as necessary.
```conf title="rspamd/override.d/arc.conf"
sign_local = true;
sign_authenticated = true;
domain {
example.com {
path = "/tmp/docker-mailserver/rspamd/dkim/rsa-2048-mail-example.private.txt";
selector = "mail";
}
}
```
!!! tip "Using a common keypair"
As with DKIM, the keypair can be shared across your configured domains.
Your ARC config can share the same DKIM private key + selector (_with associated DNS record for the public key_).
### _Abusix_ Integration
This subsection gives information about the integration of [Abusix], "a set of blocklists that work as an additional email security layer for your existing mail environment". The setup is straight-forward and well documented:
This subsection provides information about the integration of [Abusix][abusix-web], "a set of blocklists that work as an additional email security layer for your existing mail environment". The setup is straight-forward and well documented:
1. [Create an account](https://app.abusix.com/signup)
1. [Create an account][abusix-web::register]
2. Retrieve your API key
3. Navigate to the ["Getting Started" documentation for Rspamd][abusix-rspamd-integration] and follow the steps described there
3. Navigate to the ["Getting Started" documentation for Rspamd][abusix-docs::rspamd-integration] and follow the steps described there
4. Make sure to change `<APIKEY>` to your private API key
We recommend mounting the files directly into the container, as they are rather big and not manageable with the [modules script](#with-the-help-of-a-custom-file). If mounted to the correct location, Rspamd will automatically pick them up.
We recommend mounting the files directly into the container, as they are rather big and not manageable with our [`custom-command.conf` script](#with-the-help-of-a-custom-file). If mounted to the correct location, Rspamd will automatically pick them up.
While _Abusix_ can be integrated into Postfix, Postscreen and a multitude of other software, we recommend integrating _Abusix_ only into a single piece of software running in your mail server - everything else would be excessive and wasting queries. Moreover, we recommend the integration into suitable filtering software and not Postfix itself, as software like Postscreen or Rspamd can properly evaluate the return codes and other configuration.
[Abusix]: https://abusix.com/
[abusix-rspamd-integration]: https://docs.abusix.com/abusix-mail-intelligence/gbG8EcJ3x3fSUv8cMZLiwA/getting-started/dmw9dcwSGSNQiLTssFAnBW#rspamd
[rspamd-web]: https://rspamd.com/
[rspamd-docs::bayes]: https://rspamd.com/doc/configuration/statistic.html
[rspamd-docs::proxy-self-scan-mode]: https://rspamd.com/doc/workers/rspamd_proxy.html#self-scan-mode
[rspamd-docs::web-ui]: https://rspamd.com/webui/
[rspamd-docs::web-ui::password]: https://www.rspamd.com/doc/tutorials/quickstart.html#setting-the-controller-password
[rspamd-docs::modules]: https://rspamd.com/doc/modules/
[rspamd-docs::modules::rbl]: https://rspamd.com/doc/modules/rbl.html
[rspamd-docs::config-directories]: https://rspamd.com/doc/faq.html#what-are-the-locald-and-overrided-directories
[rspamd-docs::config::ucl-syntax]: https://rspamd.com/doc/configuration/ucl.html
[rspamd-docs::config::overview]: https://rspamd.com/doc/configuration/index.html
[rspamd-docs::config::quickstart]: https://rspamd.com/doc/tutorials/quickstart.html#configuring-rspamd
[rspamd-docs::config::global]: https://rspamd.com/doc/configuration/options.html
[rspamd-docs::config::workers]: https://rspamd.com/doc/workers/
[rspamd-docs::config::worker-controller]: https://rspamd.com/doc/workers/controller.html
[rspamd-docs::config::worker-proxy]: https://rspamd.com/doc/workers/rspamd_proxy.html
[wikipedia::arc]: https://en.wikipedia.org/wiki/Authenticated_Received_Chain
[rspamd-docs::arc]: https://rspamd.com/doc/modules/arc.html
[www::rbl-vs-dnsbl]: https://forum.eset.com/topic/25277-dnsbl-vs-rbl-mail-security/#comment-119818
[abusix-web]: https://abusix.com/
[abusix-web::register]: https://app.abusix.com/
[abusix-docs::rspamd-integration]: https://abusix.com/docs/rspamd/
[spamhaus::faq::dnsbl-usage]: https://www.spamhaus.org/faq/section/DNSBL%20Usage#365
[dms-repo::rspamd-actions-config]: https://github.com/docker-mailserver/docker-mailserver/tree/v15.0.0/target/rspamd/local.d/actions.conf
[dms-repo::default-rspamd-configuration]: https://github.com/docker-mailserver/docker-mailserver/tree/v15.0.0/target/rspamd
[gh-dms::guide::valkey]: https://github.com/docker-mailserver/docker-mailserver/issues/4001#issuecomment-2652596692
[gh-dms::guide::rspamd-web]: https://github.com/orgs/docker-mailserver/discussions/4269#discussioncomment-11329588
[docs::env::enable-redis]: ../environment.md#enable_rspamd_redis
[docs::spam-to-junk]: ../environment.md#move_spam_to_junk
[docs::dkim-dmarc-spf]: ../best-practices/dkim_dmarc_spf.md
[docs::dkim-with-rspamd]: ../best-practices/dkim_dmarc_spf.md#dkim
[docs::dms-volumes-config]: ../advanced/optional-config.md#volumes-config
[docs::dms-volumes-state]: ../advanced/optional-config.md#volumes-state
[docs::faq::dns-servers]: ../../faq.md#what-about-dns-servers

View file

@ -6,14 +6,14 @@ There are multiple options to enable SSL (via [`SSL_TYPE`][docs-env::ssl-type]):
- Using [letsencrypt](#lets-encrypt-recommended) (recommended)
- Using [Caddy](#caddy)
- Using [Traefik](#traefik-v2)
- Using [Traefik](#traefik)
- Using [self-signed certificates](#self-signed-certificates)
- Using [your own certificates](#bring-your-own-certificates)
After installation, you can test your setup with:
- [`checktls.com`](https://www.checktls.com/TestReceiver)
- [`testssl.sh`](https://github.com/drwetter/testssl.sh)
- [`testssl.sh`](https://github.com/testssl/testssl.sh)
!!! warning "Exposure of DNS labels through Certificate Transparency"
@ -134,6 +134,8 @@ Certbot provisions certificates to `/etc/letsencrypt`. Add a volume to store the
```
This process can also be [automated via _cron_ or _systemd timers_][certbot::automated-renewal].
- [Example with a systemd timer][certbot::automated-renewal::example-systemd-timer]
!!! note "Using a different ACME CA"
@ -238,7 +240,7 @@ After completing the steps above, your certificate should be ready to use.
image: certbot/dns-cloudflare:latest
command: renew --dns-cloudflare --dns-cloudflare-credentials /run/secrets/cloudflare-api-token
volumes:
- ./docker-data/certbot/certs/:/etc/letsencrtypt/
- ./docker-data/certbot/certs/:/etc/letsencrypt/
- ./docker-data/certbot/logs/:/var/log/letsencrypt/
secrets:
- cloudflare-api-token
@ -408,7 +410,7 @@ The following example is the [basic setup][acme-companion::basic-setup] you need
- `LETSENCRYPT_TEST=true`: _Recommended during initial setup_. Otherwise the default production endpoint has a [rate limit of 5 duplicate certificates per week][letsencrypt::limits]. Overrides `ACME_CA_URI` to use the _Let's Encrypt_ staging endpoint.
- `LETSENCRYPT_EMAIL`: For when you don't use `DEFAULT_EMAIL` on `acme-companion`, or want to assign a different email contact for this container.
- `LETSENCRYPT_KEYSIZE`: Allows you to configure the type (RSA or ECDSA) and size of the private key for your certificate. Default is RSA 4096.
- `LETSENCRYPT_KEYSIZE`: Allows you to configure the type (RSA or ECDSA) and size of the private key for your certificate. Default is RSA 4096, but RSA 2048 is recommended.
- `LETSENCRYPT_RESTART_CONTAINER=true`: When the certificate is renewed, the entire container will be restarted to ensure the new certificate is used.
[`acme-companion` ENV for default settings][acme-companion::env-config] that apply to all containers using `LETSENCRYPT_HOST`:
@ -450,8 +452,8 @@ The following example is the [basic setup][acme-companion::basic-setup] you need
# Optional variables:
LETSENCRYPT_mail_TEST=true
LETSENCRYPT_mail_EMAIL='admin@example.com'
# RSA-4096 => `4096`, ECDSA-256 => `ec-256`:
LETSENCRYPT_mail_KEYSIZE=4096
# Supported values are `2048`, `3072` and `4096` for RSA keys, and `ec-256` or `ec-384` for elliptic curve keys.
LETSENCRYPT_mail_KEYSIZE=2048
```
Unlike with the equivalent ENV for containers, [changes to this file will **not** be detected automatically][acme-companion::standalone-changes]. You would need to wait until the next renewal check by `acme-companion` (_every hour by default_), restart `acme-companion`, or [manually invoke the _service loop_][acme-companion::service-loop]:
@ -479,114 +481,133 @@ DSM-generated letsencrypt certificates get auto-renewed every three months.
### Caddy
For Caddy v2 you can specify the `key_type` in your server's global settings, which would end up looking something like this if you're using a `Caddyfile`:
[Caddy][web::caddy] is an open-source web server with built-in TLS certificate generation. You can use the [official Docker image][dockerhub::caddy] and write your own `Caddyfile`.
```caddyfile
{
debug
admin localhost:2019
http_port 80
https_port 443
default_sni example.com
key_type rsa4096
}
```
!!! example
If you are instead using a json config for Caddy v2, you can set it in your site's TLS automation policies:
While DMS does not need a webserver to work, this workaround will provision a TLS certificate for DMS to use by adding a dummy site block to trigger cert provisioning.
??? example "Caddy v2 JSON example snippet"
```yaml title="compose.yaml"
services:
# Basic Caddy service to provision certs:
reverse-proxy:
image: caddy:2.7
ports:
- 80:80
- 443:443
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ${CADDY_DATA_DIR}:/data
```json
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"mail.example.com",
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
},
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"mail.example.com",
],
"key_type": "rsa2048",
"issuer": {
"email": "admin@example.com",
"module": "acme"
}
},
{
"issuer": {
"email": "admin@example.com",
"module": "acme"
}
}
]
}
}
# Share the Caddy data volume for certs and configure SSL_TYPE to `letsencrypt`
mailserver:
image: ghcr.io/docker-mailserver/docker-mailserver:latest
hostname: mail.example.com
environment:
SSL_TYPE: letsencrypt
# While you could use a named data volume instead of a bind mount volume, it would require the long-syntax to rename cert files:
# https://docs.docker.com/compose/compose-file/05-services/#volumes
volumes:
- ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.crt:/etc/letsencrypt/live/mail.example.com/fullchain.pem
- ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.key:/etc/letsencrypt/live/mail.example.com/privkey.pem
```
An explicit entry in your `Caddyfile` config will have Caddy provision and renew a certificate for your DMS FQDN:
```caddyfile title="Caddyfile"
mail.example.com {
# Optionally provision RSA 2048-bit certificate instead of ECDSA P-256:
tls {
key_type rsa2048
}
# Optional, can be useful for troubleshooting
# connection to Caddy with correct certificate:
respond "Hello DMS"
}
```
The generated certificates can then be mounted:
!!! info
```yaml
volumes:
- ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.crt:/etc/letsencrypt/live/mail.example.com/fullchain.pem
- ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.key:/etc/letsencrypt/live/mail.example.com/privkey.pem
```
An explicit `tls` directive affects only the site-address block it's used in:
### Traefik v2
- Use [`tls internal { ... }`][caddy-docs::tls-internal] if wanting to create a local self-signed cert, which may be useful for testing. This allows opt-in to use self-signed certs unlike the global `local_certs` option.
- [`key_type`][caddy-docs::key-type] can be used in the `tls` block if you need to enforce RSA as the key type for certificates provisioned. The default is currently ECDSA (P-256). This may improve compatibility with legacy clients.
[Traefik][traefik::github] is an open-source application proxy using the [ACME protocol][ietf::rfc::acme]. [Traefik][traefik::github] can request certificates for domains and subdomains, and it will take care of renewals, challenge negotiations, etc. We strongly recommend to use [Traefik][traefik::github]'s major version 2.
??? example "With `caddy-docker-proxy`"
[Traefik][traefik::github]'s storage format is natively supported if the `acme.json` store is mounted into the container at `/etc/letsencrypt/acme.json`. The file is also monitored for changes and will trigger a reload of the mail services (Postfix and Dovecot).
Using [`lucaslorentz/caddy-docker-proxy`][github::caddy-docker-proxy] allows you to generate a `Caddyfile` by adding labels to your services in `compose.yaml`:
Wildcard certificates are supported. If your FQDN is `mail.example.com` and your wildcard certificate is `*.example.com`, add the ENV: `#!bash SSL_DOMAIN=example.com`.
```yaml title="compose.yaml"
services:
reverse-proxy:
image: lucaslorentz/caddy-docker-proxy:2.8
ports:
- 80:80
- 443:443
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ${CADDY_DATA_DIR}:/data
labels:
# Set global config here, this option has an empty value to enable self-signed certs for local testing:
# NOTE: Remove this label when going to production.
caddy.local_certs: ""
DMS will select it's certificate from `acme.json` checking these ENV for a matching FQDN (_in order of priority_):
# Use labels to configure Caddy to provision DMS certs
mailserver:
image: ghcr.io/docker-mailserver/docker-mailserver:latest
hostname: mail.example.com
environment:
SSL_TYPE: letsencrypt
volumes:
- ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.crt:/etc/letsencrypt/live/mail.example.com/fullchain.pem
- ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.key:/etc/letsencrypt/live/mail.example.com/privkey.pem
labels:
# Set your DMS FQDN here to add the site-address into the generated Caddyfile:
caddy_0: mail.example.com
# Adding a dummy directive is required:
caddy_0.respond: "Hello DMS"
# Uncomment to make a proxy for Rspamd:
# caddy_1: rspamd.example.com
# caddy_1.reverse_proxy: "{{upstreams 11334}}"
```
1. `#!bash ${SSL_DOMAIN}`
2. `#!bash ${HOSTNAME}`
3. `#!bash ${DOMAINNAME}`
!!! warning "Caddy certificate location varies"
This setup only comes with one caveat: The domain has to be configured on another service for [Traefik][traefik::github] to actually request it from _Let's Encrypt_, i.e. [Traefik][traefik::github] will not issue a certificate without a service / router demanding it.
The path contains the certificate provisioner used. This path may be different from the example above for you and may change over time when [multiple ACME provisioner services are used][dms-pr-feedback::caddy-provisioning-gotcha].
This can make the volume mounting for DMS to find the certificates non-deterministic, but you can [restrict provisioning to single service via the `acme_ca` setting][caddy::restrict-acme-provisioner].
---
**NOTE:** Bind mounting a file directly instead of a directory will mount by inode. If the file is updated at renewal and this modifies the inode on the host system, then the container will still point to the old certificate.
If this happens, consider using our manual TLS type instead:
```yaml title="compose.yaml"
services:
mailserver:
environment:
SSL_TYPE: manual
SSL_CERT_PATH: /srv/tls/mail.example.com/mail.example.com.crt
SSL_KEY_PATH: /srv/tls/mail.example.com/mail.example.com.key
volumes:
- ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/:/srv/tls/mail.example.com/:ro
```
### Traefik
[Traefik][traefik::github] is an open-source application proxy using the [ACME protocol][ietf::rfc::acme]. Traefik can request certificates for domains and subdomains, and it will take care of renewals, challenge negotiations, etc.
Traefik's storage format is natively supported if the `acme.json` store is mounted into the container at `/etc/letsencrypt/acme.json`. The file is also monitored for changes and will trigger a reload of the mail services (Postfix and Dovecot).
DMS will select it's certificate from `acme.json` prioritizing a match for the DMS FQDN (hostname), while also checking one DNS level up (_eg: `mail.example.com` => `example.com`_). Wildcard certificates are supported.
This setup only comes with one caveat - The domain has to be configured on another service for Traefik to actually request it from _Let's Encrypt_ (_i.e. Traefik will not issue a certificate without a service / router demanding it_).
???+ example "Example Code"
Here is an example setup for [`docker-compose`](https://docs.docker.com/compose/):
Here is an example setup for [`Docker Compose`](https://docs.docker.com/compose/):
```yaml
services:
@ -634,7 +655,7 @@ This setup only comes with one caveat: The domain has to be configured on anothe
Use self-signed certificates only for testing purposes!
This feature requires you to provide the following files into your [`docker-data/dms/config/ssl/` directory][docs-optional-config] (_internal location: `/tmp/docker-mailserver/ssl/`_):
This feature requires you to provide the following files into your [`docker-data/dms/config/ssl/` directory][docs::dms-volumes-config] (_internal location: `/tmp/docker-mailserver/ssl/`_):
- `<FQDN>-key.pem`
- `<FQDN>-cert.pem`
@ -714,7 +735,7 @@ The local and internal paths may be whatever you prefer, so long as both `SSL_CE
## Testing a Certificate is Valid
- From your host:
!!! example "Connect to DMS on port 25"
```sh
docker exec mailserver openssl s_client \
@ -723,26 +744,42 @@ The local and internal paths may be whatever you prefer, so long as both `SSL_CE
-CApath /etc/ssl/certs/
```
- Or:
The response should show the certificate chain with a line further down: `Verify return code: 0 (ok)`
---
This example runs within the DMS container itself to verify the cert is working locally.
- Adjust the `-connect` IP if testing externally from another system. Additionally testing for port 143 (Dovecot IMAP) is encouraged (_change the protocol for `-starttls` from `smtp` to `imap`_).
- `-CApath` will help verify the certificate chain, provided the location contains the root CA that signed your TLS cert for DMS.
??? example "Verify certificate dates"
```sh
docker exec mailserver openssl s_client \
-connect 0.0.0.0:143 \
-starttls imap \
-CApath /etc/ssl/certs/
-connect 0.0.0.0:25 \
-starttls smtp \
-CApath /etc/ssl/certs/ \
2>/dev/null | openssl x509 -noout -dates
```
And you should see the certificate chain, the server certificate and: `Verify return code: 0 (ok)`
!!! tip "Testing and troubleshooting"
In addition, to verify certificate dates:
If you need to test a connection without resolving DNS, `curl` can connect with `--resolve` option to map an FQDN + Port to an IP address, instead of the request address provided.
```sh
docker exec mailserver openssl s_client \
-connect 0.0.0.0:25 \
-starttls smtp \
-CApath /etc/ssl/certs/ \
2>/dev/null | openssl x509 -noout -dates
```
```bash
# NOTE: You may want to use `--insecure` if the cert was provisioned with a private CA not present on the curl client:
# Use `--verbose` for additional insights on the connection.
curl --resolve mail.example.com:443:127.0.0.1 https://mail.example.com
```
Similarly with `openssl` you can connect to an IP as shown previously, but provide an explicit SNI if necessary with `-servername mail.example.com`.
---
Both `curl` and `openssl` also support `-4` and `-6` for enforcing IPv4 or IPv6 lookup.
This can be useful, such as when [DNS resolves the IP to different servers leading to different certificates returned][dms-discussion::gotcha-fqdn-bad-dns]. As shown in that link, `step certificate inspect` is also handy for viewing details of the cert returned or on disk.
## Plain-Text Access
@ -876,7 +913,7 @@ By default DMS uses [`ffdhe4096`][ffdhe4096-src] from [IETF RFC 7919][ietf::rfc:
Despite this, if you must use non-standard DH parameters or you would like to swap `ffdhe4096` for a different group (eg `ffdhe2048`); Add your own PEM encoded DH params file via a volume to `/tmp/docker-mailserver/dhparams.pem`. This will replace DH params for both Dovecot and Postfix services during container startup.
[docs-env::ssl-type]: ../environment.md#ssl_type
[docs-optional-config]: ../advanced/optional-config.md
[docs::dms-volumes-config]: ../advanced/optional-config.md#volumes-config
[docs-faq-baredomain]: ../../faq.md#can-i-use-a-nakedbare-domain-ie-no-hostname
[github-file-compose]: https://github.com/docker-mailserver/docker-mailserver/blob/master/compose.yaml
@ -903,6 +940,7 @@ Despite this, if you must use non-standard DH parameters or you would like to sw
[certbot::standalone]: https://certbot.eff.org/docs/using.html#standalone
[certbot::renew]: https://certbot.eff.org/docs/using.html#renewing-certificates
[certbot::automated-renewal]: https://certbot.eff.org/docs/using.html#automated-renewals
[certbot::automated-renewal::example-systemd-timer]: https://github.com/orgs/docker-mailserver/discussions/3917#discussioncomment-8661690
[certbot::custom-ca]: https://certbot.eff.org/docs/using.htmlchanging-the-acme-server
[certbot::webroot]: https://certbot.eff.org/docs/using.html#webroot
@ -916,3 +954,13 @@ Despite this, if you must use non-standard DH parameters or you would like to sw
[acme-companion::standalone]: https://github.com/nginx-proxy/acme-companion/blob/main/docs/Standalone-certificates.md
[acme-companion::standalone-changes]: https://github.com/nginx-proxy/acme-companion/blob/main/docs/Standalone-certificates.md#picking-up-changes-to-letsencrypt_user_data
[acme-companion::service-loop]: https://github.com/nginx-proxy/acme-companion/blob/main/docs/Container-utilities.md
[web::caddy]: https://caddyserver.com
[dockerhub::caddy]: https://hub.docker.com/_/caddy
[github::caddy-docker-proxy]: https://github.com/lucaslorentz/caddy-docker-proxy
[dms-pr-feedback::caddy-provisioning-gotcha]: https://github.com/docker-mailserver/docker-mailserver/pull/3485/files#r1297512818
[caddy-docs::tls-internal]: https://caddyserver.com/docs/caddyfile/directives/tls#syntax
[caddy-docs::key-type]: https://caddyserver.com/docs/caddyfile/options#key-type
[caddy::restrict-acme-provisioner]: https://caddy.community/t/is-there-a-way-on-a-caddyfile-to-force-a-specific-acme-ca/14506
[dms-discussion::gotcha-fqdn-bad-dns]: https://github.com/docker-mailserver/docker-mailserver/issues/3955#issuecomment-2027882633

View file

@ -145,7 +145,7 @@ Unlike with HTTP where a web browser client communicates directly with the serve
Other machines that facilitate a connection that generally aren't taken into account can exist between a client and server, such as those where your connection passes through your ISP provider are capable of compromising a `cleartext` connection through interception.
[docs-accounts]: ../user-management.md#accounts
[docs-accounts]: ../account-management/overview.md#accounts
[docs-relays]: ../advanced/mail-forwarding/relay-hosts.md
[iana-services-465]: https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=465
[starttls-policy-list]: https://github.com/EFForg/starttls-everywhere#email-security-database-starttls-policy-list

View file

@ -1,86 +0,0 @@
# User Management
## Accounts
Users (email accounts) are managed in `/tmp/docker-mailserver/postfix-accounts.cf`. The best way to manage accounts is to use the reliable `setup` command inside the container. Just run `docker exec <CONTAINER NAME> setup help` and have a look at the section about subcommands, specifically the `email` subcommand.
### Adding a new Account
#### Via `setup` inside the container
You can add an account by running `docker exec -ti <CONTAINER NAME> setup email add <NEW ADDRESS>`. This method is strongly preferred.
#### Manually
!!! warning
This method is discouraged!
Alternatively, you may directly add the full email address and its encrypted password, separated by a pipe. To generate a new mail account data, directly from your host, you could for example run the following:
```sh
docker run --rm -it \
--env MAIL_USER=user1@example.com \
--env MAIL_PASS=mypassword \
ghcr.io/docker-mailserver/docker-mailserver:latest \
/bin/bash -c \
'echo "${MAIL_USER}|$(doveadm pw -s SHA512-CRYPT -u ${MAIL_USER} -p ${MAIL_PASS})" >>docker-data/dms/config/postfix-accounts.cf'
```
You will then be asked for a password, and be given back the data for a new account entry, as text. To actually _add_ this new account, just copy all the output text in `docker-data/dms/config/postfix-accounts.cf` file of your running container.
The result could look like this:
```cf
user1@example.com|{SHA512-CRYPT}$6$2YpW1nYtPBs2yLYS$z.5PGH1OEzsHHNhl3gJrc3D.YMZkvKw/vp.r5WIiwya6z7P/CQ9GDEJDr2G2V0cAfjDFeAQPUoopsuWPXLk3u1
```
### Quotas
- `imap-quota` is enabled and allow clients to query their mailbox usage.
- When the mailbox is deleted, the quota directive is deleted as well.
- Dovecot quotas support LDAP, **but it's not implemented** (_PRs are welcome!_).
## Aliases
The best way to manage aliases is to use the reliable `setup` script inside the container. Just run `docker exec <CONTAINER NAME> setup help` and have a look at the section about subcommands, specifically the `alias`-subcommand.
### About
You may read [Postfix's documentation on virtual aliases][postfix-docs-alias] first. Aliases are managed in `/tmp/docker-mailserver/postfix-virtual.cf`. An alias is a full email address that will either be:
- delivered to an existing account registered in `/tmp/docker-mailserver/postfix-accounts.cf`
- redirected to one or more other email addresses
Alias and target are space separated. An example on a server with `example.com` as its domain:
```cf
# Alias delivered to an existing account
alias1@example.com user1@example.com
# Alias forwarded to an external email address
alias2@example.com external-account@gmail.com
```
### Configuring RegExp Aliases
Additional regexp aliases can be configured by placing them into `docker-data/dms/config/postfix-regexp.cf`. The regexp aliases get evaluated after the virtual aliases (container path: `/tmp/docker-mailserver/postfix-virtual.cf`). For example, the following `docker-data/dms/config/postfix-regexp.cf` causes all email sent to "test" users to be delivered to `qa@example.com` instead:
```cf
/^test[0-9][0-9]*@example.com/ qa@example.com
```
### Address Tags (Extension Delimiters) as an alternative to Aliases
Postfix supports so-called address tags, in the form of plus (+) tags - i.e. `address+tag@example.com` will end up at `address@example.com`. This is configured by default and the (configurable!) separator is set to `+`. For more info, see [Postfix's official documentation][postfix-docs-extension-delimiters].
!!! note
If you do decide to change the configurable separator, you must add the same line to *both* `docker-data/dms/config/postfix-main.cf` and `docker-data/dms/config/dovecot.cf`, because Dovecot is acting as the delivery agent. For example, to switch to `-`, add:
```cf
recipient_delimiter = -
```
[postfix-docs-alias]: http://www.postfix.org/VIRTUAL_README.html#virtual_alias
[postfix-docs-extension-delimiters]: http://www.postfix.org/postconf.5.html#recipient_delimiter

View file

@ -15,13 +15,20 @@ When refactoring, writing or altering scripts or other files, adhere to these ru
Make sure to select `edge` in the dropdown menu at the top. Navigate to the page you would like to edit and click the edit button in the top right. This allows you to make changes and create a pull-request.
Alternatively you can make the changes locally. For that you'll need to have Docker installed. Navigate into the `docs/` directory. Then run:
Alternatively you can make the changes locally. For that you'll need to have Docker installed and run:
```sh
docker run --rm -it -p 8000:8000 -v "${PWD}:/docs" squidfunk/mkdocs-material
# From the root directory of the git clone:
docker run --rm -it -p 8000:8000 -v "./docs:/docs" squidfunk/mkdocs-material
```
This serves the documentation on your local machine on port `8000`. Each change will be hot-reloaded onto the page you view, just edit, save and look at the result.
!!! note
The container logs will inform you of invalid links detected, but a [few are false-positives][gh-dms::mkdocs-link-error-false-positives] due to our usage of linking to specific [content tabs][mkdocs::content-tabs].
[get-docker]: https://docs.docker.com/get-docker/
[docs-bats-parallel]: https://bats-core.readthedocs.io/en/v1.8.2/usage.html#parallel-execution
[gh-dms::mkdocs-link-error-false-positives]: https://github.com/docker-mailserver/docker-mailserver/pull/4366
[mkdocs::content-tabs]: https://squidfunk.github.io/mkdocs-material/reference/content-tabs/#anchor-links

View file

@ -45,15 +45,15 @@ The development workflow is the following:
1. Fork the project and clone your fork with `git clone --recurse-submodules ...` or run `git submodule update --init --recursive` after you cloned your fork
2. Write the code that is needed :D
3. Add integration tests if necessary
4. [Prepare your environment and run linting and tests][docs-general-tests]
5. Document your improvements if necessary (e.g. if you introduced new environment variables, describe those in the [ENV documentation][docs-environment]) and add your changes the changelog under the "Unreleased" section
4. [Prepare your environment and run linting and tests][docs::contributing::tests]
5. Document your improvements if necessary (e.g. if you introduced new environment variables, describe those in the [ENV documentation][docs::env]) and add your changes the changelog under the "Unreleased" section
6. [Commit][commit] (and [sign your commit][gpg]), push and create a pull-request to merge into `master`. Please **use the pull-request template** to provide a minimum of contextual information and make sure to meet the requirements of the checklist.
Pull requests are automatically tested against the CI and will be reviewed when tests pass. When your changes are validated, your branch is merged. CI builds the new `:edge` image immediately and your changes will be includes in the next version release.
[docs-latest]: https://docker-mailserver.github.io/docker-mailserver/latest
[github-file-readme]: https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md
[docs-environment]: ../config/environment.md
[docs-general-tests]: ./general.md#tests
[docs::env]: ../config/environment.md
[docs::contributing::tests]: ./tests.md
[commit]: https://help.github.com/articles/closing-issues-via-commit-messages/
[gpg]: https://docs.github.com/en/github/authenticating-to-github/generating-a-new-gpg-key

View file

@ -35,6 +35,10 @@ There are many functions that aid in writing tests. **We urge you to use them!**
We encourage you to try both of the approaches mentioned above. To make understanding and using the helper functions easy, every function contains detailed documentation comments. Read them carefully!
!!! tip
If your test needs to add or create additional files, our helpers will [manage a disposable config directory per container][technical-guide::test-tmp-config] for you with the path stored in the `TEST_TMP_CONFIG` ENV (outside the container, `/tmp/docker-mailserver` within the container).
### How Are Tests Run?
Tests are split into two categories:
@ -51,7 +55,7 @@ Parallel tests are further partitioned into smaller sets. If your system has the
To run the test suite, you will need to:
1. [Install Docker][get-docker]
2. Install `jq` and (GNU) `parallel` (under Ubuntu, use `sudo apt-get -y install jq parallel`)
2. Install `jq` , (GNU) `parallel` and `file` (under Ubuntu, use `sudo apt-get -y install jq parallel file`)
3. Execute `git submodule update --init --recursive` if you haven't already initialized the git submodules
### Executing Test(s)
@ -141,3 +145,4 @@ $ make clean tests
[testing-prs]: https://github.com/docker-mailserver/docker-mailserver/blob/master/.github/workflows/test_merge_requests.yml
[get-docker]: https://docs.docker.com/get-docker/
[docs-bats-parallel]: https://bats-core.readthedocs.io/en/v1.8.2/usage.html#parallel-execution
[technical-guide::test-tmp-config]: https://github.com/docker-mailserver/docker-mailserver/pull/4359#issuecomment-2817591085

View file

@ -32,7 +32,7 @@ We make use of build features that require a recent version of Docker. v23.0 or
The `Dockerfile` includes several build [`ARG`][docker-docs::builder-arg] instructions that can be configured:
- `DOVECOT_COMMUNITY_REPO`: Install Dovecot from the community repo instead of from Debian (default = 1)
- `DOVECOT_COMMUNITY_REPO`: Install Dovecot from the community repo instead of from Debian (default = 0)
- `DMS_RELEASE`: The image version (default = edge)
- `VCS_REVISION`: The git commit hash used for the build (default = unknown)

View file

@ -0,0 +1,178 @@
# Dovecot Full Text Search (FTS) using the Solr Backend
Dovecot supports several FTS backends for providing fast and efficient full text searching of e-mails directly from the IMAP server.
As the size of your mail storage grows, the benefits of FTS are especially notable:
- Without FTS, Dovecot would perform a search query by checking each individual email stored for a match, and then repeat this process again from scratch for the exact same query in future.
- Some mail clients (_like Thunderbird_) may provide their own indexing and search features when all mail to search is stored locally, otherwise Dovecot needs to handle the search query (_for example webmail and mobile clients, like Gmail_).
- FTS indexes each mail into a database for querying instead, where it can skip the cost of inspecting irrelevant emails for a query.
!!! warning "This is a community contributed guide"
It extends [our official docs for Dovecot FTS][docs::dovecot::full-text-search] with a focus on Apache Solr. DMS does not officially support this integration.
## Setup Solr for DMS
An FTS backend supported by Dovecot is [Apache Solr][github-solr], a fast and efficient multi-purpose search indexer.
### Add the required `dovecot-solr` package
As the official DMS image does not provide `dovecot-solr`, you'll need to include the package in your own image (_extending a DMS release as a base image_), or via our [`user-patches.sh` feature][docs::user-patches]:
<!-- This empty quote block is purely for a visual border -->
!!! quote ""
=== "`user-patches.sh`"
If you'd prefer to avoid a custom image build. This approach is simpler but with the caveat that any time the container is restarted, you'll have a delay as the package is installed each time.
```bash
#!/bin/bash
apt-get update && apt-get install dovecot-solr
```
=== "`compose.yaml`"
A custom DMS image does not add much friction. You do not need a separate `Dockerfile` as Docker Compose supports building from an inline `Dockerfile` in your `compose.yaml`.
The `image` key of the service is swapped for the `build` key instead, as shown below:
```yaml
services:
mailserver:
hostname: mail.example.com
# The `image` setting now represents the tag for the local build configured below:
image: local/dms:${DMS_TAG?Must set DMS image tag}
# Local build (no need to try pull `image` remotely):
pull_policy: build
# Add this `build` section to your real `compose.yaml` for your DMS service:
build:
dockerfile_inline: |
FROM docker.io/mailserver/docker-mailserver:${DMS_TAG?Must set DMS image tag}
RUN apt-get update && apt-get install dovecot-solr
```
This approach only needs to install the package once with the image build itself which minimizes the delay of container startup.
- Just run `DMS_TAG='14.0' docker compose up` and it will pull the DMS image, then build your custom DMS image to run a new container instance.
- Updating to a new DMS release is straight-forward, just adjust the `DMS_TAG` ENV value or change the image tag directly in `compose.yaml` as you normally would to upgrade an image.
- If you make future changes to the `dockerfile_inline` that don't seem to be applied, you may need to force a rebuild with `DMS_TAG='14.0' docker compose up --build`.
!!! note "Why doesn't DMS include `dovecot-solr`?"
This integration is not officially supported in DMS as no maintainer is able to provide troubleshooting support.
Prior to v14, the package was included but the community contributed guide had been outdated for several years that it was non-functional. It was decided that it was better to drop support and docs, however some DMS users voiced active use of Solr and it's benefits over Xapian for FTS which led to these revised docs.
**ARM64 builds do not have support for `dovecot-solr`**. Additionally the [user demand for including `dovecot-solr` is presently too low][gh-dms::feature-request::dovecot-solr-package] to justify vs the minimal effort to add additional packages as shown above.
### `compose.yaml` config
Firstly you need a working Solr container, for this the [official docker image][dockerhub-solr] will do:
```yaml
services:
solr:
image: solr:latest
container_name: dms-solr
environment:
# As Solr can be quite resource hungry, raise the memory limit to 2GB.
# The default is 512MB, which may be exhausted quickly.
SOLR_JAVA_MEM: "-Xms2g -Xmx2g"
volumes:
- ./docker-data/solr:/var/solr
restart: always
```
DMS will connect internally to the `solr` service above. Either have both services in the same `compose.yaml` file, or ensure that the containers are connected to the same docker network.
### Configure Solr for Dovecot
1. Once the Solr container is started, you need to configure a "Solr core" for Dovecot:
```bash
docker exec -it dms-solr /bin/sh
solr create -c dovecot
cp -R /opt/solr/contrib/analysis-extras/lib /var/solr/data/dovecot
```
Stop the `dms-solr` container and you should now have a `./data/dovecot` folder in the local bind mount volume.
2. Solr needs a schema that is specifically tailored for Dovecot FTS.
As of writing of this guide, Solr 9 is the current release. [Dovecot provides the required schema configs][github-dovecot::core-docs] for Solr, copy the following two v9 config files to `./data/dovecot` and rename them accordingly:
- `solr-config-9.xml` (_rename to `solrconfig.xml`_)
- `solr-schema-9.xml` (_rename to `schema.xml`_)
Additionally, remove the `managed-schema.xml` file from `./data/dovecot` and ensure the two files you copied have a [UID and GID of `8983`][dockerfile-solr-uidgid] assigned.
Start the Solr container once again, you should now have a working Solr core specifically for Dovecot FTS.
3. Configure Dovecot in DMS to connect to this Solr core:
Create a `10-plugin.conf` file in your `./config/dovecot` folder with this contents:
```config
mail_plugins = $mail_plugins fts fts_solr
plugin {
fts = solr
fts_autoindex = yes
fts_solr = url=http://dms-solr:8983/solr/dovecot/
}
```
Add a volume mount for that config to your DMS service in `compose.yaml`:
```yaml
services:
mailserver:
volumes:
- ./docker-data/config/dovecot/10-plugin.conf:/etc/dovecot/conf.d/10-plugin.conf:ro
```
### Trigger Dovecot FTS indexing
After following the previous steps, restart DMS and run this command to have Dovecot re-index all mail:
```bash
docker compose exec mailserver doveadm fts rescan -A
```
!!! info "Indexing will take a while depending on how large your mail folders"
Usually within 15 minutes or so, you should be able to search your mail using the Dovecot FTS feature! :tada:
### Compatibility
Since Solr 9.8.0 was released (Jan 2025), a breaking change [deprecates support for `<lib>` directives][solr::9.8::lib-directive] which is presently used by the Dovecot supplied Solr config (`solr-config-9.xml`) to automatically load additional jars required.
To enable support for `<lib>` directives, add the following ENV to your `solr` container:
```yaml
services:
solr:
environment:
SOLR_CONFIG_LIB_ENABLED: true
```
!!! warning "Solr 10"
From the Solr 10 release onwards, this opt-in ENV will no longer be available.
If Dovecot has not updated their example Solr config ([upstream PR][dovecot::pr::solr-config-lib]), you will need to manually modify the Solr XML config to remove the `<lib>` directives and replace the suggested ENV `SOLR_CONFIG_LIB_ENABLED=true` with `SOLR_MODULES=analysis-extras`.
[docs::user-patches]: ../../config/advanced/override-defaults/user-patches.md
[docs::dovecot::full-text-search]: ../../config/advanced/full-text-search.md
[gh-dms::feature-request::dovecot-solr-package]: https://github.com/docker-mailserver/docker-mailserver/issues/4052
[dockerhub-solr]: https://hub.docker.com/_/solr
[dockerfile-solr-uidgid]: https://github.com/apache/solr-docker/blob/9cd850b72309de05169544395c83a85b329d6b86/9.6/Dockerfile#L89-L92
[github-solr]: https://github.com/apache/solr
[github-dovecot::core-docs]: https://github.com/dovecot/core/tree/main/doc
[solr::9.8::lib-directive]: https://issues.apache.org/jira/browse/SOLR-16781
[dovecot::pr::solr-config-lib]: https://github.com/dovecot/core/pull/238

View file

@ -2,128 +2,421 @@
title: 'Tutorials | Mail Server behind a Proxy'
---
## Using DMS behind a Proxy
## Using a Reverse Proxy
### Information
Guidance is provided via a Traefik config example, however if you're only familiar with configuring a reverse proxy for web services there are some differences to keep in mind.
If you are hiding your container behind a proxy service you might have discovered that the proxied requests from now on contain the proxy IP as the request origin. Whilst this behavior is technical correct it produces certain problems on the containers behind the proxy as they cannot distinguish the real origin of the requests anymore.
- A security concern where preserving the client IP is important but needs to be handled at Layer 4 (TCP).
- TLS will be handled differently due protocols like STARTTLS and the need to comply with standards for interoperability with other MTAs.
- The ability to route the same port to different containers by FQDN can be limited.
To solve this problem on TCP connections we can make use of the [proxy protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt). Compared to other workarounds that exist (`X-Forwarded-For` which only works for HTTP requests or `Tproxy` that requires you to recompile your kernel) the proxy protocol:
This reduces many of the benefits for why you might use a reverse proxy, but they can still be useful.
- It is protocol agnostic (can work with any layer 7 protocols, even when encrypted).
- It does not require any infrastructure changes.
- NAT-ing firewalls have no impact it.
- It is scalable.
Some deployments may require a service to route traffic (kubernetes) when deploying, in which case the below advice is important to understand well.
There is only one condition: **both endpoints** of the connection MUST be compatible with proxy protocol.
The guide here has also been adapted for [our Kubernetes docs][docs::kubernetes].
Luckily `dovecot` and `postfix` are both Proxy-Protocol ready softwares so it depends only on your used reverse-proxy / loadbalancer.
## What can go wrong?
### Configuration of the used Proxy Software
Without a reverse proxy involved, a service is typically aware of the client IP for a connection.
The configuration depends on the used proxy system. I will provide the configuration examples of [traefik v2](https://traefik.io/) using IMAP and SMTP with implicit TLS.
However when a reverse proxy routes the connection this information can be lost, and the proxied service mistakenly treats the client IP as the reverse proxy handling the connection.
Feel free to add your configuration if you achieved the same goal using different proxy software below:
- That can be problematic when the client IP is meaningful information for the proxied service to act upon, especially when it [impacts security](#security-concerns).
- The [PROXY protocol][networking::spec:proxy-protocol] is a well established solution to preserve the client IP when both the proxy and service have enabled the support.
??? "Traefik v2"
??? abstract "Technical Details - HTTP vs TCP proxying"
Truncated configuration of traefik itself:
A key difference for how the network is proxied relates to the [OSI Model][networking::osi-model]:
```yaml
- Layer 7 (_Application layer protocols: SMTP / IMAP / HTTP / etc_)
- Layer 4 (_Transport layer protocols: TCP / UDP_)
When working with Layer 7 and a protocol like HTTP, it is possible to inspect a protocol header like [`Forwarded`][networking::http-header::forwarded] (_or it's predecessor: [`X-Forwarded-For`][networking::http-header::x-forwarded-for]_). At a lower level with Layer 4, that information is not available and we are routing traffic agnostic to the application protocol being proxied.
A proxy can prepend the [PROXY protocol][networking::spec:proxy-protocol] header to the TCP/UDP connection as it is routed to the service, which must be configured to be compatible with PROXY protocol (_often this adds a restriction that connections must provide the header, otherwise they're rejected_).
Beyond your own proxy, traffic may be routed in the network by other means that would also rewrite this information such as Docker's own network management via `iptables` and `userland-proxy` (NAT). The PROXY header ensures the original source and destination IP addresses, along with their ports is preserved across transit.
## Configuration
### Reverse Proxy
The below guidance is focused on configuring [Traefik][traefik-web], but the advice should be roughly applicable elsewhere (_eg: [NGINX][nginx-docs::proxyprotocol], [Caddy][caddy::plugin::l4]_).
- Support requires the capability to proxy TCP (Layer 4) connections with PROXY protocol enabled for the upstream (DMS). The upstream must also support enabling PROXY protocol (_which for DMS services rejects any connection not using the protocol_).
- TLS should not be terminated at the proxy, that should be delegated to DMS (_which should be configured with the TLS certs_). Reasoning is covered under the [ports section](#ports).
???+ example "Traefik service"
The Traefik service config is fairly standard, just define the necessary entrypoints:
```yaml title="compose.yaml"
services:
reverse-proxy:
image: docker.io/traefik:latest # v2.5
container_name: docker-traefik
restart: always
image: docker.io/traefik:latest # 2.10 / 3.0
# CAUTION: In production you should configure the Docker API endpoint securely:
# https://doc.traefik.io/traefik/providers/docker/#docker-api-access
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command:
- "--providers.docker"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=proxy"
- "--entrypoints.web.address=:80"
- "--entryPoints.websecure.address=:443"
- "--entryPoints.smtp.address=:25"
- "--entryPoints.smtp-ssl.address=:465"
- "--entryPoints.imap-ssl.address=:993"
- "--entryPoints.sieve.address=:4190"
# Docker provider config:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
# DMS ports you want to proxy:
- --entryPoints.mail-smtp.address=:25
- --entryPoints.mail-submission.address=:587
- --entryPoints.mail-submissions.address=:465
- --entryPoints.mail-imap.address=:143
- --entryPoints.mail-imaps.address=:993
- --entryPoints.mail-pop3.address=:110
- --entryPoints.mail-pop3s.address=:995
- --entryPoints.mail-managesieve.address=:4190
# Publish external access ports mapped to traefik entrypoint ports:
ports:
- "25:25"
- "587:587"
- "465:465"
- "143:143"
- "993:993"
- "110:110"
- "995:995"
- "4190:4190"
[...]
```
Truncated list of necessary labels on the DMS container:
```yaml
services:
mailserver:
image: ghcr.io/docker-mailserver/docker-mailserver:latest
container_name: mailserver
hostname: mail.example.com
restart: always
# An IP is assigned here for other services (Dovecot) to trust for PROXY protocol:
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.tcp.routers.smtp.rule=HostSNI(`*`)"
- "traefik.tcp.routers.smtp.entrypoints=smtp"
- "traefik.tcp.routers.smtp.service=smtp"
- "traefik.tcp.services.smtp.loadbalancer.server.port=25"
- "traefik.tcp.services.smtp.loadbalancer.proxyProtocol.version=1"
- "traefik.tcp.routers.smtp-ssl.rule=HostSNI(`*`)"
- "traefik.tcp.routers.smtp-ssl.entrypoints=smtp-ssl"
- "traefik.tcp.routers.smtp-ssl.tls.passthrough=true"
- "traefik.tcp.routers.smtp-ssl.service=smtp-ssl"
- "traefik.tcp.services.smtp-ssl.loadbalancer.server.port=465"
- "traefik.tcp.services.smtp-ssl.loadbalancer.proxyProtocol.version=1"
- "traefik.tcp.routers.imap-ssl.rule=HostSNI(`*`)"
- "traefik.tcp.routers.imap-ssl.entrypoints=imap-ssl"
- "traefik.tcp.routers.imap-ssl.service=imap-ssl"
- "traefik.tcp.routers.imap-ssl.tls.passthrough=true"
- "traefik.tcp.services.imap-ssl.loadbalancer.server.port=10993"
- "traefik.tcp.services.imap-ssl.loadbalancer.proxyProtocol.version=2"
- "traefik.tcp.routers.sieve.rule=HostSNI(`*`)"
- "traefik.tcp.routers.sieve.entrypoints=sieve"
- "traefik.tcp.routers.sieve.service=sieve"
- "traefik.tcp.services.sieve.loadbalancer.server.port=4190"
[...]
default:
ipv4_address: 172.16.42.2
# Specifying a subnet to assign a fixed container IP to the reverse proxy:
networks:
default:
name: my-network
ipam:
config:
- subnet: "172.16.42.0/24"
```
Keep in mind that it is necessary to use port `10993` here. More information below at `dovecot` configuration.
!!! note "Extra considerations"
### Configuration of the Backend (`dovecot` and `postfix`)
- [`--providers.docker.network=my-network`][traefik-docs::provider-docker::network] is useful when there is more than one network to consider.
- If your deployment has any other hops (an edge proxy, load balancer, etc) between the reverse proxy and the client, you'll need PROXY protocol support throughout that chain. For Traefik this additionally requires [enabling PROXY protocol on your entry points][traefik-docs::entrypoint::proxyprotocol].
The following changes can be achieved completely by adding the content to the appropriate files by using the projects [function to overwrite config files][docs-optionalconfig].
???+ example "Traefik labels for DMS"
Changes for `postfix` can be applied by adding the following content to `docker-data/dms/config/postfix-main.cf`:
```yaml title="compose.yaml"
services:
dms:
image: ghcr.io/docker-mailserver/docker-mailserver:latest
hostname: mail.example.com
labels:
- traefik.enable=true
```cf
postscreen_upstream_proxy_protocol = haproxy
```
# These are examples, configure the equivalent for any additional ports you proxy.
# Explicit TLS (STARTTLS):
- traefik.tcp.routers.mail-smtp.rule=HostSNI(`*`)
- traefik.tcp.routers.mail-smtp.entrypoints=mail-smtp
- traefik.tcp.routers.mail-smtp.service=mail-smtp
- traefik.tcp.services.mail-smtp.loadbalancer.server.port=25
- traefik.tcp.services.mail-smtp.loadbalancer.proxyProtocol.version=2
and to `docker-data/dms/config/postfix-master.cf`:
# Implicit TLS is no different, except for optional HostSNI support:
- traefik.tcp.routers.mail-submissions.rule=HostSNI(`*`)
- traefik.tcp.routers.mail-submissions.entrypoints=mail-submissions
- traefik.tcp.routers.mail-submissions.service=mail-submissions
- traefik.tcp.services.mail-submissions.loadbalancer.server.port=465
- traefik.tcp.services.mail-submissions.loadbalancer.proxyProtocol.version=2
# NOTE: Optionally match by SNI rule, this requires TLS passthrough (not compatible with STARTTLS):
#- traefik.tcp.routers.mail-submissions.rule=HostSNI(`mail.example.com`)
#- traefik.tcp.routers.mail-submissions.tls.passthrough=true
```
```cf
submission/inet/smtpd_upstream_proxy_protocol=haproxy
submissions/inet/smtpd_upstream_proxy_protocol=haproxy
```
!!! note "PROXY protocol compatibility"
Changes for `dovecot` can be applied by adding the following content to `docker-data/dms/config/dovecot.cf`:
Only TCP routers support enabling PROXY Protocol (via [`proxyProtocol.version=2`][traefik-docs::service-tcp::proxyprotocol])
```cf
haproxy_trusted_networks = <your-proxy-ip>, <optional-cidr-notation>
haproxy_timeout = 3 secs
service imap-login {
inet_listener imaps {
haproxy = yes
ssl = yes
port = 10993
}
}
```
Postfix and Dovecot are both compatible with PROXY protocol v1 and v2.
!!! note
Port `10993` is used here to avoid conflicts with internal systems like `postscreen` and `amavis` as they will exchange messages on the default port and obviously have a different origin then compared to the proxy.
#### Ports
[docs-optionalconfig]: ../../config/advanced/optional-config.md
??? abstract "Technical Details - Ports (Traefik config)"
!!! info "Explicit TLS (STARTTLS)"
**Service Ports:** `mail-smtp` (25), `mail-submission` (587), `mail-imap` (143), `mail-pop3` (110), `mail-managesieve` (4190)
---
- [Traefik expects the TCP router to not enable TLS][traefik-docs::router-tcp::server-first-protocols] (_see "Server First protocols"_) for these connections. They begin in plaintext and potentially upgrade the connection to TLS, Traefik has no involvement in STARTTLS.
- Without an initial TLS connection, the [`HostSNI` router rule is not usable][traefik-docs::router-tcp::host-sni] (_see "HostSNI & TLS"_). This limits routing flexibility for these ports (_eg: routing these ports by the FQDN to different DMS containers_).
!!! info "Implicit TLS"
**Service Ports:** `mail-submissions` (465), `mail-imaps` (993), `mail-pop3s` (995)
---
The `HostSNI` router rule could specify the DMS FQDN instead of `*`:
- This requires the router to have TLS enabled, so that Traefik can inspect the server name sent by the client.
- Traefik can only match the SNI to `*` when the client does not provide a server name. Some clients must explicitly opt-in, such as CLI clients `openssl` (`-servername`) and `swaks` (`--tls-sni`).
- Add [`tls.passthrough=true` to the router][traefik-docs::router-tcp::passthrough] (_this implicitly enables TLS_).
- Traefik should not terminate TLS, decryption should occur within DMS instead when proxying to the same implicit TLS ports.
- Passthrough ignores any certificates configured for Traefik; DMS must be configured with the certificates instead (_[DMS can use `acme.json` from Traefik][docs::tls::traefik]_).
Unlike proxying HTTPS (port 443) to a container via HTTP (port 80), the equivalent for DMS service ports is not supported:
- Port 25 must secure the connection via STARTTLS to be reached publicly.
- STARTTLS ports requiring authentication for Postfix (587) and Dovecot (110, 143, 4190) are configured to only permit authentication over an encrypted connection.
- Support would require routing the implicit TLS ports to their explicit TLS equivalent ports with auth restrictions removed. `tls.passthrough.true` would not be required, additionally port 25 would always be unencrypted (_if the proxy exclusively manages TLS/certs_), or unreachable by public MTAs attempting delivery if the proxy enables implicit TLS for this port.
### DMS (Postfix + Dovecot)
???+ example "Enable PROXY protocol on existing service ports"
This can be handled via our config override support.
---
Postfix via [`postfix-master.cf`][docs::overrides::postfix]:
```cf title="docker-data/dms/config/postfix-master.cf"
smtp/inet/postscreen_upstream_proxy_protocol=haproxy
submission/inet/smtpd_upstream_proxy_protocol=haproxy
submissions/inet/smtpd_upstream_proxy_protocol=haproxy
```
[`postscreen_upstream_proxy_protocol`][postfix-docs::settings::postscreen_upstream_proxy_protocol] and [`smtpd_upstream_proxy_protocol`][postfix-docs::settings::smtpd_upstream_proxy_protocol] both specify the protocol type used by a proxy. `haproxy` represents the PROXY protocol.
---
Dovecot via [`dovecot.cf`][docs::overrides::dovecot]:
```cf title="docker-data/dms/config/dovecot.cf"
haproxy_trusted_networks = 172.16.42.2
service imap-login {
inet_listener imap {
haproxy = yes
}
inet_listener imaps {
haproxy = yes
}
}
service pop3-login {
inet_listener pop3 {
haproxy = yes
}
inet_listener pop3s {
haproxy = yes
}
}
service managesieve-login {
inet_listener sieve {
haproxy = yes
}
}
```
- [`haproxy_trusted_networks`][dovecot-docs::settings::haproxy-trusted-networks] must reference the reverse proxy IP, or a wider subnet using CIDR notation.
- [`haproxy = yes`][dovecot-docs::service-config::haproxy] for the TCP listeners of each login service.
!!! warning "Internal traffic (_within the network or DMS itself_)"
- Direct connections to DMS from other containers within the internal network will be rejected when they don't provide the required PROXY header.
- This can also affect services running within the DMS container itself if they attempt to make a connection and aren't PROXY protocol capable.
---
A solution is to configure alternative service ports that offer PROXY protocol support (as shown next).
Alternatively routing connections to DMS through the local reverse proxy via [DNS query rewriting][gh-dms::dns-rewrite-example] can work too.
??? example "Configuring services with separate ports for PROXY protocol"
In this example we'll take the original service ports and add `10000` for the new PROXY protocol service ports.
Traefik labels will need to update their service ports accordingly (eg: `.loadbalancer.server.port=10465`).
---
Postfix config now requires [our `user-patches.sh` support][docs::overrides::user-patches] to add new services in `/etc/postfix/master.cf`:
```bash title="docker-data/dms/config/user-patches.sh"
#!/bin/bash
# Duplicate the config for the submission(s) service ports (587 / 465) with adjustments for the PROXY ports (10587 / 10465) and `syslog_name` setting:
postconf -Mf submission/inet | sed -e s/^submission/10587/ -e 's/submission/submission-proxyprotocol/' >> /etc/postfix/master.cf
postconf -Mf submissions/inet | sed -e s/^submissions/10465/ -e 's/submissions/submissions-proxyprotocol/' >> /etc/postfix/master.cf
# Enable PROXY Protocol support for these new service variants:
postconf -P 10587/inet/smtpd_upstream_proxy_protocol=haproxy
postconf -P 10465/inet/smtpd_upstream_proxy_protocol=haproxy
# Create a variant for port 25 too (NOTE: Port 10025 is already assigned in DMS to Amavis):
postconf -Mf smtp/inet | sed -e s/^smtp/12525/ >> /etc/postfix/master.cf
# Enable PROXY Protocol support (different setting as port 25 is handled via postscreen), optionally configure a `syslog_name` to distinguish in logs:
postconf -P 12525/inet/postscreen_upstream_proxy_protocol=haproxy 12525/inet/syslog_name=postfix/smtpd-proxyprotocol
```
Supporting port 25 with an additional PROXY protocol port will also require a `postfix-main.cf` override line for `postscreen` to work correctly:
```cf title="docker-data/dms/config/postfix-main.cf"
postscreen_cache_map = proxy:btree:$data_directory/postscreen_cache
```
---
Dovecot is mostly the same as before:
- A new service name instead of targeting one to modify.
- Add the new port assignment.
- Set [`ssl = yes`][dovecot-docs::service-config::ssl] when implicit TLS is needed.
```cf title="docker-data/dms/config/dovecot.cf"
haproxy_trusted_networks = 172.16.42.2
service imap-login {
inet_listener imap-proxyprotocol {
haproxy = yes
port = 10143
}
inet_listener imaps-proxyprotocol {
haproxy = yes
port = 10993
ssl = yes
}
}
service pop3-login {
inet_listener pop3-proxyprotocol {
haproxy = yes
port = 10110
}
inet_listener pop3s-proxyprotocol {
haproxy = yes
port = 10995
ssl = yes
}
}
service managesieve-login {
inet_listener sieve-proxyprotocol {
haproxy = yes
port = 14190
}
}
```
## Verification
Send an email through the reverse proxy. If you do not use the DNS query rewriting approach, you'll need to do this from an external client.
??? example "Sending a generic test mail through `swaks` CLI"
Run a `swaks` command and then check your DMS logs for the expected client IP, it should no longer be using the reverse proxy IP.
```bash
# NOTE: It is common to find port 25 is blocked from outbound connections, you may only be able to test the submission(s) ports.
swaks --helo not-relevant.test --server mail.example.com --port 25 -tls --from hello@not-relevant.test --to user@example.com
```
- You can specify the `--server` as the DMS FQDN or an IP address, where either should connect to the reverse proxy service.
- `not-relevant.test` technically may be subject to some tests, at least for port 25. With the submission(s) ports those should be exempt.
- `-tls` will use STARTTLS on port 25, you can exclude it to send unencrypted, but it would still go through the same port/route being tested.
- To test the submission ports use `--port 587 -tls` or `--port 465 -tlsc` with your credentials `--auth-user user@example.com --auth-password secret`
- Add `--tls-sni mail.example.com` if you have configured `HostSNI` in Traefik router rules (_SNI routing is only valid for implicit TLS ports_).
??? warning "Do not rely on local testing alone"
Testing from the Docker host technically works, however the IP is likely subject to more manipulation via `iptables` than an external client.
The IP will likely appear as from the gateway IP of the Docker network associated to the reverse proxy, where that gateway IP then becomes the client IP when writing the PROXY protocol header.
## Security concerns
### Forgery
Since the PROXY protocol sends a header with the client IP rewritten for software to use instead, this could be abused by bad actors.
Software on the receiving end of the connection often supports configuring an IP or CIDR range of clients to trust receiving the PROXY protocol header from.
??? warning "Risk exposure"
If you trust more than the reverse proxy IP, you must consider the risk exposure:
- Any container within the network that is compromised could impersonate another IP (_container or external client_) which may have been configured to have additional access/exceptions granted.
- If the reverse proxy is on a separate network/host than DMS, exposure of the PROXY protocol enabled ports outside the network increases the importance of narrowing trust. For example with the [known IPv6 to subnet Gateway IP routing gotcha][docs::ipv6::security-risks] in Docker, trusting the entire subnet DMS belongs to would wrongly trust external clients that have the subnet Gateway IP to impersonate any client IP.
- There is a [known risk with Layer 2 switching][docker::networking::l2-switch-gotcha] (_applicable to VPC networks, impact varies by cloud vendor_):
- Neighbouring hosts can indirectly route to ports published on the interfaces of a separate host system that shouldn't be reachable (_eg: localhost `127.0.0.1`, or a private subnet `172.16.0.0/12`_).
- The scope of this in Docker is limited to published ports only when Docker uses `iptables` with the kernel tunable `sysctl net.ipv4.ip_forward=1` (enabled implicitly). Port access is via `HOST:CONTAINER` ports published to their respective interface(s), that includes the container IP + port.
While some concerns raised above are rather specific, these type of issues aren't exclusive to Docker and difficult to keep on top of as software is constantly changing. Limit the trusted networks where possible.
??? warning "Postfix has no concept of trusted proxies"
Postfix does not appear to have a way to configure trusted proxies like Dovecot does (`haproxy_trusted_networks`).
[`postscreen_access_list`][postfix-docs::settings::postscreen_access_list] (_or [`smtpd_client_restrictions`][postfix-docs::settings::smtpd_client_restrictions] with [`check_client_access`][postfix-docs::settings::check_client_access] for ports 587/465_) can both restrict access by IP via a [CIDR lookup table][postfix-docs::config-table::cidr], however the client IP is already rewritten at this point via PROXY protocol.
Thus those settings cannot be used for restricting access to only trusted proxies, only to the actual clients.
A similar setting [`mynetworks`][postfix-docs::settings::mynetworks] / [`PERMIT_DOCKER`][docs::env::permit_docker] manages elevated trust for bypassing security restrictions. While it is intended for trusted clients, it has no relevance to trusting proxies for the same reasons.
### Monitoring
While PROXY protocol works well with the reverse proxy, you may have some containers internally that interact with DMS on behalf of multiple clients.
??? example "Roundcube + Fail2Ban"
You may have other services with functionality like an API to send mail through DMS that likewise delegates credentials through DMS.
Roundcube is an example of this where authentication is delegated to DMS, which introduces the same concern with loss of client IP.
- While this service does implement some support for preserving the client IP, it is limited.
- This may be problematic when monitoring services like Fail2Ban are enabled that scan logs for multiple failed authentication attempts which triggers a ban on the shared IP address.
You should adjust configuration of these monitoring services to monitor for auth failures from those services directly instead, adding an exclusion for that service IP from any DMS logs monitored (_but be mindful of PROXY header forgery risks_).
[docs::kubernetes]: ../../config/advanced/kubernetes.md#using-the-proxy-protocol
[docs::overrides::dovecot]: ../../config/advanced/override-defaults/dovecot.md
[docs::overrides::postfix]: ../../config/advanced/override-defaults/postfix.md
[docs::overrides::user-patches]: ../../config/advanced/override-defaults/user-patches.md
[docs::ipv6::security-risks]: ../../config/advanced/ipv6.md#what-can-go-wrong
[docs::tls::traefik]: ../../config/security/ssl.md#traefik
[docs::env::permit_docker]: ../../config/environment.md#permit_docker
[gh-dms::dns-rewrite-example]: https://github.com/docker-mailserver/docker-mailserver/issues/3866#issuecomment-1928877236
[nginx-docs::proxyprotocol]: https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol
[caddy::plugin::l4]: https://github.com/mholt/caddy-l4
[traefik-web]: https://traefik.io
[traefik-docs::entrypoint::proxyprotocol]: https://doc.traefik.io/traefik/routing/entrypoints/#proxyprotocol
[traefik-docs::provider-docker::network]: https://doc.traefik.io/traefik/providers/docker/#network
[traefik-docs::router-tcp::server-first-protocols]: https://doc.traefik.io/traefik/routing/routers/#entrypoints_1
[traefik-docs::router-tcp::host-sni]: https://doc.traefik.io/traefik/routing/routers/#rule_1
[traefik-docs::router-tcp::passthrough]: https://doc.traefik.io/traefik/routing/routers/#passthrough
[traefik-docs::service-tcp::proxyprotocol]:https://doc.traefik.io/traefik/routing/services/#proxy-protocol
[dovecot-docs::settings::haproxy-trusted-networks]: https://doc.dovecot.org/settings/core/#core_setting-haproxy_trusted_networks
[dovecot-docs::service-config::haproxy]: https://doc.dovecot.org/configuration_manual/service_configuration/#haproxy-v2-2-19
[dovecot-docs::service-config::ssl]: https://doc.dovecot.org/configuration_manual/service_configuration/#ssl
[postfix-docs::config-table::cidr]: https://www.postfix.org/cidr_table.5.html
[postfix-docs::settings::check_client_access]: https://www.postfix.org/postconf.5.html#check_client_access
[postfix-docs::settings::mynetworks]: https://www.postfix.org/postconf.5.html#mynetworks
[postfix-docs::settings::postscreen_access_list]: https://www.postfix.org/postconf.5.html#postscreen_access_list
[postfix-docs::settings::postscreen_upstream_proxy_protocol]: https://www.postfix.org/postconf.5.html#postscreen_upstream_proxy_protocol
[postfix-docs::settings::smtpd_client_restrictions]: https://www.postfix.org/postconf.5.html#smtpd_client_restrictions
[postfix-docs::settings::smtpd_upstream_proxy_protocol]: https://www.postfix.org/postconf.5.html#smtpd_upstream_proxy_protocol
[docker::networking::l2-switch-gotcha]: https://github.com/moby/moby/issues/45610
[networking::spec:proxy-protocol]: https://github.com/haproxy/haproxy/blob/master/doc/proxy-protocol.txt
[networking::http-header::x-forwarded-for]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For
[networking::http-header::forwarded]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
[networking::osi-model]: https://www.cloudflare.com/learning/ddos/glossary/open-systems-interconnection-model-osi/

View file

@ -34,7 +34,7 @@ A drawback of this method is that any (compromised) Nextcloud application passwo
To answer the questions asked earlier for this specific scenario:
1. Do I want to use Lua to identify mailboxes and verify that users are are authorized to use mail services? **No. Provisioning is done through LDAP.**
1. Do I want to use Lua to identify mailboxes and verify that users are authorized to use mail services? **No. Provisioning is done through LDAP.**
1. Do I want to use Lua to verify passwords that users authenticate with for IMAP/POP3/SMTP in their mail clients? **Yes. Password authentication is done through Lua against Nextcloud.**
1. If the answer is 'yes' to question 1 or 2: are there other methods that better facilitate my use case instead of custom scripts which rely on me being a developer and not just a user? **No. Only HTTP can be used to authenticate against Nextcloud, which is not supported natively by Dovecot or DMS.**
@ -156,7 +156,7 @@ If working with HTTP in Lua, setting `debug = true;` when initiating `dovecot.ht
Note that Lua runs compiled bytecode, and that scripts will be compiled when they are initially started. Once compiled, the bytecode is cached and changes in the Lua script will not be processed automatically. Dovecot will reload its configuration and clear its cached Lua bytecode when running `docker exec CONTAINER_NAME dovecot reload`. A (changed) Lua script will be compiled to bytecode the next time it is executed after running the Dovecot reload command.
[docs::auth-ldap]: ../../config/advanced/auth-ldap.md
[docs::auth-ldap]: ../../config/account-management/provisioner/ldap.md
[docs::dovecot-override-configuration]: ../../config/advanced/override-defaults/dovecot.md#override-configuration
[docs::dovecot-add-configuration]: ../../config/advanced/override-defaults/dovecot.md#add-configuration
[docs::faq-alter-running-dms-instance-without-container-relaunch]: ../../faq.md#how-to-alter-a-running-dms-instance-without-relaunching-the-container

View file

@ -21,10 +21,6 @@ This can be configured by [overriding the default Postfix configurations][docs::
In `postfix-main.cf` you'll have to set the [`smtp_bind_address`][postfix-docs::smtp-bind-address-ipv4] and [`smtp_bind_address6`][postfix-docs::smtp-bind-address-ipv6]
to the respective IP-address on the server you want to use.
[docs::overrides-postfix]: ../../config/advanced/override-defaults/postfix.md
[postfix-docs::smtp-bind-address-ipv4]: https://www.postfix.org/postconf.5.html#smtp_bind_address
[postfix-docs::smtp-bind-address-ipv6]: https://www.postfix.org/postconf.5.html#smtp_bind_address6
!!! example
=== "Contributed solution"
@ -55,14 +51,56 @@ to the respective IP-address on the server you want to use.
```
If that avoids the concern with `smtp-amavis`, you may still need to additionally override for the [`relay` transport][gh-src::postfix-master-cf::relay-transport] as well if you have configured DMS to relay mail.
=== "Bridged Networks"
When your DMS container is using a bridge network, you'll instead need to restrict which IP address inbound and outbound traffic is routed through via the bridged interface.
For **inbound** traffic, you may configure this at whatever scope is most appropriate for you:
- **Daemon:** Change the [default bind address][inbound-ip::docker-docs::daemon] configured in `/etc/docker/daemon.json` (default `0.0.0.0`)
- **Network:** Assign the [`host_binding_ipv4` bridge driver option][inbound-ip::docker-docs::network] as shown in the below `compose.yaml` snippet.
- **Container:** Provide an explicit host IP address when [publishing a port][inbound-ip::docker-docs::container].
For **outbound** traffic, the bridge network will use the default route. You can change this by either:
- [Manually routing networks][outbound-ip::route-manually] on the host.
- Use the [`host_ipv4` driver option][outbind-ip::host-ipv4] for Docker networks to force the SNAT (source IP) that the bridged network will route outbound traffic through.
- This IP address must belong to a network interface to be routed through it.
- IPv6 support via `host_ipv6` [requires at least Docker v25][outbind-ip::host-ipv6].
---
Here is a `compose.yaml` snippet that applies the inbound + outbound settings to the default bridge network Docker Compose creates (_if it already exists, you will need to ensure it's re-created to apply the updated settings_):
```yaml title="compose.yaml"
networks:
default:
driver_opts:
# Inbound IP (sets the host IP that published ports receive traffic from):
com.docker.network.bridge.host_binding_ipv4: 198.51.100.42
# Outbound IP (sets the host IP that external hosts will receive connections from):
com.docker.network.host_ipv4: 198.51.100.42
```
!!! note "IP addresses for documentation"
IP addresses shown in above examples are placeholders, they are IP addresses reserved for documentation by IANA (_[RFC-5737 (IPv4)][rfc-5737] and [RFC-3849 (IPv6)][rfc-3849]_). Replace them with the IP addresses you want DMS to send mail through.
IP addresses shown in above examples (`198.51.100.42` + `2001:DB8::42`) are placeholders, they are IP addresses reserved for documentation by IANA (_[RFC-5737 (IPv4)][rfc-5737] and [RFC-3849 (IPv6)][rfc-3849]_). Replace them with the IP addresses you want DMS to send mail through.
[docs::overrides-postfix]: ../../config/advanced/override-defaults/postfix.md
[postfix-docs::smtp-bind-address-ipv4]: https://www.postfix.org/postconf.5.html#smtp_bind_address
[postfix-docs::smtp-bind-address-ipv6]: https://www.postfix.org/postconf.5.html#smtp_bind_address6
[rfc-5737]: https://datatracker.ietf.org/doc/html/rfc5737
[rfc-3849]: https://datatracker.ietf.org/doc/html/rfc3849
[gh-pr::3465::comment-restrictions]: https://github.com/docker-mailserver/docker-mailserver/pull/3465#discussion_r1458114528
[gh-pr::3465::alternative-solution]: https://github.com/docker-mailserver/docker-mailserver/pull/3465#issuecomment-1678107233
[gh-src::postfix-master-cf::relay-transport]: https://github.com/docker-mailserver/docker-mailserver/blob/9cdbef2b369fb4fb0f1b4e534da8703daf92abc9/target/postfix/master.cf#L65
[inbound-ip::docker-docs::daemon]: https://docs.docker.com/reference/cli/dockerd/#default-network-options
[inbound-ip::docker-docs::network]: https://docs.docker.com/engine/network/drivers/bridge/#default-host-binding-address
[inbound-ip::docker-docs::container]: https://docs.docker.com/reference/compose-file/services/#ports
[outbound-ip::route-manually]: https://github.com/moby/moby/issues/30053#issuecomment-1077041045
[outbind-ip::host-ipv4]: https://github.com/moby/libnetwork/pull/2454
[outbind-ip::host-ipv6]: https://github.com/moby/moby/issues/46469

View file

@ -0,0 +1,233 @@
---
title: 'Use Cases | Relay inbound and outbound mail for an internal DMS'
hide:
- toc
---
## Introduction
!!! info "Community contributed guide"
Adapted into a guide from [this discussion](https://github.com/orgs/docker-mailserver/discussions/3965).
**Requirements:**
- A _public server_ with a static IP, like many VPS providers offer. It will only relay mail to DMS, no mail is stored on this system.
- A _private server_ (e.g.: a local system at home) that will run DMS.
- Both servers are connected to the same network via a VPN (_optional convenience for trust via the `mynetworks` setting_).
---
The guide below will assume the VPN is setup on `192.168.2.0/24` with:
- The _public server_ is using `192.168.2.2`
- The _private server_ is using `192.168.2.3`
The goal of this guide is to configure a _public server_ that can receive inbound mail and relay that over to DMS on a _private server_, which can likewise submit mail outbound through a _public server_ or service.
The primary motivation is to keep your mail storage private instead of storing it to disk unencrypted on a VPS host.
## DNS setup
Follow our [standard guidance][docs::usage-dns-setup] for DNS setup.
Set your A, MX and PTR records for the _public server_ as if it were running DMS.
!!! example "DNS Zone file example"
For this guide, we assume DNS is configured with:
- A public reachable IP address of `11.22.33.44`
- Mail for `@example.com` addresses must have an MX record pointing to `mail.example.com`.
- An A record for `mail.example.com` pointing to the IP address of your _public server_.
```txt
$ORIGIN example.com
@ IN A 11.22.33.44
mail IN A 11.22.33.44
; mail server for example.com
@ IN MX 10 mail.example.com.
```
SPF records should also be set up as you normally would for `mail.example.com`.
## Public Server (Basic Postfix setup)
You will need to install Postfix on your _public server_. The functionality that is needed for this setup is not yet implemented in DMS, so a vanilla Postfix will probably be easier to work with, especially since this server will only be used as an inbound and outbound relay.
It's necessary to adjust some settings afterwards.
<!-- This empty quote block is purely for a visual border -->
!!! quote ""
=== "Postfix main config"
??? example "Create or replace `/etc/postfix/main.cf`"
```cf
# See /usr/share/postfix/main.cf.dist for a commented, more complete version
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no
# appending .domain is the MUA's job.
append_dot_mydomain = no
# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h
# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 3.6 on
# fresh installs.
compatibility_level = 3.6
# TLS parameters
smtpd_tls_cert_file=/etc/postfix/certificates/mail.example.com.crt
smtpd_tls_key_file=/etc/postfix/certificates/mail.example.com.key
smtpd_tls_security_level=may
smtp_tls_CApath=/etc/ssl/certs
smtp_tls_security_level=may
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
alias_database = hash:/etc/aliases
alias_maps = hash:/etc/aliases
maillog_file = /var/log/postfix.log
mailbox_size_limit = 0
inet_interfaces = all
inet_protocols = ipv4
readme_directory = no
recipient_delimiter = +
# Customizations relevant to this guide:
myhostname = mail.example.com
myorigin = example.com
mydestination = localhost
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 192.168.2.0/24
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
transport_maps = hash:/etc/postfix/transport
relay_domains = $mydestination, hash:/etc/postfix/relay
# Disable local system accounts and delivery:
local_recipient_maps =
local_transport = error:local mail delivery is disabled
```
Let's highlight some of the important parts:
- Avoid including `mail.example.com` in `mydestination`, in fact you can just set `localhost` or nothing at all here as we want all mail to be relayed to our _private server_ (DMS).
- `mynetworks` should contain your VPN network (_eg: `192.168.2.0/24` subnet_).
- Important are `transport_maps = hash:/etc/postfix/transport` and `relay_domains = $mydestination, hash:/etc/postfix/relay`, with their file contents covered below.
- For good measure, also disable `local_recipient_maps`.
- You should have a valid certificate configured for `mail.example.com`.
!!! warning "Open relay"
Please be aware that setting `mynetworks` to a public CIDR will leave you with an open relay. **Only** set it to the CIDR of your VPN beyond the localhost ranges.
=== "Route outbound mail through a separate transport"
When mail arrives to the _public server_ for an `@example.com` address, we want to send it via the `relay` transport to our _private server_ over port 25 for delivery to DMS.
[`transport_maps`][postfix-docs::transport_maps] is configured with a [`transport` table][postfix-docs::transport_table] file that matches recipient addresses and assigns a non-default transport. This setting has priority over [`relay_transport`][postfix-docs::relay_transport].
!!! example "Create `/etc/postfix/transport`"
```txt
example.com relay:[192.168.2.3]:25
```
**Other considerations:**
- If you have multiple domains, you can add them here too (on separate lines).
- If you use a smarthost add `* relay:[X.X.X.X]:port` to the bottom (eg: `* relay:[relay1.org]:587`), which will relay everything outbound via this relay host.
!!! tip
Instead of a file, you could alternatively configure `main.cf` with `transport_maps = inline:{ example.com=relay:[192.168.2.3]:25 }`
=== "Configure recipient domains to relay mail"
We want `example.com` to be relayed inbound and everything else relayed outbound.
[`relay_domains`][postfix-docs::relay_domains] is configured with a file with a list of domains that should be relayed (one per line), the 2nd value is required but can be anything.
!!! example "Create `/etc/postfix/relay`"
```txt
example.com OK
```
!!! tip
Instead of a file, you could alternatively configure `main.cf` with `relay_domains = example.com`.
!!! note "Files configured with `hash:` table type must run `postmap` to apply changes"
Run `postmap /etc/postfix/transport` and `postmap /etc/postfix/relay` after creating or updating either of these files, this processes them into a separate file for Postfix to use.
## Private Server (Running DMS)
You can set up your DMS instance as you normally would.
- Be careful not to give it a hostname of `mail.example.com`. Instead, use `internal-mail.example.com` or something similar.
- DKIM can be setup as usual since it considers checks whether the message body has been tampered with, which our public relay doesn't do. Set DKIM up for `mail.example.com`.
Next, we need to configure our _private server_ to relay all outbound mail through the _public server_ (or a separate smarthost service). The setup is [similar to the default relay setup][docs::relay-host-details].
<!-- This empty quote block is purely for a visual border -->
!!! quote ""
=== "Configure the relay host"
!!! example "Create `postfix-relaymap.cf`"
```txt
@example.com [192.168.2.2]:25
```
Meaning all mail sent outbound from `@example.com` addresses will be relayed through the _public server_ at that VPN IP.
The _public server_ `mynetworks` setting from earlier trusts any mail received on port 25 from the VPN network, which is what allows the mail to be sent outbound when it'd otherwise be denied.
=== "Trust the _public server_"
!!! example "Create `postfix-main.cf`"
```txt
mynetworks = 192.168.2.0/24
```
This will trust any connection from the VPN network to DMS, such as from the _public server_ when relaying mail over to DMS at the _private server_.
This step is necessary to skip some security measures that DMS normally checks for, like verifying DNS records like SPF are valid. As the mail is being relayed, those checks would fail otherwise as the IP of your _public server_ would not be authorized to send mail on behalf of the sender address in mail being relayed.
??? tip "Alternative to `mynetworks` setting"
Instead of trusting connections by their IP with the `mynetworks` setting, those same security measures can be skipped for any authenticated deliveries to DMS over port 587 instead.
This is a bit more work. `mynetworks` on the _public server_ `main.cf` Postfix config is for trusting DMS when it sends mail from the _private server_, thus you'll need to have that public Postfix service configured with a login account that DMS can use.
On the _private server_, DMS needs to know the credentials for that login account, that is handled with `postfix-sasl-password.cf`:
```txt
@example.com user:secret
```
You could also relay mail through SendGrid, AWS SES or similar instead of the _public server_ you're running to receive mail from. Login credentials for those relay services are provided via the same `postfix-sasl-password.cf` file.
---
Likewise for the _public server_ to send mail to DMS, it would need to be configured to relay mail with credentials too, removing the need for `mynetworks` on the DMS `postfix-main.cf` config.
The extra effort to require authentication instead of blind trust of your private subnet can be beneficial at reducing the impact of a compromised system or service on that network that wasn't expected to be permitted to send mail.
## IMAP / POP3
IMAP and POP3 need to point towards your _private server_, since that is where the mailboxes are located, which means you need to have a way for your MUA to connect to it.
[docs::usage-dns-setup]: ../../usage.md#minimal-dns-setup
[docs::relay-host-details]: ../../config/advanced/mail-forwarding/relay-hosts.md#technical-details
[postfix-docs::relay_domains]: https://www.postfix.org/postconf.5.html#relay_domains
[postfix-docs::relay_transport]: https://www.postfix.org/postconf.5.html#relay_transport
[postfix-docs::transport_maps]: https://www.postfix.org/postconf.5.html#transport_maps
[postfix-docs::transport_table]: https://www.postfix.org/transport.5.html

View file

@ -2,6 +2,15 @@
title: 'Advanced | iOS Mail Push Support'
---
!!! warning "Status - August 2025"
Apple has since deprecated their API used for certificate renewal (_see [this Apple Developer thread][apple::dev-push-issue-reference]_) as it is currently implemented in `dovecot-xaps-daemon` for `XAPPLEPUSHSERVICE`. There is no actionable resolution for this issue known at this time.
[Apple has communicated plans][apple::push-open-standard] to implement an open IETF standard for push notifications.
[apple::dev-push-issue-reference]: https://developer.apple.com/forums/thread/778671?answerId=850357022#850357022
[apple::push-open-standard]: https://github.com/stalwartlabs/stalwart/issues/747#issuecomment-3142925679
## Introduction
iOS Mail currently does not support the IMAP idle extension. Therefore users can only either check manually or configure intervals for fetching mails in their mail account preferences when using the default configuration.

View file

@ -79,6 +79,14 @@ volumes:
Optionally, you can set the `TZ` ENV variable; e.g. `TZ=Europe/Berlin`. Check [this list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for which values are allowed.
### What About DNS Servers?
Properly working DNS servers are crucial for differentiating spam from legitimate e-mails. Records like `SPF`, `DKIM` and `DMARC` records, as well as working name (resolving `A` records) and reverse name (resolving `PTR` records) resolution ensures legitimate e-mails arrive while e-mails that are more likely phishing and spam do not.
Anti-spam measures (like SpamAssassin or Rspamd) make use of DNS block lists. To learn more check out our [Rspamd documentation on this topic][docs::rspamd-rbl-dnsbl]. In case you want to utilize RBL/DNSBLs, you need a recursive DNS resolver (_not big custom resolvers like Cloudflare, Quad9, Google, etc._).
DMS does not integrate support for an internal DNS service as this is a [responsibility that is sensitive to the host environment][gh-discussion::dms-avoid-maintaining-internal-dns]. You can configure internal services within DMS to use your own managed DNS server, or configure for such at the host or container level (_such as with [`compose.yaml`][docker-compose::docs::config-dns]_).
### What is the file format?
All files are using the Unix format with `LF` line endings. Please do not use `CRLF`.
@ -283,6 +291,12 @@ mydestination = localhost.$mydomain, localhost
proxy_interfaces = X.X.X.X (your public IP)
```
For reverse proxy support you will want to view [our dedicated guide][docs::examples::reverse-proxy].
### How to restrict login by IP?
There are a few ways you could approach this, see [this discussion answer][gh-discussion::restrict-login-by-ip] for advice.
### How to adjust settings with the `user-patches.sh` script
Suppose you want to change a number of settings that are not listed as variables or add things to the server that are not included?
@ -360,20 +374,6 @@ DMS does not manage those concerns, verify they are not causing your delivery pr
- [mail-tester](https://www.mail-tester.com/) can test your deliverability.
- [helloinbox](https://www.helloinbox.email/) provides a checklist of things to improve your deliverability.
### Special Directories
#### What About the `docker-data/dms/config/` Directory?
This documentation and all example configuration files in the GitHub repository use `docker-data/dms/config/` to refer to the directory in the host that is mounted (e.g. via a bind mount) to `/tmp/docker-mailserver/` inside the container.
Most configuration files for Postfix, Dovecot, etc. are persisted here. [Optional configuration][docs-optional-configuration] is stored here as well.
#### What About the `docker-data/dms/mail-state/` Directory?
This documentation and all example configuration files in the GitHub repository use `docker-data/dms/mail-state/` to refer to the directory in the host that is mounted (e.g. via a bind mount) to `/var/mail-state/` inside the container.
When you run DMS with the ENV variable `ONE_DIR=1` (default), this directory will provide support to persist Fail2Ban blocks, ClamAV signature updates, and the like when the container is restarted or recreated. Service data is [relocated to the `mail-state` folder][mail-state-folders] for the following services: Postfix, Dovecot, Fail2Ban, Amavis, PostGrey, ClamAV, SpamAssassin, Rspamd & Redis.
### SpamAssasin
#### How can I manage my custom SpamAssassin rules?
@ -390,14 +390,15 @@ The default setup `@local_domains_acl = ( ".$mydomain" );` does not match subdom
Put received spams in `.Junk/` imap folder using `SPAMASSASSIN_SPAM_TO_INBOX=1` and `MOVE_SPAM_TO_JUNK=1` and add a _user_ cron like the following:
```conf
# This assumes you're having `environment: ONE_DIR=1` in the `mailserver.env`,
# with a consolidated config in `/var/mail-state`
#
# m h dom mon dow command
# Everyday 2:00AM, learn spam from a specific user
0 2 * * * docker exec mailserver sa-learn --spam /var/mail/example.com/username/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin
```
!!! example
**NOTE:** This example assumes you have a [`/var/mail-state` volume][docs::dms-volumes-state] mounted.
```conf
# m h dom mon dow command
# Everyday 2:00AM, learn spam from a specific user
0 2 * * * docker exec mailserver sa-learn --spam /var/mail/example.com/username/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin
```
With `docker-compose` you can more easily use the internal instance of `cron` within DMS. This is less problematic than the simple solution shown above, because it decouples the learning from the host on which DMS is running, and avoids errors if the mail server is not running.
@ -405,6 +406,8 @@ The following configuration works nicely:
??? example
**NOTE:** This example assumes you have a [`/var/mail-state` volume][docs::dms-volumes-state] mounted.
Create a _system_ cron file:
```sh
@ -418,9 +421,6 @@ The following configuration works nicely:
Edit the system cron file `nano ./docker-data/dms/cron/sa-learn`, and set an appropriate configuration:
```conf
# This assumes you're having `environment: ONE_DIR=1` in the env-mailserver,
# with a consolidated config in `/var/mail-state`
#
# '> /dev/null' to send error notifications from 'stderr' to 'postmaster@example.com'
#
# m h dom mon dow user command
@ -495,12 +495,14 @@ $spam_quarantine_to = "quarantine\@example.com";
```
[fail2ban-customize]: ./config/security/fail2ban.md
[docs::dms-volumes-state]: ./config/advanced/optional-config.md#volumes-state
[docs::rspamd-rbl-dnsbl]: ./config/security/rspamd.md#rbls-real-time-blacklists-dnsbls-dns-based-blacklists
[docs-maintenance]: ./config/advanced/maintenance/update-and-cleanup.md
[docs-override-postfix]: ./config/advanced/override-defaults/postfix.md
[docs-userpatches]: ./config/advanced/override-defaults/user-patches.md
[docs-optional-configuration]: ./config/advanced/optional-config.md
[docs::env::sa_env]: ./config/environment.md#spamassassin
[docs::env::sa_kill]: ./config/environment.md#sa_kill
[docs::examples::reverse-proxy]: ./examples/tutorials/mailserver-behind-proxy.md
[github-comment-baredomain]: https://github.com/docker-mailserver/docker-mailserver/issues/3048#issuecomment-1432358353
[github-comment-override-hostname]: https://github.com/docker-mailserver/docker-mailserver/issues/1731#issuecomment-753968425
[github-issue-95]: https://github.com/docker-mailserver/docker-mailserver/issues/95
@ -509,5 +511,7 @@ $spam_quarantine_to = "quarantine\@example.com";
[github-issue-1405-comment]: https://github.com/docker-mailserver/docker-mailserver/issues/1405#issuecomment-590106498
[github-issue-1639]: https://github.com/docker-mailserver/docker-mailserver/issues/1639
[github-issue-1792]: https://github.com/docker-mailserver/docker-mailserver/pull/1792
[gh-discussion::dms-avoid-maintaining-internal-dns]: https://github.com/orgs/docker-mailserver/discussions/3959#discussioncomment-8956322
[gh-discussion::restrict-login-by-ip]: https://github.com/orgs/docker-mailserver/discussions/3847
[docker-compose::docs::config-dns]: https://docs.docker.com/compose/compose-file/compose-file-v3/#dns
[hanscees-userpatches]: https://github.com/hanscees/dockerscripts/blob/master/scripts/tomav-user-patches.sh
[mail-state-folders]: https://github.com/docker-mailserver/docker-mailserver/blob/c7e498194546416fb7231cb03254e77e085d18df/target/scripts/startup/misc-stack.sh#L24-L33

View file

@ -2,16 +2,16 @@
title: Usage
---
This pages explains how to get started with DMS. The guide uses Docker Compose as a reference. In our examples, a volume mounts the host location [`docker-data/dms/config/`][docs-dms-config-volume] to `/tmp/docker-mailserver/` inside the container.
This page explains how to get started with DMS. The guide uses Docker Compose as a reference. In our examples, a volume mounts the host location [`docker-data/dms/config/`][docs::dms-volumes-config] to `/tmp/docker-mailserver/` inside the container.
[docs-dms-config-volume]: ./faq.md#what-about-the-docker-datadmsconfig-directory
[docs::dms-volumes-config]: ./config/advanced/optional-config.md#volumes-config
## Preliminary Steps
Before you can get started with deploying your own mail server, there are some requirements to be met:
1. You need to have a host that you can manage.
2. You need to own a domain, and you need to able to manage DNS for this domain.
2. You need to own a domain, and you need to be able to manage DNS for this domain.
### Host Setup
@ -48,7 +48,7 @@ We will later dig into DKIM, DMARC & SPF, but for now, these are the records tha
- The **MX record** tells everyone which (DNS) name is responsible for e-mails on your domain.
Because you want to keep the option of running another service on the domain name itself, you run your mail server on `mail.example.com`.
This does not imply your e-mails will look like `test@mail.example.com`, the DNS name of your mail server is decoupled of the domain it serves e-mails for.
In theory, you mail server could even serve e-mails for `test@some-other-domain.com`, if the MX record for `some-other-domain.com` points to `mail.example.com`.
Your mail server could also handle emails for `test@some-other-domain.com`, if the MX record for `some-other-domain.com` points to `mail.example.com`.
- The **A record** tells everyone which IP address the DNS name `mail.example.com` resolves to.
- The **PTR record** is the counterpart of the A record, telling everyone what name the IP address `11.22.33.44` resolves to.
@ -164,7 +164,7 @@ You definitely want to setup TLS. Please refer to [our documentation about TLS][
You should add at least one [alias][docs-aliases], the [_postmaster alias_][docs-env-postmaster]. This is a common convention, but not strictly required.
[docs-aliases]: ./config/user-management.md#aliases
[docs-aliases]: ./config/account-management/overview.md#aliases
[docs-env-postmaster]: ./config/environment.md#postmaster_address
```bash

View file

@ -82,6 +82,11 @@ markdown_extensions:
format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.tabbed:
alternate_style: true
slugify: !!python/object/apply:pymdownx.slugs.slugify
kwds:
case: lower
- pymdownx.tasklist:
custom_checkbox: true
- pymdownx.magiclink
- pymdownx.inlinehilite
- pymdownx.tilde
@ -89,6 +94,7 @@ markdown_extensions:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
- pymdownx.highlight:
# Configures an alias (name) to a supported syntax (lang):
extend_pygments_lang:
- name: yml
lang: yaml
@ -98,8 +104,20 @@ markdown_extensions:
lang: cfg
- name: env
lang: properties
# Not helpful with Python Pygments lexer highlighting, but we might change to a JS highlighter in future
# Ideally, this type of codefence might also have word-wrap enabled (CSS: {white-space: pre-wrap})
# We only show PHP snippets, requires config change to work:
# https://squidfunk.github.io/mkdocs-material/setup/extensions/python-markdown-extensions/#highlight
# https://facelessuser.github.io/pymdown-extensions/extensions/highlight/#extended-pygments-lexer-options
- name: php
lang: php
options:
startinline: true
# A variant that sometimes has nicer syntax highlighting:
- name: cf-extra
lang: linuxconfig
- name: cli-syntax
lang: linuxconfig
# These formats aren't supported by Python Pygments lexer,
# but we use them when the context is appropriate.
- name: log
lang: shell-session
- name: fetchmailrc
@ -120,7 +138,14 @@ nav:
- 'Usage': usage.md
- 'Configuration':
- 'Environment Variables': config/environment.md
- 'User Management': config/user-management.md
- 'Account Management':
- 'Overview': config/account-management/overview.md
- 'Provisioner':
- 'File Based': config/account-management/provisioner/file.md
- 'LDAP Service': config/account-management/provisioner/ldap.md
- 'Supplementary':
- 'Master Accounts': config/account-management/supplementary/master-accounts.md
- 'OAuth2 Authentication': config/account-management/supplementary/oauth2.md
- 'Best Practices':
- 'Auto-discovery': config/best-practices/autodiscover.md
- 'DKIM, DMARC & SPF': config/best-practices/dkim_dmarc_spf.md
@ -142,19 +167,17 @@ nav:
- 'Dovecot': config/advanced/override-defaults/dovecot.md
- 'Postfix': config/advanced/override-defaults/postfix.md
- 'Modifications via Script': config/advanced/override-defaults/user-patches.md
- 'LDAP Authentication': config/advanced/auth-ldap.md
- 'OAuth2 Authentication': config/advanced/auth-oauth2.md
- 'Email Filtering with Sieve': config/advanced/mail-sieve.md
- 'Email Gathering with Fetchmail': config/advanced/mail-fetchmail.md
- 'Email Gathering with Getmail': config/advanced/mail-getmail.md
- 'Email Forwarding':
- 'Relay Hosts': config/advanced/mail-forwarding/relay-hosts.md
- 'AWS SES': config/advanced/mail-forwarding/aws-ses.md
- 'Configure Gmail as a relay host': config/advanced/mail-forwarding/gmail-smtp.md
- 'Full-Text Search': config/advanced/full-text-search.md
- 'Kubernetes': config/advanced/kubernetes.md
- 'IPv6': config/advanced/ipv6.md
- 'Podman': config/advanced/podman.md
- 'Dovecot Master Accounts': config/advanced/dovecot-master-accounts.md
- 'Examples':
- 'Tutorials':
- 'Basic Installation': examples/tutorials/basic-installation.md
@ -162,16 +185,18 @@ nav:
- 'Crowdsec': examples/tutorials/crowdsec.md
- 'Building your own Docker image': examples/tutorials/docker-build.md
- 'Blog Posts': examples/tutorials/blog-posts.md
- 'Dovecot FTS with Apache Solr': examples/tutorials/dovecot-solr.md
- 'Use Cases':
- 'Forward-Only Mail-Server with LDAP': examples/use-cases/forward-only-mailserver-with-ldap-authentication.md
- 'Customize IMAP Folders': examples/use-cases/imap-folders.md
- 'iOS Mail Push Support': examples/use-cases/ios-mail-push-support.md
- 'Lua Authentication': examples/use-cases/auth-lua.md
- 'Bind outbound SMTP to a specific network': examples/use-cases/bind-smtp-network-interface.md
- 'Relay inbound and outbound mail for an internal DMS': examples/use-cases/external-relay-only-mailserver.md
- 'FAQ' : faq.md
- 'Contributing':
- 'General Information': contributing/general.md
- 'Tests': contributing/tests.md
- 'Issues and Pull Requests': contributing/issues-and-pull-requests.md
- 'DockerHub': https://hub.docker.com/r/mailserver/docker-mailserver/
- 'GHCR': https://github.com/docker-mailserver/docker-mailserver/pkgs/container/docker-mailserver
- '<span class="icon-external-link"></span>DockerHub': https://hub.docker.com/r/mailserver/docker-mailserver/
- '<span class="icon-external-link"></span>GHCR': https://github.com/docker-mailserver/docker-mailserver/pkgs/container/docker-mailserver

View file

@ -9,13 +9,13 @@
# --- General Section ---------------------------
# -----------------------------------------------
# empty => uses the `hostname` command to get the mail server's canonical hostname
# => Specify a fully-qualified domainname to serve mail for. This is used for many of the config features so if you can't set your hostname (e.g. you're in a container platform that doesn't let you) specify it in this environment variable.
# **empty** => Internally uses the `hostname --fqdn` command to get the canonical hostname assigned to the DMS container.
# => Specify an FQDN (fully-qualified domain name) to serve mail for. The hostname is required for DMS to function correctly
#
# **WARNING**: Setting OVERRIDE_HOSTNAME can have difficult to predict side effects:
# https://docker-mailserver.github.io/docker-mailserver/latest/config/environment/#override_hostname
OVERRIDE_HOSTNAME=
# REMOVED in version v11.0.0! Use LOG_LEVEL instead.
DMS_DEBUG=0
# Set the log level for DMS.
# This is mostly relevant for container startup scripts and change detection event feedback.
#
@ -30,10 +30,6 @@ LOG_LEVEL=info
# debug => Also show debug messages
SUPERVISOR_LOGLEVEL=
# 0 => mail state in default directories
# 1 => consolidate all states into a single directory (`/var/mail-state`) to allow persistence using docker volumes
ONE_DIR=1
# Support for deployment where these defaults are not compatible (eg: some NAS appliances):
# /var/mail vmail User ID (default: 5000)
DMS_VMAIL_UID=
@ -86,17 +82,19 @@ TZ=
NETWORK_INTERFACE=
# empty => modern
# modern => Enables TLSv1.2 and modern ciphers only. (default)
# intermediate => Enables TLSv1, TLSv1.1 and TLSv1.2 and broad compatibility ciphers.
# modern => Limits the cipher suite to secure ciphers only.
# intermediate => Relaxes security by adding additional ciphers for broader compatibility.
# NOTE: The minimum TLS version supported is 1.2, if you need to lower that follow this workaround advice:
# https://github.com/docker-mailserver/docker-mailserver/pull/2945#issuecomment-1949907964
TLS_LEVEL=
# Configures the handling of creating mails with forged sender addresses.
#
# **0** => (not recommended) Mail address spoofing allowed. Any logged in user may create email messages with a forged sender address (see also https://en.wikipedia.org/wiki/Email_spoofing).
# 1 => Mail spoofing denied. Each user may only send with his own or his alias addresses. Addresses with extension delimiters(http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
# 1 => Mail spoofing denied. Each user may only send with their own or their alias addresses. Addresses with extension delimiters(http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
SPOOF_PROTECTION=
# Enables the Sender Rewriting Scheme. SRS is needed if your mail server acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/master/README.md#sender-rewriting-scheme-crash-course) for further explanation.
# Enables the Sender Rewriting Scheme. SRS is needed if your mail server acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/main/README.rst) for further explanation.
# - **0** => Disabled
# - 1 => Enabled
ENABLE_SRS=0
@ -134,6 +132,12 @@ ENABLE_IMAP=1
# **0** => Disabled
ENABLE_CLAMAV=0
# Add the value of this ENV as a prefix to the mail subject when spam is detected.
# NOTE: This subject prefix may be redundant (by default spam is delivered to a junk folder).
# It provides value when your junk mail is stored alongside legitimate mail instead of a separate location (like with `SPAMASSASSIN_SPAM_TO_INBOX=1` or `MOVE_SPAM_TO_JUNK=0` or a POP3 only setup, without IMAP).
# NOTE: When not using Docker Compose, other CRI may not support quote-wrapping the value here to preserve any trailing white-space.
SPAM_SUBJECT=
# Enables Rspamd
# **0** => Disabled
# 1 => Enabled
@ -182,6 +186,12 @@ RSPAMD_HFILTER=1
# Default: 6
RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE=6
# Can be used to enable or disable the (still experimental) neural module.
#
# - **0** => Disabled
# - 1 => Enabled
RSPAMD_NEURAL=0
# Amavis content filter (used for ClamAV & SpamAssassin)
# 0 => Disabled
# 1 => Enabled
@ -259,7 +269,7 @@ POSTFIX_DAGENT=
# empty => 0
POSTFIX_MAILBOX_SIZE_LIMIT=
# See https://docker-mailserver.github.io/docker-mailserver/edge/config/user-management/accounts/#notes
# See https://docker-mailserver.github.io/docker-mailserver/latest/config/account-management/overview/#quotas
# 0 => Dovecot quota is disabled
# 1 => Dovecot quota is enabled
ENABLE_QUOTAS=1
@ -339,6 +349,9 @@ REPORT_SENDER=
# Note: This variable can also determine the interval for Postfix's log summary reports, see [`PFLOGSUMM_TRIGGER`](#pflogsumm_trigger).
LOGROTATE_INTERVAL=weekly
# Defines how many log files are kept by logrorate
LOGROTATE_COUNT=4
# If enabled, employs `reject_unknown_client_hostname` to sender restrictions in Postfix's configuration.
#
@ -355,7 +368,7 @@ POSTFIX_REJECT_UNKNOWN_CLIENT_HOSTNAME=0
POSTFIX_INET_PROTOCOLS=all
# Enables MTA-STS support for outbound mail.
# More details: https://docker-mailserver.github.io/docker-mailserver/latest/config/advanced/mail-mta-sts/
# More details: https://docker-mailserver.github.io/docker-mailserver/v13.3/config/best-practices/mta-sts/
# - **0** ==> MTA-STS disabled
# - 1 => MTA-STS enabled
ENABLE_MTA_STS=0
@ -382,7 +395,7 @@ ENABLE_SPAMASSASSIN=0
# Note: only has an effect if `ENABLE_SPAMASSASSIN=1`
ENABLE_SPAMASSASSIN_KAM=0
# deliver spam messages to the inbox (tagged using SA_SPAM_SUBJECT)
# deliver spam messages to the inbox (tagged using SPAM_SUBJECT)
SPAMASSASSIN_SPAM_TO_INBOX=1
# spam messages will be moved in the Junk folder (SPAMASSASSIN_SPAM_TO_INBOX=1 required)
@ -400,12 +413,6 @@ SA_TAG2=6.31
# triggers spam evasive actions
SA_KILL=10.0
# add tag to subject if spam detected
# The value `undef` opts-out of this feature. The value shown below is the default.
# NOTE: By default spam is delivered to a junk folder, reducing the value of adding a subject prefix.
# NOTE: If not using Docker Compose, other CRI may require the single quotes removed.
#SA_SPAM_SUBJECT='***SPAM*** '
# -----------------------------------------------
# --- Fetchmail Section -------------------------
# -----------------------------------------------
@ -425,7 +432,7 @@ FETCHMAIL_PARALLEL=0
# - 1 => Enabled
ENABLE_GETMAIL=0
# The number of minutes for the interval. Min: 1; Max: 30.
# The number of minutes for the interval. Min: 1; Default: 5.
GETMAIL_POLL=5
# -----------------------------------------------
@ -503,7 +510,7 @@ DOVECOT_MAILBOX_FORMAT=maildir
# empty => no
# yes => Allow bind authentication for LDAP
# https://wiki.dovecot.org/AuthDatabase/LDAP/AuthBinds
# https://doc.dovecot.org/2.4.0/core/config/auth/databases/ldap.html#authentication-bind
DOVECOT_AUTH_BIND=
# -----------------------------------------------
@ -526,12 +533,9 @@ POSTGREY_AUTO_WHITELIST_CLIENTS=5
ENABLE_SASLAUTHD=0
# empty => pam
# empty => ldap
# `ldap` => authenticate against ldap server
# `shadow` => authenticate against local user db
# `mysql` => authenticate against mysql db
# `rimap` => authenticate against imap server
# Note: can be a list of mechanisms like pam ldap shadow
SASLAUTHD_MECHANISMS=
# empty => None
@ -624,8 +628,8 @@ SRS_SECRET=
# Setup relaying all mail through a default relay host
#
# empty => don't configure default relay host
# default host and optional port to relay all mail through
# Set a default host to relay all mail through (optionally include a port)
# Example: [mail.example.com]:587
DEFAULT_RELAY_HOST=
# -----------------------------------------------
@ -635,18 +639,22 @@ DEFAULT_RELAY_HOST=
# Setup relaying for multiple domains based on the domain name of the sender
# optionally uses usernames and passwords in postfix-sasl-password.cf and relay host mappings in postfix-relaymap.cf
#
# empty => don't configure relay host
# default host to relay mail through
# Set a default host to relay mail through
# Example: mail.example.com
RELAY_HOST=
# empty => 25
# default port to relay mail
RELAY_PORT=25
# -----------------------------------------------
# --- Relay Host Credentials Section ------------
# -----------------------------------------------
# Configure a relay user and password to use with RELAY_HOST / DEFAULT_RELAY_HOST
# empty => no default
# default relay username (if no specific entry exists in postfix-sasl-password.cf)
RELAY_USER=
# empty => no default
# password for default relay user
RELAY_PASSWORD=

View file

@ -7,8 +7,7 @@ function _main() {
_require_n_parameters_or_print_usage 1 "${@}"
local MAIL_ACCOUNT="${1}"
shift
local PASSWD="${*}"
local PASSWD="${2}"
_manage_accounts_dovecotmaster_create "${MAIL_ACCOUNT}" "${PASSWD}"
}

View file

@ -7,8 +7,7 @@ function _main() {
_require_n_parameters_or_print_usage 1 "${@}"
local MAIL_ACCOUNT="${1}"
shift
local PASSWD="${*}"
local PASSWD="${2}"
_manage_accounts_create "${MAIL_ACCOUNT}" "${PASSWD}"

View file

@ -8,8 +8,7 @@ function _main() {
local DOMAIN="${1}"
local RELAY_ACCOUNT="${2}"
shift 2
local PASSWD="${*}"
local PASSWD="${3}"
_validate_parameters
_add_relayhost_credentials

View file

@ -5,6 +5,12 @@ source /usr/local/bin/helpers/log.sh
# shellcheck source=../scripts/startup/setup.d/fetchmail.sh
source /usr/local/bin/setup.d/fetchmail.sh
# TODO: This should probably not implicitly enable the feature.
# The setup method will feature gate and output a debug log if
# the feature is not enabled.
#
# Dropping the ENV here will require updating legacy test:
# test/tests/parallel/set3/scripts/setup_cli.bats
ENABLE_FETCHMAIL=1 _setup_fetchmail
su -s /bin/sh -c "/usr/bin/fetchmail \

View file

@ -5,15 +5,20 @@ source /usr/local/bin/helpers/log.sh
# shellcheck source=../scripts/startup/setup-stack.sh
source /usr/local/bin/setup.d/getmail.sh
_setup_getmail
# Setup getmail, even if not enabled.
ENABLE_GETMAIL=1 _setup_getmail
if [[ -d /var/lib/getmail ]]; then
GETMAILDIR=/var/lib/getmail
else
mkdir -p /tmp/docker-mailserver/getmail
GETMAILDIR=/tmp/docker-mailserver/getmail
fi
# Directory, where "oldmail" files are stored.
# Getmail stores its state - its "memory" of what it has seen in your POP/IMAP account - in the oldmail files.
GETMAIL_DIR=/var/lib/getmail
for FILE in /etc/getmailrc.d/getmailrc*; do
/usr/local/bin/getmail --getmaildir "${GETMAILDIR}" --rcfile "${FILE}" --dump | tail -n +7
# If no matching filenames are found, and the shell option nullglob is disabled, the word is left unchanged.
# If the nullglob option is set, and no matches are found, the word is removed.
shopt -s nullglob
# Dump configuration from each RC file.
for RC_FILE in /etc/getmailrc.d/*; do
echo "${RC_FILE##*/}:"
echo
getmail --getmaildir "${GETMAIL_DIR}" --rcfile "${RC_FILE}" --dump | tail -n +6
done

View file

@ -19,7 +19,11 @@ function _main() {
for MAIL_ACCOUNT in "${@}"; do
_account_should_already_exist
[[ ${MAILDEL} -eq 1 ]] && _remove_maildir "${MAIL_ACCOUNT}"
if [[ ${MAILDEL} -eq 1 ]]; then
_remove_maildir "${MAIL_ACCOUNT}"
else
_log 'info' "The mailbox data will not be deleted."
fi
_manage_virtual_aliases_delete '_' "${MAIL_ACCOUNT}" \
|| _exit_with_error "Aliases for '${MAIL_ACCOUNT}' could not be deleted"
@ -31,7 +35,7 @@ function _main() {
_manage_accounts_delete "${MAIL_ACCOUNT}" \
|| _exit_with_error "'${MAIL_ACCOUNT}' could not be deleted"
_log 'info' "'${MAIL_ACCOUNT}' and associated data deleted"
_log 'info' "'${MAIL_ACCOUNT}' and associated data (aliases, quotas) deleted"
done
}
@ -43,14 +47,14 @@ ${ORANGE}USAGE${RESET}
${ORANGE}OPTIONS${RESET}
-y
Skip prompt by approving to ${LWHITE}delete all mail storage${RESET} for the account(s).
Skip prompt by approving to ${LWHITE}delete all mail data${RESET} for the account(s).
${BLUE}Generic Program Information${RESET}
help Print the usage information.
${ORANGE}DESCRIPTION${RESET}
Delete a mail account, including associated data (aliases, quotas) and
optionally the mailbox storage for that account.
optionally the mailbox data for that account.
${ORANGE}EXAMPLES${RESET}
${LWHITE}./setup.sh email del user@example.com${RESET}
@ -87,12 +91,10 @@ function _parse_options() {
function _maildel_request_if_missing() {
if [[ ${MAILDEL} -eq 0 ]]; then
local MAILDEL_CHOSEN
read -r -p "Do you want to delete the mailbox as well (removing all mails)? [Y/n] " MAILDEL_CHOSEN
read -r -p "Do you want to delete the mailbox data as well (removing all mails)? [y/N] " MAILDEL_CHOSEN
# TODO: Why would MAILDEL be set to true if MAILDEL_CHOSEN is empty?
if [[ ${MAILDEL_CHOSEN} =~ (y|Y|yes|Yes) ]] || [[ -z ${MAILDEL_CHOSEN} ]]; then
MAILDEL=1
fi
# Delete mailbox data only if the user provides explicit confirmation.
[[ ${MAILDEL_CHOSEN,,} == "y" ]] && MAILDEL=1
fi
}
@ -103,10 +105,10 @@ function _remove_maildir() {
local DOMAIN_PART="${MAIL_ACCOUNT#*@}"
local MAIL_ACCOUNT_STORAGE_DIR="/var/mail/${DOMAIN_PART}/${LOCAL_PART}"
[[ ! -d ${MAIL_ACCOUNT_STORAGE_DIR} ]] && _exit_with_error "Mailbox directory '${MAIL_ACCOUNT_STORAGE_DIR}' does not exist"
[[ ! -d ${MAIL_ACCOUNT_STORAGE_DIR} ]] && _exit_with_error "Mailbox data directory '${MAIL_ACCOUNT_STORAGE_DIR}' does not exist"
_log 'info' "Deleting Mailbox: '${MAIL_ACCOUNT_STORAGE_DIR}'"
rm -R "${MAIL_ACCOUNT_STORAGE_DIR}" || _exit_with_error 'Mailbox could not be deleted'
_log 'info' "Deleting mailbox data: '${MAIL_ACCOUNT_STORAGE_DIR}'"
rm -R "${MAIL_ACCOUNT_STORAGE_DIR}" || _exit_with_error 'Mailbox data could not be deleted'
# Remove parent directory too if it's empty:
rmdir "/var/mail/${DOMAIN_PART}" &>/dev/null
}

View file

@ -1,7 +0,0 @@
#! /bin/bash
for FILE in /etc/getmailrc.d/getmailrc*; do
if ! pgrep -f "${FILE}$" &>/dev/null; then
/usr/local/bin/getmail --getmaildir /var/lib/getmail --rcfile "${FILE}"
fi
done

View file

@ -8,6 +8,10 @@ source /usr/local/bin/helpers/index.sh
source /etc/dms-settings 2>/dev/null
function _main() {
if [[ ${ACCOUNT_PROVISIONER} != 'FILE' ]]; then
_exit_with_error "This command is only compatible with 'ACCOUNT_PROVISIONER=FILE'"
fi
local DATABASE_ACCOUNTS='/tmp/docker-mailserver/postfix-accounts.cf'
local DATABASE_VIRTUAL='/tmp/docker-mailserver/postfix-virtual.cf'

View file

@ -5,16 +5,24 @@ source /usr/local/bin/helpers/index.sh
if [[ -f /etc/dms-settings ]] && [[ $(_get_dms_env_value 'ENABLE_RSPAMD') -eq 1 ]]; then
if [[ $(_get_dms_env_value 'ENABLE_OPENDKIM') -eq 1 ]]; then
log 'error' "You enabled Rspamd and OpenDKIM - OpenDKIM will be implicitly used for DKIM keys"
_log 'warn' "Conflicting DKIM support, both Rspamd and OpenDKIM enabled - OpenDKIM will manage DKIM keys"
else
/usr/local/bin/rspamd-dkim "${@}"
exit
fi
fi
KEYSIZE=2048
SELECTOR=mail
DOMAINS=
function _main() {
# Default parameters (updated by `_parse_arguments()`):
local KEYSIZE=2048
local SELECTOR=mail
local DMS_DOMAINS=
_require_n_parameters_or_print_usage 0 "${@}"
_parse_arguments "${@}"
_generate_dkim_keys
}
function __usage() {
printf '%s' "${PURPLE}OPEN-DKIM${RED}(${YELLOW}8${RED})
@ -57,122 +65,172 @@ ${ORANGE}EXAMPLES${RESET}
${ORANGE}EXIT STATUS${RESET}
Exit status is 0 if command was successful. If wrong arguments are provided or arguments contain
errors, the script will exit early with exit status 2.
errors, the script will exit early with a non-zero exit status.
"
}
_require_n_parameters_or_print_usage 0 "${@}"
function _parse_arguments() {
# Parse the command args through iteration:
while [[ ${#} -gt 0 ]]; do
case "${1}" in
while [[ ${#} -gt 0 ]]; do
case "${1}" in
( 'keysize' )
if [[ -n ${2+set} ]]; then
KEYSIZE="${2}"
shift
shift
else
_exit_with_error "No keysize provided after 'keysize' argument"
fi
;;
( 'keysize' )
if [[ -n ${2:-} ]]; then
KEYSIZE="${2}"
_log 'trace' "Keysize set to '${KEYSIZE}'"
else
_exit_with_error "No keysize provided after 'keysize' argument"
fi
;;
( 'selector' )
if [[ -n ${2+set} ]]; then
# shellcheck disable=SC2034
SELECTOR="${2}"
shift
shift
else
_exit_with_error "No selector provided after 'selector' argument"
fi
;;
( 'selector' )
if [[ -n ${2:-} ]]; then
SELECTOR="${2}"
_log 'trace' "Selector set to '${SELECTOR}'"
else
_exit_with_error "No selector provided after 'selector' argument"
fi
;;
( 'domain' )
if [[ -n ${2+set} ]]; then
DOMAINS="${2}"
shift
shift
else
_exit_with_error "No domain(s) provided after 'domain' argument"
fi
;;
( 'domain' )
if [[ -n ${2:-} ]]; then
DMS_DOMAINS="${2}"
_log 'trace' "Domain(s) set to '${DMS_DOMAINS}'"
else
_exit_with_error "No domain(s) provided after 'domain' argument"
fi
;;
( * )
__usage
_exit_with_error "Unknown options '${1}' ${2:+and \'${2}\'}"
;;
( 'help' )
__usage
exit 0
;;
esac
done
( * )
__usage
_exit_with_error "Unknown option(s) ${*}"
;;
esac
# Discard these two args (option + value) now that they've been processed:
shift 2
done
}
function _generate_dkim_keys() {
_generate_domains_config
if [[ ! -s ${DATABASE_VHOST} ]]; then
_log 'warn' 'No entries found, no keys to make'
exit 0
fi
# Initialize OpenDKIM configs if necessary:
_create_opendkim_configs
# Generate a keypair per domain and add reference to OpenDKIM configs:
local ENTRY_KEY KEY_TABLE_ENTRY SIGNING_TABLE_ENTRY
while read -r DKIM_DOMAIN; do
_create_dkim_key "${DKIM_DOMAIN}"
# Create / Update OpenDKIM configs with new DKIM key:
ENTRY_KEY="${SELECTOR}._domainkey.${DKIM_DOMAIN}"
KEY_TABLE_ENTRY="${ENTRY_KEY} ${DKIM_DOMAIN}:${SELECTOR}:/etc/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private"
SIGNING_TABLE_ENTRY="*@${DKIM_DOMAIN} ${ENTRY_KEY}"
# If no existing entry, add one:
if ! grep -q "${KEY_TABLE_ENTRY}" "${KEY_TABLE_FILE}"; then
echo "${KEY_TABLE_ENTRY}" >> "${KEY_TABLE_FILE}"
fi
if ! grep -q "${SIGNING_TABLE_ENTRY}" "${SIGNING_TABLE_FILE}"; then
echo "${SIGNING_TABLE_ENTRY}" >> "${SIGNING_TABLE_FILE}"
fi
done < <(_get_valid_lines_from_file "${DATABASE_VHOST}")
# No longer needed, remove:
rm "${DATABASE_VHOST}"
# Ensure ownership is consistent for all content belonging to the base directory,
# During container startup, an internal copy will be made via `_setup_opendkim()`
# with ownership we expect, while this chown is for the benefit of the users' ownership.
# use numerical uid and gid in case the owner of the directory does not exist inside container
chown -R "$(stat -c '%u:%g' "${OPENDKIM_BASE_DIR}")" "${OPENDKIM_BASE_DIR}"
}
# Prepare a file with one domain per line (iterated via while loop as DKIM_DOMAIN):
# Depends on methods from `scripts/helpers/postfix.sh`:
DATABASE_VHOST='/tmp/vhost.dkim'
# Prepare a file with one domain per line:
function _generate_domains_config() {
local TMP_VHOST='/tmp/vhost.dkim.tmp'
# Generate the default vhost (equivalent to /etc/postfix/vhost),
# unless CLI arg DOMAINS provided an alternative list to use instead:
if [[ -z ${DOMAINS} ]]; then
# unless CLI arg DMS_DOMAINS provided an alternative list to use instead:
if [[ -z ${DMS_DOMAINS:-} ]]; then
_obtain_hostname_and_domainname
# uses TMP_VHOST:
_vhost_collect_postfix_domains
else
tr ',' '\n' <<< "${DOMAINS}" >"${TMP_VHOST}"
tr ',' '\n' <<< "${DMS_DOMAINS}" >"${TMP_VHOST}"
fi
# uses DATABASE_VHOST + TMP_VHOST:
# Uses DATABASE_VHOST + TMP_VHOST:
_create_vhost
}
_generate_domains_config
if [[ ! -s ${DATABASE_VHOST} ]]; then
_log 'warn' 'No entries found, no keys to make'
exit 0
fi
# `opendkim-genkey` generates two files at the configured `--directory`:
# - <selector>.private (Private key, PEM encoded)
# - <selector>.txt (Public key, formatted as a TXT record for a RFC 1035 DNS Zone file)
function _create_dkim_key() {
DKIM_DOMAIN=${1?Expected to be provided a domain}
while read -r DKIM_DOMAIN; do
mkdir -p "/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}"
OPENDKIM_DOMAINKEY_DIR="${OPENDKIM_BASE_DIR}/keys/${DKIM_DOMAIN}"
mkdir -p "${OPENDKIM_DOMAINKEY_DIR}"
if [[ ! -f "/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private" ]]; then
_log 'info' "Creating DKIM private key '/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private'"
DKIM_KEY_FILE="${OPENDKIM_DOMAINKEY_DIR}/${SELECTOR}.private"
if [[ ! -f "${DKIM_KEY_FILE}" ]]; then
_log 'info' "Creating DKIM private key '${DKIM_KEY_FILE}'"
# NOTE:
# --domain only affects a comment in the generated DNS Zone file
# --subdomains is the default,
# --nosubdomains would add `t=s` to the DNS TXT record generated
# http://www.opendkim.org/opendkim-genkey.8.html
opendkim-genkey \
--bits="${KEYSIZE}" \
--subdomains \
--domain="${DKIM_DOMAIN}" \
--selector="${SELECTOR}" \
--directory="/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}"
--directory="${OPENDKIM_DOMAINKEY_DIR}"
fi
}
# fix permissions to use the same user:group as /tmp/docker-mailserver/opendkim/keys
chown -R "$(stat -c '%U:%G' /tmp/docker-mailserver/opendkim/keys)" "/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}"
OPENDKIM_BASE_DIR='/tmp/docker-mailserver/opendkim'
KEY_TABLE_FILE="${OPENDKIM_BASE_DIR}/KeyTable"
SIGNING_TABLE_FILE="${OPENDKIM_BASE_DIR}/SigningTable"
TRUSTED_HOSTS_FILE="${OPENDKIM_BASE_DIR}/TrustedHosts"
# Create configs if missing:
function _create_opendkim_configs() {
mkdir -p "${OPENDKIM_BASE_DIR}"
local OPENDKIM_CONFIGS=(
"${KEY_TABLE_FILE}"
"${SIGNING_TABLE_FILE}"
"${TRUSTED_HOSTS_FILE}"
)
# write to KeyTable if necessary
KEYTABLEENTRY="${SELECTOR}._domainkey.${DKIM_DOMAIN} ${DKIM_DOMAIN}:${SELECTOR}:/etc/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private"
if [[ ! -f "/tmp/docker-mailserver/opendkim/KeyTable" ]]; then
_log 'debug' 'Creating DKIM KeyTable'
echo "${KEYTABLEENTRY}" >/tmp/docker-mailserver/opendkim/KeyTable
else
if ! grep -q "${KEYTABLEENTRY}" "/tmp/docker-mailserver/opendkim/KeyTable"; then
echo "${KEYTABLEENTRY}" >>/tmp/docker-mailserver/opendkim/KeyTable
# Only create if the file doesn't exist (avoids modifying mtime):
for FILE in "${OPENDKIM_CONFIGS[@]}"; do
if [[ ! -f "${FILE}" ]]; then
_log 'debug' "Creating OpenDKIM config '${FILE}'"
touch "${FILE}"
fi
fi
done
# write to SigningTable if necessary
SIGNINGTABLEENTRY="*@${DKIM_DOMAIN} ${SELECTOR}._domainkey.${DKIM_DOMAIN}"
if [[ ! -f /tmp/docker-mailserver/opendkim/SigningTable ]]; then
_log 'debug' 'Creating DKIM SigningTable'
echo "*@${DKIM_DOMAIN} ${SELECTOR}._domainkey.${DKIM_DOMAIN}" >/tmp/docker-mailserver/opendkim/SigningTable
else
if ! grep -q "${SIGNINGTABLEENTRY}" /tmp/docker-mailserver/opendkim/SigningTable; then
echo "${SIGNINGTABLEENTRY}" >>/tmp/docker-mailserver/opendkim/SigningTable
fi
# If file exists but is empty, add default hosts to trust:
if [[ ! -s "${TRUSTED_HOSTS_FILE}" ]]; then
_log 'debug' 'Adding default trust to OpenDKIM TrustedHosts config'
echo "127.0.0.1" > "${TRUSTED_HOSTS_FILE}"
echo "localhost" >> "${TRUSTED_HOSTS_FILE}"
fi
done < <(_get_valid_lines_from_file "${DATABASE_VHOST}")
}
# create TrustedHosts if missing
if [[ -d /tmp/docker-mailserver/opendkim ]] && [[ ! -f /tmp/docker-mailserver/opendkim/TrustedHosts ]]; then
_log 'debug' 'Creating DKIM TrustedHosts'
echo "127.0.0.1" >/tmp/docker-mailserver/opendkim/TrustedHosts
echo "localhost" >>/tmp/docker-mailserver/opendkim/TrustedHosts
fi
_main "${@}"

View file

@ -55,6 +55,10 @@ ${ORANGE}EXAMPLES${RESET}
${LWHITE}setup config dkim domain example.com${RESET}
Generate the DKIM key for a different domain (example.com).
${LWHITE}setup config dkim keytype ed25519 domain edward.com selector elliptic-test${RESET}
Generate the DKIM key using the ED25519 elliptic curve for the domain
edward.com and the selector elliptic-test.
${ORANGE}EXIT STATUS${RESET}
Exit status is 0 if command was successful. If wrong arguments are provided or arguments contain
errors, the script will exit early with a non-zero exit status.
@ -177,10 +181,14 @@ function _create_keys() {
exit 1
else
_log 'info' "Overwriting existing files as the '--force' option was supplied"
rm "${PUBLIC_KEY_FILE}" "${PUBLIC_KEY_DNS_FILE}" "${PRIVATE_KEY_FILE}"
[[ -f ${PUBLIC_KEY_FILE} ]] && rm "${PUBLIC_KEY_FILE}"
[[ -f ${PUBLIC_KEY_DNS_FILE} ]] && rm "${PUBLIC_KEY_DNS_FILE}"
[[ -f ${PRIVATE_KEY_FILE} ]] && rm "${PRIVATE_KEY_FILE}"
fi
fi
__create_rspamd_err_log
# shellcheck disable=SC2310
if __do_as_rspamd_user rspamadm \
dkim_keygen \
@ -188,12 +196,14 @@ function _create_keys() {
-d "${DOMAIN}" \
"${KEYTYPE_OPTIONS[@]}" \
-k "${PRIVATE_KEY_FILE}" \
>"${PUBLIC_KEY_FILE}"
>"${PUBLIC_KEY_FILE}" \
&& ! __filter_rspamd_err_log 'Permission denied' # we also need to check the log for error messages
then
_log 'info' 'Successfully created DKIM keys'
_log 'debug' "Public key written to '${PUBLIC_KEY_FILE}'"
_log 'debug' "Private key written to '${PRIVATE_KEY_FILE}'"
_log 'info' 'Successfully created DKIM keys'
_log 'debug' "Public key written to '${PUBLIC_KEY_FILE}'"
_log 'debug' "Private key written to '${PRIVATE_KEY_FILE}'"
else
__print_rspamd_err_log
_exit_with_error 'Creating keys failed'
fi
}

View file

@ -64,6 +64,7 @@ ${RED}[${ORANGE}SUB${RED}]${ORANGE}COMMANDS${RESET}
${LBLUE}COMMAND${RESET} debug ${RED}:=${RESET}
setup debug ${CYAN}fetchmail${RESET}
setup debug ${CYAN}getmail${RESET}
setup debug ${CYAN}login${RESET} <COMMANDS>
setup debug ${CYAN}show-mail-logs${RESET}
@ -150,6 +151,7 @@ function _main() {
( debug )
case ${2:-} in
( fetchmail ) debug-fetchmail ;;
( getmail ) debug-getmail ;;
( show-mail-logs ) cat /var/log/mail/mail.log ;;
( login )
shift 2

View file

@ -7,8 +7,7 @@ function _main() {
_require_n_parameters_or_print_usage 1 "${@}"
local MAIL_ACCOUNT="${1}"
shift
local PASSWD="${*}"
local PASSWD="${2}"
_manage_accounts_dovecotmaster_update "${MAIL_ACCOUNT}" "${PASSWD}"
}

View file

@ -7,8 +7,7 @@ function _main() {
_require_n_parameters_or_print_usage 1 "${@}"
local MAIL_ACCOUNT="${1}"
shift
local PASSWD="${*}"
local PASSWD="${2}"
_manage_accounts_update "${MAIL_ACCOUNT}" "${PASSWD}"
}

View file

@ -51,7 +51,7 @@ plugin {
# deprecated imapflags extension in addition to all extensions were already
# enabled by default.
#sieve_extensions = +notify +imapflags
sieve_extensions = +notify +imapflags +vnd.dovecot.pipe +vnd.dovecot.filter
sieve_extensions = +notify +imapflags +special-use +vnd.dovecot.pipe +vnd.dovecot.filter
# Which Sieve language extensions are ONLY available in global scripts. This
# can be used to restrict the use of certain Sieve extensions to administrator

View file

@ -6,7 +6,7 @@
passdb {
driver = ldap
mechanism = plain login
mechanisms = plain login
# Path for LDAP configuration file, see example-config/dovecot-ldap.conf.ext
args = /etc/dovecot/dovecot-ldap.conf.ext

View file

@ -29,9 +29,6 @@ enabled = true
# https://github.com/docker-mailserver/docker-mailserver/issues/3256#issuecomment-1511188760
mode = extra
[postfix-sasl]
enabled = true
# This jail is used for manual bans.
# To ban an IP address use: setup.sh fail2ban ban <IP>
[custom]

View file

@ -0,0 +1,47 @@
#!/bin/bash
# shellcheck source=../scripts/helpers/log.sh
source /usr/local/bin/helpers/log.sh
# Directory, where "oldmail" files are stored.
# getmail stores its state - its "memory" of what it has seen in your POP/IMAP account - in the oldmail files.
GETMAIL_DIR=/var/lib/getmail
# Kill all child processes on EXIT.
# Otherwise 'supervisorctl restart getmail' leads to zombie 'sleep' processes.
trap 'pkill --parent ${$}' EXIT
function _syslog_error() {
logger --priority mail.err --tag getmail "${1}"
}
function _stop_service() {
_syslog_error "Stopping service"
exec supervisorctl stop getmail
}
# Verify the correct value for GETMAIL_POLL. Valid are any numbers greater than 0.
if [[ ! ${GETMAIL_POLL} =~ ^[0-9]+$ ]] || [[ ${GETMAIL_POLL} -lt 1 ]]; then
_syslog_error "Invalid value for GETMAIL_POLL: ${GETMAIL_POLL}"
_stop_service
fi
# If no matching filenames are found, and the shell option nullglob is disabled, the word is left unchanged.
# If the nullglob option is set, and no matches are found, the word is removed.
shopt -s nullglob
# Run each getmailrc periodically.
while :; do
for RC_FILE in /etc/getmailrc.d/*; do
_log 'debug' "Processing ${RC_FILE}"
getmail --getmaildir "${GETMAIL_DIR}" --rcfile "${RC_FILE}"
done
# Stop service if no configuration is found.
if [[ -z ${RC_FILE} ]]; then
_syslog_error 'No configuration found'
_stop_service
fi
sleep "${GETMAIL_POLL}m"
done

View file

@ -0,0 +1,11 @@
# https://getmail6.org/configuration.html#conf-options
[options]
verbose = 0
read_all = false
delete = false
max_messages_per_session = 500
received = false
delivered_to = false
message_log_syslog = true

View file

@ -0,0 +1,3 @@
# ignore output from dovecot-fts-xapian about successfully indexed messages
dovecot: indexer-worker\([^\)]+\).*Indexed
dovecot: indexer-worker\([^\)]+\).*FTS Xapian: Waiting for all pending documents to be processed

View file

@ -68,9 +68,10 @@ smtpd_forbid_bare_newline = yes
# smtpd_forbid_bare_newline_exclusions = $mynetworks
# Custom defined parameters for DMS:
# reject_unknown_sender_domain: https://github.com/docker-mailserver/docker-mailserver/issues/3716#issuecomment-1868033234
# Custom sender restrictions overview: https://github.com/docker-mailserver/docker-mailserver/pull/4379#issuecomment-2670365917
# `reject_unknown_sender_domain`: https://github.com/docker-mailserver/docker-mailserver/issues/3716#issuecomment-1868033234
dms_smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unknown_sender_domain
# Submission ports 587 and 465 support for SPOOF_PROTECTION=1
# `SPOOF_PROTECTION=1` support requires prepending `reject_authenticated_sender_login_mismatch`
mua_sender_restrictions = reject_authenticated_sender_login_mismatch, $dms_smtpd_sender_restrictions
# Postscreen settings to drop zombies/open relays/spam early
@ -86,7 +87,7 @@ postscreen_dnsbl_sites =
list.dnswl.org=127.0.[0..255].1*-3
list.dnswl.org=127.0.[0..255].[2..3]*-4
postscreen_dnsbl_threshold = 3
postscreen_dnsbl_whitelist_threshold = -1
postscreen_dnsbl_allowlist_threshold = -1
postscreen_greet_action = enforce
postscreen_bare_newline_action = enforce
@ -119,9 +120,6 @@ smtp_header_checks = pcre:/etc/postfix/maps/sender_header_filter.pcre
# The default compatibility_level is 0 - which retains legacy settings defaults:
# http://www.postfix.org/COMPATIBILITY_README.html
# If backwards-compaitibilty log messages appear, fix them by explicitly adding
# If backwards-compatibility log messages appear, fix them by explicitly adding
# the legacy or new default value (alternatively raise the compatibility_level)
#
# TODO: The next compatibility_level is 3.6, when Postfix 3.6 is available consider
# bumping this value after taking the compaitibilty changes into account.
compatibility_level = 2
compatibility_level = 3.6

View file

@ -46,6 +46,8 @@ pickup fifo n - n 60 1 pickup
-o content_filter=
-o receive_override_options=no_header_body_checks
# This relates to submission(s) services defined above:
# https://www.postfix.org/BUILTIN_FILTER_README.html#mx_submission
sender-cleanup unix n - n - 0 cleanup
-o syslog_name=postfix/sender-cleanup
-o header_checks=pcre:/etc/postfix/maps/sender_header_filter.pcre

View file

@ -8,4 +8,5 @@
/^\s*X-Mailer/ IGNORE
/^\s*X-Originating-IP/ IGNORE
/^\s*Received: from.*127.0.0.1/ IGNORE
/^\s*X-MS-Reactions:/ IGNORE
/^\s*Message-Id:/i PREPEND X-MS-Reactions: disallow

View file

@ -6,7 +6,11 @@
# and to be able to explain the impact on the whole system.
greylist = 4;
add_header = 6;
rewrite_subject = 7;
reject = 11;
subject = "***SPAM*** %s"
# The value `null` disabled the action. A subject rewrite is handled by `SPAM_SUBJECT`:
# https://docker-mailserver.github.io/docker-mailserver/latest/config/environment/#spam_subject
#
# The reasoning for this can be found in
# https://github.com/docker-mailserver/docker-mailserver/issues/3804
rewrite_subject = null;

View file

@ -0,0 +1,18 @@
# In addition to `policies_group.conf`, this file contains
# symbols that are applied when certain other symbols are
# applied (or not applied).
#
# We are especially interested in the `policy` field, because
# there are cases in which `remove_weight` is undesirable.
# When neither SPF, DKIM, nor DMARC are available, we want
# to increase the base score so we apply at least greylisting.
AUTH_NA {
score = 2.5;
policy = "leave";
}
AUTH_NA_OR_FAIL {
score = 1;
policy = "leave";
}

View file

@ -0,0 +1,42 @@
#https://github.com/rspamd/rspamd/issues/3099
rules {
"NEURAL_WEEK_1000" {
train {
max_trains = 1000;
max_usages = 50;
max_iterations = 25;
learning_rate = 0.01,
spam_score = 8;
ham_score = -2;
}
symbol_spam = "NEURAL_WEEK_SPAM";
symbol_ham = "NEURAL_WEEK_HAM";
ann_expire = 300d;
}
"NEURAL_DAYS_200" {
train {
max_trains = 200;
max_usages = 10;
max_iterations = 25;
learning_rate = 0.01,
spam_score = 8;
ham_score = -2;
}
symbol_spam = "NEURAL_DAYS_SPAM";
symbol_ham = "NEURAL_DAYS_HAM";
ann_expire = 100d;
}
"NEURAL_HALF_DAY_50" {
train {
max_trains = 50;
max_usages = 4;
max_iterations = 25;
learning_rate = 0.01,
spam_score = 8;
ham_score = -2;
}
symbol_spam = "NEURAL_HALF_DAY_SPAM";
symbol_ham = "NEURAL_HALF_DAY_HAM";
ann_expire = 13d;
}
}

View file

@ -0,0 +1,26 @@
symbols = {
"NEURAL_WEEK_SPAM" {
weight = 3.0; # sample weight
description = "Neural network spam (long)";
}
"NEURAL_WEEK_HAM" {
weight = -3.0; # sample weight
description = "Neural network ham (long)";
}
"NEURAL_DAYS_SPAM" {
weight = 2.5; # sample weight
description = "Neural network spam (medium)";
}
"NEURAL_DAYS_HAM" {
weight = -1.5; # sample weight
description = "Neural network ham (medium)";
}
"NEURAL_HALF_DAY_SPAM" {
weight = 2.0; # sample weight
description = "Neural network spam (short)";
}
"NEURAL_HALF_DAY_HAM" {
weight = -1.0; # sample weight
description = "Neural network ham (short)";
}
}

View file

@ -1,3 +1,3 @@
pidfile = false;
soft_reject_on_timeout = true;
local_networks = "127.0.0.1/8, 10.0.0.0/8, 172.16.0.0/12 192.168.0.0/16";
local_networks = "127.0.0.1/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16";

View file

@ -1,72 +1,75 @@
# Please refer to
# https://github.com/docker-mailserver/docker-mailserver/issues/3690
# for understanding this file and its scores' values.
#
# This configuration is not 100% compliant with RFC7489.
# This is intentional! Rspamd has additional symbols than those defined in this file.
# 100% compliance is not desirable as those symbols will change the overall spam score.
symbols = {
# SPF
"R_SPF_ALLOW" {
"R_SPF_ALLOW" { # SPF check succeeded
weight = -1;
description = "SPF verification allows sending";
groups = ["spf"];
}
"R_SPF_NA" {
"R_SPF_NA" { # SPF is not available for this domain
weight = 1.5;
description = "Missing SPF record";
one_shot = true;
groups = ["spf"];
}
"R_SPF_SOFTFAIL" {
weight = 2.5;
description = "SPF verification soft-failed";
groups = ["spf"];
}
"R_SPF_FAIL" {
weight = 4.5;
description = "SPF verification failed";
groups = ["spf"];
}
"R_SPF_NEUTRAL" { # == R_SPF_NA
"R_SPF_NEUTRAL" { # same as R_SPF_NA
weight = 1.5;
description = "SPF policy is neutral";
groups = ["spf"];
}
"R_SPF_DNSFAIL" { # == R_SPF_SOFTFAIL
"R_SPF_SOFTFAIL" { # there was a temporary DNS issue and SPF could not be checked
weight = 2.5;
description = "SPF verification soft-failed";
groups = ["spf"];
}
"R_SPF_DNSFAIL" { # same as R_SPF_SOFTFAIL
weight = 2.5;
description = "SPF DNS failure";
groups = ["spf"];
}
"R_SPF_PERMFAIL" { # == R_SPF_FAIL
"R_SPF_FAIL" { # SPF check failed
weight = 4.5;
description = "SPF verification failed";
groups = ["spf"];
}
"R_SPF_PERMFAIL" { # same as R_SPF_FAIL
weight = 4.5;
description = "SPF record is malformed or persistent DNS error";
groups = ["spf"];
}
# DKIM
"R_DKIM_ALLOW" {
"R_DKIM_ALLOW" { # DKIM check succeeded
weight = -1;
description = "DKIM verification succeed";
one_shot = true;
groups = ["dkim"];
}
"R_DKIM_NA" {
weight = 0;
"R_DKIM_NA" { # DKIM is not available for this domain
weight = 1;
description = "Missing DKIM signature";
one_shot = true;
groups = ["dkim"];
}
"R_DKIM_TEMPFAIL" {
"R_DKIM_TEMPFAIL" { # there was a temporary DNS issue and DKIM could not be checked
weight = 1.5;
description = "DKIM verification soft-failed";
groups = ["dkim"];
}
"R_DKIM_PERMFAIL" {
"R_DKIM_PERMFAIL" { # DKIM check failed
weight = 4.5;
description = "DKIM verification hard-failed (invalid)";
groups = ["dkim"];
}
"R_DKIM_REJECT" { # == R_DKIM_PERMFAIL
"R_DKIM_REJECT" { # same as R_DKIM_PERMFAIL
weight = 4.5;
description = "DKIM verification failed";
one_shot = true;
@ -74,35 +77,34 @@ symbols = {
}
# DMARC
"DMARC_NA" {
weight = 1;
description = "No DMARC record";
groups = ["dmarc"];
}
"DMARC_POLICY_QUARANTINE" {
weight = 1.5;
description = "DMARC quarantine policy";
groups = ["dmarc"];
}
"DMARC_POLICY_REJECT" {
weight = 2;
description = "DMARC reject policy";
groups = ["dmarc"];
}
"DMARC_POLICY_ALLOW" { # no equivalent
"DMARC_POLICY_ALLOW" { # DMARC check succeeded
weight = -1;
description = "DMARC permit policy";
groups = ["dmarc"];
}
"DMARC_POLICY_ALLOW_WITH_FAILURES" { # no equivalent
weight = -0.5;
"DMARC_POLICY_ALLOW_WITH_FAILURES" { # DMARC check succeeded but either SPF or DKIM was not successful
weight = 0;
description = "DMARC permit policy with DKIM/SPF failure";
groups = ["dmarc"];
}
"DMARC_POLICY_SOFTFAIL" { # == DMARC_POLICY_QUARANTINE
"DMARC_NA" { # DMARC is not available for this domain
weight = 0.5;
description = "No DMARC record";
groups = ["dmarc"];
}
"DMARC_POLICY_SOFTFAIL" { # there was a temporary DNS issue and DMARC could not be checked
weight = 1.5;
description = "DMARC soft-failed";
groups = ["dmarc"];
}
"DMARC_POLICY_QUARANTINE" { # DMARC check failed and the policy is to quarantine
weight = 3;
description = "DMARC quarantine policy";
groups = ["dmarc"];
}
"DMARC_POLICY_REJECT" { # DMARC check failed and the policy is to reject
weight = 5.5;
description = "DMARC reject policy";
groups = ["dmarc"];
}
}

View file

@ -8,23 +8,42 @@ set -eE -u -o pipefail
# shellcheck source=../helpers/log.sh
source /usr/local/bin/helpers/log.sh
# shellcheck disable=SC2310
_log_level_is 'trace' && QUIET='-y' || QUIET='-qq'
function _compile_dovecot_fts_xapian() {
function _install_build_deps() {
apt-get "${QUIET}" update
apt-get "${QUIET}" --no-install-recommends install automake libtool pkg-config libicu-dev libsqlite3-dev libxapian-dev make build-essential dh-make devscripts dovecot-dev
curl -Lso dovecot-fts-xapian.tar.gz https://github.com/grosjo/fts-xapian/releases/download/1.5.5/dovecot-fts-xapian-1.5.5.tar.gz
tar xzvf dovecot-fts-xapian.tar.gz
cd fts-xapian-1.5.5
USER=root dh_make -p dovecot-fts-xapian-1.5.5 --single --native --copyright gpl2 -y
rm debian/*.ex debian/*.EX
cp PACKAGES/DEB/control debian/
cp PACKAGES/DEB/changelog debian/
cp PACKAGES/DEB/compat debian/
sed -i 's/1\.4\.11-6/1.5.5/g' debian/control
sed -i 's/1\.4\.11-6/1.5.5/g' debian/changelog
debuild -us -uc -B | tee /tmp/debuild.log 2>&1
apt-get "${QUIET}" install --no-install-recommends \
automake libtool pkg-config libicu-dev libsqlite3-dev libxapian-dev make build-essential dh-make devscripts dovecot-dev
}
_compile_dovecot_fts_xapian
function _build_package() {
local XAPIAN_VERSION='1.9'
curl -fsSL "https://github.com/grosjo/fts-xapian/releases/download/${XAPIAN_VERSION}/dovecot-fts-xapian-${XAPIAN_VERSION}.tar.gz" \
| tar -xz
cd "fts-xapian-${XAPIAN_VERSION}"
# Prepare for building DEB source package:
# https://manpages.debian.org/bookworm/dh-make/dh_make.1.en.html
# License LGPL 2.1: https://github.com/grosjo/fts-xapian/issues/174#issuecomment-2422404568
USER=root dh_make --packagename "dovecot-fts-xapian-${XAPIAN_VERSION}" --single --native --copyright lgpl2 -y
# Remove generated example files:
rm debian/*.ex
# Add required package metadata:
# https://www.debian.org/doc/manuals/maint-guide/dreq.en.html#control
curl -fsSL https://raw.githubusercontent.com/grosjo/fts-xapian/refs/tags/1.7.16/PACKAGES/DEB/control > debian/control
# Replace version number:
sed -i -E "s|(dovecot-fts-xapian)-[1-9\.-]+|\1-${XAPIAN_VERSION}|g" debian/control
# Required to proceed with debuild:
# https://www.debian.org/doc/manuals/maint-guide/dother.en.html#compat
# (13 is the default debhelper version from the original `dh_make` generated `debian/control`):
echo '13' > debian/compat
# Build arch specific binary package via debuild:
# https://manpages.debian.org/bookworm/devscripts/debuild.1.en.html
# https://manpages.debian.org/bookworm/dpkg-dev/dpkg-buildpackage.1.en.html
debuild --no-sign --build=any | tee /tmp/debuild.log 2>&1
}
_install_build_deps
_build_package

View file

@ -1,10 +1,13 @@
#!/bin/bash
# -eE :: exit on error (do this in functions as well)
# -u :: show (and exit) when using unset variables
# -e :: exit on error (do this in functions as well)
# -E :: inherit the ERR trap to functions, command substitutions and sub-shells
# -u :: show (and exit) when using unset variables
# -o pipefail :: exit on error in pipes
set -eE -u -o pipefail
VERSION_CODENAME='bookworm'
# shellcheck source=../helpers/log.sh
source /usr/local/bin/helpers/log.sh
@ -17,38 +20,75 @@ function _pre_installation_steps() {
_log 'trace' 'Updating package signatures'
apt-get "${QUIET}" update
_log 'trace' 'Installing packages that are needed early'
apt-get "${QUIET}" install --no-install-recommends apt-utils 2>/dev/null
_log 'trace' 'Upgrading packages'
apt-get "${QUIET}" upgrade
_log 'trace' 'Installing packages that are needed early'
# Add packages usually required by apt to:
local EARLY_PACKAGES=(
# Avoid logging unnecessary warnings:
apt-utils
# Required for adding third-party repos (/etc/apt/sources.list.d) as alternative package sources (eg: Dovecot CE and Rspamd):
apt-transport-https ca-certificates curl gnupg
# Avoid problems with SA / Amavis (https://github.com/docker-mailserver/docker-mailserver/pull/3403#pullrequestreview-1596689953):
systemd-standalone-sysusers
)
apt-get "${QUIET}" install --no-install-recommends "${EARLY_PACKAGES[@]}" 2>/dev/null
}
function _install_postfix() {
_log 'debug' 'Installing Postfix'
# Install third-party commands to /usr/local/bin
function _install_utils() {
local ARCH_A
ARCH_A=$(uname --machine)
# Alternate naming convention support: x86_64 (amd64) / aarch64 (arm64)
# https://en.wikipedia.org/wiki/X86-64#Industry_naming_conventions
local ARCH_B
case "${ARCH_A}" in
( 'x86_64' ) ARCH_B='amd64' ;;
( 'aarch64' ) ARCH_B='arm64' ;;
( * )
_log 'error' "Unsupported arch: '${ARCH_A}'"
return 1
;;
esac
_log 'warn' 'Applying workaround for Postfix bug (see https://github.com//issues/2023#issuecomment-855326403)'
# TIP: `*.tar.gz` releases tend to forget to reset UID/GID ownership when archiving.
# When extracting with `tar` as `root` the archived UID/GID is kept, unless using `--no-same-owner`.
# Likewise when the binary is in a nested location the full archived path
# must be provided + `--strip-components` to extract the file to the target directory.
# Doing this avoids the need for (`mv` + `rm`) or (`--to-stdout` + `chmod +x`)
_log 'debug' 'Installing utils sourced from Github'
# Debians postfix package has a post-install script that expects a valid FQDN hostname to work:
mv /bin/hostname /bin/hostname.bak
echo "echo 'docker-mailserver.invalid'" >/bin/hostname
chmod +x /bin/hostname
apt-get "${QUIET}" install --no-install-recommends postfix
mv /bin/hostname.bak /bin/hostname
_log 'trace' 'Installing jaq'
local JAQ_TAG='v2.1.0'
curl -sSfL "https://github.com/01mf02/jaq/releases/download/${JAQ_TAG}/jaq-$(uname -m)-unknown-linux-gnu" -o /usr/local/bin/jaq
chmod +x /usr/local/bin/jaq
# Irrelevant - Debian's default `chroot` jail config for Postfix needed a separate syslog socket:
rm /etc/rsyslog.d/postfix.conf
_log 'trace' 'Installing step'
local STEP_RELEASE='0.28.2'
curl -sSfL "https://github.com/smallstep/cli/releases/download/v${STEP_RELEASE}/step_linux_${STEP_RELEASE}_${ARCH_B}.tar.gz" \
| tar -xz --directory /usr/local/bin --no-same-owner --strip-components=2 "step_${STEP_RELEASE}/bin/step"
_log 'trace' 'Installing swaks'
# `perl-doc` is required for `swaks --help` to work:
apt-get "${QUIET}" install --no-install-recommends perl-doc
local SWAKS_VERSION='20240103.0'
local SWAKS_RELEASE="swaks-${SWAKS_VERSION}"
curl -sSfL "https://github.com/jetmore/swaks/releases/download/v${SWAKS_VERSION}/${SWAKS_RELEASE}.tar.gz" \
| tar -xz --directory /usr/local/bin --no-same-owner --strip-components=1 "${SWAKS_RELEASE}/swaks"
}
function _install_packages() {
_log 'debug' 'Installing all packages now'
ANTI_VIRUS_SPAM_PACKAGES=(
amavisd-new clamav clamav-daemon
pyzor razor spamassassin
local ANTI_VIRUS_SPAM_PACKAGES=(
clamav clamav-daemon
# spamassassin is used only with amavisd-new, while pyzor + razor are used by spamassasin
amavisd-new spamassassin pyzor razor
)
CODECS_PACKAGES=(
# predominantly for Amavis support
local CODECS_PACKAGES=(
altermime arj bzip2
cabextract cpio file
gzip lhasa liblz4-tool
@ -57,103 +97,125 @@ function _install_packages() {
unrar-free unzip xz-utils
)
MISCELLANEOUS_PACKAGES=(
apt-transport-https binutils bsd-mailx
ca-certificates curl dbconfig-no-thanks
dumb-init gnupg iproute2 libdate-manip-perl
libldap-common libmail-spf-perl
libnet-dns-perl locales logwatch
netcat-openbsd nftables rsyslog
supervisor uuid whois
local MISCELLANEOUS_PACKAGES=(
binutils bsd-mailx
dbconfig-no-thanks dumb-init iproute2
libdate-manip-perl libldap-common libmail-spf-perl libnet-dns-perl
locales logwatch netcat-openbsd
nftables # primarily for Fail2Ban
rsyslog supervisor
uuid # used for file-locking
whois
)
POSTFIX_PACKAGES=(
pflogsumm postgrey postfix-ldap postfix-mta-sts-resolver
local POSTFIX_PACKAGES=(
pflogsumm postgrey postfix postfix-ldap postfix-mta-sts-resolver
postfix-pcre postfix-policyd-spf-python postsrsd
)
MAIL_PROGRAMS_PACKAGES=(
fetchmail opendkim opendkim-tools
local MAIL_PROGRAMS_PACKAGES=(
opendkim opendkim-tools
opendmarc libsasl2-modules sasl2-bin
)
# These packages support community contributed features.
# If they cause too much maintenance burden in future, they are liable for removal.
local COMMUNITY_PACKAGES=(
fetchmail getmail6
)
# `bind9-dnsutils` provides the `dig` command
# `iputils-ping` provides the `ping` command
DEBUG_PACKAGES=(
bind9-dnsutils iputils-ping less nano
)
apt-get "${QUIET}" --no-install-recommends install \
apt-get "${QUIET}" install --no-install-recommends \
"${ANTI_VIRUS_SPAM_PACKAGES[@]}" \
"${CODECS_PACKAGES[@]}" \
"${MISCELLANEOUS_PACKAGES[@]}" \
"${POSTFIX_PACKAGES[@]}" \
"${MAIL_PROGRAMS_PACKAGES[@]}" \
"${DEBUG_PACKAGES[@]}"
"${DEBUG_PACKAGES[@]}" \
"${COMMUNITY_PACKAGES[@]}"
}
function _install_dovecot() {
declare -a DOVECOT_PACKAGES
# Dovecot packages for officially supported features.
DOVECOT_PACKAGES=(
local DOVECOT_PACKAGES=(
dovecot-core dovecot-imapd
dovecot-ldap dovecot-lmtpd dovecot-managesieved
dovecot-pop3d dovecot-sieve dovecot-solr
dovecot-pop3d dovecot-sieve
)
# Dovecot packages for community supported features.
# Additional Dovecot packages for supporting the DMS community (docs-only guide contributions).
DOVECOT_PACKAGES+=(dovecot-auth-lua)
# Dovecot's deb community repository only provides x86_64 packages, so do not include it
# when building for another architecture.
# (Opt-in via ENV) Change repo source for dovecot packages to a third-party repo maintained by Dovecot.
# NOTE: AMD64 / x86_64 is the only supported arch from the Dovecot CE repo (thus noDMS built for ARM64 / aarch64)
# Repo: https://repo.dovecot.org/ce-2.4-latest/debian/bookworm/dists/bookworm/main/
# Docs: https://repo.dovecot.org/#debian
if [[ ${DOVECOT_COMMUNITY_REPO} -eq 1 ]] && [[ "$(uname --machine)" == "x86_64" ]]; then
_log 'trace' 'Using Dovecot community repository'
curl https://repo.dovecot.org/DOVECOT-REPO-GPG | gpg --import
gpg --export ED409DA1 > /etc/apt/trusted.gpg.d/dovecot.gpg
echo "deb https://repo.dovecot.org/ce-2.3-latest/debian/bullseye bullseye main" > /etc/apt/sources.list.d/dovecot.list
# WARNING: Repo only provides Debian Bookworm package support for Dovecot CE 2.4+.
# As Debian Bookworm only packages Dovecot 2.3.x, building DMS with this alternative package repo may not yet be compatible with DMS:
# - 2.3.19: https://salsa.debian.org/debian/dovecot/-/tree/stable/bookworm
# - 2.3.21: https://salsa.debian.org/debian/dovecot/-/tree/stable/bookworm-backports
_log 'trace' 'Updating Dovecot package signatures'
_log 'trace' 'Adding third-party package repository (Dovecot)'
curl -fsSL https://repo.dovecot.org/DOVECOT-REPO-GPG-2.4 | gpg --dearmor > /usr/share/keyrings/upstream-dovecot.gpg
echo \
"deb [signed-by=/usr/share/keyrings/upstream-dovecot.gpg] https://repo.dovecot.org/ce-2.4-latest/debian/${VERSION_CODENAME} ${VERSION_CODENAME} main" \
> /etc/apt/sources.list.d/upstream-dovecot.list
# Refresh package index:
apt-get "${QUIET}" update
# Additional community package needed for Lua support if the Dovecot community repository is used.
# This repo instead provides `dovecot-auth-lua` as a transitional package to `dovecot-lua`,
# thus this extra package is required to retain lua support:
DOVECOT_PACKAGES+=(dovecot-lua)
fi
_log 'debug' 'Installing Dovecot'
apt-get "${QUIET}" --no-install-recommends install "${DOVECOT_PACKAGES[@]}"
apt-get "${QUIET}" install --no-install-recommends "${DOVECOT_PACKAGES[@]}"
# dependency for fts_xapian
apt-get "${QUIET}" --no-install-recommends install libxapian30
# Runtime dependency for fts_xapian (built via `compile.sh`):
apt-get "${QUIET}" install --no-install-recommends libxapian30
}
function _install_rspamd() {
_log 'trace' 'Adding Rspamd package signatures'
local DEB_FILE='/etc/apt/sources.list.d/rspamd.list'
# NOTE: DMS only supports the rspamd package via using the third-party repo maintained by Rspamd (AMD64 + ARM64):
# Repo: https://rspamd.com/apt-stable/dists/bookworm/main/
# Docs: https://rspamd.com/downloads.html#debian-and-ubuntu-linux
# NOTE: Debian 12 provides Rspamd 3.4 (too old) and Rspamd discourages it's use
curl -sSfL https://rspamd.com/apt-stable/gpg.key | gpg --dearmor >/etc/apt/trusted.gpg.d/rspamd.gpg
local URL='[signed-by=/etc/apt/trusted.gpg.d/rspamd.gpg] http://rspamd.com/apt-stable/ bullseye main'
echo "deb ${URL}" >"${DEB_FILE}"
_log 'trace' 'Adding third-party package repository (Rspamd)'
curl -fsSL https://rspamd.com/apt-stable/gpg.key | gpg --dearmor > /usr/share/keyrings/upstream-rspamd.gpg
echo \
"deb [signed-by=/usr/share/keyrings/upstream-rspamd.gpg] https://rspamd.com/apt-stable/ ${VERSION_CODENAME} main" \
> /etc/apt/sources.list.d/upstream-rspamd.list
# Refresh package index:
apt-get "${QUIET}" update
_log 'debug' 'Installing Rspamd'
apt-get "${QUIET}" update
apt-get "${QUIET}" --no-install-recommends install 'rspamd' 'redis-server'
apt-get "${QUIET}" install rspamd redis-server
}
function _install_fail2ban() {
local FAIL2BAN_DEB_URL='https://github.com/fail2ban/fail2ban/releases/download/1.0.2/fail2ban_1.0.2-1.upstream1_all.deb'
local FAIL2BAN_VERSION=1.1.0
local FAIL2BAN_DEB_URL="https://github.com/fail2ban/fail2ban/releases/download/${FAIL2BAN_VERSION}/fail2ban_${FAIL2BAN_VERSION}-1.upstream1_all.deb"
local FAIL2BAN_DEB_ASC_URL="${FAIL2BAN_DEB_URL}.asc"
local FAIL2BAN_GPG_FINGERPRINT='8738 559E 26F6 71DF 9E2C 6D9E 683B F1BE BD0A 882C'
local FAIL2BAN_GPG_PUBLIC_KEY_ID='0x683BF1BEBD0A882C'
local FAIL2BAN_GPG_PUBLIC_KEY_SERVER='hkps://keyserver.ubuntu.com'
_log 'debug' 'Installing Fail2ban'
apt-get "${QUIET}" --no-install-recommends install python3-pyinotify python3-dnspython
# Dependencies (https://github.com/docker-mailserver/docker-mailserver/pull/3403#discussion_r1306581431)
apt-get "${QUIET}" install --no-install-recommends python3-pyinotify python3-dnspython python3-systemd
gpg --keyserver "${FAIL2BAN_GPG_PUBLIC_KEY_SERVER}" --recv-keys "${FAIL2BAN_GPG_PUBLIC_KEY_ID}" 2>&1
curl -Lkso fail2ban.deb "${FAIL2BAN_DEB_URL}"
curl -Lkso fail2ban.deb.asc "${FAIL2BAN_DEB_ASC_URL}"
curl -fsSLo fail2ban.deb "${FAIL2BAN_DEB_URL}"
curl -fsSLo fail2ban.deb.asc "${FAIL2BAN_DEB_ASC_URL}"
FINGERPRINT=$(LANG=C gpg --verify fail2ban.deb.asc fail2ban.deb |& sed -n 's#Primary key fingerprint: \(.*\)#\1#p')
@ -173,59 +235,30 @@ function _install_fail2ban() {
_log 'debug' 'Patching Fail2ban to enable network bans'
# Enable network bans
# https://github.com/docker-mailserver/docker-mailserver/issues/2669
# https://github.com/fail2ban/fail2ban/issues/3125
sedfile -i -r 's/^_nft_add_set = .+/_nft_add_set = <nftables> add set <table_family> <table> <addr_set> \\{ type <addr_type>\\; flags interval\\; \\}/' /etc/fail2ban/action.d/nftables.conf
}
# Presently the getmail6 package is v6.14, which is too old.
# v6.18 contains fixes for Google and Microsoft OAuth support.
# using pip to install getmail.
# TODO This can be removed when the base image is updated to Debian 12 (Bookworm)
function _install_getmail() {
_log 'debug' 'Installing getmail6'
apt-get "${QUIET}" --no-install-recommends install python3-pip
pip3 install --no-cache-dir 'getmail6~=6.18.12'
ln -s /usr/local/bin/getmail /usr/bin/getmail
ln -s /usr/local/bin/getmail-gmail-xoauth-tokens /usr/bin/getmail-gmail-xoauth-tokens
apt-get "${QUIET}" purge python3-pip
apt-get "${QUIET}" autoremove
}
function _install_utils() {
_log 'debug' 'Installing utils sourced from Github'
_log 'trace' 'Installing jaq'
curl -sL "https://github.com/01mf02/jaq/releases/latest/download/jaq-v1.2.0-$(uname -m)-unknown-linux-gnu" -o /usr/bin/jaq && chmod +x /usr/bin/jaq
_log 'trace' 'Installing swaks'
local SWAKS_VERSION='20240103.0'
local SWAKS_RELEASE="swaks-${SWAKS_VERSION}"
curl -sSfL "https://github.com/jetmore/swaks/releases/download/v${SWAKS_VERSION}/${SWAKS_RELEASE}.tar.gz" | tar -xz
mv "${SWAKS_RELEASE}/swaks" /usr/local/bin
rm -r "${SWAKS_RELEASE}"
}
function _remove_data_after_package_installations() {
function _post_installation_steps() {
_log 'debug' 'Running post-installation steps (cleanup)'
_log 'debug' 'Deleting sensitive files (secrets)'
rm /etc/postsrsd.secret
_log 'debug' 'Deleting default logwatch cronjob'
rm /etc/cron.daily/00logwatch
}
function _post_installation_steps() {
_log 'debug' 'Running post-installation steps (cleanup)'
_log 'trace' 'Removing leftovers from APT'
apt-get "${QUIET}" clean
rm -rf /var/lib/apt/lists/*
_log 'info' 'Finished installing packages'
# Irrelevant - Debian's default `chroot` jail config for Postfix needed a separate syslog socket:
rm /etc/rsyslog.d/postfix.conf
}
_pre_installation_steps
_install_postfix
_install_utils
_install_packages
_install_dovecot
_install_rspamd
_install_fail2ban
_install_getmail
_install_utils
_remove_data_after_package_installations
_post_installation_steps

View file

@ -7,7 +7,7 @@
# shellcheck source=./helpers/index.sh
source /usr/local/bin/helpers/index.sh
_log_with_date 'debug' 'Starting changedetector'
_log 'debug' 'Starting changedetector'
# ATTENTION: Do not remove!
# This script requires some environment variables to be properly set.
@ -30,9 +30,9 @@ if [[ ! -f ${CHKSUM_FILE} ]]; then
_exit_with_error "'${CHKSUM_FILE}' is missing" 0
fi
_log_with_date 'trace' "Using postmaster address '${POSTMASTER_ADDRESS}'"
_log 'trace' "Using postmaster address '${POSTMASTER_ADDRESS}'"
_log_with_date 'debug' "Changedetector is ready"
_log 'debug' "Changedetector is ready"
function _check_for_changes() {
# get chksum and check it, no need to lock config yet
@ -44,31 +44,44 @@ function _check_for_changes() {
# 1 files differ
# 2 inaccessible or missing argument
if [[ ${?} -eq 1 ]]; then
_log_with_date 'info' 'Change detected'
_log 'info' 'Change detected'
_create_lock # Shared config safety lock
local CHANGED
CHANGED=$(_get_changed_files "${CHKSUM_FILE}" "${CHKSUM_FILE}.new")
# Handle any changes
_ssl_changes
_postfix_dovecot_changes
_rspamd_changes
_log_with_date 'debug' 'Reloading services due to detected changes'
[[ ${ENABLE_AMAVIS} -eq 1 ]] && _reload_amavis
_reload_postfix
[[ ${SMTP_ONLY} -ne 1 ]] && dovecot reload
_handle_changes
_remove_lock
_log_with_date 'debug' 'Completed handling of detected change'
_log 'debug' 'Completed handling of detected change'
# mark changes as applied
mv "${CHKSUM_FILE}.new" "${CHKSUM_FILE}"
fi
}
function _handle_changes() {
# Variable to identify any config updates dependent upon vhost changes.
local VHOST_UPDATED=0
# These two configs are the source for /etc/postfix/vhost (managed mail domains)
if [[ ${CHANGED} =~ ${DMS_DIR}/postfix-(accounts|virtual).cf ]]; then
_log 'trace' 'Regenerating vhosts (Postfix)'
# Regenerate via `helpers/postfix.sh`:
_create_postfix_vhost
VHOST_UPDATED=1
fi
_ssl_changes
_postfix_dovecot_changes
_rspamd_changes
_log 'debug' 'Reloading services due to detected changes'
[[ ${ENABLE_AMAVIS} -eq 1 ]] && _reload_amavis
_reload_postfix
[[ ${SMTP_ONLY} -ne 1 ]] && dovecot reload
}
function _get_changed_files() {
local CHKSUM_CURRENT=${1}
local CHKSUM_NEW=${2}
@ -85,10 +98,10 @@ function _get_changed_files() {
}
function _reload_amavis() {
if [[ ${CHANGED} =~ ${DMS_DIR}/postfix-accounts.cf ]] || [[ ${CHANGED} =~ ${DMS_DIR}/postfix-virtual.cf ]]; then
# /etc/postfix/vhost was updated, amavis must refresh it's config by
# reading this file again in case of new domains, otherwise they will be ignored.
amavisd-new reload
# /etc/postfix/vhost was updated, amavis must refresh it's config by
# reading this file again in case of new domains, otherwise they will be ignored.
if [[ ${VHOST_UPDATED} -eq 1 ]]; then
amavisd reload
fi
}
@ -106,7 +119,7 @@ function _postfix_dovecot_changes() {
|| [[ ${CHANGED} =~ ${DMS_DIR}/dovecot-quotas.cf ]] \
|| [[ ${CHANGED} =~ ${DMS_DIR}/dovecot-masters.cf ]]
then
_log_with_date 'trace' 'Regenerating accounts (Dovecot + Postfix)'
_log 'trace' 'Regenerating accounts (Dovecot + Postfix)'
[[ ${SMTP_ONLY} -ne 1 ]] && _create_accounts
fi
@ -114,14 +127,12 @@ function _postfix_dovecot_changes() {
# - postfix-sasl-password.cf used by _relayhost_sasl
# - _populate_relayhost_map relies on:
# - postfix-relaymap.cf
# - postfix-accounts.cf + postfix-virtual.cf (both will be dropped in future)
if [[ ${CHANGED} =~ ${DMS_DIR}/postfix-accounts.cf ]] \
|| [[ ${CHANGED} =~ ${DMS_DIR}/postfix-virtual.cf ]] \
if [[ ${VHOST_UPDATED} -eq 1 ]] \
|| [[ ${CHANGED} =~ ${DMS_DIR}/postfix-relaymap.cf ]] \
|| [[ ${CHANGED} =~ ${DMS_DIR}/postfix-sasl-password.cf ]]
then
_log_with_date 'trace' 'Regenerating relay config (Postfix)'
_rebuild_relayhost
_log 'trace' 'Regenerating relay config (Postfix)'
_process_relayhost_configs
fi
# Regenerate system + virtual account aliases via `helpers/aliases.sh`:
@ -129,14 +140,6 @@ function _postfix_dovecot_changes() {
[[ ${CHANGED} =~ ${DMS_DIR}/postfix-regexp.cf ]] && _handle_postfix_regexp_config
[[ ${CHANGED} =~ ${DMS_DIR}/postfix-aliases.cf ]] && _handle_postfix_aliases_config
# Regenerate `/etc/postfix/vhost` (managed mail domains) via `helpers/postfix.sh`:
if [[ ${CHANGED} =~ ${DMS_DIR}/postfix-accounts.cf ]] \
|| [[ ${CHANGED} =~ ${DMS_DIR}/postfix-virtual.cf ]]
then
_log_with_date 'trace' 'Regenerating vhosts (Postfix)'
_create_postfix_vhost
fi
# Legacy workaround handled here, only seems necessary for _create_accounts:
# - `helpers/accounts.sh` logic creates folders/files with wrong ownership.
_chown_var_mail_if_necessary
@ -156,14 +159,14 @@ function _ssl_changes() {
|| [[ ${CHANGED} =~ ${SSL_ALT_CERT_PATH:-${REGEX_NEVER_MATCH}} ]] \
|| [[ ${CHANGED} =~ ${SSL_ALT_KEY_PATH:-${REGEX_NEVER_MATCH}} ]]
then
_log_with_date 'debug' 'Manual certificates have changed - extracting certificates'
_log 'debug' 'Manual certificates have changed - extracting certificates'
_setup_ssl
fi
# `acme.json` is only relevant to Traefik, and is where it stores the certificates it manages.
# When a change is detected it's assumed to be a possible cert renewal that needs to be
# extracted for `docker-mailserver` services to adjust to.
elif [[ ${CHANGED} =~ /etc/letsencrypt/acme.json ]]; then
_log_with_date 'debug' "'/etc/letsencrypt/acme.json' has changed - extracting certificates"
_log 'debug' "'/etc/letsencrypt/acme.json' has changed - extracting certificates"
_setup_ssl
# Prevent an unnecessary change detection from the newly extracted cert files by updating their hashes in advance:
@ -185,30 +188,30 @@ function _rspamd_changes() {
# "${RSPAMD_DMS_D}/override.d"
if [[ ${CHANGED} =~ ${RSPAMD_DMS_OVERRIDE_D}/.* ]]; then
_log_with_date 'trace' 'Rspamd - Copying configuration overrides'
_log 'trace' 'Rspamd - Copying configuration overrides'
rm "${RSPAMD_OVERRIDE_D}"/*
cp "${RSPAMD_DMS_OVERRIDE_D}"/* "${RSPAMD_OVERRIDE_D}"
fi
# "${RSPAMD_DMS_D}/custom-commands.conf"
if [[ ${CHANGED} =~ ${RSPAMD_DMS_CUSTOM_COMMANDS_F} ]]; then
_log_with_date 'trace' 'Rspamd - Generating new configuration from custom commands'
_log 'trace' 'Rspamd - Generating new configuration from custom commands'
_rspamd_handle_user_modules_adjustments
fi
# "${RSPAMD_DMS_D}/dkim"
if [[ ${CHANGED} =~ ${RSPAMD_DMS_DKIM_D} ]]; then
_log_with_date 'trace' 'Rspamd - DKIM files updated'
_log 'trace' 'Rspamd - DKIM files updated'
fi
_log_with_date 'debug' 'Rspamd configuration has changed - restarting service'
_log 'debug' 'Rspamd configuration has changed - restarting service'
supervisorctl restart rspamd
fi
}
while true; do
_check_for_changes
sleep 2
sleep "${DMS_CONFIG_POLL:-2}"
done
exit 0

Some files were not shown because too many files have changed in this diff Show more