1b1877f025
* chore: Extract letsencrypt logic into methods This allows other scripts to share the functionality to discover the correct letsencrypt folder from the 3 possible locations (where specific order is important). As these methods should now return a string value, the `return 1` after a panic is now dropped. * chore: Update comments The todo is resolved with this PR, `_setup_ssl` will be called by both cert conditional statements with purpose for each better documented to maintainers at the start of the logic block. * refactor: Defer most logic to helper/ssl.sh The loop is no longer required, extraction is delegated to `_setup_ssl` now. For the change event prevention, we retrieve the relevant FQDN via the new helper method, beyond that it's just indentation diff. `check-for-changes.sh` adjusted to allow locally scoped var declarations by wrapping a function. Presently no loop control flow is needed so this seems fine. Made it clear that `CHANGED` is local and `CHKSUM_FILE` is not. Panic scope doesn't require `SSL_TYPE` for context, it's clearly`letsencrypt`. * fix: Correctly match wildcard results Now that the service configs are properly updated, when the services restart they will return a cert with the SAN `DNS:*.example.test`, which is valid for `mail.example.test`, however the test function did not properly account for this in the regexp query. Resolved by truncating the left-most DNS label from FQDN and adding a third check to match a returned wildcard DNS result. Extracted out the common logic to create the regexp query and renamed the methods to communicate more clearly that they check the FQDN is supported, not necessarily explicitly listed by the cert. * tests(letsencrypt): Enable remaining tests These will now pass. Adjusted comments accordingly. Added an additional test on a fake FQDN that should still be valid to a wildcard cert (SNI validation in a proper setup would reject the connection afterwards). Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com> |
||
---|---|---|
.github | ||
config-examples | ||
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 | ||
VERSION |
Docker Mailserver
A production-ready 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.
Originally created by @tomav, docker-mailserver is now maintained by volunteers since January 2021.
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 mail-server
- Basic Sieve support using dovecot
- SASLauthd with LDAP auth (please see the note down below)
- 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
- 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 | Image Tags |
---|---|
push on master |
edge |
push tag |
1.2.3 , 1.2 , 1 , latest |
Get the Tools
Since Docker Mailserver v10.2.0
, setup.sh
functionality is included within the container image. The external convenience script is no longer required if you prefer using docker exec <CONTAINER NAME> setup <COMMAND>
instead. If you're 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
Create a docker-compose Environment
- Install the latest docker-compose
- Edit
docker-compose.yml
to your liking- substitute
mail
(hostname) andexample.com
(domainname) according to your FQDN - if you want to use SELinux for the
./docker-data/dms/config/:/tmp/docker-mailserver/
mount, append-z
or-Z
- substitute
- Configure the mailserver container to your liking by editing
mailserver.env
(Documentation), but keep in mind this.env
file:- only basic
VAR=VAL
is supported (do not quote your values!) - variable substitution is not supported (e.g. 🚫
OVERRIDE_HOSTNAME=$HOSTNAME.$DOMAINNAME
🚫)
- only basic
Note: If you're using podman, make sure to read the related documentation
Get up and running
First Things First
Use docker-compose up / down
, not docker-compose start / stop
. Otherwise, the container is not properly destroyed and you may experience problems during startup because of inconsistent state.
You are able to get a full overview of how the configuration works by either running:
./setup.sh help
which includes the options ofsetup.sh
.docker run --rm docker.io/mailserver/docker-mailserver:latest setup help
which provides you with all the information on configuration provided "inside" the container itself.
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 docker pull ...
if necessary followed by running the command in a temporary container.
$ ./setup.sh help
Image 'docker.io/mailserver/docker-mailserver:latest' not found. Pulling ...
SETUP(1)
NAME
setup - 'docker-mailserver' Administration & Configuration script
...
$ docker run --rm docker.io/mailserver/docker-mailserver:latest setup help
SETUP(1)
NAME
setup - 'docker-mailserver' Administration & Configuration script
...
Starting for the first time
On first start, you will need to add at least one email account (unless you're using LDAP). You have two minutes to do so, otherwise DMS will shutdown and restart. You can add accounts with the following two methods:
- Use
setup.sh
:./setup.sh email add <user@domain>
- Run the command directly in the container:
docker exec -ti <CONTAINER NAME> setup email add <user@domain>
You can then proceed by creating the postmaster alias and by creating DKIM keys.
docker-compose up -d mailserver
# you may add some more users
# for SELinux, use -Z
./setup.sh [-Z] email add <user@domain> [<password>]
# and configure aliases, DKIM and more
./setup.sh [-Z] alias add postmaster@<domain> <user@domain>
Miscellaneous
DNS - DKIM
You can (and you should) generate DKIM keys by running
./setup.sh [-Z] config dkim
If you want to see detailed usage information, run
./setup.sh config dkim help
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>]'
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.
Updating docker-mailserver
Make sure to read the CHANGELOG before updating to new versions, 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 mail-server 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
container_name: mailserver
hostname: mail
domainname: example.com
ports:
- "25:25"
- "143:143"
- "587:587"
- "993:993"
volumes:
- ./docker-data/dms/mail-data/:/var/mail/
- ./docker-data/dms/mail-state/:/var/mail-state/
- ./docker-data/dms/mail-logs/:/var/log/mail/
- ./docker-data/dms/config/:/tmp/docker-mailserver/
- /etc/localtime:/etc/localtime:ro
environment:
- ENABLE_SPAMASSASSIN=1
- SPAMASSASSIN_SPAM_TO_INBOX=1
- ENABLE_CLAMAV=1
- ENABLE_FAIL2BAN=1
- ENABLE_POSTGREY=1
- ENABLE_SASLAUTHD=0
- ONE_DIR=1
cap_add:
- NET_ADMIN
- SYS_PTRACE
restart: always
LDAP Setup
Note There are currently no LDAP maintainers. If you encounter issues, please raise them in the issue tracker, but be aware that the core maintainers team will most likely not be able to help you. We would appreciate and we encourage everyone to actively participate in maintaining LDAP-related code by becoming a maintainer!
version: '3.8'
services:
mailserver:
image: docker.io/mailserver/docker-mailserver:latest
container_name: mailserver
hostname: mail
domainname: example.com
ports:
- "25:25"
- "143:143"
- "587:587"
- "993:993"
volumes:
- ./docker-data/dms/mail-data/:/var/mail/
- ./docker-data/dms/mail-state/:/var/mail-state/
- ./docker-data/dms/mail-logs/:/var/log/mail/
- ./docker-data/dms/config/:/tmp/docker-mailserver/
- /etc/localtime:/etc/localtime:ro
environment:
- ENABLE_SPAMASSASSIN=1
- SPAMASSASSIN_SPAM_TO_INBOX=1
- ENABLE_CLAMAV=1
- ENABLE_FAIL2BAN=1
- ENABLE_POSTGREY=1
- ONE_DIR=1
- 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