Compare commits

..

No commits in common. "master" and "v12.1.0" have entirely different histories.

351 changed files with 7825 additions and 11610 deletions

26
.all-contributorsrc Normal file
View File

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

View File

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

View File

@ -8,7 +8,6 @@ root = true
[*] [*]
charset = utf-8 charset = utf-8
end_of_line = lf end_of_line = lf
indent_size = 2
indent_style = space indent_style = space
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
@ -17,9 +16,21 @@ trim_trailing_whitespace = true
# --- Specific ---------------------------------- # --- Specific ----------------------------------
# ----------------------------------------------- # -----------------------------------------------
[{Makefile,.gitmodules}] [*.{yaml,yml,sh,bats}]
indent_size = 2
[Makefile]
indent_style = tab indent_style = tab
indent_size = 4 indent_size = 4
[*.md] [*.md]
trim_trailing_whitespace = false trim_trailing_whitespace = false
# -----------------------------------------------
# --- Git Submodules ----------------------------
# -----------------------------------------------
[{test/bats/**,test/test_helper/**}]
indent_style = none
indent_size = none
end_of_line = none

158
.gitattributes vendored
View File

@ -1,158 +0,0 @@
# Normalize line endings of all non-binary files to LF upon check-in (`git add` / `git commit`):
* text=auto
#################################################
### General ###################################
#################################################
## GENERIC
### CI + docs/mkdocs.yml
*.yml text
### Documentation (Project, Tests, Docs site)
*.md text
### TLS certs (test/files/) + DHE params (target/shared/)
*.pem text
*.pem.sha512sum text
#################################################
### Project ###################################
#################################################
## BUILD:
.dockerignore text
Dockerfile text eol=lf
Makefile
## EXAMPLE (RUNTIME):
*.env text
*.yaml text
## PROJECT
.editorconfig text export-ignore
.gitattributes text export-ignore
.gitignore text export-ignore
.gitkeep text export-ignore
.gitmodules text export-ignore
LICENSE text
## SOURCE CODE
*.sh text eol=lf
### acme.json extractor (target/bin/)
*.py text eol=lf
### Only contain scripts (glob for extensionless)
target/bin/** text eol=lf
#################################################
### Config ####################################
#################################################
## CONFIG
### Contains all text files (glob for extensionless)
target/amavis/** text
target/fetchmail/** text
target/getmail/** text
target/opendkim/** text
target/opendmarc/** text
target/postgrey/** text
target/postsrsd/** text
### Generic target/ + test/config/
*.cf text
*.conf text
### Dovecot
*.ext text
*.sieve text
### Dovecot + Rspamd
*.inc text
### Fail2Ban + Postgrey (test/config/)
*.local text
### Postfix
*.pcre text
#################################################
### Tests #####################################
#################################################
## BATS
*.bash text eol=lf
*.bats text eol=lf
## CONFIG (test/config/)
### OpenLDAP image
*.ldif text
### OpenDKIM
*.private text
KeyTable text
SigningTable text
TrustedHosts text
### Postgrey
whitelist_recipients text
## MISC
### test/config/ + test/files/
*.txt text
### test/linting/ (.ecrc.json) + test/files/ (*.acme.json):
*.json text
#################################################
### Documentation Website #####################
#################################################
## DOCUMENTATION
### docs/content/assets/
*.css text
*.png binary
*.svg text -diff
*.woff binary
### docs/overrides/
*.html text
*.ico binary
*.webp binary
#################################################
### Info # #####################################
#################################################
### WHAT IS THIS FILE?
# `.gitattributes` - Pattern-based overrides (Project specific)
# Documentation: https://git-scm.com/docs/gitattributes
#
# Travels with the project and can override the defaults from `.gitconfig`.
# This helps to enforce consistent line endings (CRLF / LF) where needed via
# patterns (_when the git client supports `.gitattributes`_).
# `.gitconfig` - Global Git defaults (Dev environment)
# Documentation: https://git-scm.com/docs/git-config
#
# Git settings `core.autocrlf` and `core.eol` can vary across dev environments.
# Those defaults can introduce subtle bugs due to incompatible line endings.
### WHY SHOULD I CARE?
# The desired result is to ensure the repo contains normalized LF line endings,
# notably avoiding unhelpful noise in diffs or issues incurred from mixed line
# endings. Storing as LF ensures no surprises for line endings during checkout.
# Additionally for checkout to the local working directory, line endings can be
# forced to CRLF or LF per file where appropriate, which ensures the files have
# compatible line endings where software expects a specific kind.
#
# Examples:
# Diffs with nothing visual changed. Line endings appear invisible.
# Tests that compare text from two sources where only line endings differ fail.
# /bin/sh with a shebang fails to run a binary at the given path due to a CRLF.
### ATTRIBUTES
# `text` normalizes the line endings of a file to LF upon commit (CRLF -> LF).
# `text=auto` sets `text` if Git doesn't consider the file as binary data.
# `eol` sets an explicit line ending to write files to the working directory.
# `core.eol` is used for any files not explicitly set with an `eol` attr value.
# `core.eol` uses the native line endings for your platform by default.
# `core.autocrlf` (if set to `true` or `input`) overrides the `core.eol` value.
# `binary` is an alias for `-text -diff`. The file won't be normalized (-text).
# `-diff` indicates to avoid creating a diff. Useful when diffs are unlikely
# to be meaningful, such as generated content (SVG, Source Maps, Lockfiles).
# `export-ignore` excludes matched files and directories during `git archive`,
# which services like Github use to create releases with archived source files.

View File

@ -1,69 +1,164 @@
name: Bug Report name: Bug Report
description: Submit a bug report to help us improve description: File a bug report
title: 'bug report: ' title: 'bug report: '
labels: labels:
- kind/bug/report - kind/bug/report
- meta/needs triage - meta/needs triage
body: body:
- type: markdown
attributes:
value: |
# Filing a report
Thank you for participating in this project and reporting a bug. Docker Mail Server (DMS) is a community-driven project, and each contribution counts. Maintainers and moderators are volunteers that need you to fill this template with accurate information in order to help you in the best and quickest way. We will have to label your request with `meta/no template - no support` if your request is sloppy and provides no way to help you correctly.
Markdown formatting can be used in almost all text fields. The description will tell you if this is not the case for a specific field.
Be as precise as possible, and if in doubt, it's best to add more information that too few.
We provide official support for all options not marked with "not officially supported" or "unsupported". When something is "not officially supported", support for these cases is dependent on specific maintainers.
---
- type: checkboxes - type: checkboxes
id: preliminary-checks id: preliminary-checks
attributes: attributes:
label: 📝 Preliminary Checks label: Preliminary Checks
description: | description: Please read these carefully.
By submitting this issue, you agree to our [Code of Conduct](https://github.com/docker-mailserver/docker-mailserver/blob/master/CODE_OF_CONDUCT.md).
options: options:
- label: I tried searching for an existing issue and followed the [debugging docs](https://docker-mailserver.github.io/docker-mailserver/latest/config/debugging/) advice, but still need assistance. - label: I checked that all ports are open and not blocked by my ISP / hosting provider.
required: true required: true
- type: textarea - label: I know that SSL errors are likely the result of a wrong setup on the user side and not caused by DMS itself. I'm confident my setup is correct.
id: what-happened required: true
- label: I searched the issue tracker but was unable to find my issue.
required: true
- label: I read the [extended documentation in general](https://docker-mailserver.github.io/docker-mailserver/latest/) but found nothing to resolve the issue.
required: true
- label: I read the [documentation on debugging](https://docker-mailserver.github.io/docker-mailserver/latest/config/debugging/), tried the proposed steps to debug the problem, but was still unable to resolve the issue.
required: true
- label: I have read this project's [Code of Conduct](https://github.com/docker-mailserver/docker-mailserver/blob/master/CODE_OF_CONDUCT.md) and I agree
required: true
- type: input
id: affected-components
attributes: attributes:
label: 👀 What Happened? label: Affected Component(s)
description: How did this differ from your expectations? description: What is affected by this bug? Please describe it with only a few words here. Detailed description can be given later.
placeholder: Although `LOG_LEVEL=debug` is set, the logs are missing debug output. placeholder: No debug output is shown.
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: steps-to-reproduce id: when-does-it-occur
attributes: attributes:
label: 👟 Reproduction Steps label: What happened and when does this occur?
description: | description: Tell us what happened. Use [fenced code blocks](https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#fenced-code-blocks) when pasting lots of text!
How did you trigger this bug? Please walk us through it step by step. placeholder: Although `LOG_LEVEL=debug` is set, I see no debug output.
Please use [fenced code blocks](https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#fenced-code-blocks) when pasting lots of text! validations:
placeholder: The easier it is for us to reproduce your issue, the sooner we can help resolve it 😉 required: true
- type: textarea
id: what-did-you-expect-to-happen
attributes:
label: What did you expect to happen?
description: Tell us what you expected.
placeholder: I expected to see debug messages.
validations:
required: true
- type: textarea
id: how-do-we-replicate-this-issue
attributes:
label: How do we replicate the issue?
description: What did you do and how can we replicate this issue?
value: |
1.
2.
3.
...
validations:
required: true
- type: input - type: input
id: mailserver-version id: mailserver-version
attributes: attributes:
label: 🐋 DMS Version label: DMS version
description: On which version (image tag) did you encounter this bug? description: On which version (image tag) did you encounter this bug?
placeholder: v12.1.0 (do not put "latest") placeholder: v10.1.2
validations:
required: true
- type: dropdown
id: operating-system
attributes:
label: What operating system is DMS running on?
options:
- Linux
- macOS (not officially supported)
- Windows (unsupported)
- Other (unsupported)
validations: validations:
required: true required: true
- type: input - type: input
id: operating-system id: operating-system-version
attributes: attributes:
label: 💻 Operating System and Architecture label: Which operating system version?
description: | placeholder: e.g. Debian 11
Which OS is your docker host running on? validations:
**NOTE:** Windows and macOS have limited support. required: true
placeholder: Debian 11 (Bullseye) x86_64, Fedora 38 ARM64 - type: dropdown
id: isa
attributes:
label: What instruction set architecture is DMS running on?
options:
- AMD64 / x86_64
- ARM64 / AArch64 (ARM v8 and newer)
- Other (unsupported)
validations:
required: true
- type: dropdown
id: container-orchestrator
attributes:
label: What container orchestration tool are you using?
options:
- Docker
- Docker Compose
- Podman (not officially supported)
- Kubernetes (not officially supported)
- Other (unsupported)
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: container-configuration-files id: important-environment-variables
attributes: attributes:
label: ⚙️ Container configuration files label: docker-compose.yml
description: | description: Show us your `docker-compose.yml` file or your equivalent `docker run` command, if applicable. This filed is formatted as YAML.
Show us the `compose.yaml` file or command that you used to run DMS (and possibly other related services).
- This field is formatted as YAML.
- If you are using Kubernetes, you can alternatively share your manifest files here.
render: yml render: yml
- type: textarea - type: textarea
id: relevant-log-output id: relevant-log-output
attributes: attributes:
label: 📜 Relevant log output label: Relevant log output
description: | description: Show us relevant log output here. You can enable debug output by setting the environment variable `LOG_LEVEL` to `debug` or `trace`. This field's contents are interpreted as pure text.
Show us relevant log output here.
- This field expects only plain text (_rendered as a fenced code block_).
- You can enable debug output by setting the environment variable `LOG_LEVEL` to `debug` or `trace`.
render: Text render: Text
- type: textarea
id: other-relevant-information
attributes:
label: Other relevant information
description: If there is more, you can tell us here.
- type: checkboxes
id: experience
attributes:
label: What level of experience do you have with Docker and mail servers?
description: >
**You are not obliged to answer this question**.
We do encourage answering though as it provides context to better assist you.
Less experienced users tend to make common mistakes, which is ok; by letting us know we can spot those more easily. If you are experienced, we can skip basic questions and save time.
options:
- label: I am inexperienced with docker
- label: I am rather experienced with docker
- label: I am inexperienced with mail servers
- label: I am rather experienced with mail servers
- label: I am uncomfortable with the CLI
- label: I am rather comfortable with the CLI
- type: input
id: form-improvements
attributes:
label: Improvements to this form?
description: If you have criticism or general feedback about this issue form, feel free to tell us so we can enhance the experience for everyone.
validations:
required: false

View File

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

View File

@ -1,10 +1,18 @@
name: Other name: Other
description: Miscellaneous questions and reports for the project (not support) description: Miscellaneous questions and reports
title: 'other: ' title: 'other: '
labels: labels:
- meta/help wanted - meta/help wanted
body: body:
- type: markdown
attributes:
value: |
Markdown formatting can be used in almost all text fields. The description will tell you if this is not the case for a specific field.
Be as precise as possible, and if in doubt, it's best to add more information that too few.
---
- type: dropdown - type: dropdown
id: subject id: subject
attributes: attributes:
@ -13,18 +21,13 @@ body:
- I would like to contribute to the project - I would like to contribute to the project
- I would like to configure a not documented mail server use case - I would like to configure a not documented mail server use case
- I would like some feedback concerning a use case - I would like some feedback concerning a use case
- Something else that requires developers attention - I have questions about TLS/SSL/STARTTLS/OpenSSL
- Other
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: description id: description
validations:
required: true
attributes: attributes:
label: Description label: Description
value: | validations:
<!--- required: true
Please do not use this form to bypass doing a proper bug report.
The issue tracker is for anything relevant to the project itself, not individual support queries.
If you don't want to fill out a bug report, please ask support questions at our community discussions page: https://github.com/orgs/docker-mailserver/discussions
-->

View File

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

View File

@ -1,9 +1,7 @@
# Description # Description
<!-- <!-- Include a summary of the change.
Include a summary of the change. Please also include relevant motivation and context. -->
Please also include relevant motivation and context.
-->
<!-- Link the issue which will be fixed (if any) here: --> <!-- Link the issue which will be fixed (if any) here: -->
Fixes # Fixes #
@ -18,12 +16,11 @@ Fixes #
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update - [ ] This change requires a documentation update
## Checklist ## Checklist:
- [ ] My code follows the style guidelines of this project - [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code - [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation (README.md or the documentation under `docs/`) - [ ] I have made corresponding changes to the documentation (README.md or the documentation under `docs/`)
- [ ] If necessary, I have added tests that prove my fix is effective or that my feature works - [ ] If necessary I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes - [ ] New and existing unit tests pass locally with my changes
- [ ] **I have added information about changes made in this PR to `CHANGELOG.md`**

87
.github/workflows/contributors.yml vendored Normal file
View File

@ -0,0 +1,87 @@
name: 'Update Contributors List'
on:
workflow_dispatch:
schedule:
- cron: 0 4 * * 0
permissions:
contents: write
pull-requests: write
statuses: write
env:
# Assign commit authorship to official Github Actions bot:
GIT_USER: github-actions[bot]
GIT_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
BRANCH_NAME: contributors-update
jobs:
add-contributors:
name: 'Add Contributors'
runs-on: ubuntu-22.04
steps:
- name: 'Checkout'
uses: actions/checkout@v3
- name: 'Checkout New Branch and Push It'
run: |
git checkout -b ${{ env.BRANCH_NAME }}
git push --force https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git HEAD:${{ env.BRANCH_NAME }}
git checkout master
# See https://github.com/marketplace/actions/auto-add-contributors for reference of the action.
#
# This action is not well documented, but it does the job for now. We pin the version in order
# to not have any issues in the future.
- name: 'Update CONTRIBUTORS.md'
uses: BobAnkh/add-contributors@v0.2.2
with:
ACCESS_TOKEN: ${{secrets.GITHUB_TOKEN}}
BRANCH: ${{ env.BRANCH_NAME }}
COMMIT_MESSAGE: 'docs: update `CONTRIBUTORS.md`'
PATH: /CONTRIBUTORS.md
CONTRIBUTOR: '## Contributors'
COLUMN_PER_ROW: 6
IMG_WIDTH: 100
FONT_SIZE: 14
AVATAR_SHAPE: round
# See https://github.com/marketplace/actions/create-pull-request for reference of the action.
- name: 'Create Pull Request'
uses: peter-evans/create-pull-request@v5.0.1
id: create-pr
with:
token: ${{ secrets.GITHUB_TOKEN }}
base: master
branch: ${{ env.BRANCH_NAME }}
title: 'docs: update `CONTRIBUTORS.md`'
commit-message: 'docs: update `CONTRIBUTORS.md`'
delete-branch: true
committer: ${{ env.GIT_USER }} <${{ env.GIT_EMAIL }}>
author: ${{ env.GIT_USER }} <${{ env.GIT_EMAIL }}>
signoff: true
body: |
Updated `CONTRIBUTORS.md` via the CI workflow: [`contributors.yml`][workflow].
[workflow]: https://github.com/docker-mailserver/docker-mailserver/blob/master/.github/workflows/contributors.yml
# See https://github.com/marketplace/actions/set-commit-status for reference of the action.
#
# GH Actions are limited when it comes to actions triggering other actions. Hence,
# this whole workflow will not trigger a `pull_request` event without a PAT. The lint
# workflow, which is required due to branch protection, is not important for this type
# of PR, so we skip it and pretend it was successful.
- name: 'Set Status for Linting Actions to Success (Skipped)'
uses: myrotvorets/set-commit-status-action@1.1.6
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
# Skipped workflows are still assigned a "success" status:
status: success
# This should be the correct commit SHA on ${{ env.BRANCH_NAME }}:
sha: ${{ steps.create-pr.outputs.pull-request-head-sha }}
# Name of status check to add/update:
context: lint
# Optional message/note we can inline to the right of the context name in the UI:
description: Lint skipped. Not relevant.

View File

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

View File

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

View File

@ -28,7 +28,7 @@ jobs:
name: 'Deploy Docs' name: 'Deploy Docs'
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: 'Check if deploy is for a `v<major>.<minor>` tag version instead of `edge`' - name: 'Check if deploy is for a `v<major>.<minor>` tag version instead of `edge`'
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
@ -59,7 +59,7 @@ jobs:
{} + {} +
- name: 'Deploy to Github Pages' - name: 'Deploy to Github Pages'
uses: peaceiris/actions-gh-pages@v4.0.0 uses: peaceiris/actions-gh-pages@v3.9.3
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
# Build directory contents to publish to the `gh-pages` branch: # Build directory contents to publish to the `gh-pages` branch:
@ -79,10 +79,10 @@ jobs:
needs: deploy needs: deploy
steps: steps:
- name: 'Checkout the tagged commit (shallow clone)' - name: 'Checkout the tagged commit (shallow clone)'
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: 'Checkout the docs deployment branch to a subdirectory' - name: 'Checkout the docs deployment branch to a subdirectory'
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
ref: gh-pages ref: gh-pages
path: gh-pages path: gh-pages
@ -115,7 +115,7 @@ jobs:
needs: add-version-to-docs needs: add-version-to-docs
steps: steps:
- name: 'Checkout the docs deployment branch' - name: 'Checkout the docs deployment branch'
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
ref: gh-pages ref: gh-pages

View File

@ -28,11 +28,11 @@ jobs:
build-cache-key: ${{ steps.derive-image-cache-key.outputs.digest }} build-cache-key: ${{ steps.derive-image-cache-key.outputs.digest }}
steps: steps:
- name: 'Checkout' - name: 'Checkout'
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
submodules: recursive submodules: recursive
# Can potentially be replaced by: `${{ hashFiles('target/**', 'Dockerfile') }}` # Can potentially be replaced by: `${{ hashFiles('target/**', 'Dockerfile', 'VERSION') }}`
# Must not be affected by file metadata changes and have a consistent sort order: # Must not be affected by file metadata changes and have a consistent sort order:
# https://docs.github.com/en/actions/learn-github-actions/expressions#hashfiles # https://docs.github.com/en/actions/learn-github-actions/expressions#hashfiles
# Keying by the relevant build context is more re-usable than a commit SHA. # Keying by the relevant build context is more re-usable than a commit SHA.
@ -40,7 +40,10 @@ jobs:
id: derive-image-cache-key id: derive-image-cache-key
shell: bash shell: bash
run: | run: |
ADDITIONAL_FILES=( 'Dockerfile' ) ADDITIONAL_FILES=(
'Dockerfile'
'VERSION'
)
# Recursively collect file paths from `target/` and pipe a list of # Recursively collect file paths from `target/` and pipe a list of
# checksums to be sorted (by hash value) and finally generate a checksum # checksums to be sorted (by hash value) and finally generate a checksum
@ -61,7 +64,7 @@ jobs:
# When full, the least accessed cache upload is evicted to free up storage. # When full, the least accessed cache upload is evicted to free up storage.
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows # https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows
- name: 'Handle Docker build layer cache' - name: 'Handle Docker build layer cache'
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: /tmp/.buildx-cache path: /tmp/.buildx-cache
key: cache-buildx-${{ steps.derive-image-cache-key.outputs.digest }} key: cache-buildx-${{ steps.derive-image-cache-key.outputs.digest }}
@ -71,16 +74,16 @@ jobs:
cache-buildx- cache-buildx-
- name: 'Set up QEMU' - name: 'Set up QEMU'
uses: docker/setup-qemu-action@v3.6.0 uses: docker/setup-qemu-action@v2.1.0
with: with:
platforms: arm64 platforms: arm64
- name: 'Set up Docker Buildx' - name: 'Set up Docker Buildx'
uses: docker/setup-buildx-action@v3.10.0 uses: docker/setup-buildx-action@v2.5.0
# NOTE: AMD64 can build within 2 minutes # NOTE: AMD64 can build within 2 minutes
- name: 'Build images' - name: 'Build images'
uses: docker/build-push-action@v6.15.0 uses: docker/build-push-action@v4.0.0
with: with:
context: . context: .
# Build at least the AMD64 image (which runs against the test suite). # Build at least the AMD64 image (which runs against the test suite).

View File

@ -17,13 +17,13 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: 'Checkout' - name: 'Checkout'
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
submodules: recursive submodules: recursive
- name: 'Prepare tags' - name: 'Prepare tags'
id: prep id: prep
uses: docker/metadata-action@v5.7.0 uses: docker/metadata-action@v4.4.0
with: with:
images: | images: |
${{ secrets.DOCKER_REPOSITORY }} ${{ secrets.DOCKER_REPOSITORY }}
@ -35,18 +35,18 @@ jobs:
type=semver,pattern={{major}}.{{minor}}.{{patch}} type=semver,pattern={{major}}.{{minor}}.{{patch}}
- name: 'Set up QEMU' - name: 'Set up QEMU'
uses: docker/setup-qemu-action@v3.6.0 uses: docker/setup-qemu-action@v2.1.0
with: with:
platforms: arm64 platforms: arm64
- name: 'Set up Docker Buildx' - name: 'Set up Docker Buildx'
uses: docker/setup-buildx-action@v3.10.0 uses: docker/setup-buildx-action@v2.5.0
# Try get the cached build layers from a prior `generic_build.yml` job. # Try get the cached build layers from a prior `generic_build.yml` job.
# NOTE: Until adopting `type=gha` scoped cache exporter (in `docker/build-push-action`), # NOTE: Until adopting `type=gha` scoped cache exporter (in `docker/build-push-action`),
# only AMD64 image is expected to be cached, ARM images will build from scratch. # only AMD64 image is expected to be cached, ARM images will build from scratch.
- name: 'Retrieve image build from build cache' - name: 'Retrieve image build from build cache'
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: /tmp/.buildx-cache path: /tmp/.buildx-cache
key: cache-buildx-${{ inputs.cache-key }} key: cache-buildx-${{ inputs.cache-key }}
@ -54,25 +54,30 @@ jobs:
cache-buildx- cache-buildx-
- name: 'Login to DockerHub' - name: 'Login to DockerHub'
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: 'Login to GitHub Container Registry' - name: 'Login to GitHub Container Registry'
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: 'Acquire the image version'
id: get-version
shell: bash
run: echo "version=$(<VERSION)" >>"${GITHUB_OUTPUT}"
- name: 'Build and publish images' - name: 'Build and publish images'
uses: docker/build-push-action@v6.15.0 uses: docker/build-push-action@v4.0.0
with: with:
context: . context: .
build-args: | build-args: |
DMS_RELEASE=${{ github.ref_type == 'tag' && github.ref_name || 'edge' }}
VCS_REVISION=${{ github.sha }} VCS_REVISION=${{ github.sha }}
VCS_VERSION=${{ steps.get-version.outputs.version }}
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
push: true push: true
tags: ${{ steps.prep.outputs.tags }} tags: ${{ steps.prep.outputs.tags }}

View File

@ -20,7 +20,7 @@ jobs:
fail-fast: false fail-fast: false
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
# Required to retrieve bats (core + extras): # Required to retrieve bats (core + extras):
submodules: recursive submodules: recursive
@ -29,7 +29,7 @@ jobs:
# This should always be a cache-hit, thus `restore-keys` fallback is not used. # This should always be a cache-hit, thus `restore-keys` fallback is not used.
# No new cache uploads should ever happen for this job. # No new cache uploads should ever happen for this job.
- name: 'Retrieve image built from build cache' - name: 'Retrieve image built from build cache'
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: /tmp/.buildx-cache path: /tmp/.buildx-cache
key: cache-buildx-${{ inputs.cache-key }} key: cache-buildx-${{ inputs.cache-key }}
@ -38,12 +38,12 @@ jobs:
# Ensures consistent BuildKit version (not coupled to Docker Engine), # Ensures consistent BuildKit version (not coupled to Docker Engine),
# and increased compatibility of the build cache vs mixing buildx drivers. # and increased compatibility of the build cache vs mixing buildx drivers.
- name: 'Set up Docker Buildx' - name: 'Set up Docker Buildx'
uses: docker/setup-buildx-action@v3.10.0 uses: docker/setup-buildx-action@v2.5.0
# Importing from the cache should create the image within approx 30 seconds: # Importing from the cache should create the image within approx 30 seconds:
# NOTE: `qemu` step is not needed as we only test for AMD64. # NOTE: `qemu` step is not needed as we only test for AMD64.
- name: 'Build AMD64 image from cache' - name: 'Build AMD64 image from cache'
uses: docker/build-push-action@v6.15.0 uses: docker/build-push-action@v4.0.0
with: with:
context: . context: .
tags: mailserver-testing:ci tags: mailserver-testing:ci

View File

@ -22,13 +22,13 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: 'Checkout' - name: 'Checkout'
uses: actions/checkout@v4 uses: actions/checkout@v3
# Get the cached build layers from the build job: # Get the cached build layers from the build job:
# This should always be a cache-hit, thus `restore-keys` fallback is not used. # This should always be a cache-hit, thus `restore-keys` fallback is not used.
# No new cache uploads should ever happen for this job. # No new cache uploads should ever happen for this job.
- name: 'Retrieve image built from build cache' - name: 'Retrieve image built from build cache'
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: /tmp/.buildx-cache path: /tmp/.buildx-cache
key: cache-buildx-${{ inputs.cache-key }} key: cache-buildx-${{ inputs.cache-key }}
@ -37,12 +37,12 @@ jobs:
# Ensures consistent BuildKit version (not coupled to Docker Engine), # Ensures consistent BuildKit version (not coupled to Docker Engine),
# and increased compatibility of the build cache vs mixing buildx drivers. # and increased compatibility of the build cache vs mixing buildx drivers.
- name: 'Set up Docker Buildx' - name: 'Set up Docker Buildx'
uses: docker/setup-buildx-action@v3.10.0 uses: docker/setup-buildx-action@v2.5.0
# Importing from the cache should create the image within approx 30 seconds: # Importing from the cache should create the image within approx 30 seconds:
# NOTE: `qemu` step is not needed as we only test for AMD64. # NOTE: `qemu` step is not needed as we only test for AMD64.
- name: 'Build AMD64 image from cache' - name: 'Build AMD64 image from cache'
uses: docker/build-push-action@v6.15.0 uses: docker/build-push-action@v4.0.0
with: with:
context: . context: .
tags: mailserver-testing:ci tags: mailserver-testing:ci
@ -55,13 +55,13 @@ jobs:
provenance: false provenance: false
- name: 'Run the Anchore Grype scan action' - name: 'Run the Anchore Grype scan action'
uses: anchore/scan-action@v6.1.0 uses: anchore/scan-action@v3.3.5
id: scan id: scan
with: with:
image: mailserver-testing:ci image: mailserver-testing:ci
fail-build: false fail-build: false
- name: 'Upload vulnerability report' - name: 'Upload vulnerability report'
uses: github/codeql-action/upload-sarif@v3 uses: github/codeql-action/upload-sarif@v2
with: with:
sarif_file: ${{ steps.scan.outputs.sarif }} sarif_file: ${{ steps.scan.outputs.sarif }}

View File

@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Close stale issues - name: Close stale issues
uses: actions/stale@v9 uses: actions/stale@v8
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 20 days-before-stale: 20

View File

@ -1,9 +1,6 @@
name: Lint name: Lint
on: on:
# A workflow that creates a PR will not trigger this workflow,
# Providing a manual trigger as a workaround
workflow_dispatch:
pull_request: pull_request:
push: push:
branches: [ master ] branches: [ master ]
@ -16,7 +13,7 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Hadolint - name: Hadolint
run: make hadolint run: make hadolint

View File

@ -1,14 +1,12 @@
name: 'Deploy :edge on Schedule' name: 'Deploy :edge on Schedule'
on: on:
workflow_dispatch:
schedule: schedule:
- cron: 0 0 * * 5 - cron: 0 0 * * 5
permissions: permissions:
contents: read contents: read
packages: write packages: write
security-events: write
jobs: jobs:
build-images: build-images:

View File

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

1
.gitignore vendored
View File

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

View File

@ -2,487 +2,10 @@
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). 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/v15.0.2...HEAD) ## [Unreleased](https://github.com/docker-mailserver/docker-mailserver/compare/v12.1.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. > **Note**: Changes and additions listed here are contained in the `:edge` image tag. These changes may not be as stable as released changes.
### Updates
- **Documentation:**
- Added a compatibility note for a Dovecot + Solr 9.8 breaking change ([#4433](https://github.com/docker-mailserver/docker-mailserver/pull/4433))
- **Internal:**
- Refactored `setup config dkim` (`open-dkim`) ([#4375](https://github.com/docker-mailserver/docker-mailserver/pull/4375))
## [v15.0.2](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v15.0.2)
### Fixes
- **Postfix**
- Avoid modifying the message body when filtering sender headers. This regression was introduced from [#4120](https://github.com/docker-mailserver/docker-mailserver/pull/4120) as part of DMS v15.0.0 ([#4429](https://github.com/docker-mailserver/docker-mailserver/pull/4429))
## [v15.0.1](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v15.0.1)
### Added
- **Internal:**
- Added the Smallstep `step` CLI command for future internal usage ([#4376](https://github.com/docker-mailserver/docker-mailserver/pull/4376))
### Fixes
- **Postfix:**
- `setup email restrict` generated configs now only prepend to `dms_smtpd_sender_restrictions` ([#4379](https://github.com/docker-mailserver/docker-mailserver/pull/4379))
- **Rspamd:**
- Change detection support now monitors all files found within the DMS _Config Volume_ Rspamd directory ([#4418](https://github.com/docker-mailserver/docker-mailserver/pull/4418))
- **Internal:**
- A permissions fix for `/var/log/mail` that was [added in DMS v15]((https://github.com/docker-mailserver/docker-mailserver/pull/4374)) no longer encounters an error when no log files are present during a container restart, such as with a `tmpfs` volume mount ([#4391](https://github.com/docker-mailserver/docker-mailserver/pull/4391))
- The DMS _State Volume_ (`/var/mail-state`) will now ensure it's file tree is accessible for services when the volume was created with missing executable bit ([#4420](https://github.com/docker-mailserver/docker-mailserver/pull/4420))
- The DMS _Config Volume_ (`/tmp/docker-mailserver`) now correctly updates permissions on container restarts ([#4417](https://github.com/docker-mailserver/docker-mailserver/pull/4417))
### Updates
- **Internal:**
- Minor improvements to `_install_utils()` in `packages.sh` ([#4376](https://github.com/docker-mailserver/docker-mailserver/pull/4376))
## [v15.0.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v15.0.0)
### Breaking
- **saslauthd** mechanism support via ENV `SASLAUTHD_MECHANISMS` with `pam`, `shadow`, `mysql` values has been removed. Only `ldap` and `rimap` remain supported ([#4259](https://github.com/docker-mailserver/docker-mailserver/pull/4259))
- **getmail6** has been refactored: ([#4156](https://github.com/docker-mailserver/docker-mailserver/pull/4156))
- The [DMS config volume](https://docker-mailserver.github.io/docker-mailserver/v15.0/config/advanced/optional-config/#volumes) now has support for `getmailrc_general.cf` for overriding [common default settings](https://docker-mailserver.github.io/docker-mailserver/v15.0/config/advanced/mail-getmail/#common-options). If you previously mounted this config file directly to `/etc/getmailrc_general` you should switch to our config volume support.
- Generated getmail configuration files no longer set the `message_log` option. Instead of individual log files per config, the [default base settings DMS configures](https://github.com/docker-mailserver/docker-mailserver/tree/v15.0.0/target/getmail/getmailrc_general) now enables `message_log_syslog`. This aligns with how other services in DMS log to syslog where it is captured in `mail.log`.
- Getmail configurations have changed location from the base of the DMS Config Volume, to the `getmail/` subdirectory. Any existing configurations **must be migrated manually.**
- **DMS v14 mistakenly** relocated the _getmail state directory_ to the _DMS Config Volume_ as a `getmail/` subdirectory.
- This has been corrected to `/var/lib/getmail` (_if you have mounted a DMS State Volume to `/var/mail-state`, `/var/lib/getmail` will be symlinked to `/var/mail-state/lib-getmail`_).
- To preserve this state when upgrading to DMS v15, **you must manually migrate `getmail/` from the _DMS Config Volume_ to `lib-getmail/` in the _DMS State Volume_.**
- `setup email delete <EMAIL ADDRESS>` now requires explicit confirmation if the mailbox data should be deleted ([#4365](https://github.com/docker-mailserver/docker-mailserver/pull/4365)).
- **Rspamd:** Removed deprecated file path check (_DMS config volume: `./rspamd-modules.conf` => `./rspamd/custom-commands.conf`_) ([#4373](https://github.com/docker-mailserver/docker-mailserver/pull/4373))
### Added
- **Internal:**
- Add password confirmation to several `setup` CLI subcommands ([#4072](https://github.com/docker-mailserver/docker-mailserver/pull/4072))
- Added a `debug getmail` subcommand to `setup` ([#4346](https://github.com/docker-mailserver/docker-mailserver/pull/4346))
### Updates
- **Internal:**
- **Removed `VERSION` file** from the repo. Releases of DMS prior to v13 (Nov 2023) would check this to detect new releases ([#3677](https://github.com/docker-mailserver/docker-mailserver/issues/3677), [#4321](https://github.com/docker-mailserver/docker-mailserver/pull/4321))
- During image build, ensure a secure connection when downloading the `fail2ban` package ([#4080](https://github.com/docker-mailserver/docker-mailserver/pull/4080))
- **Documentation:**
- Account Management and Authentication pages have been rewritten and better organized ([#4122](https://github.com/docker-mailserver/docker-mailserver/pull/4122))
- Add a caveat for `DMS_VMAIL_UID` not being compatible with `0` / root ([#4143](https://github.com/docker-mailserver/docker-mailserver/pull/4143))
- **Getmail:** ([#4156](https://github.com/docker-mailserver/docker-mailserver/pull/4156))
- Added `getmail` as a new service for `supervisor` to manage, replacing cron for periodic polling.
- IMAP/POP3 example configs added to our [`config-examples`](https://github.com/docker-mailserver/docker-mailserver/tree/v15.0.0/config-examples/getmail).
- ENV [`GETMAIL_POLL`](https://docker-mailserver.github.io/docker-mailserver/v15.0/config/environment/#getmail_poll) now supports values above 30 minutes.
- **Postfix:**
- By default opt-out from _Microsoft reactions_ for outbound mail ([#4120](https://github.com/docker-mailserver/docker-mailserver/pull/4120))
- **Rspamd:**
- Updated GTube settings and tests ([#4191](https://github.com/docker-mailserver/docker-mailserver/pull/4191))
- Updated externally installed software ([#4357](https://github.com/docker-mailserver/docker-mailserver/pull/4357)):
- `DOVECOT_COMMUNITY_REPO=1` custom image build ARG now supports the latest Dovecot [`2.4.x`](https://github.com/dovecot/core/releases/tag/2.4.0) (_DMS provides Dovecot `2.3.19` by default_)
- Dovecot FTS Xapian module (`1.7.12` => [`1.9.0`](https://github.com/grosjo/fts-xapian/releases/tag/1.9))
- `jaq` (`1.3.0` => [`2.1.0`](https://github.com/01mf02/jaq/releases/tag/v2.1.0))
- Fail2Ban (`1.0.2-2` => [`1.1.0`](https://github.com/fail2ban/fail2ban/releases/tag/1.1.0)) ([#4045](https://github.com/docker-mailserver/docker-mailserver/pull/4045))
- Rspamd (`3.8.4` => [`3.11.0`](https://github.com/rspamd/rspamd/releases/tag/3.11.0)) - Implicitly upgraded during image build, as the third-party repo lacks version pinning support.
### Fixes
- **Dovecot:**
- The logwatch `ignore.conf` now also excludes Xapian messages about pending documents ([#4060](https://github.com/docker-mailserver/docker-mailserver/pull/4060))
- `dovecot-fts-xapian` plugin was updated, fixing a regression with indexing ([#4095](https://github.com/docker-mailserver/docker-mailserver/pull/4095))
- The "dummy account" workaround for _Dovecot Quota_ feature support no longer treats the alias as a regex when checking the Dovecot UserDB ([#4222](https://github.com/docker-mailserver/docker-mailserver/pull/4222))
- **LDAP:**
- Correctly apply a compatibility fix for OAuth2 introduced in DMS `v13.3.1` which had not been applied to the actual LDAP config changes ([#4175](https://github.com/docker-mailserver/docker-mailserver/pull/4175))
- **Internal:**
- The main `mail.log` (_which is piped to stdout via `tail`_) now correctly begins from the first log line of the active container run. Previously some daemon logs and potential warnings/errors were omitted ([#4146](https://github.com/docker-mailserver/docker-mailserver/pull/4146))
- `start-mailserver.sh` removed unused `shopt -s inherit_errexit` ([#4161](https://github.com/docker-mailserver/docker-mailserver/pull/4161))
- Fixed a regression introduced in DMS v14 where `postfix-main.cf` appended `stderr` output into `/etc/postfix/main.cf`, causing Postfix startup to fail ([#4147](https://github.com/docker-mailserver/docker-mailserver/pull/4147))
- Fixed a regression introduced in DMS v14 to better support running `start-mailserver.sh` with container restarts, which now only skip calling `_setup()` ([#4323](https://github.com/docker-mailserver/docker-mailserver/pull/4323#issuecomment-2629559254), [#4374](https://github.com/docker-mailserver/docker-mailserver/pull/4374))
- The command `swaks --help` is now functional ([#4282](https://github.com/docker-mailserver/docker-mailserver/pull/4282))
- **Rspamd:**
- DKIM private key path checking is now performed only on paths that do not contain `$` ([#4201](https://github.com/docker-mailserver/docker-mailserver/pull/4201))
### CI
- Removed `CONTRIBUTORS.md`, `.all-contributorsrc`, and workflow ([#4141](https://github.com/docker-mailserver/docker-mailserver/pull/4141))
- Refactored the workflows to be more secure for generating documentation previews on PRs ([#4267](https://github.com/docker-mailserver/docker-mailserver/pull/4267), [#4264](https://github.com/docker-mailserver/docker-mailserver/pull/4264), [#4262](https://github.com/docker-mailserver/docker-mailserver/pull/4262), [#4247](https://github.com/docker-mailserver/docker-mailserver/pull/4247), [#4244](https://github.com/docker-mailserver/docker-mailserver/pull/4244))
## [v14.0.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v14.0.0)
The most noteworthy change of this release is the update of the container's base image from Debian 11 ("Bullseye") to Debian 12 ("Bookworm"). This update alone involves breaking changes and requires a careful update!
### Breaking
- **Updated base image to Debian 12** ([#3403](https://github.com/docker-mailserver/docker-mailserver/pull/3403))
- Changed the default of `DOVECOT_COMMUNITY_REPO` to `0` (disabled) - the Dovecot community repo will (for now) not be the default when building the DMS.
- While Debian 12 (Bookworm) was released in June 2023 and the latest Dovecot `2.3.21` in Sep 2023, as of Jan 2024 there is no [Dovecot community repo available for Debian 12](https://repo.dovecot.org).
- This results in the Dovecot version being downgraded from `2.3.21` (DMS v13.3) to `2.3.19`, which [may affect functionality when you've explicitly configured for these features](https://github.com/dovecot/core/blob/30cde20f63650d8dcc4c7ad45418986f03159946/NEWS#L1-L158):
- OAuth2 (_mostly regarding JWT usage, or POST requests (`introspection_mode = post`) with `client_id` + `client_secret`_).
- Lua HTTP client (_DNS related_).
- Updated packages. For an overview, [we have a review comment on the PR that introduces Debian 12](https://github.com/docker-mailserver/docker-mailserver/pull/3403#issuecomment-1694563615)
- Notable major version bump: `openssl 3`, `clamav 1`, `spamassassin 4`, `redis-server 7`.
- Notable minor version bump: `postfix 3.5.23 => 3.7.9`
- Notable minor version bump + downgrade: `dovecot 2.3.13 => 2.3.19` (_Previous release provided `2.3.21` via community repo, `2.3.19` is now the default_)
- Updates to `packages.sh`:
- Removed custom installations of Fail2Ban, getmail6 and Rspamd
- Updated packages lists and added comments for maintainability
- OpenDMARC upgrade: `v1.4.0` => `v1.4.2` ([#3841](https://github.com/docker-mailserver/docker-mailserver/pull/3841))
- Previous versions of OpenDMARC would place incoming mail from domains announcing `p=quarantaine` (_that fail the DMARC check_) into the [Postfix "hold" queue](https://www.postfix.org/QSHAPE_README.html#hold_queue) until administrative intervention.
- [OpenDMARC v1.4.2 has disabled that feature by default](https://github.com/trusteddomainproject/OpenDMARC/issues/105), but it can be enabled again by adding the setting `HoldQuarantinedMessages true` to [`/etc/opendmarc.conf`](https://github.com/docker-mailserver/docker-mailserver/blob/v13.3.1/target/opendmarc/opendmarc.conf) (_provided from DMS_).
- [Our `user-patches.sh` feature](https://docker-mailserver.github.io/docker-mailserver/latest/config/advanced/override-defaults/user-patches/) provides a convenient approach to updating that config file.
- Please let us know if you disagree with the upstream default being carried with DMS, or the value of providing alternative configuration support within DMS.
- **Postfix:**
- Postfix upgrade from 3.5 to 3.7 ([#3403](https://github.com/docker-mailserver/docker-mailserver/pull/3403))
- `compatibility_level` was raised from `2` to `3.6`
- Postfix has deprecated the usage of `whitelist` / `blacklist` in config parameters and logging in favor of `allowlist` / `denylist` and similar variations. ([#3403](https://github.com/docker-mailserver/docker-mailserver/pull/3403/files#r1306356328))
- This [may affect monitoring / analysis of logs output from Postfix](https://www.postfix.org/COMPATIBILITY_README.html#respectful_logging) that expects to match patterns on the prior terminology used.
- DMS `main.cf` has renamed `postscreen_dnsbl_whitelist_threshold` to `postscreen_dnsbl_allowlist_threshold` as part of this change.
- `smtpd_relay_restrictions` (relay policy) is now evaluated after `smtpd_recipient_restrictions` (spam policy). Previously it was evaluated before `smtpd_recipient_restrictions`. Mail to be relayed via DMS must now pass through the spam policy first.
- The TLS fingerprint policy has changed the default from MD5 to SHA256 (_DMS does not modify this Postfix parameter, but may affect any user customizations that do_).
- **Dovecot**
- The "Junk" mailbox (folder) is now referenced by it's [special-use flag `\Junk`](https://docker-mailserver.github.io/docker-mailserver/v13.3/examples/use-cases/imap-folders/) instead of an explicit mailbox. ([#3925](https://github.com/docker-mailserver/docker-mailserver/pull/3925))
- This provides compatibility for the Junk mailbox when it's folder name differs (_eg: Renamed to "Spam"_).
- Potential breakage if your deployment modifies our `spam_to_junk.sieve` sieve script (_which is created during container startup when ENV `MOVE_SPAM_TO_JUNK=1`_) that handles storing spam mail into a users "Junk" mailbox folder.
- **Removed support for Solr integration:** ([#4025](https://github.com/docker-mailserver/docker-mailserver/pull/4025))
- This was a community contributed feature for FTS (Full Text Search), the docs advise using an image that has not been maintained for over 2 years and lacks ARM64 support. Based on user engagement over the years this feature has very niche value to continue to support, thus is being removed.
- If you use Solr, support can be restored if you're willing to contribute docs for the feature that resolves the concerns raised
- **Log:**
- The format of DMS specific logs (_from our scripts, not running services_) has been changed. The new format is `<RFC 3339 TIMESTAMP> <LOG LEVEL> <LOG EVENT SRC>: <MESSAGE>` ([#4035](https://github.com/docker-mailserver/docker-mailserver/pull/4035))
- **rsyslog:**
- Debian 12 adjusted the `rsyslog` configuration for the default file template from `RSYSLOG_TraditionalFileFormat` to `RSYSLOG_FileFormat` (_upstream default since 2012_). This change may affect you if you have any monitoring / analysis of log output (_eg: `mail.log` / `docker logs`_).
- The two formats are roughly equivalent to [RFC 3164](https://www.rfc-editor.org/rfc/rfc3164)) and [RFC 5424](https://datatracker.ietf.org/doc/html/rfc5424#section-1) respectively.
- A notable difference is the change to [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html#appendix-A) timestamps (_a strict subset of ISO 8601_). The [previous non-standardized timestamp format was defined in RFC 3164](https://www.rfc-editor.org/rfc/rfc3164.html#section-4.1.2) as `Mmm dd hh:mm:ss`.
- To revert this change you can add `sedfile -i '1i module(load="builtin:omfile" template="RSYSLOG_TraditionalFileFormat")' /etc/rsyslog.conf` via [our `user-patches.sh` feature](https://docker-mailserver.github.io/docker-mailserver/v14.0/config/advanced/override-defaults/user-patches/).
- Rsyslog now creates fewer log files:
- The files `/var/log/mail/mail.{info,warn,err}` are no longer created. These files represented `/var/log/mail.log` filtered into separate priority levels. As `/var/log/mail.log` contains all mail related messages, these files (_and their rotated counterparts_) can be deleted safely.
- `/var/log/messages`, `/var/log/debug` and several other log files not relevant to DMS were configured by default by Debian previously. These are not part of the `/var/log/mail/` volume mount, so should not impact anyone.
- **Features:**
- The relay host feature was refactored ([#3845](https://github.com/docker-mailserver/docker-mailserver/pull/3845))
- The only breaking change this should introduce is with the Change Detection service (`check-for-changes.sh`).
- When credentials are configured for relays, change events that trigger the relayhost logic now reapply the relevant Postfix settings:
- `smtp_sasl_auth_enable = yes` (_SASL auth to outbound MTA connections is enabled_)
- `smtp_sasl_security_options = noanonymous` (_credentials are mandatory for outbound mail delivery_)
- `smtp_tls_security_level = encrypt` (_the outbound MTA connection must always be secure due to credentials sent_)
- **Environment Variables:**
- `SA_SPAM_SUBJECT` has been renamed into `SPAM_SUBJECT` to become anti-spam service agnostic. ([#3820](https://github.com/docker-mailserver/docker-mailserver/pull/3820))
- As this functionality is now handled in Dovecot via a Sieve script instead of the respective anti-spam service during Postfix processing, this feature will only apply to mail stored in Dovecot. If you have relied on this feature in a different context, it will no longer be available.
- Rspamd previously handled this functionality via the `rewrite_subject` action which as now been disabled by default in favor of the new approach with `SPAM_SUBJECT`.
- `SA_SPAM_SUBJECT` is now deprecated and will log a warning if used. The value is copied as a fallback to `SPAM_SUBJECT`.
- The default has changed to not prepend any prefix to the subject unless configured to do so. If you relied on the implicit prefix, you will now need to provide one explicitly.
- `undef` was previously supported as an opt-out with `SA_SPAM_SUBJECT`. This is no longer valid, the equivalent opt-out value is now an empty value (_or rather the omission of this ENV being configured_).
- The feature to include [`_SCORE_` tag](https://spamassassin.apache.org/full/4.0.x/doc/Mail_SpamAssassin_Conf.html#rewrite_header-subject-from-to-STRING) in your value to be replaced by the associated spam score is no longer available.
- **Supervisord:**
- `supervisor-app.conf` renamed to `dms-services.conf` ([#3908](https://github.com/docker-mailserver/docker-mailserver/pull/3908))
- **Rspamd:**
- The Redis history key has been changed in order to not incorporate the hostname of the container (which is desirable in Kubernetes environments) ([#3927](https://github.com/docker-mailserver/docker-mailserver/pull/3927))
- **Account Management:**
- Addresses (accounts) are now normalized to lowercase automatically and a warning is logged in case uppercase letters are supplied ([#4033](https://github.com/docker-mailserver/docker-mailserver/pull/4033))
### Added
- **Documentation:**
- A guide for configuring a public server to relay inbound and outbound mail from DMS on a private server ([#3973](https://github.com/docker-mailserver/docker-mailserver/pull/3973))
- **Environment Variables:**
- `LOGROTATE_COUNT` defines the number of files kept by logrotate ([#3907](https://github.com/docker-mailserver/docker-mailserver/pull/3907))
- The fail2ban log file is now also taken into account by `LOGROTATE_COUNT` and `LOGROTATE_INTERVAL` ([#3915](https://github.com/docker-mailserver/docker-mailserver/pull/3915), [#3919](https://github.com/docker-mailserver/docker-mailserver/pull/3919))
- **Internal:**
- Regular container restarts are now better supported. Setup scripts that ran previously will now be skipped ([#3929](https://github.com/docker-mailserver/docker-mailserver/pull/3929))
### Updates
- **Environment Variables:**
- `ONE_DIR` has been removed (legacy ENV) ([#3840](https://github.com/docker-mailserver/docker-mailserver/pull/3840))
- It's only functionality remaining was to opt-out of run-time state consolidation with `ONE_DIR=0` (_when a volume was already mounted to `/var/mail-state`_).
- **Internal:**
- Changed the Postgrey whitelist retrieved during build to [source directly from Github](https://github.com/schweikert/postgrey/blob/master/postgrey_whitelist_clients) as the list is updated more frequently than the [author publishes at their website](https://postgrey.schweikert.ch) ([#3879](https://github.com/docker-mailserver/docker-mailserver/pull/3879))
- Enable spamassassin only, when amavis is enabled too. ([#3943](https://github.com/docker-mailserver/docker-mailserver/pull/3943))
- **Tests:**
- Refactored helper methods for sending e-mails with specific `Message-ID` headers and the helpers for retrieving + filtering logs, which together help isolate logs relevant to specific mail when multiple mails have been processed within a single test. ([#3786](https://github.com/docker-mailserver/docker-mailserver/pull/3786))
- **Rspamd:**
- The `rewrite_subject` action, is now disabled by default. It has been replaced with the new `SPAM_SUBJECT` environment variable, which implements the functionality via a Sieve script instead which is anti-spam service agnostic ([#3820](https://github.com/docker-mailserver/docker-mailserver/pull/3820))
- `RSPAMD_NEURAL` was added and is disabled by default. If switched on it will enable the experimental Rspamd "Neural network" module to add a layer of analysis to spam detection ([#3833](https://github.com/docker-mailserver/docker-mailserver/pull/3833))
- The symbol weights of SPF, DKIM and DMARC have been adjusted again. Fixes a bug and includes more appropriate combinations of symbols ([#3913](https://github.com/docker-mailserver/docker-mailserver/pull/3913), [#3923](https://github.com/docker-mailserver/docker-mailserver/pull/3923))
- **Dovecot:**
- `logwatch` now filters out non-error logs related to the status of the `index-worker` process for FTS indexing. ([#4012](https://github.com/docker-mailserver/docker-mailserver/pull/4012))
- updated FTS Xapian from version 1.5.5 to 1.7.12
### Fixes
- DMS config:
- Files that are parsed line by line are now more robust to parse by detecting and fixing line-endings ([#3819](https://github.com/docker-mailserver/docker-mailserver/pull/3819))
- The override config `postfix-main.cf` now retains custom parameters intended for use with `postfix-master.cf` ([#3880](https://github.com/docker-mailserver/docker-mailserver/pull/3880))
- Variables related to Rspamd are declared as `readonly`, which would cause warnings in the log when being re-declared; we now guard against this issue ([#3837](https://github.com/docker-mailserver/docker-mailserver/pull/3837))
- Relay host feature refactored ([#3845](https://github.com/docker-mailserver/docker-mailserver/pull/3845))
- `DEFAULT_RELAY_HOST` ENV can now also use the `RELAY_USER` + `RELAY_PASSWORD` ENV for supplying credentials.
- `RELAY_HOST` ENV no longer enforces configuring outbound SMTP to require credentials. Like `DEFAULT_RELAY_HOST` it can now configure a relay where credentials are optional.
- Restarting DMS should not be required when configuring relay hosts without these ENV, but solely via `setup relay ...`, as change detection events now apply relevant Postfix setting changes for supporting credentials too.
- Rspamd configuration: Add a missing comma in `local_networks` so that all internal IP addresses are actually considered as internal ([#3862](https://github.com/docker-mailserver/docker-mailserver/pull/3862))
- Ensure correct SELinux security context labels for files and directories moved to the mail-state volume during setup ([#3890](https://github.com/docker-mailserver/docker-mailserver/pull/3890))
- Use correct environment variable for fetchmail ([#3901](https://github.com/docker-mailserver/docker-mailserver/pull/3901))
- When using `ENABLE_GETMAIL=1` the undocumented internal location `/var/lib/getmail/` usage has been removed. Only the config volume `/tmp/docker-mailserver/getmail/` location is supported when Getmail has not been configured to deliver mail to Dovecot as advised in the DMS docs ([#4018](https://github.com/docker-mailserver/docker-mailserver/pull/4018))
- Dovecot dummy accounts (_virtual alias workaround for dovecot feature `ENABLE_QUOTAS=1`_) now correctly matches the home location of the user for that alias ([#3997](https://github.com/docker-mailserver/docker-mailserver/pull/3997))
## [v13.3.1](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.3.1)
### Fixes
- **Dovecot:**
- Restrict the auth mechanisms for PassDB configs we manage (oauth2, passwd-file, ldap) ([#3812](https://github.com/docker-mailserver/docker-mailserver/pull/3812))
- Prevents misleading auth failures from attempting to authenticate against a PassDB with incompatible auth mechanisms.
- When the new OAuth2 feature was enabled, it introduced false-positives with logged auth failures which triggered Fail2Ban to ban the IP.
- **Rspamd:**
- Ensure correct ownership (`_rspamd:_rspamd`) for the Rspamd DKIM directory + files `/tmp/docker-mailserver/rspamd/dkim/` ([#3813](https://github.com/docker-mailserver/docker-mailserver/pull/3813))
## [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.
### Added
- **Documentation:**
- An example for how to bind outbound SMTP connections to a specific network interface ([#3465](https://github.com/docker-mailserver/docker-mailserver/pull/3465))
### Updates
- **Tests:**
- Revised OAuth2 test ([#3795](https://github.com/docker-mailserver/docker-mailserver/pull/3795))
- Replace `wc -l` with `grep -c` ([#3752](https://github.com/docker-mailserver/docker-mailserver/pull/3752))
- Revised testing of service process management (supervisord) to be more robust ([#3780](https://github.com/docker-mailserver/docker-mailserver/pull/3780))
- 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:**
- 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 that complete alignment is undesirable as other symbols may be added as well, which changes the overall score calculation again, see [this issue](https://github.com/docker-mailserver/docker-mailserver/issues/3690#issuecomment-1866871996)
- **Documentation:**
- Revised the SpamAssassin ENV docs to better communicate configuration and their relation to other ENV settings. ([#3756](https://github.com/docker-mailserver/docker-mailserver/pull/3756))
- Detailed how mail received is assigned a spam score by Rspamd and processed accordingly ([#3773](https://github.com/docker-mailserver/docker-mailserver/pull/3773))
### Fixes
- **Setup:**
- `setup` CLI - `setup dkim domain` now creates the keys files with the user owning the key directory ([#3783](https://github.com/docker-mailserver/docker-mailserver/pull/3783))
- **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`_).
- **NOTE:** This Postfix update also includes the new parameter [`smtpd_forbid_bare_newline_exclusions`](https://www.postfix.org/postconf.5.html#smtpd_forbid_bare_newline_exclusions) which defaults to `$mynetworks` for excluding trusted mail clients excluded from the restriction.
- With our default `PERMIT_DOCKER=none` this is not a concern.
- Presently the Docker daemon config has `user-proxy: true` enabled by default.
- On a host that can be reached by IPv6, this will route to a DMS IPv4 only container implicitly through the Docker network bridge gateway which rewrites the source address.
- If your `PERMIT_DOCKER` setting allows that gateway IP, then it is part of `$mynetworks` and this attack would not be prevented from such connections.
- If this affects your deployment, refer to [our IPv6 docs](https://docker-mailserver.github.io/docker-mailserver/v13.2/config/advanced/ipv6/) for advice on handling IPv6 correctly in Docker. Alternatively [use our `postfix-main.cf`](https://docker-mailserver.github.io/docker-mailserver/v13.2/config/advanced/override-defaults/postfix/) to set `smtpd_forbid_bare_newline_exclusions=` as empty.
### Updates
- The test suite now uses `swaks` instead of `nc`, which has multiple benefits ([#3732](https://github.com/docker-mailserver/docker-mailserver/pull/3732)):
- `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)
### Added
- **Dovecot:**
- ENV `ENABLE_IMAP` ([#3703](https://github.com/docker-mailserver/docker-mailserver/pull/3703))
- **Tests:**
- You can now use `make run-local-instance` to run a DMS image that was built locally to test changes ([#3663](https://github.com/docker-mailserver/docker-mailserver/pull/3663))
- **Internal:**
- Log a warning when update-check is enabled, but no stable release image is used ([#3684](https://github.com/docker-mailserver/docker-mailserver/pull/3684))
### Updates
- **Documentation:**
- Debugging - Raise awareness in the troubleshooting page for a common misconfiguration when deviating from our advice by using a bare domain ([#3680](https://github.com/docker-mailserver/docker-mailserver/pull/3680))
- 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))
- **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
- **Internal:**
- The container startup welcome log message now references `DMS_RELEASE` ([#3676](https://github.com/docker-mailserver/docker-mailserver/pull/3676))
- `VERSION` was incremented for prior releases to be notified of the v13.0.1 patch release ([#3676](https://github.com/docker-mailserver/docker-mailserver/pull/3676))
- `VERSION` is no longer included in the image ([#3711](https://github.com/docker-mailserver/docker-mailserver/pull/3711))
- Update-check: fix 'read' exit status ([#3688](https://github.com/docker-mailserver/docker-mailserver/pull/3688))
- `ENABLE_QUOTAS=0` no longer tries to remove non-existent config ([#3715](https://github.com/docker-mailserver/docker-mailserver/pull/3715))
- The `postgrey` service now writes logs to the supervisor directory like all other services. Previously this was `/var/log/mail/mail.log` ([#3724](https://github.com/docker-mailserver/docker-mailserver/pull/3724))
- **Rspamd:**
- Switch to official arm64 packages to avoid segfaults ([#3686](https://github.com/docker-mailserver/docker-mailserver/pull/3686))
- **CI / Automation:**
- The lint workflow can now be manually triggered by maintainers ([#3714]https://github.com/docker-mailserver/docker-mailserver/pull/3714)
## [v13.0.1](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.0.1)
This patch release fixes two bugs that Rspamd users encountered with the `v13.0.0` release. Big thanks to the those that helped to identify these issues! ❤️
### Fixed
- **Internal:**
- The update check service now queries the latest GH release for a version tag (_instead of from a `VERSION` file at the GH repo_). This should provide more reliable update notifications ([#3666](https://github.com/docker-mailserver/docker-mailserver/pull/3666))
- **Rspamd:**
- The check for correct permission on the private key when signing e-mails with DKIM was flawed. The result was that a false warning was emitted ([#3669](https://github.com/docker-mailserver/docker-mailserver/pull/3669))
- When [`RSPAMD_CHECK_AUTHENTICATED=0`][docs::env-rspamd-check-auth], DKIM signing for outbound e-mail was disabled, which is undesirable ([#3669](https://github.com/docker-mailserver/docker-mailserver/pull/3669)). **Make sure to check the documentation of [`RSPAMD_CHECK_AUTHENTICATED`][docs::env-rspamd-check-auth]**!
[docs::env-rspamd-check-auth]: https://docker-mailserver.github.io/docker-mailserver/v13.0/config/environment/#rspamd_check_authenticated
## [v13.0.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.0.0)
### Breaking
- **LDAP:**
- ENV `LDAP_SERVER_HOST`, `DOVECOT_URIS`, and `SASLAUTHD_LDAP_SERVER` will now log an error if the LDAP URI scheme is missing. Previously there was an implicit fallback to `ldap://` ([#3522](https://github.com/docker-mailserver/docker-mailserver/pull/3522))
- `ENABLE_LDAP=1` is no longer supported, please use `ACCOUNT_PROVISIONER=LDAP` ([#3507](https://github.com/docker-mailserver/docker-mailserver/pull/3507))
- **Rspamd:**
- The deprecated path for the Rspamd custom commands file (`/tmp/docker-mailserver/rspamd-modules.conf`) now prevents successful startup. The correct path is `/tmp/docker-mailserver/rspamd/custom-commands.conf`.
- **Dovecot:**
- Dovecot mail storage per account in `/var/mail` previously shared the same path for the accounts home directory ([#3335](https://github.com/docker-mailserver/docker-mailserver/pull/3335))
- The home directory now is a subdirectory `home/`. This change better supports sieve scripts.
- **NOTE:** The change has not yet been implemented for `ACCOUNT_PROVISIONER=LDAP`.
- **Postfix:**
- `/etc/postfix/master.cf` has renamed the "smtps" service to "submissions" ([#3235](https://github.com/docker-mailserver/docker-mailserver/pull/3235))
- This is the modern `/etc/services` name for port 465, aligning with the similar "submission" port 587.
- If you have configured Proxy Protocol support with a reverse proxy via `postfix-master.cf` (_as [per our docs guide](https://docker-mailserver.github.io/docker-mailserver/v13.0/examples/tutorials/mailserver-behind-proxy/)_), you will want to update `smtps` to `submissions` there.
- Postfix now defaults to supporting DSNs (_[Delivery Status Notifications](https://github.com/docker-mailserver/docker-mailserver/pull/3572#issuecomment-1751880574)_) only for authenticated users (_via ports 465 + 587_). This is a security measure to reduce spammer abuse of your DMS instance as a backscatter source. ([#3572](https://github.com/docker-mailserver/docker-mailserver/pull/3572))
- If you need to modify this change, please let us know by opening an issue / discussion.
- You can [opt out (_enable DSNs_) via the `postfix-main.cf` override support](https://docker-mailserver.github.io/docker-mailserver/v12.1/config/advanced/override-defaults/postfix/) using the contents: `smtpd_discard_ehlo_keywords =`.
- Likewise for authenticated users, the submission(s) ports (465 + 587) are configured internally via `master.cf` to keep DSNs enabled (_since authentication protects from abuse_).
If necessary, DSNs for authenticated users can be disabled via the `postfix-master.cf` override with the following contents:
```cf
submission/inet/smtpd_discard_ehlo_keywords=silent-discard,dsn
submissions/inet/smtpd_discard_ehlo_keywords=silent-discard,dsn
```
### Added
- **Features:**
- `getmail` as an alternative to `fetchmail` ([#2803](https://github.com/docker-mailserver/docker-mailserver/pull/2803))
- `setup` CLI - `setup fail2ban` gained a new `status <JAIL>` subcommand ([#3455](https://github.com/docker-mailserver/docker-mailserver/pull/3455))
- **Environment Variables:**
- `MARK_SPAM_AS_READ`. When set to `1`, marks incoming spam as "read" to avoid unwanted "new mail" notifications for junk mail ([#3489](https://github.com/docker-mailserver/docker-mailserver/pull/3489))
- `DMS_VMAIL_UID` and `DMS_VMAIL_GID` allow changing the default ID values (`5000:5000`) for the Dovecot vmail user and group ([#3550](https://github.com/docker-mailserver/docker-mailserver/pull/3550))
- `RSPAMD_CHECK_AUTHENTICATED` allows authenticated users to avoid additional security checks by Rspamd ([#3440](https://github.com/docker-mailserver/docker-mailserver/pull/3440))
- **Documentation:**
- Use-case examples / tutorials:
- iOS mail push support ([#3513](https://github.com/docker-mailserver/docker-mailserver/pull/3513))
- Guide for setting up Dovecot Authentication via Lua ([#3579](https://github.com/docker-mailserver/docker-mailserver/pull/3579))
- Guide for integrating with the Crowdsec service ([#3651](https://github.com/docker-mailserver/docker-mailserver/pull/3651))
- Debugging page:
- New compatibility section ([#3404](https://github.com/docker-mailserver/docker-mailserver/pull/3404))
- Now advises how to (re)start DMS correctly ([#3654](https://github.com/docker-mailserver/docker-mailserver/pull/3654))
- Better communicate distinction between DMS FQDN and DMS mail accounts ([#3372](https://github.com/docker-mailserver/docker-mailserver/pull/3372))
- Traefik example now includes `passthrough=true` on implicit ports ([#3568](https://github.com/docker-mailserver/docker-mailserver/pull/3568))
- Rspamd docs have received a variety of revisions ([#3318](https://github.com/docker-mailserver/docker-mailserver/pull/3318), [#3325](https://github.com/docker-mailserver/docker-mailserver/pull/3325), [#3329](https://github.com/docker-mailserver/docker-mailserver/pull/3329))
- IPv6 config examples with content tabs ([#3436](https://github.com/docker-mailserver/docker-mailserver/pull/3436))
- Mention [internet.nl](https://internet.nl/test-mail/) as another testing service ([#3445](https://github.com/docker-mailserver/docker-mailserver/pull/3445))
- `setup alias add ...` CLI help message now includes an example for aliasing to multiple recipients ([#3600](https://github.com/docker-mailserver/docker-mailserver/pull/3600))
- `SPAMASSASSIN_SPAM_TO_INBOX=1`, now emits a debug log to raise awareness that `SA_KILL` will be ignored ([#3360](https://github.com/docker-mailserver/docker-mailserver/pull/3360))
- `CLAMAV_MESSAGE_SIZE_LIMIT` now logs a warning when the value exceeds what ClamAV is capable of supporting (4GiB max scan size [#3332](https://github.com/docker-mailserver/docker-mailserver/pull/3332), 2GiB max file size [#3341](https://github.com/docker-mailserver/docker-mailserver/pull/3341))
- Added note to caution against changing `mydestination` in Postfix's `main.cf` ([#3316](https://github.com/docker-mailserver/docker-mailserver/pull/3316))
- **Internal:**
- Added a wrapper to update Postfix configuration safely ([#3484](https://github.com/docker-mailserver/docker-mailserver/pull/3484), [#3503](https://github.com/docker-mailserver/docker-mailserver/pull/3503))
- Add debug group to `packages.sh` ([#3578](https://github.com/docker-mailserver/docker-mailserver/pull/3578))
- **Tests:**
- Additional linting check for BASH syntax ([#3369](https://github.com/docker-mailserver/docker-mailserver/pull/3369))
### Updates
- **Misc:**
- Changed `setup config dkim` default key size to `2048` (`open-dkim`) ([#3508](https://github.com/docker-mailserver/docker-mailserver/pull/3508))
- **Postfix:**
- Dropped special bits from `maildrop/` and `public/` directory permissions ([#3625](https://github.com/docker-mailserver/docker-mailserver/pull/3625))
- **Rspamd:**
- Adjusted learning of ham ([#3334](https://github.com/docker-mailserver/docker-mailserver/pull/3334))
- Adjusted `antivirus.conf` ([#3331](https://github.com/docker-mailserver/docker-mailserver/pull/3331))
- `logrotate` setup + Rspamd log path + tests log helper fallback path ([#3576](https://github.com/docker-mailserver/docker-mailserver/pull/3576))
- Setup during container startup is now more resilient ([#3578](https://github.com/docker-mailserver/docker-mailserver/pull/3578))
- Changed DKIM default config location ([#3597](https://github.com/docker-mailserver/docker-mailserver/pull/3597))
- Removed the symlink for the `override.d/` directory in favor of using `cp`, integrated into the changedetector service, added a `--force` option for the Rspamd DKIM management, and provided a dedicated helper script for common ENV variables ([#3599](https://github.com/docker-mailserver/docker-mailserver/pull/3599))
- Required permissions are now verified for DKIM private key files ([#3627](https://github.com/docker-mailserver/docker-mailserver/pull/3627))
- **Documentation:**
- Documentation aligned to Compose v2 conventions, `docker-compose` command changed to `docker compose`, `docker-compose.yaml` to `compose.yaml` ([#3295](https://github.com/docker-mailserver/docker-mailserver/pull/3295))
- Restored missing edit button ([#3338](https://github.com/docker-mailserver/docker-mailserver/pull/3338))
- Complete rewrite of the IPv6 page ([#3244](https://github.com/docker-mailserver/docker-mailserver/pull/3244), [#3531](https://github.com/docker-mailserver/docker-mailserver/pull/3531))
- Complete rewrite of the "Update and Cleanup" maintenance page ([#3539](https://github.com/docker-mailserver/docker-mailserver/pull/3539), [#3583](https://github.com/docker-mailserver/docker-mailserver/pull/3583))
- Improved debugging page advice on working with logs ([#3626](https://github.com/docker-mailserver/docker-mailserver/pull/3626), [#3640](https://github.com/docker-mailserver/docker-mailserver/pull/3640))
- Clarified the default for ENV `FETCHMAIL_PARALLEL` ([#3603](https://github.com/docker-mailserver/docker-mailserver/pull/3603))
- Removed port 25 from FAQ entry for mail client ports supporting authenticated submission ([#3496](https://github.com/docker-mailserver/docker-mailserver/pull/3496))
- Updated home path in docs for Dovecot Sieve ([#3370](https://github.com/docker-mailserver/docker-mailserver/pull/3370), [#3650](https://github.com/docker-mailserver/docker-mailserver/pull/3650))
- Fixed path to `rspamd.log` ([#3585](https://github.com/docker-mailserver/docker-mailserver/pull/3585))
- "Optional Config" page now uses consistent lowercase convention for directory names ([#3629](https://github.com/docker-mailserver/docker-mailserver/pull/3629))
- `CONTRIBUTORS.md`: Removed redundant "All Contributors" section ([#3638](https://github.com/docker-mailserver/docker-mailserver/pull/3638))
- **Internal:**
- LDAP config improvements (Removed implicit `ldap://` LDAP URI scheme fallback) ([#3522](https://github.com/docker-mailserver/docker-mailserver/pull/3522))
- Changed style conventions for internal scripts ([#3361](https://github.com/docker-mailserver/docker-mailserver/pull/3361), [#3364](https://github.com/docker-mailserver/docker-mailserver/pull/3364), [#3365](https://github.com/docker-mailserver/docker-mailserver/pull/3365), [#3366](https://github.com/docker-mailserver/docker-mailserver/pull/3366), [#3368](https://github.com/docker-mailserver/docker-mailserver/pull/3368), [#3464](https://github.com/docker-mailserver/docker-mailserver/pull/3464))
- **CI / Automation:**
- `.gitattributes` now ensures files are committed with `eol=lf` ([#3527](https://github.com/docker-mailserver/docker-mailserver/pull/3527))
- Revised the GitHub issue bug report template ([#3317](https://github.com/docker-mailserver/docker-mailserver/pull/3317), [#3381](https://github.com/docker-mailserver/docker-mailserver/pull/3381), [#3435](https://github.com/docker-mailserver/docker-mailserver/pull/3435))
- Clarified that the issue tracker is not for personal support ([#3498](https://github.com/docker-mailserver/docker-mailserver/pull/3498), [#3502](https://github.com/docker-mailserver/docker-mailserver/pull/3502))
- Bumped versions of miscellaneous software (also shoutout to @dependabot) ([#3371](https://github.com/docker-mailserver/docker-mailserver/pull/3371), [#3584](https://github.com/docker-mailserver/docker-mailserver/pull/3584), [#3504](https://github.com/docker-mailserver/docker-mailserver/pull/3504), [#3516](https://github.com/docker-mailserver/docker-mailserver/pull/3516))
- **Tests:**
- Refactored LDAP tests to current conventions ([#3483](https://github.com/docker-mailserver/docker-mailserver/pull/3483))
- Changed OpenLDAP image to `bitnami/openldap` ([#3494](https://github.com/docker-mailserver/docker-mailserver/pull/3494))
- Revised LDAP config + setup ([#3514](https://github.com/docker-mailserver/docker-mailserver/pull/3514))
- Added tests for the helper function `_add_to_or_update_postfix_main()` ([#3505](https://github.com/docker-mailserver/docker-mailserver/pull/3505))
- EditorConfig Checker lint now uses a mount path to `/check` instead of `/ci` ([#3655](https://github.com/docker-mailserver/docker-mailserver/pull/3655))
### Fixed
- **Security:**
- Fixed issue with concatenating `$dmarc_milter` and `$dkim_milter` in `main.cf` ([#3380](https://github.com/docker-mailserver/docker-mailserver/pull/3380))
- Fixed Rspamd DKIM signing for inbound emails ([#3439](https://github.com/docker-mailserver/docker-mailserver/pull/3439), [#3453](https://github.com/docker-mailserver/docker-mailserver/pull/3453))
- OpenDKIM key generation is no longer broken when Rspamd is also enabled ([#3535](https://github.com/docker-mailserver/docker-mailserver/pull/3535))
- **Internal:**
- The "database" files (_for managing users and aliases_) now correctly filters within lookup query ([#3359](https://github.com/docker-mailserver/docker-mailserver/pull/3359))
- `_setup_spam_to_junk()` no longer registered when `SMTP_ONLY=1` ([#3385](https://github.com/docker-mailserver/docker-mailserver/pull/3385))
- Dovecot `fts_xapian` is now compiled from source to match the Dovecot package ABI ([#3373](https://github.com/docker-mailserver/docker-mailserver/pull/3373))
- **CI:**
- Scheduled build now have the correct permissions to run successfully ([#3345](https://github.com/docker-mailserver/docker-mailserver/pull/3345))
- **Documentation:**
- Miscellaneous spelling and wording improvements ([#3324](https://github.com/docker-mailserver/docker-mailserver/pull/3324), [#3330](https://github.com/docker-mailserver/docker-mailserver/pull/3330), [#3337](https://github.com/docker-mailserver/docker-mailserver/pull/3337), [#3339](https://github.com/docker-mailserver/docker-mailserver/pull/3339), [#3344](https://github.com/docker-mailserver/docker-mailserver/pull/3344), [#3367](https://github.com/docker-mailserver/docker-mailserver/pull/3367), [#3411](https://github.com/docker-mailserver/docker-mailserver/pull/3411), [#3443](https://github.com/docker-mailserver/docker-mailserver/pull/3443))
- **Tests:**
- Run `pgrep` within the actual container ([#3553](https://github.com/docker-mailserver/docker-mailserver/pull/3553))
- `lmtp_ip.bats` improved partial failure output ([#3552](https://github.com/docker-mailserver/docker-mailserver/pull/3552))
- Improvements to LDIF test data ([#3506](https://github.com/docker-mailserver/docker-mailserver/pull/3506))
- Normalized for `.gitattributes` + improved `eclint` coverage ([#3566](https://github.com/docker-mailserver/docker-mailserver/pull/3566))
- Fixed ShellCheck linting for BATS tests ([#3347](https://github.com/docker-mailserver/docker-mailserver/pull/3347))
## [v12.1.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v12.1.0) ## [v12.1.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v12.1.0)
### Added ### Added
@ -494,7 +17,7 @@ This patch release fixes two bugs that Rspamd users encountered with the `v13.0.
- add option to re-enable `reject_unknown_client_hostname` after #3248 ([#3255](https://github.com/docker-mailserver/docker-mailserver/pull/3255)) - add option to re-enable `reject_unknown_client_hostname` after #3248 ([#3255](https://github.com/docker-mailserver/docker-mailserver/pull/3255))
- add DKIM helper script ([#3286](https://github.com/docker-mailserver/docker-mailserver/pull/3286)) - add DKIM helper script ([#3286](https://github.com/docker-mailserver/docker-mailserver/pull/3286))
- make `policyd-spf` configurable ([#3246](https://github.com/docker-mailserver/docker-mailserver/pull/3246)) - make `policyd-spf` configurable ([#3246](https://github.com/docker-mailserver/docker-mailserver/pull/3246))
- add 'log' command to set up for Fail2Ban ([#3299](https://github.com/docker-mailserver/docker-mailserver/pull/3299)) - add 'log' command to setup for Fail2Ban ([#3299](https://github.com/docker-mailserver/docker-mailserver/pull/3299))
- `setup` command now expects accounts and aliases to be mutually exclusive ([#3270](https://github.com/docker-mailserver/docker-mailserver/pull/3270)) - `setup` command now expects accounts and aliases to be mutually exclusive ([#3270](https://github.com/docker-mailserver/docker-mailserver/pull/3270))
### Updated ### Updated
@ -683,11 +206,7 @@ Notable changes are:
### Summary ### Summary
This release features a lot of small and medium-sized changes, many related to how the image is build and tested during CI. The build now multi-stage based and requires Docker Buildkit, as the ClamAV Signatures are added via `COPY --link ...` during build-time. This release features a lot of small and medium-sized changes, many related to how the image is build and tested during CI. The build now requires Docker Buildkit as the ClamAV Signatures are added via `COPY --link ...` during build-time. Moreover, the build is now multi-stage. `ENABLE_LDAP` is now deprecated.
### Deprecated
- The environment variable `ENABLE_LDAP` is deprecated and will be removed in [13.0.0]. Use `ACCOUNT_PROVISIONER=LDAP` now.
### Added ### Added
@ -714,6 +233,10 @@ This release features a lot of small and medium-sized changes, many related to h
- **build**: adjust build arguments - **build**: adjust build arguments
- **build**: enhance build process - **build**: enhance build process
### Deprecated
- The environment variable `ENABLE_LDAP` is deprecated and will be removed in [13.0.0]. Use `ACCOUNT_PROVISIONER=LDAP` now.
### Removed ### Removed
- **configuration**: remove unnecessary configuration files - **configuration**: remove unnecessary configuration files
@ -780,8 +303,8 @@ In this release the relay-host support saw [significant internal refactoring](ht
1. **Many** minor improvements were made (cleanup & refactoring). Please refer to the section below to get an overview over all improvements. Moreover, there was a lot of cleanup in the scripts and in the tests. The documentation was adjusted accordingly. 1. **Many** minor improvements were made (cleanup & refactoring). Please refer to the section below to get an overview over all improvements. Moreover, there was a lot of cleanup in the scripts and in the tests. The documentation was adjusted accordingly.
2. New environment variables were added: 2. New environment variables were added:
1. [`CLAMAV_MESSAGE_SIZE_LIMIT`](https://docker-mailserver.github.io/docker-mailserver/v11.0/config/environment/#clamav_message_size_limit) 1. [`CLAMAV_MESSAGE_SIZE_LIMIT`](https://docker-mailserver.github.io/docker-mailserver/v11.0/config/environment/#clamav_message_size_limit)
2. [`TZ`](https://docker-mailserver.github.io/docker-mailserver/v11.0/config/environment/#tz) 2. [`TZ`](https://docker-mailserver.github.io/docker-mailserver/v11.0/config/environment/#tz)
3. SpamAssassin KAM was added with [`ENABLE_SPAMASSASSIN_KAM`](https://docker-mailserver.github.io/docker-mailserver/v11.0/config/environment/#enable_spamassassin_kam). 3. SpamAssassin KAM was added with [`ENABLE_SPAMASSASSIN_KAM`](https://docker-mailserver.github.io/docker-mailserver/v11.0/config/environment/#enable_spamassassin_kam).
4. The `fail2ban` command was reworked and can now ban IP addresses as well. 4. The `fail2ban` command was reworked and can now ban IP addresses as well.
5. There were a few small fixes, especially when it comes to bugs in scripts and service restart loops (no functionality changes, only fixes of existing functionality). When building an image from the Dockerfile - Installation of Postfix on modern Linux distributions should now always succeed. 5. There were a few small fixes, especially when it comes to bugs in scripts and service restart loops (no functionality changes, only fixes of existing functionality). When building an image from the Dockerfile - Installation of Postfix on modern Linux distributions should now always succeed.
@ -837,7 +360,8 @@ In this release the relay-host support saw [significant internal refactoring](ht
### Critical Changes ### Critical Changes
1. This release fixes a critical issue for LDAP users, installing a needed package on Debian 11 on build-time. Moreover, a race-condition was eliminated ([#2341](https://github.com/docker-mailserver/docker-mailserver/pull/2341)). 1. This release fixes a critical issue for LDAP users, installing a needed package on Debian 11
on build-time. Moreover, a race-condition was eliminated ([#2341](https://github.com/docker-mailserver/docker-mailserver/pull/2341)).
2. A resource leak in `check-for-changes.sh` was fixed ([#2401](https://github.com/docker-mailserver/docker-mailserver/pull/2401)) 2. A resource leak in `check-for-changes.sh` was fixed ([#2401](https://github.com/docker-mailserver/docker-mailserver/pull/2401))
### Other Minor Changes ### Other Minor Changes

1905
CONTRIBUTORS.md Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,18 @@
# syntax=docker.io/docker/dockerfile:1 # syntax=docker.io/docker/dockerfile:1
# This Dockerfile provides four stages: stage-base, stage-compile, stage-main and stage-final # This Dockerfile provides two stages: stage-base and stage-final
# This is in preparation for more granular stages (eg ClamAV and Fail2Ban split into their own) # This is in preparation for more granular stages (eg ClamAV and Fail2Ban split into their own)
#
# Base stage provides all packages, config, and adds scripts
#
FROM docker.io/debian:11-slim AS stage-base
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
ARG DOVECOT_COMMUNITY_REPO=0 ARG DOVECOT_COMMUNITY_REPO=1
ARG LOG_LEVEL=trace ARG LOG_LEVEL=trace
FROM docker.io/debian:12-slim AS stage-base
ARG DEBIAN_FRONTEND
ARG DOVECOT_COMMUNITY_REPO
ARG LOG_LEVEL
SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"] SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
# ----------------------------------------------- # -----------------------------------------------
@ -25,34 +25,10 @@ RUN <<EOF
adduser --quiet --system --group --disabled-password --home /var/lib/clamav --no-create-home --uid 200 clamav adduser --quiet --system --group --disabled-password --home /var/lib/clamav --no-create-home --uid 200 clamav
EOF EOF
COPY target/scripts/build/packages.sh /build/ COPY target/scripts/build/* /build/
COPY target/scripts/helpers/log.sh /usr/local/bin/helpers/log.sh COPY target/scripts/helpers/log.sh /usr/local/bin/helpers/log.sh
RUN /bin/bash /build/packages.sh && rm -r /build RUN /bin/bash /build/packages.sh
# -----------------------------------------------
# --- Compile deb packages ----------------------
# -----------------------------------------------
FROM stage-base AS stage-compile
ARG LOG_LEVEL
ARG DEBIAN_FRONTEND
COPY target/scripts/build/compile.sh /build/
RUN /bin/bash /build/compile.sh
#
# main stage provides all packages, config, and adds scripts
#
FROM stage-base AS stage-main
ARG DEBIAN_FRONTEND
ARG LOG_LEVEL
SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
# ----------------------------------------------- # -----------------------------------------------
# --- ClamAV & FeshClam ------------------------- # --- ClamAV & FeshClam -------------------------
@ -62,12 +38,9 @@ SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
# which would require an extra memory of 500MB+ during an image build. # which would require an extra memory of 500MB+ during an image build.
# When using `COPY --link`, the `--chown` option is only compatible with numeric ID values. # When using `COPY --link`, the `--chown` option is only compatible with numeric ID values.
# hadolint ignore=DL3021 # hadolint ignore=DL3021
COPY --link --chown=200 --from=docker.io/clamav/clamav-debian:latest /var/lib/clamav /var/lib/clamav COPY --link --chown=200 --from=docker.io/clamav/clamav:latest /var/lib/clamav /var/lib/clamav
RUN <<EOF RUN <<EOF
# `COPY --link --chown=200` has a bug when built by the buildx docker-container driver.
# Restore ownership of parent dirs (Bug: https://github.com/moby/buildkit/issues/3912)
chown root:root /var /var/lib
echo '0 */6 * * * clamav /usr/bin/freshclam --quiet' >/etc/cron.d/clamav-freshclam echo '0 */6 * * * clamav /usr/bin/freshclam --quiet' >/etc/cron.d/clamav-freshclam
chmod 644 /etc/clamav/freshclam.conf chmod 644 /etc/clamav/freshclam.conf
sedfile -i 's/Foreground false/Foreground true/g' /etc/clamav/clamd.conf sedfile -i 's/Foreground false/Foreground true/g' /etc/clamav/clamd.conf
@ -80,14 +53,19 @@ EOF
# --- Dovecot ----------------------------------- # --- Dovecot -----------------------------------
# ----------------------------------------------- # -----------------------------------------------
# install fts_xapian plugin
COPY --from=stage-compile dovecot-fts-xapian-*.deb /
RUN dpkg -i /dovecot-fts-xapian-*.deb && rm /dovecot-fts-xapian-*.deb
COPY target/dovecot/*.inc target/dovecot/*.conf /etc/dovecot/conf.d/ COPY target/dovecot/*.inc target/dovecot/*.conf /etc/dovecot/conf.d/
COPY target/dovecot/dovecot-purge.cron /etc/cron.d/dovecot-purge.disabled COPY target/dovecot/dovecot-purge.cron /etc/cron.d/dovecot-purge.disabled
RUN chmod 0 /etc/cron.d/dovecot-purge.disabled RUN chmod 0 /etc/cron.d/dovecot-purge.disabled
WORKDIR /usr/share/dovecot
# hadolint ignore=SC2016,SC2086,SC2069
RUN <<EOF
sedfile -i -e 's/include_try \/usr\/share\/dovecot\/protocols\.d/include_try \/etc\/dovecot\/protocols\.d/g' /etc/dovecot/dovecot.conf
sedfile -i -e 's/#mail_plugins = \$mail_plugins/mail_plugins = \$mail_plugins sieve/g' /etc/dovecot/conf.d/15-lda.conf
sedfile -i -e 's/^.*lda_mailbox_autocreate.*/lda_mailbox_autocreate = yes/g' /etc/dovecot/conf.d/15-lda.conf
sedfile -i -e 's/^.*lda_mailbox_autosubscribe.*/lda_mailbox_autosubscribe = yes/g' /etc/dovecot/conf.d/15-lda.conf
sedfile -i -e 's/^.*postmaster_address.*/postmaster_address = '${POSTMASTER_ADDRESS:="postmaster@domain.com"}'/g' /etc/dovecot/conf.d/15-lda.conf
EOF
# ----------------------------------------------- # -----------------------------------------------
# --- Rspamd ------------------------------------ # --- Rspamd ------------------------------------
@ -95,19 +73,11 @@ RUN chmod 0 /etc/cron.d/dovecot-purge.disabled
COPY target/rspamd/local.d/ /etc/rspamd/local.d/ COPY target/rspamd/local.d/ /etc/rspamd/local.d/
# -----------------------------------------------
# --- OAUTH2 ------------------------------------
# -----------------------------------------------
COPY target/dovecot/dovecot-oauth2.conf.ext /etc/dovecot
COPY target/dovecot/auth-oauth2.conf.ext /etc/dovecot/conf.d
# ----------------------------------------------- # -----------------------------------------------
# --- LDAP & SpamAssassin's Cron ---------------- # --- LDAP & SpamAssassin's Cron ----------------
# ----------------------------------------------- # -----------------------------------------------
COPY target/dovecot/dovecot-ldap.conf.ext /etc/dovecot COPY target/dovecot/dovecot-ldap.conf.ext /etc/dovecot
COPY target/dovecot/auth-ldap.conf.ext /etc/dovecot/conf.d
COPY \ COPY \
target/postfix/ldap-users.cf \ target/postfix/ldap-users.cf \
target/postfix/ldap-groups.cf \ target/postfix/ldap-groups.cf \
@ -118,8 +88,7 @@ COPY \
# hadolint ignore=SC2016 # hadolint ignore=SC2016
RUN <<EOF RUN <<EOF
# ref: https://github.com/docker-mailserver/docker-mailserver/pull/3403#discussion_r1306282387 sedfile -i -r 's/^(CRON)=0/\1=1/g' /etc/default/spamassassin
echo 'CRON=1' >/etc/default/spamassassin
sedfile -i -r 's/^\$INIT restart/supervisorctl restart amavis/g' /etc/spamassassin/sa-update-hooks.d/amavisd-new sedfile -i -r 's/^\$INIT restart/supervisorctl restart amavis/g' /etc/spamassassin/sa-update-hooks.d/amavisd-new
mkdir /etc/spamassassin/kam/ mkdir /etc/spamassassin/kam/
curl -sSfLo /etc/spamassassin/kam/kam.sa-channels.mcgrail.com.key https://mcgrail.com/downloads/kam.sa-channels.mcgrail.com.key curl -sSfLo /etc/spamassassin/kam/kam.sa-channels.mcgrail.com.key https://mcgrail.com/downloads/kam.sa-channels.mcgrail.com.key
@ -131,10 +100,12 @@ EOF
COPY target/postsrsd/postsrsd /etc/default/postsrsd COPY target/postsrsd/postsrsd /etc/default/postsrsd
COPY target/postgrey/postgrey /etc/default/postgrey COPY target/postgrey/postgrey /etc/default/postgrey
COPY target/postgrey/postgrey.init /etc/init.d/postgrey
RUN <<EOF RUN <<EOF
chmod 755 /etc/init.d/postgrey
mkdir /var/run/postgrey mkdir /var/run/postgrey
chown postgrey:postgrey /var/run/postgrey chown postgrey:postgrey /var/run/postgrey
curl -Lsfo /etc/postgrey/whitelist_clients https://raw.githubusercontent.com/schweikert/postgrey/master/postgrey_whitelist_clients curl -Lsfo /etc/postgrey/whitelist_clients https://postgrey.schweikert.ch/pub/postgrey_whitelist_clients
EOF EOF
COPY target/amavis/conf.d/* /etc/amavis/conf.d/ COPY target/amavis/conf.d/* /etc/amavis/conf.d/
@ -152,7 +123,6 @@ RUN <<EOF
EOF EOF
# overcomplication necessary for CI # overcomplication necessary for CI
# hadolint ignore=SC2086
RUN <<EOF RUN <<EOF
for _ in {1..10}; do for _ in {1..10}; do
su - amavis -c "razor-admin -create" su - amavis -c "razor-admin -create"
@ -176,6 +146,9 @@ COPY target/fail2ban/fail2ban.d/fixes.local /etc/fail2ban/fail2ban.d/fixes.local
RUN <<EOF RUN <<EOF
ln -s /var/log/mail/mail.log /var/log/mail.log ln -s /var/log/mail/mail.log /var/log/mail.log
ln -sf /var/log/mail/fail2ban.log /var/log/fail2ban.log ln -sf /var/log/mail/fail2ban.log /var/log/fail2ban.log
# disable sshd jail
rm /etc/fail2ban/jail.d/defaults-debian.conf
mkdir /var/run/fail2ban
EOF EOF
COPY target/opendkim/opendkim.conf /etc/opendkim.conf COPY target/opendkim/opendkim.conf /etc/opendkim.conf
@ -185,26 +158,15 @@ COPY target/opendmarc/opendmarc.conf /etc/opendmarc.conf
COPY target/opendmarc/default-opendmarc /etc/default/opendmarc COPY target/opendmarc/default-opendmarc /etc/default/opendmarc
COPY target/opendmarc/ignore.hosts /etc/opendmarc/ignore.hosts COPY target/opendmarc/ignore.hosts /etc/opendmarc/ignore.hosts
# -------------------------------------------------- # -----------------------------------------------
# --- postfix-mta-sts-daemon ----------------------- # --- Fetchmail, Postfix & Let'sEncrypt ---------
# -------------------------------------------------- # -----------------------------------------------
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 ---
# --------------------------------------------------
# Remove invalid URL from SPF message # Remove invalid URL from SPF message
# https://bugs.launchpad.net/spf-engine/+bug/1896912 # https://bugs.launchpad.net/spf-engine/+bug/1896912
RUN echo 'Reason_Message = Message {rejectdefer} due to: {spf}.' >>/etc/postfix-policyd-spf-python/policyd-spf.conf RUN echo 'Reason_Message = Message {rejectdefer} due to: {spf}.' >>/etc/postfix-policyd-spf-python/policyd-spf.conf
COPY target/fetchmail/fetchmailrc /etc/fetchmailrc_general COPY target/fetchmail/fetchmailrc /etc/fetchmailrc_general
COPY target/getmail/getmailrc_general /etc/getmailrc_general
COPY target/getmail/getmail-service.sh /usr/local/bin/
COPY target/postfix/main.cf target/postfix/master.cf /etc/postfix/ COPY target/postfix/main.cf target/postfix/master.cf /etc/postfix/
# DH parameters for DHE cipher suites, ffdhe4096 is the official standard 4096-bit DH params now part of TLS 1.3 # DH parameters for DHE cipher suites, ffdhe4096 is the official standard 4096-bit DH params now part of TLS 1.3
@ -253,9 +215,8 @@ RUN <<EOF
sedfile -i -e 's/^\(POLICYHELPER=\).*/\1/' /usr/sbin/invoke-rc.d sedfile -i -e 's/^\(POLICYHELPER=\).*/\1/' /usr/sbin/invoke-rc.d
# prevent syslog warning about imklog permissions # prevent syslog warning about imklog permissions
sedfile -i -e 's/^module(load=\"imklog\")/#module(load=\"imklog\")/' /etc/rsyslog.conf sedfile -i -e 's/^module(load=\"imklog\")/#module(load=\"imklog\")/' /etc/rsyslog.conf
# this change is for our alternative process manager rather than part of # prevent email when /sbin/init or init system is not existing
# a fix related to the change preceding it. sedfile -i -e 's|invoke-rc.d rsyslog rotate > /dev/null|/usr/bin/supervisorctl signal hup rsyslog >/dev/null|g' /usr/lib/rsyslog/rsyslog-rotate
echo -e '\n/usr/bin/supervisorctl signal hup rsyslog >/dev/null' >>/usr/lib/rsyslog/rsyslog-rotate
EOF EOF
# ----------------------------------------------- # -----------------------------------------------
@ -263,7 +224,6 @@ EOF
# ----------------------------------------------- # -----------------------------------------------
COPY target/logwatch/maillog.conf /etc/logwatch/conf/logfiles/maillog.conf COPY target/logwatch/maillog.conf /etc/logwatch/conf/logfiles/maillog.conf
COPY target/logwatch/ignore.conf /etc/logwatch/conf/ignore.conf
# ----------------------------------------------- # -----------------------------------------------
# --- Supervisord & Start ----------------------- # --- Supervisord & Start -----------------------
@ -283,6 +243,8 @@ RUN <<EOF
update-locale update-locale
EOF EOF
COPY VERSION /
COPY \ COPY \
target/bin/* \ target/bin/* \
target/scripts/*.sh \ target/scripts/*.sh \
@ -298,9 +260,9 @@ COPY target/scripts/startup/setup.d /usr/local/bin/setup.d
# Final stage focuses only on image config # Final stage focuses only on image config
# #
FROM stage-main AS stage-final FROM stage-base AS stage-final
ARG DMS_RELEASE=edge
ARG VCS_REVISION=unknown ARG VCS_REVISION=unknown
ARG VCS_VERSION=edge
WORKDIR / WORKDIR /
EXPOSE 25 587 143 465 993 110 995 4190 EXPOSE 25 587 143 465 993 110 995 4190
@ -324,12 +286,11 @@ LABEL org.opencontainers.image.title="docker-mailserver"
LABEL org.opencontainers.image.vendor="The Docker Mailserver Organization" LABEL org.opencontainers.image.vendor="The Docker Mailserver Organization"
LABEL org.opencontainers.image.authors="The Docker Mailserver Organization on GitHub" LABEL org.opencontainers.image.authors="The Docker Mailserver Organization on GitHub"
LABEL org.opencontainers.image.licenses="MIT" LABEL org.opencontainers.image.licenses="MIT"
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.description="A fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). Only configuration files, no SQL database."
LABEL org.opencontainers.image.url="https://github.com/docker-mailserver" 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.documentation="https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md"
LABEL org.opencontainers.image.source="https://github.com/docker-mailserver/docker-mailserver" LABEL org.opencontainers.image.source="https://github.com/docker-mailserver/docker-mailserver"
# ARG invalidates cache when it is used by a layer (implicitly affects RUN) # ARG invalidates cache when it is used by a layer (implicitly affects RUN)
# Thus to maximize cache, keep these lines last: # Thus to maximize cache, keep these lines last:
LABEL org.opencontainers.image.revision=${VCS_REVISION} LABEL org.opencontainers.image.revision=${VCS_REVISION}
LABEL org.opencontainers.image.version=${DMS_RELEASE} LABEL org.opencontainers.image.version=${VCS_VERSION}
ENV DMS_RELEASE=${DMS_RELEASE}

View File

@ -18,7 +18,11 @@ BATS_PARALLEL_JOBS ?= 2
all: lint build generate-accounts tests clean all: lint build generate-accounts tests clean
build: ALWAYS_RUN build: ALWAYS_RUN
@ docker build --tag $(IMAGE_NAME) . @ DOCKER_BUILDKIT=1 docker build \
--tag $(IMAGE_NAME) \
--build-arg VCS_VERSION=$(shell git rev-parse --short HEAD) \
--build-arg VCS_REVISION=$(shell cat VERSION) \
.
generate-accounts: ALWAYS_RUN generate-accounts: ALWAYS_RUN
@ cp test/config/templates/postfix-accounts.cf test/config/postfix-accounts.cf @ cp test/config/templates/postfix-accounts.cf test/config/postfix-accounts.cf
@ -32,22 +36,6 @@ clean: ALWAYS_RUN
-@ while read -r LINE; do [[ $${LINE} =~ test/.+ ]] && FILES+=("/mnt$${LINE#test}"); done < .gitignore ; \ -@ while read -r LINE; do [[ $${LINE} =~ test/.+ ]] && FILES+=("/mnt$${LINE#test}"); done < .gitignore ; \
docker run --rm -v "$(REPOSITORY_ROOT)/test/:/mnt" alpine ash -c "rm -rf $${FILES[@]}" docker run --rm -v "$(REPOSITORY_ROOT)/test/:/mnt" alpine ash -c "rm -rf $${FILES[@]}"
run-local-instance: ALWAYS_RUN
bash -c 'sleep 8 ; ./setup.sh email add postmaster@example.test 123' &
docker run --rm --interactive --tty --name dms-test_example \
--env OVERRIDE_HOSTNAME=mail.example.test \
--env POSTFIX_INET_PROTOCOLS=ipv4 \
--env DOVECOT_INET_PROTOCOLS=ipv4 \
--env ENABLE_CLAMAV=0 \
--env ENABLE_AMAVIS=0 \
--env ENABLE_RSPAMD=0 \
--env ENABLE_OPENDKIM=0 \
--env ENABLE_OPENDMARC=0 \
--env ENABLE_POLICYD_SPF=0 \
--env ENABLE_SPAMASSASSIN=0 \
--env LOG_LEVEL=trace \
$(IMAGE_NAME)
# ----------------------------------------------- # -----------------------------------------------
# --- Tests ------------------------------------ # --- Tests ------------------------------------
# ----------------------------------------------- # -----------------------------------------------
@ -73,14 +61,11 @@ test/%: ALWAYS_RUN
# --- Lints ------------------------------------- # --- Lints -------------------------------------
# ----------------------------------------------- # -----------------------------------------------
lint: ALWAYS_RUN eclint hadolint bashcheck shellcheck lint: ALWAYS_RUN eclint hadolint shellcheck
hadolint: ALWAYS_RUN hadolint: ALWAYS_RUN
@ ./test/linting/lint.sh hadolint @ ./test/linting/lint.sh hadolint
bashcheck: ALWAYS_RUN
@ ./test/linting/lint.sh bashcheck
shellcheck: ALWAYS_RUN shellcheck: ALWAYS_RUN
@ ./test/linting/lint.sh shellcheck @ ./test/linting/lint.sh shellcheck

View File

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

1
VERSION Normal file
View File

@ -0,0 +1 @@
12.1.0

View File

@ -1,4 +1,4 @@
# File for additional dovecot configurations. # File for additional dovecot configurations.
# For more information read https://doc.dovecot.org/configuration_manual/quick_configuration/ # For more informations read https://doc.dovecot.org/configuration_manual/quick_configuration/
#mail_max_userip_connections = 50 #mail_max_userip_connections = 50

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,8 +7,9 @@ services:
env_file: mailserver.env env_file: mailserver.env
# More information about the mail-server ports: # More information about the mail-server ports:
# https://docker-mailserver.github.io/docker-mailserver/latest/config/security/understanding-the-ports/ # https://docker-mailserver.github.io/docker-mailserver/latest/config/security/understanding-the-ports/
# To avoid conflicts with yaml base-60 float, DO NOT remove the quotation marks.
ports: ports:
- "25:25" # SMTP (explicit TLS => STARTTLS, Authentication is DISABLED => use port 465/587 instead) - "25:25" # SMTP (explicit TLS => STARTTLS)
- "143:143" # IMAP4 (explicit TLS => STARTTLS) - "143:143" # IMAP4 (explicit TLS => STARTTLS)
- "465:465" # ESMTP (implicit TLS) - "465:465" # ESMTP (implicit TLS)
- "587:587" # ESMTP (explicit TLS => STARTTLS) - "587:587" # ESMTP (explicit TLS => STARTTLS)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
--- ---
title: 'Account Management | Provisioner (LDAP)' title: 'Advanced | LDAP Authentication'
--- ---
## Introduction ## Introduction
@ -26,7 +26,7 @@ Those variables contain the LDAP lookup filters for postfix, using `%s` as the p
- Technically, there is no difference between `ALIAS` and `GROUP`, but ideally you should use `ALIAS` for personal aliases for a singular person (like `ceo@example.org`) and `GROUP` for multiple people (like `hr@example.org`). - Technically, there is no difference between `ALIAS` and `GROUP`, but ideally you should use `ALIAS` for personal aliases for a singular person (like `ceo@example.org`) and `GROUP` for multiple people (like `hr@example.org`).
- ...for outgoing email, the sender address is put through the `SENDERS` filter, and only if the authenticated user is one of the returned entries, the email can be sent. - ...for outgoing email, the sender address is put through the `SENDERS` filter, and only if the authenticated user is one of the returned entries, the email can be sent.
- This only applies if `SPOOF_PROTECTION=1`. - This only applies if `SPOOF_PROTECTION=1`.
- If the `SENDERS` filter is missing, the `USER`, `ALIAS` and `GROUP` filters will be used in a disjunction (OR). - If the `SENDERS` filter is missing, the `USER`, `ALIAS` and `GROUP` filters will be used in in a disjunction (OR).
- To for example allow users from the `admin` group to spoof any sender email address, and to force everyone else to only use their personal mailbox address for outgoing email, you can use something like this: `(|(memberOf=cn=admin,*)(mail=%s))` - To for example allow users from the `admin` group to spoof any sender email address, and to force everyone else to only use their personal mailbox address for outgoing email, you can use something like this: `(|(memberOf=cn=admin,*)(mail=%s))`
???+ example ???+ example
@ -34,6 +34,7 @@ Those variables contain the LDAP lookup filters for postfix, using `%s` as the p
A really simple `LDAP_QUERY_FILTER` configuration, using only the _user filter_ and allowing only `admin@*` to spoof any sender addresses. A really simple `LDAP_QUERY_FILTER` configuration, using only the _user filter_ and allowing only `admin@*` to spoof any sender addresses.
```yaml ```yaml
- ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER
- LDAP_START_TLS=yes - LDAP_START_TLS=yes
- ACCOUNT_PROVISIONER=LDAP - ACCOUNT_PROVISIONER=LDAP
- LDAP_SERVER_HOST=ldap.example.org - LDAP_SERVER_HOST=ldap.example.org
@ -56,7 +57,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. 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_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 behavior 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 behaviour 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: 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:
@ -214,6 +215,7 @@ The changes on the configurations necessary to work with Active Directory (**onl
- ENABLE_POSTGREY=1 - ENABLE_POSTGREY=1
# >>> Postfix LDAP Integration # >>> Postfix LDAP Integration
- ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER
- ACCOUNT_PROVISIONER=LDAP - ACCOUNT_PROVISIONER=LDAP
- LDAP_SERVER_HOST=ldap.example.org - LDAP_SERVER_HOST=ldap.example.org
- LDAP_BIND_DN=cn=admin,ou=users,dc=example,dc=org - LDAP_BIND_DN=cn=admin,ou=users,dc=example,dc=org
@ -282,6 +284,7 @@ The changes on the configurations necessary to work with Active Directory (**onl
# <<< SASL Authentication # <<< SASL Authentication
# >>> Postfix Ldap Integration # >>> Postfix Ldap Integration
- ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER
- ACCOUNT_PROVISIONER=LDAP - ACCOUNT_PROVISIONER=LDAP
- LDAP_SERVER_HOST=<yourLdapContainer/yourLdapServer> - LDAP_SERVER_HOST=<yourLdapContainer/yourLdapServer>
- LDAP_SEARCH_BASE=dc=mydomain,dc=loc - LDAP_SEARCH_BASE=dc=mydomain,dc=loc
@ -304,5 +307,5 @@ The changes on the configurations necessary to work with Active Directory (**onl
- NET_ADMIN - NET_ADMIN
``` ```
[docs-environment]: ../../environment.md [docs-environment]: ../environment.md
[docs-userpatches]: ../../advanced/override-defaults/user-patches.md [docs-userpatches]: ./override-defaults/user-patches.md

View File

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

View File

@ -16,7 +16,7 @@ The [dovecot-fts-xapian](https://github.com/grosjo/fts-xapian) plugin makes use
The indexes will be stored as a subfolder named `xapian-indexes` inside your local `mail-data` folder (_`/var/mail` internally_). With the default settings, 10GB of email data may generate around 4GB of indexed data. The indexes will be stored as a subfolder named `xapian-indexes` inside your local `mail-data` folder (_`/var/mail` internally_). With the default settings, 10GB of email data may generate around 4GB of indexed data.
While indexing is memory intensive, you can configure the plugin to limit the amount of memory consumed by the index workers. With Xapian being small and fast, this plugin is a good choice for low memory environments (2GB). While indexing is memory intensive, you can configure the plugin to limit the amount of memory consumed by the index workers. With Xapian being small and fast, this plugin is a good choice for low memory environments (2GB) as compared to Solr.
#### Setup #### Setup
@ -55,7 +55,7 @@ While indexing is memory intensive, you can configure the plugin to limit the am
adjust the settings to tune for your desired memory limits, exclude folders and enable searching text inside of attachments adjust the settings to tune for your desired memory limits, exclude folders and enable searching text inside of attachments
2. Update `compose.yaml` to load the previously created dovecot plugin config file: 2. Update `docker-compose.yml` to load the previously created dovecot plugin config file:
```yaml ```yaml
services: services:
@ -86,20 +86,20 @@ While indexing is memory intensive, you can configure the plugin to limit the am
3. Recreate containers: 3. Recreate containers:
``` ```
docker compose down docker-compose down
docker compose up -d docker-compose up -d
``` ```
4. Initialize indexing on all users for all mail: 4. Initialize indexing on all users for all mail:
``` ```
docker compose exec mailserver doveadm index -A -q \* docker-compose exec mailserver doveadm index -A -q \*
``` ```
5. Run the following command in a daily cron job: 5. Run the following command in a daily cron job:
``` ```
docker compose exec mailserver doveadm fts optimize -A docker-compose exec mailserver doveadm fts optimize -A
``` ```
Or like the [Spamassassin example][docs-faq-sa-learn-cron] shows, you can instead use `cron` from within DMS to avoid potential errors if the mail server is not running: Or like the [Spamassassin example][docs-faq-sa-learn-cron] shows, you can instead use `cron` from within DMS to avoid potential errors if the mail server is not running:
@ -108,7 +108,7 @@ While indexing is memory intensive, you can configure the plugin to limit the am
Create a _system_ cron file: Create a _system_ cron file:
```sh ```sh
# in the compose.yaml root directory # in the docker-compose.yml root directory
mkdir -p ./docker-data/dms/cron # if you didn't have this folder before mkdir -p ./docker-data/dms/cron # if you didn't have this folder before
touch ./docker-data/dms/cron/fts_xapian touch ./docker-data/dms/cron/fts_xapian
chown root:root ./docker-data/dms/cron/fts_xapian chown root:root ./docker-data/dms/cron/fts_xapian
@ -127,7 +127,7 @@ While indexing is memory intensive, you can configure the plugin to limit the am
0 4 * * * root doveadm fts optimize -A 0 4 * * * root doveadm fts optimize -A
``` ```
Then with `compose.yaml`: Then with `docker-compose.yml`:
```yaml ```yaml
services: services:
@ -137,6 +137,53 @@ While indexing is memory intensive, you can configure the plugin to limit the am
- ./docker-data/dms/cron/fts_xapian:/etc/cron.d/fts_xapian - ./docker-data/dms/cron/fts_xapian:/etc/cron.d/fts_xapian
``` ```
### Solr
The [dovecot-solr Plugin](https://wiki2.dovecot.org/Plugins/FTS/Solr) is used in conjunction with [Apache Solr](https://lucene.apache.org/solr/) running in a separate container. This is quite straightforward to setup using the following instructions.
Solr is a mature and fast indexing backend that runs on the JVM. The indexes are relatively compact compared to the size of your total email.
However, Solr also requires a fair bit of RAM. While Solr is [highly tuneable](https://solr.apache.org/guide/7_0/query-settings-in-solrconfig.html), it may require a bit of testing to get it right.
#### Setup
1. `docker-compose.yml`:
```yaml
solr:
image: lmmdock/dovecot-solr:latest
volumes:
- ./docker-data/dms/config/dovecot/solr-dovecot:/opt/solr/server/solr/dovecot
restart: always
mailserver:
depends_on:
- solr
image: ghcr.io/docker-mailserver/docker-mailserver:latest
...
volumes:
...
- ./docker-data/dms/config/dovecot/10-plugin.conf:/etc/dovecot/conf.d/10-plugin.conf:ro
...
```
2. `./docker-data/dms/config/dovecot/10-plugin.conf`:
```conf
mail_plugins = $mail_plugins fts fts_solr
plugin {
fts = solr
fts_autoindex = yes
fts_solr = url=http://solr:8983/solr/dovecot/
}
```
3. Recreate containers: `docker-compose down ; docker-compose up -d`
4. Flag all user mailbox FTS indexes as invalid, so they are rescanned on demand when they are next searched: `docker-compose exec mailserver doveadm fts rescan -A`
#### Further Discussion #### Further Discussion
See [#905](https://github.com/docker-mailserver/docker-mailserver/issues/905) See [#905](https://github.com/docker-mailserver/docker-mailserver/issues/905)

View File

@ -2,216 +2,43 @@
title: 'Advanced | IPv6' title: 'Advanced | IPv6'
--- ---
!!! bug "Ample Opportunities for Issues" ## Background
Numerous bug reports have been raised in the past about IPv6. Please make sure your setup around DMS is correct when using IPv6! If your container host supports IPv6, then DMS will automatically accept IPv6 connections by way of the docker host's IPv6. However, incoming mail will fail SPF checks because they will appear to come from the IPv4 gateway that docker is using to proxy the IPv6 connection (`172.20.0.1` is the gateway).
## IPv6 networking problems with Docker defaults This can be solved by supporting IPv6 connections all the way to the DMS container.
### What can go wrong? ## Setup steps
If your host system supports IPv6 and an `AAAA` DNS record exists to direct IPv6 traffic to DMS, you may experience issues when an IPv6 connection is made: ```diff
+++ b/serv/docker-compose.yml
@@ ... @@ services:
- The original client IP is replaced with the gateway IP of a docker network. + ipv6nat:
- Connections fail or hang. + image: robbertkl/ipv6nat
+ restart: always
+ network_mode: "host"
+ cap_add:
+ - NET_ADMIN
+ - SYS_MODULE
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock:ro
+ - /lib/modules:/lib/modules:ro
The impact of losing the real IP of the client connection can negatively affect DMS: @@ ... @@ networks:
- Users unable to login (_Fail2Ban action triggered by repeated login failures all seen as from the same internal Gateway IP_) + default:
- Mail inbound to DMS is rejected (_[SPF verification failure][gh-issue-1438-spf], IP mismatch_) + driver: bridge
- Delivery failures from [sender reputation][sender-score] being reduced (_due to [bouncing inbound mail][gh-issue-3057-bounce] from rejected IPv6 clients_) + enable_ipv6: true
- Some services may be configured to trust connecting clients within the containers subnet, which includes the Gateway IP. This can risk bypassing or relaxing security measures, such as exposing an [open relay][wikipedia-openrelay]. + ipam:
+ driver: default
### Why does this happen? + config:
+ - subnet: fd00:0123:4567::/48
When the host network receives a connection to a containers published port, it is routed to the containers internal network managed by Docker (_typically a bridge network_). + gateway: fd00:0123:4567::1
By default, the Docker daemon only assigns IPv4 addresses to containers, thus it will only accept IPv4 connections (_unless a `docker-proxy` process is listening, which the default daemon setting `userland-proxy: true` enables_). With the daemon setting `userland-proxy: true` (default), IPv6 connections from the host can also be accepted and routed to containers (_even when they only have IPv4 addresses assigned_). `userland-proxy: false` will require the container to have atleast an IPv6 address assigned.
This can be problematic for IPv6 host connections when internally the container is no longer aware of the original client IPv6 address, as it has been proxied through the IPv4 or IPv6 gateway address of it's connected network (_eg: `172.17.0.1` - Docker allocates networks from a set of [default subnets][docker-subnets]_).
This can be fixed by enabling a Docker network to assign IPv6 addresses to containers, along with some additional configuration. Alternatively you could configure the opposite to prevent IPv6 connections being made.
## Prevent IPv6 connections
- Avoiding an `AAAA` DNS record for your DMS FQDN would prevent resolving an IPv6 address to connect to.
- You can also use `userland-proxy: false`, which will fail to establish a remote connection to DMS (_provided no IPv6 address was assigned_).
!!! tip "With UFW or Firewalld"
When one of these firewall frontends are active, remote clients should fail to connect instead of being masqueraded as the docker network gateway IP. Keep in mind that this only affects remote clients, it does not affect local IPv6 connections originating within the same host.
## Enable proper IPv6 support
You can enable IPv6 support in Docker for container networks, however [compatibility concerns][docs-compat] may affect your success.
The [official Docker documentation on enabling IPv6][docker-docs-enable-ipv6] has been improving and is a good resource to reference.
Enable `ip6tables` support so that Docker will manage IPv6 networking rules as well. This will allow for IPv6 NAT to work like the existing IPv4 NAT already does for your containers, avoiding the above issue with external connections having their IP address seen as the container network gateway IP (_provided an IPv6 address is also assigned to the container_).
!!! example "Configure the following in `/etc/docker/daemon.json`"
```json
{
"ip6tables": true,
"experimental" : true,
"userland-proxy": true
}
```
- `experimental: true` is currently required for `ip6tables: true` to work.
- `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`.
Next, configure a network with an IPv6 subnet for your container with any of these examples:
???+ example "Create an IPv6 ULA subnet"
??? info "About these examples"
These examples are focused on a [IPv6 ULA subnet][wikipedia-ipv6-ula] which is suitable for most users as described in the next section.
- You may prefer a subnet size smaller than `/64` (eg: `/112`, which still provides over 65k IPv6 addresses), especially if instead configuring for an IPv6 GUA subnet.
- The network will also implicitly be assigned an IPv4 subnet (_from the Docker daemon config `default-address-pools`_).
=== "User-defined Network"
The preferred approach is with [user-defined networks][docker-docs-ipv6-create-custom] via `compose.yaml` (recommended) or CLI with `docker network create`:
=== "Compose"
Create the network in `compose.yaml` and attach a service to it:
```yaml title="compose.yaml"
services:
mailserver:
networks:
- dms-ipv6
networks:
dms-ipv6:
enable_ipv6: true
ipam:
config:
- subnet: fd00:cafe:face:feed::/64
```
??? 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`.
The Docker Compose `default` bridge is not affected by settings for the default `bridge` (aka `docker0`) in `/etc/docker/daemon.json`.
??? tip "Using the network outside of this `compose.yaml`"
To reference this network externally (_from other compose files or `docker run`_), assign the [networks `name` key in `compose.yaml`][docker-docs-network-external].
=== "CLI"
Create the network via a CLI command (_which can then be used with `docker run --network dms-ipv6`_):
```bash
docker network create --ipv6 --subnet fd00:cafe:face:feed::/64 dms-ipv6
```
Optionally reference it from one or more `compose.yaml` files:
```yaml title="compose.yaml"
services:
mailserver:
networks:
- dms-ipv6
networks:
dms-ipv6:
external: true
```
=== "Default Bridge (daemon)"
!!! warning "This approach is discouraged"
The [`bridge` network is considered legacy][docker-docs-network-bridge-legacy].
Add these two extra IPv6 settings to your daemon config. They only apply to the [default `bridge` docker network][docker-docs-ipv6-create-default] aka `docker0` (_which containers are attached to by default when using `docker run`_).
```json title="/etc/docker/daemon.json"
{
"ipv6": true,
"fixed-cidr-v6": "fd00:cafe:face:feed::/64",
}
```
Compose projects can also use this network via `network_mode`:
```yaml title="compose.yaml"
services:
mailserver:
network_mode: bridge
```
!!! danger "Do not use `2001:db8:1::/64` for your private subnet"
The `2001:db8` address prefix is [reserved for documentation][wikipedia-ipv6-reserved]. Avoid creating a subnet with this prefix.
Presently this is used in examples for Dockers IPv6 docs as a placeholder, while mixed in with private IPv4 addresses which can be misleading.
### Configuring an IPv6 subnet
If you've [configured IPv6 address pools in `/etc/docker/daemon.json`][docker-docs-ipv6-supernets], you do not need to specify a subnet explicitly. Otherwise if you're unsure what value to provide, here's a quick guide (_Tip: Prefer IPv6 ULA, it's the least hassle_):
- `fd00:cafe:face:feed::/64` is an example of a [IPv6 ULA subnet][wikipedia-ipv6-ula]. ULA addresses are akin to the [private IPv4 subnets][wikipedia-ipv4-private] you may already be familiar with. You can use that example, or choose your own ULA address. This is a good choice for getting Docker containers to their have networks support IPv6 via NAT like they already do by default with IPv4.
- IPv6 without NAT, using public address space like your server is assigned belongs to an [IPv6 GUA subnet][wikipedia-ipv6-gua].
- Typically these will be a `/64` block assigned to your host, but this varies by provider.
- These addresses do not need to publish ports of a container to another IP to be publicly reached (_thus `ip6tables: true` is not required_), you will want a firewall configured to manage which ports are accessible instead as no NAT is involved. Note that this may not be desired if the container should also be reachable via the host IPv4 public address.
- You may want to subdivide the `/64` into smaller subnets for Docker to use only portions of the `/64`. This can reduce some routing features, and [require additional setup / management via a NDP Proxy][gh-pull-3244-gua] for your public interface to know of IPv6 assignments managed by Docker and accept external traffic.
### Verify remote IP is correct
With Docker CLI or Docker Compose, run a `traefik/whoami` container with your IPv6 docker network and port 80 published. You can then send a curl request (or via address in the browser) from another host (as your remote client) with an IPv6 network, the `RemoteAddr` value returned should match your client IPv6 address.
```bash
docker run --rm -d --network dms-ipv6 -p 80:80 traefik/whoami
# On a different host, replace `2001:db8::1` with your DMS host IPv6 address
curl --max-time 5 http://[2001:db8::1]:80
``` ```
!!! warning "IPv6 gateway IP" ## Further Discussion
If instead of the remote IPv6 address, you may notice the gateway IP for the IPv6 subnet your DMS container belongs to. See [#1438][github-issue-1438]
This will happen when DMS has an IPv6 IP address assigned, for the same reason as with IPv4, `userland-proxy: true`. It indicates that your `daemon.json` has not been configured correctly or had the updated config applied for `ip6tables :true` + `experimental: true`. Make sure you used `systemctl restart docker` after updating `daemon.json`. [github-issue-1438]: https://github.com/docker-mailserver/docker-mailserver/issues/1438
!!! 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
[sender-score]: https://senderscore.org/assess/get-your-score/
[gh-issue-1438-spf]: https://github.com/docker-mailserver/docker-mailserver/issues/1438
[gh-issue-3057-bounce]: https://github.com/docker-mailserver/docker-mailserver/pull/3057#issuecomment-1416700046
[wikipedia-openrelay]: https://en.wikipedia.org/wiki/Open_mail_relay
[docs-compat]: ../debugging.md#compatibility
[gh-pull-3244-proxy]: https://github.com/docker-mailserver/docker-mailserver/pull/3244#issuecomment-1603436809
[docker-docs-enable-ipv6]: https://docs.docker.com/config/daemon/ipv6/
[docker-docs-ipv6-create-custom]: https://docs.docker.com/config/daemon/ipv6/#create-an-ipv6-network
[docker-docs-ipv6-create-default]: https://docs.docker.com/config/daemon/ipv6/#use-ipv6-for-the-default-bridge-network
[docker-docs-ipv6-supernets]: https://docs.docker.com/config/daemon/ipv6/#dynamic-ipv6-subnet-allocation
[docker-docs-network-external]: https://docs.docker.com/compose/compose-file/06-networks/#name
[docker-docs-network-compose-default]: https://docs.docker.com/compose/networking/#configure-the-default-network
[docker-docs-network-bridge-legacy]: https://docs.docker.com/network/drivers/bridge/#use-the-default-bridge-network
[wikipedia-ipv6-reserved]: https://en.wikipedia.org/wiki/IPv6_address#Documentation
[wikipedia-ipv4-private]: https://en.wikipedia.org/wiki/Private_network#Private_IPv4_addresses
[wikipedia-ipv6-ula]: https://en.wikipedia.org/wiki/Unique_local_address
[wikipedia-ipv6-gua]: https://en.wikipedia.org/wiki/IPv6#Global_addressing
[gh-pull-3244-gua]: https://github.com/docker-mailserver/docker-mailserver/pull/3244#issuecomment-1528984894
[networking-gai]: https://linux.die.net/man/5/gai.conf
[networking-gai-blog]: https://thomas-leister.de/en/lxd-prefer-ipv6-outgoing/
[networking-gai-rfc]:https://datatracker.ietf.org/doc/html/draft-ietf-v6ops-ula

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@ -1,118 +0,0 @@
---
title: 'Advanced | Email Gathering with Getmail'
---
To enable the [getmail][getmail-website] service to retrieve e-mails set the environment variable `ENABLE_GETMAIL` to `1`. Your `compose.yaml` file should include the following:
```yaml
environment:
- ENABLE_GETMAIL=1
- GETMAIL_POLL=5
```
In your DMS config volume (eg: `docker-data/dms/config/`), add a subdirectory `getmail/` for including your getmail config files (eg: `imap-example.cf`) for each remote account that you want to retrieve mail from and deliver to the mailbox of a DMS account.
The content of these config files is documented in the next section with an IMAP and POP3 example to reference.
The directory structure should look similar to this:
```txt
├── docker-data/dms/config
│   ├── dovecot.cf
│ ├── getmail
│   │ ├── getmailrc_general.cf
│   │ ├── remote-account1.cf
│   │ ├── remote-account2.cf
│   ├── postfix-accounts.cf
│   └── postfix-virtual.cf
├── docker-compose.yml
└── README.md
```
## Configuration
A detailed description of the configuration options can be found in the [online version of the manual page][getmail-docs].
### Common Options
The default options added to each `getmail` config are:
```getmailrc
[options]
verbose = 0
read_all = false
delete = false
max_messages_per_session = 500
received = false
delivered_to = false
```
The DMS integration for Getmail generates a `getmailrc` config that prepends the common options of the base config to each remote account config file (`*.cf`) found in the DMS Config Volume `getmail/` directory.
!!! tip "Change the base options"
Add your own base config as `getmail/getmailrc_general.cf` into the DMS Config Volume. It will replace the DMS defaults shown above.
??? example "IMAP Configuration"
This example will:
1. Connect to the remote IMAP server from Gmail.
2. Retrieve mail from the gmail account `alice` with password `notsecure`.
3. Store any mail retrieved from the remote mail-server into DMS for the `user1@example.com` account that DMS manages.
```getmailrc
[retriever]
type = SimpleIMAPSSLRetriever
server = imap.gmail.com
username = alice
password = notsecure
[destination]
type = MDA_external
path = /usr/lib/dovecot/deliver
allow_root_commands = true
arguments =("-d","user1@example.com")
```
??? example "POP3 Configuration"
Just like the IMAP example above, but instead via POP3 protocol if you prefer that over IMAP.
```getmailrc
[retriever]
type = SimplePOP3SSLRetriever
server = pop3.gmail.com
username = alice
password = notsecure
[destination]
type = MDA_external
path = /usr/lib/dovecot/deliver
allow_root_commands = true
arguments =("-d","user1@example.com")
```
### Polling Interval
By default the `getmail` service checks external mail accounts for new mail every 5 minutes. That polling interval is configurable via the `GETMAIL_POLL` ENV variable, with a value in minutes (_default: 5, min: 1_):
```yaml
environment:
- GETMAIL_POLL=1
```
### XOAUTH2 Authentication
It is possible to utilize the `getmail-gmail-xoauth-tokens` helper to provide authentication using `xoauth2` for [gmail (example 12)][getmail-docs-xoauth-12] or [Microsoft Office 365 (example 13)][getmail-docs-xoauth-13]
[getmail-website]: https://www.getmail6.org
[getmail-docs]: https://getmail6.org/configuration.html
[getmail-docs-xoauth-12]: https://github.com/getmail6/getmail6/blob/1f95606156231f1e074ba62a9baa64f892a92ef8/docs/getmailrc-examples#L286
[getmail-docs-xoauth-13]: https://github.com/getmail6/getmail6/blob/1f95606156231f1e074ba62a9baa64f892a92ef8/docs/getmailrc-examples#L351
## Debugging
To debug your `getmail` configurations, run this `setup debug` command:
```sh
docker exec -it dms-container-name setup debug getmail
```

View File

@ -4,24 +4,16 @@ title: 'Advanced | Email Filtering with Sieve'
## User-Defined Sieve Filters ## User-Defined Sieve Filters
!!! warning "Advice may be outdated" [Sieve](http://sieve.info/) allows to specify filtering rules for incoming emails that allow for example sorting mails into different folders depending on the title of an email.
There are global and user specific filters which are filtering the incoming emails in the following order:
This section was contributed by the community some time ago and some configuration examples may be outdated. - Global-before -> User specific -> Global-after
[Sieve][sieve-info] allows to specify filtering rules for incoming emails that allow for example sorting mails into different folders depending on the title of an email.
!!! info "Global vs User order"
There are global and user specific filters which are filtering the incoming emails in the following order:
Global-before -> User specific -> Global-after
Global filters are applied to EVERY incoming mail for EVERY email address. Global filters are applied to EVERY incoming mail for EVERY email address.
To specify a global Sieve filter provide a `docker-data/dms/config/before.dovecot.sieve` or a `docker-data/dms/config/after.dovecot.sieve` file with your filter rules.
If any filter in this filtering chain discards an incoming mail, the delivery process will stop as well and the mail will not reach any following filters(e.g. global-before stops an incoming spam mail: The mail will get discarded and a user-specific filter won't get applied.)
- To specify a global Sieve filter provide a `docker-data/dms/config/before.dovecot.sieve` or a `docker-data/dms/config/after.dovecot.sieve` file with your filter rules. To specify a user-defined Sieve filter place a `.dovecot.sieve` file into a virtual user's mail folder e.g. `/var/mail/example.com/user1/.dovecot.sieve`. If this file exists dovecot will apply the filtering rules.
- If any filter in this filtering chain discards an incoming mail, the delivery process will stop as well and the mail will not reach any following filters (e.g. global-before stops an incoming spam mail: The mail will get discarded and a user-specific filter won't get applied.)
To specify a user-defined Sieve filter place a `.dovecot.sieve` file into a virtual user's mail folder (e.g. `/var/mail/example.com/user1/home/.dovecot.sieve`). If this file exists dovecot will apply the filtering rules.
It's even possible to install a user provided Sieve filter at startup during users setup: simply include a Sieve file in the `docker-data/dms/config/` path for each user login that needs a filter. The file name provided should be in the form `<user_login>.dovecot.sieve`, so for example for `user1@example.com` you should provide a Sieve file named `docker-data/dms/config/user1@example.com.dovecot.sieve`. It's even possible to install a user provided Sieve filter at startup during users setup: simply include a Sieve file in the `docker-data/dms/config/` path for each user login that needs a filter. The file name provided should be in the form `<user_login>.dovecot.sieve`, so for example for `user1@example.com` you should provide a Sieve file named `docker-data/dms/config/user1@example.com.dovecot.sieve`.
@ -40,7 +32,6 @@ An example of a sieve filter that moves mails to a folder `INBOX/spam` depending
``` ```
!!! warning !!! warning
That folders have to exist beforehand if sieve should move them. That folders have to exist beforehand if sieve should move them.
Another example of a sieve filter that forward mails to a different address: Another example of a sieve filter that forward mails to a different address:
@ -61,96 +52,31 @@ Just forward all incoming emails and do not save them locally:
redirect "user2@not-example.com"; redirect "user2@not-example.com";
``` ```
You can also use external programs to filter or pipe (process) messages by adding executable scripts in `docker-data/dms/config/sieve-pipe` or `docker-data/dms/config/sieve-filter`. You can also use external programs to filter or pipe (process) messages by adding executable scripts in `docker-data/dms/config/sieve-pipe` or `docker-data/dms/config/sieve-filter`. This can be used in lieu of a local alias file, for instance to forward an email to a webservice. These programs can then be referenced by filename, by all users. Note that the process running the scripts run as a privileged user. For further information see [Dovecot's wiki](https://wiki.dovecot.org/Pigeonhole/Sieve/Plugins/Pipe).
This can be used in lieu of a local alias file, for instance to forward an email to a webservice.
- These programs can then be referenced by filename, by all users.
- Note that the process running the scripts run as a privileged user.
- For further information see [Dovecot's docs][dovecot-docs::sieve-pipe].
```sieve ```sieve
require ["vnd.dovecot.pipe"]; require ["vnd.dovecot.pipe"];
pipe "external-program"; pipe "external-program";
``` ```
For more examples or a detailed description of the Sieve language have a look at [the official site][sieve-info::examples]. Other resources are available on the internet where you can find several [examples][third-party::sieve-examples]. For more examples or a detailed description of the Sieve language have a look at [the official site](http://sieve.info/examplescripts). Other resources are available on the internet where you can find several [examples](https://support.tigertech.net/sieve#sieve-example-rules-jmp).
[dovecot-docs::sieve-pipe]: https://doc.dovecot.org/configuration_manual/sieve/plugins/extprograms/#pigeonhole-plugin-extprograms ## Automatic Sorting Based on Subaddresses
[sieve-info]: http://sieve.info/
[sieve-info::examples]: http://sieve.info/examplescripts
[third-party::sieve-examples]: https://support.tigertech.net/sieve#sieve-example-rules-jmp
## Automatic Sorting Based on Sub-addresses { #subaddress-mailbox-routing } It is possible to sort subaddresses such as `user+mailing-lists@example.com` into a corresponding folder (here: `INBOX/Mailing-lists`) automatically.
When mail is delivered to your account, it is possible to organize storing mail into folders by the [subaddress (tag)][docs::accounts-subaddressing] used. ```sieve
require ["envelope", "fileinto", "mailbox", "subaddress", "variables"];
!!! example "Example: `user+<tag>@example.com` to `INBOX/<Tag>`" if envelope :detail :matches "to" "*" {
set :lower :upperfirst "tag" "${1}";
This example sorts mail into inbox folders by their tag: if mailboxexists "INBOX.${1}" {
fileinto "INBOX.${1}";
```sieve title="docker-data/dms/config/user@example.com.dovecot.sieve" } else {
require ["envelope", "fileinto", "mailbox", "subaddress", "variables"]; fileinto :create "INBOX.${tag}";
}
# Check if the mail recipient address has a tag (:detail) }
if envelope :detail :matches "to" "*" { ```
# Create a variable `tag`, with the the captured `to` value normalized (SoCIAL => Social)
set :lower :upperfirst "tag" "${1}";
# Store the mail into a folder with the tag name, nested under your inbox folder:
if mailboxexists "INBOX.${tag}" {
fileinto "INBOX.${tag}";
} else {
fileinto :create "INBOX.${tag}";
}
}
```
When receiving mail for `user+social@example.com` it would be delivered into the `INBOX/Social` folder.
??? tip "Only redirect mail for specific tags"
If you want to only handle specific tags, you could replace the envelope condition and tag assignment from the prior example with:
```sieve title="docker-data/dms/config/user@example.com.dovecot.sieve"
# Instead of `:matches`, use the default comparator `:is` (exact match)
if envelope :detail "to" "social" {
set "tag" "Social";
```
```sieve title="docker-data/dms/config/user@example.com.dovecot.sieve"
# Alternatively you can also provide a list of values to match:
if envelope :detail "to" ["azure", "aws"] {
set "tag" "Cloud";
```
```sieve title="docker-data/dms/config/user@example.com.dovecot.sieve"
# Similar to `:matches`, except `:regex` provides enhanced pattern matching.
# NOTE: This example needs you to `require` the "regex" extension
if envelope :detail :regex "to" "^cloud-(azure|aws)$" {
# Normalize the captured azure/aws tag as the resolved value is no longer fixed:
set :lower :upperfirst "vendor" "${1}";
# If a `.` exists in the tag, it will create nested folders:
set "tag" "Cloud.${vendor}";
```
**NOTE:** There is no need to lowercase the tag in the conditional as the [`to` value is a case-insensitive check][sieve-docs::envelope].
??? abstract "Technical Details"
- Dovecot supports this feature via the _Sieve subaddress extension_ ([RFC 5233][rfc::5233::sieve-subaddress]).
- Only a single tag per subaddress is supported. Any additional tag delimiters are part of the tag value itself.
- The Dovecot setting [`recipient_delimiter`][dovecot-docs::config::recipient_delimiter] (default: `+`) configures the tag delimiter. This is where the `local-part` of the recipient address will split at, providing the `:detail` (tag) value for Sieve.
---
`INBOX` is the [default namespace configured by Dovecot][dovecot-docs::namespace].
- If you omit the `INBOX.` prefix from the sieve script above, the mailbox (folder) for that tag is created at the top-level alongside your Trash and Junk folders.
- The `.` between `INBOX` and `${tag}` is important as a [separator to distinguish mailbox names][dovecot-docs::mailbox-names]. This can vary by mailbox format or configuration. DMS uses [`Maildir`][dovecot-docs::mailbox-formats::maildir] by default, which uses `.` as the separator.
- [`lmtp_save_to_detail_mailbox = yes`][dovecot-docs::config::lmtp_save_to_detail_mailbox] can be set in `/etc/dovecot/conf.d/20-lmtp.conf`:
- This implements the feature globally, except for the tag normalization and `INBOX.` prefix parts of the example script.
- However, if the sieve script is also present, the script has precedence and will handle this task instead when the condition is successful, otherwise falling back to the global feature.
## Manage Sieve ## Manage Sieve
@ -158,31 +84,20 @@ The [Manage Sieve](https://doc.dovecot.org/admin_manual/pigeonhole_managesieve_s
!!! example !!! example
```yaml title="compose.yaml" ```yaml
# docker-compose.yml
ports: ports:
- "4190:4190" - "4190:4190"
environment: environment:
- ENABLE_MANAGESIEVE=1 - ENABLE_MANAGESIEVE=1
``` ```
All user defined sieve scripts that are managed by ManageSieve are stored in the user's home folder in `/var/mail/example.com/user1/home/sieve`. Just one Sieve script might be active for a user and is sym-linked to `/var/mail/example.com/user1/home/.dovecot.sieve` automatically. All user defined sieve scripts that are managed by ManageSieve are stored in the user's home folder in `/var/mail/example.com/user1/sieve`. Just one sieve script might be active for a user and is sym-linked to `/var/mail/example.com/user1/.dovecot.sieve` automatically.
!!! note !!! note
ManageSieve makes sure to not overwrite an existing `.dovecot.sieve` file. If a user activates a new sieve script the old one is backuped and moved to the `sieve` folder.
ManageSieve makes sure to not overwrite an existing `.dovecot.sieve` file. If a user activates a new sieve script the old one is backed up and moved to the `sieve` folder.
The extension is known to work with the following ManageSieve clients: The extension is known to work with the following ManageSieve clients:
- **[Sieve Editor](https://github.com/thsmi/sieve)** a portable standalone application based on the former Thunderbird plugin. - **[Sieve Editor](https://github.com/thsmi/sieve)** a portable standalone application based on the former Thunderbird plugin.
- **[Kmail](https://kontact.kde.org/components/kmail/)** the mail client of [KDE](https://kde.org/)'s Kontact Suite. - **[Kmail](https://kontact.kde.org/components/kmail/)** the mail client of [KDE](https://kde.org/)'s Kontact Suite.
[docs::accounts-subaddressing]: ../account-management/overview.md#sub-addressing
[dovecot-docs::namespace]: https://doc.dovecot.org/configuration_manual/namespace/
[dovecot-docs::mailbox-names]: https://doc.dovecot.org/configuration_manual/sieve/usage/#mailbox-names
[dovecot-docs::mailbox-formats::maildir]: https://doc.dovecot.org/admin_manual/mailbox_formats/maildir/#maildir-mbox-format
[dovecot-docs::config::lmtp_save_to_detail_mailbox]: https://doc.dovecot.org/settings/core/#core_setting-lmtp_save_to_detail_mailbox
[dovecot-docs::config::recipient_delimiter]: https://doc.dovecot.org/settings/core/#core_setting-recipient_delimiter
[rfc::5233::sieve-subaddress]: https://datatracker.ietf.org/doc/html/rfc5233
[sieve-docs::envelope]: https://thsmi.github.io/sieve-reference/en/test/core/envelope.html

View File

@ -2,52 +2,40 @@
title: 'Maintenance | Update and Cleanup' title: 'Maintenance | Update and Cleanup'
--- ---
[`containrrr/watchtower`][watchtower-dockerhub] is a service that monitors Docker images for updates, automatically applying them to running containers. ## Automatic Update
!!! example "Automatic image updates + cleanup" Docker images are handy but it can become a hassle to keep them updated. Also when a repository is automated you want to get these images when they get out.
Run a `watchtower` container with access to `docker.sock`, enabling the service to manage Docker: One could setup a complex action/hook-based workflow using probes, but there is a nice, easy to use docker image that solves this issue and could prove useful: [`watchtower`](https://hub.docker.com/r/containrrr/watchtower).
```yaml title="compose.yaml" A docker-compose example:
services:
watchtower:
image: containrrr/watchtower:latest
# Automatic cleanup (removes older image pulls from wasting disk space):
environment:
- WATCHTOWER_CLEANUP=true
volumes:
- /var/run/docker.sock:/var/run/docker.sock
```
!!! tip "The image tag used for a container is monitored for updates (eg: `:latest`, `:edge`, `:13`)" ```yaml
services:
watchtower:
restart: always
image: containrrr/watchtower:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
```
The automatic update support is **only for updates to that specific image tag**. For more details, see the [manual](https://containrrr.github.io/watchtower/)
- Your container will not update to a new major version tag (_unless using `:latest`_). ## Automatic Cleanup
- Omit the minor or patch portion of the semver tag to receive updates for the omitted portion (_eg: `13` will represent the latest minor + patch release of `v13`_).
!!! tip "Updating only specific containers" When you are pulling new images in automatically, it would be nice to have them cleaned up as well. There is also a docker image for this: [`spotify/docker-gc`](https://hub.docker.com/r/spotify/docker-gc/).
By default the `watchtower` service will check every 24 hours for new image updates to pull, based on currently running containers (_**not restricted** to only those running within your `compose.yaml`_). A docker-compose example:
Images eligible for updates can configured with a [custom `command`][docker-docs-compose-command] that provides a list of container names, or via other supported options (eg: labels). This configuration is detailed in the [`watchtower` docs][watchtower-docs]. ```yaml
services:
docker-gc:
restart: always
image: spotify/docker-gc:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
```
!!! info "Manual cleanup" For more details, see the [manual](https://github.com/spotify/docker-gc/blob/master/README.md)
`watchtower` also supports running on-demand with `docker run` or `compose.yaml` via the `--run-once` option. Or you can just use the [`--cleanup`](https://containrrr.github.io/watchtower/arguments/#cleanup) option provided by `containrrr/watchtower`.
You can alternatively invoke cleanup of Docker storage directly with:
- [`docker image prune --all`][docker-docs-prune-image]
- [`docker system prune --all`][docker-docs-prune-system] (_also removes unused containers, networks, build cache_).
If you omit the `--all` option, this will instead only remove ["dangling" content][docker-prune-dangling] (_eg: Orphaned images_).
[watchtower-dockerhub]: https://hub.docker.com/r/containrrr/watchtower
[watchtower-cleanup]: https://containrrr.github.io/watchtower/arguments/#cleanup
[watchtower-docs]: https://containrrr.dev/watchtower/
[docker-docs-compose-command]: https://docs.docker.com/compose/compose-file/05-services/#command
[docker-docs-prune-image]: https://docs.docker.com/engine/reference/commandline/image_prune/
[docker-docs-prune-system]: https://docs.docker.com/engine/reference/commandline/system_prune/
[docker-prune-dangling]: https://stackoverflow.com/questions/45142528/what-is-a-dangling-image-and-what-is-an-unused-image/60756668#60756668

View File

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

View File

@ -14,7 +14,7 @@ Your DMS folder structure should look like this example:
│ ├── dovecot.cf │ ├── dovecot.cf
│ ├── postfix-accounts.cf │ ├── postfix-accounts.cf
│ └── postfix-virtual.cf │ └── postfix-virtual.cf
├── compose.yaml ├── docker-compose.yml
└── README.md └── README.md
``` ```

View File

@ -23,7 +23,7 @@ Podman is a daemonless container engine for developing, managing, and running OC
While using Podman, you can just manage docker-mailserver as what you did with Docker. Your best friend `setup.sh` includes the minimum code in order to support Podman since it's 100% compatible with the Docker CLI. While using Podman, you can just manage docker-mailserver as what you did with Docker. Your best friend `setup.sh` includes the minimum code in order to support Podman since it's 100% compatible with the Docker CLI.
The installation is basically the same. Podman v3.2 introduced a RESTful API that is 100% compatible with the Docker API, so you can use Docker Compose with Podman easily. Install Podman and Docker Compose with your package manager first. The installation is basically the same. Podman v3.2 introduced a RESTful API that is 100% compatible with the Docker API, so you can use docker-compose with Podman easily. Install Podman and docker-compose with your package manager first.
```bash ```bash
sudo dnf install podman docker-compose sudo dnf install podman docker-compose
@ -39,8 +39,8 @@ This will create a unix socket locate under `/run/podman/podman.sock`, which is
```bash ```bash
export DOCKER_HOST="unix:///run/podman/podman.sock" export DOCKER_HOST="unix:///run/podman/podman.sock"
docker compose up -d mailserver docker-compose up -d mailserver
docker compose ps docker-compose ps
``` ```
You should see that docker-mailserver is running now. You should see that docker-mailserver is running now.
@ -75,7 +75,7 @@ First, enable `podman.socket` in systemd's userspace with a non-root user.
systemctl enable --now --user podman.socket systemctl enable --now --user podman.socket
``` ```
The socket file should be located at `/var/run/user/$(id -u)/podman/podman.sock`. Then, modify `compose.yaml` to make sure all ports are bindings are on non-privileged ports. The socket file should be located at `/var/run/user/$(id -u)/podman/podman.sock`. Then, modify `docker-compose.yml` to make sure all ports are bindings are on non-privileged ports.
```yaml ```yaml
services: services:
@ -88,12 +88,12 @@ services:
- "10993:993" # IMAP4 (implicit TLS) - "10993:993" # IMAP4 (implicit TLS)
``` ```
Then, setup your `mailserver.env` file follow the documentation and use Docker Compose to start the container. Then, setup your `mailserver.env` file follow the documentation and use docker-compose to start the container.
```bash ```bash
export DOCKER_HOST="unix:///var/run/user/$(id -u)/podman/podman.sock" export DOCKER_HOST="unix:///var/run/user/$(id -u)/podman/podman.sock"
docker compose up -d mailserver docker-compose up -d mailserver
docker compose ps docker-compose ps
``` ```
### Security in Rootless Mode ### Security in Rootless Mode
@ -106,12 +106,12 @@ The `PERMIT_DOCKER` variable in the `mailserver.env` file allows to specify trus
#### Use the slip4netns network driver #### Use the slip4netns network driver
The second workaround is slightly more complicated because the `compose.yaml` has to be modified. The second workaround is slightly more complicated because the `docker-compose.yml` has to be modified.
As shown in the [fail2ban section][docs::fail2ban::rootless] the `slirp4netns` network driver has to be enabled. As shown in the [fail2ban section](../../security/fail2ban/#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 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. user defined networks which might be a problem depending on your setup.
[Rootless Podman][rootless::podman] requires adding the value `slirp4netns:port_handler=slirp4netns` to the `--network` CLI option, or `network_mode` setting in your `compose.yaml`. [Rootless Podman][rootless::podman] requires adding the value `slirp4netns:port_handler=slirp4netns` to the `--network` CLI option, or `network_mode` setting in your `docker-compose.yml`.
You must also add the ENV `NETWORK_INTERFACE=tap0`, because Podman uses a [hard-coded interface name][rootless::podman::interface] for `slirp4netns`. You must also add the ENV `NETWORK_INTERFACE=tap0`, because Podman uses a [hard-coded interface name][rootless::podman::interface] for `slirp4netns`.
@ -150,7 +150,7 @@ Remember to run this command as root user.
### Port Forwarding ### Port Forwarding
When it comes to forwarding ports using `firewalld`, see [these port forwarding docs][firewalld-port-forwarding] for more information. When it comes to forwarding ports using `firewalld`, see <https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/securing_networks/using-and-configuring-firewalld_securing-networks#port-forwarding_using-and-configuring-firewalld> for more information.
```bash ```bash
firewall-cmd --permanent --add-forward-port=port=<25|143|465|587|993>:proto=<tcp>:toport=<10025|10143|10465|10587|10993> firewall-cmd --permanent --add-forward-port=port=<25|143|465|587|993>:proto=<tcp>:toport=<10025|10143|10465|10587|10993>
@ -169,9 +169,7 @@ firewall-cmd --permanent --direct --add-rule <ipv4|ipv6> nat OUTPUT 0 -p <tcp|ud
firewall-cmd --reload firewall-cmd --reload
``` ```
Just map all the privilege port with non-privilege port you set in compose.yaml before as root user. Just map all the privilege port with non-privilege port you set in docker-compose.yml before as root user.
[docs::fail2ban::rootless]: ../security/fail2ban.md#rootless-container
[rootless::podman]: https://github.com/containers/podman/blob/v3.4.1/docs/source/markdown/podman-run.1.md#--networkmode---net [rootless::podman]: https://github.com/containers/podman/blob/v3.4.1/docs/source/markdown/podman-run.1.md#--networkmode---net
[rootless::podman::interface]: https://github.com/containers/podman/blob/v3.4.1/libpod/networking_slirp4netns.go#L264 [rootless::podman::interface]: https://github.com/containers/podman/blob/v3.4.1/libpod/networking_slirp4netns.go#L264
[firewalld-port-forwarding]: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/securing_networks/using-and-configuring-firewalld_securing-networks#port-forwarding_using-and-configuring-firewalld

View File

@ -29,88 +29,20 @@ Cloudflare has written an [article about DKIM, DMARC and SPF][cloudflare-dkim-dm
When DKIM is enabled: When DKIM is enabled:
1. Inbound mail will verify any included DKIM signatures 1. Inbound mail will verify any included DKIM signatures
2. Outbound mail is signed (_when your sending domain has a configured DKIM key_) 2. Outbound mail is signed (_when you're sending domain has a configured DKIM key_)
DKIM requires a public/private key pair to enable **signing (_via private key_)** your outgoing mail, while the receiving end must query DNS to **verify (_via public key_)** that the signature is trustworthy. DKIM requires a public/private key pair to enable **signing (_via private key_)** your outgoing mail, while the receiving end must query DNS to **verify (_via public key_)** that the signature is trustworthy.
??? info "Verification expiry"
Unlike your TLS certificate, your DKIM keypair does not have a fixed expiry associated to it.
Instead, an expiry may be included in your DKIM signature for each mail sent, where a receiver will [refuse to validate the signature for an email after that expiry date][dkim-verification-expiry-refusal]. This is an added precaution to mitigate malicious activity like "DKIM replay attacks", where an already delivered email from a third-party with a trustworthy DKIM signature is leveraged by a spammer when sending mail to an MTA which verifies the DKIM signature successfully, enabling the spammer to bypass spam protections.
Unlike a TLS handshake where you are authenticating trust with future communications, with DKIM once the mail has been received and trust of the signature has been verified, the value of verifying the signature again at a later date is less meaningful since the signature was to ensure no tampering had occurred during delivery through the network.
??? tip "DKIM key rotation"
You can rotate your DKIM keypair by switching to a new DKIM selector (_and DNS updates_), while the previous key and selector remains valid for verification until the last mail signed with that key reaches it's expiry.
DMS does not provide any automation or support for key rotation, [nor is it likely to provide a notable security benefit][gh-discussion::dkim-key-rotation-expiry] to the typical small scale DMS deployment.
### Generating Keys ### Generating Keys
You'll need to repeat this process if you add any new domains.
You should have: You should have:
- At least one [email account setup][docs-accounts] - At least one [email account setup][docs-accounts-add]
- Attached a [volume for config][docs-volumes-config] to persist the generated files to local storage - Attached a [volume for config][docs-volumes-config] to persist the generated files to local storage
!!! example "Creating DKIM Keys" !!! warning "RSA Key Sizes >= 4096 Bit"
DKIM keys can be generated with good defaults by running: Keys of 4096 bits could be denied by some mail servers. According to [RFC 6376][rfc-6376], keys are [preferably between 512 and 2048 bits][github-issue-dkimlength].
```bash
docker exec -it <CONTAINER NAME> setup config dkim
```
If you need to generate your keys with different settings, check the `help` output for supported config options and examples:
```bash
docker exec -it <CONTAINER NAME> setup config dkim help
```
As described by the help output, you may need to use the `domain` option explicitly when you're using LDAP or Rspamd.
??? info "Changing the key size"
The keypair generated for using with DKIM presently defaults to RSA-2048. This is a good size but you can lower the security to `1024-bit`, or increase it to `4096-bit` (_discouraged as that is excessive_).
To generate a key with different size (_for RSA 1024-bit_) run:
```sh
setup config dkim keysize 1024
```
!!! warning "RSA Key Sizes >= 4096 Bit"
According to [RFC 8301][rfc-8301], keys are preferably between 1024 and 2048 bits. Keys of size 4096-bit or larger may not be compatible to all systems your mail is intended for.
You [should not need a key length beyond 2048-bit][gh-issue::dkim-length]. If 2048-bit does not meet your security needs, you may want to instead consider adopting key rotation or switching from RSA to ECC keys for DKIM.
??? note "You may need to specify mail domains explicitly"
Required when using LDAP and Rspamd.
`setup config dkim` will generate DKIM keys for what is assumed as the primary mail domain (_derived from the FQDN assigned to DMS, minus any subdomain_).
When the DMS FQDN is `mail.example.com` or `example.com`, by default this command will generate DKIM keys for `example.com` as the primary domain for your users mail accounts (eg: `hello@example.com`).
The DKIM generation does not have support to query LDAP for additional mail domains it should know about. If the primary mail domain is not sufficient, then you must explicitly specify any extra domains via the `domain` option:
```sh
# ENABLE_OPENDKIM=1 (default):
setup config dkim domain 'example.com,another-example.com'
# ENABLE_RSPAMD=1 + ENABLE_OPENDKIM=0:
setup config dkim domain example.com
setup config dkim domain another-example.com
```
!!! info "OpenDKIM with `ACCOUNT_PROVISIONER=FILE`"
When DMS uses this configuration, it will by default also detect mail domains (_from accounts added via `setup email add`_), generating additional DKIM keys.
DKIM is currently supported by either OpenDKIM or Rspamd: DKIM is currently supported by either OpenDKIM or Rspamd:
@ -118,27 +50,64 @@ DKIM is currently supported by either OpenDKIM or Rspamd:
OpenDKIM is currently [enabled by default][docs-env-opendkim]. OpenDKIM is currently [enabled by default][docs-env-opendkim].
After running `setup config dkim`, your new DKIM key files (_and OpenDKIM config_) have been added to `/tmp/docker-mailserver/opendkim/`. The command `docker exec <CONTAINER NAME> setup config dkim help` details supported config options, along with some examples.
!!! example "Creating a DKIM key"
Generate the DKIM files with:
```sh
docker exec -ti <CONTAINER NAME> setup config dkim
```
Your new DKIM key(s) and OpenDKIM config files have been added to `/tmp/docker-mailserver/opendkim/`.
??? note "LDAP accounts need to specify domains explicitly"
The command is unable to infer the domains from LDAP user accounts, you must specify them:
```sh
setup config dkim domain 'example.com,example.io'
```
??? tip "Changing the key size"
The private key presently defaults to RSA-4096. To create an RSA 2048-bit key run:
```sh
setup config dkim keysize 2048
```
!!! info "Restart required" !!! info "Restart required"
After restarting DMS, outgoing mail will now be signed with your new DKIM key(s) :tada: After restarting DMS, outgoing mail will now be signed with your new DKIM key(s) :tada:
You'll need to repeat this process if you add any new domains.
=== "Rspamd" === "Rspamd"
Requires opt-in via [`ENABLE_RSPAMD=1`][docs-env-rspamd] (_and disable the default OpenDKIM: `ENABLE_OPENDKIM=0`_). Opt-in via [`ENABLE_RSPAMD=1`][docs-env-rspamd] (_and disable the default OpenDKIM: `ENABLE_OPENDKIM=0`_).
Rspamd provides DKIM support through two separate modules: Rspamd provides DKIM support through two separate modules:
1. [Verifying DKIM signatures from inbound mail][rspamd-docs-dkim-checks] is enabled by default. 1. [Verifying DKIM signatures from inbound mail][rspamd-docs-dkim-checks] is enabled by default.
2. [Signing outbound mail with your DKIM key][rspamd-docs-dkim-signing] needs additional setup (key + dns + config). 2. [Signing outbound mail with your DKIM key][rspamd-docs-dkim-signing] needs additional setup (key + dns + config).
??? warning "Using Multiple Domains" !!! example "Creating DKIM Keys"
If you have multiple domains, you need to: You can simply run
- Create a key wth `docker exec -it <CONTAINER NAME> setup config dkim domain <DOMAIN>` for each domain DMS should sign outgoing mail for. ```bash
- Provide a custom `dkim_signing.conf` (for which an example is shown below), as the default config only supports one domain. docker exec -ti <CONTAINER NAME> setup config dkim help
```
which provides you with an overview of what the script can do. Just running
```bash
docker exec -ti <CONTAINER NAME> setup config dkim
```
will execute the helper script with default parameters.
!!! info "About the Helper Script" !!! info "About the Helper Script"
@ -148,9 +117,7 @@ DKIM is currently supported by either OpenDKIM or Rspamd:
--- ---
In case you have not already provided a default DKIM signing configuration, the script will create one and write it to `/etc/rspamd/override.d/dkim_signing.conf`. If this file already exists, it will not be overwritten. In case you have not already provided a default DKIM signing configuration, the script will create one and write it to `/etc/rspamd/override.d/dkim_signing.conf`. If this file already exist, it will not be overwritten. When you're already using [the `rspamd/override.d/` directory][docs-rspamd-override-d], the file is created inside your volume and therefore persisted correctly. If you are not using `rspamd/override.d/`, you will need to persist the file yourself (otherwise it is lost on container restart).
When you're already using [the `rspamd/override.d/` directory][docs-rspamd-config-dropin], the file is created inside your volume and therefore persisted correctly. If you are not using `rspamd/override.d/`, you will need to persist the file yourself (otherwise it is lost on container restart).
An example of what a default configuration file for DKIM signing looks like can be found by expanding the example below. An example of what a default configuration file for DKIM signing looks like can be found by expanding the example below.
@ -169,15 +136,9 @@ DKIM is currently supported by either OpenDKIM or Rspamd:
use_domain = "header"; use_domain = "header";
use_redis = false; # don't change unless Redis also provides the DKIM keys use_redis = false; # don't change unless Redis also provides the DKIM keys
use_esld = true; use_esld = true;
check_pubkey = true; # you want to use this in the beginning
selector = "mail"; check_pubkey = true; # you wan't to use this in the beginning
# 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 { domain {
example.com { example.com {
path = "/tmp/docker-mailserver/rspamd/dkim/mail.private"; path = "/tmp/docker-mailserver/rspamd/dkim/mail.private";
@ -188,16 +149,24 @@ DKIM is currently supported by either OpenDKIM or Rspamd:
As shown next: As shown next:
- You can add more domains into the `domain { ... }` section (in the following example: `example.com` and `example.org`). - You can add more domains into the `domain { ... }` section.
- A domain can also be configured with multiple selectors and keys within a `selectors [ ... ]` array (in the following example, this is done for `example.org`). - A domain can also be configured with multiple selectors and keys within a `selectors [ ... ]` array.
```cf ```cf
# ... # ...
domain { domain {
example.com { example.com {
path = /tmp/docker-mailserver/rspamd/example.com/ed25519.private"; selectors [
selector = "dkim-ed25519"; {
path = "/tmp/docker-mailserver/rspamd/dkim/example.com/rsa.private";
selector = "dkim-rsa";
},
{
path = /tmp/docker-mailserver/rspamd/example.com/ed25519.private";
selector = "dkim-ed25519";
}
]
} }
example.org { example.org {
selectors [ selectors [
@ -224,7 +193,9 @@ DKIM is currently supported by either OpenDKIM or Rspamd:
When `check_pubkey = true;` is set, Rspamd will query the DNS record for each DKIM selector, verifying each public key matches the private key configured. When `check_pubkey = true;` is set, Rspamd will query the DNS record for each DKIM selector, verifying each public key matches the private key configured.
If there is a mismatch, a warning will be emitted to the Rspamd log `/var/log/mail/rspamd.log`. If there is a mismatch, a warning will be omitted to the Rspamd log `/var/log/supervisor/rspamd.log`.
[docs-rspamd-override-d]: ../security/rspamd.md#manually
### DNS Record { #dkim-dns } ### DNS Record { #dkim-dns }
@ -255,13 +226,11 @@ When mail signed with your DKIM key is sent from your mail server, the receiver
??? info "`<selector>.txt` - Formatting the `TXT` record value correctly" ??? info "`<selector>.txt` - Formatting the `TXT` record value correctly"
This file was generated for use within a [DNS zone file][dns::wikipedia-zonefile]. The file name uses the DKIM selector it was generated with (default DKIM selector is `mail`, which creates `mail.txt`_). This file was generated for use within a [DNS zone file][dns::wikipedia-zonefile]. DNS `TXT` records values that are longer than 255 characters need to be split into multiple parts. This is why the public key has multiple parts wrapped within double-quotes between `(` and `)`.
For your DNS setup, DKIM support needs to create a `TXT` record to store the public key for mail clients to use. `TXT` records with values that are longer than 255 characters need to be split into multiple parts. This is why the generated `<selector>.txt` file (_containing your public key for use with DKIM_) has multiple value parts wrapped within double-quotes between `(` and `)`. A DNS web-interface may handle this internally instead, while [others may not, but expect the input as a single line][dns::webui-dkim]_). You'll need to manually format the value as described below.
A DNS web-interface may handle this separation internally instead, and [could expect the value provided all as a single line][dns::webui-dkim] instead of split. When that is required, you'll need to manually format the value as described below. Your DNS record file (eg: `mail.txt`) should look similar to this:
Your generated DNS record file (`<selector>.txt`) should look similar to this:
```txt ```txt
mail._domainkey IN TXT ( "v=DKIM1; k=rsa; " mail._domainkey IN TXT ( "v=DKIM1; k=rsa; "
@ -279,7 +248,7 @@ When mail signed with your DKIM key is sent from your mail server, the receiver
To test that your new DKIM record is correct, query it with the `dig` command. The `TXT` value response should be a single line split into multiple parts wrapped in double-quotes: To test that your new DKIM record is correct, query it with the `dig` command. The `TXT` value response should be a single line split into multiple parts wrapped in double-quotes:
```console ```console
$ dig +short TXT mail._domainkey.example.com $ dig +short TXT dkim-rsa._domainkey.example.com
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQMMqhb1S52Rg7VFS3EC6JQIMxNDdiBmOKZvY5fiVtD3Z+yd9ZV+V8e4IARVoMXWcJWSR6xkloitzfrRtJRwOYvmrcgugOalkmM0V4Gy/2aXeamuiBuUc4esDQEI3egmtAsHcVY1XCoYfs+9VqoHEq3vdr3UQ8zP/l+FP5UfcaJFCK/ZllqcO2P1GjIDVSHLdPpRHbMP/tU1a9mNZ5QMZBJ/JuJK/s+2bp8gpxKn8rh1akSQjlynlV9NI+7J3CC7CUf3bGvoXIrb37C/lpJehS39" "KNtcGdaRufKauSfqx/7SxA0zyZC+r13f7ASbMaQFzm+/RRusTqozY/p/MsWx8QIDAQAB" "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQMMqhb1S52Rg7VFS3EC6JQIMxNDdiBmOKZvY5fiVtD3Z+yd9ZV+V8e4IARVoMXWcJWSR6xkloitzfrRtJRwOYvmrcgugOalkmM0V4Gy/2aXeamuiBuUc4esDQEI3egmtAsHcVY1XCoYfs+9VqoHEq3vdr3UQ8zP/l+FP5UfcaJFCK/ZllqcO2P1GjIDVSHLdPpRHbMP/tU1a9mNZ5QMZBJ/JuJK/s+2bp8gpxKn8rh1akSQjlynlV9NI+7J3CC7CUf3bGvoXIrb37C/lpJehS39" "KNtcGdaRufKauSfqx/7SxA0zyZC+r13f7ASbMaQFzm+/RRusTqozY/p/MsWx8QIDAQAB"
``` ```
@ -287,7 +256,7 @@ When mail signed with your DKIM key is sent from your mail server, the receiver
[MxToolbox has a DKIM Verifier][mxtoolbox-dkim-verifier] that you can use to check your DKIM DNS record(s). [MxToolbox has a DKIM Verifier][mxtoolbox-dkim-verifier] that you can use to check your DKIM DNS record(s).
When using Rspamd, we recommend you turn on `check_pubkey = true;` in `dkim_signing.conf`. Rspamd will then check whether your private key matches your public key, and you can check possible mismatches by looking at `/var/log/mail/rspamd.log`. When using Rspamd, we recommend you turn on `check_pubkey = true;` in `dkim_signing.conf`. Rspamd will then check whether your private key matches your public key, and you can check possible mismatches by looking at `/var/log/supervisor/rspamd.log`.
## DMARC ## DMARC
@ -298,7 +267,7 @@ The only thing you need to do in order to enable DMARC on a "DNS-level" is to ad
Typically something like this should be good to start with: Typically something like this should be good to start with:
```txt ```txt
_dmarc.example.com. IN TXT "v=DMARC1; p=none; sp=none; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com" _dmarc.example.com. IN TXT "v=DMARC1; p=none; sp=none; fo=0; adkim=4; aspf=r; pct=100; rf=afrf; ri=86400; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com"
``` ```
Or a bit more strict policies (_mind `p=quarantine` and `sp=quarantine`_): Or a bit more strict policies (_mind `p=quarantine` and `sp=quarantine`_):
@ -317,9 +286,9 @@ The DMARC status may not be displayed instantly due to delays in DNS (caches). D
[Source][wikipedia-spf] [Source][wikipedia-spf]
!!! tip "Disabling the default SPF service `policy-spf`" !!! note "Disabling `policyd-spf`?"
Set [`ENABLE_POLICYD_SPF=0`][docs-env-spf-policyd] to opt-out of the default SPF service. Advised when Rspamd is configured to handle SPF instead. As of now, `policyd-spf` cannot be disabled. This is WIP.
### Adding an SPF Record ### Adding an SPF Record
@ -352,23 +321,23 @@ Whitelist = 192.168.0.0/31,192.168.1.0/30
# Domain_Whitelist = mx1.not-example.com,mx2.not-example.com # Domain_Whitelist = mx1.not-example.com,mx2.not-example.com
``` ```
Then add this line to `compose.yaml`: Then add this line to `docker-compose.yml`:
```yaml ```yaml
volumes: volumes:
- ./docker-data/dms/config/postfix-policyd-spf.conf:/etc/postfix-policyd-spf-python/policyd-spf.conf - ./docker-data/dms/config/postfix-policyd-spf.conf:/etc/postfix-policyd-spf-python/policyd-spf.conf
``` ```
[docs-accounts]: ../account-management/overview.md#accounts
[docs-volumes-config]: ../advanced/optional-config.md#volumes-config [docs-accounts-add]: ../user-management.md#adding-a-new-account
[docs-volumes-config]: ../advanced/optional-config.md
[docs-env-opendkim]: ../environment.md#enable_opendkim [docs-env-opendkim]: ../environment.md#enable_opendkim
[docs-env-rspamd]: ../environment.md#enable_rspamd [docs-env-rspamd]: ../environment.md#enable_rspamd
[docs-env-spf-policyd]: ../environment.md#enable_policyd_spf
[docs-rspamd-config-dropin]: ../security/rspamd.md#manually [docs-rspamd-config-dropin]: ../security/rspamd.md#manually
[docs-rspamd-config-declarative]: ../security/rspamd.md#with-the-help-of-a-custom-file
[cloudflare-dkim-dmarc-spf]: https://www.cloudflare.com/learning/email-security/dmarc-dkim-spf/ [cloudflare-dkim-dmarc-spf]: https://www.cloudflare.com/learning/email-security/dmarc-dkim-spf/
[rfc-8301]: https://datatracker.ietf.org/doc/html/rfc8301#section-3.2 [rfc-6376]: https://tools.ietf.org/html/rfc6376
[gh-discussion::dkim-key-rotation-expiry]: https://github.com/orgs/docker-mailserver/discussions/4068#discussioncomment-9784263 [github-issue-dkimlength]: https://github.com/docker-mailserver/docker-mailserver/issues/1854
[gh-issue::dkim-length]: https://github.com/docker-mailserver/docker-mailserver/issues/1854#issuecomment-806280929
[rspamd-docs-dkim-checks]: https://www.rspamd.com/doc/modules/dkim.html [rspamd-docs-dkim-checks]: https://www.rspamd.com/doc/modules/dkim.html
[rspamd-docs-dkim-signing]: https://www.rspamd.com/doc/modules/dkim_signing.html [rspamd-docs-dkim-signing]: https://www.rspamd.com/doc/modules/dkim_signing.html
[dns::example-webui]: https://www.vultr.com/docs/introduction-to-vultr-dns/ [dns::example-webui]: https://www.vultr.com/docs/introduction-to-vultr-dns/
@ -376,7 +345,6 @@ volumes:
[dns::wikipedia-zonefile]: https://en.wikipedia.org/wiki/Zone_file [dns::wikipedia-zonefile]: https://en.wikipedia.org/wiki/Zone_file
[dns::webui-dkim]: https://serverfault.com/questions/763815/route-53-doesnt-allow-adding-dkim-keys-because-length-is-too-long [dns::webui-dkim]: https://serverfault.com/questions/763815/route-53-doesnt-allow-adding-dkim-keys-because-length-is-too-long
[dkim-ed25519-support]: https://serverfault.com/questions/1023674/is-ed25519-well-supported-for-the-dkim-validation/1074545#1074545 [dkim-ed25519-support]: https://serverfault.com/questions/1023674/is-ed25519-well-supported-for-the-dkim-validation/1074545#1074545
[dkim-verification-expiry-refusal]: https://mxtoolbox.com/problem/dkim/dkim-signature-expiration
[mxtoolbox-dkim-verifier]: https://mxtoolbox.com/dkim.aspx [mxtoolbox-dkim-verifier]: https://mxtoolbox.com/dkim.aspx
[dmarc-howto-configtags]: https://github.com/internetstandards/toolbox-wiki/blob/master/DMARC-how-to.md#overview-of-dmarc-configuration-tags [dmarc-howto-configtags]: https://github.com/internetstandards/toolbox-wiki/blob/master/DMARC-how-to.md#overview-of-dmarc-configuration-tags
[dmarc-tool-gca]: https://dmarcguide.globalcyberalliance.org [dmarc-tool-gca]: https://dmarcguide.globalcyberalliance.org

View File

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

@ -10,31 +10,7 @@ This page contains valuable information when it comes to resolving issues you en
Please consider contributing solutions to the [FAQ][docs-faq] :heart: Please consider contributing solutions to the [FAQ][docs-faq] :heart:
## Preliminary Checks ## Preliminary Information
- Check that all published DMS ports are actually open and not blocked by your ISP / hosting provider.
- SSL errors are likely the result of a wrong setup on the user side and not caused by DMS itself.
- Ensure that you have correctly started DMS. Many problems related to configuration are due to this.
!!! danger "Correctly starting DMS"
Use the [`--force-recreate`][docker-docs::force-recreate] option to avoid configuration mishaps: `docker compose up --force-recreate`
Alternatively, always use `docker compose down` to stop DMS. **Do not** rely on `CTRL + C`, `docker compose stop`, or `docker compose restart`.
---
DMS setup scripts are run when a container starts, but may fail to work properly if you do the following:
- Stopping a container with commands like: `docker stop` or `docker compose up` stopped via `CTRL + C` instead of `docker compose down`.
- Restarting a container.
Volumes persist data across container instances, however the same container instance will keep internal changes not stored in a volume until the container is removed.
Due to this, DMS setup scripts may modify configuration it has already modified in the past.
- This is brittle as some changes are naive by assuming they are applied to the original configs from the image.
- Volumes in `compose.yaml` are expected to persist any important data. Thus it should be safe to throwaway the container created each time, avoiding this config problem.
### Mail sent from DMS does not arrive at destination ### Mail sent from DMS does not arrive at destination
@ -46,92 +22,27 @@ Some service providers block outbound traffic on port 25. Common hosting provide
These links may advise how the provider can unblock the port through additional services offered, or via a support ticket request. These links may advise how the provider can unblock the port through additional services offered, or via a support ticket request.
### Mail sent to DMS does not get delivered to user
Common logs related to this are:
- `warning: do not list domain domain.fr in BOTH mydestination and virtual_mailbox_domains`
- `Recipient address rejected: User unknown in local recipient table`
If your logs look like this, you likely have [assigned the same FQDN to the DMS `hostname` and your mail accounts][gh-issues::dms-fqdn-misconfigured] which is not supported by default. You can either adjust your DMS `hostname` or follow [this FAQ advice][docs::faq-bare-domain]
It is also possible that [DMS services are temporarily unavailable][gh-issues::dms-services-unavailable] when configuration changes are detected, producing the 2nd error. Certificate updates may be a less obvious trigger.
## Steps for Debugging DMS ## Steps for Debugging DMS
1. **Increase log verbosity**: Very helpful for troubleshooting problems during container startup. Set the environment variable [`LOG_LEVEL`][docs-environment-log-level] to `debug` or `trace`. 1. **Increase log verbosity**: Very helpful for troubleshooting problems during container startup. Set the environment variable [`LOG_LEVEL`][docs-environment-log-level] to `debug` or `trace`.
2. **Use error logs as a search query**: Try [finding an _existing issue_][gh-issues] or _search engine result_ from any errors in your container log output. Often you'll find answers or more insights. If you still need to open an issue, sharing links from your search may help us assist you. The mail server log can be acquired by running `docker log <CONTAINER NAME>` (_or `docker logs -f <CONTAINER NAME>` if you want to follow the log_). 2. **Use error logs as a search query**: Try finding an _existing issue_ or _search engine result_ from any errors in your container log output. Often you'll find answers or more insights. If you still need to open an issue, sharing links from your search may help us assist you. The mail server log can be acquired by running `docker log <CONTAINER NAME>` (_or `docker logs -f <CONTAINER NAME>` if you want to follow the log_).
3. **Inspect the logs of the service that is failing**: We provide a dedicated paragraph on this topic [further down below](#logs). 3. **Understand the basics of mail servers**: Especially for beginners, make sure you read our [Introduction][docs-introduction] and [Usage][docs-usage] articles.
4. **Understand the basics of mail servers**: Especially for beginners, make sure you read our [Introduction][docs-introduction] and [Usage][docs-usage] articles. 4. **Search the whole FAQ**: Our [FAQ][docs-faq] contains answers for common problems. Make sure you go through the list.
5. **Search the whole FAQ**: Our [FAQ][docs-faq] contains answers for common problems. Make sure you go through the list. 5. **Reduce the scope**: Ensure that you can run a basic setup of DMS first. Then incrementally restore parts of your original configuration until the problem is reproduced again. If you're new to DMS, it is common to find the cause is misunderstanding how to configure a minimal setup.
6. **Reduce the scope**: Ensure that you can run a basic setup of DMS first. Then incrementally restore parts of your original configuration until the problem is reproduced again. If you're new to DMS, it is common to find the cause is misunderstanding how to configure a minimal setup.
### Debug a running container ### Debug a running container
#### General To get a shell inside the container run: `docker exec -it <CONTAINER NAME> bash`.
To get a shell inside the container run: `docker exec -it <CONTAINER NAME> bash`. To install additional software, run: If you need more flexibility than `docker logs` offers, within the container `/var/log/mail/mail.log` and `/var/log/supervisor/` are the most useful locations to get relevant DMS logs. Use the `tail` or `cat` commands to view their contents.
1. `apt-get update` to update repository metadata. To install additional software:
2. `apt-get install <PACKAGE>` to install a package, e.g., `apt-get install neovim` if you want to use NeoVim instead of `nano` (which is shipped by default).
#### Logs - `apt-get update` is needed to update repository metadata.
- `apt-get install <PACKAGE>`
- For example if you need a text editor, `nano` is a good package choice for beginners.
If you need more flexibility than what the `docker logs` command offers, then the most useful locations to get relevant DMS logs within the container are:
- `/var/log/mail/<SERVICE>.log`
- `/var/log/supervisor/<SERVICE>.log`
You may use `nano` (a text editor) to edit files, while `less` (a file viewer) and `tail`/`cat` are useful tools to inspect the contents of logs.
## Compatibility
It's possible that the issue you're experiencing is due to a compatibility conflict.
This could be from outdated software, or running a system that isn't able to provide you newer software and kernels. You may want to verify if you can reproduce the issue on a system that is not affected by these concerns.
### Network
- Misconfigured network connections can cause the client IP address to be proxied through a docker network gateway IP, or a [service that acts on behalf of connecting clients for logins][gh-discuss-roundcube-fail2ban] where the connections client IP appears to be only from that service (eg: Container IP) instead. This can relay the wrong information to other services (eg: monitoring like Fail2Ban, SPF verification) causing unexpected failures.
- **`userland-proxy`:** Prior to Docker `v23`, [changing the `userland-proxy` setting did not reliably remove NAT rules][network::docker-userlandproxy].
- **UFW / firewalld:** Some users expect only their firewall frontend to manage the firewall rules, but these will be bypassed when Docker publishes a container port (_as there is no integration between the two_).
- **`iptables` / `nftables`:**
- Docker [only manages the NAT rules via `iptables`][network::docker-nftables], relying on compatibility shims for supporting the successor `nftables`. Internally DMS expects `nftables` support on the host kernel for services like Fail2Ban to function correctly.
- [Kernels older than 5.2 may affect management of NAT rules via `nftables`][network::kernel-nftables]. Other software outside of DMS may also manipulate these rules, such as firewall frontends.
- **IPv6:**
- Requires [additional configuration][docs-ipv6] to prevent or properly support IPv6 connections (eg: Preserving the Client IP).
- Support in 2023 is still considered experimental. You are advised to use at least Docker Engine `v23` (2023Q1).
- Various networking bug fixes have been addressed since the initial IPv6 support arrived in Docker Engine `v20.10.0` (2020Q4).
### System
- **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 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 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::fail2ban::rootless-portdriver].
[network::docker-userlandproxy]: https://github.com/moby/moby/issues/44721
[network::docker-nftables]: https://github.com/moby/moby/issues/26824
[network::kernels-modified]: https://github.com/docker-mailserver/docker-mailserver/pull/2662#issuecomment-1168435970
[network::kernel-nftables]: https://unix.stackexchange.com/questions/596493/can-nftables-and-iptables-ip6tables-rules-be-applied-at-the-same-time-if-so-wh/596497#596497
[docs-environment-log-level]: ./environment.md#log_level
[docs-faq]: ../faq.md [docs-faq]: ../faq.md
[docs::faq-bare-domain]: ../faq.md#can-i-use-a-nakedbare-domain-ie-no-hostname [docs-environment-log-level]: ./environment.md#log_level
[docs-ipv6]: ./advanced/ipv6.md
[docs-introduction]: ../introduction.md [docs-introduction]: ../introduction.md
[docs::fail2ban::rootless-portdriver]: ./security/fail2ban.md#rootless-container
[docs-usage]: ../usage.md [docs-usage]: ../usage.md
[gh-issues]: https://github.com/docker-mailserver/docker-mailserver/issues
[gh-issues::dms-fqdn-misconfigured]: https://github.com/docker-mailserver/docker-mailserver/issues/3679#issuecomment-1837609043
[gh-issues::dms-services-unavailable]: https://github.com/docker-mailserver/docker-mailserver/issues/3679#issuecomment-1848083358
[gh-macos-support]: https://github.com/docker-mailserver/docker-mailserver/issues/3648#issuecomment-1822774080
[gh-discuss-roundcube-fail2ban]: https://github.com/orgs/docker-mailserver/discussions/3273#discussioncomment-5654603
[docker-rootless-interface]: https://github.com/moby/moby/issues/45742
[docker-macos-virtiofs]: https://www.docker.com/blog/speed-boost-achievement-unlocked-on-docker-desktop-4-6-for-mac/
[docker-docs::force-recreate]: https://docs.docker.com/compose/reference/up/

View File

@ -10,7 +10,7 @@ title: Environment Variables
##### OVERRIDE_HOSTNAME ##### OVERRIDE_HOSTNAME
If you can't set your hostname (_eg: you're in a container platform that doesn't let you_) specify it via this environment variable. It will have priority over `docker run --hostname`, or the equivalent `hostname:` field in `compose.yaml`. If you can't set your hostname (_eg: you're in a container platform that doesn't let you_) specify it via this environment variable. It will have priority over `docker run --hostname`, or the equivalent `hostname:` field in `docker-compose.yml`.
- **empty** => Uses the `hostname -f` command to get canonical hostname for DMS to use. - **empty** => Uses the `hostname -f` command to get canonical hostname for DMS to use.
- => Specify an FQDN (fully-qualified domain name) to serve mail for. The hostname is required for DMS to function correctly. - => Specify an FQDN (fully-qualified domain name) to serve mail for. The hostname is required for DMS to function correctly.
@ -33,32 +33,23 @@ Here you can adjust the [log-level for Supervisor](http://supervisord.org/loggin
The log-level will show everything in its class and above. The log-level will show everything in its class and above.
##### DMS_VMAIL_UID ##### ONE_DIR
Default: 5000 - 0 => state in default directories.
- **1** => consolidate all states into a single directory (`/var/mail-state`) to allow persistence using docker volumes. See the [related FAQ entry][docs-faq-onedir] for more information.
The User ID assigned to the static vmail user for `/var/mail` (_Mail storage managed by Dovecot_).
!!! warning "Incompatible UID values"
- A value of [`0` (root) is not compatible][gh-issue::vmail-uid-cannot-be-root].
- This feature will attempt to adjust the `uid` for the `docker` user (`/etc/passwd`), hence the error emitted to logs if the UID is already assigned to another user.
- The feature appears to work with other UID values that are already assigned in `/etc/passwd`, even though Dovecot by default has a setting for the minimum UID as `500`.
##### DMS_VMAIL_GID
Default: 5000
The Group ID assigned to the static vmail group for `/var/mail` (_Mail storage managed by Dovecot_).
##### ACCOUNT_PROVISIONER ##### ACCOUNT_PROVISIONER
Configures the [provisioning source of user accounts][docs::account-management::overview] (including aliases) for user queries and authentication by services managed by DMS (_Postfix and Dovecot_). Configures the provisioning source of user accounts (including aliases) for user queries and authentication by services managed by DMS (_Postfix and Dovecot_).
- **FILE** => use local files User provisioning via OIDC is planned for the future, see [this tracking issue](https://github.com/docker-mailserver/docker-mailserver/issues/2713).
- **empty** => use FILE
- LDAP => use LDAP authentication - LDAP => use LDAP authentication
- OIDC => use OIDC authentication (**not yet implemented**)
- FILE => use local files (this is used as the default)
LDAP requires an external service (e.g. [`bitnami/openldap`](https://hub.docker.com/r/bitnami/openldap/)). A second container for the ldap service is necessary (e.g. [docker-openldap](https://github.com/osixia/docker-openldap))
##### PERMIT_DOCKER ##### PERMIT_DOCKER
@ -105,15 +96,6 @@ This enables DNS block lists in _Postscreen_. If you want to know which lists we
- **0** => DNS block lists are disabled - **0** => DNS block lists are disabled
- 1 => DNS block lists are enabled - 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 ##### ENABLE_OPENDKIM
Enables the OpenDKIM service. Enables the OpenDKIM service.
@ -137,14 +119,9 @@ Enabled `policyd-spf` in Postfix's configuration. You will likely want to set th
##### ENABLE_POP3 ##### ENABLE_POP3
- **0** => POP3 service disabled - **empty** => POP3 service disabled
- 1 => Enables POP3 service - 1 => Enables POP3 service
##### ENABLE_IMAP
- 0 => Disabled
- **1** => Enabled
##### ENABLE_CLAMAV ##### ENABLE_CLAMAV
- **0** => ClamAV is disabled - **0** => ClamAV is disabled
@ -155,7 +132,7 @@ Enabled `policyd-spf` in Postfix's configuration. You will likely want to set th
- **0** => fail2ban service disabled - **0** => fail2ban service disabled
- 1 => Enables fail2ban service - 1 => Enables fail2ban service
If you enable Fail2Ban, don't forget to add the following lines to your `compose.yaml`: If you enable Fail2Ban, don't forget to add the following lines to your `docker-compose.yml`:
``` BASH ``` BASH
cap_add: cap_add:
@ -202,11 +179,11 @@ Please read [the SSL page in the documentation][docs-tls] for more information.
Configures the handling of creating mails with forged sender addresses. Configures the handling of creating mails with forged sender addresses.
- **0** => (not recommended) Mail address spoofing allowed. Any logged in user may create email messages with a [forged sender address](https://en.wikipedia.org/wiki/Email_spoofing). - **0** => (not recommended) Mail address spoofing allowed. Any logged in user may create email messages with a [forged sender address](https://en.wikipedia.org/wiki/Email_spoofing).
- 1 => Mail spoofing denied. Each user may only send with their own or their alias addresses. Addresses with [extension delimiters](http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages. - 1 => Mail spoofing denied. Each user may only send with his own or his alias addresses. Addresses with [extension delimiters](http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
##### ENABLE_SRS ##### ENABLE_SRS
Enables the Sender Rewriting Scheme. SRS is needed if DMS acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/main/README.rst) for further explanation. Enables the Sender Rewriting Scheme. SRS is needed if DMS acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/master/README.md#sender-rewriting-scheme-crash-course) for further explanation.
- **0** => Disabled - **0** => Disabled
- 1 => Enabled - 1 => Enabled
@ -234,9 +211,9 @@ Provide any valid URI. Examples:
- `lmtps:inet:<host>:<port>` (secure lmtp with starttls) - `lmtps:inet:<host>:<port>` (secure lmtp with starttls)
- `lmtp:<kopano-host>:2003` (use kopano as mailstore) - `lmtp:<kopano-host>:2003` (use kopano as mailstore)
##### POSTFIX_MAILBOX_SIZE_LIMIT ##### POSTFIX\_MAILBOX\_SIZE\_LIMIT
Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default). Size is in bytes. Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default).
- **empty** => 0 (no limit) - **empty** => 0 (no limit)
@ -247,9 +224,9 @@ Set the mailbox size limit for all users. If set to zero, the size will be unlim
See [mailbox quota][docs-accounts-quota]. See [mailbox quota][docs-accounts-quota].
##### POSTFIX_MESSAGE_SIZE_LIMIT ##### POSTFIX\_MESSAGE\_SIZE\_LIMIT
Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!). Size is in bytes. Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!)
- **empty** => 10240000 (~10 MB) - **empty** => 10240000 (~10 MB)
@ -295,7 +272,7 @@ Customize the update check interval. Number + Suffix. Suffix must be 's' for sec
- sdbox => (experimental) uses Dovecot high-performance mailbox format, one file contains one message - sdbox => (experimental) uses Dovecot high-performance mailbox format, one file contains one message
- mdbox ==> (experimental) uses Dovecot high-performance mailbox format, multiple messages per file and multiple files per box - mdbox ==> (experimental) uses Dovecot high-performance mailbox format, multiple messages per file and multiple files per box
This option has been added in November 2019. Using other format than Maildir is considered as experimental in docker-mailserver and should only be used for testing purpose. For more details, please refer to [Dovecot Documentation](https://doc.dovecot.org/admin_manual/mailbox_formats/#mailbox-formats). This option has been added in November 2019. Using other format than Maildir is considered as experimental in docker-mailserver and should only be used for testing purpose. For more details, please refer to [Dovecot Documentation](https://wiki2.dovecot.org/MailboxFormat).
##### POSTFIX_REJECT_UNKNOWN_CLIENT_HOSTNAME ##### POSTFIX_REJECT_UNKNOWN_CLIENT_HOSTNAME
@ -322,43 +299,15 @@ Note: More information at <https://dovecot.org/doc/dovecot-example.conf>
##### MOVE_SPAM_TO_JUNK ##### MOVE_SPAM_TO_JUNK
- 0 => Spam messages will be delivered to the inbox. When enabled, e-mails marked with the
- **1** => Spam messages will be delivered to the Junk mailbox.
Routes mail identified as spam into the recipient(s) Junk mailbox (_a specialized folder for junk associated to the [special-use flag `\Junk`][docs::dovecot::special-use-flag], handled via a Dovecot sieve script internally_). 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))
[docs::dovecot::special-use-flag]: ../examples/use-cases/imap-folders.md will be automatically moved to the Junk folder (with the help of a Sieve script).
!!! info - 0 => Spam messages will be delivered in the mailbox.
- **1** => Spam messages will be delivered in the `Junk` folder.
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
- **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)_)
##### SPAM_SUBJECT
This variable defines a prefix for e-mails tagged with the `X-Spam: Yes` (Rspamd) or `X-Spam-Flag: YES` (SpamAssassin/Amavis) header.
Default: empty (no prefix will be added to e-mails)
??? example "Including trailing white-space"
Add trailing white-space by quote wrapping the value: `SPAM_SUBJECT='[SPAM] '`
#### Rspamd #### Rspamd
@ -389,19 +338,6 @@ The purpose of this setting is to opt-out of starting an internal Redis instance
- 0 => Disabled - 0 => Disabled
- 1 => Enabled - 1 => Enabled
##### RSPAMD_CHECK_AUTHENTICATED
This settings controls whether checks should be performed on emails coming from authenticated users (i.e. most likely outgoing emails). The default value is `0` in order to align better with SpamAssassin. **We recommend** reading through [the Rspamd documentation on scanning outbound emails][rspamd-scanning-outbound] though to decide for yourself whether you need and want this feature.
!!! note "Not all checks and actions are disabled"
DKIM signing of e-mails will still happen.
- **0** => No checks will be performed for authenticated users
- 1 => All default checks will be performed for authenticated users
[rspamd-scanning-outbound]: https://rspamd.com/doc/tutorials/scanning_outbound.html
##### RSPAMD_GREYLISTING ##### RSPAMD_GREYLISTING
Controls whether the [Rspamd Greylisting module][rspamd-greylisting-module] is enabled. This module can further assist in avoiding spam emails by [greylisting] e-mails with a certain spam score. Controls whether the [Rspamd Greylisting module][rspamd-greylisting-module] is enabled. This module can further assist in avoiding spam emails by [greylisting] e-mails with a certain spam score.
@ -417,9 +353,7 @@ Controls whether the [Rspamd Greylisting module][rspamd-greylisting-module] is e
When enabled, When enabled,
1. the "[autolearning][rspamd-autolearn]" feature is turned on; 1. the "[autolearning][rspamd-autolearn]" feature is turned on;
2. the Bayes classifier will be trained (with the help of Sieve scripts) when moving mails 2. the Bayes classifier will be trained when moving mails from or to the Junk folder (with the help of Sieve scripts).
1. from anywhere to the `Junk` folder (learning this email as spam);
2. from the `Junk` folder into the `INBOX` (learning this email as ham).
!!! warning "Attention" !!! warning "Attention"
@ -434,7 +368,7 @@ When enabled,
##### RSPAMD_HFILTER ##### RSPAMD_HFILTER
Can be used to enable or disable the [Hfilter group module][rspamd-docs-hfilter-group-module]. This is used by DMS to adjust the `HFILTER_HOSTNAME_UNKNOWN` symbol, increasing its default weight to act similar to Postfix's `reject_unknown_client_hostname`, without the need to outright reject a message. Can be used to enable or disable the [Hfilter group module][rspamd-docs-hfilter-group-module]. This is used by DMS to adjust the `HFILTER_HOSTNAME_UNKNOWN` symbol, increasing it's default weight to act similar to Postfix's `reject_unknown_client_hostname`, without the need to outright reject a message.
- 0 => Disabled - 0 => Disabled
- **1** => Enabled - **1** => Enabled
@ -447,17 +381,6 @@ Can be used to control the score when the [`HFILTER_HOSTNAME_UNKNOWN` symbol](#r
Default: 6 (which corresponds to the `add_header` action) Default: 6 (which corresponds to the `add_header` action)
##### RSPAMD_NEURAL
Can be used to enable or disable the [Neural network module][rspamd-docs-neural-network]. This is an experimental anti-spam weigh method using three neural networks in the configuration added here. As far as we can tell it trains itself by using other modules to find out what spam is. It will take a while (a week or more) to train its first neural network. The config trains new networks all the time and discards old networks.
Since it is experimental, it is switched off by default.
- **0** => Disabled
- 1 => Enabled
[rspamd-docs-neural-network]: https://www.rspamd.com/doc/modules/neural.html
#### Reports #### Reports
##### PFLOGSUMM_TRIGGER ##### PFLOGSUMM_TRIGGER
@ -535,18 +458,12 @@ Changes the interval in which log files are rotated.
The entire log output for the container is still available via `docker logs mailserver` (or your respective container name). If you want to configure external log rotation for that container output as well, : [Docker Logging Drivers](https://docs.docker.com/config/containers/logging/configure/). The entire log output for the container is still available via `docker logs mailserver` (or your respective container name). If you want to configure external log rotation for that container output as well, : [Docker Logging Drivers](https://docs.docker.com/config/containers/logging/configure/).
By default, the logs are lost when the container is destroyed (eg: re-creating via `docker compose down && docker compose up -d`). To keep the logs, mount a volume (to `/var/log/mail/`). By default, the logs are lost when the container is destroyed (eg: re-creating via `docker-compose down && docker-compose up -d`). To keep the logs, mount a volume (to `/var/log/mail/`).
!!! note !!! note
This variable can also determine the interval for Postfix's log summary reports, see [`PFLOGSUMM_TRIGGER`](#pflogsumm_trigger). This variable can also determine the interval for Postfix's log summary reports, see [`PFLOGSUMM_TRIGGER`](#pflogsumm_trigger).
##### LOGROTATE_COUNT
Defines how many files are kept by logrotate.
- **4** => Number of files
#### SpamAssassin #### SpamAssassin
##### ENABLE_SPAMASSASSIN ##### ENABLE_SPAMASSASSIN
@ -554,153 +471,63 @@ Defines how many files are kept by logrotate.
- **0** => SpamAssassin is disabled - **0** => SpamAssassin is disabled
- 1 => SpamAssassin is enabled - 1 => SpamAssassin is enabled
??? info "SpamAssassin analyzes incoming mail and assigns a spam score" ##### SPAMASSASSIN_SPAM_TO_INBOX
Integration with Amavis involves processing mail based on the assigned spam score via [`SA_TAG`, `SA_TAG2` and `SA_KILL`][amavis-docs::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`.
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 ##### 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 - **0** => KAM disabled
- 1 => KAM enabled - 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)
- [`SPAM_SUBJECT`](#spam_subject)
##### SA_TAG ##### SA_TAG
- **2.0** => add 'spam info' headers at, or above this spam score - **2.0** => add spam info headers if at, or above that level
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`. Note: this SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1`
??? 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 ##### SA_TAG2
- **6.31** => add 'spam detected' headers at, or above this level - **6.31** => add 'spam detected' headers at that level
When a spam score is high enough, mark mail as spam (_Appends the mail header: `X-Spam-Flag: YES`_). Note: this SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1`
!!! info "Interaction with other ENV"
- [`SPAM_SUBJECT`](#spam_subject) modifies the mail subject to better communicate spam mail to the user.
- [`MOVE_SPAM_TO_JUNK=1`](#move_spam_to_junk): The mail is still delivered, but to the recipient(s) junk folder instead. This feature reduces the usefulness of `SPAM_SUBJECT`.
##### SA_KILL ##### SA_KILL
- **10.0** => quarantine + triggers action to handle spam - **10.0** => triggers spam evasive actions
Controls the spam score threshold for triggering an action on mail that has a high spam score. !!! note "This SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1`"
??? tip "Choosing an appropriate `SA_KILL` value" By default, DMS is configured to quarantine spam emails.
The value should be high enough to be represent confidence in mail as spam: 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`.
- Too low: The action taken may prevent legitimate mail (ham) that was incorrectly detected as spam from being delivered successfully. 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 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"_). ##### SA_SPAM_SUBJECT
[gh-issue::sa-tunables-insights]: https://github.com/docker-mailserver/docker-mailserver/pull/3058#issuecomment-1420268148 - **\*\*\*SPAM\*\*\*** => add tag to subject if spam detected
[gh-issue::sa-tunables-guides]: https://github.com/docker-mailserver/docker-mailserver/pull/3058#issuecomment-1416547911
??? info "Trigger action" Note: this SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1`. Add the SpamAssassin score to the subject line by inserting the keyword \_SCORE\_: **\*\*\*SPAM(\_SCORE\_)\*\*\***.
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:
- Default: `/var/lib/amavis/virusmails/`
- When the [`/var/mail-state/` volume][docs::dms-volumes-state] is present: `/var/mail-state/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_SHORTCIRCUIT_BAYES_SPAM ##### SA_SHORTCIRCUIT_BAYES_SPAM
- **1** => will activate SpamAssassin short circuiting for bayes spam detection. - **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```
!!! warning Note: activate this only if you are confident in your bayes database for identifying spam.
Activate this only if you are confident in your bayes database for identifying spam.
##### SA_SHORTCIRCUIT_BAYES_HAM ##### SA_SHORTCIRCUIT_BAYES_HAM
- **1** => will activate SpamAssassin short circuiting for bayes ham detection - **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```
!!! warning Note: activate this only if you are confident in your bayes database for identifying ham.
Activate this only if you are confident in your bayes database for identifying ham.
#### Fetchmail #### Fetchmail
@ -715,39 +542,17 @@ This will uncomment the respective line in `/etc/spamassasin/local.cf`
##### FETCHMAIL_PARALLEL ##### FETCHMAIL_PARALLEL
- **0** => `fetchmail` runs with a single config file `/etc/fetchmailrc` **0** => `fetchmail` runs with a single config file `/etc/fetchmailrc`
- 1 => `/etc/fetchmailrc` is split per poll entry. For every poll entry a separate fetchmail instance is started to [allow having multiple imap idle connections per server][fetchmail-imap-workaround] (_when poll entries reference the same IMAP server_). **1** => `/etc/fetchmailrc` is split per poll entry. For every poll entry a separate fetchmail instance is started to allow having multiple imap idle configurations defined.
[fetchmail-imap-workaround]: https://otremba.net/wiki/Fetchmail_(Debian)#Immediate_Download_via_IMAP_IDLE
Note: The defaults of your fetchmailrc file need to be at the top of the file. Otherwise it won't be added correctly to all separate `fetchmail` instances. Note: The defaults of your fetchmailrc file need to be at the top of the file. Otherwise it won't be added correctly to all separate `fetchmail` instances.
#### Getmail
##### ENABLE_GETMAIL
Enable or disable `getmail`.
- **0** => Disabled
- 1 => Enabled
##### GETMAIL_POLL
- **5** => `getmail` The number of minutes for the interval. Min: 1; 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
##### ENABLE_LDAP
Deprecated. See [`ACCOUNT_PROVISIONER`](#account_provisioner).
##### LDAP_START_TLS ##### LDAP_START_TLS
- **empty** => no - **empty** => no
@ -756,8 +561,8 @@ Enable or disable `getmail`.
##### LDAP_SERVER_HOST ##### LDAP_SERVER_HOST
- **empty** => mail.example.com - **empty** => mail.example.com
- => Specify the `<dns-name>` / `<ip-address>` where the LDAP server is reachable via a URI like: `ldaps://mail.example.com`. - => Specify the dns-name/ip-address where the ldap-server is listening, or an URI like `ldaps://mail.example.com`
- Note: You must include the desired URI scheme (`ldap://`, `ldaps://`, `ldapi://`). - NOTE: If you going to use DMS in combination with `docker-compose.yml` you can set the service name here
##### LDAP_SEARCH_BASE ##### LDAP_SEARCH_BASE
@ -826,13 +631,14 @@ The following variables overwrite the default values for ```/etc/dovecot/dovecot
##### DOVECOT_DNPASS ##### DOVECOT_DNPASS
- **empty** => same as `LDAP_BIND_PW` - **empty** => same as `LDAP_BIND_PW`
- => Password for LDAP dn specified in `DOVECOT_DN`. - => Password for LDAP dn sepecifified in `DOVECOT_DN`.
##### DOVECOT_URIS ##### DOVECOT_URIS
- **empty** => same as `LDAP_SERVER_HOST` - **empty** => same as `LDAP_SERVER_HOST`
- => Specify a space separated list of LDAP URIs. - => Specify a space separated list of LDAP uris.
- Note: You must include the desired URI scheme (`ldap://`, `ldaps://`, `ldapi://`). - Note: If the protocol is missing, `ldap://` will be used.
- Note: This deprecates `DOVECOT_HOSTS` (as it didn't allow to use LDAPS), which is currently still supported for backwards compatibility.
##### DOVECOT_LDAP_VERSION ##### DOVECOT_LDAP_VERSION
@ -910,26 +716,22 @@ Note: This postgrey setting needs `ENABLE_POSTGREY=1`
##### SASLAUTHD_MECHANISMS ##### SASLAUTHD_MECHANISMS
DMS only implements support for these mechanisms: - **empty** => pam
- `ldap` => authenticate against ldap server
- **`ldap`** => Authenticate against an LDAP server - `shadow` => authenticate against local user db
- `rimap` => Authenticate against an IMAP server - `mysql` => authenticate against mysql db
- `rimap` => authenticate against imap server
- NOTE: can be a list of mechanisms like pam ldap shadow
##### SASLAUTHD_MECH_OPTIONS ##### SASLAUTHD_MECH_OPTIONS
- **empty** => None - **empty** => None
- e.g. with SASLAUTHD_MECHANISMS rimap you need to specify the ip-address/servername of the imap server ==> xxx.xxx.xxx.xxx
!!! info
With `SASLAUTHD_MECHANISMS=rimap` you need to specify the ip-address / servername of the IMAP server, such as `SASLAUTHD_MECH_OPTIONS=127.0.0.1`.
##### SASLAUTHD_LDAP_SERVER ##### SASLAUTHD_LDAP_SERVER
- **empty** => Use the same value as `LDAP_SERVER_HOST` - **empty** => same as `LDAP_SERVER_HOST`
- Note: since version 10.0.0, you can specify a protocol here (like ldaps://); this deprecates SASLAUTHD_LDAP_SSL.
!!! note
You must include the desired URI scheme (`ldap://`, `ldaps://`, `ldapi://`).
##### SASLAUTHD_LDAP_START_TLS ##### SASLAUTHD_LDAP_START_TLS
@ -1029,121 +831,41 @@ you to replace both instead of just the envelope sender.
- **empty** => Derived from [`OVERRIDE_HOSTNAME`](#override_hostname), `$DOMAINNAME` (internal), or the container's hostname - **empty** => Derived from [`OVERRIDE_HOSTNAME`](#override_hostname), `$DOMAINNAME` (internal), or the container's hostname
- Set this if auto-detection fails, isn't what you want, or you wish to have a separate container handle DSNs - Set this if auto-detection fails, isn't what you want, or you wish to have a separate container handle DSNs
#### Relay Host #### Default Relay Host
Supported ENV for the [Relay Host][docs::relay-host] feature.
!!! note "Prefer `DEFAULT_RELAY_HOST` instead of `RELAY_HOST`"
This is advised unless you need support for sender domain opt-out (via `setup relay exclude-domain`).
The implementation for `RELAY_HOST` is not compatible with LDAP.
!!! tip "Opt-in for relay host support"
Enable relaying only for specific sender domains instead by using `setup relay add-domain`.
**NOTE:** Presently there is a caveat when relay host credentials are configured (_which is incompatible with opt-in_).
##### DEFAULT_RELAY_HOST ##### DEFAULT_RELAY_HOST
Configures a default relay host. - **empty** => don't set default relayhost setting in main.cf
- default host and port to relay all mail through.
Format: `[example.com]:587` (don't forget the brackets if you need this to
be compatible with `$RELAY_USER` and `$RELAY_PASSWORD`, explained below).
!!! info #### Multi-domain Relay Hosts
- All mail sent outbound from DMS will be relayed through the configured host, unless sender-dependent relayhost maps have been configured (_which have precedence_).
- The host value may optionally be wrapped in brackets (_skips DNS query for MX record_): `[mail.example.com]:587` vs `example.com:587`
!!! abstract "Technical Details"
This ENV internally configures the Postfix `main.cf` setting: [`relayhost`][postfix-config::relayhost]
##### RELAY_HOST ##### RELAY_HOST
Configures a default relay host. - **empty** => don't configure relay host
- default host to relay mail through
!!! note
Expects a value like `mail.example.com`. Internally this will be wrapped to `[mail.example.com]`, so it should resolve to the MTA directly.
!!! warning "Do not use with `DEFAULT_RELAY_HOST`"
`RELAY_HOST` has precedence as it is configured with `sender_dependent_relayhost_maps`.
!!! info
- This is a legacy ENV. It is however required for the opt-out feature of `postfix-relaymap.cf` to work.
- Internal configuration however differs from `DEFAULT_RELAY_HOST`.
!!! abstract "Technical Details"
This feature is configured internally using the:
- Postfix setting with config: [`sender_dependent_relayhost_maps = texthash:/etc/postfix/relayhost_map`][postfix-config::relayhost_maps]
- DMS Config volume support via: `postfix-relaymap.cf` (_generates `/etc/postfix/relayhost_map`_)
All known mail domains managed by DMS will be configured to relay outbound mail to `RELAY_HOST` by adding them implicitly to `/etc/postfix/relayhost_map`, except for domains using the opt-out feature of `postfix-relaymap.cf`.
##### RELAY_PORT ##### RELAY_PORT
Default => 25 - **empty** => 25
- default port to relay mail through
Support for configuring a different port than 25 for `RELAY_HOST` to use.
!!! note
Requires `RELAY_HOST`.
#### Relay Host Credentials
!!! warning "Configuring relay host credentials enforces outbound authentication"
Presently when `RELAY_USER` + `RELAY_PASSWORD` or `postfix-sasl-password.cf` are configured, all outbound mail traffic is configured to require a secure connection established and forbids the omission of credentials.
Additional feature work is required to only enforce these requirements on mail sent through a configured relay host.
##### RELAY_USER ##### RELAY_USER
- **empty** => no default
- default relay username (if no specific entry exists in postfix-sasl-password.cf)
##### RELAY_PASSWORD ##### RELAY_PASSWORD
Provide the credentials to use with `RELAY_HOST` or `DEFAULT_RELAY_HOST`. - **empty** => no default
- password for default relay user
!!! tip "Alternative credentials config"
You may prefer to use `setup relay add-auth` to avoid risking ENV exposing secrets.
- With the CLI command, you must provide relay credentials for each of your sender domains.
- Alternatively manually edit `postfix-sasl-password.cf` with the correct relayhost entry (_`DEFAULT_RELAY_HOST` value, or as defined in `/etc/postfix/relayhost_map`_) to provide credentials per relayhost configured.
!!! abstract "Technical Details"
Credentials for relay hosts are configured internally using the:
- Postfix setting with config: [`smtp_sasl_password_maps = texthash:/etc/postfix/sasl_passwd`][postfix-config::sasl_passwd]
- DMS Config volume support via: `postfix-sasl-password.cf` (_generates `/etc/postfix/sasl_passwd`_)
---
When `postfix-sasl-password.cf` is present, DMS will copy it internally to `/etc/postfix/sasl_passwd`.
- DMS provides support for mapping credentials by sender domain:
- Explicitly via `setup relay add-auth` (_creates / updates `postfix-sasl-password.cf`_).
- Implicitly via the relay ENV support (_configures all known DMS managed domains to use the relay ENV_).
- Credentials can be explicitly configured for specific relay hosts instead of sender domains:
- Add the exact relayhost value (`host:port` / `[host]:port`) from the generated `/etc/postfix/relayhost_map`, or `main.cf:relayhost` (`DEFAULT_RELAY_HOST`).
- `setup relay ...` is missing support, you must instead add these manually to `postfix-sasl-password.cf`.
[gh-issue::vmail-uid-cannot-be-root]: https://github.com/docker-mailserver/docker-mailserver/issues/4098#issuecomment-2257201025
[docs-rspamd]: ./security/rspamd.md [docs-rspamd]: ./security/rspamd.md
[docs-faq-onedir]: ../faq.md#what-about-docker-datadmsmail-state-folder-varmail-state-internally
[docs-tls]: ./security/ssl.md [docs-tls]: ./security/ssl.md
[docs-tls-letsencrypt]: ./security/ssl.md#lets-encrypt-recommended [docs-tls-letsencrypt]: ./security/ssl.md#lets-encrypt-recommended
[docs-tls-manual]: ./security/ssl.md#bring-your-own-certificates [docs-tls-manual]: ./security/ssl.md#bring-your-own-certificates
[docs-tls-selfsigned]: ./security/ssl.md#self-signed-certificates [docs-tls-selfsigned]: ./security/ssl.md#self-signed-certificates
[docs-accounts-quota]: ./account-management/overview.md#quotas [docs-accounts-quota]: ./user-management.md#quotas
[docs::account-management::overview]: ./account-management/overview.md
[docs::relay-host]: ./advanced/mail-forwarding/relay-hosts.md
[docs::dms-volumes-state]: ./advanced/optional-config.md#volumes-state
[postfix-config::relayhost]: https://www.postfix.org/postconf.5.html#relayhost
[postfix-config::relayhost_maps]: https://www.postfix.org/postconf.5.html#sender_dependent_relayhost_maps
[postfix-config::sasl_passwd]: https://www.postfix.org/postconf.5.html#smtp_sasl_password_maps

View File

@ -4,7 +4,7 @@ hide:
- toc # Hide Table of Contents for this page - toc # Hide Table of Contents for this page
--- ---
If you want to use POP3(S), you have to add the ports 110 and/or 995 (TLS secured) and the environment variable `ENABLE_POP3` to your `compose.yaml`: If you want to use POP3(S), you have to add the ports 110 and/or 995 (TLS secured) and the environment variable `ENABLE_POP3` to your `docker-compose.yml`:
```yaml ```yaml
mailserver: mailserver:

View File

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

View File

@ -30,7 +30,7 @@ Official Dovecot documentation: https://doc.dovecot.org/configuration_manual/mai
} }
``` ```
2. Shutdown your mailserver (`docker compose down`) 2. Shutdown your mailserver (`docker-compose down`)
3. You then need to [generate your global EC key](https://doc.dovecot.org/configuration_manual/mail_crypt_plugin/#ec-key). We named them `/certs/ecprivkey.pem` and `/certs/ecpubkey.pem` in step #1. 3. You then need to [generate your global EC key](https://doc.dovecot.org/configuration_manual/mail_crypt_plugin/#ec-key). We named them `/certs/ecprivkey.pem` and `/certs/ecpubkey.pem` in step #1.
@ -45,7 +45,7 @@ Official Dovecot documentation: https://doc.dovecot.org/configuration_manual/mai
. . . . . .
``` ```
5. While you're editing the `compose.yaml`, add the configuration file: 5. While you're editing the `docker-compose.yml`, add the configuration file:
```yaml ```yaml
services: services:
mailserver: mailserver:

View File

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

View File

@ -6,7 +6,7 @@ There are multiple options to enable SSL (via [`SSL_TYPE`][docs-env::ssl-type]):
- Using [letsencrypt](#lets-encrypt-recommended) (recommended) - Using [letsencrypt](#lets-encrypt-recommended) (recommended)
- Using [Caddy](#caddy) - Using [Caddy](#caddy)
- Using [Traefik](#traefik) - Using [Traefik](#traefik-v2)
- Using [self-signed certificates](#self-signed-certificates) - Using [self-signed certificates](#self-signed-certificates)
- Using [your own certificates](#bring-your-own-certificates) - Using [your own certificates](#bring-your-own-certificates)
@ -44,7 +44,7 @@ An [FQDN](https://en.wikipedia.org/wiki/Fully_qualified_domain_name) (_Fully Qua
or or
```yml ```yml
# compose.yaml # docker-compose.yml
services: services:
mailserver: mailserver:
hostname: mail.example.com hostname: mail.example.com
@ -72,7 +72,7 @@ You don't have to do anything else. Enjoy!
!!! example !!! example
Add these additions to the `mailserver` service in your [`compose.yaml`][github-file-compose]: Add these additions to the `mailserver` service in your [`docker-compose.yml`][github-file-compose]:
```yaml ```yaml
services: services:
@ -103,7 +103,7 @@ Certbot provisions certificates to `/etc/letsencrypt`. Add a volume to store the
!!! example !!! example
Add these additions to the `mailserver` service in your [`compose.yaml`][github-file-compose]: Add these additions to the `mailserver` service in your [`docker-compose.yml`][github-file-compose]:
```yaml ```yaml
services: services:
@ -135,8 +135,6 @@ Certbot provisions certificates to `/etc/letsencrypt`. Add a volume to store the
This process can also be [automated via _cron_ or _systemd timers_][certbot::automated-renewal]. This process can also be [automated via _cron_ or _systemd timers_][certbot::automated-renewal].
- [Example with a systemd timer][certbot::automated-renewal::example-systemd-timer]
!!! note "Using a different ACME CA" !!! note "Using a different ACME CA"
Certbot does support [alternative certificate providers via the `--server`][certbot::custom-ca] option. In most cases you'll want to use the default _Let's Encrypt_. Certbot does support [alternative certificate providers via the `--server`][certbot::custom-ca] option. In most cases you'll want to use the default _Let's Encrypt_.
@ -163,10 +161,9 @@ Obtain a Cloudflare API token:
dns_cloudflare_api_token = YOUR_CLOUDFLARE_TOKEN_HERE dns_cloudflare_api_token = YOUR_CLOUDFLARE_TOKEN_HERE
``` ```
- As this is sensitive data, you should restrict access to it with `chmod 600` and `chown 0:0`. - As this is sensitive data, you should restrict access to it with `chmod 600` and `chown 0:0`.
- Store the file in a folder if you like, such as `docker-data/certbot/secrets/`. - Store the file in a folder if you like, such as `docker-data/certbot/secrets/`.
5. Your `docker-compose.yml` should include the following:
5. Your `compose.yaml` should include the following:
```yaml ```yaml
services: services:
@ -209,7 +206,7 @@ Obtain a Cloudflare API token:
6. Run the service to provision a certificate: 6. Run the service to provision a certificate:
```sh ```sh
docker compose run certbot-cloudflare docker-compose run certbot-cloudflare
``` ```
7. You should see the following log output: 7. You should see the following log output:
@ -223,7 +220,7 @@ Obtain a Cloudflare API token:
This certificate expires on YYYY-MM-DD. This certificate expires on YYYY-MM-DD.
These files will be updated when the certificate renews. These files will be updated when the certificate renews.
NEXT STEPS: NEXT STEPS:
- The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal instructions. - The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal structions.
``` ```
After completing the steps above, your certificate should be ready to use. After completing the steps above, your certificate should be ready to use.
@ -232,7 +229,7 @@ After completing the steps above, your certificate should be ready to use.
We've only demonstrated how to provision a certificate, but it will expire in 90 days and need to be renewed before then. We've only demonstrated how to provision a certificate, but it will expire in 90 days and need to be renewed before then.
In the following example, add a new service (`certbot-cloudflare-renew`) into `compose.yaml` that will handle certificate renewals: In the following example, add a new service (`certbot-cloudflare-renew`) into `docker-compose.yml` that will handle certificate renewals:
```yml ```yml
services: services:
@ -240,7 +237,7 @@ After completing the steps above, your certificate should be ready to use.
image: certbot/dns-cloudflare:latest image: certbot/dns-cloudflare:latest
command: renew --dns-cloudflare --dns-cloudflare-credentials /run/secrets/cloudflare-api-token command: renew --dns-cloudflare --dns-cloudflare-credentials /run/secrets/cloudflare-api-token
volumes: volumes:
- ./docker-data/certbot/certs/:/etc/letsencrypt/ - ./docker-data/certbot/certs/:/etc/letsencrtypt/
- ./docker-data/certbot/logs/:/var/log/letsencrypt/ - ./docker-data/certbot/logs/:/var/log/letsencrypt/
secrets: secrets:
- cloudflare-api-token - cloudflare-api-token
@ -250,7 +247,7 @@ After completing the steps above, your certificate should be ready to use.
You can manually run this service to renew the cert within 90 days: You can manually run this service to renew the cert within 90 days:
```sh ```sh
docker compose run certbot-cloudflare-renew docker-compose run certbot-cloudflare-renew
``` ```
You should see the following output You should see the following output
@ -276,7 +273,7 @@ After completing the steps above, your certificate should be ready to use.
(`crontab` example: Checks every day if the certificate should be renewed) (`crontab` example: Checks every day if the certificate should be renewed)
```sh ```sh
0 0 * * * docker compose -f PATH_TO_YOUR_DOCKER_COMPOSE_YML up certbot-cloudflare-renew 0 0 * * * docker-compose -f PATH_TO_YOUR_DOCKER_COMPOSE_YML up certbot-cloudflare-renew
``` ```
#### Example using `nginx-proxy` and `acme-companion` with Docker { data-toc-label='nginx-proxy with Docker' } #### Example using `nginx-proxy` and `acme-companion` with Docker { data-toc-label='nginx-proxy with Docker' }
@ -330,7 +327,7 @@ In the following example, we show how DMS can be run alongside the docker contai
You may want to add `--env LETSENCRYPT_TEST=true` to the above while testing, to avoid the _Let's Encrypt_ certificate generation rate limits. You may want to add `--env LETSENCRYPT_TEST=true` to the above while testing, to avoid the _Let's Encrypt_ certificate generation rate limits.
5. Make sure your mount path to the `letsencrypt` certificates directory is correct. Edit your `compose.yaml` for the `mailserver` service to have volumes added like below: 5. Make sure your mount path to the `letsencrypt` certificates directory is correct. Edit your `docker-compose.yml` for the `mailserver` service to have volumes added like below:
```yaml ```yaml
volumes: volumes:
@ -340,15 +337,15 @@ In the following example, we show how DMS can be run alongside the docker contai
- ./docker-data/acme-companion/certs/:/etc/letsencrypt/live/:ro - ./docker-data/acme-companion/certs/:/etc/letsencrypt/live/:ro
``` ```
6. Then from the `compose.yaml` project directory, run: `docker compose up -d mailserver`. 6. Then from the `docker-compose.yml` project directory, run: `docker-compose up -d mailserver`.
#### Example using `nginx-proxy` and `acme-companion` with `docker-compose` { data-toc-label='nginx-proxy with docker-compose' } #### Example using `nginx-proxy` and `acme-companion` with `docker-compose` { data-toc-label='nginx-proxy with docker-compose' }
The following example is the [basic setup][acme-companion::basic-setup] you need for using `nginx-proxy` and `acme-companion` with DMS (_Referencing: [`acme-companion` documentation][acme-companion::docs]_): The following example is the [basic setup][acme-companion::basic-setup] you need for using `nginx-proxy` and `acme-companion` with DMS (_Referencing: [`acme-companion` documentation][acme-companion::docs]_):
???+ example "Example: `compose.yaml`" ???+ example "Example: `docker-compose.yml`"
You should have an existing `compose.yaml` with a `mailserver` service. Below are the modifications to add for integrating with `nginx-proxy` and `acme-companion` services: You should have an existing `docker-compose.yml` with a `mailserver` service. Below are the modifications to add for integrating with `nginx-proxy` and `acme-companion` services:
```yaml ```yaml
services: services:
@ -388,7 +385,7 @@ The following example is the [basic setup][acme-companion::basic-setup] you need
container_name: nginx-proxy-acme container_name: nginx-proxy-acme
restart: always restart: always
environment: environment:
# When `volumes_from: [nginx-proxy]` is not supported, # Only docker-compose v2 supports: `volumes_from: [nginx-proxy]`,
# reference the _reverse-proxy_ `container_name` here: # reference the _reverse-proxy_ `container_name` here:
- NGINX_PROXY_CONTAINER=nginx-proxy - NGINX_PROXY_CONTAINER=nginx-proxy
volumes: volumes:
@ -410,7 +407,7 @@ The following example is the [basic setup][acme-companion::basic-setup] you need
- `LETSENCRYPT_TEST=true`: _Recommended during initial setup_. Otherwise the default production endpoint has a [rate limit of 5 duplicate certificates per week][letsencrypt::limits]. Overrides `ACME_CA_URI` to use the _Let's Encrypt_ staging endpoint. - `LETSENCRYPT_TEST=true`: _Recommended during initial setup_. Otherwise the default production endpoint has a [rate limit of 5 duplicate certificates per week][letsencrypt::limits]. Overrides `ACME_CA_URI` to use the _Let's Encrypt_ staging endpoint.
- `LETSENCRYPT_EMAIL`: For when you don't use `DEFAULT_EMAIL` on `acme-companion`, or want to assign a different email contact for this container. - `LETSENCRYPT_EMAIL`: For when you don't use `DEFAULT_EMAIL` on `acme-companion`, or want to assign a different email contact for this container.
- `LETSENCRYPT_KEYSIZE`: Allows you to configure the type (RSA or ECDSA) and size of the private key for your certificate. Default is RSA 4096, but RSA 2048 is recommended. - `LETSENCRYPT_KEYSIZE`: Allows you to configure the type (RSA or ECDSA) and size of the private key for your certificate. Default is RSA 4096.
- `LETSENCRYPT_RESTART_CONTAINER=true`: When the certificate is renewed, the entire container will be restarted to ensure the new certificate is used. - `LETSENCRYPT_RESTART_CONTAINER=true`: When the certificate is renewed, the entire container will be restarted to ensure the new certificate is used.
[`acme-companion` ENV for default settings][acme-companion::env-config] that apply to all containers using `LETSENCRYPT_HOST`: [`acme-companion` ENV for default settings][acme-companion::env-config] that apply to all containers using `LETSENCRYPT_HOST`:
@ -452,8 +449,8 @@ The following example is the [basic setup][acme-companion::basic-setup] you need
# Optional variables: # Optional variables:
LETSENCRYPT_mail_TEST=true LETSENCRYPT_mail_TEST=true
LETSENCRYPT_mail_EMAIL='admin@example.com' LETSENCRYPT_mail_EMAIL='admin@example.com'
# Supported values are `2048`, `3072` and `4096` for RSA keys, and `ec-256` or `ec-384` for elliptic curve keys. # RSA-4096 => `4096`, ECDSA-256 => `ec-256`:
LETSENCRYPT_mail_KEYSIZE=2048 LETSENCRYPT_mail_KEYSIZE=4096
``` ```
Unlike with the equivalent ENV for containers, [changes to this file will **not** be detected automatically][acme-companion::standalone-changes]. You would need to wait until the next renewal check by `acme-companion` (_every hour by default_), restart `acme-companion`, or [manually invoke the _service loop_][acme-companion::service-loop]: Unlike with the equivalent ENV for containers, [changes to this file will **not** be detected automatically][acme-companion::standalone-changes]. You would need to wait until the next renewal check by `acme-companion` (_every hour by default_), restart `acme-companion`, or [manually invoke the _service loop_][acme-companion::service-loop]:
@ -466,7 +463,7 @@ Version 6.2 and later of the Synology NAS DSM OS now come with an interface to g
Amongst other things, you can use these to secure your mail server. DSM locates the generated certificates in a folder below `/usr/syno/etc/certificate/_archive/`. Amongst other things, you can use these to secure your mail server. DSM locates the generated certificates in a folder below `/usr/syno/etc/certificate/_archive/`.
Navigate to that folder and note the 6 character random folder name of the certificate you'd like to use. Then, add the following to your `compose.yaml` declaration file: Navigate to that folder and note the 6 character random folder name of the certificate you'd like to use. Then, add the following to your `docker-compose.yml` declaration file:
```yaml ```yaml
volumes: volumes:
@ -481,133 +478,114 @@ DSM-generated letsencrypt certificates get auto-renewed every three months.
### Caddy ### Caddy
[Caddy][web::caddy] is an open-source web server with built-in TLS certificate generation. You can use the [official Docker image][dockerhub::caddy] and write your own `Caddyfile`. For Caddy v2 you can specify the `key_type` in your server's global settings, which would end up looking something like this if you're using a `Caddyfile`:
!!! example ```caddyfile
{
debug
admin localhost:2019
http_port 80
https_port 443
default_sni example.com
key_type rsa4096
}
```
While DMS does not need a webserver to work, this workaround will provision a TLS certificate for DMS to use by adding a dummy site block to trigger cert provisioning. If you are instead using a json config for Caddy v2, you can set it in your site's TLS automation policies:
```yaml title="compose.yaml" ??? example "Caddy v2 JSON example snippet"
services:
# Basic Caddy service to provision certs:
reverse-proxy:
image: caddy:2.7
ports:
- 80:80
- 443:443
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ${CADDY_DATA_DIR}:/data
# Share the Caddy data volume for certs and configure SSL_TYPE to `letsencrypt` ```json
mailserver: {
image: ghcr.io/docker-mailserver/docker-mailserver:latest "apps": {
hostname: mail.example.com "http": {
environment: "servers": {
SSL_TYPE: letsencrypt "srv0": {
# While you could use a named data volume instead of a bind mount volume, it would require the long-syntax to rename cert files: "listen": [
# https://docs.docker.com/compose/compose-file/05-services/#volumes ":443"
volumes: ],
- ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.crt:/etc/letsencrypt/live/mail.example.com/fullchain.pem "routes": [
- ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.key:/etc/letsencrypt/live/mail.example.com/privkey.pem {
``` "match": [
{
An explicit entry in your `Caddyfile` config will have Caddy provision and renew a certificate for your DMS FQDN: "host": [
"mail.example.com",
```caddyfile title="Caddyfile" ]
mail.example.com { }
# Optionally provision RSA 2048-bit certificate instead of ECDSA P-256: ],
tls { "handle": [
key_type rsa2048 {
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
},
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"mail.example.com",
],
"key_type": "rsa2048",
"issuer": {
"email": "admin@example.com",
"module": "acme"
}
},
{
"issuer": {
"email": "admin@example.com",
"module": "acme"
}
}
]
}
}
} }
# Optional, can be useful for troubleshooting
# connection to Caddy with correct certificate:
respond "Hello DMS"
} }
``` ```
!!! info The generated certificates can then be mounted:
An explicit `tls` directive affects only the site-address block it's used in: ```yaml
volumes:
- ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.crt:/etc/letsencrypt/live/mail.example.com/fullchain.pem
- ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.key:/etc/letsencrypt/live/mail.example.com/privkey.pem
```
- Use [`tls internal { ... }`][caddy-docs::tls-internal] if wanting to create a local self-signed cert, which may be useful for testing. This allows opt-in to use self-signed certs unlike the global `local_certs` option. ### Traefik v2
- [`key_type`][caddy-docs::key-type] can be used in the `tls` block if you need to enforce RSA as the key type for certificates provisioned. The default is currently ECDSA (P-256). This may improve compatibility with legacy clients.
??? example "With `caddy-docker-proxy`" [Traefik][traefik::github] is an open-source application proxy using the [ACME protocol][ietf::rfc::acme]. [Traefik][traefik::github] can request certificates for domains and subdomains, and it will take care of renewals, challenge negotiations, etc. We strongly recommend to use [Traefik][traefik::github]'s major version 2.
Using [`lucaslorentz/caddy-docker-proxy`][github::caddy-docker-proxy] allows you to generate a `Caddyfile` by adding labels to your services in `compose.yaml`: [Traefik][traefik::github]'s storage format is natively supported if the `acme.json` store is mounted into the container at `/etc/letsencrypt/acme.json`. The file is also monitored for changes and will trigger a reload of the mail services (Postfix and Dovecot).
```yaml title="compose.yaml" Wildcard certificates are supported. If your FQDN is `mail.example.com` and your wildcard certificate is `*.example.com`, add the ENV: `#!bash SSL_DOMAIN=example.com`.
services:
reverse-proxy:
image: lucaslorentz/caddy-docker-proxy:2.8
ports:
- 80:80
- 443:443
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ${CADDY_DATA_DIR}:/data
labels:
# Set global config here, this option has an empty value to enable self-signed certs for local testing:
# NOTE: Remove this label when going to production.
caddy.local_certs: ""
# Use labels to configure Caddy to provision DMS certs DMS will select it's certificate from `acme.json` checking these ENV for a matching FQDN (_in order of priority_):
mailserver:
image: ghcr.io/docker-mailserver/docker-mailserver:latest
hostname: mail.example.com
environment:
SSL_TYPE: letsencrypt
volumes:
- ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.crt:/etc/letsencrypt/live/mail.example.com/fullchain.pem
- ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.key:/etc/letsencrypt/live/mail.example.com/privkey.pem
labels:
# Set your DMS FQDN here to add the site-address into the generated Caddyfile:
caddy_0: mail.example.com
# Adding a dummy directive is required:
caddy_0.respond: "Hello DMS"
# Uncomment to make a proxy for Rspamd:
# caddy_1: rspamd.example.com
# caddy_1.reverse_proxy: "{{upstreams 11334}}"
```
!!! warning "Caddy certificate location varies" 1. `#!bash ${SSL_DOMAIN}`
2. `#!bash ${HOSTNAME}`
3. `#!bash ${DOMAINNAME}`
The path contains the certificate provisioner used. This path may be different from the example above for you and may change over time when [multiple ACME provisioner services are used][dms-pr-feedback::caddy-provisioning-gotcha]. This setup only comes with one caveat: The domain has to be configured on another service for [Traefik][traefik::github] to actually request it from _Let's Encrypt_, i.e. [Traefik][traefik::github] will not issue a certificate without a service / router demanding it.
This can make the volume mounting for DMS to find the certificates non-deterministic, but you can [restrict provisioning to single service via the `acme_ca` setting][caddy::restrict-acme-provisioner].
---
**NOTE:** Bind mounting a file directly instead of a directory will mount by inode. If the file is updated at renewal and this modifies the inode on the host system, then the container will still point to the old certificate.
If this happens, consider using our manual TLS type instead:
```yaml title="compose.yaml"
services:
mailserver:
environment:
SSL_TYPE: manual
SSL_CERT_PATH: /srv/tls/mail.example.com/mail.example.com.crt
SSL_KEY_PATH: /srv/tls/mail.example.com/mail.example.com.key
volumes:
- ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/:/srv/tls/mail.example.com/:ro
```
### Traefik
[Traefik][traefik::github] is an open-source application proxy using the [ACME protocol][ietf::rfc::acme]. Traefik can request certificates for domains and subdomains, and it will take care of renewals, challenge negotiations, etc.
Traefik's storage format is natively supported if the `acme.json` store is mounted into the container at `/etc/letsencrypt/acme.json`. The file is also monitored for changes and will trigger a reload of the mail services (Postfix and Dovecot).
DMS will select it's certificate from `acme.json` prioritizing a match for the DMS FQDN (hostname), while also checking one DNS level up (_eg: `mail.example.com` => `example.com`_). Wildcard certificates are supported.
This setup only comes with one caveat - The domain has to be configured on another service for Traefik to actually request it from _Let's Encrypt_ (_i.e. Traefik will not issue a certificate without a service / router demanding it_).
???+ example "Example Code" ???+ example "Example Code"
Here is an example setup for [`docker-compose`](https://docs.docker.com/compose/):
Here is an example setup for [`Docker Compose`](https://docs.docker.com/compose/):
```yaml ```yaml
services: services:
@ -616,7 +594,7 @@ This setup only comes with one caveat - The domain has to be configured on anoth
container_name: mailserver container_name: mailserver
hostname: mail.example.com hostname: mail.example.com
volumes: volumes:
- ./docker-data/traefik/acme.json:/etc/letsencrypt/acme.json:ro - ./docker-data/traefik/acme.json:/etc/letsencrypt/acme.json:ro
environment: environment:
SSL_TYPE: letsencrypt SSL_TYPE: letsencrypt
SSL_DOMAIN: mail.example.com SSL_DOMAIN: mail.example.com
@ -627,26 +605,26 @@ This setup only comes with one caveat - The domain has to be configured on anoth
image: docker.io/traefik:latest #v2.5 image: docker.io/traefik:latest #v2.5
container_name: docker-traefik container_name: docker-traefik
ports: ports:
- "80:80" - "80:80"
- "443:443" - "443:443"
command: command:
- --providers.docker - --providers.docker
- --entrypoints.http.address=:80 - --entrypoints.http.address=:80
- --entrypoints.http.http.redirections.entryPoint.to=https - --entrypoints.http.http.redirections.entryPoint.to=https
- --entrypoints.http.http.redirections.entryPoint.scheme=https - --entrypoints.http.http.redirections.entryPoint.scheme=https
- --entrypoints.https.address=:443 - --entrypoints.https.address=:443
- --entrypoints.https.http.tls.certResolver=letsencrypt - --entrypoints.https.http.tls.certResolver=letsencrypt
- --certificatesresolvers.letsencrypt.acme.email=admin@example.com - --certificatesresolvers.letsencrypt.acme.email=admin@example.com
- --certificatesresolvers.letsencrypt.acme.storage=/acme.json - --certificatesresolvers.letsencrypt.acme.storage=/acme.json
- --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http
volumes: volumes:
- ./docker-data/traefik/acme.json:/acme.json - ./docker-data/traefik/acme.json:/acme.json
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
whoami: whoami:
image: docker.io/traefik/whoami:latest image: docker.io/traefik/whoami:latest
labels: labels:
- "traefik.http.routers.whoami.rule=Host(`mail.example.com`)" - "traefik.http.routers.whoami.rule=Host(`mail.example.com`)"
``` ```
### Self-Signed Certificates ### Self-Signed Certificates
@ -655,7 +633,7 @@ This setup only comes with one caveat - The domain has to be configured on anoth
Use self-signed certificates only for testing purposes! Use self-signed certificates only for testing purposes!
This feature requires you to provide the following files into your [`docker-data/dms/config/ssl/` directory][docs::dms-volumes-config] (_internal location: `/tmp/docker-mailserver/ssl/`_): This feature requires you to provide the following files into your [`docker-data/dms/config/ssl/` directory][docs-optional-config] (_internal location: `/tmp/docker-mailserver/ssl/`_):
- `<FQDN>-key.pem` - `<FQDN>-key.pem`
- `<FQDN>-cert.pem` - `<FQDN>-cert.pem`
@ -711,7 +689,7 @@ docker run --rm -it \
### Bring Your Own Certificates ### Bring Your Own Certificates
You can also provide your own certificate files. Add these entries to your `compose.yaml`: You can also provide your own certificate files. Add these entries to your `docker-compose.yml`:
```yaml ```yaml
volumes: volumes:
@ -735,7 +713,7 @@ The local and internal paths may be whatever you prefer, so long as both `SSL_CE
## Testing a Certificate is Valid ## Testing a Certificate is Valid
!!! example "Connect to DMS on port 25" - From your host:
```sh ```sh
docker exec mailserver openssl s_client \ docker exec mailserver openssl s_client \
@ -744,42 +722,26 @@ The local and internal paths may be whatever you prefer, so long as both `SSL_CE
-CApath /etc/ssl/certs/ -CApath /etc/ssl/certs/
``` ```
The response should show the certificate chain with a line further down: `Verify return code: 0 (ok)` - Or:
---
This example runs within the DMS container itself to verify the cert is working locally.
- Adjust the `-connect` IP if testing externally from another system. Additionally testing for port 143 (Dovecot IMAP) is encouraged (_change the protocol for `-starttls` from `smtp` to `imap`_).
- `-CApath` will help verify the certificate chain, provided the location contains the root CA that signed your TLS cert for DMS.
??? example "Verify certificate dates"
```sh ```sh
docker exec mailserver openssl s_client \ docker exec mailserver openssl s_client \
-connect 0.0.0.0:25 \ -connect 0.0.0.0:143 \
-starttls smtp \ -starttls imap \
-CApath /etc/ssl/certs/ \ -CApath /etc/ssl/certs/
2>/dev/null | openssl x509 -noout -dates
``` ```
!!! tip "Testing and troubleshooting" And you should see the certificate chain, the server certificate and: `Verify return code: 0 (ok)`
If you need to test a connection without resolving DNS, `curl` can connect with `--resolve` option to map an FQDN + Port to an IP address, instead of the request address provided. In addition, to verify certificate dates:
```bash ```sh
# NOTE: You may want to use `--insecure` if the cert was provisioned with a private CA not present on the curl client: docker exec mailserver openssl s_client \
# Use `--verbose` for additional insights on the connection. -connect 0.0.0.0:25 \
curl --resolve mail.example.com:443:127.0.0.1 https://mail.example.com -starttls smtp \
``` -CApath /etc/ssl/certs/ \
2>/dev/null | openssl x509 -noout -dates
Similarly with `openssl` you can connect to an IP as shown previously, but provide an explicit SNI if necessary with `-servername mail.example.com`. ```
---
Both `curl` and `openssl` also support `-4` and `-6` for enforcing IPv4 or IPv6 lookup.
This can be useful, such as when [DNS resolves the IP to different servers leading to different certificates returned][dms-discussion::gotcha-fqdn-bad-dns]. As shown in that link, `step certificate inspect` is also handy for viewing details of the cert returned or on disk.
## Plain-Text Access ## Plain-Text Access
@ -873,7 +835,7 @@ You can of course run the script by cron once a week or something. In that way y
```sh ```sh
# This script is run inside docker-mailserver via 'docker exec ...', using the 'mail' command to send alerts. # This script is run inside docker-mailserver via 'docker exec ...', using the 'mail' command to send alerts.
## code below will alert if certificate expires in less than two weeks ## code below will alert if certificate expires in less than two weeks
## please adjust variables! ## please adjust varables!
## make sure the 'mail -s' command works! Test! ## make sure the 'mail -s' command works! Test!
export SITE_URL="mail.example.com" export SITE_URL="mail.example.com"
@ -913,10 +875,10 @@ By default DMS uses [`ffdhe4096`][ffdhe4096-src] from [IETF RFC 7919][ietf::rfc:
Despite this, if you must use non-standard DH parameters or you would like to swap `ffdhe4096` for a different group (eg `ffdhe2048`); Add your own PEM encoded DH params file via a volume to `/tmp/docker-mailserver/dhparams.pem`. This will replace DH params for both Dovecot and Postfix services during container startup. Despite this, if you must use non-standard DH parameters or you would like to swap `ffdhe4096` for a different group (eg `ffdhe2048`); Add your own PEM encoded DH params file via a volume to `/tmp/docker-mailserver/dhparams.pem`. This will replace DH params for both Dovecot and Postfix services during container startup.
[docs-env::ssl-type]: ../environment.md#ssl_type [docs-env::ssl-type]: ../environment.md#ssl_type
[docs::dms-volumes-config]: ../advanced/optional-config.md#volumes-config [docs-optional-config]: ../advanced/optional-config.md
[docs-faq-baredomain]: ../../faq.md#can-i-use-a-nakedbare-domain-ie-no-hostname [docs-faq-baredomain]: ../../faq.md#can-i-use-a-nakedbare-domain-ie-no-hostname
[github-file-compose]: https://github.com/docker-mailserver/docker-mailserver/blob/master/compose.yaml [github-file-compose]: https://github.com/docker-mailserver/docker-mailserver/blob/master/docker-compose.yml
[github-file::tls-readme]: https://github.com/docker-mailserver/docker-mailserver/blob/3b8059f2daca80d967635e04d8d81e9abb755a4d/test/test-files/ssl/example.test/README.md [github-file::tls-readme]: https://github.com/docker-mailserver/docker-mailserver/blob/3b8059f2daca80d967635e04d8d81e9abb755a4d/test/test-files/ssl/example.test/README.md
[hanscees-renewcerts]: https://github.com/hanscees/dockerscripts/blob/master/scripts/tomav-renew-certs [hanscees-renewcerts]: https://github.com/hanscees/dockerscripts/blob/master/scripts/tomav-renew-certs
@ -940,7 +902,6 @@ Despite this, if you must use non-standard DH parameters or you would like to sw
[certbot::standalone]: https://certbot.eff.org/docs/using.html#standalone [certbot::standalone]: https://certbot.eff.org/docs/using.html#standalone
[certbot::renew]: https://certbot.eff.org/docs/using.html#renewing-certificates [certbot::renew]: https://certbot.eff.org/docs/using.html#renewing-certificates
[certbot::automated-renewal]: https://certbot.eff.org/docs/using.html#automated-renewals [certbot::automated-renewal]: https://certbot.eff.org/docs/using.html#automated-renewals
[certbot::automated-renewal::example-systemd-timer]: https://github.com/orgs/docker-mailserver/discussions/3917#discussioncomment-8661690
[certbot::custom-ca]: https://certbot.eff.org/docs/using.htmlchanging-the-acme-server [certbot::custom-ca]: https://certbot.eff.org/docs/using.htmlchanging-the-acme-server
[certbot::webroot]: https://certbot.eff.org/docs/using.html#webroot [certbot::webroot]: https://certbot.eff.org/docs/using.html#webroot
@ -954,13 +915,3 @@ Despite this, if you must use non-standard DH parameters or you would like to sw
[acme-companion::standalone]: https://github.com/nginx-proxy/acme-companion/blob/main/docs/Standalone-certificates.md [acme-companion::standalone]: https://github.com/nginx-proxy/acme-companion/blob/main/docs/Standalone-certificates.md
[acme-companion::standalone-changes]: https://github.com/nginx-proxy/acme-companion/blob/main/docs/Standalone-certificates.md#picking-up-changes-to-letsencrypt_user_data [acme-companion::standalone-changes]: https://github.com/nginx-proxy/acme-companion/blob/main/docs/Standalone-certificates.md#picking-up-changes-to-letsencrypt_user_data
[acme-companion::service-loop]: https://github.com/nginx-proxy/acme-companion/blob/main/docs/Container-utilities.md [acme-companion::service-loop]: https://github.com/nginx-proxy/acme-companion/blob/main/docs/Container-utilities.md
[web::caddy]: https://caddyserver.com
[dockerhub::caddy]: https://hub.docker.com/_/caddy
[github::caddy-docker-proxy]: https://github.com/lucaslorentz/caddy-docker-proxy
[dms-pr-feedback::caddy-provisioning-gotcha]: https://github.com/docker-mailserver/docker-mailserver/pull/3485/files#r1297512818
[caddy-docs::tls-internal]: https://caddyserver.com/docs/caddyfile/directives/tls#syntax
[caddy-docs::key-type]: https://caddyserver.com/docs/caddyfile/options#key-type
[caddy::restrict-acme-provisioner]: https://caddy.community/t/is-there-a-way-on-a-caddyfile-to-force-a-specific-acme-ca/14506
[dms-discussion::gotcha-fqdn-bad-dns]: https://github.com/docker-mailserver/docker-mailserver/issues/3955#issuecomment-2027882633

View File

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

View File

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

View File

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

View File

@ -20,18 +20,6 @@ By raising issues, I agree to these terms and I understand, that the rules set f
Maintainers take the time to improve on this project and help by solving issues together. It is therefore expected from others to make an effort and **comply with the rules**. Maintainers take the time to improve on this project and help by solving issues together. It is therefore expected from others to make an effort and **comply with the rules**.
### Filing a Bug Report
Thank you for participating in this project and reporting a bug. Docker Mail Server (DMS) is a community-driven project, and each contribution counts!
Maintainers and moderators are volunteers. We greatly appreciate reports that take the time to provide detailed information via the template, enabling us to help you in the best and quickest way. Ignoring the template provided may seem easier, but discourages receiving any support (_via assignment of the label `meta/no template - no support`_).
Markdown formatting can be used in almost all text fields (_unless stated otherwise in the description_).
Be as precise as possible, and if in doubt, it's best to add more information that too few.
When an option is marked with "not officially supported" / "unsupported", then support is dependent on availability from specific maintainers.
## Pull Requests ## Pull Requests
!!! question "Motivation" !!! question "Motivation"
@ -45,15 +33,15 @@ The development workflow is the following:
1. Fork the project and clone your fork with `git clone --recurse-submodules ...` or run `git submodule update --init --recursive` after you cloned your fork 1. Fork the project and clone your fork with `git clone --recurse-submodules ...` or run `git submodule update --init --recursive` after you cloned your fork
2. Write the code that is needed :D 2. Write the code that is needed :D
3. Add integration tests if necessary 3. Add integration tests if necessary
4. [Prepare your environment and run linting and tests][docs::contributing::tests] 4. [Prepare your environment and run linting and tests][docs-general-tests]
5. Document your improvements if necessary (e.g. if you introduced new environment variables, describe those in the [ENV documentation][docs::env]) and add your changes the changelog under the "Unreleased" section 5. Document your improvements if necessary (e.g. if you introduced new environment variables, describe those in the [ENV documentation][docs-environment]) and add your changes the changelog under the "Unreleased" section
6. [Commit][commit] (and [sign your commit][gpg]), push and create a pull-request to merge into `master`. Please **use the pull-request template** to provide a minimum of contextual information and make sure to meet the requirements of the checklist. 6. [Commit][commit] (and [sign your commit][gpg]), push and create a pull-request to merge into `master`. Please **use the pull-request template** to provide a minimum of contextual information and make sure to meet the requirements of the checklist.
Pull requests are automatically tested against the CI and will be reviewed when tests pass. When your changes are validated, your branch is merged. CI builds the new `:edge` image immediately and your changes will be includes in the next version release. Pull requests are automatically tested against the CI and will be reviewed when tests pass. When your changes are validated, your branch is merged. CI builds the new `:edge` image immediately and your changes will be includes in the next version release.
[docs-latest]: https://docker-mailserver.github.io/docker-mailserver/latest [docs-latest]: https://docker-mailserver.github.io/docker-mailserver/latest
[github-file-readme]: https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md [github-file-readme]: https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md
[docs::env]: ../config/environment.md [docs-environment]: ../config/environment.md
[docs::contributing::tests]: ./tests.md [docs-general-tests]: ./general.md#tests
[commit]: https://help.github.com/articles/closing-issues-via-commit-messages/ [commit]: https://help.github.com/articles/closing-issues-via-commit-messages/
[gpg]: https://docs.github.com/en/github/authenticating-to-github/generating-a-new-gpg-key [gpg]: https://docs.github.com/en/github/authenticating-to-github/generating-a-new-gpg-key

View File

@ -51,7 +51,7 @@ Parallel tests are further partitioned into smaller sets. If your system has the
To run the test suite, you will need to: To run the test suite, you will need to:
1. [Install Docker][get-docker] 1. [Install Docker][get-docker]
2. Install `jq` , (GNU) `parallel` and `file` (under Ubuntu, use `sudo apt-get -y install jq parallel file`) 2. Install `jq` and (GNU) `parallel` (under Ubuntu, use `sudo apt-get -y install jq parallel`)
3. Execute `git submodule update --init --recursive` if you haven't already initialized the git submodules 3. Execute `git submodule update --init --recursive` if you haven't already initialized the git submodules
### Executing Test(s) ### Executing Test(s)
@ -78,10 +78,6 @@ We use `make` to run commands.
When writing tests, ensure that parallel set tests still pass when run in parallel. You need to account for other tests running in parallel that may interfere with your own tests logic. When writing tests, ensure that parallel set tests still pass when run in parallel. You need to account for other tests running in parallel that may interfere with your own tests logic.
!!! tip
You may use `make run-local-instance` to run a version of the image built locally to test and edit your changes in a running DMS instance.
### An Example ### An Example
In this example, you've made a change to the Rspamd feature support (_or adjusted it's tests_). First verify no regressions have been introduced by running it's specific test file: In this example, you've made a change to the Rspamd feature support (_or adjusted it's tests_). First verify no regressions have been introduced by running it's specific test file:
@ -89,10 +85,10 @@ In this example, you've made a change to the Rspamd feature support (_or adjuste
```console ```console
$ make clean generate-accounts test/rspamd $ make clean generate-accounts test/rspamd
rspamd.bats rspamd.bats
✓ [Rspamd] Postfix's main.cf was adjusted [12] ✓ [Rspamd] Postfix's main.cf was adjusted [12]
✓ [Rspamd] normal mail passes fine [44] ✓ [Rspamd] normal mail passes fine [44]
✓ [Rspamd] detects and rejects spam [122] ✓ [Rspamd] detects and rejects spam [122]
✓ [Rspamd] detects and rejects virus [189] ✓ [Rspamd] detects and rejects virus [189]
``` ```
As your feature work progresses your change for Rspamd also affects ClamAV. As your change now spans more than just the Rspamd test file, you could run multiple test files serially: As your feature work progresses your change for Rspamd also affects ClamAV. As your change now spans more than just the Rspamd test file, you could run multiple test files serially:
@ -100,17 +96,16 @@ As your feature work progresses your change for Rspamd also affects ClamAV. As y
```console ```console
$ make clean generate-accounts test/rspamd,clamav $ make clean generate-accounts test/rspamd,clamav
rspamd.bats rspamd.bats
✓ [Rspamd] Postfix's main.cf was adjusted [12] ✓ [Rspamd] Postfix's main.cf was adjusted [12]
✓ [Rspamd] normal mail passes fine [44] ✓ [Rspamd] normal mail passes fine [44]
✓ [Rspamd] detects and rejects spam [122] ✓ [Rspamd] detects and rejects spam [122]
✓ [Rspamd] detects and rejects virus [189] ✓ [Rspamd] detects and rejects virus [189]
clamav.bats clamav.bats
✓ [ClamAV] log files exist at /var/log/mail directory [68] ✓ [ClamAV] log files exist at /var/log/mail directory [68]
✓ [ClamAV] should be identified by Amavis [67] ✓ [ClamAV] should be identified by Amavis [67]
✓ [ClamAV] freshclam cron is enabled [76] ✓ [ClamAV] freshclam cron is enabled [76]
✓ [ClamAV] env CLAMAV_MESSAGE_SIZE_LIMIT is set correctly [63] ✓ [ClamAV] env CLAMAV_MESSAGE_SIZE_LIMIT is set correctly [63]
✓ [ClamAV] rejects virus [60] ✓ [ClamAV] rejects virus [60]
``` ```
You're almost finished with your change before submitting it as a PR. It's a good idea to run the full parallel set those individual tests belong to (_especially if you've modified any tests_): You're almost finished with your change before submitting it as a PR. It's a good idea to run the full parallel set those individual tests belong to (_especially if you've modified any tests_):
@ -118,15 +113,13 @@ You're almost finished with your change before submitting it as a PR. It's a goo
```console ```console
$ make clean generate-accounts tests/parallel/set1 $ make clean generate-accounts tests/parallel/set1
default_relay_host.bats default_relay_host.bats
✓ [Relay] (ENV) 'DEFAULT_RELAY_HOST' should configure 'main.cf:relayhost' [88] ✓ [Relay] (ENV) 'DEFAULT_RELAY_HOST' should configure 'main.cf:relayhost' [88]
spam_virus/amavis.bats spam_virus/amavis.bats
✓ [Amavis] SpamAssassin integration should be active [1165] ✓ [Amavis] SpamAssassin integration should be active [1165]
spam_virus/clamav.bats spam_virus/clamav.bats
✓ [ClamAV] log files exist at /var/log/mail directory [73] ✓ [ClamAV] log files exist at /var/log/mail directory [73]
✓ [ClamAV] should be identified by Amavis [67] ✓ [ClamAV] should be identified by Amavis [67]
✓ [ClamAV] freshclam cron is enabled [76] ✓ [ClamAV] freshclam cron is enabled [76]
... ...
``` ```
@ -134,6 +127,7 @@ Even better, before opening a PR run the full test suite:
```console ```console
$ make clean tests $ make clean tests
...
``` ```
[BATS]: https://github.com/bats-core/bats-core [BATS]: https://github.com/bats-core/bats-core

View File

@ -4,7 +4,7 @@ title: 'Tutorials | Basic Installation'
## A Basic Example With Relevant Environmental Variables ## A Basic Example With Relevant Environmental Variables
This example provides you only with a basic example of what a minimal setup could look like. We **strongly recommend** that you go through the configuration file yourself and adjust everything to your needs. The default [compose.yaml](https://github.com/docker-mailserver/docker-mailserver/blob/master/compose.yaml) can be used for the purpose out-of-the-box, see the [_Usage_ chapter](../../usage.md). This example provides you only with a basic example of what a minimal setup could look like. We **strongly recommend** that you go through the configuration file yourself and adjust everything to your needs. The default [docker-compose.yml](https://github.com/docker-mailserver/docker-mailserver/blob/master/docker-compose.yml) can be used for the purpose out-of-the-box, see the [_Usage_ chapter](../../usage.md).
``` YAML ``` YAML
services: services:
@ -96,7 +96,7 @@ In this setup DMS is not intended to receive email from the outside world, so no
Adding the docker network's gateway to the list of trusted hosts (_eg: using the `network` or `connected-networks` option_), can create an [**open relay**](https://en.wikipedia.org/wiki/Open_mail_relay). For instance [if IPv6 is enabled on the host machine, but not in Docker][github-issue-1405-comment]. Adding the docker network's gateway to the list of trusted hosts (_eg: using the `network` or `connected-networks` option_), can create an [**open relay**](https://en.wikipedia.org/wiki/Open_mail_relay). For instance [if IPv6 is enabled on the host machine, but not in Docker][github-issue-1405-comment].
1. Create the file `compose.yaml` with a content like this: 1. Create the file `docker-compose.yml` with a content like this:
!!! example !!! example
@ -182,7 +182,7 @@ In this setup DMS is not intended to receive email from the outside world, so no
4. Get an SSL certificate, [we have a guide for you here][docs-ssl] (_Let's Encrypt_ is a popular service to get free SSL certificates). 4. Get an SSL certificate, [we have a guide for you here][docs-ssl] (_Let's Encrypt_ is a popular service to get free SSL certificates).
5. Start DMS and check the terminal output for any errors: `docker compose up`. 5. Start DMS and check the terminal output for any errors: `docker-compose up`.
6. Create email accounts and aliases: 6. Create email accounts and aliases:
@ -214,7 +214,7 @@ In this setup DMS is not intended to receive email from the outside world, so no
This extra step is required to avoid the `553 5.7.1 Sender address rejected: not owned by user` error (_the accounts used for submitting mail to Gmail are `admin.gmail@example.com` and `info.gmail@example.com`_) This extra step is required to avoid the `553 5.7.1 Sender address rejected: not owned by user` error (_the accounts used for submitting mail to Gmail are `admin.gmail@example.com` and `info.gmail@example.com`_)
7. Send some test emails to these addresses and make other tests. Once everything is working well, stop the container with `ctrl+c` and start it again as a daemon: `docker compose up -d`. 7. Send some test emails to these addresses and make other tests. Once everything is working well, stop the container with `ctrl+c` and start it again as a daemon: `docker-compose up -d`.
[docs-ports]: ../../config/security/understanding-the-ports.md [docs-ports]: ../../config/security/understanding-the-ports.md
[docs-environment]: ../../config/environment.md [docs-environment]: ../../config/environment.md

View File

@ -1,74 +0,0 @@
---
title: 'Tutorials | Crowdsec'
---
!!! quote "What is Crowdsec?"
Crowdsec is an open source software that detects and blocks attackers using log analysis.
It has access to a global community-wide IP reputation database.
[Source](https://www.crowdsec.net)
## Installation
Crowdsec supports multiple [installation methods][crowdsec-installation-docs], however this page will use the docker installation.
### Docker mailserver
In your `compose.yaml` for the DMS service, add a bind mount volume for `/var/log/mail`. This is to share the DMS logs to a separate crowdsec container.
!!! example
```yaml
services:
mailserver:
- /docker-data/dms/mail-logs/:/var/log/mail/
```
### Crowdsec
The crowdsec container should also bind mount the same host path for the DMS logs that was added in the DMS example above.
```yaml
services:
image: crowdsecurity/crowdsec
restart: unless-stopped
ports:
- "8080:8080"
- "6060:6060"
volumes:
- /docker-data/dms/mail-logs/:/var/log/dms:ro
- ./acquis.d:/etc/crowdsec/acquis.d
- crowdsec-db:/var/lib/crowdsec/data/
environment:
# These collection contains parsers and scenarios for postfix and dovecot
COLLECTIONS: crowdsecurity/postfix crowdsecurity/dovecot
TZ: Europe/Paris
volumes:
crowdsec-db:
```
## Configuration
Configure crowdsec to read and parse DMS logs file.
!!! example
Create the file `dms.yml` in `./acquis.d/`
```yaml
---
source: file
filenames:
- /var/log/dms/mail.log
labels:
type: syslog
```
!!! warning Bouncers
Crowdsec on its own is just a detection software, the remediation is done by components called bouncers.
This page does not explain how to install or configure a bouncer. It can be found in [crowdsec documentation][crowdsec-bouncer-docs].
[crowdsec-installation-docs]: https://doc.crowdsec.net/docs/getting_started/install_crowdsec
[crowdsec-bouncer-docs]: https://doc.crowdsec.net/docs/bouncers/intro

View File

@ -10,7 +10,7 @@ You'll need to retrieve the git submodules prior to building your own Docker ima
```sh ```sh
git submodule update --init --recursive git submodule update --init --recursive
docker build --tag <YOUR CUSTOM IMAGE NAME> . docker build -t <YOUR CUSTOM IMAGE NAME> .
``` ```
Or, you can clone and retrieve the submodules in one command: Or, you can clone and retrieve the submodules in one command:
@ -21,26 +21,19 @@ git clone --recurse-submodules https://github.com/docker-mailserver/docker-mails
### About Docker ### About Docker
#### Minimum supported version #### Version
We make use of build features that require a recent version of Docker. v23.0 or newer is advised, but earlier releases may work. We make use of build-features that require a recent version of Docker. Depending on your distribution, please have a look at [the official installation documentation for Docker](https://docs.docker.com/engine/install/) to get the latest version. Otherwise, you may encounter issues, for example with the `--link` flag for a [`#!dockerfile COPY`](https://docs.docker.com/engine/reference/builder/#copy) command.
- To get the latest version for your distribution, please have a look at [the official installation documentation for Docker](https://docs.docker.com/engine/install/). #### Environment
- If you are using a version of Docker prior to v23.0, you will need to enable BuildKit via the ENV [`DOCKER_BUILDKIT=1`](https://docs.docker.com/build/buildkit/#getting-started).
#### Build Arguments (Optional) If you are not using `make` to build the image, note that you will need to provide `DOCKER_BUILDKIT=1` to the `docker build` command for the build to succeed.
The `Dockerfile` includes several build [`ARG`][docker-docs::builder-arg] instructions that can be configured: #### Build Arguments
- `DOVECOT_COMMUNITY_REPO`: Install Dovecot from the community repo instead of from Debian (default = 0) The `Dockerfile` takes additional, so-called build arguments. These are
- `DMS_RELEASE`: The image version (default = edge)
- `VCS_REVISION`: The git commit hash used for the build (default = unknown)
!!! note 1. `VCS_VERSION`: the image version (default = edge)
2. `VCS_REVISION`: the image revision (default = unknown)
- `DMS_RELEASE` (_when not `edge`_) will be used to check for updates from our GH releases page at runtime due to the default feature [`ENABLE_UPDATE_CHECK=1`][docs::env-update-check]. When using `make` to build the image, these are filled with proper values. You can build the image without supplying these arguments just fine though.
- Both `DMS_RELEASE` and `VCS_REVISION` are also used with `opencontainers` metadata [`LABEL`][docker-docs::builder-label] instructions.
[docs::env-update-check]: https://docker-mailserver.github.io/docker-mailserver/latest/config/environment/#enable_update_check
[docker-docs::builder-arg]: https://docs.docker.com/engine/reference/builder/#using-arg-variables
[docker-docs::builder-label]: https://docs.docker.com/engine/reference/builder/#label

View File

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

View File

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

View File

@ -1,162 +0,0 @@
---
title: 'Examples | Use Cases | Lua Authentication'
---
## Introduction
Dovecot has the ability to let users create their own custom user provisioning and authentication providers in [Lua](https://en.wikipedia.org/wiki/Lua_(programming_language)#Syntax). This allows any data source that can be approached from Lua to be used for authentication, including web servers. It is possible to do more with Dovecot and Lua, but other use cases fall outside of the scope of this documentation page.
!!! warning "Community contributed guide"
Dovecot authentication via Lua scripting is not officially supported in DMS. No assistance will be provided should you encounter any issues.
DMS provides the required packages to support this guide. Note that these packages will be removed should they introduce any future maintenance burden.
The example in this guide relies on the current way in which DMS works with Dovecot configuration files. Changes to this to accommodate new authentication methods such as OpenID Connect will likely break this example in the future. This guide is updated on a best-effort base.
Dovecot's Lua support can be used for user provisioning (userdb functionality) and/or password verification (passdb functionality). Consider using other userdb and passdb options before considering Lua, since Lua does require the use of additional (unsupported) program code that might require maintenance when updating DMS.
Each implementation of Lua-based authentication is custom. Therefore it is impossible to write documentation that covers every scenario. Instead, this page describes a single example scenario. If that scenario is followed, you will learn vital aspects that are necessary to kickstart your own Lua development:
- How to override Dovecot's default configuration to disable parts that conflict with your scenario.
- How to make Dovecot use your Lua script.
- How to add your own Lua script and any libraries it uses.
- How to debug your Lua script.
## The example scenario
This scenario starts with [DMS being configured to use LDAP][docs::auth-ldap] for mailbox identification, user authorization and user authentication. In this scenario, [Nextcloud](https://nextcloud.com/) is also a service that uses the same LDAP server for user identification, authorization and authentication.
The goal of this scenario is to have Dovecot not authenticate the user against LDAP, but against Nextcloud using an [application password](https://docs.nextcloud.com/server/latest/user_manual/en/session_management.html#managing-devices). The idea behind this is that a compromised mailbox password does not compromise the user's account entirely. To make this work, Nextcloud is configured to [deny the use of account passwords by clients](https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/config_sample_php_parameters.html#token-auth-enforced) and to [disable account password reset through mail verification](https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/config_sample_php_parameters.html#lost-password-link).
If the application password is configured correctly, an adversary can only use it to access the user's mailbox on DMS, and CalDAV and CardDAV data on Nextcloud. File access through WebDAV can be disabled for the application password used to access mail. Having CalDAV and CardDAV compromised by the same password is a minor setback. If an adversary gets access to a Nextcloud application password through a device of the user, it is likely that the adversary also gets access to the user's calendars and contact lists anyway (locally or through the same account settings used for mail and CalDAV/CardDAV synchronization). The user's stored files in Nextcloud, the LDAP account password and any other services that rely on it would still be protected. A bonus is that a user is able to revoke and renew the mailbox password in Nextcloud for whatever reason, through a friendly user interface with all the security measures with which the Nextcloud instance is configured (e.g. verification of the current account password).
A drawback of this method is that any (compromised) Nextcloud application password can be used to access the user's mailbox. This introduces a risk that a Nextcloud application password used for something else (e.g. WebDAV file access) is compromised and used to access the user's mailbox. Discussion of that risk and possible mitigations fall outside of the scope of this scenario.
To answer the questions asked earlier for this specific scenario:
1. Do I want to use Lua to identify mailboxes and verify that users are authorized to use mail services? **No. Provisioning is done through LDAP.**
1. Do I want to use Lua to verify passwords that users authenticate with for IMAP/POP3/SMTP in their mail clients? **Yes. Password authentication is done through Lua against Nextcloud.**
1. If the answer is 'yes' to question 1 or 2: are there other methods that better facilitate my use case instead of custom scripts which rely on me being a developer and not just a user? **No. Only HTTP can be used to authenticate against Nextcloud, which is not supported natively by Dovecot or DMS.**
While it is possible to extend the authentication methods which Nextcloud can facilitate with [Nextcloud apps](https://apps.nextcloud.com/), there is currently a mismatch between what DMS supports and what Nextcloud applications can provide. This might change in the future. For now, Lua will be used to bridge the gap between DMS and Nextcloud for authentication only (Dovecot passdb), while LDAP will still be used to identify mailboxes and verify authorization (Dovecot userdb).
## Modify Dovecot's configuration
???+ example "Add to DMS volumes in `compose.yaml`"
```yaml
# All new volumes are marked :ro to configure them as read-only, since their contents are not changed from inside the container
volumes:
# Configuration override to disable LDAP authentication
- ./docker-data/dms/config/dovecot/auth-ldap.conf.ext:/etc/dovecot/conf.d/auth-ldap.conf.ext:ro
# Configuration addition to enable Lua authentication
- ./docker-data/dms/config/dovecot/auth-lua-httpbasic.conf:/etc/dovecot/conf.d/auth-lua-httpbasic.conf:ro
# Directory containing Lua scripts
- ./docker-data/dms/config/dovecot/lua/:/etc/dovecot/lua/:ro
```
Create a directory for Lua scripts:
```bash
mkdir -p ./docker-data/dms/config/dovecot/lua
```
Create configuration file `./docker-data/dms/config/dovecot/auth-ldap.conf.ext` for LDAP user provisioning:
```
userdb {
driver = ldap
args = /etc/dovecot/dovecot-ldap.conf.ext
}
```
Create configuration file `./docker-data/dms/config/dovecot/auth-lua-httpbasic.conf` for Lua user authentication:
```
passdb {
driver = lua
args = file=/etc/dovecot/lua/auth-httpbasic.lua blocking=yes
}
```
That is all for configuring Dovecot.
## Create the Lua script
Create Lua file `./docker-data/dms/config/dovecot/lua/auth-httpbasic.lua` with contents:
```lua
local http_url = "https://nextcloud.example.com/remote.php/dav/"
local http_method = "PROPFIND"
local http_status_ok = 207
local http_status_failure = 401
local http_header_forwarded_for = "X-Forwarded-For"
package.path = package.path .. ";/etc/dovecot/lua/?.lua"
local base64 = require("base64")
local http_client = dovecot.http.client {
timeout = 1000;
max_attempts = 1;
debug = false;
}
function script_init()
return 0
end
function script_deinit()
end
function auth_passdb_lookup(req)
local auth_request = http_client:request {
url = http_url;
method = http_method;
}
auth_request:add_header("Authorization", "Basic " .. (base64.encode(req.user .. ":" .. req.password)))
auth_request:add_header(http_header_forwarded_for, req.remote_ip)
local auth_response = auth_request:submit()
local resp_status = auth_response:status()
local reason = auth_response:reason()
local returnStatus = dovecot.auth.PASSDB_RESULT_INTERNAL_FAILURE
local returnDesc = http_method .. " - " .. http_url .. " - " .. resp_status .. " " .. reason
if resp_status == http_status_ok
then
returnStatus = dovecot.auth.PASSDB_RESULT_OK
returnDesc = "nopassword=y"
elseif resp_status == http_status_failure
then
returnStatus = dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH
returnDesc = ""
end
return returnStatus, returnDesc
end
```
Replace the hostname in the URL to the actual hostname of Nextcloud.
Dovecot [provides an HTTP client for use in Lua](https://doc.dovecot.org/admin_manual/lua/#dovecot.http.client). Aside of that, Lua by itself is pretty barebones. It chooses library compactness over included functionality. You can see that in that a separate library is referenced to add support for Base64 encoding, which is required for [HTTP basic access authentication](https://en.wikipedia.org/wiki/Basic_access_authentication). This library (also a Lua script) is not included. It must be downloaded and stored in the same directory:
```bash
cd ./docker-data/dms/config/dovecot/lua
curl -JLO https://raw.githubusercontent.com/iskolbin/lbase64/master/base64.lua
```
Only use native (pure Lua) libraries as dependencies if possible, such as `base64.lua` from the example. This ensures maximum compatibility. Performance is less of an issue since Lua scripts written for Dovecot probably won't be long or complex, and there won't be a lot of data processing by Lua itself.
## Debugging a Lua script
To see which Lua version is used by Dovecot if you plan to do something that is version dependent, run:
```bash
docker exec CONTAINER_NAME strings /usr/lib/dovecot/libdovecot-lua.so|grep '^LUA_'
```
While Dovecot logs the status of authentication attempts for any passdb backend, Dovecot will also log Lua scripting errors and messages sent to Dovecot's [Lua API log functions](https://doc.dovecot.org/admin_manual/lua/#dovecot.i_debug). The combined DMS log (including that of Dovecot) can be viewed using `docker logs CONTAINER_NAME`. If the log is too noisy (_due to other processes in the container also logging to it_), `docker exec CONTAINER_NAME cat /var/log/mail/mail.log` can be used to view the log of Dovecot and Postfix specifically.
If working with HTTP in Lua, setting `debug = true;` when initiating `dovecot.http.client` will create debug log messages for every HTTP request and response.
Note that Lua runs compiled bytecode, and that scripts will be compiled when they are initially started. Once compiled, the bytecode is cached and changes in the Lua script will not be processed automatically. Dovecot will reload its configuration and clear its cached Lua bytecode when running `docker exec CONTAINER_NAME dovecot reload`. A (changed) Lua script will be compiled to bytecode the next time it is executed after running the Dovecot reload command.
[docs::auth-ldap]: ../../config/account-management/provisioner/ldap.md
[docs::dovecot-override-configuration]: ../../config/advanced/override-defaults/dovecot.md#override-configuration
[docs::dovecot-add-configuration]: ../../config/advanced/override-defaults/dovecot.md#add-configuration
[docs::faq-alter-running-dms-instance-without-container-relaunch]: ../../faq.md#how-to-alter-a-running-dms-instance-without-relaunching-the-container

View File

@ -1,106 +0,0 @@
---
title: 'Use Cases | Binding outbound SMTP to a specific network'
hide:
- toc
---
!!! warning "Advice not extensively tested"
This configuration advice is a community contribution which has only been verified as a solution when using `network: host`, where you have direct access to the host interfaces.
It may be applicable in other network modes if the container has control of the outbound IPs to bind to. This is not the case with bridge networks that typically bind to a private range network for containers which are bridged to a public interface via Docker.
If your Docker host is running multiple IPv4 and IPv6 IP-addresses, it may be beneficial to bind outgoing SMTP connections to specific IP-address / interface.
- When a mail is sent outbound from DMS, it greets the MTA it is connecting to with a EHLO (DMS FQDN) which might be verified against the IP resolved, and that a `PTR` record for that IP resolves an address back to the same IP.
- A similar check with SPF can be against the envelope-sender address which may verify a DNS record like MX / A is valid (_or a similar restriction check from an MTA like [Postfix has with `reject_unknown_sender`][gh-pr::3465::comment-restrictions]_).
- If the IP address is inconsistent for those connections from DMS, these DNS checks are likely to fail.
This can be configured by [overriding the default Postfix configurations][docs::overrides-postfix] DMS provides. Create `postfix-master.cf` and `postfix-main.cf` files for your config volume (`docker-data/dms/config`).
In `postfix-main.cf` you'll have to set the [`smtp_bind_address`][postfix-docs::smtp-bind-address-ipv4] and [`smtp_bind_address6`][postfix-docs::smtp-bind-address-ipv6]
to the respective IP-address on the server you want to use.
!!! example
=== "Contributed solution"
```title="postfix-main.cf"
smtp_bind_address = 198.51.100.42
smtp_bind_address6 = 2001:DB8::42
```
!!! bug "Inheriting the bind from `main.cf` can misconfigure services"
One problem when setting `smtp_bind_address` in `main.cf` is that it will be inherited by any services in `master.cf` that extend the `smtp` transport. One of these is `smtp-amavis`, which is explicitly configured to listen / connect via loopback (localhost / `127.0.0.1`).
A `postfix-master.cf` override can workaround that issue by ensuring `smtp-amavis` binds to the expected internal IP:
```title="postfix-master.cf"
smtp-amavis/unix/smtp_bind_address=127.0.0.1
smtp-amavis/unix/smtp_bind_address6=::1
```
=== "Alternative (unverified)"
A potentially better solution might be to instead [explicitly set the `smtp_bind_address` override on the `smtp` transport service][gh-pr::3465::alternative-solution]:
```title="postfix-master.cf"
smtp/inet/smtp_bind_address = 198.51.100.42
smtp/inet/smtp_bind_address6 = 2001:DB8::42
```
If that avoids the concern with `smtp-amavis`, you may still need to additionally override for the [`relay` transport][gh-src::postfix-master-cf::relay-transport] as well if you have configured DMS to relay mail.
=== "Bridged Networks"
When your DMS container is using a bridge network, you'll instead need to restrict which IP address inbound and outbound traffic is routed through via the bridged interface.
For **inbound** traffic, you may configure this at whatever scope is most appropriate for you:
- **Daemon:** Change the [default bind address][inbound-ip::docker-docs::daemon] configured in `/etc/docker/daemon.json` (default `0.0.0.0`)
- **Network:** Assign the [`host_binding_ipv4` bridge driver option][inbound-ip::docker-docs::network] as shown in the below `compose.yaml` snippet.
- **Container:** Provide an explicit host IP address when [publishing a port][inbound-ip::docker-docs::container].
For **outbound** traffic, the bridge network will use the default route. You can change this by either:
- [Manually routing networks][outbound-ip::route-manually] on the host.
- Use the [`host_ipv4` driver option][outbind-ip::host-ipv4] for Docker networks to force the SNAT (source IP) that the bridged network will route outbound traffic through.
- This IP address must belong to a network interface to be routed through it.
- IPv6 support via `host_ipv6` [requires at least Docker v25][outbind-ip::host-ipv6].
---
Here is a `compose.yaml` snippet that applies the inbound + outbound settings to the default bridge network Docker Compose creates (_if it already exists, you will need to ensure it's re-created to apply the updated settings_):
```yaml title="compose.yaml"
networks:
default:
driver_opts:
# Inbound IP (sets the host IP that published ports receive traffic from):
com.docker.network.bridge.host_binding_ipv4: 198.51.100.42
# Outbound IP (sets the host IP that external hosts will receive connections from):
com.docker.network.host_ipv4: 198.51.100.42
```
!!! note "IP addresses for documentation"
IP addresses shown in above examples (`198.51.100.42` + `2001:DB8::42`) are placeholders, they are IP addresses reserved for documentation by IANA (_[RFC-5737 (IPv4)][rfc-5737] and [RFC-3849 (IPv6)][rfc-3849]_). Replace them with the IP addresses you want DMS to send mail through.
[docs::overrides-postfix]: ../../config/advanced/override-defaults/postfix.md
[postfix-docs::smtp-bind-address-ipv4]: https://www.postfix.org/postconf.5.html#smtp_bind_address
[postfix-docs::smtp-bind-address-ipv6]: https://www.postfix.org/postconf.5.html#smtp_bind_address6
[rfc-5737]: https://datatracker.ietf.org/doc/html/rfc5737
[rfc-3849]: https://datatracker.ietf.org/doc/html/rfc3849
[gh-pr::3465::comment-restrictions]: https://github.com/docker-mailserver/docker-mailserver/pull/3465#discussion_r1458114528
[gh-pr::3465::alternative-solution]: https://github.com/docker-mailserver/docker-mailserver/pull/3465#issuecomment-1678107233
[gh-src::postfix-master-cf::relay-transport]: https://github.com/docker-mailserver/docker-mailserver/blob/9cdbef2b369fb4fb0f1b4e534da8703daf92abc9/target/postfix/master.cf#L65
[inbound-ip::docker-docs::daemon]: https://docs.docker.com/reference/cli/dockerd/#default-network-options
[inbound-ip::docker-docs::network]: https://docs.docker.com/engine/network/drivers/bridge/#default-host-binding-address
[inbound-ip::docker-docs::container]: https://docs.docker.com/reference/compose-file/services/#ports
[outbound-ip::route-manually]: https://github.com/moby/moby/issues/30053#issuecomment-1077041045
[outbind-ip::host-ipv4]: https://github.com/moby/libnetwork/pull/2454
[outbind-ip::host-ipv6]: https://github.com/moby/moby/issues/46469

View File

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

View File

@ -30,6 +30,7 @@ We can create aliases with `./setup.sh`, like this:
If you want to send emails from outside the mail server you have to authenticate somehow (with a username and password). One way of doing it is described in [this discussion][github-issue-1247]. However if there are many user accounts, it is better to use authentication with LDAP. The settings for this on `mailserver.env` are: If you want to send emails from outside the mail server you have to authenticate somehow (with a username and password). One way of doing it is described in [this discussion][github-issue-1247]. However if there are many user accounts, it is better to use authentication with LDAP. The settings for this on `mailserver.env` are:
```env ```env
ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER
ACCOUNT_PROVISIONER=LDAP ACCOUNT_PROVISIONER=LDAP
LDAP_START_TLS=yes LDAP_START_TLS=yes
LDAP_SERVER_HOST=ldap.example.org LDAP_SERVER_HOST=ldap.example.org

View File

@ -16,7 +16,7 @@ See [`target/dovecot/15-mailboxes.conf`][github-config-dovecot-mailboxes] for ex
The `Archive` special IMAP folder may be useful to enable. To do so, make a copy of [`target/dovecot/15-mailboxes.conf`][github-config-dovecot-mailboxes] and uncomment the `Archive` mailbox definition. Mail clients should understand that this folder is intended for archiving mail due to the [`\Archive` _"SPECIAL-USE"_ attribute][rfc-6154]. The `Archive` special IMAP folder may be useful to enable. To do so, make a copy of [`target/dovecot/15-mailboxes.conf`][github-config-dovecot-mailboxes] and uncomment the `Archive` mailbox definition. Mail clients should understand that this folder is intended for archiving mail due to the [`\Archive` _"SPECIAL-USE"_ attribute][rfc-6154].
With the provided [compose.yaml][github-config-dockercompose] example, a volume bind mounts the host directory `docker-data/dms/config/` to the container location `/tmp/docker-mailserver/`. Config file overrides should instead be mounted to a different location as described in [Overriding Configuration for Dovecot][docs-config-overrides-dovecot]: With the provided [docker-compose.yml][github-config-dockercompose] example, a volume bind mounts the host directory `docker-data/dms/config/` to the container location `/tmp/docker-mailserver/`. Config file overrides should instead be mounted to a different location as described in [Overriding Configuration for Dovecot][docs-config-overrides-dovecot]:
```yaml ```yaml
volumes: volumes:
@ -61,11 +61,11 @@ Take care to test localized names work well as well.
This information is provided by the community. This information is provided by the community.
It presently lacks references to confirm the behavior. If any information is incorrect please let us know! :smile: It presently lacks references to confirm the behaviour. If any information is incorrect please let us know! :smile:
[docs-config-overrides-dovecot]: ../../config/advanced/override-defaults/dovecot.md#override-configuration [docs-config-overrides-dovecot]: ../../config/advanced/override-defaults/dovecot.md#override-configuration
[github-config-dockercompose]: https://github.com/docker-mailserver/docker-mailserver/blob/master/compose.yaml [github-config-dockercompose]: https://github.com/docker-mailserver/docker-mailserver/blob/master/docker-compose.yml
[github-config-dovecot-mailboxes]: https://github.com/docker-mailserver/docker-mailserver/blob/master/target/dovecot/15-mailboxes.conf [github-config-dovecot-mailboxes]: https://github.com/docker-mailserver/docker-mailserver/blob/master/target/dovecot/15-mailboxes.conf
[dovecot-docs-namespaces]: https://doc.dovecot.org/configuration_manual/namespace/#namespace-inbox [dovecot-docs-namespaces]: https://doc.dovecot.org/configuration_manual/namespace/#namespace-inbox
[dovecot-docs-mailboxes]: https://doc.dovecot.org/configuration_manual/namespace/#mailbox-settings [dovecot-docs-mailboxes]: https://doc.dovecot.org/configuration_manual/namespace/#mailbox-settings

View File

@ -1,232 +0,0 @@
---
title: 'Advanced | iOS Mail Push Support'
---
## Introduction
iOS Mail currently does not support the IMAP idle extension. Therefore users can only either check manually or configure intervals for fetching mails in their mail account preferences when using the default configuration.
To support mail push Dovecot needs to advertise the `XAPPLEPUSHSERVICE` IMAP extension as well as sending the actual push notifications to the Apple Push Notification service (APNs) which will forward them to the device.
This can be done with two components:
- A Dovecot plugin (`dovecot-xaps-plugin`) which is triggered whenever a mail is created or moved from/to a mail folder.
- A daemon service (`dovecot-xaps-daemon`) that manages both the device registrations as well as sending notifications to the APNs.
## Prerequisites
- An Apple developer account to create the required Apple Push Notification service certificate.
- Knowledge creating Docker images, using the command-line, and creating shell scripts.
## Limitations
- You need to maintain a custom `docker-mailserver` image.
- Push support is limited to the INBOX folder. Changes to other folders will not be pushed to the device regardless of the configuration settings.
- You currently cannot use the same account UUID on multiple devices. This means that if you use the same backup on multiple devices (e.g. old phone / new phone) only one of them will get the notification. Use different backups or recreate the mail account.
## Privacy concerns
- The service does not send any part of the actual message to Apple.
- The information sent contains the device UUID to notify and the (on-device) account UUID which was generated by the iOS mail application when creating the account.
- Upon receiving the notification, the iOS mail application will connect to the IMAP server given by the provided account UUID and fetch the mail to notify the user.
- Apple therefore does not know the mail address for which the mail was received, only that a specific account on a specific device should be notified that a new mail or that a mail was moved to the INBOX folder.
## Installation
Both components will be built using Docker and included into a custom `docker-mailserver` image. Afterwards the required configuration is added to `docker-data/dms/config`. The registration data is stored in `/var/mail-state/lib-xapsd`.
1. Create a Dockerfile to build a `docker-mailserver` image that includes the [`dovecot-xaps-plugin`](https://github.com/freswa/dovecot-xaps-plugin) as well as the [`dovecot-xaps-daemon`](https://github.com/freswa/dovecot-xaps-daemon). This is required to ensure that the Dovecot plugin is built against the same Dovecot version. The `:edge` tag is used here, but you might want to use a released version instead.
```dockerfile
FROM mailserver/docker-mailserver:edge AS dovecot-plugin-xaps
WORKDIR /tmp/dovecot-xaps-plugin
RUN <<EOF
apt-get update
apt-get -y --no-install-recommends install git cmake make build-essential dovecot-dev
git clone --single-branch --depth=1 https://github.com/freswa/dovecot-xaps-plugin.git .
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make install
EOF
# Use an older Go version as Go >= 1.20 causes this issue: https://github.com/freswa/dovecot-xaps-daemon/issues/24#issuecomment-1483876081
# Note that the underlying issue are non-standard-compliant Apple http servers which might get fixed at some point
FROM golang:1.19-alpine AS dovecot-xaps-daemon
ENV GOPROXY=https://proxy.golang.org,direct
ENV CGO_ENABLED=0
WORKDIR /go/dovecot-xaps-daemon
RUN <<EOF
apk add --no-cache --virtual build-dependencies git
git clone --single-branch --depth=1 https://github.com/freswa/dovecot-xaps-daemon .
go build ./cmd/xapsd
EOF
FROM mailserver/docker-mailserver:edge
COPY --from=dovecot-plugin-xaps /usr/lib/dovecot/modules/*_xaps_* /usr/lib/dovecot/modules/
COPY --from=dovecot-xaps-daemon /go/dovecot-xaps-daemon/xapsd /usr/bin/xapsd
# create a non-root user for the daemon process as well as configuration and run state directories
RUN <<EOF
adduser --quiet --system --group --disabled-password --home /var/mail-state/lib-xapsd --no-create-home xapsd
mkdir -p /var/run/xapsd /etc/xapsd
EOF
```
2. Build the new image:
```sh
docker build -t yourname/docker-mailserver .
```
3. Modify your `compose.yaml` to use the newly created image:
```yaml
services:
mailserver:
image: yourname/docker-mailserver:latest
```
4. Recreate the container:
```sh
docker compose down
docker compose up -d
```
5. Create a hash of your Apple developer account password using the provided `xapsd -pass` command:
```sh
docker exec -it mailserver xapsd -pass
```
6. Add configuration for both components:
- Create a folder named `xaps` in `docker-data/dms/config`.
- Create a file named `xapsd.yaml` in `docker-data/dms/config/xaps`.
- Replace `appleId` and `appleIdHashedPassword` with your actual credentials. For reference see also [here](https://github.com/freswa/dovecot-xaps-daemon/blob/master/configs/xapsd/xapsd.yaml).
- The service will use the provided username/hash combination to automatically request a new certificate from Apple as well as renewing an older certificate if needed.
```yaml title="xapsd.yaml"
# set the loglevel to either
# trace, debug, error, fatal, info, panic or warn
# Default: info
loglevel: info
# xapsd creates a json file to store the registration persistent on disk.
# This sets the location of the file.
databaseFile: /var/mail-state/lib-xapsd/database.json
# xapsd listens on a socket for http/https requests from the dovecot plugin.
# This sets the address and port number of the listen socket.
listenAddr: '127.0.0.1'
port: 11619
# xapsd is able to listen on a HTTPS Socket to allow HTTP/2 to be used
# SSL is enabled implicitly when certfile and keyfile exist
# !!! only use HTTPS for connection pooling with a proxy e.g. nginx or HaProxy
# !!! direct usage with the plugin is discouraged and unsupported
tlsCertfile:
tlsKeyfile:
tlsListenAddr:
tlsPort: 11620
# Notifications that are not initiated by new messages are not sent immediately for two reasons:
# 1. When you move/copy/delete messages you most likely move/copy/delete more messages within a short period of time.
# 2. You don't need your mailboxes to synchronize immediately since they are automatically synchronized when opening
# the app
# If a new message comes and the move/copy/delete notification is still on hold it will be sent with the notification
# for the new message.
# This sets the interval to check for delayed messages.
checkInterval: 20
# Set the time how long notifications for not-new messages should be delayed until they are sent.
# Whenever checkInterval runs, it checks if "delay" <= "waiting time" and sends the notification if the expression is
# true.
delay: 30
# To retrieve certificates from Apple, we need to login with a valid Apple ID
# The accounts email must be given in cleartext, but the password has to
# be hashed before sending it. To not leak working credentials on running servers,
# we do not accept the cleartext password here.
appleId: foo@example.com
# use `xaps -pass` to calculate the hash of the apple id password
appleIdHashedPassword: bar
```
- Create a file named `95-xaps.conf` in `docker-data/dms/config/xaps`. For reference see also [here](https://github.com/freswa/dovecot-xaps-plugin/blob/master/xaps.conf).
```txt title="95-xaps.conf"
protocol imap {
mail_plugins = $mail_plugins notify push_notification xaps_push_notification xaps_imap
}
protocol lda {
mail_plugins = $mail_plugins notify push_notification xaps_push_notification
}
protocol lmtp {
mail_plugins = $mail_plugins notify push_notification xaps_push_notification
}
plugin {
# xaps_config contains xaps specific configuration parameters
# url: protocol, hostname and port under which xapsd listens
# user_lookup: Use if you want to determine the username used for PNs from environment variables provided by
# login mechanism. Value is variable name to look up.
# max_retries: maximum num of retries the http client connects to the xaps daemon
# timeout_msecs http timeout of the http connection
xaps_config = url=http://127.0.0.1:11619 user_lookup=theattribute max_retries=6 timeout_msecs=5000
push_notification_driver = xaps
}
```
- Create a supervisord file named `xapsd.conf` in `docker-data/dms/config/xaps` with the following content:
```txt title="xapsd.conf"
[program:xapsd]
startsecs=0
autostart=false
autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
user=xapsd
command=/usr/bin/xapsd
pidfile=/var/run/xapsd/xapsd.pid
```
- Create or update your `user-patches.sh` in `docker-data/dms/config` to move the files to their final location as well as starting the daemon service:
```sh title="user-patches.sh"
#!/bin/bash
# Copy the configs to internal locations:
cp /tmp/docker-mailserver/xaps/95-xaps.conf /etc/dovecot/conf.d/95-xaps.conf
cp /tmp/docker-mailserver/xaps/xapsd.yaml /etc/xapsd/xapsd.yaml
cp /tmp/docker-mailserver/xaps/xapsd.conf /etc/supervisor/conf.d/xapsd.conf
# Setup data persistence and ensure ownership is always for xapsd:
mkdir -p /var/mail-state/lib-xapsd
chown -R xapsd:xapsd /var/mail-state/lib-xapsd
# Start the xaps daemon:
supervisorctl update
supervisorctl start xapsd
```
7. Recreate the container again to apply the new configuration:
```sh
docker compose down
docker compose up -d
```
8. Recreate your mail account on your iOS device and check the logs in `/var/log/supervisor/dovecot.log` and `/var/log/supervisor/xapsd.log` for any errors.
## Other configuration options
Both device registration and notifications send a username to the daemon to lookup the device. While the registration and other IMAP operations in Dovecot will send the Dovecot username, LMTP will send the provided authentication username.
The format of that username is specified by the `auth_username_format` Dovecot setting. If you are not using mail addresses as Dovecot usernames - e.g. when using LDAP - you can either change the `auth_username_format` or add the mail address as property to the user account and use the lookup feature (see below).
```sh title="user-patches.sh"
sed -i -r "s|^#?(auth_username_format =).*|\1 %Ln|" /etc/dovecot/conf.d/10-auth.conf
```
You can also use notifications for Dovecot alias mailboxes. Depending on your server configuration, this might require to add the original Dovecot username as [Dovecot attribute](https://doc.dovecot.org/configuration_manual/authentication/user_database_extra_fields/) to the login user as well as changing the `user_lookup=theattribute` in `95-xaps.conf` to perform the lookup of that attribute.

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? ### 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.md) 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) for more information.
### How do I update DMS? ### How do I update DMS?
@ -21,9 +21,9 @@ Mails are stored in `/var/mail/${domain}/${username}`. Since `v9.0.0` it is poss
Then, run the following commands: Then, run the following commands:
``` BASH ``` BASH
docker compose pull docker-compose pull
docker compose down docker-compose down
docker compose up -d docker-compose up -d
``` ```
You should see the new version number on startup, for example: `[ INF ] Welcome to docker-mailserver 11.3.1`. And you're done! Don't forget to have a look at the remaining functions of the `setup.sh` script with `./setup.sh help`. You should see the new version number on startup, for example: `[ INF ] Welcome to docker-mailserver 11.3.1`. And you're done! Don't forget to have a look at the remaining functions of the `setup.sh` script with `./setup.sh help`.
@ -79,14 +79,6 @@ volumes:
Optionally, you can set the `TZ` ENV variable; e.g. `TZ=Europe/Berlin`. Check [this list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for which values are allowed. Optionally, you can set the `TZ` ENV variable; e.g. `TZ=Europe/Berlin`. Check [this list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for which values are allowed.
### What About DNS Servers?
Properly working DNS servers are crucial for differentiating spam from legitimate e-mails. Records like `SPF`, `DKIM` and `DMARC` records, as well as working name (resolving `A` records) and reverse name (resolving `PTR` records) resolution ensures legitimate e-mails arrive while e-mails that are more likely phishing and spam do not.
Anti-spam measures (like SpamAssassin or Rspamd) make use of DNS block lists. To learn more check out our [Rspamd documentation on this topic][docs::rspamd-rbl-dnsbl]. In case you want to utilize RBL/DNSBLs, you need a recursive DNS resolver (_not big custom resolvers like Cloudflare, Quad9, Google, etc._).
DMS does not integrate support for an internal DNS service as this is a [responsibility that is sensitive to the host environment][gh-discussion::dms-avoid-maintaining-internal-dns]. You can configure internal services within DMS to use your own managed DNS server, or configure for such at the host or container level (_such as with [`compose.yaml`][docker-compose::docs::config-dns]_).
### What is the file format? ### What is the file format?
All files are using the Unix format with `LF` line endings. Please do not use `CRLF`. All files are using the Unix format with `LF` line endings. Please do not use `CRLF`.
@ -105,7 +97,7 @@ DMS supports multiple domains out of the box, so you can do this:
#### Bind mounts (default) #### Bind mounts (default)
From the location of your `compose.yaml`, create a compressed archive of your `docker-data/dms/config/` and `docker-data/dms/mail-*` folders: From the location of your `docker-compose.yml`, create a compressed archive of your `docker-data/dms/config/` and `docker-data/dms/mail-*` folders:
```bash ```bash
tar --gzip -cf "backup-$(date +%F).tar.gz" ./docker-data/dms tar --gzip -cf "backup-$(date +%F).tar.gz" ./docker-data/dms
@ -136,7 +128,7 @@ find "${PWD}/docker-data/dms-backups/" -type f -mtime +30 -delete
### I Want to Know More About the Ports ### I Want to Know More About the Ports
See [this part of the documentation](./config/security/understanding-the-ports.md) for further details and best practice advice, **especially regarding security concerns**. See [this part of the documentation](../config/security/understanding-the-ports/) for further details and best practice advice, **especially regarding security concerns**.
### How can I configure my email client? ### How can I configure my email client?
@ -151,7 +143,7 @@ imap port: 143 or 993 with STARTTLS/SSL (recommended)
imap path prefix: INBOX imap path prefix: INBOX
# SMTP # SMTP
smtp port: 587 or 465 with STARTTLS/SSL (recommended) smtp port: 25 or 587/465 with STARTTLS/SSL (recommended)
username: <user1@example.com> username: <user1@example.com>
password: <mypassword> password: <mypassword>
``` ```
@ -175,7 +167,7 @@ warning: do not list domain example.com in BOTH mydestination and virtual_mailbo
Plus of course mail delivery fails. Plus of course mail delivery fails.
Also you need to define `hostname: example.com` in your `compose.yaml`. Also you need to define `hostname: example.com` in your `docker-compose.yml`.
!!! tip "You might not want a bare domain" !!! tip "You might not want a bare domain"
@ -184,6 +176,7 @@ Also you need to define `hostname: example.com` in your `compose.yaml`.
- There are [benefits][github-comment-baredomain] to preferring a subdomain. - There are [benefits][github-comment-baredomain] to preferring a subdomain.
- A bare domain is not required to have `user@example.com`, that is distinct from your hostname which is identified by a DNS MX record. - A bare domain is not required to have `user@example.com`, that is distinct from your hostname which is identified by a DNS MX record.
### How can I configure a catch-all? ### How can I configure a catch-all?
Considering you want to redirect all incoming e-mails for the domain `example.com` to `user1@example.com`, add the following line to `docker-data/dms/config/postfix-virtual.cf`: Considering you want to redirect all incoming e-mails for the domain `example.com` to `user1@example.com`, add the following line to `docker-data/dms/config/postfix-virtual.cf`:
@ -218,7 +211,7 @@ baduser@example.com devnull
### What kind of SSL certificates can I use? ### 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.md#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/#ssl_type) for more details.
### I just moved from my old mail server to DMS, but "it doesn't work"? ### I just moved from my old mail server to DMS, but "it doesn't work"?
@ -257,32 +250,23 @@ See [#1247][github-issue-1247] for an example.
### Common Errors ### Common Errors
#### Creating an alias or account with an address for `hostname`
Normally you will assign DMS a `hostname` such as `mail.example.com`. If you instead use a bare domain (_such as `example.com`_) or add an alias / account with the same value as your `hostname`, this can cause a conflict for mail addressed to `@hostname` as Postfix gets confused where to deliver the mail (_`hostname` is configured for only system accounts via the Postfix `main.cf` setting `mydestination`_).
When this conflict is detected you'll find logs similar to this:
```log ```log
warning: do not list domain mail.example.com in BOTH mydestination and virtual_mailbox_domains warning: connect to Milter service inet:localhost:8893: Connection refused
... # DMARC not running
NOQUEUE: reject: RCPT from HOST[IP]: 550 5.1.1 <RECIPIENT>: Recipient address rejected: User unknown in local recipient table; ... # => /etc/init.d/opendmarc restart
warning: connect to Milter service inet:localhost:8891: Connection refused
# DKIM not running
# => /etc/init.d/opendkim restart
mail amavis[1459]: (01459-01) (!)connect to /var/run/clamav/clamd.ctl failed, attempt #1: Can't connect to a UNIX socket /var/run/clamav/clamd.ctl: No such file or directory
mail amavis[1459]: (01459-01) (!)ClamAV-clamd: All attempts (1) failed connecting to /var/run/clamav/clamd.ctl, retrying (2)
mail amavis[1459]: (01459-01) (!)ClamAV-clamscan av-scanner FAILED: /usr/bin/clamscan KILLED, signal 9 (0009) at (eval 100) line 905.
mail amavis[1459]: (01459-01) (!!)AV: ALL VIRUS SCANNERS FAILED
# Clamav is not running (not started or because you don't have enough memory)
# => check requirements and/or start Clamav
``` ```
Opt-out of mail being directed to services by excluding `$myhostname` as a destination with a [`postfix-main.cf`][docs-override-postfix] override config:
```cf
mydestination = localhost.$mydomain, localhost
```
!!! tip
You may want to configure a `postmaster` alias via `setup alias add` to receive system notifications.
!!! warning
Internal mail destined for `root`, `amavis` or other accounts will now no longer be received without an alias or account created for them.
### How to use DMS behind a proxy ### How to use DMS behind a proxy
[Using `user-patches.sh`][docs-userpatches], update the container file `/etc/postfix/main.cf` to include: [Using `user-patches.sh`][docs-userpatches], update the container file `/etc/postfix/main.cf` to include:
@ -291,19 +275,13 @@ mydestination = localhost.$mydomain, localhost
proxy_interfaces = X.X.X.X (your public IP) proxy_interfaces = X.X.X.X (your public IP)
``` ```
For reverse proxy support you will want to view [our dedicated guide][docs::examples::reverse-proxy].
### How to restrict login by IP?
There are a few ways you could approach this, see [this discussion answer][gh-discussion::restrict-login-by-ip] for advice.
### How to adjust settings with the `user-patches.sh` script ### How to adjust settings with the `user-patches.sh` script
Suppose you want to change a number of settings that are not listed as variables or add things to the server that are not included? Suppose you want to change a number of settings that are not listed as variables or add things to the server that are not included?
DMS has a built-in way to do post-install processes. If you place a script called **`user-patches.sh`** in the config directory it will be run after all configuration files are set up, but before the postfix, amavis and other daemons are started. DMS has a built-in way to do post-install processes. If you place a script called **`user-patches.sh`** in the config directory it will be run after all configuration files are set up, but before the postfix, amavis and other daemons are started.
It is common to use a local directory for config added to `docker-mailsever` via a volume mount in your `compose.yaml` (eg: `./docker-data/dms/config/:/tmp/docker-mailserver/`). It is common to use a local directory for config added to `docker-mailsever` via a volume mount in your `docker-compose.yml` (eg: `./docker-data/dms/config/:/tmp/docker-mailserver/`).
Add or create the script file to your config directory: Add or create the script file to your config directory:
@ -374,11 +352,36 @@ DMS does not manage those concerns, verify they are not causing your delivery pr
- [mail-tester](https://www.mail-tester.com/) can test your deliverability. - [mail-tester](https://www.mail-tester.com/) can test your deliverability.
- [helloinbox](https://www.helloinbox.email/) provides a checklist of things to improve your deliverability. - [helloinbox](https://www.helloinbox.email/) provides a checklist of things to improve your deliverability.
### Special Directories
#### What About the `docker-data/dms/config/` Directory?
This documentation and all example configuration files in the GitHub repository use `docker-data/dms/config/` to refer to the directory in the host that is mounted (e.g. via a bind mount) to `/tmp/docker-mailserver/` inside the container.
Most configuration files for Postfix, Dovecot, etc. are persisted here. [Optional configuration][docs-optional-configuration] is stored here as well.
#### What About the `docker-data/dms/mail-state/` Directory?
This documentation and all example configuration files in the GitHub repository use `docker-data/dms/mail-state/` to refer to the directory in the host that is mounted (e.g. via a bind mount) to `/var/mail-state/` inside the container.
When you run DMS with the ENV variable `ONE_DIR=1` (default), this directory will provide support to persist Fail2Ban blocks, ClamAV signature updates, and the like when the container is restarted or recreated. Service data is [relocated to the `mail-state` folder][mail-state-folders] for the following services: Postfix, Dovecot, Fail2Ban, Amavis, PostGrey, ClamAV, SpamAssassin, Rspamd & Redis.
### SpamAssasin ### SpamAssasin
#### How can I manage my custom SpamAssassin rules? #### How can I manage my custom SpamAssassin rules?
Anti-spam rules are managed in `docker-data/dms/config/spamassassin-rules.cf`. 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 `docker-compose.yml`:
```yaml
environment:
- "SA_SPAM_SUBJECT=[SPAM] "
```
#### Why are SpamAssassin `x-headers` not inserted into my `subdomain.example.com` subdomain emails? #### Why are SpamAssassin `x-headers` not inserted into my `subdomain.example.com` subdomain emails?
@ -390,15 +393,14 @@ The default setup `@local_domains_acl = ( ".$mydomain" );` does not match subdom
Put received spams in `.Junk/` imap folder using `SPAMASSASSIN_SPAM_TO_INBOX=1` and `MOVE_SPAM_TO_JUNK=1` and add a _user_ cron like the following: Put received spams in `.Junk/` imap folder using `SPAMASSASSIN_SPAM_TO_INBOX=1` and `MOVE_SPAM_TO_JUNK=1` and add a _user_ cron like the following:
!!! example ```conf
# This assumes you're having `environment: ONE_DIR=1` in the `mailserver.env`,
**NOTE:** This example assumes you have a [`/var/mail-state` volume][docs::dms-volumes-state] mounted. # with a consolidated config in `/var/mail-state`
#
```conf # m h dom mon dow command
# m h dom mon dow command # Everyday 2:00AM, learn spam from a specific user
# Everyday 2:00AM, learn spam from a specific user 0 2 * * * docker exec mailserver sa-learn --spam /var/mail/example.com/username/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin
0 2 * * * docker exec mailserver sa-learn --spam /var/mail/example.com/username/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin ```
```
With `docker-compose` you can more easily use the internal instance of `cron` within DMS. This is less problematic than the simple solution shown above, because it decouples the learning from the host on which DMS is running, and avoids errors if the mail server is not running. With `docker-compose` you can more easily use the internal instance of `cron` within DMS. This is less problematic than the simple solution shown above, because it decouples the learning from the host on which DMS is running, and avoids errors if the mail server is not running.
@ -406,12 +408,10 @@ The following configuration works nicely:
??? example ??? example
**NOTE:** This example assumes you have a [`/var/mail-state` volume][docs::dms-volumes-state] mounted.
Create a _system_ cron file: Create a _system_ cron file:
```sh ```sh
# in the compose.yaml root directory # in the docker-compose.yml root directory
mkdir -p ./docker-data/dms/cron mkdir -p ./docker-data/dms/cron
touch ./docker-data/dms/cron/sa-learn touch ./docker-data/dms/cron/sa-learn
chown root:root ./docker-data/dms/cron/sa-learn chown root:root ./docker-data/dms/cron/sa-learn
@ -421,6 +421,9 @@ The following configuration works nicely:
Edit the system cron file `nano ./docker-data/dms/cron/sa-learn`, and set an appropriate configuration: Edit the system cron file `nano ./docker-data/dms/cron/sa-learn`, and set an appropriate configuration:
```conf ```conf
# This assumes you're having `environment: ONE_DIR=1` in the env-mailserver,
# with a consolidated config in `/var/mail-state`
#
# '> /dev/null' to send error notifications from 'stderr' to 'postmaster@example.com' # '> /dev/null' to send error notifications from 'stderr' to 'postmaster@example.com'
# #
# m h dom mon dow user command # m h dom mon dow user command
@ -442,7 +445,7 @@ The following configuration works nicely:
30 3 * * * root sa-learn --ham /var/mail/not-example.com/*/cur* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null 30 3 * * * root sa-learn --ham /var/mail/not-example.com/*/cur* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null
``` ```
Then with `compose.yaml`: Then with `docker-compose.yml`:
```yaml ```yaml
services: services:
@ -468,41 +471,57 @@ The following configuration works nicely:
file: ./docker-data/dms/cron/sa-learn 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). 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).
- 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? #### How do I have more control about what SpamAssassin is filtering?
This is related to Amavis processing the mail after SpamAssassin has analyzed it and assigned a spam score. 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:
- DMS provides some [common SA tunables via ENV][docs::env::sa_env]. First, make sure you have the proper thresholds set:
- Additional configuration can be managed with the DMS config volume by providing `docker-data/dms/config/amavis.cf`.
#### How can I send quarantined mail to a mailbox? ```conf
SA_TAG=-100000.0
SA_TAG2=3.75
SA_KILL=100000.0
```
SPAM and INFECTED emails that [reach the `SA_KILL` threshold are archived into quarantine][docs::env::sa_kill]. - The very negative vaue 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`)
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`: 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`
```cf ```cf
$clean_quarantine_to = "quarantine\@example.com"; $clean_quarantine_to = "amavis\@example.com";
$virus_quarantine_to = "quarantine\@example.com"; $virus_quarantine_to = "amavis\@example.com";
$banned_quarantine_to = "quarantine\@example.com"; $banned_quarantine_to = "amavis\@example.com";
$bad_header_quarantine_to = "quarantine\@example.com"; $bad_header_quarantine_to = "amavis\@example.com";
$spam_quarantine_to = "quarantine\@example.com"; $spam_quarantine_to = "amavis\@example.com";
``` ```
[fail2ban-customize]: ./config/security/fail2ban.md [fail2ban-customize]: ./config/security/fail2ban.md
[docs::dms-volumes-state]: ./config/advanced/optional-config.md#volumes-state
[docs::rspamd-rbl-dnsbl]: ./config/security/rspamd.md#rbls-real-time-blacklists-dnsbls-dns-based-blacklists
[docs-maintenance]: ./config/advanced/maintenance/update-and-cleanup.md [docs-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-userpatches]: ./config/advanced/override-defaults/user-patches.md
[docs::env::sa_env]: ./config/environment.md#spamassassin
[docs::env::sa_kill]: ./config/environment.md#sa_kill
[docs::examples::reverse-proxy]: ./examples/tutorials/mailserver-behind-proxy.md
[github-comment-baredomain]: https://github.com/docker-mailserver/docker-mailserver/issues/3048#issuecomment-1432358353 [github-comment-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-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 [github-issue-95]: https://github.com/docker-mailserver/docker-mailserver/issues/95
@ -511,7 +530,6 @@ $spam_quarantine_to = "quarantine\@example.com";
[github-issue-1405-comment]: https://github.com/docker-mailserver/docker-mailserver/issues/1405#issuecomment-590106498 [github-issue-1405-comment]: https://github.com/docker-mailserver/docker-mailserver/issues/1405#issuecomment-590106498
[github-issue-1639]: https://github.com/docker-mailserver/docker-mailserver/issues/1639 [github-issue-1639]: https://github.com/docker-mailserver/docker-mailserver/issues/1639
[github-issue-1792]: https://github.com/docker-mailserver/docker-mailserver/pull/1792 [github-issue-1792]: https://github.com/docker-mailserver/docker-mailserver/pull/1792
[gh-discussion::dms-avoid-maintaining-internal-dns]: https://github.com/orgs/docker-mailserver/discussions/3959#discussioncomment-8956322
[gh-discussion::restrict-login-by-ip]: https://github.com/orgs/docker-mailserver/discussions/3847
[docker-compose::docs::config-dns]: https://docs.docker.com/compose/compose-file/compose-file-v3/#dns
[hanscees-userpatches]: https://github.com/hanscees/dockerscripts/blob/master/scripts/tomav-user-patches.sh [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 ## About
`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". `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".
## Contents ## 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]. 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.md [docs-introduction]: ./introduction/
[docs-usage]: ./usage.md [docs-usage]: ./usage/
[docs-examples]: ./examples/tutorials/basic-installation.md [docs-examples]: ./examples/tutorials/basic-installation/
[github-file-setupsh]: https://github.com/docker-mailserver/docker-mailserver/blob/master/setup.sh [github-file-setupsh]: https://github.com/docker-mailserver/docker-mailserver/blob/master/setup.sh
[docs-setupsh]: ./config/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]. 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.md [docs-tests]: ./contributing/tests/
### Contributing ### Contributing
We are always happy to welcome new contributors. For guidelines and entrypoints please have a look at the [Contributing section][docs-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.md [docs-contributing]: ./contributing/issues-and-pull-requests/

View File

@ -39,14 +39,14 @@ In a nutshell, DMS provides you with the following components:
- A MDA: [Dovecot](https://dovecot.org/) - A MDA: [Dovecot](https://dovecot.org/)
- A bunch of additional programs to improve security and emails processing - A bunch of additional programs to improve security and emails processing
Here's where DMS's toolchain fits within the delivery chain: Here's where DMS's toochain fits within the delivery chain:
```txt ```txt
docker-mailserver is here: docker-mailserver is here:
┏━━━━━━━┓ ┏━━━━━━━┓
Sending an email: MUA ---> MTA ---> (MTA relays) ---> ┫ MTA ╮ ┃ Sending an email: MUA ---> MTA ---> (MTA relays) ---> ┫ MTA ╮ ┃
Fetching an email: MUA <------------------------------ MDA Fetching an email: MUA <------------------------------ MDA
┗━━━━━━━┛ ┗━━━━━━━┛
``` ```
??? example "An Example" ??? example "An Example"
@ -86,18 +86,18 @@ When it comes to the specifics of email exchange, we have to look at protocols a
The following picture gives a visualization of the interplay of all components and their [respective ports][docs-understandports]: The following picture gives a visualization of the interplay of all components and their [respective ports][docs-understandports]:
```txt ```txt
┏━━━━━━━━━━ Submission ━━━━━━━━━━━━┓┏━━━━━━━━━━━━━ Transfer/Relay ━━━━━━━━━━━┓ ┏━━━━━━━━━━ Submission ━━━━━━━━━━━━┓┏━━━━━━━━━━━━━ Transfer/Relay ━━━━━━━━━━━┓
┌─────────────────────┐ ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ ┌─────────────────────┐ ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
MUA ----- STARTTLS -------> ┤(587) MTA ╮ (25)├ <-- cleartext ---> ┊ Third-party MTA ┊ MUA ----- STARTTLS ------> ┤(587) MTA ╮ (25)├ <-- cleartext ---> ┊ Third-party MTA ┊
----- implicit TLS ---> ┤(465) │ | └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘ ----- implicit TLS --> ┤(465) │ | └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
----- cleartext ------> ┤(25) │ | ----- cleartext -----> ┤(25) │ |
|┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄| |┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄|
MUA <---- STARTTLS -------- (143) MDA | MUA <---- STARTTLS ------- (143) MDA |
<---- implicit TLS ---- (993) | <---- implicit TLS --- (993) |
└─────────────────────┘ └─────────────────────┘
┗━━━━━━━━━━ Retrieval ━━━━━━━━━━━━━┛ ┗━━━━━━━━━━ Retrieval ━━━━━━━━━━━━━┛
``` ```
If you're new to email infrastructure, both that table and the schema may be confusing. If you're new to email infrastructure, both that table and the schema may be confusing.
@ -124,7 +124,7 @@ My MTA will thus have to support two kinds of Submission:
- Inbound Submission (third-party email has been submitted & relayed, then is accepted "inside" by the MTA) - Inbound Submission (third-party email has been submitted & relayed, then is accepted "inside" by the MTA)
```txt ```txt
┏━━━ Outbound Submission ━━━┓ ━━━ Outbound Submission ━━━┓
┌────────────────────┐ ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ ┌────────────────────┐ ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
Me ---------------> ┤ ├ -----------------> ┊ ┊ Me ---------------> ┤ ├ -----------------> ┊ ┊
@ -132,7 +132,7 @@ Me ---------------> ┤ ├ -----------------> ┊
│ ├ <----------------- │ ├ <-----------------
└────────────────────┘ └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘ └────────────────────┘ └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
┗━━━━━━━━━━ Inbound Submission ━━━━━━━━━━┛ ┗━━━━━━━━━━ Inbound Submission ━━━━━━━━━━┛
``` ```
#### Outbound Submission #### Outbound Submission
@ -168,7 +168,7 @@ Granted it's still very difficult enforcing encryption between MTAs (Transfer/Re
Overall, DMS's default configuration for SMTP looks like this: Overall, DMS's default configuration for SMTP looks like this:
```txt ```txt
┏━━━ Outbound Submission ━━━┓ ━━━ Outbound Submission ━━━┓
┌────────────────────┐ ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ ┌────────────────────┐ ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
Me -- cleartext --> ┤(25) (25)├ --- cleartext ---> ┊ ┊ Me -- cleartext --> ┤(25) (25)├ --- cleartext ---> ┊ ┊
@ -177,7 +177,7 @@ Me -- STARTTLS ---> ┤(587) │ ┊
│ (25)├ <---cleartext ---- │ (25)├ <---cleartext ----
└────────────────────┘ └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘ └────────────────────┘ └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
┗━━━━━━━━━━ Inbound Submission ━━━━━━━━━━┛ ┗━━━━━━━━━━ Inbound Submission ━━━━━━━━━━┛
``` ```
### Retrieval - IMAP ### Retrieval - IMAP
@ -204,13 +204,14 @@ The best practice as of 2020 would be [POP3S][wikipedia-pop3s] on port 995, rath
As a _batteries included_ container image, DMS provides you with all the required components and a default configuration to run a decent and secure mail server. One may then customize all aspects of its internal components. As a _batteries included_ container image, DMS provides you with all the required components and a default configuration to run a decent and secure mail server. One may then customize all aspects of its internal components.
- Simple customization is supported through [Docker Compose configuration][github-file-compose] and the [env-mailserver][github-file-envmailserver] configuration file. - Simple customization is supported through [docker-compose configuration][github-file-compose] and the [env-mailserver][github-file-envmailserver] configuration file.
- Advanced customization is supported through providing "monkey-patching" configuration files and/or [deriving your own image][github-file-dockerfile] from DMS's upstream, for a complete control over how things run. - Advanced customization is supported through providing "monkey-patching" configuration files and/or [deriving your own image][github-file-dockerfile] from DMS's upstream, for a complete control over how things run.
Eventually, it is up to _you_ deciding exactly what kind of transportation/encryption to use and/or enforce, and to customize your instance accordingly (with looser or stricter security). Be also aware that protocols and ports on your server can only go so far with security; third-party MTAs might relay your emails on insecure connections, man-in-the-middle attacks might still prove effective, etc. Advanced counter-measure such as DANE, MTA-STS and/or full body encryption (eg. PGP) should be considered as well for increased confidentiality, but ideally without compromising backwards compatibility so as to not block emails. Eventually, it is up to _you_ deciding exactly what kind of transportation/encryption to use and/or enforce, and to customize your instance accordingly (with looser or stricter security). Be also aware that protocols and ports on your server can only go so far with security; third-party MTAs might relay your emails on insecure connections, man-in-the-middle attacks might still prove effective, etc. Advanced counter-measure such as DANE, MTA-STS and/or full body encryption (eg. PGP) should be considered as well for increased confidentiality, but ideally without compromising backwards compatibility so as to not block emails.
[docs-understandports]: ./config/security/understanding-the-ports.md [docs-understandports]: ./config/security/understanding-the-ports.md
[github-file-compose]: https://github.com/docker-mailserver/docker-mailserver/blob/master/compose.yaml [github-file-compose]: https://github.com/docker-mailserver/docker-mailserver/blob/master/docker-compose.yml
[github-file-envmailserver]: https://github.com/docker-mailserver/docker-mailserver/blob/master/mailserver.env [github-file-envmailserver]: https://github.com/docker-mailserver/docker-mailserver/blob/master/mailserver.env
[github-file-dockerfile]: https://github.com/docker-mailserver/docker-mailserver/blob/master/Dockerfile [github-file-dockerfile]: https://github.com/docker-mailserver/docker-mailserver/blob/master/Dockerfile
[rfc-2487]: https://tools.ietf.org/html/rfc2487 [rfc-2487]: https://tools.ietf.org/html/rfc2487

View File

@ -2,16 +2,16 @@
title: Usage title: Usage
--- ---
This page explains how to get started with DMS. The guide uses Docker Compose as a reference. In our examples, a volume mounts the host location [`docker-data/dms/config/`][docs::dms-volumes-config] to `/tmp/docker-mailserver/` inside the container. This pages explains how to get started with DMS. The guide uses Docker Compose as a reference. In our examples, a volume mounts the host location [`docker-data/dms/config/`][docs-dms-config-volume] to `/tmp/docker-mailserver/` inside the container.
[docs::dms-volumes-config]: ./config/advanced/optional-config.md#volumes-config [docs-dms-config-volume]: ./faq.md#what-about-the-docker-datadmsconfig-directory
## Preliminary Steps ## Preliminary Steps
Before you can get started with deploying your own mail server, there are some requirements to be met: Before you can get started with deploying your own mail server, there are some requirements to be met:
1. You need to have a host that you can manage. 1. You need to have a host that you can manage.
2. You need to own a domain, and you need to be able to manage DNS for this domain. 2. You need to own a domain, and you need to able to manage DNS for this domain.
### Host Setup ### Host Setup
@ -21,17 +21,16 @@ There are a few requirements for a suitable host system:
2. The host should be able to send/receive on the [necessary ports for mail][docs-ports-overview] 2. The host should be able to send/receive on the [necessary ports for mail][docs-ports-overview]
3. You should be able to set a `PTR` record for your host; security-hardened mail servers might otherwise reject your mail server as the IP address of your host does not resolve correctly/at all to the DNS name of your server. 3. You should be able to set a `PTR` record for your host; security-hardened mail servers might otherwise reject your mail server as the IP address of your host does not resolve correctly/at all to the DNS name of your server.
!!! note "About the Container Runtime" On the host, you should have a suitable container runtime (like _Docker_ or _Podman_) installed. We assume [_Docker Compose_][docker-compose] is [installed][docker-compose-installation].
On the host, you need to have a suitable container runtime (like _Docker_ or _Podman_) installed. We assume [_Docker Compose_][docker-compose] is [installed][docker-compose-installation]. We have aligned file names and configuration conventions with the latest [Docker Compose (currently V2) specification][docker-compose-specification]. !!! info "Podman Support"
If you're using podman, make sure to read the related [documentation][docs-podman]. If you're using podman, make sure to read the related [documentation][docs-podman].
[docs-podman]: ./config/advanced/podman.md
[docs-ports-overview]: ./config/security/understanding-the-ports.md#overview-of-email-ports [docs-ports-overview]: ./config/security/understanding-the-ports.md#overview-of-email-ports
[docker-compose]: https://docs.docker.com/compose/ [docker-compose]: https://docs.docker.com/compose/
[docker-compose-installation]: https://docs.docker.com/compose/install/ [docker-compose-installation]: https://docs.docker.com/compose/install/
[docker-compose-specification]: https://docs.docker.com/compose/compose-file/
[docs-podman]: ./config/advanced/podman.md
### Minimal DNS Setup ### Minimal DNS Setup
@ -48,21 +47,10 @@ We will later dig into DKIM, DMARC & SPF, but for now, these are the records tha
- The **MX record** tells everyone which (DNS) name is responsible for e-mails on your domain. - The **MX record** tells everyone which (DNS) name is responsible for e-mails on your domain.
Because you want to keep the option of running another service on the domain name itself, you run your mail server on `mail.example.com`. Because you want to keep the option of running another service on the domain name itself, you run your mail server on `mail.example.com`.
This does not imply your e-mails will look like `test@mail.example.com`, the DNS name of your mail server is decoupled of the domain it serves e-mails for. This does not imply your e-mails will look like `test@mail.example.com`, the DNS name of your mail server is decoupled of the domain it serves e-mails for.
Your mail server could also handle emails for `test@some-other-domain.com`, if the MX record for `some-other-domain.com` points to `mail.example.com`. In theory, you mail server could even serve e-mails for `test@some-other-domain.com`, if the MX record for `some-other-domain.com` points to `mail.example.com`.
- The **A record** tells everyone which IP address the DNS name `mail.example.com` resolves to. - The **A record** tells everyone which IP address the DNS name `mail.example.com` resolves to.
- The **PTR record** is the counterpart of the A record, telling everyone what name the IP address `11.22.33.44` resolves to. - The **PTR record** is the counterpart of the A record, telling everyone what name the IP address `11.22.33.44` resolves to.
!!! note "About The Mail Server's Fully Qualified Domain Name"
The mail server's fully qualified domain name (FQDN) in our example above is `mail.example.com`. Please note though that this is more of a convention, and not due to technical restrictions. One could also run the mail server
1. on `foo.example.com`: you would just need to change your `MX` record;
2. on `example.com` directly: you would need to change your `MX` record and probably [read our docs on bare domain setups][docs-faq-bare-domain], as these setups are called "bare domain" setups.
The FQDN is what is relevant for TLS certificates, it has no (inherent/technical) relation to the email addresses and accounts DMS manages. That is to say: even though DMS runs on `mail.example.com`, or `foo.example.com`, or `example.com`, there is nothing that prevents it from managing mail for `barbaz.org` - `barbaz.org` will just need to set its `MX` record to `mail.example.com` (or `foo.example.com` or `example.com`).
[docs-faq-bare-domain]: ./faq.md#can-i-use-a-nakedbare-domain-ie-no-hostname
If you setup everything, it should roughly look like this: If you setup everything, it should roughly look like this:
```console ```console
@ -99,14 +87,14 @@ All workflows are using the tagging convention listed below. It is subsequently
Issue the following commands to acquire the necessary files: Issue the following commands to acquire the necessary files:
``` BASH ``` BASH
DMS_GITHUB_URL="https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master" DMS_GITHUB_URL="https://github.com/docker-mailserver/docker-mailserver/blob/latest"
wget "${DMS_GITHUB_URL}/compose.yaml" wget "${DMS_GITHUB_URL}/docker-compose.yml"
wget "${DMS_GITHUB_URL}/mailserver.env" wget "${DMS_GITHUB_URL}/mailserver.env"
``` ```
### Configuration Steps ### Configuration Steps
1. First edit `compose.yaml` to your liking 1. First edit `docker-compose.yml` to your liking
- Substitute `mail.example.com` according to your FQDN. - Substitute `mail.example.com` according to your FQDN.
- If you want to use SELinux for the `./docker-data/dms/config/:/tmp/docker-mailserver/` mount, append `-z` or `-Z`. - If you want to use SELinux for the `./docker-data/dms/config/:/tmp/docker-mailserver/` mount, append `-z` or `-Z`.
2. Then configure the environment specific to the mail server by editing [`mailserver.env`][docs-environment], but keep in mind that: 2. Then configure the environment specific to the mail server by editing [`mailserver.env`][docs-environment], but keep in mind that:
@ -164,7 +152,7 @@ You definitely want to setup TLS. Please refer to [our documentation about TLS][
You should add at least one [alias][docs-aliases], the [_postmaster alias_][docs-env-postmaster]. This is a common convention, but not strictly required. You should add at least one [alias][docs-aliases], the [_postmaster alias_][docs-env-postmaster]. This is a common convention, but not strictly required.
[docs-aliases]: ./config/account-management/overview.md#aliases [docs-aliases]: ./config/user-management.md#aliases
[docs-env-postmaster]: ./config/environment.md#postmaster_address [docs-env-postmaster]: ./config/environment.md#postmaster_address
```bash ```bash
@ -192,4 +180,3 @@ Here are some tools you can use to verify your configuration:
2. [DMARC Analyzer](https://www.mimecast.com/products/dmarc-analyzer/spf-record-check/) 2. [DMARC Analyzer](https://www.mimecast.com/products/dmarc-analyzer/spf-record-check/)
3. [mail-tester.com](https://www.mail-tester.com/) 3. [mail-tester.com](https://www.mail-tester.com/)
4. [multiRBL.valli.org](https://multirbl.valli.org/) 4. [multiRBL.valli.org](https://multirbl.valli.org/)
5. [internet.nl](https://internet.nl/test-mail/)

View File

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

View File

@ -30,11 +30,9 @@ LOG_LEVEL=info
# debug => Also show debug messages # debug => Also show debug messages
SUPERVISOR_LOGLEVEL= SUPERVISOR_LOGLEVEL=
# Support for deployment where these defaults are not compatible (eg: some NAS appliances): # 0 => mail state in default directories
# /var/mail vmail User ID (default: 5000) # 1 => consolidate all states into a single directory (`/var/mail-state`) to allow persistence using docker volumes
DMS_VMAIL_UID= ONE_DIR=1
# /var/mail vmail Group ID (default: 5000)
DMS_VMAIL_GID=
# **empty** => use FILE # **empty** => use FILE
# LDAP => use LDAP authentication # LDAP => use LDAP authentication
@ -89,10 +87,10 @@ TLS_LEVEL=
# Configures the handling of creating mails with forged sender addresses. # Configures the handling of creating mails with forged sender addresses.
# #
# **0** => (not recommended) Mail address spoofing allowed. Any logged in user may create email messages with a forged sender address (see also https://en.wikipedia.org/wiki/Email_spoofing). # **0** => (not recommended) Mail address spoofing allowed. Any logged in user may create email messages with a forged sender address (see also https://en.wikipedia.org/wiki/Email_spoofing).
# 1 => Mail spoofing denied. Each user may only send with their own or their alias addresses. Addresses with extension delimiters(http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages. # 1 => Mail spoofing denied. Each user may only send with his own or his alias addresses. Addresses with extension delimiters(http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
SPOOF_PROTECTION= SPOOF_PROTECTION=
# Enables the Sender Rewriting Scheme. SRS is needed if your mail server acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/main/README.rst) for further explanation. # Enables the Sender Rewriting Scheme. SRS is needed if your mail server acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/master/README.md#sender-rewriting-scheme-crash-course) for further explanation.
# - **0** => Disabled # - **0** => Disabled
# - 1 => Enabled # - 1 => Enabled
ENABLE_SRS=0 ENABLE_SRS=0
@ -115,27 +113,15 @@ ENABLE_OPENDMARC=1
# - **1** => Enabled # - **1** => Enabled
ENABLE_POLICYD_SPF=1 ENABLE_POLICYD_SPF=1
# Enables POP3 service # 1 => Enables POP3 service
# - **0** => Disabled # empty => disables POP3
# - 1 => Enabled
ENABLE_POP3= ENABLE_POP3=
# Enables IMAP service
# - 0 => Disabled
# - **1** => Enabled
ENABLE_IMAP=1
# Enables ClamAV, and anti-virus scanner. # Enables ClamAV, and anti-virus scanner.
# 1 => Enabled # 1 => Enabled
# **0** => Disabled # **0** => Disabled
ENABLE_CLAMAV=0 ENABLE_CLAMAV=0
# Add the value of this ENV as a prefix to the mail subject when spam is detected.
# NOTE: This subject prefix may be redundant (by default spam is delivered to a junk folder).
# It provides value when your junk mail is stored alongside legitimate mail instead of a separate location (like with `SPAMASSASSIN_SPAM_TO_INBOX=1` or `MOVE_SPAM_TO_JUNK=0` or a POP3 only setup, without IMAP).
# NOTE: When not using Docker Compose, other CRI may not support quote-wrapping the value here to preserve any trailing white-space.
SPAM_SUBJECT=
# Enables Rspamd # Enables Rspamd
# **0** => Disabled # **0** => Disabled
# 1 => Enabled # 1 => Enabled
@ -156,15 +142,6 @@ ENABLE_RSPAMD_REDIS=
# 1 => enabled # 1 => enabled
RSPAMD_LEARN=0 RSPAMD_LEARN=0
# This settings controls whether checks should be performed on emails coming
# from authenticated users (i.e. most likely outgoing emails). The default value
# is `0` in order to align better with SpamAssassin. We recommend reading
# through https://rspamd.com/doc/tutorials/scanning_outbound.html though to
# decide for yourself whether you need and want this feature.
#
# Note that DKIM signing of e-mails will still happen.
RSPAMD_CHECK_AUTHENTICATED=0
# Controls whether the Rspamd Greylisting module is enabled. # Controls whether the Rspamd Greylisting module is enabled.
# This module can further assist in avoiding spam emails by greylisting # This module can further assist in avoiding spam emails by greylisting
# e-mails with a certain spam score. # e-mails with a certain spam score.
@ -173,7 +150,7 @@ RSPAMD_CHECK_AUTHENTICATED=0
# 1 => enabled # 1 => enabled
RSPAMD_GREYLISTING=0 RSPAMD_GREYLISTING=0
# Can be used to enable or disable the Hfilter group module. # Can be used to enable or diable the Hfilter group module.
# #
# - 0 => Disabled # - 0 => Disabled
# - **1** => Enabled # - **1** => Enabled
@ -184,12 +161,6 @@ RSPAMD_HFILTER=1
# Default: 6 # Default: 6
RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE=6 RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE=6
# Can be used to enable or disable the (still experimental) neural module.
#
# - **0** => Disabled
# - 1 => Enabled
RSPAMD_NEURAL=0
# Amavis content filter (used for ClamAV & SpamAssassin) # Amavis content filter (used for ClamAV & SpamAssassin)
# 0 => Disabled # 0 => Disabled
# 1 => Enabled # 1 => Enabled
@ -207,7 +178,7 @@ AMAVIS_LOGLEVEL=0
# 1 => DNS block lists are enabled # 1 => DNS block lists are enabled
ENABLE_DNSBL=0 ENABLE_DNSBL=0
# If you enable Fail2Ban, don't forget to add the following lines to your `compose.yaml`: # If you enable Fail2Ban, don't forget to add the following lines to your `docker-compose.yml`:
# cap_add: # cap_add:
# - NET_ADMIN # - NET_ADMIN
# Otherwise, `nftables` won't be able to ban IPs. # Otherwise, `nftables` won't be able to ban IPs.
@ -262,17 +233,17 @@ VIRUSMAILS_DELETE_DELAY=
# `lmtp:<kopano-host>:2003` (use kopano as mailstore) # `lmtp:<kopano-host>:2003` (use kopano as mailstore)
POSTFIX_DAGENT= POSTFIX_DAGENT=
# Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default). Size is in bytes. # Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default).
# #
# empty => 0 # empty => 0
POSTFIX_MAILBOX_SIZE_LIMIT= POSTFIX_MAILBOX_SIZE_LIMIT=
# See https://docker-mailserver.github.io/docker-mailserver/latest/config/account-management/overview/#quotas # See https://docker-mailserver.github.io/docker-mailserver/edge/config/user-management/accounts/#notes
# 0 => Dovecot quota is disabled # 0 => Dovecot quota is disabled
# 1 => Dovecot quota is enabled # 1 => Dovecot quota is enabled
ENABLE_QUOTAS=1 ENABLE_QUOTAS=1
# Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!). Size is in bytes. # Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!)
# #
# empty => 10240000 (~10 MB) # empty => 10240000 (~10 MB)
POSTFIX_MESSAGE_SIZE_LIMIT= POSTFIX_MESSAGE_SIZE_LIMIT=
@ -347,9 +318,6 @@ REPORT_SENDER=
# Note: This variable can also determine the interval for Postfix's log summary reports, see [`PFLOGSUMM_TRIGGER`](#pflogsumm_trigger). # Note: This variable can also determine the interval for Postfix's log summary reports, see [`PFLOGSUMM_TRIGGER`](#pflogsumm_trigger).
LOGROTATE_INTERVAL=weekly LOGROTATE_INTERVAL=weekly
# Defines how many log files are kept by logrorate
LOGROTATE_COUNT=4
# If enabled, employs `reject_unknown_client_hostname` to sender restrictions in Postfix's configuration. # If enabled, employs `reject_unknown_client_hostname` to sender restrictions in Postfix's configuration.
# #
@ -365,12 +333,6 @@ POSTFIX_REJECT_UNKNOWN_CLIENT_HOSTNAME=0
# Note: More details at http://www.postfix.org/postconf.5.html#inet_protocols # Note: More details at http://www.postfix.org/postconf.5.html#inet_protocols
POSTFIX_INET_PROTOCOLS=all POSTFIX_INET_PROTOCOLS=all
# Enables MTA-STS support for outbound mail.
# More details: https://docker-mailserver.github.io/docker-mailserver/v13.3/config/best-practices/mta-sts/
# - **0** ==> MTA-STS disabled
# - 1 => MTA-STS enabled
ENABLE_MTA_STS=0
# Choose TCP/IP protocols for dovecot to use # Choose TCP/IP protocols for dovecot to use
# **all** => Listen on all interfaces # **all** => Listen on all interfaces
# ipv4 => Listen only on IPv4 interfaces. Most likely you want this behind Docker. # ipv4 => Listen only on IPv4 interfaces. Most likely you want this behind Docker.
@ -385,6 +347,9 @@ DOVECOT_INET_PROTOCOLS=all
ENABLE_SPAMASSASSIN=0 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. # 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. # If SpamAssassin is enabled, KAM can be used in addition to the default ruleset.
# - **0** => KAM disabled # - **0** => KAM disabled
@ -393,24 +358,21 @@ ENABLE_SPAMASSASSIN=0
# Note: only has an effect if `ENABLE_SPAMASSASSIN=1` # Note: only has an effect if `ENABLE_SPAMASSASSIN=1`
ENABLE_SPAMASSASSIN_KAM=0 ENABLE_SPAMASSASSIN_KAM=0
# deliver spam messages to the inbox (tagged using SPAM_SUBJECT)
SPAMASSASSIN_SPAM_TO_INBOX=1
# spam messages will be moved in the Junk folder (SPAMASSASSIN_SPAM_TO_INBOX=1 required) # spam messages will be moved in the Junk folder (SPAMASSASSIN_SPAM_TO_INBOX=1 required)
MOVE_SPAM_TO_JUNK=1 MOVE_SPAM_TO_JUNK=1
# spam messages will be marked as read # add spam info headers if at, or above that level:
MARK_SPAM_AS_READ=0
# add 'spam info' headers at, or above this level
SA_TAG=2.0 SA_TAG=2.0
# add 'spam detected' headers at, or above this level # add 'spam detected' headers at that level
SA_TAG2=6.31 SA_TAG2=6.31
# triggers spam evasive actions # triggers spam evasive actions
SA_KILL=10.0 SA_KILL=10.0
# add tag to subject if spam detected
SA_SPAM_SUBJECT=***SPAM*****
# ----------------------------------------------- # -----------------------------------------------
# --- Fetchmail Section ------------------------- # --- Fetchmail Section -------------------------
# ----------------------------------------------- # -----------------------------------------------
@ -419,45 +381,25 @@ ENABLE_FETCHMAIL=0
# The interval to fetch mail in seconds # The interval to fetch mail in seconds
FETCHMAIL_POLL=300 FETCHMAIL_POLL=300
# Use multiple fetchmail instances (1 per poll entry in fetchmail.cf)
# Supports multiple IMAP IDLE connections when a server is used across multiple poll entries
# https://otremba.net/wiki/Fetchmail_(Debian)#Immediate_Download_via_IMAP_IDLE
FETCHMAIL_PARALLEL=0
# Enable or disable `getmail`.
#
# - **0** => Disabled
# - 1 => Enabled
ENABLE_GETMAIL=0
# The number of minutes for the interval. Min: 1; Default: 5.
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 ------------------------------ # --- LDAP Section ------------------------------
# ----------------------------------------------- # -----------------------------------------------
# A second container for the ldap service is necessary (i.e. https://hub.docker.com/r/bitnami/openldap/) # A second container for the ldap service is necessary (i.e. https://github.com/osixia/docker-openldap)
# with the :edge tag, use ACCOUNT_PROVISIONER=LDAP
# empty => LDAP authentification is disabled
# 1 => LDAP authentification is enabled
ENABLE_LDAP=
# empty => no # empty => no
# yes => LDAP over TLS enabled for Postfix # yes => LDAP over TLS enabled for Postfix
LDAP_START_TLS= LDAP_START_TLS=
# empty => mail.example.com # If you going to use the mailserver in combination with docker-compose you can set the service name here
# Specify the `<dns-name>` / `<ip-address>` where the LDAP server is reachable via a URI like: `ldaps://mail.example.com`. # empty => mail.domain.com
# Note: You must include the desired URI scheme (`ldap://`, `ldaps://`, `ldapi://`). # Specify the dns-name/ip-address where the ldap-server
LDAP_SERVER_HOST= LDAP_SERVER_HOST=
# empty => ou=people,dc=domain,dc=com # empty => ou=people,dc=domain,dc=com
@ -508,7 +450,7 @@ DOVECOT_MAILBOX_FORMAT=maildir
# empty => no # empty => no
# yes => Allow bind authentication for LDAP # yes => Allow bind authentication for LDAP
# https://doc.dovecot.org/2.4.0/core/config/auth/databases/ldap.html#authentication-bind # https://wiki.dovecot.org/AuthDatabase/LDAP/AuthBinds
DOVECOT_AUTH_BIND= DOVECOT_AUTH_BIND=
# ----------------------------------------------- # -----------------------------------------------
@ -544,11 +486,11 @@ SASLAUTHD_MECHANISMS=
SASLAUTHD_MECH_OPTIONS= SASLAUTHD_MECH_OPTIONS=
# empty => Use value of LDAP_SERVER_HOST # empty => Use value of LDAP_SERVER_HOST
# Note: You must include the desired URI scheme (`ldap://`, `ldaps://`, `ldapi://`). # Note: since version 10.0.0, you can specify a protocol here (like ldaps://); this deprecates SASLAUTHD_LDAP_SSL.
SASLAUTHD_LDAP_SERVER= SASLAUTHD_LDAP_SERVER=
# empty => Use value of LDAP_BIND_DN # empty => Use value of LDAP_BIND_DN
# specify an object with privileges to search the directory tree # specify an object with priviliges to search the directory tree
# e.g. active directory: SASLAUTHD_LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=net # e.g. active directory: SASLAUTHD_LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=net
# e.g. openldap: SASLAUTHD_LDAP_BIND_DN=cn=admin,dc=mydomain,dc=net # e.g. openldap: SASLAUTHD_LDAP_BIND_DN=cn=admin,dc=mydomain,dc=net
SASLAUTHD_LDAP_BIND_DN= SASLAUTHD_LDAP_BIND_DN=
@ -629,8 +571,8 @@ SRS_SECRET=
# Setup relaying all mail through a default relay host # Setup relaying all mail through a default relay host
# #
# Set a default host to relay all mail through (optionally include a port) # empty => don't configure default relay host
# Example: [mail.example.com]:587 # default host and optional port to relay all mail through
DEFAULT_RELAY_HOST= DEFAULT_RELAY_HOST=
# ----------------------------------------------- # -----------------------------------------------
@ -640,22 +582,18 @@ DEFAULT_RELAY_HOST=
# Setup relaying for multiple domains based on the domain name of the sender # Setup relaying for multiple domains based on the domain name of the sender
# optionally uses usernames and passwords in postfix-sasl-password.cf and relay host mappings in postfix-relaymap.cf # optionally uses usernames and passwords in postfix-sasl-password.cf and relay host mappings in postfix-relaymap.cf
# #
# Set a default host to relay mail through # empty => don't configure relay host
# Example: mail.example.com # default host to relay mail through
RELAY_HOST= RELAY_HOST=
# empty => 25 # empty => 25
# default port to relay mail # default port to relay mail
RELAY_PORT=25 RELAY_PORT=25
# -----------------------------------------------
# --- Relay Host Credentials Section ------------
# -----------------------------------------------
# Configure a relay user and password to use with RELAY_HOST / DEFAULT_RELAY_HOST
# empty => no default # empty => no default
# default relay username (if no specific entry exists in postfix-sasl-password.cf)
RELAY_USER= RELAY_USER=
# empty => no default # empty => no default
# password for default relay user
RELAY_PASSWORD= RELAY_PASSWORD=

View File

@ -27,7 +27,8 @@ RESET=$(echo -ne '\e[0m')
set -euEo pipefail set -euEo pipefail
shopt -s inherit_errexit 2>/dev/null || true shopt -s inherit_errexit 2>/dev/null || true
function _show_local_usage() { function _show_local_usage
{
# shellcheck disable=SC2059 # shellcheck disable=SC2059
printf '%s' "${ORANGE}OPTIONS${RESET} printf '%s' "${ORANGE}OPTIONS${RESET}
${LBLUE}Config path, container or image adjustments${RESET} ${LBLUE}Config path, container or image adjustments${RESET}
@ -68,17 +69,22 @@ function _show_local_usage() {
" "
} }
function _get_absolute_script_directory() { function _get_absolute_script_directory
if dirname "$(readlink -f "${0}")" &>/dev/null; then {
if dirname "$(readlink -f "${0}")" &>/dev/null
then
DIR=$(dirname "$(readlink -f "${0}")") DIR=$(dirname "$(readlink -f "${0}")")
elif realpath -e -L "${0}" &>/dev/null; then elif realpath -e -L "${0}" &>/dev/null
then
DIR=$(realpath -e -L "${0}") DIR=$(realpath -e -L "${0}")
DIR="${DIR%/setup.sh}" DIR="${DIR%/setup.sh}"
fi fi
} }
function _set_default_config_path() { function _set_default_config_path
if [[ -d "${DIR}/config" ]]; then {
if [[ -d "${DIR}/config" ]]
then
# legacy path (pre v10.2.0) # legacy path (pre v10.2.0)
DEFAULT_CONFIG_PATH="${DIR}/config" DEFAULT_CONFIG_PATH="${DIR}/config"
else else
@ -86,20 +92,25 @@ function _set_default_config_path() {
fi fi
} }
function _handle_config_path() { function _handle_config_path
if [[ -z ${DESIRED_CONFIG_PATH} ]]; then {
if [[ -z ${DESIRED_CONFIG_PATH} ]]
then
# no desired config path # no desired config path
if [[ -n ${CONTAINER_NAME} ]]; then if [[ -n ${CONTAINER_NAME} ]]
then
VOLUME=$(${CRI} inspect "${CONTAINER_NAME}" \ VOLUME=$(${CRI} inspect "${CONTAINER_NAME}" \
--format="{{range .Mounts}}{{ println .Source .Destination}}{{end}}" | \ --format="{{range .Mounts}}{{ println .Source .Destination}}{{end}}" | \
grep "${DMS_CONFIG}$" 2>/dev/null || :) grep "${DMS_CONFIG}$" 2>/dev/null || :)
fi fi
if [[ -n ${VOLUME} ]]; then if [[ -n ${VOLUME} ]]
then
CONFIG_PATH=$(echo "${VOLUME}" | awk '{print $1}') CONFIG_PATH=$(echo "${VOLUME}" | awk '{print $1}')
fi fi
if [[ -z ${CONFIG_PATH} ]]; then if [[ -z ${CONFIG_PATH} ]]
then
CONFIG_PATH=${DEFAULT_CONFIG_PATH} CONFIG_PATH=${DEFAULT_CONFIG_PATH}
fi fi
else else
@ -107,9 +118,11 @@ function _handle_config_path() {
fi fi
} }
function _run_in_new_container() { function _run_in_new_container
{
# start temporary container with specified image # start temporary container with specified image
if ! ${CRI} history -q "${IMAGE_NAME}" &>/dev/null; then if ! ${CRI} history -q "${IMAGE_NAME}" &>/dev/null
then
echo "Image '${IMAGE_NAME}' not found. Pulling ..." echo "Image '${IMAGE_NAME}' not found. Pulling ..."
${CRI} pull "${IMAGE_NAME}" ${CRI} pull "${IMAGE_NAME}"
fi fi
@ -119,12 +132,14 @@ function _run_in_new_container() {
"${IMAGE_NAME}" "${@}" "${IMAGE_NAME}" "${@}"
} }
function _main() { function _main
{
_get_absolute_script_directory _get_absolute_script_directory
_set_default_config_path _set_default_config_path
local OPTIND local OPTIND
while getopts ":c:i:p:zZR" OPT; do while getopts ":c:i:p:zZR" OPT
do
case ${OPT} in case ${OPT} in
( i ) IMAGE_NAME="${OPTARG}" ;; ( i ) IMAGE_NAME="${OPTARG}" ;;
( z | Z ) USE_SELINUX=":${OPT}" ;; ( z | Z ) USE_SELINUX=":${OPT}" ;;
@ -136,7 +151,8 @@ function _main() {
( * ) DESIRED_CONFIG_PATH="${DIR}/${OPTARG}" ;; ( * ) DESIRED_CONFIG_PATH="${DIR}/${OPTARG}" ;;
esac esac
if [[ ! -d ${DESIRED_CONFIG_PATH} ]]; then if [[ ! -d ${DESIRED_CONFIG_PATH} ]]
then
echo "Specified directory '${DESIRED_CONFIG_PATH}' doesn't exist" >&2 echo "Specified directory '${DESIRED_CONFIG_PATH}' doesn't exist" >&2
exit 1 exit 1
fi fi
@ -153,11 +169,14 @@ function _main() {
done done
shift $(( OPTIND - 1 )) shift $(( OPTIND - 1 ))
if command -v docker &>/dev/null; then if command -v docker &>/dev/null
then
CRI=docker CRI=docker
elif command -v podman &>/dev/null; then elif command -v podman &>/dev/null
then
CRI=podman CRI=podman
if ! ${PODMAN_ROOTLESS} && [[ ${EUID} -ne 0 ]]; then if ! ${PODMAN_ROOTLESS} && [[ ${EUID} -ne 0 ]]
then
read -r -p "You are running Podman in rootless mode. Continue? [Y/n] " read -r -p "You are running Podman in rootless mode. Continue? [Y/n] "
[[ -n ${REPLY} ]] && [[ ${REPLY} =~ (n|N) ]] && exit 0 [[ -n ${REPLY} ]] && [[ ${REPLY} =~ (n|N) ]] && exit 0
fi fi
@ -171,11 +190,13 @@ function _main() {
[[ -z ${CONTAINER_NAME} ]] && CONTAINER_NAME=${INFO#*;} [[ -z ${CONTAINER_NAME} ]] && CONTAINER_NAME=${INFO#*;}
[[ -z ${IMAGE_NAME} ]] && IMAGE_NAME=${INFO%;*} [[ -z ${IMAGE_NAME} ]] && IMAGE_NAME=${INFO%;*}
if [[ -z ${IMAGE_NAME} ]]; then if [[ -z ${IMAGE_NAME} ]]
then
IMAGE_NAME=${NAME:-${DEFAULT_IMAGE_NAME}} IMAGE_NAME=${NAME:-${DEFAULT_IMAGE_NAME}}
fi fi
if test -t 0; then if test -t 0
then
USE_TTY="-it" USE_TTY="-it"
else else
# GitHub Actions will fail (or really anything else # GitHub Actions will fail (or really anything else
@ -186,7 +207,8 @@ function _main() {
_handle_config_path _handle_config_path
if [[ -n ${CONTAINER_NAME} ]]; then if [[ -n ${CONTAINER_NAME} ]]
then
${CRI} exec "${USE_TTY}" "${CONTAINER_NAME}" setup "${@}" ${CRI} exec "${USE_TTY}" "${CONTAINER_NAME}" setup "${@}"
else else
_run_in_new_container setup "${@}" _run_in_new_container setup "${@}"

View File

@ -1,7 +1,7 @@
use strict; use strict;
@local_domains_maps = ( @local_domains_maps = (
read_hash('/etc/postfix/vhost') read_hash('/etc/postfix/vhost')
); );
1; # ensure a defined return 1; # ensure a defined return

View File

@ -3,7 +3,8 @@
# shellcheck source=../scripts/helpers/index.sh # shellcheck source=../scripts/helpers/index.sh
source /usr/local/bin/helpers/index.sh source /usr/local/bin/helpers/index.sh
function _main() { function _main
{
_require_n_parameters_or_print_usage 2 "${@}" _require_n_parameters_or_print_usage 2 "${@}"
local MAIL_ALIAS="${1}" local MAIL_ALIAS="${1}"
@ -13,7 +14,8 @@ function _main() {
|| _exit_with_error "'${MAIL_ALIAS}' is already an alias for recipient: '${RECIPIENT}'" || _exit_with_error "'${MAIL_ALIAS}' is already an alias for recipient: '${RECIPIENT}'"
} }
function __usage() { function __usage
{
printf '%s' "${PURPLE}addalias${RED}(${YELLOW}8${RED}) printf '%s' "${PURPLE}addalias${RED}(${YELLOW}8${RED})
${ORANGE}USAGE${RESET} ${ORANGE}USAGE${RESET}
@ -33,9 +35,6 @@ ${ORANGE}EXAMPLES${RESET}
${LWHITE}./setup.sh alias add alias@example.com recipient@example.com${RESET} ${LWHITE}./setup.sh alias add alias@example.com recipient@example.com${RESET}
Add the alias 'alias@example.com' for the mail account 'recipient@example.com'. Add the alias 'alias@example.com' for the mail account 'recipient@example.com'.
${LWHITE}./setup.sh alias add alias@example.com 'recipient@example.com, another-recipient@example.com'${RESET}
Multiple recipients are separated by comma.
${ORANGE}EXIT STATUS${RESET} ${ORANGE}EXIT STATUS${RESET}
Exit status is 0 if command was successful. If wrong arguments are provided Exit status is 0 if command was successful. If wrong arguments are provided
or arguments contain errors, the script will exit early with exit status 1. or arguments contain errors, the script will exit early with exit status 1.

View File

@ -3,16 +3,19 @@
# shellcheck source=../scripts/helpers/index.sh # shellcheck source=../scripts/helpers/index.sh
source /usr/local/bin/helpers/index.sh source /usr/local/bin/helpers/index.sh
function _main() { function _main
{
_require_n_parameters_or_print_usage 1 "${@}" _require_n_parameters_or_print_usage 1 "${@}"
local MAIL_ACCOUNT="${1}" local MAIL_ACCOUNT="${1}"
local PASSWD="${2}" shift
local PASSWD="${*}"
_manage_accounts_dovecotmaster_create "${MAIL_ACCOUNT}" "${PASSWD}" _manage_accounts_dovecotmaster_create "${MAIL_ACCOUNT}" "${PASSWD}"
} }
function __usage() { function __usage
{
printf '%s' "${PURPLE}adddovecotmasteruser${RED}(${YELLOW}8${RED}) printf '%s' "${PURPLE}adddovecotmasteruser${RED}(${YELLOW}8${RED})
${ORANGE}USAGE${RESET} ${ORANGE}USAGE${RESET}

View File

@ -3,11 +3,13 @@
# shellcheck source=../scripts/helpers/index.sh # shellcheck source=../scripts/helpers/index.sh
source /usr/local/bin/helpers/index.sh source /usr/local/bin/helpers/index.sh
function _main() { function _main
{
_require_n_parameters_or_print_usage 1 "${@}" _require_n_parameters_or_print_usage 1 "${@}"
local MAIL_ACCOUNT="${1}" local MAIL_ACCOUNT="${1}"
local PASSWD="${2}" shift
local PASSWD="${*}"
_manage_accounts_create "${MAIL_ACCOUNT}" "${PASSWD}" _manage_accounts_create "${MAIL_ACCOUNT}" "${PASSWD}"
@ -16,7 +18,8 @@ function _main() {
# where the actual account is created in Dovecot. Expect a delay. # where the actual account is created in Dovecot. Expect a delay.
} }
function __usage() { function __usage
{
printf '%s' "${PURPLE}addmailuser${RED}(${YELLOW}8${RED}) printf '%s' "${PURPLE}addmailuser${RED}(${YELLOW}8${RED})
${ORANGE}USAGE${RESET} ${ORANGE}USAGE${RESET}

View File

@ -3,7 +3,8 @@
# shellcheck source=../scripts/helpers/index.sh # shellcheck source=../scripts/helpers/index.sh
source /usr/local/bin/helpers/index.sh source /usr/local/bin/helpers/index.sh
function _main() { function _main
{
_require_n_parameters_or_print_usage 2 "${@}" _require_n_parameters_or_print_usage 2 "${@}"
local DOMAIN="${1}" local DOMAIN="${1}"
@ -14,7 +15,8 @@ function _main() {
_add_relayhost _add_relayhost
} }
function __usage() { function __usage
{
printf '%s' "${PURPLE}addrelayhost${RED}(${YELLOW}8${RED}) printf '%s' "${PURPLE}addrelayhost${RED}(${YELLOW}8${RED})
${ORANGE}USAGE${RESET} ${ORANGE}USAGE${RESET}
@ -45,7 +47,8 @@ ${ORANGE}EXIT STATUS${RESET}
" "
} }
function _validate_parameters() { function _validate_parameters
{
[[ -z ${DOMAIN} ]] && { __usage ; _exit_with_error 'No domain specified' ; } [[ -z ${DOMAIN} ]] && { __usage ; _exit_with_error 'No domain specified' ; }
[[ -z ${HOST} ]] && { __usage ; _exit_with_error 'No relay host specified' ; } [[ -z ${HOST} ]] && { __usage ; _exit_with_error 'No relay host specified' ; }
[[ -z ${PORT} ]] && PORT=25 [[ -z ${PORT} ]] && PORT=25
@ -53,7 +56,8 @@ function _validate_parameters() {
# Config is for sender dependent relay-host mapping, # Config is for sender dependent relay-host mapping,
# current support restricts senders to domain scope (port is also enforced). # current support restricts senders to domain scope (port is also enforced).
function _add_relayhost() { function _add_relayhost
{
local SENDER="@${DOMAIN}" local SENDER="@${DOMAIN}"
local RELAY_HOST_ENTRY="[${HOST}]:${PORT}" local RELAY_HOST_ENTRY="[${HOST}]:${PORT}"
local DATABASE_RELAY='/tmp/docker-mailserver/postfix-relaymap.cf' local DATABASE_RELAY='/tmp/docker-mailserver/postfix-relaymap.cf'

View File

@ -3,18 +3,21 @@
# shellcheck source=../scripts/helpers/index.sh # shellcheck source=../scripts/helpers/index.sh
source /usr/local/bin/helpers/index.sh source /usr/local/bin/helpers/index.sh
function _main() { function _main
{
_require_n_parameters_or_print_usage 2 "${@}" _require_n_parameters_or_print_usage 2 "${@}"
local DOMAIN="${1}" local DOMAIN="${1}"
local RELAY_ACCOUNT="${2}" local RELAY_ACCOUNT="${2}"
local PASSWD="${3}" shift 2
local PASSWD="${*}"
_validate_parameters _validate_parameters
_add_relayhost_credentials _add_relayhost_credentials
} }
function __usage() { function __usage
{
printf '%s' "${PURPLE}addsaslpassword${RED}(${YELLOW}8${RED}) printf '%s' "${PURPLE}addsaslpassword${RED}(${YELLOW}8${RED})
${ORANGE}USAGE${RESET} ${ORANGE}USAGE${RESET}
@ -43,7 +46,8 @@ ${ORANGE}EXIT STATUS${RESET}
" "
} }
function _validate_parameters() { function _validate_parameters
{
[[ -z ${DOMAIN} ]] && { __usage ; _exit_with_error 'No domain specified' ; } [[ -z ${DOMAIN} ]] && { __usage ; _exit_with_error 'No domain specified' ; }
[[ -z ${RELAY_ACCOUNT} ]] && { __usage ; _exit_with_error 'No relay account specified' ; } [[ -z ${RELAY_ACCOUNT} ]] && { __usage ; _exit_with_error 'No relay account specified' ; }
_password_request_if_missing _password_request_if_missing
@ -54,7 +58,8 @@ function _validate_parameters() {
# #
# NOTE: This command does not support providing a relay-host # NOTE: This command does not support providing a relay-host
# as the lookup key, it only supports a lookup via sender domain. # as the lookup key, it only supports a lookup via sender domain.
function _add_relayhost_credentials() { function _add_relayhost_credentials
{
local SENDER="@${DOMAIN}" local SENDER="@${DOMAIN}"
local RELAY_HOST_ENTRY_AUTH="${RELAY_ACCOUNT}:${PASSWD}" local RELAY_HOST_ENTRY_AUTH="${RELAY_ACCOUNT}:${PASSWD}"
local DATABASE_PASSWD='/tmp/docker-mailserver/postfix-sasl-password.cf' local DATABASE_PASSWD='/tmp/docker-mailserver/postfix-sasl-password.cf'

View File

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

View File

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

View File

@ -3,7 +3,8 @@
# shellcheck source=../scripts/helpers/index.sh # shellcheck source=../scripts/helpers/index.sh
source /usr/local/bin/helpers/index.sh source /usr/local/bin/helpers/index.sh
function _main() { function _main
{
_require_n_parameters_or_print_usage 2 "${@}" _require_n_parameters_or_print_usage 2 "${@}"
local MAIL_ALIAS="${1}" local MAIL_ALIAS="${1}"
@ -12,7 +13,8 @@ function _main() {
_manage_virtual_aliases_delete "${MAIL_ALIAS}" "${RECIPIENT}" _manage_virtual_aliases_delete "${MAIL_ALIAS}" "${RECIPIENT}"
} }
function __usage() { function __usage
{
printf '%s' "${PURPLE}delalias${RED}(${YELLOW}8${RED}) printf '%s' "${PURPLE}delalias${RED}(${YELLOW}8${RED})
${ORANGE}USAGE${RESET} ${ORANGE}USAGE${RESET}

View File

@ -3,17 +3,20 @@
# shellcheck source=../scripts/helpers/index.sh # shellcheck source=../scripts/helpers/index.sh
source /usr/local/bin/helpers/index.sh source /usr/local/bin/helpers/index.sh
function _main() { function _main
{
_require_n_parameters_or_print_usage 1 "${@}" _require_n_parameters_or_print_usage 1 "${@}"
# Actual command to perform: # Actual command to perform:
for MAIL_ACCOUNT in "${@}"; do for MAIL_ACCOUNT in "${@}"
do
_manage_accounts_dovecotmaster_delete "${MAIL_ACCOUNT}" \ _manage_accounts_dovecotmaster_delete "${MAIL_ACCOUNT}" \
|| _exit_with_error "'${MAIL_ACCOUNT}' could not be deleted" || _exit_with_error "'${MAIL_ACCOUNT}' could not be deleted"
done done
} }
function __usage() { function __usage
{
printf '%s' "${PURPLE}deldovecotmasteruser${RED}(${YELLOW}8${RED}) printf '%s' "${PURPLE}deldovecotmasteruser${RED}(${YELLOW}8${RED})
${ORANGE}USAGE${RESET} ${ORANGE}USAGE${RESET}

View File

@ -3,7 +3,8 @@
# shellcheck source=../scripts/helpers/index.sh # shellcheck source=../scripts/helpers/index.sh
source /usr/local/bin/helpers/index.sh source /usr/local/bin/helpers/index.sh
function _main() { function _main
{
_require_n_parameters_or_print_usage 1 "${@}" _require_n_parameters_or_print_usage 1 "${@}"
# Tests expect early exit without error if no DB exists: # Tests expect early exit without error if no DB exists:
@ -16,14 +17,11 @@ function _main() {
_create_lock _create_lock
# Actual command to perform: # Actual command to perform:
for MAIL_ACCOUNT in "${@}"; do for MAIL_ACCOUNT in "${@}"
do
_account_should_already_exist _account_should_already_exist
if [[ ${MAILDEL} -eq 1 ]]; then [[ ${MAILDEL} -eq 1 ]] && _remove_maildir "${MAIL_ACCOUNT}"
_remove_maildir "${MAIL_ACCOUNT}"
else
_log 'info' "The mailbox data will not be deleted."
fi
_manage_virtual_aliases_delete '_' "${MAIL_ACCOUNT}" \ _manage_virtual_aliases_delete '_' "${MAIL_ACCOUNT}" \
|| _exit_with_error "Aliases for '${MAIL_ACCOUNT}' could not be deleted" || _exit_with_error "Aliases for '${MAIL_ACCOUNT}' could not be deleted"
@ -35,11 +33,12 @@ function _main() {
_manage_accounts_delete "${MAIL_ACCOUNT}" \ _manage_accounts_delete "${MAIL_ACCOUNT}" \
|| _exit_with_error "'${MAIL_ACCOUNT}' could not be deleted" || _exit_with_error "'${MAIL_ACCOUNT}' could not be deleted"
_log 'info' "'${MAIL_ACCOUNT}' and associated data (aliases, quotas) deleted" _log 'info' "'${MAIL_ACCOUNT}' and associated data deleted"
done done
} }
function __usage() { function __usage
{
printf '%s' "${PURPLE}delmailuser${RED}(${YELLOW}8${RED}) printf '%s' "${PURPLE}delmailuser${RED}(${YELLOW}8${RED})
${ORANGE}USAGE${RESET} ${ORANGE}USAGE${RESET}
@ -47,14 +46,14 @@ ${ORANGE}USAGE${RESET}
${ORANGE}OPTIONS${RESET} ${ORANGE}OPTIONS${RESET}
-y -y
Skip prompt by approving to ${LWHITE}delete all mail data${RESET} for the account(s). Skip prompt by approving to ${LWHITE}delete all mail storage${RESET} for the account(s).
${BLUE}Generic Program Information${RESET} ${BLUE}Generic Program Information${RESET}
help Print the usage information. help Print the usage information.
${ORANGE}DESCRIPTION${RESET} ${ORANGE}DESCRIPTION${RESET}
Delete a mail account, including associated data (aliases, quotas) and Delete a mail account, including associated data (aliases, quotas) and
optionally the mailbox data for that account. optionally the mailbox storage for that account.
${ORANGE}EXAMPLES${RESET} ${ORANGE}EXAMPLES${RESET}
${LWHITE}./setup.sh email del user@example.com${RESET} ${LWHITE}./setup.sh email del user@example.com${RESET}
@ -72,8 +71,10 @@ ${ORANGE}EXIT STATUS${RESET}
" "
} }
function _parse_options() { function _parse_options
while getopts ":yY" OPT; do {
while getopts ":yY" OPT
do
case "${OPT}" in case "${OPT}" in
( 'y' | 'Y' ) ( 'y' | 'Y' )
MAILDEL=1 MAILDEL=1
@ -88,27 +89,33 @@ function _parse_options() {
done done
} }
function _maildel_request_if_missing() { function _maildel_request_if_missing
if [[ ${MAILDEL} -eq 0 ]]; then {
if [[ ${MAILDEL} -eq 0 ]]
then
local MAILDEL_CHOSEN local MAILDEL_CHOSEN
read -r -p "Do you want to delete the mailbox data as well (removing all mails)? [y/N] " MAILDEL_CHOSEN read -r -p "Do you want to delete the mailbox as well (removing all mails)? [Y/n] " MAILDEL_CHOSEN
# Delete mailbox data only if the user provides explicit confirmation. # TODO: Why would MAILDEL be set to true if MAILDEL_CHOSEN is empty?
[[ ${MAILDEL_CHOSEN,,} == "y" ]] && MAILDEL=1 if [[ ${MAILDEL_CHOSEN} =~ (y|Y|yes|Yes) ]] || [[ -z ${MAILDEL_CHOSEN} ]]
then
MAILDEL=1
fi
fi fi
} }
function _remove_maildir() { function _remove_maildir
{
local MAIL_ACCOUNT=${1} local MAIL_ACCOUNT=${1}
local LOCAL_PART="${MAIL_ACCOUNT%@*}" local LOCAL_PART="${MAIL_ACCOUNT%@*}"
local DOMAIN_PART="${MAIL_ACCOUNT#*@}" local DOMAIN_PART="${MAIL_ACCOUNT#*@}"
local MAIL_ACCOUNT_STORAGE_DIR="/var/mail/${DOMAIN_PART}/${LOCAL_PART}" local MAIL_ACCOUNT_STORAGE_DIR="/var/mail/${DOMAIN_PART}/${LOCAL_PART}"
[[ ! -d ${MAIL_ACCOUNT_STORAGE_DIR} ]] && _exit_with_error "Mailbox data directory '${MAIL_ACCOUNT_STORAGE_DIR}' does not exist" [[ ! -d ${MAIL_ACCOUNT_STORAGE_DIR} ]] && _exit_with_error "Mailbox directory '${MAIL_ACCOUNT_STORAGE_DIR}' does not exist"
_log 'info' "Deleting mailbox data: '${MAIL_ACCOUNT_STORAGE_DIR}'" _log 'info' "Deleting Mailbox: '${MAIL_ACCOUNT_STORAGE_DIR}'"
rm -R "${MAIL_ACCOUNT_STORAGE_DIR}" || _exit_with_error 'Mailbox data could not be deleted' rm -R "${MAIL_ACCOUNT_STORAGE_DIR}" || _exit_with_error 'Mailbox could not be deleted'
# Remove parent directory too if it's empty: # Remove parent directory too if it's empty:
rmdir "/var/mail/${DOMAIN_PART}" &>/dev/null rmdir "/var/mail/${DOMAIN_PART}" &>/dev/null
} }

View File

@ -3,7 +3,8 @@
# shellcheck source=../scripts/helpers/index.sh # shellcheck source=../scripts/helpers/index.sh
source /usr/local/bin/helpers/index.sh source /usr/local/bin/helpers/index.sh
function _main() { function _main
{
_require_n_parameters_or_print_usage 1 "${@}" _require_n_parameters_or_print_usage 1 "${@}"
local MAIL_ACCOUNT="${1}" local MAIL_ACCOUNT="${1}"
@ -12,7 +13,8 @@ function _main() {
_manage_dovecot_quota_delete "${MAIL_ACCOUNT}" _manage_dovecot_quota_delete "${MAIL_ACCOUNT}"
} }
function __usage() { function __usage
{
printf '%s' "${PURPLE}delquota${RED}(${YELLOW}8${RED}) printf '%s' "${PURPLE}delquota${RED}(${YELLOW}8${RED})
${ORANGE}USAGE${RESET} ${ORANGE}USAGE${RESET}
@ -35,7 +37,8 @@ ${ORANGE}EXIT STATUS${RESET}
" "
} }
function _validate_parameters() { function _validate_parameters
{
_arg_expect_mail_account _arg_expect_mail_account
_account_should_already_exist _account_should_already_exist
} }

View File

@ -3,7 +3,8 @@
# shellcheck source=../scripts/helpers/index.sh # shellcheck source=../scripts/helpers/index.sh
source /usr/local/bin/helpers/index.sh source /usr/local/bin/helpers/index.sh
function _main() { function _main
{
_require_n_parameters_or_print_usage 1 "${@}" _require_n_parameters_or_print_usage 1 "${@}"
local DOMAIN="${1}" local DOMAIN="${1}"
@ -11,7 +12,8 @@ function _main() {
_exclude_domain_from_relayhosts _exclude_domain_from_relayhosts
} }
function __usage() { function __usage
{
printf '%s' "${PURPLE}excluderelayhost${RED}(${YELLOW}8${RED}) printf '%s' "${PURPLE}excluderelayhost${RED}(${YELLOW}8${RED})
${ORANGE}USAGE${RESET} ${ORANGE}USAGE${RESET}
@ -42,7 +44,8 @@ ${ORANGE}EXIT STATUS${RESET}
# Config is for sender dependent relay-host mapping, # Config is for sender dependent relay-host mapping,
# excludes appending a sender from the real generated mapping in `helpers/relay.sh`. # excludes appending a sender from the real generated mapping in `helpers/relay.sh`.
function _exclude_domain_from_relayhosts() { function _exclude_domain_from_relayhosts
{
local SENDER="@${DOMAIN}" local SENDER="@${DOMAIN}"
local DATABASE_RELAY='/tmp/docker-mailserver/postfix-relaymap.cf' local DATABASE_RELAY='/tmp/docker-mailserver/postfix-relaymap.cf'

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