diff --git a/management/daily_tasks.sh b/management/daily_tasks.sh index db496399..24a97245 100755 --- a/management/daily_tasks.sh +++ b/management/daily_tasks.sh @@ -11,7 +11,7 @@ export LC_TYPE=en_US.UTF-8 # On Mondays, i.e. once a week, send the administrator a report of total emails # sent and received so the admin might notice server abuse. -if [ `date "+%u"` -eq 1 ]; then +if [ "$(date "+%u")" -eq 1 ]; then management/mail_log.py -t week | management/email_administrator.py "Mail-in-a-Box Usage Report" fi diff --git a/setup/bootstrap.sh b/setup/bootstrap.sh index de373aff..58c9243e 100644 --- a/setup/bootstrap.sh +++ b/setup/bootstrap.sh @@ -51,9 +51,9 @@ if [[ $EUID -ne 0 ]]; then fi # Clone the Mail-in-a-Box repository if it doesn't exist. -if [ ! -d $HOME/mailinabox ]; then +if [ ! -d "$HOME/mailinabox" ]; then if [ ! -f /usr/bin/git ]; then - echo Installing git . . . + echo "Installing git . . ." apt-get -q -q update DEBIAN_FRONTEND=noninteractive apt-get -q -q install -y git < /dev/null echo @@ -63,25 +63,25 @@ if [ ! -d $HOME/mailinabox ]; then SOURCE=https://github.com/mail-in-a-box/mailinabox fi - echo Downloading Mail-in-a-Box $TAG. . . + echo "Downloading Mail-in-a-Box $TAG. . ." git clone \ - -b $TAG --depth 1 \ - $SOURCE \ - $HOME/mailinabox \ + -b "$TAG" --depth 1 \ + "$SOURCE" \ + "$HOME/mailinabox" \ < /dev/null 2> /dev/null echo fi # Change directory to it. -cd $HOME/mailinabox +cd "$HOME/mailinabox" || exit # Update it. -if [ "$TAG" != $(git describe --always) ]; then - echo Updating Mail-in-a-Box to $TAG . . . - git fetch --depth 1 --force --prune origin tag $TAG - if ! git checkout -q $TAG; then - echo "Update failed. Did you modify something in $(pwd)?" +if [ "$TAG" != "$(git describe --always)" ]; then + echo "Updating Mail-in-a-Box to $TAG . . ." + git fetch --depth 1 --force --prune origin tag "$TAG" + if ! git checkout -q "$TAG"; then + echo "Update failed. Did you modify something in $PWD?" exit 1 fi echo diff --git a/setup/dkim.sh b/setup/dkim.sh index d2d162a7..e4def11f 100755 --- a/setup/dkim.sh +++ b/setup/dkim.sh @@ -10,12 +10,12 @@ source setup/functions.sh # load our functions source /etc/mailinabox.conf # load global vars # Install DKIM... -echo Installing OpenDKIM/OpenDMARC... +echo "Installing OpenDKIM/OpenDMARC..." apt_install opendkim opendkim-tools opendmarc # Make sure configuration directories exist. mkdir -p /etc/opendkim; -mkdir -p $STORAGE_ROOT/mail/dkim +mkdir -p "$STORAGE_ROOT/mail/dkim" # Used in InternalHosts and ExternalIgnoreList configuration directives. # Not quite sure why. @@ -53,12 +53,12 @@ fi # such as Google. But they and others use a 2048 bit key, so we'll # do the same. Keys beyond 2048 bits may exceed DNS record limits. if [ ! -f "$STORAGE_ROOT/mail/dkim/mail.private" ]; then - opendkim-genkey -b 2048 -r -s mail -D $STORAGE_ROOT/mail/dkim + opendkim-genkey -b 2048 -r -s mail -D "$STORAGE_ROOT/mail/dkim" fi # Ensure files are owned by the opendkim user and are private otherwise. -chown -R opendkim:opendkim $STORAGE_ROOT/mail/dkim -chmod go-rwx $STORAGE_ROOT/mail/dkim +chown -R opendkim:opendkim "$STORAGE_ROOT/mail/dkim" +chmod go-rwx "$STORAGE_ROOT/mail/dkim" tools/editconf.py /etc/opendmarc.conf -s \ "Syslog=true" \ diff --git a/setup/dns.sh b/setup/dns.sh index 9b9b1b0a..a94e890d 100755 --- a/setup/dns.sh +++ b/setup/dns.sh @@ -106,7 +106,7 @@ if [ ! -f "$STORAGE_ROOT/dns/dnssec/$algo.conf" ]; then # (This previously used -b 2048 but it's unclear if this setting makes sense # for non-RSA keys, so it's removed. The RSA-based keys are not recommended # anymore anyway.) - KSK=$(umask 077; cd $STORAGE_ROOT/dns/dnssec; ldns-keygen -r /dev/urandom -a $algo -k _domain_); + KSK=$(umask 077; cd "$STORAGE_ROOT/dns/dnssec"; ldns-keygen -r /dev/urandom -a $algo -k _domain_); # Now create a Zone-Signing Key (ZSK) which is expected to be # rotated more often than a KSK, although we have no plans to @@ -114,7 +114,7 @@ if [ ! -f "$STORAGE_ROOT/dns/dnssec/$algo.conf" ]; then # disturbing DNS availability.) Omit `-k`. # (This previously used -b 1024 but it's unclear if this setting makes sense # for non-RSA keys, so it's removed.) - ZSK=$(umask 077; cd $STORAGE_ROOT/dns/dnssec; ldns-keygen -r /dev/urandom -a $algo _domain_); + ZSK=$(umask 077; cd "$STORAGE_ROOT/dns/dnssec"; ldns-keygen -r /dev/urandom -a $algo _domain_); # These generate two sets of files like: # @@ -126,7 +126,7 @@ if [ ! -f "$STORAGE_ROOT/dns/dnssec/$algo.conf" ]; then # options. So we'll store the names of the files we just generated. # We might have multiple keys down the road. This will identify # what keys are the current keys. - cat > $STORAGE_ROOT/dns/dnssec/$algo.conf << EOF; + cat > "$STORAGE_ROOT/dns/dnssec/$algo.conf" << EOF; KSK=$KSK ZSK=$ZSK EOF @@ -142,7 +142,7 @@ cat > /etc/cron.daily/mailinabox-dnssec << EOF; #!/bin/bash # Mail-in-a-Box # Re-sign any DNS zones with DNSSEC because the signatures expire periodically. -$(pwd)/tools/dns_update +$PWD/tools/dns_update EOF chmod +x /etc/cron.daily/mailinabox-dnssec diff --git a/setup/firstuser.sh b/setup/firstuser.sh index 7caec35d..76514222 100644 --- a/setup/firstuser.sh +++ b/setup/firstuser.sh @@ -1,3 +1,4 @@ +#!/bin/bash # If there aren't any mail users yet, create one. if [ -z "$(management/cli.py user)" ]; then # The outut of "management/cli.py user" is a list of mail users. If there @@ -10,7 +11,7 @@ if [ -z "$(management/cli.py user)" ]; then input_box "Mail Account" \ "Let's create your first mail account. \n\nWhat email address do you want?" \ - me@$(get_default_hostname) \ + "me@$(get_default_hostname)" \ EMAIL_ADDR if [ -z "$EMAIL_ADDR" ]; then @@ -22,7 +23,7 @@ if [ -z "$(management/cli.py user)" ]; then input_box "Mail Account" \ "That's not a valid email address. \n\nWhat email address do you want?" \ - $EMAIL_ADDR \ + "$EMAIL_ADDR" \ EMAIL_ADDR if [ -z "$EMAIL_ADDR" ]; then # user hit ESC/cancel @@ -47,11 +48,11 @@ if [ -z "$(management/cli.py user)" ]; then fi # Create the user's mail account. This will ask for a password if none was given above. - management/cli.py user add $EMAIL_ADDR ${EMAIL_PW:-} + management/cli.py user add "$EMAIL_ADDR" "${EMAIL_PW:-}" # Make it an admin. - hide_output management/cli.py user make-admin $EMAIL_ADDR + hide_output management/cli.py user make-admin "$EMAIL_ADDR" # Create an alias to which we'll direct all automatically-created administrative aliases. - management/cli.py alias add administrator@$PRIMARY_HOSTNAME $EMAIL_ADDR > /dev/null + management/cli.py alias add "administrator@$PRIMARY_HOSTNAME" "$EMAIL_ADDR" > /dev/null fi diff --git a/setup/functions.sh b/setup/functions.sh index 151c5f40..6a96cc1f 100644 --- a/setup/functions.sh +++ b/setup/functions.sh @@ -1,3 +1,4 @@ +#!/bin/bash # Turn on "strict mode." See http://redsymbol.net/articles/unofficial-bash-strict-mode/. # -e: exit if any command unexpectedly fails. # -u: exit if we have a variable typo. @@ -16,7 +17,7 @@ function hide_output { # Execute command, redirecting stderr/stdout to the temporary file. Since we # check the return code ourselves, disable 'set -e' temporarily. set +e - "$@" &> $OUTPUT + "$@" &> "$OUTPUT" E=$? set -e @@ -24,15 +25,15 @@ function hide_output { if [ $E != 0 ]; then # Something failed. echo - echo FAILED: "$@" + echo "FAILED: $*" echo ----------------------------------------- - cat $OUTPUT + cat "$OUTPUT" echo ----------------------------------------- exit $E fi # Remove temporary file. - rm -f $OUTPUT + rm -f "$OUTPUT" } function apt_get_quiet { @@ -62,9 +63,9 @@ function get_default_hostname { # Guess the machine's hostname. It should be a fully qualified # domain name suitable for DNS. None of these calls may provide # the right value, but it's the best guess we can make. - set -- $(hostname --fqdn 2>/dev/null || + set -- "$(hostname --fqdn 2>/dev/null || hostname --all-fqdns 2>/dev/null || - hostname 2>/dev/null) + hostname 2>/dev/null)" printf '%s\n' "$1" # return this value } @@ -76,7 +77,7 @@ function get_publicip_from_web_service { # # Pass '4' or '6' as an argument to this function to specify # what type of address to get (IPv4, IPv6). - curl -$1 --fail --silent --max-time 15 icanhazip.com 2>/dev/null || /bin/true + curl -"$1" --fail --silent --max-time 15 icanhazip.com 2>/dev/null || /bin/true } function get_default_privateip { @@ -119,19 +120,19 @@ function get_default_privateip { if [ "$1" == "6" ]; then target=2001:4860:4860::8888; fi # Get the route information. - route=$(ip -$1 -o route get $target 2>/dev/null | grep -v unreachable) + route=$(ip -"$1" -o route get $target 2>/dev/null | grep -v unreachable) # Parse the address out of the route information. - address=$(echo $route | sed "s/.* src \([^ ]*\).*/\1/") + address=$(echo "$route" | sed "s/.* src \([^ ]*\).*/\1/") if [[ "$1" == "6" && $address == fe80:* ]]; then # For IPv6 link-local addresses, parse the interface out # of the route information and append it with a '%'. - interface=$(echo $route | sed "s/.* dev \([^ ]*\).*/\1/") + interface=$(echo "$route" | sed "s/.* dev \([^ ]*\).*/\1/") address=$address%$interface fi - echo $address + echo "$address" } function ufw_allow { @@ -149,7 +150,7 @@ function ufw_limit { } function restart_service { - hide_output service $1 restart + hide_output service "$1" restart } ## Dialog Functions ## @@ -178,7 +179,7 @@ function input_menu { declare -n result_code=$4_EXITCODE local IFS=^$'\n' set +e - result=$(dialog --stdout --title "$1" --menu "$2" 0 0 0 $3) + result=$(dialog --stdout --title "$1" --menu "$2" 0 0 0 "$3") result_code=$? set -e } @@ -190,17 +191,17 @@ function wget_verify { HASH=$2 DEST=$3 CHECKSUM="$HASH $DEST" - rm -f $DEST - hide_output wget -O $DEST $URL + rm -f "$DEST" + hide_output wget -O "$DEST" "$URL" if ! echo "$CHECKSUM" | sha1sum --check --strict > /dev/null; then echo "------------------------------------------------------------" echo "Download of $URL did not match expected checksum." echo "Found:" - sha1sum $DEST + sha1sum "$DEST" echo echo "Expected:" echo "$CHECKSUM" - rm -f $DEST + rm -f "$DEST" exit 1 fi } @@ -216,9 +217,9 @@ function git_clone { SUBDIR=$3 TARGETPATH=$4 TMPPATH=/tmp/git-clone-$$ - rm -rf $TMPPATH $TARGETPATH - git clone -q $REPO $TMPPATH || exit 1 - (cd $TMPPATH; git checkout -q $TREEISH;) || exit 1 - mv $TMPPATH/$SUBDIR $TARGETPATH + rm -rf $TMPPATH "$TARGETPATH" + git clone -q "$REPO" $TMPPATH || exit 1 + (cd $TMPPATH; git checkout -q "$TREEISH";) || exit 1 + mv $TMPPATH/"$SUBDIR" "$TARGETPATH" rm -rf $TMPPATH } diff --git a/setup/mail-dovecot.sh b/setup/mail-dovecot.sh index 8d45a50b..b146e44a 100755 --- a/setup/mail-dovecot.sh +++ b/setup/mail-dovecot.sh @@ -45,8 +45,8 @@ apt_install \ # - https://www.dovecot.org/list/dovecot/2012-August/137569.html # - https://www.dovecot.org/list/dovecot/2011-December/132455.html tools/editconf.py /etc/dovecot/conf.d/10-master.conf \ - default_process_limit=$(echo "$(nproc) * 250" | bc) \ - default_vsz_limit=$(echo "$(free -tm | tail -1 | awk '{print $2}') / 3" | bc)M \ + default_process_limit="$(($(nproc) * 250))" \ + default_vsz_limit="$(($(free -tm | tail -1 | awk '{print $2}') / 3))M" \ log_path=/var/log/mail.log # The inotify `max_user_instances` default is 128, which constrains @@ -61,7 +61,7 @@ tools/editconf.py /etc/sysctl.conf \ # username part of the user's email address. We'll ensure that no bad domains or email addresses # are created within the management daemon. tools/editconf.py /etc/dovecot/conf.d/10-mail.conf \ - mail_location=maildir:$STORAGE_ROOT/mail/mailboxes/%d/%n \ + mail_location="maildir:$STORAGE_ROOT/mail/mailboxes/%d/%n" \ mail_privileged_group=mail \ first_valid_uid=0 @@ -152,7 +152,7 @@ EOF # Setting a `postmaster_address` is required or LMTP won't start. An alias # will be created automatically by our management daemon. tools/editconf.py /etc/dovecot/conf.d/15-lda.conf \ - postmaster_address=postmaster@$PRIMARY_HOSTNAME + "postmaster_address=postmaster@$PRIMARY_HOSTNAME" # ### Sieve @@ -201,14 +201,14 @@ chown -R mail:dovecot /etc/dovecot chmod -R o-rwx /etc/dovecot # Ensure mailbox files have a directory that exists and are owned by the mail user. -mkdir -p $STORAGE_ROOT/mail/mailboxes -chown -R mail:mail $STORAGE_ROOT/mail/mailboxes +mkdir -p "$STORAGE_ROOT/mail/mailboxes" +chown -R mail:mail "$STORAGE_ROOT/mail/mailboxes" # Same for the sieve scripts. -mkdir -p $STORAGE_ROOT/mail/sieve -mkdir -p $STORAGE_ROOT/mail/sieve/global_before -mkdir -p $STORAGE_ROOT/mail/sieve/global_after -chown -R mail:mail $STORAGE_ROOT/mail/sieve +mkdir -p "$STORAGE_ROOT/mail/sieve" +mkdir -p "$STORAGE_ROOT/mail/sieve/global_before" +mkdir -p "$STORAGE_ROOT/mail/sieve/global_after" +chown -R mail:mail "$STORAGE_ROOT/mail/sieve" # Allow the IMAP/POP ports in the firewall. ufw_allow imaps diff --git a/setup/mail-postfix.sh b/setup/mail-postfix.sh index 24969513..49dfaafa 100755 --- a/setup/mail-postfix.sh +++ b/setup/mail-postfix.sh @@ -55,9 +55,9 @@ apt_install postfix postfix-sqlite postfix-pcre postgrey ca-certificates # * Set the SMTP banner (which must have the hostname first, then anything). tools/editconf.py /etc/postfix/main.cf \ inet_interfaces=all \ - smtp_bind_address=$PRIVATE_IP \ - smtp_bind_address6=$PRIVATE_IPV6 \ - myhostname=$PRIMARY_HOSTNAME\ + smtp_bind_address="$PRIVATE_IP" \ + smtp_bind_address6="$PRIVATE_IPV6" \ + myhostname="$PRIMARY_HOSTNAME"\ smtpd_banner="\$myhostname ESMTP Hi, I'm a Mail-in-a-Box (Ubuntu/Postfix; see https://mailinabox.email/)" \ mydestination=localhost @@ -138,9 +138,9 @@ sed -i "s/PUBLIC_IP/$PUBLIC_IP/" /etc/postfix/outgoing_mail_header_filters tools/editconf.py /etc/postfix/main.cf \ smtpd_tls_security_level=may\ smtpd_tls_auth_only=yes \ - smtpd_tls_cert_file=$STORAGE_ROOT/ssl/ssl_certificate.pem \ - smtpd_tls_key_file=$STORAGE_ROOT/ssl/ssl_private_key.pem \ - smtpd_tls_dh1024_param_file=$STORAGE_ROOT/ssl/dh2048.pem \ + smtpd_tls_cert_file="$STORAGE_ROOT/ssl/ssl_certificate.pem" \ + smtpd_tls_key_file="$STORAGE_ROOT/ssl/ssl_private_key.pem" \ + smtpd_tls_dh1024_param_file="$STORAGE_ROOT/ssl/dh2048.pem" \ smtpd_tls_protocols="!SSLv2,!SSLv3" \ smtpd_tls_ciphers=medium \ tls_medium_cipherlist=ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA \ @@ -260,17 +260,17 @@ tools/editconf.py /etc/default/postgrey \ # If the $STORAGE_ROOT/mail/postgrey is empty, copy the postgrey database over from the old location -if [ ! -d $STORAGE_ROOT/mail/postgrey/db ]; then +if [ ! -d "$STORAGE_ROOT/mail/postgrey/db" ]; then # Stop the service service postgrey stop # Ensure the new paths for postgrey db exists - mkdir -p $STORAGE_ROOT/mail/postgrey/db + mkdir -p "$STORAGE_ROOT/mail/postgrey/db" # Move over database files - mv /var/lib/postgrey/* $STORAGE_ROOT/mail/postgrey/db/ || true + mv /var/lib/postgrey/* "$STORAGE_ROOT/mail/postgrey/db/" || true fi # Ensure permissions are set -chown -R postgrey:postgrey $STORAGE_ROOT/mail/postgrey/ -chmod 700 $STORAGE_ROOT/mail/postgrey/{,db} +chown -R postgrey:postgrey "$STORAGE_ROOT/mail/postgrey/" +chmod 700 "$STORAGE_ROOT/mail/postgrey/"{,db} # We are going to setup a newer whitelist for postgrey, the version included in the distribution is old cat > /etc/cron.daily/mailinabox-postgrey-whitelist << EOF; diff --git a/setup/mail-users.sh b/setup/mail-users.sh index b570f037..0c6be0c8 100755 --- a/setup/mail-users.sh +++ b/setup/mail-users.sh @@ -18,12 +18,12 @@ source /etc/mailinabox.conf # load global vars db_path=$STORAGE_ROOT/mail/users.sqlite # Create an empty database if it doesn't yet exist. -if [ ! -f $db_path ]; then - echo Creating new user database: $db_path; - echo "CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT NOT NULL UNIQUE, password TEXT NOT NULL, extra, privileges TEXT NOT NULL DEFAULT '');" | sqlite3 $db_path; - echo "CREATE TABLE aliases (id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT NOT NULL UNIQUE, destination TEXT NOT NULL, permitted_senders TEXT);" | sqlite3 $db_path; - echo "CREATE TABLE mfa (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, type TEXT NOT NULL, secret TEXT NOT NULL, mru_token TEXT, label TEXT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE);" | sqlite3 $db_path; - echo "CREATE TABLE auto_aliases (id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT NOT NULL UNIQUE, destination TEXT NOT NULL, permitted_senders TEXT);" | sqlite3 $db_path; +if [ ! -f "$db_path" ]; then + echo "Creating new user database: $db_path"; + echo "CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT NOT NULL UNIQUE, password TEXT NOT NULL, extra, privileges TEXT NOT NULL DEFAULT '');" | sqlite3 "$db_path"; + echo "CREATE TABLE aliases (id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT NOT NULL UNIQUE, destination TEXT NOT NULL, permitted_senders TEXT);" | sqlite3 "$db_path"; + echo "CREATE TABLE mfa (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, type TEXT NOT NULL, secret TEXT NOT NULL, mru_token TEXT, label TEXT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE);" | sqlite3 "$db_path"; + echo "CREATE TABLE auto_aliases (id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT NOT NULL UNIQUE, destination TEXT NOT NULL, permitted_senders TEXT);" | sqlite3 "$db_path"; fi # ### User Authentication diff --git a/setup/management.sh b/setup/management.sh index 34c0aeaf..7fd844ea 100755 --- a/setup/management.sh +++ b/setup/management.sh @@ -52,9 +52,9 @@ hide_output $venv/bin/pip install --upgrade \ # CONFIGURATION # Create a backup directory and a random key for encrypting backups. -mkdir -p $STORAGE_ROOT/backup -if [ ! -f $STORAGE_ROOT/backup/secret_key.txt ]; then - $(umask 077; openssl rand -base64 2048 > $STORAGE_ROOT/backup/secret_key.txt) +mkdir -p "$STORAGE_ROOT/backup" +if [ ! -f "$STORAGE_ROOT/backup/secret_key.txt" ]; then + umask 077; openssl rand -base64 2048 > "$STORAGE_ROOT/backup/secret_key.txt" fi @@ -100,7 +100,7 @@ tr -cd '[:xdigit:]' < /dev/urandom | head -c 32 > /var/lib/mailinabox/api.key chmod 640 /var/lib/mailinabox/api.key source $venv/bin/activate -export PYTHONPATH=$(pwd)/management +export PYTHONPATH=$PWD/management exec gunicorn -b localhost:10222 -w 1 --timeout 630 wsgi:app EOF chmod +x $inst_dir/start @@ -116,7 +116,7 @@ minute=$((RANDOM % 60)) # avoid overloading mailinabox.email cat > /etc/cron.d/mailinabox-nightly << EOF; # Mail-in-a-Box --- Do not edit / will be overwritten on update. # Run nightly tasks: backup, status checks. -$minute 3 * * * root (cd $(pwd) && management/daily_tasks.sh) +$minute 3 * * * root (cd $PWD && management/daily_tasks.sh) EOF # Start the management server. diff --git a/setup/munin.sh b/setup/munin.sh index 90f93521..017862de 100755 --- a/setup/munin.sh +++ b/setup/munin.sh @@ -40,7 +40,7 @@ chown munin /var/log/munin/munin-cgi-graph.log # ensure munin-node knows the name of this machine # and reduce logging level to warning tools/editconf.py /etc/munin/munin-node.conf -s \ - host_name=$PRIMARY_HOSTNAME \ + host_name="$PRIMARY_HOSTNAME" \ log_level=1 # Update the activated plugins through munin's autoconfiguration. @@ -52,9 +52,9 @@ find /etc/munin/plugins/ -lname /usr/share/munin/plugins/ntp_ -print0 | xargs -0 # Deactivate monitoring of network interfaces that are not up. Otherwise we can get a lot of empty charts. for f in $(find /etc/munin/plugins/ \( -lname /usr/share/munin/plugins/if_ -o -lname /usr/share/munin/plugins/if_err_ -o -lname /usr/share/munin/plugins/bonding_err_ \)); do - IF=$(echo $f | sed s/.*_//); - if ! grep -qFx up /sys/class/net/$IF/operstate 2>/dev/null; then - rm $f; + IF=$(echo "$f" | sed s/.*_//); + if ! grep -qFx up "/sys/class/net/$IF/operstate" 2>/dev/null; then + rm "$f"; fi; done @@ -62,7 +62,7 @@ done mkdir -p /var/lib/munin-node/plugin-state/ # Create a systemd service for munin. -ln -sf $(pwd)/management/munin_start.sh /usr/local/lib/mailinabox/munin_start.sh +ln -sf "$PWD/management/munin_start.sh" /usr/local/lib/mailinabox/munin_start.sh chmod 0744 /usr/local/lib/mailinabox/munin_start.sh cp --remove-destination conf/munin.service /lib/systemd/system/munin.service # target was previously a symlink so remove first hide_output systemctl link -f /lib/systemd/system/munin.service diff --git a/setup/network-checks.sh b/setup/network-checks.sh index 428fa4ca..16b9a175 100644 --- a/setup/network-checks.sh +++ b/setup/network-checks.sh @@ -1,3 +1,4 @@ +#!/bin/bash # Install the 'host', 'sed', and and 'nc' tools. This script is run before # the rest of the system setup so we may not yet have things installed. apt_get_quiet install bind9-host sed netcat-openbsd @@ -6,7 +7,7 @@ apt_get_quiet install bind9-host sed netcat-openbsd # The user might have chosen a name that was previously in use by a spammer # and will not be able to reliably send mail. Do this after any automatic # choices made above. -if host $PRIMARY_HOSTNAME.dbl.spamhaus.org > /dev/null; then +if host "$PRIMARY_HOSTNAME.dbl.spamhaus.org" > /dev/null; then echo echo "The hostname you chose '$PRIMARY_HOSTNAME' is listed in the" echo "Spamhaus Domain Block List. See http://www.spamhaus.org/dbl/" @@ -22,8 +23,8 @@ fi # The user might have ended up on an IP address that was previously in use # by a spammer, or the user may be deploying on a residential network. We # will not be able to reliably send mail in these cases. -REVERSED_IPV4=$(echo $PUBLIC_IP | sed "s/\([0-9]*\).\([0-9]*\).\([0-9]*\).\([0-9]*\)/\4.\3.\2.\1/") -if host $REVERSED_IPV4.zen.spamhaus.org > /dev/null; then +REVERSED_IPV4=$(echo "$PUBLIC_IP" | sed "s/\([0-9]*\).\([0-9]*\).\([0-9]*\).\([0-9]*\)/\4.\3.\2.\1/") +if host "$REVERSED_IPV4.zen.spamhaus.org" > /dev/null; then echo echo "The IP address $PUBLIC_IP is listed in the Spamhaus Block List." echo "See http://www.spamhaus.org/query/ip/$PUBLIC_IP." diff --git a/setup/nextcloud.sh b/setup/nextcloud.sh index 947d2f9a..1fb19ade 100755 --- a/setup/nextcloud.sh +++ b/setup/nextcloud.sh @@ -65,13 +65,13 @@ user_external_hash=a494073dcdecbbbc79a9c77f72524ac9994d2eec # Clear prior packages and install dependencies from apt. apt-get purge -qq -y owncloud* # we used to use the package manager -apt_install curl php${PHP_VER} php${PHP_VER}-fpm \ - php${PHP_VER}-cli php${PHP_VER}-sqlite3 php${PHP_VER}-gd php${PHP_VER}-imap php${PHP_VER}-curl \ - php${PHP_VER}-dev php${PHP_VER}-gd php${PHP_VER}-xml php${PHP_VER}-mbstring php${PHP_VER}-zip php${PHP_VER}-apcu \ - php${PHP_VER}-intl php${PHP_VER}-imagick php${PHP_VER}-gmp php${PHP_VER}-bcmath +apt_install curl php"${PHP_VER}" php"${PHP_VER}"-fpm \ + php"${PHP_VER}"-cli php"${PHP_VER}"-sqlite3 php"${PHP_VER}"-gd php"${PHP_VER}"-imap php"${PHP_VER}"-curl \ + php"${PHP_VER}"-dev php"${PHP_VER}"-gd php"${PHP_VER}"-xml php"${PHP_VER}"-mbstring php"${PHP_VER}"-zip php"${PHP_VER}"-apcu \ + php"${PHP_VER}"-intl php"${PHP_VER}"-imagick php"${PHP_VER}"-gmp php"${PHP_VER}"-bcmath # Enable APC before Nextcloud tools are run. -tools/editconf.py /etc/php/$PHP_VER/mods-available/apcu.ini -c ';' \ +tools/editconf.py /etc/php/"$PHP_VER"/mods-available/apcu.ini -c ';' \ apc.enabled=1 \ apc.enable_cli=1 @@ -91,7 +91,7 @@ InstallNextcloud() { echo # Download and verify - wget_verify https://download.nextcloud.com/server/releases/nextcloud-$version.zip $hash /tmp/nextcloud.zip + wget_verify "https://download.nextcloud.com/server/releases/nextcloud-$version.zip" "$hash" /tmp/nextcloud.zip # Remove the current owncloud/Nextcloud rm -rf /usr/local/lib/owncloud @@ -105,18 +105,18 @@ InstallNextcloud() { # their github repositories. mkdir -p /usr/local/lib/owncloud/apps - wget_verify https://github.com/nextcloud-releases/contacts/archive/refs/tags/v$version_contacts.tar.gz $hash_contacts /tmp/contacts.tgz + wget_verify "https://github.com/nextcloud-releases/contacts/archive/refs/tags/v$version_contacts.tar.gz" "$hash_contacts" /tmp/contacts.tgz tar xf /tmp/contacts.tgz -C /usr/local/lib/owncloud/apps/ rm /tmp/contacts.tgz - wget_verify https://github.com/nextcloud-releases/calendar/archive/refs/tags/v$version_calendar.tar.gz $hash_calendar /tmp/calendar.tgz + wget_verify "https://github.com/nextcloud-releases/calendar/archive/refs/tags/v$version_calendar.tar.gz" "$hash_calendar" /tmp/calendar.tgz tar xf /tmp/calendar.tgz -C /usr/local/lib/owncloud/apps/ rm /tmp/calendar.tgz # Starting with Nextcloud 15, the app user_external is no longer included in Nextcloud core, # we will install from their github repository. if [ -n "$version_user_external" ]; then - wget_verify https://github.com/nextcloud-releases/user_external/releases/download/v$version_user_external/user_external-v$version_user_external.tar.gz $hash_user_external /tmp/user_external.tgz + wget_verify "https://github.com/nextcloud-releases/user_external/releases/download/v$version_user_external/user_external-v$version_user_external.tar.gz" "$hash_user_external" /tmp/user_external.tgz tar -xf /tmp/user_external.tgz -C /usr/local/lib/owncloud/apps/ rm /tmp/user_external.tgz fi @@ -126,33 +126,35 @@ InstallNextcloud() { # Create a symlink to the config.php in STORAGE_ROOT (for upgrades we're restoring the symlink we previously # put in, and in new installs we're creating a symlink and will create the actual config later). - ln -sf $STORAGE_ROOT/owncloud/config.php /usr/local/lib/owncloud/config/config.php + ln -sf "$STORAGE_ROOT/owncloud/config.php" /usr/local/lib/owncloud/config/config.php # Make sure permissions are correct or the upgrade step won't run. # $STORAGE_ROOT/owncloud may not yet exist, so use -f to suppress # that error. - chown -f -R www-data:www-data $STORAGE_ROOT/owncloud /usr/local/lib/owncloud || /bin/true + chown -f -R www-data:www-data "$STORAGE_ROOT/owncloud /usr/local/lib/owncloud" || /bin/true # If this isn't a new installation, immediately run the upgrade script. # Then check for success (0=ok and 3=no upgrade needed, both are success). - if [ -e $STORAGE_ROOT/owncloud/owncloud.db ]; then + if [ -e "$STORAGE_ROOT/owncloud/owncloud.db" ]; then # ownCloud 8.1.1 broke upgrades. It may fail on the first attempt, but # that can be OK. - sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ upgrade - if [ \( $? -ne 0 \) -a \( $? -ne 3 \) ]; then + sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/occ upgrade + E=$? + if [ $E -ne 0 ] && [ $E -ne 3 ]; then echo "Trying ownCloud upgrade again to work around ownCloud upgrade bug..." - sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ upgrade - if [ \( $? -ne 0 \) -a \( $? -ne 3 \) ]; then exit 1; fi - sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ maintenance:mode --off + sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/occ upgrade + E=$? + if [ $E -ne 0 ] && [ $E -ne 3 ]; then exit 1; fi + sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/occ maintenance:mode --off echo "...which seemed to work." fi # Add missing indices. NextCloud didn't include this in the normal upgrade because it might take some time. - sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ db:add-missing-indices - sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ db:add-missing-primary-keys + sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/occ db:add-missing-indices + sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/occ db:add-missing-primary-keys # Run conversion to BigInt identifiers, this process may take some time on large tables. - sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ db:convert-filecache-bigint --no-interaction + sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/occ db:convert-filecache-bigint --no-interaction fi } @@ -164,7 +166,7 @@ InstallNextcloud() { # If config.php exists, get version number, otherwise CURRENT_NEXTCLOUD_VER is empty. if [ -f "$STORAGE_ROOT/owncloud/config.php" ]; then - CURRENT_NEXTCLOUD_VER=$(php$PHP_VER -r "include(\"$STORAGE_ROOT/owncloud/config.php\"); echo(\$CONFIG['version']);") + CURRENT_NEXTCLOUD_VER=$(php"$PHP_VER" -r "include(\"$STORAGE_ROOT/owncloud/config.php\"); echo(\$CONFIG['version']);") else CURRENT_NEXTCLOUD_VER="" fi @@ -174,7 +176,7 @@ fi if [ ! -d /usr/local/lib/owncloud/ ] || [[ ! ${CURRENT_NEXTCLOUD_VER} =~ ^$nextcloud_ver ]]; then # Stop php-fpm if running. If they are not running (which happens on a previously failed install), dont bail. - service php$PHP_VER-fpm stop &> /dev/null || /bin/true + service php"$PHP_VER"-fpm stop &> /dev/null || /bin/true # Backup the existing ownCloud/Nextcloud. # Create a backup directory to store the current installation and database to @@ -184,21 +186,21 @@ if [ ! -d /usr/local/lib/owncloud/ ] || [[ ! ${CURRENT_NEXTCLOUD_VER} =~ ^$nextc echo "Upgrading Nextcloud --- backing up existing installation, configuration, and database to directory to $BACKUP_DIRECTORY..." cp -r /usr/local/lib/owncloud "$BACKUP_DIRECTORY/owncloud-install" fi - if [ -e $STORAGE_ROOT/owncloud/owncloud.db ]; then - cp $STORAGE_ROOT/owncloud/owncloud.db $BACKUP_DIRECTORY + if [ -e "$STORAGE_ROOT/owncloud/owncloud.db" ]; then + cp "$STORAGE_ROOT/owncloud/owncloud.db" "$BACKUP_DIRECTORY" fi - if [ -e $STORAGE_ROOT/owncloud/config.php ]; then - cp $STORAGE_ROOT/owncloud/config.php $BACKUP_DIRECTORY + if [ -e "$STORAGE_ROOT/owncloud/config.php" ]; then + cp "$STORAGE_ROOT/owncloud/config.php" "$BACKUP_DIRECTORY" fi # If ownCloud or Nextcloud was previously installed.... - if [ ! -z ${CURRENT_NEXTCLOUD_VER} ]; then + if [ -n "${CURRENT_NEXTCLOUD_VER}" ]; then # Database migrations from ownCloud are no longer possible because ownCloud cannot be run under # PHP 7. - if [ -e $STORAGE_ROOT/owncloud/config.php ]; then + if [ -e "$STORAGE_ROOT/owncloud/config.php" ]; then # Remove the read-onlyness of the config, which is needed for migrations, especially for v24 - sed -i -e '/config_is_read_only/d' $STORAGE_ROOT/owncloud/config.php + sed -i -e '/config_is_read_only/d' "$STORAGE_ROOT/owncloud/config.php" fi if [[ ${CURRENT_NEXTCLOUD_VER} =~ ^[89] ]]; then @@ -246,13 +248,13 @@ fi # Setup Nextcloud if the Nextcloud database does not yet exist. Running setup when # the database does exist wipes the database and user data. -if [ ! -f $STORAGE_ROOT/owncloud/owncloud.db ]; then +if [ ! -f "$STORAGE_ROOT/owncloud/owncloud.db" ]; then # Create user data directory - mkdir -p $STORAGE_ROOT/owncloud + mkdir -p "$STORAGE_ROOT/owncloud" # Create an initial configuration file. - instanceid=oc$(echo $PRIMARY_HOSTNAME | sha1sum | fold -w 10 | head -n 1) - cat > $STORAGE_ROOT/owncloud/config.php < "$STORAGE_ROOT/owncloud/config.php" < '$STORAGE_ROOT/owncloud', @@ -305,12 +307,12 @@ EOF EOF # Set permissions - chown -R www-data:www-data $STORAGE_ROOT/owncloud /usr/local/lib/owncloud + chown -R www-data:www-data "$STORAGE_ROOT/owncloud" /usr/local/lib/owncloud # Execute Nextcloud's setup step, which creates the Nextcloud sqlite database. # It also wipes it if it exists. And it updates config.php with database # settings and deletes the autoconfig.php file. - (cd /usr/local/lib/owncloud; sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/index.php;) + (cd /usr/local/lib/owncloud || exit; sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/index.php;) fi # Update config.php. @@ -326,7 +328,7 @@ fi # Use PHP to read the settings file, modify it, and write out the new settings array. TIMEZONE=$(cat /etc/timezone) CONFIG_TEMP=$(/bin/mktemp) -php$PHP_VER < $CONFIG_TEMP && mv $CONFIG_TEMP $STORAGE_ROOT/owncloud/config.php; +php"$PHP_VER" < "$CONFIG_TEMP" && mv "$CONFIG_TEMP" "$STORAGE_ROOT/owncloud/config.php"; EOF -chown www-data:www-data $STORAGE_ROOT/owncloud/config.php +chown www-data:www-data "$STORAGE_ROOT/owncloud/config.php" # Enable/disable apps. Note that this must be done after the Nextcloud setup. # The firstrunwizard gave Josh all sorts of problems, so disabling that. # user_external is what allows Nextcloud to use IMAP for login. The contacts # and calendar apps are the extensions we really care about here. -hide_output sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/console.php app:disable firstrunwizard -hide_output sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/console.php app:enable user_external -hide_output sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/console.php app:enable contacts -hide_output sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/console.php app:enable calendar +hide_output sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/console.php app:disable firstrunwizard +hide_output sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/console.php app:enable user_external +hide_output sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/console.php app:enable contacts +hide_output sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/console.php app:enable calendar # When upgrading, run the upgrade script again now that apps are enabled. It seems like # the first upgrade at the top won't work because apps may be disabled during upgrade? # Check for success (0=ok, 3=no upgrade needed). -sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ upgrade -if [ \( $? -ne 0 \) -a \( $? -ne 3 \) ]; then exit 1; fi +sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/occ upgrade +E=$? +if [ $E -ne 0 ] && [ $E -ne 3 ]; then exit 1; fi # Disable default apps that we don't support sudo -u www-data \ - php$PHP_VER /usr/local/lib/owncloud/occ app:disable photos dashboard activity \ + php"$PHP_VER" /usr/local/lib/owncloud/occ app:disable photos dashboard activity \ | (grep -v "No such app enabled" || /bin/true) # Set PHP FPM values to support large file uploads # (semicolon is the comment character in this file, hashes produce deprecation warnings) -tools/editconf.py /etc/php/$PHP_VER/fpm/php.ini -c ';' \ +tools/editconf.py /etc/php/"$PHP_VER"/fpm/php.ini -c ';' \ upload_max_filesize=16G \ post_max_size=16G \ output_buffering=16384 \ @@ -390,7 +393,7 @@ tools/editconf.py /etc/php/$PHP_VER/fpm/php.ini -c ';' \ short_open_tag=On # Set Nextcloud recommended opcache settings -tools/editconf.py /etc/php/$PHP_VER/cli/conf.d/10-opcache.ini -c ';' \ +tools/editconf.py /etc/php/"$PHP_VER"/cli/conf.d/10-opcache.ini -c ';' \ opcache.enable=1 \ opcache.enable_cli=1 \ opcache.interned_strings_buffer=8 \ @@ -404,7 +407,7 @@ tools/editconf.py /etc/php/$PHP_VER/cli/conf.d/10-opcache.ini -c ';' \ # This version was probably in use in Mail-in-a-Box v0.41 (February 26, 2019) and earlier. # We moved to v0.6.3 in 193763f8. Ignore errors - maybe there are duplicated users with the # correct backend already. -sqlite3 $STORAGE_ROOT/owncloud/owncloud.db "UPDATE oc_users_external SET backend='127.0.0.1';" || /bin/true +sqlite3 "$STORAGE_ROOT/owncloud/owncloud.db" "UPDATE oc_users_external SET backend='127.0.0.1';" || /bin/true # Set up a general cron job for Nextcloud. # Also add another job for Calendar updates, per advice in the Nextcloud docs @@ -419,11 +422,11 @@ chmod +x /etc/cron.d/mailinabox-nextcloud # We also need to change the sending mode from background-job to occ. # Or else the reminders will just be sent as soon as possible when the background jobs run. -hide_output sudo -u www-data php$PHP_VER -f /usr/local/lib/owncloud/occ config:app:set dav sendEventRemindersMode --value occ +hide_output sudo -u www-data php"$PHP_VER" -f /usr/local/lib/owncloud/occ config:app:set dav sendEventRemindersMode --value occ # Now set the config to read-only. # Do this only at the very bottom when no further occ commands are needed. -sed -i'' "s/'config_is_read_only'\s*=>\s*false/'config_is_read_only' => true/" $STORAGE_ROOT/owncloud/config.php +sed -i'' "s/'config_is_read_only'\s*=>\s*false/'config_is_read_only' => true/" "$STORAGE_ROOT/owncloud/config.php" # Rotate the nextcloud.log file cat > /etc/logrotate.d/nextcloud </dev/null -chmod -R 660 $STORAGE_ROOT/mail/spamassassin -chmod 770 $STORAGE_ROOT/mail/spamassassin +chmod -R 660 "$STORAGE_ROOT/mail/spamassassin" +chmod 770 "$STORAGE_ROOT/mail/spamassassin" # Initial training? # sa-learn --ham storage/mail/mailboxes/*/*/cur/ diff --git a/setup/ssl.sh b/setup/ssl.sh index 9bd5d539..19a0c048 100755 --- a/setup/ssl.sh +++ b/setup/ssl.sh @@ -26,9 +26,9 @@ source /etc/mailinabox.conf # load global vars # Show a status line if we are going to take any action in this file. if [ ! -f /usr/bin/openssl ] \ - || [ ! -f $STORAGE_ROOT/ssl/ssl_private_key.pem ] \ - || [ ! -f $STORAGE_ROOT/ssl/ssl_certificate.pem ] \ - || [ ! -f $STORAGE_ROOT/ssl/dh2048.pem ]; then + || [ ! -f "$STORAGE_ROOT/ssl/ssl_private_key.pem" ] \ + || [ ! -f "$STORAGE_ROOT/ssl/ssl_certificate.pem" ] \ + || [ ! -f "$STORAGE_ROOT/ssl/dh2048.pem" ]; then echo "Creating initial SSL certificate and perfect forward secrecy Diffie-Hellman parameters..." fi @@ -38,7 +38,7 @@ apt_install openssl # Create a directory to store TLS-related things like "SSL" certificates. -mkdir -p $STORAGE_ROOT/ssl +mkdir -p "$STORAGE_ROOT/ssl" # Generate a new private key. # @@ -60,39 +60,39 @@ mkdir -p $STORAGE_ROOT/ssl # # Since we properly seed /dev/urandom in system.sh we should be fine, but I leave # in the rest of the notes in case that ever changes. -if [ ! -f $STORAGE_ROOT/ssl/ssl_private_key.pem ]; then +if [ ! -f "$STORAGE_ROOT/ssl/ssl_private_key.pem" ]; then # Set the umask so the key file is never world-readable. (umask 077; hide_output \ - openssl genrsa -out $STORAGE_ROOT/ssl/ssl_private_key.pem 2048) + openssl genrsa -out "$STORAGE_ROOT/ssl/ssl_private_key.pem" 2048) fi # Generate a self-signed SSL certificate because things like nginx, dovecot, # etc. won't even start without some certificate in place, and we need nginx # so we can offer the user a control panel to install a better certificate. -if [ ! -f $STORAGE_ROOT/ssl/ssl_certificate.pem ]; then +if [ ! -f "$STORAGE_ROOT/ssl/ssl_certificate.pem" ]; then # Generate a certificate signing request. CSR=/tmp/ssl_cert_sign_req-$$.csr hide_output \ - openssl req -new -key $STORAGE_ROOT/ssl/ssl_private_key.pem -out $CSR \ + openssl req -new -key "$STORAGE_ROOT/ssl/ssl_private_key.pem" -out $CSR \ -sha256 -subj "/CN=$PRIMARY_HOSTNAME" # Generate the self-signed certificate. CERT=$STORAGE_ROOT/ssl/$PRIMARY_HOSTNAME-selfsigned-$(date --rfc-3339=date | sed s/-//g).pem hide_output \ openssl x509 -req -days 365 \ - -in $CSR -signkey $STORAGE_ROOT/ssl/ssl_private_key.pem -out $CERT + -in $CSR -signkey "$STORAGE_ROOT/ssl/ssl_private_key.pem" -out "$CERT" # Delete the certificate signing request because it has no other purpose. rm -f $CSR # Symlink the certificate into the system certificate path, so system services # can find it. - ln -s $CERT $STORAGE_ROOT/ssl/ssl_certificate.pem + ln -s "$CERT" "$STORAGE_ROOT/ssl/ssl_certificate.pem" fi # Generate some Diffie-Hellman cipher bits. # openssl's default bit length for this is 1024 bits, but we'll create # 2048 bits of bits per the latest recommendations. -if [ ! -f $STORAGE_ROOT/ssl/dh2048.pem ]; then - openssl dhparam -out $STORAGE_ROOT/ssl/dh2048.pem 2048 +if [ ! -f "$STORAGE_ROOT/ssl/dh2048.pem" ]; then + openssl dhparam -out "$STORAGE_ROOT/ssl/dh2048.pem" 2048 fi diff --git a/setup/start.sh b/setup/start.sh index 3abb4fe3..dbf1c16c 100755 --- a/setup/start.sh +++ b/setup/start.sh @@ -46,7 +46,7 @@ fi # in the first dialog prompt, so we should do this before that starts. cat > /usr/local/bin/mailinabox << EOF; #!/bin/bash -cd $(pwd) +cd $PWD source setup/start.sh EOF chmod +x /usr/local/bin/mailinabox @@ -75,17 +75,17 @@ fi # migration (schema) number for the files stored there, assume this is a fresh # installation to that directory and write the file to contain the current # migration number for this version of Mail-in-a-Box. -if ! id -u $STORAGE_USER >/dev/null 2>&1; then - useradd -m $STORAGE_USER +if ! id -u "$STORAGE_USER" >/dev/null 2>&1; then + useradd -m "$STORAGE_USER" fi -if [ ! -d $STORAGE_ROOT ]; then - mkdir -p $STORAGE_ROOT +if [ ! -d "$STORAGE_ROOT" ]; then + mkdir -p "$STORAGE_ROOT" fi f=$STORAGE_ROOT while [[ $f != / ]]; do chmod a+rx "$f"; f=$(dirname "$f"); done; -if [ ! -f $STORAGE_ROOT/mailinabox.version ]; then - setup/migrate.py --current > $STORAGE_ROOT/mailinabox.version - chown $STORAGE_USER:$STORAGE_USER $STORAGE_ROOT/mailinabox.version +if [ ! -f "$STORAGE_ROOT/mailinabox.version" ]; then + setup/migrate.py --current > "$STORAGE_ROOT/mailinabox.version" + chown "$STORAGE_USER:$STORAGE_USER" "$STORAGE_ROOT/mailinabox.version" fi # Save the global options in /etc/mailinabox.conf so that standalone @@ -122,7 +122,7 @@ source setup/munin.sh # Wait for the management daemon to start... until nc -z -w 4 127.0.0.1 10222 do - echo Waiting for the Mail-in-a-Box management daemon to start... + echo "Waiting for the Mail-in-a-Box management daemon to start..." sleep 2 done @@ -142,41 +142,41 @@ source setup/firstuser.sh # We'd let certbot ask the user interactively, but when this script is # run in the recommended curl-pipe-to-bash method there is no TTY and # certbot will fail if it tries to ask. -if [ ! -d $STORAGE_ROOT/ssl/lets_encrypt/accounts/acme-v02.api.letsencrypt.org/ ]; then +if [ ! -d "$STORAGE_ROOT/ssl/lets_encrypt/accounts/acme-v02.api.letsencrypt.org/" ]; then echo echo "-----------------------------------------------" echo "Mail-in-a-Box uses Let's Encrypt to provision free SSL/TLS certificates" echo "to enable HTTPS connections to your box. We're automatically" echo "agreeing you to their subscriber agreement. See https://letsencrypt.org." echo -certbot register --register-unsafely-without-email --agree-tos --config-dir $STORAGE_ROOT/ssl/lets_encrypt +certbot register --register-unsafely-without-email --agree-tos --config-dir "$STORAGE_ROOT/ssl/lets_encrypt" fi # Done. echo echo "-----------------------------------------------" echo -echo Your Mail-in-a-Box is running. +echo "Your Mail-in-a-Box is running." echo -echo Please log in to the control panel for further instructions at: +echo "Please log in to the control panel for further instructions at:" echo if management/status_checks.py --check-primary-hostname; then # Show the nice URL if it appears to be resolving and has a valid certificate. - echo https://$PRIMARY_HOSTNAME/admin + echo "https://$PRIMARY_HOSTNAME/admin" echo echo "If you have a DNS problem put the box's IP address in the URL" echo "(https://$PUBLIC_IP/admin) but then check the TLS fingerprint:" - openssl x509 -in $STORAGE_ROOT/ssl/ssl_certificate.pem -noout -fingerprint -sha256\ + openssl x509 -in "$STORAGE_ROOT/ssl/ssl_certificate.pem" -noout -fingerprint -sha256\ | sed "s/SHA256 Fingerprint=//i" else - echo https://$PUBLIC_IP/admin + echo "https://$PUBLIC_IP/admin" echo - echo You will be alerted that the website has an invalid certificate. Check that - echo the certificate fingerprint matches: + echo "You will be alerted that the website has an invalid certificate. Check that" + echo "the certificate fingerprint matches:" echo - openssl x509 -in $STORAGE_ROOT/ssl/ssl_certificate.pem -noout -fingerprint -sha256\ + openssl x509 -in "$STORAGE_ROOT/ssl/ssl_certificate.pem" -noout -fingerprint -sha256\ | sed "s/SHA256 Fingerprint=//i" echo - echo Then you can confirm the security exception and continue. + echo "Then you can confirm the security exception and continue." echo fi diff --git a/setup/system.sh b/setup/system.sh index 216c2cd8..c0959510 100755 --- a/setup/system.sh +++ b/setup/system.sh @@ -1,3 +1,4 @@ +#!/bin/bash source /etc/mailinabox.conf source setup/functions.sh # load our functions @@ -11,8 +12,8 @@ source setup/functions.sh # load our functions # # First set the hostname in the configuration file, then activate the setting -echo $PRIMARY_HOSTNAME > /etc/hostname -hostname $PRIMARY_HOSTNAME +echo "$PRIMARY_HOSTNAME" > /etc/hostname +hostname "$PRIMARY_HOSTNAME" # ### Fix permissions @@ -53,14 +54,14 @@ if [ -z "$SWAP_IN_FSTAB" ] && [ ! -e /swapfile ] && [ -z "$ROOT_IS_BTRFS" ] && - [ $TOTAL_PHYSICAL_MEM -lt 1900000 ] && - [ $AVAILABLE_DISK_SPACE -gt 5242880 ] + [ "$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 + dd if=/dev/zero of=/swapfile bs=1024 count=$((1024*1024)) status=none if [ -e /swapfile ]; then chmod 600 /swapfile hide_output mkswap /swapfile @@ -110,7 +111,7 @@ hide_output add-apt-repository --y ppa:ondrej/php # of things from Ubuntu, as well as the directory of packages provide by the # PPAs so we can install those packages later. -echo Updating system packages... +echo "Updating system packages..." hide_output apt-get update apt_get_quiet upgrade @@ -135,7 +136,7 @@ apt_get_quiet autoremove # * bc: allows us to do math to compute sane defaults # * openssh-client: provides ssh-keygen -echo Installing system packages... +echo "Installing system packages..." apt_install python3 python3-dev python3-pip python3-setuptools \ netcat-openbsd wget curl git sudo coreutils bc file \ pollinate openssh-client unzip \ @@ -164,7 +165,7 @@ fi # 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 [ ! -f /etc/timezone ] || [ -n "${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. @@ -226,7 +227,7 @@ fi # 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... +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 @@ -270,11 +271,11 @@ if [ -z "${DISABLE_FIREWALL:-}" ]; then # 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 [ -n "$SSH_PORT" ]; then if [ "$SSH_PORT" != "22" ]; then - echo Opening alternate SSH port $SSH_PORT. #NODOC - ufw_limit $SSH_PORT #NODOC + echo "Opening alternate SSH port $SSH_PORT." #NODOC + ufw_limit "$SSH_PORT" #NODOC fi fi diff --git a/setup/web.sh b/setup/web.sh index 392b6d92..acf879a1 100755 --- a/setup/web.sh +++ b/setup/web.sh @@ -8,7 +8,7 @@ source /etc/mailinabox.conf # load global vars # Some Ubuntu images start off with Apache. Remove it since we # will use nginx. Use autoremove to remove any Apache depenencies. if [ -f /usr/sbin/apache2 ]; then - echo Removing apache... + echo "Removing apache..." hide_output apt-get -y purge apache2 apache2-* hide_output apt-get -y --purge autoremove fi @@ -19,7 +19,7 @@ fi echo "Installing Nginx (web server)..." -apt_install nginx php${PHP_VER}-cli php${PHP_VER}-fpm idn2 +apt_install nginx php"${PHP_VER}"-cli php"${PHP_VER}"-fpm idn2 rm -f /etc/nginx/sites-enabled/default @@ -46,15 +46,15 @@ tools/editconf.py /etc/nginx/nginx.conf -s \ ssl_protocols="TLSv1.2 TLSv1.3;" # Tell PHP not to expose its version number in the X-Powered-By header. -tools/editconf.py /etc/php/$PHP_VER/fpm/php.ini -c ';' \ +tools/editconf.py /etc/php/"$PHP_VER"/fpm/php.ini -c ';' \ expose_php=Off # Set PHPs default charset to UTF-8, since we use it. See #367. -tools/editconf.py /etc/php/$PHP_VER/fpm/php.ini -c ';' \ +tools/editconf.py /etc/php/"$PHP_VER"/fpm/php.ini -c ';' \ default_charset="UTF-8" # Configure the path environment for php-fpm -tools/editconf.py /etc/php/$PHP_VER/fpm/pool.d/www.conf -c ';' \ +tools/editconf.py /etc/php/"$PHP_VER"/fpm/pool.d/www.conf -c ';' \ env[PATH]=/usr/local/bin:/usr/bin:/bin \ # Configure php-fpm based on the amount of memory the machine has @@ -62,32 +62,32 @@ tools/editconf.py /etc/php/$PHP_VER/fpm/pool.d/www.conf -c ';' \ # Some synchronisation issues can occur when many people access the site at once. # The pm=ondemand setting is used for memory constrained machines < 2GB, this is copied over from PR: 1216 TOTAL_PHYSICAL_MEM=$(head -n 1 /proc/meminfo | awk '{print $2}' || /bin/true) -if [ $TOTAL_PHYSICAL_MEM -lt 1000000 ] +if [ "$TOTAL_PHYSICAL_MEM" -lt 1000000 ] then - tools/editconf.py /etc/php/$PHP_VER/fpm/pool.d/www.conf -c ';' \ + tools/editconf.py /etc/php/"$PHP_VER"/fpm/pool.d/www.conf -c ';' \ pm=ondemand \ pm.max_children=8 \ pm.start_servers=2 \ pm.min_spare_servers=1 \ pm.max_spare_servers=3 -elif [ $TOTAL_PHYSICAL_MEM -lt 2000000 ] +elif [ "$TOTAL_PHYSICAL_MEM" -lt 2000000 ] then - tools/editconf.py /etc/php/$PHP_VER/fpm/pool.d/www.conf -c ';' \ + tools/editconf.py /etc/php/"$PHP_VER"/fpm/pool.d/www.conf -c ';' \ pm=ondemand \ pm.max_children=16 \ pm.start_servers=4 \ pm.min_spare_servers=1 \ pm.max_spare_servers=6 -elif [ $TOTAL_PHYSICAL_MEM -lt 3000000 ] +elif [ "$TOTAL_PHYSICAL_MEM" -lt 3000000 ] then - tools/editconf.py /etc/php/$PHP_VER/fpm/pool.d/www.conf -c ';' \ + tools/editconf.py /etc/php/"$PHP_VER"/fpm/pool.d/www.conf -c ';' \ pm=dynamic \ pm.max_children=60 \ pm.start_servers=6 \ pm.min_spare_servers=3 \ pm.max_spare_servers=9 else - tools/editconf.py /etc/php/$PHP_VER/fpm/pool.d/www.conf -c ';' \ + tools/editconf.py /etc/php/"$PHP_VER"/fpm/pool.d/www.conf -c ';' \ pm=dynamic \ pm.max_children=120 \ pm.start_servers=12 \ @@ -124,7 +124,7 @@ chmod a+r /var/lib/mailinabox/mozilla-autoconfig.xml # Create a generic mta-sts.txt file which is exposed via the # nginx configuration at /.well-known/mta-sts.txt -# more documentation is available on: +# more documentation is available on: # https://www.uriports.com/blog/mta-sts-explained/ # default mode is "enforce". In /etc/mailinabox.conf change # "MTA_STS_MODE=testing" which means "Messages will be delivered @@ -138,16 +138,16 @@ cat conf/mta-sts.txt \ chmod a+r /var/lib/mailinabox/mta-sts.txt # make a default homepage -if [ -d $STORAGE_ROOT/www/static ]; then mv $STORAGE_ROOT/www/static $STORAGE_ROOT/www/default; fi # migration #NODOC -mkdir -p $STORAGE_ROOT/www/default -if [ ! -f $STORAGE_ROOT/www/default/index.html ]; then - cp conf/www_default.html $STORAGE_ROOT/www/default/index.html +if [ -d "$STORAGE_ROOT/www/static" ]; then mv "$STORAGE_ROOT/www/static" "$STORAGE_ROOT/www/default"; fi # migration #NODOC +mkdir -p "$STORAGE_ROOT/www/default" +if [ ! -f "$STORAGE_ROOT/www/default/index.html" ]; then + cp conf/www_default.html "$STORAGE_ROOT/www/default/index.html" fi -chown -R $STORAGE_USER $STORAGE_ROOT/www +chown -R "$STORAGE_USER" "$STORAGE_ROOT/www" # Start services. restart_service nginx -restart_service php$PHP_VER-fpm +restart_service php"$PHP_VER"-fpm # Open ports. ufw_allow http diff --git a/setup/webmail.sh b/setup/webmail.sh old mode 100755 new mode 100644 index 3cff1416..66a85596 --- a/setup/webmail.sh +++ b/setup/webmail.sh @@ -22,8 +22,8 @@ source /etc/mailinabox.conf # load global vars echo "Installing Roundcube (webmail)..." apt_install \ dbconfig-common \ - php${PHP_VER}-cli php${PHP_VER}-sqlite3 php${PHP_VER}-intl php${PHP_VER}-common php${PHP_VER}-curl php${PHP_VER}-imap \ - php${PHP_VER}-gd php${PHP_VER}-pspell php${PHP_VER}-mbstring libjs-jquery libjs-jquery-mousewheel libmagic1 \ + php"${PHP_VER}"-cli php"${PHP_VER}"-sqlite3 php"${PHP_VER}"-intl php"${PHP_VER}"-common php"${PHP_VER}"-curl php"${PHP_VER}"-imap \ + php"${PHP_VER}"-gd php"${PHP_VER}"-pspell php"${PHP_VER}"-mbstring libjs-jquery libjs-jquery-mousewheel libmagic1 \ sqlite3 # Install Roundcube from source if it is not already present or if it is out of date. @@ -170,8 +170,8 @@ cat > ${RCM_PLUGIN_DIR}/carddav/config.inc.php < /usr/sbin/z-push-admin - echo php$PHP_VER /usr/local/lib/z-push/z-push-admin.php '"$@"' >> /usr/sbin/z-push-admin + echo php"$PHP_VER" /usr/local/lib/z-push/z-push-admin.php '"$@"' >> /usr/sbin/z-push-admin chmod 755 /usr/sbin/z-push-admin echo '#!/bin/bash' > /usr/sbin/z-push-top - echo php$PHP_VER /usr/local/lib/z-push/z-push-top.php '"$@"' >> /usr/sbin/z-push-top + echo php"$PHP_VER" /usr/local/lib/z-push/z-push-top.php '"$@"' >> /usr/sbin/z-push-top chmod 755 /usr/sbin/z-push-top echo $VERSION > /usr/local/lib/z-push/version @@ -108,8 +108,8 @@ EOF # Restart service. -restart_service php$PHP_VER-fpm +restart_service php"$PHP_VER"-fpm # Fix states after upgrade -hide_output php$PHP_VER /usr/local/lib/z-push/z-push-admin.php -a fixstates +hide_output php"$PHP_VER" /usr/local/lib/z-push/z-push-admin.php -a fixstates diff --git a/tools/archive_conf_files.sh b/tools/archive_conf_files.sh index 28b6b24f..d2ec2c9a 100644 --- a/tools/archive_conf_files.sh +++ b/tools/archive_conf_files.sh @@ -1,9 +1,10 @@ +#!/bin/bash # Use this script to make an archive of the contents of all # of the configuration files we edit with editconf.py. -for fn in `grep -hr editconf.py setup | sed "s/tools\/editconf.py //" | sed "s/ .*//" | sort | uniq`; do +for fn in $(grep -hr editconf.py setup | sed "s/tools\/editconf.py //" | sed "s/ .*//" | sort | uniq); do echo ====================================================================== - echo $fn + echo "$fn" echo ====================================================================== - cat $fn + cat "$fn" done diff --git a/tools/dns_update b/tools/dns_update index 947c3dd1..2bbcce67 100755 --- a/tools/dns_update +++ b/tools/dns_update @@ -3,4 +3,4 @@ POSTDATA=dummy if [ "$1" == "--force" ]; then POSTDATA=force=1 fi -curl -s -d $POSTDATA --user $(