[DOCKER][MAIL] Fused services into single container

This commit is contained in:
Pastilhas 2020-10-27 11:21:10 +00:00 committed by Hugo Sales
parent 86a95def01
commit 419a2ceb1a
Signed by untrusted user: someonewithpc
GPG Key ID: 7D0C7EAFC9D835A0
89 changed files with 492 additions and 1759 deletions

View File

@ -83,6 +83,24 @@ services:
tty: false
ports:
- 6379:6379
mail:
build: docker/php
restart: always
tty: true
ports:
- "25:25"
- "143:143"
- "587:587"
- "993:993"
environment:
- DOMAINNAME=mail.test
- HOSTNAME=mail
- POSTMASTER=postmaster@mail.test
- SSL_CERT=/etc/dovecot/ssl/mailserver.crt
- SSL_KEY=/etc/dovecot/ssl/mailserver.key
volumes:
database:

47
docker/mail/Dockerfile Normal file
View File

@ -0,0 +1,47 @@
FROM debian:buster-slim
ENV \
DEBIAN_FRONTEND=noninteractive \
DOMAINNAME=example.com \
MAILNAME=mail \
POSTMASTER=postmaster@example.com \
SSL_CERT=/etc/ssl/mailserver.crt \
SSL_KEY=/etc/ssl/mailserver.key
RUN \
apt-get update \
&& apt-get upgrade -y \
&& apt-get install -y --no-install-recommends \
postfix \
telnet \
dovecot-core \
dovecot-pop3d \
dovecot-imapd \
dovecot-lmtpd \
supervisor \
rsyslog \
opendkim \
opendkim-tools \
&& apt-get autoclean \
&& apt-get autoremove
RUN \
rm /etc/postfix/main.cf /etc/postfix/master.cf /etc/dovecot/dovecot.conf /etc/dovecot/conf.d/* /etc/rsyslog.conf /etc/rsyslog.d/* \
&& groupadd -g 5000 vmail \
&& useradd -d /var/mail -M -s /usr/sbin/nologin -u 5000 -g 5000 vmail \
&& usermod -aG vmail postfix \
&& usermod -aG vmail dovecot \
&& usermod -aG vmail opendkim\
&& mkdir -p -m 751 /var/mail/ \
&& mkdir -p -m 755 /etc/mail/ \
&& mkdir -p -m 755 /etc/ssl/ \
&& mkdir -p -m 700 /etc/opendkim/keys \
&& chown vmail:vmail /var/mail \
&& chown opendkim:opendkim /etc/opendkim/keys
# Copy config files
COPY rootfs/ /
EXPOSE 25 143 587 993
ENTRYPOINT /usr/bin/start.sh

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2019 Jeffrey Boehm
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,9 +0,0 @@
POP3 STARTTLS 127.0.0.1:110
POP3S 127.0.0.1:995
IMAP STARTTLS 127.0.0.1:143
IMAPS 127.0.0.1:993
SMTP 127.0.0.1:25
Mail Submission STARTTLS 127.0.0.1:587
Management Interface 127.0.0.1:81/manager/
Webmail 127.0.0.1:81/webmail/
Rspamd interface 127.0.0.1:81/rspamd/

View File

@ -1,10 +0,0 @@
FROM mysql:5.7
LABEL maintainer="jeff@ressourcenkonflikt.de"
ENV MYSQL_DATABASE=mailserver \
MYSQL_USER=mailserver \
MYSQL_PASSWORD=changeme \
MYSQL_ROOT_PASSWORD=changeme
COPY rootfs/ /
VOLUME /run/mysqld

View File

@ -1,82 +0,0 @@
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
# Export von Tabelle mail_aliases
# ------------------------------------------------------------
DROP TABLE IF EXISTS `mail_aliases`;
CREATE TABLE `mail_aliases` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) DEFAULT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`destination` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `alias_idx` (`domain_id`,`name`,`destination`),
KEY `IDX_85AF3A56115F0EE5` (`domain_id`),
CONSTRAINT `FK_5F12BB39115F0EE5` FOREIGN KEY (`domain_id`) REFERENCES `mail_domains` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Export von Tabelle mail_domains
# ------------------------------------------------------------
DROP TABLE IF EXISTS `mail_domains`;
CREATE TABLE `mail_domains` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_56C63EF25E237E06` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
# Export von Tabelle mail_users
# ------------------------------------------------------------
DROP TABLE IF EXISTS `mail_users`;
CREATE TABLE `mail_users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) DEFAULT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`password` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_idx` (`name`,`domain_id`),
KEY `IDX_20400786115F0EE5` (`domain_id`),
CONSTRAINT `FK_1483A5E9115F0EE5` FOREIGN KEY (`domain_id`) REFERENCES `mail_domains` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Export von Tabelle migration_versions
# ------------------------------------------------------------
DROP TABLE IF EXISTS `migration_versions`;
CREATE TABLE `migration_versions` (
`version` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
LOCK TABLES `migration_versions` WRITE;
/*!40000 ALTER TABLE `migration_versions` DISABLE KEYS */;
INSERT INTO `migration_versions` (`version`)
VALUES
('20180320164351'),
('20180320171339');
/*!40000 ALTER TABLE `migration_versions` ENABLE KEYS */;
UNLOCK TABLES;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

View File

