v0.43 (September 1, 2019)

-------------------------
 
 Security fixes:
 
 * A security issue was discovered in rsync backups. If you have enabled rsync backups, the file `id_rsa_miab` may have been copied to your backup destination. This file can be used to access your backup destination. If the file was copied to your backup destination, we recommend that you delete the file on your backup destination, delete `/root/.ssh/id_rsa_miab` on your Mail-in-a-Box, then re-run Mail-in-a-Box setup, and re-configure your SSH public key at your backup destination according to the instructions in the Mail-in-a-Box control panel.
 * Brute force attack prevention was missing for the managesieve service.
 
 Setup:
 
 * Nextcloud was not upgraded properly after restoring Mail-in-a-Box from a backup from v0.40 or earlier.
 
 Mail:
 
 * Upgraded Roundcube to 1.3.10.
 * Fetch an updated whitelist for greylisting on a monthly basis to reduce the number of delayed incoming emails.
 
 Control panel:
 
 * When using secondary DNS, it is now possible to specify a subnet range with the `xfr:` option.
 * Fixed an issue when the secondary DNS option is used and the secondary DNS hostname resolves to multiple IP addresses.
 * Fix a bug in how a backup configuration error is shown.
 -----BEGIN PGP SIGNATURE-----
 
 iQFDBAABCgAtFiEEX0wOcxPM10RpOyrquSBB9MEL3YEFAl1rrwIPHGp0QG9jY2Ft
 cy5pbmZvAAoJELkgQfTBC92BgckIALFnDFxhQ18MtClpi79+rnl1aA5DqbToCuI2
 MHIAOmxIVSavnd5MZZ3efXWAzIniEpbq0X+6Rlzas5lkreT1mHoJsKdkt0bOqy1a
 ZF2vT5UnUM9cwPHkU1ak/TaD9v97wbHpWWGwAK+/zTL6w1ReCVfQ2QzCzoDaY7xh
 OZFXE+YsaI7qZeG3Q4jfFr0IYDowLgjgBpdWvO71QKzWjIIvBNX1ZGt2r+cuKmQ5
 JOXIAR4fdri0p8dMd2sqq0FatBBCfjHDBykA/+GzJJDBX7MNoZsQT3bowrhj8XPS
 f5cKUKm7zlDsm02bfCtDD6nvYYUxvOdQx7yfdL8RYSdy71Chs20=
 =7M/i
 -----END PGP SIGNATURE-----

Merge tag 'v0.43' of https://github.com/mail-in-a-box/mailinabox

v0.43 (September 1, 2019)
-------------------------

Security fixes:

* A security issue was discovered in rsync backups. If you have enabled
rsync backups, the file `id_rsa_miab` may have been copied to your
backup destination. This file can be used to access your backup
destination. If the file was copied to your backup destination, we
recommend that you delete the file on your backup destination, delete
`/root/.ssh/id_rsa_miab` on your Mail-in-a-Box, then re-run
Mail-in-a-Box setup, and re-configure your SSH public key at your backup
destination according to the instructions in the Mail-in-a-Box control
panel.
* Brute force attack prevention was missing for the managesieve service.

Setup:

* Nextcloud was not upgraded properly after restoring Mail-in-a-Box from
a backup from v0.40 or earlier.

Mail:

* Upgraded Roundcube to 1.3.10.
* Fetch an updated whitelist for greylisting on a monthly basis to
reduce the number of delayed incoming emails.

Control panel:

* When using secondary DNS, it is now possible to specify a subnet range
with the `xfr:` option.
* Fixed an issue when the secondary DNS option is used and the secondary
DNS hostname resolves to multiple IP addresses.
* Fix a bug in how a backup configuration error is shown.

5F4C0E7313CCD744693B2AEAB92041F4C10BDD81
This commit is contained in:
John R. Supplee 2019-09-02 18:33:26 -04:00
commit fa3c3236d8
24 changed files with 264 additions and 51 deletions

View file

