mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2026-03-13 17:17:23 +01:00
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:
@@ -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):
|
||||
@@ -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." \
|
||||
@@ -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 = ''
|
||||
|
||||
@@ -288,6 +288,21 @@ 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
|
||||
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 ""))
|
||||
|
||||
@@ -888,10 +903,14 @@ 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:])
|
||||
raise ValueError("'%s' is not an IPv4 address or subnet." % item[4:])
|
||||
|
||||
# Set.
|
||||
set_custom_dns_record("_secondary_nameserver", "A", " ".join(hostnames), "set", env)
|
||||
|
||||
@@ -195,7 +195,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,
|
||||
}
|
||||
|
||||
@@ -488,10 +488,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)
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
<div class="col-sm-offset-1 col-sm-11">
|
||||
<p class="small">
|
||||
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 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.
|
||||
|
||||
@@ -17,13 +17,13 @@ sudo tools/mail.py user make-admin me@{{hostname}}</pre>
|
||||
{% endif %}
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<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;">
|
||||
<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">
|
||||
<label for="inputEmail3" class="col-sm-3 control-label">Email</label>
|
||||
<div class="col-sm-9">
|
||||
@@ -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") {
|
||||
|
||||
@@ -77,15 +77,22 @@
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<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 %}
|
||||
<option value="{{host}}">{{name}}</option>
|
||||
{% endfor %}
|
||||
<option value="other">Other</option>
|
||||
</select>
|
||||
</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">
|
||||
<label for="backup-target-s3-path" class="col-sm-2 control-label">S3 Path</label>
|
||||
<div class="col-sm-8">
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user