Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mailserver2/debian-mail-overlay:1.0.20
FROM mailserver2/debian-mail-overlay:1.1.0

LABEL description="Simple and full-featured mail server using Docker"

Expand Down
84 changes: 68 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ ldap: init_ldap fixtures_ldap run_ldap stop_ldap
ldap2: init_ldap2 fixtures_ldap2 run_ldap2 stop_ldap2
sieve: init_sieve fixtures_sieve run_sieve stop_sieve
ecdsa: init_ecdsa run_ecdsa stop_ecdsa
traefik_acmev1: init_traefik_acmev1 run_traefik_acmev1 stop_traefik_acmev1
traefik_acmev2: init_traefik_acmev2 run_traefik_acmev2 stop_traefik_acmev2
traefik_acmev1: init_traefik_acmev1 fixtures_traefik_acmev1 run_traefik_acmev1 stop_traefik_acmev1
traefik_acmev2: init_traefik_acmev2 fixtures_traefik_acmev2 run_traefik_acmev2 stop_traefik_acmev2

build-no-cache:
docker build --no-cache -t $(NAME) .
Expand Down Expand Up @@ -129,11 +129,11 @@ init_ldap: init_openldap init_redis
-e LDAP_GROUP_RESULT_MEMBER="member" \
-e LDAP_SENDER_FILTER="(&(|(mail=%s)(mailalias=%s))(objectClass=mailAccount))" \
-e LDAP_SENDER_ATTRIBUTE="mail" \
-e LDAP_DOVECOT_USER_ATTRS="=home=/var/mail/vhosts/%d/%n/,=mail=maildir:/var/mail/vhosts/%d/%n/mail/,mailuserquota=quota_rule=*:bytes=%\$$" \
-e LDAP_DOVECOT_USER_FILTER="(&(mail=%u)(objectClass=mailAccount))" \
-e LDAP_DOVECOT_PASS_ATTRS="mail=user,userPassword=password" \
-e LDAP_DOVECOT_PASS_FILTER="(&(mail=%u)(objectClass=mailAccount))" \
-e LDAP_DOVECOT_ITERATE_ATTRS="mail=user" \
-e LDAP_DOVECOT_USER_ATTRS="home=/var/mail/vhosts/%{user|domain}/%{user|username}/,mail_driver=maildir,mail_path=/var/mail/vhosts/%{user|domain}/%{user|username}/mail/,quota_storage_size=%{ldap:mailuserquota}" \
-e LDAP_DOVECOT_USER_FILTER="(&(mail=%{user})(objectClass=mailAccount))" \
-e LDAP_DOVECOT_PASS_ATTRS="user=%{ldap:mail}" \
-e LDAP_DOVECOT_PASS_FILTER="(&(mail=%{user})(objectClass=mailAccount))" \
-e LDAP_DOVECOT_ITERATE_ATTRS="user=%{ldap:mail}" \
-e LDAP_DOVECOT_ITERATE_FILTER="(objectClass=mailAccount)" \
-e DKIM_SELECTOR="mail20190101" \
-e VMAILUID=`id -u` \
Expand Down Expand Up @@ -206,15 +206,15 @@ init_ldap2: init_openldap init_redis
-e LDAP_ALIAS_ATTRIBUTE="mail" \
-e LDAP_SENDER_FILTER="(&(|(mail=%s)(mailalias=%s))(objectClass=mailAccount))" \
-e LDAP_SENDER_ATTRIBUTE="mail" \
-e LDAP_DOVECOT_USER_ATTRS="=home=/var/mail/vhosts/%d/%n/,=mail=maildir:/var/mail/vhosts/%d/%n/mail/,mailuserquota=quota_rule=*:bytes=%\$$" \
-e LDAP_DOVECOT_USER_FILTER="(&(mail=%u)(objectClass=mailAccount))" \
-e LDAP_DOVECOT_PASS_ATTRS="mail=user,userPassword=password" \
-e LDAP_DOVECOT_PASS_FILTER="(&(mail=%u)(objectClass=mailAccount))" \
-e LDAP_DOVECOT_ITERATE_ATTRS="mail=user" \
-e LDAP_DOVECOT_USER_ATTRS="home=/var/mail/vhosts/%{user|domain}/%{user|username}/,mail_driver=maildir,mail_path=/var/mail/vhosts/%{user|domain}/%{user|username}/mail/,quota_storage_size=%{ldap:mailuserquota}" \
-e LDAP_DOVECOT_USER_FILTER="(&(mail=%{user})(objectClass=mailAccount))" \
-e LDAP_DOVECOT_PASS_ATTRS="user=%{ldap:mail}" \
-e LDAP_DOVECOT_PASS_FILTER="(&(mail=%{user})(objectClass=mailAccount))" \
-e LDAP_DOVECOT_ITERATE_ATTRS="user=%{ldap:mail}" \
-e LDAP_DOVECOT_ITERATE_FILTER="(objectClass=mailAccount)" \
-e LDAP_MASTER_USER_ENABLED=true \
-e LDAP_DOVECOT_MASTER_PASS_ATTRS="mail=user,userPassword=password" \
-e LDAP_DOVECOT_MASTER_PASS_FILTER="(&(mail=%u)(st=%{login_user})(objectClass=mailAccount))" \
-e LDAP_DOVECOT_MASTER_PASS_ATTRS="user=%{ldap:mail}" \
-e LDAP_DOVECOT_MASTER_PASS_FILTER="(&(mail=%{user})(st=%{login_user})(objectClass=mailAccount))" \
-e DISABLE_CLAMAV=true \
-e DISABLE_SIEVE=true \
-e DISABLE_SIGNING=true \
Expand Down Expand Up @@ -342,6 +342,16 @@ fixtures_reverse:
@echo "Waiting for the postfix queue to drain (mailserver_reverse) ..."
@timeout $(WAIT_TIMEOUT) sh -c 'until [ -z "$$(docker exec mailserver_reverse find /var/spool/postfix/incoming /var/spool/postfix/active /var/spool/postfix/maildrop -type f 2>/dev/null)" ]; do sleep 2; done' \
|| { echo "TIMEOUT: postfix queue did not drain"; docker exec mailserver_reverse postqueue -p; exit 1; }
# One in-place rewrite of a watched certificate (same bytes) has to produce
# exactly one reload; the tests count them. Last, so no delivery above is
# racing a postfix/dovecot reload.
@timeout $(WAIT_TIMEOUT) sh -c 'until docker logs mailserver_reverse 2>&1 | grep -q "Watching /etc/letsencrypt/live"; do sleep 2; done' \
|| { echo "TIMEOUT: cert watcher did not start"; docker logs --tail 20 mailserver_reverse; exit 1; }
docker exec mailserver_reverse python3 -c "p='/etc/letsencrypt/live/mail.domain.tld/fullchain.pem'; d=open(p,'rb').read(); f=open(p,'r+b'); f.write(d); f.close()"
@timeout $(WAIT_TIMEOUT) sh -c 'until docker logs mailserver_reverse 2>&1 | grep -q "Updating SSL certificates and reloading"; do sleep 2; done' \
|| { echo "TIMEOUT: watcher did not react to writing the certificate"; docker logs --tail 20 mailserver_reverse; exit 1; }
# Settle time in which a watcher reacting to its own reads would reload again
sleep 15
run_reverse:
./test/bats/bin/bats test/reverse.bats
stop_reverse:
Expand Down Expand Up @@ -390,9 +400,22 @@ init_traefik_acmev1: init_redis init_mariadb
-e VMAILGID=`id -g` \
-e DISABLE_CLAMAV=true \
-e TESTING=true \
-v "`pwd`/test/share/traefik/acme.v1.json":/etc/letsencrypt/acme/acme.json \
-v "`pwd`/test/share/traefik/acme.v1":/etc/letsencrypt/acme \
-h mail.domain.tld \
-t $(NAME)
fixtures_traefik_acmev1:
# One in-place rewrite of acme.json (same bytes) has to produce exactly one
# reload; the tests count them. Postfix and dovecot must be up so the
# reload has something to signal.
@timeout $(WAIT_TIMEOUT) sh -c 'until docker exec mailserver_traefik_acmev1 nc -z 0.0.0.0 25 && docker exec mailserver_traefik_acmev1 nc -z 0.0.0.0 143; do sleep 2; done' \
|| { echo "TIMEOUT: postfix/dovecot not listening"; docker logs --tail 20 mailserver_traefik_acmev1; exit 1; }
@timeout $(WAIT_TIMEOUT) sh -c 'until docker logs mailserver_traefik_acmev1 2>&1 | grep -q "Watching /etc/letsencrypt/acme"; do sleep 2; done' \
|| { echo "TIMEOUT: cert watcher did not start"; docker logs --tail 20 mailserver_traefik_acmev1; exit 1; }
docker exec mailserver_traefik_acmev1 python3 -c "p='/etc/letsencrypt/acme/acme.json'; d=open(p,'rb').read(); f=open(p,'r+b'); f.write(d); f.close()"
@timeout $(WAIT_TIMEOUT) sh -c 'until docker logs mailserver_traefik_acmev1 2>&1 | grep -q "Updating SSL certificates and reloading"; do sleep 2; done' \
|| { echo "TIMEOUT: watcher did not react to writing acme.json"; docker logs --tail 20 mailserver_traefik_acmev1; exit 1; }
# Settle time in which a watcher reacting to its own reads would reload again
sleep 15
run_traefik_acmev1:
docker exec mailserver_traefik_acmev1 /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 587 ; do sleep 1 ; done"
./test/bats/bin/bats test/traefik_acmev1.bats
Expand All @@ -415,9 +438,22 @@ init_traefik_acmev2: init_redis init_mariadb
-e VMAILGID=`id -g` \
-e DISABLE_CLAMAV=true \
-e TESTING=true \
-v "`pwd`/test/share/traefik/acme.v2.json":/etc/letsencrypt/acme/acme.json \
-v "`pwd`/test/share/traefik/acme.v2":/etc/letsencrypt/acme \
-h mail.domain.tld \
-t $(NAME)
fixtures_traefik_acmev2:
# One in-place rewrite of acme.json (same bytes) has to produce exactly one
# reload; the tests count them. Postfix and dovecot must be up so the
# reload has something to signal.
@timeout $(WAIT_TIMEOUT) sh -c 'until docker exec mailserver_traefik_acmev2 nc -z 0.0.0.0 25 && docker exec mailserver_traefik_acmev2 nc -z 0.0.0.0 143; do sleep 2; done' \
|| { echo "TIMEOUT: postfix/dovecot not listening"; docker logs --tail 20 mailserver_traefik_acmev2; exit 1; }
@timeout $(WAIT_TIMEOUT) sh -c 'until docker logs mailserver_traefik_acmev2 2>&1 | grep -q "Watching /etc/letsencrypt/acme"; do sleep 2; done' \
|| { echo "TIMEOUT: cert watcher did not start"; docker logs --tail 20 mailserver_traefik_acmev2; exit 1; }
docker exec mailserver_traefik_acmev2 python3 -c "p='/etc/letsencrypt/acme/acme.json'; d=open(p,'rb').read(); f=open(p,'r+b'); f.write(d); f.close()"
@timeout $(WAIT_TIMEOUT) sh -c 'until docker logs mailserver_traefik_acmev2 2>&1 | grep -q "Updating SSL certificates and reloading"; do sleep 2; done' \
|| { echo "TIMEOUT: watcher did not react to writing acme.json"; docker logs --tail 20 mailserver_traefik_acmev2; exit 1; }
# Settle time in which a watcher reacting to its own reads would reload again
sleep 15
run_traefik_acmev2:
docker exec mailserver_traefik_acmev2 /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 587 ; do sleep 1 ; done"
./test/bats/bin/bats test/traefik_acmev2.bats
Expand Down Expand Up @@ -453,6 +489,22 @@ fixtures_default:
@echo "Waiting for the postfix queue to drain (mailserver_default) ..."
@timeout $(WAIT_TIMEOUT) sh -c 'until [ -z "$$(docker exec mailserver_default find /var/spool/postfix/incoming /var/spool/postfix/active /var/spool/postfix/maildrop -type f 2>/dev/null)" ]; do sleep 2; done' \
|| { echo "TIMEOUT: postfix queue did not drain"; docker exec mailserver_default postqueue -p; exit 1; }

