Skip to content
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ test/config/postfix-receive-access.cfe
test/config/postfix-send-access.cf
test/config/postfix-send-access.cfe
test/config/relay-hosts/chksum
test/config/relay-hosts/postfix-aliases.cf
test/config/relay-hosts/postfix-aliases.cf
6 changes: 6 additions & 0 deletions .hadolint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ignored:
# disable explicit version for apt install
- DL3008
trustedRegistries:
- docker.io

54 changes: 33 additions & 21 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ ENV POSTGREY_TEXT="Delayed by postgrey"
ENV SASLAUTHD_MECHANISMS=pam
ENV SASLAUTHD_MECH_OPTIONS=""

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

# Packages
RUN apt-get update -q --fix-missing && \
apt-get -y upgrade && \
# hadolint ignore=DL3015
RUN echo "deb http://ftp.debian.org/debian stretch-backports main" | tee -a /etc/apt/sources.list.d/stretch-bp.list && \
apt-get update -q --fix-missing && \
# TODO installing postfix with --no-install-recommends makes "checking ssl: generated default cert works correctly" fail
apt-get -y install postfix && \
apt-get -y install --no-install-recommends \
amavisd-new \
Expand Down Expand Up @@ -53,6 +57,7 @@ RUN apt-get update -q --fix-missing && \
pax \
pflogsumm \
p7zip-full \
postfix \
postfix-ldap \
postfix-pcre \
postfix-policyd-spf-python \
Expand All @@ -72,13 +77,6 @@ RUN apt-get update -q --fix-missing && \
xz-utils \
zoo \
&& \
curl https://packages.elasticsearch.org/GPG-KEY-elasticsearch | apt-key add - && \
echo "deb http://packages.elastic.co/beats/apt stable main" | tee -a /etc/apt/sources.list.d/beats.list && \
echo "deb http://ftp.debian.org/debian stretch-backports main" | tee -a /etc/apt/sources.list.d/stretch-bp.list && \
apt-get update -q --fix-missing && \
apt-get -y upgrade \
filebeat \
&& \
apt-get -t stretch-backports -y install --no-install-recommends \
dovecot-core \
dovecot-imapd \
Expand All @@ -98,8 +96,20 @@ RUN apt-get update -q --fix-missing && \
rm -f /etc/cron.weekly/fstrim && \
rm -f /etc/postsrsd.secret

RUN echo "0 */6 * * * clamav /usr/bin/freshclam --quiet" > /etc/cron.d/clamav-freshclam && \
# install filebeat for logging
RUN curl https://packages.elasticsearch.org/GPG-KEY-elasticsearch | apt-key add - && \
echo "deb http://packages.elastic.co/beats/apt stable main" | tee -a /etc/apt/sources.list.d/beats.list && \
apt-get update -q --fix-missing && \
apt-get -y install --no-install-recommends \
filebeat \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

COPY target/filebeat.yml.tmpl /etc/filebeat/filebeat.yml.tmpl

RUN echo "0 0,6,12,18 * * * root /usr/bin/freshclam --quiet" > /etc/cron.d/clamav-freshclam && \
chmod 644 /etc/clamav/freshclam.conf && \
# TODO does freshclam really need to be run during build?
freshclam && \
sed -i 's/Foreground false/Foreground true/g' /etc/clamav/clamd.conf && \
sed -i 's/AllowSupplementaryGroups false/AllowSupplementaryGroups true/g' /etc/clamav/clamd.conf && \
Expand All @@ -108,6 +118,8 @@ RUN echo "0 */6 * * * clamav /usr/bin/freshclam --quiet" > /etc/cron.d/clamav-fr

# Configures Dovecot
COPY target/dovecot/auth-passwdfile.inc target/dovecot/??-*.conf /etc/dovecot/conf.d/
WORKDIR /usr/share/dovecot
# hadolint ignore=SC2016,SC2086
RUN sed -i -e 's/include_try \/usr\/share\/dovecot\/protocols\.d/include_try \/etc\/dovecot\/protocols\.d/g' /etc/dovecot/dovecot.conf && \
sed -i -e 's/#mail_plugins = \$mail_plugins/mail_plugins = \$mail_plugins sieve/g' /etc/dovecot/conf.d/15-lda.conf && \
sed -i -e 's/^.*lda_mailbox_autocreate.*/lda_mailbox_autocreate = yes/g' /etc/dovecot/conf.d/15-lda.conf && \
Expand All @@ -117,17 +129,19 @@ RUN sed -i -e 's/include_try \/usr\/share\/dovecot\/protocols\.d/include_try \/e
# stretch-backport of dovecot needs this folder
mkdir /etc/dovecot/ssl && \
chmod 755 /etc/dovecot/ssl && \
cd /usr/share/dovecot && \
# TODO this creates a private key for dovecot
./mkcert.sh && \
mkdir -p /usr/lib/dovecot/sieve-pipe /usr/lib/dovecot/sieve-filter /usr/lib/dovecot/sieve-global && \
chmod 755 -R /usr/lib/dovecot/sieve-pipe /usr/lib/dovecot/sieve-filter /usr/lib/dovecot/sieve-global && \
# TODO do not generate dhparam during build
openssl dhparam -out /etc/dovecot/dh.pem 2048

