Compare commits

...

29 commits

Author SHA1 Message Date
Georg Lauterbach 60ed4c3652
Apply suggestions from code review
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-01-17 10:02:39 +01:00
georglauterbach e1ae733dbf
corrected log output
my build cache had the "old" Debian 11 version still in place, hence
68a67c9 contained "fixes" that were wrong
2024-01-16 15:58:36 +01:00
georglauterbach 7cdf318d4d
apply feedback by @polarathene
ref: https://github.com/docker-mailserver/docker-mailserver/pull/3403#discussion_r1453323287
2024-01-16 15:24:33 +01:00
georglauterbach 68a67c95e2
apply feedback from @polarathene
ref: https://github.com/docker-mailserver/docker-mailserver/pull/3403#discussion_r1453332402
2024-01-16 15:13:03 +01:00
georglauterbach e1fd02c1f7
Merge branch 'master' into update-base-image 2024-01-16 11:32:01 +01:00
Georg Lauterbach b4038cde84
Apply suggestions from code review
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-01-16 11:27:32 +01:00
Georg Lauterbach 068ceb1d1a
docs: misc improvements (#3773)
* correct misc typos

We also seem to be favoring `behavior` over `behaviour`.

* bump MkDocs version

* resolve errors shown when buildg docs

* improve the Rspamd page

* behaviour -> behavior

Streamline the usage of this word. The majority used behavior, so I
opted to go with this way of spelling it.

* Apply suggestions from code review

---------

Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-01-16 08:38:08 +00:00
dependabot[bot] 2bf5234250
chore(deps): Bump anchore/scan-action from 3.4.0 to 3.5.0 (#3782)
Bumps [anchore/scan-action](https://github.com/anchore/scan-action) from 3.4.0 to 3.5.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.4.0...v3.5.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-01-16 10:18:13 +13:00
georglauterbach 69c83c4f81
Merge branch 'master' into update-base-image 2024-01-15 10:49:49 +01:00
Brennan Kinney 265440b2bb
fix: Ensure .svbin files are newer than .sieve source files (#3779) 2024-01-15 10:34:15 +01:00
Den ce6ebcc021
docs: Rspamd DKIM config simplify via path setting (#3702)
docs: Rspamd DKIM config (`dkim_signing.conf`) example has been simplified via `path` + `selector` settings.

---------

Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-01-15 13:10:03 +13:00
Brennan Kinney 1449629479
fix: Revert quoting SA_SPAM_SUBJECT in mailserver.env (#3767)
In Docker Compose `.env` files are parsed properly when values are wrapped with quotes. Trailing white-space is also discarded, like it would be with shell variables.

This is not the case with `docker run` or other CRI like `podman` (_including it's compose equivalent support_). Those will parse the quotes to be included in a literal string value. Trailing white-space is also retained.

Hence a default with a trailing space is not compatible across CRI. This change documents the default with additional context on how to include a trailing white-space with a custom value for the users CRI choice. It additionally clearly communicates the opt-out value for this feature.
2024-01-15 11:23:23 +13:00
Georg Lauterbach 3a142f9726
tests: small adjustments (#3772) 2024-01-14 23:14:03 +01:00
github-actions[bot] f794f65caa
docs: updated CONTRIBUTORS.md (#3777)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-01-14 14:08:27 +01:00
Joerg Sonnenberger e3331b0f44
feat: Add MTA-STS support for outbound mail (#3592)
* feat: add support for MTA-STS for outgoing mails

* Hook-up mta-sts-daemon into basic process handling test

* fix: Call python script directly

The python3 shebang will run it, which will now meet the expectations of the process testing via pgrep. fail2ban has the same approach.

---------

Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-01-13 21:37:20 +13:00
Casper 71e1102749
Tiny #3480 follow up: Add missing ENABLE_OAUTH2 var (#3775) 2024-01-12 23:48:14 +01:00
Keval Kapdee 52c4582f7b
feat: Auth - OAuth2 (Dovecot PassDB) (#3480)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-01-13 09:45:14 +13:00
Georg Lauterbach 06fab3f129
tests: streamline tests and helpers further (#3747)
Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-01-11 10:34:08 +01:00
Casper aba218e6d7
Fix jaq: Download platform specific binary (#3766)
* choose architecture dynamically
2024-01-10 12:31:30 +13:00
dependabot[bot] 8d8936dfac
chore(deps): Bump anchore/scan-action from 3.3.8 to 3.4.0 (#3761)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-08 17:40:50 +00:00
dependabot[bot] ac25fb495b
chore(deps): Bump docker/metadata-action from 5.4.0 to 5.5.0 (#3762)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-08 18:39:28 +01:00
Brennan Kinney 5e28c17cf4
docs: SpamAssassin ENV docs refactor (#3756)
* chore: Log `SPAMASSASSIN_SPAM_TO_INBOX=1` ENV correctly

ENV name logged was incomplete.

* docs: Update SA related ENV docs

* fix: Log level `warning` should be `warn`

* docs: FAQ - Revise outdated SA entry

* chore: Antispam / Antivirus => Anti-spam / Anti-virus

* docs: ENV - Additional revisions to SA ENV

* docs: ENV - Move `ENABLE_SPAMASSASSIN_KAM`
2024-01-08 03:07:38 +01:00
github-actions[bot] 8e20173200
docs: updated CONTRIBUTORS.md (#3757)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-01-08 09:40:01 +13:00
Brennan Kinney 6d666512c1
ci: .gitattributes - Ensure eol=lf for shell scripts (#3755)
* ci: `.gitattributes` - Ensure `eol=lf` for shell scripts

- These files should always use LF for line endings during a checkout.
- `Dockerfile` does not like building with HereDoc `RUN` scripts that expect LF.
2024-01-08 09:34:24 +13:00
Casper 0eb4ac7714
tests: Replace wc -l with grep -c (#3752) 2024-01-06 17:07:00 +01:00
Brennan Kinney 6082d5f8d0
chore: Disable smtputf8 support in config directly (#3750)
* chore: Disable `smtputf8` support in config

This was always configured disabled at runtime, better to just set explicitly in `main.cf` unless config diverges when Dovecot is enabled to opt-out of this feature.
2024-01-05 23:18:30 +01:00
Georg Lauterbach 04f4ae4569
Rspamd: add custom symbol scores for SPF, DKIM & DMARC (#3726) 2024-01-05 09:07:31 +01:00
Georg Lauterbach 8f391e4d5a
release: v13.2.0 (#3746)
* bump `VERSION` & adjust `CHANGELOG.md`

* chore: Bump the unreleased base compare tag in `CHANGELOG.md`

---------

Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-01-04 22:14:51 +01:00
Georg Lauterbach bf69ef248e
Postfix: add smtpd_data_restrictions = reject_unauth_pipelining (#3744)
* add `smtpd_data_restrictions = reject_unauth_pipelining`

* fix: Skip restriction if trusted

* add changelog entry

* revert change to `postfix-amavis.cf`

* Update CHANGELOG.md

---------

Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2024-01-04 22:13:13 +01:00
86 changed files with 2221 additions and 1549 deletions

7
.gitattributes vendored
View file

@ -20,9 +20,8 @@
## BUILD:
.dockerignore text
Dockerfile text
Dockerfile text eol=lf
Makefile
VERSION
## EXAMPLE (RUNTIME):
*.env text
@ -75,8 +74,8 @@ target/postsrsd/** text
#################################################
## BATS
*.bash text
*.bats text
*.bash text eol=lf
*.bats text eol=lf
## CONFIG (test/config/)
### OpenLDAP image

View file

@ -23,7 +23,7 @@ jobs:
- name: 'Prepare tags'
id: prep
uses: docker/metadata-action@v5.4.0
uses: docker/metadata-action@v5.5.0
with:
images: |
${{ secrets.DOCKER_REPOSITORY }}

View file

@ -55,7 +55,7 @@ jobs:
provenance: false
- name: 'Run the Anchore Grype scan action'
uses: anchore/scan-action@v3.3.8
uses: anchore/scan-action@v3.5.0
id: scan
with:
image: mailserver-testing:ci

View file

@ -10,7 +10,7 @@ docker run \
--user "$(id -u):$(id -g)" \
--volume "${PWD}:/docs" \
--name "build-docs" \
squidfunk/mkdocs-material:9.2 build --strict
squidfunk/mkdocs-material:9.5 build --strict
# Remove unnecessary build artifacts: https://github.com/squidfunk/mkdocs-material/issues/2519
# site/ is the build output folder.

1
.gitignore vendored
View file

@ -3,6 +3,7 @@
#################################################
.env
compose.override.yaml
docs/site/
docker-data/

View file

@ -2,30 +2,84 @@
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.1.0...HEAD)
## [Unreleased](https://github.com/docker-mailserver/docker-mailserver/compare/v14.0.0...HEAD)
> **Note**: Changes and additions listed here are contained in the `:edge` image tag. These changes may not be as stable as released changes.
## [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**
- 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.
- 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.18 => 3.7.6`
- **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`:
- The script now uses `/etc/os-release` to determine the release name of Debian
- Removed custom installations of Fail2Ban, getmail6 and Rspamd
- Updated packages lists and added comments for maintainability
- **Postfix:**
- `compatibility_level` was raised from `2` to `3.6` ([#3403](https://github.com/docker-mailserver/docker-mailserver/pull/3403))
- 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_).
## [v13.3.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.3.0)
### Features
- **Authentication with OIDC / OAuth 2.0** 🎉
- DMS now supports authentication via OAuth2 (_via `XOAUTH2` or `OAUTHBEARER` SASL mechanisms_) from capable services (_like Roundcube_).
- This does not replace the need for an `ACCOUNT_PROVISIONER` (`FILE` / `LDAP`), which is required for an account to receive or send mail.
- Successful authentication (_via Dovecot PassDB_) still requires an existing account (_lookup via Dovecot UserDB_).
- **MTA-STS** (_Optional support for mandatory outgoing TLS encryption_)
- If enabled and the outbound recipient has an MTA-STS policy set, TLS is mandatory for delivering to that recipient.
- Enable via the ENV `ENABLE_MTA_STS=1`
- Supported by major email service providers like Gmail, Yahoo and Outlook.
### Updates
- **Tests**:
- Refactored mail sending ([#3747](https://github.com/docker-mailserver/docker-mailserver/pull/3747) & [#3772](https://github.com/docker-mailserver/docker-mailserver/pull/3772)):
- This change is a follow-up to [#3732](https://github.com/docker-mailserver/docker-mailserver/pull/3732) from DMS v13.2.
- `swaks` version is now the latest from Github releases instead of the Debian package.
- `_nc_wrapper`, `_send_mail` and related helpers expect the `.txt` filepath extension again.
- `sending.bash` helper methods were refactored to better integrate `swaks` and accommodate different usage contexts.
- `test/files/emails/existing/` files were removed similar to previous removal of SMTP auth files as they became redundant with `swaks`.
- **Internal:**
- tests: Replace `wc -l` with `grep -c` ([#3752](https://github.com/docker-mailserver/docker-mailserver/pull/3752))
- 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)):
- 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 though that complete alignment is undesirable, because other symbols might 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:**
- 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))
### Fixes
- **Dovecot:**
- During container startup for Dovecot Sieve, `.sievec` source files compiled to `.svbin` now have their `mtime` adjusted post setup to ensure it is always older than the associated `.svbin` file. This avoids superfluous error logs for sieve scripts that don't actually need to be compiled again ([#3779](https://github.com/docker-mailserver/docker-mailserver/pull/3779))
- **Internal:**
- `.gitattributes`: Always use LF line endings on checkout for files with shell script content ([#3755](https://github.com/docker-mailserver/docker-mailserver/pull/3755))
- Fix missing 'jaq' binary for ARM architecture ([#3766](https://github.com/docker-mailserver/docker-mailserver/pull/3766))
## [v13.2.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.2.0)
### Security
DMS is now secured against the [recently published spoofing attack "SMTP Smuggling"](https://www.postfix.org/smtp-smuggling.html) that affected Postfix ([#3727](https://github.com/docker-mailserver/docker-mailserver/pull/3727)):
- Postfix upgraded from `3.5.18` to `3.5.23` which provides the [long-term fix with `smtpd_forbid_bare_newline = yes`](https://www.postfix.org/smtp-smuggling.html#long)
- If you are unable to upgrade to this release of DMS, you may follow [these instructions](https://github.com/docker-mailserver/docker-mailserver/issues/3719#issuecomment-1870865118) for applying the [short-term workaround](https://www.postfix.org/smtp-smuggling.html#short).
- This change should not cause compatibility concerns for legitimate mail clients, however if you use software like `netcat` to send mail to DMS (_like our test-suite previously did_) it may now be rejected (_especially with the the short-term workaround `smtpd_data_restrictions = reject_unauth_pipelining`_).
@ -42,6 +96,10 @@ DMS is now secured against the [recently published spoofing attack "SMTP Smuggli
- `swaks` handles pipelining correctly, hence we can now use `reject_unauth_pipelining` in Postfix's configuration.
- `swaks` provides better CLI options that make many files superflous.
- `swaks` can also replace `openssl s_client` and handles authentication on submission ports better.
- **Postfix:**
- We now defer rejection from unauthorized pipelining until the SMTP `DATA` command via `smtpd_data_restrictions` (_i.e. at the end of the mail transfer transaction_) ([#3744](https://github.com/docker-mailserver/docker-mailserver/pull/3744))
- Prevously our configuration only handled this during the client and recipient restriction stages. Postfix will flag this activity when encountered, but the rejection now is handled at `DATA` where unauthorized pipelining would have been valid from this point.
- If you had the Amavis service enabled (default), this restriction was already in place. Otherwise the concerns expressed with `smtpd_data_restrictions = reject_unauth_pipelining` from the security section above apply. We have permitted trusted clients (_`$mynetworks` or authenticated_) to bypass this restriction.
## [v13.1.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.1.0)
@ -61,10 +119,8 @@ DMS is now secured against the [recently published spoofing attack "SMTP Smuggli
- Debugging - Raise awareness of temporary downtime during certificate renewal that can cause a failure to deliver local mail ([#3718](https://github.com/docker-mailserver/docker-mailserver/pull/3718))
- **Internal:**
- Postfix configures `virtual_mailbox_maps` and `virtual_transport` during startup instead of using defaults (configured for Dovecot) via our `main.cf` ([#3681](https://github.com/docker-mailserver/docker-mailserver/pull/3681))
- Updates to `packages.sh`:
- The script now uses `/etc/os-release` to determine the release name of Debian
- Removed custom installations of Fail2Ban, getmail6 and Rspamd
- Updated packages lists and added comments for maintainability
- **Rspamd:**
- Upgraded to version `3.7.5`. This was previously inconsistent between our AMD64 (`3.5`) and ARM64 (`3.4`) images ([#3686](https://github.com/docker-mailserver/docker-mailserver/pull/3686))
### Fixed

File diff suppressed because it is too large Load diff

View file

@ -104,6 +104,14 @@ EOF
# -----------------------------------------------
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
# -----------------------------------------------
# --- LDAP & SpamAssassin's Cron ----------------
@ -189,6 +197,15 @@ COPY target/opendmarc/opendmarc.conf /etc/opendmarc.conf
COPY target/opendmarc/default-opendmarc /etc/default/opendmarc
COPY target/opendmarc/ignore.hosts /etc/opendmarc/ignore.hosts
# --------------------------------------------------
# --- postfix-mta-sts-daemon -----------------------
# --------------------------------------------------
COPY target/mta-sts-daemon/mta-sts-daemon.yml /etc/mta-sts-daemon.yml
RUN <<EOF
mkdir /var/run/mta-sts
chown -R _mta-sts:root /var/run/mta-sts
EOF
# --------------------------------------------------
# --- Fetchmail, Getmail, Postfix & Let'sEncrypt ---
# --------------------------------------------------
@ -317,7 +334,7 @@ LABEL org.opencontainers.image.title="docker-mailserver"
LABEL org.opencontainers.image.vendor="The Docker Mailserver Organization"
LABEL org.opencontainers.image.authors="The Docker Mailserver Organization on GitHub"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.description="A fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). Only configuration files, no SQL database."
LABEL org.opencontainers.image.description="A fullstack but simple mail server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.). Only configuration files, no SQL database."
LABEL org.opencontainers.image.url="https://github.com/docker-mailserver"
LABEL org.opencontainers.image.documentation="https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md"
LABEL org.opencontainers.image.source="https://github.com/docker-mailserver/docker-mailserver"

View file

@ -11,7 +11,7 @@
## :page_with_curl: About
A production-ready fullstack but simple containerized mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, 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, this project is now maintained by volunteers since January 2021.
## :bulb: Documentation
@ -48,3 +48,4 @@ If you have issues, please search through [the documentation][documentation::web
- Support for [LetsEncrypt](https://letsencrypt.org/), manual and self-signed certificates
- A [setup script](https://docker-mailserver.github.io/docker-mailserver/latest/config/setup.sh) for easy configuration and maintenance
- SASLauthd with LDAP authentication
- OAuth2 authentication (_via `XOAUTH2` or `OAUTHBEARER` SASL mechanisms_)

View file

@ -1 +1 @@
13.1.0
13.2.0

View file

@ -56,7 +56,7 @@ These variables specify the LDAP filters that dovecot uses to determine if a use
This is split into the following two lookups, both using `%u` as the placeholder for the full login name ([see dovecot documentation for a full list of placeholders](https://doc.dovecot.org/configuration_manual/config_file/config_variables/)). Usually you only need to set `DOVECOT_USER_FILTER`, in which case it will be used for both filters.
- `DOVECOT_USER_FILTER` is used to get the account details (uid, gid, home directory, quota, ...) of a user.
- `DOVECOT_PASS_FILTER` is used to get the password information of the user, and is in pretty much all cases identical to `DOVECOT_USER_FILTER` (which is the default behaviour if left away).
- `DOVECOT_PASS_FILTER` is used to get the password information of the user, and is in pretty much all cases identical to `DOVECOT_USER_FILTER` (which is the default behavior if left away).
If your directory doesn't have the [postfix-book schema](https://github.com/variablenix/ldap-mail-schema/blob/master/postfix-book.schema) installed, then you must change the internal attribute handling for dovecot. For this you have to change the `pass_attr` and the `user_attr` mapping, as shown in the example below:

View file

@ -0,0 +1,69 @@
---
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

@ -60,7 +60,7 @@ Enable `ip6tables` support so that Docker will manage IPv6 networking rules as w
```
- `experimental: true` is currently required for `ip6tables: true` to work.
- `userland-proxy` setting [can potentially affect connection behaviour][gh-pull-3244-proxy] for local connections.
- `userland-proxy` setting [can potentially affect connection behavior][gh-pull-3244-proxy] for local connections.
Now restart the daemon if it's running: `systemctl restart docker`.
@ -99,7 +99,7 @@ Next, configure a network with an IPv6 subnet for your container with any of the
??? tip "Override the implicit `default` network"
You can optionally avoid the service assignment by [overriding the `default` user-defined network that Docker Compose generates](docker-docs-network-compose-default). Just replace `dms-ipv6` with `default`.
You can optionally avoid the service assignment by [overriding the `default` user-defined network that Docker Compose generates][docker-docs-network-compose-default]. Just replace `dms-ipv6` with `default`.
The Docker Compose `default` bridge is not affected by settings for the default `bridge` (aka `docker0`) in `/etc/docker/daemon.json`.
@ -180,7 +180,7 @@ curl --max-time 5 http://[2001:db8::1]:80
!!! 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.
The preference can be controlled with [`/etc/gai.conf`][networking-gai], and appears was configured this way based on [the assumption that IPv6 ULA would never be used with NAT][networking-gai-blog]. It should only affect the destination resolved for outgoing connections, which for IPv6 ULA should only really affect connections between your containers / host. In future [IPv6 ULA may also be prioritized][networking-gai-rfc].
[docker-subnets]: https://straz.to/2021-09-08-docker-address-pools/#what-are-the-default-address-pools-when-no-configuration-is-given-vanilla-pools

View file

@ -33,9 +33,9 @@ This is a list of all configuration files and directories which are optional or
- **ldap-aliases.cf:** Configuration for the virtual alias mapping `virtual_alias_maps`. See the [`setup-stack.sh`][github-commit-setup-stack.sh-L411] script.
- **ldap-domains.cf:** Configuration for the virtual domain mapping `virtual_mailbox_domains`. See the [`setup-stack.sh`][github-commit-setup-stack.sh-L411] script.
- **whitelist_clients.local:** Whitelisted domains, not considered by postgrey. Enter one host or domain per line.
- **spamassassin-rules.cf:** Antispam rules for Spamassassin. (Docs: [FAQ - SpamAssassin Rules][docs-faq-spamrules])
- **spamassassin-rules.cf:** Anti-spam rules for Spamassassin. (Docs: [FAQ - SpamAssassin Rules][docs-faq-spamrules])
- **fail2ban-fail2ban.cf:** Additional config options for `fail2ban.cf`. (Docs: [Fail2Ban][docs-fail2ban])
- **fail2ban-jail.cf:** Additional config options for fail2ban's jail behaviour. (Docs: [Fail2Ban][docs-fail2ban])
- **fail2ban-jail.cf:** Additional config options for fail2ban's jail behavior. (Docs: [Fail2Ban][docs-fail2ban])
- **amavis.cf:** replaces the `/etc/amavis/conf.d/50-user` file
- **dovecot.cf:** replaces `/etc/dovecot/local.conf`. (Docs: [Override Dovecot Defaults][docs-override-dovecot])
- **dovecot-quotas.cf:** list of custom quotas per mailbox. (Docs: [Accounts][docs-accounts-quota])

View file

@ -107,7 +107,7 @@ The `PERMIT_DOCKER` variable in the `mailserver.env` file allows to specify trus
#### 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/#podman-with-slirp4netns-port-driver) the `slirp4netns` network driver has to be enabled.
As shown in the [fail2ban section](../security/fail2ban.md#podman-with-slirp4netns-port-driver) 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.

View file

@ -156,6 +156,13 @@ DKIM is currently supported by either OpenDKIM or Rspamd:
use_esld = true;
check_pubkey = true; # you want to use this in the beginning
selector = "mail";
# The path location is searched for a DKIM key with these variables:
# - `$domain` is sourced from the MIME mail message `From` header
# - `$selector` is configured for `mail` (as a default fallback)
path = "/tmp/docker-mailserver/dkim/keys/$domain/$selector.private";
# domain specific configurations can be provided below:
domain {
example.com {
path = "/tmp/docker-mailserver/rspamd/dkim/mail.private";

View file

@ -0,0 +1,30 @@
---
title: 'Best practices | MTA-STS'
hide:
- toc # Hide Table of Contents for this page
---
MTA-STS is an optional mechanism for a domain to signal support for STARTTLS.
- It can be used to prevent man-in-the-middle-attacks from hiding STARTTLS support that would force DMS to send outbound mail through an insecure connection.
- MTA-STS is an alternative to DANE without the need of DNSSEC.
- MTA-STS is supported by some of the biggest mail providers like Google Mail and Outlook.
## Supporting MTA-STS for outbound mail
Enable this feature via the ENV setting [`ENABLE_MTA_STS=1`](../environment.md#enable_mta_sts).
!!! warning "If you have configured DANE"
Enabling MTA-STS will by default override DANE if both are configured for a domain.
This can be partially addressed by configuring a dane-only policy resolver before the MTA-STS entry in `smtp_tls_policy_maps`. See the [`postfix-mta-sts-resolver` documentation][postfix-mta-sts-resolver::dane] for further details.
[postfix-mta-sts-resolver::dane]: https://github.com/Snawoot/postfix-mta-sts-resolver#warning-mta-sts-policy-overrides-dane-tls-authentication
## Supporting MTA-STS for inbound mail
While this feature in DMS supports ensuring STARTTLS is used when mail is sent to another mail server, you may setup similar for mail servers sending mail to DMS.
This requires configuring your DNS and hosting the MTA-STS policy file via a webserver. A good introduction can be found on [dmarcian.com](https://dmarcian.com/mta-sts/).

View file

@ -107,9 +107,9 @@ This could be from outdated software, or running a system that isn't able to pro
- **macOS:** DMS has limited support for macOS. Often an issue encountered is due to permissions related to the `volumes` config in `compose.yaml`. You may have luck [trying `gRPC FUSE`][gh-macos-support] as the file sharing implementation; [`VirtioFS` is the successor][docker-macos-virtiofs] but presently appears incompatible with DMS.
- **Kernel:** Some systems provide [kernels with modifications (_replacing defaults and backporting patches_)][network::kernels-modified] to support running legacy software or kernels, complicating compatibility. This can be commonly experienced with products like NAS.
- **CGroups v2:** Hosts running older kernels (prior to 5.2) and systemd (prior to v244) are not likely to leverage cgroup v2, or have not defaulted to the cgroup v2 `unified` hierarchy. Not meeting this baseline may influence the behaviour of your DMS container, even with the latest Docker Engine installed.
- **CGroups v2:** Hosts running older kernels (prior to 5.2) and systemd (prior to v244) are not likely to leverage cgroup v2, or have not defaulted to the cgroup v2 `unified` hierarchy. Not meeting this baseline may influence the behavior of your DMS container, even with the latest Docker Engine installed.
- **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 behaviour or requirements:
- **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].

View file

@ -54,7 +54,15 @@ The Group ID assigned to the static vmail group for `/var/mail` (_Mail storage m
Configures the provisioning source of user accounts (including aliases) for user queries and authentication by services managed by DMS (_Postfix and Dovecot_).
User provisioning via OIDC is planned for the future, see [this tracking issue](https://github.com/docker-mailserver/docker-mailserver/issues/2713).
!!! 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
- LDAP => use LDAP authentication
@ -108,6 +116,15 @@ This enables DNS block lists in _Postscreen_. If you want to know which lists we
- **0** => DNS block lists are disabled
- 1 => DNS block lists are enabled
##### ENABLE_MTA_STS
Enables MTA-STS support for outbound mail.
- **0** => Disabled
- 1 => Enabled
See [MTA-STS](best-practices/mta-sts.md) for further explanation.
##### ENABLE_OPENDKIM
Enables the OpenDKIM service.
@ -316,28 +333,32 @@ Note: More information at <https://dovecot.org/doc/dovecot-example.conf>
##### MOVE_SPAM_TO_JUNK
When enabled, e-mails marked with the
1. `X-Spam: Yes` header added by Rspamd
2. `X-Spam-Flag: YES` header added by SpamAssassin (requires [`SPAMASSASSIN_SPAM_TO_INBOX=1`](#spamassassin_spam_to_inbox))
will be automatically moved to the Junk folder (with the help of a Sieve script).
- 0 => Spam messages will be delivered in the mailbox.
- **1** => Spam messages will be delivered in the `Junk` folder.
Routes mail identified as spam into the recipient(s) Junk folder (_via a Dovecot Sieve script_).
!!! info
Mail is received as spam when it has been marked with either header:
- `X-Spam: Yes` (_added by Rspamd_)
- `X-Spam-Flag: YES` (_added by SpamAssassin - requires [`SPAMASSASSIN_SPAM_TO_INBOX=1`](#spamassassin_spam_to_inbox)_)
##### MARK_SPAM_AS_READ
Enable to treat received spam as "read" (_avoids notification to MUA client of new mail_).
Mail is received as spam when it has been marked with either header:
1. `X-Spam: Yes` (_by Rspamd_)
2. `X-Spam-Flag: YES` (_by SpamAssassin - requires [`SPAMASSASSIN_SPAM_TO_INBOX=1`](#spamassassin_spam_to_inbox)_)
- **0** => disabled
- 1 => Spam messages will be marked as read
Enable to treat received spam as "read" (_avoids notification to MUA client of new mail_).
!!! info
Mail is received as spam when it has been marked with either header:
- `X-Spam: Yes` (_added by Rspamd_)
- `X-Spam-Flag: YES` (_added by SpamAssassin - requires [`SPAMASSASSIN_SPAM_TO_INBOX=1`](#spamassassin_spam_to_inbox)_)
#### Rspamd
##### ENABLE_RSPAMD
@ -515,63 +536,170 @@ Changes the interval in which log files are rotated.
- **0** => SpamAssassin is disabled
- 1 => SpamAssassin is enabled
##### SPAMASSASSIN_SPAM_TO_INBOX
??? info "SpamAssassin analyzes incoming mail and assigns a spam score"
- 0 => Spam messages will be bounced (_rejected_) without any notification (_dangerous_).
- **1** => Spam messages will be delivered to the inbox and tagged as spam using `SA_SPAM_SUBJECT`.
Integration with Amavis involves processing mail based on the assigned spam score via [`SA_TAG`, `SA_TAG2` and `SA_KILL`][amavis-docs::spam-score].
These settings have equivalent ENV supported by DMS for easy adjustments, as documented below.
[amavis-docs::spam-score]: https://www.ijs.si/software/amavisd/amavisd-new-docs.html#tagkill
##### ENABLE_SPAMASSASSIN_KAM
[KAM](https://mcgrail.com/template/projects#KAM1) is a 3rd party SpamAssassin ruleset, provided by the McGrail Foundation. If SpamAssassin is enabled, KAM can be used in addition to the default ruleset.
- **0** => KAM disabled
- 1 => KAM enabled
[KAM](https://mcgrail.com/template/projects#KAM1) is a 3rd party SpamAssassin ruleset, provided by the McGrail Foundation. If SpamAssassin is enabled, KAM can be used in addition to the default ruleset.
##### SPAMASSASSIN_SPAM_TO_INBOX
- 0 => (_Amavis action: `D_BOUNCE`_): Spam messages will be bounced (_rejected_) without any notification (_dangerous_).
- **1** => (_Amavis action: `D_PASS`_): Spam messages will be delivered to the inbox.
!!! note
The Amavis action configured by this setting:
- Influences the behavior of the [`SA_KILL`](#sa_kill) setting.
- Applies to the Amavis config parameters `$final_spam_destiny` and `$final_bad_header_destiny`.
!!! note "This ENV setting is related to"
- [`MOVE_SPAM_TO_JUNK=1`](#move_spam_to_junk)
- [`MARK_SPAM_AS_READ=1`](#mark_spam_as_read)
- [`SA_SPAM_SUBJECT`](#sa_spam_subject)
##### SA_TAG
- **2.0** => add spam info headers if at, or above that level
- **2.0** => add 'spam info' headers at, or above this spam score
Note: this SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1`
Mail is not yet considered spam at this spam score, but for purposes like diagnostics it can be useful to identify mail with a spam score at a lower bound than `SA_TAG2`.
??? example "`X-Spam` headers appended to mail"
Send a simple mail to a local DMS account `hello@example.com`:
```bash
docker exec dms swaks --server 0.0.0.0 --to hello@example.com --body 'spam'
```
Inspecting the raw mail you will notice several `X-Spam` headers were added to the mail like this:
```
X-Spam-Flag: NO
X-Spam-Score: 4.162
X-Spam-Level: ****
X-Spam-Status: No, score=4.162 tagged_above=2 required=4
tests=[BODY_SINGLE_WORD=1, DKIM_ADSP_NXDOMAIN=0.8,
NO_DNS_FOR_FROM=0.379, NO_RECEIVED=-0.001, NO_RELAYS=-0.001,
PYZOR_CHECK=1.985] autolearn=no autolearn_force=no
```
!!! info "The `X-Spam-Score` is `4.162`"
High enough for `SA_TAG` to trigger adding these headers, but not high enough for `SA_TAG2` (_which would set `X-Spam-Flag: YES` instead_).
##### SA_TAG2
- **6.31** => add 'spam detected' headers at that level
- **6.31** => add 'spam detected' headers at, or above this level
Note: this SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1`
When a spam score is high enough, mark mail as spam (_Appends the mail header: `X-Spam-Flag: YES`_).
!!! 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`.
##### SA_KILL
- **10.0** => triggers spam evasive actions
- **10.0** => quarantine + triggers action to handle spam
!!! note "This SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1`"
Controls the spam score threshold for triggering an action on mail that has a high spam score.
By default, DMS is configured to quarantine spam emails.
??? tip "Choosing an appropriate `SA_KILL` value"
If emails are quarantined, they are compressed and stored in a location dependent on the `ONE_DIR` setting above. To inhibit this behaviour and deliver spam emails, set this to a very high value e.g. `100.0`.
The value should be high enough to be represent confidence in mail as spam:
If `ONE_DIR=1` (default) the location is `/var/mail-state/lib-amavis/virusmails/`, or if `ONE_DIR=0`: `/var/lib/amavis/virusmails/`. These paths are inside the docker container.
- Too low: The action taken may prevent legitimate mail (ham) that was incorrectly detected as spam from being delivered successfully.
- Too high: Allows more spam to bypass the `SA_KILL` trigger (_how to treat mail with high confidence that it is actually spam_).
Experiences from DMS users with these settings has been [collected here][gh-issue::sa-tunables-insights], along with [some direct configuration guides][gh-issue::sa-tunables-guides] (_under "Resources for references"_).
[gh-issue::sa-tunables-insights]: https://github.com/docker-mailserver/docker-mailserver/pull/3058#issuecomment-1420268148
[gh-issue::sa-tunables-guides]: https://github.com/docker-mailserver/docker-mailserver/pull/3058#issuecomment-1416547911
??? info "Trigger action"
DMS will configure Amavis with either of these actions based on the DMS [`SPAMASSASSIN_SPAM_TO_INBOX`](#spamassassin_spam_to_inbox) ENV setting:
- `D_PASS` (**default**):
- Accept mail and deliver it to the recipient(s), despite the high spam score. A copy is still stored in quarantine.
- This is a good default to start with until you are more confident in an `SA_KILL` threshold that won't accidentally discard / bounce legitimate mail users are expecting to arrive but is detected as spam.
- `D_BOUNCE`:
- Additionally sends a bounce notification (DSN).
- The [DSN is suppressed][amavis-docs::actions] (_no bounce sent_) when the spam score exceeds the Amavis `$sa_dsn_cutoff_level` config setting (default: `10`). With the DMS `SA_KILL` default also being `10`, no DSN will ever be sent.
- `D_REJECT` / `D_DISCARD`:
- These two aren't configured by DMS, but are valid alternative action values if configuring Amavis directly.
??? note "Quarantined mail"
When mail has a spam score that reaches the `SA_KILL` threshold:
- [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:
- `ONE_DIR=1` (default): `/var/mail-state/lib-amavis/virusmails/`
- `ONE_DIR=0`: `/var/lib/amavis/virusmails/`
!!! tip
Easily list mail stored in quarantine with `find` and the quarantine path:
```bash
find /var/lib/amavis/virusmails -type f
```
[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
- **\*\*\*SPAM\*\*\*** => add tag to subject if spam detected
Adds a prefix to the subject header when mail is marked as spam (_via [`SA_TAG2`](#sa_tag2)_).
Note: this SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1`. Add the SpamAssassin score to the subject line by inserting the keyword \_SCORE\_: **\*\*\*SPAM(\_SCORE\_)\*\*\***.
- **`'***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.
This will uncomment the respective line in ```/etc/spamassasin/local.cf```
This will uncomment the respective line in `/etc/spamassasin/local.cf`
Note: activate this only if you are confident in your bayes database for identifying spam.
!!! warning
Activate this only if you are confident in your bayes database for identifying spam.
##### SA_SHORTCIRCUIT_BAYES_HAM
- **1** => will activate SpamAssassin short circuiting for bayes ham detection
This will uncomment the respective line in ```/etc/spamassasin/local.cf```
This will uncomment the respective line in `/etc/spamassasin/local.cf`
Note: activate this only if you are confident in your bayes database for identifying ham.
!!! warning
Activate this only if you are confident in your bayes database for identifying ham.
#### Fetchmail
@ -605,10 +733,20 @@ Enable or disable `getmail`.
- **5** => `getmail` The number of minutes for the interval. Min: 1; Max: 30; Default: 5.
#### OAUTH2
##### ENABLE_OAUTH2
- **empty** => OAUTH2 authentication is disabled
- 1 => OAUTH2 authentication is enabled
##### OAUTH2_INTROSPECTION_URL
- => Specify the user info endpoint URL of the oauth2 provider (_eg: `https://oauth2.example.com/userinfo/`_)
#### LDAP
##### LDAP_START_TLS
- **empty** => no

View file

@ -22,15 +22,59 @@ The following environment variables are related to Rspamd:
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`](../environment.md#move_spam_to_junk)
8. [`MOVE_SPAM_TO_JUNK`][docs-spam-to-junk]
9. [`MARK_SPAM_AS_READ`](../environment.md#mark_spam_as_read)
With these variables, you can enable Rspamd itself and you can enable / disable certain features related to Rspamd.
With these variables, you can enable Rspamd itself, and you can enable / disable certain features related to Rspamd.
[docs-spam-to-junk]: ../environment.md#move_spam_to_junk
## The Default Configuration
### Mode of Operation
!!! tip "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.
- 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).
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:
```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]
```
And then there is a corresponding `X-Rspamd-Action` header, which shows the overall result and the action that is taken. In our example, it would be:
```txt
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:
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_.
---
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].
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).
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.
@ -39,9 +83,9 @@ DMS does not set a default password for the controller worker. You may want to d
### Persistence with Redis
When Rspamd is enabled, we implicitly also start an instance of Redis in the container. Redis is configured to persist it's 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 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.
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 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_).
### Web Interface
@ -77,13 +121,13 @@ You can find a list of all Rspamd modules [on their website][rspamd-docs-modules
#### Disabled By Default
DMS disables certain modules (clickhouse, elastic, neural, reputation, spamassassin, url_redirector, metric_exporter) by default. We believe these are not required in a standard setup, and they would otherwise needlessly use system resources.
DMS disables certain modules (`clickhouse`, `elastic`, `neural`, `reputation`, `spamassassin`, `url_redirector`, `metric_exporter`) by default. We believe these are not required in a standard setup, and they would otherwise needlessly use system resources.
#### Anti-Virus (ClamAV)
You can choose to enable ClamAV, and Rspamd will then use it to check for viruses. Just set the environment variable `ENABLE_CLAMAV=1`.
#### RBLs (Realtime Blacklists) / DNSBLs (DNS-based Blacklists)
#### 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]\].
@ -105,12 +149,15 @@ DMS brings sane default settings for Rspamd. They are located at `/etc/rspamd/lo
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.
!!! question "What is the [`local.d` directory and how does it compare to `override.d`][rspamd-docs-config-directories]?"
!!! warning "Clashing Overrides"
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.
[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
### With the Help of a Custom File
@ -127,7 +174,7 @@ where `COMMAND` can be:
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 behaviour][rspamd-docs-basic-options] to value `ARGUMENT2`
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>`
!!! example "An Example Is [Shown Down Below](#adjusting-and-extending-the-very-basic-configuration)"

View file

@ -61,7 +61,7 @@ Take care to test localized names work well as well.
This information is provided by the community.
It presently lacks references to confirm the behaviour. If any information is incorrect please let us know! :smile:
It presently lacks references to confirm the behavior. If any information is incorrect please let us know! :smile:
[docs-config-overrides-dovecot]: ../../config/advanced/override-defaults/dovecot.md#override-configuration

View file

@ -12,7 +12,7 @@ Mails are stored in `/var/mail/${domain}/${username}`. Since `v9.0.0` it is poss
### How are IMAP mailboxes (_aka IMAP Folders_) set up?
`INBOX` is setup by default with the special IMAP folders `Drafts`, `Sent`, `Junk` and `Trash`. You can learn how to modify or add your own folders (_including additional special folders like `Archive`_) by visiting our docs page [_Customizing IMAP Folders_](../examples/use-cases/imap-folders) for more information.
`INBOX` is setup by default with the special IMAP folders `Drafts`, `Sent`, `Junk` and `Trash`. You can learn how to modify or add your own folders (_including additional special folders like `Archive`_) by visiting our docs page [_Customizing IMAP Folders_](./examples/use-cases/imap-folders.md) for more information.
### How do I update DMS?
@ -128,7 +128,7 @@ find "${PWD}/docker-data/dms-backups/" -type f -mtime +30 -delete
### I Want to Know More About the Ports
See [this part of the documentation](../config/security/understanding-the-ports/) for further details and best practice advice, **especially regarding security concerns**.
See [this part of the documentation](./config/security/understanding-the-ports.md) for further details and best practice advice, **especially regarding security concerns**.
### How can I configure my email client?
@ -210,7 +210,7 @@ baduser@example.com devnull
### What kind of SSL certificates can I use?
Both RSA and ECDSA certs are supported. You can provide your own cert files manually, or mount a `letsencrypt` generated directory (_with alternative support for Traefik's `acme.json`_). Check out the [`SSL_TYPE` documentation](../config/environment/#ssl_type) for more details.
Both RSA and ECDSA certs are supported. You can provide your own cert files manually, or mount a `letsencrypt` generated directory (_with alternative support for Traefik's `acme.json`_). Check out the [`SSL_TYPE` documentation](./config/environment.md#ssl_type) for more details.
### I just moved from my old mail server to DMS, but "it doesn't work"?
@ -378,18 +378,7 @@ When you run DMS with the ENV variable `ONE_DIR=1` (default), this directory wil
#### How can I manage my custom SpamAssassin rules?
Antispam rules are managed in `docker-data/dms/config/spamassassin-rules.cf`.
#### What are acceptable `SA_SPAM_SUBJECT` values?
For no subject set `SA_SPAM_SUBJECT=undef`.
For a trailing white-space subject one can define the whole variable with quotes in `compose.yaml`:
```yaml
environment:
- "SA_SPAM_SUBJECT=[SPAM] "
```
Anti-spam rules are managed in `docker-data/dms/config/spamassassin-rules.cf`.
#### Why are SpamAssassin `x-headers` not inserted into my `subdomain.example.com` subdomain emails?
@ -479,59 +468,39 @@ The following configuration works nicely:
file: ./docker-data/dms/cron/sa-learn
```
With the default settings, SpamAssassin will require 200 mails trained for spam (for example with the method explained above) and 200 mails trained for ham (using the same command as above but using `--ham` and providing it with some ham mails). Until you provided these 200+200 mails, SpamAssassin will not take the learned mails into account. For further reference, see the [SpamAssassin Wiki](https://wiki.apache.org/spamassassin/BayesNotWorking).
With the default settings, SpamAssassin will require 200 mails trained for spam (for example with the method explained above) and 200 mails trained for ham (using the same command as above but using `--ham` and providing it with some ham mails).
- Until you provided these 200+200 mails, SpamAssassin will not take the learned mails into account.
- For further reference, see the [SpamAssassin Wiki](https://wiki.apache.org/spamassassin/BayesNotWorking).
#### How do I have more control about what SpamAssassin is filtering?
By default, SPAM and INFECTED emails are put to a quarantine which is not very straight forward to access. Several config settings are affecting this behavior:
This is related to Amavis processing the mail after SpamAssassin has analyzed it and assigned a spam score.
First, make sure you have the proper thresholds set:
- DMS provides some [common SA tunables via ENV][docs::env::sa_env].
- Additional configuration can be managed with the DMS config volume by providing `docker-data/dms/config/amavis.cf`.
```conf
SA_TAG=-100000.0
SA_TAG2=3.75
SA_KILL=100000.0
```
#### How can I send quarantined mail to a mailbox?
- The very negative value in `SA_TAG` makes sure, that all emails have the SpamAssassin headers included.
- `SA_TAG2` is the actual threshold to set the YES/NO flag for spam detection.
- `SA_KILL` needs to be very high, to make sure nothing is bounced at all (`SA_KILL` superseeds `SPAMASSASSIN_SPAM_TO_INBOX`)
SPAM and INFECTED emails that [reach the `SA_KILL` threshold are archived into quarantine][docs::env::sa_kill].
Make sure everything (including SPAM) is delivered to the inbox and not quarantined:
```conf
SPAMASSASSIN_SPAM_TO_INBOX=1
```
Use `MOVE_SPAM_TO_JUNK=1` or create a sieve script which puts spam to the Junk folder:
```sieve
require ["comparator-i;ascii-numeric","relational","fileinto"];
if header :contains "X-Spam-Flag" "YES" {
fileinto "Junk";
} elsif allof (
not header :matches "x-spam-score" "-*",
header :value "ge" :comparator "i;ascii-numeric" "x-spam-score" "3.75"
) {
fileinto "Junk";
}
```
Create a dedicated mailbox for emails which are infected/bad header and everything amavis is blocking by default and put its address into `docker-data/dms/config/amavis.cf`
Instead of a quarantine folder, you can use a dedicated mailbox instead. Create an account like `quarantine@example.com` and create `docker-data/dms/config/amavis.cf`:
```cf
$clean_quarantine_to = "amavis\@example.com";
$virus_quarantine_to = "amavis\@example.com";
$banned_quarantine_to = "amavis\@example.com";
$bad_header_quarantine_to = "amavis\@example.com";
$spam_quarantine_to = "amavis\@example.com";
$clean_quarantine_to = "quarantine\@example.com";
$virus_quarantine_to = "quarantine\@example.com";
$banned_quarantine_to = "quarantine\@example.com";
$bad_header_quarantine_to = "quarantine\@example.com";
$spam_quarantine_to = "quarantine\@example.com";
```
[fail2ban-customize]: ./config/security/fail2ban.md
[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
[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
@ -542,4 +511,3 @@ $spam_quarantine_to = "amavis\@example.com";
[github-issue-1792]: https://github.com/docker-mailserver/docker-mailserver/pull/1792
[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
[docs-optional-configuration]: ./config/advanced/optional-config.md

View file

@ -14,7 +14,7 @@ This documentation provides you not only with the basic setup and configuration
## About
`docker-mailserver`, or DMS for short, is a production-ready fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). It employs only configuration files, no SQL database. The image is focused around the slogan "Keep it simple and versioned".
`docker-mailserver`, or DMS for short, is a production-ready fullstack but simple mail server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.). It employs only configuration files, no SQL database. The image is focused around the slogan "Keep it simple and versioned".
## Contents
@ -24,9 +24,9 @@ If you're completely new to mail servers or you want to read up on them, check o
There is also a script - [`setup.sh`][github-file-setupsh] - supplied with this project. It supports you in configuring and administrating your server. Information on how to get it and how to use it is available [on a dedicated page][docs-setupsh].
[docs-introduction]: ./introduction/
[docs-usage]: ./usage/
[docs-examples]: ./examples/tutorials/basic-installation/
[docs-introduction]: ./introduction.md
[docs-usage]: ./usage.md
[docs-examples]: ./examples/tutorials/basic-installation.md
[github-file-setupsh]: https://github.com/docker-mailserver/docker-mailserver/blob/master/setup.sh
[docs-setupsh]: ./config/setup.sh/
@ -59,10 +59,10 @@ You might also want to check out:
DMS employs a variety of tests. If you want to know more about our test suite, view our [testing docs][docs-tests].
[docs-tests]: ./contributing/tests/
[docs-tests]: ./contributing/tests.md
### Contributing
We are always happy to welcome new contributors. For guidelines and entrypoints please have a look at the [Contributing section][docs-contributing].
[docs-contributing]: ./contributing/issues-and-pull-requests/
[docs-contributing]: ./contributing/issues-and-pull-requests.md

View file

@ -1,6 +1,6 @@
# Site specific:
site_name: 'Docker Mailserver'
site_description: 'A fullstack but simple mail-server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.) using Docker.'
site_description: 'A fullstack but simple mail-server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.) using Docker.'
site_author: 'docker-mailserver (Github Organization)'
copyright: '<p>&copy <a href="https://github.com/docker-mailserver"><em>Docker Mailserver Organization</em></a><br/><span>This project is licensed under the MIT license.</span></p>'
@ -86,8 +86,8 @@ markdown_extensions:
- pymdownx.inlinehilite
- pymdownx.tilde
- pymdownx.emoji:
emoji_index: !!python/name:materialx.emoji.twemoji
emoji_generator: !!python/name:materialx.emoji.to_svg
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
- pymdownx.highlight:
extend_pygments_lang:
- name: yml
@ -122,8 +122,9 @@ nav:
- 'Environment Variables': config/environment.md
- 'User Management': config/user-management.md
- 'Best Practices':
- 'DKIM, DMARC & SPF': config/best-practices/dkim_dmarc_spf.md
- 'Auto-discovery': config/best-practices/autodiscover.md
- 'DKIM, DMARC & SPF': config/best-practices/dkim_dmarc_spf.md
- 'MTA-STS': config/best-practices/mta-sts.md
- 'Security':
- 'Understanding the Ports': config/security/understanding-the-ports.md
- 'SSL/TLS': config/security/ssl.md
@ -142,6 +143,7 @@ nav:
- '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

View file

@ -354,6 +354,12 @@ POSTFIX_REJECT_UNKNOWN_CLIENT_HOSTNAME=0
# Note: More details at http://www.postfix.org/postconf.5.html#inet_protocols
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/
# - **0** ==> MTA-STS disabled
# - 1 => MTA-STS enabled
ENABLE_MTA_STS=0
# Choose TCP/IP protocols for dovecot to use
# **all** => Listen on all interfaces
# ipv4 => Listen only on IPv4 interfaces. Most likely you want this behind Docker.
@ -368,9 +374,6 @@ DOVECOT_INET_PROTOCOLS=all
ENABLE_SPAMASSASSIN=0
# deliver spam messages in the inbox (eventually tagged using SA_SPAM_SUBJECT)
SPAMASSASSIN_SPAM_TO_INBOX=1
# KAM is a 3rd party SpamAssassin ruleset, provided by the McGrail Foundation.
# If SpamAssassin is enabled, KAM can be used in addition to the default ruleset.
# - **0** => KAM disabled
@ -379,23 +382,29 @@ SPAMASSASSIN_SPAM_TO_INBOX=1
# Note: only has an effect if `ENABLE_SPAMASSASSIN=1`
ENABLE_SPAMASSASSIN_KAM=0
# deliver spam messages to the inbox (tagged using SA_SPAM_SUBJECT)
SPAMASSASSIN_SPAM_TO_INBOX=1
# spam messages will be moved in the Junk folder (SPAMASSASSIN_SPAM_TO_INBOX=1 required)
MOVE_SPAM_TO_JUNK=1
# spam messages wil be marked as read
MARK_SPAM_AS_READ=0
# add spam info headers if at, or above that level:
# add 'spam info' headers at, or above this level
SA_TAG=2.0
# add 'spam detected' headers at that level
# add 'spam detected' headers at, or above this level
SA_TAG2=6.31
# triggers spam evasive actions
SA_KILL=10.0
# add tag to subject if spam detected
SA_SPAM_SUBJECT=***SPAM*****
# 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 -------------------------
@ -419,6 +428,18 @@ ENABLE_GETMAIL=0
# The number of minutes for the interval. Min: 1; Max: 30.
GETMAIL_POLL=5
# -----------------------------------------------
# --- OAUTH2 Section ----------------------------
# -----------------------------------------------
# empty => OAUTH2 authentication is disabled
# 1 => OAUTH2 authentication is enabled
ENABLE_OAUTH2=
# Specify the user info endpoint URL of the oauth2 provider
# Example: https://oauth2.example.com/userinfo/
OAUTH2_INTROSPECTION_URL=
# -----------------------------------------------
# --- LDAP Section ------------------------------
# -----------------------------------------------

View file

@ -123,6 +123,7 @@ auth_mechanisms = plain login
#!include auth-sql.conf.ext
#!include auth-ldap.conf.ext
!include auth-passwdfile.inc
#!include auth-oauth2.conf.ext
#!include auth-checkpassword.conf.ext
#!include auth-vpopmail.conf.ext
#!include auth-static.conf.ext

View file

@ -0,0 +1,7 @@
auth_mechanisms = $auth_mechanisms oauthbearer xoauth2
passdb {
driver = oauth2
mechanisms = xoauth2 oauthbearer
args = /etc/dovecot/dovecot-oauth2.conf.ext
}

View file

@ -0,0 +1,4 @@
introspection_url =
# Dovecot defaults:
introspection_mode = auth
username_attribute = email

View file

@ -0,0 +1,7 @@
# Docs: https://github.com/Snawoot/postfix-mta-sts-resolver/blob/master/man/mta-sts-daemon.yml.5.adoc
path: /var/run/mta-sts/daemon.sock
mode: 0666
cache:
type: sqlite
options:
filename: "/var/lib/mta-sts/cache.db"

View file

@ -5,6 +5,9 @@ biff = no
append_dot_mydomain = no
readme_directory = no
# Disabled as not compatible with Dovecot
smtputf8_enable = no
# Basic configuration
# myhostname =
alias_maps = hash:/etc/aliases
@ -51,10 +54,11 @@ smtpd_helo_required = yes
smtpd_delay_reject = yes
smtpd_helo_restrictions = permit_mynetworks, reject_invalid_helo_hostname, permit
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, reject_unauth_pipelining, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_unknown_recipient_domain
smtpd_client_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, reject_unauth_pipelining
smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_unknown_recipient_domain
smtpd_client_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
smtpd_sender_restrictions = $dms_smtpd_sender_restrictions
smtpd_discard_ehlo_keywords = silent-discard, dsn
smtpd_data_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_pipelining
disable_vrfy_command = yes
# Security - Prevent SMTP Smuggling attack

View file

@ -1,9 +1,12 @@
# documentation: https://rspamd.com/doc/configuration/metrics.html#actions
# and https://rspamd.com/doc/configuration/metrics.html
#greylist = 4;
#add_header = 6;
#rewrite_subject = 7;
#reject = 15;
# These values work in conjunction with the symbol scores in
# `scores.d/*.conf`. When adjusting them, make sure to understand
# 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"

View file

@ -0,0 +1,108 @@
# Please refer to
# https://github.com/docker-mailserver/docker-mailserver/issues/3690
# for understanding this file and its scores' values.
symbols = {
# SPF
"R_SPF_ALLOW" {
weight = -1;
description = "SPF verification allows sending";
groups = ["spf"];
}
"R_SPF_NA" {
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
weight = 1.5;
description = "SPF policy is neutral";
groups = ["spf"];
}
"R_SPF_DNSFAIL" { # == R_SPF_SOFTFAIL
weight = 2.5;
description = "SPF DNS failure";
groups = ["spf"];
}
"R_SPF_PERMFAIL" { # == R_SPF_FAIL
weight = 4.5;
description = "SPF record is malformed or persistent DNS error";
groups = ["spf"];
}
# DKIM
"R_DKIM_ALLOW" {
weight = -1;
description = "DKIM verification succeed";
one_shot = true;
groups = ["dkim"];
}
"R_DKIM_NA" {
weight = 0;
description = "Missing DKIM signature";
one_shot = true;
groups = ["dkim"];
}
"R_DKIM_TEMPFAIL" {
weight = 1.5;
description = "DKIM verification soft-failed";
groups = ["dkim"];
}
"R_DKIM_PERMFAIL" {
weight = 4.5;
description = "DKIM verification hard-failed (invalid)";
groups = ["dkim"];
}
"R_DKIM_REJECT" { # == R_DKIM_PERMFAIL
weight = 4.5;
description = "DKIM verification failed";
one_shot = true;
groups = ["dkim"];
}
# 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
weight = -1;
description = "DMARC permit policy";
groups = ["dmarc"];
}
"DMARC_POLICY_ALLOW_WITH_FAILURES" { # no equivalent
weight = -0.5;
description = "DMARC permit policy with DKIM/SPF failure";
groups = ["dmarc"];
}
"DMARC_POLICY_SOFTFAIL" { # == DMARC_POLICY_QUARANTINE
weight = 1.5;
description = "DMARC soft-failed";
groups = ["dmarc"];
}
}

View file

@ -44,7 +44,15 @@ function _pre_installation_steps() {
function _install_utils() {
_log 'debug' 'Installing utils sourced from Github'
curl -sL https://github.com/01mf02/jaq/releases/latest/download/jaq-v1.2.0-x86_64-unknown-linux-musl -o /usr/bin/jaq && chmod +x /usr/bin/jaq
_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 _install_postfix() {
@ -99,7 +107,7 @@ function _install_packages() {
)
local POSTFIX_PACKAGES=(
pflogsumm postgrey postfix-ldap
pflogsumm postgrey postfix-ldap postfix-mta-sts-resolver
postfix-pcre postfix-policyd-spf-python postsrsd
)
@ -117,7 +125,7 @@ function _install_packages() {
# `bind9-dnsutils` provides the `dig` command
# `iputils-ping` provides the `ping` command
DEBUG_PACKAGES=(
bind9-dnsutils iputils-ping less nano swaks
bind9-dnsutils iputils-ping less nano
)
apt-get "${QUIET}" --no-install-recommends install \

View file

@ -71,6 +71,11 @@ function _register_functions() {
;;
esac
if [[ ${ENABLE_OAUTH2} -eq 1 ]]; then
_environment_variables_oauth2
_register_setup_function '_setup_oauth2'
fi
if [[ ${ENABLE_SASLAUTHD} -eq 1 ]]; then
_environment_variables_saslauthd
_register_setup_function '_setup_saslauthd'
@ -115,6 +120,11 @@ function _register_functions() {
_register_setup_function '_setup_apply_fixes_after_configuration'
_register_setup_function '_environment_variables_export'
if [[ ${ENABLE_MTA_STS} -eq 1 ]]; then
_register_setup_function '_setup_mta_sts'
_register_start_daemon '_start_daemon_mta_sts_daemon'
fi
# ? >> Daemons
_register_start_daemon '_start_daemon_cron'

View file

@ -38,6 +38,7 @@ function _start_daemon_opendkim { _default_start_daemon 'opendkim' ;
function _start_daemon_opendmarc { _default_start_daemon 'opendmarc' ; }
function _start_daemon_postgrey { _default_start_daemon 'postgrey' ; }
function _start_daemon_postsrsd { _default_start_daemon 'postsrsd' ; }
function _start_daemon_mta_sts_daemon { _default_start_daemon 'mta-sts-daemon' ; }
function _start_daemon_rspamd { _default_start_daemon 'rspamd' ; }
function _start_daemon_rspamd_redis { _default_start_daemon 'rspamd-redis' ; }
function _start_daemon_rsyslog { _default_start_daemon 'rsyslog' ; }

View file

@ -20,6 +20,16 @@ function _setup() {
${FUNC}
done
_setup_post
}
function _setup_post() {
# Dovecot `.svbin` files must have a newer mtime than their `.sieve` source files,
# Modifications during setup to these files sometimes results in a common mtime value.
# Handled during post-setup as setup of Dovecot Sieve scripts is not centralized.
find /usr/lib/dovecot/ -iname '*.sieve' -exec touch -d '2 seconds ago' {} +
find /usr/lib/dovecot/ -iname '*.svbin' -exec touch -d '1 seconds ago' {} +
# All startup modifications to configs should have taken place before calling this:
_prepare_for_change_detection
}

View file

@ -24,6 +24,7 @@ function _setup_save_states() {
[[ ${ENABLE_FAIL2BAN} -eq 1 ]] && SERVICEDIRS+=('lib/fail2ban')
[[ ${ENABLE_FETCHMAIL} -eq 1 ]] && SERVICEDIRS+=('lib/fetchmail')
[[ ${ENABLE_GETMAIL} -eq 1 ]] && SERVICEDIRS+=('lib/getmail')
[[ ${ENABLE_MTA_STS} -eq 1 ]] && SERVICEDIRS+=('lib/mta-sts')
[[ ${ENABLE_POSTGREY} -eq 1 ]] && SERVICEDIRS+=('lib/postgrey')
[[ ${ENABLE_RSPAMD} -eq 1 ]] && SERVICEDIRS+=('lib/rspamd')
[[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && SERVICEDIRS+=('lib/redis')
@ -84,6 +85,7 @@ function _setup_save_states() {
[[ ${ENABLE_AMAVIS} -eq 1 ]] && chown -R amavis:amavis "${STATEDIR}/lib-amavis"
[[ ${ENABLE_CLAMAV} -eq 1 ]] && chown -R clamav:clamav "${STATEDIR}/lib-clamav"
[[ ${ENABLE_FETCHMAIL} -eq 1 ]] && chown -R fetchmail:nogroup "${STATEDIR}/lib-fetchmail"
[[ ${ENABLE_MTA_STS} -eq 1 ]] && chown -R _mta-sts:_mta-sts "${STATEDIR}/lib-mta-sts"
[[ ${ENABLE_POSTGREY} -eq 1 ]] && chown -R postgrey:postgrey "${STATEDIR}/lib-postgrey"
[[ ${ENABLE_RSPAMD} -eq 1 ]] && chown -R _rspamd:_rspamd "${STATEDIR}/lib-rspamd"
[[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && chown -R redis:redis "${STATEDIR}/lib-redis"

View file

@ -0,0 +1,7 @@
#!/bin/bash
function _setup_mta_sts() {
_log 'trace' 'Adding MTA-STS lookup to the Postfix TLS policy map'
_add_to_or_update_postfix_main smtp_tls_policy_maps 'socketmap:unix:/var/run/mta-sts/daemon.sock:postfix'
}

View file

@ -0,0 +1,11 @@
#!/bin/bash
function _setup_oauth2() {
_log 'debug' 'Setting up OAUTH2'
# Enable OAuth2 PassDB (Authentication):
sedfile -i -e '/\!include auth-oauth2\.conf\.ext/s/^#//' /etc/dovecot/conf.d/10-auth.conf
_replace_by_env_in_file 'OAUTH2_' '/etc/dovecot/dovecot-oauth2.conf.ext'
return 0
}

View file

@ -19,9 +19,6 @@ function _setup_postfix_early() {
postconf "inet_protocols = ${POSTFIX_INET_PROTOCOLS}"
fi
__postfix__log 'trace' "Disabling SMTPUTF8 support"
postconf 'smtputf8_enable = no'
__postfix__log 'trace' "Configuring SASLauthd"
if [[ ${ENABLE_SASLAUTHD} -eq 1 ]] && [[ ! -f /etc/postfix/sasl/smtpd.conf ]]; then
cat >/etc/postfix/sasl/smtpd.conf << EOF

View file

@ -106,7 +106,7 @@ function __setup__security__spamassassin() {
if [[ ${SPAMASSASSIN_SPAM_TO_INBOX} -eq 1 ]]; then
_log 'trace' 'Configuring Spamassassin/Amavis to send SPAM to inbox'
_log 'debug' 'SPAM_TO_INBOX=1 is set. SA_KILL will be ignored.'
_log 'debug' "'SPAMASSASSIN_SPAM_TO_INBOX=1' is set. The 'SA_KILL' ENV will be ignored."
sed -i "s|\$final_spam_destiny.*=.*$|\$final_spam_destiny = D_PASS;|g" /etc/amavis/conf.d/49-docker-mailserver
sed -i "s|\$final_bad_header_destiny.*=.*$|\$final_bad_header_destiny = D_PASS;|g" /etc/amavis/conf.d/49-docker-mailserver
@ -260,7 +260,7 @@ EOF
chown dovecot:root /usr/lib/dovecot/sieve-global/after/spam_to_junk.{sieve,svbin}
if [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]] && [[ ${SPAMASSASSIN_SPAM_TO_INBOX} -eq 0 ]]; then
_log 'warning' "'SPAMASSASSIN_SPAM_TO_INBOX=0' but it is required to be 1 for 'MOVE_SPAM_TO_JUNK=1' to work"
_log 'warn' "'SPAMASSASSIN_SPAM_TO_INBOX=0' but it is required to be 1 for 'MOVE_SPAM_TO_JUNK=1' to work"
fi
else
_log 'debug' 'Spam emails will not be moved to the Junk folder'
@ -285,7 +285,7 @@ EOF
chown dovecot:root /usr/lib/dovecot/sieve-global/after/spam_mark_as_read.{sieve,svbin}
if [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]] && [[ ${SPAMASSASSIN_SPAM_TO_INBOX} -eq 0 ]]; then
_log 'warning' "'SPAMASSASSIN_SPAM_TO_INBOX=0' but it is required to be 1 for 'MARK_SPAM_AS_READ=1' to work"
_log 'warn' "'SPAMASSASSIN_SPAM_TO_INBOX=0' but it is required to be 1 for 'MARK_SPAM_AS_READ=1' to work"
fi
else
_log 'debug' 'Spam emails will not be marked as read'

View file

@ -83,6 +83,7 @@ function __environment_variables_general_setup() {
VARS[ENABLE_FETCHMAIL]="${ENABLE_FETCHMAIL:=0}"
VARS[ENABLE_GETMAIL]="${ENABLE_GETMAIL:=0}"
VARS[ENABLE_MANAGESIEVE]="${ENABLE_MANAGESIEVE:=0}"
VARS[ENABLE_OAUTH2]="${ENABLE_OAUTH2:=0}"
VARS[ENABLE_OPENDKIM]="${ENABLE_OPENDKIM:=1}"
VARS[ENABLE_OPENDMARC]="${ENABLE_OPENDMARC:=1}"
VARS[ENABLE_POLICYD_SPF]="${ENABLE_POLICYD_SPF:=1}"
@ -151,6 +152,12 @@ function __environment_variables_general_setup() {
VARS[UPDATE_CHECK_INTERVAL]="${UPDATE_CHECK_INTERVAL:=1d}"
}
function _environment_variables_oauth2() {
_log 'debug' 'Setting OAUTH2-related environment variables now'
VARS[OAUTH2_INTROSPECTION_URL]="${OAUTH2_INTROSPECTION_URL:=}"
}
# This function handles environment variables related to LDAP.
# NOTE: SASLAuthd and Dovecot LDAP support inherit these common ENV.
function _environment_variables_ldap() {

View file

@ -157,3 +157,15 @@ autostart=false
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
command=/bin/bash -l -c /usr/local/bin/update-check.sh
# Docs: https://github.com/Snawoot/postfix-mta-sts-resolver/blob/master/man/mta-sts-daemon.1.adoc
[program:mta-sts-daemon]
startsecs=0
stopwaitsecs=55
autostart=false
autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
command=/usr/bin/mta-sts-daemon --config /etc/mta-sts-daemon.yml
user=_mta-sts
environment=HOME=/var/lib/mta-sts

View file

@ -0,0 +1,56 @@
# OAuth2 mock service
#
# Dovecot will query this service with the token it was provided.
# If the session for the token is valid, a response provides an attribute to perform a UserDB lookup on (default: email).
import json
import base64
from http.server import BaseHTTPRequestHandler, HTTPServer
# OAuth2.0 Bearer token (paste into https://jwt.io/ to check it's contents).
# You should never need to edit this unless you REALLY need to change the issuer.
token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwOi8vcHJvdmlkZXIuZXhhbXBsZS50ZXN0OjgwMDAvIiwic3ViIjoiODJjMWMzMzRkY2M2ZTMxMWFlNGFhZWJmZTk0NmM1ZTg1OGYwNTVhZmYxY2U1YTM3YWE3Y2M5MWFhYjE3ZTM1YyIsImF1ZCI6Im1haWxzZXJ2ZXIiLCJ1aWQiOiI4OU4zR0NuN1M1Y090WkZNRTVBeVhNbmxURFdVcnEzRmd4YWlyWWhFIn0.zuCytArbphhJn9XT_y9cBdGqDCNo68tBrtOwPIsuKNyF340SaOuZa0xarZofygytdDpLtYr56QlPTKImi-n1ZWrHkRZkwrQi5jQ-j_n2hEAL0vUToLbDnXYfc5q2w7z7X0aoCmiK8-fV7Kx4CVTM7riBgpElf6F3wNAIcX6R1ijUh6ISCL0XYsdogf8WUNZipXY-O4R7YHXdOENuOp3G48hWhxuUh9PsUqE5yxDwLsOVzCTqg9S5gxPQzF2eCN9J0I2XiIlLKvLQPIZ2Y_K7iYvVwjpNdgb4xhm9wuKoIVinYkF_6CwIzAawBWIDJAbix1IslkUPQMGbupTDtOgTiQ"
# This is the string the user-facing client (e.g. Roundcube) should send via IMAP to Dovecot.
# We include the user and the above token separated by '\1' chars as per the XOAUTH2 spec.
xoauth2 = base64.b64encode(f"user=user1@localhost.localdomain\1auth=Bearer {token}\1\1".encode("utf-8"))
# If changing the user above, use the new output from the below line with the contents of the AUTHENTICATE command in test/test-files/auth/imap-oauth2-auth.txt
print("XOAUTH2 string: " + str(xoauth2))
class HTTPRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
auth = self.headers.get("Authorization")
if auth is None:
self.send_response(401)
self.end_headers()
return
if len(auth.split()) != 2:
self.send_response(401)
self.end_headers()
return
auth = auth.split()[1]
# Valid session, respond with JSON containing the expected `email` claim to match as Dovecot username:
if auth == token:
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps({
"email": "user1@localhost.localdomain",
"email_verified": True,
"sub": "82c1c334dcc6e311ae4aaebfe946c5e858f055aff1ce5a37aa7cc91aab17e35c"
}).encode("utf-8"))
else:
self.send_response(401)
self.end_headers()
server = HTTPServer(('', 80), HTTPRequestHandler)
print("Starting server", flush=True)
try:
server.serve_forever()
except KeyboardInterrupt:
print()
print("Received keyboard interrupt")
finally:
print("Exiting")

View file

@ -0,0 +1,4 @@
a0 NOOP See test/config/oauth2/provider.py to generate the below XOAUTH2 string
a1 AUTHENTICATE XOAUTH2 dXNlcj11c2VyMUBsb2NhbGhvc3QubG9jYWxkb21haW4BYXV0aD1CZWFyZXIgZXlKaGJHY2lPaUpTVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnBjM01pT2lKb2RIUndPaTh2Y0hKdmRtbGtaWEl1WlhoaGJYQnNaUzUwWlhOME9qZ3dNREF2SWl3aWMzVmlJam9pT0RKak1XTXpNelJrWTJNMlpUTXhNV0ZsTkdGaFpXSm1aVGswTm1NMVpUZzFPR1l3TlRWaFptWXhZMlUxWVRNM1lXRTNZMk01TVdGaFlqRTNaVE0xWXlJc0ltRjFaQ0k2SW0xaGFXeHpaWEoyWlhJaUxDSjFhV1FpT2lJNE9VNHpSME51TjFNMVkwOTBXa1pOUlRWQmVWaE5ibXhVUkZkVmNuRXpSbWQ0WVdseVdXaEZJbjAuenVDeXRBcmJwaGhKbjlYVF95OWNCZEdxRENObzY4dEJydE93UElzdUtOeUYzNDBTYU91WmEweGFyWm9meWd5dGREcEx0WXI1NlFsUFRLSW1pLW4xWldySGtSWmt3clFpNWpRLWpfbjJoRUFMMHZVVG9MYkRuWFlmYzVxMnc3ejdYMGFvQ21pSzgtZlY3S3g0Q1ZUTTdyaUJncEVsZjZGM3dOQUljWDZSMWlqVWg2SVNDTDBYWXNkb2dmOFdVTlppcFhZLU80UjdZSFhkT0VOdU9wM0c0OGhXaHh1VWg5UHNVcUU1eXhEd0xzT1Z6Q1RxZzlTNWd4UFF6RjJlQ045SjBJMlhpSWxMS3ZMUVBJWjJZX0s3aVl2VndqcE5kZ2I0eGhtOXd1S29JVmluWWtGXzZDd0l6QWF3QldJREpBYml4MUlzbGtVUFFNR2J1cFREdE9nVGlRAQE=
a2 EXAMINE INBOX
a3 LOGOUT

View file

@ -1,6 +0,0 @@
From: Docker Mail Server <dockermailserver@external.tld>
To: Existing Local User <user1@localhost.localdomain>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message amavis/spam.txt
This is a test mail.
XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X

View file

@ -1,5 +0,0 @@
From: Docker Mail Server <dockermailserver@external.tld>
To: Existing Local User <added@localhost.localdomain>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message existing-added.txt
This is a test mail.

View file

@ -1,5 +0,0 @@
From: Docker Mail Server <dockermailserver@external.tld>
To: Existing Local User <alias1@localhost.localdomain>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message existing-alias-external.txt
This is a test mail.

View file

@ -1,5 +0,0 @@
From: Docker Mail Server <dockermailserver@external.tld>
To: Existing Local Alias <alias2@localhost.localdomain>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message existing-alias-local.txt
This is a test mail.

View file

@ -1,5 +0,0 @@
From: Docker Mail Server <dockermailserver@external.tld>
To: Existing Local Alias With Delimiter <alias1+test@localhost.localdomain>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message existing-alias-recipient-delimiter.txt
This is a test mail.

View file

@ -1,5 +0,0 @@
From: Docker Mail Server <dockermailserver@external.tld>
To: Existing Local User <wildcard@localdomain2.com>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message existing-catchall-local.txt
This is a test mail.

View file

@ -1,5 +0,0 @@
From: Docker Mail Server <dockermailserver@external.tld>
To: Existing Local User <bounce-always@localhost.localdomain>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message existing-regexp-alias-external.txt
This is a test mail.

View file

@ -1,5 +0,0 @@
From: Docker Mail Server <dockermailserver@external.tld>
To: Existing Local User <test123@localhost.localdomain>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message existing-regexp-alias-local.txt
This is a test mail.

View file

@ -1,6 +0,0 @@
From: Docker Mail Server <dockermailserver@external.tld>
To: Existing Local User <user1@localhost.localdomain>
Cc: Existing Local Alias <alias2@localhost.localdomain>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message existing-user-and-cc-local-alias.txt
This is a test mail.

View file

@ -1,5 +0,0 @@
From: Docker Mail Server <dockermailserver@external.tld>
To: Existing Local User <user1@localhost.localdomain>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message existing-user1.txt
This is a test mail.

View file

@ -1,5 +0,0 @@
From: Docker Mail Server <dockermailserver@external.tld>
To: Existing Local User <user1@localhost.localdomain>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message non-existing-user.txt
This is a test mail.

View file

@ -1,5 +0,0 @@
From: Docker Mail Server <pass@example.test>
To: Existing Local User <user1@localhost.localdomain>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message rspamd/pass.txt
This mail should pass and Rspamd should not mark it.

View file

@ -1,5 +0,0 @@
From: Docker Mail Server <spam-header@example.test>
To: Existing Local User <user1@localhost.localdomain>
Date: Sat, 21 Jan 2023 11:11:11 +0000
Subject: Test Message rspamd-spam-header.txt
YJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X

View file

@ -1,5 +0,0 @@
From: Docker Mail Server <spam@example.test>
To: Existing Local User <user1@localhost.localdomain>
Date: Sat, 21 Jan 2023 11:11:11 +0000
Subject: Test Message rspamd-spam.txt
XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X

View file

@ -1,5 +0,0 @@
From: Docker Mail Server <virus@example.test>
To: Existing Local User <user1@localhost.localdomain>
Date: Sat, 21 Jan 2023 11:11:11 +0000
Subject: Test Message rspamd-virus.txt
X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*

View file

@ -466,7 +466,7 @@ function _print_mail_log_for_id() {
local MAIL_ID=${1:?Mail ID must be provided}
local CONTAINER_NAME=$(__handle_container_name "${2:-}")
_run_in_container grep -F "${MAIL_ID}" /var/log/mail.log
_run_in_container grep -E "${MAIL_ID}" /var/log/mail.log
}
# A simple wrapper for netcat (`nc`). This is useful when sending
@ -480,7 +480,7 @@ function _nc_wrapper() {
[[ -v CONTAINER_NAME ]] || return 1
_run_in_container_bash "nc ${NC_PARAMETERS} < /tmp/docker-mailserver-test/${FILE}.txt"
_run_in_container_bash "nc ${NC_PARAMETERS} < /tmp/docker-mailserver-test/${FILE}"
}
# ? << Miscellaneous helper functions

View file

@ -7,68 +7,118 @@
# ! ATTENTION: This file is loaded by `common.sh` - do not load it yourself!
# ! ATTENTION: This file requires helper functions from `common.sh`!
# Sends a mail from localhost (127.0.0.1) to a container. To send
# a custom email, create a file at `test/files/<TEST FILE>`,
# and provide `<TEST FILE>` as an argument to this function.
# Sends an e-mail from the container named by the environment variable `CONTAINER_NAME`
# to the same or another container.
#
# Parameters include all options that one can supply to `swaks`
# itself. The `--data` parameter expects a relative path from `emails/`
# where the contents will be implicitly provided to `swaks` via STDIN.
# To send a custom email, you can
#
# 1. create a file at `test/files/<TEST FILE>` and provide `<TEST FILE>` via `--data` as an argument to this function;
# 2. use this function without the `--data` argument, in which case we provide a default;
# 3. provide data inline (`--data <INLINE DATA>`).
#
# The very first parameter **may** be `--expect-rejection` - use it of you expect the mail transaction to not finish
# successfully. All other (following) parameters include all options that one can supply to `swaks` itself.
# As mentioned before, the `--data` parameter expects a value of either:
#
# - A relative path from `test/files/emails/`
# - An "inline" data string (e.g., `Date: 1 Jan 2024\nSubject: This is a test`)
#
# ## Output
#
# This functions prints the output of the transaction that `swaks` prints.
#
# ## Attention
#
# This function assumes `CONTAINER_NAME` to be properly set (to the container
# name the command should be executed in)!
#
# This function will just send the email in an "asynchronous" fashion, i.e. it will
# send the email but it will not make sure the mail queue is empty after the mail
# has been sent.
# This function will send the email in an "asynchronous" fashion,
# it will return without waiting for the Postfix mail queue to be emptied.
function _send_email() {
[[ -v CONTAINER_NAME ]] || return 1
local RETURN_VALUE=0
local COMMAND_STRING
# Parameter defaults common to our testing needs:
local EHLO='mail.external.tld'
local FROM='user@external.tld'
local TO='user1@localhost.localdomain'
local SERVER='0.0.0.0'
local PORT=25
# Extra options for `swaks` that aren't covered by the default options above:
local ADDITIONAL_SWAKS_OPTIONS=()
# Specifically for handling `--data` option below:
local FINAL_SWAKS_OPTIONS=()
function __parse_arguments() {
[[ -v CONTAINER_NAME ]] || return 1
while [[ ${#} -gt 0 ]]; do
case "${1}" in
( '--ehlo' ) EHLO=${2:?--ehlo given but no argument} ; shift 2 ;;
( '--from' ) FROM=${2:?--from given but no argument} ; shift 2 ;;
( '--to' ) TO=${2:?--to given but no argument} ; shift 2 ;;
( '--server' ) SERVER=${2:?--server given but no argument} ; shift 2 ;;
( '--port' ) PORT=${2:?--port given but no argument} ; shift 2 ;;
( '--data' )
local TEMPLATE_FILE="/tmp/docker-mailserver-test/emails/${2:?--data given but no argument provided}.txt"
FINAL_SWAKS_OPTIONS+=('--data')
FINAL_SWAKS_OPTIONS+=('-')
FINAL_SWAKS_OPTIONS+=('<')
FINAL_SWAKS_OPTIONS+=("${TEMPLATE_FILE}")
shift 2
;;
( * ) ADDITIONAL_SWAKS_OPTIONS+=("${1}") ; shift 1 ;;
esac
done
# Parameter defaults common to our testing needs:
local EHLO='mail.external.tld'
local FROM='user@external.tld'
local TO='user1@localhost.localdomain'
local SERVER='0.0.0.0'
local PORT=25
# Extra options for `swaks` that aren't covered by the default options above:
local ADDITIONAL_SWAKS_OPTIONS=()
local DATA_WAS_SUPPLIED=0
_run_in_container_bash "swaks --server ${SERVER} --port ${PORT} --ehlo ${EHLO} --from ${FROM} --to ${TO} ${ADDITIONAL_SWAKS_OPTIONS[*]} ${FINAL_SWAKS_OPTIONS[*]}"
while [[ ${#} -gt 0 ]]; do
case "${1}" in
( '--ehlo' ) EHLO=${2:?--ehlo given but no argument} ; shift 2 ;;
( '--from' ) FROM=${2:?--from given but no argument} ; shift 2 ;;
( '--to' ) TO=${2:?--to given but no argument} ; shift 2 ;;
( '--server' ) SERVER=${2:?--server given but no argument} ; shift 2 ;;
( '--port' ) PORT=${2:?--port given but no argument} ; shift 2 ;;
( '--data' )
ADDITIONAL_SWAKS_OPTIONS+=('--data')
local FILE_PATH="/tmp/docker-mailserver-test/emails/${2:?--data given but no argument provided}"
if _exec_in_container_bash "[[ -e ${FILE_PATH} ]]"; then
ADDITIONAL_SWAKS_OPTIONS+=("@${FILE_PATH}")
else
ADDITIONAL_SWAKS_OPTIONS+=("'${2}'")
fi
shift 2
DATA_WAS_SUPPLIED=1
;;
( * ) ADDITIONAL_SWAKS_OPTIONS+=("'${1}'") ; shift 1 ;;
esac
done
if [[ ${DATA_WAS_SUPPLIED} -eq 0 ]]; then
# Fallback template (without the implicit `Message-Id` + `X-Mailer` headers from swaks):
# NOTE: It is better to let Postfix generate and append the `Message-Id` header itself,
# as it will contain the Queue ID for tracking in logs (which is also returned in swaks output).
ADDITIONAL_SWAKS_OPTIONS+=('--data')
ADDITIONAL_SWAKS_OPTIONS+=("'Date: %DATE%\nTo: %TO_ADDRESS%\nFrom: %FROM_ADDRESS%\nSubject: test %DATE%\n%NEW_HEADERS%\n%BODY%\n'")
fi
echo "swaks --server '${SERVER}' --port '${PORT}' --ehlo '${EHLO}' --from '${FROM}' --to '${TO}' ${ADDITIONAL_SWAKS_OPTIONS[*]}"
}
if [[ ${1:-} == --expect-rejection ]]; then
shift 1
COMMAND_STRING=$(__parse_arguments "${@}")
_run_in_container_bash "${COMMAND_STRING}"
RETURN_VALUE=${?}
else
COMMAND_STRING=$(__parse_arguments "${@}")
_run_in_container_bash "${COMMAND_STRING}"
assert_success
fi
# shellcheck disable=SC2154
echo "${output}"
return "${RETURN_VALUE}"
}
# Like `_send_email` with two major differences:
#
# 1. this function waits for the mail to be processed; there is no asynchronicity
# because filtering the logs in a synchronous way is easier and safer!
# 2. this function prints an ID one can later filter by to check logs
# because filtering the logs in a synchronous way is easier and safer;
# 2. this function takes the name of a variable and inserts IDs one can later
# filter by to check logs.
#
# No. 2 is especially useful in case you send more than one email in a single
# test file and need to assert certain log entries for each mail individually.
#
# This function takes the same arguments as `_send_mail`.
# The first argument has to be the name of the variable that the e-mail ID is stored in.
# The second argument **can** be the flag `--expect-rejection`.
#
# - If this flag is supplied, the function does not check whether the whole mail delivery
# transaction was successful. Additionally the queue ID will be retrieved differently.
# - CAUTION: It must still be possible to `grep` for the Message-ID that Postfix
# generated in the mail log; otherwise this function fails.
#
# The rest of the arguments are the same as `_send_email`.
#
# ## Attention
#
@ -82,20 +132,42 @@ function _send_email() {
# chosen. Sending more than one mail at any given point in time with this function
# is UNDEFINED BEHAVIOR!
function _send_email_and_get_id() {
[[ -v CONTAINER_NAME ]] || return 1
# Export the variable denoted by ${1} so everyone has access
export "${1:?Mail ID must be set for _send_email_and_get_id}"
# Get a "reference" to the content of the variable denoted by ${1} so we can manipulate the content
local -n ID_ENV_VAR_REF=${1:?}
# Prepare the message ID header here because we will shift away ${1} later
local MID="<${1}@dms-tests>"
# Get rid of ${1} so only the arguments for swaks remain
shift 1
_wait_for_empty_mail_queue_in_container
_send_email "${@}"
_wait_for_empty_mail_queue_in_container
local MAIL_ID
# The unique ID Postfix (and other services) use may be different in length
# on different systems (e.g. amd64 (11) vs aarch64 (10)). Hence, we use a
# range to safely capture it.
MAIL_ID=$(_exec_in_container tac /var/log/mail.log \
| grep -E -m 1 'postfix/smtpd.*: [A-Z0-9]+: client=localhost' \
| grep -E -o '[A-Z0-9]{9,12}' || true)
# on different systems. Hence, we use a range to safely capture it.
local QUEUE_ID_REGEX='[A-Z0-9]{9,12}'
assert_not_equal "${MAIL_ID}" ''
echo "${MAIL_ID}"
_wait_for_empty_mail_queue_in_container
_send_email "${@}" --header "Message-Id: ${MID}"
_wait_for_empty_mail_queue_in_container
# We store Postfix's queue ID first
ID_ENV_VAR_REF=$(_exec_in_container tac /var/log/mail.log \
| grep -E "postfix/cleanup.*: ${QUEUE_ID_REGEX}:.*message-id=${MID}" \
| grep -E --only-matching --max-count 1 "${QUEUE_ID_REGEX}" || :)
# But we also requre potential Dovecot sieve output, which requires the mesage ID,
# so we need to provide the message ID too.
ID_ENV_VAR_REF+="|${MID}"
# Last but not least, we perform plausibility checks on the IDs.
assert_not_equal "${ID_ENV_VAR_REF}" ''
run echo "${ID_ENV_VAR_REF}"
assert_line --regexp "^${QUEUE_ID_REGEX}\|${MID}$"
}
# Send a spam e-mail by utilizing GTUBE.
#
# Extra arguments given to this function will be supplied by `_send_email_and_get_id` directly.
function _send_spam() {
_send_email_and_get_id MAIL_ID_SPAM "${@}" \
--from 'spam@external.tld' \
--body 'XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X'
}

View file

@ -225,12 +225,9 @@ function teardown_file() { _default_teardown ; }
sleep 10
# send some big emails
_send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded'
assert_success
_send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded'
assert_success
_send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded'
assert_success
_send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded.txt'
_send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded.txt'
_send_email --to 'quotauser@otherdomain.tld' --data 'quota-exceeded.txt'
# check for quota warn message existence
run _repeat_until_success_or_timeout 20 _exec_in_container grep -R 'Subject: quota warning' /var/mail/otherdomain.tld/quotauser/new/
assert_success

View file

@ -26,11 +26,9 @@ function setup_file() {
_wait_for_smtp_port_in_container
# Single mail sent from 'spam@spam.com' that is handled by User (relocate) and Global (copy) sieves for user1:
_send_email --data 'sieve/spam-folder'
assert_success
_send_email --data 'sieve/spam-folder.txt'
# Mail for user2 triggers the sieve-pipe:
_send_email --to 'user2@otherdomain.tld' --data 'sieve/pipe'
assert_success
_send_email --to 'user2@otherdomain.tld' --data 'sieve/pipe.txt'
_wait_for_empty_mail_queue_in_container
}

View file

@ -26,8 +26,7 @@ function teardown() { _default_teardown ; }
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_smtp_port_in_container
_send_email --data 'existing/user1'
assert_success
_send_email
_wait_for_empty_mail_queue_in_container
# Mail received should be stored as `u.1` (one file per message)
@ -48,8 +47,7 @@ function teardown() { _default_teardown ; }
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_smtp_port_in_container
_send_email --data 'existing/user1'
assert_success
_send_email
_wait_for_empty_mail_queue_in_container
# Mail received should be stored in `m.1` (1 or more messages)

View file

@ -14,8 +14,7 @@ function setup_file() {
function teardown_file() { _default_teardown ; }
@test 'normal delivery works' {
_send_email --data 'existing/user1'
assert_success
_send_email
_count_files_in_directory_in_container /var/mail/localhost.localdomain/user1/new 1
}
@ -27,7 +26,7 @@ function teardown_file() { _default_teardown ; }
}
@test "(IMAP) special-use folders should be created when necessary" {
_nc_wrapper 'nc/imap_special_use_folders' '-w 8 0.0.0.0 143'
_nc_wrapper 'nc/imap_special_use_folders.txt' '-w 8 0.0.0.0 143'
assert_output --partial 'Drafts'
assert_output --partial 'Junk'
assert_output --partial 'Trash'

View file

@ -25,15 +25,14 @@ function setup_file() {
_wait_for_service postfix
_wait_for_smtp_port_in_container
_send_email --from 'virus@external.tld' --data 'amavis/virus'
assert_success
_send_email --from 'virus@external.tld' --data 'amavis/virus.txt'
_wait_for_empty_mail_queue_in_container
}
function teardown_file() { _default_teardown ; }
@test 'log files exist at /var/log/mail directory' {
_run_in_container_bash "ls -1 /var/log/mail/ | grep -E 'clamav|freshclam|mail.log' | wc -l"
_run_in_container_bash "ls -1 /var/log/mail/ | grep -c -E 'clamav|freshclam|mail.log'"
assert_success
assert_output 3
}

View file

@ -18,8 +18,7 @@ function setup_file() {
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_smtp_port_in_container
_send_email --data 'existing/user1'
assert_success
_send_email
_wait_for_empty_mail_queue_in_container
}

View file

@ -74,7 +74,7 @@ function teardown_file() {
CONTAINER1_IP=$(_get_container_ip "${CONTAINER1_NAME}")
# Trigger a ban by failing to login twice:
for _ in {1..2}; do
CONTAINER_NAME=${CONTAINER2_NAME} _send_email \
CONTAINER_NAME=${CONTAINER2_NAME} _send_email --expect-rejection \
--server "${CONTAINER1_IP}" \
--port 465 \
--auth PLAIN \

View file

@ -44,14 +44,14 @@ function teardown_file() { _default_teardown ; }
# The other spam checks in `main.cf:smtpd_recipient_restrictions` would interfere with testing postgrey.
_run_in_container sed -i \
-e 's/permit_sasl_authenticated.*policyd-spf,$//g' \
-e 's/reject_unauth_pipelining.*reject_unknown_recipient_domain,$//g' \
-e 's/reject_invalid_helo_hostname.*reject_unknown_recipient_domain,$//g' \
-e 's/reject_rbl_client.*inet:127\.0\.0\.1:10023$//g' \
-e 's/smtpd_recipient_restrictions =/smtpd_recipient_restrictions = check_policy_service inet:127.0.0.1:10023/g' \
/etc/postfix/main.cf
_reload_postfix
# Send test mail (it should fail to deliver):
_send_email --from 'user@external.tld' --port 25 --data 'postgrey'
_send_email --expect-rejection --from 'user@external.tld' --port 25 --data 'postgrey.txt'
assert_failure
assert_output --partial 'Recipient address rejected: Delayed by Postgrey'
@ -59,11 +59,7 @@ function teardown_file() { _default_teardown ; }
_should_have_log_entry \
'action=greylist' \
'reason=new' \
'client_address=127.0.0.1/32, sender=user@external.tld, recipient=user1@localhost.localdomain'
_repeat_until_success_or_timeout 10 _run_in_container grep \
'Recipient address rejected: Delayed by Postgrey' \
/var/log/mail/mail.log
'client_address=127.0.0.1, sender=user@external.tld, recipient=user1@localhost.localdomain'
}
# NOTE: This test case depends on the previous one
@ -71,8 +67,7 @@ function teardown_file() { _default_teardown ; }
# Wait until `$POSTGREY_DELAY` seconds pass before trying again:
sleep 3
# Retry delivering test mail (it should be trusted this time):
_send_email --from 'user@external.tld' --port 25 --data 'postgrey'
assert_success
_send_email --from 'user@external.tld' --port 25 --data 'postgrey.txt'
# Confirm postgrey permitted delivery (triplet is now trusted):
_should_have_log_entry \
@ -91,7 +86,7 @@ function teardown_file() { _default_teardown ; }
# - It'd also cause the earlier greylist test to fail.
# - TODO: Actually confirm whitelist feature works correctly as these test cases are using a workaround:
@test "should whitelist sender 'user@whitelist.tld'" {
_nc_wrapper 'nc/postgrey_whitelist' '-w 0 0.0.0.0 10023'
_nc_wrapper 'nc/postgrey_whitelist.txt' '-w 0 0.0.0.0 10023'
_should_have_log_entry \
'action=pass' \
@ -100,7 +95,7 @@ function teardown_file() { _default_teardown ; }
}
@test "should whitelist recipient 'user2@otherdomain.tld'" {
_nc_wrapper 'nc/postgrey_whitelist_recipients' '-w 0 0.0.0.0 10023'
_nc_wrapper 'nc/postgrey_whitelist_recipients.txt' '-w 0 0.0.0.0 10023'
_should_have_log_entry \
'action=pass' \

View file

@ -44,7 +44,7 @@ function teardown_file() {
# Use `nc` to send all SMTP commands at once instead (emulate a misbehaving client that should be rejected)
# NOTE: Postscreen only runs on port 25, avoid implicit ports in test methods
@test 'should fail send when talking out of turn' {
CONTAINER_NAME=${CONTAINER2_NAME} _nc_wrapper 'emails/nc_raw/postscreen' "${CONTAINER1_IP} 25"
CONTAINER_NAME=${CONTAINER2_NAME} _nc_wrapper 'emails/nc_raw/postscreen.txt' "${CONTAINER1_IP} 25"
# Expected postscreen log entry:
assert_output --partial 'Protocol error'
@ -56,14 +56,10 @@ function teardown_file() {
@test "should successfully pass postscreen and get postfix greeting message (respecting postscreen_greet_wait time)" {
# Configure `send_email()` to send from the mail client container (CONTAINER2_NAME) via ENV override,
# mail is sent to the DMS server container (CONTAINER1_NAME) via `--server` parameter:
CONTAINER_NAME=${CONTAINER2_NAME} _send_email --server "${CONTAINER1_IP}" --port 25 --data 'postscreen'
# NOTE: Cannot assert_success due to sender address not being resolvable.
# TODO: Uncomment when proper resolution of domain names is possible:
# assert_success
# TODO: Prefer this approach when `_send_email_and_get_id()` can support separate client and server containers:
# local MAIL_ID=$(_send_email_and_get_id --port 25 --data 'postscreen')
# _print_mail_log_for_id "${MAIL_ID}"
# TODO: Use _send_email_and_get_id when proper resolution of domain names is possible:
CONTAINER_NAME=${CONTAINER2_NAME} _send_email --expect-rejection --server "${CONTAINER1_IP}" --port 25 --data 'postscreen.txt'
# CONTAINER_NAME=${CONTAINER2_NAME} _send_email_and_get_id MAIL_ID_POSTSCREEN --server "${CONTAINER1_IP}" --data 'postscreen.txt'
# _print_mail_log_for_id "${MAIL_ID_POSTSCREEN}"
# assert_output --partial "stored mail into mailbox 'INBOX'"
_run_in_container cat /var/log/mail.log

View file

@ -43,16 +43,22 @@ function setup_file() {
_wait_for_service postfix
_wait_for_smtp_port_in_container
# We will send 3 emails: the first one should pass just fine; the second one should
# be rejected due to spam; the third one should be rejected due to a virus.
export MAIL_ID1=$(_send_email_and_get_id --from 'rspamd-pass@example.test' --data 'rspamd/pass')
export MAIL_ID2=$(_send_email_and_get_id --from 'rspamd-spam@example.test' --data 'rspamd/spam')
export MAIL_ID3=$(_send_email_and_get_id --from 'rspamd-virus@example.test' --data 'rspamd/virus')
export MAIL_ID4=$(_send_email_and_get_id --from 'rspamd-spam-header@example.test' --data 'rspamd/spam-header')
# We will send 4 emails:
# 1. The first one should pass just fine
_send_email_and_get_id MAIL_ID_PASS
# 2. The second one should be rejected (Rspamd-specific GTUBE pattern for rejection)
_send_spam --expect-rejection
# 3. The third one should be rejected due to a virus (ClamAV EICAR pattern)
# shellcheck disable=SC2016
_send_email_and_get_id MAIL_ID_VIRUS --expect-rejection \
--body 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*'
# 4. The fourth one will receive an added header (Rspamd-specific GTUBE pattern for adding a spam header)
# ref: https://rspamd.com/doc/gtube_patterns.html
_send_email_and_get_id MAIL_ID_HEADER --body "YJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X"
for ID in MAIL_ID{1,2,3,4}; do
[[ -n ${!ID} ]] || { echo "${ID} is empty - aborting!" ; return 1 ; }
done
_run_in_container cat /var/log/mail.log
assert_success
refute_output --partial 'inet:localhost:11332: Connection refused'
}
function teardown_file() { _default_teardown ; }
@ -104,7 +110,7 @@ function teardown_file() { _default_teardown ; }
@test 'normal mail passes fine' {
_service_log_should_contain_string 'rspamd' 'F \(no action\)'
_print_mail_log_for_id "${MAIL_ID1}"
_print_mail_log_for_id "${MAIL_ID_PASS}"
assert_output --partial "stored mail into mailbox 'INBOX'"
_count_files_in_directory_in_container /var/mail/localhost.localdomain/user1/new/ 1
@ -114,7 +120,7 @@ function teardown_file() { _default_teardown ; }
_service_log_should_contain_string 'rspamd' 'S \(reject\)'
_service_log_should_contain_string 'rspamd' 'reject "Gtube pattern"'
_print_mail_log_for_id "${MAIL_ID2}"
_print_mail_log_for_id "${MAIL_ID_SPAM}"
assert_output --partial 'milter-reject'
assert_output --partial '5.7.1 Gtube pattern'
@ -125,7 +131,7 @@ function teardown_file() { _default_teardown ; }
_service_log_should_contain_string 'rspamd' 'T \(reject\)'
_service_log_should_contain_string 'rspamd' 'reject "ClamAV FOUND VIRUS "Eicar-Signature"'
_print_mail_log_for_id "${MAIL_ID3}"
_print_mail_log_for_id "${MAIL_ID_VIRUS}"
assert_output --partial 'milter-reject'
assert_output --partial '5.7.1 ClamAV FOUND VIRUS "Eicar-Signature"'
refute_output --partial "stored mail into mailbox 'INBOX'"
@ -214,7 +220,7 @@ function teardown_file() { _default_teardown ; }
_service_log_should_contain_string 'rspamd' 'S \(add header\)'
_service_log_should_contain_string 'rspamd' 'add header "Gtube pattern"'
_print_mail_log_for_id "${MAIL_ID4}"
_print_mail_log_for_id "${MAIL_ID_HEADER}"
assert_output --partial "fileinto action: stored mail into mailbox 'Junk'"
_count_files_in_directory_in_container /var/mail/localhost.localdomain/user1/new/ 1
@ -256,7 +262,7 @@ function teardown_file() { _default_teardown ; }
# Move an email to the "Junk" folder from "INBOX"; the first email we
# sent should pass fine, hence we can now move it.
_nc_wrapper 'nc/rspamd_imap_move_to_junk' '0.0.0.0 143'
_nc_wrapper 'nc/rspamd_imap_move_to_junk.txt' '0.0.0.0 143'
sleep 1 # wait for the transaction to finish
_run_in_container cat /var/log/mail/mail.log
@ -270,7 +276,7 @@ function teardown_file() { _default_teardown ; }
# Move an email to the "INBOX" folder from "Junk"; there should be two mails
# in the "Junk" folder, since the second email we sent during setup should
# have landed in the Junk folder already.
_nc_wrapper 'nc/rspamd_imap_move_to_inbox' '0.0.0.0 143'
_nc_wrapper 'nc/rspamd_imap_move_to_inbox.txt' '0.0.0.0 143'
sleep 1 # wait for the transaction to finish
_run_in_container cat /var/log/mail/mail.log

View file

@ -95,7 +95,7 @@ function teardown() { _default_teardown ; }
function _should_send_spam_message() {
_wait_for_smtp_port_in_container
_wait_for_tcp_port_in_container 10024 # port 10024 is for Amavis
_send_email --from 'spam@external.tld' --data 'amavis/spam'
_send_spam
}
function _should_be_received_by_amavis() {

View file

@ -207,7 +207,7 @@ function _should_have_correct_mail_headers() {
# (eg: OVERRIDE_HOSTNAME or `--hostname mail --domainname example.test`)
local EXPECTED_HOSTNAME=${3:-${EXPECTED_FQDN}}
_send_email --from 'user@external.tld' --data 'existing/user1'
_send_email --from 'user@external.tld'
_wait_for_empty_mail_queue_in_container
_count_files_in_directory_in_container '/var/mail/localhost.localdomain/user1/new/' '1'

View file

@ -21,6 +21,7 @@ function teardown() { _default_teardown ; }
# dovecot (/usr/sbin/dovecot)
# fetchmail (/usr/bin/fetchmail)
# fail2ban-server (/usr/bin/python3 /usr/bin/fail2ban-server) - Started by fail2ban-wrapper.sh
# mta-sts-daemon (/usr/bin/bin/python3 /usr/bin/mta-sts-daemon)
# postgrey (postgrey) - NOTE: This process lacks path information to match with `--full` in pgrep / pkill
# postsrsd (/usr/sbin/postsrsd) - NOTE: Also matches the wrapper: `/bin/bash /usr/local/bin/postsrsd-wrapper.sh`
# saslauthd (/usr/sbin/saslauthd) - x5 of the same process are found running (1 is a parent of 4)
@ -44,6 +45,7 @@ ENV_PROCESS_LIST=(
dovecot
fail2ban-server
fetchmail
mta-sts-daemon
opendkim
opendmarc
postgrey
@ -58,6 +60,7 @@ ENV_PROCESS_LIST=(
--env ENABLE_CLAMAV=0
--env ENABLE_FAIL2BAN=0
--env ENABLE_FETCHMAIL=0
--env ENABLE_MTA_STS=0
--env ENABLE_OPENDKIM=0
--env ENABLE_OPENDMARC=0
--env ENABLE_POSTGREY=0
@ -93,6 +96,7 @@ ENV_PROCESS_LIST=(
--env ENABLE_AMAVIS=1
--env ENABLE_FAIL2BAN=1
--env ENABLE_FETCHMAIL=1
--env ENABLE_MTA_STS=1
--env ENABLE_OPENDKIM=1
--env ENABLE_OPENDMARC=1
--env FETCHMAIL_PARALLEL=1

View file

@ -49,9 +49,9 @@ function teardown_file() {
# TODO replace with _send_email as soon as it supports DSN
# TODO ref: https://github.com/jetmore/swaks/issues/41
_nc_wrapper 'emails/nc_raw/dsn/unauthenticated'
_nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 465'
_nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 587'
_nc_wrapper 'emails/nc_raw/dsn/unauthenticated.txt'
_nc_wrapper 'emails/nc_raw/dsn/authenticated.txt' '0.0.0.0 465'
_nc_wrapper 'emails/nc_raw/dsn/authenticated.txt' '0.0.0.0 587'
_wait_for_empty_mail_queue_in_container
_run_in_container grep "${LOG_DSN}" /var/log/mail/mail.log
@ -62,7 +62,7 @@ function teardown_file() {
@test "should only send a DSN when requested from ports 465/587" {
export CONTAINER_NAME=${CONTAINER2_NAME}
_nc_wrapper 'emails/nc_raw/dsn/unauthenticated'
_nc_wrapper 'emails/nc_raw/dsn/unauthenticated.txt'
_wait_for_empty_mail_queue_in_container
# DSN requests can now only be made on ports 465 and 587,
@ -74,8 +74,8 @@ function teardown_file() {
assert_failure
# These ports are excluded via master.cf.
_nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 465'
_nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 587'
_nc_wrapper 'emails/nc_raw/dsn/authenticated.txt' '0.0.0.0 465'
_nc_wrapper 'emails/nc_raw/dsn/authenticated.txt' '0.0.0.0 587'
_wait_for_empty_mail_queue_in_container
_run_in_container grep "${LOG_DSN}" /var/log/mail/mail.log
@ -85,9 +85,9 @@ function teardown_file() {
@test "should never send a DSN" {
export CONTAINER_NAME=${CONTAINER3_NAME}
_nc_wrapper 'emails/nc_raw/dsn/unauthenticated'
_nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 465'
_nc_wrapper 'emails/nc_raw/dsn/authenticated' '0.0.0.0 587'
_nc_wrapper 'emails/nc_raw/dsn/unauthenticated.txt'
_nc_wrapper 'emails/nc_raw/dsn/authenticated.txt' '0.0.0.0 465'
_nc_wrapper 'emails/nc_raw/dsn/authenticated.txt' '0.0.0.0 587'
_wait_for_empty_mail_queue_in_container
# DSN requests are rejected regardless of origin.

View file

@ -38,7 +38,7 @@ function teardown_file() { _default_teardown ; }
@test "delivers mail to existing account" {
_wait_for_smtp_port_in_container
_send_email --data 'existing/user1' # send a test email
_send_email
# Verify delivery was successful, log line should look similar to:
# postfix/lmtp[1274]: 0EA424ABE7D9: to=<user1@localhost.localdomain>, relay=127.0.0.1[127.0.0.1]:24, delay=0.13, delays=0.07/0.01/0.01/0.05, dsn=2.0.0, status=sent (250 2.0.0 <user1@localhost.localdomain> ixPpB+Zvv2P7BAAAUi6ngw Saved)

View file

@ -26,11 +26,10 @@ function teardown_file() { _default_teardown ; }
# this test covers https://github.com/docker-mailserver/docker-mailserver/issues/681
@test "(Postfix) remove privacy details of the sender" {
_send_email \
--port 587 -tls --auth LOGIN \
--port 587 -tls --auth PLAIN \
--auth-user user1@localhost.localdomain \
--auth-password mypassword \
--data 'privacy'
assert_success
--data 'privacy.txt'
_run_until_success_or_timeout 120 _exec_in_container_bash '[[ -d /var/mail/localhost.localdomain/user1/new ]]'
assert_success

View file

@ -63,46 +63,43 @@ function setup_file() {
# TODO: Move to clamav tests (For use when ClamAV is enabled):
# _repeat_in_container_until_success_or_timeout 60 "${CONTAINER_NAME}" test -e /var/run/clamav/clamd.ctl
# _send_email --from 'virus@external.tld' --data 'amavis/virus'
# _send_email --from 'virus@external.tld' --data 'amavis/virus.txt'
# Required for 'delivers mail to existing alias':
_send_email --to alias1@localhost.localdomain --data 'existing/alias-external'
_send_email --to alias1@localhost.localdomain --header "Subject: Test Message existing-alias-external"
# Required for 'delivers mail to existing alias with recipient delimiter':
_send_email --to alias1~test@localhost.localdomain --data 'existing/alias-recipient-delimiter'
_send_email --to alias1~test@localhost.localdomain --header 'Subject: Test Message existing-alias-recipient-delimiter'
# Required for 'delivers mail to existing catchall':
_send_email --to wildcard@localdomain2.com --data 'existing/catchall-local'
_send_email --to wildcard@localdomain2.com --header 'Subject: Test Message existing-catchall-local'
# Required for 'delivers mail to regexp alias':
_send_email --to test123@localhost.localdomain --data 'existing/regexp-alias-local'
_send_email --to test123@localhost.localdomain --header 'Subject: Test Message existing-regexp-alias-local'
# Required for 'rejects mail to unknown user':
_send_email --to nouser@localhost.localdomain --data 'non-existing-user'
_send_email --expect-rejection --to nouser@localhost.localdomain
assert_failure
# Required for 'redirects mail to external aliases':
_send_email --to bounce-always@localhost.localdomain --data 'existing/regexp-alias-external'
_send_email --to alias2@localhost.localdomain --data 'existing/alias-local'
_send_email --to bounce-always@localhost.localdomain
_send_email --to alias2@localhost.localdomain
# Required for 'rejects spam':
_send_email --from 'spam@external.tld' --data 'amavis/spam'
_send_spam
# Required for 'delivers mail to existing account':
_send_email --data 'existing/user1'
assert_success
_send_email --header 'Subject: Test Message existing-user1'
_send_email --to user2@otherdomain.tld
assert_success
_send_email --to user3@localhost.localdomain
assert_success
_send_email --to added@localhost.localdomain --data 'existing/added'
assert_success
_send_email --to user1@localhost.localdomain --data 'existing/user-and-cc-local-alias'
assert_success
_send_email --data 'sieve/spam-folder'
assert_success
_send_email --to user2@otherdomain.tld --data 'sieve/pipe'
assert_success
_send_email --to added@localhost.localdomain --header 'Subject: Test Message existing-added'
_send_email \
--to user1@localhost.localdomain \
--header 'Subject: Test Message existing-user-and-cc-local-alias' \
--cc 'alias2@localhost.localdomain'
_send_email --data 'sieve/spam-folder.txt'
_send_email --to user2@otherdomain.tld --data 'sieve/pipe.txt'
_run_in_container_bash 'sendmail root < /tmp/docker-mailserver-test/emails/sendmail/root-email.txt'
assert_success
}
function _unsuccessful() {
_send_email --port 465 --auth "${1}" --auth-user "${2}" --auth-password wrongpassword
_send_email --expect-rejection --port 465 --auth "${1}" --auth-user "${2}" --auth-password wrongpassword --quit-after AUTH
assert_failure
assert_output --partial 'authentication failed'
assert_output --partial 'No authentication type succeeded'
@ -110,7 +107,6 @@ function _unsuccessful() {
function _successful() {
_send_email --port 465 --auth "${1}" --auth-user "${2}" --auth-password mypassword --quit-after AUTH
assert_success
assert_output --partial 'Authentication successful'
}
@ -277,6 +273,10 @@ function _successful() {
--protocol ESMTP \
--server mail.example.test \
--quit-after FIRST-EHLO
# Ensure the output is actually related to what we want to refute against:
assert_output --partial 'EHLO mail.external.tld'
assert_output --partial '221 2.0.0 Bye'
refute_output --partial 'SMTPUTF8'
}

View file

@ -204,12 +204,12 @@ function teardown_file() { _default_teardown ; }
run ./setup.sh -c "${CONTAINER_NAME}" quota set quota_user2 51M
assert_failure
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -E '^quota_user@example.com\:12M\$' | wc -l | grep 1"
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -c -E '^quota_user@example.com\:12M\$' | grep 1"
assert_success
run ./setup.sh -c "${CONTAINER_NAME}" quota set quota_user@example.com 26M
assert_success
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -E '^quota_user@example.com\:26M\$' | wc -l | grep 1"
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -c -E '^quota_user@example.com\:26M\$' | grep 1"
assert_success
run grep "quota_user2@example.com" "${TEST_TMP_CONFIG}/dovecot-quotas.cf"
@ -220,12 +220,12 @@ function teardown_file() { _default_teardown ; }
@test "delquota" {
run ./setup.sh -c "${CONTAINER_NAME}" quota set quota_user@example.com 12M
assert_success
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -E '^quota_user@example.com\:12M\$' | wc -l | grep 1"
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -c -E '^quota_user@example.com\:12M\$' | grep 1"
assert_success
run ./setup.sh -c "${CONTAINER_NAME}" quota del unknown@domain.com
assert_failure
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -E '^quota_user@example.com\:12M\$' | wc -l | grep 1"
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/dovecot-quotas.cf | grep -c -E '^quota_user@example.com\:12M\$' | grep 1"
assert_success
run ./setup.sh -c "${CONTAINER_NAME}" quota del quota_user@example.com
@ -260,13 +260,13 @@ function teardown_file() { _default_teardown ; }
./setup.sh -c "${CONTAINER_NAME}" relay add-domain example3.org smtp.relay.com 587
# check adding
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -e '^@example1.org\s\+\[smtp.relay1.com\]:2525' | wc -l | grep 1"
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -c -e '^@example1.org\s\+\[smtp.relay1.com\]:2525' | grep 1"
assert_success
# test default port
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -e '^@example2.org\s\+\[smtp.relay2.com\]:25' | wc -l | grep 1"
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -c -e '^@example2.org\s\+\[smtp.relay2.com\]:25' | grep 1"
assert_success
# test modifying
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -e '^@example3.org\s\+\[smtp.relay.com\]:587' | wc -l | grep 1"
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -c -e '^@example3.org\s\+\[smtp.relay.com\]:587' | grep 1"
assert_success
}
@ -276,16 +276,16 @@ function teardown_file() { _default_teardown ; }
./setup.sh -c "${CONTAINER_NAME}" relay add-auth example2.org smtp_user2 smtp_pass_new
# test adding
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-sasl-password.cf | grep -e '^@example.org\s\+smtp_user:smtp_pass' | wc -l | grep 1"
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-sasl-password.cf | grep -c -e '^@example.org\s\+smtp_user:smtp_pass' | grep 1"
assert_success
# test updating
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-sasl-password.cf | grep -e '^@example2.org\s\+smtp_user2:smtp_pass_new' | wc -l | grep 1"
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-sasl-password.cf | grep -c -e '^@example2.org\s\+smtp_user2:smtp_pass_new' | grep 1"
assert_success
}
@test "relay exclude-domain" {
./setup.sh -c "${CONTAINER_NAME}" relay exclude-domain example.org
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -e '^@example.org\s*$' | wc -l | grep 1"
run /bin/sh -c "cat ${TEST_TMP_CONFIG}/postfix-relaymap.cf | grep -c -e '^@example.org\s*$' | grep 1"
assert_success
}

View file

@ -24,12 +24,12 @@ function teardown_file() { _default_teardown ; }
}
@test 'authentication works' {
_nc_wrapper 'auth/pop3-auth' '-w 1 0.0.0.0 110'
_nc_wrapper 'auth/pop3-auth.txt' '-w 1 0.0.0.0 110'
assert_success
}
@test 'added user authentication works' {
_nc_wrapper 'auth/added-pop3-auth' '-w 1 0.0.0.0 110'
_nc_wrapper 'auth/added-pop3-auth.txt' '-w 1 0.0.0.0 110'
assert_success
}

View file

@ -21,7 +21,7 @@ function setup_file() {
function teardown_file() { _default_teardown ; }
@test '(Dovecot) LDAP RIMAP connection and authentication works' {
_nc_wrapper 'auth/imap-auth' '-w 1 0.0.0.0 143'
_nc_wrapper 'auth/imap-auth.txt' '-w 1 0.0.0.0 143'
assert_success
}
@ -31,8 +31,8 @@ function teardown_file() { _default_teardown ; }
}
@test '(SASLauthd) RIMAP SMTP authentication works' {
_send_email \
--auth LOGIN \
_send_email --expect-rejection \
--auth PLAIN \
--auth-user user1@localhost.localdomain \
--auth-password mypassword \
--quit-after AUTH
@ -41,20 +41,18 @@ function teardown_file() { _default_teardown ; }
_send_email \
--port 465 \
--auth LOGIN \
--auth PLAIN \
--auth-user user1@localhost.localdomain \
--auth-password mypassword \
--quit-after AUTH
assert_success
assert_output --partial 'Authentication successful'
_send_email \
--port 587 \
--auth LOGIN \
--auth PLAIN \
--auth-user user1@localhost.localdomain \
--auth-password mypassword \
--quit-after AUTH
assert_success
assert_output --partial 'Authentication successful'
}

View file

@ -248,7 +248,7 @@ function teardown() {
# dovecot
@test "dovecot: ldap imap connection and authentication works" {
_nc_wrapper 'auth/imap-ldap-auth' '-w 1 0.0.0.0 143'
_nc_wrapper 'auth/imap-ldap-auth.txt' '-w 1 0.0.0.0 143'
assert_success
}
@ -326,25 +326,26 @@ function teardown() {
@test "spoofing (with LDAP): rejects sender forging" {
_wait_for_smtp_port_in_container_to_respond dms-test_ldap
_send_email \
--port 465 -tlsc --auth LOGIN \
_send_email --expect-rejection \
--port 465 -tlsc --auth PLAIN \
--auth-user some.user@localhost.localdomain \
--auth-password secret \
--ehlo mail \
--from ldap@localhost.localdomain \
--data 'auth/ldap-smtp-auth-spoofed'
--data 'auth/ldap-smtp-auth-spoofed.txt'
assert_failure
assert_output --partial 'Sender address rejected: not owned by user'
}
@test "spoofing (with LDAP): accepts sending as alias" {
_send_email \
--port 465 -tlsc --auth LOGIN \
--port 465 -tlsc --auth PLAIN \
--auth-user some.user@localhost.localdomain \
--auth-password secret \
--ehlo mail \
--from postmaster@localhost.localdomain \
--to some.user@localhost.localdomain \
--data 'auth/ldap-smtp-auth-spoofed-alias'
--data 'auth/ldap-smtp-auth-spoofed-alias.txt'
assert_output --partial 'End data with'
}
@ -353,20 +354,21 @@ function teardown() {
# Template used has invalid AUTH: https://github.com/docker-mailserver/docker-mailserver/pull/3006#discussion_r1073321432
skip 'TODO: This test seems to have been broken from the start (?)'
_send_email \
--port 465 -tlsc --auth LOGIN \
_send_email --expect-rejection \
--port 465 -tlsc --auth PLAIN \
--auth-user some.user.email@localhost.localdomain \
--auth-password secret \
--ehlo mail \
--from randomspoofedaddress@localhost.localdomain \
--to some.user@localhost.localdomain \
--data 'auth/ldap-smtp-auth-spoofed-sender-with-filter-exception'
--data 'auth/ldap-smtp-auth-spoofed-sender-with-filter-exception.txt'
assert_failure
assert_output --partial 'Sender address rejected: not owned by user'
}
@test "saslauthd: ldap smtp authentication" {
_send_email \
--auth LOGIN \
_send_email --expect-rejection \
--auth PLAIN \
--auth-user some.user@localhost.localdomain \
--auth-password wrongpassword \
--quit-after AUTH
@ -379,12 +381,11 @@ function teardown() {
--auth-user some.user@localhost.localdomain \
--auth-password secret \
--quit-after AUTH
assert_success
assert_output --partial 'Authentication successful'
_send_email \
--port 587 -tls \
--auth LOGIN \
--auth PLAIN \
--auth-user some.user@localhost.localdomain \
--auth-password secret \
--quit-after AUTH

View file

@ -0,0 +1,66 @@
load "${REPOSITORY_ROOT}/test/helper/setup"
load "${REPOSITORY_ROOT}/test/helper/common"
BATS_TEST_NAME_PREFIX='[OAuth2] '
CONTAINER1_NAME='dms-test_oauth2'
CONTAINER2_NAME='dms-test_oauth2_provider'
function setup_file() {
export DMS_TEST_NETWORK='test-network-oauth2'
export DMS_DOMAIN='example.test'
export FQDN_MAIL="mail.${DMS_DOMAIN}"
export FQDN_OAUTH2="oauth2.${DMS_DOMAIN}"
# Link the test containers to separate network:
# NOTE: If the network already exists, test will fail to start.
docker network create "${DMS_TEST_NETWORK}"
# Setup local oauth2 provider service:
docker run --rm -d --name "${CONTAINER2_NAME}" \
--hostname "${FQDN_OAUTH2}" \
--network "${DMS_TEST_NETWORK}" \
--volume "${REPOSITORY_ROOT}/test/config/oauth2/:/app/" \
docker.io/library/python:latest \
python /app/provider.py
_run_until_success_or_timeout 20 sh -c "docker logs ${CONTAINER2_NAME} 2>&1 | grep 'Starting server'"
#
# Setup DMS container
#
# Add OAUTH2 configuration so that Dovecot can reach out to our mock provider (CONTAINER2)
local ENV_OAUTH2_CONFIG=(
--env ENABLE_OAUTH2=1
--env OAUTH2_INTROSPECTION_URL=http://oauth2.example.test/userinfo/
)
export CONTAINER_NAME=${CONTAINER1_NAME}
local CUSTOM_SETUP_ARGUMENTS=(
"${ENV_OAUTH2_CONFIG[@]}"
--hostname "${FQDN_MAIL}"
--network "${DMS_TEST_NETWORK}"
)
_init_with_defaults
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_tcp_port_in_container 143
# Set default implicit container fallback for helpers:
export CONTAINER_NAME=${CONTAINER1_NAME}
}
function teardown_file() {
docker rm -f "${CONTAINER1_NAME}" "${CONTAINER2_NAME}"
docker network rm "${DMS_TEST_NETWORK}"
}
@test "oauth2: imap connect and authentication works" {
# An initial connection needs to be made first, otherwise the auth attempt fails
_run_in_container_bash 'nc -vz 0.0.0.0 143'
_nc_wrapper 'auth/imap-oauth2-auth.txt' '-w 1 0.0.0.0 143'
assert_output --partial 'Examine completed'
}

View file

@ -80,12 +80,12 @@ function teardown_file() { _default_teardown ; }
}
@test "imap: authentication works" {
_nc_wrapper 'auth/imap-auth' '-w 1 0.0.0.0 143'
_nc_wrapper 'auth/imap-auth.txt' '-w 1 0.0.0.0 143'
assert_success
}
@test "imap: added user authentication works" {
_nc_wrapper 'auth/added-imap-auth' '-w 1 0.0.0.0 143'
_nc_wrapper 'auth/added-imap-auth.txt' '-w 1 0.0.0.0 143'
assert_success
}
@ -264,7 +264,7 @@ EOF
#
@test "amavis: config overrides" {
_run_in_container_bash "grep 'Test Verification' /etc/amavis/conf.d/50-user | wc -l"
_run_in_container_bash "grep -c 'Test Verification' /etc/amavis/conf.d/50-user"
assert_success
assert_output 1
}
@ -293,13 +293,13 @@ EOF
# An authenticated user cannot use an envelope sender (MAIL FROM)
# address they do not own according to `main.cf:smtpd_sender_login_maps` lookup
_send_email \
--port 465 -tlsc --auth LOGIN \
_send_email --expect-rejection \
--port 465 -tlsc --auth PLAIN \
--auth-user added@localhost.localdomain \
--auth-password mypassword \
--ehlo mail \
--from user2@localhost.localdomain \
--data 'auth/added-smtp-auth-spoofed'
--data 'auth/added-smtp-auth-spoofed.txt'
assert_output --partial 'Sender address rejected: not owned by user'
}
@ -310,12 +310,12 @@ EOF
# to each table. Address is authorized when a result that maps to
# the DMS account is returned.
_send_email \
--port 465 -tlsc --auth LOGIN \
--port 465 -tlsc --auth PLAIN \
--auth-user user1@localhost.localdomain \
--auth-password mypassword \
--ehlo mail \
--from alias1@localhost.localdomain \
--data 'auth/added-smtp-auth-spoofed-alias'
--data 'auth/added-smtp-auth-spoofed-alias.txt'
assert_success
assert_output --partial 'End data with'
}

View file

@ -20,7 +20,7 @@ function setup_file() {
function teardown_file() { _default_teardown ; }
@test 'should successfully deliver mail' {
_send_email --data 'existing/user1'
_send_email --header 'Subject: Test Message existing-user1'
_wait_for_empty_mail_queue_in_container
# Should be successfully sent (received) by Postfix:
@ -31,7 +31,7 @@ function teardown_file() { _default_teardown ; }
# Verify successful delivery via Dovecot to `/var/mail` account by searching for the subject:
_repeat_in_container_until_success_or_timeout 20 "${CONTAINER_NAME}" grep -R \
'Subject: Test Message existing-user1.txt' \
'Subject: Test Message existing-user1' \
'/var/mail/localhost.localdomain/user1/new/'
assert_success
_should_output_number_of_lines 1