6ea1a06a12
On every login we're notified: New release '16.04.1 LTS' available. Run 'do-release-upgrade' to upgrade to it. Disable this so that an eager yet inattentive admin doesn't accidentally follow these instructions.
313 lines
12 KiB
Bash
Executable file
313 lines
12 KiB
Bash
Executable file
source /etc/mailinabox.conf
|
|
source setup/functions.sh # load our functions
|
|
|
|
# Basic System Configuration
|
|
# -------------------------
|
|
|
|
# ### Set hostname of the box
|
|
|
|
# If the hostname is not correctly resolvable sudo can't be used. This will result in
|
|
# errors during the install
|
|
#
|
|
# First set the hostname in the configuration file, then activate the setting
|
|
|
|
echo $PRIMARY_HOSTNAME > /etc/hostname
|
|
hostname $PRIMARY_HOSTNAME
|
|
|
|
# ### Add swap space to the system
|
|
|
|
# If the physical memory of the system is below 2GB it is wise to create a
|
|
# swap file. This will make the system more resiliant to memory spikes and
|
|
# prevent for instance spam filtering from crashing
|
|
|
|
# We will create a 1G file, this should be a good balance between disk usage
|
|
# and buffers for the system. We will only allocate this file if there is more
|
|
# than 5GB of disk space available
|
|
|
|
# The following checks are performed:
|
|
# - Check if swap is currently mountend by looking at /proc/swaps
|
|
# - Check if the user intents to activate swap on next boot by checking fstab entries.
|
|
# - Check if a swapfile already exists
|
|
# - Check if the root file system is not btrfs, might be an incompatible version with
|
|
# swapfiles. User should hanle it them selves.
|
|
# - Check the memory requirements
|
|
# - Check available diskspace
|
|
|
|
# See https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04
|
|
# for reference
|
|
|
|
SWAP_MOUNTED=$(cat /proc/swaps | tail -n+2)
|
|
SWAP_IN_FSTAB=$(grep "swap" /etc/fstab)
|
|
ROOT_IS_BTRFS=$(grep "\/ .*btrfs" /proc/mounts)
|
|
TOTAL_PHYSICAL_MEM=$(head -n 1 /proc/meminfo | awk '{print $2}')
|
|
AVAILABLE_DISK_SPACE=$(df / --output=avail | tail -n 1)
|
|
if
|
|
[ -z "$SWAP_MOUNTED" ] &&
|
|
[ -z "$SWAP_IN_FSTAB" ] &&
|
|
[ ! -e /swapfile ] &&
|
|
[ -z "$ROOT_IS_BTRFS" ] &&
|
|
[ $TOTAL_PHYSICAL_MEM -lt 1900000 ] &&
|
|
[ $AVAILABLE_DISK_SPACE -gt 5242880 ]
|
|
then
|
|
echo "Adding a swap file to the system..."
|
|
|
|
# Allocate and activate the swap file. Allocate in 1KB chuncks
|
|
# doing it in one go, could fail on low memory systems
|
|
dd if=/dev/zero of=/swapfile bs=1024 count=$[1024*1024] status=none
|
|
if [ -e /swapfile ]; then
|
|
chmod 600 /swapfile
|
|
hide_output mkswap /swapfile
|
|
swapon /swapfile
|
|
fi
|
|
|
|
# Check if swap is mounted then activate on boot
|
|
if swapon -s | grep -q "\/swapfile"; then
|
|
echo "/swapfile none swap sw 0 0" >> /etc/fstab
|
|
else
|
|
echo "ERROR: Swap allocation failed"
|
|
fi
|
|
fi
|
|
|
|
# ### Add Mail-in-a-Box's PPA.
|
|
|
|
# We've built several .deb packages on our own that we want to include.
|
|
# One is a replacement for Ubuntu's stock postgrey package that makes
|
|
# some enhancements. The other is dovecot-lucene, a Lucene-based full
|
|
# text search plugin for (and by) dovecot, which is not available in
|
|
# Ubuntu currently.
|
|
#
|
|
# So, first ensure add-apt-repository is installed, then use it to install
|
|
# the [mail-in-a-box ppa](https://launchpad.net/~mail-in-a-box/+archive/ubuntu/ppa).
|
|
|
|
|
|
if [ ! -f /usr/bin/add-apt-repository ]; then
|
|
echo "Installing add-apt-repository..."
|
|
hide_output apt-get update
|
|
apt_install software-properties-common
|
|
fi
|
|
|
|
hide_output add-apt-repository -y ppa:mail-in-a-box/ppa
|
|
|
|
# ### Update Packages
|
|
|
|
# Update system packages to make sure we have the latest upstream versions of things from Ubuntu.
|
|
|
|
echo Updating system packages...
|
|
hide_output apt-get update
|
|
apt_get_quiet upgrade
|
|
|
|
# ### Install System Packages
|
|
|
|
# Install basic utilities.
|
|
#
|
|
# * haveged: Provides extra entropy to /dev/random so it doesn't stall
|
|
# when generating random numbers for private keys (e.g. during
|
|
# ldns-keygen).
|
|
# * unattended-upgrades: Apt tool to install security updates automatically.
|
|
# * cron: Runs background processes periodically.
|
|
# * ntp: keeps the system time correct
|
|
# * fail2ban: scans log files for repeated failed login attempts and blocks the remote IP at the firewall
|
|
# * netcat-openbsd: `nc` command line networking tool
|
|
# * git: we install some things directly from github
|
|
# * sudo: allows privileged users to execute commands as root without being root
|
|
# * coreutils: includes `nproc` tool to report number of processors, mktemp
|
|
# * bc: allows us to do math to compute sane defaults
|
|
|
|
echo Installing system packages...
|
|
apt_install python3 python3-dev python3-pip \
|
|
netcat-openbsd wget curl git sudo coreutils bc \
|
|
haveged pollinate \
|
|
unattended-upgrades cron ntp fail2ban
|
|
|
|
# ### Suppress Upgrade Prompts
|
|
# Since Mail-in-a-Box might jump straight to 18.04 LTS, there's no need
|
|
# to be reminded about 16.04 on every login.
|
|
tools/editconf.py /etc/update-manager/release-upgrades Prompt=never
|
|
rm -f /var/lib/ubuntu-release-upgrader/release-upgrade-available
|
|
|
|
# ### Set the system timezone
|
|
#
|
|
# Some systems are missing /etc/timezone, which we cat into the configs for
|
|
# Z-Push and ownCloud, so we need to set it to something. Daily cron tasks
|
|
# like the system backup are run at a time tied to the system timezone, so
|
|
# letting the user choose will help us identify the right time to do those
|
|
# things (i.e. late at night in whatever timezone the user actually lives
|
|
# in).
|
|
#
|
|
# However, changing the timezone once it is set seems to confuse fail2ban
|
|
# and requires restarting fail2ban (done below in the fail2ban
|
|
# section) and syslog (see #328). There might be other issues, and it's
|
|
# not likely the user will want to change this, so we only ask on first
|
|
# setup.
|
|
if [ -z "$NONINTERACTIVE" ]; then
|
|
if [ ! -f /etc/timezone ] || [ ! -z $FIRST_TIME_SETUP ]; then
|
|
# If the file is missing or this is the user's first time running
|
|
# Mail-in-a-Box setup, run the interactive timezone configuration
|
|
# tool.
|
|
dpkg-reconfigure tzdata
|
|
restart_service rsyslog
|
|
fi
|
|
else
|
|
# This is a non-interactive setup so we can't ask the user.
|
|
# If /etc/timezone is missing, set it to UTC.
|
|
if [ ! -f /etc/timezone ]; then
|
|
echo "Setting timezone to UTC."
|
|
echo "Etc/UTC" > /etc/timezone
|
|
restart_service rsyslog
|
|
fi
|
|
fi
|
|
|
|
# ### Seed /dev/urandom
|
|
#
|
|
# /dev/urandom is used by various components for generating random bytes for
|
|
# encryption keys and passwords:
|
|
#
|
|
# * TLS private key (see `ssl.sh`, which calls `openssl genrsa`)
|
|
# * DNSSEC signing keys (see `dns.sh`)
|
|
# * our management server's API key (via Python's os.urandom method)
|
|
# * Roundcube's SECRET_KEY (`webmail.sh`)
|
|
# * ownCloud's administrator account password (`owncloud.sh`)
|
|
#
|
|
# Why /dev/urandom? It's the same as /dev/random, except that it doesn't wait
|
|
# for a constant new stream of entropy. In practice, we only need a little
|
|
# entropy at the start to get going. After that, we can safely pull a random
|
|
# stream from /dev/urandom and not worry about how much entropy has been
|
|
# added to the stream. (http://www.2uo.de/myths-about-urandom/) So we need
|
|
# to worry about /dev/urandom being seeded properly (which is also an issue
|
|
# for /dev/random), but after that /dev/urandom is superior to /dev/random
|
|
# because it's faster and doesn't block indefinitely to wait for hardware
|
|
# entropy. Note that `openssl genrsa` even uses `/dev/urandom`, and if it's
|
|
# good enough for generating an RSA private key, it's good enough for anything
|
|
# else we may need.
|
|
#
|
|
# Now about that seeding issue....
|
|
#
|
|
# /dev/urandom is seeded from "the uninitialized contents of the pool buffers when
|
|
# the kernel starts, the startup clock time in nanosecond resolution,...and
|
|
# entropy saved across boots to a local file" as well as the order of
|
|
# execution of concurrent accesses to /dev/urandom. (Heninger et al 2012,
|
|
# https://factorable.net/weakkeys12.conference.pdf) But when memory is zeroed,
|
|
# the system clock is reset on boot, /etc/init.d/urandom has not yet run, or
|
|
# the machine is single CPU or has no concurrent accesses to /dev/urandom prior
|
|
# to this point, /dev/urandom may not be seeded well. After this, /dev/urandom
|
|
# draws from the same entropy sources as /dev/random, but it doesn't block or
|
|
# issue any warnings if no entropy is actually available. (http://www.2uo.de/myths-about-urandom/)
|
|
# Entropy might not be readily available because this machine has no user input
|
|
# devices (common on servers!) and either no hard disk or not enough IO has
|
|
# ocurred yet --- although haveged tries to mitigate this. So there's a good chance
|
|
# that accessing /dev/urandom will not be drawing from any hardware entropy and under
|
|
# a perfect-storm circumstance where the other seeds are meaningless, /dev/urandom
|
|
# may not be seeded at all.
|
|
#
|
|
# The first thing we'll do is block until we can seed /dev/urandom with enough
|
|
# hardware entropy to get going, by drawing from /dev/random. haveged makes this
|
|
# less likely to stall for very long.
|
|
|
|
echo Initializing system random number generator...
|
|
dd if=/dev/random of=/dev/urandom bs=1 count=32 2> /dev/null
|
|
|
|
# This is supposedly sufficient. But because we're not sure if hardware entropy
|
|
# is really any good on virtualized systems, we'll also seed from Ubuntu's
|
|
# pollinate servers:
|
|
|
|
pollinate -q -r
|
|
|
|
# Between these two, we really ought to be all set.
|
|
|
|
# ### Package maintenance
|
|
#
|
|
# Allow apt to install system updates automatically every day.
|
|
|
|
cat > /etc/apt/apt.conf.d/02periodic <<EOF;
|
|
APT::Periodic::MaxAge "7";
|
|
APT::Periodic::Update-Package-Lists "1";
|
|
APT::Periodic::Unattended-Upgrade "1";
|
|
APT::Periodic::Verbose "1";
|
|
EOF
|
|
|
|
# ### Firewall
|
|
|
|
# Various virtualized environments like Docker and some VPSs don't provide #NODOC
|
|
# a kernel that supports iptables. To avoid error-like output in these cases, #NODOC
|
|
# we skip this if the user sets DISABLE_FIREWALL=1. #NODOC
|
|
if [ -z "$DISABLE_FIREWALL" ]; then
|
|
# Install `ufw` which provides a simple firewall configuration.
|
|
apt_install ufw
|
|
|
|
# Allow incoming connections to SSH.
|
|
ufw_allow ssh;
|
|
|
|
# ssh might be running on an alternate port. Use sshd -T to dump sshd's #NODOC
|
|
# settings, find the port it is supposedly running on, and open that port #NODOC
|
|
# too. #NODOC
|
|
SSH_PORT=$(sshd -T 2>/dev/null | grep "^port " | sed "s/port //") #NODOC
|
|
if [ ! -z "$SSH_PORT" ]; then
|
|
if [ "$SSH_PORT" != "22" ]; then
|
|
|
|
echo Opening alternate SSH port $SSH_PORT. #NODOC
|
|
ufw_allow $SSH_PORT #NODOC
|
|
|
|
fi
|
|
fi
|
|
|
|
ufw --force enable;
|
|
fi #NODOC
|
|
|
|
# ### Local DNS Service
|
|
|
|
# Install a local DNS server, rather than using the DNS server provided by the
|
|
# ISP's network configuration.
|
|
#
|
|
# We do this to ensure that DNS queries
|
|
# that *we* make (i.e. looking up other external domains) perform DNSSEC checks.
|
|
# We could use Google's Public DNS, but we don't want to create a dependency on
|
|
# Google per our goals of decentralization. `bind9`, as packaged for Ubuntu, has
|
|
# DNSSEC enabled by default via "dnssec-validation auto".
|
|
#
|
|
# So we'll be running `bind9` bound to 127.0.0.1 for locally-issued DNS queries
|
|
# and `nsd` bound to the public ethernet interface for remote DNS queries asking
|
|
# about our domain names. `nsd` is configured later.
|
|
#
|
|
# About the settings:
|
|
#
|
|
# * RESOLVCONF=yes will have `bind9` take over /etc/resolv.conf to tell
|
|
# local services that DNS queries are handled on localhost.
|
|
# * Adding -4 to OPTIONS will have `bind9` not listen on IPv6 addresses
|
|
# so that we're sure there's no conflict with nsd, our public domain
|
|
# name server, on IPV6.
|
|
# * The listen-on directive in named.conf.options restricts `bind9` to
|
|
# binding to the loopback interface instead of all interfaces.
|
|
apt_install bind9 resolvconf
|
|
tools/editconf.py /etc/default/bind9 \
|
|
RESOLVCONF=yes \
|
|
"OPTIONS=\"-u bind -4\""
|
|
if ! grep -q "listen-on " /etc/bind/named.conf.options; then
|
|
# Add a listen-on directive if it doesn't exist inside the options block.
|
|
sed -i "s/^}/\n\tlisten-on { 127.0.0.1; };\n}/" /etc/bind/named.conf.options
|
|
fi
|
|
if [ -f /etc/resolvconf/resolv.conf.d/original ]; then
|
|
echo "Archiving old resolv.conf (was /etc/resolvconf/resolv.conf.d/original, now /etc/resolvconf/resolv.conf.original)." #NODOC
|
|
mv /etc/resolvconf/resolv.conf.d/original /etc/resolvconf/resolv.conf.original #NODOC
|
|
fi
|
|
|
|
# Restart the DNS services.
|
|
|
|
restart_service bind9
|
|
restart_service resolvconf
|
|
|
|
# ### Fail2Ban Service
|
|
|
|
# Configure the Fail2Ban installation to prevent dumb bruce-force attacks against dovecot, postfix, ssh, etc.
|
|
rm -f /etc/fail2ban/jail.local # we used to use this file but don't anymore
|
|
cat conf/fail2ban/jails.conf \
|
|
| sed "s/PUBLIC_IP/$PUBLIC_IP/g" \
|
|
| sed "s#STORAGE_ROOT#$STORAGE_ROOT#" \
|
|
> /etc/fail2ban/jail.d/mailinabox.conf
|
|
cp -f conf/fail2ban/filter.d/* /etc/fail2ban/filter.d/
|
|
|
|
# On first installation, the log files that the jails look at don't all exist.
|
|
# e.g., The roundcube error log isn't normally created until someone logs into
|
|
# Roundcube for the first time. This causes fail2ban to fail to start. Later
|
|
# scripts will ensure the files exist and then fail2ban is given another
|
|
# restart at the very end of setup.
|
|
restart_service fail2ban
|