#!/usr/bin/env bash set -a set -e # Script is meant for Debian hostnamectl | grep -q 'Debian' || exit 1 # Insert SSH keys here sshkeys='' # Backup mailservers backup_mailserver=() # Exit function function die { read -n 1 -s -p $'\n\e[1;33mError encountered, exiting...\e[0m\n' exit 1 } # Grab options while [ ${1} ] do case ${1} in -u | --user ) if [ ${2} ] then username=${2} shift fi ;; -p | --port ) if [ ${2} ] then ssh_port=${2} shift fi ;; -d | --domain ) if [ ${2} ] then domain_url=${2} shift fi ;; -m | --mail-user ) if [ ${2} ] then mailuser=${2} shift fi ;; -? | -h | --help ) cat <&2 exit 1 ;; esac shift done clear # Assign random alternate SSH port if [ -z ${ssh_port} ] then ssh_port=$(shuf -i 10027-65000 -n 1) fi # Random username if [ -z ${username} ] then username=$(cat /dev/urandom | tr -d -c 'a-z' | fold -w 8 | head -n 1) fi # Domain if [ -z ${domain_url} ] then echo -e '\e[1;34mType in your full mail domain name (eg. mael.elgoog.com)\e[0m' until [ "${domain_url}" ] do read -r -p 'Domain name: ' domain_url [ "${domain_url}" ] || echo -e '\n\e[1;31mDomain name cannot be empty, try again\e[0m' done echo fi # Superuser password echo -e '\e[1;34mCreate a root superuser password\e[0m' until [ "${rootpass}" = "${rootpass2}" -a "${rootpass}" ] do read -s -r -p 'Superuser password: ' rootpass read -s -r -p $'\nVerify superuser password: ' rootpass2 if [ -z "${rootpass}" ] then echo -e '\n\n\e[1;31mPassword field cannot be empty, try again\e[0m' elif [ "${rootpass}" != "${rootpass2}" ] then echo -e '\n\n\e[1;31mPasswords did not match, try again\e[0m' fi done printf '%s\n' "${rootpass}" "${rootpass}" | passwd &>/dev/null echo -e '\n\n\e[1;32mRoot superuser password has been saved\e[0m\n' unset rootpass rootpass2 # User password echo -e '\e[1;34mSet a password for '"${username}"'\e[0m' until [ "${userpass}" = "${userpass2}" -a "${userpass}" ] do read -s -r -p 'User password: ' userpass read -s -r -p $'\nVerify user password: ' userpass2 if [ -z "${userpass}" ] then echo -e '\n\n\e[1;31mPassword field cannot be empty, try again\e[0m' elif [ "${userpass}" != "${userpass2}" ] then echo -e '\n\n\e[1;31mPasswords did not match, try again\e[0m' fi done printf '%s\n' "${userpass}" "${userpass}" "" "" "" "" "" | adduser ${username} &>/dev/null echo -e '\n\n\e[1;32mPassword for '${username}'@'${domain_url}' -p' ${ssh_port}' has been saved\e[0m\n' unset userpass userpass2 # Mail username if [ -z ${mailuser} ] then echo -e '\e[1;34mType in your email username\e[0m' until [ ${mailuser} ] do read -r -p 'Username: ' mailuser [ ${mailuser} ] || echo -e '\n\e[1;31mUsername cannot be empty, try again\e[0m' done echo -e '\n\e[1;32mMail user '${mailuser}'@'${domain_url}' has been saved\e[0m\n' fi # Mail account password echo -e '\e[1;34mCreate a password for your mail account\e[0m' until [ "${mailpass}" = "${mailpass2}" -a "${mailpass}" ] do read -s -r -p 'Mail password: ' mailpass read -s -r -p $'\nVerify mail password: ' mailpass2 if [ -z "${mailpass}" ] then echo -e '\n\n\e[1;31mPassword field cannot be empty, try again\e[0m' elif [ "${mailpass}" != "${mailpass2}" ] then echo -e '\n\n\e[1;31mPasswords did not match, try again\e[0m' fi done echo -e '\n\n\e[1;32mMail password has been saved\e[0m\n' # Postfixadmin password echo -e '\e[1;34mCreate a postfixadmin setup password\e[0m' until [ "${postfixadminpass}" = "${postfixadminpass2}" -a "${postfixadminpass}" ] do read -s -r -p 'Postfixadmin password: ' postfixadminpass read -s -r -p $'\nVerify Postfixadmin password: ' postfixadminpass2 if [ -z "${postfixadminpass}" ] then echo -e '\n\n\e[1;31mPassword field cannot be empty, try again\e[0m' elif [ "${postfixadminpass}" != "${postfixadminpass2}" ] then echo -e '\n\n\e[1;31mPasswords did not match, try again\e[0m' fi done echo -e '\n\n\e[1;32mPostfixadmin password has been saved\e[0m\n' # Static variables ip_addr=$(wget -q4O- ipv4.icanhazip.com) subdomain=$(echo ${domain_url} | awk -F . '{print $1}') domain="$(echo ${domain_url} | awk -F . '{print $2}').$(echo ${domain_url} | awk -F . '{print $3}')" eff_email=eff@${domain} roundcubedbpass=$(cat /dev/urandom | tr -d -c 'a-zA-Z0-9' | fold -w 128 | head -n 1) postfixadmindbpass=$(cat /dev/urandom | tr -d -c 'a-zA-Z0-9' | fold -w 128 | head -n 1) roundcubepass_strength_drive=$(cat /dev/urandom | tr -d -c 'a-z' | fold -w 8 | head -n 1) # Exit if static variable is null if [ -z ${ip_addr} ] || [ -z ${subdomain} ] || [ -z ${domain} ] || [ -z ${eff_email} ] then die 'A static variable is nonexistent' fi # Interaction end echo -e '\e[1;34mUpgrading system...\e[0m' apt remove -y nano exim* &>/dev/null apt update || die apt upgrade -y || die apt dist-upgrade -y || die debconf-set-selections <<< "postfix postfix/mailname string ${domain}" debconf-set-selections <<< "postfix postfix/main_mailer_type string 'Internet Site'" apt install -y sudo ufw vim fail2ban wget telnet dnsutils rsyslog zram-tools \ composer git acl dbconfig-no-thanks \ nginx certbot python3-certbot-nginx \ mariadb-server mariadb-client postfix-mysql dovecot-mysql \ php php-fpm php-imap php-mbstring php-mysql php-json php-curl php-zip php-xml php-bz2 php-intl php-gmp php-net-ldap3 php-imagick php-common php-gd php-sqlite3 php-cli \ postfix postfix-pcre \ dovecot-core dovecot-imapd dovecot-lmtpd \ postfix-policyd-spf-python opendkim opendkim-tools \ opendmarc || die 'Apt failed' apt autoremove -y # cron rsyslog sed -i 's/#cron/cron/' /etc/rsyslog.conf # UFW # SSH ufw allow ${ssh_port}/tcp >/dev/null # Mail ports ufw allow 25/tcp >/dev/null ufw allow 80,443/tcp >/dev/null ufw allow 587/tcp >/dev/null ufw allow 143,993/tcp >/dev/null yes | ufw enable >/dev/null systemctl -q enable --now ufw.service # Check port 25 echo if ! grep -q 'Connected to' <<< $(printf 'quit' | telnet -4 gmail-smtp-in.l.google.com 25) then echo -e '\n\e[1;31mPort 25 needs to be open for mail server installation to continue, exiting...\e[0m\n' exit 1 fi # fail2ban install /dev/stdin /usr/local/bin/fail2ban-jails <<'ALL-JAILS' #!/bin/bash JAILS=$(sudo fail2ban-client status | grep "Jail list" | sed -E 's/^[^:]+:[ \t]+//' | sed 's/,//g') for JAIL in $JAILS do sudo fail2ban-client status $JAIL done ALL-JAILS cat >/etc/fail2ban/jail.d/sshd.conf <<'SSHD' [sshd] enabled = true filter = sshd backend = systemd maxretry = 5 findtime = 1d bantime = 4w ignoreip = 127.0.0.1/8 SSHD cat >/etc/fail2ban/jail.d/postfix.local <<'POSTFIX-FLOOD-ATTACK' [postfix-flood-attack] enabled = true bantime = 12h filter = postfix-flood-attack action = iptables-multiport[name=postfix, port="http,https,smtp,submission,pop3,pop3s,imap,imaps,sieve", protocol=tcp] logpath = /var/log/mail.log [postfix] enabled = true maxretry = 3 bantime = 12h filter = postfix logpath = /var/log/mail.log POSTFIX-FLOOD-ATTACK cat >/etc/fail2ban/filter.d/postfix-flood-attack.conf <<'POSTFIX-FLOOD-ATTACK' [Definition] failregex = lost connection after AUTH from (.*)\[\] ignoreregex = POSTFIX-FLOOD-ATTACK systemctl -q enable --now fail2ban.service # zram swap echo -e "ALGO=zstd\nPERCENT=60" >>/etc/default/zramswap # Shut up fstrim rm -f /etc/cron.weekly/fstrim &>/dev/null # Hostname and unix users hostnamectl set-hostname ${domain_url} sed -i '/127.0.0.1/ s/$/ '${domain_url}'/' /etc/hosts adduser ${username} sudo &>/dev/null # SSH settings echo "Port ${ssh_port} PermitRootLogin no PasswordAuthentication no Protocol 2" >/etc/ssh/sshd_config.d/zz-ssh.conf # Disable history saving cat >>~/.bashrc </dev/null # Roundcube database mysql -u root </dev/null # Roundcube password plugin cp /usr/share/webapps/roundcube/plugins/password/config.inc.php.dist /usr/share/webapps/roundcube/plugins/password/config.inc.php sed -i "/^\$config\['password_db_dsn'\]/ s|=.*|= 'mysql://postfixadmin:${postfixadmindbpass}@localhost/postfixadmin';|" /usr/share/webapps/roundcube/plugins/password/config.inc.php sed -i "/^\$config\['password_query'\]/ s/=.*/= 'UPDATE mailbox SET password=%P,modified=NOW() WHERE username=%u';/" /usr/share/webapps/roundcube/plugins/password/config.inc.php sed -i "/^\$config\['password_strength_driver'\]/ s/=.*/= '${roundcubepass_strength_drive}';\\ \$config['password_"${roundcubepass_strength_drive}"_min_score'] = 5;/" /usr/share/webapps/roundcube/plugins/password/config.inc.php sed -i "/^\$config\['password_algorithm'\]/ s/=.*/= 'dovecot';/" /usr/share/webapps/roundcube/plugins/password/config.inc.php sed -i "/^\$config\['password_dovecotpw'\]/ s|=.*|= '/usr/bin/doveadm pw -r 5';|" /usr/share/webapps/roundcube/plugins/password/config.inc.php sed -i "/^\$config\['password_dovecotpw_method'\]/ s/=.*/= 'ARGON2I';/" /usr/share/webapps/roundcube/plugins/password/config.inc.php sed -i "/^\$config\['password_dovecotpw_with_method'\]/ s/=.*/= true;/" /usr/share/webapps/roundcube/plugins/password/config.inc.php rm /usr/share/webapps/roundcube/installer/ -r chown www-data:www-data /usr/share/webapps/roundcube/plugins/password/config.inc.php chmod 600 /usr/share/webapps/roundcube/plugins/password/config.inc.php # Postfixadmin config setup # https://git.banananet.work/banananetwork/postfixadmin/raw/commit/864065cd37ef34b6dab915206eea4bd2ac4ebaed/config.inc.php echo -e '\e[1;34mCreating mail users\e[0m' echo -e ' '\''postmaster@'${domain}''\'', '\''eff'\'' => '\''postmaster@'${domain}''\'', '\''dmarc'\'' => '\''postmaster@'${domain}''\'', ); $CONF['\''password_validation'\''] = array( # # '\''/regular expression/'\'' => '\''$PALANG key (optional: + parameter)'\'', # '\''/.{5}/'\'' => '\''password_too_short 5'\'', # minimum length 5 characters # '\''/([a-zA-Z].*){3}/'\'' => '\''password_no_characters 3'\'', # must contain at least 3 characters # '\''/([0-9].*){2}/'\'' => '\''password_no_digits 2'\'', # must contain at least 2 digits ); $CONF['\''fetchmail'\''] = '\''NO'\''; $CONF['\''show_footer_text'\''] = '\''NO'\''; $CONF['\''quota'\''] = '\''YES'\''; $CONF['\''domain_quota'\''] = '\''YES'\''; $CONF['\''quota_multiplier'\''] = '\''1024000'\''; $CONF['\''used_quotas'\''] = '\''YES'\''; $CONF['\''new_quota_table'\''] = '\''YES'\''; $CONF['\''aliases'\''] = '\''0'\''; $CONF['\''mailboxes'\''] = '\''0'\''; $CONF['\''maxquota'\''] = '\''0'\''; $CONF['\''domain_quota_default'\''] = '\''0'\''; $CONF['\''password_expiration'\''] = '\''NO'\''; # Postfixadmin hash $CONF['\''setup_password'\''] = '\'$(php -r "echo password_hash('${postfixadminpass}', PASSWORD_DEFAULT);")\'';' | tee /usr/share/webapps/postfixadmin/config.local.php >/dev/null # Update Postfixadmin databases # https://git.banananet.work/banananetwork/postfixadmin/raw/commit/864065cd37ef34b6dab915206eea4bd2ac4ebaed/config.inc.php runuser -u www-data -- php /usr/share/webapps/postfixadmin/public/upgrade.php # Create Postfixadmin domain bash /usr/share/webapps/postfixadmin/scripts/postfixadmin-cli domain add "${domain}" --aliases 0 --mailboxes 0 --maxquota 0 --quota 0 --active --default-aliases -q # Create Postfixadmin admin bash /usr/share/webapps/postfixadmin/scripts/postfixadmin-cli admin add "postmaster@${domain}" --superadmin --active --domains "${domain}" --password "${postfixadminpass}" --password2 "${postfixadminpass}" -q # Create Postfixadmin mail users bash /usr/share/webapps/postfixadmin/scripts/postfixadmin-cli mailbox add "postmaster@${domain}" --active --password "${postfixadminpass}" --password2 "${postfixadminpass}" -q bash /usr/share/webapps/postfixadmin/scripts/postfixadmin-cli mailbox add "${mailuser}@${domain}" --active --password "${mailpass}" --password2 "${mailpass}" -q # Create Postfixadmin mail catch-all alias bash /usr/share/webapps/postfixadmin/scripts/postfixadmin-cli alias add "*@${domain}" --goto "${mailuser}@${domain}" --active -q unset mailuser postfixadminpass postfixadminpass2 mailpass mailpass2 roundcubedbpass roundcubepass_strength_drive echo # Autorestart services mkdir -p /etc/systemd/system/{dovecot,postfix}.service.d tee /etc/systemd/system/dovecot.service.d/restart.conf >/dev/null <<'RESTART' [Service] Restart=always RestartSec=5s RESTART tee /etc/systemd/system/postfix.service.d/restart.conf >/dev/null <<'RESTART' [Service] Restart=on-failure RestartSec=5s RESTART systemctl -q daemon-reload # Postfix # postconf myhostname alias_maps alias_database myorigin mydestination relayhost mynetworks mailbox_size_limit recipient_delimiter inet_interfaces inet_protocols # cat /etc/mailname echo -e '\e[1;34mConfiguring Postfix\e[0m' echo ${domain} >/etc/mailname postconf -e "myhostname = ${subdomain}.${domain}" postconf -e 'relay_domains = $mydestination' postconf -e 'smtp_tls_security_level = may' postconf -e 'smtp_tls_loglevel = 1' postconf -e 'smtpd_tls_security_level = may' postconf -e 'smtpd_tls_loglevel = 1' postconf -e 'mailbox_size_limit = 0' postconf -e 'message_size_limit = 0' postconf -e "authorized_submit_users = root,www-data,${USER}" postconf -e 'smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache' postconf -e 'smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache' postconf -e 'smtp_tls_note_starttls_offer = yes' postconf -e 'smtpd_tls_received_header = yes' postconf -e 'inet_interfaces = all' postconf -e 'inet_protocols = ipv4' postconf -e 'smtp_address_preference = ipv4' postconf -e "mydomain = ${domain}" postconf -e 'myorigin = $mydomain' postconf -e 'mydestination = $myhostname, localhost.$mydomain, localhost' # Configure backup mail servers if [ ${backup_mailserver} ] then postconf -e "$(postconf mynetworks)$(printf ' %s/32' ${backup_mailserver[@]})" postconf -e "smtp_fallback_relay =$(printf ' [%s]:25' ${backup_mailserver[@]})" fi # main.cf echo -e '\n# Enforce TLSv1.3 or TLSv1.2 smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 # Virtual mailboxes mailbox_transport = lmtp:unix:private/dovecot-lmtp smtputf8_enable = no virtual_mailbox_domains = proxy:mysql:/etc/postfix/sql/mysql_virtual_domains_maps.cf virtual_mailbox_maps = proxy:mysql:/etc/postfix/sql/mysql_virtual_mailbox_maps.cf, proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf virtual_alias_maps = proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_maps.cf, proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_maps.cf, proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf virtual_transport = lmtp:unix:private/dovecot-lmtp virtual_mailbox_base = /var/vmail virtual_minimum_uid = 600 virtual_uid_maps = static:600 virtual_gid_maps = static:600 virtual_mailbox_limit = 0 smtp_header_checks = pcre:/etc/postfix/smtp_header_checks # SPF and DKIM checks policyd-spf_time_limit = 3600 smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, check_policy_service unix:private/policyd-spf, check_client_access hash:/etc/postfix/rbl_override, reject_rhsbl_helo dbl.spamhaus.org, reject_rhsbl_reverse_client dbl.spamhaus.org, reject_rhsbl_sender dbl.spamhaus.org # Milter configuration milter_default_action = accept milter_protocol = 6 smtpd_milters = local:opendkim/opendkim.sock non_smtpd_milters = $smtpd_milters # Blocking spam smtpd_sender_restrictions = permit_mynetworks permit_sasl_authenticated reject_unknown_sender_domain reject_unknown_reverse_client_hostname reject_unknown_client_hostname smtpd_helo_required = yes smtpd_helo_restrictions = permit_mynetworks permit_sasl_authenticated check_helo_access hash:/etc/postfix/helo_access reject_invalid_helo_hostname reject_non_fqdn_helo_hostname reject_unknown_helo_hostname # Postscreen spambots postscreen_access_list = permit_mynetworks cidr:/etc/postfix/postscreen_access.cidr cidr:/etc/postfix/postscreen_spf_whitelist.cidr postscreen_blacklist_action = drop postscreen_greet_action = enforce postscreen_dnsbl_threshold = 3 postscreen_dnsbl_action = enforce postscreen_dnsbl_sites = zen.spamhaus.org*3 bl.spameatingmonkey.net*2 bl.spamcop.net dnsbl.sorbs.net postscreen_dnsbl_whitelist_threshold = -2 header_checks = pcre:/etc/postfix/header_checks body_checks = pcre:/etc/postfix/body_checks' | tee -a /etc/postfix/main.cf >/dev/null # master.cf sed -i 's/smtp .*smtpd/# &/' /etc/postfix/master.cf sed -i '/#smtp\|#smtpd\|#dnsblog\|#tlsproxy/ s/^#//' /etc/postfix/master.cf echo -e '\n# Enable submission submission inet n - y - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_tls_wrappermode=no -o smtpd_sasl_auth_enable=yes -o smtpd_relay_restrictions=permit_sasl_authenticated,reject -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject -o smtpd_sasl_type=dovecot -o smtpd_sasl_path=private/auth # SPF Policy policyd-spf unix - n n - 0 spawn user=policyd-spf argv=/usr/bin/policyd-spf' | tee -a /etc/postfix/master.cf >/dev/null # Deleting Email Headers For Outgoing Emails echo -e '/^X-Spam-Status:/ IGNORE /^X-Spam-Checker-Version:/ IGNORE /^Received:.*/ IGNORE /^User-Agent:.*/ IGNORE' | tee /etc/postfix/smtp_header_checks >/dev/null # Header checks echo "/free mortgage quote/ DISCARD /repair your credit/ DISCARD /lose weight/ DISCARD /To:.*(gmail.com|yahoo.com|outlook|hotmail.com).*${mailuser}@${domain}/ DISCARD /To:.*${mailuser}@${domain}.*(gmail.com|yahoo.com|outlook|hotmail.com)/ DISCARD /Cc:.*(gmail.com|yahoo.com|outlook|hotmail.com).*${mailuser}@${domain}/ DISCARD /Cc:.*${mailuser}@${domain}.*(gmail.com|yahoo.com|outlook|hotmail.com)/ DISCARD /To:.*<>/ DISCARD /From:.*<>/ DISCARD" | tee -a /etc/postfix/header_checks >/dev/null # Body checks echo "/free mortgage quote/ DISCARD /repair your credit/ DISCARD /lose weight/ DISCARD" | tee -a /etc/postfix/body_checks >/dev/null # Whitelist localhost echo -e "# Permit my own IP addresses ${ip_addr}/32\tpermit" | tee /etc/postfix/postscreen_access.cidr >/dev/null # Blocking Email Spam with Postfix echo -e "${domain} OK" | tee /etc/postfix/{helo_access,rbl_override} >/dev/null # Virtual mailboxes mkdir -p /etc/postfix/sql tee /etc/postfix/sql/mysql_virtual_domains_maps.cf >/dev/null </dev/null </dev/null </dev/null </dev/null </dev/null </dev/null mkdir -p /var/vmail chown vmail:vmail /var/vmail/ -R sed -i 's/#!include auth-sql.conf.ext/!include auth-sql.conf.ext/' /etc/dovecot/conf.d/10-auth.conf sed -i 's/!include auth-system.conf.ext/#&/' /etc/dovecot/conf.d/10-auth.conf echo -e '\nauth_debug = yes auth_debug_passwords = yes' | tee -a /etc/dovecot/conf.d/10-auth.conf >/dev/null tee -a /etc/dovecot/dovecot-sql.conf.ext >/dev/null </dev/null sed -i 's|^mail_location =.*|mail_location = maildir:~/Maildir\ '"$(if ! grep -q '^mail_home = /var/vmail/%d/%n/' /etc/dovecot/conf.d/10-mail.conf then echo 'mail_home = /var/vmail/%d/%n/' fi)"'|g' /etc/dovecot/conf.d/10-mail.conf sed -i 's|unix_listener lmtp {|unix_listener /var/spool/postfix/private/dovecot-lmtp {\ mode = 0600\ user = postfix\ group = postfix|' /etc/dovecot/conf.d/10-master.conf sed -i 's|unix_listener auth-userdb {|unix_listener /var/spool/postfix/private/auth {\ mode = 0660\ user = postfix\ group = postfix|' /etc/dovecot/conf.d/10-master.conf sed -i '/#disable_plaintext_auth/ s/^#//' /etc/dovecot/conf.d/10-auth.conf sed -i 's/#auth_username_format = %Lu/auth_username_format = %u/' /etc/dovecot/conf.d/10-auth.conf sed -i 's/#auth_default_realm.*/auth_default_realm = '${domain}'/' /etc/dovecot/conf.d/10-auth.conf sed -i 's/auth_mechanisms = plain/& login/' /etc/dovecot/conf.d/10-auth.conf sed -i 's/ssl = yes/ssl = required/' /etc/dovecot/conf.d/10-ssl.conf sed -i 's/#ssl_prefer_server_ciphers = no/ssl_prefer_server_ciphers = yes/' /etc/dovecot/conf.d/10-ssl.conf sed -i 's/#ssl_min_protocol =.*/ssl_min_protocol = TLSv1.2/' /etc/dovecot/conf.d/10-ssl.conf echo -e '\nservice stats { unix_listener stats-reader { user = www-data group = www-data mode = 0660 } unix_listener stats-writer { user = www-data group = www-data mode = 0660 } }' | tee -a /etc/dovecot/conf.d/10-master.conf >/dev/null gpasswd -a www-data dovecot >/dev/null # Mailboxes sed -i 's/namespace inbox {/&\ # Archive folder\ mailbox Archive {\ special_use = \\Archive\ }/' /etc/dovecot/conf.d/15-mailboxes.conf sed -i '/Sent Messages/! s/^ mailbox.*{/&\ auto = subscribe/' /etc/dovecot/conf.d/15-mailboxes.conf adduser dovecot mail >/dev/null systemctl restart dovecot.service postfix.service # SPF and DKIM echo -e '\e[1;34mConfiguring SPF and DKIM policies\e[0m' gpasswd -a postfix opendkim >/dev/null grep -q '^Canonicalization.*relaxed/simple$' /etc/opendkim.conf || sed -i 's|.*Canonicalization.*|Canonicalization relaxed/simple|' /etc/opendkim.conf sed -i '/^#Mode.*sv\|^#SubDomains.*no/ s/^#//' /etc/opendkim.conf echo -e '\n# Map domains in "from" addresses to keys used to sign messages KeyTable refile:/etc/opendkim/KeyTable SigningTable refile:/etc/opendkim/SigningTable # Hosts to ignore when verifying signatures ExternalIgnoreList /etc/opendkim/trusted.hosts # A set of internal hosts whose mail should be signed InternalHosts /etc/opendkim/trusted.hosts' | tee -a /etc/opendkim.conf >/dev/null mkdir -p /etc/opendkim/keys chown -R opendkim:opendkim /etc/opendkim chmod go-rw /etc/opendkim/keys echo "*@${domain} default._domainkey.${domain}" | tee -a /etc/opendkim/SigningTable >/dev/null echo "default._domainkey.${domain} ${domain}:default:/etc/opendkim/keys/${domain}/default.private" | tee -a /etc/opendkim/KeyTable >/dev/null echo "127.0.0.1 localhost *.${domain}" | tee -a /etc/opendkim/trusted.hosts >/dev/null mkdir -p /etc/opendkim/keys/${domain} opendkim-genkey -b 2048 -d ${domain} -D /etc/opendkim/keys/${domain} -s default chown opendkim:opendkim /etc/opendkim/keys/${domain}/default.private chmod 600 /etc/opendkim/keys/${domain}/default.private mkdir -p /var/spool/postfix/opendkim chown opendkim:postfix /var/spool/postfix/opendkim sed -i '/^#Socket.*local:\/var\/spool\/postfix\/opendkim\/opendkim.sock/ s/^#//' /etc/opendkim.conf || sed -i 's\^Socket.*local:/run/opendkim/opendkim.sock\Socket local:/var/spool/postfix/opendkim/opendkim.sock\' /etc/opendkim.conf sed -i 's\^SOCKET=local:$RUNDIR/opendkim.sock\SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"\' /etc/default/opendkim # DMARC echo -e '\e[1;34mConfiguring DMARC\e[0m' sed -i 's/# AuthservID name/AuthservID OpenDMARC/' /etc/opendmarc.conf sed -i 's/# TrustedAuthservIDs HOSTNAME/TrustedAuthservIDs '${subdomain}'.'${domain}'/' /etc/opendmarc.conf sed -i 's/# RejectFailures false/RejectFailures true\ IgnoreAuthenticatedClients true\ RequiredHeaders true\ SPFSelfValidate true/' /etc/opendmarc.conf sed -i 's|Socket local:/run/opendmarc/opendmarc.sock|Socket local:/var/spool/postfix/opendmarc/opendmarc.sock|' /etc/opendmarc.conf mkdir -p /var/spool/postfix/opendmarc chown opendmarc:opendmarc /var/spool/postfix/opendmarc -R chmod 750 /var/spool/postfix/opendmarc/ -R adduser postfix opendmarc >/dev/null echo -e '\nIgnoreHosts /etc/opendmarc/ignore.hosts' | tee -a /etc/opendmarc.conf >/dev/null mkdir -p /etc/opendmarc echo '127.0.0.1' | tee -a /etc/opendmarc/ignore.hosts >/dev/null # OpenDKIM and OpenDMARC headers echo -e '\nSoftwareHeader yes' | tee -a /etc/{opendkim,opendmarc}.conf >/dev/null systemctl restart postfix.service opendkim.service opendmarc.service # Roundcube/Nginx echo -e '\e[1;34mConfiguring Nginx\e[0m' tee $(find /etc/php/ -type d -name "pool.d")/zz-php-handler.conf >/dev/null </dev/null <<'PROXY' proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; PROXY echo -e ' upstream php-handler { server unix:/run/php/php-fpm.sock; } server { server_name '${subdomain}'.'${domain}'; index index.php index.html; error_log /var/log/nginx/roundcube_error.log; access_log /var/log/nginx/roundcube_access.log; root /usr/share/webapps/roundcube; client_max_body_size 0; # Postfixadmin location ^~ /admin/ { proxy_pass http://127.0.0.1:12000/; include proxy_params; } # Roundcube location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass php-handler; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } } server { listen 12000; root /usr/share/webapps/postfixadmin/public/; index index.php index.html; client_max_body_size 0; error_log /var/log/nginx/postfixadmin_error.log; access_log /var/log/nginx/postfixadmin_access.log; location / { try_files $uri $uri/ /index.php; } location ~ ^/(.+\.php)$ { try_files $uri =404; fastcgi_pass php-handler; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include /etc/nginx/fastcgi_params; } }' | tee /etc/nginx/sites-available/mail.conf >/dev/null ln -s /etc/nginx/sites-available/mail.conf /etc/nginx/sites-enabled/ # Certbot systemctl -q disable --now apache2.service systemctl -q reload nginx.service /etc/init.d/php*-fpm reload >/dev/null until certbot --nginx --agree-tos --redirect --hsts --no-eff-email --staple-ocsp -m ${eff_email} -d ${subdomain}.${domain} do sleep 10 done setfacl -R -m u:www-data:rx /etc/letsencrypt/live/ /etc/letsencrypt/archive/ # Postfix postconf -e "smtpd_tls_key_file = /etc/letsencrypt/live/${subdomain}.${domain}/privkey.pem" postconf -e "smtpd_tls_cert_file = /etc/letsencrypt/live/${subdomain}.${domain}/fullchain.pem" # Dovecot # sed -i 's|ssl_cert = /dev/null chmod 600 /etc/dovecot/ssl-keys.conf echo -e '!include_try ssl-keys.conf' | tee -a /etc/dovecot/dovecot.conf >/dev/null # Cron tasks echo '#!/bin/sh certbot renew --quiet && systemctl -q reload postfix dovecot nginx /usr/local/bin/postwhite/postwhite &>/dev/null # Update Postscreen Whitelists' | install /dev/stdin /etc/cron.daily/mailserver echo '#!/bin/sh /usr/local/bin/postwhite/scrape_yahoo &>/dev/null # Update Yahoo! IPs for Postscreen Whitelists' | install /dev/stdin /etc/cron.weekly/mailserver ln -s /usr/share/webapps/roundcube/bin/cleandb.sh /etc/cron.daily/roundcube-cleandb # Run postwhite installer echo -e '\n\e[1;34mInstalling Postwhite\e[0m' /usr/local/bin/postwhite/postwhite su ${username} <<"CHANGEUSER" # SSH yes | ssh-keygen -t ed25519 -q -f ~/.ssh/id_ed25519 -P "" echo "${sshkeys}" >~/.ssh/authorized_keys # Home directory mods cat >>~/.bashrc </dev/null } function mail-check { if systemctl list-units --state failed | grep -q 'postfix\|dovecot\|opendkim\|opendmarc\|nginx\|mariadb\|php.*-fpm' then echo -e '\nErrors found...\n' systemctl list-units --state failed else echo -e '\nAll good!\n' fi } function mail-test { sudo systemctl stop postfix dovecot read -n 1 -s -p $'\nPress any key to continue...\n' sudo systemctl start postfix dovecot && mail-watch } # SSH pubkey access function ssh-on { sudo sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config.d/10-personal-sshd.conf sudo systemctl -q restart sshd } function ssh-off { sudo sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config.d/10-personal-sshd.conf sudo systemctl -q restart sshd } bashrc install /dev/stdin ~/dhparam >/dev/null <<'dhparam' #!/usr/bin/env bash echo # Create certificates echo -e '\e[1;34mGenerating DH parameters with openssl\e[0m' echo -e '\e[3m# a notification will pop up once completed\e[0m' sudo openssl dhparam -out /usr/share/dovecot/dh.pem 4096 && echo -e '\e[1;32mOpenssl certificates have successfully been generated!\e[0m\n' rm ${0} dhparam CHANGEUSER # In your DNS manager, create a TXT record, enter default._domainkey in the name field echo -e "\n\e[1;34mUpdate DKIM TXT on DNS registrar and press any key to continue\e[5m...\e[0m" echo -e "\e[3m# Use default._domainkey in the host field" echo -e "# Check with 'sudo opendkim-testkey -d ${domain} -s default'" echo -e "# Or visit https://www.dmarcanalyzer.com/dkim/dkim-checker/\e[0m" cat /etc/opendkim/keys/${domain}/default.txt | sed 's/.*( //' | sed 's/ ).*//' | sed 's/"//g' | sed 's/^[ \t]*//g' | sed ':a;N;$!ba;s/\n//g' cat <