Merge branch 'master' into document-tips-multiple-ip-addresses

This commit is contained in:
Brennan Kinney 2024-01-19 14:13:28 +13:00 committed by GitHub
commit 7315a59ea3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
255 changed files with 5480 additions and 3357 deletions

View File

@ -8,6 +8,7 @@ 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
@ -16,21 +17,9 @@ trim_trailing_whitespace = true
# --- Specific ---------------------------------- # --- Specific ----------------------------------
# ----------------------------------------------- # -----------------------------------------------
[*.{yaml,yml,sh,bats}] [{Makefile,.gitmodules}]
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

159
.gitattributes vendored Normal file
View File

@ -0,0 +1,159 @@
# 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
.all-contributorsrc text export-ignore
.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,18 +1,10 @@
name: Other name: Other
description: Miscellaneous questions and reports description: Miscellaneous questions and reports for the project (not support)
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:
@ -21,13 +13,18 @@ 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
- I have questions about TLS/SSL/STARTTLS/OpenSSL - Something else that requires developers attention
- Other
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: description id: description
attributes:
label: Description
validations: validations:
required: true required: true
attributes:
label: Description
value: |
<!---
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

@ -1,7 +1,9 @@
# Description # Description
<!-- Include a summary of the change. <!--
Please also include relevant motivation and context. --> Include a summary of the change.
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 #
@ -24,3 +26,4 @@ Fixes #
- [ ] 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`**

View File

@ -10,78 +10,24 @@ permissions:
pull-requests: write pull-requests: write
statuses: 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: jobs:
add-contributors: add-contributors:
name: 'Add Contributors' name: 'Add Contributors'
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: 'Checkout' - name: 'Checkout'
uses: actions/checkout@v3 uses: actions/checkout@v4
- 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' - name: 'Update CONTRIBUTORS.md'
uses: BobAnkh/add-contributors@v0.2.2 uses: akhilmhdh/contributors-readme-action@v2.3.6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
ACCESS_TOKEN: ${{secrets.GITHUB_TOKEN}} readme_path: CONTRIBUTORS.md
BRANCH: ${{ env.BRANCH_NAME }} collaborators: all
COMMIT_MESSAGE: 'docs: update `CONTRIBUTORS.md`' use_username: true
PATH: /CONTRIBUTORS.md commit_message: 'docs: updated `CONTRIBUTORS.md`'
CONTRIBUTOR: '## Contributors' committer_username: github-actions[bot]
COLUMN_PER_ROW: 6 committer_email: 41898282+github-actions[bot]@users.noreply.github.com
IMG_WIDTH: 100 pr_title_on_protected: 'docs: update `CONTRIBUTORS.md`'
FONT_SIZE: 14 auto_detect_branch_protection: true
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.2
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@v1.1.7
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

@ -25,7 +25,7 @@ jobs:
# The official Github Action for downloading artifacts does not support multi-workflow # The official Github Action for downloading artifacts does not support multi-workflow
- name: 'Download build artifact' - name: 'Download build artifact'
uses: dawidd6/action-download-artifact@v2 uses: dawidd6/action-download-artifact@v3
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
run_id: ${{ github.event.workflow_run.id }} run_id: ${{ github.event.workflow_run.id }}
@ -46,7 +46,7 @@ jobs:
# but presently does not work correctly via split workflow. It is useful in a split workflow as the 1st stage # but presently does not work correctly via split workflow. It is useful in a split workflow as the 1st stage
# no longer indicates if the entire workflow/deployment was successful. # no longer indicates if the entire workflow/deployment was successful.
- name: 'Commit Status: Set Workflow Status as Pending' - name: 'Commit Status: Set Workflow Status as Pending'
uses: myrotvorets/set-commit-status-action@v1.1.7 uses: myrotvorets/set-commit-status-action@v2.0.0
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
status: pending status: pending
@ -56,7 +56,7 @@ jobs:
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@v2.0 uses: nwtgck/actions-netlify@v2.1
id: preview id: preview
timeout-minutes: 1 timeout-minutes: 1
env: env:
@ -106,7 +106,7 @@ jobs:
Built with commit: ${{ env.PR_HEADSHA }} Built with commit: ${{ env.PR_HEADSHA }}
- name: 'Commit Status: Update deployment status' - name: 'Commit Status: Update deployment status'
uses: myrotvorets/set-commit-status-action@v1.1.7 uses: myrotvorets/set-commit-status-action@v2.0.0
# Always run this step regardless of job failing early: # Always run this step regardless of job failing early:
if: ${{ always() }} if: ${{ always() }}
env: env:

View File

@ -29,7 +29,7 @@ jobs:
NETLIFY_SITE_PREFIX: pullrequest-${{ github.event.pull_request.number }} NETLIFY_SITE_PREFIX: pullrequest-${{ github.event.pull_request.number }}
NETLIFY_SITE_NAME: dms-doc-previews NETLIFY_SITE_NAME: dms-doc-previews
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: 'Build with mkdocs-material via Docker' - name: 'Build with mkdocs-material via Docker'
working-directory: docs working-directory: docs
@ -73,7 +73,7 @@ jobs:
tar --zstd -cf artifact.tar.zst pr.env ${{ env.BUILD_DIR }} 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@v3 uses: actions/upload-artifact@v4
with: with:
name: preview-build name: preview-build
path: artifact.tar.zst path: artifact.tar.zst

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@v3 - uses: actions/checkout@v4
- 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/')
@ -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@v3 uses: actions/checkout@v4
- name: 'Checkout the docs deployment branch to a subdirectory' - name: 'Checkout the docs deployment branch to a subdirectory'
uses: actions/checkout@v3 uses: actions/checkout@v4
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@v3 uses: actions/checkout@v4
with: with:
ref: gh-pages ref: gh-pages

View File

@ -28,7 +28,7 @@ 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@v3 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
@ -74,16 +74,16 @@ jobs:
cache-buildx- cache-buildx-
- name: 'Set up QEMU' - name: 'Set up QEMU'
uses: docker/setup-qemu-action@v2.2.0 uses: docker/setup-qemu-action@v3.0.0
with: with:
platforms: arm64 platforms: arm64
- name: 'Set up Docker Buildx' - name: 'Set up Docker Buildx'
uses: docker/setup-buildx-action@v2.9.1 uses: docker/setup-buildx-action@v3.0.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@v4.1.1 uses: docker/build-push-action@v5.1.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@v3 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: 'Prepare tags' - name: 'Prepare tags'
id: prep id: prep
uses: docker/metadata-action@v4.6.0 uses: docker/metadata-action@v5.5.0
with: with:
images: | images: |
${{ secrets.DOCKER_REPOSITORY }} ${{ secrets.DOCKER_REPOSITORY }}
@ -35,12 +35,12 @@ 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@v2.2.0 uses: docker/setup-qemu-action@v3.0.0
with: with:
platforms: arm64 platforms: arm64
- name: 'Set up Docker Buildx' - name: 'Set up Docker Buildx'
uses: docker/setup-buildx-action@v2.9.1 uses: docker/setup-buildx-action@v3.0.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`),
@ -54,30 +54,25 @@ jobs:
cache-buildx- cache-buildx-
- name: 'Login to DockerHub' - name: 'Login to DockerHub'
uses: docker/login-action@v2 uses: docker/login-action@v3
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@v2 uses: docker/login-action@v3
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@v4.1.1 uses: docker/build-push-action@v5.1.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@v3 uses: actions/checkout@v4
with: with:
# Required to retrieve bats (core + extras): # Required to retrieve bats (core + extras):
submodules: recursive submodules: recursive
@ -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@v2.9.1 uses: docker/setup-buildx-action@v3.0.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@v4.1.1 uses: docker/build-push-action@v5.1.0
with: with:
context: . context: .
tags: mailserver-testing:ci tags: mailserver-testing:ci

View File

@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: 'Checkout' - name: 'Checkout'
uses: actions/checkout@v3 uses: actions/checkout@v4
# 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.
@ -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@v2.9.1 uses: docker/setup-buildx-action@v3.0.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@v4.1.1 uses: docker/build-push-action@v5.1.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@v3.3.6 uses: anchore/scan-action@v3.5.0
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@v2 uses: github/codeql-action/upload-sarif@v3
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@v8 uses: actions/stale@v9
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 20 days-before-stale: 20

View File

@ -1,6 +1,9 @@
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 ]
@ -13,7 +16,7 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Hadolint - name: Hadolint
run: make hadolint run: make hadolint

View File

@ -10,7 +10,7 @@ docker run \
--user "$(id -u):$(id -g)" \ --user "$(id -u):$(id -g)" \
--volume "${PWD}:/docs" \ --volume "${PWD}:/docs" \
--name "build-docs" \ --name "build-docs" \
squidfunk/mkdocs-material:9.1.5 build --strict squidfunk/mkdocs-material:9.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,6 +3,7 @@
################################################# #################################################
.env .env
compose.override.yaml
docs/site/ docs/site/
docker-data/ docker-data/

View File

