diff --git a/.gitignore b/.gitignore index e3fbc7f..0000a95 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ -environment -aliases -rainloop.env -ldap.env -postfix.env -ssl.env +aliases.d +docker-compose.yml +spool +vmail diff --git a/aliases.dist b/aliases.dist deleted file mode 100644 index e69de29..0000000 diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index c32f55e..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,72 +0,0 @@ -version: '3.5' -services: - postfix: - build: postfix - restart: unless-stopped - env_file: - - ssl.env - - postfix.env - - ldap.env - volumes: - - ./aliases:/etc/postfix/virtual - - /var/lib/acme/:/var/lib/acme/:ro - - /var/lib/docker-volumes/mail/queue/:/var/spool/postfix - - postfix-dh:/etc/postfix/dh-params - networks: - mail: - auth: - ports: - - "25:25" - - "587:587" - depends_on: - - postgrey - - dovecot - logging: - driver: journald - - postgrey: - build: postgrey - restart: unless-stopped - volumes: - - postgrey:/var/spool/postfix/postgrey/ - networks: - mail: - - dovecot: - build: dovecot - restart: unless-stopped - env_file: - - ssl.env - - ldap.env - volumes: - - /var/lib/docker-volumes/mail/vmail/:/var/vmail/ - - /var/lib/acme/:/var/lib/acme/:ro - - dovecot-dh:/var/lib/dovecot/ - networks: - mail: - auth: - ports: - - "143:143" - logging: - driver: journald - - rainloop: - image: runningman84/rainloop - restart: unless-stopped - env_file: - - rainloop.env - volumes: - - /var/lib/docker-volumes/mail/rainloop/:/var/www/html/data - networks: - mail: - -networks: - mail: - auth: - external: true - name: auth_auth - -volumes: - postfix-dh: {} - dovecot-dh: {} - postgrey: {} diff --git a/docker-compose.yml.example b/docker-compose.yml.example new file mode 100644 index 0000000..0ed661d --- /dev/null +++ b/docker-compose.yml.example @@ -0,0 +1,51 @@ +version: "3" +services: + postfix: + build: postfix + restart: always + environment: + - MYHOSTNAME=mail.example.com + - MYDOMAIN=example.com + - MAIL_NAME=sbrudermail + - MESSAGE_SIZE_LIMIT=15360000 + - DOMAINS=example.com example.net + - POSTMASTER=postmaster@example.com + - LDAP_SERVER=ldap.example.com + - LDAP_BASE=ou=users,dc=example,dc=com + - LDAP_USER=cn=readonly,dc=example,dc=com + - LDAP_PASSWORD=v3rys3cur3 + volumes: + - ./aliases.d:/etc/postfix/aliases.d + - ../web/certs/mail.example.com/:/tls/:ro + - ./spool/postfix:/var/spool/postfix + - dhparams:/etc/postfix/dh-params + ports: + - "25:25" + - "587:587" + depends_on: + - postgrey + - dovecot + + postgrey: + build: postgrey + restart: always + volumes: + - ./spool/postgrey:/var/spool/postfix/postgrey + + dovecot: + build: dovecot + restart: always + environment: + - LDAP_SERVER=ldap.example.com + - LDAP_BASE=ou=users,dc=example,dc=com + - LDAP_USER=cn=readonly,dc=example,dc=com + - LDAP_PASSWORD=v3rys3cur3 + volumes: + - ./vmail/:/var/vmail/ + - ../web/certs/mail.example.com/:/tls/:ro + - dhparams:/etc/dovecot/dh-params + ports: + - "143:143" + +volumes: + dhparams: {} diff --git a/dovecot/10-logging.conf b/dovecot/10-logging.conf index f5bfbfd..387553e 100644 --- a/dovecot/10-logging.conf +++ b/dovecot/10-logging.conf @@ -1,9 +1 @@ log_path = /dev/stdout - -plugin { - # Events to log. Also available: flag_change append - #mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename - # Available fields: uid, box, msgid, from, subject, size, vsize, flags - # size and vsize are available only for expunge and copy events. - #mail_log_fields = uid box msgid size -} diff --git a/dovecot/10-master.conf b/dovecot/10-master.conf index 7cb5541..82500ce 100644 --- a/dovecot/10-master.conf +++ b/dovecot/10-master.conf @@ -1,118 +1,24 @@ -#default_process_limit = 100 -#default_client_limit = 1000 - -# Default VSZ (virtual memory size) limit for service processes. This is mainly -# intended to catch and kill processes that leak memory before they eat up -# everything. -#default_vsz_limit = 256M - -# Login user is internally used by login processes. This is the most untrusted -# user in Dovecot system. It shouldn't have access to anything at all. -#default_login_user = dovenull - -# Internal user is used by unprivileged processes. It should be separate from -# login user, so that login processes can't disturb other processes. -#default_internal_user = dovecot +protocols = imap lmtp service imap-login { inet_listener imap { - #port = 143 - } - inet_listener imaps { - #port = 993 - #ssl = yes - } - - # Number of connections to handle before starting a new process. Typically - # the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0 - # is faster. - #service_count = 1 - - # Number of processes to always keep waiting for more connections. - #process_min_avail = 0 - - # If you set service_count=0, you probably need to grow this. - #vsz_limit = $default_vsz_limit -} - -service pop3-login { - inet_listener pop3 { - #port = 110 - } - inet_listener pop3s { - #port = 995 - #ssl = yes } } service lmtp { - unix_listener lmtp { - #mode = 0666 - } - - # Create inet listener only if you can't use the above UNIX socket inet_listener lmtp { port = 24 } } service imap { - # Most of the memory goes to mmap()ing files. You may need to increase this - # limit if you have huge mailboxes. - #vsz_limit = $default_vsz_limit - - # Max. number of IMAP processes (connections) - #process_limit = 1024 -} - -service pop3 { - # Max. number of POP3 processes (connections) - #process_limit = 1024 } service auth { - # auth_socket_path points to this userdb socket by default. It's typically - # used by dovecot-lda, doveadm, possibly imap process, etc. Users that have - # full permissions to this socket are able to get a list of all usernames and - # get the results of everyone's userdb lookups. - # - # The default 0666 mode allows anyone to connect to the socket, but the - # userdb lookups will succeed only if the userdb returns an "uid" field that - # matches the caller process's UID. Also if caller's uid or gid matches the - # socket's uid or gid the lookup succeeds. Anything else causes a failure. - # - # To give the caller full permissions to lookup all users, set the mode to - # something else than 0666 and Dovecot lets the kernel enforce the - # permissions (e.g. 0777 allows everyone full permissions). unix_listener auth-userdb { - #mode = 0666 - #user = - #group = } - # Postfix smtp-auth inet_listener { port = 100 } - - # Auth process is run as this user. - #user = $default_internal_user } - -service auth-worker { - # Auth worker process is run as root by default, so that it can access - # /etc/shadow. If this isn't necessary, the user should be changed to - # $default_internal_user. - #user = root -} - -service dict { - # If dict proxy is used, mail processes should have access to its socket. - # For example: mode=0660, group=vmail and global mail_access_groups=vmail - unix_listener dict { - #mode = 0600 - #user = - #group = - } -} - diff --git a/dovecot/10-ssl.conf b/dovecot/10-ssl.conf new file mode 100644 index 0000000..044133c --- /dev/null +++ b/dovecot/10-ssl.conf @@ -0,0 +1,4 @@ +ssl = required +ssl_cert = > /etc/dovecot/dovecot-ldap.conf.ext << DOVECOTLDAP +cat >> /etc/dovecot/dovecot-ldap.conf.ext << EOF +auth_bind = yes hosts = $LDAP_SERVER base = $LDAP_BASE -auth_bind = yes -pass_filter = (&(objectClass=posixAccount)(mail=%u)) -DOVECOTLDAP - -cat > /etc/dovecot/conf.d/10-ssl.conf << SSL -ssl = required -ssl_cert = <$TLS_FULLCHAIN -ssl_key = <$TLS_KEY - -ssl_dh_parameters_length = 2048 -SSL +dn = $LDAP_USER +dnpass = $LDAP_PASSWORD +tls = yes +pass_filter = (mail=%u) +EOF [ -e /var/lib/dovecot/instances ] && rm -rf /var/lib/dovecot/instances diff --git a/ldap.env.dist b/ldap.env.dist deleted file mode 100644 index fec1a04..0000000 --- a/ldap.env.dist +++ /dev/null @@ -1,2 +0,0 @@ -LDAP_SERVER=ldap -LDAP_BASE=dc=ldap,dc=sbruder,dc=de diff --git a/newaliases.sh b/newaliases.sh deleted file mode 100755 index bf9116f..0000000 --- a/newaliases.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh -docker-compose exec postfix sh -c " -chown root:root /etc/postfix/virtual; -postmap /etc/postfix/virtual; -chown $(id -u):$(id -g) /etc/postfix/virtual" diff --git a/postfix.env.dist b/postfix.env.dist deleted file mode 100644 index 7cfdfaf..0000000 --- a/postfix.env.dist +++ /dev/null @@ -1,12 +0,0 @@ -# FQDN! -MYHOSTNAME=home.sbruder.de -# Without first part -MYDOMAIN=sbruder.de -# Used in EHLO -MAIL_NAME=sbrudermail - -MESSAGE_SIZE_LIMIT=15360000 - -DOMAINS=sbruder.de - -POSTMASTER=postmaster@sbruder.de diff --git a/postfix/Dockerfile b/postfix/Dockerfile index 6a66211..5759a4a 100644 --- a/postfix/Dockerfile +++ b/postfix/Dockerfile @@ -1,59 +1,68 @@ -FROM alpine:latest +# needed for postfix 3.4 +FROM alpine:edge RUN apk add --no-cache \ - supervisor \ - rsyslog \ - ca-certificates \ - postfix \ - postfix-pcre \ - postfix-ldap \ - openssl - -COPY rsyslog.conf /etc/rsyslog.conf + ca-certificates \ + openssl \ + postfix \ + postfix-ldap \ + postfix-pcre RUN echo '' > /etc/postfix/main.cf \ && postconf -e myorigin='$mydomain' \ && postconf -e mynetworks='127.0.0.0/8 [::1]/128' \ - && postconf -e smtpd_relay_restrictions='permit_sasl_authenticated, permit_mynetworks, reject_unlisted_sender, reject_unlisted_recipient, reject_unknown_sender_domain, reject_unknown_recipient_domain, reject_invalid_hostname, reject_non_fqdn_sender, reject_non_fqdn_recipient, reject_unauth_destination, reject_unknown_hostname' \ - && postconf -e smtpd_recipient_restrictions='check_recipient_access hash:/etc/postfix/access_recipient, check_sender_access hash:/etc/postfix/access_sender, check_helo_access hash:/etc/postfix/access_helo, check_client_access cidr:/etc/postfix/access_client, reject_non_fqdn_sender, reject_non_fqdn_recipient, reject_unknown_sender_domain, reject_unknown_recipient_domain, permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, reject_rbl_client zen.spamhaus.org, reject_rbl_client ix.dnsbl.manitu.net, check_policy_service inet:postgrey:25 reject_unverified_recipient, permit' \ + && postconf -e smtpd_recipient_restrictions=' \ + reject_non_fqdn_sender, \ + reject_non_fqdn_recipient, \ + reject_unknown_sender_domain, \ + reject_unknown_recipient_domain, \ + permit_sasl_authenticated, \ + permit_mynetworks, \ + reject_unauth_destination, \ + reject_rbl_client zen.spamhaus.org, \ + reject_rbl_client ix.dnsbl.manitu.net, \ + check_policy_service inet:postgrey:25 \ + reject_unverified_recipient, \ + permit \ + ' \ && postconf -e recipient_delimiter='+' \ && postconf -e smtpd_banner='$myhostname ESMTP $mail_name' \ && postconf -e smtpd_use_tls='yes' \ + && postconf -e smtpd_tls_loglevel='0' \ + && postconf -e smtpd_tls_key_file='/tls/privkey' \ + && postconf -e smtpd_tls_cert_file='/tls/fullchain' \ && postconf -e smtpd_tls_security_level='may' \ && postconf -e smtpd_tls_auth_only='yes' \ - && postconf -e smtpd_tls_loglevel='1' \ && postconf -e smtpd_tls_mandatory_protocols='!SSLv2,!SSLv3,!TLSv1,!TLSv1.1' \ && postconf -e smtpd_tls_protocols='!SSLv2,!SSLv3,!TLSv1,!TLSv1.1' \ - && postconf -e smtpd_tls_mandatory_ciphers='medium' \ - && postconf -e tls_medium_cipherlist='AES128+EECDH:AES128+EDH' \ + && postconf -e smtpd_tls_mandatory_ciphers='high' \ + && postconf -e smtpd_tls_exclude_ciphers='aNULL' \ && postconf -e smtpd_tls_dh1024_param_file='/etc/postfix/dh-params/2048.pem' \ - && postconf -e smtpd_tls_dh512_param_file='/etc/postfix/dh-params/512.pem' \ && postconf -e smtpd_tls_eecdh_grade='strong' \ + && postconf -e smtp_tls_security_level='may' \ + && postconf -e smtp_tls_mandatory_ciphers='medium' \ && postconf -e tls_preempt_cipherlist='yes' \ && postconf -e smtpd_sasl_auth_enable='yes' \ && postconf -e smtpd_sasl_type='dovecot' \ && postconf -e smtpd_sasl_path='inet:dovecot:100' \ && postconf -e virtual_transport='lmtp:[dovecot]' \ && postconf -e virtual_alias_maps='hash:/etc/postfix/virtual' \ - && postconf -e virtual_mailbox_maps='ldap:/etc/postfix/ldap/virtual_mailbox_maps.cf' \ + && postconf -e virtual_mailbox_maps='ldap:/etc/postfix/virtual_mailbox_maps.cf' \ && postconf -e virtual_mailbox_limit='0' \ - && postconf -e smtp_tls_security_level='may' \ && postconf -e disable_vrfy_command='yes' \ && postconf -e enable_long_queue_ids='yes' \ && postconf -e strict_rfc821_envelopes='yes' \ - && echo 'MAILER-DAEMON: postmaster\npostmaster: root' > /etc/postfix/aliases + && postconf -e maillog_file='/dev/stdout' \ + && newaliases -COPY master.cf /etc/postfix/master.cf -COPY smtp_header_checks /etc/postfix/smtp_header_checks +RUN cp -r /var/spool/postfix /var/spool/postfix-skel -COPY postfix.sh /postfix.sh -COPY supervisord.conf /etc/supervisord.conf +COPY master.cf /etc/postfix/ +COPY smtp_header_checks /etc/postfix/ +COPY virtual_mailbox_maps.cf /etc/postfix/ -COPY ldap /etc/postfix/ldap +COPY scripts /usr/local/bin/ -ENTRYPOINT ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"] - -VOLUME ["/etc/postfix/dh-params/"] -VOLUME ["/var/spool/postfix"] +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] EXPOSE 25 587 diff --git a/postfix/ldap/virtual_alias_maps.cf b/postfix/ldap/virtual_alias_maps.cf deleted file mode 100644 index 46113fb..0000000 --- a/postfix/ldap/virtual_alias_maps.cf +++ /dev/null @@ -1,4 +0,0 @@ -server_host = @@SERVER_HOST@@ -search_base = @@SEARCH_BASE@@ -query_filter = mail=%s -result_attribute = mail diff --git a/postfix/ldap/virtual_mailbox_maps.cf b/postfix/ldap/virtual_mailbox_maps.cf deleted file mode 100644 index a71617c..0000000 --- a/postfix/ldap/virtual_mailbox_maps.cf +++ /dev/null @@ -1,10 +0,0 @@ -server_host = @@SERVER_HOST@@ -server_port = 389 -version = 3 -bind = no -search_base = @@SEARCH_BASE@@ -scope = sub -query_filter = (mail=%s) -result_attribute= mail -result_format = %d/%u/Maildir/ -debuglevel = 0 diff --git a/postfix/master.cf b/postfix/master.cf index 459170b..760dedb 100644 --- a/postfix/master.cf +++ b/postfix/master.cf @@ -31,4 +31,6 @@ anvil unix - - n - 1 anvil scache unix - - n - 1 scache subcleanup unix n - n - 0 cleanup - -o header_checks=pcre:/etc/postfix/smtp_header_checks + -o header_checks=pcre:/etc/postfix/smtp_header_checks + +postlog unix-dgram n - n - 1 postlogd diff --git a/postfix/postfix.sh b/postfix/postfix.sh deleted file mode 100755 index 34acee1..0000000 --- a/postfix/postfix.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh -[ -e /etc/postfix/dh-params/512.pem ] || openssl gendh -out /etc/postfix/dh-params/512.pem -2 512 -[ -e /etc/postfix/dh-params/2048.pem ] || openssl gendh -out /etc/postfix/dh-params/2048.pem -2 2048 - -postconf -e myhostname="$MYHOSTNAME" -postconf -e mydomain="$MYDOMAIN" -postconf -e mail_name="$MAIL_NAME" - -postconf -e message_size_limit="$MESSAGE_SIZE_LIMIT" - -postconf -e smtpd_tls_key_file="$TLS_KEY" -postconf -e smtpd_tls_cert_file="$TLS_FULLCHAIN" - -postconf -e virtual_mailbox_domains="$DOMAINS" - -for hash in access_recipient access_sender access_helo virtual;do - touch /etc/postfix/$hash - chown root:root /etc/postfix/$hash - postmap /etc/postfix/$hash -done - -# set ldap search base -sed -i \ - -e "s/@@SEARCH_BASE@@/${LDAP_BASE}/g" \ - -e "s/@@SERVER_HOST@@/${LDAP_SERVER}/g" \ - /etc/postfix/ldap/virtual_mailbox_maps.cf - -# is cidr, so not included in the hash section -touch /etc/postfix/access_client - -echo "MAILER-DAEMON: postmaster\npostmaster: $POSTMASTER" > /etc/postfix/aliases - -newaliases - -# is mounted, so no default structure -( - cd /var/spool/postfix - chown postfix:postfix . - for dir in active bounce corrupt defer deferred flush hold incoming maildrop pid private public saved trace; do - mkdir -p $dir - chown postfix:postfix $dir - done -) - -exec /usr/lib/postfix/master -c /etc/postfix -d diff --git a/postfix/scripts/entrypoint.sh b/postfix/scripts/entrypoint.sh new file mode 100755 index 0000000..93f1804 --- /dev/null +++ b/postfix/scripts/entrypoint.sh @@ -0,0 +1,30 @@ +#!/bin/sh +if ! [ -e /etc/postfix/dh-params/2048.pem ]; then + openssl dhparam -out /etc/postfix/dh-params/2048.pem -2 2048 +fi + +postconf -e myhostname="$MYHOSTNAME" +postconf -e mydomain="$MYDOMAIN" +postconf -e mail_name="$MAIL_NAME" + +postconf -e message_size_limit="$MESSAGE_SIZE_LIMIT" + +postconf -e virtual_mailbox_domains="$DOMAINS" + +sed -i \ + -e "s/@@LDAP_BASE@@/${LDAP_BASE}/g" \ + -e "s/@@LDAP_HOST@@/${LDAP_SERVER}/g" \ + -e "s/@@LDAP_USER@@/${LDAP_USER}/g" \ + -e "s/@@LDAP_PASSWORD@@/${LDAP_PASSWORD}/g" \ + /etc/postfix/virtual_mailbox_maps.cf + +if ! [ -e /var/spool/postfix/.initialized ]; then + cp -r /var/spool/postfix-skel/* /var/spool/postfix + chown -R 100:101 /var/spool/postfix + touch /var/spool/postfix/.initialized +fi + +map-virtual.sh c /etc/postfix/aliases.d/ virtual + +inotifyd map-virtual.sh /etc/postfix/aliases.d/ & +/usr/libexec/postfix/master -c /etc/postfix -d diff --git a/postfix/scripts/map-virtual.sh b/postfix/scripts/map-virtual.sh new file mode 100755 index 0000000..31601de --- /dev/null +++ b/postfix/scripts/map-virtual.sh @@ -0,0 +1,7 @@ +#!/bin/sh +if [ "$1" == "c" ] && [ "$2" == "/etc/postfix/aliases.d/" ] && [ "$3" == "virtual" ]; then + cp /etc/postfix/aliases.d/virtual /etc/postfix/virtual + chown root:root /etc/postfix/virtual + postmap /etc/postfix/virtual + echo "$(date +"%b %d %H:%M:%H"): remapped virtual aliases" +fi diff --git a/postfix/supervisord.conf b/postfix/supervisord.conf deleted file mode 100644 index 7099a3e..0000000 --- a/postfix/supervisord.conf +++ /dev/null @@ -1,23 +0,0 @@ -[supervisord] -nodaemon=true -loglevel=critical -logfile=/dev/stdout -logfile_maxbytes=0 - -[program:rsyslog] -command=/usr/sbin/rsyslogd -n -stdout_logfile=/dev/null -stdout_logfile_maxbytes=0 -redirect_stderr=true - -[program:postfix] -command=/postfix.sh -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -redirect_stderr=true - -[program:logs] -command=/usr/bin/tail -F /var/log/maillog -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -redirect_stderr=true diff --git a/postfix/virtual_mailbox_maps.cf b/postfix/virtual_mailbox_maps.cf new file mode 100644 index 0000000..ad8c718 --- /dev/null +++ b/postfix/virtual_mailbox_maps.cf @@ -0,0 +1,13 @@ +server_host = @@LDAP_HOST@@ +server_port = 389 +version = 3 +bind = yes +start_tls = yes +bind_dn = @@LDAP_USER@@ +bind_pw = @@LDAP_PASSWORD@@ +search_base = @@LDAP_BASE@@ +scope = sub +query_filter = (mail=%s) +result_attribute= mail +result_format = %d/%u/Maildir/ +debuglevel = 0 diff --git a/postgrey/Dockerfile b/postgrey/Dockerfile index 9336218..250d908 100644 --- a/postgrey/Dockerfile +++ b/postgrey/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:latest +FROM alpine RUN apk add --no-cache \ perl-netaddr-ip \ diff --git a/rainloop.env.dist b/rainloop.env.dist deleted file mode 100644 index c8158e7..0000000 --- a/rainloop.env.dist +++ /dev/null @@ -1,2 +0,0 @@ -RAINLOOP_ADMIN_LOGIN=admin -RAINLOOP_ADMIN_PASSWORD=Oong7Shohg5caebi diff --git a/ssl.env.dist b/ssl.env.dist deleted file mode 100644 index f284538..0000000 --- a/ssl.env.dist +++ /dev/null @@ -1,2 +0,0 @@ -TLS_FULLCHAIN=/var/lib/acme/live/home.sbruder.de/fullchain -TLS_KEY=/var/lib/acme/live/home.sbruder.de/privkey