#!/usr/bin/env bash # Mail Installer # Min. Requirement : GNU/Linux Ubuntu 14.04 # Last Build : 12/07/2019 # Author : ESLabs.ID (eslabs.id@gmail.com) # Since Version : 1.0.0 # Include helper functions. if [ "$(type -t run)" != "function" ]; then BASEDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd ) # shellchechk source=scripts/helper.sh # shellcheck disable=SC1090 . "${BASEDIR}/helper.sh" fi # Make sure only root can run this installer script. requires_root ## # Install Postfix mail server. # function install_postfix() { if "${AUTO_INSTALL}"; then DO_INSTALL_POSTFIX="y" else while [[ "${DO_INSTALL_POSTFIX}" != "y" && "${DO_INSTALL_POSTFIX}" != "n" ]]; do read -rp "Do you want to install Postfix Mail Transfer Agent? [y/n]: " -i y -e DO_INSTALL_POSTFIX done fi if [[ ${DO_INSTALL_POSTFIX} == y* && "${INSTALL_MAILER}" == true ]]; then echo "Installing Postfix Mail Transfer Agent..." if hash apt 2>/dev/null; then run apt install -qq -y mailutils postfix else fail "Unable to install NGiNX, this GNU/Linux distribution is not supported." fi # Configure Postfix. run postconf -e "inet_interfaces = all" run postconf -e "inet_protocols = all" run postconf -e "alias_maps = hash:/etc/aliases" run postconf -e "alias_database = hash:/etc/aliases" run postconf -e "home_mailbox = Maildir/" run postconf -e "myhostname = ${HOSTNAME}" #run postconf -e "mydomain = eslabs.id" run postconf -e "myorigin = localhost" run postconf -e "mydestination = \$myhostname, localhost, localhost.localdomain" # Setting up SMTP authentication. run postconf -e "smtpd_sasl_type = dovecot" run postconf -e "smtpd_sasl_path = private/auth" run postconf -e "smtpd_sasl_local_domain =" run postconf -e "smtpd_sasl_security_options = noanonymous" run postconf -e "broken_sasl_auth_clients = yes" run postconf -e "smtpd_sasl_auth_enable = yes" run postconf -e "smtpd_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination,reject_invalid_hostname,reject_non_fqdn_hostname,reject_non_fqdn_sender,reject_non_fqdn_recipient,reject_unknown_sender_domain,reject_rbl_client sbl.spamhaus.org,reject_rbl_client cbl.abuseat.org" # Getting Let's Encrypt certificates. local CERTPATH="" if [[ $(validate_fqdn "${SENDER_DOMAIN}") = true \ && $(dig "${SENDER_DOMAIN}" +short) = "${SERVER_IP}" ]]; then run certbot certonly --standalone --agree-tos --preferred-challenges http -d "${SENDER_DOMAIN}" CERTPATH="/etc/letsencrypt/live/${SENDER_DOMAIN}" elif [[ $(dig "${HOSTNAME}" +short) = "${SERVER_IP}" ]]; then run certbot certonly --standalone --agree-tos --preferred-challenges http --webroot-path=/usr/share/nginx/html -d "${HOSTNAME}" CERTPATH="/etc/letsencrypt/live/${HOSTNAME}" fi # Enable Postfix secure. if [ -n "${CERTPATH}" ]; then run postconf -e "smtpd_tls_cert_file = ${CERTPATH}/fullchain.pem" run postconf -e "smtpd_tls_key_file = ${CERTPATH}/privkey.pem" run postconf -e "smtp_tls_security_level = may" run postconf -e "smtpd_tls_security_level = may" run postconf -e "smtp_tls_note_starttls_offer = yes" run postconf -e "smtpd_tls_loglevel = 1" run postconf -e "smtpd_tls_received_header = yes" fi # Multiple domain, multiple user settings. # Ref: https://debian-administration.org/article/243/Handling_mail_for_multiple_virtual_domains_with_postfix # Virtual alias mapping. [ ! -d /etc/postfix/virtual ] && run mkdir -p /etc/postfix/virtual [ ! -f /etc/postfix/virtual/addresses ] && run touch /etc/postfix/virtual/addresses run bash -c "echo '${HOSTNAME} DOMAIN ${LEMPER_USERNAME}@${HOSTNAME} ${LEMPER_USERNAME} postmaster@${HOSTNAME} ${LEMPER_USERNAME} root@${HOSTNAME} ${LEMPER_USERNAME} wordpress@${HOSTNAME} ${LEMPER_USERNAME}' > /etc/postfix/virtual/addresses" if [[ $(validate_fqdn "${SENDER_DOMAIN}") = true && "${SENDER_DOMAIN}" != "example.com" ]]; then run bash -c "echo '${SENDER_DOMAIN} DOMAIN @${SENDER_DOMAIN} ${LEMPER_USERNAME}' > /etc/postfix/virtual/addresses" fi postmap /etc/postfix/virtual/addresses run postconf -e "virtual_alias_maps = hash:/etc/postfix/virtual/addresses" # Virtual domain mapping. [ ! -f /etc/postfix/virtual/domains ] && touch /etc/postfix/virtual/domains run bash -c "echo '${HOSTNAME}' > /etc/postfix/virtual/domains" if [[ $(validate_fqdn "${SENDER_DOMAIN}") = true && "${SENDER_DOMAIN}" != "example.com" ]]; then run bash -c "echo '${SENDER_DOMAIN}' >> /etc/postfix/virtual/domains" fi run postconf -e "virtual_alias_domains = /etc/postfix/virtual/domains" # Installation status. if "${DRYRUN}"; then info "Postfix reloaded in dry run mode." else if [[ $(pgrep -c postfix) -gt 0 ]]; then run systemctl reload postfix success "Postfix reloaded successfully." elif [[ -n $(command -v postfix) ]]; then run systemctl start postfix if [[ $(pgrep -c postfix) -gt 0 ]]; then success "Postfix started successfully." else error "Something goes wrong with Postfix installation." fi fi fi fi } ## # Install Dovecot # function install_dovecot() { if "${AUTO_INSTALL}"; then DO_INSTALL_DOVECOT="y" else while [[ "${DO_INSTALL_DOVECOT}" != "y" && "${DO_INSTALL_DOVECOT}" != "n" ]]; do read -rp "Do you want to install Dovecot IMAP and POP3 email server? [y/n]: " -i y -e DO_INSTALL_DOVECOT done fi if [[ ${DO_INSTALL_DOVECOT} == y* && "${INSTALL_MAILER}" == true ]]; then echo "Installing Dovecot IMAP and POP3 email server..." if hash apt 2>/dev/null; then run apt install -qq -y dovecot-core dovecot-common dovecot-imapd dovecot-pop3d else fail "Unable to install NGiNX, this GNU/Linux distribution is not supported." fi # Configure Dovecot. run maildirmake.dovecot /etc/skel/Maildir run maildirmake.dovecot /etc/skel/Maildir/.Drafts run maildirmake.dovecot /etc/skel/Maildir/.Sent run maildirmake.dovecot /etc/skel/Maildir/.Trash run maildirmake.dovecot /etc/skel/Maildir/.Templates run cp -r /etc/skel/Maildir "/home/${LEMPER_USERNAME}" run chown -R lemper:lemper "/home/${LEMPER_USERNAME}/Maildir" run chmod -R 700 "/home/${LEMPER_USERNAME}/Maildir" # Add LEMPer default user to mail group. run adduser "${LEMPER_USERNAME}" mail # Include the Maildir location in terminal and mail profiles. run bash -c "echo -e '\nexport MAIL=~/Maildir' >> /etc/bash.bashrc" run bash -c "echo -e '\nexport MAIL=~/Maildir' >> /etc/profile.d/mail.sh" # User authentication (with SASL). if [ -f /etc/dovecot/conf.d/10-auth.conf ]; then # Disabling the plaintext authentication. if grep -qwE "^#disable_plaintext_auth\ =\ [a-zA-Z]*" /etc/dovecot/conf.d/10-auth.conf; then run sed -i "/^#disable_plaintext_auth/a disable_plaintext_auth\ =\ yes" \ /etc/dovecot/conf.d/10-auth.conf else run sed -i "s/disable_plaintext_auth\ =\ [a-zA-Z]*/disable_plaintext_auth\ =\ yes/g" \ /etc/dovecot/conf.d/10-auth.conf fi # Enabling login authentication mechanism. if grep -qwE "^#auth_mechanisms\ =\ [a-zA-Z]*" /etc/dovecot/conf.d/10-auth.conf; then run sed -i "/^#auth_mechanisms/a auth_mechanisms = plain login" \ /etc/dovecot/conf.d/10-auth.conf else run sed -i "s/auth_mechanisms\ =\ [a-zA-Z]*/auth_mechanisms\ =\ plain\ login/g" \ /etc/dovecot/conf.d/10-auth.conf fi fi # Set the mail directory to use the same format as Postfix. if [ -f /etc/dovecot/conf.d/10-mail.conf ]; then # Maildir. if grep -qwE "^mail_location\ =\ [^[:digit:]]*$" /etc/dovecot/conf.d/10-mail.conf; then run sed -i "s/^mail_location\ =\ [^[:digit:]]*$/mail_location\ =\ maildir:~\/Maildir/g" \ /etc/dovecot/conf.d/10-mail.conf else run sed -iE "/^#mail_location\ =\ [^[:digit:]]*$/a mail_location\ =\ maildir:~\/Maildir" \ /etc/dovecot/conf.d/10-mail.conf fi fi # Enable IMAP and POP3 protocols for email clients. if [ -f /etc/dovecot/conf.d/10-master.conf ]; then # IMAP run sed -i "s/#port\ =\ 143/port\ =\ 143/g" /etc/dovecot/conf.d/10-master.conf # IMAPS run sed -i "s/#port\ =\ 993/port\ =\ 993/g" /etc/dovecot/conf.d/10-master.conf run sed -i "s/#ssl\ =\ yes/ssl\ =\ yes/g" /etc/dovecot/conf.d/10-master.conf # POP3 run sed -i "s/#port\ =\ 110/port\ =\ 110/g" /etc/dovecot/conf.d/10-master.conf # IMAPS run sed -i "s/#port\ =\ 995/port\ =\ 995/g" /etc/dovecot/conf.d/10-master.conf run sed -i "s/#ssl\ =\ yes/ssl\ =\ yes/g" /etc/dovecot/conf.d/10-master.conf # Postfix SMTP auth. run sed -i "s/#mode\ =\ 0666/mode\ =\ 0666/g" /etc/dovecot/conf.d/10-master.conf run sed -i "s/#user\ =\ postfix/user\ =\ postfix/g" /etc/dovecot/conf.d/10-master.conf run sed -i "s/#group\ =\ postfix/group\ =\ postfix/g" /etc/dovecot/conf.d/10-master.conf fi # Installation status. if "${DRYRUN}"; then info "Dovecot installed in dryrun mode." else if [[ $(pgrep -c dovecot) -gt 0 ]]; then run systemctl reload dovecot success "Dovecot reloaded successfully." elif [[ -n $(command -v dovecot) ]]; then run systemctl start dovecot if [[ $(pgrep -c dovecot) -gt 0 ]]; then success "Dovecot started successfully." else error "Something goes wrong with Dovecot installation." fi fi fi fi } ## TODO: Postfix and Dovecot default configuration # https://www.linode.com/docs/email/postfix/email-with-postfix-dovecot-and-mysql/ ## # Install SPF + DKIM # Ref: https://www.linuxbabe.com/mail-server/setting-up-dkim-and-spf # function install_spf_dkim() { if "${AUTO_INSTALL}"; then DO_INSTALL_SPFDKIM="y" else while [[ "${DO_INSTALL_SPFDKIM}" != "y" && "${DO_INSTALL_SPFDKIM}" != "n" ]]; do read -rp "Do you want to install Postfix Policy Agent and OpenDKIM? [y/n]: " -i y -e DO_INSTALL_SPFDKIM done fi if [[ ${DO_INSTALL_SPFDKIM} == y* && "${INSTALL_SPFDKIM}" == true ]]; then echo "Installing SPF + DKIM email server..." # Installing SPF Policy Agent & OpenDKIM. if hash apt 2>/dev/null; then run apt install -qq -y postfix-policyd-spf-python opendkim opendkim-tools else fail "Unable to install NGiNX, this GNU/Linux distribution is not supported." fi # Update postfix master conf. run bash -c "echo 'policyd-spf unix - n n - 0 spawn user=policyd-spf argv=/usr/bin/policyd-spf' >> /etc/postfix/master.cf" # Update postfix main conf. run postconf -e 'policyd-spf_time_limit = 3600' run postconf -e 'smtpd_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination,reject_invalid_hostname,reject_non_fqdn_hostname,reject_non_fqdn_sender,reject_non_fqdn_recipient,reject_unknown_sender_domain,reject_rbl_client sbl.spamhaus.org,reject_rbl_client cbl.abuseat.org,check_policy_service unix:private/policyd-spf' # Add postfix user to opendkim group. run adduser postfix opendkim # Connect Postfix to OpenDKIM. # Ubuntu ships Postfix in chrooted jail, # need to update OpenDKIM socket to be able to communicate with OpenDKIM. # Create a directory to hold the OpenDKIM socket file and only allow opendkim user and postfix group to access it. run mkdir -p /var/spool/postfix/opendkim run chown opendkim:postfix /var/spool/postfix/opendkim # Update OpenDKIM default socket configuration file. if [ -f /etc/default/opendkim ]; then run sed -i "s/^RUNDIR=\/var\/run\/opendkim/#RUNDIR=\/var\/run\/opendkim/g" /etc/default/opendkim run sed -i "/^#RUNDIR=\/var\/spool\/postfix\/var\/run\/opendkim/s/^#//" /etc/default/opendkim fi # Update OpenDKIM socket config. local OPENDKIM_SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock" run sed -i "/^Socket\s[^[:digit:]]*$/s//#&/" /etc/opendkim.conf run sed -i "/^#Socket\s[^[:digit:]]*$/a Socket\ ${OPENDKIM_SOCKET}" /etc/opendkim.conf # Postfix Milter configuration. run postconf -e "milter_default_action = accept" run postconf -e "milter_protocol = 6" run postconf -e "smtpd_milters = local:/opendkim/opendkim.sock" run postconf -e "non_smtpd_milters = \$smtpd_milters" # Adjusts OpenDKIM config. #run sed -i "/^#Canonicalization/a Canonicalization\ relaxed\/simple" /etc/opendkim.conf run sed -i "/^#Canonicalization/a Canonicalization\ relaxed\/relaxed" /etc/opendkim.conf run sed -i "/^#Mode/a Mode\ sv" /etc/opendkim.conf run sed -i "/^#SubDomains/a SubDomains\ no" /etc/opendkim.conf run sed -i "/^SubDomains/a #ADSPAction\ continue\nAutoRestart\ yes\nAutoRestartRate\ 10\/1M\nBackground\ yes\nDNSTimeout\ 5\nSignatureAlgorithm\ rsa-sha256" /etc/opendkim.conf # Add opendkim user id. if ! grep -qwE "^UserID\ opendkim" /etc/opendkim.conf; then run bash -c "echo 'UserID opendkim' >> /etc/opendkim.conf" fi # Add domains keys. if ! "${DRYRUN}"; then cat >> /etc/opendkim.conf < /etc/opendkim/signing.table" fi # Create the key table. [ ! -f /etc/opendkim/key.table ] && run touch /etc/opendkim/key.table #DOMAIN_KEY="default._domainkey.your-domain.com your-domain.com:default:/etc/opendkim/keys/your-domain.com/default.private" if [[ $(validate_fqdn "${SENDER_DOMAIN}") = true && "${SENDER_DOMAIN}" != "example.com" ]]; then DOMAIN_KEY="lemper._domainkey.${SENDER_DOMAIN} ${SENDER_DOMAIN}:lemper:/etc/opendkim/keys/${SENDER_DOMAIN}/lemper.private" run bash -c "echo '${DOMAIN_KEY}' > /etc/opendkim/key.table" fi # Create trusted hosts. [ ! -f /etc/opendkim/trusted.hosts ] && run touch /etc/opendkim/trusted.hosts run bash -c "echo -e '127.0.0.1\nlocalhost' > /etc/opendkim/trusted.hosts" if [[ $(validate_fqdn "${SENDER_DOMAIN}") = true && "${SENDER_DOMAIN}" != "example.com" ]]; then run bash -c "echo -e '\n*.${SENDER_DOMAIN}' >> /etc/opendkim/trusted.hosts" fi # Generate Private/Public Keypair for sender domain. if [[ $(validate_fqdn "${SENDER_DOMAIN}") = true && "${SENDER_DOMAIN}" != "example.com" ]]; then # Create a separate folder for the domain. run mkdir -p "/etc/opendkim/keys/${SENDER_DOMAIN}" # Generate keys using opendkim-genkey tool. #opendkim-genkey -b 2048 -d your-domain.com -D /etc/opendkim/keys/your-domain.com -s default -v local HASH_LENGTH=${HASH_LENGTH:-2048} run opendkim-genkey -b "${HASH_LENGTH}" -d "${SENDER_DOMAIN}" -D "/etc/opendkim/keys/${SENDER_DOMAIN}" -s lemper -v # Make opendkim as the owner of the private key. #chown opendkim:opendkim /etc/opendkim/keys/your-domain.com/default.private run chown opendkim:opendkim "/etc/opendkim/keys/${SENDER_DOMAIN}/lemper.private" # Publish Your Public Key in DNS Records. DKIM_KEY=$(cat "/etc/opendkim/keys/${SENDER_DOMAIN}/lemper.txt") echo -e "Add this DKIM key to your DNS TXT record!\nDKIM Key: ${DKIM_KEY}" # Test DKIM Key. #run opendkim-testkey -d "${SENDER_DOMAIN}" -s lemper -vvv echo -e "\nAfter then run this command to check your DNS record" echo "opendkim-testkey -d ${SENDER_DOMAIN} -s lemper -vvv" fi fi } echo "[Mail Server Installation]" # Start running things from a call at the end so if this script is executed # after a partial download it doesn't do anything. if [[ -n $(command -v postfix) ]]; then info "Postfix already exists. Installation skipped..." else install_postfix "$@" fi if [[ -n $(command -v dovecot) ]]; then info "Dovecot already exists. Installation skipped..." else install_dovecot "$@" fi install_spf_dkim