#!/bin/bash # # Mail Service PATH=$HOME/.docker/cli-plugins:/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin config_mail() { echo -ne "* Configuring mail container.." if [ ! -d "/federated/apps/mail" ]; then mkdir -p /federated/apps/mail/data/root/certs &> /dev/null mkdir -p /federated/apps/mail/data/var/mail &> /dev/null mkdir -p /federated/apps/mail/data/var/mail-state &> /dev/null mkdir -p /federated/apps/mail/data/var/log/mail &> /dev/null mkdir -p /federated/apps/mail/data/tmp/docker-mailserver &> /dev/null mkdir -p /federated/apps/mail/data/etc/dovecot/conf.d &> /dev/null mkdir -p /federated/apps/mail/data/home &> /dev/null cp /federated/certs/certs/$DOMAIN.crt /federated/certs/private/$DOMAIN.key /federated/apps/mail/data/root/certs/ chgrp 5000 /federated/apps/mail/data/home chmod g+rwx /federated/apps/mail/data/home fi cat > /federated/apps/mail/docker-compose.yml < /federated/apps/mail/.env < /federated/apps/mail/data/tmp/docker-mailserver/postfix-main.cf <<'EOF' smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unknown_sender_domain, reject_sender_login_mismatch, reject_unknown_reverse_client_hostname, reject_unknown_client_hostname smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_invalid_helo_hostname, check_policy_service unix:private/policyd-spf smtpd_sender_login_maps = ldap:/etc/postfix/ldap-aliases.cf smtpd_helo_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_unknown_helo_hostname EOF cat > /federated/apps/mail/data/etc/dovecot/conf.d/90-sieve.conf <<'EOF' ## ## Settings for the Sieve interpreter ## # Do not forget to enable the Sieve plugin in 15-lda.conf and 20-lmtp.conf # by adding it to the respective mail_plugins= settings. plugin { # The path to the user's main active script. If ManageSieve is used, this the # location of the symbolic link controlled by ManageSieve. sieve = ~/.dovecot.sieve # The default Sieve script when the user has none. This is a path to a global # sieve script file, which gets executed ONLY if user's private Sieve script # doesn't exist. Be sure to pre-compile this script manually using the sievec # command line tool. # --> See sieve_before fore executing scripts before the user's personal # script. #sieve_default = /var/lib/dovecot/sieve/default.sieve # Directory for :personal include scripts for the include extension. This # is also where the ManageSieve service stores the user's scripts. sieve_dir = ~/sieve # Directory for :global include scripts for the include extension. #sieve_global_dir = # Path to a script file or a directory containing script files that need to be # executed before the user's script. If the path points to a directory, all # the Sieve scripts contained therein (with the proper .sieve extension) are # executed. The order of execution within a directory is determined by the # file names, using a normal 8bit per-character comparison. Multiple script # file or directory paths can be specified by appending an increasing number. sieve_before = /usr/lib/dovecot/sieve-global/before/ #sieve_before2 = #sieve_before3 = (etc...) # Identical to sieve_before, only the specified scripts are executed after the # user's script (only when keep is still in effect!). Multiple script file or # directory paths can be specified by appending an increasing number. sieve_after = /usr/lib/dovecot/sieve-global/after/ #sieve_after2 = #sieve_after2 = (etc...) # Which Sieve language extensions are available to users. By default, all # supported extensions are available, except for deprecated extensions or # those that are still under development. Some system administrators may want # to disable certain Sieve extensions or enable those that are not available # by default. This setting can use '+' and '-' to specify differences relative # to the default. For example `sieve_extensions = +imapflags' will enable the # deprecated imapflags extension in addition to all extensions were already # enabled by default. #sieve_extensions = +notify +imapflags sieve_extensions = +notify +imapflags +special-use +vnd.dovecot.pipe +vnd.dovecot.filter # Which Sieve language extensions are ONLY available in global scripts. This # can be used to restrict the use of certain Sieve extensions to administrator # control, for instance when these extensions can cause security concerns. # This setting has higher precedence than the `sieve_extensions' setting # (above), meaning that the extensions enabled with this setting are never # available to the user's personal script no matter what is specified for the # `sieve_extensions' setting. The syntax of this setting is similar to the # `sieve_extensions' setting, with the difference that extensions are # enabled or disabled for exclusive use in global scripts. Currently, no # extensions are marked as such by default. #sieve_global_extensions = # The Pigeonhole Sieve interpreter can have plugins of its own. Using this # setting, the used plugins can be specified. Check the Dovecot wiki # (wiki2.dovecot.org) or the pigeonhole website # (http://pigeonhole.dovecot.org) for available plugins. # The sieve_extprograms plugin is included in this release. #sieve_plugins = sieve_plugins = sieve_imapsieve sieve_extprograms # The separator that is expected between the :user and :detail # address parts introduced by the subaddress extension. This may # also be a sequence of characters (e.g. '--'). The current # implementation looks for the separator from the left of the # localpart and uses the first one encountered. The :user part is # left of the separator and the :detail part is right. This setting # is also used by Dovecot's LMTP service. #recipient_delimiter = + # The maximum size of a Sieve script. The compiler will refuse to compile any # script larger than this limit. If set to 0, no limit on the script size is # enforced. #sieve_max_script_size = 1M # The maximum number of actions that can be performed during a single script # execution. If set to 0, no limit on the total number of actions is enforced. #sieve_max_actions = 32 # The maximum number of redirect actions that can be performed during a single # script execution. If set to 0, no redirect actions are allowed. #sieve_max_redirects = 4 # The maximum number of personal Sieve scripts a single user can have. If set # to 0, no limit on the number of scripts is enforced. # (Currently only relevant for ManageSieve) #sieve_quota_max_scripts = 0 # The maximum amount of disk storage a single user's scripts may occupy. If # set to 0, no limit on the used amount of disk storage is enforced. # (Currently only relevant for ManageSieve) #sieve_quota_max_storage = 0 # Locations of programs that can be called by the sieve_extprograms plugin sieve_pipe_bin_dir = /usr/lib/dovecot/sieve-pipe sieve_filter_bin_dir = /usr/lib/dovecot/sieve-filter sieve_vacation_send_from_recipient = yes } EOF cat > /federated/apps/mail/data/tmp/docker-mailserver/fail2ban-jail.cf <<'EOF' [DEFAULT] # "bantime" is the number of seconds that a host is banned. bantime = 3h # A host is banned if it has generated "maxretry" during the last "findtime" # seconds. findtime = 5m # "maxretry" is the number of failures before a host get banned. maxretry = 12 # "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban # will not ban a host which matches an address in this list. Several addresses # can be defined using space (and/or comma) separator. ignoreip = 127.0.0.1/8,192.168.0.0/16 # default ban action # nftables-multiport: block IP only on affected port # nftables-allports: block IP on all ports banaction = nftables-allports [dovecot] enabled = true [postfix] enabled = true [postfix-sasl] enabled = true # This jail is used for manual bans. # To ban an IP address use: setup.sh fail2ban ban [custom] enabled = true bantime = 180d port = smtp,pop3,pop3s,imap,imaps,submission,submissions,sieve EOF echo -ne "done.\n" } start_mail() { # Start service with command to make sure it's up before proceeding start_service "mail" "nc -z 192.168.0.16 25 &> /dev/null" "25" # Generate the DKIM DNS key and setup run_command "docker exec mail setup config dkim" run_command "docker exec mail setup config dkim keysize 2048 domain $DOMAIN" docker exec mail bash -c "setup config dkim domain '$DOMAIN' &> /dev/null" [ $? -ne 0 ] && fail "Couldn't setup DKIM domain" # Insert the DKIM DNS TXT entry into /federated/apps/pdns container DKIM_RECORD_STRIP=`cat /federated/apps/mail/data/tmp/docker-mailserver/opendkim/keys/$DOMAIN/mail.txt | sed 's/.*(//'` DKIM_RECORD=`echo $DKIM_RECORD_STRIP | sed 's/).*//'` docker exec pdns pdnsutil add-record $DOMAIN mail._domainkey TXT 86400 "$DKIM_RECORD" &> /dev/null [ $? -ne 0 ] && fail "Couldn't insert DKIM record into /federated/apps/pdns container" # Insert the DMARC DNS TXT entry into /federated/apps/pdns container docker exec pdns pdnsutil add-record $DOMAIN _dmarc TXT 86400 "\"v=DMARC1; p=quarantine; rua=mailto:admin@$DOMAIN; ruf=mailto:admin@$DOMAIN; sp=none; ri=86400\"" &> /dev/null [ $? -ne 0 ] && fail "Couldn't insert DMARC record into /federated/apps/pdns container" # Stop and Start mail to reload DKIM run_command "/federated/bin/stop mail" run_command "/federated/bin/start mail" echo -ne "done.\n" } uninstall_mail() { echo -ne "* Uninstalling mail container.." # First stop the service cd /federated/apps/mail && docker compose -f docker-compose.yml -p mail down &> /dev/null # Delete the app directory rm -rf /federated/apps/mail # Delete the image docker.io/mailserver/docker-mailserver:$IMAGE_VERSION &> /dev/null # Delete the DNS record docker exec pdns pdnsutil delete-rrset $DOMAIN mail._domainkey TXT docker exec pdns pdnsutil delete-rrset $DOMAIN _dmarc TXT echo -ne "done.\n" }