@ -1,226 +0,0 @@
-- Roundcube Webmail initial database structure
/*!40014 SET FOREIGN_KEY_CHECKS=0 */;
-- Table structure for table `session`
CREATE TABLE `session` (
`sess_id` varchar(128) NOT NULL,
`changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`ip` varchar(40) NOT NULL,
`vars` mediumtext NOT NULL,
PRIMARY KEY(`sess_id`),
INDEX `changed_index` (`changed`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- Table structure for table `users`
CREATE TABLE `users` (
`user_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`username` varchar(128) BINARY NOT NULL,
`mail_host` varchar(128) NOT NULL,
`created` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`last_login` datetime DEFAULT NULL,
`failed_login` datetime DEFAULT NULL,
`failed_login_counter` int(10) UNSIGNED DEFAULT NULL,
`language` varchar(5),
`preferences` longtext,
PRIMARY KEY(`user_id`),
UNIQUE `username` (`username`, `mail_host`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- Table structure for table `cache`
CREATE TABLE `cache` (
`user_id` int(10) UNSIGNED NOT NULL,
`cache_key` varchar(128) BINARY NOT NULL,
`expires` datetime DEFAULT NULL,
`data` longtext NOT NULL,
PRIMARY KEY (`user_id`, `cache_key`),
CONSTRAINT `user_id_fk_cache` FOREIGN KEY (`user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
INDEX `expires_index` (`expires`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- Table structure for table `cache_shared`
CREATE TABLE `cache_shared` (
`cache_key` varchar(255) BINARY NOT NULL,
`expires` datetime DEFAULT NULL,
`data` longtext NOT NULL,
PRIMARY KEY (`cache_key`),
INDEX `expires_index` (`expires`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- Table structure for table `cache_index`
CREATE TABLE `cache_index` (
`user_id` int(10) UNSIGNED NOT NULL,
`mailbox` varchar(255) BINARY NOT NULL,
`expires` datetime DEFAULT NULL,
`valid` tinyint(1) NOT NULL DEFAULT '0',
`data` longtext NOT NULL,
CONSTRAINT `user_id_fk_cache_index` FOREIGN KEY (`user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
INDEX `expires_index` (`expires`),
PRIMARY KEY (`user_id`, `mailbox`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- Table structure for table `cache_thread`
CREATE TABLE `cache_thread` (
`user_id` int(10) UNSIGNED NOT NULL,
`mailbox` varchar(255) BINARY NOT NULL,
`expires` datetime DEFAULT NULL,
`data` longtext NOT NULL,
CONSTRAINT `user_id_fk_cache_thread` FOREIGN KEY (`user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
INDEX `expires_index` (`expires`),
PRIMARY KEY (`user_id`, `mailbox`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- Table structure for table `cache_messages`
CREATE TABLE `cache_messages` (
`user_id` int(10) UNSIGNED NOT NULL,
`mailbox` varchar(255) BINARY NOT NULL,
`uid` int(11) UNSIGNED NOT NULL DEFAULT '0',
`expires` datetime DEFAULT NULL,
`data` longtext NOT NULL,
`flags` int(11) NOT NULL DEFAULT '0',
CONSTRAINT `user_id_fk_cache_messages` FOREIGN KEY (`user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
INDEX `expires_index` (`expires`),
PRIMARY KEY (`user_id`, `mailbox`, `uid`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- Table structure for table `contacts`
CREATE TABLE `contacts` (
`contact_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`del` tinyint(1) NOT NULL DEFAULT '0',
`name` varchar(128) NOT NULL DEFAULT '',
`email` text NOT NULL,
`firstname` varchar(128) NOT NULL DEFAULT '',
`surname` varchar(128) NOT NULL DEFAULT '',
`vcard` longtext NULL,
`words` text NULL,
`user_id` int(10) UNSIGNED NOT NULL,
PRIMARY KEY(`contact_id`),
CONSTRAINT `user_id_fk_contacts` FOREIGN KEY (`user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
INDEX `user_contacts_index` (`user_id`,`del`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- Table structure for table `contactgroups`
CREATE TABLE `contactgroups` (
`contactgroup_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` int(10) UNSIGNED NOT NULL,
`changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`del` tinyint(1) NOT NULL DEFAULT '0',
`name` varchar(128) NOT NULL DEFAULT '',
PRIMARY KEY(`contactgroup_id`),
CONSTRAINT `user_id_fk_contactgroups` FOREIGN KEY (`user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
INDEX `contactgroups_user_index` (`user_id`,`del`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
CREATE TABLE `contactgroupmembers` (
`contactgroup_id` int(10) UNSIGNED NOT NULL,
`contact_id` int(10) UNSIGNED NOT NULL,
`created` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
PRIMARY KEY (`contactgroup_id`, `contact_id`),
CONSTRAINT `contactgroup_id_fk_contactgroups` FOREIGN KEY (`contactgroup_id`)
REFERENCES `contactgroups`(`contactgroup_id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `contact_id_fk_contacts` FOREIGN KEY (`contact_id`)
REFERENCES `contacts`(`contact_id`) ON DELETE CASCADE ON UPDATE CASCADE,
INDEX `contactgroupmembers_contact_index` (`contact_id`)
) /*!40000 ENGINE=INNODB */;
-- Table structure for table `identities`
CREATE TABLE `identities` (
`identity_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` int(10) UNSIGNED NOT NULL,
`changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`del` tinyint(1) NOT NULL DEFAULT '0',
`standard` tinyint(1) NOT NULL DEFAULT '0',
`name` varchar(128) NOT NULL,
`organization` varchar(128) NOT NULL DEFAULT '',
`email` varchar(128) NOT NULL,
`reply-to` varchar(128) NOT NULL DEFAULT '',
`bcc` varchar(128) NOT NULL DEFAULT '',
`signature` longtext,
`html_signature` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY(`identity_id`),
CONSTRAINT `user_id_fk_identities` FOREIGN KEY (`user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
INDEX `user_identities_index` (`user_id`, `del`),
INDEX `email_identities_index` (`email`, `del`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- Table structure for table `dictionary`
CREATE TABLE `dictionary` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, -- redundant, for compat. with Galera Cluster
`user_id` int(10) UNSIGNED DEFAULT NULL, -- NULL here is for "shared dictionaries"
`language` varchar(5) NOT NULL,
`data` longtext NOT NULL,
CONSTRAINT `user_id_fk_dictionary` FOREIGN KEY (`user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
UNIQUE `uniqueness` (`user_id`, `language`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- Table structure for table `searches`
CREATE TABLE `searches` (
`search_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` int(10) UNSIGNED NOT NULL,
`type` int(3) NOT NULL DEFAULT '0',
`name` varchar(128) NOT NULL,
`data` text,
PRIMARY KEY(`search_id`),
CONSTRAINT `user_id_fk_searches` FOREIGN KEY (`user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
UNIQUE `uniqueness` (`user_id`, `type`, `name`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- Table structure for table `filestore`
CREATE TABLE `filestore` (
`file_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` int(10) UNSIGNED NOT NULL,
`context` varchar(32) NOT NULL,
`filename` varchar(128) NOT NULL,
`mtime` int(10) NOT NULL,
`data` longtext NOT NULL,
PRIMARY KEY (`file_id`),
CONSTRAINT `user_id_fk_filestore` FOREIGN KEY (`user_id`)
REFERENCES `users` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
UNIQUE `uniqueness` (`user_id`, `context`, `filename`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- Table structure for table `system`
CREATE TABLE `system` (
`name` varchar(64) NOT NULL,
`value` mediumtext,
PRIMARY KEY(`name`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
/*!40014 SET FOREIGN_KEY_CHECKS=1 */;
INSERT INTO `system` (`name`, `value`) VALUES ('roundcube-version', '2019092900');

View File

@ -1,125 +1,28 @@
version: '3.5'
version: '3.8'
services:
# Creates self signed tls certificates. Remove if you
# use your own.
ssl:
image: jeboehm/mailserver-ssl:latest
build: ./ssl
env_file: .env
volumes:
- data-tls:/media/tls:rw
# Responsible for storing users and their aliases. Remove
# if you already have a MySQL server.
db:
image: jeboehm/mailserver-db:latest
build: ./db
restart: on-failure:5
env_file: .env
volumes:
- data-db:/var/lib/mysql
# The Mail Transfer Agent (Postfix) receives incoming mail
# on TCP port 25.
mta:
image: jeboehm/mailserver-mta:latest
build: ./mta
restart: on-failure:5
env_file: .env
volumes:
- data-tls:/media/tls:ro
# For using external certificates uncomment the following lines
# and change the path on the left side of the colon.
# - /home/user/certs/mail.example.com.crt:/media/tls/mailserver.crt:ro
# - /home/user/certs/mail.example.com.key:/media/tls/mailserver.key:ro
ports:
- "0.0.0.0:25:25"
# The Mail Delivery Agent (Dovecot) is responsible for storing
# incoming mail into a users mailbox and also delivers them
# via POP3 or IMAP4.
mda:
image: jeboehm/mailserver-mda:latest
build: ./mda
restart: on-failure:5
env_file: .env
volumes:
- data-mail:/var/vmail
- data-tls:/media/tls:ro
# For using external certificates uncomment the following lines
# and change the path on the left side of the colon.
# - /home/user/certs/mail.example.com.crt:/media/tls/mailserver.crt:ro
# - /home/user/certs/mail.example.com.key:/media/tls/mailserver.key:ro
ports:
- "0.0.0.0:143:143"
- "0.0.0.0:993:993"
- "0.0.0.0:110:110"
- "0.0.0.0:995:995"
- "0.0.0.0:587:587"
# The admin (mailserver-admin) and webmail (roundcube) interfaces
# live here. Can be removed if not needed.
web:
image: jeboehm/mailserver-web:latest
build: ./web
restart: on-failure:5
env_file: .env
volumes:
- data-dkim:/media/dkim
# For use with jwilder/nginx-proxy.
# environment:
# - VIRTUAL_HOST=mail.example.com
ports:
- "0.0.0.0:81:80"
# Incoming spam is (hopefully) filtered by rspamd which runs
# in this service.
filter:
image: jeboehm/mailserver-filter:latest
build: ./filter
restart: on-failure:5
env_file: .env
volumes:
- data-filter:/var/lib/rspamd
- data-dkim:/media/dkim
links:
- virus:virus.local
# Incoming viruses or malware is detected and rejected by
# this service. Can be removed if FILTER_VIRUS is set to false.
virus:
image: jeboehm/mailserver-virus:latest
build: ./virus
restart: on-failure:5
env_file: .env
volumes:
- data-virusdb:/var/lib/clamav
# If you want unhealthy containers to be restarted automatically
# just uncomment the following lines.
# autoheal:
# image: willfarrell/autoheal:latest
# restart: always
# networks: []
# volumes:
# - /var/run/docker.sock:/var/run/docker.sock:ro
# environment:
# - AUTOHEAL_CONTAINER_LABEL=de.ressourcenkonflikt.docker-mailserver.autoheal
# Optional service: extend ClamAV (used in the virus service)
# by downloading additional databases provided by different
# companys. Run it regulary.
# virus_unof_sig_updater:
# build: ./virus/contrib/unofficial-sigs
# env_file: .env
# volumes:
# - data-virusdb:/var/lib/clamav
mail:
image: mail
build: .
environment:
DOMAINNAME: mail.test
MAILNAME: mail.mail.test
POSTMASTER: postmaster@mail.test
SSL_CERT: /etc/ssl/mailserver.crt
SSL_KEY: /etc/ssl/mailserver.key
container_name: mail
privileged: true
ports:
- "25:25"
- "143:143"
- "587:587"
- "993:993"
volumes:
- maildata:/var/mail
- mailconf:/etc/mail
volumes:
data-db:
data-dkim:
data-mail:
data-tls:
data-filter:
data-virusdb:
maildata:
mailconf:

14
docker/mail/exec.sh Normal file
View File

@ -0,0 +1,14 @@
#!/bin/sh
if [ "$1" == "new" ]
then
if [ "$2" == "domain" ]
then
docker exec mail new-domain "${*,3}"
elif [ "$2" == "user" ]
then
docker exec mail new-user "${*,3}"
elif [ "$2" == "alias" ]
docker exec mail new-alias "${*,3}"
fi
fi

View File

@ -1,45 +0,0 @@
ARG DOCKERIZE_VER=0.6.0
ARG ALPINE_VER=3.9
FROM jwilder/dockerize:${DOCKERIZE_VER} AS dockerize
FROM alpine:${ALPINE_VER}
LABEL maintainer="jeff@ressourcenkonflikt.de"
LABEL vendor="https://github.com/jeboehm/docker-mailserver"
LABEL de.ressourcenkonflikt.docker-mailserver.autoheal="true"
ENV FILTER_VIRUS=false \
FILTER_VIRUS_HOST=virus.local \
WAITSTART_TIMEOUT=1m \
CONTROLLER_PASSWORD=changeme
RUN apk --no-cache add \
openssl \
rspamd \
rspamd-client \
rspamd-controller \
rspamd-proxy && \
mkdir /run/rspamd && \
touch \
/etc/rspamd/local.d/antivirus.conf \
/etc/rspamd/local.d/worker-controller.inc && \
chown -R rspamd \
/run/rspamd \
/var/lib/rspamd \
/etc/rspamd/local.d/antivirus.conf \
/etc/rspamd/local.d/worker-controller.inc && \
wget -O /usr/share/rspamd/bayes.spam.sqlite https://rspamd.com/rspamd_statistics/bayes.spam.sqlite && \
wget -O /usr/share/rspamd/bayes.ham.sqlite https://rspamd.com/rspamd_statistics/bayes.ham.sqlite && \
apk --no-cache del \
openssl
COPY --from=dockerize /usr/local/bin/dockerize /usr/local/bin
COPY rootfs/ /
EXPOSE 11332 11334
USER rspamd
VOLUME ["/var/lib/rspamd"]
HEALTHCHECK CMD wget -O- -T 10 http://127.0.0.1:11334/stat
CMD ["/usr/local/bin/entrypoint.sh"]

View File

@ -1,11 +0,0 @@
{{ $filter_virus := eq (or ($.Env.FILTER_VIRUS) "") "true" }}
{{ if $filter_virus }}
clamav {
scan_mime_parts = false;
symbol = "CLAM_VIRUS";
type = "clamav";
action = "reject";
servers = "{{$.Env.FILTER_VIRUS_HOST}}:3310";
}
{{ end }}

View File

@ -1 +0,0 @@
autolearn = true;

View File

@ -1,2 +0,0 @@
path = "/media/dkim/$domain.$selector.key";
selector_map = "/media/dkim/dkim_selectors.map";

View File

@ -1 +0,0 @@
type = console

View File

@ -1,11 +0,0 @@
group "rbl" {
symbol "RBL_NIXSPAM_BAD" {
weight = 7.0;
description = "From address is listed in ix.dnsbl.manitu.net BL";
}
symbol "RBL_NIXSPAM" {
weight = 0.0;
description = "Unrecognised result from ix.dnsbl.manitu.net BL";
}
}

View File

@ -1 +0,0 @@
extended_spam_headers = true;

View File

@ -1,7 +0,0 @@
dns {
timeout = 1s;
sockets = 16;
retransmits = 2;
nameserver = [ "8.8.8.8:53", "8.8.4.4:53", "1.1.1.1:53", "1.0.0.1:53" ];
}

View File

@ -1,9 +0,0 @@
rbls {
nixspam {
symbol = "RBL_NIXSPAM";
rbl = "ix.dnsbl.manitu.net";
returncodes {
RBL_NIXSPAM_BAD = "127.0.0.2";
}
}
}

View File

@ -1,5 +0,0 @@
bind_socket = "*:11334";
secure_ip = "127.0.0.1";
secure_ip = "::1";
secure_ip = "172.16.0.0/12";
password = "{{$.Env.CONTROLLER_PASSWORD_ENC}}"

View File

@ -1 +0,0 @@
bind_socket = "*:11332";

View File

@ -1,32 +0,0 @@
#!/bin/sh
FILTER_VIRUS_ARGS=""
if [ ${FILTER_VIRUS} == "true" ]
then
FILTER_VIRUS_ARGS="-wait tcp://${FILTER_VIRUS_HOST}:3310"
fi
if ! [ -f /var/lib/rspamd/bayes.spam.sqlite ]
then
cp /usr/share/rspamd/bayes.spam.sqlite /var/lib/rspamd/bayes.spam.sqlite
fi
if ! [ -f /var/lib/rspamd/bayes.ham.sqlite ]
then
cp /usr/share/rspamd/bayes.ham.sqlite /var/lib/rspamd/bayes.ham.sqlite
fi
if [ "${CONTROLLER_PASSWORD}" == "changeme" ]
then
# q1 is disabled in rspamd.
export CONTROLLER_PASSWORD_ENC="q1"
else
export CONTROLLER_PASSWORD_ENC=`rspamadm pw -e -p ${CONTROLLER_PASSWORD}`
fi
dockerize \
-template /etc/rspamd/local.d/antivirus.conf.templ:/etc/rspamd/local.d/antivirus.conf \
-template /etc/rspamd/local.d/worker-controller.inc.templ:/etc/rspamd/local.d/worker-controller.inc \
${FILTER_VIRUS_ARGS} \
-timeout ${WAITSTART_TIMEOUT} \
/usr/sbin/rspamd -c /etc/rspamd/rspamd.conf -f

View File

@ -1,47 +0,0 @@
ARG DOCKERIZE_VER=0.6.0
ARG ALPINE_VER=3.9
FROM jwilder/dockerize:${DOCKERIZE_VER} AS dockerize
FROM alpine:${ALPINE_VER}
LABEL maintainer="jeff@ressourcenkonflikt.de"
LABEL vendor="https://github.com/jeboehm/docker-mailserver"
LABEL de.ressourcenkonflikt.docker-mailserver.autoheal="true"
ENV MYSQL_HOST=db \
MYSQL_USER=root \
MYSQL_PASSWORD=changeme \
MYSQL_DATABASE=mailserver \
MAILNAME=mail.example.com \
POSTMASTER=postmaster@example.com \
SUBMISSION_HOST=mta \
ENABLE_POP3=true \
ENABLE_IMAP=true \
SSL_CERT=/media/tls/mailserver.crt \
SSL_KEY=/media/tls/mailserver.key \
WAITSTART_TIMEOUT=1m
RUN apk --no-cache add \
curl \
dovecot \
dovecot-lmtpd \
dovecot-mysql \
dovecot-pigeonhole-plugin \
dovecot-pop3d \
dovecot-submissiond && \
adduser -h /var/vmail -u 5000 -D vmail && \
rm -rf /etc/ssl/dovecot && \
openssl dhparam -out /etc/dovecot/dh.pem 2048
COPY --from=dockerize /usr/local/bin/dockerize /usr/local/bin
COPY rootfs/ /
RUN sievec /etc/dovecot/sieve/global/spam-to-folder.sieve && \
sievec /etc/dovecot/sieve/global/learn-ham.sieve && \
sievec /etc/dovecot/sieve/global/learn-spam.sieve
EXPOSE 2003 4190 143 110 993 995
VOLUME ["/var/vmail"]
HEALTHCHECK CMD echo "? LOGOUT" | nc 127.0.0.1 143 | grep "Dovecot ready."
CMD ["/usr/local/bin/entrypoint.sh"]

View File

@ -1,3 +0,0 @@
auth_mechanisms = plain login
!include auth-sql.conf.ext

View File

@ -1,2 +0,0 @@
log_path = /dev/stderr
info_log_path = /dev/stdout

View File

@ -1,10 +0,0 @@
mail_location = maildir:/var/vmail/%d/%n/Maildir
mail_home = /var/vmail/%d/%n
mail_uid = vmail
mail_gid = vmail
mail_privileged_group = vmail
mail_plugins = $mail_plugins quota
namespace inbox {
inbox = yes
}

View File

@ -1,56 +0,0 @@
protocols = lmtp submission
{{ $enable_pop3 := eq (or ($.Env.ENABLE_POP3) "") "true" }}
{{ $enable_imap := eq (or ($.Env.ENABLE_IMAP) "") "true" }}
{{ if $enable_imap }}
service imap-login {
inet_listener imap {
#port = 143
}
inet_listener imaps {
#port = 993
#ssl = yes
}
}
service imap {
}
protocols = $protocols imap
{{ end }}
{{ if $enable_pop3 }}
service pop3-login {
inet_listener pop3 {
#port = 110
}
inet_listener pop3s {
#port = 995
#ssl = yes
}
}
service pop3 {
}
protocols = $protocols pop3
{{ end }}
service submission-login {
inet_listener submission {
#port = 587
}
}
service submission {
#process_limit = 1024
}
service lmtp {
inet_listener lmtp {
port = 2003
}
}

View File

@ -1,4 +0,0 @@
ssl = yes
ssl_cert = </media/tls/mailserver.crt
ssl_key = </media/tls/mailserver.key
ssl_dh = </etc/dovecot/dh.pem

View File

@ -1,5 +0,0 @@
postmaster_address = {{ .Env.POSTMASTER }}
hostname = {{ .Env.MAILNAME }}
submission_host = {{ .Env.SUBMISSION_HOST }}
lda_mailbox_autosubscribe = yes
recipient_delimiter = -

View File

@ -1,24 +0,0 @@
namespace inbox {
mailbox Drafts {
auto = subscribe
special_use = \Drafts
}
mailbox Junk {
auto = subscribe
special_use = \Junk
}
mailbox Trash {
auto = subscribe
special_use = \Trash
}
mailbox Sent {
special_use = \Sent
}
mailbox "Sent Messages" {
special_use = \Sent
}
}

View File

@ -1,3 +0,0 @@
protocol imap {
mail_plugins = $mail_plugins imap_sieve imap_quota
}

View File

@ -1,3 +0,0 @@
protocol lmtp {
mail_plugins = $mail_plugins sieve
}

View File

@ -1,7 +0,0 @@
protocols = $protocols sieve
service managesieve-login {
inet_listener sieve {
port = 4190
}
}

View File

@ -1,6 +0,0 @@
submission_relay_host = {{ .Env.SUBMISSION_HOST }}
submission_relay_port = 25
submission_relay_trusted = yes
protocol submission {
}

View File

@ -1,4 +0,0 @@
plugin {
quota = maildir:User quota
quota_exceeded_message = "Benutzer %u hat das Speichervolumen überschritten. / User %u has exhausted allowed storage space."
}

View File

@ -1,20 +0,0 @@
plugin {
sieve_plugins = sieve_imapsieve sieve_extprograms
sieve_global_extensions = +vnd.dovecot.pipe
sieve = file:~/sieve;active=~/.dovecot.sieve
sieve_before = /etc/dovecot/sieve/global/spam-to-folder.sieve
sieve_pipe_bin_dir = /usr/lib/dovecot/sieve
sieve_pipe_exec_timeout = 60s
recipient_delimiter = -
# From somewhere to junk folder
imapsieve_mailbox1_name = Junk
imapsieve_mailbox1_causes = COPY
imapsieve_mailbox1_before = file:/etc/dovecot/sieve/global/learn-spam.sieve
# From junk folder to elsewhere
imapsieve_mailbox2_name = *
imapsieve_mailbox2_from = Junk
imapsieve_mailbox2_causes = COPY
imapsieve_mailbox2_before = file:/etc/dovecot/sieve/global/learn-ham.sieve
}

View File

@ -1,9 +0,0 @@
passdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}

View File

@ -1,6 +0,0 @@
driver = mysql
connect = host={{ .Env.MYSQL_HOST }} dbname={{ .Env.MYSQL_DATABASE }} user={{ .Env.MYSQL_USER }} password={{ .Env.MYSQL_PASSWORD }}
default_pass_scheme = SHA256-CRYPT
password_query = SELECT mail_users.name AS user, mail_domains.name AS domain, password FROM mail_users JOIN mail_domains ON mail_users.domain_id = mail_domains.id WHERE mail_users.name = '%n' AND mail_domains.name = '%d' AND enabled = 1 AND NOT (send_only = 1 AND "%s" in ('imap', 'pop3'));
user_query = SELECT concat('*:storage=', quota, 'M') AS quota_rule FROM mail_users JOIN mail_domains ON mail_users.domain_id = mail_domains.id WHERE mail_users.name = '%n' AND mail_domains.name = '%d';
iterate_query = SELECT mail_users.name AS username, mail_domains.name AS domain FROM mail_users JOIN mail_domains ON mail_users.domain_id = mail_domains.id;

View File

@ -1,2 +0,0 @@
require ["vnd.dovecot.pipe", "copy", "imapsieve"];
pipe :copy "rspamc" ["learnham"];

View File

@ -1,2 +0,0 @@
require ["vnd.dovecot.pipe", "copy", "imapsieve"];
pipe :copy "rspamc" ["learnspam"];

View File

@ -1,6 +0,0 @@
require ["fileinto","mailbox"];
if header :contains "X-Spam" "YES" {
fileinto :create "Junk";
stop;
}

View File

@ -1,5 +0,0 @@
#!/bin/sh
ACTION=${1}
curl -q -s --connect-timeout 60 --data-binary @- http://filter:11334/${ACTION}

View File

@ -1,11 +0,0 @@
#!/bin/sh
set -e
openssl dhparam -out /etc/dovecot/dh.pem.tmp 4096
mv /etc/dovecot/dh.pem.tmp /etc/dovecot/dh.pem
touch /etc/dovecot/dh.pem.created
echo "The Diffie Hellman file was generated."
echo "YOU SHOULD RESTART THIS CONTAINER NOW."
exit 0

View File

@ -1,20 +0,0 @@
#!/bin/sh
if ! [ -r /etc/dovecot/dh.pem.created ]
then
echo "Using pre-generated Diffie Hellman parameters until the new one is generated."
/usr/local/bin/dh.sh &
fi
rm -f /run/dovecot/master.pid
dockerize \
-template /etc/dovecot/conf.d/10-master.conf.templ:/etc/dovecot/conf.d/10-master.conf \
-template /etc/dovecot/conf.d/15-lda.conf.templ:/etc/dovecot/conf.d/15-lda.conf \
-template /etc/dovecot/conf.d/20-submission.conf.templ:/etc/dovecot/conf.d/20-submission.conf \
-template /etc/dovecot/dovecot-sql.conf.ext.templ:/etc/dovecot/dovecot-sql.conf.ext \
-wait tcp://${MYSQL_HOST}:3306 \
-wait file://${SSL_CERT} \
-wait file://${SSL_KEY} \
-timeout ${WAITSTART_TIMEOUT} \
/usr/sbin/dovecot -F

View File

@ -1,66 +0,0 @@
ARG DOCKERIZE_VER=0.6.0
ARG ALPINE_VER=3.9
FROM jwilder/dockerize:${DOCKERIZE_VER} AS dockerize
FROM alpine:${ALPINE_VER}
LABEL maintainer="jeff@ressourcenkonflikt.de"
LABEL vendor="https://github.com/jeboehm/docker-mailserver"
LABEL de.ressourcenkonflikt.docker-mailserver.autoheal="true"
ENV MAILNAME=mail.example.com \
MYNETWORKS=127.0.0.0/8\ 10.0.0.0/8\ 172.16.0.0/12\ 192.168.0.0/16 \
MYSQL_HOST=db \
MYSQL_USER=root \
MYSQL_PASSWORD=changeme \
MYSQL_DATABASE=mailserver \
FILTER_MIME=false \
RSPAMD_HOST=filter \
MDA_HOST=mda \
MTA_HOST=mta \
RELAYHOST=false \
SSL_CERT=/media/tls/mailserver.crt \
SSL_KEY=/media/tls/mailserver.key \
WAITSTART_TIMEOUT=1m
RUN apk --no-cache add \
postfix-mysql \
postfix \
supervisor && \
postconf virtual_mailbox_domains=mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf && \
postconf virtual_mailbox_maps=mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf && \
postconf virtual_alias_maps=mysql:/etc/postfix/mysql-virtual-alias-maps.cf,mysql:/etc/postfix/mysql-email2email.cf && \
postconf smtpd_recipient_restrictions="check_recipient_access mysql:/etc/postfix/mysql-recipient-access.cf" && \
postconf smtputf8_enable=no && \
postconf smtpd_milters="inet:${RSPAMD_HOST}:11332" && \
postconf non_smtpd_milters="inet:${RSPAMD_HOST}:11332" && \
postconf milter_protocol=6 && \
postconf milter_mail_macros="i {mail_addr} {client_addr} {client_name} {auth_authen}" && \
postconf milter_default_action=accept && \
postconf virtual_transport="lmtp:${MDA_HOST}:2003" && \
postconf smtp_tls_security_level=may && \
postconf smtpd_tls_security_level=may && \
postconf smtpd_tls_auth_only=yes && \
postconf smtpd_tls_cert_file="${SSL_CERT}" && \
postconf smtpd_tls_key_file="${SSL_KEY}" && \
postconf smtpd_discard_ehlo_keywords="silent-discard, dsn" && \
postconf soft_bounce=no && \
postconf message_size_limit=52428800 && \
postconf mailbox_size_limit=0 && \
postconf recipient_delimiter=- && \
postconf mynetworks="$MYNETWORKS" && \
postconf maximal_queue_lifetime=1h && \
postconf bounce_queue_lifetime=1h && \
postconf maximal_backoff_time=15m && \
postconf minimal_backoff_time=5m && \
postconf queue_run_delay=5m && \
newaliases
COPY --from=dockerize /usr/local/bin/dockerize /usr/local/bin
COPY rootfs/ /
EXPOSE 25
VOLUME ["/var/spool/postfix"]
HEALTHCHECK CMD postfix status || exit 1
CMD ["/usr/local/bin/entrypoint.sh"]

View File

@ -1 +0,0 @@
/name=[^>]*\.(bat|com|exe|dll|vbs|docm|doc|dzip)/ REJECT

View File

@ -1,5 +0,0 @@
user = {{ .Env.MYSQL_USER }}
password = {{ .Env.MYSQL_PASSWORD }}
hosts = {{ .Env.MYSQL_HOST }}
dbname = {{ .Env.MYSQL_DATABASE }}
query = SELECT CONCAT(mail_users.name, '@', mail_domains.name) AS email FROM mail_users JOIN mail_domains ON mail_users.domain_id = mail_domains.id HAVING email='%s'

View File

@ -1,5 +0,0 @@
user = {{ .Env.MYSQL_USER }}
password = {{ .Env.MYSQL_PASSWORD }}
hosts = {{ .Env.MYSQL_HOST }}
dbname = {{ .Env.MYSQL_DATABASE }}
query = SELECT IF(send_only = true, 'REJECT', 'OK') AS access FROM mail_users JOIN mail_domains ON mail_users.domain_id = mail_domains.id WHERE mail_users.name = '%u' AND mail_domains.name = '%d'

View File

@ -1,5 +0,0 @@
user = {{ .Env.MYSQL_USER }}
password = {{ .Env.MYSQL_PASSWORD }}
hosts = {{ .Env.MYSQL_HOST }}
dbname = {{ .Env.MYSQL_DATABASE }}
query = SELECT destination FROM mail_aliases JOIN mail_domains ON mail_aliases.domain_id = mail_domains.id WHERE CONCAT(mail_aliases.name, '@', mail_domains.name) = '%s'

View File

@ -1,5 +0,0 @@
user = {{ .Env.MYSQL_USER }}
password = {{ .Env.MYSQL_PASSWORD }}
hosts = {{ .Env.MYSQL_HOST }}
dbname = {{ .Env.MYSQL_DATABASE }}
query = SELECT 1 FROM mail_domains WHERE name='%s'

View File

@ -1,5 +0,0 @@
user = {{ .Env.MYSQL_USER }}
password = {{ .Env.MYSQL_PASSWORD }}
hosts = {{ .Env.MYSQL_HOST }}
dbname = {{ .Env.MYSQL_DATABASE }}
query = SELECT 1 FROM mail_users JOIN mail_domains ON mail_users.domain_id = mail_domains.id WHERE mail_users.name = '%u' AND mail_domains.name = '%d' AND enabled = 1

View File

@ -1,18 +0,0 @@
[supervisord]
nodaemon=true
logfile=/dev/stderr
logfile_maxbytes=0
pidfile=/tmp/supervisord.pid
user=root
[program:syslogd]
command=/bin/busybox syslogd -n -O - -S
redirect_stderr=true
stdout_logfile=/dev/stderr
stdout_logfile_maxbytes=0
[program:postfix]
command=/usr/libexec/postfix/master -d
redirect_stderr=true
stdout_logfile=/dev/stderr
stdout_logfile_maxbytes=0

View File

@ -1,29 +0,0 @@
#!/bin/sh
set -e
postconf myhostname="${MAILNAME}"
postconf mynetworks="${MYNETWORKS}"
if [ "${FILTER_MIME}" == "true" ]
then
postconf mime_header_checks=regexp:/etc/postfix/mime_header_checks
fi
if [ "${RELAYHOST}" != "false" ]
then
postconf relayhost=${RELAYHOST}
fi
dockerize \
-template /etc/postfix/mysql-email2email.cf.templ:/etc/postfix/mysql-email2email.cf \
-template /etc/postfix/mysql-virtual-alias-maps.cf.templ:/etc/postfix/mysql-virtual-alias-maps.cf \
-template /etc/postfix/mysql-virtual-mailbox-domains.cf.templ:/etc/postfix/mysql-virtual-mailbox-domains.cf \
-template /etc/postfix/mysql-virtual-mailbox-maps.cf.templ:/etc/postfix/mysql-virtual-mailbox-maps.cf \
-template /etc/postfix/mysql-recipient-access.cf.templ:/etc/postfix/mysql-recipient-access.cf \
-wait tcp://${MYSQL_HOST}:3306 \
-wait tcp://${MDA_HOST}:2003 \
-wait tcp://${RSPAMD_HOST}:11332 \
-wait file://${SSL_CERT} \
-wait file://${SSL_KEY} \
-timeout ${WAITSTART_TIMEOUT} \
/usr/bin/supervisord

View File

@ -0,0 +1,55 @@
protocols = imap pop3 lmtp
ssl = yes
ssl_cert = </etc/ssl/mailcerts/mailserver.crt
ssl_key = </etc/ssl/mailcerts/mailserver.key
ssl_cipher_list = ALL:!LOW:!SSLv2:!EXP:!aNULL
listen = *, ::
dict {
#quota = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext
#expire = sqlite:/etc/dovecot/dovecot-dict-sql.conf.ext
}
disable_plaintext_auth = yes
auth_mechanisms = plain login
mail_access_groups = vmail
default_login_user = vmail
first_valid_uid = 5000
first_valid_gid = 5000
mail_location = maildir:/var/mail/%d/%n
passdb {
driver = passwd-file
args = scheme=SHA1 /etc/mail/passwd
}
userdb {
driver = static
args = uid=5000 gid=5000 home=/var/mail/%d/%n allow_all_users=yes
}
service auth {
unix_listener auth-client {
group = postfix
mode = 0660
user = postfix
}
user = root
}
service imap-login {
process_min_avail = 1
user = vmail
}
protocol lmtp {
postmaster_address =
mail_plugins = $mail_plugins sieve
}
protocol imap {
mail_max_userip_connections = 30
}
!include_try conf.d/*.conf
!include_try local.conf

View File

@ -0,0 +1,7 @@
127.0.0.1
localhost
192.168.1.0/24
#HOSTNAME
#host.example.com
#192.168.1.0/24

View File

@ -0,0 +1,16 @@
PidFile /var/run/opendkim/opendkim.pid
Mode s
Syslog yes
SyslogSuccess yes
LogWhy yes
UserID opendkim:opendkim
Socket inet:8891@127.0.0.1
Umask 002
SoftwareHeader yes
Canonicalization relaxed/simple
Domain file:/etc/mail/domains
Selector default
MinimumKeyBits 1024
KeyFile /etc/opendkim/keys/default.private
InternalHosts refile:/etc/opendkim/TrustedHosts
OversignHeaders From

View File

@ -0,0 +1,77 @@
smtpd_banner = $myhostname ESMTP
biff = no
append_dot_mydomain = no
compatibility_level=2
queue_directory = /var/spool/postfix
command_directory = /usr/sbin
data_directory = /var/lib/postfix
mail_owner = postfix
myhostname = %MAIL_HOSTNAME%
mydomain = %MAIL_HOSTNAME_FQDN%
myorigin = $myhostname
inet_interfaces = all
inet_protocols = all
mydestination = $myhostname, localhost.$mydomain, localhost
unknown_local_recipient_reject_code = 550
mynetworks = 127.0.0.0/8, [::1]/128
mailbox_command = /usr/libexec/dovecot/deliver
debug_peer_level = 2
debugger_command =
PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
ddd $daemon_directory/$process_name $process_id & sleep 5
sendmail_path = /usr/sbin/sendmail.postfix
newaliases_path = /usr/bin/newaliases.postfix
mailq_path = /usr/bin/mailq.postfix
setgid_group = postdrop
html_directory = no
manpage_directory = /usr/share/man
sample_directory = /usr/share/doc/postfix-2.10.1/samples
readme_directory = /usr/share/doc/postfix-2.10.1/README_FILES
smtp_tls_security_level = may
smtp_tls_loglevel = 1
smtp_tls_protocols = !SSLv2
smtp_tls_exclude_ciphers = EXPORT, LOW
### VIRTUAL MAIL CONFIG PARAMS ###
relay_domains = *
virtual_alias_maps = hash:/etc/mail/aliases
virtual_mailbox_domains = hash:/etc/mail/domains
virtual_mailbox_maps = hash:/etc/mail/mailboxes
virtual_mailbox_base = /var/mail
virtual_minimum_uid = 5000
virtual_transport = dovecot
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
dovecot_destination_recipient_limit = 1
milter_protocol = 2
milter_default_action = accept
smtpd_milters = inet:localhost:8891
non_smtpd_milters = "inet:localhost:8891"
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = /var/run/dovecot/auth-client
smtpd_sasl_security_options = noanonymous
smtpd_sasl_tls_security_options = $smtpd_sasl_security_options
smtpd_sasl_local_domain = $mydomain
broken_sasl_auth_clients = yes
smtpd_tls_security_level = may
smtpd_tls_key_file = /etc/ssl/mailserver.key
smtpd_tls_cert_file = /etc/ssl/mailserver.crt
smtpd_tls_loglevel = 1
smtpd_tls_session_cache_timeout = 3600s
smtpd_tls_session_cache_database = btree:/var/lib/postfix/smtpd_tls_cache
smtpd_tls_protocols = $smtp_tls_protocols
tls_random_source = dev:/dev/urandom
tls_random_exchange_name = /var/lib/postfix/prng_exch
smtpd_tls_auth_only = yes
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination

View File

@ -0,0 +1,41 @@
smtp inet n - - - - smtpd
8080 inet n - - - - smtpd
smtps inet n - - - - smtpd
submission inet n - n - - smtpd
pickup fifo n - - 60 1 pickup
cleanup unix n - - - 0 cleanup
qmgr fifo n - n 300 1 qmgr
tlsmgr unix - - - 1000? 1 tlsmgr
rewrite unix - - - - - trivial-rewrite
bounce unix - - - - 0 bounce
defer unix - - - - 0 bounce
trace unix - - - - 0 bounce
verify unix - - - - 1 verify
flush unix n - - 1000? 0 flush
postlog unix-dgram n - n - 1 postlogd
proxymap unix - - n - - proxymap
proxywrite unix - - n - 1 proxymap
smtp unix - - - - - smtp
relay unix - - - - - smtp
showq unix n - - - - showq
error unix - - - - - error
retry unix - - - - - error
discard unix - - - - - discard
local unix - n n - - local
virtual unix - n n - - virtual
lmtp unix - - - - - lmtp
anvil unix - - - - 1 anvil
scache unix - - - - 1 scache
uucp unix - n n - - pipe
flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
ifmail unix - n n - - pipe
flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp unix - n n - - pipe
flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
scalemail-backend unix - n n - 2 pipe
flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
mailman unix - n n - - pipe
flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
${nexthop} ${user}
dovecot unix - n n - - pipe
flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -f ${sender} -d ${recipient}

View File

@ -0,0 +1,44 @@
#### MODULES ####
# The imjournal module bellow is now used as a message source instead of imuxsock.
$ModLoad imuxsock # provides support for local system logging (e.g. via logger command)
#### GLOBAL DIRECTIVES ####
# Where to place auxiliary files
#$WorkDirectory /var/lib/rsyslog
# Use default timestamp format
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
# Include all config files in /etc/rsyslog.d/
$IncludeConfig /etc/rsyslog.d/*.conf
# Turn off message reception via local log socket;
# local messages are retrieved through imjournal now.
$OmitLocalLogging off
#### RULES ####
# Log anything (except mail) of level info or higher.
# Don't log private authentication messages!
*.info;mail.none;authpriv.none;cron.none /var/log/messages
# The authpriv file has restricted access.
authpriv.* /var/log/secure
# Log all the mail messages in one place.
mail.* -/var/log/maillog
# Log cron stuff
cron.* /var/log/cron
# Everybody gets emergency messages
*.emerg :omusrmsg:*
# Save news errors of level crit and higher in a special file.
uucp,news.crit /var/log/spooler
# Save boot messages also to boot.log
local7.* /var/log/boot.log

View File

@ -0,0 +1,46 @@
[unix_http_server]
file=/tmp/supervisor.sock
[supervisord]
nodaemon=true
user=root
loglevel=warn
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock
user=root
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[program:opendkim]
autostart=false
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true
command=opendkim -x /etc/opendkim/opendkim.conf
[program:postfix]
autostart=false
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true
command=postfix -c /etc/postfix/postfix.conf
[program:dovecot]
autostart=false
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true
command=dovecot -c /etc/dovecot/dovecot.conf
[program:rsyslog]
autostart=false
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true
command=rsyslog -f /etc/rsyslog/rsyslog.conf

View File

@ -0,0 +1,21 @@
#!/bin/sh
USAGE="Usage: $0 ALIAS TARGET";
if [ -z "$2" ]
then
echo "$USAGE";
exit 1;
fi
DOMAINPART=$(echo $1 | sed -e "s/^.*\@//")
if ! grep "^$DOMAINPART\s" /etc/mail/domains &> /dev/null; then
echo "This server is not responsible for the domain of this alias."
exit 1
fi
echo -e "$1\t\t$2" >> /etc/mail/aliases
postmap /etc/mail/aliases
postfix reload
echo "Alias added."

View File

@ -0,0 +1,16 @@
#!/bin/sh
USAGE="Usage: $0 DOMAIN";
if [ -z "$1" ]
then
echo "$USAGE";
exit 1;
fi
echo -e "$1" >> /etc/mail/domains
postmap /etc/mail/domains
mkdir "/var/mail/$1"
chown vmail:vmail "/var/mail/$1"
postfix reload
echo "Domain added."

View File

@ -0,0 +1,30 @@
#!/bin/sh
USAGE="Usage: $0 EMAIL PASSWORD";
if [ -z "$2" ]
then
echo "$USAGE";
exit 1;
fi
DOMAINPART=$(echo $1 | sed -e "s/^.*\@//")
USERPART=$(echo $1 | sed -e "s/\@.*$//")
if ! grep -q "^$DOMAINPART" /etc/mail/domains
then
echo "This server is not responsible for the domain of this user."
exit 1
fi
PASSHASH=$(doveadm pw -s SHA512-CRYPT)
new-alias.sh $1 $1
echo "$1 $DOMAINPART/$USERPART/" >> /etc/mail/mailboxes
postmap /etc/mail/mailboxes
echo "$1:$PASSHASH" >> /etc/mail/passwd
mkdir "/var/mail/$DOMAINPART/$USERPART"
chown vmail:vmail "/var/mail/$DOMAINPART/$USERPART"
postfix reload
dovecot reload
echo "User added"

View File

@ -0,0 +1,35 @@
#!/bin/sh
postconf -e myhostname="$MAILNAME"
postconf -e mydomain="$DOMAINNAME"
postconf -e smtpd_tls_cert_file="$SSL_CERT"
postconf -e smtpd_tls_key_file="$SSL_KEY"
touch /etc/mail/aliases /etc/mail/domains /etc/mail/mailbox /etc/mail/passwd
if [ ! -d "/var/mail/$DOMAINNAME" ]
then
echo "$DOMAINNAME #OK" >> /etc/mail/domains
mkdir "/var/mail/$DOMAINNAME"
chown vmail:vmail "/var/mail/$DOMAINNAME"
fi
postmap /etc/mail/aliases && postmap /etc/mail/domains && postmap /etc/mail/mailbox
sed -i -e "s#^\s*ssl_cert\s*=.*#ssl_cert = $SSL_CERT#" /etc/dovecot/dovecot.conf
sed -i -e "s#^\s*ssl_key\s*=.*#ssl_key = $SSL_KEY#" /etc/dovecot/dovecot.conf
sed -i -e "s#^\s*hostname\s*=.*#hostname = $MAILNAME#" /etc/dovecot/dovecot.conf
sed -i -e "s#^\s*postmaster_address\s*=.*#postmaster_address = $POSTMASTER#" /etc/dovecot/dovecot.conf
sed -i -e "s/#HOSTNAME/$MAILNAME/" /etc/opendkim/TrustedHosts
if [ ! -e "/etc/opendkim/keys/default.private" ]
then
opendkim-genkey -d "$DOMAINNAME" -D "/etc/opendkim/keys"
fi
# Start services
rsyslogd -f /etc/rsyslogd/rsyslogd.conf
/usr/sbin/opendkim #-x /etc/opendkim/opendkim.conf
dovecot -c /etc/dovecot/dovecot.conf
postfix start -c /etc/postfix
supervisord -c /etc/supervisord/supervisord.conf

View File

@ -1,20 +0,0 @@
ARG ALPINE_VER=3.9
FROM alpine:${ALPINE_VER}
LABEL maintainer="jeff@ressourcenkonflikt.de"
LABEL vendor="https://github.com/jeboehm/docker-mailserver"
ENV SSL_CERT=/media/tls/mailserver.crt \
SSL_KEY=/media/tls/mailserver.key \
SSL_CSR=/media/tls/mailserver.csr \
SSL_SUBJ_COUNTRY=DE \
SSL_SUBJ_STATE=Northrhine-Westfalia \
SSL_SUBJ_LOCALITY=Duesseldorf \
SSL_SUBJ_ORGANIZATION=Mail \
SSL_SUBJ_ORGANIZATIONAL_UNIT=Mail
RUN apk --no-cache add openssl
COPY create_tls.sh /usr/local/bin
CMD ["/usr/local/bin/create_tls.sh"]

View File

@ -1,17 +0,0 @@
#!/bin/sh
set -e
if [ -r ${SSL_CERT} ]
then
echo "SSL certificate found. Exiting..."
exit 0
fi
echo "No SSL certificate found. Creating a new one..."
openssl req -nodes -newkey rsa:2048 -keyout ${SSL_KEY} -out ${SSL_CSR} -subj "/C=${SSL_SUBJ_COUNTRY}/ST=${SSL_SUBJ_STATE}/L=${SSL_SUBJ_LOCALITY}/O=${SSL_SUBJ_ORGANIZATION}/OU=${SSL_SUBJ_ORGANIZATIONAL_UNIT}/CN=${MAILNAME}"
openssl x509 -req -days 3000 -in ${SSL_CSR} -signkey ${SSL_KEY} -out ${SSL_CERT}
echo "SSL certificate was successfully created! Exiting..."
exit 0

View File

@ -1,46 +0,0 @@
ARG DOCKERIZE_VER=0.6.0
ARG ALPINE_VER=3.9
FROM jwilder/dockerize:${DOCKERIZE_VER} AS dockerize
FROM alpine:${ALPINE_VER}
LABEL maintainer="jeff@ressourcenkonflikt.de"
LABEL vendor="https://github.com/jeboehm/docker-mailserver"
ENV MYSQL_HOST=db \
MYSQL_USER=root \
MYSQL_PASSWORD=changeme \
MYSQL_DATABASE=mailserver \
WAITSTART_TIMEOUT=1m
# Iconv fix: https://github.com/docker-library/php/issues/240#issuecomment-305038173
RUN apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/community/ gnu-libiconv
ENV LD_PRELOAD=/usr/lib/preloadable_libiconv.so
RUN apk --no-cache add \
bash \
bats \
curl \
docker \
jq \
mariadb-client \
openssl \
perl \
perl-net-ssleay \
php7 \
php7-imap \
php7-phar \
php7-iconv \
php7-openssl \
&& wget -q -O /usr/local/bin/swaks https://www.jetmore.org/john/code/swaks/files/swaks-20130209.0/swaks \
&& chmod +x /usr/local/bin/swaks \
&& wget -q -O /usr/local/bin/imap-tester https://github.com/jeboehm/imap-tester/releases/download/v0.2.1/imap-tester.phar \
&& chmod +x /usr/local/bin/imap-tester \
&& mkdir -p /usr/share/fixtures \
&& wget -q -O /usr/share/fixtures/gtube.txt https://spamassassin.apache.org/gtube/gtube.txt \
&& wget -q -O /usr/share/fixtures/eicar.com https://secure.eicar.org/eicar.com
COPY --from=dockerize /usr/local/bin/dockerize /usr/local/bin
COPY rootfs/ /
CMD ["/usr/local/bin/run-tests.sh"]

View File

@ -1,10 +0,0 @@
#!/bin/sh
dockerize \
-wait tcp://db:3306 \
-wait tcp://mta:25 \
-wait tcp://web:80 \
-wait tcp://mda:143 \
-wait tcp://filter:11334 \
-timeout ${WAITSTART_TIMEOUT} \
bats /usr/share/tests/*.bats

View File

@ -1,30 +0,0 @@
#!/usr/bin/env bats
@test "certificates were created" {
[ -f /media/tls/mailserver.crt ]
}
@test "connection to imaps" {
true | openssl s_client -showcerts -connect mda:993
[ "$?" -eq 0 ]
}
@test "connection to pop3s" {
true | openssl s_client -showcerts -connect mda:995
[ "$?" -eq 0 ]
}
@test "connection to pop3 with starttls" {
true | openssl s_client -showcerts -connect mda:110 -starttls pop3
[ "$?" -eq 0 ]
}
@test "connection to imap with starttls" {
true | openssl s_client -showcerts -connect mda:143 -starttls imap
[ "$?" -eq 0 ]
}
@test "connection to smtp with starttls" {
true | openssl s_client -showcerts -connect mta:25 -starttls smtp
[ "$?" -eq 0 ]
}

View File

@ -1,16 +0,0 @@
#!/usr/bin/env bats
@test "user table exists" {
run mysql --batch -u "${MYSQL_USER}" --password="${MYSQL_PASSWORD}" -h "${MYSQL_HOST}" "${MYSQL_DATABASE}" -e "select * from mail_users;"
[ "$status" = 0 ]
}
@test "alias table exists" {
run mysql --batch -u "${MYSQL_USER}" --password="${MYSQL_PASSWORD}" -h "${MYSQL_HOST}" "${MYSQL_DATABASE}" -e "select * from mail_aliases;"
[ "$status" = 0 ]
}
@test "domain table exists" {
run mysql --batch -u "${MYSQL_USER}" --password="${MYSQL_PASSWORD}" -h "${MYSQL_HOST}" "${MYSQL_DATABASE}" -e "select * from mail_domains;"
[ "$status" = 0 ]
}

View File

@ -1,131 +0,0 @@
#!/usr/bin/env bats
@test "send mail to local account address" {
run swaks -s mta --to admin@example.com --body "$BATS_TEST_DESCRIPTION"
[ "$status" -eq 0 ]
}
@test "send mail to local address with extension" {
run swaks -s mta --to admin-test@example.com --body "$BATS_TEST_DESCRIPTION"
[ "$status" -eq 0 ]
}
@test "authentification on smtp with disabled account should fail" {
run swaks -s mta --to admin@example.com --from disabled@example.com -a -au disabled@example.com -ap test1234 -tls --body "$BATS_TEST_DESCRIPTION"
[ "$status" -eq 28 ]
}
@test "authentification on smtp with disabled and send only account should fail" {
run swaks -s mta --to admin@example.com --from disabledsendonly@example.com -a -au disabled@example.com -ap test1234 -tls --body "$BATS_TEST_DESCRIPTION"
[ "$status" -eq 28 ]
}
@test "send mail to mda with smtp authentification (submission service)" {
run swaks -s mda --port 587 --to admin@example.com --from admin@example.com -a -au admin@example.com -ap changeme -tls --body "$BATS_TEST_DESCRIPTION"
[ "$status" -eq 0 ]
}
@test "send mail to mda with smtp authentification, with address extension (submission service)" {
run swaks -s mda --port 587 --to admin@example.com --from admin-extension@example.com -a -au admin@example.com -ap changeme -tls --body "$BATS_TEST_DESCRIPTION"
[ "$status" -eq 0 ]
}
@test "send mail to mda from sendonly account with smtp authentification (submission service)" {
run swaks -s mda --port 587 --to admin@example.com --from sendonly@example.com -a -au sendonly@example.com -ap test1234 -tls --body "$BATS_TEST_DESCRIPTION"
[ "$status" -eq 0 ]
}
@test "send mail to local alias" {
run swaks -s mta --to foo@example.com --body "$BATS_TEST_DESCRIPTION"
[ "$status" -eq 0 ]
}
@test "send junk mail to local address" {
run swaks -s mta --to admin@example.com --body "$BATS_TEST_DESCRIPTION" --header "X-Spam: Yes"
[ "$status" -eq 0 ]
}
@test "send mail with too big attachment to quota user" {
dd if=/dev/urandom of=/tmp/bigfile bs=1M count=5
run swaks -s mta --to quota@example.com --body "$BATS_TEST_DESCRIPTION" --attach /tmp/bigfile
[ "$status" -eq 0 ]
}
@test "send mail to disabled user" {
run swaks -s mta --to disabled@example.com --body "$BATS_TEST_DESCRIPTION"
[ "$status" -eq 0 ]
}
@test "maildir was created" {
sleep 10 # MTA + MDA need some time. :)
[ -d /var/vmail/example.com/admin/Maildir/new/ ]
}
@test "mail to local account address is stored" {
run grep -r "send mail to local account address" /var/vmail/example.com/admin/Maildir/
[ "$status" -eq 0 ]
}
@test "mail to local alias is stored" {
run grep -r "send mail to local alias" /var/vmail/example.com/admin/Maildir/
[ "$status" -eq 0 ]
}
@test "mail to local address with extension is stored" {
run grep -r "send mail to local address with extension" /var/vmail/example.com/admin/Maildir/
[ "$status" -eq 0 ]
}
@test "mail to mda with smtp authentification (submission service) is stored" {
run grep -r "send mail to mda with smtp authentification (submission service)" /var/vmail/example.com/admin/Maildir/
[ "$status" -eq 0 ]
}
@test "send mail to mda with smtp authentification, with address extension (submission service) is stored" {
run grep -r "send mail to mda with smtp authentification, with address extension (submission service)" /var/vmail/example.com/admin/Maildir/
[ "$status" -eq 0 ]
}
@test "send mail to mda from sendonly account with smtp authentification (submission service) is stored" {
run grep -r "send mail to mda from sendonly account with smtp authentification (submission service)" /var/vmail/example.com/admin/Maildir/
[ "$status" -eq 0 ]
}
@test "junk mail is assorted to the junk folder" {
run grep -r "send junk mail to local address" /var/vmail/example.com/admin/Maildir/.Junk/
[ "$status" -eq 0 ]
}
@test "mail with too big attachment is not found" {
run grep -r "send mail with too big attachment to quota user" /var/vmail/example.com/quota/Maildir/
[ "$status" -ne 0 ]
}
@test "mail to disabled user is stored anyway" {
run grep -r "send mail to disabled user" /var/vmail/example.com/disabled/Maildir/
[ "$status" -eq 0 ]
}
@test "send gtube mail is rejected" {
run swaks -s mta --to admin@example.com --data /usr/share/fixtures/gtube.txt
[ "$status" -eq 26 ]
}
@test "mail to send only mailbox is rejected" {
run swaks -s mta --to sendonly@example.com --body "$BATS_TEST_DESCRIPTION"
[ "$status" -eq 24 ]
}
@test "mail to disabled and send only mailbox is rejected anyway" {
run swaks -s mta --to disabledsendonly@example.com --body "$BATS_TEST_DESCRIPTION"
[ "$status" -eq 24 ]
}
@test "virus is rejected" {
if [ ${FILTER_VIRUS} = "false" ]; then
skip
fi
run swaks -s mta --to admin@example.com --attach - < /usr/share/fixtures/eicar.com
[ "$status" -eq 26 ]
}

View File

@ -1,16 +0,0 @@
#!/usr/bin/env bats
@test "http connection to manager web interface" {
curl -L http://web/manager/ | grep "Email address"
[ "$?" -eq 0 ]
}
@test "http connection to webmail interface" {
curl http://web/webmail/ | grep "jeboehm"
[ "$?" -eq 0 ]
}
@test "http connection to rspamd interface" {
curl http://web/rspamd/ | grep "Rspamd Web Interface"
[ "$?" -eq 0 ]
}

View File

@ -1,72 +0,0 @@
#!/usr/bin/env bats
@test "send mail to mda from disabled account with smtp authentification (submission service)" {
run swaks -s mda --port 587 --to admin@example.com --from disabled@example.com -a -au disabled@example.com -ap test1234 -tls --body "$BATS_TEST_DESCRIPTION"
[ "$status" -eq 28 ]
}
@test "send mail to mda without authentification (submission service)" {
run swaks -s mda --port 587 --to admin@example.com --from disabled@example.com -tls --body "$BATS_TEST_DESCRIPTION"
[ "$status" -eq 23 ]
}
@test "send mail to mda without tls (submission service)" {
run swaks -s mda --port 587 --to admin@example.com --from admin@example.com -a -au admin@example.com -ap changeme --body "$BATS_TEST_DESCRIPTION"
[ "$status" -eq 28 ]
}
@test "count mails in inbox via imap" {
run imap-tester test:count mda 143 admin@example.com changeme INBOX
[ "$output" -gt 3 ]
}
@test "count mails in inbox via imaps" {
run imap-tester test:count mda 993 admin@example.com changeme INBOX
[ "$output" -gt 3 ]
}
@test "count mails in inbox via pop3" {
run imap-tester test:count mda 110 admin@example.com changeme INBOX
[ "$output" -gt 3 ]
}
@test "count mails in inbox via pop3s" {
run imap-tester test:count mda 995 admin@example.com changeme INBOX
[ "$output" -gt 3 ]
}
@test "imap login to send only mailbox is not possible" {
run imap-tester test:count mda 143 sendonly@example.com test1234 INBOX
[ "$status" -eq 1 ]
}
@test "pop3 login to send only mailbox is not possible" {
run imap-tester test:count mda 110 sendonly@example.com test1234 INBOX
[ "$status" -eq 1 ]
}
@test "pop3 login to quota mailbox is possible" {
run imap-tester test:count mda 110 quota@example.com test1234 INBOX
[ "$status" -eq 0 ]
}
@test "imap login to quota mailbox is possible" {
run imap-tester test:count mda 143 quota@example.com test1234 INBOX
[ "$status" -eq 0 ]
}
@test "pop3 login to disabled mailbox is not possible" {
run imap-tester test:count mda 110 disabled@example.com test1234 INBOX
[ "$status" -eq 1 ]
}
@test "imap login to disabled mailbox is not possible" {
run imap-tester test:count mda 143 disabled@example.com test1234 INBOX
[ "$status" -eq 1 ]
}
@test "mails are owned by vmail" {
run find /var/vmail/example.com/ -not -user 5000
[ "$status" -eq 0 ]
[ "$output" = "" ]
}

View File

@ -1,18 +0,0 @@
#!/usr/bin/env bats
@test "no unhealthy containers exist" {
run docker ps -q --filter health=unhealthy
[ "$status" -eq 0 ]
[ "$output" = "" ]
}
@test "Virus container is not running when filtering is disabled" {
if [ ${FILTER_VIRUS} = "true" ]; then
echo '# Filtering is disabled, skipping test' >&3
skip
fi
run docker ps -q --filter name=docker-mailserver_virus_1
[ "$status" -eq 0 ]
[ "$output" = "" ]
}

View File

@ -1,34 +0,0 @@
#!/usr/bin/env bats
@test "check mailhog api for messages" {
if [ ${RELAYHOST} = "false" ]; then
echo '# Relayhost is disabled, skipping test' >&3
skip
fi
run curl "http://mailhog:8025/api/v2/messages"
[ "$status" -eq 0 ]
}
@test "send mail to mda with smtp authentification, external recipient" {
if [ ${RELAYHOST} = "false" ]; then
echo '# Relayhost is disabled, skipping test' >&3
skip
fi
run swaks -s mda --port 587 --to nobody@ressourcenkonflikt.de --from admin@example.com -a -au admin@example.com -ap changeme -tls --body "$BATS_TEST_DESCRIPTION"
[ "$status" -eq 0 ]
}
@test "check mailhog api for outgoing message" {
if [ ${RELAYHOST} = "false" ]; then
echo '# Relayhost is disabled, skipping test' >&3
skip
fi
sleep 5 # Give mailhog some time
RESULT=$(curl -s "http://mailhog:8025/api/v2/messages" | jq -cr .items[0].Content.Body | tr -d '[:space:]')
[ "$RESULT" = "sendmailtomdawithsmtpauthentification,externalrecipient" ]
}

View File

@ -1,9 +0,0 @@
#!/usr/bin/env bats
@test "check DKIM selector map exists" {
[ -r /media/dkim/dkim_selectors.map ]
}
@test "check DKIM key for example.com exists" {
[ -r /media/dkim/example.com.1337.key ]
}

View File

@ -1,26 +0,0 @@
ARG ALPINE_VER=3.11
FROM alpine:${ALPINE_VER}
LABEL maintainer="jeff@ressourcenkonflikt.de"
LABEL vendor="https://github.com/jeboehm/docker-mailserver"
LABEL de.ressourcenkonflikt.docker-mailserver.autoheal="true"
ENV FILTER_VIRUS=true
RUN apk --no-cache add \
clamav-daemon \
clamav-libunrar && \
rm -rf /var/log/clamav
COPY rootfs/ /
EXPOSE 3310
USER clamav
RUN /usr/bin/freshclam -l /dev/null
VOLUME ["/var/lib/clamav"]
HEALTHCHECK CMD echo PING | nc 127.0.0.1 3310 | grep PONG
CMD ["/usr/local/bin/entrypoint.sh"]

View File

@ -1,35 +0,0 @@
ARG ALPINE_VER=3.9
FROM alpine:${ALPINE_VER}
LABEL maintainer="jeff@ressourcenkonflikt.de"
LABEL vendor="https://github.com/jeboehm/docker-mailserver"
# hadolint ignore=DL3003
RUN apk --no-cache add \
bash \
bind-tools \
clamav-scanner \
gnupg \
ncurses \
rsync \
wget && \
wget -q -O /tmp/master.tar.gz https://github.com/extremeshok/clamav-unofficial-sigs/archive/master.tar.gz && \
cd /tmp && \
tar -xvf master.tar.gz && \
cd clamav-unofficial-sigs-master && \
cp clamav-unofficial-sigs.sh /usr/local/bin/ && \
chmod +x /usr/local/bin/clamav-unofficial-sigs.sh && \
cp -r config /etc/clamav-unofficial-sigs && \
mkdir /var/lib/clamav-unofficial-sigs && \
chown clamav /var/lib/clamav-unofficial-sigs && \
cp /etc/clamav-unofficial-sigs/os/os.ubuntu.conf /etc/clamav-unofficial-sigs/os.conf && \
echo "user_configuration_complete=\"yes\"" >> /etc/clamav-unofficial-sigs/user.conf && \
echo "logging_enabled=\"no\"" >> /etc/clamav-unofficial-sigs/user.conf && \
echo "enable_random=\"no\"" >> /etc/clamav-unofficial-sigs/user.conf && \
echo "reload_dbs=\"no\"" >> /etc/clamav-unofficial-sigs/user.conf && \
rm -rf /tmp/* /var/log/* /etc/clamav-unofficial-sigs/os/
USER clamav
CMD ["/usr/local/bin/clamav-unofficial-sigs.sh"]

View File

@ -1,2 +0,0 @@
TCPSocket 3310
Foreground true

View File

@ -1,4 +0,0 @@
DatabaseOwner clamav
DatabaseMirror database.clamav.net
ScriptedUpdates yes
NotifyClamd /etc/clamav/clamd.conf

View File

@ -1,10 +0,0 @@
#!/bin/sh
if [ "${FILTER_VIRUS}" = "false" ]
then
echo "Virus filtering is disabled, exiting."
exit 0
fi
/usr/bin/freshclam -d -l /dev/stdout &
/usr/sbin/clamd

View File

@ -1,50 +0,0 @@
ARG ROUNDCUBE_VER=1.4.x-fpm
ARG PHP_VER=7.4
ARG DOCKERIZE_VER=0.6.0
FROM jwilder/dockerize:${DOCKERIZE_VER} AS dockerize
FROM roundcube/roundcubemail:${ROUNDCUBE_VER} AS roundcube
FROM jeboehm/php-nginx-base:${PHP_VER}
ARG ADMIN_VER=1.6.1
LABEL maintainer="jeff@ressourcenkonflikt.de"
LABEL vendor="https://github.com/jeboehm/docker-mailserver"
LABEL de.ressourcenkonflikt.docker-mailserver.autoheal="true"
ENV MYSQL_HOST=db \
MYSQL_DATABASE=mailserver \
MYSQL_USER=mailserver \
MYSQL_PASSWORD=changeme \
MTA_HOST=mta \
MDA_HOST=mda \
FILTER_HOST=filter \
SUPPORT_URL=https://github.com/jeboehm/docker-mailserver \
APP_ENV=prod \
TRUSTED_PROXIES=172.16.0.0/12 \
WAITSTART_TIMEOUT=1m \
ADMIN_VER=${ADMIN_VER}
COPY --from=roundcube /usr/src/roundcubemail/ /var/www/html/webmail/
COPY --from=dockerize /usr/local/bin/dockerize /usr/local/bin
COPY rootfs/ /
WORKDIR /opt/manager
RUN wget -O /tmp/admin.tar.gz -q https://github.com/jeboehm/mailserver-admin/archive/${ADMIN_VER}.tar.gz && \
tar -xf /tmp/admin.tar.gz -C /opt/manager --strip=1 && \
rm /tmp/admin.tar.gz && \
composer install --no-dev -o
RUN ln -s /opt/manager/public /var/www/html/manager && \
chown -R www-data \
/opt/manager/var/cache/ \
/opt/manager/var/log/ \
/var/www/html/webmail/temp/ \
/var/www/html/webmail/logs/
WORKDIR /var/www/html
HEALTHCHECK CMD curl -s http://127.0.0.1/login | grep docker-mailserver
CMD ["/usr/local/bin/entrypoint.sh"]

View File

@ -1,6 +0,0 @@
# mailserver-web
This image contains
[roundcube](https://roundcube.net)
and [mailserver-admin](https://github.com/jeboehm/mailserver-admin).

View File

@ -1,68 +0,0 @@
server {
listen 80;
absolute_redirect off;
root /var/www/html/manager;
location = /favicon.ico {
log_not_found off;
access_log off;
}
location /manager {
return 301 /;
}
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~ \.(tpl|yml|ini|log)$ {
deny all;
}
location / {
try_files $uri /index.php$is_args$args;
}
location /webmail {
alias /var/www/html/webmail;
index index.php;
try_files $uri $uri/ @webmail;
location ~ \.php$ {
include fastcgi_params;
# Mitigate httpoxy vulnerability, see: https://httpoxy.org/
fastcgi_param HTTP_PROXY "";
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
client_max_body_size 24M;
client_body_buffer_size 128k;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass php-fpm;
}
}
location @webmail {
rewrite /webmail/(.*)$ /webmail/index.php?/$1 last;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass php-fpm;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
internal;
}
location /rspamd/ {
proxy_pass http://filter:11334/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

View File

@ -1,42 +0,0 @@
#!/bin/sh
set -e
manager_init() {
cd /opt/manager
bin/console doctrine:migrations:migrate -n
bin/console doctrine:schema:update --force
}
roundcube_init() {
cd /var/www/html/webmail
PWD=`pwd`
bin/initdb.sh --dir=$PWD/SQL || bin/updatedb.sh --dir=$PWD/SQL --package=roundcube || echo "Failed to initialize databse. Please run $PWD/bin/initdb.sh manually."
}
permissions() {
chown -R www-data /media/dkim
chmod 777 /media/dkim
}
dkim_refresh() {
cd /opt/manager
bin/console dkim:refresh
}
dockerize \
-wait tcp://${MYSQL_HOST}:3306 \
-wait tcp://${MDA_HOST}:143 \
-wait tcp://${MTA_HOST}:25 \
-wait tcp://${FILTER_HOST}:11334 \
-wait file:///media/dkim/ \
-timeout ${WAITSTART_TIMEOUT}
manager_init
roundcube_init
permissions
dkim_refresh
/usr/bin/supervisord

View File

@ -1,7 +0,0 @@
#!/bin/sh
dockerize \
-wait tcp://web:80 \
-wait tcp://${MYSQL_HOST}:3306 \
-timeout ${WAITSTART_TIMEOUT} \
${@}

View File

@ -1,4 +0,0 @@
#!/bin/sh
/usr/local/bin/fixtures.sh \
/opt/manager/bin/console init:setup

View File

@ -1,39 +0,0 @@
<?php
$config = [];
$config['smtp_log'] = false;
$config['log_dir'] = '/tmp/';
$config['temp_dir'] = '/tmp/';
$config['imap_cache'] = 'apc';
$config['db_dsnw'] = sprintf(
'mysql://%s:%s@%s/%s',
getenv('MYSQL_USER'),
getenv('MYSQL_PASSWORD'),
getenv('MYSQL_HOST'),
getenv('MYSQL_DATABASE')
);
$config['default_host'] = 'tls://' . getenv('MDA_HOST');
$config['smtp_server'] = 'tls://' . getenv('MDA_HOST');
$config['smtp_port'] = 587;
$config['smtp_user'] = '%u';
$config['smtp_pass'] = '%p';
$config['support_url'] = getenv('SUPPORT_URL');
$config['plugins'] = [
'archive',
'zipdownload',
'managesieve',
'password',
];
$config['imap_conn_options'] = [
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => false,
],
];
$config['smtp_conn_options'] = [
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => false,
],
];

View File

@ -1,29 +0,0 @@
<?php
$config['managesieve_port'] = 4190;
$config['managesieve_host'] = '%h';
$config['managesieve_auth_type'] = 'PLAIN';
$config['managesieve_auth_cid'] = null;
$config['managesieve_auth_pw'] = null;
$config['managesieve_usetls'] = true;
$config['managesieve_conn_options'] = [
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => false,
],
];
$config['managesieve_default'] = '/etc/dovecot/sieve/global';
$config['managesieve_script_name'] = 'managesieve';
$config['managesieve_mbox_encoding'] = 'UTF-8';
$config['managesieve_replace_delimiter'] = '';
$config['managesieve_disabled_extensions'] = [];
$config['managesieve_debug'] = false;
$config['managesieve_kolab_master'] = false;
$config['managesieve_filename_extension'] = '.sieve';
$config['managesieve_filename_exceptions'] = [];
$config['managesieve_domains'] = [];
$config['managesieve_vacation'] = 0;
$config['managesieve_vacation_interval'] = 0;
$config['managesieve_vacation_addresses_init'] = false;
$config['managesieve_notify_methods'] = ['mailto'];

View File

@ -1,28 +0,0 @@
<?php
$config['password_driver'] = 'sql';
$config['password_confirm_current'] = true;
$config['password_minimum_length'] = 6;
$config['password_require_nonalpha'] = false;
$config['password_log'] = false;
$config['password_login_exceptions'] = null;
$config['password_hosts'] = null;
$config['password_force_save'] = true;
$config['password_force_new_user'] = false;
$config['password_algorithm'] = 'clear';
$config['password_algorithm_prefix'] = '';
$config['password_blowfish_cost'] = 12;
$config['password_disabled'] = false;
$config['password_db_dsn'] = sprintf(
'mysql://%s:%s@%s/%s',
getenv('MYSQL_USER'),
getenv('MYSQL_PASSWORD'),
getenv('MYSQL_HOST'),
getenv('MYSQL_DATABASE')
);
$config['password_query'] = "UPDATE mail_users JOIN mail_domains ON mail_users.domain_id = mail_domains.id SET password=CONCAT('{SHA256-CRYPT}', ENCRYPT (%p, CONCAT('$5$', SUBSTRING(SHA(RAND()), -16)))) WHERE CONCAT(mail_users.name, '@', mail_domains.name)=%u;";
$config['password_crypt_hash'] = 'md5';
$config['password_idn_ascii'] = false;
$config['password_dovecotpw_with_method'] = false;
$config['password_hash_algorithm'] = 'sha1';
$config['password_hash_base64'] = false;