§2024-09-26

 ls
00_install_global_functions_variables.sh  02_install_prerequisites.sh           04_install_import_keys.sh  06_install_enable_services.sh  08_install_haraka.sh    10_install_wildduck_webmail.sh  12_install_ufw_rules.sh  14_install_start_services.sh  hc4Noble.yushei.net-nameserver.txt  log
01_install_commits.sh                     03_install_check_running_services.sh  05_install_packages.sh     07_install_wildduck.sh         09_install_zone_mta.sh  11_install_nginx.sh             13_install_ssl_certs.sh  15_install_deploy.sh          install.sh                          tee

¶ 00_install_global_functions_variables.sh `

#! /bin/bash

# These are all the common global variables and global functions.

AUT_HOSTNAME=`hostname`
export AUT_SAFETY=true

export AUT_HOME="${HOME}" # maybe a more robust way?

# COLOR VARIABLES
export RED='\033[0;31m'
export GREEN='\033[0;32m'
export ORANGE='\033[0;33m'
export YELLOW='\033[1;33m'
export BLUE='\033[0;34m'
export NC='\033[0m' # No Color

# GLOBAL FUNCTIONS

fun_get_password(){
# If we are not root, we should aquire the sudo password
if [ `whoami` == 'root' ]
  then
    PASSWORD=''
  else
    echo -n "GIMME your password! ($OURNAME):"
    read -s PASSWORD
    echo -e "\n"
fi
}
export -f fun_get_password

fun_check_password_boolean(){
# Check if the $PASSWORD is good or not

# TODO: if hostname can not be resolved (/etc/hosts misses 127.0.0.1)
# then sudo outputs 'cannot resolve hostname', so this check
# "obviously" fails.

sudo -k #disable sudo timeout

#prime it
echo $PASSWORD | sudo -S echo hello &> /dev/null

local RESULT=$(echo $PASSWORD | sudo -S sudo -n echo hello 2>&1)
if [ "$RESULT" == "hello" ]; then
  echo 'Correct password.'
  return 0
else
  echo 'Wrong password.'
  return 1
fi

}
export -f fun_check_password_boolean

fun_check_password(){
if ! fun_check_password_boolean; then
  echo -e "${RED}ERROR:${NC} Wrong password, we should quit now."
  exit 1
fi
}
export -f fun_check_password


fun_get_user_variables_no_default(){
# get REMOTE_SERVER_EMAIL, if not supplied, quit. No default.

local VARIABLES=("${!1}")
local TMP_DEFAULT
local TMP_USER
local TMP_READ

echo "Automatic timeout is 120 sec"

for i in ${VARIABLES[@]}; do
  TMP_DEFAULT=DEFAULT_$i
  echo -n "GIMMME the $i (username, eg: ${!TMP_DEFAULT}), no default value:"
  read -t 120 TMP_READ
  echo ""
  declare -g USER_$i=$TMP_READ
  TMP_USER=USER_$i

  if [ "${!TMP_USER}" == "" ]; then
    echo -n "${TMP_USER} can not be empty. Please give it again:"
    read -t 130 TMP_READ
    declare -g USER_$i=$TMP_READ
    TMP_USER=USER_$i
    if [ "${!TMP_USER}" == "" ]; then
      echo "Second try failed. Quitting..."
      exit 1
    fi
  fi
done

}
export -f fun_get_user_variables_no_default

fun_get_user_variables_password(){
# get REMOTE_SERVER_PASSWORD, no default, suppress echoing back

local VARIABLES=("${!1}")
local TMP_DEFAULT
local TMP_USER
local TMP_READ

echo "Automatic timeout is 120 sec"

for i in ${VARIABLES[@]}; do
  TMP_DEFAULT=DEFAULT_$i
  echo -n "GIMMME the $i (password, eg: ${!TMP_DEFAULT}), no default value:"
  read -t 120 -s TMP_READ
  echo ""
  declare -g USER_$i=$TMP_READ
  TMP_USER=USER_$i

  if [ "${!TMP_USER}" == "" ]; then
    echo -n "${TMP_USER} can not be empty. Please give it again:"
    read -t 130 -s TMP_READ
    declare -g USER_$i=$TMP_READ
    TMP_USER=USER_$i
    if [ "${!TMP_USER}" == "" ]; then
      echo "Second try failed. Quitting..."
      exit 1
    fi
  fi
done
}
export -f fun_get_user_variables_password

fun_get_user_variables(){
# get USER_HOST_PORT, if not supplied, autofill with DEFAULT_HOST_PORT, etc

local VARIABLES=("${!1}")
local TMP_DEFAULT
local TMP_USER
local TMP_READ

echo "Automatic timeout is 30 sec"

for i in ${VARIABLES[@]}; do
  TMP_DEFAULT=DEFAULT_$i
  echo -n "GIMMME the $i (default: ${!TMP_DEFAULT}):"
  read -t 30 TMP_READ
  echo ""
  declare -g USER_$i=$TMP_READ
  TMP_USER=USER_$i

  if [ "${!TMP_USER}" == "" ]; then
    declare -g USER_$i=${!TMP_DEFAULT}
  fi
done
}
export -f fun_get_user_variables


fun_prepare_run_command(){
# prepare the runcommand variable.
# Must be called before fun_echo_command and fun_run_command

TEMPLATE=$(cat <<EOF
-e "ORIGINAL_COMMAND=RUNCOMMAND_TEMPLATE" \
$USER_IMAGE
EOF
)

RUNCOMMAND_ADDED_ENV=${RUNCOMMAND_ORIG//$USER_IMAGE/$TEMPLATE}
RUNCOMMAND_NOPASSWD=${RUNCOMMAND_ADDED_ENV//$PASSWORD/PASSWORD}
RUNCOMMAND=${RUNCOMMAND_ADDED_ENV//RUNCOMMAND_TEMPLATE/$RUNCOMMAND_NOPASSWD}

}
export -f fun_prepare_run_command

fun_echo_command(){
# echo the command which will be launched (fun_run_command())

echo ${RUNCOMMAND//$PASSWORD/PASSWORD}

}
export -f fun_echo_command

fun_run_command(){
# execute the final command

echo `eval $RUNCOMMAND`
}
export -f fun_run_command

fun_print_help(){
USAGE=$(cat <<EOF

# Manual
# The main installation script is:
./install.sh domainname [hostname]
eg. ${GREEN}./install.sh amazeme.com mail.amazeme.com${NC}

There is a slight difference between domainname and hostname.

${ORANGE}Simplest case${NC}:
One server serves everything: company website, emails, webmails.
One ip address, and domainname is the same az hostname.
Eg. amazme.com

${GREEN}More general case${NC}:
The domainname is part of the email address:
username@domainname

The hostname is the actual machine name, eg. this machine
name is: `hostname`

On larger organizations, the company homepage is independent from
the mail servers. Or the webmail servers.
Eg. the company homepage is amazme.com [11.22.33.44],
the mail server is mail.amazme.com [11.22.33.43]

So domainname = amazme.com
hostname = mail.amazme.com

${RED}IP address${NC} case:
You can call this script with ip address instead of domain name:
./install.sh 11.22.33.44
(with the server's public IP address)
In that case both domainname and hostname becomes the IP address.
Dunno why anyone wanna that...

EOF
)

# echo -e for the colored output, "quotes" for the newline preserves
echo -e "$USAGE"
}
export -f fun_print_help

function hook_script {
    echo "#!/bin/bash
git --git-dir=/var/opt/$1.git --work-tree=\"/opt/$1\" checkout "\$3" -f
cd \"/opt/$1\"
rm -rf package-lock.json
npm install --production --no-optional --no-package-lock --no-audit --ignore-scripts --no-shrinkwrap --progress=false
sudo $SYSTEMCTL_PATH restart $1 || echo \"Failed restarting service\"" > "/var/opt/$1.git/hooks/update"
    chmod +x "/var/opt/$1.git/hooks/update"
}
export -f hook_script

function hook_script_bower {
    echo "#!/bin/bash
git --git-dir=/var/opt/$1.git --work-tree=\"/opt/$1\" checkout "\$3" -f
cd \"/opt/$1\"
rm -rf package-lock.json
npm install --progress=false
npm run bowerdeps
sudo $SYSTEMCTL_PATH restart $1 || echo \"Failed restarting service\"" > "/var/opt/$1.git/hooks/update"
    chmod +x "/var/opt/$1.git/hooks/update"
}
export -f hook_script_bower

function log_script {

SERVICE_NAME=$1

# Ensure required files and permissions
echo "d /var/log/${SERVICE_NAME} 0750 syslog adm" > /etc/tmpfiles.d/${SERVICE_NAME}-log.conf

# Redirect MongoDB log output from syslog to service specific log file
echo "if ( \$programname startswith \"$SERVICE_NAME\" ) then {
    action(type=\"omfile\" file=\"/var/log/${SERVICE_NAME}/${SERVICE_NAME}.log\")
    stop
}" > /etc/rsyslog.d/25-${SERVICE_NAME}.conf

# Setup log rotate
echo "/var/log/${SERVICE_NAME}/${SERVICE_NAME}.log {
    daily
    ifempty
    missingok
    rotate 7
    compress
    create 640 syslog adm
    su root root
    sharedscripts
    postrotate
        systemctl kill --signal=SIGHUP --kill-who=main rsyslog.service 2>/dev/null || true
    endscript
}" > /etc/logrotate.d/${SERVICE_NAME}

}

Overview

The script sets up various functions and environment variables to facilitate the installation and configuration of WildDuck. It includes user prompts for necessary inputs, checks for permissions, and sets up hooks and logging.

Key Sections

Global Variables and Functions:

Hostname and Safety: AUT_HOSTNAME: Stores the current hostname. AUT_SAFETY: A flag for safety during execution. Home Directory: AUT_HOME: Sets the home directory of the user. Color Variables:

These variables are defined for colored output in the terminal (e.g., red for errors, green for success). User Password Functions:

fun_get_password: Prompts the user for their password unless they are root, in which case it assumes no password is needed. fun_check_password_boolean: Verifies the provided password by trying to execute a command with sudo. fun_check_password: Calls the previous function and exits if the password is incorrect. User Input Functions:

fun_get_user_variables_no_default: Prompts the user for certain variables without default values. fun_get_user_variables_password: Similar to the above but for sensitive information (passwords). fun_get_user_variables: Prompts for variables with the option of using defaults. Command Preparation:

fun_prepare_run_command: Prepares the command that will be executed later, handling the necessary environment variables. fun_echo_command: Displays the command that will be run, masking the password for security. fun_run_command: Executes the prepared command. Help Function:

fun_print_help: Outputs usage information for the script, explaining the difference between domain names and hostnames. Git Hook Functions:

hook_script and hook_script_bower: These functions set up Git hooks for updating the working directory and installing dependencies, respectively. Logging Setup:

log_script: Sets up logging for the service, including creating log directories, configuring rsyslog, and setting up log rotation. Important Notes The script uses bash-specific syntax and features, such as declare -g for global variable declaration and export -f for exporting functions to sub-shells. It includes error handling to ensure that the installation process is robust and user-friendly. The script is designed to be executed with root privileges or with sudo to allow it to perform system-level changes. Usage To run the script, the user would typically invoke it with:

bash コードをコピーする ./install.sh domainname [hostname] This means they would need to specify the primary domain for the email service and optionally a specific hostname.

Conclusion This installation script is a comprehensive tool for setting up the WildDuck service, ensuring that user inputs are collected securely and that system configurations are correctly applied for both functionality and logging.


¶ 01_install_commits.sh

#! /bin/bash

OURNAME=01_install_commits.sh

apt-get update
apt-get install -y lsb-release ca-certificates curl gnupg

NODE_MAJOR="20"
MONGODB="7.0"
CODENAME=`lsb_release -c -s`

WILDDUCK_COMMIT="9a61cff18689f773b01a77d00d6e309507fb5dd8"
ZONEMTA_COMMIT="2ec1ba85a44c4665a6326271c8162ee76c4d6d02" # zone-mta-template
WEBMAIL_COMMIT="40ee1ef973de33de5bdf3e6b7e877d156d87436a"
WILDDUCK_ZONEMTA_COMMIT="f029b8d69aaa255b1c4f426848f994be21be8cd0"
WILDDUCK_HARAKA_COMMIT="a209775ceac579b8ac1a4c04c052674eba763691"
HARAKA_VERSION="3.0.3"

echo -e "\n-- Executing ${ORANGE}${OURNAME}${NC} subscript --"
#! /bin/bash

OURNAME=02_install_prerequisites.sh

# No $AUT_SAFETY variable present, so we have not sourced install_variables.sh yet
if [ -z ${AUT_SAFETY+x} ]
  then
    echo "this script ${RED}called directly${NC}, and not from the main ./install.sh script"
    echo "initializing common variables ('install_variables.sh')"
    source "$INSTALLDIR/install_variables.sh"
fi

echo -e "\n-- Executing ${ORANGE}${OURNAME}${NC} subscript --"

echo -e "Checking ${YELLOW}lsof${NC}"
PROGRAM_LSOF=`command -v lsof`

if ! [ -x "$PROGRAM_LSOF" ]; then
  echo -e "${RED}ERROR:${NC} lsof is not installed."
  echo    "to know which package contains the particular executable, launch:"
  echo    "dpkg -S lsof |grep lsof$ # on ubuntu/debian variants"
  echo -e "Launching for you:\n"
  echo -e "`dpkg -S lsof | grep /lsof$`"
  echo -e "\nOn ubuntu 16.04 it is: ${GREEN}apt install lsof${NC}"
fi


echo -e "Checking ${YELLOW}ps${NC}"
PROGRAM_PS=`command -v ps`

if ! [ -x "$PROGRAM_PS" ]; then
  echo -e "${RED}ERROR:${NC} ps is not installed."
  echo    "to know which package contains the particular executable, launch:"
  echo    "dpkg -S ps |grep ps$ # on ubuntu/debian variants"
  echo -e "Launching for you:\n"
  echo -e "`dpkg -S ps | grep /ps$`"
  echo -e "\nOn ubuntu 16.04 it is: ${GREEN}apt install procps${NC}"
fiE}${OURNAME}${NC} subscript --"

¶ 03_install_check_running_services.sh

#! /bin/bash

OURNAME=03_install_check_running_services.sh

# No $AUT_SAFETY variable present, so we have not sourced install_variables.sh yet
# check if $AUT_SAFETY is unset (as opposed to empty "" string)
if [ -z ${AUT_SAFETY+x} ]
  then
    echo "this script ${RED}called directly${NC}, and not from the main ./install.sh script"
    echo "initializing common variables ('install_variables.sh')"
    source "$INSTALLDIR/install_variables.sh"
fi

echo -e "\n\n-- Executing ${ORANGE}${OURNAME}${NC} subscript --"

echo -e "Checking programs listening on port 25,587,993,995,80,443"
PORT25=`lsof -Pi :25 -sTCP:LISTEN -t`
PORT587=`lsof -Pi :587 -sTCP:LISTEN -t`
PORT993=`lsof -Pi :993 -sTCP:LISTEN -t`
PORT995=`lsof -Pi :995 -sTCP:LISTEN -t`
PORT80=`lsof -Pi :80 -sTCP:LISTEN -t`
PORT443=`lsof -Pi :443 -sTCP:LISTEN -t`


# check if $PORT25 is empty "" string (as opposed to unset)
if  ! [ -z $PORT25 ] ; then
    echo -e "${RED}Error:${NC} SMTP server already running on port 25"
    echo -e "PID: ${YELLOW}$PORT25${NC}"
    BINARY=`ps -p $PORT25 -o comm=`
    echo -e "binary: ${YELLOW}$BINARY${NC}"
    echo -e "full command with arguments: ${YELLOW}`ps -p $PORT25 -o command=`${NC}"
    echo -e "possible packages (dpkg -S $BINARY | grep /${BINARY}$):"
    echo -e "`dpkg -S $BINARY | grep /${BINARY}$`"
    echo -e "If it is launched by systemd, finding the service with"
    echo -e "Executing ${YELLOW}systemctl status $PORT25${NC}"
    echo -e "`systemctl status $PORT25`"
    echo -e "\nList all enabled services:"
    echo -e "systemctl list-unit-files | grep enabled"
    echo -e "stop a service: systemctl stop [service]"
    echo -e "${RED}QUITTING... (please stop the service and launch again)${NC}"
    exit 1
  else
    echo -e 'OK: port 25 (SMTP) is free'
fi

if  ! [ -z $PORT587 ] ; then
    echo -e "${RED}Error:${NC} SMTP server already running on port 587"
    echo -e "PID: ${YELLOW}$PORT587${NC}"
    BINARY=`ps -p $PORT587 -o comm=`
    echo -e "binary: ${YELLOW}$BINARY${NC}"
    echo -e "full command with arguments: ${YELLOW}`ps -p $PORT587 -o command=`${NC}"
    echo -e "possible packages (dpkg -S $BINARY | grep /${BINARY}$):"
    echo -e "`dpkg -S $BINARY | grep /${BINARY}$`"
    echo -e "If it is launched by systemd, finding the service with"
    echo -e "Executing ${YELLOW}systemctl status $PORT587${NC}"
    echo -e "`systemctl status $PORT587`"
    echo -e "\nList all enabled services:"
    echo -e "systemctl list-unit-files | grep enabled"
    echo -e "stop a service: systemctl stop [service]"
    echo -e "${RED}QUITTING... (please stop the service and launch again)${NC}"
    exit 1
  else
    echo -e 'OK: port 587 (SMTP TLS) is free'
fi

¶ 04_install_import_keys.sh

#! /bin/bash

OURNAME=04_install_import_keys.sh

echo -e "\n-- Executing ${ORANGE}${OURNAME}${NC} subscript --"

# create user for running applications
useradd wildduck || echo "User wildduck already exists"

# remove old sudoers file
rm -rf /etc/sudoers.d/wildduck

# create user for deploying code
useradd deploy || echo "User deploy already exists"

mkdir -p /home/deploy/.ssh
# add your own key to the authorized_keys file
echo "# Add your public key here
" >> /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy

export DEBIAN_FRONTEND=noninteractive
keyring="/usr/share/keyrings"

# nodejs
node_key_url="https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key"
local_node_key="${keyring}/nodesource.gpg"
curl -fsSL $node_key_url | gpg --dearmor | tee $local_node_key >/dev/null

echo "deb [signed-by=${local_node_key}] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" > /etc/apt/sources.list.d/nodesource.list
echo "deb-src [signed-by=${local_node_key}] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" >> /etc/apt/sources.list.d/nodesource.list

# mongodb
mongo_key_url="https://pgp.mongodb.com/server-${MONGODB}.asc"
local_mongo_key="${keyring}/mongodb-server-${MONGODB}.gpg"
curl -fsSL $mongo_key_url | gpg --dearmor | tee ${local_mongo_key} >/dev/null
echo "deb [ arch=amd64,arm64 signed-by=${local_mongo_key} ] https://repo.mongodb.org/apt/ubuntu ${CODENAME}/mongodb-org/${MONGODB} multiverse" > /etc/apt/sources.list.d/mongodb-org-${MONGODB}.list

# rspamd
rspamd_key_url="https://rspamd.com/apt-stable/gpg.key"
local_rspamd_key="${keyring}/rspamd.gpg"
curl -fsSL $rspamd_key_url | gpg --dearmor | tee ${local_rspamd_key} >/dev/null

echo "deb [signed-by=${local_rspamd_key}] http://rspamd.com/apt-stable/ $CODENAME main" > /etc/apt/sources.list.d/rspamd.list
echo "deb-src [signed-by=${local_rspamd_key}] http://rspamd.com/apt-stable/ $CODENAME main" >> /etc/apt/sources.list.d/rspamd.list

# redis
redis_key_url="https://packages.redis.io/gpg"
local_redis_key="${keyring}/redis-archive-keyring.gpg"
curl -fsSL $redis_key_url | gpg --dearmor | tee ${local_redis_key} >/dev/null

echo "deb [signed-by=${local_redis_key}] https://packages.redis.io/deb $CODENAME main" > tee /etc/apt/sources.list.d/redis.list

if ! [ -z $PORT993 ] ; then echo -e "${RED}Error:${NC} IMAP server already running on port 993" echo -e "PID: ${YELLOW}$PORT993${NC}" BINARY=ps -p $PORT993 -o comm= echo -e "binary: ${YELLOW}$BINARY${NC}" echo -e "full command with arguments: ${YELLOW}ps -p $PORT993 -o command=${NC}" echo -e "possible packages (dpkg -S $BINARY | grep /${BINARY}$):" echo -e "dpkg -S $BINARY | grep /${BINARY}$" echo -e "If it is launched by systemd, finding the service with" echo -e "Executing ${YELLOW}systemctl status $PORT993${NC}" echo -e "systemctl status $PORT993" echo -e "\nList all enabled services:" echo -e "systemctl list-unit-files | grep enabled" echo -e "stop a service: systemctl stop [service]" echo -e "${RED}QUITTING... (please stop the service and launch again)${NC}" exit 1 else echo -e 'OK: port 993 (IMAP SSL/TLS) is free' fi

if ! [ -z $PORT995 ] ; then echo -e "${RED}Error:${NC} POP3 server already running on port 995" echo -e "PID: ${YELLOW}$PORT995${NC}" BINARY=ps -p $PORT995 -o comm= echo -e "binary: ${YELLOW}$BINARY${NC}" echo -e "full command with arguments: ${YELLOW}ps -p $PORT995 -o command=${NC}" echo -e "possible packages (dpkg -S $BINARY | grep /${BINARY}$):" echo -e "dpkg -S $BINARY | grep /${BINARY}$" echo -e "If it is launched by systemd, finding the service with" echo -e "Executing ${YELLOW}systemctl status $PORT995${NC}" echo -e "systemctl status $PORT995" echo -e "\nList all enabled services:" echo -e "systemctl list-unit-files | grep enabled" echo -e "stop a service: systemctl stop [service]" echo -e "${RED}QUITTING... (please stop the service and launch again)${NC}" exit 1 else echo -e 'OK: port 995 (POP3 SSL/TLS) is free' fi

if ! [ -z $PORT80 ] ; then echo -e "${RED}Error:${NC} HTTP server already running on port 80" echo -e "PID: ${YELLOW}$PORT80${NC}" BINARY=ps -p $PORT80 -o comm= echo -e "binary: ${YELLOW}$BINARY${NC}" echo -e "full command with arguments: ${YELLOW}ps -p $PORT80 -o command=${NC}" echo -e "possible packages (dpkg -S $BINARY | grep /${BINARY}$):" echo -e "dpkg -S $BINARY | grep /${BINARY}$" echo -e "If it is launched by systemd, finding the service with" echo -e "Executing ${YELLOW}systemctl status $PORT80${NC}" echo -e "systemctl status $PORT80" echo -e "\nList all enabled services:" echo -e "systemctl list-unit-files | grep enabled" echo -e "stop a service: systemctl stop [service]" echo -e "${RED}QUITTING... (please stop the service and launch again)${NC}" exit 1 else echo -e 'OK: port 80 (HTTP) is free' fi

if ! [ -z $PORT443 ] ; then echo -e "${RED}Error:${NC} HTTPS server already running on port 443" echo -e "PID: ${YELLOW}$PORT443${NC}" BINARY=ps -p $PORT443 -o comm= echo -e "binary: ${YELLOW}$BINARY${NC}" echo -e "full command with arguments: ${YELLOW}ps -p $PORT443 -o command=${NC}" echo -e "possible packages (dpkg -S $BINARY | grep /${BINARY}$):" echo -e "dpkg -S $BINARY | grep /${BINARY}$" echo -e "If it is launched by systemd, finding the service with" echo -e "Executing ${YELLOW}systemctl status $PORT443${NC}" echo -e "systemctl status $PORT443" echo -e "\nList all enabled services:" echo -e "systemctl list-unit-files | grep enabled" echo -e "stop a service: systemctl stop [service]" echo -e "${RED}QUITTING... (please stop the service and launch again)${NC}" exit 1 else echo -e 'OK: port 443 (HTTPS) is free' fi


---


&para; 05_install_packages.sh 

#! /bin/bash

OURNAME=05_install_packages.sh

echo -e "\n-- Executing ${ORANGE}${OURNAME}${NC} subscript --"

install nginx

apt-get update apt-get -q -y install pwgen git ufw build-essential libssl-dev dnsutils python3 software-properties-common nginx wget mongodb-org nodejs redis-server clamav clamav-daemon

rspamd

apt-get -q -y --no-install-recommends install rspamd apt-get clean

DMARC policy=reject rules

echo 'actions = { quarantine = "add_header"; reject = "reject"; }' > /etc/rspamd/override.d/dmarc.conf


---

&para; 06_install_enable_services.sh 

#! /bin/bash

OURNAME=06_install_enable_services.sh

echo -e "\n-- Executing ${ORANGE}${OURNAME}${NC} subscript --"

NODE_PATH=command -v node SYSTEMCTL_PATH=command -v systemctl

SRS_SECRET=pwgen 12 -1 DKIM_SECRET=pwgen 12 -1 ZONEMTA_SECRET=pwgen 12 -1 DKIM_SELECTOR=$NODE_PATH -e 'console.log(Date().toString().substr(4, 3).toLowerCase() + new Date().getFullYear())'

$SYSTEMCTL_PATH enable mongod.service $SYSTEMCTL_PATH enable redis-server.service

echo -e "\n-- These are the installed and required programs:" node -v redis-server -v mongod --version echo "HOSTNAME: $HOSTNAME"

echo -e "-- Installing ${RED}npm globally${NC} (workaround)"

See issue https://github.com/nodemailer/wildduck/issues/82

npm install npm -g


---

&para; 07_install_wildduck.sh 

#! /bin/bash

OURNAME=07_install_wildduck.sh

echo -e "\n-- Executing ${ORANGE}${OURNAME}${NC} subscript --"

####### WILD DUCK #######

clear previous install

if [ -f "/etc/systemd/system/wildduck.service" ] then $SYSTEMCTL_PATH stop wildduck || true $SYSTEMCTL_PATH disable wildduck || true rm -rf /etc/systemd/system/wildduck.service fi rm -rf /var/opt/wildduck.git rm -rf /opt/wildduck rm -rf /etc/wildduck

fresh install

cd /var/opt git clone --bare https://github.com/nodemailer/wildduck.git

create update hook so we can later deploy to this location

hook_script wildduck

allow deploy user to restart wildduck service

echo "deploy ALL = (root) NOPASSWD: $SYSTEMCTL_PATH restart wildduck" >> /etc/sudoers.d/wildduck

checkout files from git to working directory

mkdir -p /opt/wildduck git --git-dir=/var/opt/wildduck.git --work-tree=/opt/wildduck checkout "$WILDDUCK_COMMIT" cp -r /opt/wildduck/config /etc/wildduck mv /etc/wildduck/default.toml /etc/wildduck/wildduck.toml

enable example message

sed -i -e 's/"disabled": true/"disabled": false/g' /opt/wildduck/emails/00-example.json

update ports

sed -i -e "s/999/99/g;s/localhost/$HOSTNAME/g" /etc/wildduck/imap.toml sed -i -e "s/999/99/g;s/localhost/$HOSTNAME/g" /etc/wildduck/pop3.toml

echo "enabled=true port=24 disableSTARTTLS=true" > /etc/wildduck/lmtp.toml

make sure that DKIM keys are not stored to database as cleartext

echo "secret="$DKIM_SECRET"" >> /etc/wildduck/dkim.toml

echo "user="wildduck" group="wildduck" emailDomain="$MAILDOMAIN"" | cat - /etc/wildduck/wildduck.toml > temp && mv temp /etc/wildduck/wildduck.toml

sed -i -e "s/localhost:3000/$HOSTNAME/g;s/localhost/$HOSTNAME/g;s/2587/587/g" /etc/wildduck/wildduck.toml sed -i -e "s/secret value/$SRS_SECRET/g;s/#loopSecret/loopSecret/g" /etc/wildduck/sender.toml

cd /opt/wildduck npm install --production --unsafe-perm --no-optional --no-package-lock --no-audit --ignore-scripts --no-shrinkwrap

chown -R deploy:deploy /var/opt/wildduck.git chown -R deploy:deploy /opt/wildduck

echo "d /opt/wildduck 0755 deploy deploy d /etc/wildduck 0755 wildduck wildduck" > /etc/tmpfiles.d/zone-mta.conf log_script "wildduck-server"

echo "[Unit] Description=WildDuck Mail Server Conflicts=cyrus.service dovecot.service After=mongod.service redis.service

[Service] Environment="NODE_ENV=production" WorkingDirectory=/opt/wildduck ExecStart=$NODE_PATH server.js --config="/etc/wildduck/wildduck.toml" ExecReload=/bin/kill -HUP $MAINPID Type=simple Restart=always SyslogIdentifier=wildduck-server

[Install] WantedBy=multi-user.target" > /etc/systemd/system/wildduck.service

$SYSTEMCTL_PATH enable wildduck.service


---

&para; 08_install_haraka.sh 

#! /bin/bash

OURNAME=08_install_haraka.sh

echo -e "\n-- Executing ${ORANGE}${OURNAME}${NC} subscript --"

####### HARAKA #######

clear previous install

if [ -f "/etc/systemd/system/haraka.service" ] then $SYSTEMCTL_PATH stop haraka || true $SYSTEMCTL_PATH disable haraka || true rm -rf /etc/systemd/system/haraka.service fi rm -rf /var/opt/haraka-plugin-wildduck.git rm -rf /opt/haraka

fresh install

cd /var/opt git clone --bare https://github.com/nodemailer/haraka-plugin-wildduck.git echo "#!/bin/bash git --git-dir=/var/opt/haraka-plugin-wildduck.git --work-tree=/opt/haraka/plugins/wildduck checkout "$3" -f cd /opt/haraka/plugins/wildduck rm -rf package-lock.json npm install --production --no-optional --no-package-lock --no-audit --ignore-scripts --no-shrinkwrap --progress=false sudo $SYSTEMCTL_PATH restart haraka || echo "Failed restarting service"" > "/var/opt/haraka-plugin-wildduck.git/hooks/update" chmod +x "/var/opt/haraka-plugin-wildduck.git/hooks/update"

allow deploy user to restart wildduck service

echo "deploy ALL = (root) NOPASSWD: $SYSTEMCTL_PATH restart haraka" >> /etc/sudoers.d/wildduck

cd npm install --production --no-optional --no-package-lock --no-audit --no-shrinkwrap --unsafe-perm -g Haraka@$HARAKA_VERSION haraka -i /opt/haraka cd /opt/haraka npm install --production --no-optional --no-package-lock --no-audit --no-shrinkwrap --unsafe-perm --save haraka-plugin-rspamd haraka-plugin-redis Haraka@$HARAKA_VERSION

Haraka WildDuck plugin. Install as separate repo as it can be edited more easily later

mkdir -p plugins/wildduck git --git-dir=/var/opt/haraka-plugin-wildduck.git --work-tree=/opt/haraka/plugins/wildduck checkout "$WILDDUCK_HARAKA_COMMIT"

cd plugins/wildduck npm install --production --no-optional --no-package-lock --no-audit --ignore-scripts --no-shrinkwrap --unsafe-perm --progress=false

cd /opt/haraka mv config/plugins config/plugins.bak

echo "26214400" > config/databytes echo "$HOSTNAME" > config/me echo "WildDuck MX" > config/smtpgreeting

echo "#spf #dkim_verify

ClamAV is disabled by default. Make sure freshclam has updated all

virus definitions and clamav-daemon has successfully started before

enabling it.

#clamd

rspamd tls

WildDuck plugin handles recipient checking and queueing

wildduck" > config/plugins

echo "key=/etc/wildduck/certs/privkey.pem cert=/etc/wildduck/certs/fullchain.pem" > config/tls.ini

echo 'host = localhost port = 11333 add_headers = always [dkim] enabled = true [header] bar = X-Rspamd-Bar report = X-Rspamd-Report score = X-Rspamd-Score spam = X-Rspamd-Spam [check] authenticated=true private_ip=true [reject] spam = false [soft_reject] enabled = true [rmilter_headers] enabled = true [spambar] positive = + negative = - neutral = /' > config/rspamd.ini

echo 'clamd_socket = /var/run/clamav/clamd.ctl [reject] virus=true error=false' > config/clamd.ini

cp plugins/wildduck/config/wildduck.yaml config/wildduck.yaml sed -i -e "s/secret value/$SRS_SECRET/g;s/#loopSecret/loopSecret/g" config/wildduck.yaml

Ensure required files and permissions

echo "d /opt/haraka 0755 deploy deploy" > /etc/tmpfiles.d/haraka.conf log_script "haraka"

echo '[Unit] Description=Haraka MX Server After=mongod.service redis.service

[Service] Environment="NODE_ENV=production" WorkingDirectory=/opt/haraka ExecStart=/usr/bin/node ./node_modules/.bin/haraka -c . Type=simple Restart=always SyslogIdentifier=haraka

[Install] WantedBy=multi-user.target' > /etc/systemd/system/haraka.service

echo 'user=wildduck group=wildduck' >> config/smtp.ini

chown -R deploy:deploy /opt/haraka chown -R deploy:deploy /var/opt/haraka-plugin-wildduck.git

ensure queue folder for Haraka

mkdir -p /opt/haraka/queue chown -R wildduck:wildduck /opt/haraka/queue

$SYSTEMCTL_PATH enable haraka.service


---

&para; cat 09_install_zone_mta.sh

#! /bin/bash

OURNAME=09_install_zone_mta.sh

echo -e "\n-- Executing ${ORANGE}${OURNAME}${NC} subscript --"

ZoneMTA

clear previous install

if [ -f "/etc/systemd/system/zone-mta.service" ] then $SYSTEMCTL_PATH stop zone-mta || true $SYSTEMCTL_PATH disable zone-mta || true rm -rf /etc/systemd/system/zone-mta.service fi rm -rf /var/opt/zone-mta.git rm -rf /var/opt/zonemta-wildduck.git rm -rf /opt/zone-mta rm -rf /etc/zone-mta

fresh install

cd /var/opt git clone --bare https://github.com/zone-eu/zone-mta-template.git zone-mta.git git clone --bare https://github.com/nodemailer/zonemta-wildduck.git

create update hooks so we can later deploy to this location

hook_script zone-mta echo "#!/bin/bash git --git-dir=/var/opt/zonemta-wildduck.git --work-tree=/opt/zone-mta/plugins/wildduck checkout "$3" -f cd /opt/zone-mta/plugins/wildduck rm -rf package-lock.json npm install --production --no-optional --no-package-lock --no-audit --ignore-scripts --no-shrinkwrap --progress=false sudo $SYSTEMCTL_PATH restart zone-mta || echo "Failed restarting service"" > "/var/opt/zonemta-wildduck.git/hooks/update" chmod +x "/var/opt/zonemta-wildduck.git/hooks/update"

allow deploy user to restart zone-mta service

echo "deploy ALL = (root) NOPASSWD: $SYSTEMCTL_PATH restart zone-mta" >> /etc/sudoers.d/zone-mta

checkout files from git to working directory

mkdir -p /opt/zone-mta git --git-dir=/var/opt/zone-mta.git --work-tree=/opt/zone-mta checkout "$ZONEMTA_COMMIT"

mkdir -p /opt/zone-mta/plugins/wildduck git --git-dir=/var/opt/zonemta-wildduck.git --work-tree=/opt/zone-mta/plugins/wildduck checkout "$WILDDUCK_ZONEMTA_COMMIT"

cp -r /opt/zone-mta/config /etc/zone-mta sed -i -e 's/port=2525/port=587/g;s/host="127.0.0.1"/host="0.0.0.0"/g;s/authentication=false/authentication=true/g' /etc/zone-mta/interfaces/feeder.toml rm -rf /etc/zone-mta/plugins/dkim.toml echo '# @include "/etc/wildduck/dbs.toml"' > /etc/zone-mta/dbs-production.toml echo 'user="wildduck" group="wildduck"' | cat - /etc/zone-mta/zonemta.toml > temp && mv temp /etc/zone-mta/zonemta.toml

echo "[[default]] address="0.0.0.0" name="$HOSTNAME"" > /etc/zone-mta/pools.toml

echo "["modules/zonemta-loop-breaker"] enabled="sender" secret="$ZONEMTA_SECRET" algo="md5"" > /etc/zone-mta/plugins/loop-breaker.toml

echo "[wildduck] enabled=["receiver", "sender"]

which interfaces this plugin applies to

interfaces=["feeder"]

optional hostname to be used in headers

defaults to os.hostname()

hostname="$HOSTNAME"

SRS settings for forwarded emails

[wildduck.srs] # Handle rewriting of forwarded emails enabled=true # SRS secret value. Must be the same as in the MX side secret="$SRS_SECRET" # SRS domain, must resolve back to MX rewriteDomain="$MAILDOMAIN"

[wildduck.dkim]

share config with WildDuck installation

@include "/etc/wildduck/dkim.toml"

" > /etc/zone-mta/plugins/wildduck.toml

cd /opt/zone-mta/keys

Many registrar limits dns TXT fields to 255 char. 1024bit is almost too long:-\

openssl genrsa -out "$MAILDOMAIN-dkim.pem" 1024 chmod 400 "$MAILDOMAIN-dkim.pem" openssl rsa -in "$MAILDOMAIN-dkim.pem" -out "$MAILDOMAIN-dkim.cert" -pubout DKIM_DNS="v=DKIM1;k=rsa;p=$(grep -v -e '^-' $MAILDOMAIN-dkim.cert | tr -d "\n")"

DKIM_JSON=DOMAIN="$MAILDOMAIN" SELECTOR="$DKIM_SELECTOR" node -e 'console.log(JSON.stringify({ domain: process.env.DOMAIN, selector: process.env.SELECTOR, description: "Default DKIM key for "+process.env.DOMAIN, privateKey: fs.readFileSync("/opt/zone-mta/keys/"+process.env.DOMAIN+"-dkim.pem", "UTF-8") }))'

cd /opt/zone-mta npm install --production --no-optional --no-package-lock --no-audit --ignore-scripts --no-shrinkwrap --unsafe-perm

cd /opt/zone-mta/plugins/wildduck npm install --production --no-optional --no-package-lock --no-audit --ignore-scripts --no-shrinkwrap --unsafe-perm

chown -R deploy:deploy /var/opt/zone-mta.git chown -R deploy:deploy /var/opt/zonemta-wildduck.git chown -R deploy:deploy /opt/zone-mta chown -R wildduck:wildduck /etc/zone-mta

Ensure required files and permissions

echo "d /opt/zone-mta 0755 deploy deploy d /etc/zone-mta 0755 wildduck wildduck" > /etc/tmpfiles.d/zone-mta.conf log_script "zone-mta"

echo '[Unit] Description=Zone Mail Transport Agent Conflicts=sendmail.service exim.service postfix.service After=mongod.service redis.service

[Service] Environment="NODE_ENV=production" WorkingDirectory=/opt/zone-mta ExecStart=/usr/bin/node index.js --config="/etc/zone-mta/zonemta.toml" ExecReload=/bin/kill -HUP $MAINPID Type=simple Restart=always SyslogIdentifier=zone-mta

[Install] WantedBy=multi-user.target' > /etc/systemd/system/zone-mta.service

$SYSTEMCTL_PATH enable zone-mta.service


---

&para;10_install_wildduck_webmail.sh 

#! /bin/bash

OURNAME=10_install_wildduck_webmail.sh

echo -e "\n-- Executing ${ORANGE}${OURNAME}${NC} subscript --"

WWW

clear previous install

if [ -f "/etc/systemd/system/wildduck-webmail.service" ] then $SYSTEMCTL_PATH stop wildduck-webmail || true $SYSTEMCTL_PATH disable wildduck-webmail || true rm -rf /etc/systemd/system/wildduck-webmail.service fi rm -rf /var/opt/wildduck-webmail.git rm -rf /opt/wildduck-webmail

fresh install

cd /var/opt git clone --bare https://github.com/nodemailer/wildduck-webmail.git

create update hook so we can later deploy to this location

hook_script_bower wildduck-webmail chmod +x /var/opt/wildduck-webmail.git/hooks/update

allow deploy user to restart zone-mta service

echo "deploy ALL = (root) NOPASSWD: $SYSTEMCTL_PATH restart wildduck-webmail" >> /etc/sudoers.d/wildduck-webmail

checkout files from git to working directory

mkdir -p /opt/wildduck-webmail git --git-dir=/var/opt/wildduck-webmail.git --work-tree=/opt/wildduck-webmail checkout "$WEBMAIL_COMMIT" cp /opt/wildduck-webmail/config/default.toml /etc/wildduck/wildduck-webmail.toml

sed -i -e "s/localhost/$HOSTNAME/g;s/999/99/g;s/2587/587/g;s/proxy=false/proxy=true/g;s/domains=.*/domains=["$MAILDOMAIN"]/g" /etc/wildduck/wildduck-webmail.toml

cd /opt/wildduck-webmail

chown -R deploy:deploy /var/opt/wildduck-webmail.git chown -R deploy:deploy /opt/wildduck-webmail

we need to run bower which reject root

HOME=/home/deploy sudo -u deploy npm install HOME=/home/deploy sudo -u deploy npm run bowerdeps

echo "d /opt/wildduck-webmail 0755 deploy deploy" > /etc/tmpfiles.d/zone-mta.conf log_script "wildduck-www"

echo '[Unit] Description=Wildduck Webmail After=wildduck.service

[Service] Environment="NODE_ENV=production" WorkingDirectory=/opt/wildduck-webmail ExecStart=/usr/bin/node server.js --config="/etc/wildduck/wildduck-webmail.toml" ExecReload=/bin/kill -HUP $MAINPID Type=simple Restart=always SyslogIdentifier=wildduck-www

[Install] WantedBy=multi-user.target' > /etc/systemd/system/wildduck-webmail.service

$SYSTEMCTL_PATH enable wildduck-webmail.service


---

&para; 11_install_nginx.sh 

#! /bin/bash

OURNAME=11_install_nginx.sh

echo -e "\n-- Executing ${ORANGE}${OURNAME}${NC} subscript --"

NGINX

Create initial certs. These will be overwritten later by Let's Encrypt certs

mkdir -p /etc/wildduck/certs cd /etc/wildduck/certs openssl req -subj "/CN=$HOSTNAME/O=My Company Name LTD./C=US" -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout privkey.pem -out fullchain.pem

chown -R wildduck:wildduck /etc/wildduck/certs chmod 0700 /etc/wildduck/certs/privkey.pem

create nginx config dirs - if missing

if [[ ! -d /etc/nginx/sites-available ]]; then mkdir -p /etc/nginx/sites-available fi if [[ ! -d /etc/nginx/sites-enabled ]]; then mkdir -p /etc/nginx/sites-enabled fi

Setup domain without SSL at first, otherwise acme.sh will fail

echo "server { listen 80;

server_name $HOSTNAME;

ssl_certificate /etc/wildduck/certs/fullchain.pem;
ssl_certificate_key /etc/wildduck/certs/privkey.pem;

# special config for EventSource to disable gzip
location /api/events {
    proxy_http_version 1.1;
    gzip off;
    proxy_set_header X-Real-IP \$remote_addr;
    proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
    proxy_set_header HOST \$http_host;
    proxy_set_header X-NginX-Proxy true;
    proxy_pass http://127.0.0.1:3000;
    proxy_redirect off;
}

# special config for uploads
location /webmail/send {
    client_max_body_size 15M;
    proxy_http_version 1.1;
    proxy_set_header X-Real-IP \$remote_addr;
    proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
    proxy_set_header HOST \$http_host;
    proxy_set_header X-NginX-Proxy true;
    proxy_pass http://127.0.0.1:3000;
    proxy_redirect off;
}

location / {
    proxy_http_version 1.1;
    proxy_set_header X-Real-IP \$remote_addr;
    proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
    proxy_set_header HOST \$http_host;
    proxy_set_header X-NginX-Proxy true;
    proxy_pass http://127.0.0.1:3000;
    proxy_redirect off;
}

}" > "/etc/nginx/sites-available/$HOSTNAME" rm -rf "/etc/nginx/sites-enabled/$HOSTNAME" ln -s "/etc/nginx/sites-available/$HOSTNAME" "/etc/nginx/sites-enabled/$HOSTNAME" $SYSTEMCTL_PATH reload nginx


---

&para; 12_install_ufw_rules.sh 

#! /bin/bash

OURNAME=12_install_ufw_rules.sh

echo -e "\n-- Executing ${ORANGE}${OURNAME}${NC} subscript --"

get sshd port from /etc/ssh/sshd_config

_var_sshd_port="$(cat /etc/ssh/sshd_config|grep -i -E ^port|cut -f2 -d' ')" if [[ $_var_sshd_port == "" ]]; then _var_sshd_port=22 fi

UFW

ufw allow $_var_sshd_port/tcp ufw allow 80/tcp ufw allow 443/tcp ufw allow 25/tcp ufw allow 587/tcp ufw allow 993/tcp ufw allow 995/tcp ufw --force enable


-  cat 13_install_ssl_certs.sh 

#! /bin/bash

OURNAME=13_install_ssl_certs.sh

echo -e "\n-- Executing ${ORANGE}${OURNAME}${NC} subscript --"

SSL CERTS

Install acme.sh

NOTE: the version 3.0.7 has a bug with Nginx certs, so version is pinned to 3.0.6

ACME_VERSION="3.0.6" wget https://raw.githubusercontent.com/acmesh-official/acme.sh/${ACME_VERSION}/acme.sh sh acme.sh --install --auto-upgrade 0 rm -rf acme.sh

WildDuck TLS config

echo 'cert="/etc/wildduck/certs/fullchain.pem" key="/etc/wildduck/certs/privkey.pem"' > /etc/wildduck/tls.toml

sed -i -e "s/key=/#key=/g;s/cert=/#cert=/g" /etc/zone-mta/interfaces/feeder.toml echo '# @include "../../wildduck/tls.toml"' >> /etc/zone-mta/interfaces/feeder.toml

vanity script as first run should not restart anything

echo '#!/bin/bash echo "OK"' > /usr/local/bin/reload-services.sh chmod +x /usr/local/bin/reload-services.sh

~/.acme.sh/acme.sh --issue --nginx --server letsencrypt
-d "$HOSTNAME"
--key-file /etc/wildduck/certs/privkey.pem
--fullchain-file /etc/wildduck/certs/fullchain.pem
--reloadcmd "/usr/local/bin/reload-services.sh"
--force || echo "Warning: Failed to generate certificates, using self-signed certs"

Update site config, make sure ssl is enabled

echo "server { listen 80; listen [::]:80; listen 443 ssl http2; listen [::]:443 ssl http2;

server_name $HOSTNAME;

ssl_certificate /etc/wildduck/certs/fullchain.pem;
ssl_certificate_key /etc/wildduck/certs/privkey.pem;

# special config for EventSource to disable gzip
location /api/events {
    proxy_http_version 1.1;
    gzip off;
    proxy_set_header X-Real-IP \$remote_addr;
    proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
    proxy_set_header HOST \$http_host;
    proxy_set_header X-NginX-Proxy true;
    proxy_pass http://127.0.0.1:3000;
    proxy_redirect off;
}

# special config for uploads
location /webmail/send {
    client_max_body_size 15M;
    proxy_http_version 1.1;
    proxy_set_header X-Real-IP \$remote_addr;
    proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
    proxy_set_header HOST \$http_host;
    proxy_set_header X-NginX-Proxy true;
    proxy_pass http://127.0.0.1:3000;
    proxy_redirect off;
}

location / {
    proxy_http_version 1.1;
    proxy_set_header X-Real-IP \$remote_addr;
    proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
    proxy_set_header HOST \$http_host;
    proxy_set_header X-NginX-Proxy true;
    proxy_pass http://127.0.0.1:3000;
    proxy_redirect off;
}

}" > "/etc/nginx/sites-available/$HOSTNAME"

#See issue https://github.com/nodemailer/wildduck/issues/83 $SYSTEMCTL_PATH start nginx $SYSTEMCTL_PATH reload nginx


---

&para; 14_install_start_services.sh 

#! /bin/bash

OURNAME=14_install_start_services.sh

echo -e "\n-- Executing ${ORANGE}${OURNAME}${NC} subscript --"

Run tmpfiles definitions to ensure required directories/files

systemd-tmpfiles --create --remove

Restart rsyslog for the changes to take effect

systemctl restart rsyslog

update reload script for future updates

echo "#!/bin/bash $SYSTEMCTL_PATH reload nginx $SYSTEMCTL_PATH restart wildduck $SYSTEMCTL_PATH restart zone-mta $SYSTEMCTL_PATH restart haraka $SYSTEMCTL_PATH restart wildduck-webmail" > /usr/local/bin/reload-services.sh chmod +x /usr/local/bin/reload-services.sh

start services

$SYSTEMCTL_PATH start mongod $SYSTEMCTL_PATH start redis $SYSTEMCTL_PATH start wildduck $SYSTEMCTL_PATH start haraka $SYSTEMCTL_PATH start zone-mta $SYSTEMCTL_PATH start wildduck-webmail $SYSTEMCTL_PATH reload nginx


---

&para; 15_install_deploy.sh 

#! /bin/bash

OURNAME=15_install_deploy.sh

echo -e "\n-- Executing ${ORANGE}${OURNAME}${NC} subscript --"

cd "$INSTALLDIR"

echo "DEPLOY SETUP

  1. Add your ssh key to /home/deploy/.ssh/authorized_keys

  2. Clone application code $ git clone deploy@$HOSTNAME:/var/opt/wildduck.git $ git clone deploy@$HOSTNAME:/var/opt/zone-mta.git $ git clone deploy@$HOSTNAME:/var/opt/wildduck-webmail.git $ git clone deploy@$HOSTNAME:/var/opt/haraka-plugin-wildduck.git $ git clone deploy@$HOSTNAME:/var/opt/zonemta-wildduck.git

  3. After making a change in local copy deploy to server $ git push origin master (you might need to use -f when pushing first time)

NAMESERVER SETUP

MX

Add this MX record to the $MAILDOMAIN DNS zone:

$MAILDOMAIN. IN MX 5 $HOSTNAME.

SPF

Add this TXT record to the $MAILDOMAIN DNS zone:

$MAILDOMAIN. IN TXT "v=spf1 a:$HOSTNAME a:$MAILDOMAIN ip4:$PUBLIC_IP ~all"

Or: $MAILDOMAIN. IN TXT "v=spf1 a:$HOSTNAME ip4:$PUBLIC_IP ~all" $MAILDOMAIN. IN TXT "v=spf1 ip4:$PUBLIC_IP ~all"

Some explanation: SPF is basically a DNS entry (TXT), where you can define, which server hosts (a:[HOSTNAME]) or ip address (ip4:[IP_ADDRESS]) are allowed to send emails. So the receiver server (eg. gmail's server) can look up this entry and decide if you(as a sender server) is allowed to send emails as this email address.

If you are unsure, list more a:, ip4 entries, rather then fewer.

Example: company website: awesome.com company's email server: mail.awesome.com company's reverse dns entry for this email server: mail.awesome.com -> 11.22.33.44

SPF record in this case would be: awesome.com. IN TXT "v=spf1 a:mail.awesome.com a:awesome.com ip4:11.22.33.44 ~all"

The following servers can send emails for *@awesome.com email addresses: awesome.com (company's website handling server) mail.awesome.com (company's mail server) 11.22.33.44 (company's mail server's ip address)

Please note, that a:mail.awesome.com is the same as ip4:11.22.33.44, so it is redundant. But better safe than sorry. And in this example, the company's website handling server can also send emails and in general it is an outbound only server. If a website handles email sending (confirmation emails, contact form, etc).

DKIM

Add this TXT record to the $MAILDOMAIN DNS zone:

$DKIM_SELECTOR._domainkey.$MAILDOMAIN. IN TXT "$DKIM_DNS"

The DKIM .json text we added to wildduck server: curl -i -XPOST http://localhost:8080/dkim \ -H 'Content-type: application/json' \ -d '$DKIM_JSON'

Please refer to the manual how to change/delete/update DKIM keys via the REST api (with curl on localhost) for the newest version.

List DKIM keys: curl -i http://localhost:8080/dkim Delete DKIM: curl -i -XDELETE http://localhost:8080/dkim/59ef21aef255ed1d9d790e81

Move DKIM keys to another machine:

Save the above curl command and dns entry. Also copy the following two files too: /opt/zone-mta/keys/[MAILDOMAIN]-dkim.cert /opt/zone-mta/keys/[MAILDOMAIN]-dkim.pem

pem: private key (guard it well) cert: public key

DMARC

Add this TXT record to the $MAILDOMAIN DNS zone:

_dmarc.$MAILDOMAIN. IN TXT "v=DMARC1; p=reject;"

PTR

Make sure that your public IP has a PTR record set to $HOSTNAME. If your hosting provider does not allow you to set PTR records but has assigned their own hostname, then edit /etc/zone-mta/pools.toml and replace the hostname $HOSTNAME with the actual hostname of this server.

TL;DR

Add the following DNS records to the $MAILDOMAIN DNS zone:

$MAILDOMAIN. IN MX 5 $HOSTNAME. $MAILDOMAIN. IN TXT "v=spf1 ip4:$PUBLIC_IP ~all" $DKIM_SELECTOR._domainkey.$MAILDOMAIN. IN TXT "$DKIM_DNS" _dmarc.$MAILDOMAIN. IN TXT "v=DMARC1; p=reject;"

(this text is also stored to $INSTALLDIR/$MAILDOMAIN-nameserver.txt)" > "$INSTALLDIR/$MAILDOMAIN-nameserver.txt"

printf "Waiting for the server to start up.."

until $(curl --output /dev/null --silent --fail http://localhost:8080/users); do printf '.' sleep 2 done echo "."

Ensure DKIM key

echo "Registering DKIM key for $MAILDOMAIN" echo $DKIM_JSON

curl -i -XPOST http://localhost:8080/dkim
-H 'Content-type: application/json'
-d "$DKIM_JSON"

echo "" cat "$INSTALLDIR/$MAILDOMAIN-nameserver.txt" echo "" echo "All done, open https://$HOSTNAME/ in your browser"