@ -1,6 +1,49 @@
CHANGELOG CHANGELOG
========= =========
v0.43 (September 1, 2019)
-------------------------
Security fixes:
* A security issue was discovered in rsync backups. If you have enabled rsync backups, the file `id_rsa_miab` may have been copied to your backup destination. This file can be used to access your backup destination. If the file was copied to your backup destination, we recommend that you delete the file on your backup destination, delete `/root/.ssh/id_rsa_miab` on your Mail-in-a-Box, then re-run Mail-in-a-Box setup, and re-configure your SSH public key at your backup destination according to the instructions in the Mail-in-a-Box control panel.
* Brute force attack prevention was missing for the managesieve service.
Setup:
* Nextcloud was not upgraded properly after restoring Mail-in-a-Box from a backup from v0.40 or earlier.
Mail:
* Upgraded Roundcube to 1.3.10.
* Fetch an updated whitelist for greylisting on a monthly basis to reduce the number of delayed incoming emails.
Control panel:
* When using secondary DNS, it is now possible to specify a subnet range with the `xfr:` option.
* Fixed an issue when the secondary DNS option is used and the secondary DNS hostname resolves to multiple IP addresses.
* Fix a bug in how a backup configuration error is shown.
v0.42b (August 3, 2019)
-----------------------
Changes:
* 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.
Software updates:
* 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.
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) v0.41 (February 26, 2019)
------------------------- -------------------------

View file