# Push tiny.quota@domain.tld (100 KB) over its limit; the body is generated
# because it only has to be big. Grace admits it and fires the quota warnings.
docker exec mailserver_default /bin/sh -c "{ echo 'HELO mx.gmail.com'; echo 'MAIL FROM: user@gmail.com'; echo 'RCPT TO: tiny.quota@domain.tld'; echo 'DATA'; echo 'From: Docker Mail Server <user@gmail.com>'; echo 'To: Tiny Quota <tiny.quota@domain.tld>'; echo 'Date: Sat, 28 Nov 2016 12:00:00 +0200'; echo 'Subject: Quota Filler'; echo 'Test:external-to-tiny-quota-filler'; echo ''; yes AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | head -4000; echo '.'; echo 'QUIT'; } > /tmp/quota-filler.txt"
docker exec mailserver_default /bin/sh -c "python3 /tmp/tests/smtp-send.py 0.0.0.0 25 /tmp/quota-filler.txt"
# Wait until postfix has delivered everything it accepted
@echo "Waiting for the postfix queue to drain (mailserver_default) ..."
@timeout $(WAIT_TIMEOUT) sh -c 'until [ -z "$$(docker exec mailserver_default find /var/spool/postfix/incoming /var/spool/postfix/active /var/spool/postfix/maildrop -type f 2>/dev/null)" ]; do sleep 2; done' \
|| { echo "TIMEOUT: postfix queue did not drain"; docker exec mailserver_default postqueue -p; exit 1; }

# Now that the mailbox is over quota, the next message has to be refused.
docker exec mailserver_default /bin/sh -c "python3 /tmp/tests/smtp-send.py 0.0.0.0 25 /tmp/tests/email-templates/external-to-tiny-quota-rejected.txt"
# Wait until postfix has delivered everything it accepted
@echo "Waiting for the postfix queue to drain (mailserver_default) ..."
@timeout $(WAIT_TIMEOUT) sh -c 'until [ -z "$$(docker exec mailserver_default find /var/spool/postfix/incoming /var/spool/postfix/active /var/spool/postfix/maildrop -type f 2>/dev/null)" ]; do sleep 2; done' \
|| { echo "TIMEOUT: postfix queue did not drain"; docker exec mailserver_default postqueue -p; exit 1; }
run_default:
./test/bats/bin/bats test/default.bats
stop_default:
Expand Down
Loading
Loading