1
0
mirror of https://github.com/mail-in-a-box/mailinabox.git synced 2026-03-12 17:07:23 +01:00

Compare commits

...

28 Commits
v69b ... v72

Author SHA1 Message Date
Joshua Tauberer
58dca6e4ab v72 2025-06-03 20:41:46 -04:00
KiekerJan
1a8a50e4ae Update roundcube to 1.6.11
Merges #2511.
2025-06-03 20:41:38 -04:00
Tomasz Stanczak
41cbf0ba8e Handle no existence of expired certificates before trying to move them into ssl.expired subdirectory (#2480)
Shell option 'nullglob' to prevent the following 'for' loop from being entered even when no matching files are present.
2025-02-15 14:31:58 -05:00
KiekerJan
5ef85f3d02 Update roundcube to 1.6.10 (#2483) 2025-02-15 14:29:15 -05:00
Joshua Tauberer
e6c354c312 v71a 2025-01-06 07:08:06 -05:00
Paul
432b470d29 New & improved Disable MOTD advertisements (#2470)
Checks if /etc/default/motd-news exists before running commands.
2025-01-06 07:06:01 -05:00
Joshua Tauberer
d58dd0c91d v71 2025-01-04 14:39:25 -05:00
Joshua Tauberer
f73da3db60 Fix likely merge mistake in 564ed59bb4
Fixes #2466
2025-01-04 14:28:36 -05:00
yeah
18721e42d1 Cronjob for cleaning up expired SSL certificates in order to improve page load times with many domains (#2410)
Fixes #2316.
2024-12-22 08:07:04 -05:00
yeah
e0b93718a3 Revert "increase timeout for the nginx proxy that provides access to the Mail…" (#2411)
Reverts #2407 - as per #2316

This reverts commit 2803d88894.
2024-12-22 08:02:49 -05:00
KiekerJan
2e0482e181 Exclude the owncloud-backup folder from the nightly backup (#2413) 2024-12-22 08:01:02 -05:00
Tomasz Stanczak
0d7388899c Allow DSA end EllipticCurve private keys to be used additionally to RSA for HTTPS certificates (#2416)
Co-authored-by: Tomasz Stanczak <tomasz@cocoturtle.com>
2024-12-22 07:59:58 -05:00
zoof
4f094f7859 Change hour of daily tasks to run at 1am and only run full backups on weekends (#2424)
* Change hour of daily tasks to run at 1am
* Change to only do full backup on weekends
2024-12-22 07:57:59 -05:00
KiekerJan
564ed59bb4 Add check on ipv6 for spamhaus (#2428) 2024-12-22 07:48:36 -05:00
KiekerJan
9f87b36ba1 add check on SOA record to determine up to date synchronization of secondary nameserver (#2429) 2024-12-22 07:45:45 -05:00
matidau
e36c17fc72 Fixstates only after Z-Push upgrade (#2432) 2024-12-22 07:42:56 -05:00
KiekerJan
3d59f2d7e0 Update roundcube to 1.6.9 (#2440) 2024-12-22 07:28:39 -05:00
Harm Berntsen
ee0d750b85 Add missing php-xml package for Roundcube without Nextcloud (#2441)
When the Nextcloud installation is skipped, php8.0-xml will also not be installed. This causes issues for Roundcube because it won't load: `PHP Fatal error:  Uncaught Error: Class "DOMDocument" not found in /usr/local/lib/roundcubemail/program/lib/Roundcube/html.php:367`. Installing the package on the Roundcube side as well fixes it for me.
2024-12-22 07:28:04 -05:00
Paul
d8563be38b Disable MOTD advertisements (#2457)
Disables MOTD advertisements which use a script to send server information in `wget` headers to Canonical.
2024-12-22 07:27:36 -05:00
Nicholas Wilson
81b0e0a64f Updated CHANGELOG.md, fix typo(s) (#2459) 2024-12-22 07:26:59 -05:00
matidau
7ef859ce96 Update zpush.sh to version 2.7.5 (#2463) 2024-12-13 09:28:45 -05:00
Downtown Allday
a8d13b84b4 fix: NameError: name 'subprocess' is not defined (#2425) 2024-11-27 08:22:45 -05:00
matidau
1699ab8c02 Update zpush.sh to version 2.7.4 (#2423) 2024-09-17 14:51:26 -04:00
Downtown Allday
ca123515aa fix variable (#2439) 2024-09-02 21:30:01 -04:00
matidau
3b8f4a2fe8 Z-Push remove config lines no longer supported (#2433) 2024-08-30 14:27:44 -04:00
darren
f453c44d52 Update setup to handle multiple SSH ports (#2437)
This PR addresses an issue reported in the mailinabox
Slack channel where a system had sshd configured to listen
on two ports.

Co-authored-by: Darren Sanders <darren@dms00.com>
2024-08-30 14:26:05 -04:00
Joshua Tauberer
41870d22b0 v70 2024-08-15 08:53:27 -04:00
matidau
b9c5cd248f Update Roundcube to 1.6.8 (#2422) 2024-08-15 08:49:52 -04:00
13 changed files with 183 additions and 48 deletions

View File

@@ -1,6 +1,53 @@
CHANGELOG CHANGELOG
========= =========
Version 72 (June 3, 2025)
-------------------------
Upgrades
* Roundcube upgraded to version 1.6.11, fixing a security vulnerability.
Control Panel
* A warning during daily tasks related to no TLS certificates being expired is fixed.
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)
----------------------------
* Roundcube is updated to version 1.6.8 fixing security vulnerabilities.
Version 69 (July 20, 2024) Version 69 (July 20, 2024)
-------------------------- --------------------------
@@ -68,7 +115,7 @@ Version 64 (September 2, 2023)
* Fixed backups to work with the latest duplicity package which was not backwards compatible. * Fixed backups to work with the latest duplicity package which was not backwards compatible.
* Fixed setting B2 as a backup target with a slash in the application key. * 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. * Turned off OpenDMARC diagnostic reports sent in response to incoming mail.
* Fixed some crashes when using an unrelased version of Mail-in-a-Box. * Fixed some crashes when using an unreleased version of Mail-in-a-Box.
* Added z-push administration scripts. * Added z-push administration scripts.
Version 63 (July 27, 2023) Version 63 (July 27, 2023)
@@ -1124,7 +1171,7 @@ Control panel:
System: System:
* The munin system monitoring tool is now installed and accessible at /admin/munin. * 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. * 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.
@@ -1162,7 +1209,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. * 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. * 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.) * 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: System / Control Panel:

View File

@@ -8,7 +8,6 @@
rewrite ^/admin/munin$ /admin/munin/ redirect; rewrite ^/admin/munin$ /admin/munin/ redirect;
location /admin/ { location /admin/ {
proxy_pass http://127.0.0.1:10222/; proxy_pass http://127.0.0.1:10222/;
proxy_read_timeout 600s;
proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-For $remote_addr;
add_header X-Frame-Options "DENY"; add_header X-Frame-Options "DENY";
add_header X-Content-Type-Options nosniff; add_header X-Content-Type-Options nosniff;

View File

@@ -9,6 +9,7 @@
import os, os.path, re, datetime, sys import os, os.path, re, datetime, sys
import dateutil.parser, dateutil.relativedelta, dateutil.tz import dateutil.parser, dateutil.relativedelta, dateutil.tz
from datetime import date
import rtyaml import rtyaml
from exclusiveprocess import Lock from exclusiveprocess import Lock
@@ -157,6 +158,8 @@ def should_force_full(config, env):
# since the last full backup is greater than half the size # since the last full backup is greater than half the size
# of that full backup. # of that full backup.
inc_size = 0 inc_size = 0
# Check if day of week is a weekend day
weekend = date.today().weekday()>=5
for bak in backup_status(env)["backups"]: for bak in backup_status(env)["backups"]:
if not bak["full"]: if not bak["full"]:
# Scan through the incremental backups cumulating # Scan through the incremental backups cumulating
@@ -165,12 +168,14 @@ def should_force_full(config, env):
else: else:
# ...until we reach the most recent full backup. # ...until we reach the most recent full backup.
# Return if we should to a full backup, which is based # Return if we should to a full backup, which is based
# on the size of the increments relative to the full # on whether it is a weekend day, the size of the
# backup, as well as the age of the full backup. # increments relative to the full backup, as well as
if inc_size > .5*bak["size"]: # the age of the full backup.
return True if weekend:
if dateutil.parser.parse(bak["date"]) + datetime.timedelta(days=config["min_age_in_days"]*10+1) < datetime.datetime.now(dateutil.tz.tzlocal()): if inc_size > .5*bak["size"]:
return True 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 return False
else: else:
# If we got here there are no (full) backups, so make one. # If we got here there are no (full) backups, so make one.
@@ -320,6 +325,7 @@ def perform_backup(full_backup):
"--verbosity", "warning", "--no-print-statistics", "--verbosity", "warning", "--no-print-statistics",
"--archive-dir", backup_cache_dir, "--archive-dir", backup_cache_dir,
"--exclude", backup_root, "--exclude", backup_root,
"--exclude", os.path.join(env["STORAGE_ROOT"], "owncloud-backup"),
"--volsize", "250", "--volsize", "250",
"--gpg-options", "'--cipher-algo=AES256'", "--gpg-options", "'--cipher-algo=AES256'",
"--allow-source-mismatch", "--allow-source-mismatch",
@@ -399,6 +405,7 @@ def run_duplicity_verification():
"--compare-data", "--compare-data",
"--archive-dir", backup_cache_dir, "--archive-dir", backup_cache_dir,
"--exclude", backup_root, "--exclude", backup_root,
"--exclude", os.path.join(env["STORAGE_ROOT"], "owncloud-backup"),
*get_duplicity_additional_args(env), *get_duplicity_additional_args(env),
get_duplicity_target_url(config), get_duplicity_target_url(config),
env["STORAGE_ROOT"], env["STORAGE_ROOT"],

View File

@@ -14,7 +14,7 @@ def get_ssl_certificates(env):
# that the certificates are good for to the best certificate for # that the certificates are good for to the best certificate for
# the domain. # the domain.
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey from cryptography.hazmat.primitives.asymmetric import dsa, rsa, ec
from cryptography.x509 import Certificate from cryptography.x509 import Certificate
# The certificates are all stored here: # The certificates are all stored here:
@@ -59,13 +59,15 @@ def get_ssl_certificates(env):
# Not a valid PEM format for a PEM type we care about. # Not a valid PEM format for a PEM type we care about.
continue 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? # Is it a certificate?
if isinstance(pem, Certificate): if isinstance(pem, Certificate):
certificates.append({ "filename": fn, "cert": pem }) 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. # Process the certificates.
domains = { } domains = { }
@@ -505,7 +507,7 @@ def check_certificate(domain, ssl_certificate, ssl_private_key, warn_if_expiring
# Check that the ssl_certificate & ssl_private_key files are good # Check that the ssl_certificate & ssl_private_key files are good
# for the provided domain. # 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 from cryptography.x509 import Certificate
# The ssl_certificate file may contain a chain of certificates. We'll # The ssl_certificate file may contain a chain of certificates. We'll
@@ -539,7 +541,9 @@ def check_certificate(domain, ssl_certificate, ssl_private_key, warn_if_expiring
except ValueError as e: except ValueError as e:
return (f"The private key file {ssl_private_key} is not a private key file: {e!s}", None) 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) 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(): if priv_key.public_key().public_numbers() != cert.public_key().public_numbers():
@@ -639,7 +643,7 @@ def load_pem(pem):
msg = "File is not a valid PEM-formatted file." msg = "File is not a valid PEM-formatted file."
raise ValueError(msg) raise ValueError(msg)
pem_type = pem_type.group(1) 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()) return serialization.load_pem_private_key(pem, password=None, backend=default_backend())
if pem_type == b"CERTIFICATE": if pem_type == b"CERTIFICATE":
return load_pem_x509_certificate(pem, default_backend()) return load_pem_x509_certificate(pem, default_backend())

View File

@@ -282,26 +282,45 @@ def run_network_checks(env, output):
# The user might have ended up on an IP address that was previously in use # The user might have ended up on an IP address that was previously in use
# by a spammer, or the user may be deploying on a residential network. We # 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. # 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('.'))) rev_ip4 = ".".join(reversed(env['PUBLIC_IP'].split('.')))
zen = query_dns(rev_ip4+'.zen.spamhaus.org', 'A', nxdomain=None) zen = query_dns(rev_ip4+'.zen.spamhaus.org', 'A', nxdomain=None)
evaluate_spamhaus_lookup(env['PUBLIC_IP'], 'IPv4', rev_ip4, output, zen)
if not env['PUBLIC_IPV6']:
return
from ipaddress import IPv6Address
rev_ip6 = ".".join(reversed(IPv6Address(env['PUBLIC_IPV6']).exploded.split(':')))
zen = query_dns(rev_ip6+'.zen.spamhaus.org', 'A', nxdomain=None)
evaluate_spamhaus_lookup(env['PUBLIC_IPV6'], 'IPv6', rev_ip6, output, zen)
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: if zen is None:
output.print_ok("IP address is not blacklisted by zen.spamhaus.org.") output.print_ok(f"{lookuptype} address is not blacklisted by zen.spamhaus.org.")
elif zen == "[timeout]": elif zen == "[timeout]":
output.print_warning("Connection to zen.spamhaus.org timed out. Could not determine whether this box's IP address is blacklisted. Please try again later.") output.print_warning(f"""Connection to zen.spamhaus.org timed out. Could not determine whether this box's
{lookuptype} address is blacklisted. Please try again later.""")
elif zen == "[Not Set]": elif zen == "[Not Set]":
output.print_warning("Could not connect to zen.spamhaus.org. Could not determine whether this box's IP address is blacklisted. Please try again later.") output.print_warning(f"""Could not connect to zen.spamhaus.org. Could not determine whether this box's
{lookuptype} address is blacklisted. Please try again later.""")
elif zen == "127.255.255.252": elif zen == "127.255.255.252":
output.print_warning("Incorrect spamhaus query: %s. Could not determine whether this box's IP address is blacklisted." % (rev_ip4+'.zen.spamhaus.org')) output.print_warning(f"""Incorrect spamhaus query: {lookupdomain + '.zen.spamhaus.org'}. Could not determine whether
this box's {lookuptype} address is blacklisted.""")
elif zen == "127.255.255.254": elif zen == "127.255.255.254":
output.print_warning("Mail-in-a-Box is configured to use a public DNS server. This is not supported by spamhaus. Could not determine whether this box's IP address is blacklisted.") output.print_warning(f"""Mail-in-a-Box is configured to use a public DNS server. This is not supported by
spamhaus. Could not determine whether this box's {lookuptype} address is blacklisted.""")
elif zen == "127.255.255.255": elif zen == "127.255.255.255":
output.print_warning("Too many queries have been performed on the spamhaus server. Could not determine whether this box's IP address is blacklisted.") output.print_warning(f"""Too many queries have been performed on the spamhaus server. Could not determine
whether this box's {lookuptype} address is blacklisted.""")
else: else:
output.print_error("""The IP address of this machine {} is listed in the Spamhaus Block List (code {}), output.print_error(f"""The {lookuptype} address of this machine {lookupaddress} is listed in the Spamhaus Block
which may prevent recipients from receiving your email. See http://www.spamhaus.org/query/ip/{}.""".format(env['PUBLIC_IP'], zen, env['PUBLIC_IP'])) List (code {zen}), which may prevent recipients from receiving your email. See
http://www.spamhaus.org/query/ip/{lookupaddress}.""")
def run_domain_checks(rounded_time, env, output, pool, domains_to_check=None): def run_domain_checks(rounded_time, env, output, pool, domains_to_check=None):
# Get the list of domains we handle mail for. # Get the list of domains we handle mail for.
@@ -521,6 +540,8 @@ def check_dns_zone(domain, env, output, dns_zonefiles):
# Check that each custom secondary nameserver resolves the IP address. # Check that each custom secondary nameserver resolves the IP address.
if custom_secondary_ns and not probably_external_dns: if custom_secondary_ns and not probably_external_dns:
SOARecord = query_dns(domain, "SOA", at=env['PUBLIC_IP'])# Explicitly ask the local dns server.
for ns in custom_secondary_ns: for ns in custom_secondary_ns:
# We must first resolve the nameserver to an IP address so we can query it. # We must first resolve the nameserver to an IP address so we can query it.
ns_ips = query_dns(ns, "A") ns_ips = query_dns(ns, "A")
@@ -530,15 +551,36 @@ def check_dns_zone(domain, env, output, dns_zonefiles):
# Choose the first IP if nameserver returns multiple # Choose the first IP if nameserver returns multiple
ns_ip = ns_ips.split('; ')[0] ns_ip = ns_ips.split('; ')[0]
checkSOA = True
# Now query it to see what it says about this domain. # Now query it to see what it says about this domain.
ip = query_dns(domain, "A", at=ns_ip, nxdomain=None) ip = query_dns(domain, "A", at=ns_ip, nxdomain=None)
if ip == correct_ip: if ip == correct_ip:
output.print_ok("Secondary nameserver %s resolved the domain correctly." % ns) output.print_ok(f"Secondary nameserver {ns} resolved the domain correctly.")
elif ip is None: elif ip is None:
output.print_error("Secondary nameserver %s is not configured to resolve this domain." % ns) output.print_error(f"Secondary nameserver {ns} is not configured to resolve this domain.")
# No need to check SOA record if not configured as nameserver
checkSOA = False
elif ip == '[timeout]':
output.print_error(f"Secondary nameserver {ns} did not resolve this domain, result: {ip}")
checkSOA = False
else: else:
output.print_error(f"Secondary nameserver {ns} is not configured correctly. (It resolved this domain as {ip}. It should be {correct_ip}.)") output.print_error(f"Secondary nameserver {ns} is not configured correctly. (It resolved this domain as {ip}. It should be {correct_ip}.)")
if checkSOA:
# Check that secondary DNS server is synchronized with our primary DNS server. Simplified by checking the SOA record which has a version number
SOASecondary = query_dns(domain, "SOA", at=ns_ip)
if SOARecord == SOASecondary:
output.print_ok(f"Secondary nameserver {ns} has consistent SOA record.")
elif SOARecord == '[Not Set]':
output.print_error(f"Secondary nameserver {ns} has no SOA record configured.")
elif SOARecord == '[timeout]':
output.print_error(f"Secondary nameserver {ns} timed out on checking SOA record.")
else:
output.print_error(f"""Secondary nameserver {ns} has inconsistent SOA record (primary: {SOARecord} versus secondary: {SOASecondary}).
Check that synchronization between secondary and primary DNS servers is properly set-up.""")
def check_dns_zone_suggestions(domain, env, output, dns_zonefiles, domains_with_a_records): def check_dns_zone_suggestions(domain, env, output, dns_zonefiles, domains_with_a_records):
# Warn if a custom DNS record is preventing this or the automatic www redirect from # Warn if a custom DNS record is preventing this or the automatic www redirect from
# being served. # being served.

View File

@@ -189,6 +189,7 @@ def get_ssh_port():
def get_ssh_config_value(parameter_name): def get_ssh_config_value(parameter_name):
# Returns ssh configuration value for the provided parameter # Returns ssh configuration value for the provided parameter
import subprocess
try: try:
output = shell('check_output', ['sshd', '-T']) output = shell('check_output', ['sshd', '-T'])
except FileNotFoundError: except FileNotFoundError:

View File

@@ -23,7 +23,7 @@ if [ -z "$TAG" ]; then
if [ "$UBUNTU_VERSION" == "Ubuntu 22.04 LTS" ]; then if [ "$UBUNTU_VERSION" == "Ubuntu 22.04 LTS" ]; then
# This machine is running Ubuntu 22.04, which is supported by # This machine is running Ubuntu 22.04, which is supported by
# Mail-in-a-Box versions 60 and later. # Mail-in-a-Box versions 60 and later.
TAG=v69b TAG=v72
elif [ "$UBUNTU_VERSION" == "Ubuntu 18.04 LTS" ]; then elif [ "$UBUNTU_VERSION" == "Ubuntu 18.04 LTS" ]; then
# This machine is running Ubuntu 18.04, which is supported by # This machine is running Ubuntu 18.04, which is supported by
# Mail-in-a-Box versions 0.40 through 5x. # Mail-in-a-Box versions 0.40 through 5x.

View File

@@ -116,7 +116,7 @@ minute=$((RANDOM % 60)) # avoid overloading mailinabox.email
cat > /etc/cron.d/mailinabox-nightly << EOF; cat > /etc/cron.d/mailinabox-nightly << EOF;
# Mail-in-a-Box --- Do not edit / will be overwritten on update. # Mail-in-a-Box --- Do not edit / will be overwritten on update.
# Run nightly tasks: backup, status checks. # 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 EOF
# Start the management server. # Start the management server.

View File

@@ -96,3 +96,12 @@ fi
if [ ! -f "$STORAGE_ROOT/ssl/dh2048.pem" ]; then if [ ! -f "$STORAGE_ROOT/ssl/dh2048.pem" ]; then
openssl dhparam -out "$STORAGE_ROOT/ssl/dh2048.pem" 2048 openssl dhparam -out "$STORAGE_ROOT/ssl/dh2048.pem" 2048
fi fi
# Cleanup expired SSL certificates from $STORAGE_ROOT/ssl daily
cat > /etc/cron.daily/mailinabox-ssl-cleanup << EOF;
#!/bin/bash
# Mail-in-a-Box
# Cleanup expired SSL certificates
$(pwd)/tools/ssl_cleanup
EOF
chmod +x /etc/cron.daily/mailinabox-ssl-cleanup

View File

@@ -83,6 +83,15 @@ fi
# (See https://discourse.mailinabox.email/t/journalctl-reclaim-space-on-small-mailinabox/6728/11.) # (See https://discourse.mailinabox.email/t/journalctl-reclaim-space-on-small-mailinabox/6728/11.)
tools/editconf.py /etc/systemd/journald.conf MaxRetentionSec=10day tools/editconf.py /etc/systemd/journald.conf MaxRetentionSec=10day
# ### 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. # ### Add PPAs.
# We install some non-standard Ubuntu packages maintained by other # We install some non-standard Ubuntu packages maintained by other
@@ -270,14 +279,14 @@ if [ -z "${DISABLE_FIREWALL:-}" ]; then
# ssh might be running on an alternate port. Use sshd -T to dump sshd's #NODOC # 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 # settings, find the port it is supposedly running on, and open that port #NODOC
# too. #NODOC # too. #NODOC
SSH_PORT=$(sshd -T 2>/dev/null | grep "^port " | sed "s/port //") #NODOC SSH_PORT=$(sshd -T 2>/dev/null | grep "^port " | sed "s/port //" | tr '\n' ' ') #NODOC
if [ -n "$SSH_PORT" ]; then if [ -n "$SSH_PORT" ]; then
if [ "$SSH_PORT" != "22" ]; then for port in $SSH_PORT; do
if [ "$port" != "22" ]; then
echo "Opening alternate SSH port $SSH_PORT." #NODOC echo "Opening alternate SSH port $port." #NODOC
ufw_limit "$SSH_PORT" #NODOC ufw_limit "$port" #NODOC
fi
fi done
fi fi
ufw --force enable; ufw --force enable;

View File

@@ -23,7 +23,7 @@ echo "Installing Roundcube (webmail)..."
apt_install \ apt_install \
dbconfig-common \ dbconfig-common \
php"${PHP_VER}"-cli php"${PHP_VER}"-sqlite3 php"${PHP_VER}"-intl php"${PHP_VER}"-common php"${PHP_VER}"-curl php"${PHP_VER}"-imap \ php"${PHP_VER}"-cli php"${PHP_VER}"-sqlite3 php"${PHP_VER}"-intl php"${PHP_VER}"-common php"${PHP_VER}"-curl php"${PHP_VER}"-imap \
php"${PHP_VER}"-gd php"${PHP_VER}"-pspell php"${PHP_VER}"-mbstring libjs-jquery libjs-jquery-mousewheel libmagic1 \ php"${PHP_VER}"-gd php"${PHP_VER}"-pspell php"${PHP_VER}"-mbstring php"${PHP_VER}"-xml libjs-jquery libjs-jquery-mousewheel libmagic1 \
sqlite3 sqlite3
# Install Roundcube from source if it is not already present or if it is out of date. # Install Roundcube from source if it is not already present or if it is out of date.
@@ -36,8 +36,8 @@ apt_install \
# https://github.com/mstilkerich/rcmcarddav/releases # https://github.com/mstilkerich/rcmcarddav/releases
# The easiest way to get the package hashes is to run this script and get the hash from # The easiest way to get the package hashes is to run this script and get the hash from
# the error message. # the error message.
VERSION=1.6.6 VERSION=1.6.11
HASH=7705d2736890c49e7ae3ac75e3ae00ba56187056 HASH=d72da06b5f65142dab8b574f7676e0220541a3d4
PERSISTENT_LOGIN_VERSION=bde7b6840c7d91de627ea14e81cf4133cbb3c07a # version 5.3 PERSISTENT_LOGIN_VERSION=bde7b6840c7d91de627ea14e81cf4133cbb3c07a # version 5.3
HTML5_NOTIFIER_VERSION=68d9ca194212e15b3c7225eb6085dbcf02fd13d7 # version 0.6.4+ HTML5_NOTIFIER_VERSION=68d9ca194212e15b3c7225eb6085dbcf02fd13d7 # version 0.6.4+
CARDDAV_VERSION=4.4.3 CARDDAV_VERSION=4.4.3

View File

@@ -22,8 +22,8 @@ apt_install \
phpenmod -v "$PHP_VER" imap phpenmod -v "$PHP_VER" imap
# Copy Z-Push into place. # Copy Z-Push into place.
VERSION=2.7.3 VERSION=2.7.5
TARGETHASH=9d4bec41935e9a4e07880c5ff915bcddbda4443b TARGETHASH=f0b0b06e255f3496173ab9d28a4f2d985184720e
needs_update=0 #NODOC needs_update=0 #NODOC
if [ ! -f /usr/local/lib/z-push/version ]; then if [ ! -f /usr/local/lib/z-push/version ]; then
needs_update=1 #NODOC needs_update=1 #NODOC
@@ -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('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('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('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 sed -i "s/define('LOGLEVEL', .*/define('LOGLEVEL', LOGLEVEL_ERROR);/" /usr/local/lib/z-push/config.php
# Configure BACKEND # Configure BACKEND
@@ -112,4 +110,6 @@ restart_service php"$PHP_VER"-fpm
# Fix states after upgrade # 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

17
tools/ssl_cleanup Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Cleanup SSL certificates which expired more than 7 days ago from $STORAGE_ROOT/ssl and move them to $STORAGE_ROOT/ssl.expired
source /etc/mailinabox.conf
shopt -s extglob nullglob
retain_after="$(date --date="7 days ago" +%Y%m%d)"
mkdir -p $STORAGE_ROOT/ssl.expired
for file in $STORAGE_ROOT/ssl/*-+([0-9])-+([0-9a-f]).pem; do
pem="$(basename "$file")"
not_valid_after="$(cut -d- -f1 <<< "${pem: -21}")"
if [ "$not_valid_after" -lt "$retain_after" ]; then
mv "$file" "$STORAGE_ROOT/ssl.expired/${pem}"
fi
done