1
0
mirror of https://github.com/mail-in-a-box/mailinabox.git synced 2025-04-03 00:07:05 +00:00

Merge remote-tracking branch 'upstream/main' into mergeupstream

This commit is contained in:
KiekerJan 2023-05-19 15:40:40 +02:00
commit 1334f094ab
14 changed files with 146 additions and 83 deletions

View File

@ -1,7 +1,25 @@
CHANGELOG
=========
Version 61.1 (January 28, 2022)
In Development
--------------
Package updates:
* Nextcloud updated to 23.0.12 (and its apps also updated).
* Roundcube updated to 1.6.1.
Control panel:
* Allow setting the backup location's S3 region name for non-AWS S3-compatible backup hosts.
* Control panel pages can be opened in a new tab/window and bookmarked and browser history navigation now works.
* Add a Copy button to put the rsync backup public key on clipboard.
* Allow secondary DNS xfr: items added in the control panel to be hostnames too.
* Fixed issue where sshkeygen fails when IPv6 is disabled.
* Fixed issue opening munin reports.
* Fixed report formatting in status emails sent to the administrator.
Version 61.1 (January 28, 2023)
-------------------------------
* Fixed rsync backups not working with the default port.

View File

@ -205,7 +205,9 @@ def get_duplicity_target_url(config):
# the target URL must be the bucket name. The hostname is passed
# via get_duplicity_additional_args. Move the first part of the
# path (the bucket name) into the hostname URL component, and leave
# the rest for the path.
# the rest for the path. (The S3 region name is also stored in the
# hostname part of the URL, in the username portion, which we also
# have to drop here).
target[1], target[2] = target[2].lstrip('/').split('/', 1)
target = urlunsplit(target)
@ -234,10 +236,15 @@ def get_duplicity_additional_args(env):
]
elif get_target_type(config) == 's3':
# See note about hostname in get_duplicity_target_url.
# The region name, which is required by some non-AWS endpoints,
# is saved inside the username portion of the URL.
from urllib.parse import urlsplit, urlunsplit
target = urlsplit(config["target"])
endpoint_url = urlunsplit(("https", target.netloc, '', '', ''))
return ["--s3-endpoint-url", endpoint_url]
endpoint_url = urlunsplit(("https", target.hostname, '', '', ''))
args = ["--s3-endpoint-url", endpoint_url]
if target.username: # region name is stuffed here
args += ["--s3-region-name", target.username]
return args
return []

View File

