c851f5b6aa
* chore(refactor): DRY up the `_setup_ssl` method - `/etc/postfix/ssl` was a bit misleading in usage here. As a maintainer (of my own contribution!) I was confused why only `/etc/postfix/ssl` was referenced and not `/etc/dovecot/ssl`. - The postfix specific path is unnecessary, dovecot was referencing it via it's config, the same can be done from postfix to a generic DMS specific config location instead. - This location is defined and created early as `/etc/dms/tls` (with var `DMS_TLS_PATH`). All usage of `/etc/postfix/ssl` has been replaced, making it easier to grok. Several `mkdir` commands related to this have been dropped as a result. - Likewise, a related `TMP_DMS_TLS_PATH` var provides a reference to the config volume path `/tmp/docker-mailserver` which is used for conditions on presently hard-coded paths. - Other values that benefit from being DRY have been lifted up into vars. Definitely easier to follow now and makes some further opportunities clearer to tackle in a future refactor. - `chmod` has been updated where appropriate. Public key/cert is acceptable to have as readable by non-root users (644). The custom type with single fullchain file was not root accessible only, but should as it contains a private key. - That said, the security benefit can be a bit moot due to source files that were copied remain present, the user would be responsible to ensure similar permissions on their source files. - I've not touched LetsEncrypt section as I don't have time to investigate into that yet (not familiar with that portion). --- * chore: Remove mkcert logic and dovecot cert - No longer serving a purpose. - Our own TLS startup script handles a variety of cert scenarios, while the dropped code was always generating a self-signed cert and persisting an unused cert regardless with `ONE_DIR=1`. - To avoid similar issues that DH params had with doveadm validating filepath values in the SSL config, the default dummy values match postfix pointing to "snakeoil" cert. That serves the same purpose as mkcert was covering in the image. - Bonus, no more hassle with differing mkcert target paths for users replacing our supplied Dovecot with the latest community edition. --- * Error handling for SSL_TYPE - Added a panic utility to exit early when SSL_TYPE conditions are misconfigured. - Some info text had order of key/cert occurrence swapped to be consistent with key then cert. - Some existing comments moved and rephrased. - Additional comments added. - `-f` test for cert files instead of `-e` (true also for directories/devices/symlinks). - _notify messages lifted out of conditionals so that they always output when the case is hit. - ~~Empty SSL_TYPE collapsed into catch all panic, while it's contents is now mapped to a new 'disabled' value.~~ --- * Use sedfile + improve sed expressions + update case style - Uses sedfile when appropriate (file change intentional, not optional match/check). - sed expressions modified to be DRY and reduce escaping via `-r` flag (acceptable if actual text content contains no `?`,`+`,`()` or `{}` characters, [otherwise they must be escaped](https://www.gnu.org/software/sed/manual/html_node/Extended-regexps.html)). - sed captures anything matched between the parenthesis`()` and inserts it via `\1` as part of the replacement. - case statements adopt the `(` prefix, adopting recent shell style for consistency. --- * Refactor SSL_TYPE=disabled - Postfix is also disabled now. - Included heavy inline documentation reference for maintainers. - Dropped an obsolete postfix config option 'use_tls' on the relayhost function, it was replaced by 'security_level'. --- * I'm a friggin' sed wizard now - The `modern` TLS_LEVEL is the default values for the configs they modify. As such, `sedfile` outputs an "Error" which isn't an actual concern, back to regular `sed`. - I realized that multiple edits for the same file can all be done at once via `-e` (assuming other sed options are the same for each operation), and that `g` suffix is global scope for single line match, not whole file (default as sed iterates through individual lines). - Some postfix replacements have `smtp` and `smtpd` lines, collapsed into a single `smtpd?` instead now that I know sed better. --- * tests(fix): Tests that require SSL/TLS to pass - SSL_TYPE=snakeoil added as temporary workaround. - nmap tests are being dropped. These were added about 4-5 years ago, I have since made these redundant with the `testssl.sh` tests. - Additionally the `--link` option is deprecated and IIRC these grades were a bit misleading when I initially used nmap in my own TLS cipher suite update PRs in the past. - The removed SSL test is already handled in mail_ssl_manual.bats ldap test: - Replace `--link` alias option with `--network` and alias assignment. - Parameterized some values and added the `SSL_TYPE` to resolve the starttls test failure. privacy test: - Also needed `SSL_TYPE` to pass the starttls test. `tests.bats` had another starttls test for imap: - Workaround for now is to give the main test container `SSL_TYPE=snakeoil`. --- * Remove the expired lets-encrypt cert This expired in March 2021. It was originally required when first added back in 2016 as LetsEncrypt was fairly new and not as broadly accepted into OS trust stores. No longer the case today. --- * chore: Housekeeping Not required for this PR branch, little bit of tidying up while working on these two test files. - privacy test copied over content when extracted from `tests.bats` that isn't relevant. - ldap test was not as easy to identify the source of DOVECOT_TLS. Added comment to make the prefix connection to `configomat.sh` and `.ext` files more easier to find. - Additionally converted the two localhost FQDN to vars. --- * Default SSL_TYPE becomes `''` (aka equivalent to desired `disabled` case) - This is to prevent other tests from failing by hitting the panic catchall case. - More ideal would be adjusting tests to default to `disabled`, rather than treating `disabled` as an empty / unset SSL_TYPE value. --- * Add inline documentation for `dms_panic` - This could later be better formatted and placed into contributor docs. Panic with kill (shutdown) not exit (errex): - `kill 1` from `_shutdown` will send SIGTERM signal to PID 1 (init process). - `exit 1` within the `start-mailserver.sh` init scripts context, will just exit the initialization script leaving the container running when it shouldn't. The two previous `_shutdown` methods can benefit from using `dms_panic` wrapper instead to standardize on panic messages. |
||
---|---|---|
.github | ||
config | ||
docs | ||
target | ||
test | ||
.all-contributorsrc | ||
.dockerignore | ||
.editorconfig | ||
.gitignore | ||
.gitmodules | ||
CHANGELOG.md | ||
CODE_OF_CONDUCT.md | ||
CONTRIBUTORS.md | ||
docker-compose.yml | ||
Dockerfile | ||
LICENSE | ||
mailserver.env | ||
Makefile | ||
README.md | ||
setup.sh | ||
setup.sh.10.2.0 | ||
VERSION |
Docker Mailserver
A fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). Only configuration files, no SQL database. Keep it simple and versioned. Easy to deploy and upgrade. Documentation via MkDocs. Why this image was created.
If you have issues, read the full README
and the documentation for your version (default is edge
) first before opening an issue. The issue tracker is for issues, not for personal support.
- Included Services
- Issues and Contributing
- Requirements
- Usage
- Examples
- Environment Variables
- Documentation
- Release Notes
Included Services
- Postfix with SMTP or LDAP auth
- Dovecot for SASL, IMAP (or POP3), with LDAP Auth, Sieve and quotas
- Amavis
- SpamAssassin supporting custom rules
- ClamAV with automatic updates
- OpenDKIM
- OpenDMARC
- Fail2ban
- Fetchmail
- Postscreen
- Postgrey
- LetsEncrypt and self-signed certificates
- Setup script to easily configure and maintain your mailserver
- Basic Sieve support using dovecot
- SASLauthd with LDAP auth
- Persistent data and state
- CI/CD
- Extension Delimiters (
you+extension@example.com
go toyou@example.com
)
Requirements
Recommended:
- 1 Core
- 2GB RAM
- Swap enabled for the container
Minimum:
- 1 vCore
- 512MB RAM
Note: You'll need to deactivate some services like ClamAV to be able to run on a host with 512MB of RAM. Even with 1G RAM you may run into problems without swap, see FAQ.
Usage
Available Images / Tags - Tagging Convention
CI/CD will automatically build, test and push new images to container registries. Currently, the following registries are supported:
All workflows are using the tagging convention listed below. It is subsequently applied to all images.
Event | Ref | Image Tags |
---|---|---|
push |
refs/heads/master |
edge |
push tag |
refs/tags/[v]1.2.3 |
1.2.3 , 1.2 , 1 , latest |
Get the tools
Since Docker Mailserver v10.2.0
, setup.sh
functionality is included within the Docker image. The external convenience script is no longer required if you prefer using docker exec <CONTAINER NAME> setup <COMMAND>
instead.
Note: If you're using Docker or Docker Compose and are new to Docker Mailserver, it is recommended to use the script
setup.sh
for convenience.
DMS_GITHUB_URL='https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master'
wget "${DMS_GITHUB_URL}/docker-compose.yml"
wget "${DMS_GITHUB_URL}/mailserver.env"
wget "${DMS_GITHUB_URL}/setup.sh"
chmod a+x ./setup.sh
./setup.sh help
If no docker-mailserver
container is running, any ./setup.sh
command will check online for the :latest
image tag (the current stable release), performing a pull
if necessary followed by running the command in a temporary container.
NOTE If you're using Docker Mailserver version v10.1.x
or below, you will need to get setup.sh
with a specific version. Substitute <VERSION>
with the mail server version you're using: wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/<VERSION>/setup.sh
.
Create a docker-compose environment
- Install the latest docker-compose
- Edit
docker-compose.yml
to your liking- substitute
<HOSTNAME>
and<DOMAINNAME>
according to your domain - if you want to use SELinux for the
./config/:/tmp/docker-mailserver/
mount, append-z
or-Z
- substitute
- Configure the mailserver container to your liking by editing
mailserver.env
(Documentation)- this file supports only simple
VAR=VAL
(don't quote your values) - variable substitution is not supported (e.g. 🚫
OVERRIDE_HOSTNAME=$HOSTNAME.$DOMAINNAME
🚫)
- this file supports only simple
Get up and running
docker-compose up -d mailserver
# for SELinux, use -Z
./setup.sh [-Z] email add <user@domain> [<password>]
./setup.sh [-Z] alias add postmaster@<domain> <user@domain>
./setup.sh [-Z] config dkim
If you're seeing error messages about unchecked errors, please verify that you're using the right version of setup.sh
. Refer to the Get the tools section and / or execute ./setup.sh help
and read the VERSION
section.
In case you're using LDAP, the setup looks a bit different as you do not add user accounts directly. Postfix doesn't know your domain(s) and you need to provide it when configuring DKIM:
./setup.sh config dkim domain '<domain.tld>[,<domain2.tld>]'
If you want to see detailed usage information, run ./setup.sh config dkim help
.
Miscellaneous
DNS - DKIM
When keys are generated, you can configure your DNS server by just pasting the content of config/opendkim/keys/domain.tld/mail.txt
to set up DKIM. See the documentation for more details.
Custom User Changes & Patches
If you'd like to change, patch or alter files or behavior of docker-mailserver
, you can use a script. See the documentation for a detailed explanation.
Update docker-mailserver
Make sure to read the CHANGELOG before, to be prepared for possible breaking changes.
docker-compose pull
docker-compose down
docker-compose up -d mailserver
You should see the new version number on startup, for example: [ TASKLOG ] Welcome to docker-mailserver 10.1.2
.
You're done! And don't forget to have a look at the remaining functions of the setup.sh
script with ./setup.sh help
.
Supported Operating Systems
We are currently providing support for Linux. Windows is not supported and is known to cause problems. Similarly, macOS is not officially supported - but you may get it to work there. In the end, Linux should be your preferred operating system for this image, especially when using this mailserver in production.
Bare Domains
If you want to use a bare domain (hostname
== domainname
), see FAQ.
Support for Multiple Domains
docker-mailserver
supports multiple domains out of the box, so you can do this:
./setup.sh email add user1@docker.example.com
./setup.sh email add user1@mail.example.de
./setup.sh email add user1@server.example.org
SPF/Forwarding Problems
If you got any problems with SPF and/or forwarding mails, give SRS a try. You enable SRS by setting ENABLE_SRS=1
. See the variable description for further information.
Ports
See the documentation for further details and best practice advice, especially regarding security concerns.
Mailboxes (aka IMAP Folders)
INBOX
is setup by default with the special IMAP folders Drafts
, Sent
, Junk
and Trash
. You can learn how to modify or add your own folders (including additional special folders like Archive
) by visiting our docs page Customizing IMAP Folders for more information.
Examples
With Relevant Environmental Variables
This example provides you only with a basic example of what a minimal setup could look like. We strongly recommend that you go through the configuration file yourself and adjust everything to your needs. The default docker-compose.yml can be used for the purpose out-of-the-box, see the usage section.
version: '3.8'
services:
mailserver:
image: docker.io/mailserver/docker-mailserver:latest
hostname: mail
domainname: example.com
container_name: mailserver
ports:
- "25:25"
- "143:143"
- "587:587"
- "993:993"
volumes:
- ./data/maildata:/var/mail
- ./data/mailstate:/var/mail-state
- ./data/maillogs:/var/log/mail
- /etc/localtime:/etc/localtime:ro
- ./config/:/tmp/docker-mailserver/
environment:
- ENABLE_SPAMASSASSIN=1
- SPAMASSASSIN_SPAM_TO_INBOX=1
- ENABLE_CLAMAV=1
- ENABLE_FAIL2BAN=1
- ENABLE_POSTGREY=1
- ENABLE_SASLAUTHD=0
- ONE_DIR=1
- DMS_DEBUG=0
cap_add:
- NET_ADMIN
- SYS_PTRACE
restart: always
LDAP setup
version: '3.8'
services:
mailserver:
image: docker.io/mailserver/docker-mailserver:latest
hostname: mail
domainname: example.com
container_name: mailserver
ports:
- "25:25"
- "143:143"
- "587:587"
- "993:993"
volumes:
- ./data/maildata:/var/mail
- ./data/mailstate:/var/mail-state
- ./data/maillogs:/var/log/mail
- /etc/localtime:/etc/localtime:ro
- ./config/:/tmp/docker-mailserver/
environment:
- ENABLE_SPAMASSASSIN=1
- SPAMASSASSIN_SPAM_TO_INBOX=1
- ENABLE_CLAMAV=1
- ENABLE_FAIL2BAN=1
- ENABLE_POSTGREY=1
- ONE_DIR=1
- DMS_DEBUG=0
- ENABLE_LDAP=1
- LDAP_SERVER_HOST=ldap # your ldap container/IP/ServerName
- LDAP_SEARCH_BASE=ou=people,dc=localhost,dc=localdomain
- LDAP_BIND_DN=cn=admin,dc=localhost,dc=localdomain
- LDAP_BIND_PW=admin
- LDAP_QUERY_FILTER_USER=(&(mail=%s)(mailEnabled=TRUE))
- LDAP_QUERY_FILTER_GROUP=(&(mailGroupMember=%s)(mailEnabled=TRUE))
- LDAP_QUERY_FILTER_ALIAS=(|(&(mailAlias=%s)(objectClass=PostfixBookMailForward))(&(mailAlias=%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE)))
- LDAP_QUERY_FILTER_DOMAIN=(|(&(mail=*@%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE))(&(mailGroupMember=*@%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE))(&(mailalias=*@%s)(objectClass=PostfixBookMailForward)))
- DOVECOT_PASS_FILTER=(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))
- DOVECOT_USER_FILTER=(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))
- ENABLE_SASLAUTHD=1
- SASLAUTHD_MECHANISMS=ldap
- SASLAUTHD_LDAP_SERVER=ldap
- SASLAUTHD_LDAP_BIND_DN=cn=admin,dc=localhost,dc=localdomain
- SASLAUTHD_LDAP_PASSWORD=admin
- SASLAUTHD_LDAP_SEARCH_BASE=ou=people,dc=localhost,dc=localdomain
- SASLAUTHD_LDAP_FILTER=(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%U))
- POSTMASTER_ADDRESS=postmaster@localhost.localdomain
- POSTFIX_MESSAGE_SIZE_LIMIT=100000000
cap_add:
- NET_ADMIN
- SYS_PTRACE
restart: always