Compare commits
5 Commits
master
...
feat/unify
Author | SHA1 | Date |
---|---|---|
|
6076faee3a | |
|
d046ab5b57 | |
|
523f5b8fbb | |
|
466602c66d | |
|
8fa186ae76 |
|
@ -71,16 +71,16 @@ jobs:
|
||||||
cache-buildx-
|
cache-buildx-
|
||||||
|
|
||||||
- name: 'Set up QEMU'
|
- name: 'Set up QEMU'
|
||||||
uses: docker/setup-qemu-action@v3.6.0
|
uses: docker/setup-qemu-action@v3.4.0
|
||||||
with:
|
with:
|
||||||
platforms: arm64
|
platforms: arm64
|
||||||
|
|
||||||
- name: 'Set up Docker Buildx'
|
- name: 'Set up Docker Buildx'
|
||||||
uses: docker/setup-buildx-action@v3.10.0
|
uses: docker/setup-buildx-action@v3.9.0
|
||||||
|
|
||||||
# NOTE: AMD64 can build within 2 minutes
|
# NOTE: AMD64 can build within 2 minutes
|
||||||
- name: 'Build images'
|
- name: 'Build images'
|
||||||
uses: docker/build-push-action@v6.15.0
|
uses: docker/build-push-action@v6.13.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).
|
||||||
|
|
|
@ -23,7 +23,7 @@ jobs:
|
||||||
|
|
||||||
- name: 'Prepare tags'
|
- name: 'Prepare tags'
|
||||||
id: prep
|
id: prep
|
||||||
uses: docker/metadata-action@v5.7.0
|
uses: docker/metadata-action@v5.6.1
|
||||||
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@v3.6.0
|
uses: docker/setup-qemu-action@v3.4.0
|
||||||
with:
|
with:
|
||||||
platforms: arm64
|
platforms: arm64
|
||||||
|
|
||||||
- name: 'Set up Docker Buildx'
|
- name: 'Set up Docker Buildx'
|
||||||
uses: docker/setup-buildx-action@v3.10.0
|
uses: docker/setup-buildx-action@v3.9.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`),
|
||||||
|
@ -67,7 +67,7 @@ jobs:
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: 'Build and publish images'
|
- name: 'Build and publish images'
|
||||||
uses: docker/build-push-action@v6.15.0
|
uses: docker/build-push-action@v6.13.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
build-args: |
|
build-args: |
|
||||||
|
|
|
@ -38,12 +38,12 @@ jobs:
|
||||||
# Ensures consistent BuildKit version (not coupled to Docker Engine),
|
# Ensures consistent BuildKit version (not coupled to Docker Engine),
|
||||||
# and increased compatibility of the build cache vs mixing buildx drivers.
|
# and increased compatibility of the build cache vs mixing buildx drivers.
|
||||||
- name: 'Set up Docker Buildx'
|
- name: 'Set up Docker Buildx'
|
||||||
uses: docker/setup-buildx-action@v3.10.0
|
uses: docker/setup-buildx-action@v3.9.0
|
||||||
|
|
||||||
# Importing from the cache should create the image within approx 30 seconds:
|
# Importing from the cache should create the image within approx 30 seconds:
|
||||||
# NOTE: `qemu` step is not needed as we only test for AMD64.
|
# NOTE: `qemu` step is not needed as we only test for AMD64.
|
||||||
- name: 'Build AMD64 image from cache'
|
- name: 'Build AMD64 image from cache'
|
||||||
uses: docker/build-push-action@v6.15.0
|
uses: docker/build-push-action@v6.13.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
tags: mailserver-testing:ci
|
tags: mailserver-testing:ci
|
||||||
|
|
|
@ -37,12 +37,12 @@ jobs:
|
||||||
# Ensures consistent BuildKit version (not coupled to Docker Engine),
|
# Ensures consistent BuildKit version (not coupled to Docker Engine),
|
||||||
# and increased compatibility of the build cache vs mixing buildx drivers.
|
# and increased compatibility of the build cache vs mixing buildx drivers.
|
||||||
- name: 'Set up Docker Buildx'
|
- name: 'Set up Docker Buildx'
|
||||||
uses: docker/setup-buildx-action@v3.10.0
|
uses: docker/setup-buildx-action@v3.9.0
|
||||||
|
|
||||||
# Importing from the cache should create the image within approx 30 seconds:
|
# Importing from the cache should create the image within approx 30 seconds:
|
||||||
# NOTE: `qemu` step is not needed as we only test for AMD64.
|
# NOTE: `qemu` step is not needed as we only test for AMD64.
|
||||||
- name: 'Build AMD64 image from cache'
|
- name: 'Build AMD64 image from cache'
|
||||||
uses: docker/build-push-action@v6.15.0
|
uses: docker/build-push-action@v6.13.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
tags: mailserver-testing:ci
|
tags: mailserver-testing:ci
|
||||||
|
|
|
@ -11,7 +11,7 @@ docker run \
|
||||||
--user "$(id -u):$(id -g)" \
|
--user "$(id -u):$(id -g)" \
|
||||||
--volume "./:/docs" \
|
--volume "./:/docs" \
|
||||||
--name "build-docs" \
|
--name "build-docs" \
|
||||||
squidfunk/mkdocs-material:9.6 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.
|
||||||
|
|
47
CHANGELOG.md
47
CHANGELOG.md
|
@ -2,49 +2,10 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased](https://github.com/docker-mailserver/docker-mailserver/compare/v15.0.2...HEAD)
|
## [Unreleased](https://github.com/docker-mailserver/docker-mailserver/compare/v14.0.0...HEAD)
|
||||||
|
|
||||||
> **Note**: Changes and additions listed here are contained in the `:edge` image tag. These changes may not be as stable as released changes.
|
> **Note**: Changes and additions listed here are contained in the `:edge` image tag. These changes may not be as stable as released changes.
|
||||||
|
|
||||||
### Updates
|
|
||||||
|
|
||||||
- **Documentation:**
|
|
||||||
- Added a compatibility note for a Dovecot + Solr 9.8 breaking change ([#4433](https://github.com/docker-mailserver/docker-mailserver/pull/4433))
|
|
||||||
- **Internal:**
|
|
||||||
- Refactored `setup config dkim` (`open-dkim`) ([#4375](https://github.com/docker-mailserver/docker-mailserver/pull/4375))
|
|
||||||
|
|
||||||
## [v15.0.2](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v15.0.2)
|
|
||||||
|
|
||||||
### Fixes
|
|
||||||
|
|
||||||
- **Postfix**
|
|
||||||
- Avoid modifying the message body when filtering sender headers. This regression was introduced from [#4120](https://github.com/docker-mailserver/docker-mailserver/pull/4120) as part of DMS v15.0.0 ([#4429](https://github.com/docker-mailserver/docker-mailserver/pull/4429))
|
|
||||||
|
|
||||||
## [v15.0.1](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v15.0.1)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- **Internal:**
|
|
||||||
- Added the Smallstep `step` CLI command for future internal usage ([#4376](https://github.com/docker-mailserver/docker-mailserver/pull/4376))
|
|
||||||
|
|
||||||
### Fixes
|
|
||||||
|
|
||||||
- **Postfix:**
|
|
||||||
- `setup email restrict` generated configs now only prepend to `dms_smtpd_sender_restrictions` ([#4379](https://github.com/docker-mailserver/docker-mailserver/pull/4379))
|
|
||||||
- **Rspamd:**
|
|
||||||
- Change detection support now monitors all files found within the DMS _Config Volume_ Rspamd directory ([#4418](https://github.com/docker-mailserver/docker-mailserver/pull/4418))
|
|
||||||
- **Internal:**
|
|
||||||
- A permissions fix for `/var/log/mail` that was [added in DMS v15]((https://github.com/docker-mailserver/docker-mailserver/pull/4374)) no longer encounters an error when no log files are present during a container restart, such as with a `tmpfs` volume mount ([#4391](https://github.com/docker-mailserver/docker-mailserver/pull/4391))
|
|
||||||
- The DMS _State Volume_ (`/var/mail-state`) will now ensure it's file tree is accessible for services when the volume was created with missing executable bit ([#4420](https://github.com/docker-mailserver/docker-mailserver/pull/4420))
|
|
||||||
- The DMS _Config Volume_ (`/tmp/docker-mailserver`) now correctly updates permissions on container restarts ([#4417](https://github.com/docker-mailserver/docker-mailserver/pull/4417))
|
|
||||||
|
|
||||||
### Updates
|
|
||||||
|
|
||||||
- **Internal:**
|
|
||||||
- Minor improvements to `_install_utils()` in `packages.sh` ([#4376](https://github.com/docker-mailserver/docker-mailserver/pull/4376))
|
|
||||||
|
|
||||||
## [v15.0.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v15.0.0)
|
|
||||||
|
|
||||||
### Breaking
|
### Breaking
|
||||||
|
|
||||||
- **saslauthd** mechanism support via ENV `SASLAUTHD_MECHANISMS` with `pam`, `shadow`, `mysql` values has been removed. Only `ldap` and `rimap` remain supported ([#4259](https://github.com/docker-mailserver/docker-mailserver/pull/4259))
|
- **saslauthd** mechanism support via ENV `SASLAUTHD_MECHANISMS` with `pam`, `shadow`, `mysql` values has been removed. Only `ldap` and `rimap` remain supported ([#4259](https://github.com/docker-mailserver/docker-mailserver/pull/4259))
|
||||||
|
@ -55,8 +16,6 @@ All notable changes to this project will be documented in this file. The format
|
||||||
- **DMS v14 mistakenly** relocated the _getmail state directory_ to the _DMS Config Volume_ as a `getmail/` subdirectory.
|
- **DMS v14 mistakenly** relocated the _getmail state directory_ to the _DMS Config Volume_ as a `getmail/` subdirectory.
|
||||||
- This has been corrected to `/var/lib/getmail` (_if you have mounted a DMS State Volume to `/var/mail-state`, `/var/lib/getmail` will be symlinked to `/var/mail-state/lib-getmail`_).
|
- This has been corrected to `/var/lib/getmail` (_if you have mounted a DMS State Volume to `/var/mail-state`, `/var/lib/getmail` will be symlinked to `/var/mail-state/lib-getmail`_).
|
||||||
- To preserve this state when upgrading to DMS v15, **you must manually migrate `getmail/` from the _DMS Config Volume_ to `lib-getmail/` in the _DMS State Volume_.**
|
- To preserve this state when upgrading to DMS v15, **you must manually migrate `getmail/` from the _DMS Config Volume_ to `lib-getmail/` in the _DMS State Volume_.**
|
||||||
- `setup email delete <EMAIL ADDRESS>` now requires explicit confirmation if the mailbox data should be deleted ([#4365](https://github.com/docker-mailserver/docker-mailserver/pull/4365)).
|
|
||||||
- **Rspamd:** Removed deprecated file path check (_DMS config volume: `./rspamd-modules.conf` => `./rspamd/custom-commands.conf`_) ([#4373](https://github.com/docker-mailserver/docker-mailserver/pull/4373))
|
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
@ -66,7 +25,7 @@ All notable changes to this project will be documented in this file. The format
|
||||||
|
|
||||||
### Updates
|
### Updates
|
||||||
|
|
||||||
- **Internal:**
|
**Internal:**
|
||||||
- **Removed `VERSION` file** from the repo. Releases of DMS prior to v13 (Nov 2023) would check this to detect new releases ([#3677](https://github.com/docker-mailserver/docker-mailserver/issues/3677), [#4321](https://github.com/docker-mailserver/docker-mailserver/pull/4321))
|
- **Removed `VERSION` file** from the repo. Releases of DMS prior to v13 (Nov 2023) would check this to detect new releases ([#3677](https://github.com/docker-mailserver/docker-mailserver/issues/3677), [#4321](https://github.com/docker-mailserver/docker-mailserver/pull/4321))
|
||||||
- During image build, ensure a secure connection when downloading the `fail2ban` package ([#4080](https://github.com/docker-mailserver/docker-mailserver/pull/4080))
|
- During image build, ensure a secure connection when downloading the `fail2ban` package ([#4080](https://github.com/docker-mailserver/docker-mailserver/pull/4080))
|
||||||
- **Documentation:**
|
- **Documentation:**
|
||||||
|
@ -99,7 +58,7 @@ All notable changes to this project will be documented in this file. The format
|
||||||
- The main `mail.log` (_which is piped to stdout via `tail`_) now correctly begins from the first log line of the active container run. Previously some daemon logs and potential warnings/errors were omitted ([#4146](https://github.com/docker-mailserver/docker-mailserver/pull/4146))
|
- The main `mail.log` (_which is piped to stdout via `tail`_) now correctly begins from the first log line of the active container run. Previously some daemon logs and potential warnings/errors were omitted ([#4146](https://github.com/docker-mailserver/docker-mailserver/pull/4146))
|
||||||
- `start-mailserver.sh` removed unused `shopt -s inherit_errexit` ([#4161](https://github.com/docker-mailserver/docker-mailserver/pull/4161))
|
- `start-mailserver.sh` removed unused `shopt -s inherit_errexit` ([#4161](https://github.com/docker-mailserver/docker-mailserver/pull/4161))
|
||||||
- Fixed a regression introduced in DMS v14 where `postfix-main.cf` appended `stderr` output into `/etc/postfix/main.cf`, causing Postfix startup to fail ([#4147](https://github.com/docker-mailserver/docker-mailserver/pull/4147))
|
- Fixed a regression introduced in DMS v14 where `postfix-main.cf` appended `stderr` output into `/etc/postfix/main.cf`, causing Postfix startup to fail ([#4147](https://github.com/docker-mailserver/docker-mailserver/pull/4147))
|
||||||
- Fixed a regression introduced in DMS v14 to better support running `start-mailserver.sh` with container restarts, which now only skip calling `_setup()` ([#4323](https://github.com/docker-mailserver/docker-mailserver/pull/4323#issuecomment-2629559254), [#4374](https://github.com/docker-mailserver/docker-mailserver/pull/4374))
|
- Fixed a regression introduced in DMS v14 to better support running `start-mailserver.sh` with container restarts, which now only skip calling `_setup()` ([#4323](https://github.com/docker-mailserver/docker-mailserver/pull/4323#issuecomment-2629559254))
|
||||||
- The command `swaks --help` is now functional ([#4282](https://github.com/docker-mailserver/docker-mailserver/pull/4282))
|
- The command `swaks --help` is now functional ([#4282](https://github.com/docker-mailserver/docker-mailserver/pull/4282))
|
||||||
- **Rspamd:**
|
- **Rspamd:**
|
||||||
- DKIM private key path checking is now performed only on paths that do not contain `$` ([#4201](https://github.com/docker-mailserver/docker-mailserver/pull/4201))
|
- DKIM private key path checking is now performed only on paths that do not contain `$` ([#4201](https://github.com/docker-mailserver/docker-mailserver/pull/4201))
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# Docs: https://docker-mailserver.github.io/docker-mailserver/v15.0/config/advanced/mail-fetchmail
|
# Docs: https://docker-mailserver.github.io/docker-mailserver/v14.0/config/advanced/mail-fetchmail
|
||||||
# Additional context, with CLI commands for verification:
|
# Additional context, with CLI commands for verification:
|
||||||
# https://github.com/orgs/docker-mailserver/discussions/3994#discussioncomment-9290570
|
# https://github.com/orgs/docker-mailserver/discussions/3994#discussioncomment-9290570
|
||||||
|
|
||||||
services:
|
services:
|
||||||
dms-fetch:
|
dms-fetch:
|
||||||
image: ghcr.io/docker-mailserver/docker-mailserver:latest # :15.0
|
image: ghcr.io/docker-mailserver/docker-mailserver:latest # :14.0
|
||||||
hostname: mail.example.test
|
hostname: mail.example.test
|
||||||
environment:
|
environment:
|
||||||
ENABLE_FETCHMAIL: 1
|
ENABLE_FETCHMAIL: 1
|
||||||
|
@ -26,7 +26,7 @@ services:
|
||||||
target: /tmp/docker-mailserver/fetchmail.cf
|
target: /tmp/docker-mailserver/fetchmail.cf
|
||||||
|
|
||||||
dms-remote:
|
dms-remote:
|
||||||
image: ghcr.io/docker-mailserver/docker-mailserver:latest # :15.0
|
image: ghcr.io/docker-mailserver/docker-mailserver:latest # :14.0
|
||||||
hostname: mail.remote.test
|
hostname: mail.remote.test
|
||||||
environment:
|
environment:
|
||||||
# Allows for us send a test mail easily by trusting any mail client run within this container (`swaks`):
|
# Allows for us send a test mail easily by trusting any mail client run within this container (`swaks`):
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Docs: https://docker-mailserver.github.io/docker-mailserver/v15.0/config/advanced/mail-forwarding/relay-hosts/
|
# Docs: https://docker-mailserver.github.io/docker-mailserver/v14.0/config/advanced/mail-forwarding/relay-hosts/
|
||||||
# Additional context, with CLI commands for verification:
|
# Additional context, with CLI commands for verification:
|
||||||
# https://github.com/docker-mailserver/docker-mailserver/issues/4136#issuecomment-2253693490
|
# https://github.com/docker-mailserver/docker-mailserver/issues/4136#issuecomment-2253693490
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# This would represent your actual DMS container:
|
# This would represent your actual DMS container:
|
||||||
dms-sender:
|
dms-sender:
|
||||||
image: mailserver/docker-mailserver:latest # :15.0
|
image: mailserver/docker-mailserver:latest # :14.0
|
||||||
hostname: mail.example.test
|
hostname: mail.example.test
|
||||||
environment:
|
environment:
|
||||||
# All outbound mail will be relayed through this host
|
# All outbound mail will be relayed through this host
|
||||||
|
@ -37,7 +37,7 @@ services:
|
||||||
|
|
||||||
# Pretend this is your third-party relay service:
|
# Pretend this is your third-party relay service:
|
||||||
dms-relay:
|
dms-relay:
|
||||||
image: mailserver/docker-mailserver:latest # :15.0
|
image: mailserver/docker-mailserver:latest # :14.0
|
||||||
hostname: smtp.relay-service.test
|
hostname: smtp.relay-service.test
|
||||||
environment:
|
environment:
|
||||||
# WORKAROUND: Bypass security checks from the mail-client (dms-sender container)
|
# WORKAROUND: Bypass security checks from the mail-client (dms-sender container)
|
||||||
|
@ -58,7 +58,7 @@ services:
|
||||||
|
|
||||||
# Pretend this is another mail server that your target recipient belongs to (like Gmail):
|
# Pretend this is another mail server that your target recipient belongs to (like Gmail):
|
||||||
dms-destination:
|
dms-destination:
|
||||||
image: mailserver/docker-mailserver:latest # :15.0
|
image: mailserver/docker-mailserver:latest # :14.0
|
||||||
hostname: mail.destination.test
|
hostname: mail.destination.test
|
||||||
# WORKAROUND: dms-relay must be able to resolve DNS for `@destination.test` to the IP of this container:
|
# WORKAROUND: dms-relay must be able to resolve DNS for `@destination.test` to the IP of this container:
|
||||||
# Normally a MX record would direct mail to the MTA (eg: `mail.destination.test`)
|
# Normally a MX record would direct mail to the MTA (eg: `mail.destination.test`)
|
||||||
|
|
|
@ -151,6 +151,6 @@ We provide this support via two config files:
|
||||||
[wikipedia::smarthost]: https://en.wikipedia.org/wiki/Smart_host
|
[wikipedia::smarthost]: https://en.wikipedia.org/wiki/Smart_host
|
||||||
|
|
||||||
[docs::env-relay]: ../../environment.md#relay-host
|
[docs::env-relay]: ../../environment.md#relay-host
|
||||||
[dms-repo::helpers-relay]: https://github.com/docker-mailserver/docker-mailserver/blob/v15.0.0/target/scripts/helpers/relay.sh
|
[dms-repo::helpers-relay]: https://github.com/docker-mailserver/docker-mailserver/blob/v14.0.0/target/scripts/helpers/relay.sh
|
||||||
[dms-gh::pr-3607]: https://github.com/docker-mailserver/docker-mailserver/issues/3607
|
[dms-gh::pr-3607]: https://github.com/docker-mailserver/docker-mailserver/issues/3607
|
||||||
[dms-gh::relay-example]: https://github.com/docker-mailserver/docker-mailserver/issues/3842#issuecomment-1913380639
|
[dms-gh::relay-example]: https://github.com/docker-mailserver/docker-mailserver/issues/3842#issuecomment-1913380639
|
||||||
|
|
|
@ -54,7 +54,7 @@ You'll need to repeat this process if you add any new domains.
|
||||||
|
|
||||||
You should have:
|
You should have:
|
||||||
|
|
||||||
- At least one [email account setup][docs-accounts]
|
- At least one [email account setup][docs-accounts-add]
|
||||||
- Attached a [volume for config][docs-volumes-config] to persist the generated files to local storage
|
- Attached a [volume for config][docs-volumes-config] to persist the generated files to local storage
|
||||||
|
|
||||||
!!! example "Creating DKIM Keys"
|
!!! example "Creating DKIM Keys"
|
||||||
|
|
|
@ -111,7 +111,7 @@ This could be from outdated software, or running a system that isn't able to pro
|
||||||
- **Container runtime:** Docker and Podman for example have subtle differences. DMS docs are primarily focused on Docker, but we try to document known issues where relevant.
|
- **Container runtime:** Docker and Podman for example have subtle differences. DMS docs are primarily focused on Docker, but we try to document known issues where relevant.
|
||||||
- **Rootless containers:** Introduces additional differences in behavior or requirements:
|
- **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::fail2ban::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].
|
||||||
|
|
||||||
[network::docker-userlandproxy]: https://github.com/moby/moby/issues/44721
|
[network::docker-userlandproxy]: https://github.com/moby/moby/issues/44721
|
||||||
[network::docker-nftables]: https://github.com/moby/moby/issues/26824
|
[network::docker-nftables]: https://github.com/moby/moby/issues/26824
|
||||||
|
|
|
@ -202,7 +202,7 @@ Please read [the SSL page in the documentation][docs-tls] for more information.
|
||||||
Configures the handling of creating mails with forged sender addresses.
|
Configures the handling of creating mails with forged sender addresses.
|
||||||
|
|
||||||
- **0** => (not recommended) Mail address spoofing allowed. Any logged in user may create email messages with a [forged sender address](https://en.wikipedia.org/wiki/Email_spoofing).
|
- **0** => (not recommended) Mail address spoofing allowed. Any logged in user may create email messages with a [forged sender address](https://en.wikipedia.org/wiki/Email_spoofing).
|
||||||
- 1 => Mail spoofing denied. Each user may only send with their own or their alias addresses. Addresses with [extension delimiters](http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
|
- 1 => Mail spoofing denied. Each user may only send with his own or his alias addresses. Addresses with [extension delimiters](http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
|
||||||
|
|
||||||
##### ENABLE_SRS
|
##### ENABLE_SRS
|
||||||
|
|
||||||
|
|
|
@ -14,48 +14,18 @@ hide:
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Enabling Fail2Ban support can be done via ENV, but also requires granting at least the `NET_ADMIN` capability to interact with the kernel and ban IP addresses.
|
!!! warning
|
||||||
|
|
||||||
!!! example
|
DMS must be launched with the `NET_ADMIN` capability in order to be able to install the NFTables rules that actually ban IP addresses. Thus, either include `--cap-add=NET_ADMIN` in the `docker run` command, or the equivalent in the `compose.yaml`:
|
||||||
|
|
||||||
=== "Docker Compose"
|
```yaml
|
||||||
|
cap_add:
|
||||||
```yaml title="compose.yaml"
|
- NET_ADMIN
|
||||||
services:
|
```
|
||||||
mailserver:
|
|
||||||
environment:
|
|
||||||
- ENABLE_FAIL2BAN=1
|
|
||||||
cap_add:
|
|
||||||
- NET_ADMIN
|
|
||||||
```
|
|
||||||
|
|
||||||
=== "Docker CLI"
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run --rm -it \
|
|
||||||
--cap-add=NET_ADMIN \
|
|
||||||
--env ENABLE_FAIL2BAN=1
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! warning "Security risk of adding non-default capabilties"
|
|
||||||
|
|
||||||
DMS bundles F2B into the image for convenience to simplify integration and deployment.
|
|
||||||
|
|
||||||
The [`NET_ADMIN`][security::cap-net-admin] and [`NET_RAW`][security::cap-net-raw] capabilities are not granted by default to the container root user, as they can be used to compromise security.
|
|
||||||
|
|
||||||
If this risk concerns you, it may be wiser to instead prefer only granting these capabilities to a dedicated Fail2Ban container ([example][lsio:f2b-image]).
|
|
||||||
|
|
||||||
!!! bug "Running Fail2Ban on Older Kernels"
|
!!! bug "Running Fail2Ban on Older Kernels"
|
||||||
|
|
||||||
DMS configures F2B to use [NFTables][network::nftables], not [IPTables (legacy)][network::iptables-legacy].
|
DMS configures F2B to use NFTables, not IPTables (legacy). We have observed that older systems, for example NAS systems, do not support the modern NFTables rules. You will need to configure F2B to use legacy IPTables again, for example with the [``fail2ban-jail.cf``][github-file-f2bjail], see the [section on configuration further down below](#custom-files).
|
||||||
|
|
||||||
We have observed that older systems (for example NAS systems), do not support the modern NFTables rules. You will need to configure F2B to use legacy IPTables again, for example with the [`fail2ban-jail.cf`][github-file-f2bjail], see the [section on configuration further down below](#custom-files).
|
|
||||||
|
|
||||||
[security::cap-net-admin]: https://0xn3va.gitbook.io/cheat-sheets/container/escaping/excessive-capabilities#cap_net_admin
|
|
||||||
[security::cap-net-raw]: https://0xn3va.gitbook.io/cheat-sheets/container/escaping/excessive-capabilities#cap_net_raw
|
|
||||||
[lsio:f2b-image]: https://docs.linuxserver.io/images/docker-fail2ban
|
|
||||||
[network::nftables]: https://en.wikipedia.org/wiki/Nftables
|
|
||||||
[network::iptables-legacy]: https://developers.redhat.com/blog/2020/08/18/iptables-the-two-variants-and-their-relationship-with-nftables#two_variants_of_the_iptables_command
|
|
||||||
|
|
||||||
### DMS Defaults
|
### DMS Defaults
|
||||||
|
|
||||||
|
|
|
@ -6,60 +6,34 @@ title: 'Security | Rspamd'
|
||||||
|
|
||||||
Rspamd is a ["fast, free and open-source spam filtering system"][rspamd-web]. DMS integrates Rspamd like any other service. We provide a basic but easy to maintain setup of Rspamd.
|
Rspamd is a ["fast, free and open-source spam filtering system"][rspamd-web]. DMS integrates Rspamd like any other service. We provide a basic but easy to maintain setup of Rspamd.
|
||||||
|
|
||||||
If you want to take a look at the default configuration files for Rspamd that DMS adds, navigate to [`target/rspamd/` inside the repository][dms-repo::default-rspamd-configuration]. Please consult the [section "The Default Configuration"](#the-default-configuration) section down below for a written overview.
|
If you want to take a look at the default configuration files for Rspamd that DMS packs, navigate to [`target/rspamd/` inside the repository][dms-repo::default-rspamd-configuration]. Please consult the [section "The Default Configuration"](#the-default-configuration) section down below for a written overview.
|
||||||
|
|
||||||
### Enable Rspamd
|
## Related Environment Variables
|
||||||
|
|
||||||
Rspamd is presently opt-in for DMS, but intended to become the default anti-spam service in a future release.
|
The following environment variables are related to Rspamd:
|
||||||
|
|
||||||
DMS offers two anti-spam solutions:
|
1. [`ENABLE_RSPAMD`](../environment.md#enable_rspamd)
|
||||||
|
2. [`ENABLE_RSPAMD_REDIS`](../environment.md#enable_rspamd_redis)
|
||||||
|
3. [`RSPAMD_CHECK_AUTHENTICATED`](../environment.md#rspamd_check_authenticated)
|
||||||
|
4. [`RSPAMD_GREYLISTING`](../environment.md#rspamd_greylisting)
|
||||||
|
5. [`RSPAMD_HFILTER`](../environment.md#rspamd_hfilter)
|
||||||
|
6. [`RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE`](../environment.md#rspamd_hfilter_hostname_unknown_score)
|
||||||
|
7. [`RSPAMD_LEARN`](../environment.md#rspamd_learn)
|
||||||
|
8. [`SPAM_SUBJECT`](../environment.md#spam_subject)
|
||||||
|
9. [`MOVE_SPAM_TO_JUNK`][docs::spam-to-junk]
|
||||||
|
10. [`MARK_SPAM_AS_READ`](../environment.md#mark_spam_as_read)
|
||||||
|
|
||||||
- Legacy (_Amavis, SpamAssassin, OpenDKIM, OpenDMARC_)
|
With these variables, you can enable Rspamd itself, and you can enable / disable certain features related to Rspamd.
|
||||||
- Rspamd (_Provides equivalent features of software from the legacy solution_)
|
|
||||||
|
|
||||||
While you could configure Rspamd to only replace some of the legacy services, it is advised to only use Rspamd with the legacy services disabled.
|
## The Default Configuration
|
||||||
|
|
||||||
!!! example "Switch to Rspamd"
|
### Other Anti-Spam-Services
|
||||||
|
|
||||||
To use Rspamd add the following ENV config changes:
|
DMS packs other anti-spam services, like SpamAssassin or Amavis, next to Rspamd. There exist services, like ClamAV (`ENABLE_CLAMAV`), that Rspamd can utilize to improve the scanning. Except for ClamAV, we recommend disabling **all other** anti-spam services when using Rspamd. The [basic configuration shown below](#a-very-basic-configuration) provides a good starting point.
|
||||||
|
|
||||||
```env
|
|
||||||
ENABLE_RSPAMD=1
|
|
||||||
|
|
||||||
# Rspamd replaces the functionality of all these anti-spam services, disable them:
|
|
||||||
ENABLE_OPENDKIM=0
|
|
||||||
ENABLE_OPENDMARC=0
|
|
||||||
ENABLE_POLICYD_SPF=0
|
|
||||||
ENABLE_AMAVIS=0
|
|
||||||
ENABLE_SPAMASSASSIN=0
|
|
||||||
# Greylisting is opt-in, if you had enabled Postgrey switch to the Rspamd equivalent:
|
|
||||||
ENABLE_POSTGREY=0
|
|
||||||
RSPAMD_GREYLISTING=1
|
|
||||||
|
|
||||||
# Optional: Add anti-virus support with ClamAV (compatible with Rspamd):
|
|
||||||
ENABLE_CLAMAV=1
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! info "Relevant Environment Variables"
|
|
||||||
|
|
||||||
The following environment variables are related to Rspamd:
|
|
||||||
|
|
||||||
1. [`ENABLE_RSPAMD`](../environment.md#enable_rspamd)
|
|
||||||
2. [`ENABLE_RSPAMD_REDIS`](../environment.md#enable_rspamd_redis)
|
|
||||||
3. [`RSPAMD_CHECK_AUTHENTICATED`](../environment.md#rspamd_check_authenticated)
|
|
||||||
4. [`RSPAMD_GREYLISTING`](../environment.md#rspamd_greylisting)
|
|
||||||
5. [`RSPAMD_HFILTER`](../environment.md#rspamd_hfilter)
|
|
||||||
6. [`RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE`](../environment.md#rspamd_hfilter_hostname_unknown_score)
|
|
||||||
7. [`RSPAMD_LEARN`](../environment.md#rspamd_learn)
|
|
||||||
8. [`SPAM_SUBJECT`](../environment.md#spam_subject)
|
|
||||||
9. [`MOVE_SPAM_TO_JUNK`][docs::spam-to-junk]
|
|
||||||
10. [`MARK_SPAM_AS_READ`](../environment.md#mark_spam_as_read)
|
|
||||||
|
|
||||||
## Overview of Rspamd support
|
|
||||||
|
|
||||||
### Mode of Operation
|
### Mode of Operation
|
||||||
|
|
||||||
!!! note "Attention"
|
!!! 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).
|
Read this section carefully if you want to understand how Rspamd is integrated into DMS and how it works (on a surface level).
|
||||||
|
|
||||||
|
@ -105,16 +79,13 @@ DMS does not set a default password for the controller worker. You may want to d
|
||||||
When Rspamd is enabled, we implicitly also start an instance of Redis in the container:
|
When Rspamd is enabled, we implicitly also start an instance of Redis in the container:
|
||||||
|
|
||||||
- Redis is configured to persist its data via RDB snapshots to disk in the directory `/var/lib/redis` (_or the [`/var/mail-state/`][docs::dms-volumes-state] volume when present_).
|
- Redis is configured to persist its data via RDB snapshots to disk in the directory `/var/lib/redis` (_or the [`/var/mail-state/`][docs::dms-volumes-state] volume when present_).
|
||||||
- With the volume mount, the snapshot will restore the Redis data across container updates, and provide a way to keep a backup.
|
- With the volume mount, the snapshot will restore the Redis data across container restarts, and provide a way to keep backup.
|
||||||
- Without a volume mount a containers internal state will persist across restarts until the container is recreated due to changes like ENV or upgrading the image for the container.
|
|
||||||
|
|
||||||
Redis uses `/etc/redis/redis.conf` for configuration:
|
Redis uses `/etc/redis/redis.conf` for configuration:
|
||||||
|
|
||||||
- We adjust this file when enabling the internal Redis service.
|
- We adjust this file when enabling the internal Redis service.
|
||||||
- If you have an external instance of Redis to use, the internal Redis service can be opt-out via setting the ENV [`ENABLE_RSPAMD_REDIS=0`][docs::env::enable-redis] (_link also details required changes to the DMS Rspamd config_).
|
- If you have an external instance of Redis to use, the internal Redis service can be opt-out via setting the ENV [`ENABLE_RSPAMD_REDIS=0`][docs::env::enable-redis] (_link also details required changes to the DMS Rspamd config_).
|
||||||
|
|
||||||
If you are interested in using Valkey instead of Redis, please refer to [this guidance][gh-dms::guide::valkey].
|
|
||||||
|
|
||||||
### Web Interface
|
### Web Interface
|
||||||
|
|
||||||
Rspamd provides a [web interface][rspamd-docs::web-ui], which contains statistics and data Rspamd collects. The interface is enabled by default and reachable on port 11334.
|
Rspamd provides a [web interface][rspamd-docs::web-ui], which contains statistics and data Rspamd collects. The interface is enabled by default and reachable on port 11334.
|
||||||
|
@ -125,7 +96,7 @@ To use the web interface you will need to configure a password, [otherwise you w
|
||||||
|
|
||||||
??? example "Set a custom password"
|
??? example "Set a custom password"
|
||||||
|
|
||||||
Add this line to [your Rspamd `custom-commands.conf` config](#with-the-help-of-a-custom-file) which sets the `password` option of the _controller worker_:
|
Add this line to [your rspamd `custom-commands.conf` config](#with-the-help-of-a-custom-file) which sets the `password` option of the _controller worker_:
|
||||||
|
|
||||||
```
|
```
|
||||||
set-option-for-controller password "your hashed password here"
|
set-option-for-controller password "your hashed password here"
|
||||||
|
@ -137,13 +108,9 @@ To use the web interface you will need to configure a password, [otherwise you w
|
||||||
docker exec -it <CONTAINER_NAME> rspamadm pw
|
docker exec -it <CONTAINER_NAME> rspamadm pw
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Related:** A minimal Rspamd `compose.yaml` [example with a reverse-proxy for web access][gh-dms::guide::rspamd-web].
|
|
||||||
|
|
||||||
### DNS
|
### DNS
|
||||||
|
|
||||||
DMS does not supply custom values for DNS servers (to Rspamd). If you need to use custom DNS servers, which could be required when using [DNS-based deny/allowlists](#rbls-real-time-blacklists-dnsbls-dns-based-blacklists), you need to adjust [`options.inc`][rspamd-docs::config::global] yourself. Make sure to also read our [FAQ page on DNS servers][docs::faq::dns-servers].
|
DMS does not supply custom values for DNS servers (to Rspamd). If you need to use custom DNS servers, which could be required when using [DNS-based deny/allowlists](#rbls-real-time-blacklists-dnsbls-dns-based-blacklists), you need to adjust [`options.inc`][rspamd-docs::basic-options] yourself. Make sure to also read our [FAQ page on DNS servers][docs::faq::dns-servers].
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
|
|
||||||
|
@ -175,7 +142,7 @@ You can choose to enable ClamAV, and Rspamd will then use it to check for viruse
|
||||||
|
|
||||||
#### RBLs (Real-time Blacklists) / DNSBLs (DNS-based Blacklists)
|
#### RBLs (Real-time Blacklists) / DNSBLs (DNS-based Blacklists)
|
||||||
|
|
||||||
The [RBL module][rspamd-docs::modules::rbl] is enabled by default. As a consequence, Rspamd will perform DNS lookups to various blacklists. Whether an RBL or a DNSBL is queried depends on where the domain name was obtained: RBL servers are queried with IP addresses extracted from message headers, DNSBL server are queried with domains and IP addresses extracted from the message body ([source][www::rbl-vs-dnsbl]).
|
The [RBL module][rspamd-docs::modules::rbl] is enabled by default. As a consequence, Rspamd will perform DNS lookups to various blacklists. Whether an RBL or a DNSBL is queried depends on where the domain name was obtained: RBL servers are queried with IP addresses extracted from message headers, DNSBL server are queried with domains and IP addresses extracted from the message body \[[source][www::rbl-vs-dnsbl]\].
|
||||||
|
|
||||||
!!! danger "Rspamd and DNS Block Lists"
|
!!! danger "Rspamd and DNS Block Lists"
|
||||||
|
|
||||||
|
@ -185,141 +152,124 @@ The [RBL module][rspamd-docs::modules::rbl] is enabled by default. As a conseque
|
||||||
|
|
||||||
## Providing Custom Settings & Overriding Settings
|
## Providing Custom Settings & Overriding Settings
|
||||||
|
|
||||||
!!! info "Rspamd config overriding precedence"
|
DMS brings sane default settings for Rspamd. They are located at `/etc/rspamd/local.d/` inside the container (or `target/rspamd/local.d/` in the repository).
|
||||||
|
|
||||||
Rspamd has a layered approach for configuration with [`local.d` and `override.d` config directories][rspamd-docs::config-directories].
|
### Manually
|
||||||
|
|
||||||
- DMS [extends the Rspamd default configs via `/etc/rspamd/local.d/`][dms-repo::default-rspamd-configuration].
|
!!! question "What is [`docker-data/dms/config/`][docs::dms-volumes-config]?"
|
||||||
- User config changes should be handled separately as overrides via the [DMS Config Volume][docs::dms-volumes-config] (`docker-data/dms/config/`) with either:
|
|
||||||
- `./rspamd/override.d/` - Config files placed here are copied to `/etc/rspamd/override.d/` during container startup.
|
|
||||||
- [`./rspamd/custom-commands.conf`](#with-the-help-of-a-custom-file) - Applied after copying any provided configs from `rspamd/override.d/` (DMS Config volume) to `/etc/rspamd/override.d/`.
|
|
||||||
|
|
||||||
!!! abstract "Reference docs for Rspamd config"
|
If you want to overwrite the default settings or provide your 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.
|
||||||
|
|
||||||
- [Config Overview][rspamd-docs::config::overview], [Quickstart guide][rspamd-docs::config::quickstart], and [Config Syntax (UCL)][rspamd-docs::config::ucl-syntax]
|
!!! question "What is the [`local.d` directory and how does it compare to `override.d`][rspamd-docs::config-directories]?"
|
||||||
- Global Options ([`options.inc`][rspamd-docs::config::global])
|
|
||||||
- [Workers][rspamd-docs::config::workers] ([`worker-controller.inc`][rspamd-docs::config::worker-controller], [`worker-proxy.inc`][rspamd-docs::config::worker-proxy])
|
|
||||||
- [Modules][rspamd-docs::modules] (_view each module page for their specific config options_)
|
|
||||||
|
|
||||||
!!! tip "View rendered config"
|
!!! warning "Clashing Overrides"
|
||||||
|
|
||||||
`rspamadm configdump` will output the full rspamd configuration that is used should you need it for troubleshooting / inspection.
|
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.
|
||||||
|
|
||||||
- You can also see which modules are enabled / disabled via `rspamadm configdump --modules-state`
|
### With the Help of a Custom File
|
||||||
- Specific config sections like `dkim` or `worker` can also be used to filter the output to just those sections: `rspamadm configdump dkim worker`
|
|
||||||
- Use `--show-help` to include inline documentation for many settings.
|
|
||||||
|
|
||||||
### Using `custom-commands.conf` { #with-the-help-of-a-custom-file }
|
DMS provides the ability to do simple adjustments to Rspamd modules with the help of a single file. Just place a file called `custom-commands.conf` into `docker-data/dms/config/rspamd/`. If this file is present, DMS will evaluate it. The structure is simple, as each line in the file looks like this:
|
||||||
|
|
||||||
For convenience DMS provides a single config file that will directly create or modify multiple configs at `/etc/rspamd/override.d/`. This is handled as the final rspamd configuration step during container startup.
|
```txt
|
||||||
|
COMMAND ARGUMENT1 ARGUMENT2 ARGUMENT3
|
||||||
|
```
|
||||||
|
|
||||||
DMS will apply this config when you provide `rspamd/custom-commands.conf` in your DMS Config volume. Configure it with directive lines as documented below.
|
where `COMMAND` can be:
|
||||||
|
|
||||||
!!! note "Only use this feature for `option = value` changes"
|
1. `disable-module`: disables the module with name `ARGUMENT1`
|
||||||
|
2. `enable-module`: explicitly enables the module with name `ARGUMENT1`
|
||||||
|
3. `set-option-for-module`: sets the value for option `ARGUMENT2` to `ARGUMENT3` inside module `ARGUMENT1`
|
||||||
|
4. `set-option-for-controller`: set the value of option `ARGUMENT1` to `ARGUMENT2` for the controller worker
|
||||||
|
5. `set-option-for-proxy`: set the value of option `ARGUMENT1` to `ARGUMENT2` for the proxy worker
|
||||||
|
6. `set-common-option`: set the option `ARGUMENT1` that [defines basic Rspamd behavior][rspamd-docs::basic-options] to value `ARGUMENT2`
|
||||||
|
7. `add-line`: this will add the complete line after `ARGUMENT1` (with all characters) to the file `/etc/rspamd/override.d/<ARGUMENT1>`
|
||||||
|
|
||||||
`custom-commands.conf` is only suitable for adding or replacing simple `option = value` settings for configs at `/etc/rspamd/override.d/`.
|
!!! example "An Example Is [Shown Down Below](#adjusting-and-extending-the-very-basic-configuration)"
|
||||||
|
|
||||||
- New settings are appended to the associated config file.
|
|
||||||
- When replacing an existing setting in an override config, that setting may be any matching line (_allowing for nested scopes, instead of only top-level keys_).
|
|
||||||
|
|
||||||
Any changes involving more advanced [UCL config syntax][rspamd-docs::config::ucl-syntax] should instead add UCL config files directly to `rspamd/override.d/` (_in the DMS Config volume_).
|
|
||||||
|
|
||||||
!!! info "`custom-commands.conf` syntax"
|
!!! note "File Names & Extensions"
|
||||||
|
|
||||||
There are 7 directives available to manage custom Rspamd configurations. Add these directive lines into `custom-commands.conf`, they will be processed sequentially.
|
For command 1 - 3, we append the `.conf` suffix to the module name to get the correct file name automatically. For commands 4 - 6, the file name is fixed (you don't even need to provide it). For command 7, you will need to provide the whole file name (including the suffix) yourself!
|
||||||
|
|
||||||
**Directives:**
|
You can also have comments (the line starts with `#`) and blank lines in `custom-commands.conf` - they are properly handled and not evaluated.
|
||||||
|
|
||||||
```txt
|
!!! tip "Adjusting Modules This Way"
|
||||||
# For /etc/rspamd/override.d/{options.inc,worker-controller.inc,worker-proxy}.inc
|
|
||||||
set-common-option <OPTION NAME> <OPTION VALUE>
|
|
||||||
set-option-for-controller <OPTION NAME> <OPTION VALUE>
|
|
||||||
set-option-for-proxy <OPTION NAME> <OPTION VALUE>
|
|
||||||
|
|
||||||
# For /etc/rspamd/override.d/<MODULE NAME>.conf
|
These simple commands are meant to give users the ability to _easily_ alter modules and their options. As a consequence, they are not powerful enough to enable multi-line adjustments. If you need to do something more complex, we advise to do that [manually](#manually)!
|
||||||
enable-module <MODULE NAME>
|
|
||||||
disable-module <MODULE NAME>
|
|
||||||
set-option-for-module <MODULE NAME> <OPTION NAME> <OPTION VALUE>
|
|
||||||
|
|
||||||
# For /etc/rspamd/override.d/<FILENAME>
|
## Examples & Advanced Configuration
|
||||||
add-line <FILENAME> <CONTENT>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Syntax:**
|
### A Very Basic Configuration
|
||||||
|
|
||||||
- Blank lines are ok.
|
Do you want to start using Rspamd? Rspamd is disabled by default, so you need to set the following environment variables:
|
||||||
- `#` at the start of a line represents a comment for adding notes.
|
|
||||||
- `<OPTION VALUE>` and `<CONTENT>` will contain the remaining content of their line, any preceding inputs are delimited by white-space.
|
|
||||||
|
|
||||||
---
|
```env
|
||||||
|
ENABLE_RSPAMD=1
|
||||||
|
# ClamAV is compatible with Rspamd. Optionally enable it for anti-virus support:
|
||||||
|
ENABLE_CLAMAV=1
|
||||||
|
|
||||||
??? note "`<MODULE NAME>` can also target non-module configs"
|
# Rspamd replaces the functionality of all these anti-spam services, disable them:
|
||||||
|
ENABLE_OPENDKIM=0
|
||||||
|
ENABLE_OPENDMARC=0
|
||||||
|
ENABLE_POLICYD_SPF=0
|
||||||
|
ENABLE_AMAVIS=0
|
||||||
|
ENABLE_SPAMASSASSIN=0
|
||||||
|
|
||||||
An example is the `statistics` module, which has config to import a separate file (`classifier-bayes.conf`) for easier overrides to this section of the module config.
|
# Provided you've set `RSPAMD_GREYLISTING=1`, also disable Postgrey:
|
||||||
|
ENABLE_POSTGREY=0
|
||||||
|
```
|
||||||
|
|
||||||
??? example
|
This will enable Rspamd and disable services you don't need when using Rspamd.
|
||||||
|
|
||||||
```conf title="rspamd/custom-commands.conf"
|
### Adjusting and Extending The Very Basic Configuration
|
||||||
# If you're confident you've properly secured access to the rspamd web service/API (Default port: 11334)
|
|
||||||
# with your own auth layer (eg: reverse-proxy) you can bypass rspamd requiring credentials:
|
Rspamd is running, but you want or need to adjust it? First, create a file named `custom-commands.conf` under `docker-data/dms/config/rspamd` (which translates to `/tmp/docker-mailserver/rspamd/` inside the container). Then add your changes:
|
||||||
# https://rspamd.com/doc/workers/controller.html#controller-configuration
|
|
||||||
|
1. Say you want to be able to easily look at the frontend Rspamd provides on port 11334 (default) without the need to enter a password (maybe because you already provide authorization and authentication). You will have to adjust the controller worker: `set-option-for-controller secure_ip "0.0.0.0/0"`.
|
||||||
|
2. Do you additionally want to enable the auto-spam-learning for the Bayes module? No problem: `set-option-for-module classifier-bayes autolearn true`.
|
||||||
|
3. But the chartable module gets on your nerves? Easy: `disable-module chartable`.
|
||||||
|
|
||||||
|
??? example "What Does the Result Look Like?"
|
||||||
|
Here is what the file looks like in the end:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# See 1.
|
||||||
|
# ATTENTION: this disables authentication on the website - make sure you know what you're doing!
|
||||||
set-option-for-controller secure_ip "0.0.0.0/0"
|
set-option-for-controller secure_ip "0.0.0.0/0"
|
||||||
|
|
||||||
# Some settings aren't documented well, you may find them in snippets or Rspamds default config files:
|
# See 2.
|
||||||
# https://rspamd.com/doc/tutorials/quickstart.html#using-of-milter-protocol-for-rspamd--16
|
|
||||||
# /etc/rspamd/worker-proxy.inc
|
|
||||||
set-option-for-proxy reject_message "Rejected - Detected as spam"
|
|
||||||
|
|
||||||
# Equivalent to the previous example, but `add-line` is more verbose:
|
|
||||||
add-line worker-proxy.inc reject_message = "Rejected - Detected as spam"
|
|
||||||
|
|
||||||
# Enable Bayes auto-learning feature to classify spam based on Rspamd action/score results:
|
|
||||||
# NOTE: The statistics module imports a separate file for classifier-bayes config
|
|
||||||
# https://rspamd.com/doc/configuration/statistic.html#autolearning
|
|
||||||
set-option-for-module classifier-bayes autolearn true
|
set-option-for-module classifier-bayes autolearn true
|
||||||
|
|
||||||
# Disable the `chartable` module:
|
# See 3.
|
||||||
# https://rspamd.com/doc/modules/chartable.html
|
|
||||||
disable-module chartable
|
disable-module chartable
|
||||||
```
|
```
|
||||||
|
|
||||||
## Advanced Configuration
|
|
||||||
|
|
||||||
### DKIM Signing
|
### DKIM Signing
|
||||||
|
|
||||||
There is a dedicated [section for setting up DKIM with Rspamd in our documentation][docs::dkim-with-rspamd].
|
There is a dedicated [section for setting up DKIM with Rspamd in our documentation][docs::dkim-with-rspamd].
|
||||||
|
|
||||||
### ARC (Authenticated Received Chain)
|
### ARC (Authenticated Received Chain)
|
||||||
|
|
||||||
[ARC][wikipedia::arc] support in DMS is opt-in via config file. [Enable the ARC Rspamd module][rspamd-docs::arc] by creating a config file at `docker-data/dms/config/rspamd/override.d/arc.conf`.
|
ARC is not set up by default, but you can easily enable it by adding a file called `arc.conf` to `docker-data/dms/config/rspamd/override.d/`. ARC can use DKIM keys that you should have already created. The configuration file could then contain the following:
|
||||||
|
|
||||||
!!! example
|
```conf
|
||||||
|
sign_local = true;
|
||||||
|
sign_authenticated = true;
|
||||||
|
|
||||||
For each mail domain you have DMS manage, add the equivalent `example.com` sub-section to `domain` and adjust the `path` + `selector` fields as necessary.
|
domain {
|
||||||
|
<DOMAIN NAME> {
|
||||||
```conf title="rspamd/override.d/arc.conf"
|
# Change the path here to your actual private key
|
||||||
sign_local = true;
|
path = "/tmp/docker-mailserver/rspamd/dkim/rsa-2048-mail-<DOMAIN NAME>.private.txt";
|
||||||
sign_authenticated = true;
|
# Changhe the selected if you chose a non-default one
|
||||||
|
selector = "mail";
|
||||||
domain {
|
|
||||||
example.com {
|
|
||||||
path = "/tmp/docker-mailserver/rspamd/dkim/rsa-2048-mail-example.private.txt";
|
|
||||||
selector = "mail";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
}
|
||||||
|
```
|
||||||
!!! tip "Using a common keypair"
|
|
||||||
|
|
||||||
As with DKIM, the keypair can be shared across your configured domains.
|
|
||||||
|
|
||||||
Your ARC config can share the same DKIM private key + selector (_with associated DNS record for the public key_).
|
|
||||||
|
|
||||||
### _Abusix_ Integration
|
### _Abusix_ Integration
|
||||||
|
|
||||||
This subsection provides information about the integration of [Abusix][abusix-web], "a set of blocklists that work as an additional email security layer for your existing mail environment". The setup is straight-forward and well documented:
|
This subsection provides information about the integration of [Abusix][abusix-web], "a set of blocklists that work as an additional email security layer for your existing mail environment". The setup is straight-forward and well documented:
|
||||||
|
|
||||||
1. [Create an account][abusix-web::register]
|
1. [Create an account](https://app.abusix.com/)
|
||||||
2. Retrieve your API key
|
2. Retrieve your API key
|
||||||
3. Navigate to the ["Getting Started" documentation for Rspamd][abusix-docs::rspamd-integration] and follow the steps described there
|
3. Navigate to the ["Getting Started" documentation for Rspamd][abusix-docs::rspamd-integration] and follow the steps described there
|
||||||
4. Make sure to change `<APIKEY>` to your private API key
|
4. Make sure to change `<APIKEY>` to your private API key
|
||||||
|
@ -335,28 +285,17 @@ While _Abusix_ can be integrated into Postfix, Postscreen and a multitude of oth
|
||||||
[rspamd-docs::web-ui::password]: https://www.rspamd.com/doc/tutorials/quickstart.html#setting-the-controller-password
|
[rspamd-docs::web-ui::password]: https://www.rspamd.com/doc/tutorials/quickstart.html#setting-the-controller-password
|
||||||
[rspamd-docs::modules]: https://rspamd.com/doc/modules/
|
[rspamd-docs::modules]: https://rspamd.com/doc/modules/
|
||||||
[rspamd-docs::modules::rbl]: https://rspamd.com/doc/modules/rbl.html
|
[rspamd-docs::modules::rbl]: https://rspamd.com/doc/modules/rbl.html
|
||||||
|
[rspamd-docs::override-dir]: https://www.rspamd.com/doc/faq.html#what-are-the-locald-and-overrided-directories
|
||||||
[rspamd-docs::config-directories]: https://rspamd.com/doc/faq.html#what-are-the-locald-and-overrided-directories
|
[rspamd-docs::config-directories]: https://rspamd.com/doc/faq.html#what-are-the-locald-and-overrided-directories
|
||||||
[rspamd-docs::config::ucl-syntax]: https://rspamd.com/doc/configuration/ucl.html
|
[rspamd-docs::basic-options]: https://rspamd.com/doc/configuration/options.html
|
||||||
[rspamd-docs::config::overview]: https://rspamd.com/doc/configuration/index.html
|
|
||||||
[rspamd-docs::config::quickstart]: https://rspamd.com/doc/tutorials/quickstart.html#configuring-rspamd
|
|
||||||
[rspamd-docs::config::global]: https://rspamd.com/doc/configuration/options.html
|
|
||||||
[rspamd-docs::config::workers]: https://rspamd.com/doc/workers/
|
|
||||||
[rspamd-docs::config::worker-controller]: https://rspamd.com/doc/workers/controller.html
|
|
||||||
[rspamd-docs::config::worker-proxy]: https://rspamd.com/doc/workers/rspamd_proxy.html
|
|
||||||
|
|
||||||
[wikipedia::arc]: https://en.wikipedia.org/wiki/Authenticated_Received_Chain
|
[www::rbl-vs-dnsbl]: https://forum.eset.com/topic/25277-dnsbl-vs-rbl-mail-security/?do=findComment&comment=119818
|
||||||
[rspamd-docs::arc]: https://rspamd.com/doc/modules/arc.html
|
|
||||||
|
|
||||||
[www::rbl-vs-dnsbl]: https://forum.eset.com/topic/25277-dnsbl-vs-rbl-mail-security/#comment-119818
|
|
||||||
[abusix-web]: https://abusix.com/
|
[abusix-web]: https://abusix.com/
|
||||||
[abusix-web::register]: https://app.abusix.com/
|
|
||||||
[abusix-docs::rspamd-integration]: https://abusix.com/docs/rspamd/
|
[abusix-docs::rspamd-integration]: https://abusix.com/docs/rspamd/
|
||||||
[spamhaus::faq::dnsbl-usage]: https://www.spamhaus.org/faq/section/DNSBL%20Usage#365
|
[spamhaus::faq::dnsbl-usage]: https://www.spamhaus.org/faq/section/DNSBL%20Usage#365
|
||||||
|
|
||||||
[dms-repo::rspamd-actions-config]: https://github.com/docker-mailserver/docker-mailserver/tree/v15.0.0/target/rspamd/local.d/actions.conf
|
[dms-repo::rspamd-actions-config]: https://github.com/docker-mailserver/docker-mailserver/blob/v14.0.0/target/rspamd/local.d/actions.conf
|
||||||
[dms-repo::default-rspamd-configuration]: https://github.com/docker-mailserver/docker-mailserver/tree/v15.0.0/target/rspamd
|
[dms-repo::default-rspamd-configuration]: https://github.com/docker-mailserver/docker-mailserver/tree/v14.0.0/target/rspamd
|
||||||
[gh-dms::guide::valkey]: https://github.com/docker-mailserver/docker-mailserver/issues/4001#issuecomment-2652596692
|
|
||||||
[gh-dms::guide::rspamd-web]: https://github.com/orgs/docker-mailserver/discussions/4269#discussioncomment-11329588
|
|
||||||
|
|
||||||
[docs::env::enable-redis]: ../environment.md#enable_rspamd_redis
|
[docs::env::enable-redis]: ../environment.md#enable_rspamd_redis
|
||||||
[docs::spam-to-junk]: ../environment.md#move_spam_to_junk
|
[docs::spam-to-junk]: ../environment.md#move_spam_to_junk
|
||||||
|
|
|
@ -15,20 +15,13 @@ When refactoring, writing or altering scripts or other files, adhere to these ru
|
||||||
|
|
||||||
Make sure to select `edge` in the dropdown menu at the top. Navigate to the page you would like to edit and click the edit button in the top right. This allows you to make changes and create a pull-request.
|
Make sure to select `edge` in the dropdown menu at the top. Navigate to the page you would like to edit and click the edit button in the top right. This allows you to make changes and create a pull-request.
|
||||||
|
|
||||||
Alternatively you can make the changes locally. For that you'll need to have Docker installed and run:
|
Alternatively you can make the changes locally. For that you'll need to have Docker installed. Navigate into the `docs/` directory. Then run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# From the root directory of the git clone:
|
docker run --rm -it -p 8000:8000 -v "${PWD}:/docs" squidfunk/mkdocs-material
|
||||||
docker run --rm -it -p 8000:8000 -v "./docs:/docs" squidfunk/mkdocs-material
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This serves the documentation on your local machine on port `8000`. Each change will be hot-reloaded onto the page you view, just edit, save and look at the result.
|
This serves the documentation on your local machine on port `8000`. Each change will be hot-reloaded onto the page you view, just edit, save and look at the result.
|
||||||
|
|
||||||
!!! note
|
|
||||||
|
|
||||||
The container logs will inform you of invalid links detected, but a [few are false-positives][gh-dms::mkdocs-link-error-false-positives] due to our usage of linking to specific [content tabs][mkdocs::content-tabs].
|
|
||||||
|
|
||||||
[get-docker]: https://docs.docker.com/get-docker/
|
[get-docker]: https://docs.docker.com/get-docker/
|
||||||
[docs-bats-parallel]: https://bats-core.readthedocs.io/en/v1.8.2/usage.html#parallel-execution
|
[docs-bats-parallel]: https://bats-core.readthedocs.io/en/v1.8.2/usage.html#parallel-execution
|
||||||
[gh-dms::mkdocs-link-error-false-positives]: https://github.com/docker-mailserver/docker-mailserver/pull/4366
|
|
||||||
[mkdocs::content-tabs]: https://squidfunk.github.io/mkdocs-material/reference/content-tabs/#anchor-links
|
|
||||||
|
|
|
@ -24,41 +24,39 @@ As the official DMS image does not provide `dovecot-solr`, you'll need to includ
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
=== "`user-patches.sh`"
|
=== "`user-patches.sh`"
|
||||||
|
|
||||||
If you'd prefer to avoid a custom image build. This approach is simpler but with the caveat that any time the container is restarted, you'll have a delay as the package is installed each time.
|
If you'd prefer to avoid a custom image build. This approach is simpler but with the caveat that any time the container is restarted, you'll have a delay as the package is installed each time.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
apt-get update && apt-get install dovecot-solr
|
apt-get update && apt-get install dovecot-solr
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "`compose.yaml`"
|
=== "`compose.yaml`"
|
||||||
|
|
||||||
A custom DMS image does not add much friction. You do not need a separate `Dockerfile` as Docker Compose supports building from an inline `Dockerfile` in your `compose.yaml`.
|
A custom DMS image does not add much friction. You do not need a separate `Dockerfile` as Docker Compose supports building from an inline `Dockerfile` in your `compose.yaml`.
|
||||||
|
|
||||||
The `image` key of the service is swapped for the `build` key instead, as shown below:
|
The `image` key of the service is swapped for the `build` key instead, as shown below:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
mailserver:
|
mailserver:
|
||||||
hostname: mail.example.com
|
hostname: mail.example.com
|
||||||
# The `image` setting now represents the tag for the local build configured below:
|
# The `image` setting now represents the tag for the local build configured below:
|
||||||
image: local/dms:${DMS_TAG?Must set DMS image tag}
|
image: local/dms:14.0
|
||||||
# Local build (no need to try pull `image` remotely):
|
# Local build (no need to try pull `image` remotely):
|
||||||
pull_policy: build
|
pull_policy: build
|
||||||
# Add this `build` section to your real `compose.yaml` for your DMS service:
|
# Add this `build` section to your real `compose.yaml` for your DMS service:
|
||||||
build:
|
build:
|
||||||
dockerfile_inline: |
|
dockerfile_inline: |
|
||||||
FROM docker.io/mailserver/docker-mailserver:${DMS_TAG?Must set DMS image tag}
|
FROM docker.io/mailserver/docker-mailserver:14.0
|
||||||
RUN apt-get update && apt-get install dovecot-solr
|
RUN apt-get update && apt-get install dovecot-solr
|
||||||
```
|
```
|
||||||
|
|
||||||
This approach only needs to install the package once with the image build itself which minimizes the delay of container startup.
|
- Just run `docker compose up` and it will pull DMS and build your custom image to run a container.
|
||||||
|
- Updating to a new DMS release is straight-forward, just adjust the version tag as you normally would. If you make future changes that don't apply, you may need to force a rebuild.
|
||||||
- Just run `DMS_TAG='14.0' docker compose up` and it will pull the DMS image, then build your custom DMS image to run a new container instance.
|
- This approach only needs to install the package once with the image build itself. This minimizes delay of container startup.
|
||||||
- Updating to a new DMS release is straight-forward, just adjust the `DMS_TAG` ENV value or change the image tag directly in `compose.yaml` as you normally would to upgrade an image.
|
|
||||||
- If you make future changes to the `dockerfile_inline` that don't seem to be applied, you may need to force a rebuild with `DMS_TAG='14.0' docker compose up --build`.
|
|
||||||
|
|
||||||
!!! note "Why doesn't DMS include `dovecot-solr`?"
|
!!! note "Why doesn't DMS include `dovecot-solr`?"
|
||||||
|
|
||||||
|
@ -146,25 +144,6 @@ docker compose exec mailserver doveadm fts rescan -A
|
||||||
|
|
||||||
Usually within 15 minutes or so, you should be able to search your mail using the Dovecot FTS feature! :tada:
|
Usually within 15 minutes or so, you should be able to search your mail using the Dovecot FTS feature! :tada:
|
||||||
|
|
||||||
### Compatibility
|
|
||||||
|
|
||||||
Since Solr 9.8.0 was released (Jan 2025), a breaking change [deprecates support for `<lib>` directives][solr::9.8::lib-directive] which is presently used by the Dovecot supplied Solr config (`solr-config-9.xml`) to automatically load additional jars required.
|
|
||||||
|
|
||||||
To enable support for `<lib>` directives, add the following ENV to your `solr` container:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
solr:
|
|
||||||
environment:
|
|
||||||
SOLR_CONFIG_LIB_ENABLED: true
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! warning "Solr 10"
|
|
||||||
|
|
||||||
From the Solr 10 release onwards, this opt-in ENV will no longer be available.
|
|
||||||
|
|
||||||
If Dovecot has not updated their example Solr config ([upstream PR][dovecot::pr::solr-config-lib]), you will need to manually modify the Solr XML config to remove the `<lib>` directives and replace the suggested ENV `SOLR_CONFIG_LIB_ENABLED=true` with `SOLR_MODULES=analysis-extras`.
|
|
||||||
|
|
||||||
[docs::user-patches]: ../../config/advanced/override-defaults/user-patches.md
|
[docs::user-patches]: ../../config/advanced/override-defaults/user-patches.md
|
||||||
[docs::dovecot::full-text-search]: ../../config/advanced/full-text-search.md
|
[docs::dovecot::full-text-search]: ../../config/advanced/full-text-search.md
|
||||||
[gh-dms::feature-request::dovecot-solr-package]: https://github.com/docker-mailserver/docker-mailserver/issues/4052
|
[gh-dms::feature-request::dovecot-solr-package]: https://github.com/docker-mailserver/docker-mailserver/issues/4052
|
||||||
|
@ -173,6 +152,3 @@ services:
|
||||||
[dockerfile-solr-uidgid]: https://github.com/apache/solr-docker/blob/9cd850b72309de05169544395c83a85b329d6b86/9.6/Dockerfile#L89-L92
|
[dockerfile-solr-uidgid]: https://github.com/apache/solr-docker/blob/9cd850b72309de05169544395c83a85b329d6b86/9.6/Dockerfile#L89-L92
|
||||||
[github-solr]: https://github.com/apache/solr
|
[github-solr]: https://github.com/apache/solr
|
||||||
[github-dovecot::core-docs]: https://github.com/dovecot/core/tree/main/doc
|
[github-dovecot::core-docs]: https://github.com/dovecot/core/tree/main/doc
|
||||||
|
|
||||||
[solr::9.8::lib-directive]: https://issues.apache.org/jira/browse/SOLR-16781
|
|
||||||
[dovecot::pr::solr-config-lib]: https://github.com/dovecot/core/pull/238
|
|
||||||
|
|
|
@ -89,10 +89,10 @@ TLS_LEVEL=
|
||||||
# Configures the handling of creating mails with forged sender addresses.
|
# Configures the handling of creating mails with forged sender addresses.
|
||||||
#
|
#
|
||||||
# **0** => (not recommended) Mail address spoofing allowed. Any logged in user may create email messages with a forged sender address (see also https://en.wikipedia.org/wiki/Email_spoofing).
|
# **0** => (not recommended) Mail address spoofing allowed. Any logged in user may create email messages with a forged sender address (see also https://en.wikipedia.org/wiki/Email_spoofing).
|
||||||
# 1 => Mail spoofing denied. Each user may only send with their own or their alias addresses. Addresses with extension delimiters(http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
|
# 1 => Mail spoofing denied. Each user may only send with his own or his alias addresses. Addresses with extension delimiters(http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
|
||||||
SPOOF_PROTECTION=
|
SPOOF_PROTECTION=
|
||||||
|
|
||||||
# Enables the Sender Rewriting Scheme. SRS is needed if your mail server acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/main/README.rst) for further explanation.
|
# Enables the Sender Rewriting Scheme. SRS is needed if your mail server acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/master/README.md#sender-rewriting-scheme-crash-course) for further explanation.
|
||||||
# - **0** => Disabled
|
# - **0** => Disabled
|
||||||
# - 1 => Enabled
|
# - 1 => Enabled
|
||||||
ENABLE_SRS=0
|
ENABLE_SRS=0
|
||||||
|
@ -508,7 +508,7 @@ DOVECOT_MAILBOX_FORMAT=maildir
|
||||||
|
|
||||||
# empty => no
|
# empty => no
|
||||||
# yes => Allow bind authentication for LDAP
|
# yes => Allow bind authentication for LDAP
|
||||||
# https://doc.dovecot.org/2.4.0/core/config/auth/databases/ldap.html#authentication-bind
|
# https://wiki.dovecot.org/AuthDatabase/LDAP/AuthBinds
|
||||||
DOVECOT_AUTH_BIND=
|
DOVECOT_AUTH_BIND=
|
||||||
|
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
|
|
|
@ -19,11 +19,7 @@ function _main() {
|
||||||
for MAIL_ACCOUNT in "${@}"; do
|
for MAIL_ACCOUNT in "${@}"; do
|
||||||
_account_should_already_exist
|
_account_should_already_exist
|
||||||
|
|
||||||
if [[ ${MAILDEL} -eq 1 ]]; then
|
[[ ${MAILDEL} -eq 1 ]] && _remove_maildir "${MAIL_ACCOUNT}"
|
||||||
_remove_maildir "${MAIL_ACCOUNT}"
|
|
||||||
else
|
|
||||||
_log 'info' "The mailbox data will not be deleted."
|
|
||||||
fi
|
|
||||||
|
|
||||||
_manage_virtual_aliases_delete '_' "${MAIL_ACCOUNT}" \
|
_manage_virtual_aliases_delete '_' "${MAIL_ACCOUNT}" \
|
||||||
|| _exit_with_error "Aliases for '${MAIL_ACCOUNT}' could not be deleted"
|
|| _exit_with_error "Aliases for '${MAIL_ACCOUNT}' could not be deleted"
|
||||||
|
@ -35,7 +31,7 @@ function _main() {
|
||||||
_manage_accounts_delete "${MAIL_ACCOUNT}" \
|
_manage_accounts_delete "${MAIL_ACCOUNT}" \
|
||||||
|| _exit_with_error "'${MAIL_ACCOUNT}' could not be deleted"
|
|| _exit_with_error "'${MAIL_ACCOUNT}' could not be deleted"
|
||||||
|
|
||||||
_log 'info' "'${MAIL_ACCOUNT}' and associated data (aliases, quotas) deleted"
|
_log 'info' "'${MAIL_ACCOUNT}' and associated data deleted"
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,14 +43,14 @@ ${ORANGE}USAGE${RESET}
|
||||||
|
|
||||||
${ORANGE}OPTIONS${RESET}
|
${ORANGE}OPTIONS${RESET}
|
||||||
-y
|
-y
|
||||||
Skip prompt by approving to ${LWHITE}delete all mail data${RESET} for the account(s).
|
Skip prompt by approving to ${LWHITE}delete all mail storage${RESET} for the account(s).
|
||||||
|
|
||||||
${BLUE}Generic Program Information${RESET}
|
${BLUE}Generic Program Information${RESET}
|
||||||
help Print the usage information.
|
help Print the usage information.
|
||||||
|
|
||||||
${ORANGE}DESCRIPTION${RESET}
|
${ORANGE}DESCRIPTION${RESET}
|
||||||
Delete a mail account, including associated data (aliases, quotas) and
|
Delete a mail account, including associated data (aliases, quotas) and
|
||||||
optionally the mailbox data for that account.
|
optionally the mailbox storage for that account.
|
||||||
|
|
||||||
${ORANGE}EXAMPLES${RESET}
|
${ORANGE}EXAMPLES${RESET}
|
||||||
${LWHITE}./setup.sh email del user@example.com${RESET}
|
${LWHITE}./setup.sh email del user@example.com${RESET}
|
||||||
|
@ -91,10 +87,12 @@ function _parse_options() {
|
||||||
function _maildel_request_if_missing() {
|
function _maildel_request_if_missing() {
|
||||||
if [[ ${MAILDEL} -eq 0 ]]; then
|
if [[ ${MAILDEL} -eq 0 ]]; then
|
||||||
local MAILDEL_CHOSEN
|
local MAILDEL_CHOSEN
|
||||||
read -r -p "Do you want to delete the mailbox data as well (removing all mails)? [y/N] " MAILDEL_CHOSEN
|
read -r -p "Do you want to delete the mailbox as well (removing all mails)? [Y/n] " MAILDEL_CHOSEN
|
||||||
|
|
||||||
# Delete mailbox data only if the user provides explicit confirmation.
|
# TODO: Why would MAILDEL be set to true if MAILDEL_CHOSEN is empty?
|
||||||
[[ ${MAILDEL_CHOSEN,,} == "y" ]] && MAILDEL=1
|
if [[ ${MAILDEL_CHOSEN} =~ (y|Y|yes|Yes) ]] || [[ -z ${MAILDEL_CHOSEN} ]]; then
|
||||||
|
MAILDEL=1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,10 +103,10 @@ function _remove_maildir() {
|
||||||
local DOMAIN_PART="${MAIL_ACCOUNT#*@}"
|
local DOMAIN_PART="${MAIL_ACCOUNT#*@}"
|
||||||
local MAIL_ACCOUNT_STORAGE_DIR="/var/mail/${DOMAIN_PART}/${LOCAL_PART}"
|
local MAIL_ACCOUNT_STORAGE_DIR="/var/mail/${DOMAIN_PART}/${LOCAL_PART}"
|
||||||
|
|
||||||
[[ ! -d ${MAIL_ACCOUNT_STORAGE_DIR} ]] && _exit_with_error "Mailbox data directory '${MAIL_ACCOUNT_STORAGE_DIR}' does not exist"
|
[[ ! -d ${MAIL_ACCOUNT_STORAGE_DIR} ]] && _exit_with_error "Mailbox directory '${MAIL_ACCOUNT_STORAGE_DIR}' does not exist"
|
||||||
|
|
||||||
_log 'info' "Deleting mailbox data: '${MAIL_ACCOUNT_STORAGE_DIR}'"
|
_log 'info' "Deleting Mailbox: '${MAIL_ACCOUNT_STORAGE_DIR}'"
|
||||||
rm -R "${MAIL_ACCOUNT_STORAGE_DIR}" || _exit_with_error 'Mailbox data could not be deleted'
|
rm -R "${MAIL_ACCOUNT_STORAGE_DIR}" || _exit_with_error 'Mailbox could not be deleted'
|
||||||
# Remove parent directory too if it's empty:
|
# Remove parent directory too if it's empty:
|
||||||
rmdir "/var/mail/${DOMAIN_PART}" &>/dev/null
|
rmdir "/var/mail/${DOMAIN_PART}" &>/dev/null
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,255 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# shellcheck source=../scripts/helpers/index.sh
|
||||||
|
source /usr/local/bin/helpers/index.sh
|
||||||
|
|
||||||
|
function _main() {
|
||||||
|
# Default parameters (updated by `_parse_arguments()`):
|
||||||
|
local KEYSIZE=2048
|
||||||
|
local SELECTOR=mail
|
||||||
|
local DMS_DOMAINS=
|
||||||
|
|
||||||
|
_require_n_parameters_or_print_usage 0 "${@}"
|
||||||
|
_parse_arguments "${@}"
|
||||||
|
|
||||||
|
_generate_dkim_keys
|
||||||
|
}
|
||||||
|
|
||||||
|
function __usage() {
|
||||||
|
printf '%s' "${PURPLE}OPEN-DKIM${RED}(${YELLOW}8${RED})
|
||||||
|
|
||||||
|
${ORANGE}NAME${RESET}
|
||||||
|
open-dkim - Configure DKIM (DomainKeys Identified Mail)
|
||||||
|
|
||||||
|
${ORANGE}SYNOPSIS${RESET}
|
||||||
|
setup config dkim [ OPTIONS${RED}...${RESET} ]
|
||||||
|
|
||||||
|
${ORANGE}DESCRIPTION${RESET}
|
||||||
|
Creates DKIM keys and configures them within DMS for OpenDKIM.
|
||||||
|
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}
|
||||||
|
${BLUE}Generic Program Information${RESET}
|
||||||
|
help Print the usage information.
|
||||||
|
|
||||||
|
${BLUE}Configuration adjustments${RESET}
|
||||||
|
keysize Set the size of the keys to be generated.
|
||||||
|
Possible values: 1024, 2048 and 4096
|
||||||
|
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}
|
||||||
|
${LWHITE}setup config dkim keysize 4096${RESET}
|
||||||
|
Creates keys with their length increased to a size of 4096-bit.
|
||||||
|
|
||||||
|
${LWHITE}setup config dkim keysize 1024 selector 2023-dkim${RESET}
|
||||||
|
Creates 1024-bit sized keys, and changes the DKIM selector to '2023-dkim'.
|
||||||
|
|
||||||
|
${LWHITE}setup config dkim domain 'example.com,another-example.com'${RESET}
|
||||||
|
Only generates DKIM keys for the specified domains: 'example.com' and 'another-example.com'.
|
||||||
|
|
||||||
|
${ORANGE}EXIT STATUS${RESET}
|
||||||
|
Exit status is 0 if command was successful. If wrong arguments are provided or arguments contain
|
||||||
|
errors, the script will exit early with a non-zero exit status.
|
||||||
|
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
function _parse_arguments() {
|
||||||
|
# Parse the command args through iteration:
|
||||||
|
while [[ ${#} -gt 0 ]]; do
|
||||||
|
case "${1}" in
|
||||||
|
|
||||||
|
( 'keysize' )
|
||||||
|
if [[ -n ${2+set} ]]; then
|
||||||
|
KEYSIZE="${2}"
|
||||||
|
_log 'debug' "Keysize set to '${KEYSIZE}'"
|
||||||
|
else
|
||||||
|
_exit_with_error "No keysize provided after 'keysize' argument"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
( 'selector' )
|
||||||
|
if [[ -n ${2+set} ]]; then
|
||||||
|
SELECTOR="${2}"
|
||||||
|
_log 'debug' "Selector set to '${SELECTOR}'"
|
||||||
|
else
|
||||||
|
_exit_with_error "No selector provided after 'selector' argument"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
( 'domain' )
|
||||||
|
if [[ -n ${2+set} ]]; then
|
||||||
|
DMS_DOMAINS="${2}"
|
||||||
|
_log 'debug' "Domain(s) set to '${DMS_DOMAINS}'"
|
||||||
|
else
|
||||||
|
_exit_with_error "No domain(s) provided after 'domain' argument"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
( 'help' )
|
||||||
|
__usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
( * )
|
||||||
|
__usage
|
||||||
|
_exit_with_error "Unknown option(s) '${1}' ${2:+"and '${2}'"}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Discard these two args (option + value) now that they've been processed:
|
||||||
|
shift 2
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create your own keypair instead of via `opendkim-genkey` or `rspamadm dkim_keygen`:
|
||||||
|
step crypto keypair "${SELECTOR}.public" "${SELECTOR}.private" --kty RSA --size "${KEYSIZE}" --no-password --insecure
|
||||||
|
|
||||||
|
# Extract public key contents into single line prepended with `p=`
|
||||||
|
PUBLIC_KEY=$(cat "${SELECTOR}.public" | awk 'NR>2 { sub(/\r/, ""); printf "%s", last} { last=$0 }' | awk '{print "p="$1}')
|
||||||
|
|
||||||
|
# Split into quote wrapped lines 255 chars wide, then indent (8 spaces)
|
||||||
|
PUBLIC_KEY_MULTILINE=$(fold -w 255 <<< "${PUBLIC_KEY}" | sed -E 's#(.*)# "\1"#')
|
||||||
|
|
||||||
|
# Create a complimentary TXT record formatted as an RFC 1035 DNS zone file:
|
||||||
|
cat << EOF > "${SELECTOR}.zone"
|
||||||
|
${SELECTOR}._domainkey IN TXT ( "v=DKIM1; k=rsa; "
|
||||||
|
${PUBLIC_KEY_MULTILINE}
|
||||||
|
) ;
|
||||||
|
EOF
|
||||||
|
|
||||||
|
function _generate_dkim_keys() {
|
||||||
|
_generate_domains_config
|
||||||
|
if [[ ! -s ${DATABASE_VHOST} ]]; then
|
||||||
|
_log 'warn' 'No entries found, no keys to make'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Initialize OpenDKIM configs if necessary:
|
||||||
|
_create_opendkim_configs
|
||||||
|
|
||||||
|
# Generate a keypair per domain and add reference to OpenDKIM configs:
|
||||||
|
local ENTRY_KEY KEY_TABLE_ENTRY SIGNING_TABLE_ENTRY
|
||||||
|
while read -r DKIM_DOMAIN; do
|
||||||
|
_create_dkim_key "${DKIM_DOMAIN}"
|
||||||
|
|
||||||
|
# Create / Update OpenDKIM configs with new DKIM key:
|
||||||
|
ENTRY_KEY="${SELECTOR}._domainkey.${DKIM_DOMAIN}"
|
||||||
|
KEY_TABLE_ENTRY="${ENTRY_KEY} ${DKIM_DOMAIN}:${SELECTOR}:/etc/dms/keys/${SELECTOR}.private"
|
||||||
|
SIGNING_TABLE_ENTRY="*@${DKIM_DOMAIN} ${ENTRY_KEY}"
|
||||||
|
|
||||||
|
# If no existing entry, add one:
|
||||||
|
if ! grep -q "${KEY_TABLE_ENTRY}" "${KEY_TABLE_FILE}"; then
|
||||||
|
echo "${KEY_TABLE_ENTRY}" >> "${KEY_TABLE_FILE}"
|
||||||
|
fi
|
||||||
|
if ! grep -q "${SIGNING_TABLE_ENTRY}" "${SIGNING_TABLE_FILE}"; then
|
||||||
|
echo "${SIGNING_TABLE_ENTRY}" >> "${SIGNING_TABLE_FILE}"
|
||||||
|
fi
|
||||||
|
done < <(_get_valid_lines_from_file "${DATABASE_VHOST}")
|
||||||
|
|
||||||
|
# No longer needed, remove:
|
||||||
|
rm "${DATABASE_VHOST}"
|
||||||
|
|
||||||
|
# Ensure ownership is consistent for all content belonging to the base directory,
|
||||||
|
# During container startup, an internal copy will be made via `_setup_opendkim()`
|
||||||
|
# with ownership we expect, while this chown is for the benefit of the users ownership.
|
||||||
|
chown -R "$(stat -c '%U:%G' "${OPENDKIM_BASE_DIR}")" "${OPENDKIM_BASE_DIR}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prepare a file with one domain per line (iterated via while loop as DKIM_DOMAIN):
|
||||||
|
# Depends on methods from `scripts/helpers/postfix.sh`:
|
||||||
|
DATABASE_VHOST='/tmp/vhost.dkim'
|
||||||
|
function _generate_domains_config() {
|
||||||
|
local TMP_VHOST='/tmp/vhost.dkim.tmp'
|
||||||
|
|
||||||
|
# Generate the default vhost (equivalent to /etc/postfix/vhost),
|
||||||
|
# unless CLI arg DMS_DOMAINS provided an alternative list to use instead:
|
||||||
|
if [[ -z ${DMS_DOMAINS} ]]; then
|
||||||
|
_obtain_hostname_and_domainname
|
||||||
|
# uses TMP_VHOST:
|
||||||
|
_vhost_collect_postfix_domains
|
||||||
|
else
|
||||||
|
tr ',' '\n' <<< "${DMS_DOMAINS}" >"${TMP_VHOST}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Uses DATABASE_VHOST + TMP_VHOST:
|
||||||
|
_create_vhost
|
||||||
|
}
|
||||||
|
|
||||||
|
# `opendkim-genkey` generates two files at the configured `--directory`:
|
||||||
|
# - <selector>.private (Private key, PEM encoded)
|
||||||
|
# - <selector>.txt (Public key, formatted as a TXT record for a RFC 1035 DNS Zone file)
|
||||||
|
function _create_dkim_key() {
|
||||||
|
DKIM_DOMAIN=${1?Expected to be provided a domain}
|
||||||
|
|
||||||
|
OPENDKIM_DOMAINKEY_DIR="${OPENDKIM_BASE_DIR}/keys/${DKIM_DOMAIN}"
|
||||||
|
mkdir -p "${OPENDKIM_DOMAINKEY_DIR}"
|
||||||
|
|
||||||
|
DKIM_KEY_FILE="${OPENDKIM_DOMAINKEY_DIR}/${SELECTOR}.private"
|
||||||
|
if [[ ! -f "${DKIM_KEY_FILE}" ]]; then
|
||||||
|
_log 'info' "Creating DKIM private key '${DKIM_KEY_FILE}'"
|
||||||
|
|
||||||
|
# NOTE:
|
||||||
|
# --domain only affects a comment in the generated DNS Zone file
|
||||||
|
# --subdomains is the default,
|
||||||
|
# --nosubdomains would add `t=s` to the DNS TXT record generated
|
||||||
|
# http://www.opendkim.org/opendkim-genkey.8.html
|
||||||
|
opendkim-genkey \
|
||||||
|
--bits="${KEYSIZE}" \
|
||||||
|
--subdomains \
|
||||||
|
--domain="${DKIM_DOMAIN}" \
|
||||||
|
--selector="${SELECTOR}" \
|
||||||
|
--directory="${OPENDKIM_DOMAINKEY_DIR}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
OPENDKIM_BASE_DIR='/tmp/docker-mailserver/opendkim'
|
||||||
|
KEY_TABLE_FILE="${OPENDKIM_BASE_DIR}/KeyTable"
|
||||||
|
SIGNING_TABLE_FILE="${OPENDKIM_BASE_DIR}/SigningTable"
|
||||||
|
TRUSTED_HOSTS_FILE="${OPENDKIM_BASE_DIR}/TrustedHosts"
|
||||||
|
# Create configs if missing:
|
||||||
|
function _create_opendkim_configs() {
|
||||||
|
_log 'debug' 'Creating any missing OpenDKIM configs'
|
||||||
|
|
||||||
|
mkdir -p "${OPENDKIM_BASE_DIR}"
|
||||||
|
local OPENDKIM_CONFIGS=(
|
||||||
|
"${KEY_TABLE_FILE}"
|
||||||
|
"${SIGNING_TABLE_FILE}"
|
||||||
|
"${TRUSTED_HOSTS_FILE}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Only create if the file doesn't exist (avoids modifying mtime):
|
||||||
|
for FILE in ${OPENDKIM_CONFIGS[@]}; do
|
||||||
|
[[ ! -f "${FILE}" ]] && touch "${FILE}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# If file exists but is empty, add default hosts to trust:
|
||||||
|
if [[ ! -s "${TRUSTED_HOSTS_FILE}" ]]; then
|
||||||
|
_log 'debug' 'Adding default trust to OpenDKIM TrustedHosts config'
|
||||||
|
echo "127.0.0.1" > "${TRUSTED_HOSTS_FILE}"
|
||||||
|
echo "localhost" >> "${TRUSTED_HOSTS_FILE}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_main "${@}"
|
||||||
|
|
||||||
|
|
||||||
|
function _transform_public_key_file_to_dns_record_contents() {
|
||||||
|
_log 'trace' 'Transforming DNS zone format to DNS record content now'
|
||||||
|
: >"${PUBLIC_KEY_DNS_FILE}"
|
||||||
|
grep -o '".*"' "${PUBLIC_KEY_FILE}" | tr -d '"\n' >>"${PUBLIC_KEY_DNS_FILE}"
|
||||||
|
echo '' >>"${PUBLIC_KEY_DNS_FILE}"
|
||||||
|
|
||||||
|
if ! _log_level_is '(warn|error)'; then
|
||||||
|
_log 'info' "Here is the content of the TXT DNS record ${SELECTOR}._domainkey.${DOMAIN} that you need to create:\n"
|
||||||
|
cat "${PUBLIC_KEY_DNS_FILE}"
|
||||||
|
printf '\n'
|
||||||
|
fi
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# shellcheck source=../scripts/helpers/index.sh
|
# shellcheck source=../scripts/helpers/index.sh
|
||||||
source /usr/local/bin/helpers/index.sh
|
source /usr/local/bin/helpers/index.sh
|
||||||
|
@ -76,27 +76,27 @@ function _parse_arguments() {
|
||||||
case "${1}" in
|
case "${1}" in
|
||||||
|
|
||||||
( 'keysize' )
|
( 'keysize' )
|
||||||
if [[ -n ${2:-} ]]; then
|
if [[ -n ${2+set} ]]; then
|
||||||
KEYSIZE="${2}"
|
KEYSIZE="${2}"
|
||||||
_log 'trace' "Keysize set to '${KEYSIZE}'"
|
_log 'debug' "Keysize set to '${KEYSIZE}'"
|
||||||
else
|
else
|
||||||
_exit_with_error "No keysize provided after 'keysize' argument"
|
_exit_with_error "No keysize provided after 'keysize' argument"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
( 'selector' )
|
( 'selector' )
|
||||||
if [[ -n ${2:-} ]]; then
|
if [[ -n ${2+set} ]]; then
|
||||||
SELECTOR="${2}"
|
SELECTOR="${2}"
|
||||||
_log 'trace' "Selector set to '${SELECTOR}'"
|
_log 'debug' "Selector set to '${SELECTOR}'"
|
||||||
else
|
else
|
||||||
_exit_with_error "No selector provided after 'selector' argument"
|
_exit_with_error "No selector provided after 'selector' argument"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
( 'domain' )
|
( 'domain' )
|
||||||
if [[ -n ${2:-} ]]; then
|
if [[ -n ${2+set} ]]; then
|
||||||
DMS_DOMAINS="${2}"
|
DMS_DOMAINS="${2}"
|
||||||
_log 'trace' "Domain(s) set to '${DMS_DOMAINS}'"
|
_log 'debug' "Domain(s) set to '${DMS_DOMAINS}'"
|
||||||
else
|
else
|
||||||
_exit_with_error "No domain(s) provided after 'domain' argument"
|
_exit_with_error "No domain(s) provided after 'domain' argument"
|
||||||
fi
|
fi
|
||||||
|
@ -109,7 +109,7 @@ function _parse_arguments() {
|
||||||
|
|
||||||
( * )
|
( * )
|
||||||
__usage
|
__usage
|
||||||
_exit_with_error "Unknown option(s) ${*}"
|
_exit_with_error "Unknown option(s) '${1}' ${2:+"and '${2}'"}"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ function _generate_domains_config() {
|
||||||
|
|
||||||
# Generate the default vhost (equivalent to /etc/postfix/vhost),
|
# Generate the default vhost (equivalent to /etc/postfix/vhost),
|
||||||
# unless CLI arg DMS_DOMAINS provided an alternative list to use instead:
|
# unless CLI arg DMS_DOMAINS provided an alternative list to use instead:
|
||||||
if [[ -z ${DMS_DOMAINS:-} ]]; then
|
if [[ -z ${DMS_DOMAINS} ]]; then
|
||||||
_obtain_hostname_and_domainname
|
_obtain_hostname_and_domainname
|
||||||
# uses TMP_VHOST:
|
# uses TMP_VHOST:
|
||||||
_vhost_collect_postfix_domains
|
_vhost_collect_postfix_domains
|
||||||
|
@ -209,6 +209,8 @@ SIGNING_TABLE_FILE="${OPENDKIM_BASE_DIR}/SigningTable"
|
||||||
TRUSTED_HOSTS_FILE="${OPENDKIM_BASE_DIR}/TrustedHosts"
|
TRUSTED_HOSTS_FILE="${OPENDKIM_BASE_DIR}/TrustedHosts"
|
||||||
# Create configs if missing:
|
# Create configs if missing:
|
||||||
function _create_opendkim_configs() {
|
function _create_opendkim_configs() {
|
||||||
|
_log 'debug' 'Creating any missing OpenDKIM configs'
|
||||||
|
|
||||||
mkdir -p "${OPENDKIM_BASE_DIR}"
|
mkdir -p "${OPENDKIM_BASE_DIR}"
|
||||||
local OPENDKIM_CONFIGS=(
|
local OPENDKIM_CONFIGS=(
|
||||||
"${KEY_TABLE_FILE}"
|
"${KEY_TABLE_FILE}"
|
||||||
|
@ -217,11 +219,8 @@ function _create_opendkim_configs() {
|
||||||
)
|
)
|
||||||
|
|
||||||
# Only create if the file doesn't exist (avoids modifying mtime):
|
# Only create if the file doesn't exist (avoids modifying mtime):
|
||||||
for FILE in "${OPENDKIM_CONFIGS[@]}"; do
|
for FILE in ${OPENDKIM_CONFIGS[@]}; do
|
||||||
if [[ ! -f "${FILE}" ]]; then
|
[[ ! -f "${FILE}" ]] && touch "${FILE}"
|
||||||
_log 'debug' "Creating OpenDKIM config '${FILE}'"
|
|
||||||
touch "${FILE}"
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
|
|
||||||
# If file exists but is empty, add default hosts to trust:
|
# If file exists but is empty, add default hosts to trust:
|
||||||
|
|
|
@ -68,10 +68,9 @@ smtpd_forbid_bare_newline = yes
|
||||||
# smtpd_forbid_bare_newline_exclusions = $mynetworks
|
# smtpd_forbid_bare_newline_exclusions = $mynetworks
|
||||||
|
|
||||||
# Custom defined parameters for DMS:
|
# Custom defined parameters for DMS:
|
||||||
# Custom sender restrictions overview: https://github.com/docker-mailserver/docker-mailserver/pull/4379#issuecomment-2670365917
|
# reject_unknown_sender_domain: https://github.com/docker-mailserver/docker-mailserver/issues/3716#issuecomment-1868033234
|
||||||
# `reject_unknown_sender_domain`: https://github.com/docker-mailserver/docker-mailserver/issues/3716#issuecomment-1868033234
|
|
||||||
dms_smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unknown_sender_domain
|
dms_smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unknown_sender_domain
|
||||||
# `SPOOF_PROTECTION=1` support requires prepending `reject_authenticated_sender_login_mismatch`
|
# Submission ports 587 and 465 support for SPOOF_PROTECTION=1
|
||||||
mua_sender_restrictions = reject_authenticated_sender_login_mismatch, $dms_smtpd_sender_restrictions
|
mua_sender_restrictions = reject_authenticated_sender_login_mismatch, $dms_smtpd_sender_restrictions
|
||||||
|
|
||||||
# Postscreen settings to drop zombies/open relays/spam early
|
# Postscreen settings to drop zombies/open relays/spam early
|
||||||
|
|
|
@ -8,5 +8,4 @@
|
||||||
/^\s*X-Mailer/ IGNORE
|
/^\s*X-Mailer/ IGNORE
|
||||||
/^\s*X-Originating-IP/ IGNORE
|
/^\s*X-Originating-IP/ IGNORE
|
||||||
/^\s*Received: from.*127.0.0.1/ IGNORE
|
/^\s*Received: from.*127.0.0.1/ IGNORE
|
||||||
/^\s*X-MS-Reactions:/ IGNORE
|
/^Content-Type:/i PREPEND X-MS-Reactions: disallow
|
||||||
/^\s*Message-Id:/i PREPEND X-MS-Reactions: disallow
|
|
||||||
|
|
|
@ -38,18 +38,13 @@ function _pre_installation_steps() {
|
||||||
|
|
||||||
# Install third-party commands to /usr/local/bin
|
# Install third-party commands to /usr/local/bin
|
||||||
function _install_utils() {
|
function _install_utils() {
|
||||||
local ARCH_A
|
local ARCH_A=$(uname -m)
|
||||||
ARCH_A=$(uname --machine)
|
|
||||||
# Alternate naming convention support: x86_64 (amd64) / aarch64 (arm64)
|
# Alternate naming convention support: x86_64 (amd64) / aarch64 (arm64)
|
||||||
# https://en.wikipedia.org/wiki/X86-64#Industry_naming_conventions
|
# https://en.wikipedia.org/wiki/X86-64#Industry_naming_conventions
|
||||||
local ARCH_B
|
local ARCH_B
|
||||||
case "${ARCH_A}" in
|
case "${ARCH_A}" in
|
||||||
( 'x86_64' ) ARCH_B='amd64' ;;
|
( 'x86_64' ) ARCH_B='amd64' ;;
|
||||||
( 'aarch64' ) ARCH_B='arm64' ;;
|
( 'aarch64' ) ARCH_B='arm64' ;;
|
||||||
( * )
|
|
||||||
_log 'error' "Unsupported arch: '${ARCH_A}'"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# TIP: `*.tar.gz` releases tend to forget to reset UID/GID ownership when archiving.
|
# TIP: `*.tar.gz` releases tend to forget to reset UID/GID ownership when archiving.
|
||||||
|
@ -61,7 +56,7 @@ function _install_utils() {
|
||||||
|
|
||||||
_log 'trace' 'Installing jaq'
|
_log 'trace' 'Installing jaq'
|
||||||
local JAQ_TAG='v2.1.0'
|
local JAQ_TAG='v2.1.0'
|
||||||
curl -sSfL "https://github.com/01mf02/jaq/releases/download/${JAQ_TAG}/jaq-$(uname -m)-unknown-linux-gnu" -o /usr/local/bin/jaq
|
curl -sSfL "https://github.com/01mf02/jaq/releases/download/${JAQ_TAG}/jaq-${ARCH_A}-unknown-linux-gnu" -o /usr/local/bin/jaq
|
||||||
chmod +x /usr/local/bin/jaq
|
chmod +x /usr/local/bin/jaq
|
||||||
|
|
||||||
_log 'trace' 'Installing step'
|
_log 'trace' 'Installing step'
|
||||||
|
|
|
@ -43,7 +43,7 @@ function _monitored_files_checksums() {
|
||||||
|
|
||||||
# Check whether Rspamd is used and if so, monitor it's changes as well
|
# Check whether Rspamd is used and if so, monitor it's changes as well
|
||||||
if [[ ${ENABLE_RSPAMD} -eq 1 ]] && [[ -d ${RSPAMD_DMS_D} ]]; then
|
if [[ ${ENABLE_RSPAMD} -eq 1 ]] && [[ -d ${RSPAMD_DMS_D} ]]; then
|
||||||
readarray -d '' STAGING_FILES_RSPAMD < <(find "${RSPAMD_DMS_D}" -type f -print0)
|
readarray -d '' STAGING_FILES_RSPAMD < <(find "${RSPAMD_DMS_D}" -type f -name "*.sh" -print0)
|
||||||
STAGING_FILES+=("${STAGING_FILES_RSPAMD[@]}")
|
STAGING_FILES+=("${STAGING_FILES_RSPAMD[@]}")
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
function _exit_with_error() {
|
function _exit_with_error() {
|
||||||
if [[ -n ${1:-} ]]; then
|
if [[ -n ${1+set} ]]; then
|
||||||
_log 'error' "${1}"
|
_log 'error' "${1}"
|
||||||
else
|
else
|
||||||
_log 'error' "Call to '_exit_with_error' is missing a message to log"
|
_log 'error' "Call to '_exit_with_error' is missing a message to log"
|
||||||
|
|
|
@ -43,12 +43,12 @@ RESET=$(echo -ne '\e[0m')
|
||||||
# message is logged. Likewise when the second argument
|
# message is logged. Likewise when the second argument
|
||||||
# is missing. Both failures will return with exit code '1'.
|
# is missing. Both failures will return with exit code '1'.
|
||||||
function _log() {
|
function _log() {
|
||||||
if [[ -z ${1:-} ]]; then
|
if [[ -z ${1+set} ]]; then
|
||||||
_log 'error' "Call to '_log' is missing a valid log level"
|
_log 'error' "Call to '_log' is missing a valid log level"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -z ${2:-} ]]; then
|
if [[ -z ${2+set} ]]; then
|
||||||
_log 'error' "Call to '_log' is missing a message to log"
|
_log 'error' "Call to '_log' is missing a message to log"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
@ -116,7 +116,7 @@ function _log() {
|
||||||
# variables file. If this does not yield a value either,
|
# variables file. If this does not yield a value either,
|
||||||
# use the default log level.
|
# use the default log level.
|
||||||
function _get_log_level_or_default() {
|
function _get_log_level_or_default() {
|
||||||
if [[ -n ${LOG_LEVEL:-} ]]; then
|
if [[ -n ${LOG_LEVEL+set} ]]; then
|
||||||
echo "${LOG_LEVEL}"
|
echo "${LOG_LEVEL}"
|
||||||
elif [[ -e /etc/dms-settings ]] && grep -q -E "^LOG_LEVEL='[a-z]+'" /etc/dms-settings; then
|
elif [[ -e /etc/dms-settings ]] && grep -q -E "^LOG_LEVEL='[a-z]+'" /etc/dms-settings; then
|
||||||
grep '^LOG_LEVEL=' /etc/dms-settings | cut -d "'" -f 2
|
grep '^LOG_LEVEL=' /etc/dms-settings | cut -d "'" -f 2
|
||||||
|
|
|
@ -111,6 +111,14 @@ function _rspamd_handle_user_modules_adjustments() {
|
||||||
fi
|
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
|
if [[ -f "${RSPAMD_DMS_CUSTOM_COMMANDS_F}" ]]; then
|
||||||
__rspamd__log 'debug' "Found file '${RSPAMD_DMS_CUSTOM_COMMANDS_F}' - parsing and applying it"
|
__rspamd__log 'debug' "Found file '${RSPAMD_DMS_CUSTOM_COMMANDS_F}' - parsing and applying it"
|
||||||
|
|
||||||
|
|
|
@ -131,9 +131,9 @@ function _reload_postfix() {
|
||||||
# 1. No first and second argument is supplied
|
# 1. No first and second argument is supplied
|
||||||
# 2. The second argument is a path to a file that does not exist
|
# 2. The second argument is a path to a file that does not exist
|
||||||
function _replace_by_env_in_file() {
|
function _replace_by_env_in_file() {
|
||||||
if [[ -z ${1:-} ]]; then
|
if [[ -z ${1+set} ]]; then
|
||||||
_dms_panic__invalid_value 'first argument unset' 'utils.sh:_replace_by_env_in_file'
|
_dms_panic__invalid_value 'first argument unset' 'utils.sh:_replace_by_env_in_file'
|
||||||
elif [[ -z ${2:-} ]]; then
|
elif [[ -z ${2+set} ]]; then
|
||||||
_dms_panic__invalid_value 'second argument unset' 'utils.sh:_replace_by_env_in_file'
|
_dms_panic__invalid_value 'second argument unset' 'utils.sh:_replace_by_env_in_file'
|
||||||
elif [[ ! -f ${2} ]]; then
|
elif [[ ! -f ${2} ]]; then
|
||||||
_dms_panic__invalid_value "file '${2}' does not exist" 'utils.sh:_replace_by_env_in_file'
|
_dms_panic__invalid_value "file '${2}' does not exist" 'utils.sh:_replace_by_env_in_file'
|
||||||
|
|
|
@ -43,6 +43,7 @@ function _register_functions() {
|
||||||
# ? >> Setup
|
# ? >> Setup
|
||||||
|
|
||||||
_register_setup_function '_setup_vmail_id'
|
_register_setup_function '_setup_vmail_id'
|
||||||
|
_register_setup_function '_setup_logs_general'
|
||||||
_register_setup_function '_setup_timezone'
|
_register_setup_function '_setup_timezone'
|
||||||
|
|
||||||
if [[ ${SMTP_ONLY} -ne 1 ]]; then
|
if [[ ${SMTP_ONLY} -ne 1 ]]; then
|
||||||
|
@ -181,9 +182,6 @@ if [[ -f /CONTAINER_START ]]; then
|
||||||
# We cannot skip all setup routines because some need to run _after_
|
# We cannot skip all setup routines because some need to run _after_
|
||||||
# the initial setup (and hence, they cannot be moved to the check stack).
|
# the initial setup (and hence, they cannot be moved to the check stack).
|
||||||
_setup_directory_and_file_permissions
|
_setup_directory_and_file_permissions
|
||||||
|
|
||||||
# shellcheck source=./startup/setup.d/mail_state.sh
|
|
||||||
source /usr/local/bin/setup.d/mail_state.sh
|
|
||||||
_setup_adjust_state_permissions
|
_setup_adjust_state_permissions
|
||||||
else
|
else
|
||||||
_setup
|
_setup
|
||||||
|
|
|
@ -82,8 +82,6 @@ function _setup_timezone() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Misc checks and fixes migrated here until next refactor:
|
|
||||||
# NOTE: `start-mailserver.sh` runs this along with `mail-state.sh` during container restarts
|
|
||||||
function _setup_directory_and_file_permissions() {
|
function _setup_directory_and_file_permissions() {
|
||||||
_log 'trace' 'Removing leftover PID files from a stop/start'
|
_log 'trace' 'Removing leftover PID files from a stop/start'
|
||||||
find /var/run/ -not -name 'supervisord.pid' -name '*.pid' -delete
|
find /var/run/ -not -name 'supervisord.pid' -name '*.pid' -delete
|
||||||
|
@ -103,8 +101,6 @@ function _setup_directory_and_file_permissions() {
|
||||||
_log 'debug' "Ensuring '${RSPAMD_DMS_DKIM_D}' is owned by '_rspamd:_rspamd'"
|
_log 'debug' "Ensuring '${RSPAMD_DMS_DKIM_D}' is owned by '_rspamd:_rspamd'"
|
||||||
chown -R _rspamd:_rspamd "${RSPAMD_DMS_DKIM_D}"
|
chown -R _rspamd:_rspamd "${RSPAMD_DMS_DKIM_D}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
__log_fixes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _setup_run_user_patches() {
|
function _setup_run_user_patches() {
|
||||||
|
@ -117,32 +113,3 @@ function _setup_run_user_patches() {
|
||||||
_log 'trace' "No optional '${USER_PATCHES}' provided"
|
_log 'trace' "No optional '${USER_PATCHES}' provided"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function __log_fixes() {
|
|
||||||
_log 'debug' 'Ensuring /var/log/mail owneership + permissions are correct'
|
|
||||||
|
|
||||||
# File/folder permissions are fine when using docker volumes, but may be wrong
|
|
||||||
# when file system folders are mounted into the container.
|
|
||||||
# Set the expected values and create missing folders/files just in case.
|
|
||||||
mkdir -p /var/log/{mail,supervisor}
|
|
||||||
|
|
||||||
# TODO: Remove these lines in a future release once concerns are resolved:
|
|
||||||
# https://github.com/docker-mailserver/docker-mailserver/pull/4370#issuecomment-2661762043
|
|
||||||
chown syslog:root /var/log/mail
|
|
||||||
|
|
||||||
if [[ ${ENABLE_CLAMAV} -eq 1 ]]; then
|
|
||||||
# TODO: Consider assigning /var/log/mail a writable non-root group for other processes like ClamAV?
|
|
||||||
# - Check if ClamAV is capable of creating files itself when they're missing?
|
|
||||||
# - Alternatively a symlink to /var/log/mail from the original intended location would allow write access
|
|
||||||
# as a user to the symlink location, while keeping ownership as root at /var/log/mail
|
|
||||||
# - `LogSyslog false` for clamd.conf + freshclam.conf could possibly be enabled instead of log files?
|
|
||||||
# However without better filtering in place (once Vector is adopted), this should be avoided.
|
|
||||||
touch /var/log/mail/{clamav,freshclam}.log
|
|
||||||
chown clamav:adm /var/log/mail/{clamav,freshclam}.log
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Volume permissions should be corrected:
|
|
||||||
# https://github.com/docker-mailserver/docker-mailserver-helm/issues/137
|
|
||||||
chmod 755 /var/log/mail/
|
|
||||||
find /var/log/mail/ -type f -exec chmod 640 {} +
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,39 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Legacy service support for DKIM, DMARC, SPF
|
||||||
|
# TODO: Migrate this file into a common legacy feature dir
|
||||||
|
|
||||||
|
# Debian 12 package: opendkim 2.11.0
|
||||||
|
# https://salsa.debian.org/debian/opendkim
|
||||||
|
# Official project page (no HTTPS available):
|
||||||
|
# http://www.opendkim.org/
|
||||||
|
# Links to SourceForge for project source which directs users to Github:
|
||||||
|
# Last commit Dec 2022:
|
||||||
|
# https://github.com/trusteddomainproject/OpenDKIM/tree/develop
|
||||||
|
# Last release 2.11.0 (Nov 2018):
|
||||||
|
# https://github.com/trusteddomainproject/OpenDKIM/releases
|
||||||
|
|
||||||
|
# Debian 12 package: opendmarc 1.4.2
|
||||||
|
# https://salsa.debian.org/kitterman/opendmarc
|
||||||
|
# Official project page (no HTTPS available):
|
||||||
|
# http://www.trusteddomain.org/opendmarc/
|
||||||
|
# Links to SourceForge for project source which directs users to Github (since April 2021):
|
||||||
|
# Last commit Dec 2021:
|
||||||
|
# https://github.com/trusteddomainproject/OpenDMARC/branches/all
|
||||||
|
# Last release 1.4.2 (Dec 2021):
|
||||||
|
# https://github.com/trusteddomainproject/OpenDMARC/blob/master/RELEASE_NOTES
|
||||||
|
|
||||||
|
# Debian 12 package: postfix-policyd-spf-python 3.0.4 (April 2023)
|
||||||
|
# https://salsa.debian.org/python-team/packages/spf-engine
|
||||||
|
# Previously `policyd-spf` until Dec 2016, then renamed to `spf-engine`:
|
||||||
|
# https://launchpad.net/pypolicyd-spf
|
||||||
|
# https://salsa.debian.org/kitterman/postfix-policyd-spf-perl
|
||||||
|
# Official project page + repo:
|
||||||
|
# https://code.launchpad.net/spf-engine
|
||||||
|
# Last commit and release 3.1.0 (Aug 2024):
|
||||||
|
# https://git.launchpad.net/spf-engine/
|
||||||
|
|
||||||
|
|
||||||
# Set up OpenDKIM
|
# Set up OpenDKIM
|
||||||
#
|
#
|
||||||
# ## Attention
|
# ## Attention
|
||||||
|
|
|
@ -1,5 +1,15 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
function _setup_logs_general() {
|
||||||
|
_log 'debug' 'Setting up general log files'
|
||||||
|
|
||||||
|
# File/folder permissions are fine when using docker volumes, but may be wrong
|
||||||
|
# when file system folders are mounted into the container.
|
||||||
|
# Set the expected values and create missing folders/files just in case.
|
||||||
|
mkdir -p /var/log/{mail,supervisor}
|
||||||
|
chown syslog:root /var/log/mail
|
||||||
|
}
|
||||||
|
|
||||||
function _setup_logrotate() {
|
function _setup_logrotate() {
|
||||||
_log 'debug' 'Setting up logrotate'
|
_log 'debug' 'Setting up logrotate'
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
DMS_STATE_DIR='/var/mail-state'
|
|
||||||
|
|
||||||
# Consolidate all states into a single directory
|
# Consolidate all states into a single directory
|
||||||
# (/var/mail-state) to allow persistence using docker volumes
|
# (/var/mail-state) to allow persistence using docker volumes
|
||||||
function _setup_save_states() {
|
function _setup_save_states() {
|
||||||
if [[ ! -d ${DMS_STATE_DIR} ]]; then
|
if [[ ! -d ${DMS_STATE_DIR:?DMS_STATE_DIR is not set} ]]; then
|
||||||
_log 'debug' "'${DMS_STATE_DIR}' is not present - not consolidating state"
|
_log 'debug' "'${DMS_STATE_DIR}' is not present - not consolidating state"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
@ -93,12 +91,7 @@ function _setup_save_states() {
|
||||||
# These corrections are to fix changes to UID/GID values between upgrades,
|
# These corrections are to fix changes to UID/GID values between upgrades,
|
||||||
# or when ownership/permissions were altered externally on the host (eg: migration or system scripts)
|
# or when ownership/permissions were altered externally on the host (eg: migration or system scripts)
|
||||||
function _setup_adjust_state_permissions() {
|
function _setup_adjust_state_permissions() {
|
||||||
[[ ! -d ${DMS_STATE_DIR} ]] && return 0
|
[[ ! -d ${DMS_STATE_DIR:?DMS_STATE_DIR is not set} ]] && return 0
|
||||||
|
|
||||||
# Parent directories must have executable bit set to descend the file tree for access,
|
|
||||||
# as each service running as a non-root user requires this to access their state directory,
|
|
||||||
# `/var/mail-state` must allow all users `+x`:
|
|
||||||
chmod +x "${DMS_STATE_DIR}"
|
|
||||||
|
|
||||||
# This ensures the user and group of the files from the external mount have their
|
# This ensures the user and group of the files from the external mount have their
|
||||||
# numeric ID values in sync. New releases where the installed packages order changes
|
# numeric ID values in sync. New releases where the installed packages order changes
|
||||||
|
|
|
@ -93,19 +93,13 @@ EOF
|
||||||
function _setup_postfix_late() {
|
function _setup_postfix_late() {
|
||||||
_log 'debug' 'Configuring Postfix (late setup)'
|
_log 'debug' 'Configuring Postfix (late setup)'
|
||||||
|
|
||||||
# These two config files are `access` database tables managed via `setup email restrict`:
|
|
||||||
# NOTE: Prepends to existing restrictions, thus has priority over other permit/reject policies that follow.
|
|
||||||
# https://www.postfix.org/postconf.5.html#smtpd_sender_restrictions
|
|
||||||
# https://www.postfix.org/access.5.html
|
|
||||||
__postfix__log 'trace' 'Configuring user access'
|
__postfix__log 'trace' 'Configuring user access'
|
||||||
if [[ -f /tmp/docker-mailserver/postfix-send-access.cf ]]; then
|
if [[ -f /tmp/docker-mailserver/postfix-send-access.cf ]]; then
|
||||||
# Prefer to prepend to our specialized variant instead:
|
sed -i -E 's|(smtpd_sender_restrictions =)|\1 check_sender_access texthash:/tmp/docker-mailserver/postfix-send-access.cf,|' /etc/postfix/main.cf
|
||||||
# https://github.com/docker-mailserver/docker-mailserver/pull/4379
|
|
||||||
sed -i -E 's|^(dms_smtpd_sender_restrictions =)|\1 check_sender_access texthash:/tmp/docker-mailserver/postfix-send-access.cf,|' /etc/postfix/main.cf
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -f /tmp/docker-mailserver/postfix-receive-access.cf ]]; then
|
if [[ -f /tmp/docker-mailserver/postfix-receive-access.cf ]]; then
|
||||||
sed -i -E 's|^(smtpd_recipient_restrictions =)|\1 check_recipient_access texthash:/tmp/docker-mailserver/postfix-receive-access.cf,|' /etc/postfix/main.cf
|
sed -i -E 's|(smtpd_recipient_restrictions =)|\1 check_recipient_access texthash:/tmp/docker-mailserver/postfix-receive-access.cf,|' /etc/postfix/main.cf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
__postfix__log 'trace' 'Configuring relay host'
|
__postfix__log 'trace' 'Configuring relay host'
|
||||||
|
|
|
@ -1,5 +1,41 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Legacy service support with Postgrey, SpamAssassin, Amavis
|
||||||
|
# TODO: Migrate these services into a common legacy feature dir
|
||||||
|
|
||||||
|
# Debian 12 package: postgrey 1.37
|
||||||
|
# https://salsa.debian.org/debian/postgrey
|
||||||
|
# Official project page:
|
||||||
|
# https://postgrey.schweikert.ch/
|
||||||
|
# Last commit Feb 2024 (mostly whitelist updates since 2016):
|
||||||
|
# https://github.com/schweikert/postgrey
|
||||||
|
# Last release 1.37 (Sep 2016):
|
||||||
|
# https://github.com/schweikert/postgrey/tags
|
||||||
|
|
||||||
|
# Debian 12 package: spamassassin 4.0.0 (Dec 2022)
|
||||||
|
# Lack of 4.0.1 potentially causing regression/bugs in DMS v14+
|
||||||
|
# https://salsa.debian.org/debian/spamassassin
|
||||||
|
# Official project page:
|
||||||
|
# https://spamassassin.apache.org/
|
||||||
|
# NOTE: Github repo is a mirror (antiquated process for contributions/reports)
|
||||||
|
# Last commit Feb 2025:
|
||||||
|
# https://github.com/apache/spamassassin
|
||||||
|
# Last release 4.0.1 (March 2024):
|
||||||
|
# https://github.com/apache/spamassassin/tags
|
||||||
|
|
||||||
|
# Debian 12 package: amavisd-new 2.13.0 (Jan 2023)
|
||||||
|
# https://salsa.debian.org/debian/amavisd-new
|
||||||
|
# Official project page:
|
||||||
|
# https://www.ijs.si/software/amavisd/
|
||||||
|
# https://www.amavis.org/
|
||||||
|
# Last announced release 2.11.0 (2016) + 2.11.1 (Oct 2018):
|
||||||
|
# https://www.ijs.si/software/amavisd/release-notes.txt
|
||||||
|
# Development migrated to Gitlab with release 2.12.0 (July 2019)
|
||||||
|
# Last commit Feb 2025:
|
||||||
|
# https://gitlab.com/amavis/amavis
|
||||||
|
# Last release 2.13.1 (March 2024):
|
||||||
|
# https://gitlab.com/amavis/amavis/-/tags
|
||||||
|
|
||||||
function _setup_security_stack() {
|
function _setup_security_stack() {
|
||||||
_log 'debug' 'Setting up Security Stack'
|
_log 'debug' 'Setting up Security Stack'
|
||||||
|
|
||||||
|
@ -155,6 +191,13 @@ function __setup__security__clamav() {
|
||||||
if [[ ${ENABLE_CLAMAV} -eq 1 ]]; then
|
if [[ ${ENABLE_CLAMAV} -eq 1 ]]; then
|
||||||
_log 'debug' 'Enabling and configuring ClamAV'
|
_log 'debug' 'Enabling and configuring ClamAV'
|
||||||
|
|
||||||
|
local FILE
|
||||||
|
for FILE in /var/log/mail/{clamav,freshclam}.log; do
|
||||||
|
touch "${FILE}"
|
||||||
|
chown clamav:adm "${FILE}"
|
||||||
|
chmod 640 "${FILE}"
|
||||||
|
done
|
||||||
|
|
||||||
if [[ ${CLAMAV_MESSAGE_SIZE_LIMIT} != '25M' ]]; then
|
if [[ ${CLAMAV_MESSAGE_SIZE_LIMIT} != '25M' ]]; then
|
||||||
_log 'trace' "Setting ClamAV message scan size limit to '${CLAMAV_MESSAGE_SIZE_LIMIT}'"
|
_log 'trace' "Setting ClamAV message scan size limit to '${CLAMAV_MESSAGE_SIZE_LIMIT}'"
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,17 @@ function _early_variables_setup() {
|
||||||
__environment_variables_export
|
__environment_variables_export
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Declare a variable as readonly if it is not already set.
|
||||||
|
function __declare_readonly() {
|
||||||
|
local VARIABLE_NAME=${1:?Variable name required when declaring a variable as readonly}
|
||||||
|
local VARIABLE_VALUE=${2:?Variable value required when declaring a variable as readonly}
|
||||||
|
|
||||||
|
if [[ ! -v ${VARIABLE_NAME} ]]; then
|
||||||
|
readonly "${VARIABLE_NAME}=${VARIABLE_VALUE}"
|
||||||
|
VARS[${VARIABLE_NAME}]="${VARIABLE_VALUE}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# This function handles variables that are deprecated. This allows a
|
# This function handles variables that are deprecated. This allows a
|
||||||
# smooth transition period, without the need of removing a variable
|
# smooth transition period, without the need of removing a variable
|
||||||
# completely with a single version.
|
# completely with a single version.
|
||||||
|
@ -62,6 +73,10 @@ function __environment_variables_general_setup() {
|
||||||
VARS[DMS_VMAIL_UID]="${DMS_VMAIL_UID:=5000}"
|
VARS[DMS_VMAIL_UID]="${DMS_VMAIL_UID:=5000}"
|
||||||
VARS[DMS_VMAIL_GID]="${DMS_VMAIL_GID:=5000}"
|
VARS[DMS_VMAIL_GID]="${DMS_VMAIL_GID:=5000}"
|
||||||
|
|
||||||
|
# internal variables are next
|
||||||
|
|
||||||
|
__declare_readonly 'DMS_STATE_DIR' '/var/mail-state'
|
||||||
|
|
||||||
# user-customizable are last
|
# user-customizable are last
|
||||||
|
|
||||||
_log 'trace' 'Setting anti-spam & anti-virus environment variables'
|
_log 'trace' 'Setting anti-spam & anti-virus environment variables'
|
||||||
|
|
|
@ -56,7 +56,7 @@ function __handle_container_name() {
|
||||||
if [[ -n ${1:-} ]] && [[ ${1:-} =~ ^dms-test_ ]]; then
|
if [[ -n ${1:-} ]] && [[ ${1:-} =~ ^dms-test_ ]]; then
|
||||||
printf '%s' "${1}"
|
printf '%s' "${1}"
|
||||||
return 0
|
return 0
|
||||||
elif [[ -n ${CONTAINER_NAME:-} ]]; then
|
elif [[ -n ${CONTAINER_NAME+set} ]]; then
|
||||||
printf '%s' "${CONTAINER_NAME}"
|
printf '%s' "${CONTAINER_NAME}"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# This function is internal and should not be used in tests.
|
# This function is internal and should not be used in tests.
|
||||||
function __initialize_variables() {
|
function __initialize_variables() {
|
||||||
function __check_if_set() {
|
function __check_if_set() {
|
||||||
if [[ -z ${!1:-} ]]; then
|
if [[ ${!1+set} != 'set' ]]; then
|
||||||
echo "ERROR: (helper/setup.sh) '${1:?No variable name given to __check_if_set}' is not set" >&2
|
echo "ERROR: (helper/setup.sh) '${1:?No variable name given to __check_if_set}' is not set" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -234,9 +234,8 @@ function _should_have_correct_mail_headers() {
|
||||||
# but Amavis is changing that. It also changes protocol from SMTP to ESMTP.
|
# but Amavis is changing that. It also changes protocol from SMTP to ESMTP.
|
||||||
assert_line --index 7 --partial 'Received: from localhost (localhost [127.0.0.1])'
|
assert_line --index 7 --partial 'Received: from localhost (localhost [127.0.0.1])'
|
||||||
assert_line --index 8 --partial "by ${EXPECTED_FQDN} (Postfix) with ESMTP id"
|
assert_line --index 8 --partial "by ${EXPECTED_FQDN} (Postfix) with ESMTP id"
|
||||||
assert_line --index 14 'X-MS-Reactions: disallow'
|
assert_line --index 14 --partial 'Message-Id:'
|
||||||
assert_line --index 15 --partial 'Message-Id:'
|
assert_line --index 14 --partial "@${EXPECTED_FQDN}>"
|
||||||
assert_line --index 15 --partial "@${EXPECTED_FQDN}>"
|
|
||||||
|
|
||||||
# Mail contents example:
|
# Mail contents example:
|
||||||
#
|
#
|
||||||
|
|
|
@ -62,7 +62,7 @@ function teardown() { _default_teardown ; }
|
||||||
|
|
||||||
__init_container_without_waiting
|
__init_container_without_waiting
|
||||||
|
|
||||||
__should_generate_dkim_key 7
|
__should_generate_dkim_key 6
|
||||||
__assert_outputs_common_dkim_logs
|
__assert_outputs_common_dkim_logs
|
||||||
|
|
||||||
__should_have_tables_trustedhosts_for_domain
|
__should_have_tables_trustedhosts_for_domain
|
||||||
|
@ -78,7 +78,7 @@ function teardown() { _default_teardown ; }
|
||||||
# Only mount single config file (postfix-virtual.cf):
|
# Only mount single config file (postfix-virtual.cf):
|
||||||
__init_container_without_waiting "${PWD}/test/config/postfix-virtual.cf:/tmp/docker-mailserver/postfix-virtual.cf:ro"
|
__init_container_without_waiting "${PWD}/test/config/postfix-virtual.cf:/tmp/docker-mailserver/postfix-virtual.cf:ro"
|
||||||
|
|
||||||
__should_generate_dkim_key 6
|
__should_generate_dkim_key 5
|
||||||
__assert_outputs_common_dkim_logs
|
__assert_outputs_common_dkim_logs
|
||||||
|
|
||||||
__should_have_tables_trustedhosts_for_domain
|
__should_have_tables_trustedhosts_for_domain
|
||||||
|
@ -95,7 +95,7 @@ function teardown() { _default_teardown ; }
|
||||||
# Only mount single config file (postfix-accounts.cf):
|
# Only mount single config file (postfix-accounts.cf):
|
||||||
__init_container_without_waiting "${PWD}/test/config/postfix-accounts.cf:/tmp/docker-mailserver/postfix-accounts.cf:ro"
|
__init_container_without_waiting "${PWD}/test/config/postfix-accounts.cf:/tmp/docker-mailserver/postfix-accounts.cf:ro"
|
||||||
|
|
||||||
__should_generate_dkim_key 6
|
__should_generate_dkim_key 5
|
||||||
__assert_outputs_common_dkim_logs
|
__assert_outputs_common_dkim_logs
|
||||||
|
|
||||||
__should_have_tables_trustedhosts_for_domain
|
__should_have_tables_trustedhosts_for_domain
|
||||||
|
@ -113,7 +113,7 @@ function teardown() { _default_teardown ; }
|
||||||
__init_container_without_waiting '/tmp/docker-mailserver'
|
__init_container_without_waiting '/tmp/docker-mailserver'
|
||||||
|
|
||||||
# generate first key (with a custom selector)
|
# generate first key (with a custom selector)
|
||||||
__should_generate_dkim_key 5 '1024' 'domain1.tld' 'mailer'
|
__should_generate_dkim_key 4 '1024' 'domain1.tld' 'mailer'
|
||||||
__assert_outputs_common_dkim_logs
|
__assert_outputs_common_dkim_logs
|
||||||
# generate two additional keys different to the previous one
|
# generate two additional keys different to the previous one
|
||||||
__should_generate_dkim_key 2 '1024' 'domain2.tld,domain3.tld'
|
__should_generate_dkim_key 2 '1024' 'domain2.tld,domain3.tld'
|
||||||
|
@ -183,15 +183,15 @@ function __assert_logged_dkim_creation() {
|
||||||
|
|
||||||
function __assert_outputs_common_dkim_logs() {
|
function __assert_outputs_common_dkim_logs() {
|
||||||
refute_output --partial 'No entries found, no keys to make'
|
refute_output --partial 'No entries found, no keys to make'
|
||||||
assert_output --partial "Creating OpenDKIM config '/tmp/docker-mailserver/opendkim/KeyTable'"
|
assert_output --partial 'Creating DKIM KeyTable'
|
||||||
assert_output --partial "Creating OpenDKIM config '/tmp/docker-mailserver/opendkim/SigningTable'"
|
assert_output --partial 'Creating DKIM SigningTable'
|
||||||
assert_output --partial "Creating OpenDKIM config '/tmp/docker-mailserver/opendkim/TrustedHosts'"
|
assert_output --partial 'Creating DKIM TrustedHosts'
|
||||||
}
|
}
|
||||||
|
|
||||||
function __should_support_creating_key_of_size() {
|
function __should_support_creating_key_of_size() {
|
||||||
local EXPECTED_KEYSIZE=${1:-}
|
local EXPECTED_KEYSIZE=${1:-}
|
||||||
|
|
||||||
__should_generate_dkim_key 7 "${EXPECTED_KEYSIZE}"
|
__should_generate_dkim_key 6 "${EXPECTED_KEYSIZE}"
|
||||||
__assert_outputs_common_dkim_logs
|
__assert_outputs_common_dkim_logs
|
||||||
__assert_logged_dkim_creation 'localdomain2.com'
|
__assert_logged_dkim_creation 'localdomain2.com'
|
||||||
__assert_logged_dkim_creation 'localhost.localdomain'
|
__assert_logged_dkim_creation 'localhost.localdomain'
|
||||||
|
|
Loading…
Reference in New Issue