Skip to content
Open
Show file tree
Hide file tree
Changes from all 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