Compare commits
21 Commits
Author | SHA1 | Date |
---|---|---|
|
23bb1c8e50 | |
|
229ebba1b8 | |
|
df7a98ec50 | |
|
5027f4f5b6 | |
|
c2c48b2b83 | |
|
70d645d863 | |
|
a3571a88c1 | |
|
8ca2bd212c | |
|
0362fa682e | |
|
7c680a0fbc | |
|
a156c2c031 | |
|
6b1a566497 | |
|
02f068b2b2 | |
|
d0629f4cb6 | |
|
1756ba04fb | |
|
807f4f7118 | |
|
0fbbc44dd3 | |
|
3c833d8ee8 | |
|
dd595e0a05 | |
|
5686a4097a | |
|
309b5a9086 |
|
@ -71,16 +71,16 @@ jobs:
|
||||||
cache-buildx-
|
cache-buildx-
|
||||||
|
|
||||||
- name: 'Set up QEMU'
|
- name: 'Set up QEMU'
|
||||||
uses: docker/setup-qemu-action@v3.4.0
|
uses: docker/setup-qemu-action@v3.6.0
|
||||||
with:
|
with:
|
||||||
platforms: arm64
|
platforms: arm64
|
||||||
|
|
||||||
- name: 'Set up Docker Buildx'
|
- name: 'Set up Docker Buildx'
|
||||||
uses: docker/setup-buildx-action@v3.9.0
|
uses: docker/setup-buildx-action@v3.10.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.13.0
|
uses: docker/build-push-action@v6.15.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.6.1
|
uses: docker/metadata-action@v5.7.0
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
${{ secrets.DOCKER_REPOSITORY }}
|
${{ secrets.DOCKER_REPOSITORY }}
|
||||||
|
@ -35,12 +35,12 @@ jobs:
|
||||||
type=semver,pattern={{major}}.{{minor}}.{{patch}}
|
type=semver,pattern={{major}}.{{minor}}.{{patch}}
|
||||||
|
|
||||||
- name: 'Set up QEMU'
|
- name: 'Set up QEMU'
|
||||||
uses: docker/setup-qemu-action@v3.4.0
|
uses: docker/setup-qemu-action@v3.6.0
|
||||||
with:
|
with:
|
||||||
platforms: arm64
|
platforms: arm64
|
||||||
|
|
||||||
- name: 'Set up Docker Buildx'
|
- name: 'Set up Docker Buildx'
|
||||||
uses: docker/setup-buildx-action@v3.9.0
|
uses: docker/setup-buildx-action@v3.10.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.13.0
|
uses: docker/build-push-action@v6.15.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.9.0
|
uses: docker/setup-buildx-action@v3.10.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.13.0
|
uses: docker/build-push-action@v6.15.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.9.0
|
uses: docker/setup-buildx-action@v3.10.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.13.0
|
uses: docker/build-push-action@v6.15.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
tags: mailserver-testing:ci
|
tags: mailserver-testing:ci
|
||||||
|
|
39
CHANGELOG.md
39
CHANGELOG.md
|
@ -2,10 +2,47 @@
|
||||||
|
|
||||||
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.0...HEAD)
|
## [Unreleased](https://github.com/docker-mailserver/docker-mailserver/compare/v15.0.2...HEAD)
|
||||||
|
|
||||||
> **Note**: Changes and additions listed here are contained in the `:edge` image tag. These changes may not be as stable as released changes.
|
> **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)
|
## [v15.0.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v15.0.0)
|
||||||
|
|
||||||
### Breaking
|
### Breaking
|
||||||
|
|
|
@ -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 his own or his alias addresses. Addresses with [extension delimiters](http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
|
- 1 => Mail spoofing denied. Each user may only send with their own or their alias addresses. Addresses with [extension delimiters](http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
|
||||||
|
|
||||||
##### ENABLE_SRS
|
##### ENABLE_SRS
|
||||||
|
|
||||||
|
|
|
@ -14,18 +14,48 @@ hide:
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
!!! warning
|
Enabling Fail2Ban support can be done via ENV, but also requires granting at least the `NET_ADMIN` capability to interact with the kernel and ban IP addresses.
|
||||||
|
|
||||||
DMS must be launched with the `NET_ADMIN` capability in order to be able to install the NFTables rules that actually ban IP addresses. Thus, either include `--cap-add=NET_ADMIN` in the `docker run` command, or the equivalent in the `compose.yaml`:
|
!!! example
|
||||||
|
|
||||||
```yaml
|
=== "Docker Compose"
|
||||||
cap_add:
|
|
||||||
- NET_ADMIN
|
```yaml title="compose.yaml"
|
||||||
```
|
services:
|
||||||
|
mailserver:
|
||||||
|
environment:
|
||||||
|
- ENABLE_FAIL2BAN=1
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Docker CLI"
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --rm -it \
|
||||||
|
--cap-add=NET_ADMIN \
|
||||||
|
--env ENABLE_FAIL2BAN=1
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning "Security risk of adding non-default capabilties"
|
||||||
|
|
||||||
|
DMS bundles F2B into the image for convenience to simplify integration and deployment.
|
||||||
|
|
||||||
|
The [`NET_ADMIN`][security::cap-net-admin] and [`NET_RAW`][security::cap-net-raw] capabilities are not granted by default to the container root user, as they can be used to compromise security.
|
||||||
|
|
||||||
|
If this risk concerns you, it may be wiser to instead prefer only granting these capabilities to a dedicated Fail2Ban container ([example][lsio:f2b-image]).
|
||||||
|
|
||||||
!!! bug "Running Fail2Ban on Older Kernels"
|
!!! bug "Running Fail2Ban on Older Kernels"
|
||||||
|
|
||||||
DMS configures F2B to use NFTables, not IPTables (legacy). We have observed that older systems, for example NAS systems, do not support the modern NFTables rules. You will need to configure F2B to use legacy IPTables again, for example with the [``fail2ban-jail.cf``][github-file-f2bjail], see the [section on configuration further down below](#custom-files).
|
DMS configures F2B to use [NFTables][network::nftables], not [IPTables (legacy)][network::iptables-legacy].
|
||||||
|
|
||||||
|
We have observed that older systems (for example NAS systems), do not support the modern NFTables rules. You will need to configure F2B to use legacy IPTables again, for example with the [`fail2ban-jail.cf`][github-file-f2bjail], see the [section on configuration further down below](#custom-files).
|
||||||
|
|
||||||
|
[security::cap-net-admin]: https://0xn3va.gitbook.io/cheat-sheets/container/escaping/excessive-capabilities#cap_net_admin
|
||||||
|
[security::cap-net-raw]: https://0xn3va.gitbook.io/cheat-sheets/container/escaping/excessive-capabilities#cap_net_raw
|
||||||
|
[lsio:f2b-image]: https://docs.linuxserver.io/images/docker-fail2ban
|
||||||
|
[network::nftables]: https://en.wikipedia.org/wiki/Nftables
|
||||||
|
[network::iptables-legacy]: https://developers.redhat.com/blog/2020/08/18/iptables-the-two-variants-and-their-relationship-with-nftables#two_variants_of_the_iptables_command
|
||||||
|
|
||||||
### DMS Defaults
|
### DMS Defaults
|
||||||
|
|
||||||
|
|
|
@ -146,6 +146,25 @@ 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
|
||||||
|
@ -154,3 +173,6 @@ docker compose exec mailserver doveadm fts rescan -A
|
||||||
[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 his own or his alias addresses. Addresses with extension delimiters(http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
|
# 1 => Mail spoofing denied. Each user may only send with their own or their alias addresses. Addresses with extension delimiters(http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
|
||||||
SPOOF_PROTECTION=
|
SPOOF_PROTECTION=
|
||||||
|
|
||||||
# Enables the Sender Rewriting Scheme. SRS is needed if your mail server acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/master/README.md#sender-rewriting-scheme-crash-course) for further explanation.
|
# Enables the Sender Rewriting Scheme. SRS is needed if your mail server acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/main/README.rst) for further explanation.
|
||||||
# - **0** => Disabled
|
# - **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://wiki.dovecot.org/AuthDatabase/LDAP/AuthBinds
|
# https://doc.dovecot.org/2.4.0/core/config/auth/databases/ldap.html#authentication-bind
|
||||||
DOVECOT_AUTH_BIND=
|
DOVECOT_AUTH_BIND=
|
||||||
|
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
|
|
|
@ -12,9 +12,17 @@ if [[ -f /etc/dms-settings ]] && [[ $(_get_dms_env_value 'ENABLE_RSPAMD') -eq 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
KEYSIZE=2048
|
function _main() {
|
||||||
SELECTOR=mail
|
# Default parameters (updated by `_parse_arguments()`):
|
||||||
DOMAINS=
|
local KEYSIZE=2048
|
||||||
|
local SELECTOR=mail
|
||||||
|
local DMS_DOMAINS=
|
||||||
|
|
||||||
|
_require_n_parameters_or_print_usage 0 "${@}"
|
||||||
|
_parse_arguments "${@}"
|
||||||
|
|
||||||
|
_generate_dkim_keys
|
||||||
|
}
|
||||||
|
|
||||||
function __usage() {
|
function __usage() {
|
||||||
printf '%s' "${PURPLE}OPEN-DKIM${RED}(${YELLOW}8${RED})
|
printf '%s' "${PURPLE}OPEN-DKIM${RED}(${YELLOW}8${RED})
|
||||||
|
@ -57,122 +65,171 @@ ${ORANGE}EXAMPLES${RESET}
|
||||||
|
|
||||||
${ORANGE}EXIT STATUS${RESET}
|
${ORANGE}EXIT STATUS${RESET}
|
||||||
Exit status is 0 if command was successful. If wrong arguments are provided or arguments contain
|
Exit status is 0 if command was successful. If wrong arguments are provided or arguments contain
|
||||||
errors, the script will exit early with exit status 2.
|
errors, the script will exit early with a non-zero exit status.
|
||||||
|
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
|
|
||||||
_require_n_parameters_or_print_usage 0 "${@}"
|
function _parse_arguments() {
|
||||||
|
# Parse the command args through iteration:
|
||||||
|
while [[ ${#} -gt 0 ]]; do
|
||||||
|
case "${1}" in
|
||||||
|
|
||||||
while [[ ${#} -gt 0 ]]; do
|
( 'keysize' )
|
||||||
case "${1}" in
|
if [[ -n ${2:-} ]]; then
|
||||||
( 'keysize' )
|
KEYSIZE="${2}"
|
||||||
if [[ -n ${2+set} ]]; then
|
_log 'trace' "Keysize set to '${KEYSIZE}'"
|
||||||
KEYSIZE="${2}"
|
else
|
||||||
shift
|
_exit_with_error "No keysize provided after 'keysize' argument"
|
||||||
shift
|
fi
|
||||||
else
|
;;
|
||||||
_exit_with_error "No keysize provided after 'keysize' argument"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
( 'selector' )
|
( 'selector' )
|
||||||
if [[ -n ${2+set} ]]; then
|
if [[ -n ${2:-} ]]; then
|
||||||
# shellcheck disable=SC2034
|
SELECTOR="${2}"
|
||||||
SELECTOR="${2}"
|
_log 'trace' "Selector set to '${SELECTOR}'"
|
||||||
shift
|
else
|
||||||
shift
|
_exit_with_error "No selector provided after 'selector' argument"
|
||||||
else
|
fi
|
||||||
_exit_with_error "No selector provided after 'selector' argument"
|
;;
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
( 'domain' )
|
( 'domain' )
|
||||||
if [[ -n ${2+set} ]]; then
|
if [[ -n ${2:-} ]]; then
|
||||||
DOMAINS="${2}"
|
DMS_DOMAINS="${2}"
|
||||||
shift
|
_log 'trace' "Domain(s) set to '${DMS_DOMAINS}'"
|
||||||
shift
|
else
|
||||||
else
|
_exit_with_error "No domain(s) provided after 'domain' argument"
|
||||||
_exit_with_error "No domain(s) provided after 'domain' argument"
|
fi
|
||||||
fi
|
;;
|
||||||
;;
|
|
||||||
|
|
||||||
( * )
|
( 'help' )
|
||||||
__usage
|
__usage
|
||||||
_exit_with_error "Unknown options '${1}' ${2:+and \'${2}\'}"
|
exit 0
|
||||||
;;
|
;;
|
||||||
|
|
||||||
esac
|
( * )
|
||||||
done
|
__usage
|
||||||
|
_exit_with_error "Unknown option(s) ${*}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Discard these two args (option + value) now that they've been processed:
|
||||||
|
shift 2
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function _generate_dkim_keys() {
|
||||||
|
_generate_domains_config
|
||||||
|
if [[ ! -s ${DATABASE_VHOST} ]]; then
|
||||||
|
_log 'warn' 'No entries found, no keys to make'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Initialize OpenDKIM configs if necessary:
|
||||||
|
_create_opendkim_configs
|
||||||
|
|
||||||
|
# Generate a keypair per domain and add reference to OpenDKIM configs:
|
||||||
|
local ENTRY_KEY KEY_TABLE_ENTRY SIGNING_TABLE_ENTRY
|
||||||
|
while read -r DKIM_DOMAIN; do
|
||||||
|
_create_dkim_key "${DKIM_DOMAIN}"
|
||||||
|
|
||||||
|
# Create / Update OpenDKIM configs with new DKIM key:
|
||||||
|
ENTRY_KEY="${SELECTOR}._domainkey.${DKIM_DOMAIN}"
|
||||||
|
KEY_TABLE_ENTRY="${ENTRY_KEY} ${DKIM_DOMAIN}:${SELECTOR}:/etc/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private"
|
||||||
|
SIGNING_TABLE_ENTRY="*@${DKIM_DOMAIN} ${ENTRY_KEY}"
|
||||||
|
|
||||||
|
# If no existing entry, add one:
|
||||||
|
if ! grep -q "${KEY_TABLE_ENTRY}" "${KEY_TABLE_FILE}"; then
|
||||||
|
echo "${KEY_TABLE_ENTRY}" >> "${KEY_TABLE_FILE}"
|
||||||
|
fi
|
||||||
|
if ! grep -q "${SIGNING_TABLE_ENTRY}" "${SIGNING_TABLE_FILE}"; then
|
||||||
|
echo "${SIGNING_TABLE_ENTRY}" >> "${SIGNING_TABLE_FILE}"
|
||||||
|
fi
|
||||||
|
done < <(_get_valid_lines_from_file "${DATABASE_VHOST}")
|
||||||
|
|
||||||
|
# No longer needed, remove:
|
||||||
|
rm "${DATABASE_VHOST}"
|
||||||
|
|
||||||
|
# Ensure ownership is consistent for all content belonging to the base directory,
|
||||||
|
# During container startup, an internal copy will be made via `_setup_opendkim()`
|
||||||
|
# with ownership we expect, while this chown is for the benefit of the users ownership.
|
||||||
|
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'
|
DATABASE_VHOST='/tmp/vhost.dkim'
|
||||||
# Prepare a file with one domain per line:
|
|
||||||
function _generate_domains_config() {
|
function _generate_domains_config() {
|
||||||
local TMP_VHOST='/tmp/vhost.dkim.tmp'
|
local TMP_VHOST='/tmp/vhost.dkim.tmp'
|
||||||
|
|
||||||
# Generate the default vhost (equivalent to /etc/postfix/vhost),
|
# Generate the default vhost (equivalent to /etc/postfix/vhost),
|
||||||
# unless CLI arg DOMAINS provided an alternative list to use instead:
|
# unless CLI arg DMS_DOMAINS provided an alternative list to use instead:
|
||||||
if [[ -z ${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
|
||||||
else
|
else
|
||||||
tr ',' '\n' <<< "${DOMAINS}" >"${TMP_VHOST}"
|
tr ',' '\n' <<< "${DMS_DOMAINS}" >"${TMP_VHOST}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# uses DATABASE_VHOST + TMP_VHOST:
|
# Uses DATABASE_VHOST + TMP_VHOST:
|
||||||
_create_vhost
|
_create_vhost
|
||||||
}
|
}
|
||||||
|
|
||||||
_generate_domains_config
|
# `opendkim-genkey` generates two files at the configured `--directory`:
|
||||||
if [[ ! -s ${DATABASE_VHOST} ]]; then
|
# - <selector>.private (Private key, PEM encoded)
|
||||||
_log 'warn' 'No entries found, no keys to make'
|
# - <selector>.txt (Public key, formatted as a TXT record for a RFC 1035 DNS Zone file)
|
||||||
exit 0
|
function _create_dkim_key() {
|
||||||
fi
|
DKIM_DOMAIN=${1?Expected to be provided a domain}
|
||||||
|
|
||||||
while read -r DKIM_DOMAIN; do
|
OPENDKIM_DOMAINKEY_DIR="${OPENDKIM_BASE_DIR}/keys/${DKIM_DOMAIN}"
|
||||||
mkdir -p "/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}"
|
mkdir -p "${OPENDKIM_DOMAINKEY_DIR}"
|
||||||
|
|
||||||
if [[ ! -f "/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private" ]]; then
|
DKIM_KEY_FILE="${OPENDKIM_DOMAINKEY_DIR}/${SELECTOR}.private"
|
||||||
_log 'info' "Creating DKIM private key '/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}/${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 \
|
opendkim-genkey \
|
||||||
--bits="${KEYSIZE}" \
|
--bits="${KEYSIZE}" \
|
||||||
--subdomains \
|
--subdomains \
|
||||||
--domain="${DKIM_DOMAIN}" \
|
--domain="${DKIM_DOMAIN}" \
|
||||||
--selector="${SELECTOR}" \
|
--selector="${SELECTOR}" \
|
||||||
--directory="/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}"
|
--directory="${OPENDKIM_DOMAINKEY_DIR}"
|
||||||
fi
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# fix permissions to use the same user:group as /tmp/docker-mailserver/opendkim/keys
|
OPENDKIM_BASE_DIR='/tmp/docker-mailserver/opendkim'
|
||||||
chown -R "$(stat -c '%U:%G' /tmp/docker-mailserver/opendkim/keys)" "/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}"
|
KEY_TABLE_FILE="${OPENDKIM_BASE_DIR}/KeyTable"
|
||||||
|
SIGNING_TABLE_FILE="${OPENDKIM_BASE_DIR}/SigningTable"
|
||||||
|
TRUSTED_HOSTS_FILE="${OPENDKIM_BASE_DIR}/TrustedHosts"
|
||||||
|
# Create configs if missing:
|
||||||
|
function _create_opendkim_configs() {
|
||||||
|
mkdir -p "${OPENDKIM_BASE_DIR}"
|
||||||
|
local OPENDKIM_CONFIGS=(
|
||||||
|
"${KEY_TABLE_FILE}"
|
||||||
|
"${SIGNING_TABLE_FILE}"
|
||||||
|
"${TRUSTED_HOSTS_FILE}"
|
||||||
|
)
|
||||||
|
|
||||||
# write to KeyTable if necessary
|
# Only create if the file doesn't exist (avoids modifying mtime):
|
||||||
KEYTABLEENTRY="${SELECTOR}._domainkey.${DKIM_DOMAIN} ${DKIM_DOMAIN}:${SELECTOR}:/etc/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private"
|
for FILE in "${OPENDKIM_CONFIGS[@]}"; do
|
||||||
if [[ ! -f "/tmp/docker-mailserver/opendkim/KeyTable" ]]; then
|
if [[ ! -f "${FILE}" ]]; then
|
||||||
_log 'debug' 'Creating DKIM KeyTable'
|
_log 'debug' "Creating OpenDKIM config '${FILE}'"
|
||||||
echo "${KEYTABLEENTRY}" >/tmp/docker-mailserver/opendkim/KeyTable
|
touch "${FILE}"
|
||||||
else
|
|
||||||
if ! grep -q "${KEYTABLEENTRY}" "/tmp/docker-mailserver/opendkim/KeyTable"; then
|
|
||||||
echo "${KEYTABLEENTRY}" >>/tmp/docker-mailserver/opendkim/KeyTable
|
|
||||||
fi
|
fi
|
||||||
fi
|
done
|
||||||
|
|
||||||
# write to SigningTable if necessary
|
# If file exists but is empty, add default hosts to trust:
|
||||||
SIGNINGTABLEENTRY="*@${DKIM_DOMAIN} ${SELECTOR}._domainkey.${DKIM_DOMAIN}"
|
if [[ ! -s "${TRUSTED_HOSTS_FILE}" ]]; then
|
||||||
if [[ ! -f /tmp/docker-mailserver/opendkim/SigningTable ]]; then
|
_log 'debug' 'Adding default trust to OpenDKIM TrustedHosts config'
|
||||||
_log 'debug' 'Creating DKIM SigningTable'
|
echo "127.0.0.1" > "${TRUSTED_HOSTS_FILE}"
|
||||||
echo "*@${DKIM_DOMAIN} ${SELECTOR}._domainkey.${DKIM_DOMAIN}" >/tmp/docker-mailserver/opendkim/SigningTable
|
echo "localhost" >> "${TRUSTED_HOSTS_FILE}"
|
||||||
else
|
|
||||||
if ! grep -q "${SIGNINGTABLEENTRY}" /tmp/docker-mailserver/opendkim/SigningTable; then
|
|
||||||
echo "${SIGNINGTABLEENTRY}" >>/tmp/docker-mailserver/opendkim/SigningTable
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
done < <(_get_valid_lines_from_file "${DATABASE_VHOST}")
|
}
|
||||||
|
|
||||||
# create TrustedHosts if missing
|
_main "${@}"
|
||||||
if [[ -d /tmp/docker-mailserver/opendkim ]] && [[ ! -f /tmp/docker-mailserver/opendkim/TrustedHosts ]]; then
|
|
||||||
_log 'debug' 'Creating DKIM TrustedHosts'
|
|
||||||
echo "127.0.0.1" >/tmp/docker-mailserver/opendkim/TrustedHosts
|
|
||||||
echo "localhost" >>/tmp/docker-mailserver/opendkim/TrustedHosts
|
|
||||||
fi
|
|
||||||
|
|
|
@ -68,9 +68,10 @@ 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:
|
||||||
# reject_unknown_sender_domain: https://github.com/docker-mailserver/docker-mailserver/issues/3716#issuecomment-1868033234
|
# Custom sender restrictions overview: https://github.com/docker-mailserver/docker-mailserver/pull/4379#issuecomment-2670365917
|
||||||
|
# `reject_unknown_sender_domain`: https://github.com/docker-mailserver/docker-mailserver/issues/3716#issuecomment-1868033234
|
||||||
dms_smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unknown_sender_domain
|
dms_smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unknown_sender_domain
|
||||||
# Submission ports 587 and 465 support for SPOOF_PROTECTION=1
|
# `SPOOF_PROTECTION=1` support requires prepending `reject_authenticated_sender_login_mismatch`
|
||||||
mua_sender_restrictions = reject_authenticated_sender_login_mismatch, $dms_smtpd_sender_restrictions
|
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,4 +8,5 @@
|
||||||
/^\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
|
||||||
/^Content-Type:/i PREPEND X-MS-Reactions: disallow
|
/^\s*X-MS-Reactions:/ IGNORE
|
||||||
|
/^\s*Message-Id:/i PREPEND X-MS-Reactions: disallow
|
||||||
|
|
|
@ -36,20 +36,46 @@ function _pre_installation_steps() {
|
||||||
apt-get "${QUIET}" install --no-install-recommends "${EARLY_PACKAGES[@]}" 2>/dev/null
|
apt-get "${QUIET}" install --no-install-recommends "${EARLY_PACKAGES[@]}" 2>/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Install third-party commands to /usr/local/bin
|
||||||
function _install_utils() {
|
function _install_utils() {
|
||||||
|
local ARCH_A
|
||||||
|
ARCH_A=$(uname --machine)
|
||||||
|
# Alternate naming convention support: x86_64 (amd64) / aarch64 (arm64)
|
||||||
|
# https://en.wikipedia.org/wiki/X86-64#Industry_naming_conventions
|
||||||
|
local ARCH_B
|
||||||
|
case "${ARCH_A}" in
|
||||||
|
( 'x86_64' ) ARCH_B='amd64' ;;
|
||||||
|
( 'aarch64' ) ARCH_B='arm64' ;;
|
||||||
|
( * )
|
||||||
|
_log 'error' "Unsupported arch: '${ARCH_A}'"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# TIP: `*.tar.gz` releases tend to forget to reset UID/GID ownership when archiving.
|
||||||
|
# When extracting with `tar` as `root` the archived UID/GID is kept, unless using `--no-same-owner`.
|
||||||
|
# Likewise when the binary is in a nested location the full archived path
|
||||||
|
# must be provided + `--strip-components` to extract the file to the target directory.
|
||||||
|
# Doing this avoids the need for (`mv` + `rm`) or (`--to-stdout` + `chmod +x`)
|
||||||
_log 'debug' 'Installing utils sourced from Github'
|
_log 'debug' 'Installing utils sourced from Github'
|
||||||
|
|
||||||
_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/bin/jaq
|
curl -sSfL "https://github.com/01mf02/jaq/releases/download/${JAQ_TAG}/jaq-$(uname -m)-unknown-linux-gnu" -o /usr/local/bin/jaq
|
||||||
chmod +x /usr/bin/jaq
|
chmod +x /usr/local/bin/jaq
|
||||||
|
|
||||||
|
_log 'trace' 'Installing step'
|
||||||
|
local STEP_RELEASE='0.28.2'
|
||||||
|
curl -sSfL "https://github.com/smallstep/cli/releases/download/v${STEP_RELEASE}/step_linux_${STEP_RELEASE}_${ARCH_B}.tar.gz" \
|
||||||
|
| tar -xz --directory /usr/local/bin --no-same-owner --strip-components=2 "step_${STEP_RELEASE}/bin/step"
|
||||||
|
|
||||||
_log 'trace' 'Installing swaks'
|
_log 'trace' 'Installing swaks'
|
||||||
|
# `perl-doc` is required for `swaks --help` to work:
|
||||||
apt-get "${QUIET}" install --no-install-recommends perl-doc
|
apt-get "${QUIET}" install --no-install-recommends perl-doc
|
||||||
local SWAKS_VERSION='20240103.0'
|
local SWAKS_VERSION='20240103.0'
|
||||||
local SWAKS_RELEASE="swaks-${SWAKS_VERSION}"
|
local SWAKS_RELEASE="swaks-${SWAKS_VERSION}"
|
||||||
curl -sSfL "https://github.com/jetmore/swaks/releases/download/v${SWAKS_VERSION}/${SWAKS_RELEASE}.tar.gz" | tar -xz
|
curl -sSfL "https://github.com/jetmore/swaks/releases/download/v${SWAKS_VERSION}/${SWAKS_RELEASE}.tar.gz" \
|
||||||
mv "${SWAKS_RELEASE}/swaks" /usr/local/bin
|
| tar -xz --directory /usr/local/bin --no-same-owner --strip-components=1 "${SWAKS_RELEASE}/swaks"
|
||||||
rm -r "${SWAKS_RELEASE}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _install_postfix() {
|
function _install_postfix() {
|
||||||
|
|
|
@ -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 -name "*.sh" -print0)
|
readarray -d '' STAGING_FILES_RSPAMD < <(find "${RSPAMD_DMS_D}" -type f -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+set} ]]; then
|
if [[ -n ${1:-} ]]; 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+set} ]]; then
|
if [[ -z ${1:-} ]]; 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+set} ]]; then
|
if [[ -z ${2:-} ]]; 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+set} ]]; then
|
if [[ -n ${LOG_LEVEL:-} ]]; 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
|
||||||
|
|
|
@ -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+set} ]]; then
|
if [[ -z ${1:-} ]]; 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+set} ]]; then
|
elif [[ -z ${2:-} ]]; 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'
|
||||||
|
|
|
@ -181,6 +181,9 @@ 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
|
||||||
|
|
|
@ -144,5 +144,5 @@ function __log_fixes() {
|
||||||
# Volume permissions should be corrected:
|
# Volume permissions should be corrected:
|
||||||
# https://github.com/docker-mailserver/docker-mailserver-helm/issues/137
|
# https://github.com/docker-mailserver/docker-mailserver-helm/issues/137
|
||||||
chmod 755 /var/log/mail/
|
chmod 755 /var/log/mail/
|
||||||
chmod 640 /var/log/mail/*
|
find /var/log/mail/ -type f -exec chmod 640 {} +
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,11 @@ function _setup_opendkim() {
|
||||||
# check if any keys are available
|
# check if any keys are available
|
||||||
if [[ -e /tmp/docker-mailserver/opendkim/KeyTable ]]; then
|
if [[ -e /tmp/docker-mailserver/opendkim/KeyTable ]]; then
|
||||||
cp -a /tmp/docker-mailserver/opendkim/* /etc/opendkim/
|
cp -a /tmp/docker-mailserver/opendkim/* /etc/opendkim/
|
||||||
_log 'trace' "DKIM keys added for: $(find /etc/opendkim/keys/ -maxdepth 1 -type f -printf '%f ')"
|
|
||||||
|
local DKIM_DOMAINS
|
||||||
|
DKIM_DOMAINS=$(find /etc/opendkim/keys/ -maxdepth 1 -type f -printf '%f ')
|
||||||
|
_log 'trace' "DKIM keys added for: ${DKIM_DOMAINS}"
|
||||||
|
|
||||||
chown -R opendkim:opendkim /etc/opendkim/
|
chown -R opendkim:opendkim /etc/opendkim/
|
||||||
chmod -R 0700 /etc/opendkim/keys/
|
chmod -R 0700 /etc/opendkim/keys/
|
||||||
else
|
else
|
||||||
|
|
|
@ -95,6 +95,11 @@ function _setup_save_states() {
|
||||||
function _setup_adjust_state_permissions() {
|
function _setup_adjust_state_permissions() {
|
||||||
[[ ! -d ${DMS_STATE_DIR} ]] && return 0
|
[[ ! -d ${DMS_STATE_DIR} ]] && 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
|
||||||
# can change the values in the Docker image, causing an ownership mismatch.
|
# can change the values in the Docker image, causing an ownership mismatch.
|
||||||
|
|
|
@ -93,13 +93,19 @@ 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
|
||||||
sed -i -E 's|(smtpd_sender_restrictions =)|\1 check_sender_access texthash:/tmp/docker-mailserver/postfix-send-access.cf,|' /etc/postfix/main.cf
|
# Prefer to prepend to our specialized variant instead:
|
||||||
|
# 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'
|
||||||
|
|
|
@ -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+set} ]]; then
|
elif [[ -n ${CONTAINER_NAME:-} ]]; 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 [[ ${!1+set} != 'set' ]]; then
|
if [[ -z ${!1:-} ]]; 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,8 +234,9 @@ 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 --partial 'Message-Id:'
|
assert_line --index 14 'X-MS-Reactions: disallow'
|
||||||
assert_line --index 14 --partial "@${EXPECTED_FQDN}>"
|
assert_line --index 15 --partial 'Message-Id:'
|
||||||
|
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 6
|
__should_generate_dkim_key 7
|
||||||
__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 5
|
__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
|
||||||
|
@ -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 5
|
__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
|
||||||
|
@ -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 4 '1024' 'domain1.tld' 'mailer'
|
__should_generate_dkim_key 5 '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 DKIM KeyTable'
|
assert_output --partial "Creating OpenDKIM config '/tmp/docker-mailserver/opendkim/KeyTable'"
|
||||||
assert_output --partial 'Creating DKIM SigningTable'
|
assert_output --partial "Creating OpenDKIM config '/tmp/docker-mailserver/opendkim/SigningTable'"
|
||||||
assert_output --partial 'Creating DKIM TrustedHosts'
|
assert_output --partial "Creating OpenDKIM config '/tmp/docker-mailserver/opendkim/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 6 "${EXPECTED_KEYSIZE}"
|
__should_generate_dkim_key 7 "${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