@ -2,10 +2,248 @@
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/v12.1.0...HEAD) ## [Unreleased](https://github.com/docker-mailserver/docker-mailserver/compare/v13.2.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.
### Features
- **Authentication with OIDC / OAuth 2.0** 🎉
- DMS now supports authentication via OAuth2 (_via `XOAUTH2` or `OAUTHBEARER` SASL mechanisms_) from capable services (_like Roundcube_).
- This does not replace the need for an `ACCOUNT_PROVISIONER` (`FILE` / `LDAP`), which is required for an account to receive or send mail.
- Successful authentication (_via Dovecot PassDB_) still requires an existing account (_lookup via Dovecot UserDB_).
- **MTA-STS** (_Optional support for mandatory outgoing TLS encryption_)
- If enabled and the outbound recipient has an MTA-STS policy set, TLS is mandatory for delivering to that recipient.
- Enable via the ENV `ENABLE_MTA_STS=1`
- Supported by major email service providers like Gmail, Yahoo and Outlook.
### Updates
- **Tests**:
- 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:**
- tests: Replace `wc -l` with `grep -c` ([#3752](https://github.com/docker-mailserver/docker-mailserver/pull/3752))
- Postfix is now configured with `smtputf8_enable = no` in our default `main.cf` config (_instead of during container startup_). ([#3750](https://github.com/docker-mailserver/docker-mailserver/pull/3750))
- **Rspamd** ([#3726](https://github.com/docker-mailserver/docker-mailserver/pull/3726)):
- symbol scores for SPF, DKIM & DMARC were updated to more closely align with [RFC7489](https://www.rfc-editor.org/rfc/rfc7489#page-24); please note though that complete alignment is undesirable, because other symbols might be added as well, which changes the overall score calculation again, see [this issue](https://github.com/docker-mailserver/docker-mailserver/issues/3690#issuecomment-1866871996)
- **Docs:**
- Revised the SpamAssassin ENV docs to better communicate configuration and their relation to other ENV settings. ([#3756](https://github.com/docker-mailserver/docker-mailserver/pull/3756))
- Detailed how mail received is assigned a spam score by Rspamd and processed accordingly ([#3773](https://github.com/docker-mailserver/docker-mailserver/pull/3773))
### Fixes
- **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.
- 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
@ -17,7 +255,7 @@ All notable changes to this project will be documented in this file. The format
- 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 setup for Fail2Ban ([#3299](https://github.com/docker-mailserver/docker-mailserver/pull/3299)) - add 'log' command to set up 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
@ -206,7 +444,11 @@ 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 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. 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.
### Deprecated
- The environment variable `ENABLE_LDAP` is deprecated and will be removed in [13.0.0]. Use `ACCOUNT_PROVISIONER=LDAP` now.
### Added ### Added
@ -233,10 +475,6 @@ 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
@ -303,8 +541,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.
@ -360,8 +598,7 @@ 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 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)).
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

File diff suppressed because it is too large Load Diff

View File

@ -106,6 +106,14 @@ EOF
# ----------------------------------------------- # -----------------------------------------------
COPY target/rspamd/local.d/ /etc/rspamd/local.d/ COPY target/rspamd/local.d/ /etc/rspamd/local.d/
COPY target/rspamd/scores.d/* /etc/rspamd/scores.d/
# -----------------------------------------------
# --- OAUTH2 ------------------------------------
# -----------------------------------------------
COPY target/dovecot/auth-oauth2.conf.ext /etc/dovecot/conf.d
COPY target/dovecot/dovecot-oauth2.conf.ext /etc/dovecot
# ----------------------------------------------- # -----------------------------------------------
# --- LDAP & SpamAssassin's Cron ---------------- # --- LDAP & SpamAssassin's Cron ----------------
@ -134,9 +142,7 @@ 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://postgrey.schweikert.ch/pub/postgrey_whitelist_clients curl -Lsfo /etc/postgrey/whitelist_clients https://postgrey.schweikert.ch/pub/postgrey_whitelist_clients
@ -193,6 +199,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 -----------------------
# --------------------------------------------------
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 --- # --- Fetchmail, Getmail, Postfix & Let'sEncrypt ---
# -------------------------------------------------- # --------------------------------------------------
@ -279,8 +294,6 @@ RUN <<EOF
update-locale update-locale
EOF EOF
COPY VERSION /
COPY \ COPY \
target/bin/* \ target/bin/* \
target/scripts/*.sh \ target/scripts/*.sh \
@ -297,8 +310,8 @@ COPY target/scripts/startup/setup.d /usr/local/bin/setup.d
# #
FROM stage-main AS stage-final FROM stage-main 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
@ -322,11 +335,12 @@ 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, Antispam, Antivirus, etc.). Only configuration files, no SQL database." LABEL org.opencontainers.image.description="A fullstack but simple mail server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.). Only configuration files, no SQL database."
LABEL org.opencontainers.image.url="https://github.com/docker-mailserver" LABEL org.opencontainers.image.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=${VCS_VERSION} LABEL org.opencontainers.image.version=${DMS_RELEASE}
ENV DMS_RELEASE=${DMS_RELEASE}

View File

@ -18,11 +18,7 @@ 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_BUILDKIT=1 docker build \ @ docker build --tag $(IMAGE_NAME) .
--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
@ -36,6 +32,22 @@ 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 ------------------------------------
# ----------------------------------------------- # -----------------------------------------------

View File

@ -11,7 +11,7 @@
## :page_with_curl: About ## :page_with_curl: About
A production-ready fullstack but simple containerized mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). Only configuration files, no SQL database. Keep it simple and versioned. Easy to deploy and upgrade. Originally created by @tomav, this project is now maintained by volunteers since January 2021. A production-ready fullstack but simple containerized mail server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.). Only configuration files, no SQL database. Keep it simple and versioned. Easy to deploy and upgrade. Originally created by @tomav, this project is now maintained by volunteers since January 2021.
## :bulb: Documentation ## :bulb: Documentation
@ -48,3 +48,4 @@ If you have issues, please search through [the documentation][documentation::web
- Support for [LetsEncrypt](https://letsencrypt.org/), manual and self-signed certificates - 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_)

View File

@ -1 +1 @@
12.1.0 13.2.0

View File

@ -9,7 +9,7 @@ services:
# 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. # To avoid conflicts with yaml base-60 float, DO NOT remove the quotation marks.
ports: ports:
- "25:25" # SMTP (explicit TLS => STARTTLS) - "25:25" # SMTP (explicit TLS => STARTTLS, Authentication is DISABLED => use port 465/587 instead)
- "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

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

View File

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

View File

@ -18,4 +18,4 @@ Once a master account is configured, it is possible to connect to any users mail
Username: `<EMAIL ADDRESS>*<MASTER ACCOUNT NAME>` Username: `<EMAIL ADDRESS>*<MASTER ACCOUNT NAME>`
Password: `<MASTER ACCOUNT PASSWORD>` Password: `<MASTER ACCOUNT PASSWORD>`

View File

@ -60,7 +60,7 @@ Enable `ip6tables` support so that Docker will manage IPv6 networking rules as w
``` ```
- `experimental: true` is currently required for `ip6tables: true` to work. - `experimental: true` is currently required for `ip6tables: true` to work.
- `userland-proxy` setting [can potentially affect connection behaviour][gh-pull-3244-proxy] for local connections. - `userland-proxy` setting [can potentially affect connection behavior][gh-pull-3244-proxy] for local connections.
Now restart the daemon if it's running: `systemctl restart docker`. Now restart the daemon if it's running: `systemctl restart docker`.
@ -92,12 +92,14 @@ Next, configure a network with an IPv6 subnet for your container with any of the
networks: networks:
dms-ipv6: dms-ipv6:
enable_ipv6: true enable_ipv6: true
subnet: fd00:cafe:face:feed::/64 ipam:
config:
- subnet: fd00:cafe:face:feed::/64
``` ```
??? tip "Override the implicit `default` network" ??? tip "Override the implicit `default` network"
You can optionally avoid the service assignment by [overriding the `default` user-defined network that Docker Compose generates](docker-docs-network-compose-default). Just replace `dms-ipv6` with `default`. You can optionally avoid the service assignment by [overriding the `default` user-defined network that Docker Compose generates][docker-docs-network-compose-default]. Just replace `dms-ipv6` with `default`.
The Docker Compose `default` bridge is not affected by settings for the default `bridge` (aka `docker0`) in `/etc/docker/daemon.json`. The Docker Compose `default` bridge is not affected by settings for the default `bridge` (aka `docker0`) in `/etc/docker/daemon.json`.
@ -130,7 +132,7 @@ Next, configure a network with an IPv6 subnet for your container with any of the
!!! warning "This approach is discouraged" !!! warning "This approach is discouraged"
The [`bridge` network is considered legacy][docker-docs-network-bridge-legacy]. 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`_). 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`_).
@ -178,7 +180,7 @@ curl --max-time 5 http://[2001:db8::1]:80
!!! info "IPv6 ULA address priority" !!! 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. 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]. 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 [docker-subnets]: https://straz.to/2021-09-08-docker-address-pools/#what-are-the-default-address-pools-when-no-configuration-is-given-vanilla-pools

View File

@ -190,7 +190,10 @@ spec:
imagePullPolicy: IfNotPresent imagePullPolicy: IfNotPresent
securityContext: securityContext:
allowPrivilegeEscalation: false # Required to support SGID via `postdrop` executable
# in `/var/mail-state` for Postfix (maildrop + public dirs):
# https://github.com/docker-mailserver/docker-mailserver/pull/3625
allowPrivilegeEscalation: true
readOnlyRootFilesystem: false readOnlyRootFilesystem: false
runAsUser: 0 runAsUser: 0
runAsGroup: 0 runAsGroup: 0

View File

@ -11,9 +11,9 @@ There are global and user specific filters which are filtering the incoming emai
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. 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.) 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/.dovecot.sieve`. If this file exists dovecot will apply the filtering 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/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`.
@ -69,12 +69,12 @@ It is possible to sort subaddresses such as `user+mailing-lists@example.com` int
require ["envelope", "fileinto", "mailbox", "subaddress", "variables"]; require ["envelope", "fileinto", "mailbox", "subaddress", "variables"];
if envelope :detail :matches "to" "*" { if envelope :detail :matches "to" "*" {
set :lower :upperfirst "tag" "${1}"; set :lower :upperfirst "tag" "${1}";
if mailboxexists "INBOX.${1}" { if mailboxexists "INBOX.${1}" {
fileinto "INBOX.${1}"; fileinto "INBOX.${1}";
} else { } else {
fileinto :create "INBOX.${tag}"; fileinto :create "INBOX.${tag}";
} }
} }
``` ```

View File

@ -2,40 +2,52 @@
title: 'Maintenance | Update and Cleanup' title: 'Maintenance | Update and Cleanup'
--- ---
## Automatic Update [`containrrr/watchtower`][watchtower-dockerhub] is a service that monitors Docker images for updates, automatically applying them to running containers.
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. !!! example "Automatic image updates + cleanup"
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). Run a `watchtower` container with access to `docker.sock`, enabling the service to manage Docker:
A Docker Compose example: ```yaml title="compose.yaml"
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
```
```yaml !!! tip "The image tag used for a container is monitored for updates (eg: `:latest`, `:edge`, `:13`)"
services:
watchtower:
restart: always
image: containrrr/watchtower:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
```
For more details, see the [manual](https://containrrr.github.io/watchtower/) The automatic update support is **only for updates to that specific image tag**.
## Automatic Cleanup - Your container will not update to a new major version tag (_unless using `:latest`_).
- 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`_).
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/). !!! tip "Updating only specific containers"
A Docker Compose example: 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`_).
```yaml 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].
services:
docker-gc:
restart: always
image: spotify/docker-gc:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
```
For more details, see the [manual](https://github.com/spotify/docker-gc/blob/master/README.md) !!! info "Manual cleanup"
Or you can just use the [`--cleanup`](https://containrrr.github.io/watchtower/arguments/#cleanup) option provided by `containrrr/watchtower`. `watchtower` also supports running on-demand with `docker run` or `compose.yaml` via the `--run-once` option.
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

@ -12,7 +12,7 @@ This is a list of all configuration files and directories which are optional or
- **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
@ -33,9 +33,9 @@ This is a list of all configuration files and directories which are optional or
- **ldap-aliases.cf:** Configuration for the virtual alias mapping `virtual_alias_maps`. See the [`setup-stack.sh`][github-commit-setup-stack.sh-L411] script. - **ldap-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:** Antispam rules for Spamassassin. (Docs: [FAQ - SpamAssassin Rules][docs-faq-spamrules]) - **spamassassin-rules.cf:** Anti-spam rules for Spamassassin. (Docs: [FAQ - SpamAssassin Rules][docs-faq-spamrules])
- **fail2ban-fail2ban.cf:** Additional config options for `fail2ban.cf`. (Docs: [Fail2Ban][docs-fail2ban]) - **fail2ban-fail2ban.cf:** Additional config options for `fail2ban.cf`. (Docs: [Fail2Ban][docs-fail2ban])
- **fail2ban-jail.cf:** Additional config options for fail2ban's jail behaviour. (Docs: [Fail2Ban][docs-fail2ban]) - **fail2ban-jail.cf:** Additional config options for fail2ban's jail behavior. (Docs: [Fail2Ban][docs-fail2ban])
- **amavis.cf:** replaces the `/etc/amavis/conf.d/50-user` file - **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])

View File

@ -107,7 +107,7 @@ 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 `compose.yaml` has to be modified.
As shown in the [fail2ban section](../../security/fail2ban/#podman-with-slirp4netns-port-driver) the `slirp4netns` network driver has to be enabled. As shown in the [fail2ban section](../security/fail2ban.md#podman-with-slirp4netns-port-driver) the `slirp4netns` network driver has to be enabled.
This network driver enables podman to correctly resolve IP addresses but it is not compatible with 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.

View File

@ -35,14 +35,67 @@ DKIM requires a public/private key pair to enable **signing (_via private key_)*
### 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-add] - 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
!!! warning "RSA Key Sizes >= 4096 Bit" !!! example "Creating DKIM Keys"
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]. DKIM keys can be generated with good defaults by running:
```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][github-issue-dkimlength]. If 2048-bit does not meet your security needs, you may want to instead consider adopting key rotation or switching from RSA to ECC keys for DKIM.
??? 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 additionanl mail domains it should know about. If the primary mail domain is not sufficient, then you must explicitly specify any extra domains via the `domain` option:
```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:
@ -50,68 +103,27 @@ 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].
The command `docker exec <CONTAINER NAME> setup config dkim help` details supported config options, along with some examples. After running `setup config dkim`, your new DKIM key files (_and OpenDKIM config_) have been added to `/tmp/docker-mailserver/opendkim/`.
!!! 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"
Opt-in via [`ENABLE_RSPAMD=1`][docs-env-rspamd] (_and disable the default OpenDKIM: `ENABLE_OPENDKIM=0`_). Requires 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).
!!! example "Creating DKIM Keys"
You can simply run
```bash
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.
??? warning "Using Multiple Domains" ??? warning "Using Multiple Domains"
Unlike the current script for OpenDKIM, the Rspamd script will **not** create keys for all domains DMS is managing, but only for the one it assumes to be the main domain (derived from DMS' domain name). Moreover, the default `dkim_signing.conf` configuration file that DMS ships will also only contain one domain. If you have multiple domains, you need to run the command `docker exec -ti <CONTAINER NAME> setup config dkim domain <DOMAIN>` multiple times to create all the keys for all domains, and then provide a custom `dkim_signing.conf` (for which an example is shown below). If you have multiple domains, you need to:
- Create a key wth `docker exec -it <CONTAINER NAME> setup config dkim domain <DOMAIN>` for each domain DMS should sign outgoing mail for.
- Provide a custom `dkim_signing.conf` (for which an example is shown below), as the default config only supports one domain.
!!! info "About the Helper Script" !!! info "About the Helper Script"
@ -121,7 +133,9 @@ 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 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). In case you have not already provided a default DKIM signing configuration, the script will create one and write it to `/etc/rspamd/override.d/dkim_signing.conf`. If this file already exists, it will not be overwritten.
When you're already using [the `rspamd/override.d/` directory][docs-rspamd-config-dropin], the file is created inside your volume and therefore persisted correctly. If you are not using `rspamd/override.d/`, you will need to persist the file yourself (otherwise it is lost on container restart).
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.
@ -142,6 +156,13 @@ DKIM is currently supported by either OpenDKIM or Rspamd:
use_esld = true; use_esld = true;
check_pubkey = true; # you want to use this in the beginning check_pubkey = true; # you want to use this in the beginning
selector = "mail";
# The path location is searched for a DKIM key with these variables:
# - `$domain` is sourced from the MIME mail message `From` header
# - `$selector` is configured for `mail` (as a default fallback)
path = "/tmp/docker-mailserver/dkim/keys/$domain/$selector.private";
# domain specific configurations can be provided below:
domain { domain {
example.com { example.com {
path = "/tmp/docker-mailserver/rspamd/dkim/mail.private"; path = "/tmp/docker-mailserver/rspamd/dkim/mail.private";
@ -188,9 +209,7 @@ 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/supervisor/rspamd.log`. If there is a mismatch, a warning will be emitted to the Rspamd log `/var/log/mail/rspamd.log`.
[docs-rspamd-override-d]: ../security/rspamd.md#manually
### DNS Record { #dkim-dns } ### DNS Record { #dkim-dns }
@ -221,11 +240,13 @@ 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]. 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 `)`. 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`_).
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. 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 `)`.
Your DNS record file (eg: `mail.txt`) should look similar to this: 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 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; "
@ -243,7 +264,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 dkim-rsa._domainkey.example.com $ dig +short TXT mail._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"
``` ```
@ -251,7 +272,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/supervisor/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/mail/rspamd.log`.
## DMARC ## DMARC
@ -328,10 +349,9 @@ volumes:
[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-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-6376]: https://tools.ietf.org/html/rfc6376 [rfc-8301]: https://datatracker.ietf.org/doc/html/rfc8301#section-3.2
[github-issue-dkimlength]: https://github.com/docker-mailserver/docker-mailserver/issues/1854 [github-issue-dkimlength]: 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/

View File

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

View File

@ -14,6 +14,27 @@ This page contains valuable information when it comes to resolving issues you en
- Check that all published DMS ports are actually open and not blocked by your ISP / hosting provider. - 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. - 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
@ -25,26 +46,43 @@ 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_][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_).
3. **Understand the basics of mail servers**: Especially for beginners, make sure you read our [Introduction][docs-introduction] and [Usage][docs-usage] articles. 3. **Inspect the logs of the service that is failing**: We provide a dedicated paragraph on this topic [further down below](#logs).
4. **Search the whole FAQ**: Our [FAQ][docs-faq] contains answers for common problems. Make sure you go through the list. 4. **Understand the basics of mail servers**: Especially for beginners, make sure you read our [Introduction][docs-introduction] and [Usage][docs-usage] articles.
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. 5. **Search the whole FAQ**: Our [FAQ][docs-faq] contains answers for common problems. Make sure you go through the list.
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
To get a shell inside the container run: `docker exec -it <CONTAINER NAME> bash`. #### General
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. To get a shell inside the container run: `docker exec -it <CONTAINER NAME> bash`. To install additional software, run:
To install additional software:
1. `apt-get update` to update repository metadata. 1. `apt-get update` to update repository metadata.
2. `apt-get install <PACKAGE>` 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).
For example a text editor you can use in the terminal: `apt-get install nano` #### Logs
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 ## Compatibility
@ -67,10 +105,11 @@ This could be from outdated software, or running a system that isn't able to pro
### System ### 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. - **Kernel:** Some systems provide [kernels with modifications (_replacing defaults and backporting patches_)][network::kernels-modified] to support running legacy software or kernels, complicating compatibility. This can be commonly experienced with products like NAS.
- **CGroups v2:** Hosts running older kernels (prior to 5.2) and systemd (prior to v244) are not likely to leverage cgroup v2, or have not defaulted to the cgroup v2 `unified` hierarchy. Not meeting this baseline may influence the behaviour of your DMS container, even with the latest Docker Engine installed. - **CGroups v2:** Hosts running older kernels (prior to 5.2) and systemd (prior to v244) are not likely to leverage cgroup v2, or have not defaulted to the cgroup v2 `unified` hierarchy. Not meeting this baseline may influence the behavior of your DMS container, even with the latest Docker Engine installed.
- **Container runtime:** Docker and Podman for example have subtle differences. DMS docs are primarily focused on Docker, but we try to document known issues where relevant. - **Container runtime:** Docker and Podman for example have subtle differences. DMS docs are primarily focused on Docker, but we try to document known issues where relevant.
- **Rootless containers:** Introduces additional differences in behaviour or requirements: - **Rootless containers:** Introduces additional differences in behavior or requirements:
- cgroup v2 is required for supporting rootless containers. - cgroup v2 is required for supporting rootless containers.
- Differences such as for container networking which may further affect support for IPv6 and preserving the client IP (Remote address). Example with Docker rootless are [binding a port to a specific interface][docker-rootless-interface] and the choice of [port forwarding driver][docs-rootless-portdriver]. - Differences such as for container networking which may further affect support for IPv6 and preserving the client IP (Remote address). Example with Docker rootless are [binding a port to a specific interface][docker-rootless-interface] and the choice of [port forwarding driver][docs-rootless-portdriver].
@ -79,13 +118,20 @@ This could be from outdated software, or running a system that isn't able to pro
[network::kernels-modified]: https://github.com/docker-mailserver/docker-mailserver/pull/2662#issuecomment-1168435970 [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 [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-faq]: ../faq.md
[docs-environment-log-level]: ./environment.md#log_level [docs-environment-log-level]: ./environment.md#log_level
[docs-faq]: ../faq.md
[docs::faq-bare-domain]: ../faq.md#can-i-use-a-nakedbare-domain-ie-no-hostname
[docs-ipv6]: ./advanced/ipv6.md [docs-ipv6]: ./advanced/ipv6.md
[docs-introduction]: ../introduction.md [docs-introduction]: ../introduction.md
[docs-rootless-portdriver]: ./security/fail2ban.md#running-inside-a-rootless-container
[docs-usage]: ../usage.md [docs-usage]: ../usage.md
[gh-issues]: https://github.com/docker-mailserver/docker-mailserver/issues [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 [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-rootless-interface]: https://github.com/moby/moby/issues/45742
[docs-rootless-portdriver]: ./security/fail2ban.md#running-inside-a-rootless-container [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

@ -33,6 +33,18 @@ 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
Default: 5000
The User ID assigned to the static vmail user for `/var/mail` (_Mail storage managed by Dovecot_).
##### DMS_VMAIL_GID
Default: 5000
The Group ID assigned to the static vmail group for `/var/mail` (_Mail storage managed by Dovecot_).
##### ONE_DIR ##### ONE_DIR
- 0 => state in default directories. - 0 => state in default directories.
@ -42,14 +54,22 @@ The log-level will show everything in its class and above.
Configures the provisioning source of user accounts (including aliases) for user queries and authentication by services managed by DMS (_Postfix and Dovecot_). Configures the provisioning source of user accounts (including aliases) for user queries and authentication by services managed by DMS (_Postfix and Dovecot_).
User provisioning via OIDC is planned for the future, see [this tracking issue](https://github.com/docker-mailserver/docker-mailserver/issues/2713). !!! tip "OAuth2 Support"
Presently DMS supports OAuth2 only as an supplementary authentication method.
- A third-party service must provide a valid token for the user which Dovecot validates with the authentication service provider. To enable this feature reference the [OAuth2 configuration example guide][docs::auth::oauth2-config-guide].
- User accounts must be provisioned to receive mail via one of the supported `ACCOUNT_PROVISIONER` providers.
- User provisioning via OIDC is planned for the future, see [this tracking issue](https://github.com/docker-mailserver/docker-mailserver/issues/2713).
[docs::auth::oauth2-config-guide]: ./advanced/auth-oauth2.md
- **empty** => use FILE - **empty** => use FILE
- LDAP => use LDAP authentication - LDAP => use LDAP authentication
- OIDC => use OIDC authentication (**not yet implemented**) - OIDC => use OIDC authentication (**not yet implemented**)
- FILE => use local files (this is used as the default) - FILE => use local files (this is used as the default)
A second container for the ldap service is necessary (e.g. [docker-openldap](https://github.com/osixia/docker-openldap)) A second container for the ldap service is necessary (e.g. [`bitnami/openldap`](https://hub.docker.com/r/bitnami/openldap/)).
##### PERMIT_DOCKER ##### PERMIT_DOCKER
@ -96,6 +116,15 @@ 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.
@ -119,9 +148,14 @@ Enabled `policyd-spf` in Postfix's configuration. You will likely want to set th
##### ENABLE_POP3 ##### ENABLE_POP3
- **empty** => POP3 service disabled - **0** => 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
@ -211,9 +245,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). Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default). Size is in bytes.
- **empty** => 0 (no limit) - **empty** => 0 (no limit)
@ -224,9 +258,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!) Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!). Size is in bytes.
- **empty** => 10240000 (~10 MB) - **empty** => 10240000 (~10 MB)
@ -299,16 +333,32 @@ Note: More information at <https://dovecot.org/doc/dovecot-example.conf>
##### MOVE_SPAM_TO_JUNK ##### MOVE_SPAM_TO_JUNK
When enabled, e-mails marked with the
1. `X-Spam: Yes` header added by Rspamd
2. `X-Spam-Flag: YES` header added by SpamAssassin (requires [`SPAMASSASSIN_SPAM_TO_INBOX=1`](#spamassassin_spam_to_inbox))
will be automatically moved to the Junk folder (with the help of a Sieve script).
- 0 => Spam messages will be delivered in the mailbox. - 0 => Spam messages will be delivered in the mailbox.
- **1** => Spam messages will be delivered in the `Junk` folder. - **1** => Spam messages will be delivered in the `Junk` folder.
Routes mail identified as spam into the recipient(s) Junk folder (_via a Dovecot Sieve script_).
!!! info
Mail is received as spam when it has been marked with either header:
- `X-Spam: Yes` (_added by Rspamd_)
- `X-Spam-Flag: YES` (_added by SpamAssassin - requires [`SPAMASSASSIN_SPAM_TO_INBOX=1`](#spamassassin_spam_to_inbox)_)
##### MARK_SPAM_AS_READ
- **0** => disabled
- 1 => Spam messages will be marked as read
Enable to treat received spam as "read" (_avoids notification to MUA client of new mail_).
!!! info
Mail is received as spam when it has been marked with either header:
- `X-Spam: Yes` (_added by Rspamd_)
- `X-Spam-Flag: YES` (_added by SpamAssassin - requires [`SPAMASSASSIN_SPAM_TO_INBOX=1`](#spamassassin_spam_to_inbox)_)
#### Rspamd #### Rspamd
##### ENABLE_RSPAMD ##### ENABLE_RSPAMD
@ -338,6 +388,19 @@ 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.
@ -473,63 +536,170 @@ Changes the interval in which log files are rotated.
- **0** => SpamAssassin is disabled - **0** => SpamAssassin is disabled
- 1 => SpamAssassin is enabled - 1 => SpamAssassin is enabled
##### SPAMASSASSIN_SPAM_TO_INBOX ??? info "SpamAssassin analyzes incoming mail and assigns a spam score"
- 0 => Spam messages will be bounced (_rejected_) without any notification (_dangerous_). Integration with Amavis involves processing mail based on the assigned spam score via [`SA_TAG`, `SA_TAG2` and `SA_KILL`][amavis-docs::spam-score].
- **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)
- [`SA_SPAM_SUBJECT`](#sa_spam_subject)
##### SA_TAG ##### SA_TAG
- **2.0** => add spam info headers if at, or above that level - **2.0** => add 'spam info' headers at, or above this spam score
Note: this SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1` Mail is not yet considered spam at this spam score, but for purposes like diagnostics it can be useful to identify mail with a spam score at a lower bound than `SA_TAG2`.
??? example "`X-Spam` headers appended to mail"
Send a simple mail to a local DMS account `hello@example.com`:
```bash
docker exec dms swaks --server 0.0.0.0 --to hello@example.com --body 'spam'
```
Inspecting the raw mail you will notice several `X-Spam` headers were added to the mail like this:
```
X-Spam-Flag: NO
X-Spam-Score: 4.162
X-Spam-Level: ****
X-Spam-Status: No, score=4.162 tagged_above=2 required=4
tests=[BODY_SINGLE_WORD=1, DKIM_ADSP_NXDOMAIN=0.8,
NO_DNS_FOR_FROM=0.379, NO_RECEIVED=-0.001, NO_RELAYS=-0.001,
PYZOR_CHECK=1.985] autolearn=no autolearn_force=no
```
!!! info "The `X-Spam-Score` is `4.162`"
High enough for `SA_TAG` to trigger adding these headers, but not high enough for `SA_TAG2` (_which would set `X-Spam-Flag: YES` instead_).
##### SA_TAG2 ##### SA_TAG2
- **6.31** => add 'spam detected' headers at that level - **6.31** => add 'spam detected' headers at, or above this level
Note: this SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1` When a spam score is high enough, mark mail as spam (_Appends the mail header: `X-Spam-Flag: YES`_).
!!! info "Interaction with other ENV"
- [`SA_SPAM_SUBJECT`](#sa_spam_subject) modifies the mail subject to better communicate spam mail to the user.
- [`MOVE_SPAM_TO_JUNK=1`](#move_spam_to_junk): The mail is still delivered, but to the recipient(s) junk folder instead. This feature reduces the usefulness of `SA_SPAM_SUBJECT`.
##### SA_KILL ##### SA_KILL
- **10.0** => triggers spam evasive actions - **10.0** => quarantine + triggers action to handle spam
!!! note "This SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1`" Controls the spam score threshold for triggering an action on mail that has a high spam score.
By default, DMS is configured to quarantine spam emails. ??? tip "Choosing an appropriate `SA_KILL` value"
If emails are quarantined, they are compressed and stored in a location dependent on the `ONE_DIR` setting above. To inhibit this behaviour and deliver spam emails, set this to a very high value e.g. `100.0`. The value should be high enough to be represent confidence in mail as spam:
If `ONE_DIR=1` (default) the location is `/var/mail-state/lib-amavis/virusmails/`, or if `ONE_DIR=0`: `/var/lib/amavis/virusmails/`. These paths are inside the docker container. - Too low: The action taken may prevent legitimate mail (ham) that was incorrectly detected as spam from being delivered successfully.
- Too high: Allows more spam to bypass the `SA_KILL` trigger (_how to treat mail with high confidence that it is actually spam_).
Experiences from DMS users with these settings has been [collected here][gh-issue::sa-tunables-insights], along with [some direct configuration guides][gh-issue::sa-tunables-guides] (_under "Resources for references"_).
[gh-issue::sa-tunables-insights]: https://github.com/docker-mailserver/docker-mailserver/pull/3058#issuecomment-1420268148
[gh-issue::sa-tunables-guides]: https://github.com/docker-mailserver/docker-mailserver/pull/3058#issuecomment-1416547911
??? info "Trigger action"
DMS will configure Amavis with either of these actions based on the DMS [`SPAMASSASSIN_SPAM_TO_INBOX`](#spamassassin_spam_to_inbox) ENV setting:
- `D_PASS` (**default**):
- Accept mail and deliver it to the recipient(s), despite the high spam score. A copy is still stored in quarantine.
- This is a good default to start with until you are more confident in an `SA_KILL` threshold that won't accidentally discard / bounce legitimate mail users are expecting to arrive but is detected as spam.
- `D_BOUNCE`:
- Additionally sends a bounce notification (DSN).
- The [DSN is suppressed][amavis-docs::actions] (_no bounce sent_) when the spam score exceeds the Amavis `$sa_dsn_cutoff_level` config setting (default: `10`). With the DMS `SA_KILL` default also being `10`, no DSN will ever be sent.
- `D_REJECT` / `D_DISCARD`:
- These two aren't configured by DMS, but are valid alternative action values if configuring Amavis directly.
??? note "Quarantined mail"
When mail has a spam score that reaches the `SA_KILL` threshold:
- [It will be quarantined][amavis-docs::quarantine] regardless of the `SA_KILL` action to perform.
- With `D_PASS` the delivered mail also appends an `X-Quarantine-ID` mail header. The ID value of this header is part of the quarantined file name.
If emails are quarantined, they are compressed and stored at a location dependent on the [`ONE_DIR`](#one_dir) setting:
- `ONE_DIR=1` (default): `/var/mail-state/lib-amavis/virusmails/`
- `ONE_DIR=0`: `/var/lib/amavis/virusmails/`
!!! tip
Easily list mail stored in quarantine with `find` and the quarantine path:
```bash
find /var/lib/amavis/virusmails -type f
```
[amavis-docs::actions]: https://www.ijs.si/software/amavisd/amavisd-new-docs.html#actions
[amavis-docs::quarantine]: https://www.ijs.si/software/amavisd/amavisd-new-docs.html#quarantine
##### SA_SPAM_SUBJECT ##### SA_SPAM_SUBJECT
- **\*\*\*SPAM\*\*\*** => add tag to subject if spam detected Adds a prefix to the subject header when mail is marked as spam (_via [`SA_TAG2`](#sa_tag2)_).
Note: this SpamAssassin setting needs `ENABLE_SPAMASSASSIN=1`. Add the SpamAssassin score to the subject line by inserting the keyword \_SCORE\_: **\*\*\*SPAM(\_SCORE\_)\*\*\***. - **`'***SPAM*** '`** => A string value to use as a mail subject prefix.
- `undef` => Opt-out of modifying the subject for mail marked as spam.
??? example "Including trailing white-space"
Add trailing white-space by quote wrapping the value: `SA_SPAM_SUBJECT='[SPAM] '`
??? example "Including the associated spam score"
The [`_SCORE_` tag][sa-docs::score-tag] will be substituted with the SpamAssassin score: `SA_SPAM_SUBJECT=***SPAM(_SCORE_)***`.
[sa-docs::score-tag]: https://spamassassin.apache.org/full/4.0.x/doc/Mail_SpamAssassin_Conf.html#rewrite_header-subject-from-to-STRING
##### SA_SHORTCIRCUIT_BAYES_SPAM ##### 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`
Note: activate this only if you are confident in your bayes database for identifying spam. !!! warning
Activate this only if you are confident in your bayes database for identifying spam.
##### SA_SHORTCIRCUIT_BAYES_HAM ##### 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`
Note: activate this only if you are confident in your bayes database for identifying ham. !!! warning
Activate this only if you are confident in your bayes database for identifying ham.
#### Fetchmail #### Fetchmail
@ -544,8 +714,10 @@ Note: activate this only if you are confident in your bayes database for identif
##### 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 configurations defined. - 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_).
[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 #### Getmail
@ -561,12 +733,20 @@ Enable or disable `getmail`.
- **5** => `getmail` The number of minutes for the interval. Min: 1; Max: 30; Default: 5. - **5** => `getmail` The number of minutes for the interval. Min: 1; Max: 30; Default: 5.
#### OAUTH2
##### ENABLE_OAUTH2
- **empty** => OAUTH2 authentication is disabled
- 1 => OAUTH2 authentication is enabled
##### OAUTH2_INTROSPECTION_URL
- => Specify the user info endpoint URL of the oauth2 provider (_eg: `https://oauth2.example.com/userinfo/`_)
#### LDAP #### LDAP
##### ENABLE_LDAP
Deprecated. See [`ACCOUNT_PROVISIONER`](#account_provisioner).
##### LDAP_START_TLS ##### LDAP_START_TLS
- **empty** => no - **empty** => no
@ -575,8 +755,8 @@ Deprecated. See [`ACCOUNT_PROVISIONER`](#account_provisioner).
##### 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 listening, or an URI like `ldaps://mail.example.com` - => Specify the `<dns-name>` / `<ip-address>` where the LDAP server is reachable via a URI like: `ldaps://mail.example.com`.
- NOTE: If you going to use DMS in combination with `compose.yaml` you can set the service name here - Note: You must include the desired URI scheme (`ldap://`, `ldaps://`, `ldapi://`).
##### LDAP_SEARCH_BASE ##### LDAP_SEARCH_BASE
@ -650,9 +830,8 @@ The following variables overwrite the default values for ```/etc/dovecot/dovecot
##### 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: If the protocol is missing, `ldap://` will be used. - Note: You must include the desired URI scheme (`ldap://`, `ldaps://`, `ldapi://`).
- 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
@ -745,7 +924,7 @@ Note: This postgrey setting needs `ENABLE_POSTGREY=1`
##### SASLAUTHD_LDAP_SERVER ##### SASLAUTHD_LDAP_SERVER
- **empty** => same 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

View File

@ -8,10 +8,6 @@ Rspamd is a ["fast, free and open-source spam filtering system"][rspamd-homepage
If you want to have a look at the default configuration files for Rspamd that DMS packs, navigate to [`target/rspamd/` inside the repository][dms-default-configuration]. Please consult the [section "The Default Configuration"](#the-default-configuration) section down below for a written overview. If you want to 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.
!!! note "AMD64 vs ARM64"
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.
[rspamd-homepage]: https://rspamd.com/ [rspamd-homepage]: https://rspamd.com/
[dms-default-configuration]: https://github.com/docker-mailserver/docker-mailserver/tree/master/target/rspamd [dms-default-configuration]: https://github.com/docker-mailserver/docker-mailserver/tree/master/target/rspamd
@ -21,18 +17,64 @@ The following environment variables are related to Rspamd:
1. [`ENABLE_RSPAMD`](../environment.md#enable_rspamd) 1. [`ENABLE_RSPAMD`](../environment.md#enable_rspamd)
2. [`ENABLE_RSPAMD_REDIS`](../environment.md#enable_rspamd_redis) 2. [`ENABLE_RSPAMD_REDIS`](../environment.md#enable_rspamd_redis)
3. [`RSPAMD_GREYLISTING`](../environment.md#rspamd_greylisting) 3. [`RSPAMD_CHECK_AUTHENTICATED`](../environment.md#rspamd_check_authenticated)
4. [`RSPAMD_HFILTER`](../environment.md#rspamd_hfilter) 4. [`RSPAMD_GREYLISTING`](../environment.md#rspamd_greylisting)
5. [`RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE`](../environment.md#rspamd_hfilter_hostname_unknown_score) 5. [`RSPAMD_HFILTER`](../environment.md#rspamd_hfilter)
6. [`RSPAMD_LEARN`](../environment.md#rspamd_learn) 6. [`RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE`](../environment.md#rspamd_hfilter_hostname_unknown_score)
7. [`MOVE_SPAM_TO_JUNK`](../environment.md#move_spam_to_junk) 7. [`RSPAMD_LEARN`](../environment.md#rspamd_learn)
8. [`MOVE_SPAM_TO_JUNK`][docs-spam-to-junk]
9. [`MARK_SPAM_AS_READ`](../environment.md#mark_spam_as_read)
With these variables, you can enable Rspamd itself and you can enable / disable certain features related to Rspamd. With these variables, you can enable Rspamd itself, and you can enable / disable certain features related to Rspamd.
[docs-spam-to-junk]: ../environment.md#move_spam_to_junk
## The Default Configuration ## The Default Configuration
### Mode of Operation ### Mode of Operation
!!! tip "Attention"
Read this section carefully if you want to understand how Rspamd is integrated into DMS and how it works (on a surface level).
Rspamd is integrated as a milter into DMS. When enabled, Postfix's `main.cf` configuration file includes the parameter `rspamd_milter = inet:localhost:11332`, which is added to `smtpd_milters`. As a milter, Rspamd can inspect incoming and outgoing e-mails.
Each mail is assigned what Rspamd calls symbols: when an e-mail matches a specific criterion, the mail receives a symbol. Afterwards, Rspamd applies a _spam score_ (as usual with anti-spam software) to the e-mail.
- The score itself is calculated by adding the values of the individual symbols applied earlier. The higher the spam score is, the more likely the e-mail is spam.
- Symbol values can be negative (i.e., these symbols indicate the mail is legitimate, maybe because [SPF and DKIM][docs-dkim-dmarc-spf] are verified successfully) or the symbol can be positive (i.e., these symbols indicate the e-mail is spam, maybe because the e-mail contains a lot of links).
Rspamd then adds (a few) headers to the e-mail based on the spam score. Most important are `X-Spamd-Result`, which contains an overview of which symbols were applied. It could look like this:
```txt
X-Spamd-Result default: False [-2.80 / 11.00]; R_SPF_NA(1.50)[no SPF record]; R_DKIM_ALLOW(-1.00)[example.com:s=dtag1]; DWL_DNSWL_LOW(-1.00)[example.com:dkim]; RWL_AMI_LASTHOP(-1.00)[192.0.2.42:from]; DMARC_POLICY_ALLOW(-1.00)[example.com,none]; RWL_MAILSPIKE_EXCELLENT(-0.40)[192.0.2.42:from]; FORGED_SENDER(0.30)[noreply@example.com,some-reply-address@bounce.example.com]; RCVD_IN_DNSWL_LOW(-0.10)[192.0.2.42:from]; MIME_GOOD(-0.10)[multipart/mixed,multipart/related,multipart/alternative,text/plain]; MIME_TRACE(0.00)[0:+,1:+,2:+,3:+,4:~,5:~,6:~]; RCVD_COUNT_THREE(0.00)[3]; RCPT_COUNT_ONE(0.00)[1]; REPLYTO_DN_EQ_FROM_DN(0.00)[]; ARC_NA(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; RCVD_TLS_LAST(0.00)[]; DKIM_TRACE(0.00)[example.com:+]; HAS_ATTACHMENT(0.00)[]; TO_DN_NONE(0.00)[]; FROM_NEQ_ENVFROM(0.00)[noreply@example.com,some-reply-address@bounce.example.com]; FROM_HAS_DN(0.00)[]; REPLYTO_DOM_NEQ_FROM_DOM(0.00)[]; PREVIOUSLY_DELIVERED(0.00)[receiver@anotherexample.com]; ASN(0.00)[asn:3320, ipnet:192.0.2.0/24, country:DE]; MID_RHS_MATCH_FROM(0.00)[]; MISSING_XM_UA(0.00)[]; HAS_REPLYTO(0.00)[some-reply-address@dms-reply.example.com]
```
And then there is a corresponding `X-Rspamd-Action` header, which shows the overall result and the action that is taken. In our example, it would be:
```txt
X-Rspamd-Action no action
```
Since the score is `-2.80`, nothing will happen and the e-mail is not classified as spam. Our custom [`actions.conf`][rspamd-actions-config] defines what to do at certain scores:
1. At a score of 4, the e-mail is to be _greylisted_;
2. At a score of 6, the e-mail is _marked with a header_ (`X-Spam: Yes`);
3. At a score of 7, the e-mail will additionally have their _subject re-written_ (appending a prefix like `[SPAM]`);
4. At a score of 11, the e-mail is outright _rejected_.
---
There is more to spam analysis than meets the eye: we have not covered the [Bayes training and filters][rspamc-docs-bayes] here, nor have we talked about [Sieve rules for e-mails that are marked as spam][docs-spam-to-junk].
Even the calculation of the score with the individual symbols has been presented to you in a simplified manner. But with the knowledge from above, you're equipped to read on and use Rspamd confidently. Keep on reading to understand the integration even better - you will want to know about your anti-spam software, not only to keep the bad e-mail out, but also to make sure the good e-mail arrive properly!
[docs-dkim-dmarc-spf]: ../best-practices/dkim_dmarc_spf.md
[rspamd-actions-config]: https://github.com/docker-mailserver/docker-mailserver/blob/master/target/rspamd/local.d/actions.conf
[rspamc-docs-bayes]: https://rspamd.com/doc/configuration/statistic.html
### Workers
The proxy worker operates in [self-scan mode][rspamd-docs-proxy-self-scan-mode]. This simplifies the setup as we do not require a normal worker. You can easily change this though by [overriding the configuration by DMS](#providing-custom-settings-overriding-settings). 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.
@ -41,9 +83,9 @@ DMS does not set a default password for the controller worker. You may want to d
### Persistence with Redis ### Persistence with Redis
When Rspamd is enabled, we implicitly also start an instance of Redis in the container. Redis is configured to persist it's data via RDB snapshots to disk in the directory `/var/lib/redis` (_which is a symbolic link to `/var/mail-state/lib-redis/` when [`ONE_DIR=1`](../environment.md#one_dir) and a volume is mounted to `/var/mail-state/`_). With the volume mount the snapshot will restore the Redis data across container restarts, and provide a way to keep backup. When Rspamd is enabled, we implicitly also start an instance of Redis in the container. Redis is configured to persist its data via RDB snapshots to disk in the directory `/var/lib/redis` (_which is a symbolic link to `/var/mail-state/lib-redis/` when [`ONE_DIR=1`](../environment.md#one_dir) and a volume is mounted to `/var/mail-state/`_). With the volume mount the snapshot will restore the Redis data across container restarts, and provide a way to keep backup.
Redis uses `/etc/redis/redis.conf` for configuration. We adjust this file when enabling the internal Redis service. If you have an external instance of Redis to use, the internal Redis service can be opt-out via setting the ENV [`ENABLE_RSPAMD_REDIS=0`](../environment.md#enable_rspamd_redis) (_link also details required changes to the DMS rspamd config_). Redis uses `/etc/redis/redis.conf` for configuration. We adjust this file when enabling the internal Redis service. If you have an external instance of Redis to use, the internal Redis service can be opt-out via setting the ENV [`ENABLE_RSPAMD_REDIS=0`](../environment.md#enable_rspamd_redis) (_link also details required changes to the DMS Rspamd config_).
### Web Interface ### Web Interface
@ -67,6 +109,10 @@ DMS does not supply custom values for DNS servers to Rspamd. If you need to use
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].
@ -75,13 +121,13 @@ You can find a list of all Rspamd modules [on their website][rspamd-docs-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 (Realtime Blacklists) / DNSBLs (DNS-based Blacklists) #### RBLs (Real-time Blacklists) / DNSBLs (DNS-based Blacklists)
The [RBL module](https://rspamd.com/doc/modules/rbl.html) is enabled by default. As a consequence, Rspamd will perform DNS lookups to a variety of blacklists. Whether an RBL or a DNSBL is queried depends on where the domain name was obtained: RBL servers are queried with IP addresses extracted from message headers, DNSBL server are queried with domains and IP addresses extracted from the message body \[[source][rbl-vs-dnsbl]\]. 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]\].
@ -101,14 +147,17 @@ DMS brings sane default settings for Rspamd. They are located at `/etc/rspamd/lo
!!! question "What is [`docker-data/dms/config/`][docs-dms-config-volume]?" !!! question "What is [`docker-data/dms/config/`][docs-dms-config-volume]?"
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. This directory will not do a complete file override, but a [forced override of the specific settings in that file][rspamd-docs-override-dir]. If you want to overwrite the default settings and / or provide your own settings, you can place files at `docker-data/dms/config/rspamd/override.d/`. Files from this directory are copied to `/etc/rspamd/override.d/` during startup. These files [forcibly override][rspamd-docs-override-dir] Rspamd and DMS default settings.
!!! question "What is the [`local.d` directory and how does it compare to `override.d`][rspamd-docs-config-directories]?"
!!! warning "Clashing Overrides" !!! warning "Clashing Overrides"
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. Note that when also [using the `custom-commands.conf` file](#with-the-help-of-a-custom-file), files in `override.d` may be overwritten in case you adjust them manually and with the help of the file.
[rspamd-docs-override-dir]: https://www.rspamd.com/doc/faq.html#what-are-the-locald-and-overrided-directories [rspamd-docs-override-dir]: https://www.rspamd.com/doc/faq.html#what-are-the-locald-and-overrided-directories
[docs-dms-config-volume]: ../../faq.md#what-about-the-docker-datadmsconfig-directory [docs-dms-config-volume]: ../../faq.md#what-about-the-docker-datadmsconfig-directory
[rspamd-docs-config-directories]: https://rspamd.com/doc/faq.html#what-are-the-locald-and-overrided-directories
### With the Help of a Custom File ### With the Help of a Custom File
@ -125,7 +174,7 @@ where `COMMAND` can be:
3. `set-option-for-module`: sets the value for option `ARGUMENT2` to `ARGUMENT3` inside module `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 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 5. `set-option-for-proxy`: set the value of option `ARGUMENT1` to `ARGUMENT2` for the proxy worker
6. `set-common-option`: set the option `ARGUMENT1` that [defines basic Rspamd behaviour][rspamd-docs-basic-options] to value `ARGUMENT2` 6. `set-common-option`: set the option `ARGUMENT1` that [defines basic Rspamd behavior][rspamd-docs-basic-options] to value `ARGUMENT2`
7. `add-line`: this will add the complete line after `ARGUMENT1` (with all characters) to the file `/etc/rspamd/override.d/<ARGUMENT1>` 7. `add-line`: this will add the complete line after `ARGUMENT1` (with all characters) to the file `/etc/rspamd/override.d/<ARGUMENT1>`
!!! example "An Example Is [Shown Down Below](#adjusting-and-extending-the-very-basic-configuration)" !!! example "An Example Is [Shown Down Below](#adjusting-and-extending-the-very-basic-configuration)"

View File

@ -161,8 +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 `compose.yaml` should include the following: 5. Your `compose.yaml` should include the following:
```yaml ```yaml
@ -594,7 +595,7 @@ This setup only comes with one caveat: The domain has to be configured on anothe
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
@ -605,26 +606,26 @@ This setup only comes with one caveat: The domain has to be configured on anothe
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

View File

@ -78,6 +78,10 @@ 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:
@ -85,10 +89,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:
@ -96,16 +100,17 @@ 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_):
@ -113,13 +118,15 @@ 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]
... ...
``` ```
@ -127,7 +134,6 @@ 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

@ -0,0 +1,74 @@
---
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 -t <YOUR CUSTOM IMAGE NAME> . docker build --tag <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,19 +21,26 @@ git clone --recurse-submodules https://github.com/docker-mailserver/docker-mails
### About Docker ### About Docker
#### Version #### Minimum supported version
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. We make use of build features that require a recent version of Docker. v23.0 or newer is advised, but earlier releases may work.
#### Environment - 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/).
- 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).
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. #### Build Arguments (Optional)
#### Build Arguments The `Dockerfile` includes several build [`ARG`][docker-docs::builder-arg] instructions that can be configured:
The `Dockerfile` takes additional, so-called build arguments. These are - `DOVECOT_COMMUNITY_REPO`: Install Dovecot from the community repo instead of from Debian (default = 1)
- `DMS_RELEASE`: The image version (default = edge)
- `VCS_REVISION`: The git commit hash used for the build (default = unknown)
1. `VCS_VERSION`: the image version (default = edge) !!! note
2. `VCS_REVISION`: the image revision (default = unknown)
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. - `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].
- 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

@ -72,14 +72,15 @@ Feel free to add your configuration if you achieved the same goal using differen
- "traefik.tcp.services.smtp.loadbalancer.server.port=25" - "traefik.tcp.services.smtp.loadbalancer.server.port=25"
- "traefik.tcp.services.smtp.loadbalancer.proxyProtocol.version=1" - "traefik.tcp.services.smtp.loadbalancer.proxyProtocol.version=1"
- "traefik.tcp.routers.smtp-ssl.rule=HostSNI(`*`)" - "traefik.tcp.routers.smtp-ssl.rule=HostSNI(`*`)"
- "traefik.tcp.routers.smtp-ssl.tls=false"
- "traefik.tcp.routers.smtp-ssl.entrypoints=smtp-ssl" - "traefik.tcp.routers.smtp-ssl.entrypoints=smtp-ssl"
- "traefik.tcp.routers.smtp-ssl.tls.passthrough=true"
- "traefik.tcp.routers.smtp-ssl.service=smtp-ssl" - "traefik.tcp.routers.smtp-ssl.service=smtp-ssl"
- "traefik.tcp.services.smtp-ssl.loadbalancer.server.port=465" - "traefik.tcp.services.smtp-ssl.loadbalancer.server.port=465"
- "traefik.tcp.services.smtp-ssl.loadbalancer.proxyProtocol.version=1" - "traefik.tcp.services.smtp-ssl.loadbalancer.proxyProtocol.version=1"
- "traefik.tcp.routers.imap-ssl.rule=HostSNI(`*`)" - "traefik.tcp.routers.imap-ssl.rule=HostSNI(`*`)"
- "traefik.tcp.routers.imap-ssl.entrypoints=imap-ssl" - "traefik.tcp.routers.imap-ssl.entrypoints=imap-ssl"
- "traefik.tcp.routers.imap-ssl.service=imap-ssl" - "traefik.tcp.routers.imap-ssl.service=imap-ssl"
- "traefik.tcp.routers.imap-ssl.tls.passthrough=true"
- "traefik.tcp.services.imap-ssl.loadbalancer.server.port=10993" - "traefik.tcp.services.imap-ssl.loadbalancer.server.port=10993"
- "traefik.tcp.services.imap-ssl.loadbalancer.proxyProtocol.version=2" - "traefik.tcp.services.imap-ssl.loadbalancer.proxyProtocol.version=2"
- "traefik.tcp.routers.sieve.rule=HostSNI(`*`)" - "traefik.tcp.routers.sieve.rule=HostSNI(`*`)"

View File

@ -0,0 +1,162 @@
---
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 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/advanced/auth-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

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

@ -61,7 +61,7 @@ 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 behaviour. If any information is incorrect please let us know! :smile: It presently lacks references to confirm the behavior. If any information is incorrect please let us know! :smile:
[docs-config-overrides-dovecot]: ../../config/advanced/override-defaults/dovecot.md#override-configuration [docs-config-overrides-dovecot]: ../../config/advanced/override-defaults/dovecot.md#override-configuration

View File

@ -0,0 +1,232 @@
---
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) for more information. `INBOX` is setup by default with the special IMAP folders `Drafts`, `Sent`, `Junk` and `Trash`. You can learn how to modify or add your own folders (_including additional special folders like `Archive`_) by visiting our docs page [_Customizing IMAP Folders_](./examples/use-cases/imap-folders.md) for more information.
### How do I update DMS? ### How do I update DMS?
@ -128,7 +128,7 @@ find "${PWD}/docker-data/dms-backups/" -type f -mtime +30 -delete
### I Want to Know More About the Ports ### I Want to Know More About the Ports
See [this part of the documentation](../config/security/understanding-the-ports/) for further details and best practice advice, **especially regarding security concerns**. See [this part of the documentation](./config/security/understanding-the-ports.md) for further details and best practice advice, **especially regarding security concerns**.
### How can I configure my email client? ### How can I configure my email client?
@ -143,7 +143,7 @@ imap port: 143 or 993 with STARTTLS/SSL (recommended)
imap path prefix: INBOX imap path prefix: INBOX
# SMTP # SMTP
smtp port: 25 or 587/465 with STARTTLS/SSL (recommended) smtp port: 587 or 465 with STARTTLS/SSL (recommended)
username: <user1@example.com> username: <user1@example.com>
password: <mypassword> password: <mypassword>
``` ```
@ -210,7 +210,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/#ssl_type) for more details. Both RSA and ECDSA certs are supported. You can provide your own cert files manually, or mount a `letsencrypt` generated directory (_with alternative support for Traefik's `acme.json`_). Check out the [`SSL_TYPE` documentation](./config/environment.md#ssl_type) for more details.
### I just moved from my old mail server to DMS, but "it doesn't work"? ### I just moved from my old mail server to DMS, but "it doesn't work"?
@ -378,18 +378,7 @@ When you run DMS with the ENV variable `ONE_DIR=1` (default), this directory wil
#### How can I manage my custom SpamAssassin rules? #### How can I manage my custom SpamAssassin rules?
Antispam rules are managed in `docker-data/dms/config/spamassassin-rules.cf`. Anti-spam rules are managed in `docker-data/dms/config/spamassassin-rules.cf`.
#### What are acceptable `SA_SPAM_SUBJECT` values?
For no subject set `SA_SPAM_SUBJECT=undef`.
For a trailing white-space subject one can define the whole variable with quotes in `compose.yaml`:
```yaml
environment:
- "SA_SPAM_SUBJECT=[SPAM] "
```
#### 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?
@ -479,58 +468,39 @@ 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). Until you provided these 200+200 mails, SpamAssassin will not take the learned mails into account. For further reference, see the [SpamAssassin Wiki](https://wiki.apache.org/spamassassin/BayesNotWorking). With the default settings, SpamAssassin will require 200 mails trained for spam (for example with the method explained above) and 200 mails trained for ham (using the same command as above but using `--ham` and providing it with some ham mails).
- Until you provided these 200+200 mails, SpamAssassin will not take the learned mails into account.
- For further reference, see the [SpamAssassin Wiki](https://wiki.apache.org/spamassassin/BayesNotWorking).
#### How do I have more control about what SpamAssassin is filtering? #### How do I have more control about what SpamAssassin is filtering?
By default, SPAM and INFECTED emails are put to a quarantine which is not very straight forward to access. Several config settings are affecting this behavior: This is related to Amavis processing the mail after SpamAssassin has analyzed it and assigned a spam score.
First, make sure you have the proper thresholds set: - DMS provides some [common SA tunables via ENV][docs::env::sa_env].
- Additional configuration can be managed with the DMS config volume by providing `docker-data/dms/config/amavis.cf`.
```conf #### How can I send quarantined mail to a mailbox?
SA_TAG=-100000.0
SA_TAG2=3.75
SA_KILL=100000.0
```
- The very negative value in `SA_TAG` makes sure, that all emails have the SpamAssassin headers included. SPAM and INFECTED emails that [reach the `SA_KILL` threshold are archived into quarantine][docs::env::sa_kill].
- `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`)
Make sure everything (including SPAM) is delivered to the inbox and not quarantined: 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`:
```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 = "amavis\@example.com"; $clean_quarantine_to = "quarantine\@example.com";
$virus_quarantine_to = "amavis\@example.com"; $virus_quarantine_to = "quarantine\@example.com";
$banned_quarantine_to = "amavis\@example.com"; $banned_quarantine_to = "quarantine\@example.com";
$bad_header_quarantine_to = "amavis\@example.com"; $bad_header_quarantine_to = "quarantine\@example.com";
$spam_quarantine_to = "amavis\@example.com"; $spam_quarantine_to = "quarantine\@example.com";
``` ```
[fail2ban-customize]: ./config/security/fail2ban.md [fail2ban-customize]: ./config/security/fail2ban.md
[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-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-optional-configuration]: ./config/advanced/optional-config.md
[docs::env::sa_env]: ./config/environment.md#spamassassin
[docs::env::sa_kill]: ./config/environment.md#sa_kill
[github-comment-baredomain]: https://github.com/docker-mailserver/docker-mailserver/issues/3048#issuecomment-1432358353 [github-comment-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
@ -541,4 +511,3 @@ $spam_quarantine_to = "amavis\@example.com";
[github-issue-1792]: https://github.com/docker-mailserver/docker-mailserver/pull/1792 [github-issue-1792]: https://github.com/docker-mailserver/docker-mailserver/pull/1792
[hanscees-userpatches]: https://github.com/hanscees/dockerscripts/blob/master/scripts/tomav-user-patches.sh [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 [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, Antispam, Antivirus, etc.). It employs only configuration files, no SQL database. The image is focused around the slogan "Keep it simple and versioned". `docker-mailserver`, or DMS for short, is a production-ready fullstack but simple mail server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.). It employs only configuration files, no SQL database. The image is focused around the slogan "Keep it simple and versioned".
## Contents ## 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/ [docs-introduction]: ./introduction.md
[docs-usage]: ./usage/ [docs-usage]: ./usage.md
[docs-examples]: ./examples/tutorials/basic-installation/ [docs-examples]: ./examples/tutorials/basic-installation.md
[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/ [docs-tests]: ./contributing/tests.md
### 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/ [docs-contributing]: ./contributing/issues-and-pull-requests.md

View File

@ -43,10 +43,10 @@ Here's where DMS's toolchain 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

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, Antispam, Antivirus, etc.) using Docker.' site_description: 'A fullstack but simple mail-server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.) using Docker.'
site_author: 'docker-mailserver (Github Organization)' 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>'
@ -86,8 +86,8 @@ markdown_extensions:
- pymdownx.inlinehilite - pymdownx.inlinehilite
- pymdownx.tilde - pymdownx.tilde
- pymdownx.emoji: - pymdownx.emoji:
emoji_index: !!python/name:materialx.emoji.twemoji emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:materialx.emoji.to_svg emoji_generator: !!python/name:material.extensions.emoji.to_svg
- pymdownx.highlight: - pymdownx.highlight:
extend_pygments_lang: extend_pygments_lang:
- name: yml - name: yml
@ -122,8 +122,9 @@ nav:
- 'Environment Variables': config/environment.md - 'Environment Variables': config/environment.md
- 'User Management': config/user-management.md - 'User Management': config/user-management.md
- 'Best Practices': - 'Best Practices':
- 'DKIM, DMARC & SPF': config/best-practices/dkim_dmarc_spf.md
- 'Auto-discovery': config/best-practices/autodiscover.md - 'Auto-discovery': config/best-practices/autodiscover.md
- 'DKIM, DMARC & SPF': config/best-practices/dkim_dmarc_spf.md
- 'MTA-STS': config/best-practices/mta-sts.md
- 'Security': - '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
@ -142,6 +143,7 @@ nav:
- '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 - 'LDAP Authentication': config/advanced/auth-ldap.md
- 'OAuth2 Authentication': config/advanced/auth-oauth2.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 Gathering with Getmail': config/advanced/mail-getmail.md
@ -159,11 +161,14 @@ nav:
- '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
- '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
- 'FAQ' : faq.md - 'FAQ' : faq.md
- 'Contributing': - 'Contributing':
- 'General Information': contributing/general.md - 'General Information': contributing/general.md

View File

@ -34,6 +34,12 @@ SUPERVISOR_LOGLEVEL=
# 1 => consolidate all states into a single directory (`/var/mail-state`) to allow persistence using docker volumes # 1 => consolidate all states into a single directory (`/var/mail-state`) to allow persistence using docker volumes
ONE_DIR=1 ONE_DIR=1
# Support for deployment where these defaults are not compatible (eg: some NAS appliances):
# /var/mail vmail User ID (default: 5000)
DMS_VMAIL_UID=
# /var/mail vmail Group ID (default: 5000)
DMS_VMAIL_GID=
# **empty** => use FILE # **empty** => use FILE
# LDAP => use LDAP authentication # LDAP => use LDAP authentication
# OIDC => use OIDC authentication (not yet implemented) # OIDC => use OIDC authentication (not yet implemented)
@ -113,10 +119,16 @@ ENABLE_OPENDMARC=1
# - **1** => Enabled # - **1** => Enabled
ENABLE_POLICYD_SPF=1 ENABLE_POLICYD_SPF=1
# 1 => Enables POP3 service # Enables POP3 service
# empty => disables POP3 # - **0** => Disabled
# - 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
@ -142,6 +154,15 @@ 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.
@ -233,7 +254,7 @@ 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). # Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default). Size is in bytes.
# #
# empty => 0 # empty => 0
POSTFIX_MAILBOX_SIZE_LIMIT= POSTFIX_MAILBOX_SIZE_LIMIT=
@ -243,7 +264,7 @@ POSTFIX_MAILBOX_SIZE_LIMIT=
# 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!) # Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!). Size is in bytes.
# #
# empty => 10240000 (~10 MB) # empty => 10240000 (~10 MB)
POSTFIX_MESSAGE_SIZE_LIMIT= POSTFIX_MESSAGE_SIZE_LIMIT=
@ -333,6 +354,12 @@ 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/latest/config/advanced/mail-mta-sts/
# - **0** ==> MTA-STS disabled
# - 1 => MTA-STS enabled
ENABLE_MTA_STS=0
# Choose TCP/IP protocols for dovecot to use # 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.
@ -347,9 +374,6 @@ 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
@ -358,20 +382,29 @@ SPAMASSASSIN_SPAM_TO_INBOX=1
# 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 SA_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
# add spam info headers if at, or above that level: # spam messages wil be marked as read
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 that level # add 'spam detected' headers at, or above this 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 # add tag to subject if spam detected
SA_SPAM_SUBJECT=***SPAM***** # The value `undef` opts-out of this feature. The value shown below is the default.
# NOTE: By default spam is delivered to a junk folder, reducing the value of adding a subject prefix.
# NOTE: If not using Docker Compose, other CRI may require the single quotes removed.
#SA_SPAM_SUBJECT='***SPAM*** '
# ----------------------------------------------- # -----------------------------------------------
# --- Fetchmail Section ------------------------- # --- Fetchmail Section -------------------------
@ -381,6 +414,10 @@ 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`. # Enable or disable `getmail`.
# #
@ -391,24 +428,31 @@ ENABLE_GETMAIL=0
# The number of minutes for the interval. Min: 1; Max: 30. # The number of minutes for the interval. Min: 1; Max: 30.
GETMAIL_POLL=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://github.com/osixia/docker-openldap) # A second container for the ldap service is necessary (i.e. https://hub.docker.com/r/bitnami/openldap/)
# with the :edge tag, use ACCOUNT_PROVISIONER=LDAP
# empty => LDAP authentication is disabled
# 1 => LDAP authentication 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=
# If you going to use the mailserver in combination with Docker Compose you can set the service name here # empty => mail.example.com
# empty => mail.domain.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 # Note: You must include the desired URI scheme (`ldap://`, `ldaps://`, `ldapi://`).
LDAP_SERVER_HOST= LDAP_SERVER_HOST=
# empty => ou=people,dc=domain,dc=com # empty => ou=people,dc=domain,dc=com
@ -495,7 +539,7 @@ SASLAUTHD_MECHANISMS=
SASLAUTHD_MECH_OPTIONS= SASLAUTHD_MECH_OPTIONS=
# empty => Use value of LDAP_SERVER_HOST # empty => Use value of 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_SERVER= SASLAUTHD_LDAP_SERVER=
# empty => Use value of LDAP_BIND_DN # empty => Use value of LDAP_BIND_DN

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

@ -33,6 +33,9 @@ ${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

@ -4,11 +4,15 @@
source /usr/local/bin/helpers/index.sh source /usr/local/bin/helpers/index.sh
if [[ -f /etc/dms-settings ]] && [[ $(_get_dms_env_value 'ENABLE_RSPAMD') -eq 1 ]]; then if [[ -f /etc/dms-settings ]] && [[ $(_get_dms_env_value 'ENABLE_RSPAMD') -eq 1 ]]; then
/usr/local/bin/rspamd-dkim "${@}" if [[ $(_get_dms_env_value 'ENABLE_OPENDKIM') -eq 1 ]]; then
exit log 'error' "You enabled Rspamd and OpenDKIM - OpenDKIM will be implicitly used for DKIM keys"
else
/usr/local/bin/rspamd-dkim "${@}"
exit
fi
fi fi
KEYSIZE=4096 KEYSIZE=2048
SELECTOR=mail SELECTOR=mail
DOMAINS= DOMAINS=
@ -16,37 +20,40 @@ function __usage() {
printf '%s' "${PURPLE}OPEN-DKIM${RED}(${YELLOW}8${RED}) printf '%s' "${PURPLE}OPEN-DKIM${RED}(${YELLOW}8${RED})
${ORANGE}NAME${RESET} ${ORANGE}NAME${RESET}
open-dkim - configure DomainKeys Identified Mail (DKIM) open-dkim - Configure DKIM (DomainKeys Identified Mail)
${ORANGE}SYNOPSIS${RESET} ${ORANGE}SYNOPSIS${RESET}
./setup.sh config dkim [ OPTIONS${RED}...${RESET} ] setup config dkim [ OPTIONS${RED}...${RESET} ]
${ORANGE}DESCRIPTION${RESET} ${ORANGE}DESCRIPTION${RESET}
Configures DKIM keys. OPTIONS can be used to configure a more complex setup. Creates DKIM keys and configures them within DMS for OpenDKIM.
LDAP setups require these options. OPTIONS can be used when your requirements are not met by the defaults.
When not using 'ACCOUNT_PROVISIONER=FILE' (default), you may need to explicitly
use the 'domain' option to generate DKIM keys for your mail account domains.
${ORANGE}OPTIONS${RESET} ${ORANGE}OPTIONS${RESET}
${BLUE}Generic Program Information${RESET} ${BLUE}Generic Program Information${RESET}
help Print the usage information. help Print the usage information.
${BLUE}Configuration adjustments${RESET} ${BLUE}Configuration adjustments${RESET}
keysize Set the size of the keys to be generated. Possible are 1024, 2048 and 4096 (default). keysize Set the size of the keys to be generated.
selector Set a manual selector (default is 'mail') for the key. (${LCYAN}ATTENTION${RESET}: NOT IMPLEMENTED YET!) Possible values: 1024, 2048 and 4096
domain Provide the domain(s) for which keys are to be generated. Default: 2048
selector Set a manual selector for the key.
Default: mail
domain Provide the domain(s) for which to generate keys for.
Default: The FQDN assigned to DMS, excluding any subdomain.
'ACCOUNT_PROVISIONER=FILE' also sources domains from mail accounts.
${ORANGE}EXAMPLES${RESET} ${ORANGE}EXAMPLES${RESET}
${LWHITE}./setup.sh config dkim keysize 2048${RESET} ${LWHITE}setup config dkim keysize 4096${RESET}
Creates keys of length 2048 bit in a default setup where domains are obtained from Creates keys with their length increased to a size of 4096-bit.
your accounts.
${LWHITE}./setup.sh config dkim keysize 2048 selector 2021-dkim${RESET} ${LWHITE}setup config dkim keysize 1024 selector 2023-dkim${RESET}
Creates keys of length 2048 bit in a default setup where domains are obtained from Creates 1024-bit sized keys, and changes the DKIM selector to '2023-dkim'.
your accounts. The DKIM selector used is '2021-dkim'.
${LWHITE}./setup.sh config dkim keysize 2048 selector 2021-dkim domain 'whoami.com,whoareyou.org'${RESET} ${LWHITE}setup config dkim domain 'example.com,another-example.com'${RESET}
Appropriate for an LDAP setup. Creates keys of length 2048 bit in a default setup Only generates DKIM keys for the specified domains: 'example.com' and 'another-example.com'.
where domains are obtained from your accounts. The DKIM selector used is '2021-dkim'.
The domains for which DKIM keys are generated are 'whoami.com' and 'whoareyou.org'.
${ORANGE}EXIT STATUS${RESET} ${ORANGE}EXIT STATUS${RESET}
Exit status is 0 if command was successful. If wrong arguments are provided or arguments contain Exit status is 0 if command was successful. If wrong arguments are provided or arguments contain
@ -137,6 +144,9 @@ while read -r DKIM_DOMAIN; do
--directory="/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}" --directory="/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}"
fi fi
# fix permissions to use the same user:group as /tmp/docker-mailserver/opendkim/keys
chown -R "$(stat -c '%U:%G' /tmp/docker-mailserver/opendkim/keys)" "/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}"
# write to KeyTable if necessary # write to KeyTable if necessary
KEYTABLEENTRY="${SELECTOR}._domainkey.${DKIM_DOMAIN} ${DKIM_DOMAIN}:${SELECTOR}:/etc/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private" KEYTABLEENTRY="${SELECTOR}._domainkey.${DKIM_DOMAIN} ${DKIM_DOMAIN}:${SELECTOR}:/etc/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private"
if [[ ! -f "/tmp/docker-mailserver/opendkim/KeyTable" ]]; then if [[ ! -f "/tmp/docker-mailserver/opendkim/KeyTable" ]]; then

View File

@ -16,46 +16,44 @@ function __usage() {
echo -e "${PURPLE}RSPAMD-DKIM${RED}(${YELLOW}8${RED}) echo -e "${PURPLE}RSPAMD-DKIM${RED}(${YELLOW}8${RED})
${ORANGE}NAME${RESET} ${ORANGE}NAME${RESET}
rspamd-dkim - Configure DomainKeys Identified Mail (DKIM) via Rspamd rspamd-dkim - Configure DKIM (DomainKeys Identified Mail)
${ORANGE}SYNOPSIS${RESET} ${ORANGE}SYNOPSIS${RESET}
setup config dkim [ OPTIONS${RED}...${RESET} ] setup config dkim [ OPTIONS${RED}...${RESET} ]
${ORANGE}DESCRIPTION${RESET} ${ORANGE}DESCRIPTION${RESET}
This script aids in creating DKIM signing keys. The keys are created and managed by Rspamd. Creates DKIM keys and configures them within DMS for Rspamd.
OPTIONS can be used to configure a more complex setup. OPTIONS can be used when your requirements are not met by the defaults.
${ORANGE}OPTIONS${RESET} ${ORANGE}OPTIONS${RESET}
${BLUE}Generic Program Information${RESET} ${BLUE}Generic Program Information${RESET}
-v Enable verbose logging (setting the log level to 'debug'). -f | --force Overwrite existing files if there are any
-vv Enable very verbose logging (setting the log level to 'trace'). -v Enable verbose logging (setting the log level to 'debug').
help Print the usage information. -vv Enable very verbose logging (setting the log level to 'trace').
help Print the usage information.
${BLUE}Configuration adjustments${RESET} ${BLUE}Configuration adjustments${RESET}
keytype Set the type of key you want to use keytype Set the type of key you want to use.
Possible values: rsa, ed25519 Possible values: rsa, ed25519
Default: rsa Default: rsa
keysize Set the size of the keys to be generated keysize Set the size of the keys to be generated.
Possible values: 1024, 2048 and 4096 Possible values: 1024, 2048 and 4096
Default: 2048 Default: 2048
Only applies when using keytype=rsa Only applies when using keytype=rsa
selector Set a manual selector for the key selector Set a manual selector for the key.
Default: mail Default: mail
domain Provide the domain for which keys are to be generated domain Provide the domain for which to generate keys for.
Default: primary domain name of DMS Default: The FQDN assigned to DMS, excluding any subdomain.
${ORANGE}EXAMPLES${RESET} ${ORANGE}EXAMPLES${RESET}
${LWHITE}setup config dkim keysize 2048${RESET} ${LWHITE}setup config dkim keysize 4096${RESET}
Creates keys of length 2048 bit in a default setup where domains are obtained from Creates keys with their length increased to a size of 4096-bit.
your accounts.
${LWHITE}setup config dkim keysize 512 selector 2023-dkim${RESET} ${LWHITE}setup config dkim keysize 1024 selector 2023-dkim${RESET}
Creates keys of length 512 bit in a default setup where domains are obtained from Creates 1024-bit sized keys, and changes the DKIM selector to '2023-dkim'.
your accounts. The DKIM selector used is '2023-dkim'.
${LWHITE}setup config dkim keysize 1024 selector 2023-dkim domain whoami.com${RESET} ${LWHITE}setup config dkim domain example.com${RESET}
Creates keys of length 1024 bit in a default setup where domains are obtained from your accounts. Generate the DKIM key for a different domain (example.com).
The DKIM selector used is '2023-dkim'. The domain for which DKIM keys are generated is whoami.com.
${ORANGE}EXIT STATUS${RESET} ${ORANGE}EXIT STATUS${RESET}
Exit status is 0 if command was successful. If wrong arguments are provided or arguments contain Exit status is 0 if command was successful. If wrong arguments are provided or arguments contain
@ -64,14 +62,8 @@ ${ORANGE}EXIT STATUS${RESET}
" "
} }
function __do_as_rspamd_user() {
local COMMAND=${1:?Command required when using __do_as_rspamd_user}
_log 'trace' "Running '${*}' as user '_rspamd' now"
shift 1
su -l '_rspamd' -s "$(command -v "${COMMAND}")" -- "${@}"
}
function _parse_arguments() { function _parse_arguments() {
FORCE=0
KEYTYPE='rsa' KEYTYPE='rsa'
KEYSIZE='2048' KEYSIZE='2048'
SELECTOR='mail' SELECTOR='mail'
@ -115,6 +107,12 @@ function _parse_arguments() {
exit 0 exit 0
;; ;;
( '-f' | '--force' )
FORCE=1
shift 1
continue
;;
( '-vv' ) ( '-vv' )
# shellcheck disable=SC2034 # shellcheck disable=SC2034
LOG_LEVEL='trace' LOG_LEVEL='trace'
@ -135,30 +133,36 @@ function _parse_arguments() {
__usage __usage
_exit_with_error "Unknown option(s) '${1}' ${2:+"and '${2}'"}" _exit_with_error "Unknown option(s) '${1}' ${2:+"and '${2}'"}"
;; ;;
esac esac
shift 2 shift 2
done done
return 0
}
function _preflight_checks() {
if [[ ${KEYTYPE} == 'ed25519' ]] && [[ ${KEYSIZE} -ne 2048 ]]; then if [[ ${KEYTYPE} == 'ed25519' ]] && [[ ${KEYSIZE} -ne 2048 ]]; then
_exit_with_error "Chosen keytype does not accept the 'keysize' argument" _exit_with_error "Chosen keytype does not accept the 'keysize' argument"
fi fi
return 0 if [[ ! -d /tmp/docker-mailserver ]]; then
_log 'warn' "The directory '/tmp/docker-mailserver' does not seem to be mounted by a volume - the Rspamd (DKIM) configuration will not be persisted"
fi
_rspamd_get_envs
mkdir -p "${RSPAMD_DMS_DKIM_D}" "${RSPAMD_DMS_OVERRIDE_D}"
chown _rspamd:_rspamd "${RSPAMD_DMS_DKIM_D}"
} }
function _create_keys() { function _create_keys() {
# Note: Variables not marked with `local` are used
# in other functions (after this function was called).
BASE_DIR='/tmp/docker-mailserver/rspamd/dkim'
if [[ ${KEYTYPE} == 'rsa' ]]; then if [[ ${KEYTYPE} == 'rsa' ]]; then
local BASE_FILE_NAME="${BASE_DIR}/${KEYTYPE}-${KEYSIZE}-${SELECTOR}-${DOMAIN}" local BASE_FILE_NAME="${RSPAMD_DMS_DKIM_D}/${KEYTYPE}-${KEYSIZE}-${SELECTOR}-${DOMAIN}"
KEYTYPE_OPTIONS=('-b' "${KEYSIZE}") KEYTYPE_OPTIONS=('-b' "${KEYSIZE}")
_log 'info' "Creating DKIM keys of type '${KEYTYPE}' and length '${KEYSIZE}' with selector '${SELECTOR}' for domain '${DOMAIN}'" _log 'info' "Creating DKIM keys of type '${KEYTYPE}' and length '${KEYSIZE}' with selector '${SELECTOR}' for domain '${DOMAIN}'"
else else
local BASE_FILE_NAME="${BASE_DIR}/${KEYTYPE}-${SELECTOR}-${DOMAIN}" local BASE_FILE_NAME="${RSPAMD_DMS_DKIM_D}/${KEYTYPE}-${SELECTOR}-${DOMAIN}"
KEYTYPE_OPTIONS=('-t' "${KEYTYPE}") KEYTYPE_OPTIONS=('-t' "${KEYTYPE}")
_log 'info' "Creating DKIM keys of type '${KEYTYPE}' with selector '${SELECTOR}' for domain '${DOMAIN}'" _log 'info' "Creating DKIM keys of type '${KEYTYPE}' with selector '${SELECTOR}' for domain '${DOMAIN}'"
fi fi
@ -167,8 +171,15 @@ function _create_keys() {
PUBLIC_KEY_DNS_FILE="${BASE_FILE_NAME}.public.dns.txt" PUBLIC_KEY_DNS_FILE="${BASE_FILE_NAME}.public.dns.txt"
PRIVATE_KEY_FILE="${BASE_FILE_NAME}.private.txt" PRIVATE_KEY_FILE="${BASE_FILE_NAME}.private.txt"
mkdir -p "${BASE_DIR}" if [[ -f ${PUBLIC_KEY_FILE} ]] || [[ -f ${PUBLIC_KEY_DNS_FILE} ]] || [[ -f ${PRIVATE_KEY_FILE} ]]; then
chown _rspamd:_rspamd "${BASE_DIR}" if [[ ${FORCE} -eq 0 ]]; then
_log 'error' "Not overwriting existing files (use '--force' to overwrite existing files)"
exit 1
else
_log 'info' "Overwriting existing files as the '--force' option was supplied"
rm "${PUBLIC_KEY_FILE}" "${PUBLIC_KEY_DNS_FILE}" "${PRIVATE_KEY_FILE}"
fi
fi
# shellcheck disable=SC2310 # shellcheck disable=SC2310
if __do_as_rspamd_user rspamadm \ if __do_as_rspamd_user rspamadm \
@ -189,8 +200,8 @@ function _create_keys() {
function _check_permissions() { function _check_permissions() {
# shellcheck disable=SC2310 # shellcheck disable=SC2310
if ! __do_as_rspamd_user ls "${BASE_DIR}" >/dev/null; then if ! __do_as_rspamd_user ls "${RSPAMD_DMS_DKIM_D}" >/dev/null; then
_log 'warn' "The Rspamd user ('_rspamd') seems to be unable to list files in the keys directory ('${BASE_DIR}') - Rspamd may experience permission errors later" _log 'warn' "The Rspamd user ('_rspamd') seems to be unable to list files in the keys directory ('${RSPAMD_DMS_DKIM_D}') - Rspamd may experience permission errors later"
elif ! __do_as_rspamd_user cat "${PRIVATE_KEY_FILE}" >/dev/null; then elif ! __do_as_rspamd_user cat "${PRIVATE_KEY_FILE}" >/dev/null; then
_log 'warn' "The Rspamd user ('_rspamd') seems to be unable to read the private key file - Rspamd may experience permission errors later" _log 'warn' "The Rspamd user ('_rspamd') seems to be unable to read the private key file - Rspamd may experience permission errors later"
else else
@ -199,11 +210,11 @@ function _check_permissions() {
} }
function _setup_default_signing_conf() { function _setup_default_signing_conf() {
local DEFAULT_CONFIG_FILE='/etc/rspamd/override.d/dkim_signing.conf' local DEFAULT_CONFIG_FILE="${RSPAMD_DMS_OVERRIDE_D}/dkim_signing.conf"
if [[ -f ${DEFAULT_CONFIG_FILE} ]]; then if [[ -f ${DEFAULT_CONFIG_FILE} ]]; then
_log 'debug' "'${DEFAULT_CONFIG_FILE}' exists, not supplying a default" _log 'info' "'${DEFAULT_CONFIG_FILE}' exists, not supplying a default ('--force' does not overwrite this file, manual adjustment required)"
else else
_log 'info' "Supplying a default configuration ('${DEFAULT_CONFIG_FILE}')" _log 'info' "Supplying a default configuration (to '${DEFAULT_CONFIG_FILE}')"
cat >"${DEFAULT_CONFIG_FILE}" << EOF cat >"${DEFAULT_CONFIG_FILE}" << EOF
# documentation: https://rspamd.com/doc/modules/dkim_signing.html # documentation: https://rspamd.com/doc/modules/dkim_signing.html
@ -228,7 +239,15 @@ domain {
} }
EOF EOF
chown _rspamd:_rspamd "${DEFAULT_CONFIG_FILE}"
# We copy here immediately in order to not rely on the changedetector - this way, users
# can immediately use the new keys. The file should not already exist in ${RSPAMD_OVERRIDE_D}
# since it would have been copied already.
cp "${DEFAULT_CONFIG_FILE}" "${RSPAMD_OVERRIDE_D}/dkim_signing.conf"
chown _rspamd:_rspamd "${DEFAULT_CONFIG_FILE}" "${RSPAMD_OVERRIDE_D}/dkim_signing.conf"
_log 'debug' 'Restarting Rspamd as initial DKIM configuration was suppplied'
supervisorctl restart rspamd
fi fi
} }
@ -257,6 +276,7 @@ function _final_steps() {
_obtain_hostname_and_domainname _obtain_hostname_and_domainname
_require_n_parameters_or_print_usage 0 "${@}" _require_n_parameters_or_print_usage 0 "${@}"
_parse_arguments "${@}" _parse_arguments "${@}"
_preflight_checks
_create_keys _create_keys
_check_permissions _check_permissions
_setup_default_signing_conf _setup_default_signing_conf

View File

@ -59,10 +59,14 @@ function _quota_request_if_missing() {
fi fi
} }
# Dovecot docs incorrectly refer to these units with names for SI types (base 10),
# But then mentions they're actually treated as IEC type (base 2):
# https://doc.dovecot.org/settings/types/#size
function _quota_unit_is_valid() { function _quota_unit_is_valid() {
if ! grep -qE "^([0-9]+(B|k|M|G|T)|0)\$" <<< "${QUOTA}"; then if ! grep -qE "^([0-9]+(B|k|M|G|T)|0)\$" <<< "${QUOTA}"; then
__usage __usage
_exit_with_error 'Invalid quota format. e.g. 302M (B (byte), k (kilobyte), M (megabyte), G (gigabyte) or T (terabyte))' _exit_with_error 'Invalid quota format. e.g. 302M (B (byte), k (kibibyte), M (mebibyte), G (gibibyte) or T (tebibyte))'
fi fi
} }

View File

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

View File

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

View File

@ -1,8 +1,8 @@
base = ou=people,dc=domain,dc=com base = ou=people,dc=example,dc=com
default_pass_scheme = SSHA default_pass_scheme = SSHA
dn = cn=admin,dc=domain,dc=com dn = cn=admin,dc=example,dc=com
dnpass = admin dnpass = admin
uris = ldap://mail.domain.com uris = ldap://mail.example.com
tls = no tls = no
ldap_version = 3 ldap_version = 3
pass_attrs = uniqueIdentifier=user,userPassword=password pass_attrs = uniqueIdentifier=user,userPassword=password

View File

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

View File

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

View File

@ -1,9 +1,9 @@
bind = yes bind = yes
bind_dn = cn=admin,dc=domain,dc=com bind_dn = cn=admin,dc=example,dc=com
bind_pw = admin bind_pw = admin
query_filter = (&(mailAlias=%s)(mailEnabled=TRUE)) query_filter = (&(mailAlias=%s)(mailEnabled=TRUE))
result_attribute = mail result_attribute = mail
search_base = ou=people,dc=domain,dc=com search_base = ou=people,dc=example,dc=com
server_host = mail.domain.com server_host = mail.example.com
start_tls = no start_tls = no
version = 3 version = 3

View File

@ -1,9 +1,9 @@
bind = yes bind = yes
bind_dn = cn=admin,dc=domain,dc=com bind_dn = cn=admin,dc=example,dc=com
bind_pw = admin bind_pw = admin
query_filter = (&(|(mail=*@%s)(mailalias=*@%s))(mailEnabled=TRUE)) query_filter = (&(|(mail=*@%s)(mailalias=*@%s))(mailEnabled=TRUE))
result_attribute = mail result_attribute = mail
search_base = ou=people,dc=domain,dc=com search_base = ou=people,dc=example,dc=com
server_host = mail.domain.com server_host = mail.example.com
start_tls = no start_tls = no
version = 3 version = 3

View File

@ -1,9 +1,9 @@
bind = yes bind = yes
bind_dn = cn=admin,dc=domain,dc=com bind_dn = cn=admin,dc=example,dc=com
bind_pw = admin bind_pw = admin
query_filter = (&(mailGroupMember=%s)(mailEnabled=TRUE)) query_filter = (&(mailGroupMember=%s)(mailEnabled=TRUE))
result_attribute = mail result_attribute = mail
search_base = ou=people,dc=domain,dc=com search_base = ou=people,dc=example,dc=com
server_host = mail.domain.com server_host = mail.example.com
start_tls = no start_tls = no
version = 3 version = 3

View File

@ -1,9 +1,9 @@
bind = yes bind = yes
bind_dn = cn=admin,dc=domain,dc=com bind_dn = cn=admin,dc=example,dc=com
bind_pw = admin bind_pw = admin
query_filter = (mail=%s) query_filter = (mail=%s)
result_attribute = mail, uid result_attribute = mail, uid
search_base = ou=people,dc=domain,dc=com search_base = ou=people,dc=example,dc=com
server_host = mail.domain.com server_host = mail.example.com
start_tls = no start_tls = no
version = 3 version = 3

View File

@ -1,9 +1,9 @@
bind = yes bind = yes
bind_dn = cn=admin,dc=domain,dc=com bind_dn = cn=admin,dc=example,dc=com
bind_pw = admin bind_pw = admin
query_filter = (&(mail=%s)(mailEnabled=TRUE)) query_filter = (&(mail=%s)(mailEnabled=TRUE))
result_attribute = mail result_attribute = mail
search_base = ou=people,dc=domain,dc=com search_base = ou=people,dc=example,dc=com
server_host = mail.domain.com server_host = mail.example.com
start_tls = no start_tls = no
version = 3 version = 3

View File

@ -5,6 +5,9 @@ biff = no
append_dot_mydomain = no append_dot_mydomain = no
readme_directory = no readme_directory = no
# Disabled as not compatible with Dovecot
smtputf8_enable = no
# Basic configuration # Basic configuration
# myhostname = # myhostname =
alias_maps = hash:/etc/aliases alias_maps = hash:/etc/aliases
@ -51,11 +54,19 @@ smtpd_helo_required = yes
smtpd_delay_reject = yes smtpd_delay_reject = yes
smtpd_helo_restrictions = permit_mynetworks, reject_invalid_helo_hostname, permit smtpd_helo_restrictions = permit_mynetworks, reject_invalid_helo_hostname, permit
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, reject_unauth_pipelining, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_unknown_recipient_domain smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_unknown_recipient_domain
smtpd_client_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, reject_unauth_pipelining smtpd_client_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
smtpd_sender_restrictions = $dms_smtpd_sender_restrictions smtpd_sender_restrictions = $dms_smtpd_sender_restrictions
smtpd_discard_ehlo_keywords = silent-discard, dsn
smtpd_data_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_pipelining
disable_vrfy_command = yes disable_vrfy_command = yes
# Security - Prevent SMTP Smuggling attack
# https://www.postfix.org/smtp-smuggling.html#long
smtpd_forbid_bare_newline = yes
# It is possible to exclude clients on trusted networks from this restriction (the upstream default is `$mynetwork`):
# smtpd_forbid_bare_newline_exclusions = $mynetworks
# Custom defined parameters for DMS: # Custom defined parameters for DMS:
dms_smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unknown_sender_domain dms_smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unknown_sender_domain
# Submission ports 587 and 465 support for SPOOF_PROTECTION=1 # Submission ports 587 and 465 support for SPOOF_PROTECTION=1
@ -87,10 +98,10 @@ smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $mydomain smtpd_sasl_local_domain = $mydomain
broken_sasl_auth_clients = yes broken_sasl_auth_clients = yes
# Mail directory # Postfix lookup tables for verifying valid users and managed mail domains:
virtual_transport = lmtp:unix:/var/run/dovecot/lmtp # Populated during startup in: scripts/helpers/postfix.sh
virtual_mailbox_domains = /etc/postfix/vhost virtual_mailbox_domains = /etc/postfix/vhost
virtual_mailbox_maps = texthash:/etc/postfix/vmailbox # Populated during startup in: scripts/helpers/aliases.sh
virtual_alias_maps = texthash:/etc/postfix/virtual virtual_alias_maps = texthash:/etc/postfix/virtual
# Milters used by DKIM # Milters used by DKIM

View File

@ -24,6 +24,7 @@ submission inet n - n - - smtpd
-o smtpd_client_restrictions=permit_sasl_authenticated,reject -o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_sender_restrictions=$mua_sender_restrictions -o smtpd_sender_restrictions=$mua_sender_restrictions
-o smtpd_discard_ehlo_keywords=
-o milter_macro_daemon_name=ORIGINATING -o milter_macro_daemon_name=ORIGINATING
-o cleanup_service_name=sender-cleanup -o cleanup_service_name=sender-cleanup
@ -37,6 +38,7 @@ submissions inet n - n - - smtpd
-o smtpd_client_restrictions=permit_sasl_authenticated,reject -o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_sender_restrictions=$mua_sender_restrictions -o smtpd_sender_restrictions=$mua_sender_restrictions
-o smtpd_discard_ehlo_keywords=
-o milter_macro_daemon_name=ORIGINATING -o milter_macro_daemon_name=ORIGINATING
-o cleanup_service_name=sender-cleanup -o cleanup_service_name=sender-cleanup

View File

@ -1,154 +0,0 @@
#! /bin/sh
# postgrey start/stop the postgrey greylisting deamon for postfix
# (priority should be smaller than that of postfix)
#
# Author: (c)2004-2006 Adrian von Bidder <avbidder@fortytwo.ch>
# Based on Debian sarge's 'skeleton' example
# Distribute and/or modify at will.
#
# Version: $Id: postgrey.init 1436 2006-12-07 07:15:03Z avbidder $
# altered by Georg Lauterbach as aendeavor 2020-11.05 14:02:00Z
### BEGIN INIT INFO
# Provides: postgrey
# Required-Start: $syslog $local_fs $remote_fs
# Required-Stop: $syslog $local_fs $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start/stop the postgrey daemon
### END INIT INFO
set -e
PATH='/sbin:/bin:/usr/sbin:/usr/bin'
DAEMON='/usr/sbin/postgrey'
DAEMON_NAME='postgrey'
DESC='postfix greylisting daemon'
DAEMON_USER='postgrey'
PIDFILE="/var/run/${DAEMON_NAME}/${DAEMON_NAME}.pid"
SCRIPTNAME="/etc/init.d/${DAEMON_NAME}"
# gracefully exit if the package has been removed.
[ -x "${DAEMON}" ] || exit 0
# shellcheck source=/dev/null
. /lib/lsb/init-functions
# Read config file if it is present.
# shellcheck source=/dev/null
[ -r "/etc/default/${DAEMON_NAME}" ] && . "/etc/default/${DAEMON_NAME}"
POSTGREY_OPTS="--pidfile=${PIDFILE} --daemonize ${POSTGREY_OPTS}"
if [ -z "${POSTGREY_TEXT}" ]; then
POSTGREY_TEXT_OPT=""
else
POSTGREY_TEXT_OPT="--greylist-text=${POSTGREY_TEXT}"
fi
ret=0
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile \
"${PIDFILE}" --exec "${DAEMON}" --test >/dev/null || return 1
start-stop-daemon --start --quiet --pidfile \
"${PIDFILE}" --exec "${DAEMON}" -- "${POSTGREY_OPTS}" \
"${POSTGREY_TEXT_OPT}" || return 2
}
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --user "${DAEMON_USER}" --stop --quiet \
--retry=TERM/30/KILL/5 --pidfile "${PIDFILE}"
RETVAL="$?"
[ "${RETVAL}" -eq 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --user "${DAEMON_USER}" --stop --quiet \
--oknodo --retry=0/30/KILL/5 --exec "${DAEMON}"
[ "$?" -eq 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f "${PIDFILE}"
return "${RETVAL}"
}
do_reload()
{
#
# If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
start-stop-daemon --stop --signal 1 --quiet --pidfile "${PIDFILE}"
return 0
}
case "${1}" in
start )
[ "${VERBOSE}" != no ] && log_daemon_msg "Starting ${DESC}" "${DAEMON_NAME}"
do_start
case "${?}" in
0|1) [ "${VERBOSE}" != no ] && log_end_msg 0 ;;
2) [ "${VERBOSE}" != no ] && log_end_msg 1 ;;
esac
;;
stop )
[ "${VERBOSE}" != no ] && log_daemon_msg "Stopping ${DESC}" "${DAEMON_NAME}"
do_stop
case "${?}" in
0|1) [ "${VERBOSE}" != no ] && log_end_msg 0 ;;
2) [ "${VERBOSE}" != no ] && log_end_msg 1 ;;
esac
;;
reload|force-reload)
[ "${VERBOSE}" != no ] && log_daemon_msg "Reloading ${DESC}" "${DAEMON_NAME}"
do_reload
case "${?}" in
0|1) [ "${VERBOSE}" != no ] && log_end_msg 0 ;;
2) [ "${VERBOSE}" != no ] && log_end_msg 1 ;;
esac
;;
restart )
do_stop
do_start
;;
status )
status_of_proc -p "${PIDFILE}" "${DAEMON}" "${DAEMON_NAME}" 2>/dev/null
ret=${?}
;;
* )
echo "Usage: ${SCRIPTNAME} {start|stop|restart|reload|force-reload|status}" >&2
exit 1
;;
esac
exit ${ret}

View File

@ -1,9 +1,12 @@
# documentation: https://rspamd.com/doc/configuration/metrics.html#actions # documentation: https://rspamd.com/doc/configuration/metrics.html#actions
# and https://rspamd.com/doc/configuration/metrics.html # and https://rspamd.com/doc/configuration/metrics.html
#greylist = 4; # These values work in conjunction with the symbol scores in
#add_header = 6; # `scores.d/*.conf`. When adjusting them, make sure to understand
#rewrite_subject = 7; # and to be able to explain the impact on the whole system.
#reject = 15; greylist = 4;
add_header = 6;
rewrite_subject = 7;
reject = 11;
subject = "***SPAM*** %s" subject = "***SPAM*** %s"

View File

@ -0,0 +1,12 @@
# documentation: https://rspamd.com/doc/configuration/settings.html
# DMS::SED_TAG::1::START
# Disable all checks for authenticated users
authenticated {
priority = high;
authenticated = yes;
apply {
groups_enabled = [dkim];
}
}
# DMS::SED_TAG::1::END

View File

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

View File

@ -43,10 +43,6 @@ function _install_postfix() {
function _install_packages() { function _install_packages() {
_log 'debug' 'Installing all packages now' _log 'debug' 'Installing all packages now'
declare -a ANTI_VIRUS_SPAM_PACKAGES
declare -a CODECS_PACKAGES MISCELLANEOUS_PACKAGES
declare -a POSTFIX_PACKAGES MAIL_PROGRAMS_PACKAGES
ANTI_VIRUS_SPAM_PACKAGES=( ANTI_VIRUS_SPAM_PACKAGES=(
amavisd-new clamav clamav-daemon amavisd-new clamav clamav-daemon
pyzor razor spamassassin pyzor razor spamassassin
@ -62,18 +58,17 @@ function _install_packages() {
) )
MISCELLANEOUS_PACKAGES=( MISCELLANEOUS_PACKAGES=(
apt-transport-https bind9-dnsutils binutils bsd-mailx apt-transport-https binutils bsd-mailx
ca-certificates curl dbconfig-no-thanks ca-certificates curl dbconfig-no-thanks
dumb-init ed gnupg iproute2 iputils-ping dumb-init gnupg iproute2 libdate-manip-perl
libdate-manip-perl libldap-common libldap-common libmail-spf-perl
libmail-spf-perl libnet-dns-perl libnet-dns-perl locales logwatch
locales logwatch netcat-openbsd netcat-openbsd nftables rsyslog
nftables rsyslog supervisor supervisor uuid whois
uuid whois
) )
POSTFIX_PACKAGES=( POSTFIX_PACKAGES=(
pflogsumm postgrey postfix-ldap pflogsumm postgrey postfix-ldap postfix-mta-sts-resolver
postfix-pcre postfix-policyd-spf-python postsrsd postfix-pcre postfix-policyd-spf-python postsrsd
) )
@ -82,24 +77,37 @@ function _install_packages() {
opendmarc libsasl2-modules sasl2-bin opendmarc libsasl2-modules sasl2-bin
) )
# `bind9-dnsutils` provides the `dig` command
# `iputils-ping` provides the `ping` command
DEBUG_PACKAGES=(
bind9-dnsutils iputils-ping less nano
)
apt-get "${QUIET}" --no-install-recommends install \ apt-get "${QUIET}" --no-install-recommends install \
"${ANTI_VIRUS_SPAM_PACKAGES[@]}" \ "${ANTI_VIRUS_SPAM_PACKAGES[@]}" \
"${CODECS_PACKAGES[@]}" \ "${CODECS_PACKAGES[@]}" \
"${MISCELLANEOUS_PACKAGES[@]}" \ "${MISCELLANEOUS_PACKAGES[@]}" \
"${POSTFIX_PACKAGES[@]}" \ "${POSTFIX_PACKAGES[@]}" \
"${MAIL_PROGRAMS_PACKAGES[@]}" "${MAIL_PROGRAMS_PACKAGES[@]}" \
"${DEBUG_PACKAGES[@]}"
} }
function _install_dovecot() { function _install_dovecot() {
declare -a DOVECOT_PACKAGES declare -a DOVECOT_PACKAGES
# Dovecot packages for officially supported features.
DOVECOT_PACKAGES=( DOVECOT_PACKAGES=(
dovecot-core dovecot-imapd dovecot-core dovecot-imapd
dovecot-ldap dovecot-lmtpd dovecot-managesieved dovecot-ldap dovecot-lmtpd dovecot-managesieved
dovecot-pop3d dovecot-sieve dovecot-solr dovecot-pop3d dovecot-sieve dovecot-solr
) )
if [[ ${DOVECOT_COMMUNITY_REPO} -eq 1 ]]; then # Dovecot packages for community supported features.
DOVECOT_PACKAGES+=(dovecot-auth-lua)
# Dovecot's deb community repository only provides x86_64 packages, so do not include it
# when building for another architecture.
if [[ ${DOVECOT_COMMUNITY_REPO} -eq 1 ]] && [[ "$(uname --machine)" == "x86_64" ]]; then
_log 'trace' 'Using Dovecot community repository' _log 'trace' 'Using Dovecot community repository'
curl https://repo.dovecot.org/DOVECOT-REPO-GPG | gpg --import curl https://repo.dovecot.org/DOVECOT-REPO-GPG | gpg --import
gpg --export ED409DA1 > /etc/apt/trusted.gpg.d/dovecot.gpg gpg --export ED409DA1 > /etc/apt/trusted.gpg.d/dovecot.gpg
@ -107,6 +115,9 @@ function _install_dovecot() {
_log 'trace' 'Updating Dovecot package signatures' _log 'trace' 'Updating Dovecot package signatures'
apt-get "${QUIET}" update apt-get "${QUIET}" update
# Additional community package needed for Lua support if the Dovecot community repository is used.
DOVECOT_PACKAGES+=(dovecot-lua)
fi fi
_log 'debug' 'Installing Dovecot' _log 'debug' 'Installing Dovecot'
@ -119,29 +130,14 @@ function _install_dovecot() {
function _install_rspamd() { function _install_rspamd() {
_log 'trace' 'Adding Rspamd package signatures' _log 'trace' 'Adding Rspamd package signatures'
local DEB_FILE='/etc/apt/sources.list.d/rspamd.list' local DEB_FILE='/etc/apt/sources.list.d/rspamd.list'
local RSPAMD_PACKAGE_NAME
# We try getting the most recent version of Rspamd for aarch64 (from an official source, which curl -sSfL https://rspamd.com/apt-stable/gpg.key | gpg --dearmor >/etc/apt/trusted.gpg.d/rspamd.gpg
# is the backports repository). The version for aarch64 is 3.2; the most recent version for amd64 local URL='[signed-by=/etc/apt/trusted.gpg.d/rspamd.gpg] http://rspamd.com/apt-stable/ bullseye main'
# that we get with the official PPA is 3.4. echo "deb ${URL}" >"${DEB_FILE}"
#
# Not removing it later is fine as you have to explicitly opt into installing a backports package
# which is not something you could be doing by accident.
if [[ $(uname --machine) == 'aarch64' ]]; then
echo '# Official Rspamd PPA does not support aarch64, so we use the Bullseye backports' >"${DEB_FILE}"
echo 'deb [arch=arm64] http://deb.debian.org/debian bullseye-backports main' >>"${DEB_FILE}"
RSPAMD_PACKAGE_NAME='rspamd/bullseye-backports'
else
curl -sSfL https://rspamd.com/apt-stable/gpg.key | gpg --dearmor >/etc/apt/trusted.gpg.d/rspamd.gpg
local URL='[arch=amd64 signed-by=/etc/apt/trusted.gpg.d/rspamd.gpg] http://rspamd.com/apt-stable/ bullseye main'
echo "deb ${URL}" >"${DEB_FILE}"
echo "deb-src ${URL}" >>"${DEB_FILE}"
RSPAMD_PACKAGE_NAME='rspamd'
fi
_log 'debug' 'Installing Rspamd' _log 'debug' 'Installing Rspamd'
apt-get "${QUIET}" update apt-get "${QUIET}" update
apt-get "${QUIET}" --no-install-recommends install "${RSPAMD_PACKAGE_NAME}" 'redis-server' apt-get "${QUIET}" --no-install-recommends install 'rspamd' 'redis-server'
} }
function _install_fail2ban() { function _install_fail2ban() {
@ -194,6 +190,19 @@ function _install_getmail() {
apt-get "${QUIET}" autoremove apt-get "${QUIET}" autoremove
} }
function _install_utils() {
_log 'debug' 'Installing utils sourced from Github'
_log 'trace' 'Installing jaq'
curl -sL "https://github.com/01mf02/jaq/releases/latest/download/jaq-v1.2.0-$(uname -m)-unknown-linux-gnu" -o /usr/bin/jaq && chmod +x /usr/bin/jaq
_log 'trace' 'Installing swaks'
local SWAKS_VERSION='20240103.0'
local SWAKS_RELEASE="swaks-${SWAKS_VERSION}"
curl -sSfL "https://github.com/jetmore/swaks/releases/download/v${SWAKS_VERSION}/${SWAKS_RELEASE}.tar.gz" | tar -xz
mv "${SWAKS_RELEASE}/swaks" /usr/local/bin
rm -r "${SWAKS_RELEASE}"
}
function _remove_data_after_package_installations() { function _remove_data_after_package_installations() {
_log 'debug' 'Deleting sensitive files (secrets)' _log 'debug' 'Deleting sensitive files (secrets)'
rm /etc/postsrsd.secret rm /etc/postsrsd.secret
@ -217,5 +226,6 @@ _install_dovecot
_install_rspamd _install_rspamd
_install_fail2ban _install_fail2ban
_install_getmail _install_getmail
_install_utils
_remove_data_after_package_installations _remove_data_after_package_installations
_post_installation_steps _post_installation_steps

View File

@ -21,6 +21,10 @@ source /etc/dms-settings
# usage with DMS_HOSTNAME, which should remove the need to call this: # usage with DMS_HOSTNAME, which should remove the need to call this:
_obtain_hostname_and_domainname _obtain_hostname_and_domainname
# This is a helper to properly set all Rspamd-related environment variables
# correctly and in one place.
_rspamd_get_envs
# verify checksum file exists; must be prepared by start-mailserver.sh # verify checksum file exists; must be prepared by start-mailserver.sh
if [[ ! -f ${CHKSUM_FILE} ]]; then if [[ ! -f ${CHKSUM_FILE} ]]; then
_exit_with_error "'${CHKSUM_FILE}' is missing" 0 _exit_with_error "'${CHKSUM_FILE}' is missing" 0
@ -49,6 +53,7 @@ function _check_for_changes() {
# Handle any changes # Handle any changes
_ssl_changes _ssl_changes
_postfix_dovecot_changes _postfix_dovecot_changes
_rspamd_changes
_log_with_date 'debug' 'Reloading services due to detected changes' _log_with_date 'debug' 'Reloading services due to detected changes'
@ -174,6 +179,33 @@ function _ssl_changes() {
# They presently have no special handling other than to trigger a change that will restart Postfix/Dovecot. # They presently have no special handling other than to trigger a change that will restart Postfix/Dovecot.
} }
function _rspamd_changes() {
# RSPAMD_DMS_D='/tmp/docker-mailserver/rspamd'
if [[ ${CHANGED} =~ ${RSPAMD_DMS_D}/.* ]]; then
# "${RSPAMD_DMS_D}/override.d"
if [[ ${CHANGED} =~ ${RSPAMD_DMS_OVERRIDE_D}/.* ]]; then
_log_with_date 'trace' 'Rspamd - Copying configuration overrides'
rm "${RSPAMD_OVERRIDE_D}"/*
cp "${RSPAMD_DMS_OVERRIDE_D}"/* "${RSPAMD_OVERRIDE_D}"
fi
# "${RSPAMD_DMS_D}/custom-commands.conf"
if [[ ${CHANGED} =~ ${RSPAMD_DMS_CUSTOM_COMMANDS_F} ]]; then
_log_with_date 'trace' 'Rspamd - Generating new configuration from custom commands'
_rspamd_handle_user_modules_adjustments
fi
# "${RSPAMD_DMS_D}/dkim"
if [[ ${CHANGED} =~ ${RSPAMD_DMS_DKIM_D} ]]; then
_log_with_date 'trace' 'Rspamd - DKIM files updated'
fi
_log_with_date 'debug' 'Rspamd configuration has changed - restarting service'
supervisorctl restart rspamd
fi
}
while true; do while true; do
_check_for_changes _check_for_changes
sleep 2 sleep 2

View File

@ -70,7 +70,7 @@ function _create_accounts() {
# Dovecot's userdb has the following format # Dovecot's userdb has the following format
# user:password:uid:gid:(gecos):home:(shell):extra_fields # user:password:uid:gid:(gecos):home:(shell):extra_fields
DOVECOT_USERDB_LINE="${LOGIN}:${PASS}:5000:5000::/var/mail/${DOMAIN}/${USER}/home::${USER_ATTRIBUTES}" DOVECOT_USERDB_LINE="${LOGIN}:${PASS}:${DMS_VMAIL_UID}:${DMS_VMAIL_GID}::/var/mail/${DOMAIN}/${USER}/home::${USER_ATTRIBUTES}"
if grep -qF "${DOVECOT_USERDB_LINE}" "${DOVECOT_USERDB_FILE}"; then if grep -qF "${DOVECOT_USERDB_LINE}" "${DOVECOT_USERDB_FILE}"; then
_log 'warn' "Login '${LOGIN}' will not be added to '${DOVECOT_USERDB_FILE}' twice" _log 'warn' "Login '${LOGIN}' will not be added to '${DOVECOT_USERDB_FILE}' twice"
else else
@ -141,7 +141,7 @@ function _create_dovecot_alias_dummy_accounts() {
fi fi
fi fi
DOVECOT_USERDB_LINE="${ALIAS}:${REAL_ACC[1]}:5000:5000::/var/mail/${REAL_DOMAINNAME}/${REAL_USERNAME}::${REAL_ACC[2]:-}" DOVECOT_USERDB_LINE="${ALIAS}:${REAL_ACC[1]}:${DMS_VMAIL_UID}:${DMS_VMAIL_GID}::/var/mail/${REAL_DOMAINNAME}/${REAL_USERNAME}::${REAL_ACC[2]:-}"
if grep -qi "^${ALIAS}:" "${DOVECOT_USERDB_FILE}"; then if grep -qi "^${ALIAS}:" "${DOVECOT_USERDB_FILE}"; then
_log 'warn' "Alias '${ALIAS}' will not be added to '${DOVECOT_USERDB_FILE}' twice" _log 'warn' "Alias '${ALIAS}' will not be added to '${DOVECOT_USERDB_FILE}' twice"
else else

View File

@ -23,6 +23,7 @@ function _handle_postfix_virtual_config() {
fi fi
} }
# TODO: Investigate why this file is always created, nothing seems to append only the cp below?
function _handle_postfix_regexp_config() { function _handle_postfix_regexp_config() {
: >/etc/postfix/regexp : >/etc/postfix/regexp
@ -30,12 +31,7 @@ function _handle_postfix_regexp_config() {
_log 'trace' "Adding regexp alias file postfix-regexp.cf" _log 'trace' "Adding regexp alias file postfix-regexp.cf"
cp -f /tmp/docker-mailserver/postfix-regexp.cf /etc/postfix/regexp cp -f /tmp/docker-mailserver/postfix-regexp.cf /etc/postfix/regexp
_add_to_or_update_postfix_main 'virtual_alias_maps' 'pcre:/etc/postfix/regexp'
if ! grep 'virtual_alias_maps.*pcre:/etc/postfix/regexp' /etc/postfix/main.cf; then
sed -i -E \
's|virtual_alias_maps(.*)|virtual_alias_maps\1 pcre:/etc/postfix/regexp|g' \
/etc/postfix/main.cf
fi
fi fi
} }

View File

@ -40,6 +40,12 @@ function _monitored_files_checksums() {
"${DMS_DIR}/dovecot-quotas.cf" "${DMS_DIR}/dovecot-quotas.cf"
"${DMS_DIR}/dovecot-masters.cf" "${DMS_DIR}/dovecot-masters.cf"
) )
# Check whether Rspamd is used and if so, monitor it's changes as well
if [[ ${ENABLE_RSPAMD} -eq 1 ]] && [[ -d ${RSPAMD_DMS_D} ]]; then
readarray -d '' STAGING_FILES_RSPAMD < <(find "${RSPAMD_DMS_D}" -type f -name "*.sh" -print0)
STAGING_FILES+=("${STAGING_FILES_RSPAMD[@]}")
fi
fi fi
# SSL certs: # SSL certs:

View File

@ -16,6 +16,7 @@ function _import_scripts() {
source "${PATH_TO_SCRIPTS}/network.sh" source "${PATH_TO_SCRIPTS}/network.sh"
source "${PATH_TO_SCRIPTS}/postfix.sh" source "${PATH_TO_SCRIPTS}/postfix.sh"
source "${PATH_TO_SCRIPTS}/relay.sh" source "${PATH_TO_SCRIPTS}/relay.sh"
source "${PATH_TO_SCRIPTS}/rspamd.sh"
source "${PATH_TO_SCRIPTS}/ssl.sh" source "${PATH_TO_SCRIPTS}/ssl.sh"
source "${PATH_TO_SCRIPTS}/utils.sh" source "${PATH_TO_SCRIPTS}/utils.sh"

View File

@ -91,3 +91,44 @@ function _vhost_ldap_support() {
# #
# /etc/aliases is handled by `alias.sh` and uses `postalias` to update the Postfix alias database. No need for `postmap`. # /etc/aliases is handled by `alias.sh` and uses `postalias` to update the Postfix alias database. No need for `postmap`.
# http://www.postfix.org/postalias.1.html # http://www.postfix.org/postalias.1.html
# Add a key with a value to Postfix's main configuration file
# or update an existing key. An already existing key can be updated
# by either appending to the existing value (default) or by prepending.
#
# @param ${1} = key name in Postfix's main configuration file
# @param ${2} = new value (appended or prepended)
# @param ${3} = action "append" (default) or "prepend" [OPTIONAL]
function _add_to_or_update_postfix_main() {
local KEY=${1:?Key name is required}
local NEW_VALUE=${2:?New value is required}
local ACTION=${3:-append}
local CURRENT_VALUE
# Get current value from /etc/postfix/main.cf
_adjust_mtime_for_postfix_maincf
CURRENT_VALUE=$(postconf -h "${KEY}" 2>/dev/null)
# If key does not exist or value is empty, add it - otherwise update with ACTION:
if [[ -z ${CURRENT_VALUE} ]]; then
postconf "${KEY} = ${NEW_VALUE}"
else
# If $NEW_VALUE is already present --> nothing to do, skip.
if [[ " ${CURRENT_VALUE} " == *" ${NEW_VALUE} "* ]]; then
return 0
fi
case "${ACTION}" in
('append')
postconf "${KEY} = ${CURRENT_VALUE} ${NEW_VALUE}"
;;
('prepend')
postconf "${KEY} = ${NEW_VALUE} ${CURRENT_VALUE}"
;;
(*)
_log 'error' "Action '${3}' in _add_to_or_update_postfix_main is unknown"
return 1
;;
esac
fi
}

View File

@ -173,7 +173,7 @@ function _setup_relayhost() {
_log 'debug' 'Setting up Postfix Relay Hosts' _log 'debug' 'Setting up Postfix Relay Hosts'
if [[ -n ${DEFAULT_RELAY_HOST} ]]; then if [[ -n ${DEFAULT_RELAY_HOST} ]]; then
_log 'trace' "Setting default relay host ${DEFAULT_RELAY_HOST} to /etc/postfix/main.cf" _log 'trace' "Setting default relay host ${DEFAULT_RELAY_HOST}"
postconf "relayhost = ${DEFAULT_RELAY_HOST}" postconf "relayhost = ${DEFAULT_RELAY_HOST}"
fi fi

View File

@ -0,0 +1,117 @@
#! /bin/bash
# shellcheck disable=SC2034 # VAR appears unused.
# Perform a specific command as the Rspamd user (`_rspamd`). This is useful
# in case you want to have correct permissions on newly created files or if
# you want to check whether Rspamd can perform a specific action.
function __do_as_rspamd_user() {
_log 'trace' "Running '${*}' as user '_rspamd'"
su _rspamd -s /bin/bash -c "${*}"
}
# Calling this function brings common Rspamd-related environment variables
# into the current context. The environment variables are `readonly`, i.e.
# they cannot be modified. Use this function when you require common directory
# names, file names, etc.
function _rspamd_get_envs() {
readonly RSPAMD_LOCAL_D='/etc/rspamd/local.d'
readonly RSPAMD_OVERRIDE_D='/etc/rspamd/override.d'
readonly RSPAMD_DMS_D='/tmp/docker-mailserver/rspamd'
readonly RSPAMD_DMS_DKIM_D="${RSPAMD_DMS_D}/dkim"
readonly RSPAMD_DMS_OVERRIDE_D="${RSPAMD_DMS_D}/override.d"
readonly RSPAMD_DMS_CUSTOM_COMMANDS_F="${RSPAMD_DMS_D}/custom-commands.conf"
}
# Parses `RSPAMD_DMS_CUSTOM_COMMANDS_F` and executed the directives given by the file.
# To get a detailed explanation of the commands and how the file works, visit
# https://docker-mailserver.github.io/docker-mailserver/latest/config/security/rspamd/#with-the-help-of-a-custom-file
function _rspamd_handle_user_modules_adjustments() {
# Adds an option with a corresponding value to a module, or, in case the option
# is already present, overwrites it.
#
# @param ${1} = file name in ${RSPAMD_OVERRIDE_D}/
# @param ${2} = module name as it should appear in the log
# @param ${3} = option name in the module
# @param ${4} = value of the option
#
# ## Note
#
# While this function is currently bound to the scope of `_rspamd_handle_user_modules_adjustments`,
# it is written in a versatile way (taking 4 arguments instead of assuming `ARGUMENT2` / `ARGUMENT3`
# are set) so that it may be used elsewhere if needed.
function __add_or_replace() {
local MODULE_FILE=${1:?Module file name must be provided}
local MODULE_LOG_NAME=${2:?Module log name must be provided}
local OPTION=${3:?Option name must be provided}
local VALUE=${4:?Value belonging to an option must be provided}
# remove possible whitespace at the end (e.g., in case ${ARGUMENT3} is empty)
VALUE=${VALUE% }
local FILE="${RSPAMD_OVERRIDE_D}/${MODULE_FILE}"
readonly MODULE_FILE MODULE_LOG_NAME OPTION VALUE FILE
[[ -f ${FILE} ]] || touch "${FILE}"
if grep -q -E "${OPTION}.*=.*" "${FILE}"; then
__rspamd__log 'trace' "Overwriting option '${OPTION}' with value '${VALUE}' for ${MODULE_LOG_NAME}"
sed -i -E "s|([[:space:]]*${OPTION}).*|\1 = ${VALUE};|g" "${FILE}"
else
__rspamd__log 'trace' "Setting option '${OPTION}' for ${MODULE_LOG_NAME} to '${VALUE}'"
echo "${OPTION} = ${VALUE};" >>"${FILE}"
fi
}
# We check for usage of the previous location of the commands file.
# TODO This can be removed after the release of v14.0.0.
local RSPAMD_DMS_CUSTOM_COMMANDS_F_OLD="${RSPAMD_DMS_D}-modules.conf"
readonly RSPAMD_DMS_CUSTOM_COMMANDS_F_OLD
if [[ -f ${RSPAMD_DMS_CUSTOM_COMMANDS_F_OLD} ]]; then
_dms_panic__general "Old custom command file location '${RSPAMD_DMS_CUSTOM_COMMANDS_F_OLD}' is deprecated (use '${RSPAMD_DMS_CUSTOM_COMMANDS_F}' now)" 'Rspamd setup'
fi
if [[ -f "${RSPAMD_DMS_CUSTOM_COMMANDS_F}" ]]; then
__rspamd__log 'debug' "Found file '${RSPAMD_DMS_CUSTOM_COMMANDS_F}' - parsing and applying it"
local COMMAND ARGUMENT1 ARGUMENT2 ARGUMENT3
while read -r COMMAND ARGUMENT1 ARGUMENT2 ARGUMENT3; do
case "${COMMAND}" in
('disable-module')
__rspamd__helper__enable_disable_module "${ARGUMENT1}" 'false' 'override'
;;
('enable-module')
__rspamd__helper__enable_disable_module "${ARGUMENT1}" 'true' 'override'
;;
('set-option-for-module')
__add_or_replace "${ARGUMENT1}.conf" "module '${ARGUMENT1}'" "${ARGUMENT2}" "${ARGUMENT3}"
;;
('set-option-for-controller')
__add_or_replace 'worker-controller.inc' 'controller worker' "${ARGUMENT1}" "${ARGUMENT2} ${ARGUMENT3}"
;;
('set-option-for-proxy')
__add_or_replace 'worker-proxy.inc' 'proxy worker' "${ARGUMENT1}" "${ARGUMENT2} ${ARGUMENT3}"
;;
('set-common-option')
__add_or_replace 'options.inc' 'common options' "${ARGUMENT1}" "${ARGUMENT2} ${ARGUMENT3}"
;;
('add-line')
__rspamd__log 'trace' "Adding complete line to '${ARGUMENT1}'"
echo "${ARGUMENT2}${ARGUMENT3+ ${ARGUMENT3}}" >>"${RSPAMD_OVERRIDE_D}/${ARGUMENT1}"
;;
(*)
__rspamd__log 'warn' "Command '${COMMAND}' is invalid"
continue
;;
esac
done < <(_get_valid_lines_from_file "${RSPAMD_DMS_CUSTOM_COMMANDS_F}")
fi
}

View File

@ -4,6 +4,16 @@ function _escape() {
echo "${1//./\\.}" echo "${1//./\\.}"
} }
# TODO: Not in use currently. Maybe in the future: https://github.com/docker-mailserver/docker-mailserver/pull/3484/files#r1299410851
# Replaces a string so that it can be used inside
# `sed` safely.
#
# @param ${1} = string to escape
# @output = prints the escaped string
function _escape_for_sed() {
sed -E 's/[]\/$*.^[]/\\&/g' <<< "${1:?String to escape for sed is required}"
}
# Returns input after filtering out lines that are: # Returns input after filtering out lines that are:
# empty, white-space, comments (`#` as the first non-whitespace character) # empty, white-space, comments (`#` as the first non-whitespace character)
function _get_valid_lines_from_file() { function _get_valid_lines_from_file() {
@ -29,9 +39,9 @@ function _get_dms_env_value() {
# /var/mail folders (used during startup and change detection handling). # /var/mail folders (used during startup and change detection handling).
function _chown_var_mail_if_necessary() { function _chown_var_mail_if_necessary() {
# fix permissions, but skip this if 3 levels deep the user id is already set # fix permissions, but skip this if 3 levels deep the user id is already set
if find /var/mail -maxdepth 3 -a \( \! -user 5000 -o \! -group 5000 \) | read -r; then if find /var/mail -maxdepth 3 -a \( \! -user "${DMS_VMAIL_UID}" -o \! -group "${DMS_VMAIL_GID}" \) | read -r; then
_log 'trace' 'Fixing /var/mail permissions' _log 'trace' 'Fixing /var/mail permissions'
chown -R 5000:5000 /var/mail || return 1 chown -R "${DMS_VMAIL_UID}:${DMS_VMAIL_GID}" /var/mail || return 1
fi fi
} }
@ -117,9 +127,17 @@ function _replace_by_env_in_file() {
function _env_var_expect_zero_or_one() { function _env_var_expect_zero_or_one() {
local ENV_VAR_NAME=${1:?ENV var name must be provided to _env_var_expect_zero_or_one} local ENV_VAR_NAME=${1:?ENV var name must be provided to _env_var_expect_zero_or_one}
[[ ${!ENV_VAR_NAME} =~ ^(0|1)$ ]] && return 0 if [[ ! -v ${ENV_VAR_NAME} ]]; then
_log 'warn' "The value of '${ENV_VAR_NAME}' is not zero or one ('${!ENV_VAR_NAME}'), but was expected to be" _log 'warn' "'${ENV_VAR_NAME}' is not set, but was expected to be"
return 1 return 1
fi
if [[ ! ${!ENV_VAR_NAME} =~ ^(0|1)$ ]]; then
_log 'warn' "The value of '${ENV_VAR_NAME}' (= '${!ENV_VAR_NAME}') is not 0 or 1, but was expected to be"
return 1
fi
return 0
} }
# Check if an environment variable's value is an integer. # Check if an environment variable's value is an integer.

View File

@ -39,6 +39,7 @@ function _register_functions() {
# ? >> Setup # ? >> Setup
_register_setup_function '_setup_vmail_id'
_register_setup_function '_setup_logs_general' _register_setup_function '_setup_logs_general'
_register_setup_function '_setup_timezone' _register_setup_function '_setup_timezone'
@ -48,6 +49,7 @@ function _register_functions() {
_register_setup_function '_setup_dovecot_dhparam' _register_setup_function '_setup_dovecot_dhparam'
_register_setup_function '_setup_dovecot_quota' _register_setup_function '_setup_dovecot_quota'
_register_setup_function '_setup_spam_to_junk' _register_setup_function '_setup_spam_to_junk'
_register_setup_function '_setup_spam_mark_as_read'
fi fi
case "${ACCOUNT_PROVISIONER}" in case "${ACCOUNT_PROVISIONER}" in
@ -69,6 +71,11 @@ function _register_functions() {
;; ;;
esac esac
if [[ ${ENABLE_OAUTH2} -eq 1 ]]; then
_environment_variables_oauth2
_register_setup_function '_setup_oauth2'
fi
if [[ ${ENABLE_SASLAUTHD} -eq 1 ]]; then if [[ ${ENABLE_SASLAUTHD} -eq 1 ]]; then
_environment_variables_saslauthd _environment_variables_saslauthd
_register_setup_function '_setup_saslauthd' _register_setup_function '_setup_saslauthd'
@ -89,20 +96,22 @@ function _register_functions() {
_register_setup_function '_setup_dovecot_hostname' _register_setup_function '_setup_dovecot_hostname'
_register_setup_function '_setup_postfix_early' _register_setup_function '_setup_postfix_early'
_register_setup_function '_setup_fetchmail'
_register_setup_function '_setup_fetchmail_parallel'
# needs to come after _setup_postfix_early # Dependent upon _setup_postfix_early first calling _create_aliases
# Due to conditional check for /etc/postfix/regexp
_register_setup_function '_setup_spoof_protection' _register_setup_function '_setup_spoof_protection'
_register_setup_function '_setup_getmail' _register_setup_function '_setup_postfix_late'
if [[ ${ENABLE_SRS} -eq 1 ]]; then if [[ ${ENABLE_SRS} -eq 1 ]]; then
_register_setup_function '_setup_SRS' _register_setup_function '_setup_SRS'
_register_start_daemon '_start_daemon_postsrsd' _register_start_daemon '_start_daemon_postsrsd'
fi fi
_register_setup_function '_setup_postfix_late' _register_setup_function '_setup_fetchmail'
_register_setup_function '_setup_fetchmail_parallel'
_register_setup_function '_setup_getmail'
_register_setup_function '_setup_logrotate' _register_setup_function '_setup_logrotate'
_register_setup_function '_setup_mail_summary' _register_setup_function '_setup_mail_summary'
_register_setup_function '_setup_logwatch' _register_setup_function '_setup_logwatch'
@ -111,6 +120,11 @@ function _register_functions() {
_register_setup_function '_setup_apply_fixes_after_configuration' _register_setup_function '_setup_apply_fixes_after_configuration'
_register_setup_function '_environment_variables_export' _register_setup_function '_environment_variables_export'
if [[ ${ENABLE_MTA_STS} -eq 1 ]]; then
_register_setup_function '_setup_mta_sts'
_register_start_daemon '_start_daemon_mta_sts_daemon'
fi
# ? >> Daemons # ? >> Daemons
_register_start_daemon '_start_daemon_cron' _register_start_daemon '_start_daemon_cron'
@ -118,7 +132,13 @@ function _register_functions() {
[[ ${SMTP_ONLY} -ne 1 ]] && _register_start_daemon '_start_daemon_dovecot' [[ ${SMTP_ONLY} -ne 1 ]] && _register_start_daemon '_start_daemon_dovecot'
[[ ${ENABLE_UPDATE_CHECK} -eq 1 ]] && _register_start_daemon '_start_daemon_update_check' if [[ ${ENABLE_UPDATE_CHECK} -eq 1 ]]; then
if [[ ${DMS_RELEASE} != 'edge' ]]; then
_register_start_daemon '_start_daemon_update_check'
else
_log 'warn' "ENABLE_UPDATE_CHECK=1 is configured, but image is not a stable release. Update-Check is disabled."
fi
fi
# The order here matters: Since Rspamd is using Redis, Redis should be started before Rspamd. # The order here matters: Since Rspamd is using Redis, Redis should be started before Rspamd.
[[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && _register_start_daemon '_start_daemon_rspamd_redis' [[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && _register_start_daemon '_start_daemon_rspamd_redis'
@ -151,7 +171,7 @@ function _register_functions() {
_early_supervisor_setup _early_supervisor_setup
_early_variables_setup _early_variables_setup
_log 'info' "Welcome to docker-mailserver $(</VERSION)" _log 'info' "Welcome to docker-mailserver ${DMS_RELEASE}"
_register_functions _register_functions
_check _check

View File

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

View File

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

View File

@ -16,10 +16,9 @@ function _setup_opendkim() {
_log 'trace' "Adding OpenDKIM to Postfix's milters" _log 'trace' "Adding OpenDKIM to Postfix's milters"
postconf 'dkim_milter = inet:localhost:8891' postconf 'dkim_milter = inet:localhost:8891'
# shellcheck disable=SC2016 # shellcheck disable=SC2016
sed -i -E \ _add_to_or_update_postfix_main 'smtpd_milters' '$dkim_milter'
-e '/\$dkim_milter/! s|^(smtpd_milters =.*)|\1 \$dkim_milter|g' \ # shellcheck disable=SC2016
-e '/\$dkim_milter/! s|^(non_smtpd_milters =.*)|\1 \$dkim_milter|g' \ _add_to_or_update_postfix_main 'non_smtpd_milters' '$dkim_milter'
/etc/postfix/main.cf
# check if any keys are available # check if any keys are available
if [[ -e /tmp/docker-mailserver/opendkim/KeyTable ]]; then if [[ -e /tmp/docker-mailserver/opendkim/KeyTable ]]; then
@ -64,7 +63,7 @@ function _setup_opendmarc() {
postconf 'dmarc_milter = inet:localhost:8893' postconf 'dmarc_milter = inet:localhost:8893'
# Make sure to append the OpenDMARC milter _after_ the OpenDKIM milter! # Make sure to append the OpenDMARC milter _after_ the OpenDKIM milter!
# shellcheck disable=SC2016 # shellcheck disable=SC2016
sed -i -E '/\$dmarc_milter/! s|^(smtpd_milters =.*)|\1 \$dmarc_milter|g' /etc/postfix/main.cf _add_to_or_update_postfix_main 'smtpd_milters' '$dmarc_milter'
sed -i \ sed -i \
-e "s|^AuthservID.*$|AuthservID ${HOSTNAME}|g" \ -e "s|^AuthservID.*$|AuthservID ${HOSTNAME}|g" \

View File

@ -6,12 +6,10 @@ function _setup_dovecot() {
cp -a /usr/share/dovecot/protocols.d /etc/dovecot/ cp -a /usr/share/dovecot/protocols.d /etc/dovecot/
# disable pop3 (it will be eventually enabled later in the script, if requested) # disable pop3 (it will be eventually enabled later in the script, if requested)
mv /etc/dovecot/protocols.d/pop3d.protocol /etc/dovecot/protocols.d/pop3d.protocol.disab mv /etc/dovecot/protocols.d/pop3d.protocol /etc/dovecot/protocols.d/pop3d.protocol.disab
# disable imap (it will be eventually enabled later in the script, if requested)
mv /etc/dovecot/protocols.d/imapd.protocol /etc/dovecot/protocols.d/imapd.protocol.disab
mv /etc/dovecot/protocols.d/managesieved.protocol /etc/dovecot/protocols.d/managesieved.protocol.disab mv /etc/dovecot/protocols.d/managesieved.protocol /etc/dovecot/protocols.d/managesieved.protocol.disab
sed -i -e 's|#ssl = yes|ssl = yes|g' /etc/dovecot/conf.d/10-master.conf sedfile -i 's|^postmaster_address = .*$|postmaster_address = '"${POSTMASTER_ADDRESS}"'|g' /etc/dovecot/conf.d/15-lda.conf
sed -i -e 's|#port = 993|port = 993|g' /etc/dovecot/conf.d/10-master.conf
sed -i -e 's|#port = 995|port = 995|g' /etc/dovecot/conf.d/10-master.conf
sed -i -e 's|#ssl = yes|ssl = required|g' /etc/dovecot/conf.d/10-ssl.conf
sed -i 's|^postmaster_address = .*$|postmaster_address = '"${POSTMASTER_ADDRESS}"'|g' /etc/dovecot/conf.d/15-lda.conf
if ! grep -q -E '^stats_writer_socket_path=' /etc/dovecot/dovecot.conf; then if ! grep -q -E '^stats_writer_socket_path=' /etc/dovecot/dovecot.conf; then
printf '\n%s\n' 'stats_writer_socket_path=' >>/etc/dovecot/dovecot.conf printf '\n%s\n' 'stats_writer_socket_path=' >>/etc/dovecot/dovecot.conf
@ -37,9 +35,21 @@ function _setup_dovecot() {
esac esac
if [[ ${ENABLE_POP3} -eq 1 || ${ENABLE_IMAP} -eq 1 ]]; then
sedfile -i -e 's|#ssl = yes|ssl = yes|g' /etc/dovecot/conf.d/10-master.conf
sedfile -i -e 's|#ssl = yes|ssl = required|g' /etc/dovecot/conf.d/10-ssl.conf
fi
if [[ ${ENABLE_POP3} -eq 1 ]]; then if [[ ${ENABLE_POP3} -eq 1 ]]; then
_log 'debug' 'Enabling POP3 services' _log 'debug' 'Enabling POP3 services'
mv /etc/dovecot/protocols.d/pop3d.protocol.disab /etc/dovecot/protocols.d/pop3d.protocol mv /etc/dovecot/protocols.d/pop3d.protocol.disab /etc/dovecot/protocols.d/pop3d.protocol
sedfile -i -e 's|#port = 995|port = 995|g' /etc/dovecot/conf.d/10-master.conf
fi
if [[ ${ENABLE_IMAP} -eq 1 ]]; then
_log 'debug' 'Enabling IMAP services'
mv /etc/dovecot/protocols.d/imapd.protocol.disab /etc/dovecot/protocols.d/imapd.protocol
sedfile -i -e 's|#port = 993|port = 993|g' /etc/dovecot/conf.d/10-master.conf
fi fi
[[ -f /tmp/docker-mailserver/dovecot.cf ]] && cp /tmp/docker-mailserver/dovecot.cf /etc/dovecot/local.conf [[ -f /tmp/docker-mailserver/dovecot.cf ]] && cp /tmp/docker-mailserver/dovecot.cf /etc/dovecot/local.conf
@ -89,23 +99,20 @@ function _setup_dovecot_quota() {
# disable dovecot quota in docevot confs # disable dovecot quota in docevot confs
if [[ -f /etc/dovecot/conf.d/90-quota.conf ]]; then if [[ -f /etc/dovecot/conf.d/90-quota.conf ]]; then
mv /etc/dovecot/conf.d/90-quota.conf /etc/dovecot/conf.d/90-quota.conf.disab mv /etc/dovecot/conf.d/90-quota.conf /etc/dovecot/conf.d/90-quota.conf.disab
sed -i \ sedfile -i \
"s|mail_plugins = \$mail_plugins quota|mail_plugins = \$mail_plugins|g" \ "s|mail_plugins = \$mail_plugins quota|mail_plugins = \$mail_plugins|g" \
/etc/dovecot/conf.d/10-mail.conf /etc/dovecot/conf.d/10-mail.conf
sed -i \ sedfile -i \
"s|mail_plugins = \$mail_plugins imap_quota|mail_plugins = \$mail_plugins|g" \ "s|mail_plugins = \$mail_plugins imap_quota|mail_plugins = \$mail_plugins|g" \
/etc/dovecot/conf.d/20-imap.conf /etc/dovecot/conf.d/20-imap.conf
fi fi
# disable quota policy check in postfix
sed -i "s|check_policy_service inet:localhost:65265||g" /etc/postfix/main.cf
else else
if [[ -f /etc/dovecot/conf.d/90-quota.conf.disab ]]; then if [[ -f /etc/dovecot/conf.d/90-quota.conf.disab ]]; then
mv /etc/dovecot/conf.d/90-quota.conf.disab /etc/dovecot/conf.d/90-quota.conf mv /etc/dovecot/conf.d/90-quota.conf.disab /etc/dovecot/conf.d/90-quota.conf
sed -i \ sedfile -i \
"s|mail_plugins = \$mail_plugins|mail_plugins = \$mail_plugins quota|g" \ "s|mail_plugins = \$mail_plugins|mail_plugins = \$mail_plugins quota|g" \
/etc/dovecot/conf.d/10-mail.conf /etc/dovecot/conf.d/10-mail.conf
sed -i \ sedfile -i \
"s|mail_plugins = \$mail_plugins|mail_plugins = \$mail_plugins imap_quota|g" \ "s|mail_plugins = \$mail_plugins|mail_plugins = \$mail_plugins imap_quota|g" \
/etc/dovecot/conf.d/20-imap.conf /etc/dovecot/conf.d/20-imap.conf
fi fi
@ -113,11 +120,11 @@ function _setup_dovecot_quota() {
local MESSAGE_SIZE_LIMIT_MB=$((POSTFIX_MESSAGE_SIZE_LIMIT / 1000000)) local MESSAGE_SIZE_LIMIT_MB=$((POSTFIX_MESSAGE_SIZE_LIMIT / 1000000))
local MAILBOX_LIMIT_MB=$((POSTFIX_MAILBOX_SIZE_LIMIT / 1000000)) local MAILBOX_LIMIT_MB=$((POSTFIX_MAILBOX_SIZE_LIMIT / 1000000))
sed -i \ sedfile -i \
"s|quota_max_mail_size =.*|quota_max_mail_size = ${MESSAGE_SIZE_LIMIT_MB}$([[ ${MESSAGE_SIZE_LIMIT_MB} -eq 0 ]] && echo "" || echo "M")|g" \ "s|quota_max_mail_size =.*|quota_max_mail_size = ${MESSAGE_SIZE_LIMIT_MB}$([[ ${MESSAGE_SIZE_LIMIT_MB} -eq 0 ]] && echo "" || echo "M")|g" \
/etc/dovecot/conf.d/90-quota.conf /etc/dovecot/conf.d/90-quota.conf
sed -i \ sedfile -i \
"s|quota_rule = \*:storage=.*|quota_rule = *:storage=${MAILBOX_LIMIT_MB}$([[ ${MAILBOX_LIMIT_MB} -eq 0 ]] && echo "" || echo "M")|g" \ "s|quota_rule = \*:storage=.*|quota_rule = *:storage=${MAILBOX_LIMIT_MB}$([[ ${MAILBOX_LIMIT_MB} -eq 0 ]] && echo "" || echo "M")|g" \
/etc/dovecot/conf.d/90-quota.conf /etc/dovecot/conf.d/90-quota.conf
@ -127,7 +134,7 @@ function _setup_dovecot_quota() {
fi fi
# enable quota policy check in postfix # enable quota policy check in postfix
sed -i -E \ sedfile -i -E \
"s|(reject_unknown_recipient_domain)|\1, check_policy_service inet:localhost:65265|g" \ "s|(reject_unknown_recipient_domain)|\1, check_policy_service inet:localhost:65265|g" \
/etc/postfix/main.cf /etc/postfix/main.cf
fi fi
@ -188,5 +195,5 @@ function _setup_dovecot_dhparam() {
function _setup_dovecot_hostname() { function _setup_dovecot_hostname() {
_log 'debug' 'Applying hostname to Dovecot' _log 'debug' 'Applying hostname to Dovecot'
sed -i "s|^#hostname =.*$|hostname = '${HOSTNAME}'|g" /etc/dovecot/conf.d/15-lda.conf sedfile -i "s|^#hostname =.*$|hostname = '${HOSTNAME}'|g" /etc/dovecot/conf.d/15-lda.conf
} }

View File

@ -38,13 +38,7 @@ function _setup_ldap() {
DOVECOT_LDAP_MAPPING['DOVECOT_BASE']="${DOVECOT_BASE:="${LDAP_SEARCH_BASE}"}" DOVECOT_LDAP_MAPPING['DOVECOT_BASE']="${DOVECOT_BASE:="${LDAP_SEARCH_BASE}"}"
DOVECOT_LDAP_MAPPING['DOVECOT_DN']="${DOVECOT_DN:="${LDAP_BIND_DN}"}" DOVECOT_LDAP_MAPPING['DOVECOT_DN']="${DOVECOT_DN:="${LDAP_BIND_DN}"}"
DOVECOT_LDAP_MAPPING['DOVECOT_DNPASS']="${DOVECOT_DNPASS:="${LDAP_BIND_PW}"}" DOVECOT_LDAP_MAPPING['DOVECOT_DNPASS']="${DOVECOT_DNPASS:="${LDAP_BIND_PW}"}"
DOVECOT_LDAP_MAPPING['DOVECOT_URIS']="${DOVECOT_URIS:="${DOVECOT_HOSTS:="${LDAP_SERVER_HOST}"}"}" DOVECOT_LDAP_MAPPING['DOVECOT_URIS']="${DOVECOT_URIS:="${LDAP_SERVER_HOST}"}"
# Add protocol to DOVECOT_URIS so that we can use dovecot's "uris" option:
# https://doc.dovecot.org/configuration_manual/authentication/ldap/
if [[ ${DOVECOT_LDAP_MAPPING["DOVECOT_URIS"]} != *'://'* ]]; then
DOVECOT_LDAP_MAPPING['DOVECOT_URIS']="ldap://${DOVECOT_LDAP_MAPPING["DOVECOT_URIS"]}"
fi
# Default DOVECOT_PASS_FILTER to the same value as DOVECOT_USER_FILTER # Default DOVECOT_PASS_FILTER to the same value as DOVECOT_USER_FILTER
DOVECOT_LDAP_MAPPING['DOVECOT_PASS_FILTER']="${DOVECOT_PASS_FILTER:="${DOVECOT_USER_FILTER}"}" DOVECOT_LDAP_MAPPING['DOVECOT_PASS_FILTER']="${DOVECOT_PASS_FILTER:="${DOVECOT_USER_FILTER}"}"

View File

@ -13,31 +13,22 @@ function _setup_logs_general() {
function _setup_logrotate() { function _setup_logrotate() {
_log 'debug' 'Setting up logrotate' _log 'debug' 'Setting up logrotate'
LOGROTATE='/var/log/mail/mail.log\n{\n compress\n copytruncate\n delaycompress\n' if [[ ${LOGROTATE_INTERVAL} =~ ^(daily|weekly|monthly)$ ]]; then
_log 'trace' "Logrotate interval set to ${LOGROTATE_INTERVAL}"
else
_dms_panic__invalid_value 'LOGROTATE_INTERVAL' 'Setup -> Logrotate'
fi
case "${LOGROTATE_INTERVAL}" in cat >/etc/logrotate.d/maillog << EOF
( 'daily' ) /var/log/mail/mail.log
_log 'trace' 'Setting postfix logrotate interval to daily' {
LOGROTATE="${LOGROTATE} rotate 4\n daily\n" compress
;; copytruncate
delaycompress
( 'weekly' ) rotate 4
_log 'trace' 'Setting postfix logrotate interval to weekly' ${LOGROTATE_INTERVAL}
LOGROTATE="${LOGROTATE} rotate 4\n weekly\n" }
;; EOF
( 'monthly' )
_log 'trace' 'Setting postfix logrotate interval to monthly'
LOGROTATE="${LOGROTATE} rotate 4\n monthly\n"
;;
( * )
_log 'warn' 'LOGROTATE_INTERVAL not found in _setup_logrotate'
;;
esac
echo -e "${LOGROTATE}}" >/etc/logrotate.d/maillog
} }
function _setup_mail_summary() { function _setup_mail_summary() {

View File

@ -24,6 +24,7 @@ function _setup_save_states() {
[[ ${ENABLE_FAIL2BAN} -eq 1 ]] && SERVICEDIRS+=('lib/fail2ban') [[ ${ENABLE_FAIL2BAN} -eq 1 ]] && SERVICEDIRS+=('lib/fail2ban')
[[ ${ENABLE_FETCHMAIL} -eq 1 ]] && SERVICEDIRS+=('lib/fetchmail') [[ ${ENABLE_FETCHMAIL} -eq 1 ]] && SERVICEDIRS+=('lib/fetchmail')
[[ ${ENABLE_GETMAIL} -eq 1 ]] && SERVICEDIRS+=('lib/getmail') [[ ${ENABLE_GETMAIL} -eq 1 ]] && SERVICEDIRS+=('lib/getmail')
[[ ${ENABLE_MTA_STS} -eq 1 ]] && SERVICEDIRS+=('lib/mta-sts')
[[ ${ENABLE_POSTGREY} -eq 1 ]] && SERVICEDIRS+=('lib/postgrey') [[ ${ENABLE_POSTGREY} -eq 1 ]] && SERVICEDIRS+=('lib/postgrey')
[[ ${ENABLE_RSPAMD} -eq 1 ]] && SERVICEDIRS+=('lib/rspamd') [[ ${ENABLE_RSPAMD} -eq 1 ]] && SERVICEDIRS+=('lib/rspamd')
[[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && SERVICEDIRS+=('lib/redis') [[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && SERVICEDIRS+=('lib/redis')
@ -84,6 +85,7 @@ function _setup_save_states() {
[[ ${ENABLE_AMAVIS} -eq 1 ]] && chown -R amavis:amavis "${STATEDIR}/lib-amavis" [[ ${ENABLE_AMAVIS} -eq 1 ]] && chown -R amavis:amavis "${STATEDIR}/lib-amavis"
[[ ${ENABLE_CLAMAV} -eq 1 ]] && chown -R clamav:clamav "${STATEDIR}/lib-clamav" [[ ${ENABLE_CLAMAV} -eq 1 ]] && chown -R clamav:clamav "${STATEDIR}/lib-clamav"
[[ ${ENABLE_FETCHMAIL} -eq 1 ]] && chown -R fetchmail:nogroup "${STATEDIR}/lib-fetchmail" [[ ${ENABLE_FETCHMAIL} -eq 1 ]] && chown -R fetchmail:nogroup "${STATEDIR}/lib-fetchmail"
[[ ${ENABLE_MTA_STS} -eq 1 ]] && chown -R _mta-sts:_mta-sts "${STATEDIR}/lib-mta-sts"
[[ ${ENABLE_POSTGREY} -eq 1 ]] && chown -R postgrey:postgrey "${STATEDIR}/lib-postgrey" [[ ${ENABLE_POSTGREY} -eq 1 ]] && chown -R postgrey:postgrey "${STATEDIR}/lib-postgrey"
[[ ${ENABLE_RSPAMD} -eq 1 ]] && chown -R _rspamd:_rspamd "${STATEDIR}/lib-rspamd" [[ ${ENABLE_RSPAMD} -eq 1 ]] && chown -R _rspamd:_rspamd "${STATEDIR}/lib-rspamd"
[[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && chown -R redis:redis "${STATEDIR}/lib-redis" [[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && chown -R redis:redis "${STATEDIR}/lib-redis"
@ -105,10 +107,10 @@ function _setup_save_states() {
# These two require the postdrop(103) group: # These two require the postdrop(103) group:
chgrp -R postdrop "${STATEDIR}"/spool-postfix/{maildrop,public} chgrp -R postdrop "${STATEDIR}"/spool-postfix/{maildrop,public}
# After changing the group, special bits (set-gid, sticky) may be stripped, restore them: # These permissions rely on the `postdrop` binary having the SGID bit set.
# Ref: https://github.com/docker-mailserver/docker-mailserver/pull/3149#issuecomment-1454981309 # Ref: https://github.com/docker-mailserver/docker-mailserver/pull/3625
chmod 1730 "${STATEDIR}/spool-postfix/maildrop" chmod 730 "${STATEDIR}/spool-postfix/maildrop"
chmod 2710 "${STATEDIR}/spool-postfix/public" chmod 710 "${STATEDIR}/spool-postfix/public"
elif [[ ${ONE_DIR} -eq 1 ]]; then elif [[ ${ONE_DIR} -eq 1 ]]; then
_log 'warn' "'ONE_DIR=1' but no volume was mounted to '${STATEDIR}'" _log 'warn' "'ONE_DIR=1' but no volume was mounted to '${STATEDIR}'"
else else

View File

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

View File

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

View File

@ -19,9 +19,6 @@ function _setup_postfix_early() {
postconf "inet_protocols = ${POSTFIX_INET_PROTOCOLS}" postconf "inet_protocols = ${POSTFIX_INET_PROTOCOLS}"
fi fi
__postfix__log 'trace' "Disabling SMTPUTF8 support"
postconf 'smtputf8_enable = no'
__postfix__log 'trace' "Configuring SASLauthd" __postfix__log 'trace' "Configuring SASLauthd"
if [[ ${ENABLE_SASLAUTHD} -eq 1 ]] && [[ ! -f /etc/postfix/sasl/smtpd.conf ]]; then if [[ ${ENABLE_SASLAUTHD} -eq 1 ]] && [[ ! -f /etc/postfix/sasl/smtpd.conf ]]; then
cat >/etc/postfix/sasl/smtpd.conf << EOF cat >/etc/postfix/sasl/smtpd.conf << EOF
@ -30,18 +27,25 @@ mech_list: plain login
EOF EOF
fi fi
# User has explicitly requested to disable SASL auth:
# TODO: Additive config by feature would be better. Should only enable SASL auth
# on submission(s) services in master.cf when SASLAuthd or Dovecot is enabled.
if [[ ${ENABLE_SASLAUTHD} -eq 0 ]] && [[ ${SMTP_ONLY} -eq 1 ]]; then if [[ ${ENABLE_SASLAUTHD} -eq 0 ]] && [[ ${SMTP_ONLY} -eq 1 ]]; then
# Default for services (eg: Port 25); NOTE: This has since become the default:
sed -i -E \ sed -i -E \
's|^smtpd_sasl_auth_enable =.*|smtpd_sasl_auth_enable = no|g' \ 's|^smtpd_sasl_auth_enable =.*|smtpd_sasl_auth_enable = no|g' \
/etc/postfix/main.cf /etc/postfix/main.cf
# Submission services that are explicitly enabled by default:
sed -i -E \ sed -i -E \
's|^ -o smtpd_sasl_auth_enable=.*| -o smtpd_sasl_auth_enable=no|g' \ 's|^ -o smtpd_sasl_auth_enable=.*| -o smtpd_sasl_auth_enable=no|g' \
/etc/postfix/master.cf /etc/postfix/master.cf
fi fi
# scripts/helpers/aliases.sh:_create_aliases()
__postfix__log 'trace' 'Setting up aliases' __postfix__log 'trace' 'Setting up aliases'
_create_aliases _create_aliases
# scripts/helpers/postfix.sh:_create_postfix_vhost()
__postfix__log 'trace' 'Setting up Postfix vhost' __postfix__log 'trace' 'Setting up Postfix vhost'
_create_postfix_vhost _create_postfix_vhost
@ -63,6 +67,25 @@ EOF
's|^(dms_smtpd_sender_restrictions = .*)|\1, reject_unknown_client_hostname|' \ 's|^(dms_smtpd_sender_restrictions = .*)|\1, reject_unknown_client_hostname|' \
/etc/postfix/main.cf /etc/postfix/main.cf
fi fi
# Dovecot feature integration
# TODO: Alias SMTP_ONLY=0 to DOVECOT_ENABLED=1?
if [[ ${SMTP_ONLY} -ne 1 ]]; then
__postfix__log 'trace' 'Configuring Postfix with Dovecot integration'
# /etc/postfix/vmailbox is created by: scripts/helpers/accounts.sh:_create_accounts()
# This file config is for Postfix to verify a mail account exists before accepting
# mail arriving and delivering it to Dovecot over LMTP.
if [[ ${ACCOUNT_PROVISIONER} == 'FILE' ]]; then
postconf 'virtual_mailbox_maps = texthash:/etc/postfix/vmailbox'
fi
postconf 'virtual_transport = lmtp:unix:/var/run/dovecot/lmtp'
fi
if [[ -n ${POSTFIX_DAGENT} ]]; then
__postfix__log 'trace' "Changing virtual transport to '${POSTFIX_DAGENT}'"
postconf "virtual_transport = ${POSTFIX_DAGENT}"
fi
} }
function _setup_postfix_late() { function _setup_postfix_late() {
@ -80,12 +103,6 @@ function _setup_postfix_late() {
__postfix__log 'trace' 'Configuring relay host' __postfix__log 'trace' 'Configuring relay host'
_setup_relayhost _setup_relayhost
if [[ -n ${POSTFIX_DAGENT} ]]; then
__postfix__log 'trace' "Changing virtual transport to '${POSTFIX_DAGENT}'"
# Default value in main.cf should be 'lmtp:unix:/var/run/dovecot/lmtp'
postconf "virtual_transport = ${POSTFIX_DAGENT}"
fi
__postfix__setup_override_configuration __postfix__setup_override_configuration
} }

View File

@ -1,29 +1,29 @@
#!/bin/bash #!/bin/bash
function _setup_saslauthd() { function _setup_saslauthd() {
_log 'debug' 'Setting up SASLAUTHD' _log 'debug' 'Setting up SASLAUTHD'
if [[ ! -f /etc/saslauthd.conf ]]; then # NOTE: It's unlikely this file would already exist,
# Unlike Dovecot/Postfix LDAP support, this file has no ENV replacement
# nor does it copy from the DMS config volume to this internal location.
if [[ ${ACCOUNT_PROVISIONER} == 'LDAP' ]] \
&& [[ ! -f /etc/saslauthd.conf ]]; then
_log 'trace' 'Creating /etc/saslauthd.conf' _log 'trace' 'Creating /etc/saslauthd.conf'
cat > /etc/saslauthd.conf << EOF
ldap_servers: ${SASLAUTHD_LDAP_SERVER}
ldap_auth_method: ${SASLAUTHD_LDAP_AUTH_METHOD}
ldap_bind_dn: ${SASLAUTHD_LDAP_BIND_DN}
ldap_bind_pw: ${SASLAUTHD_LDAP_PASSWORD}
ldap_search_base: ${SASLAUTHD_LDAP_SEARCH_BASE}
ldap_filter: ${SASLAUTHD_LDAP_FILTER}
ldap_start_tls: ${SASLAUTHD_LDAP_START_TLS}
ldap_tls_check_peer: ${SASLAUTHD_LDAP_TLS_CHECK_PEER}
${SASLAUTHD_LDAP_TLS_CACERT_FILE}
${SASLAUTHD_LDAP_TLS_CACERT_DIR}
${SASLAUTHD_LDAP_PASSWORD_ATTR}
${SASLAUTHD_LDAP_MECH}
# Create a config based on ENV
sed '/^.*: $/d'> /etc/saslauthd.conf << EOF
ldap_servers: ${SASLAUTHD_LDAP_SERVER:=${LDAP_SERVER_HOST}}
ldap_auth_method: ${SASLAUTHD_LDAP_AUTH_METHOD:=bind}
ldap_bind_dn: ${SASLAUTHD_LDAP_BIND_DN:=${LDAP_BIND_DN}}
ldap_bind_pw: ${SASLAUTHD_LDAP_PASSWORD:=${LDAP_BIND_PW}}
ldap_search_base: ${SASLAUTHD_LDAP_SEARCH_BASE:=${LDAP_SEARCH_BASE}}
ldap_filter: ${SASLAUTHD_LDAP_FILTER:=(&(uniqueIdentifier=%u)(mailEnabled=TRUE))}
ldap_start_tls: ${SASLAUTHD_LDAP_START_TLS:=no}
ldap_tls_check_peer: ${SASLAUTHD_LDAP_TLS_CHECK_PEER:=no}
ldap_tls_cacert_file: ${SASLAUTHD_LDAP_TLS_CACERT_FILE}
ldap_tls_cacert_dir: ${SASLAUTHD_LDAP_TLS_CACERT_DIR}
ldap_password_attr: ${SASLAUTHD_LDAP_PASSWORD_ATTR}
ldap_mech: ${SASLAUTHD_LDAP_MECH}
ldap_referrals: yes ldap_referrals: yes
log_level: 10 log_level: 10
EOF EOF
@ -42,4 +42,3 @@ EOF
gpasswd -a postfix sasl >/dev/null gpasswd -a postfix sasl >/dev/null
} }

View File

@ -70,6 +70,8 @@ function __setup__security__spamassassin() {
if [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]]; then if [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]]; then
_log 'debug' 'Enabling and configuring SpamAssassin' _log 'debug' 'Enabling and configuring SpamAssassin'
# Maintainers should take care in attempting to change these sed commands. Alternatives were already explored:
# https://github.com/docker-mailserver/docker-mailserver/pull/3767#issuecomment-1885989591
# shellcheck disable=SC2016 # shellcheck disable=SC2016
sed -i -r 's|^\$sa_tag_level_deflt (.*);|\$sa_tag_level_deflt = '"${SA_TAG}"';|g' /etc/amavis/conf.d/20-debian_defaults sed -i -r 's|^\$sa_tag_level_deflt (.*);|\$sa_tag_level_deflt = '"${SA_TAG}"';|g' /etc/amavis/conf.d/20-debian_defaults
@ -111,7 +113,7 @@ function __setup__security__spamassassin() {
if [[ ${SPAMASSASSIN_SPAM_TO_INBOX} -eq 1 ]]; then if [[ ${SPAMASSASSIN_SPAM_TO_INBOX} -eq 1 ]]; then
_log 'trace' 'Configuring Spamassassin/Amavis to send SPAM to inbox' _log 'trace' 'Configuring Spamassassin/Amavis to send SPAM to inbox'
_log 'debug' 'SPAM_TO_INBOX=1 is set. SA_KILL will be ignored.' _log 'debug' "'SPAMASSASSIN_SPAM_TO_INBOX=1' is set. The 'SA_KILL' ENV will be ignored."
sed -i "s|\$final_spam_destiny.*=.*$|\$final_spam_destiny = D_PASS;|g" /etc/amavis/conf.d/49-docker-mailserver sed -i "s|\$final_spam_destiny.*=.*$|\$final_spam_destiny = D_PASS;|g" /etc/amavis/conf.d/49-docker-mailserver
sed -i "s|\$final_bad_header_destiny.*=.*$|\$final_bad_header_destiny = D_PASS;|g" /etc/amavis/conf.d/49-docker-mailserver sed -i "s|\$final_bad_header_destiny.*=.*$|\$final_bad_header_destiny = D_PASS;|g" /etc/amavis/conf.d/49-docker-mailserver
@ -265,9 +267,34 @@ EOF
chown dovecot:root /usr/lib/dovecot/sieve-global/after/spam_to_junk.{sieve,svbin} chown dovecot:root /usr/lib/dovecot/sieve-global/after/spam_to_junk.{sieve,svbin}
if [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]] && [[ ${SPAMASSASSIN_SPAM_TO_INBOX} -eq 0 ]]; then if [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]] && [[ ${SPAMASSASSIN_SPAM_TO_INBOX} -eq 0 ]]; then
_log 'warning' "'SPAMASSASSIN_SPAM_TO_INBOX=0' but it is required to be 1 for 'MOVE_SPAM_TO_JUNK=1' to work" _log 'warn' "'SPAMASSASSIN_SPAM_TO_INBOX=0' but it is required to be 1 for 'MOVE_SPAM_TO_JUNK=1' to work"
fi fi
else else
_log 'debug' 'Spam emails will not be moved to the Junk folder' _log 'debug' 'Spam emails will not be moved to the Junk folder'
fi fi
} }
function _setup_spam_mark_as_read() {
if [[ ${MARK_SPAM_AS_READ} -eq 1 ]]; then
_log 'debug' 'Spam emails will be marked as read'
mkdir -p /usr/lib/dovecot/sieve-global/after/
# Header support: `X-Spam-Flag` (SpamAssassin), `X-Spam` (Rspamd)
cat >/usr/lib/dovecot/sieve-global/after/spam_mark_as_read.sieve << EOF
require ["mailbox","imap4flags"];
if anyof (header :contains "X-Spam-Flag" "YES",
header :contains "X-Spam" "Yes") {
setflag "\\\\Seen";
}
EOF
sievec /usr/lib/dovecot/sieve-global/after/spam_mark_as_read.sieve
chown dovecot:root /usr/lib/dovecot/sieve-global/after/spam_mark_as_read.{sieve,svbin}
if [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]] && [[ ${SPAMASSASSIN_SPAM_TO_INBOX} -eq 0 ]]; then
_log 'warn' "'SPAMASSASSIN_SPAM_TO_INBOX=0' but it is required to be 1 for 'MARK_SPAM_AS_READ=1' to work"
fi
else
_log 'debug' 'Spam emails will not be marked as read'
fi
}

View File

@ -1,12 +1,18 @@
#!/bin/bash #!/bin/bash
# Function called during global setup to handle the complete setup of Rspamd. # This file is executed during startup of DMS. Hence, the `index.sh` helper has already
# been sourced, and thus, all helper functions from `rspamd.sh` are available.
# Function called during global setup to handle the complete setup of Rspamd. Functions
# with a single `_` prefix are sourced from the `rspamd.sh` helper.
function _setup_rspamd() { function _setup_rspamd() {
if _env_var_expect_zero_or_one 'ENABLE_RSPAMD' && [[ ${ENABLE_RSPAMD} -eq 1 ]]; then if _env_var_expect_zero_or_one 'ENABLE_RSPAMD' && [[ ${ENABLE_RSPAMD} -eq 1 ]]; then
_log 'debug' 'Enabling and configuring Rspamd' _log 'debug' 'Enabling and configuring Rspamd'
__rspamd__log 'trace' '---------- Setup started ----------' __rspamd__log 'trace' '---------- Setup started ----------'
__rspamd__run_early_setup_and_checks # must run first _rspamd_get_envs # must run first
__rspamd__run_early_setup_and_checks # must run second
__rspamd__setup_logfile
__rspamd__setup_redis __rspamd__setup_redis
__rspamd__setup_postfix __rspamd__setup_postfix
__rspamd__setup_clamav __rspamd__setup_clamav
@ -14,7 +20,11 @@ function _setup_rspamd() {
__rspamd__setup_learning __rspamd__setup_learning
__rspamd__setup_greylisting __rspamd__setup_greylisting
__rspamd__setup_hfilter_group __rspamd__setup_hfilter_group
__rspamd__handle_user_modules_adjustments # must run last __rspamd__setup_check_authenticated
_rspamd_handle_user_modules_adjustments # must run last
# only performing checks, no further setup handled from here onwards
__rspamd__check_dkim_permissions
__rspamd__log 'trace' '---------- Setup finished ----------' __rspamd__log 'trace' '---------- Setup finished ----------'
else else
@ -41,6 +51,8 @@ function __rspamd__helper__enable_disable_module() {
local LOCAL_OR_OVERRIDE=${3:-local} local LOCAL_OR_OVERRIDE=${3:-local}
local MESSAGE='Enabling' local MESSAGE='Enabling'
readonly MODULE ENABLE_MODULE LOCAL_OR_OVERRIDE
if [[ ! ${ENABLE_MODULE} =~ ^(true|false)$ ]]; then if [[ ! ${ENABLE_MODULE} =~ ^(true|false)$ ]]; then
__rspamd__log 'warn' "__rspamd__helper__enable_disable_module got non-boolean argument for deciding whether module should be enabled or not" __rspamd__log 'warn' "__rspamd__helper__enable_disable_module got non-boolean argument for deciding whether module should be enabled or not"
return 1 return 1
@ -60,23 +72,11 @@ EOF
# Run miscellaneous early setup tasks and checks, such as creating files needed at runtime # Run miscellaneous early setup tasks and checks, such as creating files needed at runtime
# or checking for other anti-spam/anti-virus software. # or checking for other anti-spam/anti-virus software.
function __rspamd__run_early_setup_and_checks() { function __rspamd__run_early_setup_and_checks() {
# Note: Variables not marked with `local` are
# used in other functions as well.
RSPAMD_LOCAL_D='/etc/rspamd/local.d'
RSPAMD_OVERRIDE_D='/etc/rspamd/override.d'
RSPAMD_DMS_D='/tmp/docker-mailserver/rspamd'
local RSPAMD_DMS_OVERRIDE_D="${RSPAMD_DMS_D}/override.d/"
mkdir -p /var/lib/rspamd/ mkdir -p /var/lib/rspamd/
: >/var/lib/rspamd/stats.ucl : >/var/lib/rspamd/stats.ucl
if [[ -d ${RSPAMD_DMS_OVERRIDE_D} ]]; then if [[ -d ${RSPAMD_DMS_OVERRIDE_D} ]]; then
__rspamd__log 'debug' "Found directory '${RSPAMD_DMS_OVERRIDE_D}' - linking it to '${RSPAMD_OVERRIDE_D}'" cp "${RSPAMD_DMS_OVERRIDE_D}"/* "${RSPAMD_OVERRIDE_D}"
if rmdir "${RSPAMD_OVERRIDE_D}" 2>/dev/null; then
ln -s "${RSPAMD_DMS_OVERRIDE_D}" "${RSPAMD_OVERRIDE_D}"
else
__rspamd__log 'warn' "Could not remove '${RSPAMD_OVERRIDE_D}' (not empty? not a directory?; did you restart properly?) - not linking '${RSPAMD_DMS_OVERRIDE_D}'"
fi
fi fi
if [[ ${ENABLE_AMAVIS} -eq 1 ]] || [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]]; then if [[ ${ENABLE_AMAVIS} -eq 1 ]] || [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]]; then
@ -100,6 +100,20 @@ function __rspamd__run_early_setup_and_checks() {
fi fi
} }
# Keep in sync with `target/scripts/startup/setup.d/log.sh:_setup_logrotate()`
function __rspamd__setup_logfile() {
cat >/etc/logrotate.d/rspamd << EOF
/var/log/mail/rspamd.log
{
compress
copytruncate
delaycompress
rotate 4
${LOGROTATE_INTERVAL}
}
EOF
}
# Sets up Redis. In case the user does not use a dedicated Redis instance, we # Sets up Redis. In case the user does not use a dedicated Redis instance, we
# supply a configuration for our local Redis instance which is started later. # supply a configuration for our local Redis instance which is started later.
function __rspamd__setup_redis() { function __rspamd__setup_redis() {
@ -137,7 +151,7 @@ function __rspamd__setup_postfix() {
postconf 'rspamd_milter = inet:localhost:11332' postconf 'rspamd_milter = inet:localhost:11332'
# shellcheck disable=SC2016 # shellcheck disable=SC2016
sed -i -E 's|^(smtpd_milters =.*)|\1 \$rspamd_milter|g' /etc/postfix/main.cf _add_to_or_update_postfix_main 'smtpd_milters' '$rspamd_milter'
} }
# If ClamAV is enabled, we will integrate it into Rspamd. # If ClamAV is enabled, we will integrate it into Rspamd.
@ -179,6 +193,8 @@ function __rspamd__setup_default_modules() {
metric_exporter metric_exporter
) )
readonly -a DISABLE_MODULES
local MODULE
for MODULE in "${DISABLE_MODULES[@]}"; do for MODULE in "${DISABLE_MODULES[@]}"; do
__rspamd__helper__enable_disable_module "${MODULE}" 'false' __rspamd__helper__enable_disable_module "${MODULE}" 'false'
done done
@ -194,6 +210,7 @@ function __rspamd__setup_learning() {
__rspamd__log 'debug' 'Setting up intelligent learning of spam and ham' __rspamd__log 'debug' 'Setting up intelligent learning of spam and ham'
local SIEVE_PIPE_BIN_DIR='/usr/lib/dovecot/sieve-pipe' local SIEVE_PIPE_BIN_DIR='/usr/lib/dovecot/sieve-pipe'
readonly SIEVE_PIPE_BIN_DIR
ln -s "$(type -f -P rspamc)" "${SIEVE_PIPE_BIN_DIR}/rspamc" ln -s "$(type -f -P rspamc)" "${SIEVE_PIPE_BIN_DIR}/rspamc"
sedfile -i -E 's|(mail_plugins =.*)|\1 imap_sieve|' /etc/dovecot/conf.d/20-imap.conf sedfile -i -E 's|(mail_plugins =.*)|\1 imap_sieve|' /etc/dovecot/conf.d/20-imap.conf
@ -247,10 +264,12 @@ function __rspamd__setup_greylisting() {
# succeeds. # succeeds.
function __rspamd__setup_hfilter_group() { function __rspamd__setup_hfilter_group() {
local MODULE_FILE="${RSPAMD_LOCAL_D}/hfilter_group.conf" local MODULE_FILE="${RSPAMD_LOCAL_D}/hfilter_group.conf"
readonly MODULE_FILE
if _env_var_expect_zero_or_one 'RSPAMD_HFILTER' && [[ ${RSPAMD_HFILTER} -eq 1 ]]; then if _env_var_expect_zero_or_one 'RSPAMD_HFILTER' && [[ ${RSPAMD_HFILTER} -eq 1 ]]; then
__rspamd__log 'debug' 'Hfilter (group) module is enabled' __rspamd__log 'debug' 'Hfilter (group) module is enabled'
# Check if we received a number first # Check if we received a number first
if _env_var_expect_integer 'RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE' && [[ ${RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE} -ne 6 ]]; then if _env_var_expect_integer 'RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE' \
&& [[ ${RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE} -ne 6 ]]; then
__rspamd__log 'trace' "Adjusting score for 'HFILTER_HOSTNAME_UNKNOWN' in Hfilter group module to ${RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE}" __rspamd__log 'trace' "Adjusting score for 'HFILTER_HOSTNAME_UNKNOWN' in Hfilter group module to ${RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE}"
sed -i -E \ sed -i -E \
"s|(.*score =).*(# __TAG__HFILTER_HOSTNAME_UNKNOWN)|\1 ${RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE}; \2|g" \ "s|(.*score =).*(# __TAG__HFILTER_HOSTNAME_UNKNOWN)|\1 ${RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE}; \2|g" \
@ -264,95 +283,55 @@ function __rspamd__setup_hfilter_group() {
fi fi
} }
# Parses `RSPAMD_CUSTOM_COMMANDS_FILE` and executed the directives given by the file. # If 'RSPAMD_CHECK_AUTHENTICATED' is enabled, then content checks for all users, i.e.
# To get a detailed explanation of the commands and how the file works, visit # also for authenticated users, are performed.
# https://docker-mailserver.github.io/docker-mailserver/edge/config/security/rspamd/#with-the-help-of-a-custom-file #
function __rspamd__handle_user_modules_adjustments() { # The default that DMS ships does not check authenticated users. In case the checks are
# Adds an option with a corresponding value to a module, or, in case the option # enabled, this function will remove the part of the Rspamd configuration that disables
# is already present, overwrites it. # checks for authenticated users.
# function __rspamd__setup_check_authenticated() {
# @param ${1} = file name in ${RSPAMD_OVERRIDE_D}/ local MODULE_FILE="${RSPAMD_LOCAL_D}/settings.conf"
# @param ${2} = module name as it should appear in the log readonly MODULE_FILE
# @patam ${3} = option name in the module if _env_var_expect_zero_or_one 'RSPAMD_CHECK_AUTHENTICATED' \
# @param ${4} = value of the option && [[ ${RSPAMD_CHECK_AUTHENTICATED} -eq 0 ]]
# then
# ## Note __rspamd__log 'debug' 'Content checks for authenticated users are disabled'
# else
# While this function is currently bound to the scope of `__rspamd__handle_user_modules_adjustments`, __rspamd__log 'debug' 'Enabling content checks for authenticated users'
# it is written in a versatile way (taking 4 arguments instead of assuming `ARGUMENT2` / `ARGUMENT3` sed -i -E \
# are set) so that it may be used elsewhere if needed. '/DMS::SED_TAG::1::START/{:a;N;/DMS::SED_TAG::1::END/!ba};/authenticated/d' \
function __add_or_replace() { "${MODULE_FILE}"
local MODULE_FILE=${1:?Module file name must be provided}
local MODULE_LOG_NAME=${2:?Module log name must be provided}
local OPTION=${3:?Option name must be provided}
local VALUE=${4:?Value belonging to an option must be provided}
# remove possible whitespace at the end (e.g., in case ${ARGUMENT3} is empty)
VALUE=${VALUE% }
local FILE="${RSPAMD_OVERRIDE_D}/${MODULE_FILE}"
[[ -f ${FILE} ]] || touch "${FILE}"
if grep -q -E "${OPTION}.*=.*" "${FILE}"; then
__rspamd__log 'trace' "Overwriting option '${OPTION}' with value '${VALUE}' for ${MODULE_LOG_NAME}"
sed -i -E "s|([[:space:]]*${OPTION}).*|\1 = ${VALUE};|g" "${FILE}"
else
__rspamd__log 'trace' "Setting option '${OPTION}' for ${MODULE_LOG_NAME} to '${VALUE}'"
echo "${OPTION} = ${VALUE};" >>"${FILE}"
fi
}
local RSPAMD_CUSTOM_COMMANDS_FILE="${RSPAMD_DMS_D}/custom-commands.conf"
local RSPAMD_CUSTOM_COMMANDS_FILE_OLD="${RSPAMD_DMS_D}-modules.conf"
# We check for usage of the previous location of the commands file.
# This can be removed after the release of v14.0.0.
if [[ -f ${RSPAMD_CUSTOM_COMMANDS_FILE_OLD} ]]; then
__rspamd__log 'warn' "Detected usage of old file location for modules adjustment ('${RSPAMD_CUSTOM_COMMANDS_FILE_OLD}') - please use the new location ('${RSPAMD_CUSTOM_COMMANDS_FILE}')"
__rspamd__log 'warn' "Using old file location now (deprecated) - this will prevent startup in v13.0.0"
RSPAMD_CUSTOM_COMMANDS_FILE=${RSPAMD_CUSTOM_COMMANDS_FILE_OLD}
fi
if [[ -f "${RSPAMD_CUSTOM_COMMANDS_FILE}" ]]; then
__rspamd__log 'debug' "Found file '${RSPAMD_CUSTOM_COMMANDS_FILE}' - parsing and applying it"
while read -r COMMAND ARGUMENT1 ARGUMENT2 ARGUMENT3; do
case "${COMMAND}" in
('disable-module')
__rspamd__helper__enable_disable_module "${ARGUMENT1}" 'false' 'override'
;;
('enable-module')
__rspamd__helper__enable_disable_module "${ARGUMENT1}" 'true' 'override'
;;
('set-option-for-module')
__add_or_replace "${ARGUMENT1}.conf" "module '${ARGUMENT1}'" "${ARGUMENT2}" "${ARGUMENT3}"
;;
('set-option-for-controller')
__add_or_replace 'worker-controller.inc' 'controller worker' "${ARGUMENT1}" "${ARGUMENT2} ${ARGUMENT3}"
;;
('set-option-for-proxy')
__add_or_replace 'worker-proxy.inc' 'proxy worker' "${ARGUMENT1}" "${ARGUMENT2} ${ARGUMENT3}"
;;
('set-common-option')
__add_or_replace 'options.inc' 'common options' "${ARGUMENT1}" "${ARGUMENT2} ${ARGUMENT3}"
;;
('add-line')
__rspamd__log 'trace' "Adding complete line to '${ARGUMENT1}'"
echo "${ARGUMENT2}${ARGUMENT3+ ${ARGUMENT3}}" >>"${RSPAMD_OVERRIDE_D}/${ARGUMENT1}"
;;
(*)
__rspamd__log 'warn' "Command '${COMMAND}' is invalid"
continue
;;
esac
done < <(_get_valid_lines_from_file "${RSPAMD_CUSTOM_COMMANDS_FILE}")
fi fi
} }
# This function performs a simple check: go through DKIM configuration files, acquire
# all private key file locations and check whether they exist and whether they can be
# accessed by Rspamd.
function __rspamd__check_dkim_permissions() {
local DKIM_CONF_FILES DKIM_KEY_FILES
[[ -f ${RSPAMD_LOCAL_D}/dkim_signing.conf ]] && DKIM_CONF_FILES+=("${RSPAMD_LOCAL_D}/dkim_signing.conf")
[[ -f ${RSPAMD_OVERRIDE_D}/dkim_signing.conf ]] && DKIM_CONF_FILES+=("${RSPAMD_OVERRIDE_D}/dkim_signing.conf")
# Here, we populate DKIM_KEY_FILES which we later iterate over. DKIM_KEY_FILES
# contains all keys files configured by the user.
local FILE
for FILE in "${DKIM_CONF_FILES[@]}"; do
readarray -t DKIM_KEY_FILES_TMP < <(grep -o -E 'path = .*' "${FILE}" | cut -d '=' -f 2 | tr -d ' ";')
DKIM_KEY_FILES+=("${DKIM_KEY_FILES_TMP[@]}")
done
for FILE in "${DKIM_KEY_FILES[@]}"; do
if [[ -f ${FILE} ]]; then
__rspamd__log 'trace' "Checking DKIM file '${FILE}'"
# See https://serverfault.com/a/829314 for an explanation on `-exec false {} +`
# We additionally resolve symbolic links to check the permissions of the actual files
if find "$(realpath -eL "${FILE}")" \( -user _rspamd -or -group _rspamd -or -perm -o=r \) -exec false {} +; then
__rspamd__log 'warn' "Rspamd DKIM private key file '${FILE}' does not appear to have correct permissions/ownership for Rspamd to use it"
else
__rspamd__log 'trace' "DKIM file '${FILE}' permissions and ownership appear correct"
fi
else
__rspamd__log 'warn' "Rspamd DKIM private key file '${FILE}' is configured for usage, but does not appear to exist"
fi
done
}

View File

@ -11,6 +11,9 @@ function _setup_spoof_protection() {
postconf 'smtpd_sender_login_maps = ldap:/etc/postfix/ldap-senders.cf' postconf 'smtpd_sender_login_maps = ldap:/etc/postfix/ldap-senders.cf'
fi fi
else else
# NOTE: This file is always created at startup, it potentially has content added.
# TODO: From section: "SPOOF_PROTECTION=1 handling for smtpd_sender_login_maps"
# https://github.com/docker-mailserver/docker-mailserver/issues/2819#issue-1402114383
if [[ -f /etc/postfix/regexp ]]; then if [[ -f /etc/postfix/regexp ]]; then
postconf 'smtpd_sender_login_maps = unionmap:{ texthash:/etc/postfix/virtual, hash:/etc/aliases, pcre:/etc/postfix/maps/sender_login_maps.pcre, pcre:/etc/postfix/regexp }' postconf 'smtpd_sender_login_maps = unionmap:{ texthash:/etc/postfix/virtual, hash:/etc/aliases, pcre:/etc/postfix/maps/sender_login_maps.pcre, pcre:/etc/postfix/regexp }'
else else

View File

@ -0,0 +1,12 @@
#!/bin/bash
function _setup_vmail_id() {
if [[ "${DMS_VMAIL_UID}" != "5000" ]]; then
_log 'debug' "Setting 'docker' UID to ${DMS_VMAIL_UID}"
usermod --uid "${DMS_VMAIL_UID}" docker
fi
if [[ "${DMS_VMAIL_GID}" != "5000" ]]; then
_log 'debug' "Setting 'docker' GID to ${DMS_VMAIL_GID}"
groupmod --gid "${DMS_VMAIL_GID}" docker
fi
}

View File

@ -14,8 +14,15 @@ function _early_variables_setup() {
# completely with a single version. # completely with a single version.
function __environment_variables_backwards_compatibility() { function __environment_variables_backwards_compatibility() {
if [[ ${ENABLE_LDAP:-0} -eq 1 ]]; then if [[ ${ENABLE_LDAP:-0} -eq 1 ]]; then
_log 'warn' "'ENABLE_LDAP=1' is deprecated (and will be removed in v13.0.0) => use 'ACCOUNT_PROVISIONER=LDAP' instead" _log 'error' "'ENABLE_LDAP=1' has been changed to 'ACCOUNT_PROVISIONER=LDAP' since DMS v13"
ACCOUNT_PROVISIONER='LDAP' fi
# Dovecot and SASLAuthd have applied an 'ldap://' fallback for compatibility since v10 (June 2021)
# This was silently applied, but users should be explicit:
if [[ ${LDAP_SERVER_HOST:-'://'} != *'://'* ]] \
|| [[ ${DOVECOT_URIS:-'://'} != *'://'* ]] \
|| [[ ${SASLAUTHD_LDAP_SERVER:-'://'} != *'://'* ]]; then
_log 'error' "The ENV for which LDAP host to connect to must include the URI scheme ('ldap://', 'ldaps://', 'ldapi://')"
fi fi
# TODO this can be uncommented in a PR handling the HOSTNAME/DOMAINNAME issue # TODO this can be uncommented in a PR handling the HOSTNAME/DOMAINNAME issue
@ -39,6 +46,8 @@ function __environment_variables_general_setup() {
VARS[POSTMASTER_ADDRESS]="${POSTMASTER_ADDRESS:=postmaster@${DOMAINNAME}}" VARS[POSTMASTER_ADDRESS]="${POSTMASTER_ADDRESS:=postmaster@${DOMAINNAME}}"
VARS[REPORT_RECIPIENT]="${REPORT_RECIPIENT:=${POSTMASTER_ADDRESS}}" VARS[REPORT_RECIPIENT]="${REPORT_RECIPIENT:=${POSTMASTER_ADDRESS}}"
VARS[REPORT_SENDER]="${REPORT_SENDER:=mailserver-report@${HOSTNAME}}" VARS[REPORT_SENDER]="${REPORT_SENDER:=mailserver-report@${HOSTNAME}}"
VARS[DMS_VMAIL_UID]="${DMS_VMAIL_UID:=5000}"
VARS[DMS_VMAIL_GID]="${DMS_VMAIL_GID:=5000}"
_log 'trace' 'Setting anti-spam & anti-virus environment variables' _log 'trace' 'Setting anti-spam & anti-virus environment variables'
@ -46,11 +55,13 @@ function __environment_variables_general_setup() {
VARS[CLAMAV_MESSAGE_SIZE_LIMIT]="${CLAMAV_MESSAGE_SIZE_LIMIT:=25M}" VARS[CLAMAV_MESSAGE_SIZE_LIMIT]="${CLAMAV_MESSAGE_SIZE_LIMIT:=25M}"
VARS[FAIL2BAN_BLOCKTYPE]="${FAIL2BAN_BLOCKTYPE:=drop}" VARS[FAIL2BAN_BLOCKTYPE]="${FAIL2BAN_BLOCKTYPE:=drop}"
VARS[MOVE_SPAM_TO_JUNK]="${MOVE_SPAM_TO_JUNK:=1}" VARS[MOVE_SPAM_TO_JUNK]="${MOVE_SPAM_TO_JUNK:=1}"
VARS[MARK_SPAM_AS_READ]="${MARK_SPAM_AS_READ:=0}"
VARS[POSTGREY_AUTO_WHITELIST_CLIENTS]="${POSTGREY_AUTO_WHITELIST_CLIENTS:=5}" VARS[POSTGREY_AUTO_WHITELIST_CLIENTS]="${POSTGREY_AUTO_WHITELIST_CLIENTS:=5}"
VARS[POSTGREY_DELAY]="${POSTGREY_DELAY:=300}" VARS[POSTGREY_DELAY]="${POSTGREY_DELAY:=300}"
VARS[POSTGREY_MAX_AGE]="${POSTGREY_MAX_AGE:=35}" VARS[POSTGREY_MAX_AGE]="${POSTGREY_MAX_AGE:=35}"
VARS[POSTGREY_TEXT]="${POSTGREY_TEXT:=Delayed by Postgrey}" VARS[POSTGREY_TEXT]="${POSTGREY_TEXT:=Delayed by Postgrey}"
VARS[POSTSCREEN_ACTION]="${POSTSCREEN_ACTION:=enforce}" VARS[POSTSCREEN_ACTION]="${POSTSCREEN_ACTION:=enforce}"
VARS[RSPAMD_CHECK_AUTHENTICATED]="${RSPAMD_CHECK_AUTHENTICATED:=0}"
VARS[RSPAMD_GREYLISTING]="${RSPAMD_GREYLISTING:=0}" VARS[RSPAMD_GREYLISTING]="${RSPAMD_GREYLISTING:=0}"
VARS[RSPAMD_HFILTER]="${RSPAMD_HFILTER:=1}" VARS[RSPAMD_HFILTER]="${RSPAMD_HFILTER:=1}"
VARS[RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE]="${RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE:=6}" VARS[RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE]="${RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE:=6}"
@ -72,10 +83,12 @@ function __environment_variables_general_setup() {
VARS[ENABLE_FETCHMAIL]="${ENABLE_FETCHMAIL:=0}" VARS[ENABLE_FETCHMAIL]="${ENABLE_FETCHMAIL:=0}"
VARS[ENABLE_GETMAIL]="${ENABLE_GETMAIL:=0}" VARS[ENABLE_GETMAIL]="${ENABLE_GETMAIL:=0}"
VARS[ENABLE_MANAGESIEVE]="${ENABLE_MANAGESIEVE:=0}" VARS[ENABLE_MANAGESIEVE]="${ENABLE_MANAGESIEVE:=0}"
VARS[ENABLE_OAUTH2]="${ENABLE_OAUTH2:=0}"
VARS[ENABLE_OPENDKIM]="${ENABLE_OPENDKIM:=1}" VARS[ENABLE_OPENDKIM]="${ENABLE_OPENDKIM:=1}"
VARS[ENABLE_OPENDMARC]="${ENABLE_OPENDMARC:=1}" VARS[ENABLE_OPENDMARC]="${ENABLE_OPENDMARC:=1}"
VARS[ENABLE_POLICYD_SPF]="${ENABLE_POLICYD_SPF:=1}" VARS[ENABLE_POLICYD_SPF]="${ENABLE_POLICYD_SPF:=1}"
VARS[ENABLE_POP3]="${ENABLE_POP3:=0}" VARS[ENABLE_POP3]="${ENABLE_POP3:=0}"
VARS[ENABLE_IMAP]="${ENABLE_IMAP:=1}"
VARS[ENABLE_POSTGREY]="${ENABLE_POSTGREY:=0}" VARS[ENABLE_POSTGREY]="${ENABLE_POSTGREY:=0}"
VARS[ENABLE_QUOTAS]="${ENABLE_QUOTAS:=1}" VARS[ENABLE_QUOTAS]="${ENABLE_QUOTAS:=1}"
VARS[ENABLE_RSPAMD]="${ENABLE_RSPAMD:=0}" VARS[ENABLE_RSPAMD]="${ENABLE_RSPAMD:=0}"
@ -139,7 +152,14 @@ function __environment_variables_general_setup() {
VARS[UPDATE_CHECK_INTERVAL]="${UPDATE_CHECK_INTERVAL:=1d}" VARS[UPDATE_CHECK_INTERVAL]="${UPDATE_CHECK_INTERVAL:=1d}"
} }
function _environment_variables_oauth2() {
_log 'debug' 'Setting OAUTH2-related environment variables now'
VARS[OAUTH2_INTROSPECTION_URL]="${OAUTH2_INTROSPECTION_URL:=}"
}
# This function handles environment variables related to LDAP. # This function handles environment variables related to LDAP.
# NOTE: SASLAuthd and Dovecot LDAP support inherit these common ENV.
function _environment_variables_ldap() { function _environment_variables_ldap() {
_log 'debug' 'Setting LDAP-related environment variables now' _log 'debug' 'Setting LDAP-related environment variables now'
@ -151,55 +171,12 @@ function _environment_variables_ldap() {
} }
# This function handles environment variables related to SASLAUTHD # This function handles environment variables related to SASLAUTHD
# and, if activated, variables related to SASLAUTHD and LDAP. # LDAP specific ENV handled in: `startup/setup.d/saslauthd.sh:_setup_saslauthd()`
function _environment_variables_saslauthd() { function _environment_variables_saslauthd() {
_log 'debug' 'Setting SASLAUTHD-related environment variables now' _log 'debug' 'Setting SASLAUTHD-related environment variables now'
# Only used by the supervisor service command (upstream default: `/etc/default/saslauthd`)
VARS[SASLAUTHD_MECHANISMS]="${SASLAUTHD_MECHANISMS:=pam}" VARS[SASLAUTHD_MECHANISMS]="${SASLAUTHD_MECHANISMS:=pam}"
# SASL ENV for configuring an LDAP specific
# `saslauthd.conf` via `setup-stack.sh:_setup_sasulauthd()`
if [[ ${ACCOUNT_PROVISIONER} == 'LDAP' ]]; then
_log 'trace' 'Setting SASLSAUTH-LDAP variables nnow'
VARS[SASLAUTHD_LDAP_AUTH_METHOD]="${SASLAUTHD_LDAP_AUTH_METHOD:=bind}"
VARS[SASLAUTHD_LDAP_BIND_DN]="${SASLAUTHD_LDAP_BIND_DN:=${LDAP_BIND_DN}}"
VARS[SASLAUTHD_LDAP_FILTER]="${SASLAUTHD_LDAP_FILTER:=(&(uniqueIdentifier=%u)(mailEnabled=TRUE))}"
VARS[SASLAUTHD_LDAP_PASSWORD]="${SASLAUTHD_LDAP_PASSWORD:=${LDAP_BIND_PW}}"
VARS[SASLAUTHD_LDAP_SEARCH_BASE]="${SASLAUTHD_LDAP_SEARCH_BASE:=${LDAP_SEARCH_BASE}}"
VARS[SASLAUTHD_LDAP_SERVER]="${SASLAUTHD_LDAP_SERVER:=${LDAP_SERVER_HOST}}"
[[ ${SASLAUTHD_LDAP_SERVER} != *'://'* ]] && SASLAUTHD_LDAP_SERVER="ldap://${SASLAUTHD_LDAP_SERVER}"
VARS[SASLAUTHD_LDAP_START_TLS]="${SASLAUTHD_LDAP_START_TLS:=no}"
VARS[SASLAUTHD_LDAP_TLS_CHECK_PEER]="${SASLAUTHD_LDAP_TLS_CHECK_PEER:=no}"
if [[ -z ${SASLAUTHD_LDAP_TLS_CACERT_FILE} ]]; then
SASLAUTHD_LDAP_TLS_CACERT_FILE=''
else
SASLAUTHD_LDAP_TLS_CACERT_FILE="ldap_tls_cacert_file: ${SASLAUTHD_LDAP_TLS_CACERT_FILE}"
fi
VARS[SASLAUTHD_LDAP_TLS_CACERT_FILE]="${SASLAUTHD_LDAP_TLS_CACERT_FILE}"
if [[ -z ${SASLAUTHD_LDAP_TLS_CACERT_DIR} ]]; then
SASLAUTHD_LDAP_TLS_CACERT_DIR=''
else
SASLAUTHD_LDAP_TLS_CACERT_DIR="ldap_tls_cacert_dir: ${SASLAUTHD_LDAP_TLS_CACERT_DIR}"
fi
VARS[SASLAUTHD_LDAP_TLS_CACERT_DIR]="${SASLAUTHD_LDAP_TLS_CACERT_DIR}"
if [[ -z ${SASLAUTHD_LDAP_PASSWORD_ATTR} ]]; then
SASLAUTHD_LDAP_PASSWORD_ATTR=''
else
SASLAUTHD_LDAP_PASSWORD_ATTR="ldap_password_attr: ${SASLAUTHD_LDAP_PASSWORD_ATTR}"
fi
VARS[SASLAUTHD_LDAP_PASSWORD_ATTR]="${SASLAUTHD_LDAP_PASSWORD_ATTR}"
if [[ -z ${SASLAUTHD_LDAP_MECH} ]]; then
SASLAUTHD_LDAP_MECH=''
else
SASLAUTHD_LDAP_MECH="ldap_mech: ${SASLAUTHD_LDAP_MECH}"
fi
VARS[SASLAUTHD_LDAP_MECH]="${SASLAUTHD_LDAP_MECH}"
fi
} }
# This function Writes the contents of the `VARS` map (associative array) # This function Writes the contents of the `VARS` map (associative array)

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