@ -198,7 +198,7 @@ by him:
$ curl -s https://keybase.io/joshdata/key.asc | gpg --import $ curl -s https://keybase.io/joshdata/key.asc | gpg --import
gpg: key C10BDD81: public key "Joshua Tauberer <jt@occams.info>" imported gpg: key C10BDD81: public key "Joshua Tauberer <jt@occams.info>" imported
$ git verify-tag v0.41 $ git verify-tag v0.43
gpg: Signature made ..... using RSA key ID C10BDD81 gpg: Signature made ..... using RSA key ID C10BDD81
gpg: Good signature from "Joshua Tauberer <jt@occams.info>" gpg: Good signature from "Joshua Tauberer <jt@occams.info>"
gpg: WARNING: This key is not certified with a trusted signature! gpg: WARNING: This key is not certified with a trusted signature!
@ -211,7 +211,7 @@ and on his [personal homepage](https://razor.occams.info/). (Of course, if this
Checkout the tag corresponding to the most recent release: Checkout the tag corresponding to the most recent release:
$ git checkout v0.41 $ git checkout v0.43
Begin the installation. Begin the installation.

View file

@ -1,4 +1,4 @@
# Fail2Ban filter Dovecot authentication and pop3/imap server # Fail2Ban filter Dovecot authentication and pop3/imap/managesieve server
# For Mail-in-a-Box # For Mail-in-a-Box
[INCLUDES] [INCLUDES]
@ -9,7 +9,7 @@ before = common.conf
_daemon = (auth|dovecot(-auth)?|auth-worker) _daemon = (auth|dovecot(-auth)?|auth-worker)
failregex = ^%(__prefix_line)s(pop3|imap)-login: (Info: )?(Aborted login|Disconnected)(: Inactivity)? \(((no auth attempts|auth failed, \d+ attempts)( in \d+ secs)?|tried to use (disabled|disallowed) \S+ auth)\):( user=<\S*>,)?( method=\S+,)? rip=<HOST>, lip=(\d{1,3}\.){3}\d{1,3}(, TLS( handshaking)?(: Disconnected)?)?(, session=<\S+>)?\s*$ failregex = ^%(__prefix_line)s(pop3|imap|managesieve)-login: (Info: )?(Aborted login|Disconnected)(: Inactivity)? \(((no auth attempts|auth failed, \d+ attempts)( in \d+ secs)?|tried to use (disabled|disallowed) \S+ auth)\):( user=<\S*>,)?( method=\S+,)? rip=<HOST>, lip=(\d{1,3}\.){3}\d{1,3}(, TLS( handshaking)?(: Disconnected)?)?(, session=<\S+>)?\s*$
ignoreregex = ignoreregex =

View file

@ -18,6 +18,9 @@
location = /.well-known/autoconfig/mail/config-v1.1.xml { location = /.well-known/autoconfig/mail/config-v1.1.xml {
alias /var/lib/mailinabox/mozilla-autoconfig.xml; alias /var/lib/mailinabox/mozilla-autoconfig.xml;
} }
location = /mail/config-v1.1.xml {
alias /var/lib/mailinabox/mozilla-autoconfig.xml;
}
# Roundcube Webmail configuration. # Roundcube Webmail configuration.
rewrite ^/mail$ /mail/ redirect; rewrite ^/mail$ /mail/ redirect;

View file

@ -19,6 +19,7 @@
rewrite ^/cloud/$ /cloud/index.php; rewrite ^/cloud/$ /cloud/index.php;
rewrite ^/cloud/(contacts|calendar|files)$ /cloud/index.php/apps/$1/ redirect; rewrite ^/cloud/(contacts|calendar|files)$ /cloud/index.php/apps/$1/ redirect;
rewrite ^(/cloud/core/doc/[^\/]+/)$ $1/index.html; rewrite ^(/cloud/core/doc/[^\/]+/)$ $1/index.html;
rewrite ^(/cloud/oc[sm]-provider)/$ $1/index.php redirect;
location /cloud/ { location /cloud/ {
alias /usr/local/lib/owncloud/; alias /usr/local/lib/owncloud/;
location ~ ^/cloud/(build|tests|config|lib|3rdparty|templates|data|README)/ { location ~ ^/cloud/(build|tests|config|lib|3rdparty|templates|data|README)/ {
@ -27,6 +28,14 @@
location ~ ^/cloud/(?:\.|autotest|occ|issue|indie|db_|console) { location ~ ^/cloud/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all; 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)(/.*)?$ { location ~ ^(/cloud)((?:/ocs)?/[^/]+\.php)(/.*)?$ {
# note: ~ has precendence over a regular location block # note: ~ has precendence over a regular location block

View file

@ -15,8 +15,8 @@ from exclusiveprocess import Lock
from utils import load_environment, shell, wait_for_service, fix_boto from utils import load_environment, shell, wait_for_service, fix_boto
rsync_ssh_options = [ rsync_ssh_options = [
"--ssh-options='-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\"", "--rsync-options= -e \"/usr/bin/ssh -oStrictHostKeyChecking=no -oBatchMode=yes -p 22 -i /root/.ssh/id_rsa_miab\"",
] ]
def backup_status(env): def backup_status(env):
@ -406,7 +406,7 @@ def list_target_files(config):
reason = "Provided path {} is invalid.".format(target_path) reason = "Provided path {} is invalid.".format(target_path)
elif 'Network is unreachable' in listing: elif 'Network is unreachable' in listing:
reason = "The IP address {} is unreachable.".format(target.hostname) 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) reason = "The hostname {} cannot be resolved.".format(target.hostname)
else: else:
reason = "Unknown error." \ reason = "Unknown error." \
@ -419,15 +419,22 @@ def list_target_files(config):
fix_boto() # must call prior to importing boto fix_boto() # must call prior to importing boto
import boto.s3 import boto.s3
from boto.exception import BotoServerError from boto.exception import BotoServerError
custom_region = False
for region in boto.s3.regions(): for region in boto.s3.regions():
if region.endpoint == target.hostname: if region.endpoint == target.hostname:
break break
else: 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] bucket = target.path[1:].split('/')[0]
path = '/'.join(target.path[1:].split('/')[1:]) + '/' 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 no prefix is specified, set the path to '', otherwise boto won't list the files
if path == '/': if path == '/':
path = '' path = ''

View file

@ -288,6 +288,21 @@ def build_zone(domain, all_domains, additional_records, www_redirect_domains, en
if not has_rec(qname, "SRV"): 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.")) 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
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. # 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 "")) records.sort(key = lambda rec : list(reversed(rec[0].split(".")) if rec[0] is not None else ""))
@ -888,10 +903,14 @@ def set_secondary_dns(hostnames, env):
else: else:
# Validate IP address. # Validate IP address.
try: try:
v = ipaddress.ip_address(item[4:]) # raises a ValueError if there's a problem if "/" in item[4:]:
if not isinstance(v, ipaddress.IPv4Address): raise ValueError("That's an IPv6 address.") 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: 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.
set_custom_dns_record("_secondary_nameserver", "A", " ".join(hostnames), "set", env) set_custom_dns_record("_secondary_nameserver", "A", " ".join(hostnames), "set", env)

View file

@ -195,7 +195,7 @@ def get_mail_users_ex(env, with_archived=False):
if email in active_accounts: continue if email in active_accounts: continue
user = { user = {
"email": email, "email": email,
"privileges": "", "privileges": [],
"status": "inactive", "status": "inactive",
"mailbox": mbox, "mailbox": mbox,
} }

View file

@ -488,10 +488,12 @@ def check_dns_zone(domain, env, output, dns_zonefiles):
if custom_secondary_ns and not probably_external_dns: if custom_secondary_ns and not probably_external_dns:
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_ip = query_dns(ns, "A") ns_ips = query_dns(ns, "A")
if not ns_ip: if not ns_ips:
output.print_error("Secondary nameserver %s is not valid (it doesn't resolve to an IP address)." % ns) output.print_error("Secondary nameserver %s is not valid (it doesn't resolve to an IP address)." % ns)
continue 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. # 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)

View file

@ -90,7 +90,7 @@
<div class="col-sm-offset-1 col-sm-11"> <div class="col-sm-offset-1 col-sm-11">
<p class="small"> <p class="small">
Multiple secondary servers can be separated with commas or spaces (i.e., <code>ns2.hostingcompany.com ns3.hostingcompany.com</code>). Multiple secondary servers can be separated with commas or spaces (i.e., <code>ns2.hostingcompany.com ns3.hostingcompany.com</code>).
To enable zone transfers to additional servers without listing them as secondary nameservers, add <code>xfr:IPADDRESS</code>. To enable zone transfers to additional servers without listing them as secondary nameservers, add an IP address or subnet using <code>xfr:10.20.30.40</code> or <code>xfr:10.20.30.40/24</code>.
</p> </p>
<p id="secondarydns-clear-instructions" style="display: none" class="small"> <p id="secondarydns-clear-instructions" style="display: none" class="small">
Clear the input field above and click Update to use this machine itself as secondary DNS, which is the default/normal setup. Clear the input field above and click Update to use this machine itself as secondary DNS, which is the default/normal setup.

View file

@ -17,13 +17,13 @@ sudo tools/mail.py user make-admin me@{{hostname}}</pre>
{% endif %} {% endif %}
<hr> <hr>
</div> </div>
</div> </div>
{% endif %} {% endif %}
<p style="margin: 2em; text-align: center;">Log in here for your Mail-in-a-Box control panel.</p> <p style="margin: 2em; text-align: center;">Log in here for your Mail-in-a-Box control panel.</p>
<div style="margin: 0 auto; max-width: 32em;"> <div style="margin: 0 auto; max-width: 32em;">
<form class="form-horizontal" role="form" onsubmit="do_login(); return false;"> <form class="form-horizontal" role="form" onsubmit="do_login(); return false;" method="get">
<div class="form-group"> <div class="form-group">
<label for="inputEmail3" class="col-sm-3 control-label">Email</label> <label for="inputEmail3" class="col-sm-3 control-label">Email</label>
<div class="col-sm-9"> <div class="col-sm-9">
@ -76,7 +76,7 @@ function do_login() {
"/me", "/me",
"GET", "GET",
{ }, { },
function(response){ function(response){
// This API call always succeeds. It returns a JSON object indicating // This API call always succeeds. It returns a JSON object indicating
// whether the request was authenticated or not. // whether the request was authenticated or not.
if (response.status != "ok") { if (response.status != "ok") {

View file

@ -77,15 +77,22 @@
</div> </div>
</div> </div>
<div class="form-group backup-target-s3"> <div class="form-group backup-target-s3">
<label for="backup-target-s3-host" class="col-sm-2 control-label">S3 Region</label> <label for="backup-target-s3-host-select" class="col-sm-2 control-label">S3 Region</label>
<div class="col-sm-8"> <div class="col-sm-8">
<select class="form-control" rows="1" id="backup-target-s3-host"> <select class="form-control" rows="1" id="backup-target-s3-host-select">
{% for name, host in backup_s3_hosts %} {% for name, host in backup_s3_hosts %}
<option value="{{host}}">{{name}}</option> <option value="{{host}}">{{name}}</option>
{% endfor %} {% endfor %}
<option value="other">Other</option>
</select> </select>
</div> </div>
</div> </div>
<div class="form-group backup-target-s3">
<label for="backup-target-s3-host" class="col-sm-2 control-label">S3 Host / Endpoint</label>
<div class="col-sm-8">
<input type="text" placeholder="Endpoint" class="form-control" rows="1" id="backup-target-s3-host">
</div>
</div>
<div class="form-group backup-target-s3"> <div class="form-group backup-target-s3">
<label for="backup-target-s3-path" class="col-sm-2 control-label">S3 Path</label> <label for="backup-target-s3-path" class="col-sm-2 control-label">S3 Path</label>
<div class="col-sm-8"> <div class="col-sm-8">
@ -139,6 +146,8 @@ function toggle_form() {
var target_type = $("#backup-target-type").val(); 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").hide();
$(".backup-target-" + target_type).show(); $(".backup-target-" + target_type).show();
init_inputs(target_type);
} }
function nice_size(bytes) { function nice_size(bytes) {
@ -278,4 +287,20 @@ function set_custom_backup() {
}); });
return false; 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());
}
}
</script> </script>

View file

@ -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. # IP address than this box. Remove those domains from our list.
domains -= get_domains_with_a_records(env) 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 # 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 # 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. # by a custom A/AAAA record and is never a 'www.' redirect.

View file

@ -20,7 +20,7 @@ if [ -z "$TAG" ]; then
# want to display in status checks. # 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 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. # This machine is running Ubuntu 18.04.
TAG=v0.41-quota-0.18-beta TAG=v0.43-quota-0.18-beta
elif [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" == "Ubuntu 14.04 LTS" ]; then 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. # This machine is running Ubuntu 14.04.

View file

@ -1,7 +1,7 @@
# Turn on "strict mode." See http://redsymbol.net/articles/unofficial-bash-strict-mode/. # Turn on "strict mode." See http://redsymbol.net/articles/unofficial-bash-strict-mode/.
# -e: exit if any command unexpectedly fails. # -e: exit if any command unexpectedly fails.
# -u: exit if we have a variable typo. # -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 set -euo pipefail
function hide_output { function hide_output {
@ -127,7 +127,7 @@ function get_default_privateip {
if [ "$1" == "6" ]; then target=2001:4860:4860::8888; fi if [ "$1" == "6" ]; then target=2001:4860:4860::8888; fi
# Get the route information. # 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. # Parse the address out of the route information.
address=$(echo $route | sed "s/.* src \([^ ]*\).*/\1/") address=$(echo $route | sed "s/.* src \([^ ]*\).*/\1/")

View file

@ -82,11 +82,12 @@ tools/editconf.py /etc/dovecot/conf.d/10-auth.conf \
# Enable SSL, specify the location of the SSL certificate and private key files. # 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/. # 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 # Enable strong ssl dh parameters
tools/editconf.py /etc/dovecot/conf.d/10-ssl.conf \ tools/editconf.py /etc/dovecot/conf.d/10-ssl.conf \
ssl=required \ ssl=required \
"ssl_cert=<$STORAGE_ROOT/ssl/ssl_certificate.pem" \ "ssl_cert=<$STORAGE_ROOT/ssl/ssl_certificate.pem" \
"ssl_key=<$STORAGE_ROOT/ssl/ssl_private_key.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_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_prefer_server_ciphers = yes" \
"ssl_dh_parameters_length = 2048" "ssl_dh_parameters_length = 2048"
@ -137,6 +138,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 { protocol imap {
mail_max_userip_connections = 20 mail_max_userip_connections = 20
} }

View file

@ -225,7 +225,34 @@ postconf -e smtpd_recipient_restrictions="$RECIPIENT_RESTRICTIONS"
# e-mails really latter, delay of greylisting has been set to # e-mails really latter, delay of greylisting has been set to
# 180 seconds (default is 300 seconds). # 180 seconds (default is 300 seconds).
tools/editconf.py /etc/default/postgrey \ 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. # 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. # The same limit is specified in nginx.conf for mail submitted via webmail and Z-Push.

View file

@ -38,7 +38,7 @@ inst_dir=/usr/local/lib/mailinabox
mkdir -p $inst_dir mkdir -p $inst_dir
venv=$inst_dir/env venv=$inst_dir/env
if [ ! -d $venv ]; then if [ ! -d $venv ]; then
virtualenv -ppython3 $venv hide_output virtualenv -ppython3 $venv
fi fi
# Upgrade pip because the Ubuntu-packaged version is out of date. # Upgrade pip because the Ubuntu-packaged version is out of date.

View file

@ -76,4 +76,8 @@ restart_service munin-node
# generate initial statistics so the directory isn't empty # generate initial statistics so the directory isn't empty
# (We get "Pango-WARNING **: error opening config file '/root/.config/pango/pangorc': Permission denied" # (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.) # 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

View file

@ -13,7 +13,8 @@ apt-get purge -qq -y owncloud* # we used to use the package manager
apt_install php php-fpm \ apt_install php php-fpm \
php-cli php-sqlite3 php-gd php-imap php-curl php-pear curl \ 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() { InstallNextcloud() {
@ -24,12 +25,12 @@ InstallNextcloud() {
echo "Upgrading to Nextcloud version $version" echo "Upgrading to Nextcloud version $version"
echo echo
# Download and verify
wget_verify https://download.nextcloud.com/server/releases/nextcloud-$version.zip $hash /tmp/nextcloud.zip
# Remove the current owncloud/Nextcloud # Remove the current owncloud/Nextcloud
rm -rf /usr/local/lib/owncloud 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 # Extract ownCloud/Nextcloud
unzip -q /tmp/nextcloud.zip -d /usr/local/lib unzip -q /tmp/nextcloud.zip -d /usr/local/lib
mv /usr/local/lib/nextcloud /usr/local/lib/owncloud mv /usr/local/lib/nextcloud /usr/local/lib/owncloud
@ -39,14 +40,22 @@ InstallNextcloud() {
# their github repositories. # their github repositories.
mkdir -p /usr/local/lib/owncloud/apps 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/ tar xf /tmp/contacts.tgz -C /usr/local/lib/owncloud/apps/
rm /tmp/contacts.tgz 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/ tar xf /tmp/calendar.tgz -C /usr/local/lib/owncloud/apps/
rm /tmp/calendar.tgz rm /tmp/calendar.tgz
# Starting with Nextcloud 15, the app user_external is no longer included in Nextcloud core,
# we will install from their github repository.
if [[ $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. # Fix weird permissions.
chmod 750 /usr/local/lib/owncloud/{apps,config} chmod 750 /usr/local/lib/owncloud/{apps,config}
@ -75,15 +84,32 @@ InstallNextcloud() {
# Add missing indices. NextCloud didn't include this in the normal upgrade because it might take some time. # 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 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 fi
} }
nextcloud_ver=14.0.6 # Nextcloud Version to install. Checks are done down below to step through intermediate versions.
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) # Current Nextcloud Version, #1623
if [ ! -d /usr/local/lib/owncloud/ ] \ # Checking /usr/local/lib/owncloud/version.php shows version of the Nextcloud application, not the DB
|| ! grep -q $nextcloud_ver /usr/local/lib/owncloud/version.php; then # $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. # 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 service php7.2-fpm stop &> /dev/null || /bin/true
@ -104,16 +130,22 @@ if [ ! -d /usr/local/lib/owncloud/ ] \
fi fi
# If ownCloud or Nextcloud was previously installed.... # 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 # Database migrations from ownCloud are no longer possible because ownCloud cannot be run under
# PHP 7. # 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." 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 exit 1
fi elif [[ ${CURRENT_NEXTCLOUD_VER} =~ ^1[012] ]]; then
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." 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 exit 1
elif [[ ${CURRENT_NEXTCLOUD_VER} =~ ^13 ]]; then
# If we are running Nextcloud 13, upgrade to Nextcloud 14
InstallNextcloud 14.0.6 4e43a57340f04c2da306c8eea98e30040399ae5a
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
fi fi
@ -142,10 +174,12 @@ if [ ! -f $STORAGE_ROOT/owncloud/owncloud.db ]; then
'overwritewebroot' => '/cloud', 'overwritewebroot' => '/cloud',
'overwrite.cli.url' => '/cloud', 'overwrite.cli.url' => '/cloud',
'user_backends' => array( 'user_backends' => array(
array( array(
'class'=>'OC_User_IMAP', 'class' => 'OC_User_IMAP',
'arguments'=>array('{127.0.0.1:993/imap/ssl/novalidate-cert}') 'arguments' => array(
) '127.0.0.1', 143, null
),
),
), ),
'memcache.local' => '\OC\Memcache\APCu', 'memcache.local' => '\OC\Memcache\APCu',
'mail_smtpmode' => 'sendmail', 'mail_smtpmode' => 'sendmail',
@ -217,6 +251,8 @@ include("$STORAGE_ROOT/owncloud/config.php");
\$CONFIG['mail_domain'] = '$PRIMARY_HOSTNAME'; \$CONFIG['mail_domain'] = '$PRIMARY_HOSTNAME';
\$CONFIG['user_backends'] = array(array('class' => 'OC_User_IMAP','arguments' => array('127.0.0.1', 143, null),),);
echo "<?php\n\\\$CONFIG = "; echo "<?php\n\\\$CONFIG = ";
var_export(\$CONFIG); var_export(\$CONFIG);
echo ";"; echo ";";

View file

@ -26,7 +26,7 @@ fi
# #
# Skip the check if we appear to be running inside of Vagrant, because that's really just for testing. # 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}') 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 if [ ! -d /vagrant ]; then
TOTAL_PHYSICAL_MEM=$(expr \( \( $TOTAL_PHYSICAL_MEM \* 1024 \) / 1000 \) / 1000) TOTAL_PHYSICAL_MEM=$(expr \( \( $TOTAL_PHYSICAL_MEM \* 1024 \) / 1000 \) / 1000)
echo "Your Mail-in-a-Box needs more memory (RAM) to function properly." echo "Your Mail-in-a-Box needs more memory (RAM) to function properly."

View file

@ -28,8 +28,8 @@ apt_install \
# 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.
# Combine the Roundcube version number with the commit hash of plugins to track # Combine the Roundcube version number with the commit hash of plugins to track
# whether we have the latest version of everything. # whether we have the latest version of everything.
VERSION=1.3.8 VERSION=1.3.10
HASH=90c7900ccf7b2f46fe49c650d5adb9b85ee9cc22 HASH=431625fc737e301f9b7e502cccc61e50a24786b8
PERSISTENT_LOGIN_VERSION=dc5ca3d3f4415cc41edb2fde533c8a8628a94c76 PERSISTENT_LOGIN_VERSION=dc5ca3d3f4415cc41edb2fde533c8a8628a94c76
HTML5_NOTIFIER_VERSION=4b370e3cd60dabd2f428a26f45b677ad1b7118d5 HTML5_NOTIFIER_VERSION=4b370e3cd60dabd2f428a26f45b677ad1b7118d5
CARDDAV_VERSION=3.0.3 CARDDAV_VERSION=3.0.3

View file

@ -22,8 +22,8 @@ apt_install \
phpenmod -v php imap phpenmod -v php imap
# Copy Z-Push into place. # Copy Z-Push into place.
VERSION=2.4.4 VERSION=2.5.0
TARGETHASH=104d44426852429dac8ec2783a4e9ad7752d4682 TARGETHASH=30ce5c1af3f10939036361b6032d1187651b621e
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

View file

@ -90,6 +90,26 @@ def pop_test():
if M: if M:
M.quit() M.quit()
def managesieve_test():
# We don't have a Python sieve client, so we'll
# just run the IMAP client and see what happens.
import imaplib
try:
M = imaplib.IMAP4(hostname, 4190)
except ConnectionRefusedError:
# looks like fail2ban worked
raise IsBlocked()
try:
M.login("fakeuser", "fakepassword")
raise Exception("authentication didn't fail")
except imaplib.IMAP4.error:
# authentication should fail
pass
finally:
M.logout() # shuts down connection, has nothing to do with login()
def http_test(url, expected_status, postdata=None, qsargs=None, auth=None): def http_test(url, expected_status, postdata=None, qsargs=None, auth=None):
import urllib.parse import urllib.parse
import requests import requests
@ -208,6 +228,9 @@ if __name__ == "__main__":
# POP # POP
run_test(pop_test, [], 20, 30, 4) run_test(pop_test, [], 20, 30, 4)
# Managesieve
run_test(managesieve_test, [], 20, 30, 4)
# Mail-in-a-Box control panel # Mail-in-a-Box control panel
run_test(http_test, ["/admin/me", 200], 20, 30, 1) run_test(http_test, ["/admin/me", 200], 20, 30, 1)