# Configures LDAP
COPY target/dovecot/dovecot-ldap.conf.ext /etc/dovecot
COPY target/postfix/ldap-users.cf target/postfix/ldap-groups.cf target/postfix/ldap-aliases.cf target/postfix/ldap-domains.cf /etc/postfix/

# Enables Spamassassin CRON updates and update hook for supervisor
# hadolint ignore=SC2016
RUN sed -i -r 's/^(CRON)=0/\1=1/g' /etc/default/spamassassin && \
sed -i -r 's/^\$INIT restart/supervisorctl restart amavis/g' /etc/spamassassin/sa-update-hooks.d/amavisd-new

Expand All @@ -148,19 +162,17 @@ RUN sed -i -r 's/#(@| \\%)bypass/\1bypass/g' /etc/amavis/conf.d/15-content_fil
adduser amavis clamav && \
# no syslog user in debian compared to ubuntu
adduser --system syslog && \
useradd -u 5000 -d /home/docker -s /bin/bash -p $(echo docker | openssl passwd -1 -stdin) docker && \
(echo "0 4 * * * /usr/local/bin/virus-wiper" ; crontab -l) | crontab -
useradd -u 5000 -d /home/docker -s /bin/bash -p "$(echo docker | openssl passwd -1 -stdin)" docker && \
echo "0 4 * * * /usr/local/bin/virus-wiper" | crontab -

# Configure Fail2ban
COPY target/fail2ban/jail.conf /etc/fail2ban/jail.conf
COPY target/fail2ban/filter.d/dovecot.conf /etc/fail2ban/filter.d/dovecot.conf
RUN echo "ignoreregex =" >> /etc/fail2ban/filter.d/postfix-sasl.conf && mkdir /var/run/fail2ban

# Enables Pyzor and Razor
USER amavis
RUN razor-admin -create && \
razor-admin -register
USER root
RUN su - amavis -c "razor-admin -create && \
razor-admin -register"

# Configure DKIM (opendkim)
# DKIM config files
Expand All @@ -181,9 +193,11 @@ RUN mkdir /var/run/fetchmail && chown fetchmail /var/run/fetchmail
COPY target/postfix/main.cf target/postfix/master.cf /etc/postfix/
COPY target/postfix/header_checks.pcre target/postfix/sender_header_filter.pcre target/postfix/sender_login_maps.pcre /etc/postfix/maps/
RUN echo "" > /etc/aliases && \
# TODO dhparam should not be generated during docker build.
# TODO why regenerate it each week via cron?
# TODO the setup could also use a single dhparam for all services
openssl dhparam -out /etc/postfix/dhparams.pem 2048 && \
echo "@weekly FILE=\`mktemp\` ; openssl dhparam -out \$FILE 2048 > /dev/null 2>&1 && mv -f \$FILE /etc/postfix/dhparams.pem" > /etc/cron.d/dh2048

echo "@weekly FILE=\$(mktemp) ; openssl dhparam -out \$FILE 2048 > /dev/null 2>&1 && mv -f \$FILE /etc/postfix/dhparams.pem" > /etc/cron.d/dh2048

# Configuring Logs
RUN sed -i -r "/^#?compress/c\compress\ncopytruncate" /etc/logrotate.conf && \
Expand Down Expand Up @@ -222,5 +236,3 @@ COPY target/supervisor/conf.d/* /etc/supervisor/conf.d/
EXPOSE 25 587 143 465 993 110 995 4190

CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"]

ADD target/filebeat.yml.tmpl /etc/filebeat/filebeat.yml.tmpl
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ generate-accounts:
docker run --rm -e [email protected] -e MAIL_PASS=mypassword -t $(NAME) /bin/sh -c 'echo "$$MAIL_USER|$$(doveadm pw -s SHA512-CRYPT -u $$MAIL_USER -p $$MAIL_PASS)"' >> test/config/postfix-accounts.cf

run:
docker network create --driver bridge --subnet 192.168.13.0/24 $(NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME)
docker network create --driver bridge --subnet 192.168.37.0/24 $(NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME)2
# TODO maybe this could be simplified by simply moving the following into a docker-compose.yml
docker network create --driver bridge $(NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME)
docker network create --driver bridge $(NON_DEFAULT_DOCKER_MAIL_NETWORK_NAME)2
# use two networks (default ("bridge") and our custom network) to recreate problematic test case where PERMIT_DOCKER=host would not help
# currently we cannot use --network in `docker run` multiple times, it will just use the last one
# instead we need to use create, network connect and start (see https://success.docker.com/article/multiple-docker-networks)
Expand Down
6 changes: 4 additions & 2 deletions test/tests.bats
Original file line number Diff line number Diff line change
Expand Up @@ -1233,9 +1233,11 @@ function count_processed_changes() {
}

@test "checking PERMIT_DOCKER: connected-networks" {
ipnet1=$(docker network inspect --format '{{range .Containers}}{{.IPv4Address}}{{end}}' non-default-docker-mail-network)
ipnet2=$(docker network inspect --format '{{range .Containers}}{{.IPv4Address}}{{end}}' non-default-docker-mail-network2)
run docker exec mail_smtponly_second_network /bin/sh -c "postconf | grep '^mynetworks ='"
assert_output --regexp "192\.168\.13\.[0-9]{1,3}\/24"
assert_output --regexp '192.168.37.[0-9]{1,3}/24'
assert_output --partial $ipnet1
assert_output --partial $ipnet2
}

#
Expand Down