From d96613b8fe91f3d04887aa3318d4ef2467cea08a Mon Sep 17 00:00:00 2001 From: "Christopher A. DeFlumeri" Date: Sat, 7 Jul 2018 18:41:41 +0000 Subject: [PATCH 001/190] minimal changeset to get things working on 18.04 @joshdata squashed pull request #1398, removed some comments, and added these notes: * The old init.d script for the management daemon is replaced with a systemd service. * A systemd service configuration is added to configure permissions for munin on startup. * nginx SSL settings are updated because nginx's options and defaults have changed, and we now enable http2. * Automatic SSHFP record generation is updated to know that 22 is the default SSH daemon port, since it is no longer explicit in sshd_config. * The dovecot-lucene package is dropped because the Mail-in-a-Box PPA where we built the package has not been updated for Ubuntu 18.04. * The stock postgrey package is installed instead of the one from our PPA (which we no longer support), which loses the automatic whitelisting of DNSWL.org-whitelisted senders. * Drop memcached and the status check for memcached, which we used to use with ownCloud long ago but are no longer installing. * Other minor changes. --- CHANGELOG.md | 3 + Vagrantfile | 9 +- conf/mailinabox.service | 10 ++ conf/management-initscript | 135 -------------------- conf/munin.service | 10 ++ conf/nginx-ssl.conf | 70 ++--------- conf/nginx.conf | 4 +- management/dns_update.py | 23 ++-- management/munin_start.sh | 2 + management/status_checks.py | 1 - setup/dkim.sh | 8 ++ setup/mail-dovecot.sh | 13 +- setup/mail-postfix.sh | 3 +- setup/management.sh | 8 +- setup/munin.sh | 8 ++ setup/{owncloud.sh => nextcloud.sh} | 185 ++++------------------------ setup/preflight.sh | 6 +- setup/start.sh | 2 +- setup/system.sh | 11 +- setup/webmail.sh | 2 - 20 files changed, 101 insertions(+), 412 deletions(-) create mode 100644 conf/mailinabox.service delete mode 100755 conf/management-initscript create mode 100644 conf/munin.service create mode 100644 management/munin_start.sh rename setup/{owncloud.sh => nextcloud.sh} (58%) diff --git a/CHANGELOG.md b/CHANGELOG.md index befea2a7..64beabb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,13 @@ CHANGELOG ========= +This branch supports Ubuntu 18.04 **only**. When upgrading, **always** upgrade your **existing** Ubuntu 14.04 machine to version the latest release supporting Ubuntu 14.04 --- v0.28. If you are running an older version of Mail-in-a-Box which has an old version of ownCloud or Nextcloud, you will *not* be able to upgrade your data because older versions of ownCloud and Nextcloud that are required to perform the upgrade *cannot* be run on Ubuntu 18.04. + In Development -------------- * Starting with v0.28, TLS certificate provisioning wouldn't work on new boxes until the mailinabox setup command was run a second time because of a problem with the non-interactive setup. + * Update to Nextcloud 13.0.5. * Update to Roundcube 1.3.7. * Update to Z-Push 2.4.4. diff --git a/Vagrantfile b/Vagrantfile index 770f66d2..467fb95e 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -2,14 +2,7 @@ # vi: set ft=ruby : Vagrant.configure("2") do |config| - config.vm.box = "ubuntu14.04" - config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box" - - if Vagrant.has_plugin?("vagrant-cachier") - # Configure cached packages to be shared between instances of the same base box. - # More info on http://fgrehm.viewdocs.io/vagrant-cachier/usage - config.cache.scope = :box - end + config.vm.box = "ubuntu/bionic64" # Network config: Since it's a mail server, the machine must be connected # to the public web. However, we currently don't want to expose SSH since diff --git a/conf/mailinabox.service b/conf/mailinabox.service new file mode 100644 index 00000000..b4cfa6cf --- /dev/null +++ b/conf/mailinabox.service @@ -0,0 +1,10 @@ +[Unit] +Description=Mail-in-a-Box System Management Service +After=multi-user.target + +[Service] +Type=idle +ExecStart=/usr/local/lib/mailinabox/start + +[Install] +WantedBy=multi-user.target diff --git a/conf/management-initscript b/conf/management-initscript deleted file mode 100755 index 8275da86..00000000 --- a/conf/management-initscript +++ /dev/null @@ -1,135 +0,0 @@ -#! /bin/sh -### BEGIN INIT INFO -# Provides: mailinabox -# Required-Start: $all -# Required-Stop: $all -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Start and stop the Mail-in-a-Box management daemon. -# Description: Start and stop the Mail-in-a-Box management daemon. -### END INIT INFO - -# Adapted from http://blog.codefront.net/2007/06/11/nginx-php-and-a-php-fastcgi-daemon-init-script/ - -PATH=/sbin:/usr/sbin:/bin:/usr/bin -DESC="Mail-in-a-Box Management Daemon" -NAME=mailinabox -DAEMON=/usr/local/lib/mailinabox/start -PIDFILE=/var/run/$NAME.pid -SCRIPTNAME=/etc/init.d/$NAME - -# Exit if the package is not installed -[ -x "$DAEMON" ] || exit 0 - -# Set defaults. -START=yes -EXEC_AS_USER=root - -# Ensure Python reads/writes files in UTF-8. If the machine -# triggers some other locale in Python, like ASCII encoding, -# Python may not be able to read/write files. Set also -# setup/start.sh (where the locale is also installed if not -# already present) and management/daily_tasks.sh. -export LANGUAGE=en_US.UTF-8 -export LC_ALL=en_US.UTF-8 -export LANG=en_US.UTF-8 -export LC_TYPE=en_US.UTF-8 - -# Read configuration variable file if it is present -[ -r /etc/default/$NAME ] && . /etc/default/$NAME - -# Load the VERBOSE setting and other rcS variables -. /lib/init/vars.sh - -# Define LSB log_* functions. -# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. -. /lib/lsb/init-functions - -# If the daemon is not enabled, give the user a warning and then exit, -# unless we are stopping the daemon -if [ "$START" != "yes" -a "$1" != "stop" ]; then - log_warning_msg "To enable $NAME, edit /etc/default/$NAME and set START=yes" - exit 0 -fi - -# Process configuration -#export ... -DAEMON_ARGS="" - - -do_start() -{ - # Return - # 0 if daemon has been started - # 1 if daemon was already running - # 2 if daemon could not be started - start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ - || return 1 - start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \ - --background --make-pidfile --chuid $EXEC_AS_USER --startas $DAEMON -- \ - $DAEMON_ARGS \ - || return 2 -} - -do_stop() -{ - # Return - # 0 if daemon has been stopped - # 1 if daemon was already stopped - # 2 if daemon could not be stopped - # other if a failure occurred - start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE > /dev/null # --name $DAEMON - RETVAL="$?" - [ "$RETVAL" = 2 ] && return 2 - # Wait for children to finish too if this is a daemon that forks - # and if the daemon is only ever run from this initscript. - # If the above conditions are not satisfied then add some other code - # that waits for the process to drop all resources that could be - # needed by services started subsequently. A last resort is to - # sleep for some time. - start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON - [ "$?" = 2 ] && return 2 - # Many daemons don't delete their pidfiles when they exit. - rm -f $PIDFILE - return "$RETVAL" -} -case "$1" in - start) - [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" - do_start - case "$?" in - 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; - 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; - esac - ;; - stop) - [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" - do_stop - case "$?" in - 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; - 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; - esac - ;; - restart|force-reload) - log_daemon_msg "Restarting $DESC" "$NAME" - do_stop - case "$?" in - 0|1) - do_start - case "$?" in - 0) log_end_msg 0 ;; - 1) log_end_msg 1 ;; # Old process is still running - *) log_end_msg 1 ;; # Failed to start - esac - ;; - *) - # Failed to stop - log_end_msg 1 - ;; - esac - ;; - *) - echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 - exit 3 - ;; -esac diff --git a/conf/munin.service b/conf/munin.service new file mode 100644 index 00000000..06735d04 --- /dev/null +++ b/conf/munin.service @@ -0,0 +1,10 @@ +[Unit] +Description=Munin System Monitoring Startup Script +After=multi-user.target + +[Service] +Type=idle +ExecStart=/usr/local/lib/mailinabox/munin_start.sh + +[Install] +WantedBy=multi-user.target diff --git a/conf/nginx-ssl.conf b/conf/nginx-ssl.conf index e893ad67..d4dc619b 100644 --- a/conf/nginx-ssl.conf +++ b/conf/nginx-ssl.conf @@ -1,76 +1,20 @@ -# from https://gist.github.com/konklone/6532544 and https://mozilla.github.io/server-side-tls/ssl-config-generator/ -################################################################################################################### - -# Basically the nginx configuration I use at konklone.com. -# I check it using https://www.ssllabs.com/ssltest/analyze.html?d=konklone.com -# -# To provide feedback, please tweet at @konklone or email eric@konklone.com. -# Comments on gists don't notify the author. -# -# Thanks to WubTheCaptain (https://wubthecaptain.eu) for his help and ciphersuites. -# Thanks to Ilya Grigorik (https://www.igvita.com) for constant inspiration. - -# Path to certificate and private key. -# The .crt may omit the root CA cert, if it's a standard CA that ships with clients. -#ssl_certificate /path/to/unified.crt; -#ssl_certificate_key /path/to/my-private-decrypted.key; - -# Tell browsers to require SSL (warning: difficult to change your mind) -# Handled by the management daemon because we can toggle this version or a -# preload version. -#add_header Strict-Transport-Security max-age=31536000; - -# Prefer certain ciphersuites, to enforce Forward Secrecy and avoid known vulnerabilities. -# -# Forces forward secrecy in all browsers and clients that can use TLS, -# but with a small exception (DES-CBC3-SHA) for IE8/XP users. -# -# Reference client: https://www.ssllabs.com/ssltest/analyze.html -ssl_prefer_server_ciphers on; -ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; - -# Cut out (the old, broken) SSLv3 entirely. -# This **excludes IE6 users** and (apparently) Yandexbot. -# Just comment out if you need to support IE6, bless your soul. +# We track the Mozilla "intermediate" compatibility TLS recommendations. +# Note that these settings are repeated in the SMTP and IMAP configuration. ssl_protocols TLSv1.2 TLSv1.1 TLSv1; +ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; +ssl_dhparam STORAGE_ROOT/ssl/dh2048.pem; -# Turn on session resumption, using a cache shared across nginx processes, # as recommended by http://nginx.org/en/docs/http/configuring_https_servers.html ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; -#keepalive_timeout 70; # in Ubuntu 14.04/nginx 1.4.6 the default is 65, so plenty good # Buffer size of 1400 bytes fits in one MTU. # nginx 1.5.9+ ONLY -#ssl_buffer_size 1400; +ssl_buffer_size 1400; -# SPDY header compression (0 for none, 9 for slow/heavy compression). Preferred is 6. -# -# BUT: header compression is flawed and vulnerable in SPDY versions 1 - 3. -# Disable with 0, until using a version of nginx with SPDY 4. -spdy_headers_comp 0; - -# Now let's really get fancy, and pre-generate a 2048 bit random parameter -# for DH elliptic curves. If not created and specified, default is only 1024 bits. -# -# Generated by OpenSSL with the following command: -# openssl dhparam -outform pem -out dhparam2048.pem 2048 -# -# Note: raising the bits to 2048 excludes Java 6 clients. Comment out if a problem. -ssl_dhparam STORAGE_ROOT/ssl/dh2048.pem; - - -# OCSP stapling - means nginx will poll the CA for signed OCSP responses, -# and send them to clients so clients don't make their own OCSP calls. -# http://en.wikipedia.org/wiki/OCSP_stapling -# -# while the ssl_certificate above may omit the root cert if the CA is trusted, -# ssl_trusted_certificate below must point to a chain of **all** certs -# in the trust path - (your cert, intermediary certs, root cert) -# -# 8.8.8.8 and 8.8.4.4 below are Google's public IPv4 DNS servers. -# nginx will use them to talk to the CA. ssl_stapling on; ssl_stapling_verify on; resolver 127.0.0.1 valid=86400; resolver_timeout 10; + +# h/t https://gist.github.com/konklone/6532544 diff --git a/conf/nginx.conf b/conf/nginx.conf index ce662751..fafd3409 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -31,8 +31,8 @@ server { # The secure HTTPS server. server { - listen 443 ssl; - listen [::]:443 ssl; + listen 443 ssl http2; + listen [::]:443 ssl http2; server_name $HOSTNAME; diff --git a/management/dns_update.py b/management/dns_update.py index b6e1022d..5c1969d7 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -354,19 +354,20 @@ def build_sshfp_records(): # Get our local fingerprints by running ssh-keyscan. The output looks # like the known_hosts file: hostname, keytype, fingerprint. The order # of the output is arbitrary, so sort it to prevent spurrious updates - # to the zone file (that trigger bumping the serial number). - - # scan the sshd_config and find the ssh ports (port 22 may be closed) + # to the zone file (that trigger bumping the serial number). However, + # if SSH has been configured to listen on a nonstandard port, we must + # specify that port to sshkeyscan. + port = 22 with open('/etc/ssh/sshd_config', 'r') as f: - ports = [] - t = f.readlines() - for line in t: - s = line.split() + for line in f: + s = line.rstrip().split() if len(s) == 2 and s[0] == 'Port': - ports = ports + [s[1]] - # the keys are the same at each port, so we only need to get - # them at the first port found (may not be port 22) - keys = shell("check_output", ["ssh-keyscan", "-t", "rsa,dsa,ecdsa,ed25519", "-p", ports[0], "localhost"]) + try: + port = int(s[1]) + except ValueError: + pass + break + keys = shell("check_output", ["ssh-keyscan", "-t", "rsa,dsa,ecdsa,ed25519", "-p", str(port), "localhost"]) for key in sorted(keys.split("\n")): if key.strip() == "" or key[0] == "#": continue try: diff --git a/management/munin_start.sh b/management/munin_start.sh new file mode 100644 index 00000000..682861e7 --- /dev/null +++ b/management/munin_start.sh @@ -0,0 +1,2 @@ +#!/bin/bash +mkdir -p /var/run/munin && chown munin /var/run/munin diff --git a/management/status_checks.py b/management/status_checks.py index 3b0026d9..2f4fe0a7 100755 --- a/management/status_checks.py +++ b/management/status_checks.py @@ -28,7 +28,6 @@ def get_services(): { "name": "Spamassassin", "port": 10025, "public": False, }, { "name": "OpenDKIM", "port": 8891, "public": False, }, { "name": "OpenDMARC", "port": 8893, "public": False, }, - { "name": "Memcached", "port": 11211, "public": False, }, { "name": "Mail-in-a-Box Management Daemon", "port": 10222, "public": False, }, { "name": "SSH Login (ssh)", "port": get_ssh_port(), "public": True, }, { "name": "Public DNS (nsd4)", "port": 53, "public": True, }, diff --git a/setup/dkim.sh b/setup/dkim.sh index 830e9e18..dc7e51e2 100755 --- a/setup/dkim.sh +++ b/setup/dkim.sh @@ -21,6 +21,11 @@ mkdir -p $STORAGE_ROOT/mail/dkim # Not quite sure why. echo "127.0.0.1" > /etc/opendkim/TrustedHosts +# We need to at least create these files, since we reference them later. +# Otherwise, opendkim startup will fail +touch /etc/opendkim/KeyTable +touch /etc/opendkim/SigningTable + if grep -q "ExternalIgnoreList" /etc/opendkim.conf; then true # already done #NODOC else @@ -75,6 +80,9 @@ tools/editconf.py /etc/postfix/main.cf \ non_smtpd_milters=\$smtpd_milters \ milter_default_action=accept +# We need to explicitly enable the opendmarc service, or it will not start +hide_output systemctl enable opendmarc + # Restart services. restart_service opendkim restart_service opendmarc diff --git a/setup/mail-dovecot.sh b/setup/mail-dovecot.sh index 21343964..4bcc53aa 100755 --- a/setup/mail-dovecot.sh +++ b/setup/mail-dovecot.sh @@ -26,7 +26,7 @@ source /etc/mailinabox.conf # load global vars echo "Installing Dovecot (IMAP server)..." apt_install \ dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-sqlite sqlite3 \ - dovecot-sieve dovecot-managesieved dovecot-lucene + dovecot-sieve dovecot-managesieved # The `dovecot-imapd`, `dovecot-pop3d`, and `dovecot-lmtpd` packages automatically # enable IMAP, POP and LMTP protocols. @@ -112,17 +112,6 @@ tools/editconf.py /etc/dovecot/conf.d/20-imap.conf \ tools/editconf.py /etc/dovecot/conf.d/20-pop3.conf \ pop3_uidl_format="%08Xu%08Xv" -# Full Text Search - Enable full text search of mail using dovecot's lucene plugin, -# which *we* package and distribute (dovecot-lucene package). -tools/editconf.py /etc/dovecot/conf.d/10-mail.conf \ - mail_plugins="\$mail_plugins fts fts_lucene" -cat > /etc/dovecot/conf.d/90-plugin-fts.conf << EOF; -plugin { - fts = lucene - fts_lucene = whitespace_chars=@. -} -EOF - # ### LDA (LMTP) # Enable Dovecot's LDA service with the LMTP protocol. It will listen diff --git a/setup/mail-postfix.sh b/setup/mail-postfix.sh index ca52edbd..10322bca 100755 --- a/setup/mail-postfix.sh +++ b/setup/mail-postfix.sh @@ -48,9 +48,8 @@ source /etc/mailinabox.conf # load global vars # > Every user with more than 100’000 queries per day on the public nameserver # > infrastructure and every commercial vendor of dnswl.org data (eg through # > anti-spam solutions) must register with dnswl.org and purchase a subscription. - echo "Installing Postfix (SMTP server)..." -apt_install postfix postfix-pcre postgrey ca-certificates +apt_install postfix postfix-sqlite postfix-pcre postgrey ca-certificates # ### Basic Settings diff --git a/setup/management.sh b/setup/management.sh index 064906d3..1e29ab8c 100755 --- a/setup/management.sh +++ b/setup/management.sh @@ -87,16 +87,16 @@ rm -f /tmp/bootstrap.zip # Create an init script to start the management daemon and keep it # running after a reboot. -rm -f /usr/local/bin/mailinabox-daemon # old path +rm -f /usr/local/bin/mailinabox-daemon /etc/init.d/mailinabox # old paths cat > $inst_dir/start < /dev/null || /bin/true - service php5-fpm stop &> /dev/null || /bin/true # Backup the existing ownCloud/Nextcloud. # Create a backup directory to store the current installation and database to BACKUP_DIRECTORY=$STORAGE_ROOT/owncloud-backup/`date +"%Y-%m-%d-%T"` mkdir -p "$BACKUP_DIRECTORY" if [ -d /usr/local/lib/owncloud/ ]; then - echo "upgrading ownCloud/Nextcloud to $owncloud_flavor $owncloud_ver (backing up existing installation, configuration and database to directory to $BACKUP_DIRECTORY..." + 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 /home/user-data/owncloud/owncloud.db ]; then cp /home/user-data/owncloud/owncloud.db $BACKUP_DIRECTORY - fi - if [ -e /home/user-data/owncloud/config.php ]; then - cp /home/user-data/owncloud/config.php $BACKUP_DIRECTORY - fi + fi + if [ -e /home/user-data/owncloud/config.php ]; then + cp /home/user-data/owncloud/config.php $BACKUP_DIRECTORY + fi - # We only need to check if we do upgrades when owncloud/Nextcloud was previously installed + # If ownCloud or Nextcloud was previously installed.... if [ -e /usr/local/lib/owncloud/version.php ]; then - if grep -q "OC_VersionString = '8\.1\.[0-9]" /usr/local/lib/owncloud/version.php; then - echo "We are running 8.1.x, upgrading to 8.2.11 first" - InstallOwncloud 8.2.11 e4794938fc2f15a095018ba9d6ee18b53f6f299c + # Database migrations from ownCloud are no longer possible because ownCloud cannot be run under + # PHP 7. + if grep -q "OC_VersionString = '[89]\." /usr/local/lib/owncloud/version.php; then + echo "Upgrades from Mail-in-a-Box prior to v0.26c (dated February 13, 2018) with Nextcloud < 12.0.5 (you have ownCloud 8 or 9) are not supported. Upgrade to Mail-in-a-Box version v0.28 first. Setup aborting." + exit 1 fi - - # If we are upgrading from 8.2.x we should go to 9.0 first. Owncloud doesn't support skipping minor versions - if grep -q "OC_VersionString = '8\.2\.[0-9]" /usr/local/lib/owncloud/version.php; then - echo "We are running version 8.2.x, upgrading to 9.0.11 first" - - # We need to disable memcached. The upgrade and install fails - # with memcached - CONFIG_TEMP=$(/bin/mktemp) - php < $CONFIG_TEMP && mv $CONFIG_TEMP $STORAGE_ROOT/owncloud/config.php; - -EOF - chown www-data.www-data $STORAGE_ROOT/owncloud/config.php - - # We can now install owncloud 9.0.11 - InstallOwncloud 9.0.11 fc8bad8a62179089bc58c406b28997fb0329337b - - # The owncloud 9 migration doesn't migrate calendars and contacts - # The option to migrate these are removed in 9.1 - # So the migrations should be done when we have 9.0 installed - sudo -u www-data php5 /usr/local/lib/owncloud/occ dav:migrate-addressbooks - # The following migration has to be done for each owncloud user - for directory in $STORAGE_ROOT/owncloud/*@*/ ; do - username=$(basename "${directory}") - sudo -u www-data php5 /usr/local/lib/owncloud/occ dav:migrate-calendar $username - done - sudo -u www-data php5 /usr/local/lib/owncloud/occ dav:sync-birthday-calendar - fi - - # If we are upgrading from 9.0.x we should go to 9.1 first. - if grep -q "OC_VersionString = '9\.0\.[0-9]" /usr/local/lib/owncloud/version.php; then - echo "We are running ownCloud 9.0.x, upgrading to ownCloud 9.1.7 first" - InstallOwncloud 9.1.7 1307d997d0b23dc42742d315b3e2f11423a9c808 - fi - - # Newer ownCloud 9.1.x versions cannot be upgraded to Nextcloud 10 and have to be - # upgraded to Nextcloud 11 straight away, see: - # https://github.com/nextcloud/server/issues/2203 - # However, for some reason, upgrading to the latest Nextcloud 11.0.7 doesn't - # work either. Therefore, we're upgrading to Nextcloud 11.0.0 in the interim. - # This should not be a problem since we're upgrading to the latest Nextcloud 12 - # in the next step. - if grep -q "OC_VersionString = '9\.1\.[0-9]" /usr/local/lib/owncloud/version.php; then - echo "We are running ownCloud 9.1.x, upgrading to Nextcloud 11.0.0 first" - InstallNextcloud 11.0.0 e8c9ebe72a4a76c047080de94743c5c11735e72e - fi - - # If we are upgrading from 10.0.x we should go to Nextcloud 11.0 first. - if grep -q "OC_VersionString = '10\.0\.[0-9]" /usr/local/lib/owncloud/version.php; then - echo "We are running Nextcloud 10.0.x, upgrading to Nextcloud 11.0.7 first" - InstallNextcloud 11.0.7 f936ddcb2ae3dbb66ee4926eb8b2ebbddc3facbe + if grep -q "OC_VersionString = '10\." /usr/local/lib/owncloud/version.php; then + echo "Upgrades from Mail-in-a-Box prior to v0.26c (dated February 13, 2018) with Nextcloud < 12.0.5 (you have ownCloud 10) are not supported. Upgrade to Mail-in-a-Box version v0.28 first. Setup aborting." + exit 1 fi # If we are upgrading from Nextcloud 11 we should go to Nextcloud 12 first. @@ -253,7 +120,7 @@ EOF fi fi - InstallNextcloud $owncloud_ver $owncloud_hash + InstallNextcloud $nextcloud_ver $nextcloud_hash fi # ### Configuring Nextcloud @@ -278,10 +145,10 @@ if [ ! -f $STORAGE_ROOT/owncloud/owncloud.db ]; then 'overwritewebroot' => '/cloud', 'overwrite.cli.url' => '/cloud', 'user_backends' => array( - array( - 'class'=>'OC_User_IMAP', - 'arguments'=>array('{127.0.0.1:993/imap/ssl/novalidate-cert}') - ) + array( + 'class'=>'OC_User_IMAP', + 'arguments'=>array('{127.0.0.1:993/imap/ssl/novalidate-cert}') + ) ), 'memcache.local' => '\OC\Memcache\APCu', 'mail_smtpmode' => 'sendmail', @@ -397,7 +264,7 @@ tools/editconf.py /etc/php/7.0/cli/conf.d/10-opcache.ini -c ';' \ # Configure the path environment for php-fpm tools/editconf.py /etc/php/7.0/fpm/pool.d/www.conf -c ';' \ - env[PATH]=/usr/local/bin:/usr/bin:/bin + env[PATH]=/usr/local/bin:/usr/bin:/bin # If apc is explicitly disabled we need to enable it if grep -q apc.enabled=0 /etc/php/7.0/mods-available/apcu.ini; then diff --git a/setup/preflight.sh b/setup/preflight.sh index 4be2ec41..54fcd6c9 100644 --- a/setup/preflight.sh +++ b/setup/preflight.sh @@ -7,9 +7,9 @@ if [[ $EUID -ne 0 ]]; then exit fi -# Check that we are running on Ubuntu 14.04 LTS (or 14.04.xx). -if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" != "Ubuntu 14.04 LTS" ]; then - echo "Mail-in-a-Box only supports being installed on Ubuntu 14.04, sorry. You are running:" +# Check that we are running on Ubuntu 18.04 LTS (or 18.04.xx). +if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/18\.04\.[0-9]/18.04/' `" != "Ubuntu 18.04 LTS" ]; then + echo "Mail-in-a-Box only supports being installed on Ubuntu 18.04, sorry. You are running:" echo lsb_release -d | sed 's/.*:\s*//' echo diff --git a/setup/start.sh b/setup/start.sh index 3dbaed54..9d64bba5 100755 --- a/setup/start.sh +++ b/setup/start.sh @@ -106,7 +106,7 @@ source setup/dkim.sh source setup/spamassassin.sh source setup/web.sh source setup/webmail.sh -source setup/owncloud.sh +source setup/nextcloud.sh source setup/zpush.sh source setup/management.sh source setup/munin.sh diff --git a/setup/system.sh b/setup/system.sh index 04728051..29ee97bd 100755 --- a/setup/system.sh +++ b/setup/system.sh @@ -70,7 +70,7 @@ fi # ### Add PPAs. -# We install some non-standard Ubuntu packages maintained by us and other +# We install some non-standard Ubuntu packages maintained by other # third-party providers. First ensure add-apt-repository is installed. if [ ! -f /usr/bin/add-apt-repository ]; then @@ -79,14 +79,7 @@ if [ ! -f /usr/bin/add-apt-repository ]; then apt_install software-properties-common fi -# [Main-in-a-Box's own PPA](https://launchpad.net/~mail-in-a-box/+archive/ubuntu/ppa) -# holds several .deb packages that we built on our own. -# 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. - -hide_output add-apt-repository -y ppa:mail-in-a-box/ppa +# Install the certbot PPA. hide_output add-apt-repository -y ppa:certbot/certbot # ### Update Packages diff --git a/setup/webmail.sh b/setup/webmail.sh index 38aee942..798641f9 100755 --- a/setup/webmail.sh +++ b/setup/webmail.sh @@ -25,8 +25,6 @@ apt_install \ php7.0-cli php7.0-sqlite php7.0-mcrypt php7.0-intl php7.0-json php7.0-common php7.0-curl \ php7.0-gd php7.0-pspell tinymce libjs-jquery libjs-jquery-mousewheel libmagic1 php7.0-mbstring -apt_get_quiet remove php-mail-mimedecode # no longer needed since Roundcube 1.1.3 - # We used to install Roundcube from Ubuntu, without triggering the dependencies #NODOC # on Apache and MySQL, by downloading the debs and installing them manually. #NODOC # Now that we're beyond that, get rid of those debs before installing from source. #NODOC From bb43a2127c360c8e6c2cc72c2037968263c3ac4d Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 24 Aug 2018 07:29:26 -0400 Subject: [PATCH 002/190] turn the x64/i686 architecture check into a warning since I'm not sure if we have any architecture requirements anymore, beyond what Ubuntu supports --- setup/preflight.sh | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/setup/preflight.sh b/setup/preflight.sh index 54fcd6c9..7466b857 100644 --- a/setup/preflight.sh +++ b/setup/preflight.sh @@ -53,16 +53,14 @@ if [ -e ~/.wgetrc ]; then exit fi -# Check that we are running on x86_64 or i686, any other architecture is unsupported and -# will fail later in the setup when we try to install the custom build lucene packages. -# -# Set ARM=1 to ignore this check if you have built the packages yourself. If you do this -# you are on your own! +# Check that we are running on x86_64 or i686 architecture, which are the only +# ones we support / test. ARCHITECTURE=$(uname -m) if [ "$ARCHITECTURE" != "x86_64" ] && [ "$ARCHITECTURE" != "i686" ]; then -if [ -z "$ARM" ]; then - echo "Mail-in-a-Box only supports x86_64 or i686 and will not work on any other architecture, like ARM." - echo "Your architecture is $ARCHITECTURE" - exit -fi + echo + echo "WARNING:" + echo "Mail-in-a-Box has only been tested on x86_64 and i686 platform" + echo "architectures. Your architecture, $ARCHITECTURE, may not work." + echo "You are on your own." + echo fi From 51972fd12908643d04439face99b73afde27107e Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 24 Aug 2018 08:44:53 -0400 Subject: [PATCH 003/190] fix some comments --- setup/mail-postfix.sh | 7 ------- setup/start.sh | 2 +- setup/system.sh | 5 ++--- setup/web.sh | 2 +- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/setup/mail-postfix.sh b/setup/mail-postfix.sh index 10322bca..11a2b307 100755 --- a/setup/mail-postfix.sh +++ b/setup/mail-postfix.sh @@ -41,13 +41,6 @@ source /etc/mailinabox.conf # load global vars # always will. # * `ca-certificates`: A trust store used to squelch postfix warnings about # untrusted opportunistically-encrypted connections. -# -# postgrey is going to come in via the Mail-in-a-Box PPA, which publishes -# a modified version of postgrey that lets senders whitelisted by dnswl.org -# pass through without being greylisted. So please note [dnswl's license terms](https://www.dnswl.org/?page_id=9): -# > Every user with more than 100’000 queries per day on the public nameserver -# > infrastructure and every commercial vendor of dnswl.org data (eg through -# > anti-spam solutions) must register with dnswl.org and purchase a subscription. echo "Installing Postfix (SMTP server)..." apt_install postfix postfix-sqlite postfix-pcre postgrey ca-certificates diff --git a/setup/start.sh b/setup/start.sh index 9d64bba5..671f4449 100755 --- a/setup/start.sh +++ b/setup/start.sh @@ -4,7 +4,7 @@ source setup/functions.sh # load our functions -# Check system setup: Are we running as root on Ubuntu 14.04 on a +# Check system setup: Are we running as root on Ubuntu 18.04 on a # machine with enough memory? Is /tmp mounted with exec. # If not, this shows an error and exits. source setup/preflight.sh diff --git a/setup/system.sh b/setup/system.sh index 29ee97bd..32cf9987 100755 --- a/setup/system.sh +++ b/setup/system.sh @@ -134,8 +134,8 @@ hide_output apt-get update fi # ### 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. +# When Ubuntu 20 comes out, we don't want users to be prompted to upgrade, +# because we don't yet support it. if [ -f /etc/update-manager/release-upgrades ]; then tools/editconf.py /etc/update-manager/release-upgrades Prompt=never rm -f /var/lib/ubuntu-release-upgrader/release-upgrade-available @@ -182,7 +182,6 @@ fi # * 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 diff --git a/setup/web.sh b/setup/web.sh index 021a9b7f..7f9b1638 100755 --- a/setup/web.sh +++ b/setup/web.sh @@ -21,7 +21,7 @@ echo "Installing Nginx (web server)..." apt_install nginx php7.0-cli php7.0-fpm -# Set PHP7 as the default +# Set PHP7.0 as the default since several versions are available. update-alternatives --set php /usr/bin/php7.0 rm -f /etc/nginx/sites-enabled/default From f6a641ad2318d1f28fc9910359e6b7e69e417b75 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 24 Aug 2018 08:47:18 -0400 Subject: [PATCH 004/190] remove some cleanup steps that are no longer needed since we aren't supporting upgrades of existing machines and, even if we did, we aren't supporting upgrades from really old versions of Mail-in-a-Box --- setup/management.sh | 5 ----- setup/web.sh | 15 --------------- setup/webmail.sh | 5 ----- 3 files changed, 25 deletions(-) diff --git a/setup/management.sh b/setup/management.sh index 1e29ab8c..3c3357ca 100755 --- a/setup/management.sh +++ b/setup/management.sh @@ -87,7 +87,6 @@ rm -f /tmp/bootstrap.zip # Create an init script to start the management daemon and keep it # running after a reboot. -rm -f /usr/local/bin/mailinabox-daemon /etc/init.d/mailinabox # old paths cat > $inst_dir/start < Date: Fri, 24 Aug 2018 08:46:08 -0400 Subject: [PATCH 005/190] update to PHP 7.2 * drop the ondrej/php PPA since PHP 7.x is available directly from Ubuntu 18.04 * intall PHP 7.2 which is just the "php" package in Ubuntu 18.04 * some package names changed, some unnecessary packages are no longer provided * update paths --- conf/nginx-top.conf | 2 +- management/backup.py | 4 ++-- setup/nextcloud.sh | 20 ++++++++++---------- setup/system.sh | 12 ------------ setup/web.sh | 15 ++++++--------- setup/webmail.sh | 8 ++++---- setup/zpush.sh | 6 +++--- tools/owncloud-restore.sh | 6 ++---- 8 files changed, 28 insertions(+), 45 deletions(-) diff --git a/conf/nginx-top.conf b/conf/nginx-top.conf index a5822a82..4d888366 100644 --- a/conf/nginx-top.conf +++ b/conf/nginx-top.conf @@ -7,6 +7,6 @@ ## your own --- please do not ask for help from us. upstream php-fpm { - server unix:/var/run/php/php7.0-fpm.sock; + server unix:/var/run/php/php7.2-fpm.sock; } diff --git a/management/backup.py b/management/backup.py index 8081a267..78af6a19 100755 --- a/management/backup.py +++ b/management/backup.py @@ -267,7 +267,7 @@ def perform_backup(full_backup): if quit: sys.exit(code) - service_command("php7.0-fpm", "stop", quit=True) + service_command("php7.2-fpm", "stop", quit=True) service_command("postfix", "stop", quit=True) service_command("dovecot", "stop", quit=True) @@ -301,7 +301,7 @@ def perform_backup(full_backup): # Start services again. service_command("dovecot", "start", quit=False) service_command("postfix", "start", quit=False) - service_command("php7.0-fpm", "start", quit=False) + service_command("php7.2-fpm", "start", quit=False) # Once the migrated backup is included in a new backup, it can be deleted. if os.path.isdir(migrated_unencrypted_backup_dir): diff --git a/setup/nextcloud.sh b/setup/nextcloud.sh index ed67dbf3..0105668c 100755 --- a/setup/nextcloud.sh +++ b/setup/nextcloud.sh @@ -11,9 +11,9 @@ echo "Installing Nextcloud (contacts/calendar)..." apt-get purge -qq -y owncloud* # we used to use the package manager -apt_install php7.0 php7.0-fpm \ - php7.0-cli php7.0-sqlite php7.0-gd php7.0-imap php7.0-curl php-pear php-apc curl \ - php7.0-dev php7.0-gd php7.0-xml php7.0-mbstring php7.0-zip php7.0-apcu php7.0-json php7.0-intl +apt_install php php-fpm \ + php-cli php-sqlite3 php-gd php-imap php-curl php-pear curl \ + php-dev php-gd php-xml php-mbstring php-zip php-apcu php-json php-intl InstallNextcloud() { @@ -83,7 +83,7 @@ if [ ! -d /usr/local/lib/owncloud/ ] \ || ! grep -q $nextcloud_ver /usr/local/lib/owncloud/version.php; then # Stop php-fpm if running. If theyre not running (which happens on a previously failed install), dont bail. - service php7.0-fpm stop &> /dev/null || /bin/true + service php7.2-fpm stop &> /dev/null || /bin/true # Backup the existing ownCloud/Nextcloud. # Create a backup directory to store the current installation and database to @@ -244,7 +244,7 @@ if [ \( $? -ne 0 \) -a \( $? -ne 3 \) ]; then exit 1; fi # 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/7.0/fpm/php.ini -c ';' \ +tools/editconf.py /etc/php/7.2/fpm/php.ini -c ';' \ upload_max_filesize=16G \ post_max_size=16G \ output_buffering=16384 \ @@ -253,7 +253,7 @@ tools/editconf.py /etc/php/7.0/fpm/php.ini -c ';' \ short_open_tag=On # Set Nextcloud recommended opcache settings -tools/editconf.py /etc/php/7.0/cli/conf.d/10-opcache.ini -c ';' \ +tools/editconf.py /etc/php/7.2/cli/conf.d/10-opcache.ini -c ';' \ opcache.enable=1 \ opcache.enable_cli=1 \ opcache.interned_strings_buffer=8 \ @@ -263,12 +263,12 @@ tools/editconf.py /etc/php/7.0/cli/conf.d/10-opcache.ini -c ';' \ opcache.revalidate_freq=1 # Configure the path environment for php-fpm -tools/editconf.py /etc/php/7.0/fpm/pool.d/www.conf -c ';' \ +tools/editconf.py /etc/php/7.2/fpm/pool.d/www.conf -c ';' \ env[PATH]=/usr/local/bin:/usr/bin:/bin # If apc is explicitly disabled we need to enable it -if grep -q apc.enabled=0 /etc/php/7.0/mods-available/apcu.ini; then - tools/editconf.py /etc/php/7.0/mods-available/apcu.ini -c ';' \ +if grep -q apc.enabled=0 /etc/php/7.2/mods-available/apcu.ini; then + tools/editconf.py /etc/php/7.2/mods-available/apcu.ini -c ';' \ apc.enabled=1 fi @@ -290,4 +290,4 @@ chmod +x /etc/cron.hourly/mailinabox-owncloud # ``` # Enable PHP modules and restart PHP. -restart_service php7.0-fpm +restart_service php7.2-fpm diff --git a/setup/system.sh b/setup/system.sh index 32cf9987..5dea1f45 100755 --- a/setup/system.sh +++ b/setup/system.sh @@ -121,18 +121,6 @@ apt_install python3 python3-dev python3-pip \ haveged pollinate unzip \ unattended-upgrades cron ntp fail2ban -# ### Add PHP7 PPA - -# Nextcloud requires PHP7, we will install the ppa from ubuntu php maintainer Ondřej Surý -# The PPA is located here https://launchpad.net/%7Eondrej/+archive/ubuntu/php -# Unattended upgrades are activated for the repository If it appears it's already -# installed, don't do it again so we can avoid an unnecessary call to apt-get update. -if [ ! -f /etc/apt/sources.list.d/ondrej-php-trusty.list ]; then -hide_output add-apt-repository -y ppa:ondrej/php -apt_add_repository_to_unattended_upgrades LP-PPA-ondrej-php:trusty -hide_output apt-get update -fi - # ### Suppress Upgrade Prompts # When Ubuntu 20 comes out, we don't want users to be prompted to upgrade, # because we don't yet support it. diff --git a/setup/web.sh b/setup/web.sh index 45227b81..ed37e5e3 100755 --- a/setup/web.sh +++ b/setup/web.sh @@ -19,10 +19,7 @@ fi echo "Installing Nginx (web server)..." -apt_install nginx php7.0-cli php7.0-fpm - -# Set PHP7.0 as the default since several versions are available. -update-alternatives --set php /usr/bin/php7.0 +apt_install nginx php-cli php-fpm rm -f /etc/nginx/sites-enabled/default @@ -44,19 +41,19 @@ tools/editconf.py /etc/nginx/nginx.conf -s \ server_names_hash_bucket_size="128;" # Tell PHP not to expose its version number in the X-Powered-By header. -tools/editconf.py /etc/php/7.0/fpm/php.ini -c ';' \ +tools/editconf.py /etc/php/7.2/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/7.0/fpm/php.ini -c ';' \ +tools/editconf.py /etc/php/7.2/fpm/php.ini -c ';' \ default_charset="UTF-8" # Switch from the dynamic process manager to the ondemand manager see #1216 -tools/editconf.py /etc/php/7.0/fpm/pool.d/www.conf -c ';' \ +tools/editconf.py /etc/php/7.2/fpm/pool.d/www.conf -c ';' \ pm=ondemand # Bump up PHP's max_children to support more concurrent connections -tools/editconf.py /etc/php/7.0/fpm/pool.d/www.conf -c ';' \ +tools/editconf.py /etc/php/7.2/fpm/pool.d/www.conf -c ';' \ pm.max_children=8 # Other nginx settings will be configured by the management service @@ -96,7 +93,7 @@ chown -R $STORAGE_USER $STORAGE_ROOT/www # Start services. restart_service nginx -restart_service php7.0-fpm +restart_service php7.2-fpm # Open ports. ufw_allow http diff --git a/setup/webmail.sh b/setup/webmail.sh index 9b2591e2..58743a72 100755 --- 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 \ - php7.0-cli php7.0-sqlite php7.0-mcrypt php7.0-intl php7.0-json php7.0-common php7.0-curl \ - php7.0-gd php7.0-pspell tinymce libjs-jquery libjs-jquery-mousewheel libmagic1 php7.0-mbstring + php-cli php-sqlite3 php-intl php-json php-common php-curl \ + php-gd php-pspell tinymce libjs-jquery libjs-jquery-mousewheel libmagic1 php-mbstring # Install Roundcube from source if it is not already present or if it is out of date. # Combine the Roundcube version number with the commit hash of plugins to track @@ -193,5 +193,5 @@ chown www-data:www-data $STORAGE_ROOT/mail/roundcube/roundcube.sqlite chmod 664 $STORAGE_ROOT/mail/roundcube/roundcube.sqlite # Enable PHP modules. -phpenmod -v php7.0 mcrypt imap -restart_service php7.0-fpm +phpenmod -v php mcrypt imap +restart_service php7.2-fpm diff --git a/setup/zpush.sh b/setup/zpush.sh index 84ab4556..32fc4992 100755 --- a/setup/zpush.sh +++ b/setup/zpush.sh @@ -17,9 +17,9 @@ source /etc/mailinabox.conf # load global vars echo "Installing Z-Push (Exchange/ActiveSync server)..." apt_install \ - php7.0-soap php7.0-imap libawl-php php7.0-xsl + php-soap php-imap libawl-php php-xsl -phpenmod -v php7.0 imap +phpenmod -v php imap # Copy Z-Push into place. VERSION=2.4.4 @@ -102,7 +102,7 @@ EOF # Restart service. -restart_service php7.0-fpm +restart_service php7.2-fpm # Fix states after upgrade diff --git a/tools/owncloud-restore.sh b/tools/owncloud-restore.sh index 1b006ca1..c93a322c 100755 --- a/tools/owncloud-restore.sh +++ b/tools/owncloud-restore.sh @@ -26,8 +26,7 @@ if [ ! -f $1/config.php ]; then fi echo "Restoring backup from $1" -service php5-fpm stop -service php7.0-fpm stop +service php7.2-fpm stop # remove the current ownCloud/Nextcloud installation rm -rf /usr/local/lib/owncloud/ @@ -46,6 +45,5 @@ chown www-data.www-data $STORAGE_ROOT/owncloud/config.php sudo -u www-data php /usr/local/lib/owncloud/occ maintenance:mode --off -service php5-fpm start -service php7.0-fpm start +service php7.2-fpm start echo "Done" From bc4bdca752892f66c99adeeaa827db6595e2905f Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Sat, 29 Sep 2018 20:11:48 -0400 Subject: [PATCH 006/190] update reference to Ubuntu 14.04 to 18.04 in README.md and security.md and drop mentions of our custom packages that we no longer maintain --- README.md | 5 ++--- security.md | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d7caacfa..2f5c2b75 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Additionally, this project has a [Code of Conduct](CODE_OF_CONDUCT.md), which su The Box ------- -Mail-in-a-Box turns a fresh Ubuntu 14.04 LTS 64-bit machine into a working mail server by installing and configuring various components. +Mail-in-a-Box turns a fresh Ubuntu 18.04 LTS 64-bit machine into a working mail server by installing and configuring various components. It is a one-click email appliance. There are no user-configurable setup options. It "just works". @@ -37,7 +37,6 @@ The components installed are: It also includes: * A control panel and API for adding/removing mail users, aliases, custom DNS records, etc. and detailed system monitoring. -* Our own builds of postgrey (adding better whitelisting) and dovecot-lucene (faster search for mail) distributed via the [Mail-in-a-Box PPA](https://launchpad.net/~mail-in-a-box/+archive/ubuntu/ppa) on Launchpad. For more information on how Mail-in-a-Box handles your privacy, see the [security details page](security.md). @@ -46,7 +45,7 @@ Installation See the [setup guide](https://mailinabox.email/guide.html) for detailed, user-friendly instructions. -For experts, start with a completely fresh (really, I mean it) Ubuntu 14.04 LTS 64-bit machine. On the machine... +For experts, start with a completely fresh (really, I mean it) Ubuntu 18.04 LTS 64-bit machine. On the machine... Clone this repository: diff --git a/security.md b/security.md index 5a797673..b8dc35af 100644 --- a/security.md +++ b/security.md @@ -1,7 +1,7 @@ Mail-in-a-Box Security Guide ============================ -Mail-in-a-Box turns a fresh Ubuntu 14.04 LTS 64-bit machine into a mail server appliance by installing and configuring various components. +Mail-in-a-Box turns a fresh Ubuntu 18.04 LTS 64-bit machine into a mail server appliance by installing and configuring various components. This page documents the security features of Mail-in-a-Box. The term “box” is used below to mean a configured Mail-in-a-Box. From 3dbd6c994ada76537f1d823d644403f671d5de4d Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Wed, 3 Oct 2018 14:28:43 -0400 Subject: [PATCH 007/190] update bind9 configuration --- management/munin_start.sh | 0 setup/mail-postfix.sh | 2 +- setup/system.sh | 62 +++++++++++++++++++++++++++------------ 3 files changed, 44 insertions(+), 20 deletions(-) mode change 100644 => 100755 management/munin_start.sh diff --git a/management/munin_start.sh b/management/munin_start.sh old mode 100644 new mode 100755 diff --git a/setup/mail-postfix.sh b/setup/mail-postfix.sh index 11a2b307..c3183ef0 100755 --- a/setup/mail-postfix.sh +++ b/setup/mail-postfix.sh @@ -146,7 +146,7 @@ tools/editconf.py /etc/postfix/main.cf \ # then opportunistic TLS is used. Otherwise the server certificate must match the TLSA records # or else the mail bounces. TLSA also requires DNSSEC on the MX host. Postfix doesn't do DNSSEC # itself but assumes the system's nameserver does and reports DNSSEC status. Thus this also -# relies on our local bind9 server being present and `smtp_dns_support_level=dnssec`. +# relies on our local DNS server (see system.sh) and `smtp_dns_support_level=dnssec`. # # The `smtp_tls_CAfile` is superflous, but it eliminates warnings in the logs about untrusted certs, # which we don't care about seeing because Postfix is doing opportunistic TLS anyway. Better to encrypt, diff --git a/setup/system.sh b/setup/system.sh index 5dea1f45..2fecac8e 100755 --- a/setup/system.sh +++ b/setup/system.sh @@ -264,45 +264,69 @@ fi #NODOC # ### Local DNS Service -# Install a local DNS server, rather than using the DNS server provided by the -# ISP's network configuration. +# Install a local recursive DNS server --- i.e. for DNS queries made by +# local services running on this machine. # -# 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". +# (This is unrelated to the box's public, non-recursive DNS server that +# answers remote queries about domain names hosted on this box. For that +# see dns.sh.) # -# 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. +# The default systemd-resolved service provides local DNS name resolution. By default it +# is a recursive stub nameserver, which means it simply relays requests to an +# external nameserver, usually provided by your ISP or configured in /etc/systemd/resolved.conf. +# +# This won't work for us for three reasons. +# +# 1) We have higher security goals --- we want DNSSEC to be enforced on all +# DNS queries (some upstream DNS servers do, some don't). +# 2) We will configure postfix to use DANE, which uses DNSSEC to find TLS +# certificates for remote servers. DNSSEC validation *must* be performed +# locally because we can't trust an unencrypted connection to an external +# DNS server. +# 3) DNS-based mail server blacklists (RBLs) typically block large ISP +# DNS servers because they only provide free data to small users. Since +# we use RBLs to block incoming mail from blacklisted IP addresses, +# we have to run our own DNS server. See #1424. +# +# systemd-resolved has a setting to perform local DNSSEC validation on all +# requests (in /etc/systemd/resolved.conf, set DNSSEC=yes), but because it's +# a stub server the main part of a request still goes through an upstream +# DNS server, which won't work for RBLs. So we really need a local recursive +# nameserver. +# +# We'll install `bind9`, which as packaged for Ubuntu, has DNSSEC enabled by default via "dnssec-validation auto". +# We'll have it be bound to 127.0.0.1 so that it does not interfere with +# the public, recursive nameserver `nsd` bound to the public ethernet interfaces. # # 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 +apt_install bind9 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 + +# First we'll disable systemd-resolved's management of resolv.conf and its stub server. +# Breaking the symlink to /run/systemd/resolve/stub-resolv.conf means +# systemd-resolved will read it for DNS servers to use. Put in 127.0.0.1, +# which is where bind9 will be running. Obviously don't do this before +# installing bind9 or else apt won't be able to resolve a server to +# download bind9 from. +rm -f /etc/resolv.conf +tools/editconf.py /etc/systemd/resolved.conf DNSStubListener=no +echo "127.0.0.1" > /etc/resolv.conf # Restart the DNS services. restart_service bind9 -restart_service resolvconf +systemctl restart systemd-resolved # ### Fail2Ban Service From f7396623922f87eba4de0ae3b52bf39b0e1afaa8 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Sat, 13 Oct 2018 16:16:30 -0400 Subject: [PATCH 008/190] duplicity started creating signature files with invalid filenames, fixes #1431 --- management/backup.py | 34 +++++++++++++++---------- management/templates/system-backup.html | 1 + 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/management/backup.py b/management/backup.py index 78af6a19..957ab11f 100755 --- a/management/backup.py +++ b/management/backup.py @@ -20,20 +20,17 @@ rsync_ssh_options = [ ] def backup_status(env): - # Root folder - backup_root = os.path.join(env["STORAGE_ROOT"], 'backup') - - # What is the current status of backups? - # Query duplicity to get a list of all backups. - # Use the number of volumes to estimate the size. + # If backups are dissbled, return no status. config = get_backup_config(env) - now = datetime.datetime.now(dateutil.tz.tzlocal()) - - # Are backups dissbled? if config["target"] == "off": return { } + # Query duplicity to get a list of all full and incremental + # backups available. + backups = { } + now = datetime.datetime.now(dateutil.tz.tzlocal()) + backup_root = os.path.join(env["STORAGE_ROOT"], 'backup') backup_cache_dir = os.path.join(backup_root, 'cache') def reldate(date, ref, clip): @@ -58,7 +55,7 @@ def backup_status(env): "date_delta": reldate(date, now, "the future?"), "full": keys[0] == "full", "size": 0, # collection-status doesn't give us the size - "volumes": keys[2], # number of archive volumes for this backup (not really helpful) + "volumes": int(keys[2]), # number of archive volumes for this backup (not really helpful) } code, collection_status = shell('check_output', [ @@ -80,12 +77,20 @@ def backup_status(env): backup = parse_line(line) backups[backup["date"]] = backup - # Look at the target to get the sizes of each of the backups. There is more than one file per backup. + # Look at the target directly to get the sizes of each of the backups. There is more than one file per backup. + # Starting with duplicity in Ubuntu 18.04, "signatures" files have dates in their + # filenames that are a few seconds off the backup date and so don't line up + # with the list of backups we have. Track unmatched files so we know how much other + # space is used for those. + unmatched_file_size = 0 for fn, size in list_target_files(config): m = re.match(r"duplicity-(full|full-signatures|(inc|new-signatures)\.(?P\d+T\d+Z)\.to)\.(?P\d+T\d+Z)\.", fn) if not m: continue # not a part of a current backup chain key = m.group("date") - backups[key]["size"] += size + if key in backups: + backups[key]["size"] += size + else: + unmatched_file_size += size # Ensure the rows are sorted reverse chronologically. # This is relied on by should_force_full() and the next step. @@ -148,6 +153,7 @@ def backup_status(env): return { "backups": backups, + "unmatched_file_size": unmatched_file_size, } def should_force_full(config, env): @@ -556,8 +562,7 @@ if __name__ == "__main__": run_duplicity_verification() elif sys.argv[-1] == "--list": - # Run duplicity's verification command to check a) the backup files - # are readable, and b) report if they are up to date. + # List the saved backup files. for fn, size in list_target_files(get_backup_config(load_environment())): print("{}\t{}".format(fn, size)) @@ -565,6 +570,7 @@ if __name__ == "__main__": # Show backup status. ret = backup_status(load_environment()) print(rtyaml.dump(ret["backups"])) + print("Storage for unmatched files:", ret["unmatched_file_size"]) elif len(sys.argv) >= 2 and sys.argv[1] == "--restore": # Run duplicity restore. Rest of command line passed as arguments diff --git a/management/templates/system-backup.html b/management/templates/system-backup.html index 0ccb4bd6..be528f19 100644 --- a/management/templates/system-backup.html +++ b/management/templates/system-backup.html @@ -200,6 +200,7 @@ function show_system_backup() { total_disk_size += b.size; } + total_disk_size += r.unmatched_file_size; $('#backup-total-size').text(nice_size(total_disk_size)); }) } From c9b3d88108cbe154fb70c3dcb61cf86fb340dab2 Mon Sep 17 00:00:00 2001 From: jvolkenant Date: Wed, 24 Oct 2018 14:20:48 -0700 Subject: [PATCH 009/190] Fixes #1437 - package python-virtualenv is now called just virtualenv (#1452) --- setup/management.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup/management.sh b/setup/management.sh index 3c3357ca..9c221198 100755 --- a/setup/management.sh +++ b/setup/management.sh @@ -24,12 +24,12 @@ done # S3 api used in some regions, which breaks backups to those regions. # See #627, #653. # -# python-virtualenv is used to isolate the Python 3 packages we +# virtualenv is used to isolate the Python 3 packages we # install via pip from the system-installed packages. # # certbot installs EFF's certbot which we use to # provision free TLS certificates. -apt_install duplicity python-pip python-virtualenv certbot +apt_install duplicity python-pip virtualenv certbot hide_output pip2 install --upgrade boto # Create a virtualenv for the installation of Python 3 packages From 8d5670068ac1fe077b575a51a8813c3b357f0541 Mon Sep 17 00:00:00 2001 From: jvolkenant Date: Thu, 25 Oct 2018 12:18:21 -0700 Subject: [PATCH 010/190] fixes nginx warning about duplicate ssl configuration (#1460) --- conf/nginx-ssl.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/nginx-ssl.conf b/conf/nginx-ssl.conf index d4dc619b..1aba45c3 100644 --- a/conf/nginx-ssl.conf +++ b/conf/nginx-ssl.conf @@ -1,6 +1,6 @@ # We track the Mozilla "intermediate" compatibility TLS recommendations. # Note that these settings are repeated in the SMTP and IMAP configuration. -ssl_protocols TLSv1.2 TLSv1.1 TLSv1; +# ssl_protocols has moved to nginx.conf in bionic, check there for enabled protocols. ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; ssl_dhparam STORAGE_ROOT/ssl/dh2048.pem; From 86e2cfb6c84ca3d1701a89958e316d91b98b4162 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Sat, 20 Oct 2018 10:20:43 -0400 Subject: [PATCH 011/190] remove old duplicity migration code from 2015, see 42322455 --- management/backup.py | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/management/backup.py b/management/backup.py index 957ab11f..e15fbbbf 100755 --- a/management/backup.py +++ b/management/backup.py @@ -226,32 +226,6 @@ def perform_backup(full_backup): if config["target"] == "off": return - # In an older version of this script, duplicity was called - # such that it did not encrypt the backups it created (in - # backup/duplicity), and instead openssl was called separately - # after each backup run, creating AES256 encrypted copies of - # each file created by duplicity in backup/encrypted. - # - # We detect the transition by the presence of backup/duplicity - # and handle it by 'dupliception': we move all the old *un*encrypted - # duplicity files up out of the backup/duplicity directory (as - # backup/ is excluded from duplicity runs) in order that it is - # included in the next run, and we delete backup/encrypted (which - # duplicity will output files directly to, post-transition). - old_backup_dir = os.path.join(backup_root, 'duplicity') - migrated_unencrypted_backup_dir = os.path.join(env["STORAGE_ROOT"], "migrated_unencrypted_backup") - if os.path.isdir(old_backup_dir): - # Move the old unencrypted files to a new location outside of - # the backup root so they get included in the next (new) backup. - # Then we'll delete them. Also so that they do not get in the - # way of duplicity doing a full backup on the first run after - # we take care of this. - shutil.move(old_backup_dir, migrated_unencrypted_backup_dir) - - # The backup_dir (backup/encrypted) now has a new purpose. - # Clear it out. - shutil.rmtree(backup_dir) - # On the first run, always do a full backup. Incremental # will fail. Otherwise do a full backup when the size of # the increments since the most recent full backup are @@ -309,10 +283,6 @@ def perform_backup(full_backup): service_command("postfix", "start", quit=False) service_command("php7.2-fpm", "start", quit=False) - # Once the migrated backup is included in a new backup, it can be deleted. - if os.path.isdir(migrated_unencrypted_backup_dir): - shutil.rmtree(migrated_unencrypted_backup_dir) - # Remove old backups. This deletes all backup data no longer needed # from more than 3 days ago. shell('check_call', [ From 7f8f4518e3a8980e31ad745b7665371555942086 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 30 Nov 2018 09:30:55 -0500 Subject: [PATCH 012/190] document password character limitation fixes #407 --- management/templates/users.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/management/templates/users.html b/management/templates/users.html index cf944c86..c70ac0da 100644 --- a/management/templates/users.html +++ b/management/templates/users.html @@ -31,7 +31,7 @@
    -
  • Passwords must be at least eight characters and may not contain spaces. For best results, generate a random password.
  • +
  • Passwords must be at least eight characters consisting of English lettters and numbers only. For best results, generate a random password.
  • Use aliases to create email addresses that forward to existing accounts.
  • Administrators get access to this control panel.
  • User accounts cannot contain any international (non-ASCII) characters, but aliases can.
  • From b05b06c74af92463d3187d8cb97208400291cb40 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 30 Nov 2018 09:33:24 -0500 Subject: [PATCH 013/190] remove user account mailbox size from the control panel because it takes way too long to compute on very large mailboxes fixes #531 --- management/daemon.py | 2 +- management/mailconfig.py | 7 +------ management/templates/users.html | 4 ---- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/management/daemon.py b/management/daemon.py index 2e23c8aa..334749e4 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -146,7 +146,7 @@ def me(): @authorized_personnel_only def mail_users(): if request.args.get("format", "") == "json": - return json_response(get_mail_users_ex(env, with_archived=True, with_slow_info=True)) + return json_response(get_mail_users_ex(env, with_archived=True)) else: return "".join(x+"\n" for x in get_mail_users(env)) diff --git a/management/mailconfig.py b/management/mailconfig.py index 82c922e4..28e1c623 100755 --- a/management/mailconfig.py +++ b/management/mailconfig.py @@ -105,7 +105,7 @@ def get_mail_users(env): users = [ row[0] for row in c.fetchall() ] return utils.sort_email_addresses(users, env) -def get_mail_users_ex(env, with_archived=False, with_slow_info=False): +def get_mail_users_ex(env, with_archived=False): # Returns a complex data structure of all user accounts, optionally # including archived (status="inactive") accounts. # @@ -139,9 +139,6 @@ def get_mail_users_ex(env, with_archived=False, with_slow_info=False): } users.append(user) - if with_slow_info: - user["mailbox_size"] = utils.du(os.path.join(env['STORAGE_ROOT'], 'mail/mailboxes', *reversed(email.split("@")))) - # Add in archived accounts. if with_archived: root = os.path.join(env['STORAGE_ROOT'], 'mail/mailboxes') @@ -158,8 +155,6 @@ def get_mail_users_ex(env, with_archived=False, with_slow_info=False): "mailbox": mbox, } users.append(user) - if with_slow_info: - user["mailbox_size"] = utils.du(mbox) # Group by domain. domains = { } diff --git a/management/templates/users.html b/management/templates/users.html index c70ac0da..dee79d42 100644 --- a/management/templates/users.html +++ b/management/templates/users.html @@ -43,7 +43,6 @@ Email Address Actions - Mailbox Size @@ -73,8 +72,6 @@ archive account - - @@ -156,7 +153,6 @@ function show_users() { n.attr('data-email', user.email); n.find('.address').text(user.email) - n.find('.mailboxsize').text(nice_size(user.mailbox_size)) n2.find('.restore_info tt').text(user.mailbox); if (user.status == 'inactive') continue; From aa52f52d02c39826e2c5990901b111e5f18049c0 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 30 Nov 2018 09:49:00 -0500 Subject: [PATCH 014/190] disable SMTP AUTH on port 25 to stop it accidentally being used for submission fixes #830 --- setup/mail-postfix.sh | 3 +++ setup/mail-users.sh | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/setup/mail-postfix.sh b/setup/mail-postfix.sh index c3183ef0..0c9bc97c 100755 --- a/setup/mail-postfix.sh +++ b/setup/mail-postfix.sh @@ -73,6 +73,8 @@ tools/editconf.py /etc/postfix/main.cf \ # Enable the 'submission' port 587 smtpd server and tweak its settings. # +# * Enable authentication. It's disabled globally so that it is disabled on port 25, +# so we need to explicitly enable it here. # * Do not add the OpenDMAC Authentication-Results header. That should only be added # on incoming mail. Omit the OpenDMARC milter by re-setting smtpd_milters to the # OpenDKIM milter only. See dkim.sh. @@ -87,6 +89,7 @@ tools/editconf.py /etc/postfix/main.cf \ # emails but we turn this off by setting nested_header_checks empty. tools/editconf.py /etc/postfix/master.cf -s -w \ "submission=inet n - - - - smtpd + -o smtpd_sasl_auth_enable=yes -o syslog_name=postfix/submission -o smtpd_milters=inet:127.0.0.1:8891 -o smtpd_tls_security_level=encrypt diff --git a/setup/mail-users.sh b/setup/mail-users.sh index ef9b8118..e54485bb 100755 --- a/setup/mail-users.sh +++ b/setup/mail-users.sh @@ -65,11 +65,15 @@ service auth { } EOF -# And have Postfix use that service. +# And have Postfix use that service. We *disable* it here +# so that authentication is not permitted on port 25 (which +# does not run DKIM on relayed mail, so outbound mail isn't +# correct, see #830), but we enable it specifically for the +# submission port. tools/editconf.py /etc/postfix/main.cf \ smtpd_sasl_type=dovecot \ smtpd_sasl_path=private/auth \ - smtpd_sasl_auth_enable=yes + smtpd_sasl_auth_enable=no # ### Sender Validation From e5e0c643956966cf8a4935ae4377aa83f4d0fb11 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 30 Nov 2018 10:24:19 -0500 Subject: [PATCH 015/190] turn on bash strict mode to better catch setup errors fixes #893 --- setup/firstuser.sh | 2 +- setup/functions.sh | 23 ++++++++++++++++++----- setup/munin.sh | 2 +- setup/preflight.sh | 2 +- setup/questions.sh | 36 ++++++++++++++++++------------------ setup/start.sh | 4 ++-- setup/system.sh | 12 ++++++------ 7 files changed, 47 insertions(+), 34 deletions(-) diff --git a/setup/firstuser.sh b/setup/firstuser.sh index d24fc370..71264f3b 100644 --- a/setup/firstuser.sh +++ b/setup/firstuser.sh @@ -6,7 +6,7 @@ if [ -z "`tools/mail.py user`" ]; then # If we didn't ask for an email address at the start, do so now. if [ -z "$EMAIL_ADDR" ]; then # In an interactive shell, ask the user for an email address. - if [ -z "$NONINTERACTIVE" ]; then + if [ -z "${NONINTERACTIVE:-}" ]; then input_box "Mail Account" \ "Let's create your first mail account. \n\nWhat email address do you want?" \ diff --git a/setup/functions.sh b/setup/functions.sh index 75c6821e..1a74edfd 100644 --- a/setup/functions.sh +++ b/setup/functions.sh @@ -1,3 +1,9 @@ +# 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. +# -o pipefail: don't ignore errors in the non-last command in a pipeline +set -euo pipefail + function hide_output { # This function hides the output of a command unless the command fails # and returns a non-zero exit code. @@ -5,11 +11,14 @@ function hide_output { # Get a temporary file. OUTPUT=$(tempfile) - # Execute command, redirecting stderr/stdout to the temporary file. + # Execute command, redirecting stderr/stdout to the temporary file. Since we + # check the return code ourselves, disable 'set -e' temporarily. + set +e $@ &> $OUTPUT + E=$? + set -e # If the command failed, show the output that was captured in the temporary file. - E=$? if [ $E != 0 ]; then # Something failed. echo @@ -75,7 +84,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 + curl -$1 --fail --silent --max-time 15 icanhazip.com 2>/dev/null || /bin/true } function get_default_privateip { @@ -131,11 +140,10 @@ function get_default_privateip { fi echo $address - } function ufw_allow { - if [ -z "$DISABLE_FIREWALL" ]; then + if [ -z "${DISABLE_FIREWALL:-}" ]; then # ufw has completely unhelpful output ufw allow $1 > /dev/null; fi @@ -154,10 +162,13 @@ function input_box { # input_box "title" "prompt" "defaultvalue" VARIABLE # The user's input will be stored in the variable VARIABLE. # The exit code from dialog will be stored in VARIABLE_EXITCODE. + # Temporarily turn off 'set -e' because we need the dialog return code. declare -n result=$4 declare -n result_code=$4_EXITCODE + set +e result=$(dialog --stdout --title "$1" --inputbox "$2" 0 0 "$3") result_code=$? + set -e } function input_menu { @@ -167,8 +178,10 @@ function input_menu { declare -n result=$4 declare -n result_code=$4_EXITCODE local IFS=^$'\n' + set +e result=$(dialog --stdout --title "$1" --menu "$2" 0 0 0 $3) result_code=$? + set -e } function wget_verify { diff --git a/setup/munin.sh b/setup/munin.sh index 2529ba4c..c581c6d0 100755 --- a/setup/munin.sh +++ b/setup/munin.sh @@ -29,7 +29,7 @@ address 127.0.0.1 # send alerts to the following address contacts admin -contact.admin.command mail -s "Munin notification ${var:host}" administrator@$PRIMARY_HOSTNAME +contact.admin.command mail -s "Munin notification \${var:host}" administrator@$PRIMARY_HOSTNAME contact.admin.always_send warning critical EOF diff --git a/setup/preflight.sh b/setup/preflight.sh index 7466b857..d087efe2 100644 --- a/setup/preflight.sh +++ b/setup/preflight.sh @@ -41,7 +41,7 @@ if [ $TOTAL_PHYSICAL_MEM -lt 750000 ]; then fi # Check that tempfs is mounted with exec -MOUNTED_TMP_AS_NO_EXEC=$(grep "/tmp.*noexec" /proc/mounts) +MOUNTED_TMP_AS_NO_EXEC=$(grep "/tmp.*noexec" /proc/mounts || /bin/true) if [ -n "$MOUNTED_TMP_AS_NO_EXEC" ]; then echo "Mail-in-a-Box has to have exec rights on /tmp, please mount /tmp with exec" exit diff --git a/setup/questions.sh b/setup/questions.sh index 3d227d81..3ad7eead 100644 --- a/setup/questions.sh +++ b/setup/questions.sh @@ -1,4 +1,4 @@ -if [ -z "$NONINTERACTIVE" ]; then +if [ -z "${NONINTERACTIVE:-}" ]; then # Install 'dialog' so we can ask the user questions. The original motivation for # this was being able to ask the user for input even if stdin has been redirected, # e.g. if we piped a bootstrapping install script to bash to get started. In that @@ -25,8 +25,8 @@ if [ -z "$NONINTERACTIVE" ]; then fi # The box needs a name. -if [ -z "$PRIMARY_HOSTNAME" ]; then - if [ -z "$DEFAULT_PRIMARY_HOSTNAME" ]; then +if [ -z "${PRIMARY_HOSTNAME:-}" ]; then + if [ -z "${DEFAULT_PRIMARY_HOSTNAME:-}" ]; then # We recommend to use box.example.com as this hosts name. The # domain the user possibly wants to use is example.com then. # We strip the string "box." from the hostname to get the mail @@ -86,7 +86,7 @@ fi # If the machine is behind a NAT, inside a VM, etc., it may not know # its IP address on the public network / the Internet. Ask the Internet # and possibly confirm with user. -if [ -z "$PUBLIC_IP" ]; then +if [ -z "${PUBLIC_IP:-}" ]; then # Ask the Internet. GUESSED_IP=$(get_publicip_from_web_service 4) @@ -105,11 +105,11 @@ if [ -z "$PUBLIC_IP" ]; then PUBLIC_IP=$GUESSED_IP fi - if [ -z "$PUBLIC_IP" ]; then + if [ -z "${PUBLIC_IP:-}" ]; then input_box "Public IP Address" \ "Enter the public IP address of this machine, as given to you by your ISP. \n\nPublic IP address:" \ - $DEFAULT_PUBLIC_IP \ + ${DEFAULT_PUBLIC_IP:-} \ PUBLIC_IP if [ -z "$PUBLIC_IP" ]; then @@ -121,27 +121,27 @@ fi # Same for IPv6. But it's optional. Also, if it looks like the system # doesn't have an IPv6, don't ask for one. -if [ -z "$PUBLIC_IPV6" ]; then +if [ -z "${PUBLIC_IPV6:-}" ]; then # Ask the Internet. GUESSED_IP=$(get_publicip_from_web_service 6) MATCHED=0 - if [[ -z "$DEFAULT_PUBLIC_IPV6" && ! -z "$GUESSED_IP" ]]; then + if [[ -z "${DEFAULT_PUBLIC_IPV6:-}" && ! -z "$GUESSED_IP" ]]; then PUBLIC_IPV6=$GUESSED_IP - elif [[ "$DEFAULT_PUBLIC_IPV6" == "$GUESSED_IP" ]]; then + elif [[ "${DEFAULT_PUBLIC_IPV6:-}" == "$GUESSED_IP" ]]; then # No IPv6 entered and machine seems to have none, or what # the user entered matches what the Internet tells us. PUBLIC_IPV6=$GUESSED_IP MATCHED=1 - elif [[ -z "$DEFAULT_PUBLIC_IPV6" ]]; then + elif [[ -z "${DEFAULT_PUBLIC_IPV6:-}" ]]; then DEFAULT_PUBLIC_IP=$(get_default_privateip 6) fi - if [[ -z "$PUBLIC_IPV6" && $MATCHED == 0 ]]; then + if [[ -z "${PUBLIC_IPV6:-}" && $MATCHED == 0 ]]; then input_box "IPv6 Address (Optional)" \ "Enter the public IPv6 address of this machine, as given to you by your ISP. \n\nLeave blank if the machine does not have an IPv6 address. \n\nPublic IPv6 address:" \ - $DEFAULT_PUBLIC_IPV6 \ + ${DEFAULT_PUBLIC_IPV6:-} \ PUBLIC_IPV6 if [ ! $PUBLIC_IPV6_EXITCODE ]; then @@ -154,10 +154,10 @@ fi # Get the IP addresses of the local network interface(s) that are connected # to the Internet. We need these when we want to have services bind only to # the public network interfaces (not loopback, not tunnel interfaces). -if [ -z "$PRIVATE_IP" ]; then +if [ -z "${PRIVATE_IP:-}" ]; then PRIVATE_IP=$(get_default_privateip 4) fi -if [ -z "$PRIVATE_IPV6" ]; then +if [ -z "${PRIVATE_IPV6:-}" ]; then PRIVATE_IPV6=$(get_default_privateip 6) fi if [[ -z "$PRIVATE_IP" && -z "$PRIVATE_IPV6" ]]; then @@ -186,11 +186,11 @@ fi # Set STORAGE_USER and STORAGE_ROOT to default values (user-data and /home/user-data), unless # we've already got those values from a previous run. -if [ -z "$STORAGE_USER" ]; then - STORAGE_USER=$([[ -z "$DEFAULT_STORAGE_USER" ]] && echo "user-data" || echo "$DEFAULT_STORAGE_USER") +if [ -z "${STORAGE_USER:-}" ]; then + STORAGE_USER=$([[ -z "${DEFAULT_STORAGE_USER:-}" ]] && echo "user-data" || echo "$DEFAULT_STORAGE_USER") fi -if [ -z "$STORAGE_ROOT" ]; then - STORAGE_ROOT=$([[ -z "$DEFAULT_STORAGE_ROOT" ]] && echo "/home/$STORAGE_USER" || echo "$DEFAULT_STORAGE_ROOT") +if [ -z "${STORAGE_ROOT:-}" ]; then + STORAGE_ROOT=$([[ -z "${DEFAULT_STORAGE_ROOT:-}" ]] && echo "/home/$STORAGE_USER" || echo "$DEFAULT_STORAGE_ROOT") fi # Show the configuration, since the user may have not entered it manually. diff --git a/setup/start.sh b/setup/start.sh index 671f4449..0b145022 100755 --- a/setup/start.sh +++ b/setup/start.sh @@ -60,8 +60,8 @@ source setup/questions.sh # Run some network checks to make sure setup on this machine makes sense. # Skip on existing installs since we don't want this to block the ability to # upgrade, and these checks are also in the control panel status checks. -if [ -z "$DEFAULT_PRIMARY_HOSTNAME" ]; then -if [ -z "$SKIP_NETWORK_CHECKS" ]; then +if [ -z "${DEFAULT_PRIMARY_HOSTNAME:-}" ]; then +if [ -z "${SKIP_NETWORK_CHECKS:-}" ]; then source setup/network-checks.sh fi fi diff --git a/setup/system.sh b/setup/system.sh index 2fecac8e..2db48298 100755 --- a/setup/system.sh +++ b/setup/system.sh @@ -37,9 +37,9 @@ hostname $PRIMARY_HOSTNAME # 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}') +SWAP_IN_FSTAB=$(grep "swap" /etc/fstab || /bin/true) +ROOT_IS_BTRFS=$(grep "\/ .*btrfs" /proc/mounts || /bin/true) +TOTAL_PHYSICAL_MEM=$(head -n 1 /proc/meminfo | awk '{print $2}' || /bin/true) AVAILABLE_DISK_SPACE=$(df / --output=avail | tail -n 1) if [ -z "$SWAP_MOUNTED" ] && @@ -143,8 +143,8 @@ fi # 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 [ -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. @@ -239,7 +239,7 @@ EOF # 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 +if [ -z "${DISABLE_FIREWALL:-}" ]; then # Install `ufw` which provides a simple firewall configuration. apt_install ufw From 60f9c9e3b7a9e6751b4937ce643473258e4dbced Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 30 Nov 2018 10:26:49 -0500 Subject: [PATCH 016/190] show the Mail-in-a-Box version in the system status checks even when the new-version check is disabled fixes #922 --- management/status_checks.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/management/status_checks.py b/management/status_checks.py index 2f4fe0a7..6f9bb1ef 100755 --- a/management/status_checks.py +++ b/management/status_checks.py @@ -794,14 +794,14 @@ def get_latest_miab_version(): def check_miab_version(env, output): config = load_settings(env) - if config.get("privacy", True): - output.print_warning("Mail-in-a-Box version check disabled by privacy setting.") - else: - try: - this_ver = what_version_is_this(env) - except: - this_ver = "Unknown" + try: + this_ver = what_version_is_this(env) + except: + this_ver = "Unknown" + if config.get("privacy", True): + output.print_warning("You are running version Mail-in-a-Box %s. Mail-in-a-Box version check disabled by privacy setting." % this_ver) + else: latest_ver = get_latest_miab_version() if this_ver == latest_ver: From dc6458623d1f5001f7f1a02b41c04a0debd6930e Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 30 Nov 2018 10:36:19 -0500 Subject: [PATCH 017/190] add a note on the aliases page that aliases should not be used to forward to outside domains fixes #1198 --- management/templates/aliases.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/management/templates/aliases.html b/management/templates/aliases.html index 78556df8..89af221f 100644 --- a/management/templates/aliases.html +++ b/management/templates/aliases.html @@ -39,8 +39,9 @@
    -
    - Enter just the part of an email address starting with the @-sign. +
    + Enter just the part of an email address starting with the @-sign. + Only forward mail to addresses handled by this Mail-in-a-Box, since mail forwarded by aliases to other domains may be rejected or filtered by the receiver. To forward mail to other domains, create a mail user and then log into webmail for the user and create a filter rule to forward mail.
    From 870b82637a20705cd7e3e6e07968f1298261e705 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 30 Nov 2018 10:39:53 -0500 Subject: [PATCH 018/190] fix some wrong variable names, fixes #1353 --- management/daemon.py | 2 +- tests/tls.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/management/daemon.py b/management/daemon.py index 334749e4..572b6b4a 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -572,7 +572,7 @@ def munin_cgi(filename): if code != 0: # nonzero returncode indicates error - app.logger.error("munin_cgi: munin-cgi-graph returned nonzero exit code, %s", process.returncode) + app.logger.error("munin_cgi: munin-cgi-graph returned nonzero exit code, %s", code) return ("error processing graph image", 500) # /usr/lib/munin/cgi/munin-cgi-graph returns both headers and binary png when successful. diff --git a/tests/tls.py b/tests/tls.py index 32148133..0c7b945a 100644 --- a/tests/tls.py +++ b/tests/tls.py @@ -128,7 +128,7 @@ def sslyze(opts, port, ok_ciphers): proxy_proc.terminate() try: proxy_proc.wait(5) - except TimeoutExpired: + except subprocess.TimeoutExpired: proxy_proc.kill() # Get a list of OpenSSL cipher names. From ff6d8fc672c5e1f1495d4d00513708d3749012b7 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 30 Nov 2018 10:42:44 -0500 Subject: [PATCH 019/190] remove the ppa directory since we're no longer supporting a PPA for Ubuntu 18.04 --- ppa/Makefile | 62 -------- ppa/README.md | 40 ----- ppa/Vagrantfile | 12 -- ppa/dovecot_lucene.diff | 319 -------------------------------------- ppa/postgrey.diff | 80 ---------- ppa/postgrey_sources.diff | 100 ------------ 6 files changed, 613 deletions(-) delete mode 100755 ppa/Makefile delete mode 100644 ppa/README.md delete mode 100644 ppa/Vagrantfile delete mode 100644 ppa/dovecot_lucene.diff delete mode 100644 ppa/postgrey.diff delete mode 100644 ppa/postgrey_sources.diff diff --git a/ppa/Makefile b/ppa/Makefile deleted file mode 100755 index 4e3f99df..00000000 --- a/ppa/Makefile +++ /dev/null @@ -1,62 +0,0 @@ -POSTGREY_VERSION=1.35-1+miab1 -DOVECOT_VERSION=2.2.9-1ubuntu2.1+miab1 - -all: clean build_postgrey build_dovecot_lucene - -clean: - # Clean. - rm -rf /tmp/build - mkdir -p /tmp/build - -build_postgrey: clean - # Download the latest Debian postgrey package. It is ahead of Ubuntu, - # and we might as well jump ahead. - git clone git://git.debian.org/git/collab-maint/postgrey.git /tmp/build/postgrey - - # Download the corresponding upstream package. - wget -O /tmp/build/postgrey_1.35.orig.tar.gz http://postgrey.schweikert.ch/pub/old/postgrey-1.35.tar.gz - - # Add our source patch to the debian packaging listing. - cp postgrey_sources.diff /tmp/build/postgrey/debian/patches/mailinabox - - # Patch the packaging to give it a new version. - patch -p1 -d /tmp/build/postgrey < postgrey.diff - - # Build the source package. - (cd /tmp/build/postgrey; dpkg-buildpackage -S -us -uc -nc) - - # Sign the packages. - debsign /tmp/build/postgrey_$(POSTGREY_VERSION)_source.changes - - # Upload to PPA. - dput ppa:mail-in-a-box/ppa /tmp/build/postgrey_$(POSTGREY_VERSION)_source.changes - - # Clear the intermediate files. - rm -rf /tmp/build/postgrey - - # TESTING BINARY PACKAGE - #sudo apt-get build-dep -y postgrey - #(cd /tmp/build/postgrey; dpkg-buildpackage -us -uc -nc) - -build_dovecot_lucene: clean - # Get the upstream source. - (cd /tmp/build; apt-get source dovecot) - - # Patch it so that we build dovecot-lucene (and nothing else). - patch -p1 -d /tmp/build/dovecot-2.2.9 < dovecot_lucene.diff - - # Build the source package. - (cd /tmp/build/dovecot-2.2.9; dpkg-buildpackage -S -us -uc -nc) - - # Sign the packages. - debsign /tmp/build/dovecot_$(DOVECOT_VERSION)_source.changes - - # Upload it. - dput ppa:mail-in-a-box/ppa /tmp/build/dovecot_$(DOVECOT_VERSION)_source.changes - - # TESTING BINARY PACKAGE - # Install build dependencies and build dependencies we've added in our patch, - # and then build the binary package. - #sudo apt-get build-dep -y dovecot - #sudo apt-get install libclucene-dev liblzma-dev libexttextcat-dev libstemmer-dev - #(cd /tmp/build/dovecot-2.2.9; dpkg-buildpackage -us -uc -nc) diff --git a/ppa/README.md b/ppa/README.md deleted file mode 100644 index 0f009f67..00000000 --- a/ppa/README.md +++ /dev/null @@ -1,40 +0,0 @@ -ppa instructions -================ - -Mail-in-a-Box maintains a Launchpad.net PPA ([Mail-in-a-Box PPA](https://launchpad.net/~mail-in-a-box/+archive/ubuntu/ppa)) for additional deb's that we want to have installed on systems. - -Packages --------- - -* postgrey, a fork of [postgrey](http://postgrey.schweikert.ch/) based on the [latest Debian package](http://git.debian.org/?p=collab-maint/postgrey.git), with a modification to whitelist senders that are whitelisted by [dnswl.org](https://www.dnswl.org/) (i.e. don't greylist mail from known good senders). - -* dovecot-lucene, [dovecot's lucene full text search plugin](http://wiki2.dovecot.org/Plugins/FTS/Lucene), which isn't built by Ubuntu's dovecot package maintainer unfortunately. - -Building --------- - -To rebuild the packages in the PPA, you'll need to be @JoshData. - -First: - -* You should have an account on Launchpad.net. -* Your account should have your GPG key set (to the fingerprint of a GPG key on your system matching the identity at the top of the debian/changelog files). -* You should have write permission to the PPA. - -To build: - - # Start a clean VM. - vagrant up - - # Put your signing keys (on the host machine) into the VM (so it can sign the debs). - gpg --export-secret-keys | vagrant ssh -- gpg --import - - # Build & upload to launchpad. - vagrant ssh -- "cd /vagrant && make" - -Mail-in-a-Box adds our PPA during setup, but if you need to do that yourself for testing: - - apt-add-repository ppa:mail-in-a-box/ppa - apt-get update - apt-get install postgrey dovecot-lucene - diff --git a/ppa/Vagrantfile b/ppa/Vagrantfile deleted file mode 100644 index 7d839b18..00000000 --- a/ppa/Vagrantfile +++ /dev/null @@ -1,12 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -Vagrant.configure("2") do |config| - config.vm.box = "ubuntu14.04" - config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box" - - config.vm.provision :shell, :inline => <<-SH - sudo apt-get update - sudo apt-get install -y git dpkg-dev devscripts dput -SH -end diff --git a/ppa/dovecot_lucene.diff b/ppa/dovecot_lucene.diff deleted file mode 100644 index 4d258351..00000000 --- a/ppa/dovecot_lucene.diff +++ /dev/null @@ -1,319 +0,0 @@ ---- a/debian/control -+++ b/debian/control -@@ -1,210 +1,23 @@ - Source: dovecot - Section: mail - Priority: optional --Maintainer: Ubuntu Developers --XSBC-Original-Maintainer: Dovecot Maintainers --Uploaders: Jaldhar H. Vyas , Fabio Tranchitella , Joel Johnson , Marco Nenciarini --Build-Depends: debhelper (>= 7.2.3~), dpkg-dev (>= 1.16.1), pkg-config, libssl-dev, libpam0g-dev, libldap2-dev, libpq-dev, libmysqlclient-dev, libsqlite3-dev, libsasl2-dev, zlib1g-dev, libkrb5-dev, drac-dev (>= 1.12-5), libbz2-dev, libdb-dev, libcurl4-gnutls-dev, libexpat-dev, libwrap0-dev, dh-systemd, po-debconf, lsb-release, hardening-wrapper, dh-autoreconf, autotools-dev -+Maintainer: Joshua Tauberer -+XSBC-Original-Maintainer: Ubuntu Developers -+Build-Depends: debhelper (>= 7.2.3~), dpkg-dev (>= 1.16.1), pkg-config, libssl-dev, libpam0g-dev, libldap2-dev, libpq-dev, libmysqlclient-dev, libsqlite3-dev, libsasl2-dev, zlib1g-dev, libkrb5-dev, drac-dev (>= 1.12-5), libbz2-dev, libdb-dev, libcurl4-gnutls-dev, libexpat-dev, libwrap0-dev, dh-systemd, po-debconf, lsb-release, libclucene-dev (>= 2.3), liblzma-dev, libexttextcat-dev, libstemmer-dev, hardening-wrapper, dh-autoreconf, autotools-dev - Standards-Version: 3.9.4 - Homepage: http://dovecot.org/ --Vcs-Git: git://git.debian.org/git/collab-maint/dovecot.git --Vcs-Browser: http://git.debian.org/?p=collab-maint/dovecot.git -+Vcs-Git: https://github.com/mail-in-a-box/mailinabox -+Vcs-Browser: https://github.com/mail-in-a-box/mailinabox - --Package: dovecot-core -+Package: dovecot-lucene - Architecture: any --Depends: ${shlibs:Depends}, ${misc:Depends}, libpam-runtime (>= 0.76-13.1), openssl, adduser, ucf (>= 2.0020), ssl-cert (>= 1.0-11ubuntu1), lsb-base (>= 3.2-12ubuntu3) --Suggests: ntp, dovecot-gssapi, dovecot-sieve, dovecot-pgsql, dovecot-mysql, dovecot-sqlite, dovecot-ldap, dovecot-imapd, dovecot-pop3d, dovecot-lmtpd, dovecot-managesieved, dovecot-solr, ufw --Recommends: ntpdate --Provides: dovecot-common --Replaces: dovecot-common (<< 1:2.0.14-2~), mailavenger (<< 0.8.1-4) --Breaks: dovecot-common (<< 1:2.0.14-2~), mailavenger (<< 0.8.1-4) --Description: secure POP3/IMAP server - core files -+Depends: ${shlibs:Depends}, ${misc:Depends}, dovecot-core (>= 1:2.2.9-1ubuntu2.1) -+Description: secure POP3/IMAP server - Lucene support - Dovecot is a mail server whose major goals are security and extreme - reliability. It tries very hard to handle all error conditions and verify - that all data is valid, making it nearly impossible to crash. It supports - mbox/Maildir and its own dbox/mdbox formats, and should also be pretty - fast, extensible, and portable. - . -- This package contains the Dovecot main server and its command line utility. -- --Package: dovecot-dev --Architecture: any --Depends: ${shlibs:Depends}, ${misc:Depends}, dovecot-core (= ${binary:Version}) --Replaces: dovecot-common (<< 1:2.0.14-2~) --Breaks: dovecot-common (<< 1:2.0.14-2~) --Description: secure POP3/IMAP server - header files -- Dovecot is a mail server whose major goals are security and extreme -- reliability. It tries very hard to handle all error conditions and verify -- that all data is valid, making it nearly impossible to crash. It supports -- mbox/Maildir and its own dbox/mdbox formats, and should also be pretty -- fast, extensible, and portable. -- . -- This package contains header files needed to compile plugins for the Dovecot -- mail server. -- --Package: dovecot-imapd --Architecture: any --Depends: ${shlibs:Depends}, ${misc:Depends}, dovecot-core (= ${binary:Version}), ucf (>= 2.0020) --Provides: imap-server --Description: secure POP3/IMAP server - IMAP daemon -- Dovecot is a mail server whose major goals are security and extreme -- reliability. It tries very hard to handle all error conditions and verify -- that all data is valid, making it nearly impossible to crash. It supports -- mbox/Maildir and its own dbox/mdbox formats, and should also be pretty -- fast, extensible, and portable. -- . -- This package contains the Dovecot IMAP server. -- --Package: dovecot-pop3d --Architecture: any --Depends: ${shlibs:Depends}, ${misc:Depends}, dovecot-core (= ${binary:Version}), ucf (>= 2.0020) --Provides: pop3-server --Description: secure POP3/IMAP server - POP3 daemon -- Dovecot is a mail server whose major goals are security and extreme -- reliability. It tries very hard to handle all error conditions and verify -- that all data is valid, making it nearly impossible to crash. It supports -- mbox/Maildir and its own dbox/mdbox formats, and should also be pretty -- fast, extensible, and portable. -- . -- This package contains the Dovecot POP3 server. -- --Package: dovecot-lmtpd --Architecture: any --Depends: ${shlibs:Depends}, ${misc:Depends}, dovecot-core (= ${binary:Version}), ucf (>= 2.0020) --Replaces: dovecot-common (<< 1:2.0.14-2~) --Breaks: dovecot-common (<< 1:2.0.14-2~) --Description: secure POP3/IMAP server - LMTP server -- Dovecot is a mail server whose major goals are security and extreme -- reliability. It tries very hard to handle all error conditions and verify -- that all data is valid, making it nearly impossible to crash. It supports -- mbox/Maildir and its own dbox/mdbox formats, and should also be pretty -- fast, extensible, and portable. -- . -- This package contains the Dovecot LMTP server. -- --Package: dovecot-managesieved --Architecture: any --Depends: ${shlibs:Depends}, ${misc:Depends}, dovecot-core (= ${binary:Version}), dovecot-sieve (= ${binary:Version}), ucf (>= 2.0020) --Replaces: dovecot-common (<< 1:2.0.14-2~) --Breaks: dovecot-common (<< 1:2.0.14-2~) --Description: secure POP3/IMAP server - ManageSieve server -- Dovecot is a mail server whose major goals are security and extreme -- reliability. It tries very hard to handle all error conditions and verify -- that all data is valid, making it nearly impossible to crash. It supports -- mbox/Maildir and its own dbox/mdbox formats, and should also be pretty -- fast, extensible, and portable. -- . -- This package contains the Dovecot ManageSieve server. -- --Package: dovecot-pgsql --Architecture: any --Depends: ${shlibs:Depends}, ${misc:Depends}, dovecot-core (= ${binary:Version}) --Description: secure POP3/IMAP server - PostgreSQL support -- Dovecot is a mail server whose major goals are security and extreme -- reliability. It tries very hard to handle all error conditions and verify -- that all data is valid, making it nearly impossible to crash. It supports -- mbox/Maildir and its own dbox/mdbox formats, and should also be pretty -- fast, extensible, and portable. -- . -- This package provides PostgreSQL support for Dovecot. -- --Package: dovecot-mysql --Architecture: any --Depends: ${shlibs:Depends}, ${misc:Depends}, dovecot-core (= ${binary:Version}) --Description: secure POP3/IMAP server - MySQL support -- Dovecot is a mail server whose major goals are security and extreme -- reliability. It tries very hard to handle all error conditions and verify -- that all data is valid, making it nearly impossible to crash. It supports -- mbox/Maildir and its own dbox/mdbox formats, and should also be pretty -- fast, extensible, and portable. -- . -- This package provides MySQL support for Dovecot. -- --Package: dovecot-sqlite --Architecture: any --Depends: ${shlibs:Depends}, ${misc:Depends}, dovecot-core (= ${binary:Version}) --Description: secure POP3/IMAP server - SQLite support -- Dovecot is a mail server whose major goals are security and extreme -- reliability. It tries very hard to handle all error conditions and verify -- that all data is valid, making it nearly impossible to crash. It supports -- mbox/Maildir and its own dbox/mdbox formats, and should also be pretty -- fast, extensible, and portable. -- . -- This package provides SQLite support for Dovecot. -- --Package: dovecot-ldap --Architecture: any --Depends: ${shlibs:Depends}, ${misc:Depends}, dovecot-core (= ${binary:Version}), ucf (>= 2.0020) --Description: secure POP3/IMAP server - LDAP support -- Dovecot is a mail server whose major goals are security and extreme -- reliability. It tries very hard to handle all error conditions and verify -- that all data is valid, making it nearly impossible to crash. It supports -- mbox/Maildir and its own dbox/mdbox formats, and should also be pretty -- fast, extensible, and portable. -- . -- This package provides LDAP support for Dovecot. -- --Package: dovecot-gssapi --Architecture: any --Depends: ${shlibs:Depends}, ${misc:Depends}, dovecot-core (= ${binary:Version}) --Description: secure POP3/IMAP server - GSSAPI support -- Dovecot is a mail server whose major goals are security and extreme -- reliability. It tries very hard to handle all error conditions and verify -- that all data is valid, making it nearly impossible to crash. It supports -- mbox/Maildir and its own dbox/mdbox formats, and should also be pretty -- fast, extensible, and portable. -- . -- This package provides GSSAPI authentication support for Dovecot. -- --Package: dovecot-sieve --Architecture: any --Depends: ${shlibs:Depends}, ${misc:Depends}, dovecot-core (= ${binary:Version}), ucf (>= 2.0020) --Description: secure POP3/IMAP server - Sieve filters support -- Dovecot is a mail server whose major goals are security and extreme -- reliability. It tries very hard to handle all error conditions and verify -- that all data is valid, making it nearly impossible to crash. It supports -- mbox/Maildir and its own dbox/mdbox formats, and should also be pretty -- fast, extensible, and portable. -- . -- This package provides Sieve filters support for Dovecot. -- --Package: dovecot-solr --Architecture: any --Depends: ${shlibs:Depends}, ${misc:Depends}, dovecot-core (= ${binary:Version}) --Description: secure POP3/IMAP server - Solr support -- Dovecot is a mail server whose major goals are security and extreme -- reliability. It tries very hard to handle all error conditions and verify -- that all data is valid, making it nearly impossible to crash. It supports -- mbox/Maildir and its own dbox/mdbox formats, and should also be pretty -- fast, extensible, and portable. -- . -- This package provides Solr full text search support for Dovecot. -- --Package: dovecot-dbg --Section: debug --Priority: extra --Architecture: any --Depends: ${misc:Depends}, dovecot-core (= ${binary:Version}) --Description: secure POP3/IMAP server - debug symbols -- Dovecot is a mail server whose major goals are security and extreme -- reliability. It tries very hard to handle all error conditions and verify -- that all data is valid, making it nearly impossible to crash. It supports -- mbox/Maildir and its own dbox/mdbox formats, and should also be pretty -- fast, extensible, and portable. -- . -- This package contains debug symbols for Dovecot. -- --Package: mail-stack-delivery --Architecture: all --Depends: dovecot-core, dovecot-imapd, dovecot-pop3d, dovecot-managesieved, -- postfix, ${misc:Depends} --Replaces: dovecot-postfix (<< 1:1.2.12-0ubuntu1~) --Description: mail server delivery agent stack provided by Ubuntu server team -- Ubuntu's mail stack provides fully operational delivery with -- safe defaults and additional options. Out of the box it supports IMAP, -- POP3 and SMTP services with SASL authentication and Maildir as default -- storage engine. -- . -- This package contains configuration files for dovecot. -- . -- This package modifies postfix's configuration to integrate with dovecot -+ This package provides Lucene full text search support for Dovecot. It has been modified by Mail-in-a-Box -+ to supply a dovecot-lucene package compatible with the official ubuntu trusty dovecot-core. - -diff --git a/debian/dovecot-lucene.links b/debian/dovecot-lucene.links -new file mode 100644 -index 0000000..6ffcbeb ---- /dev/null -+++ b/debian/dovecot-lucene.links -@@ -0,0 +1 @@ -+/usr/share/bug/dovecot-core /usr/share/bug/dovecot-lucene -diff --git a/debian/dovecot-lucene.lintian-overrides b/debian/dovecot-lucene.lintian-overrides -new file mode 100644 -index 0000000..60d90fd ---- /dev/null -+++ b/debian/dovecot-lucene.lintian-overrides -@@ -0,0 +1,2 @@ -+dovecot-lucene: hardening-no-fortify-functions usr/lib/dovecot/modules/lib21_fts_lucene_plugin.so -+ -diff --git a/debian/dovecot-lucene.substvars b/debian/dovecot-lucene.substvars -new file mode 100644 -index 0000000..ed54f36 ---- /dev/null -+++ b/debian/dovecot-lucene.substvars -@@ -0,0 +1,2 @@ -+shlibs:Depends=libc6 (>= 2.4), libclucene-core1 (>= 2.3.3.4), libgcc1 (>= 1:4.1.1), libstdc++6 (>= 4.1.1), libstemmer0d (>= 0+svn527) -+misc:Depends= -diff --git a/debian/dovecot-lucene.triggers b/debian/dovecot-lucene.triggers -new file mode 100644 -index 0000000..3d933a5 ---- /dev/null -+++ b/debian/dovecot-lucene.triggers -@@ -0,0 +1 @@ -+activate register-dovecot-plugin ---- a/debian/rules -+++ b/debian/rules -@@ -40,6 +40,7 @@ - --with-solr \ - --with-ioloop=best \ - --with-libwrap \ -+ --with-lucene \ - --host=$(DEB_HOST_GNU_TYPE) \ - --build=$(DEB_BUILD_GNU_TYPE) \ - --prefix=/usr \ -@@ -95,6 +96,10 @@ - dh_testroot - dh_clean -k - dh_installdirs -+ mkdir -p $(CURDIR)/debian/dovecot-lucene/usr/lib/dovecot/modules -+ mv $(CURDIR)/src/plugins/fts-lucene/.libs/* $(CURDIR)/debian/dovecot-lucene/usr/lib/dovecot/modules/ -+ -+rest_disabled_by_miab: - $(MAKE) install DESTDIR=$(CURDIR)/debian/dovecot-core - $(MAKE) -C $(PIGEONHOLE_DIR) install DESTDIR=$(CURDIR)/debian/dovecot-core - rm `find $(CURDIR)/debian -name '*.la'` -@@ -209,7 +214,7 @@ - dh_installdocs -a - dh_installexamples -a - dh_installpam -a -- mv $(CURDIR)/debian/dovecot-core/etc/pam.d/dovecot-core $(CURDIR)/debian/dovecot-core/etc/pam.d/dovecot -+ # mv $(CURDIR)/debian/dovecot-core/etc/pam.d/dovecot-core $(CURDIR)/debian/dovecot-core/etc/pam.d/dovecot - dh_systemd_enable - dh_installinit -pdovecot-core --name=dovecot - dh_systemd_start -@@ -220,10 +225,10 @@ - dh_lintian -a - dh_installchangelogs -a ChangeLog - dh_link -a -- dh_strip -a --dbg-package=dovecot-dbg -+ #dh_strip -a --dbg-package=dovecot-dbg - dh_compress -a - dh_fixperms -a -- chmod 0700 debian/dovecot-core/etc/dovecot/private -+ #chmod 0700 debian/dovecot-core/etc/dovecot/private - dh_makeshlibs -a -n - dh_installdeb -a - dh_shlibdeps -a ---- a/debian/changelog -+++ a/debian/changelog -@@ -1,3 +1,9 @@ -+dovecot (1:2.2.9-1ubuntu2.1+miab1) trusty; urgency=low -+ -+ * Changed to just build dovecot-lucene for Mail-in-a-box PPA -+ -+ -- Joshua Tauberer Sat, 14 May 2015 16:13:00 -0400 -+ - dovecot (1:2.2.9-1ubuntu2.1) trusty-security; urgency=medium - - * SECURITY UPDATE: denial of service via SSL connection exhaustion ---- a/debian/copyright 2014-03-07 07:26:37.000000000 -0500 -+++ b/debian/copyright 2015-05-23 18:17:42.668005535 -0400 -@@ -1,3 +1,7 @@ -+This package is a fork by Mail-in-a-box (https://mailinabox.email). Original -+copyright statement follows: -+---------------------------------------------------------------------------- -+ - This package was debianized by Jaldhar H. Vyas on - Tue, 3 Dec 2002 01:10:07 -0500. - diff --git a/ppa/postgrey.diff b/ppa/postgrey.diff deleted file mode 100644 index 1570be02..00000000 --- a/ppa/postgrey.diff +++ /dev/null @@ -1,80 +0,0 @@ -diff --git a/debian/NEWS b/debian/NEWS -index dd09744..de7b640 100644 ---- a/debian/NEWS -+++ b/debian/NEWS -@@ -1,3 +1,9 @@ -+postgrey (1.35-1+miab1) -+ -+ Added DNSWL.org whitelisting. -+ -+ -- Joshua Tauberer Mon May 18 18:58:40 EDT 2015 -+ - postgrey (1.32-1) unstable; urgency=low - - Postgrey is now listening to port 10023 and not 60000. The latter was an -diff --git a/debian/changelog b/debian/changelog -index 1058e15..e5e3557 100644 ---- a/debian/changelog -+++ b/debian/changelog -@@ -1,3 +1,9 @@ -+postgrey (1.35-1+miab1) trusty; urgency=low -+ -+ * Added DNSWL.org whitelisting. -+ -+ -- Joshua Tauberer Mon, 18 May 2015 21:58:40 +0000 -+ - postgrey (1.35-1) unstable; urgency=low - - * New upstream release (Closes: 756486) -diff --git a/debian/control b/debian/control -index ce12ba6..0a82855 100644 ---- a/debian/control -+++ b/debian/control -@@ -1,14 +1,11 @@ - Source: postgrey - Section: mail - Priority: optional --Maintainer: Antonio Radici --Uploaders: Jon Daley -+Maintainer: Joshua Tauberer - Build-Depends: debhelper (>= 7), quilt - Build-Depends-Indep: po-debconf - Standards-Version: 3.9.6 - Homepage: http://postgrey.schweikert.ch/ --Vcs-Browser: http://git.debian.org/?p=collab-maint/postgrey.git --Vcs-Git: git://git.debian.org/git/collab-maint/postgrey.git - - Package: postgrey - Architecture: all -@@ -25,3 +22,6 @@ Description: greylisting implementation for Postfix - . - While Postgrey is designed for use with Postfix, it can also be used - with Exim. -+ . -+ This version has been modified by Mail-in-a-Box to whitelist senders -+ in the DNSWL.org list. See https://mailinabox.email. -diff --git a/debian/copyright b/debian/copyright -index 3cbe377..bf09b89 100644 ---- a/debian/copyright -+++ b/debian/copyright -@@ -1,6 +1,10 @@ -+This package is a fork by Mail-in-a-Box (https://mailinabox.email). Original -+copyright statement follows: -+---------------------------------------------------------------------------- -+ - This Debian package was prepared by Adrian von Bidder in - July 2004, then the package was adopted by Antonio Radici --in Sept 2009 -+in Sept 2009. - - It was downloaded from http://postgrey.schweikert.ch/ - -diff --git a/debian/patches/series b/debian/patches/series -index f4c5e31..3cd62b8 100644 ---- a/debian/patches/series -+++ b/debian/patches/series -@@ -1,3 +1,3 @@ - imported-upstream-diff - disable-transaction-logic -- -+mailinabox diff --git a/ppa/postgrey_sources.diff b/ppa/postgrey_sources.diff deleted file mode 100644 index dcc1f55c..00000000 --- a/ppa/postgrey_sources.diff +++ /dev/null @@ -1,100 +0,0 @@ -Description: whitelist whatever dnswl.org whitelists - . - postgrey (1.35-1+miab1) unstable; urgency=low - . - * Added DNSWL.org whitelisting. -Author: Joshua Tauberer - ---- postgrey-1.35.orig/README -+++ postgrey-1.35/README -@@ -13,7 +13,7 @@ Requirements - - BerkeleyDB (Perl Module) - - Berkeley DB >= 4.1 (Library) - - Digest::SHA (Perl Module, only for --privacy option) -- -+- Net::DNS (Perl Module) - - Documentation - ------------- ---- postgrey-1.35.orig/postgrey -+++ postgrey-1.35/postgrey -@@ -18,6 +18,7 @@ use Fcntl ':flock'; # import LOCK_* cons - use Sys::Hostname; - use Sys::Syslog; # used only to find out which version we use - use POSIX qw(strftime setlocale LC_ALL); -+use Net::DNS; # for DNSWL.org whitelisting - - use vars qw(@ISA); - @ISA = qw(Net::Server::Multiplex); -@@ -26,6 +27,8 @@ my $VERSION = '1.35'; - my $DEFAULT_DBDIR = '/var/lib/postgrey'; - my $CONFIG_DIR = '/etc/postgrey'; - -+my $dns_resolver = Net::DNS::Resolver->new; -+ - sub cidr_parse($) - { - defined $_[0] or return undef; -@@ -48,6 +51,36 @@ sub cidr_match($$$) - return ($addr & $mask) == $net; - } - -+sub reverseDottedQuad { -+ # This is the sub _chkValidPublicIP from Net::DNSBL by PJ Goodwin -+ # at http://www.the42.net/net-dnsbl. -+ my ($quad) = @_; -+ if ($quad =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { -+ my ($ip1,$ip2,$ip3,$ip4) = ($1, $2, $3, $4); -+ if ( -+ $ip1 == 10 || #10.0.0.0/8 (10/8) -+ ($ip1 == 172 && $ip2 >= 16 && $ip2 <= 31) || #172.16.0.0/12 (172.16/12) -+ ($ip1 == 192 && $ip2 == 168) || #192.168.0.0/16 (192.168/16) -+ $quad eq '127.0.0.1' # localhost -+ ) { -+ # toss the RFC1918 specified privates -+ return undef; -+ } elsif ( -+ ($ip1 <= 1 || $ip1 > 254) || -+ ($ip2 < 0 || $ip2 > 255) || -+ ($ip3 < 0 || $ip3 > 255) || -+ ($ip4 < 0 || $ip4 > 255) -+ ) { -+ #invalid oct, toss it; -+ return undef; -+ } -+ my $revquad = $ip4 . "." . $ip3 . "." . $ip2 . "." . $ip1; -+ return $revquad; -+ } else { # invalid quad -+ return undef; -+ } -+} -+ - sub read_clients_whitelists($) - { - my ($self) = @_; -@@ -361,6 +394,25 @@ sub smtpd_access_policy($$) - } - } - -+ # whitelist clients in dnswl.org -+ my $revip = reverseDottedQuad($attr->{client_address}); -+ if ($revip) { # valid IP / plausibly in DNSWL -+ my $answer = $dns_resolver->send($revip . '.list.dnswl.org'); -+ if ($answer && scalar($answer->answer) > 0) { -+ my @rrs = $answer->answer; -+ if ($rrs[0]->type eq 'A' && $rrs[0]->address ne '127.0.0.255') { -+ # Address appears in DNSWL. (127.0.0.255 means we were rate-limited.) -+ my $code = $rrs[0]->address; -+ if ($code =~ /^127.0.(\d+)\.([0-3])$/) { -+ my %dnswltrust = (0 => 'legitimate', 1 => 'occasional spam', 2 => 'rare spam', 3 => 'highly unlikely to send spam'); -+ $code = $2 . '/' . $dnswltrust{$2}; -+ } -+ $self->mylog_action($attr, 'pass', 'client whitelisted by dnswl.org (' . $code . ')'); -+ return 'DUNNO'; -+ } -+ } -+ } -+ - # auto whitelist clients (see below for explanation) - my ($cawl_db, $cawl_key, $cawl_count, $cawl_last); - if($self->{postgrey}{awl_clients}) { From 9ddca42c9187ace187990ae11fd69ea97c2d9d0a Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 30 Nov 2018 10:44:13 -0500 Subject: [PATCH 020/190] add 'nameserver' to resolv.conf, fixes #1450 --- setup/system.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/system.sh b/setup/system.sh index 2db48298..7aafa25d 100755 --- a/setup/system.sh +++ b/setup/system.sh @@ -321,7 +321,7 @@ fi # download bind9 from. rm -f /etc/resolv.conf tools/editconf.py /etc/systemd/resolved.conf DNSStubListener=no -echo "127.0.0.1" > /etc/resolv.conf +echo "nameserver 127.0.0.1" > /etc/resolv.conf # Restart the DNS services. From 703a9376ef6a8c31555df1aed7f3d9ed0a9cfb4c Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Sun, 2 Dec 2018 18:15:34 -0500 Subject: [PATCH 021/190] fix /etc /usr permissions for Scaleway, see #1438 --- setup/system.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/setup/system.sh b/setup/system.sh index 7aafa25d..69763c84 100755 --- a/setup/system.sh +++ b/setup/system.sh @@ -14,6 +14,13 @@ source setup/functions.sh # load our functions echo $PRIMARY_HOSTNAME > /etc/hostname hostname $PRIMARY_HOSTNAME +# ### Fix permissions + +# The default Ubuntu Bionic image on Scaleway throws warnings during setup about incorrect +# permissions (group writeable) set on the following directories. + +chmod g-w /etc /etc/default /usr + # ### Add swap space to the system # If the physical memory of the system is below 2GB it is wise to create a From e80a1dd4b740b3f50bb8787c36cf2a78023e5f35 Mon Sep 17 00:00:00 2001 From: EliterScripts Date: Thu, 13 Dec 2018 18:28:21 -0700 Subject: [PATCH 022/190] fix DEFAULT_PUBLIC_IP unbound variable error (#1488) This will fix this error while installing: setup/questions.sh: line 95: DEFAULT_PUBLIC_IP: unbound variable --- setup/questions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/questions.sh b/setup/questions.sh index 3ad7eead..d070a52f 100644 --- a/setup/questions.sh +++ b/setup/questions.sh @@ -92,7 +92,7 @@ if [ -z "${PUBLIC_IP:-}" ]; then # On the first run, if we got an answer from the Internet then don't # ask the user. - if [[ -z "$DEFAULT_PUBLIC_IP" && ! -z "$GUESSED_IP" ]]; then + if [[ -z "${DEFAULT_PUBLIC_IP:-}" && ! -z "$GUESSED_IP" ]]; then PUBLIC_IP=$GUESSED_IP # Otherwise on the first run at least provide a default. From 71f1c92b9eeb9c779130a98e612965ad777e9a69 Mon Sep 17 00:00:00 2001 From: jvolkenant Date: Thu, 13 Dec 2018 17:30:05 -0800 Subject: [PATCH 023/190] bash strict mode fixes (#1482) --- setup/firstuser.sh | 4 ++-- setup/munin.sh | 2 +- setup/nextcloud.sh | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/setup/firstuser.sh b/setup/firstuser.sh index 71264f3b..f6947695 100644 --- a/setup/firstuser.sh +++ b/setup/firstuser.sh @@ -4,7 +4,7 @@ if [ -z "`tools/mail.py user`" ]; then # aren't any yet, it'll be empty. # If we didn't ask for an email address at the start, do so now. - if [ -z "$EMAIL_ADDR" ]; then + if [ -z "${EMAIL_ADDR:-}" ]; then # In an interactive shell, ask the user for an email address. if [ -z "${NONINTERACTIVE:-}" ]; then input_box "Mail Account" \ @@ -47,7 +47,7 @@ if [ -z "`tools/mail.py user`" ]; then fi # Create the user's mail account. This will ask for a password if none was given above. - tools/mail.py user add $EMAIL_ADDR $EMAIL_PW + tools/mail.py user add $EMAIL_ADDR ${EMAIL_PW:-} # Make it an admin. hide_output tools/mail.py user make-admin $EMAIL_ADDR diff --git a/setup/munin.sh b/setup/munin.sh index c581c6d0..8a85085d 100755 --- a/setup/munin.sh +++ b/setup/munin.sh @@ -44,7 +44,7 @@ tools/editconf.py /etc/munin/munin-node.conf -s \ log_level=1 # Update the activated plugins through munin's autoconfiguration. -munin-node-configure --shell --remove-also 2>/dev/null | sh +munin-node-configure --shell --remove-also 2>/dev/null | sh || /bin/true # Deactivate monitoring of NTP peers. Not sure why anyone would want to monitor a NTP peer. The addresses seem to change # (which is taken care of my munin-node-configure, but only when we re-run it.) diff --git a/setup/nextcloud.sh b/setup/nextcloud.sh index e0e66c64..167c2012 100755 --- a/setup/nextcloud.sh +++ b/setup/nextcloud.sh @@ -57,7 +57,7 @@ InstallNextcloud() { # 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 + 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). From 31b743b164c15391b05d76cfd849be200496cec8 Mon Sep 17 00:00:00 2001 From: Dean Perry Date: Wed, 26 Dec 2018 20:39:47 +0000 Subject: [PATCH 024/190] Fix some more $DEFAULT_PUBLIC_IP issues (#1494) --- setup/questions.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup/questions.sh b/setup/questions.sh index d070a52f..bf382f49 100644 --- a/setup/questions.sh +++ b/setup/questions.sh @@ -96,12 +96,12 @@ if [ -z "${PUBLIC_IP:-}" ]; then PUBLIC_IP=$GUESSED_IP # Otherwise on the first run at least provide a default. - elif [[ -z "$DEFAULT_PUBLIC_IP" ]]; then + elif [[ -z "${DEFAULT_PUBLIC_IP:-}" ]]; then DEFAULT_PUBLIC_IP=$(get_default_privateip 4) # On later runs, if the previous value matches the guessed value then # don't ask the user either. - elif [ "$DEFAULT_PUBLIC_IP" == "$GUESSED_IP" ]; then + elif [ "${DEFAULT_PUBLIC_IP:-}" == "$GUESSED_IP" ]; then PUBLIC_IP=$GUESSED_IP fi From a67aa4cfd4e42fd43b95c5cefcafb2608f1f48cc Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Wed, 9 Jan 2019 06:17:27 -0500 Subject: [PATCH 025/190] changelog --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bde872b..c098e632 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,12 @@ CHANGELOG ========= -In Development --------------- +v0.30 (January 9, 2019) +----------------------- Setup: -* Update to Roundcube 1.3.8. +* Update to Roundcube 1.3.8 and the CardDAV plugin to 3.0.3. * Add missing rsyslog package to install line since some OS images don't have it installed by default. * A log file for nsd was added. From 7b592b1e99066f6a1f512d60b8f0600e824b0998 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Wed, 9 Jan 2019 06:31:56 -0500 Subject: [PATCH 026/190] v0.30 - the last Ubuntu 14.04 release --- README.md | 4 ++-- setup/bootstrap.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ac138229..ca9a4df1 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ by me: $ curl -s https://keybase.io/joshdata/key.asc | gpg --import gpg: key C10BDD81: public key "Joshua Tauberer " imported - $ git verify-tag v0.29 + $ git verify-tag v0.30 gpg: Signature made ..... using RSA key ID C10BDD81 gpg: Good signature from "Joshua Tauberer " gpg: WARNING: This key is not certified with a trusted signature! @@ -72,7 +72,7 @@ and on my [personal homepage](https://razor.occams.info/). (Of course, if this r Checkout the tag corresponding to the most recent release: - $ git checkout v0.29 + $ git checkout v0.30 Begin the installation. diff --git a/setup/bootstrap.sh b/setup/bootstrap.sh index 738b5abf..305a06e9 100644 --- a/setup/bootstrap.sh +++ b/setup/bootstrap.sh @@ -7,7 +7,7 @@ ######################################################### if [ -z "$TAG" ]; then - TAG=v0.29 + TAG=v0.30 fi # Are we running as root? From 6e60b47cb5b98ab50965c87a2186fa96ba926c58 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Wed, 9 Jan 2019 07:11:53 -0500 Subject: [PATCH 027/190] update bootstrap.sh script to detect the operating system and choose a different version tag depending on whether the box is running Ubuntu 14.04 or Ubuntu 18.04 --- setup/bootstrap.sh | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/setup/bootstrap.sh b/setup/bootstrap.sh index 305a06e9..f46e5f1f 100644 --- a/setup/bootstrap.sh +++ b/setup/bootstrap.sh @@ -7,7 +7,34 @@ ######################################################### if [ -z "$TAG" ]; then - TAG=v0.30 + # If a version to install isn't explicitly given as an environment + # variable, then install the latest version. But the latest version + # depends on the operating system. Existing Ubuntu 14.04 users need + # to be able to upgrade to the latest version supporting Ubuntu 14.04, + # in part because an upgrade is required before jumping to Ubuntu 18.04. + # New users on Ubuntu 18 need to get the latest version number too. + # + # Also, the system status checks read this script for TAG= to get + # the latest version, so the first TAG= line must be the one that + # we want to display in status checks. + if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/18\.04\.[0-9]/18.04/' `" == "Ubuntu 18.04 LTS" ]; then + # This machine is running Ubuntu 18.04. + TAG=v0.30 + + elif [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" == "Ubuntu 14.04 LTS" ]; then + # This machine is running Ubuntu 14.04. + echo "You are installing the last version of Mail-in-a-Box that will" + echo "support Ubuntu 14.04. If this is a new installation of Mail-in-a-Box," + echo "stop now and switch to a machine running Ubuntu 18.04. If you are" + echo "upgrading an existing Mail-in-a-Box --- great. After upgrading this" + echo "box, please visit https://mailinabox.email for notes on how to upgrade" + echo "to Ubuntu 18.04." + TAG=v0.30 + + else + echo "This script must be run on a system running Ubuntu 18.04 or Ubuntu 14.04." + exit + fi fi # Are we running as root? From 8e0d9b9f213598ffa121d0487d530f4d12b62e9f Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Wed, 9 Jan 2019 07:33:21 -0500 Subject: [PATCH 028/190] update list of tls ciphers supported --- tests/tls.py | 2 +- tests/tls_results.txt | 220 ++++++++++++++++++------------------------ 2 files changed, 93 insertions(+), 129 deletions(-) diff --git a/tests/tls.py b/tests/tls.py index 0c7b945a..e06ddcc9 100644 --- a/tests/tls.py +++ b/tests/tls.py @@ -17,7 +17,7 @@ # through some other host you can ssh into (maybe the box # itself?): # -# python3 --proxy user@ssh_host yourservername +# python3 tls.py --proxy user@ssh_host yourservername # # (This will launch "ssh -N -L10023:yourservername:testport user@ssh_host" # to create a tunnel.) diff --git a/tests/tls_results.txt b/tests/tls_results.txt index 33ebb7d1..8747bac3 100644 --- a/tests/tls_results.txt +++ b/tests/tls_results.txt @@ -13,18 +13,18 @@ PORT 25 * Session Resumption: With Session IDs: OK - Supported (5 successful, 0 failed, 0 errors, 5 total attempts). - With TLS Session Tickets: NOT SUPPORTED - TLS ticket not assigned. + With TLS Session Tickets: OK - Supported * SSLV2 Cipher Suites: Server rejected all cipher suites. * TLSV1_2 Cipher Suites: Preferred: - ECDHE-RSA-AES256-GCM-SHA384 ECDH-256 bits 256 bits 250 2.0.0 Ok + ECDHE-RSA-AES256-GCM-SHA384 ECDH-521 bits 256 bits 250 2.0.0 Ok Accepted: - ECDHE-RSA-AES256-SHA384 ECDH-256 bits 256 bits 250 2.0.0 Ok - ECDHE-RSA-AES256-SHA ECDH-256 bits 256 bits 250 2.0.0 Ok - ECDHE-RSA-AES256-GCM-SHA384 ECDH-256 bits 256 bits 250 2.0.0 Ok + ECDHE-RSA-AES256-SHA384 ECDH-521 bits 256 bits 250 2.0.0 Ok + ECDHE-RSA-AES256-SHA ECDH-521 bits 256 bits 250 2.0.0 Ok + ECDHE-RSA-AES256-GCM-SHA384 ECDH-521 bits 256 bits 250 2.0.0 Ok DHE-RSA-CAMELLIA256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok DHE-RSA-AES256-SHA256 DH-2048 bits 256 bits 250 2.0.0 Ok DHE-RSA-AES256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok @@ -33,9 +33,9 @@ PORT 25 AES256-SHA256 - 256 bits 250 2.0.0 Ok AES256-SHA - 256 bits 250 2.0.0 Ok AES256-GCM-SHA384 - 256 bits 250 2.0.0 Ok - ECDHE-RSA-AES128-SHA256 ECDH-256 bits 128 bits 250 2.0.0 Ok - ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok - ECDHE-RSA-AES128-GCM-SHA256 ECDH-256 bits 128 bits 250 2.0.0 Ok + ECDHE-RSA-AES128-SHA256 ECDH-521 bits 128 bits 250 2.0.0 Ok + ECDHE-RSA-AES128-SHA ECDH-521 bits 128 bits 250 2.0.0 Ok + ECDHE-RSA-AES128-GCM-SHA256 ECDH-521 bits 128 bits 250 2.0.0 Ok DHE-RSA-SEED-SHA DH-2048 bits 128 bits 250 2.0.0 Ok DHE-RSA-CAMELLIA128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok DHE-RSA-AES128-SHA256 DH-2048 bits 128 bits 250 2.0.0 Ok @@ -46,56 +46,47 @@ PORT 25 AES128-SHA256 - 128 bits 250 2.0.0 Ok AES128-SHA - 128 bits 250 2.0.0 Ok AES128-GCM-SHA256 - 128 bits 250 2.0.0 Ok - ECDHE-RSA-DES-CBC3-SHA ECDH-256 bits 112 bits 250 2.0.0 Ok - EDH-RSA-DES-CBC3-SHA DH-2048 bits 112 bits 250 2.0.0 Ok - DES-CBC3-SHA - 112 bits 250 2.0.0 Ok * TLSV1_1 Cipher Suites: Preferred: - ECDHE-RSA-AES256-SHA ECDH-256 bits 256 bits 250 2.0.0 Ok + ECDHE-RSA-AES256-SHA ECDH-521 bits 256 bits 250 2.0.0 Ok Accepted: - ECDHE-RSA-AES256-SHA ECDH-256 bits 256 bits 250 2.0.0 Ok + ECDHE-RSA-AES256-SHA ECDH-521 bits 256 bits 250 2.0.0 Ok DHE-RSA-CAMELLIA256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok DHE-RSA-AES256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok CAMELLIA256-SHA - 256 bits 250 2.0.0 Ok AES256-SHA - 256 bits 250 2.0.0 Ok - ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok + ECDHE-RSA-AES128-SHA ECDH-521 bits 128 bits 250 2.0.0 Ok DHE-RSA-SEED-SHA DH-2048 bits 128 bits 250 2.0.0 Ok DHE-RSA-CAMELLIA128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok DHE-RSA-AES128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok SEED-SHA - 128 bits 250 2.0.0 Ok CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok AES128-SHA - 128 bits 250 2.0.0 Ok - ECDHE-RSA-DES-CBC3-SHA ECDH-256 bits 112 bits 250 2.0.0 Ok - EDH-RSA-DES-CBC3-SHA DH-2048 bits 112 bits 250 2.0.0 Ok - DES-CBC3-SHA - 112 bits 250 2.0.0 Ok - - * TLSV1 Cipher Suites: - Preferred: - ECDHE-RSA-AES256-SHA ECDH-256 bits 256 bits 250 2.0.0 Ok - Accepted: - ECDHE-RSA-AES256-SHA ECDH-256 bits 256 bits 250 2.0.0 Ok - DHE-RSA-CAMELLIA256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok - DHE-RSA-AES256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok - CAMELLIA256-SHA - 256 bits 250 2.0.0 Ok - AES256-SHA - 256 bits 250 2.0.0 Ok - ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok - DHE-RSA-SEED-SHA DH-2048 bits 128 bits 250 2.0.0 Ok - DHE-RSA-CAMELLIA128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok - DHE-RSA-AES128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok - SEED-SHA - 128 bits 250 2.0.0 Ok - CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok - AES128-SHA - 128 bits 250 2.0.0 Ok - ECDHE-RSA-DES-CBC3-SHA ECDH-256 bits 112 bits 250 2.0.0 Ok - EDH-RSA-DES-CBC3-SHA DH-2048 bits 112 bits 250 2.0.0 Ok - DES-CBC3-SHA - 112 bits 250 2.0.0 Ok * SSLV3 Cipher Suites: Server rejected all cipher suites. + * TLSV1 Cipher Suites: + Preferred: + ECDHE-RSA-AES256-SHA ECDH-521 bits 256 bits 250 2.0.0 Ok + Accepted: + ECDHE-RSA-AES256-SHA ECDH-521 bits 256 bits 250 2.0.0 Ok + DHE-RSA-CAMELLIA256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok + DHE-RSA-AES256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok + CAMELLIA256-SHA - 256 bits 250 2.0.0 Ok + AES256-SHA - 256 bits 250 2.0.0 Ok + ECDHE-RSA-AES128-SHA ECDH-521 bits 128 bits 250 2.0.0 Ok + DHE-RSA-SEED-SHA DH-2048 bits 128 bits 250 2.0.0 Ok + DHE-RSA-CAMELLIA128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok + DHE-RSA-AES128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok + SEED-SHA - 128 bits 250 2.0.0 Ok + CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok + AES128-SHA - 128 bits 250 2.0.0 Ok + Should Not Offer: (none -- good) - Could Also Offer: DHE-DSS-AES128-GCM-SHA256, DHE-DSS-AES128-SHA, DHE-DSS-AES128-SHA256, DHE-DSS-AES256-GCM-SHA384, DHE-DSS-AES256-SHA, DHE-DSS-AES256-SHA256, DHE-DSS-CAMELLIA128-SHA, DHE-DSS-CAMELLIA256-SHA, DHE-DSS-SEED-SHA, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA, ECDHE-ECDSA-AES256-SHA384, ECDHE-ECDSA-DES-CBC3-SHA - Supported Clients: BingPreview/Jan 2015, OpenSSL/1.0.2, Yahoo Slurp/Jan 2015, OpenSSL/1.0.1l, YandexBot/Jan 2015, Android/4.4.2, Safari/7/iOS 7.1, Safari/8/iOS 8.1.2, Safari/6/iOS 6.0.1, Safari/7/OS X 10.9, Safari/8/OS X 10.10, Baidu/Jan 2015, Firefox/31.3.0 ESR/Win 7, IE/11/Win 7, IE/11/Win 8.1, IE Mobile/11/Win Phone 8.1, Java/8u31, Android/5.0.0, Googlebot/Feb 2015, Chrome/42/OS X, Android/4.1.1, Android/4.3, Android/4.0.4, Android/4.2.2, Safari/5.1.9/OS X 10.6.8, Safari/6.0.4/OS X 10.8.4, Firefox/37/OS X, OpenSSL/0.9.8y, Java/7u25, IE Mobile/10/Win Phone 8.0, IE/8-10/Win 7, IE/7/Vista, Android/2.3.7, Java/6u45, IE/8/XP + Could Also Offer: AES128-CCM, AES128-CCM8, AES256-CCM, AES256-CCM8, CAMELLIA128-SHA256, CAMELLIA256-SHA256, DHE-DSS-AES128-GCM-SHA256, DHE-DSS-AES128-SHA, DHE-DSS-AES128-SHA256, DHE-DSS-AES256-GCM-SHA384, DHE-DSS-AES256-SHA, DHE-DSS-AES256-SHA256, DHE-DSS-CAMELLIA128-SHA, DHE-DSS-CAMELLIA128-SHA256, DHE-DSS-CAMELLIA256-SHA, DHE-DSS-CAMELLIA256-SHA256, DHE-DSS-SEED-SHA, DHE-RSA-AES128-CCM, DHE-RSA-AES128-CCM8, DHE-RSA-AES256-CCM, DHE-RSA-AES256-CCM8, DHE-RSA-CAMELLIA128-SHA256, DHE-RSA-CAMELLIA256-SHA256, DHE-RSA-CHACHA20-POLY1305, ECDHE-ECDSA-AES128-CCM, ECDHE-ECDSA-AES128-CCM8, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-CCM, ECDHE-ECDSA-AES256-CCM8, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA, ECDHE-ECDSA-AES256-SHA384, ECDHE-ECDSA-CAMELLIA128-SHA256, ECDHE-ECDSA-CAMELLIA256-SHA384, ECDHE-ECDSA-CHACHA20-POLY1305, ECDHE-RSA-CAMELLIA128-SHA256, ECDHE-RSA-CAMELLIA256-SHA384, ECDHE-RSA-CHACHA20-POLY1305 + Supported Clients: Yahoo Slurp/Jan 2015, OpenSSL/1.0.2, BingPreview/Jan 2015, OpenSSL/1.0.1l, YandexBot/Jan 2015, Android/4.4.2, Safari/6/iOS 6.0.1, Safari/8/OS X 10.10, Safari/7/OS X 10.9, Safari/7/iOS 7.1, IE/11/Win 8.1, Safari/8/iOS 8.1.2, IE Mobile/11/Win Phone 8.1, IE/11/Win 7, Baidu/Jan 2015, Firefox/31.3.0 ESR/Win 7, Android/5.0.0, Chrome/42/OS X, Java/8u31, Googlebot/Feb 2015, Firefox/37/OS X, Android/4.3, Android/4.2.2, Safari/5.1.9/OS X 10.6.8, Android/4.0.4, Android/4.1.1, Safari/6.0.4/OS X 10.8.4, IE Mobile/10/Win Phone 8.0, IE/8-10/Win 7, IE/7/Vista, OpenSSL/0.9.8y, Java/7u25, Android/2.3.7, Java/6u45 PORT 587 -------- @@ -112,18 +103,18 @@ PORT 587 * Session Resumption: With Session IDs: OK - Supported (5 successful, 0 failed, 0 errors, 5 total attempts). - With TLS Session Tickets: NOT SUPPORTED - TLS ticket not assigned. + With TLS Session Tickets: OK - Supported * SSLV2 Cipher Suites: Server rejected all cipher suites. * TLSV1_2 Cipher Suites: Preferred: - ECDHE-RSA-AES256-GCM-SHA384 ECDH-256 bits 256 bits 250 2.0.0 Ok + ECDHE-RSA-AES256-GCM-SHA384 ECDH-521 bits 256 bits 250 2.0.0 Ok Accepted: - ECDHE-RSA-AES256-SHA384 ECDH-256 bits 256 bits 250 2.0.0 Ok - ECDHE-RSA-AES256-SHA ECDH-256 bits 256 bits 250 2.0.0 Ok - ECDHE-RSA-AES256-GCM-SHA384 ECDH-256 bits 256 bits 250 2.0.0 Ok + ECDHE-RSA-AES256-SHA384 ECDH-521 bits 256 bits 250 2.0.0 Ok + ECDHE-RSA-AES256-SHA ECDH-521 bits 256 bits 250 2.0.0 Ok + ECDHE-RSA-AES256-GCM-SHA384 ECDH-521 bits 256 bits 250 2.0.0 Ok DHE-RSA-CAMELLIA256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok DHE-RSA-AES256-SHA256 DH-2048 bits 256 bits 250 2.0.0 Ok DHE-RSA-AES256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok @@ -132,9 +123,9 @@ PORT 587 AES256-SHA256 - 256 bits 250 2.0.0 Ok AES256-SHA - 256 bits 250 2.0.0 Ok AES256-GCM-SHA384 - 256 bits 250 2.0.0 Ok - ECDHE-RSA-AES128-SHA256 ECDH-256 bits 128 bits 250 2.0.0 Ok - ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok - ECDHE-RSA-AES128-GCM-SHA256 ECDH-256 bits 128 bits 250 2.0.0 Ok + ECDHE-RSA-AES128-SHA256 ECDH-521 bits 128 bits 250 2.0.0 Ok + ECDHE-RSA-AES128-SHA ECDH-521 bits 128 bits 250 2.0.0 Ok + ECDHE-RSA-AES128-GCM-SHA256 ECDH-521 bits 128 bits 250 2.0.0 Ok DHE-RSA-SEED-SHA DH-2048 bits 128 bits 250 2.0.0 Ok DHE-RSA-CAMELLIA128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok DHE-RSA-AES128-SHA256 DH-2048 bits 128 bits 250 2.0.0 Ok @@ -148,31 +139,14 @@ PORT 587 * TLSV1_1 Cipher Suites: Preferred: - ECDHE-RSA-AES256-SHA ECDH-256 bits 256 bits 250 2.0.0 Ok + ECDHE-RSA-AES256-SHA ECDH-521 bits 256 bits 250 2.0.0 Ok Accepted: - ECDHE-RSA-AES256-SHA ECDH-256 bits 256 bits 250 2.0.0 Ok + ECDHE-RSA-AES256-SHA ECDH-521 bits 256 bits 250 2.0.0 Ok DHE-RSA-CAMELLIA256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok DHE-RSA-AES256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok CAMELLIA256-SHA - 256 bits 250 2.0.0 Ok AES256-SHA - 256 bits 250 2.0.0 Ok - ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok - DHE-RSA-SEED-SHA DH-2048 bits 128 bits 250 2.0.0 Ok - DHE-RSA-CAMELLIA128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok - DHE-RSA-AES128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok - SEED-SHA - 128 bits 250 2.0.0 Ok - CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok - AES128-SHA - 128 bits 250 2.0.0 Ok - - * TLSV1 Cipher Suites: - Preferred: - ECDHE-RSA-AES256-SHA ECDH-256 bits 256 bits 250 2.0.0 Ok - Accepted: - ECDHE-RSA-AES256-SHA ECDH-256 bits 256 bits 250 2.0.0 Ok - DHE-RSA-CAMELLIA256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok - DHE-RSA-AES256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok - CAMELLIA256-SHA - 256 bits 250 2.0.0 Ok - AES256-SHA - 256 bits 250 2.0.0 Ok - ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok + ECDHE-RSA-AES128-SHA ECDH-521 bits 128 bits 250 2.0.0 Ok DHE-RSA-SEED-SHA DH-2048 bits 128 bits 250 2.0.0 Ok DHE-RSA-CAMELLIA128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok DHE-RSA-AES128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok @@ -183,9 +157,26 @@ PORT 587 * SSLV3 Cipher Suites: Server rejected all cipher suites. + * TLSV1 Cipher Suites: + Preferred: + ECDHE-RSA-AES256-SHA ECDH-521 bits 256 bits 250 2.0.0 Ok + Accepted: + ECDHE-RSA-AES256-SHA ECDH-521 bits 256 bits 250 2.0.0 Ok + DHE-RSA-CAMELLIA256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok + DHE-RSA-AES256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok + CAMELLIA256-SHA - 256 bits 250 2.0.0 Ok + AES256-SHA - 256 bits 250 2.0.0 Ok + ECDHE-RSA-AES128-SHA ECDH-521 bits 128 bits 250 2.0.0 Ok + DHE-RSA-SEED-SHA DH-2048 bits 128 bits 250 2.0.0 Ok + DHE-RSA-CAMELLIA128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok + DHE-RSA-AES128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok + SEED-SHA - 128 bits 250 2.0.0 Ok + CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok + AES128-SHA - 128 bits 250 2.0.0 Ok + Should Not Offer: AES128-GCM-SHA256, AES128-SHA, AES128-SHA256, AES256-GCM-SHA384, AES256-SHA, AES256-SHA256, CAMELLIA128-SHA, CAMELLIA256-SHA, DHE-RSA-AES128-GCM-SHA256, DHE-RSA-AES128-SHA, DHE-RSA-AES128-SHA256, DHE-RSA-AES256-GCM-SHA384, DHE-RSA-AES256-SHA, DHE-RSA-AES256-SHA256, DHE-RSA-CAMELLIA128-SHA, DHE-RSA-CAMELLIA256-SHA, DHE-RSA-SEED-SHA, ECDHE-RSA-AES128-SHA, ECDHE-RSA-AES256-SHA, SEED-SHA - Could Also Offer: ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA384 - Supported Clients: BingPreview/Jan 2015, OpenSSL/1.0.2, Yahoo Slurp/Jan 2015, OpenSSL/1.0.1l, YandexBot/Jan 2015, Android/4.4.2, Safari/7/iOS 7.1, IE/11/Win 7, IE/11/Win 8.1, Safari/8/iOS 8.1.2, Safari/6/iOS 6.0.1, Safari/7/OS X 10.9, IE Mobile/11/Win Phone 8.1, Safari/8/OS X 10.10, Baidu/Jan 2015, Firefox/31.3.0 ESR/Win 7, Java/8u31, Android/5.0.0, Chrome/42/OS X, Googlebot/Feb 2015, Firefox/37/OS X, Android/4.1.1, Android/4.3, Android/4.0.4, Android/4.2.2, Safari/5.1.9/OS X 10.6.8, Safari/6.0.4/OS X 10.8.4, OpenSSL/0.9.8y, IE Mobile/10/Win Phone 8.0, IE/8-10/Win 7, IE/7/Vista, Java/7u25, Android/2.3.7, Java/6u45 + Could Also Offer: ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA384, ECDHE-ECDSA-CHACHA20-POLY1305, ECDHE-RSA-CHACHA20-POLY1305 + Supported Clients: Yahoo Slurp/Jan 2015, OpenSSL/1.0.2, BingPreview/Jan 2015, OpenSSL/1.0.1l, YandexBot/Jan 2015, Android/4.4.2, Safari/6/iOS 6.0.1, Safari/8/OS X 10.10, Safari/7/OS X 10.9, Safari/7/iOS 7.1, IE/11/Win 8.1, Safari/8/iOS 8.1.2, IE Mobile/11/Win Phone 8.1, IE/11/Win 7, Baidu/Jan 2015, Firefox/31.3.0 ESR/Win 7, Android/5.0.0, Chrome/42/OS X, Java/8u31, Googlebot/Feb 2015, Firefox/37/OS X, Android/4.3, Android/4.2.2, Safari/5.1.9/OS X 10.6.8, Android/4.0.4, Android/4.1.1, Safari/6.0.4/OS X 10.8.4, IE Mobile/10/Win Phone 8.0, IE/8-10/Win 7, IE/7/Vista, OpenSSL/0.9.8y, Java/7u25, Android/2.3.7, Java/6u45 PORT 443 -------- @@ -197,19 +188,19 @@ PORT 443 Client-initiated Renegotiations: OK - Rejected Secure Renegotiation: OK - Supported - * OpenSSL Heartbleed: - OK - Not vulnerable to Heartbleed - * HTTP Strict Transport Security: OK - HSTS header received: max-age=15768000 -Unhandled exception when processing --chrome_sha1: -exceptions.TypeError - Incorrect padding - * Session Resumption: With Session IDs: OK - Supported (5 successful, 0 failed, 0 errors, 5 total attempts). With TLS Session Tickets: OK - Supported + * OpenSSL Heartbleed: + OK - Not vulnerable to Heartbleed + +Unhandled exception when processing --chrome_sha1: +exceptions.TypeError - Incorrect padding + * SSLV2 Cipher Suites: Server rejected all cipher suites. @@ -235,9 +226,6 @@ exceptions.TypeError - Incorrect padding AES128-SHA256 - 128 bits HTTP 200 OK AES128-SHA - 128 bits HTTP 200 OK AES128-GCM-SHA256 - 128 bits HTTP 200 OK - ECDHE-RSA-DES-CBC3-SHA ECDH-256 bits 112 bits HTTP 200 OK - EDH-RSA-DES-CBC3-SHA DH-2048 bits 112 bits HTTP 200 OK - DES-CBC3-SHA - 112 bits HTTP 200 OK * TLSV1_1 Cipher Suites: Preferred: @@ -249,9 +237,9 @@ exceptions.TypeError - Incorrect padding ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits HTTP 200 OK DHE-RSA-AES128-SHA DH-2048 bits 128 bits HTTP 200 OK AES128-SHA - 128 bits HTTP 200 OK - ECDHE-RSA-DES-CBC3-SHA ECDH-256 bits 112 bits HTTP 200 OK - EDH-RSA-DES-CBC3-SHA DH-2048 bits 112 bits HTTP 200 OK - DES-CBC3-SHA - 112 bits HTTP 200 OK + + * SSLV3 Cipher Suites: + Server rejected all cipher suites. * TLSV1 Cipher Suites: Preferred: @@ -262,17 +250,11 @@ exceptions.TypeError - Incorrect padding AES256-SHA - 256 bits HTTP 200 OK ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits HTTP 200 OK DHE-RSA-AES128-SHA DH-2048 bits 128 bits HTTP 200 OK - AES128-SHA - 128 bits HTTP 200 OK - ECDHE-RSA-DES-CBC3-SHA ECDH-256 bits 112 bits HTTP 200 OK - EDH-RSA-DES-CBC3-SHA DH-2048 bits 112 bits HTTP 200 OK - DES-CBC3-SHA - 112 bits HTTP 200 OK - - * SSLV3 Cipher Suites: - Server rejected all cipher suites. + AES128-SHA - 128 bits HTTP 200 OK Should Not Offer: (none -- good) - Could Also Offer: ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA, ECDHE-ECDSA-AES256-SHA384, ECDHE-ECDSA-DES-CBC3-SHA - Supported Clients: BingPreview/Jan 2015, OpenSSL/1.0.2, YandexBot/Jan 2015, OpenSSL/1.0.1l, Yahoo Slurp/Jan 2015, Android/4.4.2, Safari/7/iOS 7.1, Safari/8/iOS 8.1.2, Safari/6/iOS 6.0.1, Safari/7/OS X 10.9, Safari/8/OS X 10.10, IE/11/Win 7, IE/11/Win 8.1, IE Mobile/11/Win Phone 8.1, Java/8u31, Android/5.0.0, Googlebot/Feb 2015, Firefox/31.3.0 ESR/Win 7, Chrome/42/OS X, Baidu/Jan 2015, Android/4.1.1, Android/4.3, Android/4.0.4, Android/4.2.2, Safari/5.1.9/OS X 10.6.8, Safari/6.0.4/OS X 10.8.4, Firefox/37/OS X, OpenSSL/0.9.8y, Java/7u25, IE Mobile/10/Win Phone 8.0, IE/8-10/Win 7, IE/7/Vista, Java/6u45, Android/2.3.7, IE/8/XP + Could Also Offer: ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA, ECDHE-ECDSA-AES256-SHA384, ECDHE-ECDSA-CHACHA20-POLY1305, ECDHE-RSA-CHACHA20-POLY1305 + Supported Clients: Yahoo Slurp/Jan 2015, OpenSSL/1.0.2, YandexBot/Jan 2015, BingPreview/Jan 2015, OpenSSL/1.0.1l, Android/4.4.2, Safari/6/iOS 6.0.1, Safari/8/OS X 10.10, Safari/7/OS X 10.9, Safari/7/iOS 7.1, IE/11/Win 8.1, Safari/8/iOS 8.1.2, IE Mobile/11/Win Phone 8.1, IE/11/Win 7, Android/5.0.0, Chrome/42/OS X, Java/8u31, Googlebot/Feb 2015, Firefox/31.3.0 ESR/Win 7, Firefox/37/OS X, Android/4.3, Android/4.2.2, Baidu/Jan 2015, Safari/5.1.9/OS X 10.6.8, Android/4.0.4, Android/4.1.1, Safari/6.0.4/OS X 10.8.4, IE Mobile/10/Win Phone 8.0, IE/8-10/Win 7, IE/7/Vista, OpenSSL/0.9.8y, Java/7u25, Android/2.3.7, Java/6u45 PORT 993 -------- @@ -286,13 +268,13 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec * OpenSSL Heartbleed: OK - Not vulnerable to Heartbleed - * SSLV2 Cipher Suites: - Server rejected all cipher suites. - * Session Resumption: With Session IDs: NOT SUPPORTED (0 successful, 5 failed, 0 errors, 5 total attempts). With TLS Session Tickets: NOT SUPPORTED - TLS ticket assigned but not accepted. + * SSLV2 Cipher Suites: + Server rejected all cipher suites. + * TLSV1_2 Cipher Suites: Preferred: ECDHE-RSA-AES128-GCM-SHA256 ECDH-384 bits 128 bits @@ -315,9 +297,6 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec AES128-SHA256 - 128 bits AES128-SHA - 128 bits AES128-GCM-SHA256 - 128 bits - ECDHE-RSA-DES-CBC3-SHA ECDH-384 bits 112 bits - EDH-RSA-DES-CBC3-SHA DH-2048 bits 112 bits - DES-CBC3-SHA - 112 bits * TLSV1_1 Cipher Suites: Preferred: @@ -329,9 +308,9 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec ECDHE-RSA-AES128-SHA ECDH-384 bits 128 bits DHE-RSA-AES128-SHA DH-2048 bits 128 bits AES128-SHA - 128 bits - ECDHE-RSA-DES-CBC3-SHA ECDH-384 bits 112 bits - EDH-RSA-DES-CBC3-SHA DH-2048 bits 112 bits - DES-CBC3-SHA - 112 bits + + * SSLV3 Cipher Suites: + Server rejected all cipher suites. * TLSV1 Cipher Suites: Preferred: @@ -342,17 +321,11 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec AES256-SHA - 256 bits ECDHE-RSA-AES128-SHA ECDH-384 bits 128 bits DHE-RSA-AES128-SHA DH-2048 bits 128 bits - AES128-SHA - 128 bits - ECDHE-RSA-DES-CBC3-SHA ECDH-384 bits 112 bits - EDH-RSA-DES-CBC3-SHA DH-2048 bits 112 bits - DES-CBC3-SHA - 112 bits + AES128-SHA - 128 bits - * SSLV3 Cipher Suites: - Server rejected all cipher suites. - - Should Not Offer: AES128-GCM-SHA256, AES128-SHA, AES128-SHA256, AES256-GCM-SHA384, AES256-SHA, AES256-SHA256, DES-CBC3-SHA, DHE-RSA-AES128-GCM-SHA256, DHE-RSA-AES128-SHA, DHE-RSA-AES128-SHA256, DHE-RSA-AES256-GCM-SHA384, DHE-RSA-AES256-SHA, DHE-RSA-AES256-SHA256, ECDHE-RSA-AES128-SHA, ECDHE-RSA-AES256-SHA, ECDHE-RSA-DES-CBC3-SHA, EDH-RSA-DES-CBC3-SHA - Could Also Offer: ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA384 - Supported Clients: BingPreview/Jan 2015, OpenSSL/1.0.2, YandexBot/Jan 2015, OpenSSL/1.0.1l, Yahoo Slurp/Jan 2015, Android/4.4.2, Safari/7/iOS 7.1, Safari/8/iOS 8.1.2, Safari/6/iOS 6.0.1, Safari/7/OS X 10.9, Safari/8/OS X 10.10, IE/11/Win 7, IE/11/Win 8.1, IE Mobile/11/Win Phone 8.1, Java/8u31, Android/5.0.0, Googlebot/Feb 2015, Firefox/31.3.0 ESR/Win 7, Chrome/42/OS X, Baidu/Jan 2015, Android/4.1.1, Android/4.3, Android/4.0.4, Android/4.2.2, Safari/5.1.9/OS X 10.6.8, Safari/6.0.4/OS X 10.8.4, Firefox/37/OS X, OpenSSL/0.9.8y, Java/7u25, IE Mobile/10/Win Phone 8.0, IE/8-10/Win 7, IE/7/Vista, Java/6u45, Android/2.3.7, IE/8/XP + Should Not Offer: AES128-GCM-SHA256, AES128-SHA, AES128-SHA256, AES256-GCM-SHA384, AES256-SHA, AES256-SHA256, DHE-RSA-AES128-GCM-SHA256, DHE-RSA-AES128-SHA, DHE-RSA-AES128-SHA256, DHE-RSA-AES256-GCM-SHA384, DHE-RSA-AES256-SHA, DHE-RSA-AES256-SHA256, ECDHE-RSA-AES128-SHA, ECDHE-RSA-AES256-SHA + Could Also Offer: ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA384, ECDHE-ECDSA-CHACHA20-POLY1305, ECDHE-RSA-CHACHA20-POLY1305 + Supported Clients: Yahoo Slurp/Jan 2015, OpenSSL/1.0.2, YandexBot/Jan 2015, BingPreview/Jan 2015, OpenSSL/1.0.1l, Android/4.4.2, Safari/6/iOS 6.0.1, Safari/8/OS X 10.10, Safari/7/OS X 10.9, Safari/7/iOS 7.1, IE/11/Win 8.1, Safari/8/iOS 8.1.2, IE Mobile/11/Win Phone 8.1, IE/11/Win 7, Android/5.0.0, Chrome/42/OS X, Java/8u31, Googlebot/Feb 2015, Firefox/31.3.0 ESR/Win 7, Firefox/37/OS X, Android/4.3, Android/4.2.2, Baidu/Jan 2015, Safari/5.1.9/OS X 10.6.8, Android/4.0.4, Android/4.1.1, Safari/6.0.4/OS X 10.8.4, IE Mobile/10/Win Phone 8.0, IE/8-10/Win 7, IE/7/Vista, OpenSSL/0.9.8y, Java/7u25, Android/2.3.7, Java/6u45 PORT 995 -------- @@ -366,13 +339,13 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec * OpenSSL Heartbleed: OK - Not vulnerable to Heartbleed - * SSLV2 Cipher Suites: - Server rejected all cipher suites. - * Session Resumption: With Session IDs: NOT SUPPORTED (0 successful, 5 failed, 0 errors, 5 total attempts). With TLS Session Tickets: NOT SUPPORTED - TLS ticket assigned but not accepted. + * SSLV2 Cipher Suites: + Server rejected all cipher suites. + * TLSV1_2 Cipher Suites: Preferred: ECDHE-RSA-AES128-GCM-SHA256 ECDH-384 bits 128 bits @@ -395,9 +368,6 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec AES128-SHA256 - 128 bits AES128-SHA - 128 bits AES128-GCM-SHA256 - 128 bits - ECDHE-RSA-DES-CBC3-SHA ECDH-384 bits 112 bits - EDH-RSA-DES-CBC3-SHA DH-2048 bits 112 bits - DES-CBC3-SHA - 112 bits * TLSV1_1 Cipher Suites: Preferred: @@ -409,9 +379,9 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec ECDHE-RSA-AES128-SHA ECDH-384 bits 128 bits DHE-RSA-AES128-SHA DH-2048 bits 128 bits AES128-SHA - 128 bits - ECDHE-RSA-DES-CBC3-SHA ECDH-384 bits 112 bits - EDH-RSA-DES-CBC3-SHA DH-2048 bits 112 bits - DES-CBC3-SHA - 112 bits + + * SSLV3 Cipher Suites: + Server rejected all cipher suites. * TLSV1 Cipher Suites: Preferred: @@ -422,15 +392,9 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec AES256-SHA - 256 bits ECDHE-RSA-AES128-SHA ECDH-384 bits 128 bits DHE-RSA-AES128-SHA DH-2048 bits 128 bits - AES128-SHA - 128 bits - ECDHE-RSA-DES-CBC3-SHA ECDH-384 bits 112 bits - EDH-RSA-DES-CBC3-SHA DH-2048 bits 112 bits - DES-CBC3-SHA - 112 bits + AES128-SHA - 128 bits - * SSLV3 Cipher Suites: - Server rejected all cipher suites. - - Should Not Offer: AES128-GCM-SHA256, AES128-SHA, AES128-SHA256, AES256-GCM-SHA384, AES256-SHA, AES256-SHA256, DES-CBC3-SHA, DHE-RSA-AES128-GCM-SHA256, DHE-RSA-AES128-SHA, DHE-RSA-AES128-SHA256, DHE-RSA-AES256-GCM-SHA384, DHE-RSA-AES256-SHA, DHE-RSA-AES256-SHA256, ECDHE-RSA-AES128-SHA, ECDHE-RSA-AES256-SHA, ECDHE-RSA-DES-CBC3-SHA, EDH-RSA-DES-CBC3-SHA - Could Also Offer: ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA384 - Supported Clients: BingPreview/Jan 2015, OpenSSL/1.0.2, YandexBot/Jan 2015, OpenSSL/1.0.1l, Yahoo Slurp/Jan 2015, Android/4.4.2, Safari/7/iOS 7.1, Safari/8/iOS 8.1.2, Safari/6/iOS 6.0.1, Safari/7/OS X 10.9, Safari/8/OS X 10.10, IE/11/Win 7, IE/11/Win 8.1, IE Mobile/11/Win Phone 8.1, Java/8u31, Android/5.0.0, Googlebot/Feb 2015, Firefox/31.3.0 ESR/Win 7, Chrome/42/OS X, Baidu/Jan 2015, Android/4.1.1, Android/4.3, Android/4.0.4, Android/4.2.2, Safari/5.1.9/OS X 10.6.8, Safari/6.0.4/OS X 10.8.4, Firefox/37/OS X, OpenSSL/0.9.8y, Java/7u25, IE Mobile/10/Win Phone 8.0, IE/8-10/Win 7, IE/7/Vista, Java/6u45, Android/2.3.7, IE/8/XP + Should Not Offer: AES128-GCM-SHA256, AES128-SHA, AES128-SHA256, AES256-GCM-SHA384, AES256-SHA, AES256-SHA256, DHE-RSA-AES128-GCM-SHA256, DHE-RSA-AES128-SHA, DHE-RSA-AES128-SHA256, DHE-RSA-AES256-GCM-SHA384, DHE-RSA-AES256-SHA, DHE-RSA-AES256-SHA256, ECDHE-RSA-AES128-SHA, ECDHE-RSA-AES256-SHA + Could Also Offer: ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA384, ECDHE-ECDSA-CHACHA20-POLY1305, ECDHE-RSA-CHACHA20-POLY1305 + Supported Clients: Yahoo Slurp/Jan 2015, OpenSSL/1.0.2, YandexBot/Jan 2015, BingPreview/Jan 2015, OpenSSL/1.0.1l, Android/4.4.2, Safari/6/iOS 6.0.1, Safari/8/OS X 10.10, Safari/7/OS X 10.9, Safari/7/iOS 7.1, IE/11/Win 8.1, Safari/8/iOS 8.1.2, IE Mobile/11/Win Phone 8.1, IE/11/Win 7, Android/5.0.0, Chrome/42/OS X, Java/8u31, Googlebot/Feb 2015, Firefox/31.3.0 ESR/Win 7, Firefox/37/OS X, Android/4.3, Android/4.2.2, Baidu/Jan 2015, Safari/5.1.9/OS X 10.6.8, Android/4.0.4, Android/4.1.1, Safari/6.0.4/OS X 10.8.4, IE Mobile/10/Win Phone 8.0, IE/8-10/Win 7, IE/7/Vista, OpenSSL/0.9.8y, Java/7u25, Android/2.3.7, Java/6u45 From e56c55efe83bce2e4017ddf241cf72dc6059a263 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Wed, 9 Jan 2019 07:42:52 -0500 Subject: [PATCH 029/190] write changelog summary for the Ubuntu 18.04 upgrade --- CHANGELOG.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5b47259..4b07482b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,21 @@ CHANGELOG In Development -------------- -This is the first release for Ubuntu 18.04. Mail-in-a-Box can now **only** be installed on Ubuntu 18.04. +This is the first release for Ubuntu 18.04. This version and versions going forward can **only** be installed on Ubuntu 18.04; however, upgrades of existing Ubuntu 14.04 boxes to the latest version supporting Ubuntu 14.04 continue to work as normal. -When **upgrading**, you **must** upgrade your existing Ubuntu 14.04 Mail-in-a-Box box to the latest release supporting Ubuntu 14.04 --- that's v0.30 --- first. If you are running an older version of Mail-in-a-Box which has an old version of ownCloud or Nextcloud, you will *not* be able to upgrade your data because older versions of ownCloud and Nextcloud that are required to perform the upgrade *cannot* be run on Ubuntu 18.04. +When **upgrading**, you **must upgrade your existing Ubuntu 14.04 Mail-in-a-Box box** to the latest release supporting Ubuntu 14.04 --- that's v0.30 --- first. If you are running an older version of Mail-in-a-Box which has an old version of ownCloud or Nextcloud, you will *not* be able to upgrade your data because older versions of ownCloud and Nextcloud that are required to perform the upgrade *cannot* be run on Ubuntu 18.04. To upgrade from Ubuntu 14.04 to Ubuntu 18.04, you **must create a fresh Ubuntu 18.04 machine** before installing this version. In-place upgrades of servers are not supported. Since Ubuntu's support for Ubuntu 14.04 has almost ended, everyone is encouraged to upgrade. + +Setup: + +* Mail-in-a-Box now targets Ubuntu 18.04 LTS, which will have support from Ubuntu through 2022. +* Some of the system packages updated in virtue of using Ubuntu 18.04 include postfix (2.11=>3.3) nsd (4.0=>4.1), nginx (1.4=>1.14), PHP (7.0=>7.2), Python (3.4=>3.6), fail2ban (0.8=>0.10), Duplicity (0.6=>0.7). +* [Unofficial Bash Strict Mode](http://redsymbol.net/articles/unofficial-bash-strict-mode/) is turned on for setup, which might catch previously uncaught issues during setup. + +Mail: + +* IMAP server-side full text search is no longer supported because we were using a custom-built `dovecot-lucene` package that we are no longer maintaining. +* Sending email is now disabled on port 25 --- you must log in to port 587 to send email. +* Greylisting may delay more emails from new senders. We were using a custom-built postgrey package previously that whitelisted sending domains in dnswl.org, but we are no longer maintaining that package. v0.30 (January 9, 2019) ----------------------- From cd3fb1b4870d99329e37f5c68751cbbe0bfc224f Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Wed, 9 Jan 2019 09:03:43 -0500 Subject: [PATCH 030/190] fix bootstrap.sh to not confuse the status checks about the latest version --- setup/bootstrap.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup/bootstrap.sh b/setup/bootstrap.sh index f46e5f1f..90b43ce2 100644 --- a/setup/bootstrap.sh +++ b/setup/bootstrap.sh @@ -14,9 +14,10 @@ if [ -z "$TAG" ]; then # in part because an upgrade is required before jumping to Ubuntu 18.04. # New users on Ubuntu 18 need to get the latest version number too. # - # Also, the system status checks read this script for TAG= to get - # the latest version, so the first TAG= line must be the one that - # we want to display in status checks. + # Also, the system status checks read this script for TAG = to get + # (without the space, but if we put it in a comment it would confuse + # the status checks!) the latest version, so the first such line must + # be the one that we want to display in status checks. if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/18\.04\.[0-9]/18.04/' `" == "Ubuntu 18.04 LTS" ]; then # This machine is running Ubuntu 18.04. TAG=v0.30 From c7659d90537878832dae128d2eb3195c4b5e8866 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Sat, 12 Jan 2019 08:24:15 -0500 Subject: [PATCH 031/190] v0.40 --- CHANGELOG.md | 16 +++++++++++----- README.md | 14 +++++++------- setup/bootstrap.sh | 13 +++++++------ 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b07482b..fb64785c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,18 @@ CHANGELOG ========= -In Development --------------- +v0.40 (January 12, 2019) +------------------------ -This is the first release for Ubuntu 18.04. This version and versions going forward can **only** be installed on Ubuntu 18.04; however, upgrades of existing Ubuntu 14.04 boxes to the latest version supporting Ubuntu 14.04 continue to work as normal. +This is the first release for Ubuntu 18.04. This version and versions going forward can **only** be installed on Ubuntu 18.04; however, upgrades of existing Ubuntu 14.04 boxes to the latest version supporting Ubuntu 14.04 (v0.30) continue to work as normal. -When **upgrading**, you **must upgrade your existing Ubuntu 14.04 Mail-in-a-Box box** to the latest release supporting Ubuntu 14.04 --- that's v0.30 --- first. If you are running an older version of Mail-in-a-Box which has an old version of ownCloud or Nextcloud, you will *not* be able to upgrade your data because older versions of ownCloud and Nextcloud that are required to perform the upgrade *cannot* be run on Ubuntu 18.04. To upgrade from Ubuntu 14.04 to Ubuntu 18.04, you **must create a fresh Ubuntu 18.04 machine** before installing this version. In-place upgrades of servers are not supported. Since Ubuntu's support for Ubuntu 14.04 has almost ended, everyone is encouraged to upgrade. +When **upgrading**, you **must first upgrade your existing Ubuntu 14.04 Mail-in-a-Box box** to the latest release supporting Ubuntu 14.04 --- that's v0.30 --- before you migrate to Ubuntu 18.04. If you are running an older version of Mail-in-a-Box which has an old version of ownCloud or Nextcloud, you will *not* be able to upgrade your data because older versions of ownCloud and Nextcloud that are required to perform the upgrade *cannot* be run on Ubuntu 18.04. To upgrade from Ubuntu 14.04 to Ubuntu 18.04, you **must create a fresh Ubuntu 18.04 machine** before installing this version. In-place upgrades of servers are not supported. Since Ubuntu's support for Ubuntu 14.04 has almost ended, everyone is encouraged to create a new Ubuntu 18.04 machine and migrate to it. + +For complete upgrade instructions, see: + +https://discourse.mailinabox.email/t/mail-in-a-box-version-v0-40-and-moving-to-ubuntu-18-04/4289 + +The changelog for this release follows. Setup: @@ -17,7 +23,7 @@ Setup: Mail: * IMAP server-side full text search is no longer supported because we were using a custom-built `dovecot-lucene` package that we are no longer maintaining. -* Sending email is now disabled on port 25 --- you must log in to port 587 to send email. +* Sending email is now disabled on port 25 --- you must log in to port 587 to send email, per the long-standing mail instructions. * Greylisting may delay more emails from new senders. We were using a custom-built postgrey package previously that whitelisted sending domains in dnswl.org, but we are no longer maintaining that package. v0.30 (January 9, 2019) diff --git a/README.md b/README.md index c186a65d..300fe496 100644 --- a/README.md +++ b/README.md @@ -52,13 +52,13 @@ Clone this repository: $ git clone https://github.com/mail-in-a-box/mailinabox $ cd mailinabox -_Optional:_ Download my PGP key and then verify that the sources were signed -by me: +_Optional:_ Download Josh's PGP key and then verify that the sources were signed +by him: $ curl -s https://keybase.io/joshdata/key.asc | gpg --import gpg: key C10BDD81: public key "Joshua Tauberer " imported - $ git verify-tag v0.30 + $ git verify-tag v0.40 gpg: Signature made ..... using RSA key ID C10BDD81 gpg: Good signature from "Joshua Tauberer " gpg: WARNING: This key is not certified with a trusted signature! @@ -67,19 +67,19 @@ by me: You'll get a lot of warnings, but that's OK. Check that the primary key fingerprint matches the fingerprint in the key details at [https://keybase.io/joshdata](https://keybase.io/joshdata) -and on my [personal homepage](https://razor.occams.info/). (Of course, if this repository has been compromised you can't trust these instructions.) +and on his [personal homepage](https://razor.occams.info/). (Of course, if this repository has been compromised you can't trust these instructions.) Checkout the tag corresponding to the most recent release: - $ git checkout v0.30 + $ git checkout v0.40 Begin the installation. $ sudo setup/start.sh -For help, DO NOT contact me directly --- I don't do tech support by email or tweet (no exceptions). +For help, DO NOT contact Josh directly --- I don't do tech support by email or tweet (no exceptions). -Post your question on the [discussion forum](https://discourse.mailinabox.email/) instead, where me and other Mail-in-a-Box users may be able to help you. +Post your question on the [discussion forum](https://discourse.mailinabox.email/) instead, where maintainers and Mail-in-a-Box users may be able to help you. Contributing and Development ---------------------------- diff --git a/setup/bootstrap.sh b/setup/bootstrap.sh index 90b43ce2..3442499d 100644 --- a/setup/bootstrap.sh +++ b/setup/bootstrap.sh @@ -12,15 +12,15 @@ if [ -z "$TAG" ]; then # depends on the operating system. Existing Ubuntu 14.04 users need # to be able to upgrade to the latest version supporting Ubuntu 14.04, # in part because an upgrade is required before jumping to Ubuntu 18.04. - # New users on Ubuntu 18 need to get the latest version number too. + # New users on Ubuntu 18.04 need to get the latest version number too. # - # Also, the system status checks read this script for TAG = to get - # (without the space, but if we put it in a comment it would confuse - # the status checks!) the latest version, so the first such line must - # be the one that we want to display in status checks. + # Also, the system status checks read this script for TAG = (without the + # space, but if we put it in a comment it would confuse the status checks!) + # to get the latest version, so the first such line must be the one that we + # want to display in status checks. if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/18\.04\.[0-9]/18.04/' `" == "Ubuntu 18.04 LTS" ]; then # This machine is running Ubuntu 18.04. - TAG=v0.30 + TAG=v0.40 elif [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" == "Ubuntu 14.04 LTS" ]; then # This machine is running Ubuntu 14.04. @@ -30,6 +30,7 @@ if [ -z "$TAG" ]; then echo "upgrading an existing Mail-in-a-Box --- great. After upgrading this" echo "box, please visit https://mailinabox.email for notes on how to upgrade" echo "to Ubuntu 18.04." + echo "" TAG=v0.30 else From c60e3dc8428ebb5dddd728d9a1f2c185845dd376 Mon Sep 17 00:00:00 2001 From: jvolkenant Date: Fri, 18 Jan 2019 06:36:43 -0800 Subject: [PATCH 032/190] fail2ban ssh/ssh-ddos and sasl are now sshd and postfix-sasl (fixes #1453, merges #1454) * fail2ban ssh/ssh-ddos and sasl are now sshd and postfix-sasl * specified custom datepattern for miab-owncloud.conf --- CHANGELOG.md | 7 +++++++ conf/fail2ban/filter.d/miab-owncloud.conf | 1 + conf/fail2ban/jails.conf | 7 ++----- setup/system.sh | 1 + 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb64785c..298791ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ CHANGELOG ========= +In Development +-------------- + +System: + +* Missing brute force login attack prevention (fail2ban) filters which stopped working on Ubuntu 18.04 were added back. + v0.40 (January 12, 2019) ------------------------ diff --git a/conf/fail2ban/filter.d/miab-owncloud.conf b/conf/fail2ban/filter.d/miab-owncloud.conf index a9a13f2c..709f8bac 100644 --- a/conf/fail2ban/filter.d/miab-owncloud.conf +++ b/conf/fail2ban/filter.d/miab-owncloud.conf @@ -3,5 +3,6 @@ before = common.conf [Definition] +datepattern = %%Y-%%m-%%d %%H:%%M:%%S failregex=Login failed: .*Remote IP: '[\)'] ignoreregex = diff --git a/conf/fail2ban/jails.conf b/conf/fail2ban/jails.conf index 6c6fee32..952dc35a 100644 --- a/conf/fail2ban/jails.conf +++ b/conf/fail2ban/jails.conf @@ -69,13 +69,10 @@ action = iptables-allports[name=recidive] # So the notification is ommited. This will prevent message appearing in the mail.log that mail # can't be delivered to fail2ban@$HOSTNAME. -[sasl] +[postfix-sasl] enabled = true -[ssh] +[sshd] enabled = true maxretry = 7 bantime = 3600 - -[ssh-ddos] -enabled = true diff --git a/setup/system.sh b/setup/system.sh index 2305ccfc..ccc60231 100755 --- a/setup/system.sh +++ b/setup/system.sh @@ -339,6 +339,7 @@ systemctl restart systemd-resolved # 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 +rm -f /etc/fail2ban/jail.d/defaults-debian.conf # removes default config so we can manage all of fail2ban rules in one config cat conf/fail2ban/jails.conf \ | sed "s/PUBLIC_IP/$PUBLIC_IP/g" \ | sed "s#STORAGE_ROOT#$STORAGE_ROOT#" \ From 10050aa6010eb78a75bbd963bf758051271d7736 Mon Sep 17 00:00:00 2001 From: Yoann Colin Date: Sat, 9 Feb 2019 03:24:03 +0100 Subject: [PATCH 033/190] Upgrade to NextCloud 14 (#1504) * Upgraded Nextcloud from 13.0.6 to 14.0.6. * Upgraded Contacts from 2.1.5 to 2.1.8. * Upgraded Calendar from 1.6.1 to 1.6.4. * Cleanup unsupported version upgrades: Since an upgrade to v0.30 is mandatory before moving upward, I removed the checks for Nextcloud prior version 12. * Fix the storage root path. * Add missing indices. Thx @yodax for your feedback. --- CHANGELOG.md | 6 ++++++ setup/nextcloud.sh | 31 ++++++++++++++----------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 298791ba..6fdffbab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ System: * Missing brute force login attack prevention (fail2ban) filters which stopped working on Ubuntu 18.04 were added back. +Contacts/Calendar: + +* Upgraded Nextcloud from 13.0.6 to 14.0.6. +* Upgraded Contacts from 2.1.5 to 2.1.8. +* Upgraded Calendar from 1.6.1 to 1.6.4. + v0.40 (January 12, 2019) ------------------------ diff --git a/setup/nextcloud.sh b/setup/nextcloud.sh index 167c2012..9476bbf8 100755 --- a/setup/nextcloud.sh +++ b/setup/nextcloud.sh @@ -39,11 +39,11 @@ InstallNextcloud() { # their github repositories. mkdir -p /usr/local/lib/owncloud/apps - wget_verify https://github.com/nextcloud/contacts/releases/download/v2.1.5/contacts.tar.gz b7460d15f1b78d492ed502d778c0c458d503ba17 /tmp/contacts.tgz + wget_verify https://github.com/nextcloud/contacts/releases/download/v2.1.8/contacts.tar.gz b5d5bbee33f0c32b124b46cb6aaab90c695ac170 /tmp/contacts.tgz tar xf /tmp/contacts.tgz -C /usr/local/lib/owncloud/apps/ rm /tmp/contacts.tgz - wget_verify https://github.com/nextcloud/calendar/releases/download/v1.6.1/calendar.tar.gz f93a247cbd18bc624f427ba2a967d93ebb941f21 /tmp/calendar.tgz + wget_verify https://github.com/nextcloud/calendar/releases/download/v1.6.4/calendar.tar.gz d8a7950dba14803472b6c19625a8ceb23d6fd4ef /tmp/calendar.tgz tar xf /tmp/calendar.tgz -C /usr/local/lib/owncloud/apps/ rm /tmp/calendar.tgz @@ -72,11 +72,14 @@ InstallNextcloud() { sudo -u www-data php /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 /usr/local/lib/owncloud/occ db:add-missing-indices fi } -nextcloud_ver=13.0.6 -nextcloud_hash=33e41f476f0e2be5dc7cdb9d496673d9647aa3d6 +nextcloud_ver=14.0.6 +nextcloud_hash=4e43a57340f04c2da306c8eea98e30040399ae5a # Check if Nextcloud dir exist, and check if version matches nextcloud_ver (if either doesn't - install/upgrade) if [ ! -d /usr/local/lib/owncloud/ ] \ @@ -93,11 +96,11 @@ if [ ! -d /usr/local/lib/owncloud/ ] \ 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 /home/user-data/owncloud/owncloud.db ]; then - cp /home/user-data/owncloud/owncloud.db $BACKUP_DIRECTORY + if [ -e $STORAGE_ROOT/owncloud/owncloud.db ]; then + cp $STORAGE_ROOT/owncloud/owncloud.db $BACKUP_DIRECTORY fi - if [ -e /home/user-data/owncloud/config.php ]; then - cp /home/user-data/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.... @@ -105,19 +108,13 @@ if [ ! -d /usr/local/lib/owncloud/ ] \ # Database migrations from ownCloud are no longer possible because ownCloud cannot be run under # PHP 7. if grep -q "OC_VersionString = '[89]\." /usr/local/lib/owncloud/version.php; then - echo "Upgrades from Mail-in-a-Box prior to v0.26c (dated February 13, 2018) with Nextcloud < 12.0.5 (you have ownCloud 8 or 9) are not supported. Upgrade to Mail-in-a-Box version v0.28 first. Setup aborting." + echo "Upgrades from Mail-in-a-Box prior to v0.28 (dated July 30, 2018) with Nextcloud < 13.0.6 (you have ownCloud 8 or 9) are not supported. Upgrade to Mail-in-a-Box version v0.30 first. Setup aborting." exit 1 fi - if grep -q "OC_VersionString = '10\." /usr/local/lib/owncloud/version.php; then - echo "Upgrades from Mail-in-a-Box prior to v0.26c (dated February 13, 2018) with Nextcloud < 12.0.5 (you have ownCloud 10) are not supported. Upgrade to Mail-in-a-Box version v0.28 first. Setup aborting." + if grep -q "OC_VersionString = '1[012]\." /usr/local/lib/owncloud/version.php; then + echo "Upgrades from Mail-in-a-Box prior to v0.28 (dated July 30, 2018) with Nextcloud < 13.0.6 (you have ownCloud 10, 11 or 12) are not supported. Upgrade to Mail-in-a-Box version v0.30 first. Setup aborting." exit 1 fi - - # If we are upgrading from Nextcloud 11 we should go to Nextcloud 12 first. - if grep -q "OC_VersionString = '11\." /usr/local/lib/owncloud/version.php; then - echo "We are running Nextcloud 11, upgrading to Nextcloud 12.0.5 first" - InstallNextcloud 12.0.5 d25afbac977a4e331f5e38df50aed0844498ca86 - fi fi InstallNextcloud $nextcloud_ver $nextcloud_hash From bad38840d80b4f87ae5cc6d7afc5a682acb8c108 Mon Sep 17 00:00:00 2001 From: Ryan Stubbs Date: Tue, 12 Feb 2019 01:14:56 +0000 Subject: [PATCH 034/190] Fix type on alias edit page (#1520) --- management/templates/aliases.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/management/templates/aliases.html b/management/templates/aliases.html index 89af221f..e8d0cb1c 100644 --- a/management/templates/aliases.html +++ b/management/templates/aliases.html @@ -51,7 +51,7 @@
    From adddd95e38647598bd318c84b98a42ea98085b52 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Mon, 25 Feb 2019 13:18:30 -0500 Subject: [PATCH 035/190] add lmtp_destination_recipient_limit=1 to work around spampd bug, see #1523 --- CHANGELOG.md | 4 ++++ setup/mail-postfix.sh | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fdffbab..9e529052 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ System: * Missing brute force login attack prevention (fail2ban) filters which stopped working on Ubuntu 18.04 were added back. +Mail: + +* Incoming messages addressed to more than one local user were rejected because of a bug in spampd packaged by Ubuntu 18.04. A workaround was added. + Contacts/Calendar: * Upgraded Nextcloud from 13.0.6 to 14.0.6. diff --git a/setup/mail-postfix.sh b/setup/mail-postfix.sh index 0c9bc97c..4d66cd58 100755 --- a/setup/mail-postfix.sh +++ b/setup/mail-postfix.sh @@ -173,8 +173,11 @@ tools/editconf.py /etc/postfix/main.cf \ # # In a basic setup we would pass mail directly to Dovecot by setting # virtual_transport to `lmtp:unix:private/dovecot-lmtp`. -# tools/editconf.py /etc/postfix/main.cf virtual_transport=lmtp:[127.0.0.1]:10025 +# Because of a spampd bug, limit the number of recipients in each connection. +# See https://github.com/mail-in-a-box/mailinabox/issues/1523. +tools/editconf.py /etc/postfix/main.cf lmtp_destination_recipient_limit=1 + # Who can send mail to us? Some basic filters. # From 149552f79b865d35e1d421ad1c2b6d6d0135420e Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Tue, 26 Feb 2019 18:15:36 -0500 Subject: [PATCH 036/190] systemctl link should use -f to avoid an error if a system service already exists with that name but points to a different file https://discourse.mailinabox.email/t/new-error-failed-systemctl-link-conf-mailinabox-service/4626/2 --- CHANGELOG.md | 1 + setup/management.sh | 2 +- setup/munin.sh | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e529052..a60ba3a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ In Development System: * Missing brute force login attack prevention (fail2ban) filters which stopped working on Ubuntu 18.04 were added back. +* Upgrades would fail if Mail-in-a-Box moved to a different directory in `systemctl link`. Mail: diff --git a/setup/management.sh b/setup/management.sh index 9c221198..f7621a8b 100755 --- a/setup/management.sh +++ b/setup/management.sh @@ -93,7 +93,7 @@ source $venv/bin/activate exec python `pwd`/management/daemon.py EOF chmod +x $inst_dir/start -hide_output systemctl link conf/mailinabox.service +hide_output systemctl link -f conf/mailinabox.service hide_output systemctl daemon-reload hide_output systemctl enable mailinabox.service diff --git a/setup/munin.sh b/setup/munin.sh index 8a85085d..3cb1cd9d 100755 --- a/setup/munin.sh +++ b/setup/munin.sh @@ -64,7 +64,7 @@ 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 chmod 0744 /usr/local/lib/mailinabox/munin_start.sh -hide_output systemctl link conf/munin.service +hide_output systemctl link -f conf/munin.service hide_output systemctl daemon-reload hide_output systemctl unmask munin.service hide_output systemctl enable munin.service From dd7a2aa8a6702de0cbe0e7f766bd05d03dff9733 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Tue, 26 Feb 2019 18:17:50 -0500 Subject: [PATCH 037/190] v0.41 --- CHANGELOG.md | 4 ++-- README.md | 4 ++-- setup/bootstrap.sh | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a60ba3a0..089aa68d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ CHANGELOG ========= -In Development --------------- +v0.41 (February 26, 2019) +------------------------- System: diff --git a/README.md b/README.md index 300fe496..01997fd4 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ by him: $ curl -s https://keybase.io/joshdata/key.asc | gpg --import gpg: key C10BDD81: public key "Joshua Tauberer " imported - $ git verify-tag v0.40 + $ git verify-tag v0.41 gpg: Signature made ..... using RSA key ID C10BDD81 gpg: Good signature from "Joshua Tauberer " gpg: WARNING: This key is not certified with a trusted signature! @@ -71,7 +71,7 @@ and on his [personal homepage](https://razor.occams.info/). (Of course, if this Checkout the tag corresponding to the most recent release: - $ git checkout v0.40 + $ git checkout v0.41 Begin the installation. diff --git a/setup/bootstrap.sh b/setup/bootstrap.sh index 3442499d..74bf5e16 100644 --- a/setup/bootstrap.sh +++ b/setup/bootstrap.sh @@ -20,7 +20,7 @@ if [ -z "$TAG" ]; then # want to display in status checks. if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/18\.04\.[0-9]/18.04/' `" == "Ubuntu 18.04 LTS" ]; then # This machine is running Ubuntu 18.04. - TAG=v0.40 + TAG=v0.41 elif [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" == "Ubuntu 14.04 LTS" ]; then # This machine is running Ubuntu 14.04. From fb25013334dcb05fa6a025c8fe0694b96c05001b Mon Sep 17 00:00:00 2001 From: mbraem <1116286+mbraem@users.noreply.github.com> Date: Sun, 14 Apr 2019 20:17:43 +0200 Subject: [PATCH 038/190] user privileges is a set (#1551) fixes #1540 --- management/mailconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/management/mailconfig.py b/management/mailconfig.py index 28e1c623..5f253c14 100755 --- a/management/mailconfig.py +++ b/management/mailconfig.py @@ -150,7 +150,7 @@ def get_mail_users_ex(env, with_archived=False): if email in active_accounts: continue user = { "email": email, - "privileges": "", + "privileges": [], "status": "inactive", "mailbox": mbox, } From 9b46637aff8851db98f9eac1f38b9a32f69a14fa Mon Sep 17 00:00:00 2001 From: dexbleeker Date: Sun, 14 Apr 2019 20:19:21 +0200 Subject: [PATCH 039/190] Update Roundcube to version 1.3.9 (#1546) --- CHANGELOG.md | 4 ++++ setup/webmail.sh | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 089aa68d..32f04763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ CHANGELOG ========= +In Development +-------------- + * Update to Roundcube 1.3.9. + v0.41 (February 26, 2019) ------------------------- diff --git a/setup/webmail.sh b/setup/webmail.sh index b0e11c9b..cbe6bfca 100755 --- a/setup/webmail.sh +++ b/setup/webmail.sh @@ -28,8 +28,8 @@ apt_install \ # Install Roundcube from source if it is not already present or if it is out of date. # Combine the Roundcube version number with the commit hash of plugins to track # whether we have the latest version of everything. -VERSION=1.3.8 -HASH=90c7900ccf7b2f46fe49c650d5adb9b85ee9cc22 +VERSION=1.3.9 +HASH=02850972b416bbfa1c13580f16d06fd7ae2774aa PERSISTENT_LOGIN_VERSION=dc5ca3d3f4415cc41edb2fde533c8a8628a94c76 HTML5_NOTIFIER_VERSION=4b370e3cd60dabd2f428a26f45b677ad1b7118d5 CARDDAV_VERSION=3.0.3 From 25fec63a03be972c8989eeada1ca09b9d7b7ae50 Mon Sep 17 00:00:00 2001 From: just4t Date: Sun, 14 Apr 2019 22:33:50 +0200 Subject: [PATCH 040/190] RAM limit to 502Mb to meet EC2 & Vultr 512Mb inst. (#1560) AS told here: https://github.com/mail-in-a-box/mailinabox/pull/1534 --- setup/preflight.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/preflight.sh b/setup/preflight.sh index d087efe2..2547c410 100644 --- a/setup/preflight.sh +++ b/setup/preflight.sh @@ -26,7 +26,7 @@ fi # # Skip the check if we appear to be running inside of Vagrant, because that's really just for testing. TOTAL_PHYSICAL_MEM=$(head -n 1 /proc/meminfo | awk '{print $2}') -if [ $TOTAL_PHYSICAL_MEM -lt 500000 ]; then +if [ $TOTAL_PHYSICAL_MEM -lt 490000 ]; then if [ ! -d /vagrant ]; then TOTAL_PHYSICAL_MEM=$(expr \( \( $TOTAL_PHYSICAL_MEM \* 1024 \) / 1000 \) / 1000) echo "Your Mail-in-a-Box needs more memory (RAM) to function properly." From aff80ac58cf85e6674a03cd2638db3442e774d4b Mon Sep 17 00:00:00 2001 From: jvolkenant Date: Thu, 9 May 2019 10:13:24 -0700 Subject: [PATCH 041/190] Autodiscovery fix for additional hosted email domains, Fixes #941 (#1467) --- conf/nginx-alldomains.conf | 3 +++ management/dns_update.py | 9 +++++++++ management/web_update.py | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/conf/nginx-alldomains.conf b/conf/nginx-alldomains.conf index 1db7606c..1b3ad5a9 100644 --- a/conf/nginx-alldomains.conf +++ b/conf/nginx-alldomains.conf @@ -18,6 +18,9 @@ location = /.well-known/autoconfig/mail/config-v1.1.xml { alias /var/lib/mailinabox/mozilla-autoconfig.xml; } + location = /mail/config-v1.1.xml { + alias /var/lib/mailinabox/mozilla-autoconfig.xml; + } # Roundcube Webmail configuration. rewrite ^/mail$ /mail/ redirect; diff --git a/management/dns_update.py b/management/dns_update.py index 5c1969d7..006a00c2 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -288,6 +288,15 @@ def build_zone(domain, all_domains, additional_records, www_redirect_domains, en if not has_rec(qname, "SRV"): records.append((qname, "SRV", "0 0 443 " + env["PRIMARY_HOSTNAME"] + ".", "Recommended. Specifies the hostname of the server that handles CardDAV/CalDAV services for email addresses on this domain.")) + # Adds autoconfiguration A records for all domains. + # This allows the following clients to automatically configure email addresses in the respective applications. + # autodiscover.* - Z-Push ActiveSync Autodiscover + # autoconfig.* - Thunderbird Autoconfig + if not has_rec("autodiscover", "A"): + records.append(("autodiscover", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover.")) + if not has_rec("autoconfig", "A"): + records.append(("autoconfig", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Thunderbird Autoconfig.")) + # Sort the records. The None records *must* go first in the nsd zone file. Otherwise it doesn't matter. records.sort(key = lambda rec : list(reversed(rec[0].split(".")) if rec[0] is not None else "")) diff --git a/management/web_update.py b/management/web_update.py index 61b38a7b..12959632 100644 --- a/management/web_update.py +++ b/management/web_update.py @@ -29,6 +29,12 @@ def get_web_domains(env, include_www_redirects=True, exclude_dns_elsewhere=True) # IP address than this box. Remove those domains from our list. domains -= get_domains_with_a_records(env) + # Add Autoconfiguration domains, allowing us to serve correct SSL certs. + # 'autoconfig.' for Mozilla Thunderbird auto setup. + # 'autodiscover.' for Activesync autodiscovery. + domains |= set('autoconfig.' + maildomain for maildomain in get_mail_domains(env)) + domains |= set('autodiscover.' + maildomain for maildomain in get_mail_domains(env)) + # Ensure the PRIMARY_HOSTNAME is in the list so we can serve webmail # as well as Z-Push for Exchange ActiveSync. This can't be removed # by a custom A/AAAA record and is never a 'www.' redirect. From 77b2246010f72e03ff4b57e6c003db77ad1fdb3d Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Sun, 12 May 2019 14:09:30 +0200 Subject: [PATCH 042/190] Backup Amazon S3: Added support for custom endpoints (#1427) --- management/backup.py | 9 +++++++- management/templates/system-backup.html | 29 +++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/management/backup.py b/management/backup.py index e15fbbbf..93136bf5 100755 --- a/management/backup.py +++ b/management/backup.py @@ -419,15 +419,22 @@ def list_target_files(config): fix_boto() # must call prior to importing boto import boto.s3 from boto.exception import BotoServerError + custom_region = False for region in boto.s3.regions(): if region.endpoint == target.hostname: break else: - raise ValueError("Invalid S3 region/host.") + # If region is not found this is a custom region + custom_region = True bucket = target.path[1:].split('/')[0] path = '/'.join(target.path[1:].split('/')[1:]) + '/' + # Create a custom region with custom endpoint + if custom_region: + from boto.s3.connection import S3Connection + region = boto.s3.S3RegionInfo(name=bucket, endpoint=target.hostname, connection_cls=S3Connection) + # If no prefix is specified, set the path to '', otherwise boto won't list the files if path == '/': path = '' diff --git a/management/templates/system-backup.html b/management/templates/system-backup.html index be528f19..3860edb7 100644 --- a/management/templates/system-backup.html +++ b/management/templates/system-backup.html @@ -77,15 +77,22 @@
    - +
    - {% for name, host in backup_s3_hosts %} {% endfor %} +
    +
    + +
    + +
    +
    @@ -139,6 +146,8 @@ function toggle_form() { var target_type = $("#backup-target-type").val(); $(".backup-target-local, .backup-target-rsync, .backup-target-s3").hide(); $(".backup-target-" + target_type).show(); + + init_inputs(target_type); } function nice_size(bytes) { @@ -278,4 +287,20 @@ function set_custom_backup() { }); return false; } + +function init_inputs(target_type) { + function set_host(host) { + if(host !== 'other') { + $("#backup-target-s3-host").val(host); + } else { + $("#backup-target-s3-host").val(''); + } + } + if (target_type == "s3") { + $('#backup-target-s3-host-select').off('change').on('change', function() { + set_host($('#backup-target-s3-host-select').val()); + }); + set_host($('#backup-target-s3-host-select').val()); + } +} From 0d4c6937927a498bf7faf8e468cba4d37eafe745 Mon Sep 17 00:00:00 2001 From: Michael Heuberger Date: Mon, 13 May 2019 00:10:34 +1200 Subject: [PATCH 043/190] Add missing login form method to keep LastPass happy (#1565) --- management/templates/login.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/management/templates/login.html b/management/templates/login.html index 04c27279..b6e74df6 100644 --- a/management/templates/login.html +++ b/management/templates/login.html @@ -17,13 +17,13 @@ sudo tools/mail.py user make-admin me@{{hostname}} {% endif %}
    -
    + {% endif %}

    Log in here for your Mail-in-a-Box control panel.

    -
    +
    @@ -76,7 +76,7 @@ function do_login() { "/me", "GET", { }, - function(response){ + function(response){ // This API call always succeeds. It returns a JSON object indicating // whether the request was authenticated or not. if (response.status != "ok") { From 4232a1205c300254be8052c1e9f105b519e44d66 Mon Sep 17 00:00:00 2001 From: jvolkenant Date: Wed, 15 May 2019 11:46:52 -0700 Subject: [PATCH 044/190] fix dovecot message about SSLv2 not supported by OpenSSL (#1580) --- setup/mail-dovecot.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup/mail-dovecot.sh b/setup/mail-dovecot.sh index 4bcc53aa..6098e295 100755 --- a/setup/mail-dovecot.sh +++ b/setup/mail-dovecot.sh @@ -80,11 +80,12 @@ tools/editconf.py /etc/dovecot/conf.d/10-auth.conf \ # Enable SSL, specify the location of the SSL certificate and private key files. # Disable obsolete SSL protocols and allow only good ciphers per http://baldric.net/2013/12/07/tls-ciphers-in-postfix-and-dovecot/. # Enable strong ssl dh parameters + tools/editconf.py /etc/dovecot/conf.d/10-ssl.conf \ ssl=required \ "ssl_cert=<$STORAGE_ROOT/ssl/ssl_certificate.pem" \ "ssl_key=<$STORAGE_ROOT/ssl/ssl_private_key.pem" \ - "ssl_protocols=!SSLv3 !SSLv2" \ + "ssl_protocols=!SSLv3" \ "ssl_cipher_list=ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS" \ "ssl_prefer_server_ciphers = yes" \ "ssl_dh_parameters_length = 2048" From 85e59245fdfd2a2e633a890927a89adba79e1bf5 Mon Sep 17 00:00:00 2001 From: cmharper <1422608+cmharper@users.noreply.github.com> Date: Wed, 15 May 2019 18:57:06 +0000 Subject: [PATCH 045/190] hide 'RTNETLINK answers: Network is unreachable' error message during setup if IPv6 is not available (#1576) --- setup/functions.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup/functions.sh b/setup/functions.sh index 1a74edfd..3bb96b7a 100644 --- a/setup/functions.sh +++ b/setup/functions.sh @@ -1,7 +1,7 @@ # 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. -# -o pipefail: don't ignore errors in the non-last command in a pipeline +# -o pipefail: don't ignore errors in the non-last command in a pipeline set -euo pipefail function hide_output { @@ -127,7 +127,7 @@ 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 | 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/") From c6fa0d23df986f9065108b58f984026204cd4e20 Mon Sep 17 00:00:00 2001 From: jvolkenant Date: Wed, 15 May 2019 11:58:40 -0700 Subject: [PATCH 046/190] check that munin-cron is not running (via cron) when it is run in setup, fixes #660 (#1579) --- setup/munin.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/setup/munin.sh b/setup/munin.sh index 3cb1cd9d..df7af601 100755 --- a/setup/munin.sh +++ b/setup/munin.sh @@ -76,4 +76,8 @@ restart_service munin-node # generate initial statistics so the directory isn't empty # (We get "Pango-WARNING **: error opening config file '/root/.config/pango/pangorc': Permission denied" # if we don't explicitly set the HOME directory when sudo'ing.) -sudo -H -u munin munin-cron +# We check to see if munin-cron is already running, if it is, there is no need to run it simultaneously +# generating an error. +if [ ! -f /var/run/munin/munin-update.lock ]; then + sudo -H -u munin munin-cron +fi From 6e5ceab0f8d6496ae9dda5ae4cb134f16fa11626 Mon Sep 17 00:00:00 2001 From: jvolkenant Date: Wed, 15 May 2019 11:59:32 -0700 Subject: [PATCH 047/190] hide virtualenv output (#1578) --- setup/management.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/management.sh b/setup/management.sh index f7621a8b..3a6e187b 100755 --- a/setup/management.sh +++ b/setup/management.sh @@ -38,7 +38,7 @@ inst_dir=/usr/local/lib/mailinabox mkdir -p $inst_dir venv=$inst_dir/env if [ ! -d $venv ]; then - virtualenv -ppython3 $venv + hide_output virtualenv -ppython3 $venv fi # Upgrade pip because the Ubuntu-packaged version is out of date. From 79759ea5a39c9398460d33453d986c90db9d7d54 Mon Sep 17 00:00:00 2001 From: jvolkenant Date: Sun, 16 Jun 2019 08:07:45 -0700 Subject: [PATCH 048/190] Upgrade Z-Push to 2.5.0 (#1581) --- CHANGELOG.md | 3 +++ setup/zpush.sh | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32f04763..14afe39d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ In Development -------------- * Update to Roundcube 1.3.9. +Z-Push: + * Upgraded Z-Push from 2.4.4 to 2.5.0. + v0.41 (February 26, 2019) ------------------------- diff --git a/setup/zpush.sh b/setup/zpush.sh index 32fc4992..0cbd30ad 100755 --- a/setup/zpush.sh +++ b/setup/zpush.sh @@ -22,8 +22,8 @@ apt_install \ phpenmod -v php imap # Copy Z-Push into place. -VERSION=2.4.4 -TARGETHASH=104d44426852429dac8ec2783a4e9ad7752d4682 +VERSION=2.5.0 +TARGETHASH=30ce5c1af3f10939036361b6032d1187651b621e needs_update=0 #NODOC if [ ! -f /usr/local/lib/z-push/version ]; then needs_update=1 #NODOC From 193763f8f0a6e113053e2c8b5ae2bb12bbd5eb03 Mon Sep 17 00:00:00 2001 From: jvolkenant Date: Sun, 16 Jun 2019 08:10:53 -0700 Subject: [PATCH 049/190] Update to Nextcloud 15.0.8, Contacts to 3.1.1, and Calendar to 1.6.5 (#1577) * Update to Nextcloud 15.0.7, Contacts to 3.1.1, and Calendar to 1.6.5 * Enabled localhost-only insecure IMAP login for localhost Nextcloud auth * Add package php-imagick and BigInt conversion * added support for /cloud/oc[sm]-provider/ endpoint --- CHANGELOG.md | 11 ++++++++++- conf/nginx-primaryonly.conf | 9 +++++++++ setup/mail-dovecot.sh | 8 ++++++++ setup/nextcloud.sh | 38 +++++++++++++++++++++++++++---------- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14afe39d..faa4a918 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,16 @@ CHANGELOG In Development -------------- - * Update to Roundcube 1.3.9. + +Mail: + +* Update to Roundcube 1.3.9. + +Contacts/Calendar: + +* Upgraded Nextcloud from 14.0.6 to 15.0.8. +* Upgraded Contacts from 2.1.8 to 3.1.1. +* Upgraded Calendar from 1.6.4 to 1.6.5. Z-Push: * Upgraded Z-Push from 2.4.4 to 2.5.0. diff --git a/conf/nginx-primaryonly.conf b/conf/nginx-primaryonly.conf index d8d912ca..288fce40 100644 --- a/conf/nginx-primaryonly.conf +++ b/conf/nginx-primaryonly.conf @@ -19,6 +19,7 @@ rewrite ^/cloud/$ /cloud/index.php; rewrite ^/cloud/(contacts|calendar|files)$ /cloud/index.php/apps/$1/ redirect; rewrite ^(/cloud/core/doc/[^\/]+/)$ $1/index.html; + rewrite ^(/cloud/oc[sm]-provider)/$ $1/index.php redirect; location /cloud/ { alias /usr/local/lib/owncloud/; location ~ ^/cloud/(build|tests|config|lib|3rdparty|templates|data|README)/ { @@ -27,6 +28,14 @@ location ~ ^/cloud/(?:\.|autotest|occ|issue|indie|db_|console) { deny all; } + # Enable paths for service and cloud federation discovery + # Resolves warning in Nextcloud Settings panel + location ~ ^/cloud/(oc[sm]-provider)?/([^/]+\.php)$ { + index index.php; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /usr/local/lib/owncloud/$1/$2; + fastcgi_pass php-fpm; + } } location ~ ^(/cloud)((?:/ocs)?/[^/]+\.php)(/.*)?$ { # note: ~ has precendence over a regular location block diff --git a/setup/mail-dovecot.sh b/setup/mail-dovecot.sh index 6098e295..c3d2ee57 100755 --- a/setup/mail-dovecot.sh +++ b/setup/mail-dovecot.sh @@ -136,6 +136,14 @@ service lmtp { } } +# Enable imap-login on localhost to allow the user_external plugin +# for Nextcloud to do imap authentication. (See #1577) +service imap-login { + inet_listener imap { + address = 127.0.0.1 + port = 143 + } +} protocol imap { mail_max_userip_connections = 20 } diff --git a/setup/nextcloud.sh b/setup/nextcloud.sh index 9476bbf8..8dc8d35a 100755 --- a/setup/nextcloud.sh +++ b/setup/nextcloud.sh @@ -13,7 +13,8 @@ apt-get purge -qq -y owncloud* # we used to use the package manager apt_install php php-fpm \ php-cli php-sqlite3 php-gd php-imap php-curl php-pear curl \ - php-dev php-gd php-xml php-mbstring php-zip php-apcu php-json php-intl + php-dev php-gd php-xml php-mbstring php-zip php-apcu php-json \ + php-intl php-imagick InstallNextcloud() { @@ -39,14 +40,20 @@ InstallNextcloud() { # their github repositories. mkdir -p /usr/local/lib/owncloud/apps - wget_verify https://github.com/nextcloud/contacts/releases/download/v2.1.8/contacts.tar.gz b5d5bbee33f0c32b124b46cb6aaab90c695ac170 /tmp/contacts.tgz + wget_verify https://github.com/nextcloud/contacts/releases/download/v3.1.1/contacts.tar.gz a06bd967197dcb03c94ec1dbd698c037018669e5 /tmp/contacts.tgz tar xf /tmp/contacts.tgz -C /usr/local/lib/owncloud/apps/ rm /tmp/contacts.tgz - wget_verify https://github.com/nextcloud/calendar/releases/download/v1.6.4/calendar.tar.gz d8a7950dba14803472b6c19625a8ceb23d6fd4ef /tmp/calendar.tgz + wget_verify https://github.com/nextcloud/calendar/releases/download/v1.6.5/calendar.tar.gz 79941255521a5172f7e4ce42dc7773838b5ede2f /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. + wget_verify https://github.com/nextcloud/user_external/releases/download/v0.6.3/user_external-0.6.3.tar.gz 0f756d35fef6b64a177d6a16020486b76ea5799c /tmp/user_external.tgz + tar -xf /tmp/user_external.tgz -C /usr/local/lib/owncloud/apps/ + rm /tmp/user_external.tgz + # Fix weird permissions. chmod 750 /usr/local/lib/owncloud/{apps,config} @@ -75,12 +82,14 @@ InstallNextcloud() { # Add missing indices. NextCloud didn't include this in the normal upgrade because it might take some time. sudo -u www-data php /usr/local/lib/owncloud/occ db:add-missing-indices + + # Run conversion to BigInt identifiers, this process may take some time on large tables. + sudo -u www-data php /usr/local/lib/owncloud/occ db:convert-filecache-bigint --no-interaction fi } -nextcloud_ver=14.0.6 -nextcloud_hash=4e43a57340f04c2da306c8eea98e30040399ae5a - +nextcloud_ver=15.0.8 +nextcloud_hash=4129d8d4021c435f2e86876225fb7f15adf764a3 # Check if Nextcloud dir exist, and check if version matches nextcloud_ver (if either doesn't - install/upgrade) if [ ! -d /usr/local/lib/owncloud/ ] \ || ! grep -q $nextcloud_ver /usr/local/lib/owncloud/version.php; then @@ -115,6 +124,11 @@ if [ ! -d /usr/local/lib/owncloud/ ] \ echo "Upgrades from Mail-in-a-Box prior to v0.28 (dated July 30, 2018) with Nextcloud < 13.0.6 (you have ownCloud 10, 11 or 12) are not supported. Upgrade to Mail-in-a-Box version v0.30 first. Setup aborting." exit 1 fi + # During the upgrade from Nextcloud 14 to 15, user_external may cause the upgrade to fail. + # We will disable it here before the upgrade and install it again after the upgrade. + if grep -q "OC_VersionString = '14\." /usr/local/lib/owncloud/version.php; then + hide_output sudo -u www-data php /usr/local/lib/owncloud/console.php app:disable user_external + fi fi InstallNextcloud $nextcloud_ver $nextcloud_hash @@ -142,10 +156,12 @@ if [ ! -f $STORAGE_ROOT/owncloud/owncloud.db ]; then 'overwritewebroot' => '/cloud', 'overwrite.cli.url' => '/cloud', 'user_backends' => array( - array( - 'class'=>'OC_User_IMAP', - 'arguments'=>array('{127.0.0.1:993/imap/ssl/novalidate-cert}') - ) + array( + 'class' => 'OC_User_IMAP', + 'arguments' => array( + '127.0.0.1', 143, null + ), + ), ), 'memcache.local' => '\OC\Memcache\APCu', 'mail_smtpmode' => 'sendmail', @@ -217,6 +233,8 @@ include("$STORAGE_ROOT/owncloud/config.php"); \$CONFIG['mail_domain'] = '$PRIMARY_HOSTNAME'; +\$CONFIG['user_backends'] = array(array('class' => 'OC_User_IMAP','arguments' => array('127.0.0.1', 143, null),),); + echo " Date: Sun, 16 Jun 2019 11:40:40 -0400 Subject: [PATCH 050/190] changelog updates --- CHANGELOG.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index faa4a918..0f232670 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,18 +4,20 @@ CHANGELOG In Development -------------- -Mail: +Changes: -* Update to Roundcube 1.3.9. +* Decreased the minimum supported RAM to 502 Mb. +* Improved mail client autoconfiguration. +* Added support for S3-compatible backup services besides Amazon S3. +* Fixed the control panel login page to let LastPass save passwords. +* Fixed an error in the user privileges API. +* Silenced some spurrious messages. -Contacts/Calendar: +Software updates: -* Upgraded Nextcloud from 14.0.6 to 15.0.8. -* Upgraded Contacts from 2.1.8 to 3.1.1. -* Upgraded Calendar from 1.6.4 to 1.6.5. - -Z-Push: - * Upgraded Z-Push from 2.4.4 to 2.5.0. +* Upgraded Roundcube from 1.3.8 to 1.3.9. +* Upgraded Nextcloud from 14.0.6 to 15.0.8 (with Contacts from 2.1.8 to 3.1.1 and Calendar from 1.6.4 to 1.6.5). +* Upgraded Z-Push from 2.4.4 to 2.5.0. v0.41 (February 26, 2019) ------------------------- From 39fd4ce16c6b665f1df886083dcc0a6afda6c08e Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Thu, 4 Jul 2019 21:34:55 -0400 Subject: [PATCH 051/190] v0.42 --- CHANGELOG.md | 4 ++-- README.md | 4 ++-- setup/bootstrap.sh | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f232670..15311c84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ CHANGELOG ========= -In Development --------------- +v0.42 (July 4, 2019) +-------------------- Changes: diff --git a/README.md b/README.md index 01997fd4..d93a0847 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ by him: $ curl -s https://keybase.io/joshdata/key.asc | gpg --import gpg: key C10BDD81: public key "Joshua Tauberer " imported - $ git verify-tag v0.41 + $ git verify-tag v0.42 gpg: Signature made ..... using RSA key ID C10BDD81 gpg: Good signature from "Joshua Tauberer " gpg: WARNING: This key is not certified with a trusted signature! @@ -71,7 +71,7 @@ and on his [personal homepage](https://razor.occams.info/). (Of course, if this Checkout the tag corresponding to the most recent release: - $ git checkout v0.41 + $ git checkout v0.42 Begin the installation. diff --git a/setup/bootstrap.sh b/setup/bootstrap.sh index 74bf5e16..b8dfcc64 100644 --- a/setup/bootstrap.sh +++ b/setup/bootstrap.sh @@ -20,7 +20,7 @@ if [ -z "$TAG" ]; then # want to display in status checks. if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/18\.04\.[0-9]/18.04/' `" == "Ubuntu 18.04 LTS" ]; then # This machine is running Ubuntu 18.04. - TAG=v0.41 + TAG=v0.42 elif [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" == "Ubuntu 14.04 LTS" ]; then # This machine is running Ubuntu 14.04. From 5fc1944f04dba9e111f3c8787a5969d4c296468f Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 5 Jul 2019 11:56:54 -0400 Subject: [PATCH 052/190] pull v0.42, go back to v0.41 --- setup/bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/bootstrap.sh b/setup/bootstrap.sh index b8dfcc64..74bf5e16 100644 --- a/setup/bootstrap.sh +++ b/setup/bootstrap.sh @@ -20,7 +20,7 @@ if [ -z "$TAG" ]; then # want to display in status checks. if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/18\.04\.[0-9]/18.04/' `" == "Ubuntu 18.04 LTS" ]; then # This machine is running Ubuntu 18.04. - TAG=v0.42 + TAG=v0.41 elif [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" == "Ubuntu 14.04 LTS" ]; then # This machine is running Ubuntu 14.04. From fd5b11823ce01cd7e9bf68a247758adb46f4f146 Mon Sep 17 00:00:00 2001 From: jvolkenant Date: Wed, 10 Jul 2019 03:28:37 -0700 Subject: [PATCH 053/190] Add AAAA records for autodiscover & autoconfig (#1606) --- management/dns_update.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/management/dns_update.py b/management/dns_update.py index 006a00c2..7e006d0b 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -288,14 +288,20 @@ def build_zone(domain, all_domains, additional_records, www_redirect_domains, en if not has_rec(qname, "SRV"): records.append((qname, "SRV", "0 0 443 " + env["PRIMARY_HOSTNAME"] + ".", "Recommended. Specifies the hostname of the server that handles CardDAV/CalDAV services for email addresses on this domain.")) - # Adds autoconfiguration A records for all domains. - # This allows the following clients to automatically configure email addresses in the respective applications. - # autodiscover.* - Z-Push ActiveSync Autodiscover - # autoconfig.* - Thunderbird Autoconfig - if not has_rec("autodiscover", "A"): - records.append(("autodiscover", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover.")) - if not has_rec("autoconfig", "A"): - records.append(("autoconfig", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Thunderbird Autoconfig.")) + # Adds autoconfiguration A records for all domains. + # This allows the following clients to automatically configure email addresses in the respective applications. + # autodiscover.* - Z-Push ActiveSync Autodiscover + # autoconfig.* - Thunderbird Autoconfig + autodiscover_records = [ + ("autodiscover", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover."), + ("autodiscover", "AAAA", env["PUBLIC_IPV6"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover."), + ("autoconfig", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Thunderbird Autoconfig."), + ("autoconfig", "AAAA", env["PUBLIC_IPV6"], "Provides email configuration autodiscovery support for Thunderbird Autoconfig.") + ] + for qname, rtype, value, explanation in autodiscover_records: + if value is None or value.strip() == "": continue # skip IPV6 if not set + if not has_rec(qname, rtype): + records.append((qname, rtype, value, explanation)) # Sort the records. The None records *must* go first in the nsd zone file. Otherwise it doesn't matter. records.sort(key = lambda rec : list(reversed(rec[0].split(".")) if rec[0] is not None else "")) From bea5eb0dda083478ce0a27d0b8e6740bae26e2ab Mon Sep 17 00:00:00 2001 From: jvolkenant Date: Fri, 12 Jul 2019 03:41:16 -0700 Subject: [PATCH 054/190] Add interm upgrade step from Nextcloud 13 -> 14 (#1605) --- setup/nextcloud.sh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/setup/nextcloud.sh b/setup/nextcloud.sh index 8dc8d35a..9f36ee56 100755 --- a/setup/nextcloud.sh +++ b/setup/nextcloud.sh @@ -50,9 +50,11 @@ InstallNextcloud() { # Starting with Nextcloud 15, the app user_external is no longer included in Nextcloud core, # we will install from their github repository. - wget_verify https://github.com/nextcloud/user_external/releases/download/v0.6.3/user_external-0.6.3.tar.gz 0f756d35fef6b64a177d6a16020486b76ea5799c /tmp/user_external.tgz - tar -xf /tmp/user_external.tgz -C /usr/local/lib/owncloud/apps/ - rm /tmp/user_external.tgz + if [[ $version =~ ^15 ]]; then + wget_verify https://github.com/nextcloud/user_external/releases/download/v0.6.3/user_external-0.6.3.tar.gz 0f756d35fef6b64a177d6a16020486b76ea5799c /tmp/user_external.tgz + tar -xf /tmp/user_external.tgz -C /usr/local/lib/owncloud/apps/ + rm /tmp/user_external.tgz + fi # Fix weird permissions. chmod 750 /usr/local/lib/owncloud/{apps,config} @@ -124,6 +126,11 @@ if [ ! -d /usr/local/lib/owncloud/ ] \ echo "Upgrades from Mail-in-a-Box prior to v0.28 (dated July 30, 2018) with Nextcloud < 13.0.6 (you have ownCloud 10, 11 or 12) are not supported. Upgrade to Mail-in-a-Box version v0.30 first. Setup aborting." exit 1 fi + # If we are running Nextcloud 13, upgrade to Nextcloud 14 + if grep -q "OC_VersionString = '13\." /usr/local/lib/owncloud/version.php; then + InstallNextcloud 14.0.6 4e43a57340f04c2da306c8eea98e30040399ae5a + + fi # During the upgrade from Nextcloud 14 to 15, user_external may cause the upgrade to fail. # We will disable it here before the upgrade and install it again after the upgrade. if grep -q "OC_VersionString = '14\." /usr/local/lib/owncloud/version.php; then From e37768ca8680106b037822975445de88e3e0da14 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Sat, 3 Aug 2019 11:49:32 -0400 Subject: [PATCH 055/190] v0.42b --- CHANGELOG.md | 6 ++++-- README.md | 4 ++-- setup/bootstrap.sh | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15311c84..a07304b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ CHANGELOG ========= -v0.42 (July 4, 2019) --------------------- +v0.42b (August 3, 2019) +----------------------- Changes: @@ -19,6 +19,8 @@ Software updates: * Upgraded Nextcloud from 14.0.6 to 15.0.8 (with Contacts from 2.1.8 to 3.1.1 and Calendar from 1.6.4 to 1.6.5). * Upgraded Z-Push from 2.4.4 to 2.5.0. +Note that v0.42 (July 4, 2019) was pulled shortly after it was released to fix a Nextcloud upgrade issue. + v0.41 (February 26, 2019) ------------------------- diff --git a/README.md b/README.md index d93a0847..925e62aa 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ by him: $ curl -s https://keybase.io/joshdata/key.asc | gpg --import gpg: key C10BDD81: public key "Joshua Tauberer " imported - $ git verify-tag v0.42 + $ git verify-tag v0.42b gpg: Signature made ..... using RSA key ID C10BDD81 gpg: Good signature from "Joshua Tauberer " gpg: WARNING: This key is not certified with a trusted signature! @@ -71,7 +71,7 @@ and on his [personal homepage](https://razor.occams.info/). (Of course, if this Checkout the tag corresponding to the most recent release: - $ git checkout v0.42 + $ git checkout v0.42b Begin the installation. diff --git a/setup/bootstrap.sh b/setup/bootstrap.sh index 74bf5e16..1135107b 100644 --- a/setup/bootstrap.sh +++ b/setup/bootstrap.sh @@ -20,7 +20,7 @@ if [ -z "$TAG" ]; then # want to display in status checks. if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/18\.04\.[0-9]/18.04/' `" == "Ubuntu 18.04 LTS" ]; then # This machine is running Ubuntu 18.04. - TAG=v0.41 + TAG=v0.42b elif [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" == "Ubuntu 14.04 LTS" ]; then # This machine is running Ubuntu 14.04. From 0657f9e875895adbc46cb36b72c6aa3f55a88e45 Mon Sep 17 00:00:00 2001 From: captainwasabi Date: Tue, 13 Aug 2019 05:47:11 -0400 Subject: [PATCH 056/190] add proper check for DNS error in list_target_files (#1625) The elif needed to check to see if the string was in the listing of results of the shell command. As it was the conditional was just the string which always evaluates to true and was therefore giving a misleading error message. --- management/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/management/backup.py b/management/backup.py index 93136bf5..cd1ee6fc 100755 --- a/management/backup.py +++ b/management/backup.py @@ -406,7 +406,7 @@ def list_target_files(config): reason = "Provided path {} is invalid.".format(target_path) elif 'Network is unreachable' in listing: reason = "The IP address {} is unreachable.".format(target.hostname) - elif 'Could not resolve hostname': + elif 'Could not resolve hostname' in listing: reason = "The hostname {} cannot be resolved.".format(target.hostname) else: reason = "Unknown error." \ From c4cb828f6562ad03497e64acd2ebb0af7294d36c Mon Sep 17 00:00:00 2001 From: captainwasabi Date: Tue, 13 Aug 2019 05:57:05 -0400 Subject: [PATCH 057/190] Fix rsync backup options string: extraneous single quotes causing problems (#1629) The resulting command had nested single quotes which doesn't work I think this fixes all/most of the issues in #1627. I am getting a full backup, then the next time it's run I get an incremental. running from the CLI with --status looks good, --verify looks good, and --list looks good. --- management/backup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/management/backup.py b/management/backup.py index cd1ee6fc..11cdbb8a 100755 --- a/management/backup.py +++ b/management/backup.py @@ -15,8 +15,8 @@ from exclusiveprocess import Lock from utils import load_environment, shell, wait_for_service, fix_boto rsync_ssh_options = [ - "--ssh-options='-i /root/.ssh/id_rsa_miab'", - "--rsync-options=-e \"/usr/bin/ssh -oStrictHostKeyChecking=no -oBatchMode=yes -p 22 -i /root/.ssh/id_rsa_miab\"", + "--ssh-options= -i /root/.ssh/id_rsa_miab", + "--rsync-options= -e \"/usr/bin/ssh -oStrictHostKeyChecking=no -oBatchMode=yes -p 22 -i /root/.ssh/id_rsa_miab\"", ] def backup_status(env): From 295d481603025a603e3f39cb8da8629ba569f98f Mon Sep 17 00:00:00 2001 From: cmharper <1422608+cmharper@users.noreply.github.com> Date: Sat, 31 Aug 2019 11:55:38 +0000 Subject: [PATCH 058/190] Upgraded roundcube to 1.3.10 (#1634) --- setup/webmail.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup/webmail.sh b/setup/webmail.sh index cbe6bfca..6cbe55f9 100755 --- a/setup/webmail.sh +++ b/setup/webmail.sh @@ -28,8 +28,8 @@ apt_install \ # Install Roundcube from source if it is not already present or if it is out of date. # Combine the Roundcube version number with the commit hash of plugins to track # whether we have the latest version of everything. -VERSION=1.3.9 -HASH=02850972b416bbfa1c13580f16d06fd7ae2774aa +VERSION=1.3.10 +HASH=431625fc737e301f9b7e502cccc61e50a24786b8 PERSISTENT_LOGIN_VERSION=dc5ca3d3f4415cc41edb2fde533c8a8628a94c76 HTML5_NOTIFIER_VERSION=4b370e3cd60dabd2f428a26f45b677ad1b7118d5 CARDDAV_VERSION=3.0.3 From 08021ea19f1a5395d09e2bc557cbf8b5cdef18d4 Mon Sep 17 00:00:00 2001 From: Snacho Date: Sat, 31 Aug 2019 14:58:12 +0300 Subject: [PATCH 059/190] Fix an issue when Secondary NS has multiple A records (#1633) If a custom secondary NS server has multiple A records status_checks.py will fail with a timeout and Web UI won't load. --- management/status_checks.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/management/status_checks.py b/management/status_checks.py index 6f9bb1ef..a9d0595c 100755 --- a/management/status_checks.py +++ b/management/status_checks.py @@ -486,10 +486,12 @@ def check_dns_zone(domain, env, output, dns_zonefiles): if custom_secondary_ns and not probably_external_dns: for ns in custom_secondary_ns: # We must first resolve the nameserver to an IP address so we can query it. - ns_ip = query_dns(ns, "A") - if not ns_ip: + ns_ips = query_dns(ns, "A") + if not ns_ips: output.print_error("Secondary nameserver %s is not valid (it doesn't resolve to an IP address)." % ns) continue + # Choose the first IP if nameserver returns multiple + ns_ip = ns_ips.split('; ')[0] # Now query it to see what it says about this domain. ip = query_dns(domain, "A", at=ns_ip, nxdomain=None) From c7377e602d06a792b97a5a1047bbdeb32bdd377a Mon Sep 17 00:00:00 2001 From: Kim Schulz Date: Sat, 31 Aug 2019 14:00:18 +0200 Subject: [PATCH 060/190] make it possible to use subnet addresses for axfr (#1616) it is sometimes needed to be able to set axfr to more than just one ip address. This can be done with multiple xfr: in the secondary dns input but if you need to add an entire subnet segment (xxx.xxx.xxx.0/yy) then it will not work. With this patch it is now possible to use a subnet as input for xfr the same way as if it was an ip address. --- management/dns_update.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/management/dns_update.py b/management/dns_update.py index 7e006d0b..7eed50b5 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -903,8 +903,12 @@ def set_secondary_dns(hostnames, env): else: # Validate IP address. try: - v = ipaddress.ip_address(item[4:]) # raises a ValueError if there's a problem - if not isinstance(v, ipaddress.IPv4Address): raise ValueError("That's an IPv6 address.") + if "/" in item[4:]: + v = ipaddress.ip_network(item[4:] # raises a ValueError if there's a problem + if not isinstance(v, ipaddress.IPv4Network): raise ValueError("That's an IPv6 subnet.") + else: + v = ipaddress.ip_address(item[4:]) # raises a ValueError if there's a problem + if not isinstance(v, ipaddress.IPv4Address): raise ValueError("That's an IPv6 address.") except ValueError: raise ValueError("'%s' is not an IPv4 address." % item[4:]) From 1d6793d12434a407d47efa7dc276f63227ad29e5 Mon Sep 17 00:00:00 2001 From: Michael Kroes Date: Sat, 31 Aug 2019 14:38:41 +0200 Subject: [PATCH 061/190] Update the Postgrey whitelist to a newer version monthly (#1611) Automatically update the Postgrey whitelist to a newer version once a month. --- CHANGELOG.md | 5 +++++ setup/mail-postfix.sh | 29 ++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a07304b6..7c1849fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +In Development +-------------- + +* Fetch an updated whitelist for Postgrey on a monthly basis. + v0.42b (August 3, 2019) ----------------------- diff --git a/setup/mail-postfix.sh b/setup/mail-postfix.sh index 4d66cd58..283d08af 100755 --- a/setup/mail-postfix.sh +++ b/setup/mail-postfix.sh @@ -208,7 +208,34 @@ tools/editconf.py /etc/postfix/main.cf \ # e-mails really latter, delay of greylisting has been set to # 180 seconds (default is 300 seconds). tools/editconf.py /etc/default/postgrey \ - POSTGREY_OPTS=\"'--inet=127.0.0.1:10023 --delay=180'\" + POSTGREY_OPTS=\"'--inet=127.0.0.1:10023 --delay=180 --whitelist-recipients=/etc/postgrey/whitelist_clients'\" + + +# 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; +#!/bin/bash + +# Mail-in-a-Box + +# check we have a postgrey_whitelist_clients file and that it is not older than 28 days +if [ ! -f /etc/postgrey/whitelist_clients ] || find /etc/postgrey/whitelist_clients -mtime +28 > /dev/null ; then + # ok we need to update the file, so lets try to fetch it + if curl https://postgrey.schweikert.ch/pub/postgrey_whitelist_clients --output /tmp/postgrey_whitelist_clients -sS --fail > /dev/null 2>&1 ; then + # if fetching hasn't failed yet then check it is a plain text file + # curl manual states that --fail sometimes still produces output + # this final check will at least check the output is not html + # before moving it into place + if [ "\$(file -b --mime-type /tmp/postgrey_whitelist_clients)" == "text/plain" ]; then + mv /tmp/postgrey_whitelist_clients /etc/postgrey/whitelist_clients + service postgrey restart + else + rm /tmp/postgrey_whitelist_clients + fi + fi +fi +EOF +chmod +x /etc/cron.daily/mailinabox-postgrey-whitelist +/etc/cron.daily/mailinabox-postgrey-whitelist # Increase the message size limit from 10MB to 128MB. # The same limit is specified in nginx.conf for mail submitted via webmail and Z-Push. From d6becddbe5f120818fca111187a7313d4944fb71 Mon Sep 17 00:00:00 2001 From: jvolkenant Date: Sat, 31 Aug 2019 05:50:36 -0700 Subject: [PATCH 062/190] Change Nextcloud upgrade logic to look at STORAGE_ROOT's config.php version vs /usr/local's version.php version (#1632) * Download and verify Nextcloud download before deleting old install directory * Changed install logic to look at config.php and not version.php for database version number. When restoring from a backup, config.php in STORAGE_ROOT will hold the Nextcloud version that corresponds to the user's database and version.php in /usr/local won't even exist, so we were missing Nextcloud migration steps. In other cases they should be the same. --- setup/nextcloud.sh | 47 ++++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/setup/nextcloud.sh b/setup/nextcloud.sh index 9f36ee56..d5a8e7c5 100755 --- a/setup/nextcloud.sh +++ b/setup/nextcloud.sh @@ -25,12 +25,12 @@ InstallNextcloud() { echo "Upgrading to Nextcloud version $version" echo + # Download and verify + 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 - # Download and verify - wget_verify https://download.nextcloud.com/server/releases/nextcloud-$version.zip $hash /tmp/nextcloud.zip - # Extract ownCloud/Nextcloud unzip -q /tmp/nextcloud.zip -d /usr/local/lib mv /usr/local/lib/nextcloud /usr/local/lib/owncloud @@ -90,11 +90,26 @@ InstallNextcloud() { fi } +# Nextcloud Version to install. Checks are done down below to step through intermediate versions. nextcloud_ver=15.0.8 nextcloud_hash=4129d8d4021c435f2e86876225fb7f15adf764a3 -# Check if Nextcloud dir exist, and check if version matches nextcloud_ver (if either doesn't - install/upgrade) -if [ ! -d /usr/local/lib/owncloud/ ] \ - || ! grep -q $nextcloud_ver /usr/local/lib/owncloud/version.php; then + +# Current Nextcloud Version, #1623 +# Checking /usr/local/lib/owncloud/version.php shows version of the Nextcloud application, not the DB +# $STORAGE_ROOT/owncloud is kept together even during a backup. It is better to rely on config.php than +# version.php since the restore procedure can leave the system in a state where you have a newer Nextcloud +# application version than the database. + +# 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 -r "include(\"$STORAGE_ROOT/owncloud/config.php\"); echo(\$CONFIG['version']);") +else + CURRENT_NEXTCLOUD_VER="" +fi + +# If the Nextcloud directory is missing (never been installed before, or the nextcloud version to be installed is different +# from the version currently installed, do the install/upgrade +if [ ! -d /usr/local/lib/owncloud/ ] || [[ ! ${CURRENT_NEXTCLOUD_VER} =~ ^$nextcloud_ver ]]; then # Stop php-fpm if running. If theyre not running (which happens on a previously failed install), dont bail. service php7.2-fpm stop &> /dev/null || /bin/true @@ -115,25 +130,21 @@ if [ ! -d /usr/local/lib/owncloud/ ] \ fi # If ownCloud or Nextcloud was previously installed.... - if [ -e /usr/local/lib/owncloud/version.php ]; then + if [ ! -z ${CURRENT_NEXTCLOUD_VER} ]; then # Database migrations from ownCloud are no longer possible because ownCloud cannot be run under # PHP 7. - if grep -q "OC_VersionString = '[89]\." /usr/local/lib/owncloud/version.php; then + if [[ ${CURRENT_NEXTCLOUD_VER} =~ ^[89] ]]; then echo "Upgrades from Mail-in-a-Box prior to v0.28 (dated July 30, 2018) with Nextcloud < 13.0.6 (you have ownCloud 8 or 9) are not supported. Upgrade to Mail-in-a-Box version v0.30 first. Setup aborting." exit 1 - fi - if grep -q "OC_VersionString = '1[012]\." /usr/local/lib/owncloud/version.php; then + elif [[ ${CURRENT_NEXTCLOUD_VER} =~ ^1[012] ]]; then echo "Upgrades from Mail-in-a-Box prior to v0.28 (dated July 30, 2018) with Nextcloud < 13.0.6 (you have ownCloud 10, 11 or 12) are not supported. Upgrade to Mail-in-a-Box version v0.30 first. Setup aborting." exit 1 - fi - # If we are running Nextcloud 13, upgrade to Nextcloud 14 - if grep -q "OC_VersionString = '13\." /usr/local/lib/owncloud/version.php; then + elif [[ ${CURRENT_NEXTCLOUD_VER} =~ ^13 ]]; then + # If we are running Nextcloud 13, upgrade to Nextcloud 14 InstallNextcloud 14.0.6 4e43a57340f04c2da306c8eea98e30040399ae5a - - fi - # During the upgrade from Nextcloud 14 to 15, user_external may cause the upgrade to fail. - # We will disable it here before the upgrade and install it again after the upgrade. - if grep -q "OC_VersionString = '14\." /usr/local/lib/owncloud/version.php; then + elif [[ ${CURRENT_NEXTCLOUD_VER} =~ ^14 ]]; then + # During the upgrade from Nextcloud 14 to 15, user_external may cause the upgrade to fail. + # We will disable it here before the upgrade and install it again after the upgrade. hide_output sudo -u www-data php /usr/local/lib/owncloud/console.php app:disable user_external fi fi From 3ff9817325d6d113c03f2d8f3b1eef9623b07e87 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Sat, 31 Aug 2019 08:15:38 -0400 Subject: [PATCH 063/190] document the xfr: CIDR notation, fix spaces vs tabs and syntax error, broken by c7377e602d06a792b97a5a1047bbdeb32bdd377a, #1616 --- management/dns_update.py | 14 +++++++------- management/templates/custom-dns.html | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/management/dns_update.py b/management/dns_update.py index 7eed50b5..dba6dbc1 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -903,14 +903,14 @@ def set_secondary_dns(hostnames, env): else: # Validate IP address. try: - if "/" in item[4:]: - v = ipaddress.ip_network(item[4:] # raises a ValueError if there's a problem - if not isinstance(v, ipaddress.IPv4Network): raise ValueError("That's an IPv6 subnet.") - else: - v = ipaddress.ip_address(item[4:]) # raises a ValueError if there's a problem - if not isinstance(v, ipaddress.IPv4Address): raise ValueError("That's an IPv6 address.") + if "/" in item[4:]: + v = ipaddress.ip_network(item[4:]) # raises a ValueError if there's a problem + if not isinstance(v, ipaddress.IPv4Network): raise ValueError("That's an IPv6 subnet.") + else: + v = ipaddress.ip_address(item[4:]) # raises a ValueError if there's a problem + if not isinstance(v, ipaddress.IPv4Address): raise ValueError("That's an IPv6 address.") except ValueError: - raise ValueError("'%s' is not an IPv4 address." % item[4:]) + raise ValueError("'%s' is not an IPv4 address or subnet." % item[4:]) # Set. set_custom_dns_record("_secondary_nameserver", "A", " ".join(hostnames), "set", env) diff --git a/management/templates/custom-dns.html b/management/templates/custom-dns.html index c838d32a..56facdc9 100644 --- a/management/templates/custom-dns.html +++ b/management/templates/custom-dns.html @@ -90,7 +90,7 @@

    Multiple secondary servers can be separated with commas or spaces (i.e., ns2.hostingcompany.com ns3.hostingcompany.com). - To enable zone transfers to additional servers without listing them as secondary nameservers, add xfr:IPADDRESS. + To enable zone transfers to additional servers without listing them as secondary nameservers, add an IP address or subnet using xfr:10.20.30.40 or xfr:10.20.30.40/24.

    " + recode_bash(self.string.strip()) + "
    \n" class BashElement(Grammar): - grammar = Comment | CatEOF | EchoPipe | EchoLine | HideOutput | EditConf | SedReplace | AptGet | UfwAllow | RestartService | OtherLine + grammar = Comment | CatEOF | EchoPipe | EchoLine | HideOutput | EditConf | SedReplace | AptGet | UfwAllow | UfwLimit | RestartService | OtherLine def value(self): return self[0].value() From 339c330b4ff61e6bf116d98947e4a8e93e1b72f8 Mon Sep 17 00:00:00 2001 From: Faye Duxovni Date: Sun, 7 Jun 2020 09:50:04 -0400 Subject: [PATCH 124/190] Fix roundcube error log file path in setup script (#1775) --- setup/webmail.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/webmail.sh b/setup/webmail.sh index 20d43c57..bd31e221 100755 --- a/setup/webmail.sh +++ b/setup/webmail.sh @@ -160,7 +160,7 @@ mkdir -p /var/log/roundcubemail /var/tmp/roundcubemail $STORAGE_ROOT/mail/roundc chown -R www-data.www-data /var/log/roundcubemail /var/tmp/roundcubemail $STORAGE_ROOT/mail/roundcube # Ensure the log file monitored by fail2ban exists, or else fail2ban can't start. -sudo -u www-data touch /var/log/roundcubemail/errors +sudo -u www-data touch /var/log/roundcubemail/errors.log # Password changing plugin settings # The config comes empty by default, so we need the settings From df9bb263dc7983b71b5a1ecd400f5ae10ab16fbe Mon Sep 17 00:00:00 2001 From: Vasek Sraier Date: Sun, 7 Jun 2020 15:56:45 +0200 Subject: [PATCH 125/190] daily_tasks.sh: redirect stderr to stdout (#1768) When the management commands fail, they can print something to the standard error output. The administrator would never notice, because it wouldn't be send to him with the usual emails. Fixes #1763 --- management/daily_tasks.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/management/daily_tasks.sh b/management/daily_tasks.sh index 2f723352..db496399 100755 --- a/management/daily_tasks.sh +++ b/management/daily_tasks.sh @@ -16,10 +16,10 @@ if [ `date "+%u"` -eq 1 ]; then fi # Take a backup. -management/backup.py | management/email_administrator.py "Backup Status" +management/backup.py 2>&1 | management/email_administrator.py "Backup Status" # Provision any new certificates for new domains or domains with expiring certificates. -management/ssl_certificates.py -q | management/email_administrator.py "TLS Certificate Provisioning Result" +management/ssl_certificates.py -q 2>&1 | management/email_administrator.py "TLS Certificate Provisioning Result" # Run status checks and email the administrator if anything changed. -management/status_checks.py --show-changes | management/email_administrator.py "Status Checks Change Notice" +management/status_checks.py --show-changes 2>&1 | management/email_administrator.py "Status Checks Change Notice" From 41642f2f5947f64a267130590afd8d39aee17cb3 Mon Sep 17 00:00:00 2001 From: Faye Duxovni Date: Sun, 7 Jun 2020 09:50:04 -0400 Subject: [PATCH 126/190] [backport] Fix roundcube error log file path in setup script (#1775) --- setup/webmail.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/webmail.sh b/setup/webmail.sh index 20d43c57..bd31e221 100755 --- a/setup/webmail.sh +++ b/setup/webmail.sh @@ -160,7 +160,7 @@ mkdir -p /var/log/roundcubemail /var/tmp/roundcubemail $STORAGE_ROOT/mail/roundc chown -R www-data.www-data /var/log/roundcubemail /var/tmp/roundcubemail $STORAGE_ROOT/mail/roundcube # Ensure the log file monitored by fail2ban exists, or else fail2ban can't start. -sudo -u www-data touch /var/log/roundcubemail/errors +sudo -u www-data touch /var/log/roundcubemail/errors.log # Password changing plugin settings # The config comes empty by default, so we need the settings From e03a6541ced593b6c19a875f3fe59139d193a41c Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 5 Jun 2020 13:45:50 -0400 Subject: [PATCH 127/190] Don't make autoconfig/autodiscover subdomains and SRV records when the parent domain has no user accounts These subdomains/records are for automatic configuration of mail clients, but if there are no user accounts on a domain, there is no need to publish a DNS record, provision a TLS certificate, or create an nginx server config block. --- CHANGELOG.md | 4 ++++ management/dns_update.py | 28 +++++++++++++++------------- management/mailconfig.py | 14 ++++++++------ management/web_update.py | 8 ++++---- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01f860fe..04cfb753 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ Mail: * An MTA-STS policy for incoming mail is now published (in DNS and over HTTPS) when the primary hostname and email address domain both have a signed TLS certificate installed. * MTA-STS reporting is enabled with reports sent to administrator@ the primary hostname. +DNS: + +* autoconfig and autodiscover subdomains and CalDAV/CardDAV SRV records are no longer generated for domains that don't have user accounts since they are unnecessary. + v0.45 (May 16, 2020) -------------------- diff --git a/management/dns_update.py b/management/dns_update.py index 5fdb3e0f..80273a12 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -281,28 +281,30 @@ def build_zone(domain, all_domains, additional_records, www_redirect_domains, en if not has_rec(dmarc_qname, "TXT", prefix="v=DMARC1; "): records.append((dmarc_qname, "TXT", 'v=DMARC1; p=reject', "Recommended. Prevents use of this domain name for outbound mail by specifying that the SPF rule should be honoured for mail from @%s." % (qname + "." + domain))) - # Add CardDAV/CalDAV SRV records on the non-primary hostname that points to the primary hostname. + # Add CardDAV/CalDAV SRV records on the non-primary hostname that points to the primary hostname + # for autoconfiguration of mail clients (so only domains hosting user accounts need it). # The SRV record format is priority (0, whatever), weight (0, whatever), port, service provider hostname (w/ trailing dot). - if domain != env["PRIMARY_HOSTNAME"]: + if domain != env["PRIMARY_HOSTNAME"] and domain in get_mail_domains(env, users_only=True): for dav in ("card", "cal"): qname = "_" + dav + "davs._tcp" if not has_rec(qname, "SRV"): records.append((qname, "SRV", "0 0 443 " + env["PRIMARY_HOSTNAME"] + ".", "Recommended. Specifies the hostname of the server that handles CardDAV/CalDAV services for email addresses on this domain.")) - # Adds autoconfiguration A records for all domains. + # Adds autoconfiguration A records for all domains that there are user accounts at. # This allows the following clients to automatically configure email addresses in the respective applications. # autodiscover.* - Z-Push ActiveSync Autodiscover # autoconfig.* - Thunderbird Autoconfig - autodiscover_records = [ - ("autodiscover", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover."), - ("autodiscover", "AAAA", env["PUBLIC_IPV6"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover."), - ("autoconfig", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Thunderbird Autoconfig."), - ("autoconfig", "AAAA", env["PUBLIC_IPV6"], "Provides email configuration autodiscovery support for Thunderbird Autoconfig.") - ] - for qname, rtype, value, explanation in autodiscover_records: - if value is None or value.strip() == "": continue # skip IPV6 if not set - if not has_rec(qname, rtype): - records.append((qname, rtype, value, explanation)) + if domain in get_mail_domains(env, users_only=True): + autodiscover_records = [ + ("autodiscover", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover."), + ("autodiscover", "AAAA", env["PUBLIC_IPV6"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover."), + ("autoconfig", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Thunderbird Autoconfig."), + ("autoconfig", "AAAA", env["PUBLIC_IPV6"], "Provides email configuration autodiscovery support for Thunderbird Autoconfig.") + ] + for qname, rtype, value, explanation in autodiscover_records: + if value is None or value.strip() == "": continue # skip IPV6 if not set + if not has_rec(qname, rtype): + records.append((qname, rtype, value, explanation)) # If this is a domain name that there are email addresses configured for, i.e. "something@" # this domain name, then the domain name is a MTA-STS (https://tools.ietf.org/html/rfc8461) diff --git a/management/mailconfig.py b/management/mailconfig.py index 5f253c14..dd597cd6 100755 --- a/management/mailconfig.py +++ b/management/mailconfig.py @@ -258,13 +258,15 @@ def get_domain(emailaddr, as_unicode=True): pass return ret -def get_mail_domains(env, filter_aliases=lambda alias : True): +def get_mail_domains(env, filter_aliases=lambda alias : True, users_only=False): # Returns the domain names (IDNA-encoded) of all of the email addresses - # configured on the system. - return set( - [get_domain(login, as_unicode=False) for login in get_mail_users(env)] - + [get_domain(address, as_unicode=False) for address, *_ in get_mail_aliases(env) if filter_aliases(address) ] - ) + # configured on the system. If users_only is True, only return domains + # with email addresses that correspond to user accounts. + domains = [] + domains.extend([get_domain(login, as_unicode=False) for login in get_mail_users(env)]) + if not users_only: + domains.extend([get_domain(address, as_unicode=False) for address, *_ in get_mail_aliases(env) if filter_aliases(address) ]) + return set(domains) def add_mail_user(email, pw, privs, env): # validate email diff --git a/management/web_update.py b/management/web_update.py index 4a07dc9e..78f86f4c 100644 --- a/management/web_update.py +++ b/management/web_update.py @@ -24,13 +24,13 @@ def get_web_domains(env, include_www_redirects=True, exclude_dns_elsewhere=True) # the topmost of each domain we serve. domains |= set('www.' + zone for zone, zonefile in get_dns_zones(env)) - # Add Autoconfiguration domains, allowing us to serve correct SSL certs. + # Add Autoconfiguration domains for domains that there are user accounts at: # 'autoconfig.' for Mozilla Thunderbird auto setup. # 'autodiscover.' for Activesync autodiscovery. - domains |= set('autoconfig.' + maildomain for maildomain in get_mail_domains(env)) - domains |= set('autodiscover.' + maildomain for maildomain in get_mail_domains(env)) + domains |= set('autoconfig.' + maildomain for maildomain in get_mail_domains(env, users_only=True)) + domains |= set('autodiscover.' + maildomain for maildomain in get_mail_domains(env, users_only=True)) - # 'mta-sts.' for MTA-STS support. + # 'mta-sts.' for MTA-STS support for all domains that have email addresses. domains |= set('mta-sts.' + maildomain for maildomain in get_mail_domains(env)) if exclude_dns_elsewhere: From 9db2fc7f0551b6ea9b7c73f447495fda722473fb Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Sun, 7 Jun 2020 09:45:04 -0400 Subject: [PATCH 128/190] In web proxies, add X-{Forwarded-{Host,Proto},Real-IP} and 'proxy_set_header Host' when there is a flag Merges #1432, more or less. --- management/web_update.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/management/web_update.py b/management/web_update.py index 78f86f4c..66340619 100644 --- a/management/web_update.py +++ b/management/web_update.py @@ -158,9 +158,23 @@ def make_domain_config(domain, templates, ssl_certificates, env): # any proxy or redirect here? for path, url in yaml.get("proxies", {}).items(): + # Parse some flags in the fragment of the URL. + pass_http_host_header = False + m = re.search("#(.*)$", url) + if m: + for flag in m.group(1).split(","): + if flag == "pass-http-host": + pass_http_host_header = True + url = re.sub("#(.*)$", "", url) + nginx_conf_extra += "\tlocation %s {" % path nginx_conf_extra += "\n\t\tproxy_pass %s;" % url + if pass_http_host_header: + nginx_conf_extra += "\n\t\tproxy_set_header Host $http_host;" nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;" + nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-Host $http_host;" + nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-Proto $scheme;" + nginx_conf_extra += "\n\t\tproxy_set_header X-Real-IP $remote_addr;" nginx_conf_extra += "\n\t}\n" for path, alias in yaml.get("aliases", {}).items(): nginx_conf_extra += "\tlocation %s {" % path From 12d60d102b0cddf6a09d8b68ba2d0a2531efd0e3 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Thu, 11 Jun 2020 12:19:00 -0400 Subject: [PATCH 129/190] Update Roundcube to 1.4.6 Fixes #1776 --- CHANGELOG.md | 7 +++++++ setup/webmail.sh | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cd9e724..23ddd136 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ CHANGELOG ========= +v0.46 (June 11, 2020) +--------------------- + +Security fixes: + +* Roundcube is updated to version 1.4.6 (https://roundcube.net/news/2020/06/02/security-updates-1.4.5-and-1.3.12). + v0.45 (May 16, 2020) -------------------- diff --git a/setup/webmail.sh b/setup/webmail.sh index bd31e221..7054e38e 100755 --- a/setup/webmail.sh +++ b/setup/webmail.sh @@ -28,8 +28,8 @@ apt_install \ # Install Roundcube from source if it is not already present or if it is out of date. # Combine the Roundcube version number with the commit hash of plugins to track # whether we have the latest version of everything. -VERSION=1.4.4 -HASH=4e425263f5bec27d39c07bde524f421bda205c07 +VERSION=1.4.6 +HASH=44961ef62bb9c9875141ca34704bbc7d6f36373d PERSISTENT_LOGIN_VERSION=6b3fc450cae23ccb2f393d0ef67aa319e877e435 HTML5_NOTIFIER_VERSION=4b370e3cd60dabd2f428a26f45b677ad1b7118d5 CARDDAV_VERSION=3.0.3 From 049bfb6f7f0ce918e5437bcf3a18f66ceef2ea3d Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Thu, 11 Jun 2020 12:23:18 -0400 Subject: [PATCH 130/190] v0.46 --- README.md | 4 ++-- setup/bootstrap.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e787c8d8..1d4452b8 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ by him: $ curl -s https://keybase.io/joshdata/key.asc | gpg --import gpg: key C10BDD81: public key "Joshua Tauberer " imported - $ git verify-tag v0.45 + $ git verify-tag v0.46 gpg: Signature made ..... using RSA key ID C10BDD81 gpg: Good signature from "Joshua Tauberer " gpg: WARNING: This key is not certified with a trusted signature! @@ -71,7 +71,7 @@ and on his [personal homepage](https://razor.occams.info/). (Of course, if this Checkout the tag corresponding to the most recent release: - $ git checkout v0.45 + $ git checkout v0.46 Begin the installation. diff --git a/setup/bootstrap.sh b/setup/bootstrap.sh index 4fcb85cc..6aae9500 100644 --- a/setup/bootstrap.sh +++ b/setup/bootstrap.sh @@ -20,7 +20,7 @@ if [ -z "$TAG" ]; then # want to display in status checks. if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/18\.04\.[0-9]/18.04/' `" == "Ubuntu 18.04 LTS" ]; then # This machine is running Ubuntu 18.04. - TAG=v0.45 + TAG=v0.46 elif [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" == "Ubuntu 14.04 LTS" ]; then # This machine is running Ubuntu 14.04. From 6fd3195275fdfef3edd748a44b70dc830f320802 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 12 Jun 2020 13:09:11 -0400 Subject: [PATCH 131/190] Fix MTA-STS policy id so it does not have invalid characters, fixes #1779 --- management/dns_update.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/management/dns_update.py b/management/dns_update.py index 80273a12..2fb7b1b8 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -340,11 +340,13 @@ def build_zone(domain, all_domains, additional_records, www_redirect_domains, en # 'break' was not encountered above, so both domains are good mta_sts_enabled = True if mta_sts_enabled: - # Compute a up-to-32-character hash of the policy file. We'll take a SHA-1 hash of the policy - # file (20 bytes) and encode it as base-64 (60 bytes) but then just take its first 20 bytes - # which should be sufficient to change whenever the policy file changes. + # Compute an up-to-32-character hash of the policy file. We'll take a SHA-1 hash of the policy + # file (20 bytes) and encode it as base-64 (28 bytes, using alphanumeric alternate characters + # instead of '+' and '/' which are not allowed in an MTA-STS policy id) but then just take its + # first 20 characters, which is more than sufficient to change whenever the policy file changes + # (and ensures any '=' padding at the end of the base64 encoding is dropped). with open("/var/lib/mailinabox/mta-sts.txt", "rb") as f: - mta_sts_policy_id = base64.b64encode(hashlib.sha1(f.read()).digest()).decode("ascii")[0:20] + mta_sts_policy_id = base64.b64encode(hashlib.sha1(f.read()).digest(), altchars=b"AA").decode("ascii")[0:20] mta_sts_records.extend([ ("_mta-sts", "TXT", "v=STSv1; id=" + mta_sts_policy_id, "Optional. Part of the MTA-STS policy for incoming mail. If set, a MTA-STS policy must also be published.") ]) From e6102eacfb1637d49eed62c6a2b9d499803b9cf8 Mon Sep 17 00:00:00 2001 From: David Duque Date: Wed, 8 Jul 2020 23:26:47 +0100 Subject: [PATCH 132/190] AXFR Transfers (for secondary DNS servers): Allow IPv6 addresses (#1787) --- management/dns_update.py | 9 +++++---- management/templates/custom-dns.html | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/management/dns_update.py b/management/dns_update.py index 2fb7b1b8..19830749 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -967,18 +967,19 @@ def set_secondary_dns(hostnames, env): try: response = resolver.query(item, "A") except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): - raise ValueError("Could not resolve the IP address of %s." % item) + try: + response = resolver.query(item, "AAAA") + except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): + raise ValueError("Could not resolve the IP address of %s." % item) else: # Validate IP address. try: if "/" in item[4:]: v = ipaddress.ip_network(item[4:]) # raises a ValueError if there's a problem - if not isinstance(v, ipaddress.IPv4Network): raise ValueError("That's an IPv6 subnet.") else: v = ipaddress.ip_address(item[4:]) # raises a ValueError if there's a problem - if not isinstance(v, ipaddress.IPv4Address): raise ValueError("That's an IPv6 address.") except ValueError: - raise ValueError("'%s' is not an IPv4 address or subnet." % item[4:]) + raise ValueError("'%s' is not an IPv4 or IPv6 address or subnet." % item[4:]) # Set. set_custom_dns_record("_secondary_nameserver", "A", " ".join(hostnames), "set", env) diff --git a/management/templates/custom-dns.html b/management/templates/custom-dns.html index a2d5042d..6984b081 100644 --- a/management/templates/custom-dns.html +++ b/management/templates/custom-dns.html @@ -90,7 +90,7 @@

    Multiple secondary servers can be separated with commas or spaces (i.e., ns2.hostingcompany.com ns3.hostingcompany.com). - To enable zone transfers to additional servers without listing them as secondary nameservers, add an IP address or subnet using xfr:10.20.30.40 or xfr:10.20.30.40/24. + To enable zone transfers to additional servers without listing them as secondary nameservers, add an IP address or subnet using xfr:10.20.30.40 or xfr:10.0.0.0/8.

    ").text(r));
    +          show_modal_error("Remove Alias", $("
    ").text(r));
               show_aliases();
             });
         });
    
    From 1098e2b48e3cf542f114caed591337d3cddae762 Mon Sep 17 00:00:00 2001
    From: Hilko 
    Date: Wed, 29 Jul 2020 16:03:33 +0200
    Subject: [PATCH 137/190] Add noindex to www_default meta tags (#1791)
    
    ---
     conf/www_default.html | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/conf/www_default.html b/conf/www_default.html
    index edefc428..68d0366b 100644
    --- a/conf/www_default.html
    +++ b/conf/www_default.html
    @@ -1,6 +1,7 @@
     
     	
     		this is a mail-in-a-box
    +		
     	
     	
     		

    this is a mail-in-a-box

    From 2c34a6df2bf0a319502c251eb0f310002f996ca3 Mon Sep 17 00:00:00 2001 From: Hilko Date: Sun, 26 Jul 2020 17:50:59 +0200 Subject: [PATCH 138/190] Update roundcube to 1.4.7 --- setup/webmail.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup/webmail.sh b/setup/webmail.sh index 7054e38e..f2202244 100755 --- a/setup/webmail.sh +++ b/setup/webmail.sh @@ -28,8 +28,8 @@ apt_install \ # Install Roundcube from source if it is not already present or if it is out of date. # Combine the Roundcube version number with the commit hash of plugins to track # whether we have the latest version of everything. -VERSION=1.4.6 -HASH=44961ef62bb9c9875141ca34704bbc7d6f36373d +VERSION=1.4.7 +HASH=49F194D25AC7B9BF175BD52285BB61CDE7BAED44 PERSISTENT_LOGIN_VERSION=6b3fc450cae23ccb2f393d0ef67aa319e877e435 HTML5_NOTIFIER_VERSION=4b370e3cd60dabd2f428a26f45b677ad1b7118d5 CARDDAV_VERSION=3.0.3 From 4bbe4af37741b9dba3c766657ca36e198532b506 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Wed, 29 Jul 2020 10:11:47 -0400 Subject: [PATCH 139/190] Update CHANGELOG --- CHANGELOG.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36656e53..be38130e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,29 @@ In Development Mail: -* An MTA-STS policy for incoming mail is now published (in DNS and over HTTPS) when the primary hostname and email address domain both have a signed TLS certificate installed. +* An MTA-STS policy for incoming mail is now published (in DNS and over HTTPS) when the primary hostname and email address domain both have a signed TLS certificate installed, allowing senders to know that an encrypted connection should be enforced. * MTA-STS reporting is enabled with reports sent to administrator@ the primary hostname. +* The per-IP connection limit to the IMAP server has been doubled to allow more devices to connect at once, especially with multiple users behind a NAT. DNS: * autoconfig and autodiscover subdomains and CalDAV/CardDAV SRV records are no longer generated for domains that don't have user accounts since they are unnecessary. +* IPv6 addresses can now be specified for secondary DNS nameservers in the control panel. + +TLS: + +* TLS certificates are now provisioned in groups by parent domain to limit easy domain enumeration and make provisioning more resilient to errors for particular domains. + +Control Panel: + +* User passwords can now have spaces. +* Status checks for automatic subdomains have been moved into the section for the parent domain. +* Typo fixed. + +Web: + +* The default web page served on fresh installations now adds the `noindex` meta tag. +* The HSTS header is revised to also be sent on non-success responses. v0.46 (June 11, 2020) --------------------- From f253c400120da768fd6268f3df5220b45d4dc24d Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Sun, 7 Jun 2020 15:47:51 +0200 Subject: [PATCH 140/190] [backport] Add rate limiting of SSH in the firewall (#1770) See #1767. Backport of cfc8fb484cfdb3ee581630a869fd93d4e1b3cb03. --- setup/functions.sh | 9 ++++++++- setup/system.sh | 4 ++-- tools/readable_bash.py | 8 ++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/setup/functions.sh b/setup/functions.sh index b36d14bc..90c4c55d 100644 --- a/setup/functions.sh +++ b/setup/functions.sh @@ -136,7 +136,14 @@ function get_default_privateip { function ufw_allow { if [ -z "${DISABLE_FIREWALL:-}" ]; then # ufw has completely unhelpful output - ufw allow $1 > /dev/null; + ufw allow "$1" > /dev/null; + fi +} + +function ufw_limit { + if [ -z "${DISABLE_FIREWALL:-}" ]; then + # ufw has completely unhelpful output + ufw limit "$1" > /dev/null; fi } diff --git a/setup/system.sh b/setup/system.sh index 28043b16..4d33deb6 100755 --- a/setup/system.sh +++ b/setup/system.sh @@ -256,7 +256,7 @@ if [ -z "${DISABLE_FIREWALL:-}" ]; then apt_install ufw # Allow incoming connections to SSH. - ufw_allow ssh; + ufw_limit 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 @@ -266,7 +266,7 @@ if [ -z "${DISABLE_FIREWALL:-}" ]; then if [ "$SSH_PORT" != "22" ]; then echo Opening alternate SSH port $SSH_PORT. #NODOC - ufw_allow $SSH_PORT #NODOC + ufw_limit $SSH_PORT #NODOC fi fi diff --git a/tools/readable_bash.py b/tools/readable_bash.py index 5207a78a..1fcdd5cd 100644 --- a/tools/readable_bash.py +++ b/tools/readable_bash.py @@ -58,7 +58,7 @@ def generate_documentation(): } .prose { - padding-top: 1em; + padding-top: 1em; padding-bottom: 1em; } .terminal { @@ -261,6 +261,10 @@ class UfwAllow(Grammar): grammar = (ZERO_OR_MORE(SPACE), L("ufw_allow "), REST_OF_LINE, EOL) def value(self): return shell_line("ufw allow " + self[2].string) +class UfwLimit(Grammar): + grammar = (ZERO_OR_MORE(SPACE), L("ufw_limit "), REST_OF_LINE, EOL) + def value(self): + return shell_line("ufw limit " + self[2].string) class RestartService(Grammar): grammar = (ZERO_OR_MORE(SPACE), L("restart_service "), REST_OF_LINE, EOL) def value(self): @@ -275,7 +279,7 @@ class OtherLine(Grammar): return "
    " + recode_bash(self.string.strip()) + "
    \n" class BashElement(Grammar): - grammar = Comment | CatEOF | EchoPipe | EchoLine | HideOutput | EditConf | SedReplace | AptGet | UfwAllow | RestartService | OtherLine + grammar = Comment | CatEOF | EchoPipe | EchoLine | HideOutput | EditConf | SedReplace | AptGet | UfwAllow | UfwLimit | RestartService | OtherLine def value(self): return self[0].value() From 56d0289ed98e781ff759e62c40ff71327103fd48 Mon Sep 17 00:00:00 2001 From: hija Date: Sun, 26 Jul 2020 18:57:04 +0200 Subject: [PATCH 141/190] v0.47 --- CHANGELOG.md | 8 ++++++++ README.md | 4 ++-- setup/bootstrap.sh | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23ddd136..e9b8b759 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ CHANGELOG ========= +v0.47 (July 29, 2020) +--------------------- + +Security fixes: + +* Roundcube is updated to version 1.4.7 fixing a cross-site scripting (XSS) vulnerability with HTML messages with malicious svg/namespace (CVE-2020-15562) (https://roundcube.net/news/2020/07/05/security-updates-1.4.7-1.3.14-and-1.2.11). +* SSH connections are now rate-limited at the firewall level (in addition to fail2ban). + v0.46 (June 11, 2020) --------------------- diff --git a/README.md b/README.md index 1d4452b8..5ef58a29 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ by him: $ curl -s https://keybase.io/joshdata/key.asc | gpg --import gpg: key C10BDD81: public key "Joshua Tauberer " imported - $ git verify-tag v0.46 + $ git verify-tag v0.47 gpg: Signature made ..... using RSA key ID C10BDD81 gpg: Good signature from "Joshua Tauberer " gpg: WARNING: This key is not certified with a trusted signature! @@ -71,7 +71,7 @@ and on his [personal homepage](https://razor.occams.info/). (Of course, if this Checkout the tag corresponding to the most recent release: - $ git checkout v0.46 + $ git checkout v0.47 Begin the installation. diff --git a/setup/bootstrap.sh b/setup/bootstrap.sh index 6aae9500..098de977 100644 --- a/setup/bootstrap.sh +++ b/setup/bootstrap.sh @@ -20,7 +20,7 @@ if [ -z "$TAG" ]; then # want to display in status checks. if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/18\.04\.[0-9]/18.04/' `" == "Ubuntu 18.04 LTS" ]; then # This machine is running Ubuntu 18.04. - TAG=v0.46 + TAG=v0.47 elif [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" == "Ubuntu 14.04 LTS" ]; then # This machine is running Ubuntu 14.04. From 94da7bb088d48ff5d4b87b9bc4a43c5585a51166 Mon Sep 17 00:00:00 2001 From: David Duque Date: Sun, 9 Aug 2020 16:42:39 +0100 Subject: [PATCH 142/190] status_checks.py: Properly terminate the process pools (#1795) * Only spawn a thread pool when strictly needed For --check-primary-hostname, the pool is not used. When exiting, the other processes are left alive and will hang. * Acquire pools with the 'with' statement --- management/daemon.py | 5 ++--- management/status_checks.py | 7 ++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/management/daemon.py b/management/daemon.py index 572b6b4a..b7bf2a66 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -437,9 +437,8 @@ def system_status(): self.items[-1]["extra"].append({ "text": message, "monospace": monospace }) output = WebOutput() # Create a temporary pool of processes for the status checks - pool = multiprocessing.pool.Pool(processes=5) - run_checks(False, env, output, pool) - pool.terminate() + with multiprocessing.pool.Pool(processes=5) as pool: + run_checks(False, env, output, pool) return json_response(output.items) @app.route('/system/updates') diff --git a/management/status_checks.py b/management/status_checks.py index 101a3537..36da034a 100755 --- a/management/status_checks.py +++ b/management/status_checks.py @@ -1021,13 +1021,14 @@ if __name__ == "__main__": from utils import load_environment env = load_environment() - pool = multiprocessing.pool.Pool(processes=10) if len(sys.argv) == 1: - run_checks(False, env, ConsoleOutput(), pool) + with multiprocessing.pool.Pool(processes=10) as pool: + run_checks(False, env, ConsoleOutput(), pool) elif sys.argv[1] == "--show-changes": - run_and_output_changes(env, pool) + with multiprocessing.pool.Pool(processes=10) as pool: + run_and_output_changes(env, pool) elif sys.argv[1] == "--check-primary-hostname": # See if the primary hostname appears resolvable and has a signed certificate. From 62b9b1f15f18745d10f1bb9ae1f25722f8057007 Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Sat, 22 Aug 2020 20:44:19 +0100 Subject: [PATCH 143/190] Add OpenAPI HTTP spec (#1804) --- .gitignore | 1 + api/docs/generate-docs.sh | 23 + api/docs/template.hbs | 31 + api/mailinabox.yml | 2531 +++++++++++++++++++++++++++++++++++++ 4 files changed, 2586 insertions(+) create mode 100755 api/docs/generate-docs.sh create mode 100644 api/docs/template.hbs create mode 100644 api/mailinabox.yml diff --git a/.gitignore b/.gitignore index f3cdb1bc..14e6c4a7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ tools/__pycache__/ externals/ .env .vagrant +api/docs/api-docs.html \ No newline at end of file diff --git a/api/docs/generate-docs.sh b/api/docs/generate-docs.sh new file mode 100755 index 00000000..e7951d8a --- /dev/null +++ b/api/docs/generate-docs.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env sh + +# Requirements: +# - Node.js +# - redoc-cli (`npm install redoc-cli -g`) + +redoc-cli bundle ../mailinabox.yml \ + -t template.hbs \ + -o api-docs.html \ + --templateOptions.metaDescription="Mail-in-a-Box HTTP API" \ + --title="Mail-in-a-Box HTTP API" \ + --options.expandSingleSchemaField \ + --options.hideSingleRequestSampleTab \ + --options.jsonSampleExpandLevel=10 \ + --options.hideDownloadButton \ + --options.theme.logo.maxHeight=180px \ + --options.theme.logo.maxWidth=180px \ + --options.theme.colors.primary.main="#C52" \ + --options.theme.typography.fontSize=16px \ + --options.theme.typography.fontFamily="Raleway, sans-serif" \ + --options.theme.typography.headings.fontFamily="Ubuntu, Arial, sans-serif" \ + --options.theme.typography.code.fontSize=15px \ + --options.theme.typography.code.fontFamily='"Source Code Pro", monospace' \ No newline at end of file diff --git a/api/docs/template.hbs b/api/docs/template.hbs new file mode 100644 index 00000000..0de7d222 --- /dev/null +++ b/api/docs/template.hbs @@ -0,0 +1,31 @@ + + + + + + {{title}} + + + + + + + + + {{{redocHead}}} + + + + {{{redocHTML}}} + + + diff --git a/api/mailinabox.yml b/api/mailinabox.yml new file mode 100644 index 00000000..57ba5aa4 --- /dev/null +++ b/api/mailinabox.yml @@ -0,0 +1,2531 @@ +openapi: 3.0.3 +info: + title: Mail-in-a-Box + description: | + Mail-in-a-Box API HTTP specification. + + # Introduction + This API is documented in [**OpenAPI format**](http://spec.openapis.org/oas/v3.0.3). + ([View the full HTTP specification](https://raw.githubusercontent.com/mail-in-a-box/mailinabox/api-spec/api/mailinabox.yml).) + + All endpoints are relative to `https://{host}/admin` and are secured with [`Basic Access` authentication](https://en.wikipedia.org/wiki/Basic_access_authentication). + contact: + name: Mail-in-a-Box support + url: https://mailinabox.email/ + license: + name: CC0 1.0 Universal + url: https://creativecommons.org/publicdomain/zero/1.0/legalcode + version: 0.47.0 + x-logo: + url: https://mailinabox.email/static/logo.png + altText: Mail-in-a-Box logo +externalDocs: + description: Find out more about Mail-in-a-box. + url: https://mailinabox.email/ +servers: + - url: https://{host}/admin + variables: + host: + default: box.example.com + description: The API hostname. +security: + - basicAuth: [] +tags: + - name: User + description: Endpoints related to user authentication. + - name: Mail + description: | + Mail operations, which include getting all users, getting all aliases, adding/updating/removing users and aliases and getting all mail domains. + - name: DNS + description: | + DNS operations, which include adding custom records, adding a secondary nameserver and viewing all DNS records. + - name: SSL + description: | + TLS (SSL) Certificates operations, which include checking certificate status + and installing custom certificates. + - name: Web + description: | + Static web hosting operations, which include getting domain information and updating domain root directories. + - name: System + description: | + System operations, which include system status checks, new version checks + and reboot status. +paths: + /me: + get: + tags: + - User + summary: Get user information + description: | + Returns user information. Used for user authentication. + + Authenticate a user by supplying the auth token as a base64 encoded string in + format `email:password` using basic authentication headers. + + If successful, a long-lived `api_key` is returned which can be used for subsequent + requests to the API. + operationId: getMe + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/me" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/MeResponse' + examples: + invalid: + value: + reason: Incorrect username or password + status: invalid + ok: + value: + api_key: 1a2b3c4d5e6f7g8h9i0j + email: user@example.com + privileges: + - admin + status: ok + /system/status: + post: + tags: + - System + summary: Get system status + description: | + Returns an array of statuses which can include headings. + operationId: getSystemStatus + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/system/status" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/SystemStatusResponse' + example: + - type: heading + text: System + extra: [] + - type: warning + text: This domain's DNSSEC DS record is not set + extra: + - monospace: false + text: 'Digest Type: 2 / SHA-25' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/version: + get: + tags: + - System + summary: Get system version + description: Returns installed Mail-in-a-Box version. + operationId: getSystemVersion + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/system/version" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SystemVersionResponse' + example: v0.46 + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/latest-upstream-version: + post: + tags: + - System + summary: Get system upstream version + description: Returns Mail-in-a-Box upstream version. + operationId: getSystemUpstreamVersion + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/system/latest-upstream-version" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SystemVersionUpstreamResponse' + example: v0.47 + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/updates: + get: + tags: + - System + summary: Get system updates + description: Returns system (apt) updates. + operationId: getSystemUpdates + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/system/updates" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SystemUpdatesResponse' + example: | + libgnutls30 (3.5.18-1ubuntu1.4) + libxau6 (1:1.0.8-1ubuntu1) + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/update-packages: + post: + tags: + - System + summary: Update system packages + description: Updates system (apt) packages. + operationId: updateSystemPackages + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/system/update-packages" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SystemUpdatePackagesResponse' + example: | + Calculating upgrade... + The following packages will be upgraded: + cloud-init grub-common + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/privacy: + get: + tags: + - System + summary: Get system privacy status + description: | + Returns system privacy (new-version check) status. + + Response: + + - `true`: Private, new-version checks will not be performed + - `false`: Not private, new-version checks will be performed + operationId: getSystemPrivacyStatus + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/system/privacy" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/SystemPrivacyStatusResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + post: + tags: + - System + summary: Update system privacy + description: | + Updates system privacy (new-version checks). + + Request: + + - `value: private`: Disable new version checks + - `value: off`: Enable new version checks + operationId: updateSystemPrivacy + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/SystemPrivacyUpdateRequest' + examples: + enable: + summary: Enable new version checks + value: + value: 'off' + disable: + summary: Disable new version checks + value: + value: private + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/system/privacy" \ + -d "value=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SystemPrivacyUpdateResponse' + example: OK + 400: + description: Bad request + content: + text/html: + schema: + type: string + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/reboot: + get: + tags: + - System + summary: Get system reboot status + description: | + Returns the system reboot status. + + Response: + + - `true`: A reboot is required + - `false`: A reboot is not required + operationId: getSystemRebootStatus + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/system/reboot" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/SystemRebootStatusResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + post: + tags: + - System + summary: Reboot system + description: Reboots the system. + operationId: rebootSystem + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/system/reboot" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SystemRebootResponse' + example: No reboot is required, so it is not allowed. + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/backup/status: + get: + tags: + - System + summary: Get system backup status + description: | + Returns the system backup status. + + If the list of backups is empty, this implies no backups have been made yet. + operationId: getSystemBackupStatus + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/system/backup/status" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/SystemBackupStatusResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/backup/config: + get: + tags: + - System + summary: Get system backup config + description: Returns the system backup config. + operationId: getSystemBackupConfig + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/system/backup/config" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/SystemBackupConfigResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + post: + tags: + - System + summary: Update system backup config + description: Updates the system backup config. + operationId: updateSystemBackupConfig + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/SystemBackupConfigUpdateRequest' + examples: + s3: + summary: S3 backup + value: + target: s3://s3.eu-central-1.amazonaws.com/box-example-com + target_user: ACCESS_KEY + target_pass: SECRET_ACCESS_KEY + minAge: 3 + local: + summary: Local backup + value: + target: local + target_user: '' + target_pass: '' + minAge: 3 + rsync: + summary: Rsync backup + value: + target: rsync://username@box.example.com//backups/box.example.com + target_user: '' + target_pass: '' + minAge: 3 + off: + summary: Disable backups + value: + target: 'off' + target_user: '' + target_pass: '' + minAge: 0 + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/system/backup/config" \ + -d "target=" \ + -d "target_user=" \ + -d "target_pass=" \ + -d "min_age=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SystemBackupConfigUpdateResponse' + example: OK + 400: + description: Bad request + content: + text/html: + schema: + type: string + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /ssl/status: + get: + tags: + - SSL + summary: Get SSL status + description: Returns the SSL status for all domains. + operationId: getSSLStatus + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/ssl/status" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/SSLStatusResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /ssl/csr/{domain}: + post: + tags: + - SSL + summary: Generate SSL CSR + description: | + Generates a Certificate Signing Request (CSR) for a domain & country code. + operationId: generateSSLCSR + parameters: + - in: path + name: domain + schema: + $ref: '#/components/schemas/Hostname' + required: true + description: Domain to generate CSR for. + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/SSLCSRGenerateRequest' + example: + countrycode: 'GB' + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/ssl/csr/" \ + -d "countrycode=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SSLCSRGenerateResponse' + example: | + -----BEGIN CERTIFICATE REQUEST----- + MIICaDCCAVACAQAwIzELMAkGA1UEBhMCQlMxFDASBgNVBAMMC2V4YW1wbGUuY29t + ... + JmFDQESSfUxLPHLC660Wnf3GmrP/duZHpPC+qTe8b1AlQ7zDT3cOaAQ+Mb0= + -----END CERTIFICATE REQUEST----- + 400: + description: Bad request + content: + text/html: + schema: + type: string + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /ssl/install: + post: + tags: + - SSL + summary: Install SSL certificate + description: | + Installs a custom certificate. The chain certificate is optional. + operationId: installSSLCertificate + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/SSLCertificateInstallRequest' + example: + domain: example.com + cert: CERT_STRING + chain: CHAIN_STRING + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/ssl/install" \ + -d "domain=" \ + -d "cert=" \ + -d "chain=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SSLCertificateInstallResponse' + example: OK + 400: + description: Bad request + content: + text/html: + schema: + type: string + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /ssl/provision: + post: + tags: + - SSL + summary: Provision SSL certificates + description: | + Provisions certificates for all domains. + operationId: provisionSSLCertificates + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/ssl/provision" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/SSLCertificatesProvisionResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /dns/secondary-nameserver: + get: + tags: + - DNS + summary: Get DNS secondary nameserver + description: | + Returns a list of nameserver hostnames. + operationId: getDnsSecondaryNameserver + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/dns/secondary-nameserver" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/DNSSecondaryNameserverResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + post: + tags: + - DNS + summary: Add DNS secondary nameserver + description: | + Adds one or more secondary nameservers. + operationId: addDnsSecondaryNameserver + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/DNSSecondaryNameserverAddRequest' + example: + hostnames: ns2.hostingcompany.com, ns3.hostingcompany.com + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/dns/secondary-nameserver" \ + -d "hostnames=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/DNSSecondaryNameserverAddResponse' + example: 'updated DNS: example.com' + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: Could not resolve the IP address of badhostname + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /dns/zones: + get: + tags: + - DNS + summary: Get DNS zones + description: Returns an array of all managed top-level domains. + operationId: getDnsZones + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/dns/zones" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/DNSZonesResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /dns/update: + post: + tags: + - DNS + summary: Update DNS + description: Updates the DNS. Involves creating zone files and restarting `nsd`. + operationId: updateDns + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/DNSUpdateRequest' + example: + force: 1 + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/dns/update" \ + -d "force=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/DNSUpdateResponse' + 400: + description: Bad request + content: + text/html: + schema: + type: string + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /dns/custom: + get: + tags: + - DNS + summary: Get DNS custom records + description: Returns all custom DNS records. + operationId: getDnsCustomRecords + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/dns/custom" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/DNSCustomRecordsResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /dns/custom/{qname}/{rtype}: + parameters: + - in: path + name: qname + schema: + $ref: '#/components/schemas/Hostname' + required: true + description: DNS record query name + - in: path + name: rtype + schema: + $ref: '#/components/schemas/DNSRecordType' + required: true + description: Record type + get: + tags: + - DNS + summary: Get DNS custom records + description: Returns all custom records for the specified query name and type. + operationId: getDnsCustomRecordsForQNameAndType + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/dns/custom//" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/DNSCustomRecordsResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + post: + tags: + - DNS + summary: Add DNS custom record + description: Adds a custom DNS record for the specified query name and type. + operationId: addDnsCustomRecord + requestBody: + $ref: '#/components/requestBodies/DNSCustomRecordRequest' + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/dns/custom//" \ + -H "Content-Type: text/plain" \ + --data-raw "" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/DNSCustomRecordUpsertResponse' + example: 'updated DNS: example.com' + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: "'badhostname' does not appear to be an IPv4 or IPv6 address" + 403: + description: Forbidden + content: + text/html: + schema: + type: string + put: + tags: + - DNS + summary: Update DNS custom record + description: Updates an existing DNS custom record value for the specified qname and type. + operationId: updateDnsCustomRecord + requestBody: + $ref: '#/components/requestBodies/DNSCustomRecordRequest' + x-codeSamples: + - lang: curl + source: | + curl -x PUT "https://{host}/admin/dns/custom//" \ + -H "Content-Type: text/plain" \ + --data-raw "" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/DNSCustomRecordUpsertResponse' + example: 'updated DNS: example.com' + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: "'badhostname' does not appear to be an IPv4 or IPv6 address" + 403: + description: Forbidden + content: + text/html: + schema: + type: string + delete: + tags: + - DNS + summary: Remove DNS custom record + description: Removes a DNS custom record for the specified domain, type & value. + operationId: removeDnsCustomRecord + requestBody: + $ref: '#/components/requestBodies/DNSCustomRecordRequest' + x-codeSamples: + - lang: curl + source: | + curl -X DELETE "https://{host}/admin/dns/custom//" \ + -H "Content-Type: text/plain" \ + --data-raw "" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/DNSCustomRecordRemoveResponse' + example: 'updated DNS: example.com' + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: badhostname is not a domain name or a subdomain of a domain name managed by this box + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /dns/custom/{qname}: + parameters: + - in: path + name: qname + schema: + $ref: '#/components/schemas/Hostname' + required: true + description: DNS query name. + get: + tags: + - DNS + summary: Get DNS custom A records + description: Returns all custom A records for the specified query name. + operationId: getDnsCustomARecordsForQName + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/dns/custom/" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/DNSCustomRecordsResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + post: + tags: + - DNS + summary: Add DNS custom A record + description: Adds a custom DNS A record for the specified query name. + operationId: addDnsCustomARecord + requestBody: + $ref: '#/components/requestBodies/DNSCustomRecordRequest' + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/dns/custom/" \ + -H "Content-Type: text/plain" \ + --data-raw "" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/DNSCustomRecordUpsertResponse' + example: 'updated DNS: example.com' + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: "'badhostname' does not appear to be an IPv4 or IPv6 address" + 403: + description: Forbidden + content: + text/html: + schema: + type: string + put: + tags: + - DNS + summary: Update DNS custom A record + description: Updates an existing DNS custom A record value for the specified qname. + operationId: updateDnsCustomARecord + requestBody: + $ref: '#/components/requestBodies/DNSCustomRecordRequest' + x-codeSamples: + - lang: curl + source: | + curl -x PUT "https://{host}/admin/dns/custom/" \ + -H "Content-Type: text/plain" \ + --data-raw "" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/DNSCustomRecordUpsertResponse' + example: 'updated DNS: example.com' + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: "'badhostname' does not appear to be an IPv4 or IPv6 address" + 403: + description: Forbidden + content: + text/html: + schema: + type: string + delete: + tags: + - DNS + summary: Remove DNS custom A record + description: Removes a DNS custom A record for the specified domain & value. + operationId: removeDnsCustomARecord + requestBody: + $ref: '#/components/requestBodies/DNSCustomRecordRequest' + x-codeSamples: + - lang: curl + source: | + curl -X DELETE "https://{host}/admin/dns/custom/" \ + -H "Content-Type: text/plain" \ + --data-raw "" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/DNSCustomRecordRemoveResponse' + example: 'updated DNS: example.com' + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: badhostname is not a domain name or a subdomain of a domain name managed by this box + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /dns/dump: + get: + tags: + - DNS + summary: Get DNS dump + description: Returns all DNS records. + operationId: getDnsDump + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/dns/dump" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/DNSDumpResponse' + example: + - - example1.com + - - explanation: Required. Specifies the hostname (and priority) of the machine that handles @example.com mail. + qname: example1.com + rtype: MX + value: 10 box.example1.com. + - - example2.com + - - explanation: Required. Specifies the hostname (and priority) of the machine that handles @example.com mail. + qname: example2.com + rtype: MX + value: 10 box.example2.com. + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/users: + get: + tags: + - Mail + summary: Get mail users + description: Returns all mail users. + operationId: getMailUsers + parameters: + - in: query + name: format + schema: + $ref: '#/components/schemas/MailUsersResponseFormat' + description: The format of the response. + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/mail/users?format=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/MailUsersResponse' + text/html: + schema: + $ref: '#/components/schemas/MailUsersSimpleResponse' + example: | + user1@example.com + user2@example.com + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/users/add: + post: + tags: + - Mail + summary: Add mail user + description: Adds a new mail user. + operationId: addMailUser + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/MailUserAddRequest' + examples: + normal: + summary: Normal user + value: + email: user@example.com + password: s3curE_pa5Sw0rD + privileges: '' + admin: + summary: Admin user + value: + email: user@example.com + password: s3curE_pa5Sw0rD + privileges: admin + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mail/users/add" \ + -d "email=" \ + -d "password=" \ + -d "privileges=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailUserAddResponse' + example: | + mail user added + updated DNS: OpenDKIM configuration + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: Invalid email address + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/users/remove: + post: + tags: + - Mail + summary: Remove mail user + description: Removes an existing mail user. + operationId: removeMailUser + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/MailUserRemoveRequest' + example: + email: user@example.com + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mail/users/remove" \ + -d "email=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailUserRemoveResponse' + example: OK + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: That's not a user (invalid@example.com) + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/users/privileges/add: + post: + tags: + - Mail + summary: Add mail user privilege + description: Adds a privilege to an existing mail user. + operationId: addMailUserPrivilege + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/MailUserAddPrivilegeRequest' + example: + email: user@example.com + privilege: admin + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mail/users/privileges/add" \ + -d "email=" \ + -d "privilege=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailUserAddPrivilegeResponse' + example: OK + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: That's not a user (invalid@example.com) + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/users/privileges/remove: + post: + tags: + - Mail + summary: Remove mail user privilege + description: Removes a privilege from an existing mail user. + operationId: removeMailUserPrivilege + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/MailUserRemovePrivilegeRequest' + example: + email: user@example.com + privilege: admin + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mail/users/privileges/remove" \ + -d "email=" \ + -d "privilege=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailUserRemovePrivilegeResponse' + example: OK + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: That's not a user (invalid@example.com) + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/users/password: + post: + tags: + - Mail + summary: Set mail user password + description: Sets a password for an existing mail user. + operationId: setMailUserPassword + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/MailUserSetPasswordRequest' + example: + email: user@example.com + password: s3curE_pa5Sw0rD + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mail/users/password" \ + -d "email=" \ + -d "password=" \ + -u ":" \ + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailUserSetPasswordResponse' + example: OK + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: Passwords must be at least eight characters + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/users/privileges: + get: + tags: + - Mail + summary: Get mail user privileges + description: Returns all privileges for an existing mail user. + operationId: getMailUserPrivileges + parameters: + - in: query + name: email + schema: + $ref: '#/components/schemas/Email' + description: The email you want to get privileges for. + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/mail/users/privileges?email=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailUserPrivilegesResponse' + example: admin + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/domains: + get: + tags: + - Mail + summary: Get mail domains + description: Returns all mail domains. + operationId: getMailDomains + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/mail/domains" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailDomainsResponse' + example: | + example1.com + example2.com + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/aliases: + get: + tags: + - Mail + summary: Get mail aliases + description: Returns all mail aliases. + operationId: getMailAliases + parameters: + - in: query + name: format + schema: + $ref: '#/components/schemas/MailAliasesResponseFormat' + description: The format of the response. + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/mail/aliases?format=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/MailAliasByDomain' + text/html: + schema: + $ref: '#/components/schemas/MailAliasesSimpleResponse' + example: | + abuse@example.com administrator@example.com + admin@example.com administrator@example.com + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/aliases/add: + post: + tags: + - Mail + summary: Upsert mail alias + description: | + Adds or updates a mail alias. If updating, you need to set `update_if_exists: 1`. + operationId: upsertMailAlias + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/MailAliasUpsertRequest' + examples: + regular: + summary: Regular alias + value: + update_if_exists: 0 + address: user@example.com + forwards_to: user2@example.com + permitted_senders: + catchall: + summary: Catch-all + value: + update_if_exists: 0 + address: '@example.com' + forwards_to: user@otherexample.com + permitted_senders: + domainalias: + summary: Domain alias + value: + update_if_exists: 0 + address: '@example.com' + forwards_to: '@otherexample.com' + permitted_senders: + update: + summary: Update existing alias + value: + update_if_exists: 1 + address: user@example.com + forwards_to: user2@example.com + permitted_senders: user3@example.com, user4@example.com + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mail/aliases/add" \ + -d "update_if_exists=" \ + -d "address=" \ + -d "forwards_to=" \ + -d "permitted_senders=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailAliasUpsertResponse' + example: alias updated + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: Invalid email address (invalid@example.com) + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/aliases/remove: + post: + tags: + - Mail + summary: Remove mail alias + description: Removes a mail alias. + operationId: removeMailAlias + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/MailAliasRemoveRequest' + example: + address: user@example.com + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mail/aliases/remove" \ + -d "address=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailAliasRemoveResponse' + example: alias removed + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: That's not an alias (invalid@example) + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /web/domains: + get: + tags: + - Web + summary: Get web domains + description: Returns all static web domains. + operationId: getWebDomains + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/web/domains" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/WebDomain' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /web/update: + post: + tags: + - Web + summary: Update web + description: Updates static websites, used for updating domain root directories. + operationId: updateWeb + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/web/update" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/WebUpdateResponse' + example: web updated + 403: + description: Forbidden + content: + text/html: + schema: + type: string +components: + securitySchemes: + basicAuth: + type: http + scheme: basic + description: | + Credentials can be supplied using the `Authorization` header in + format `Authorization: Basic {access-token}`. + + The `access-token` is comprised of the Base64 encoding of `username:password`. + The `username` is the mail user's email address, and `password` can either be the mail user's + password, or the `api_key` returned from the `getMe` operation. + + When using `curl`, you can supply user credentials using the `-u` or `--user` parameter. + requestBodies: + DNSCustomRecordRequest: + required: true + content: + text/plain: + schema: + type: string + example: 1.2.3.4 + description: The value of the DNS record. + example: '1.2.3.4' + schemas: + MailUsersResponseFormat: + type: string + enum: + - text + - json + example: json + description: Response format (`application/json` or `text/html`). + MailAliasesResponseFormat: + type: string + enum: + - text + - json + example: json + description: Response format (`application/json` or `text/html`). + MailUserSetPasswordResponse: + type: string + example: OK + description: Mail user set password response. + MailUserRemoveResponse: + type: string + example: OK + description: Mail user remove response. + MailUserAddResponse: + type: string + example: | + mail user added + updated DNS: OpenDKIM configuration + description: | + Mail user add response. + + Can include information about operations related to adding new users, like updating DNS. + MailUserAddPrivilegeResponse: + type: string + example: OK + description: Mail user add admin privilege response. + MailUserRemovePrivilegeResponse: + type: string + example: OK + description: Mail user remove admin privilege response. + MailUsersSimpleResponse: + type: string + example: | + user1@example.com + user2@example.com + description: Get mail users text format response. + MailUserPrivilegesResponse: + $ref: '#/components/schemas/MailUserPrivilege' + description: Mail user privileges response. + example: admin + MailDomainsResponse: + type: string + example: | + example1.com + example2.com + description: Mail domains response. + MailUsersResponse: + type: array + items: + $ref: '#/components/schemas/MailUserByDomain' + description: Get mail aliases JSON format response. + MailUserByDomain: + type: object + required: + - domain + - users + properties: + domain: + $ref: '#/components/schemas/Hostname' + users: + type: array + items: + $ref: '#/components/schemas/MailUser' + description: Mail users by domain. + MailUser: + type: object + required: + - email + - privileges + - status + properties: + email: + $ref: '#/components/schemas/Email' + privileges: + type: array + items: + $ref: '#/components/schemas/MailUserPrivilege' + status: + $ref: '#/components/schemas/MailUserStatus' + mailbox: + type: string + example: /home/user-data/mail/mailboxes/example.com/user + description: Mail user details. + MailAliasesSimpleResponse: + type: string + example: | + abuse@example.com administrator@example.com + admin@example.com administrator@example.com + description: Get mail aliases text format response. + MailAliasByDomain: + type: object + required: + - domain + - aliases + properties: + domain: + $ref: '#/components/schemas/Hostname' + aliases: + type: array + items: + $ref: '#/components/schemas/MailAlias' + description: Mail aliases by domain. + MailAlias: + type: object + required: + - address + - address_display + - forwards_to + - permitted_senders + - required + properties: + address: + $ref: '#/components/schemas/Email' + address_display: + $ref: '#/components/schemas/Email' + forwards_to: + type: array + items: + $ref: '#/components/schemas/Email' + permitted_senders: + type: array + nullable: true + items: + $ref: '#/components/schemas/Email' + required: + type: boolean + example: true + description: Mail alias details. + MailAliasUpsertResponse: + type: string + example: alias updated + description: Mail alias add/update response. + MailAliasUpsertRequest: + type: object + required: + - update_if_exists + - address + - forwards_to + - permitted_senders + properties: + update_if_exists: + type: integer + format: int32 + minimum: 0 + maximum: 1 + example: 1 + description: Set to `1` when updating an alias. + address: + $ref: '#/components/schemas/Email' + forwards_to: + type: string + example: user1@example.com, user2@example.com + description: | + If adding a regular or catch-all alias, the format needs to be `user@example.com`. + Multiple address can be separated by newlines or commas. + + If adding a domain alias, the format needs to be `@example.com`. + permitted_senders: + type: string + nullable: true + example: user1@example.com, user2@example.com + description: | + Mail users that can send mail claiming to be from any address on the alias domain. + Multiple address can be separated by newlines or commas. + + Leave empty to allow any mail user listed in `forwards_to` to send mail claiming to be from any address on the alias domain. + description: Mail alias upsert request. + MailAliasRemoveResponse: + type: string + example: alias removed + description: Mail alias remove response. + MailAliasRemoveRequest: + type: object + required: + - address + properties: + address: + $ref: '#/components/schemas/Email' + description: Mail aliases remove request. + DNSRecordType: + enum: + - A + - AAAA + - CAA + - CNAME + - TXT + - MX + - SRV + - SSHFP + - NS + example: MX + description: DNS record type. + DNSDumpResponse: + type: array + items: + $ref: '#/components/schemas/DNSDumpDomains' + description: DNS dump response. + DNSDumpDomains: + type: array + items: + oneOf: + - $ref: '#/components/schemas/Hostname' + - $ref: '#/components/schemas/DNSDumpDomainRecords' + description: | + A list of records per domain. + + The first item in the list is the domain and the second item is the list of records. + DNSDumpDomainRecords: + type: array + items: + $ref: '#/components/schemas/DNSDumpDomainRecord' + description: List of domain records. + DNSDumpDomainRecord: + type: object + required: + - explanation + - qname + - type + - value + properties: + explanation: + type: string + example: Required. Specifies the hostname (and priority) of the machine that handles @example.com mail + qname: + $ref: '#/components/schemas/Hostname' + rtype: + $ref: '#/components/schemas/DNSRecordType' + value: + type: string + example: 10 example.com. + description: Domain DNS record details. + DNSCustomRecord: + type: object + required: + - qname + - rtype + - value + properties: + qname: + $ref: '#/components/schemas/Hostname' + rtype: + $ref: '#/components/schemas/DNSRecordType' + value: + type: string + example: 10 example.com. + description: Custom DNS record detail detail. + DNSCustomRecordsResponse: + type: array + items: + $ref: '#/components/schemas/DNSCustomRecord' + description: Custom DNS records response. + DNSZonesResponse: + type: array + items: + $ref: '#/components/schemas/Hostname' + description: DNS zones response. + DNSSecondaryNameserverResponse: + type: object + required: + - hostnames + properties: + hostnames: + type: array + items: + type: string + example: ns1.example.com + description: Secondary nameserver/s response. + DNSCustomRecordRemoveResponse: + type: string + example: 'updated DNS: example.com' + description: Custom DNS record remove response. + DNSCustomRecordUpsertResponse: + type: string + example: 'updated DNS: example.com' + description: Custom DNS record add response. + DNSUpdateRequest: + type: object + required: + - force + properties: + force: + type: integer + format: int32 + minimum: 0 + maximum: 1 + example: 1 + description: Force an update even if mailinabox detects no changes are required. + description: DNS update request. + DNSUpdateResponse: + type: string + example: | + updated DNS: example1.com,example2.com + description: DNS update response. + DNSSecondaryNameserverAddRequest: + type: object + required: + - hostnames + properties: + hostnames: + type: string + description: Hostnames separated with commas or spaces. + example: ns2.hostingcompany.com, ns3.hostingcompany.com + description: Secondary nameserver/s add request. + DNSSecondaryNameserverAddResponse: + type: string + example: 'updated DNS: example.com' + description: Secondary nameserver/s add response. + SystemPrivacyUpdateRequest: + type: object + required: + - value + properties: + value: + $ref: '#/components/schemas/SystemPrivacyStatus' + description: Update system privacy request. + SystemPrivacyStatus: + type: string + enum: + - private + - 'off' + example: private + description: System privacy status. + MailUserSetPasswordRequest: + type: object + required: + - email + - password + properties: + email: + $ref: '#/components/schemas/Email' + password: + type: string + format: password + description: Mail user set password request. + MailUserAddRequest: + type: object + required: + - email + - password + - privileges + properties: + email: + $ref: '#/components/schemas/Email' + password: + type: string + format: password + privileges: + $ref: '#/components/schemas/MailUserPrivilege' + description: Mail user add request. + MailUserRemoveRequest: + type: object + required: + - email + properties: + email: + $ref: '#/components/schemas/Email' + description: Mail user remove request. + MailUserStatus: + type: string + enum: + - active + - inactive + example: active + description: Mail user status. + MailUserPrivilege: + type: string + enum: + - admin + - '' + example: admin + description: Mail user privilege. + MailUserAddPrivilegeRequest: + type: object + required: + - email + - privilege + properties: + email: + $ref: '#/components/schemas/Email' + privilege: + $ref: '#/components/schemas/MailUserPrivilege' + description: Mail user add privilege request. + MailUserRemovePrivilegeRequest: + type: object + required: + - email + - privilege + properties: + email: + $ref: '#/components/schemas/Email' + privilege: + $ref: '#/components/schemas/MailUserPrivilege' + description: Mail user remove privilege request. + SSLCSRGenerateRequest: + type: object + required: + - countrycode + properties: + countrycode: + type: string + example: GB + description: Generate SSL CSR request. + SSLCSRGenerateResponse: + type: string + example: | + -----BEGIN CERTIFICATE REQUEST----- + MIICaDCCAVACAQAwIzELMAkGA1UEBhMCQlMxFDASBgNVBAMMC2V4YW1wbGUuY29t + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3K6dwLM2Nk8kVhIBaZmp + eY6y7O0T3jrexEKlW839TVYdcH+K35V1NxilbMFKMuHeowGwFyyiqOy/OUYNeq+T + Rz3s4b1qG2p01dwlsXHHYmXLYTAhvqvY+CU5ksieuZbyHRTwbHViQ0xtRXwoVCnj + CkN7kJVpkLfVN0/BG6NBFpv/JI8F+hwp+IHdkC1gUXRrLJNC79ERqFP8HoqdQWNw + OGGFaOe2aQhvj2zt8wFncyKVc40UKVbSzGGzdL2MPiAJHgZ2lmeY1xDyX1lOt12R + IFPwtxmbxaxYaVfe2hxl7m88xV3OjYcKgwVYDusk2XJ37cGew5g+NbBvzEeEUpF9 + 5wIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAD7UPC3/Nkgpn53mT9puUonYdJg9 + SD8vvTK/N78CzoEgPNyq+bYbqlcvVPKIdItf9TMiqfOSvW3e3NvkRisYle8Qp+0C + 8pafXBvQ9eHt5CFeJn4sH9GnxeflOZT/P9Jnp71KtZQvOobirX4GgEWs79g+/NHb + Zyf8rbadt9HruNhKA5nlP8cn7Rdc/iuJU8MVSQszI1s1DEcXMPxr6iqb2g87/ifH + lWcK59kvRJkCcPhPzjpUy9NulucH4WFA/WqKeDNFS/oC+upV5w8EDEcfnenJFG+N + JmFDQESSfUxLPHLC660Wnf3GmrP/duZHpPC+qTe8b1AlQ7zDT3cOaAQ+Mb0= + -----END CERTIFICATE REQUEST----- + description: Generate SSL CSR response. + SSLCertificateInstallRequest: + type: object + required: + - domain + - cert + - chain + properties: + domain: + $ref: '#/components/schemas/Hostname' + cert: + type: string + description: TLS/SSL certificate. + example: | + -----BEGIN CERTIFICATE----- + MIICaDCCAVACAQAwIzELMAkGA1UEBhMCQlMxFDASBgNVBAMMC2V4YW1wbGUuY29t + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3K6dwLM2Nk8kVhIBaZmp + eY6y7O0T3jrexEKlW839TVYdcH+K35V1NxilbMFKMuHeowGwFyyiqOy/OUYNeq+T + Rz3s4b1qG2p01dwlsXHHYmXLYTAhvqvY+CU5ksieuZbyHRTwbHViQ0xtRXwoVCnj + CkN7kJVpkLfVN0/BG6NBFpv/JI8F+hwp+IHdkC1gUXRrLJNC79ERqFP8HoqdQWNw + OGGFaOe2aQhvj2zt8wFncyKVc40UKVbSzGGzdL2MPiAJHgZ2lmeY1xDyX1lOt12R + IFPwtxmbxaxYaVfe2hxl7m88xV3OjYcKgwVYDusk2XJ37cGew5g+NbBvzEeEUpF9 + 5wIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAD7UPC3/Nkgpn53mT9puUonYdJg9 + SD8vvTK/N78CzoEgPNyq+bYbqlcvVPKIdItf9TMiqfOSvW3e3NvkRisYle8Qp+0C + 8pafXBvQ9eHt5CFeJn4sH9GnxeflOZT/P9Jnp71KtZQvOobirX4GgEWs79g+/NHb + Zyf8rbadt9HruNhKA5nlP8cn7Rdc/iuJU8MVSQszI1s1DEcXMPxr6iqb2g87/ifH + lWcK59kvRJkCcPhPzjpUy9NulucH4WFA/WqKeDNFS/oC+upV5w8EDEcfnenJFG+N + JmFDQESSfUxLPHLC660Wnf3GmrP/duZHpPC+qTe8b1AlQ7zDT3cOaAQ+Mb0= + -----END CERTIFICATE----- + chain: + type: string + description: TLS/SSL intermediate chain (if provided, else empty string). + example: | + -----BEGIN CERTIFICATE----- + MIICaDCCAVACAQAwIzELMAkGA1UEBhMCQlMxFDASBgNVBAMMC2V4YW1wbGUuY29t + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3K6dwLM2Nk8kVhIBaZmp + eY6y7O0T3jrexEKlW839TVYdcH+K35V1NxilbMFKMuHeowGwFyyiqOy/OUYNeq+T + Rz3s4b1qG2p01dwlsXHHYmXLYTAhvqvY+CU5ksieuZbyHRTwbHViQ0xtRXwoVCnj + CkN7kJVpkLfVN0/BG6NBFpv/JI8F+hwp+IHdkC1gUXRrLJNC79ERqFP8HoqdQWNw + OGGFaOe2aQhvj2zt8wFncyKVc40UKVbSzGGzdL2MPiAJHgZ2lmeY1xDyX1lOt12R + IFPwtxmbxaxYaVfe2hxl7m88xV3OjYcKgwVYDusk2XJ37cGew5g+NbBvzEeEUpF9 + 5wIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAD7UPC3/Nkgpn53mT9puUonYdJg9 + SD8vvTK/N78CzoEgPNyq+bYbqlcvVPKIdItf9TMiqfOSvW3e3NvkRisYle8Qp+0C + 8pafXBvQ9eHt5CFeJn4sH9GnxeflOZT/P9Jnp71KtZQvOobirX4GgEWs79g+/NHb + Zyf8rbadt9HruNhKA5nlP8cn7Rdc/iuJU8MVSQszI1s1DEcXMPxr6iqb2g87/ifH + lWcK59kvRJkCcPhPzjpUy9NulucH4WFA/WqKeDNFS/oC+upV5w8EDEcfnenJFG+N + JmFDQESSfUxLPHLC660Wnf3GmrP/duZHpPC+qTe8b1AlQ7zDT3cOaAQ+Mb0= + -----END CERTIFICATE----- + description: Install certificate request. `chain` can be an empty string. + SSLCertificateInstallResponse: + type: string + example: OK + description: Install certificate response. + SSLCertificatesProvisionResponse: + type: object + required: + - requests + properties: + requests: + type: array + items: + type: object + required: + - log + - result + - domains + properties: + log: + type: array + items: + type: string + example: + - 'The domain name does not resolve to this machine: [Not Set] (A), [Not Set] (AAAA).' + result: + type: string + enum: + - installed + - error + - skipped + example: installed + domains: + type: array + items: + $ref: '#/components/schemas/Hostname' + description: SSL certificates provision response. + SystemPrivacyStatusResponse: + type: boolean + description: | + System privacy status response. + + - `true`: Private, new-version checks will not be performed + - `false`: Not private, new-version checks will be performed + example: false + SystemVersionResponse: + type: string + description: System version response. + example: v0.46 + SystemVersionUpstreamResponse: + type: string + description: System version upstream response. + example: v0.47 + SystemUpdatesResponse: + type: string + description: System updates response. + example: | + libgnutls30 (3.5.18-1ubuntu1.4) + libxau6 (1:1.0.8-1ubuntu1) + SystemUpdatePackagesResponse: + type: string + example: | + Reading package lists... + Building dependency tree... + Reading state information... + Calculating upgrade... + The following packages will be upgraded: + cloud-init grub-common grub-pc grub-pc-bin grub2-common libgnutls30 + libldap-2.4-2 libldap-common libxau6 linux-firmware python3-distupgrade + qemu-guest-agent sosreport ubuntu-release-upgrader-core + 14 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. + Need to get 79.9 MB of archives. + After this operation, 3893 kB of additional disk space will be used. + Get:1 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libgnutls30 amd64 3.5.18-1ubuntu1.4 [645 kB] + Preconfiguring packages ... + Fetched 79.9 MB in 2s (52.4 MB/s) + (Reading database ... 48457 files and directories currently installed.) + description: System update packages response. + SystemPrivacyUpdateResponse: + type: string + example: OK + description: System privacy update response. + SystemRebootStatusResponse: + type: boolean + description: | + System reboot status response. + + - `true`: A reboot is required + - `false`: A reboot is not required + example: true + SystemRebootResponse: + type: string + example: No reboot is required, so it is not allowed. + description: System reboot response. + SystemStatusResponse: + type: array + items: + $ref: '#/components/schemas/StatusEntry' + description: System status response. + StatusEntry: + type: object + required: + - type + - text + - extra + properties: + type: + $ref: '#/components/schemas/StatusEntryType' + text: + type: string + example: This domain"s DNSSEC DS record is not set + extra: + type: array + items: + $ref: '#/components/schemas/StatusEntryExtra' + description: System status entry. + StatusEntryType: + type: string + enum: + - heading + - ok + - warning + - error + example: warning + description: System status entry type. + StatusEntryExtra: + type: object + required: + - monospace + - text + properties: + monospace: + type: boolean + example: false + text: + type: string + example: 'Digest Type: 2 / SHA-256' + description: System entry extra information. + SystemBackupConfigUpdateRequest: + type: object + required: + - target + - target_user + - target_pass + - min_age + properties: + target: + type: string + format: hostname + example: s3://s3.eu-central-1.amazonaws.com/box-example-com + target_user: + type: string + example: username + target_pass: + type: string + example: password + format: password + min_age: + type: integer + format: int32 + minimum: 1 + example: 3 + description: Backup config update request. + SystemBackupConfigUpdateResponse: + type: string + example: OK + description: Backup config update response. + SystemBackupConfigResponse: + type: object + required: + - enc_pw_file + - file_target_directory + - min_age_in_days + - ssh_pub_key + - target + properties: + enc_pw_file: + type: string + example: /home/user-data/backup/secret_key.txt + file_target_directory: + type: string + example: /home/user-data/backup/encrypted + min_age_in_days: + type: integer + format: int32 + minimum: 1 + example: 3 + ssh_pub_key: + type: string + example: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDb root@box.example.com\n + target: + type: string + format: hostname + example: s3://s3.eu-central-1.amazonaws.com/box-example-com + target_user: + type: string + target_pass: + type: string + description: Backup config response. + SystemBackupStatusResponse: + type: object + required: + - unmatched_file_size + properties: + backups: + type: array + items: + $ref: '#/components/schemas/SystemBackupStatus' + unmatched_file_size: + type: integer + format: int32 + example: 0 + error: + type: string + example: Something is wrong with the backup + description: Backup status response. Lists the status for all backups. + SystemBackupStatus: + type: object + required: + - date + - date_delta + - date_str + - full + - size + - volumes + properties: + date: + type: string + format: date-time + example: 20200801T023706Z + date_delta: + type: string + example: 15 hours, 40 minutes + date_str: + type: string + example: 2020-08-01 03:37:06 BST + deleted_in: + type: string + example: approx. 6 days + full: + type: boolean + example: false + size: + type: integer + format: int32 + example: 125332 + volumes: + type: integer + format: int32 + example: 1 + description: Backup status details. + SSLStatusResponse: + type: object + required: + - can_provision + - status + properties: + can_provision: + type: array + items: + type: string + status: + type: array + items: + $ref: '#/components/schemas/SSLStatus' + description: SSL status response for all relevant domains. + SSLStatus: + type: object + required: + - domain + - status + - text + properties: + domain: + $ref: '#/components/schemas/Hostname' + status: + $ref: '#/components/schemas/SSLStatusType' + text: + type: string + example: Signed & valid. The certificate expires in 87 days on 10/28/20. + description: SSL status details for domain. + SSLStatusType: + type: string + enum: + - success + - danger + - not-applicable + example: success + description: SSL status type. + Email: + type: string + format: email + example: user@example.com + description: Email format. + Hostname: + type: string + format: hostname + example: example.com + description: Hostname format. + MeResponse: + type: object + required: + - status + properties: + api_key: + type: string + example: 12345abcde + email: + $ref: '#/components/schemas/Email' + privileges: + type: array + items: + $ref: '#/components/schemas/MailUserPrivilege' + reason: + type: string + example: Incorrect username or password + status: + $ref: '#/components/schemas/MeAuthStatus' + description: Me (user) response. + MeAuthStatus: + type: string + enum: + - ok + - invalid + example: invalid + description: Me (user) authentication result. + WebDomain: + type: object + required: + - custom_root + - domain + - root + - ssl_certificate + - static_enabled + properties: + custom_root: + type: string + example: /home/user-data/www/example.com + domain: + $ref: '#/components/schemas/Hostname' + root: + type: string + example: /home/user-data/www/default + ssl_certificate: + type: array + minItems: 2 + maxItems: 2 + uniqueItems: true + items: + oneOf: + - type: string + example: No certificate installed. + - type: string + enum: + - danger + - success + example: danger + static_enabled: + type: boolean + example: true + description: Web domain details. + WebUpdateResponse: + type: string + example: web updated + description: Web update response. From 891de8d6c3f35eb85d6c396c97c0d9f7d08435b8 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Wed, 26 Aug 2020 14:10:04 -0400 Subject: [PATCH 144/190] Upgrade Roundcube to 1.4.8 Merges #1809 --- CHANGELOG.md | 7 +++++++ setup/webmail.sh | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9b8b759..691eb228 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ CHANGELOG ========= +In Development +-------------- + +Security fixes: + +* Roundcube is updated to version 1.4.8 fixing additional cross-site scripting (XSS) vulnerabilities. + v0.47 (July 29, 2020) --------------------- diff --git a/setup/webmail.sh b/setup/webmail.sh index f2202244..1e7d0083 100755 --- a/setup/webmail.sh +++ b/setup/webmail.sh @@ -28,8 +28,8 @@ apt_install \ # Install Roundcube from source if it is not already present or if it is out of date. # Combine the Roundcube version number with the commit hash of plugins to track # whether we have the latest version of everything. -VERSION=1.4.7 -HASH=49F194D25AC7B9BF175BD52285BB61CDE7BAED44 +VERSION=1.4.8 +HASH=3a6824fd68fef2e0d24f186cfbee5c6f9d6edbe9 PERSISTENT_LOGIN_VERSION=6b3fc450cae23ccb2f393d0ef67aa319e877e435 HTML5_NOTIFIER_VERSION=4b370e3cd60dabd2f428a26f45b677ad1b7118d5 CARDDAV_VERSION=3.0.3 From 62db58eaafe5bba7a8575a6c1a0ef1a4423a4610 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Wed, 26 Aug 2020 14:11:01 -0400 Subject: [PATCH 145/190] v0.48 --- CHANGELOG.md | 4 ++-- README.md | 4 ++-- setup/bootstrap.sh | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 691eb228..38fb419b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ CHANGELOG ========= -In Development --------------- +v0.48 (August 26, 2020) +----------------------- Security fixes: diff --git a/README.md b/README.md index 5ef58a29..d0caabd8 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ by him: $ curl -s https://keybase.io/joshdata/key.asc | gpg --import gpg: key C10BDD81: public key "Joshua Tauberer " imported - $ git verify-tag v0.47 + $ git verify-tag v0.48 gpg: Signature made ..... using RSA key ID C10BDD81 gpg: Good signature from "Joshua Tauberer " gpg: WARNING: This key is not certified with a trusted signature! @@ -71,7 +71,7 @@ and on his [personal homepage](https://razor.occams.info/). (Of course, if this Checkout the tag corresponding to the most recent release: - $ git checkout v0.47 + $ git checkout v0.48 Begin the installation. diff --git a/setup/bootstrap.sh b/setup/bootstrap.sh index 098de977..debe572b 100644 --- a/setup/bootstrap.sh +++ b/setup/bootstrap.sh @@ -20,7 +20,7 @@ if [ -z "$TAG" ]; then # want to display in status checks. if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/18\.04\.[0-9]/18.04/' `" == "Ubuntu 18.04 LTS" ]; then # This machine is running Ubuntu 18.04. - TAG=v0.47 + TAG=v0.48 elif [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" == "Ubuntu 14.04 LTS" ]; then # This machine is running Ubuntu 14.04. From a7a66929aac237777c5916eee9c0f86eeac53735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sp=C3=B6ttel?= <1682504+fspoettel@users.noreply.github.com> Date: Wed, 2 Sep 2020 16:48:23 +0200 Subject: [PATCH 146/190] add user interface for managing 2fa * update user schema with 2fa columns --- management/daemon.py | 55 +++++- management/mailconfig.py | 40 ++++ management/templates/index.html | 7 +- management/templates/two-factor-auth.html | 220 ++++++++++++++++++++++ management/totp.py | 51 +++++ setup/mail-users.sh | 3 +- setup/management.sh | 1 + 7 files changed, 370 insertions(+), 7 deletions(-) create mode 100644 management/templates/two-factor-auth.html create mode 100644 management/totp.py diff --git a/management/daemon.py b/management/daemon.py index b7bf2a66..ebf112f6 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -1,14 +1,15 @@ import os, os.path, re, json, time -import subprocess +import multiprocessing.pool, subprocess from functools import wraps from flask import Flask, request, render_template, abort, Response, send_from_directory, make_response -import auth, utils, multiprocessing.pool +import auth, utils, totp from mailconfig import get_mail_users, get_mail_users_ex, get_admins, add_mail_user, set_mail_password, remove_mail_user from mailconfig import get_mail_user_privileges, add_remove_mail_user_privilege from mailconfig import get_mail_aliases, get_mail_aliases_ex, get_mail_domains, add_mail_alias, remove_mail_alias +from mailconfig import get_two_factor_info, set_two_factor_secret, remove_two_factor_secret env = utils.load_environment() @@ -83,8 +84,8 @@ def authorized_personnel_only(viewfunc): def unauthorized(error): return auth_service.make_unauthorized_response() -def json_response(data): - return Response(json.dumps(data, indent=2, sort_keys=True)+'\n', status=200, mimetype='application/json') +def json_response(data, status=200): + return Response(json.dumps(data, indent=2, sort_keys=True)+'\n', status=status, mimetype='application/json') ################################### @@ -334,7 +335,7 @@ def ssl_get_status(): # What domains can we provision certificates for? What unexpected problems do we have? provision, cant_provision = get_certificates_to_provision(env, show_valid_certs=False) - + # What's the current status of TLS certificates on all of the domain? domains_status = get_web_domains_info(env) domains_status = [ @@ -383,6 +384,50 @@ def ssl_provision_certs(): requests = provision_certificates(env, limit_domains=None) return json_response({ "requests": requests }) +# Two Factor Auth + +@app.route('/two-factor-auth/status', methods=['GET']) +@authorized_personnel_only +def two_factor_auth_get_status(): + email, privs = auth_service.authenticate(request, env) + two_factor_secret, two_factor_token = get_two_factor_info(email, env) + + if two_factor_secret != None: + return json_response({ 'status': 'on' }) + + secret = totp.get_secret() + secret_url = totp.get_otp_uri(secret, email) + secret_qr = totp.get_qr_code(secret_url) + + return json_response({ + "status": 'off', + "secret": secret, + "qr_code": secret_qr + }) + +@app.route('/two-factor-auth/setup', methods=['POST']) +@authorized_personnel_only +def two_factor_auth_post_setup(): + email, privs = auth_service.authenticate(request, env) + + secret = request.form.get('secret') + token = request.form.get('token') + + if type(secret) != str or type(token) != str or len(token) != 6 or len(secret) != 32: + return json_response({ "error": 'bad_input' }, 400) + + if (totp.validate(secret, token)): + set_two_factor_secret(email, secret, token, env) + return json_response({}) + + return json_response({ "error": 'token_mismatch' }, 400) + +@app.route('/two-factor-auth/disable', methods=['POST']) +@authorized_personnel_only +def two_factor_auth_post_disable(): + email, privs = auth_service.authenticate(request, env) + remove_two_factor_secret(email, env) + return json_response({}) # WEB diff --git a/management/mailconfig.py b/management/mailconfig.py index b061ea7d..3bc48897 100755 --- a/management/mailconfig.py +++ b/management/mailconfig.py @@ -547,6 +547,41 @@ def get_required_aliases(env): return aliases +def get_two_factor_info(email, env): + c = open_database(env) + + c.execute('SELECT two_factor_secret, two_factor_last_used_token FROM users WHERE email=?', (email,)) + rows = c.fetchall() + if len(rows) != 1: + raise ValueError("That's not a user (%s)." % email) + return (rows[0][0], rows[0][1]) + +def set_two_factor_secret(email, secret, token, env): + validate_two_factor_secret(secret) + + conn, c = open_database(env, with_connection=True) + c.execute("UPDATE users SET two_factor_secret=?, two_factor_last_used_token=? WHERE email=?", (secret, token, email)) + if c.rowcount != 1: + raise ValueError("That's not a user (%s)." % email) + conn.commit() + return "OK" + +def set_two_factor_last_used_token(email, token, env): + conn, c = open_database(env, with_connection=True) + c.execute("UPDATE users SET two_factor_last_used_token=? WHERE email=?", (token, email)) + if c.rowcount != 1: + raise ValueError("That's not a user (%s)." % email) + conn.commit() + return "OK" + +def remove_two_factor_secret(email, env): + conn, c = open_database(env, with_connection=True) + c.execute("UPDATE users SET two_factor_secret=null, two_factor_last_used_token=null WHERE email=?", (email,)) + if c.rowcount != 1: + raise ValueError("That's not a user (%s)." % email) + conn.commit() + return "OK" + def kick(env, mail_result=None): results = [] @@ -608,6 +643,11 @@ def validate_password(pw): if len(pw) < 8: raise ValueError("Passwords must be at least eight characters.") +def validate_two_factor_secret(secret): + if type(secret) != str or secret.strip() == "": + raise ValueError("No secret provided.") + if len(secret) != 32: + raise ValueError("Secret should be a 32 characters base32 string") if __name__ == "__main__": import sys diff --git a/management/templates/index.html b/management/templates/index.html index 2c0d5a9a..3088ef63 100644 --- a/management/templates/index.html +++ b/management/templates/index.html @@ -93,6 +93,7 @@
  • Custom DNS
  • External DNS
  • +
  • Two Factor Authentication
  • Munin Monitoring
@@ -131,7 +132,11 @@ {% include "custom-dns.html" %} -
+
+ {% include "two-factor-auth.html" %} +
+ +
{% include "login.html" %}
diff --git a/management/templates/two-factor-auth.html b/management/templates/two-factor-auth.html new file mode 100644 index 00000000..9f1a8b5a --- /dev/null +++ b/management/templates/two-factor-auth.html @@ -0,0 +1,220 @@ + + +

Two Factor Authentication

+ +
+
Loading...
+ + +
+

Setup

+

After enabling two factor authentication, any login to the admin panel will require you to enter a time-limited 6-digit number from an authenticator app after entering your normal credentials.

+

1. Scan the QR code or enter the secret into an authenticator app (e.g. Google Authenticator)

+
+
+ +
+ + +
+ + + +
+ +
+ + +
+
+

Two factor authentication is active.

+
+ + +
+ +
+
+
+
+ + diff --git a/management/totp.py b/management/totp.py new file mode 100644 index 00000000..52cdc256 --- /dev/null +++ b/management/totp.py @@ -0,0 +1,51 @@ +import base64 +import hmac +import io +import os +import struct +import time +from urllib.parse import quote +import qrcode + +def get_secret(): + return base64.b32encode(os.urandom(20)).decode('utf-8') + +def get_otp_uri(secret, email): + site_name = 'mailinabox' + + return 'otpauth://totp/{}:{}?secret={}&issuer={}'.format( + quote(site_name), + quote(email), + secret, + quote(site_name) + ) + +def get_qr_code(data): + qr = qrcode.make(data) + byte_arr = io.BytesIO() + qr.save(byte_arr, format='PNG') + + encoded = base64.b64encode(byte_arr.getvalue()).decode('utf-8') + return 'data:image/png;base64,{}'.format(encoded) + +def validate(secret, token): + """ + @see https://tools.ietf.org/html/rfc6238#section-4 + @see https://tools.ietf.org/html/rfc4226#section-5.4 + @see https://git.sr.ht/~sircmpwn/meta.sr.ht/tree/master/metasrht/totp.py + @see https://github.com/susam/mintotp/blob/master/mintotp.py + TODO: resynchronisation + """ + key = base64.b32decode(secret) + tm = int(time.time() / 30) + digits = 6 + + step = 0 + counter = struct.pack('>Q', tm + step) + + hm = hmac.HMAC(key, counter, 'sha1').digest() + offset = hm[-1] &0x0F + binary = struct.unpack(">L", hm[offset:offset + 4])[0] & 0x7fffffff + + code = str(binary)[-digits:].rjust(digits, '0') + return token == code diff --git a/setup/mail-users.sh b/setup/mail-users.sh index e54485bb..3047489b 100755 --- a/setup/mail-users.sh +++ b/setup/mail-users.sh @@ -20,7 +20,8 @@ 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; + # TODO: Add migration + echo "CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT NOT NULL UNIQUE, password TEXT NOT NULL, extra, privileges TEXT NOT NULL DEFAULT '', two_factor_secret TEXT, two_factor_last_used_token TEXT);" | 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; fi diff --git a/setup/management.sh b/setup/management.sh index 4b398aa2..ce78b171 100755 --- a/setup/management.sh +++ b/setup/management.sh @@ -50,6 +50,7 @@ hide_output $venv/bin/pip install --upgrade pip hide_output $venv/bin/pip install --upgrade \ rtyaml "email_validator>=1.0.0" "exclusiveprocess" \ flask dnspython python-dateutil \ + qrcode[pil] \ "idna>=2.0.0" "cryptography==2.2.2" boto psutil postfix-mta-sts-resolver # CONFIGURATION From 3c3683429b0774bad9210829cbe0caf43d3dc14d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sp=C3=B6ttel?= <1682504+fspoettel@users.noreply.github.com> Date: Wed, 2 Sep 2020 17:23:32 +0200 Subject: [PATCH 147/190] implement two factor check during login --- management/auth.py | 48 +++++++++++++++++++--- management/daemon.py | 34 ++++++++++++++-- management/templates/index.html | 4 +- management/templates/login.html | 70 ++++++++++++++++++++++++++------- 4 files changed, 130 insertions(+), 26 deletions(-) diff --git a/management/auth.py b/management/auth.py index 55f59664..83d9c1d6 100644 --- a/management/auth.py +++ b/management/auth.py @@ -2,12 +2,19 @@ import base64, os, os.path, hmac from flask import make_response -import utils +import utils, totp from mailconfig import get_mail_password, get_mail_user_privileges +from mailconfig import get_two_factor_info, set_two_factor_last_used_token DEFAULT_KEY_PATH = '/var/lib/mailinabox/api.key' DEFAULT_AUTH_REALM = 'Mail-in-a-Box Management Server' +class MissingTokenError(ValueError): + pass + +class BadTokenError(ValueError): + pass + class KeyAuthService: """Generate an API key for authenticating clients @@ -76,23 +83,52 @@ class KeyAuthService: return (None, ["admin"]) else: # The user is trying to log in with a username and user-specific - # API key or password. Raises or returns privs. - return (username, self.get_user_credentials(username, password, env)) + # API key or password. Raises or returns privs and an indicator + # whether the user is using their password or a user-specific API-key. + privs, is_user_key = self.get_user_credentials(username, password, env) + + # If the user is using their API key to login, 2FA has been passed before + if is_user_key: + return (username, privs) + + secret, last_token = get_two_factor_info(username, env) + + # 2FA is not enabled, we can skip further checks + if secret == "" or secret == None: + return (username, privs) + + # If 2FA is enabled, raise if: + # 1. no token is provided via `x-auth-token` + # 2. a previously supplied token is used (to counter replay attacks) + # 3. the token is invalid + # in that case, we need to raise and indicate to the client to supply a TOTP + token_header = request.headers.get('x-auth-token') + if token_header == None or token_header == "": + raise MissingTokenError("Two factor code missing (no x-auth-token supplied)") + + # TODO: Should a token replay be handled as its own error? + if token_header == last_token or totp.validate(secret, token_header) != True: + raise BadTokenError("Two factor code incorrect") + + set_two_factor_last_used_token(username, token_header, env) + return (username, privs) def get_user_credentials(self, email, pw, env): # Validate a user's credentials. On success returns a list of # privileges (e.g. [] or ['admin']). On failure raises a ValueError - # with a login error message. + # with a login error message. # Sanity check. if email == "" or pw == "": raise ValueError("Enter an email address and password.") + is_user_key = False + # The password might be a user-specific API key. create_user_key raises # a ValueError if the user does not exist. if hmac.compare_digest(self.create_user_key(email, env), pw): # OK. - pass + is_user_key = True else: # Get the hashed password of the user. Raise a ValueError if the # email address does not correspond to a user. @@ -119,7 +155,7 @@ class KeyAuthService: if isinstance(privs, tuple): raise ValueError(privs[0]) # Return a list of privileges. - return privs + return (privs, is_user_key) def create_user_key(self, email, env): # Store an HMAC with the client. The hashed message of the HMAC will be the user's diff --git a/management/daemon.py b/management/daemon.py index ebf112f6..b80b1e73 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -40,14 +40,23 @@ def authorized_personnel_only(viewfunc): error = None try: email, privs = auth_service.authenticate(request, env) + except auth.MissingTokenError as e: + privs = [] + error = str(e) + except auth.BadTokenError as e: + # Write a line in the log recording the failed login + log_failed_login(request) + + privs = [] + error = str(e) except ValueError as e: + # Write a line in the log recording the failed login + log_failed_login(request) + # Authentication failed. privs = [] error = "Incorrect username or password" - # Write a line in the log recording the failed login - log_failed_login(request) - # Authorized to access an API view? if "admin" in privs: # Call view func. @@ -119,6 +128,23 @@ def me(): # Is the caller authorized? try: email, privs = auth_service.authenticate(request, env) + except auth.MissingTokenError as e: + # Log the failed login + log_failed_login(request) + + return json_response({ + "status": "missing_token", + "reason": str(e), + }) + except auth.BadTokenError as e: + # Log the failed login + log_failed_login(request) + + return json_response({ + "status": "bad_token", + "reason": str(e), + }) + except ValueError as e: # Log the failed login log_failed_login(request) @@ -126,7 +152,7 @@ def me(): return json_response({ "status": "invalid", "reason": "Incorrect username or password", - }) + }) resp = { "status": "ok", diff --git a/management/templates/index.html b/management/templates/index.html index 3088ef63..b0d86dd3 100644 --- a/management/templates/index.html +++ b/management/templates/index.html @@ -297,7 +297,7 @@ function ajax_with_indicator(options) { } var api_credentials = ["", ""]; -function api(url, method, data, callback, callback_error) { +function api(url, method, data, callback, callback_error, headers) { // from http://www.webtoolkit.info/javascript-base64.html function base64encode(input) { _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; @@ -335,7 +335,7 @@ function api(url, method, data, callback, callback_error) { method: method, cache: false, data: data, - + headers: headers, // the custom DNS api sends raw POST/PUT bodies --- prevent URL-encoding processData: typeof data != "string", mimeType: typeof data == "string" ? "text/plain; charset=ascii" : null, diff --git a/management/templates/login.html b/management/templates/login.html index b6e74df6..0322dd5f 100644 --- a/management/templates/login.html +++ b/management/templates/login.html @@ -1,4 +1,29 @@ -

{{hostname}}

+ + +

{{hostname}}

{% if no_users_exist or no_admins_exist %}
@@ -20,10 +45,10 @@ sudo tools/mail.py user make-admin me@{{hostname}}
{% endif %} -

Log in here for your Mail-in-a-Box control panel.

+

Log in here for your Mail-in-a-Box control panel.

-
-
+ +
+ +
+ +
+
@@ -53,7 +84,6 @@ sudo tools/mail.py user make-admin me@{{hostname}}
- From f66e609d3fb00f2e3c0ef8185f16975dd181b665 Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Thu, 26 Nov 2020 11:56:04 +0000 Subject: [PATCH 188/190] Api spec cleanup (#1869) * Fix indentation * Add parameter definition and remove unused model * Update version * Quote example string --- api/mailinabox.yml | 66 +++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/api/mailinabox.yml b/api/mailinabox.yml index 6358afb4..14cf54de 100644 --- a/api/mailinabox.yml +++ b/api/mailinabox.yml @@ -15,7 +15,7 @@ info: license: name: CC0 1.0 Universal url: https://creativecommons.org/publicdomain/zero/1.0/legalcode - version: 0.47.0 + version: 0.51.0 x-logo: url: https://mailinabox.email/static/logo.png altText: Mail-in-a-Box logo @@ -744,30 +744,37 @@ paths: schema: type: string /dns/zonefile/{zone}: - get: - tags: - - DNS - summary: Get DNS zonefile - description: Returns an array of all managed top-level domains. - operationId: getDnsZonefile - x-codeSamples: - - lang: curl - source: | - curl -X GET "https://{host}/admin/dns/zonefile/" \ - -u ":" - responses: - 200: - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/DNSZonefileResponse' - 403: - description: Forbidden - content: - text/html: - schema: - type: string + parameters: + - in: path + name: zone + schema: + $ref: '#/components/schemas/Hostname' + required: true + description: Hostname + get: + tags: + - DNS + summary: Get DNS zonefile + description: Returns a DNS zone file for a hostname. + operationId: getDnsZonefile + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/dns/zonefile/" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/DNSZonefileResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string /dns/update: post: tags: @@ -1806,7 +1813,7 @@ components: text/plain: schema: type: string - example: 1.2.3.4 + example: '1.2.3.4' description: The value of the DNS record. example: '1.2.3.4' schemas: @@ -2690,13 +2697,6 @@ components: type: string MfaEnableSuccessResponse: type: string - MfaEnableBadRequestResponse: - type: object - required: - - error - properties: - error: - type: string MfaDisableRequest: type: object properties: From 82229ce04baef6aeabd74a2c88e414b51236884d Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Sat, 21 Nov 2020 08:09:37 -0500 Subject: [PATCH 189/190] Document how to start the control panel from the command line and in debugging use a stable API key --- management/daemon.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/management/daemon.py b/management/daemon.py index 3c19367b..a0cfefa6 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -1,4 +1,11 @@ #!/usr/local/lib/mailinabox/env/bin/python3 +# +# During development, you can start the Mail-in-a-Box control panel +# by running this script, e.g.: +# +# service mailinabox stop # stop the system process +# DEBUG=1 management/daemon.py +# service mailinabox start # when done debugging, start it up again import os, os.path, re, json, time import multiprocessing.pool, subprocess @@ -680,7 +687,22 @@ def log_failed_login(request): # APP if __name__ == '__main__': - if "DEBUG" in os.environ: app.debug = True + if "DEBUG" in os.environ: + # Turn on Flask debugging. + app.debug = True + + # Use a stable-ish master API key so that login sessions don't restart on each run. + # Use /etc/machine-id to seed the key with a stable secret, but add something + # and hash it to prevent possibly exposing the machine id, using the time so that + # the key is not valid indefinitely. + import hashlib + with open("/etc/machine-id") as f: + api_key = f.read() + api_key += "|" + str(int(time.time() / (60*60*2))) + hasher = hashlib.sha1() + hasher.update(api_key.encode("ascii")) + auth_service.key = hasher.hexdigest() + if "APIKEY" in os.environ: auth_service.key = os.environ["APIKEY"] if not app.debug: From 8664afa99798c9dbd7b52cf67da7e90b3280bbf0 Mon Sep 17 00:00:00 2001 From: Hilko Date: Thu, 26 Nov 2020 13:13:31 +0100 Subject: [PATCH 190/190] Implement Backblaze for Backup (#1812) * Installing b2sdk for b2 support * Added Duplicity PPA so the most recent version is used * Implemented list_target_files for b2 * Implemented b2 in frontend * removed python2 boto package --- management/backup.py | 17 +++++++++ management/templates/system-backup.html | 46 +++++++++++++++++++++++-- setup/management.sh | 16 ++++----- setup/system.sh | 3 ++ 4 files changed, 71 insertions(+), 11 deletions(-) diff --git a/management/backup.py b/management/backup.py index e1651552..0a8a021e 100755 --- a/management/backup.py +++ b/management/backup.py @@ -456,6 +456,23 @@ def list_target_files(config): raise ValueError(e.reason) return [(key.name[len(path):], key.size) for key in bucket.list(prefix=path)] + elif target.scheme == 'b2': + from b2sdk.v1 import InMemoryAccountInfo, B2Api + from b2sdk.v1.exception import NonExistentBucket + info = InMemoryAccountInfo() + b2_api = B2Api(info) + + # Extract information from target + b2_application_keyid = target.netloc[:target.netloc.index(':')] + b2_application_key = target.netloc[target.netloc.index(':')+1:target.netloc.index('@')] + b2_bucket = target.netloc[target.netloc.index('@')+1:] + + try: + b2_api.authorize_account("production", b2_application_keyid, b2_application_key) + bucket = b2_api.get_bucket_by_name(b2_bucket) + except NonExistentBucket as e: + raise ValueError("B2 Bucket does not exist. Please double check your information!") + return [(key.file_name, key.size) for key, _ in bucket.ls()] else: raise ValueError(config["target"]) diff --git a/management/templates/system-backup.html b/management/templates/system-backup.html index 6afe62c8..7cdc3803 100644 --- a/management/templates/system-backup.html +++ b/management/templates/system-backup.html @@ -18,6 +18,7 @@ +
@@ -111,6 +112,31 @@
+ +
+
+

Backups are stored in a Backblaze B2 bucket. You must have a Backblaze account already.

+

You MUST manually copy the encryption password from to a safe and secure location. You will need this file to decrypt backup files. It is NOT stored in your Backblaze B2 bucket.

+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
@@ -144,7 +170,7 @@ function toggle_form() { var target_type = $("#backup-target-type").val(); - $(".backup-target-local, .backup-target-rsync, .backup-target-s3").hide(); + $(".backup-target-local, .backup-target-rsync, .backup-target-s3, .backup-target-b2").hide(); $(".backup-target-" + target_type).show(); init_inputs(target_type); @@ -215,7 +241,7 @@ function show_system_backup() { } function show_custom_backup() { - $(".backup-target-local, .backup-target-rsync, .backup-target-s3").hide(); + $(".backup-target-local, .backup-target-rsync, .backup-target-s3, .backup-target-b2").hide(); api( "/system/backup/config", "GET", @@ -245,6 +271,15 @@ function show_custom_backup() { var host = hostpath.shift(); $("#backup-target-s3-host").val(host); $("#backup-target-s3-path").val(hostpath.join('/')); + } else if (r.target.substring(0, 5) == "b2://") { + $("#backup-target-type").val("b2"); + var targetPath = r.target.substring(5); + var b2_application_keyid = targetPath.split(':')[0]; + var b2_applicationkey = targetPath.split(':')[1].split('@')[0]; + var b2_bucket = targetPath.split('@')[1]; + $("#backup-target-b2-user").val(b2_application_keyid); + $("#backup-target-b2-pass").val(b2_applicationkey); + $("#backup-target-b2-bucket").val(b2_bucket); } toggle_form() }) @@ -264,6 +299,11 @@ function set_custom_backup() { target = "rsync://" + $("#backup-target-rsync-user").val() + "@" + $("#backup-target-rsync-host").val() + "/" + $("#backup-target-rsync-path").val(); target_user = ''; + } else if (target_type == "b2") { + target = 'b2://' + $('#backup-target-b2-user').val() + ':' + $('#backup-target-b2-pass').val() + + '@' + $('#backup-target-b2-bucket').val() + target_user = ''; + target_pass = ''; } @@ -303,4 +343,4 @@ function init_inputs(target_type) { set_host($('#backup-target-s3-host-select').val()); } } - + \ No newline at end of file diff --git a/setup/management.sh b/setup/management.sh index ae942985..f5685d4f 100755 --- a/setup/management.sh +++ b/setup/management.sh @@ -18,11 +18,7 @@ while [ -d /usr/local/lib/python3.4/dist-packages/acme ]; do pip3 uninstall -y acme; done -# duplicity is used to make backups of user data. It uses boto -# (via Python 2) to do backups to AWS S3. boto from the Ubuntu -# package manager is too out-of-date -- it doesn't support the newer -# S3 api used in some regions, which breaks backups to those regions. -# See #627, #653. +# duplicity is used to make backups of user data. # # virtualenv is used to isolate the Python 3 packages we # install via pip from the system-installed packages. @@ -30,7 +26,11 @@ done # certbot installs EFF's certbot which we use to # provision free TLS certificates. apt_install duplicity python-pip virtualenv certbot -hide_output pip2 install --upgrade boto + +# b2sdk is used for backblaze backups. +# boto is used for amazon aws backups. +# Both are installed outside the pipenv, so they can be used by duplicity +hide_output pip3 install --upgrade b2sdk boto # Create a virtualenv for the installation of Python 3 packages # used by the management daemon. @@ -50,8 +50,8 @@ hide_output $venv/bin/pip install --upgrade pip hide_output $venv/bin/pip install --upgrade \ rtyaml "email_validator>=1.0.0" "exclusiveprocess" \ flask dnspython python-dateutil \ - qrcode[pil] pyotp \ - "idna>=2.0.0" "cryptography==2.2.2" boto psutil postfix-mta-sts-resolver + qrcode[pil] pyotp \ + "idna>=2.0.0" "cryptography==2.2.2" boto psutil postfix-mta-sts-resolver b2sdk # CONFIGURATION diff --git a/setup/system.sh b/setup/system.sh index 4d33deb6..07f4aa1b 100755 --- a/setup/system.sh +++ b/setup/system.sh @@ -93,6 +93,9 @@ hide_output add-apt-repository -y universe # Install the certbot PPA. hide_output add-apt-repository -y ppa:certbot/certbot +# Install the duplicity PPA. +hide_output add-apt-repository -y ppa:duplicity-team/duplicity-release-git + # ### Update Packages # Update system packages to make sure we have the latest upstream versions