Skip to content

Commit

Permalink
merge upstream v71a
Browse files Browse the repository at this point in the history
  • Loading branch information
kiekerjan committed Jan 9, 2025
2 parents cf89659 + e6c354c commit 2c8dcc6
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 41 deletions.
39 changes: 37 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,37 @@
CHANGELOG
=========

Version 71 (January 4, 2025)
----------------------------

(Version 71a was posted on January 6, 2025 and fixes a setup regression.)

Upgrades

* Roundcube upgraded to version 1.6.9.
* Z-Push upgraded to version 2.7.5.

Automated Maintenance

* Daily automated tasks are now run at 1am in the box's timezone and full backups are now restricted to running only on Saturdays and Sundays at that time.
* Backups now exclude the owncloud-backup folder so that we're not backing up backups.
* Old TLS certificates are now automatically deleted to improve control panel performance.

Setup

* Fixed broken setup if SSH was configured to listen on multiple ports.
* Ubuntu MOTD advertisements are now disabled.
* Fixed missing Roundcube dependency package if NextCloud isn't installed.

Control Panel

* Improved status checks for secondary nameservers.
* Spamhaus is now queried for the box's IPv6 address also.
* DSA and EC private keys are now accepted for TLS certificates.
* Timeouts for loading slow control panel pages are reduced.

And other minor fixes.

Version 70 (August 15, 2024)
----------------------------

Expand Down Expand Up @@ -72,6 +103,10 @@ Version 64 (September 2, 2023)
* Fixed setting B2 as a backup target with a slash in the application key.
* Turned off OpenDMARC diagnostic reports sent in response to incoming mail.
* Fixed some crashes when using an unreleased version of Mail-in-a-Box.
<<<<<<< HEAD
=======
* Added z-push administration scripts.
>>>>>>> upstream/main
Version 63 (July 27, 2023)
--------------------------
Expand Down Expand Up @@ -1126,7 +1161,7 @@ Control panel:

System:
* The munin system monitoring tool is now installed and accessible at /admin/munin.
* ownCloud updated to version 8.0.4. The ownCloud installation step now is reslient to download problems. The ownCloud configuration file is now stored in STORAGE_ROOT to fix loss of data when moving STORAGE_ROOT to a new machine.
* ownCloud updated to version 8.0.4. The ownCloud installation step now is resilient to download problems. The ownCloud configuration file is now stored in STORAGE_ROOT to fix loss of data when moving STORAGE_ROOT to a new machine.
* The setup scripts now run `apt-get update` prior to installing anything to ensure the apt database is in sync with the packages actually available.


Expand Down Expand Up @@ -1164,7 +1199,7 @@ DNS:
* Internationalized Domain Names (IDNs) should now work in email. If you had custom DNS or custom web settings for internationalized domains, check that they are still working.
* It is now possible to set multiple TXT and other types of records on the same domain in the control panel.
* The custom DNS API was completely rewritten to support setting multiple records of the same type on a domain. Any existing client code using the DNS API will have to be rewritten. (Existing code will just get 404s back.)
* On some systems the `nsd` service failed to start if network inferfaces were not ready.
* On some systems the `nsd` service failed to start if network interfaces were not ready.

System / Control Panel:

Expand Down
17 changes: 11 additions & 6 deletions management/backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import os, os.path, re, datetime, sys
import dateutil.parser, dateutil.relativedelta, dateutil.tz
from datetime import date
import rtyaml
from exclusiveprocess import Lock

Expand Down Expand Up @@ -160,6 +161,8 @@ def should_force_full(config, env):
# since the last full backup is greater than half the size
# of that full backup.
inc_size = 0
# Check if day of week is a weekend day
weekend = date.today().weekday()>=5
for bak in backup_status(env)["backups"]:
if not bak["full"]:
# Scan through the incremental backups cumulating
Expand All @@ -168,12 +171,14 @@ def should_force_full(config, env):
else:
# ...until we reach the most recent full backup.
# Return if we should to a full backup, which is based
# on the size of the increments relative to the full
# backup, as well as the age of the full backup.
if inc_size > .5*bak["size"]:
return True
if dateutil.parser.parse(bak["date"]) + datetime.timedelta(days=config["min_age_in_days"]*10+1) < datetime.datetime.now(dateutil.tz.tzlocal()):
return True
# on whether it is a weekend day, the size of the
# increments relative to the full backup, as well as
# the age of the full backup.
if weekend:
if inc_size > .5*bak["size"]:
return True
if dateutil.parser.parse(bak["date"]) + datetime.timedelta(days=config["min_age_in_days"]*10+1) < datetime.datetime.now(dateutil.tz.tzlocal()):
return True
return False
else:
# If we got here there are no (full) backups, so make one.
Expand Down
20 changes: 12 additions & 8 deletions management/ssl_certificates.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def get_ssl_certificates(env):
# that the certificates are good for to the best certificate for
# the domain.