@ -711,7 +711,7 @@ def munin_cgi(filename):
support infrastructure like spawn-fcgi.
"""
COMMAND = 'su - munin --preserve-environment --shell=/bin/bash -c /usr/lib/munin/cgi/munin-cgi-graph'
COMMAND = 'su munin --preserve-environment --shell=/bin/bash -c /usr/lib/munin/cgi/munin-cgi-graph'
# su changes user, we use the munin user here
# --preserve-environment retains the environment, which is where Popen's `env` data is
# --shell=/bin/bash ensures the shell used is bash

View File

@ -1092,32 +1092,33 @@ def get_secondary_dns(custom_dns, mode=None):
values.append(hostname)
continue
# This is a hostname. Before including in zone xfr lines,
# resolve to an IP address. Otherwise just return the hostname.
# If the entry starts with "xfr:" only include it in the zone transfer settings.
if hostname.startswith("xfr:"):
if mode != "xfr": continue
hostname = hostname[4:]
# If is a hostname, before including in zone xfr lines,
# resolve to an IP address.
# It may not resolve to IPv6, so don't throw an exception if it
# doesn't.
if not hostname.startswith("xfr:"):
if mode == "xfr":
# doesn't. Skip the entry if there is a DNS error.
if mode == "xfr":
try:
ipaddress.ip_interface(hostname) # test if it's an IP address or CIDR notation
values.append(hostname)
except ValueError:
try:
response = resolver.resolve(hostname+'.', "A", raise_on_no_answer=False)
values.extend(map(str, response))
except dns.exception.DNSException:
logging.debug("Secondary dns A lookup exception %s", hostname)
pass
try:
response = resolver.resolve(hostname+'.', "AAAA", raise_on_no_answer=False)
values.extend(map(str, response))
except dns.exception.DNSException:
logging.debug("Secondary dns AAAA lookup exception %s", hostname)
continue
values.append(hostname)
pass
# This is a zone-xfer-only IP address. Do not return if
# we're querying for NS record hostnames. Only return if
# we're querying for zone xfer IP addresses - return the
# IP address.
elif mode == "xfr":
values.append(hostname[4:])
else:
values.append(hostname)
return values

View File

@ -34,7 +34,7 @@ except:
# If there's nothing coming in, just exit.
if content == "":
sys.exit(0)
sys.exit(0)
# create MIME message
msg = MIMEMultipart('alternative')
@ -46,7 +46,7 @@ msg['From'] = "\"%s\" <%s>" % (env['PRIMARY_HOSTNAME'], admin_addr)
msg['To'] = admin_addr
msg['Subject'] = "[%s] %s" % (env['PRIMARY_HOSTNAME'], subject)
content_html = "<html><body><pre>{}</pre></body></html>".format(html.escape(content))
content_html = '<html><body><pre style="overflow-x: scroll; white-space: pre;">{}</pre></body></html>'.format(html.escape(content))
msg.attach(MIMEText(content, 'plain'))
msg.attach(MIMEText(content_html, 'html'))

View File

@ -7,7 +7,7 @@
<h3>Add a mail alias</h3>
<p>Aliases are email forwarders. An alias can forward email to a <a href="#" onclick="return show_panel('users')">mail user</a> or to any email address.</p>
<p>Aliases are email forwarders. An alias can forward email to a <a href="#users">mail user</a> or to any email address.</p>
<p>To use an alias or any address besides your own login username in outbound mail, the sending user must be included as a permitted sender for the alias.</p>

View File

@ -77,7 +77,7 @@
<h3>Using a secondary nameserver</h3>
<p>If your TLD requires you to have two separate nameservers, you can either set up <a href="#" onclick="return show_panel('external_dns')">external DNS</a> and ignore the DNS server on this box entirely, or use the DNS server on this box but add a secondary (aka &ldquo;slave&rdquo;) nameserver.</p>
<p>If your TLD requires you to have two separate nameservers, you can either set up <a href="#external_dns">external DNS</a> and ignore the DNS server on this box entirely, or use the DNS server on this box but add a secondary (aka &ldquo;slave&rdquo;) nameserver.</p>
<p>If you choose to use a secondary nameserver, you must find a secondary nameserver service provider. Your domain name registrar or virtual cloud provider may provide this service for you. Once you set up the secondary nameserver service, enter the hostname (not the IP address) of <em>their</em> secondary nameserver in the box below.</p>
<form class="form-horizontal" role="form" onsubmit="do_set_secondary_dns(); return false;">
@ -96,7 +96,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 an IP address or subnet using <code>xfr:10.20.30.40</code> or <code>xfr:10.0.0.0/8</code>.
To enable zone transfers to additional servers without listing them as secondary nameservers, prefix a hostname, IP address, or subnet with <code>xfr:</code>, e.g. <code>xfr:10.20.30.40</code> or <code>xfr:10.0.0.0/8</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.

View File

@ -11,9 +11,9 @@
<link rel="stylesheet" href="/admin/assets/bootstrap/css/bootstrap.min.css">
<style>
body {
overflow-y: scroll;
padding-bottom: 20px;
body {
overflow-y: scroll;
padding-bottom: 20px;
}
p {
@ -36,20 +36,20 @@
margin-bottom: 13px;
margin-top: 30px;
}
.panel-heading h3 {
border: none;
padding: 0;
margin: 0;
}
.panel-heading h3 {
border: none;
padding: 0;
margin: 0;
}
h4 {
font-size: 110%;
margin-bottom: 13px;
margin-top: 18px;
}
h4:first-child {
margin-top: 6px;
}
h4:first-child {
margin-top: 6px;
}
.admin_panel {
display: none;
@ -59,10 +59,10 @@
margin: 1.5em 0;
}
ol li {
margin-bottom: 1em;
}
ol li {
margin-bottom: 1em;
}
.if-logged-in { display: none; }
.if-logged-in-admin { display: none; }
@ -72,17 +72,17 @@
html {
filter: invert(100%) hue-rotate(180deg);
}
/* Override Boostrap theme here to give more contrast. The black turns to white by the filter. */
.form-control {
color: black !important;
}
}
/* Revert the invert for the navbar */
button, div.navbar {
filter: invert(100%) hue-rotate(180deg);
}
/* Revert the revert for the dropdowns */
ul.dropdown-menu {
filter: invert(100%) hue-rotate(180deg);
@ -112,30 +112,30 @@
<li class="dropdown if-logged-in-admin">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">System <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="#system_status" onclick="return show_panel(this);">Status Checks</a></li>
<li><a href="#tls" onclick="return show_panel(this);">TLS (SSL) Certificates</a></li>
<li><a href="#system_backup" onclick="return show_panel(this);">Backup Status</a></li>
<li><a href="#system_status">Status Checks</a></li>
<li><a href="#tls">TLS (SSL) Certificates</a></li>
<li><a href="#system_backup">Backup Status</a></li>
<li class="divider"></li>
<li class="dropdown-header">Advanced Pages</li>
<li><a href="#custom_dns" onclick="return show_panel(this);">Custom DNS</a></li>
<li><a href="#external_dns" onclick="return show_panel(this);">External DNS</a></li>
<li><a href="#munin" onclick="return show_panel(this);">Munin Monitoring</a></li>
<li><a href="#custom_dns">Custom DNS</a></li>
<li><a href="#external_dns">External DNS</a></li>
<li><a href="#munin">Munin Monitoring</a></li>
</ul>
</li>
<li><a href="#mail-guide" onclick="return show_panel(this);" class="if-logged-in-not-admin">Mail</a></li>
<li><a href="#mail-guide" class="if-logged-in-not-admin">Mail</a></li>
<li class="dropdown if-logged-in-admin">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Mail &amp; Users <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="#mail-guide" onclick="return show_panel(this);">Instructions</a></li>
<li><a href="#users" onclick="return show_panel(this);">Users</a></li>
<li><a href="#aliases" onclick="return show_panel(this);">Aliases</a></li>
<li><a href="#mail-guide">Instructions</a></li>
<li><a href="#users">Users</a></li>
<li><a href="#aliases">Aliases</a></li>
<li class="divider"></li>
<li class="dropdown-header">Your Account</li>
<li><a href="#mfa" onclick="return show_panel(this);">Two-Factor Authentication</a></li>
<li><a href="#mfa">Two-Factor Authentication</a></li>
</ul>
</li>
<li><a href="#sync_guide" onclick="return show_panel(this);" class="if-logged-in">Contacts/Calendar</a></li>
<li><a href="#web" onclick="return show_panel(this);" class="if-logged-in-admin">Web</a></li>
<li><a href="#sync_guide" class="if-logged-in">Contacts/Calendar</a></li>
<li><a href="#web" class="if-logged-in-admin">Web</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="if-logged-in"><a href="#" onclick="do_logout(); return false;" style="color: white">Log out</a></li>
@ -421,23 +421,25 @@ function do_logout() {
}
function show_panel(panelid) {
if (panelid.getAttribute)
if (panelid.getAttribute) {
// we might be passed an HTMLElement <a>.
panelid = panelid.getAttribute('href').substring(1);
}
$('.admin_panel').hide();
$('#panel_' + panelid).show();
if (typeof localStorage != 'undefined')
localStorage.setItem("miab-cp-lastpanel", panelid);
if (window["show_" + panelid])
window["show_" + panelid]();
current_panel = panelid;
switch_back_to_panel = null;
return false; // when called from onclick, cancel navigation
}
window.onhashchange = function() {
var panelid = window.location.hash.substring(1);
show_panel(panelid);
};
$(function() {
// Recall saved user credentials.
try {
@ -452,8 +454,9 @@ $(function() {
show_hide_menus();
// Recall what the user was last looking at.
if (api_credentials != null && typeof localStorage != 'undefined' && localStorage.getItem("miab-cp-lastpanel")) {
show_panel(localStorage.getItem("miab-cp-lastpanel"));
if (api_credentials != null && window.location.hash) {
var panelid = window.location.hash.substring(1);
show_panel(panelid);
} else if (api_credentials != null) {
show_panel('welcome');
} else {

View File

@ -168,7 +168,18 @@ function do_login() {
// Open the next panel the user wants to go to. Do this after the XHR response
// is over so that we don't start a new XHR request while this one is finishing,
// which confuses the loading indicator.
setTimeout(function() { show_panel(!switch_back_to_panel || switch_back_to_panel == "login" ? 'welcome' : switch_back_to_panel) }, 300);
setTimeout(function() {
if (window.location.hash) {
var panelid = window.location.hash.substring(1);
show_panel(panelid);
} else {
show_panel(
!switch_back_to_panel || switch_back_to_panel == "login"
? 'welcome'
: switch_back_to_panel)
}
}, 300);
}
},
undefined,

View File

@ -36,7 +36,7 @@
<tr><th>Password:</th> <td>Your mail password.</td></tr>
</table>
<p>In addition to setting up your email, you&rsquo;ll also need to set up <a href="#sync_guide" onclick="return show_panel(this);">contacts and calendar synchronization</a> separately.</p>
<p>In addition to setting up your email, you&rsquo;ll also need to set up <a href="#sync_guide">contacts and calendar synchronization</a> separately.</p>
<p>As an alternative to IMAP you can also use the POP protocol: choose POP as the protocol, port 995, and SSL or TLS security in your mail client. The SMTP settings and usernames and passwords remain the same. However, we recommend you use IMAP instead.</p>

View File

@ -17,14 +17,14 @@
<tr><th>Calendar</td> <td><a href="https://{{hostname}}/cloud/calendar">https://{{hostname}}/cloud/calendar</a></td></tr>
</table>
<p>Log in settings are the same as with <a href="#mail-guide" onclick="return show_panel(this);">mail</a>: your
<p>Log in settings are the same as with <a href="#mail-guide">mail</a>: your
complete email address and your mail password.</p>
</div>
<div class="col-sm-6">
<h4>On your mobile device</h4>
<p>If you set up your <a href="#mail-guide" onclick="return show_panel(this);">mail</a> using Exchange/ActiveSync,
<p>If you set up your <a href="#mail-guide">mail</a> using Exchange/ActiveSync,
your contacts and calendar may already appear on your device.</p>
<p>Otherwise, here are some apps that can synchronize your contacts and calendar to your Android phone.</p>

View File

@ -5,7 +5,7 @@
<h2>Backup Status</h2>
<p>The box makes an incremental backup each night. By default the backup is stored on the machine itself, but you can also store it on S3-compatible services like Amazon Web Services (AWS).</p>
<p>The box makes an incremental backup each night. You can store the backup on any Amazone Web Services S3-compatible service, or other options.</p>
<h3>Configuration</h3>
@ -70,9 +70,12 @@
<div class="small" style="margin-top: 2px">
Copy the Public SSH Key above, and paste it within the <tt>~/.ssh/authorized_keys</tt>
of target user on the backup server specified above. That way you'll enable secure and
passwordless authentication from your mail-in-a-box server and your backup server.
passwordless authentication from your Mail-in-a-Box server and your backup server.
</div>
</div>
<div id="copy_pub_key_div" class="col-sm">
<button type="button" class="btn btn-small" onclick="copy_pub_key_to_clipboard()">Copy</button>
</div>
</div>
<!-- S3 BACKUP -->
<div class="form-group backup-target-s3">
@ -95,13 +98,19 @@
<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">
<input type="text" placeholder="https://s3.backuphost.com" 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>
<label for="backup-target-s3-region-name" class="col-sm-2 control-label">S3 Region Name <span style="font-weight: normal">(if required)</span></label>
<div class="col-sm-8">
<input type="text" placeholder="your-bucket-name/backup-directory" class="form-control" rows="1" id="backup-target-s3-path">
<input type="text" placeholder="region.name" class="form-control" rows="1" id="backup-target-s3-region-name">
</div>
</div>
<div class="form-group backup-target-s3">
<label for="backup-target-s3-path" class="col-sm-2 control-label">S3 Bucket &amp; Path</label>
<div class="col-sm-8">
<input type="text" placeholder="bucket-name/backup-directory" class="form-control" rows="1" id="backup-target-s3-path">
</div>
</div>
<div class="form-group backup-target-s3">
@ -269,12 +278,12 @@ function show_custom_backup() {
$("#backup-target-rsync-host").val(spec.host);
$("#backup-target-rsync-path").val(spec.path);
} else if (r.target.substring(0, 5) == "s3://") {
const spec = url_split(r.target);
$("#backup-target-type").val("s3");
var hostpath = r.target.substring(5).split('/');
var host = hostpath.shift();
$("#backup-target-s3-host-select").val(host);
$("#backup-target-s3-host").val(host);
$("#backup-target-s3-path").val(hostpath.join('/'));
$("#backup-target-s3-host-select").val(spec.host);
$("#backup-target-s3-host").val(spec.host);
$("#backup-target-s3-region-name").val(spec.user); // stuffing the region name in the username
$("#backup-target-s3-path").val(spec.path);
} else if (r.target.substring(0, 5) == "b2://") {
$("#backup-target-type").val("b2");
var targetPath = r.target.substring(5);
@ -298,7 +307,10 @@ function set_custom_backup() {
if (target_type == "local" || target_type == "off")
target = target_type;
else if (target_type == "s3")
target = "s3://" + $("#backup-target-s3-host").val() + "/" + $("#backup-target-s3-path").val();
target = "s3://"
+ ($("#backup-target-s3-region-name").val() ? ($("#backup-target-s3-region-name").val() + "@") : "")
+ $("#backup-target-s3-host").val()
+ "/" + $("#backup-target-s3-path").val();
else if (target_type == "rsync") {
target = "rsync://" + $("#backup-target-rsync-user").val() + "@" + $("#backup-target-rsync-host").val()
+ "/" + $("#backup-target-rsync-path").val();
@ -374,4 +386,15 @@ const url_split = url => {
}
};
// Hide Copy button if not in a modern clipboard-supporting environment.
// Using document API because jQuery is not necessarily available in this script scope.
if (!(navigator && navigator.clipboard && navigator.clipboard.writeText)) {
document.getElementById('copy_pub_key_div').hidden = true;
}
function copy_pub_key_to_clipboard() {
const ssh_pub_key = $("#ssh-pub-key").val();
navigator.clipboard.writeText(ssh_pub_key);
}
</script>

View File

@ -31,9 +31,9 @@
</form>
<ul style="margin-top: 1em; padding-left: 1.5em; font-size: 90%;">
<li>Passwords must be at least eight characters consisting of English letters and numbers only. For best results, <a href="#" onclick="return generate_random_password()">generate a random password</a>.</li>
<li>Use <a href="#" onclick="return show_panel('aliases')">aliases</a> to create email addresses that forward to existing accounts.</li>
<li>Use <a href="#aliases">aliases</a> to create email addresses that forward to existing accounts.</li>
<li>Administrators get access to this control panel.</li>
<li>User accounts cannot contain any international (non-ASCII) characters, but <a href="#" onclick="return show_panel('aliases');">aliases</a> can.</li>
<li>User accounts cannot contain any international (non-ASCII) characters, but <a href="#aliases">aliases</a> can.</li>
</ul>
<h3>Existing mail users</h3>

View File

@ -10,7 +10,7 @@
<p>You can replace the default website with your own HTML pages and other static files. This control panel won&rsquo;t help you design a website, but once you have <tt>.html</tt> files you can upload them following these instructions:</p>
<ol>
<li>Ensure that any domains you are publishing a website for have no problems on the <a href="#system_status" onclick="return show_panel(this);">Status Checks</a> page.</li>
<li>Ensure that any domains you are publishing a website for have no problems on the <a href="#system_status">Status Checks</a> page.</li>
<li>On your personal computer, install an SSH file transfer program such as <a href="https://filezilla-project.org/">FileZilla</a> or <a href="http://linuxcommand.org/man_pages/scp1.html">scp</a>.</li>
@ -32,7 +32,7 @@
</tbody>
</table>
<p>To add a domain to this table, create a dummy <a href="#users" onclick="return show_panel(this);">mail user</a> or <a href="#aliases" onclick="return show_panel(this);">alias</a> on the domain first and see the <a href="https://mailinabox.email/guide.html#domain-name-configuration">setup guide</a> for adding nameserver records to the new domain at your registrar (but <i>not</i> glue records).</p>
<p>To add a domain to this table, create a dummy <a href="#users">mail user</a> or <a href="#aliases">alias</a> on the domain first and see the <a href="https://mailinabox.email/guide.html#domain-name-configuration">setup guide</a> for adding nameserver records to the new domain at your registrar (but <i>not</i> glue records).</p>
</ol>