from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
from cryptography.hazmat.primitives.asymmetric import dsa, rsa, ec
from cryptography.x509 import Certificate

# The certificates are all stored here:
Expand Down Expand Up @@ -59,13 +59,15 @@ def get_file_list():
# Not a valid PEM format for a PEM type we care about.
continue

# Is it a private key?
if isinstance(pem, RSAPrivateKey):
private_keys[pem.public_key().public_numbers()] = { "filename": fn, "key": pem }

# Is it a certificate?
if isinstance(pem, Certificate):
certificates.append({ "filename": fn, "cert": pem })
# It is a private key
elif (isinstance(pem, rsa.RSAPrivateKey)
or isinstance(pem, dsa.DSAPrivateKey)
or isinstance(pem, ec.EllipticCurvePrivateKey)):
private_keys[pem.public_key().public_numbers()] = { "filename": fn, "key": pem }


# Process the certificates.
domains = { }
Expand Down Expand Up @@ -507,7 +509,7 @@ def check_certificate(domain, ssl_certificate, ssl_private_key, warn_if_expiring
# Check that the ssl_certificate & ssl_private_key files are good
# for the provided domain.

from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
from cryptography.hazmat.primitives.asymmetric import rsa, dsa, ec
from cryptography.x509 import Certificate

# The ssl_certificate file may contain a chain of certificates. We'll
Expand Down Expand Up @@ -541,7 +543,9 @@ def check_certificate(domain, ssl_certificate, ssl_private_key, warn_if_expiring
except ValueError as e:
return (f"The private key file {ssl_private_key} is not a private key file: {e!s}", None)

if not isinstance(priv_key, RSAPrivateKey):
if (not isinstance(priv_key, rsa.RSAPrivateKey)
and not isinstance(priv_key, dsa.DSAPrivateKey)
and not isinstance(priv_key, ec.EllipticCurvePrivateKey)):
return ("The private key file %s is not a private key file." % ssl_private_key, None)

if priv_key.public_key().public_numbers() != cert.public_key().public_numbers():
Expand Down Expand Up @@ -641,7 +645,7 @@ def load_pem(pem):
msg = "File is not a valid PEM-formatted file."
raise ValueError(msg)
pem_type = pem_type.group(1)
if pem_type in {b"RSA PRIVATE KEY", b"PRIVATE KEY"}:
if pem_type.endswith(b"PRIVATE KEY"):
return serialization.load_pem_private_key(pem, password=None, backend=default_backend())
if pem_type == b"CERTIFICATE":
return load_pem_x509_certificate(pem, default_backend())
Expand Down
4 changes: 2 additions & 2 deletions management/status_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,6 @@ def run_network_checks(env, output):
# by a spammer, or the user may be deploying on a residential network. We
# will not be able to reliably send mail in these cases.

# See https://www.spamhaus.org/news/article/807/using-our-public-mirrors-check-your-return-codes-now. for
# information on spamhaus return codes
rev_ip4 = ".".join(reversed(env['PUBLIC_IP'].split('.')))
zen = query_dns(rev_ip4+'.zen.spamhaus.org', 'A', nxdomain=None, retry = False)
evaluate_spamhaus_lookup(env['PUBLIC_IP'], 'IPv4', rev_ip4, output, zen)
Expand All @@ -336,6 +334,8 @@ def run_network_checks(env, output):


def evaluate_spamhaus_lookup(lookupaddress, lookuptype, lookupdomain, output, zen):
# See https://www.spamhaus.org/news/article/807/using-our-public-mirrors-check-your-return-codes-now. for
# information on spamhaus return codes
if zen is None:
output.print_ok(f"{lookuptype} address is not blacklisted by zen.spamhaus.org.")
elif zen == "[timeout]":
Expand Down
3 changes: 1 addition & 2 deletions management/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,8 @@ def get_ssh_port():
return None

def get_ssh_config_value(parameter_name):
import subprocess

# Returns ssh configuration value for the provided parameter
import subprocess
try:
output = shell('check_output', ['sshd', '-T'])
except FileNotFoundError:
Expand Down
2 changes: 1 addition & 1 deletion setup/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ if [ -z "$TAG" ]; then
if [ "$UBUNTU_VERSION" == "Ubuntu 22.04 LTS" ]; then
# This machine is running Ubuntu 22.04, which is supported by
# Mail-in-a-Box versions 60 and later.
TAG=v70
TAG=v71a
elif [ "$UBUNTU_VERSION" == "Ubuntu 18.04 LTS" ]; then
# This machine is running Ubuntu 18.04, which is supported by
# Mail-in-a-Box versions 0.40 through 5x.
Expand Down
2 changes: 1 addition & 1 deletion setup/management.sh
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ minute=$((RANDOM % 60)) # avoid overloading mailinabox.email
cat > /etc/cron.d/mailinabox-nightly << EOF;
# Mail-in-a-Box --- Do not edit / will be overwritten on update.
# Run nightly tasks: backup, status checks.
$minute 3 * * * root (cd $PWD && management/daily_tasks.sh)
$minute 1 * * * root (cd $PWD && management/daily_tasks.sh)
EOF

# Start the management server.
Expand Down
32 changes: 20 additions & 12 deletions setup/system.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ management/editconf.py /etc/systemd/journald.conf MaxRetentionSec=10day

hide_output systemctl restart systemd-journald.service

# ### Improve server privacy

# Disable MOTD adverts to prevent revealing server information in MOTD request headers
# See https://ma.ttias.be/what-exactly-being-sent-ubuntu-motd/
if [ -f /etc/default/motd-news ]; then
tools/editconf.py /etc/default/motd-news ENABLED=0
rm -f /var/cache/motd-news
fi

# ### Add PPAs.

# We install some non-standard Ubuntu packages maintained by other
Expand Down Expand Up @@ -294,18 +303,17 @@ if [ -z "${DISABLE_FIREWALL:-}" ]; then
# ssh might be running on an alternate port. Use sshd -T to dump sshd's #NODOC
# settings, find the port it is supposedly running on, and open that port #NODOC
# too. #NODOC
SSH_PORT=$(sshd -T 2>/dev/null | grep "^port " | sed "s/port //") #NODOC
if [ ! -z "$SSH_PORT" ]; then
if [ "$SSH_PORT" != "22" ]; then
echo "Opening alternate SSH port $SSH_PORT." #NODOC
ufw_limit "$SSH_PORT" #NODOC
else
# Allow incoming connections to SSH.
ufw_limit ssh;
fi
else
# Allow incoming connections to SSH.
ufw_limit ssh;
SSH_PORT=$(sshd -T 2>/dev/null | grep "^port " | sed "s/port //" | tr '\n' ' ') #NODOC
if [ -n "$SSH_PORT" ]; then
for port in $SSH_PORT; do
if [ "$port" != "22" ]; then
echo "Opening alternate SSH port $port." #NODOC
ufw_limit "$port" #NODOC
else
# Allow incoming connections to SSH.
ufw_limit ssh;
fi
done
fi

ufw --force enable;
Expand Down
4 changes: 2 additions & 2 deletions setup/webmail.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ echo "Installing Roundcube (webmail)..."
apt_install \
dbconfig-common \
php-cli php-sqlite3 php-intl php-json php-common php-curl php-imap \
php-gd php-pspell libjs-jquery libjs-jquery-mousewheel libmagic1 php-mbstring \
sqlite3
php-gd php-pspell php-mbstring php-xml libjs-jquery libjs-jquery-mousewheel libmagic1 \
sqlite3

PHP_VER=$(php_version)

Expand Down
10 changes: 5 additions & 5 deletions setup/zpush.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ apt_install \
phpenmod -v "$PHP_VER" imap

# Copy Z-Push into place.
VERSION=2.7.3
TARGETHASH=9d4bec41935e9a4e07880c5ff915bcddbda4443b
VERSION=2.7.5
TARGETHASH=f0b0b06e255f3496173ab9d28a4f2d985184720e
needs_update=0 #NODOC
if [ ! -f /usr/local/lib/z-push/version ]; then
needs_update=1 #NODOC
Expand Down Expand Up @@ -57,8 +57,6 @@ fi
sed -i "s^define('TIMEZONE', .*^define('TIMEZONE', '$(cat /etc/timezone)');^" /usr/local/lib/z-push/config.php
sed -i "s/define('BACKEND_PROVIDER', .*/define('BACKEND_PROVIDER', 'BackendCombined');/" /usr/local/lib/z-push/config.php
sed -i "s/define('USE_FULLEMAIL_FOR_LOGIN', .*/define('USE_FULLEMAIL_FOR_LOGIN', true);/" /usr/local/lib/z-push/config.php
sed -i "s/define('LOG_MEMORY_PROFILER', .*/define('LOG_MEMORY_PROFILER', false);/" /usr/local/lib/z-push/config.php
sed -i "s/define('BUG68532FIXED', .*/define('BUG68532FIXED', false);/" /usr/local/lib/z-push/config.php
sed -i "s/define('LOGLEVEL', .*/define('LOGLEVEL', LOGLEVEL_ERROR);/" /usr/local/lib/z-push/config.php

# Configure BACKEND
Expand Down Expand Up @@ -112,4 +110,6 @@ restart_service php"$PHP_VER"-fpm

# Fix states after upgrade

hide_output php"$PHP_VER" /usr/local/lib/z-push/z-push-admin.php -a fixstates
if [ $needs_update == 1 ]; then
hide_output php"$PHP_VER" /usr/local/lib/z-push/z-push-admin.php -a fixstates
fi

0 comments on commit 2c8dcc6

Please sign in to comment.