mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2026-03-13 17:17:23 +01:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7646095b94 | ||
|
|
faf23f150c | ||
|
|
8e4e9add78 | ||
|
|
fa8c7ddef5 | ||
|
|
6d6ce25e03 | ||
|
|
371f5bc1b2 | ||
|
|
0314554207 | ||
|
|
46d55f7866 | ||
|
|
2bbc317873 | ||
|
|
28f929dc13 | ||
|
|
e419b62034 | ||
|
|
a966913963 | ||
|
|
08defb12be | ||
|
|
7be687e601 | ||
|
|
62efe985f1 | ||
|
|
df44056bae | ||
|
|
3148c621d2 | ||
|
|
81866de229 | ||
|
|
674ce92e92 | ||
|
|
c034b0f789 | ||
|
|
cd45d08409 | ||
|
|
98628622c7 | ||
|
|
8b19d15735 | ||
|
|
93380b243f | ||
|
|
fb0a3b0489 | ||
|
|
3bc9d07aeb | ||
|
|
51ed030917 | ||
|
|
e828d63a85 | ||
|
|
0ee0784bde | ||
|
|
6d43d24552 | ||
|
|
963fb9f2e6 | ||
|
|
c9584148a0 | ||
|
|
9a33f9c5ff | ||
|
|
95530affbf | ||
|
|
f72be0be7c | ||
|
|
8aa98b25b5 | ||
|
|
3c15081673 | ||
|
|
01d8e9f3b4 | ||
|
|
88260bb610 | ||
|
|
6f94412204 | ||
|
|
c77d1697a7 | ||
|
|
31bbef3401 | ||
|
|
7af713592a | ||
|
|
4408cb1fba |
64
CHANGELOG.md
64
CHANGELOG.md
@@ -1,6 +1,70 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
Version 67 (December 22, 2023)
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
* Guard against a newly published vulnerability called SMTP Smuggling. See https://sec-consult.com/blog/detail/smtp-smuggling-spoofing-e-mails-worldwide/.
|
||||||
|
|
||||||
|
Version 66 (December 17, 2023)
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
* Some users reported an error installing Mail-in-a-Box related to the virtualenv command. This is hopefully fixed.
|
||||||
|
* Roundcube is updated to 1.6.5 fixing a security vulnerability.
|
||||||
|
* For Mail-in-a-Box developers, a new setup variable is added to pull the source code from a different repository.
|
||||||
|
|
||||||
|
Version 65 (October 27, 2023)
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
* Roundcube updated to 1.6.4 fixing a security vulnerability.
|
||||||
|
* zpush.sh updated to version 2.7.1.
|
||||||
|
* Fixed a typo in the control panel.
|
||||||
|
|
||||||
|
Version 64 (September 2, 2023)
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
* Fixed broken installation when upgrading from Mail-in-a-Box version 56 (Nextcloud 22) and earlier because of an upstream packaging issue.
|
||||||
|
* 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.
|
||||||
|
* Turned off OpenDMARC diagnostic reports sent in response to incoming mail.
|
||||||
|
* Fixed some crashes when using an unrelased version of Mail-in-a-Box.
|
||||||
|
* Added z-push administration scripts.
|
||||||
|
|
||||||
|
Version 63 (July 27, 2023)
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
* Nextcloud updated to 25.0.7.
|
||||||
|
|
||||||
|
Version 62 (May 20, 2023)
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Package updates:
|
||||||
|
|
||||||
|
* Nextcloud updated to 23.0.12 (and its apps also updated).
|
||||||
|
* Roundcube updated to 1.6.1.
|
||||||
|
* Z-Push to 2.7.0, which has compatibility for Ubuntu 22.04, so it works again.
|
||||||
|
|
||||||
|
Mail:
|
||||||
|
|
||||||
|
* Roundcube's password change page is now working again.
|
||||||
|
|
||||||
|
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.
|
||||||
|
* Reverted "Improve error messages in the management tools when external command-line tools are run." because of the possibility of user secrets being included in error messages.
|
||||||
|
* Fix for TLS certificate SHA fingerprint not being displayed during setup.
|
||||||
|
|
||||||
Version 61 (January 21, 2023)
|
Version 61 (January 21, 2023)
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ Clone this repository and checkout the tag corresponding to the most recent rele
|
|||||||
|
|
||||||
$ git clone https://github.com/mail-in-a-box/mailinabox
|
$ git clone https://github.com/mail-in-a-box/mailinabox
|
||||||
$ cd mailinabox
|
$ cd mailinabox
|
||||||
$ git checkout v61
|
$ git checkout v67
|
||||||
|
|
||||||
Begin the installation.
|
Begin the installation.
|
||||||
|
|
||||||
|
|||||||
@@ -73,4 +73,9 @@
|
|||||||
rewrite ^/.well-known/carddav /cloud/remote.php/carddav/ redirect;
|
rewrite ^/.well-known/carddav /cloud/remote.php/carddav/ redirect;
|
||||||
rewrite ^/.well-known/caldav /cloud/remote.php/caldav/ redirect;
|
rewrite ^/.well-known/caldav /cloud/remote.php/caldav/ redirect;
|
||||||
|
|
||||||
|
# This addresses those service discovery issues mentioned in:
|
||||||
|
# https://docs.nextcloud.com/server/23/admin_manual/issues/general_troubleshooting.html#service-discovery
|
||||||
|
rewrite ^/.well-known/webfinger /cloud/index.php/.well-known/webfinger redirect;
|
||||||
|
rewrite ^/.well-known/nodeinfo /cloud/index.php/.well-known/nodeinfo redirect;
|
||||||
|
|
||||||
# ADDITIONAL DIRECTIVES HERE
|
# ADDITIONAL DIRECTIVES HERE
|
||||||
|
|||||||
@@ -57,10 +57,11 @@ def backup_status(env):
|
|||||||
"/usr/bin/duplicity",
|
"/usr/bin/duplicity",
|
||||||
"collection-status",
|
"collection-status",
|
||||||
"--archive-dir", backup_cache_dir,
|
"--archive-dir", backup_cache_dir,
|
||||||
"--gpg-options", "--cipher-algo=AES256",
|
"--gpg-options", "'--cipher-algo=AES256'",
|
||||||
"--log-fd", "1",
|
"--log-fd", "1",
|
||||||
get_duplicity_target_url(config),
|
] + get_duplicity_additional_args(env) + [
|
||||||
] + get_duplicity_additional_args(env),
|
get_duplicity_target_url(config)
|
||||||
|
],
|
||||||
get_duplicity_env_vars(env),
|
get_duplicity_env_vars(env),
|
||||||
trap=True)
|
trap=True)
|
||||||
if code != 0:
|
if code != 0:
|
||||||
@@ -202,7 +203,9 @@ def get_duplicity_target_url(config):
|
|||||||
# the target URL must be the bucket name. The hostname is passed
|
# the target URL must be the bucket name. The hostname is passed
|
||||||
# via get_duplicity_additional_args. Move the first part of the
|
# via get_duplicity_additional_args. Move the first part of the
|
||||||
# path (the bucket name) into the hostname URL component, and leave
|
# 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[1], target[2] = target[2].lstrip('/').split('/', 1)
|
||||||
|
|
||||||
target = urlunsplit(target)
|
target = urlunsplit(target)
|
||||||
@@ -221,17 +224,24 @@ def get_duplicity_additional_args(env):
|
|||||||
port = urlsplit(config["target"]).port
|
port = urlsplit(config["target"]).port
|
||||||
except ValueError:
|
except ValueError:
|
||||||
port = 22
|
port = 22
|
||||||
|
if port is None:
|
||||||
|
port = 22
|
||||||
|
|
||||||
return [
|
return [
|
||||||
f"--ssh-options= -i /root/.ssh/id_rsa_miab -p {port}",
|
f"--ssh-options='-i /root/.ssh/id_rsa_miab -p {port}'",
|
||||||
f"--rsync-options= -e \"/usr/bin/ssh -oStrictHostKeyChecking=no -oBatchMode=yes -p {port} -i /root/.ssh/id_rsa_miab\"",
|
f"--rsync-options='-e \"/usr/bin/ssh -oStrictHostKeyChecking=no -oBatchMode=yes -p {port} -i /root/.ssh/id_rsa_miab\"'",
|
||||||
]
|
]
|
||||||
elif get_target_type(config) == 's3':
|
elif get_target_type(config) == 's3':
|
||||||
# See note about hostname in get_duplicity_target_url.
|
# 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
|
from urllib.parse import urlsplit, urlunsplit
|
||||||
target = urlsplit(config["target"])
|
target = urlsplit(config["target"])
|
||||||
endpoint_url = urlunsplit(("https", target.netloc, '', '', ''))
|
endpoint_url = urlunsplit(("https", target.hostname, '', '', ''))
|
||||||
return ["--s3-endpoint-url", endpoint_url]
|
args = ["--s3-endpoint-url", endpoint_url]
|
||||||
|
if target.username: # region name is stuffed here
|
||||||
|
args += ["--s3-region-name", target.username]
|
||||||
|
return args
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -312,11 +322,12 @@ def perform_backup(full_backup):
|
|||||||
"--archive-dir", backup_cache_dir,
|
"--archive-dir", backup_cache_dir,
|
||||||
"--exclude", backup_root,
|
"--exclude", backup_root,
|
||||||
"--volsize", "250",
|
"--volsize", "250",
|
||||||
"--gpg-options", "--cipher-algo=AES256",
|
"--gpg-options", "'--cipher-algo=AES256'",
|
||||||
|
"--allow-source-mismatch"
|
||||||
|
] + get_duplicity_additional_args(env) + [
|
||||||
env["STORAGE_ROOT"],
|
env["STORAGE_ROOT"],
|
||||||
get_duplicity_target_url(config),
|
get_duplicity_target_url(config),
|
||||||
"--allow-source-mismatch"
|
],
|
||||||
] + get_duplicity_additional_args(env),
|
|
||||||
get_duplicity_env_vars(env))
|
get_duplicity_env_vars(env))
|
||||||
finally:
|
finally:
|
||||||
# Start services again.
|
# Start services again.
|
||||||
@@ -334,8 +345,9 @@ def perform_backup(full_backup):
|
|||||||
"--verbosity", "error",
|
"--verbosity", "error",
|
||||||
"--archive-dir", backup_cache_dir,
|
"--archive-dir", backup_cache_dir,
|
||||||
"--force",
|
"--force",
|
||||||
|
] + get_duplicity_additional_args(env) + [
|
||||||
get_duplicity_target_url(config)
|
get_duplicity_target_url(config)
|
||||||
] + get_duplicity_additional_args(env),
|
],
|
||||||
get_duplicity_env_vars(env))
|
get_duplicity_env_vars(env))
|
||||||
|
|
||||||
# From duplicity's manual:
|
# From duplicity's manual:
|
||||||
@@ -349,8 +361,9 @@ def perform_backup(full_backup):
|
|||||||
"--verbosity", "error",
|
"--verbosity", "error",
|
||||||
"--archive-dir", backup_cache_dir,
|
"--archive-dir", backup_cache_dir,
|
||||||
"--force",
|
"--force",
|
||||||
|
] + get_duplicity_additional_args(env) + [
|
||||||
get_duplicity_target_url(config)
|
get_duplicity_target_url(config)
|
||||||
] + get_duplicity_additional_args(env),
|
],
|
||||||
get_duplicity_env_vars(env))
|
get_duplicity_env_vars(env))
|
||||||
|
|
||||||
# Change ownership of backups to the user-data user, so that the after-bcakup
|
# Change ownership of backups to the user-data user, so that the after-bcakup
|
||||||
@@ -387,9 +400,10 @@ def run_duplicity_verification():
|
|||||||
"--compare-data",
|
"--compare-data",
|
||||||
"--archive-dir", backup_cache_dir,
|
"--archive-dir", backup_cache_dir,
|
||||||
"--exclude", backup_root,
|
"--exclude", backup_root,
|
||||||
|
] + get_duplicity_additional_args(env) + [
|
||||||
get_duplicity_target_url(config),
|
get_duplicity_target_url(config),
|
||||||
env["STORAGE_ROOT"],
|
env["STORAGE_ROOT"],
|
||||||
] + get_duplicity_additional_args(env), get_duplicity_env_vars(env))
|
], get_duplicity_env_vars(env))
|
||||||
|
|
||||||
def run_duplicity_restore(args):
|
def run_duplicity_restore(args):
|
||||||
env = load_environment()
|
env = load_environment()
|
||||||
@@ -399,10 +413,24 @@ def run_duplicity_restore(args):
|
|||||||
"/usr/bin/duplicity",
|
"/usr/bin/duplicity",
|
||||||
"restore",
|
"restore",
|
||||||
"--archive-dir", backup_cache_dir,
|
"--archive-dir", backup_cache_dir,
|
||||||
get_duplicity_target_url(config),
|
] + get_duplicity_additional_args(env) + [
|
||||||
] + get_duplicity_additional_args(env) + args,
|
get_duplicity_target_url(config)
|
||||||
|
] + args,
|
||||||
get_duplicity_env_vars(env))
|
get_duplicity_env_vars(env))
|
||||||
|
|
||||||
|
def print_duplicity_command():
|
||||||
|
import shlex
|
||||||
|
env = load_environment()
|
||||||
|
config = get_backup_config(env)
|
||||||
|
backup_cache_dir = os.path.join(env["STORAGE_ROOT"], 'backup', 'cache')
|
||||||
|
for k, v in get_duplicity_env_vars(env).items():
|
||||||
|
print(f"export {k}={shlex.quote(v)}")
|
||||||
|
print("duplicity", "{command}", shlex.join([
|
||||||
|
"--archive-dir", backup_cache_dir,
|
||||||
|
] + get_duplicity_additional_args(env) + [
|
||||||
|
get_duplicity_target_url(config)
|
||||||
|
]))
|
||||||
|
|
||||||
def list_target_files(config):
|
def list_target_files(config):
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
try:
|
try:
|
||||||
@@ -424,6 +452,8 @@ def list_target_files(config):
|
|||||||
port = target.port
|
port = target.port
|
||||||
except ValueError:
|
except ValueError:
|
||||||
port = 22
|
port = 22
|
||||||
|
if port is None:
|
||||||
|
port = 22
|
||||||
|
|
||||||
target_path = target.path
|
target_path = target.path
|
||||||
if not target_path.endswith('/'):
|
if not target_path.endswith('/'):
|
||||||
@@ -498,7 +528,7 @@ def list_target_files(config):
|
|||||||
|
|
||||||
# Extract information from target
|
# Extract information from target
|
||||||
b2_application_keyid = target.netloc[:target.netloc.index(':')]
|
b2_application_keyid = target.netloc[:target.netloc.index(':')]
|
||||||
b2_application_key = target.netloc[target.netloc.index(':')+1:target.netloc.index('@')]
|
b2_application_key = urllib.parse.unquote(target.netloc[target.netloc.index(':')+1:target.netloc.index('@')])
|
||||||
b2_bucket = target.netloc[target.netloc.index('@')+1:]
|
b2_bucket = target.netloc[target.netloc.index('@')+1:]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -607,6 +637,9 @@ if __name__ == "__main__":
|
|||||||
# to duplicity. The restore path should be specified.
|
# to duplicity. The restore path should be specified.
|
||||||
run_duplicity_restore(sys.argv[2:])
|
run_duplicity_restore(sys.argv[2:])
|
||||||
|
|
||||||
|
elif sys.argv[-1] == "--duplicity-command":
|
||||||
|
print_duplicity_command()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Perform a backup. Add --full to force a full backup rather than
|
# Perform a backup. Add --full to force a full backup rather than
|
||||||
# possibly performing an incremental backup.
|
# possibly performing an incremental backup.
|
||||||
|
|||||||
@@ -709,7 +709,7 @@ def munin_cgi(filename):
|
|||||||
support infrastructure like spawn-fcgi.
|
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
|
# su changes user, we use the munin user here
|
||||||
# --preserve-environment retains the environment, which is where Popen's `env` data is
|
# --preserve-environment retains the environment, which is where Popen's `env` data is
|
||||||
# --shell=/bin/bash ensures the shell used is bash
|
# --shell=/bin/bash ensures the shell used is bash
|
||||||
|
|||||||
@@ -465,7 +465,7 @@ def build_sshfp_records():
|
|||||||
pass
|
pass
|
||||||
break
|
break
|
||||||
|
|
||||||
keys = shell("check_output", ["ssh-keyscan", "-t", "rsa,dsa,ecdsa,ed25519", "-p", str(port), "localhost"])
|
keys = shell("check_output", ["ssh-keyscan", "-4", "-t", "rsa,dsa,ecdsa,ed25519", "-p", str(port), "localhost"])
|
||||||
keys = sorted(keys.split("\n"))
|
keys = sorted(keys.split("\n"))
|
||||||
|
|
||||||
for key in keys:
|
for key in keys:
|
||||||
@@ -1005,32 +1005,33 @@ def get_secondary_dns(custom_dns, mode=None):
|
|||||||
values.append(hostname)
|
values.append(hostname)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# This is a hostname. Before including in zone xfr lines,
|
# If the entry starts with "xfr:" only include it in the zone transfer settings.
|
||||||
# resolve to an IP address. Otherwise just return the hostname.
|
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
|
# It may not resolve to IPv6, so don't throw an exception if it
|
||||||
# doesn't.
|
# doesn't. Skip the entry if there is a DNS error.
|
||||||
if not hostname.startswith("xfr:"):
|
|
||||||
if mode == "xfr":
|
if mode == "xfr":
|
||||||
try:
|
try:
|
||||||
response = resolver.resolve(hostname+'.', "A", raise_on_no_answer=False)
|
ipaddress.ip_interface(hostname) # test if it's an IP address or CIDR notation
|
||||||
values.extend(map(str, response))
|
|
||||||
except dns.exception.DNSException:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = resolver.resolve(hostname+'.', "AAAA", raise_on_no_answer=False)
|
|
||||||
values.extend(map(str, response))
|
|
||||||
except dns.exception.DNSException:
|
|
||||||
pass
|
|
||||||
continue
|
|
||||||
values.append(hostname)
|
values.append(hostname)
|
||||||
|
except ValueError:
|
||||||
|
try:
|
||||||
|
response = dns.resolver.resolve(hostname+'.', "A", raise_on_no_answer=False)
|
||||||
|
values.extend(map(str, response))
|
||||||
|
except dns.exception.DNSException:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
response = dns.resolver.resolve(hostname+'.', "AAAA", raise_on_no_answer=False)
|
||||||
|
values.extend(map(str, response))
|
||||||
|
except dns.exception.DNSException:
|
||||||
|
pass
|
||||||
|
|
||||||
# This is a zone-xfer-only IP address. Do not return if
|
else:
|
||||||
# we're querying for NS record hostnames. Only return if
|
values.append(hostname)
|
||||||
# we're querying for zone xfer IP addresses - return the
|
|
||||||
# IP address.
|
|
||||||
elif mode == "xfr":
|
|
||||||
values.append(hostname[4:])
|
|
||||||
|
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ msg['From'] = "\"%s\" <%s>" % (env['PRIMARY_HOSTNAME'], admin_addr)
|
|||||||
msg['To'] = admin_addr
|
msg['To'] = admin_addr
|
||||||
msg['Subject'] = "[%s] %s" % (env['PRIMARY_HOSTNAME'], subject)
|
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, 'plain'))
|
||||||
msg.attach(MIMEText(content_html, 'html'))
|
msg.attach(MIMEText(content_html, 'html'))
|
||||||
|
|||||||
@@ -912,11 +912,11 @@ def list_apt_updates(apt_update=True):
|
|||||||
return pkgs
|
return pkgs
|
||||||
|
|
||||||
def what_version_is_this(env):
|
def what_version_is_this(env):
|
||||||
# This function runs `git describe --abbrev=0` on the Mail-in-a-Box installation directory.
|
# This function runs `git describe --always --abbrev=0` on the Mail-in-a-Box installation directory.
|
||||||
# Git may not be installed and Mail-in-a-Box may not have been cloned from github,
|
# Git may not be installed and Mail-in-a-Box may not have been cloned from github,
|
||||||
# so this function may raise all sorts of exceptions.
|
# so this function may raise all sorts of exceptions.
|
||||||
miab_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
miab_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
tag = shell("check_output", ["/usr/bin/git", "describe", "--abbrev=0"], env={"GIT_DIR": os.path.join(miab_dir, '.git')}).strip()
|
tag = shell("check_output", ["/usr/bin/git", "describe", "--always", "--abbrev=0"], env={"GIT_DIR": os.path.join(miab_dir, '.git')}).strip()
|
||||||
return tag
|
return tag
|
||||||
|
|
||||||
def get_latest_miab_version():
|
def get_latest_miab_version():
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<h3>Add a mail alias</h3>
|
<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>
|
<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>
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
|
|
||||||
<h3>Using a secondary nameserver</h3>
|
<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 “slave”) 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 “slave”) 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>
|
<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;">
|
<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">
|
<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 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>
|
||||||
<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.
|
||||||
|
|||||||
@@ -112,30 +112,30 @@
|
|||||||
<li class="dropdown if-logged-in-admin">
|
<li class="dropdown if-logged-in-admin">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">System <b class="caret"></b></a>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown">System <b class="caret"></b></a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="#system_status" onclick="return show_panel(this);">Status Checks</a></li>
|
<li><a href="#system_status">Status Checks</a></li>
|
||||||
<li><a href="#tls" onclick="return show_panel(this);">TLS (SSL) Certificates</a></li>
|
<li><a href="#tls">TLS (SSL) Certificates</a></li>
|
||||||
<li><a href="#system_backup" onclick="return show_panel(this);">Backup Status</a></li>
|
<li><a href="#system_backup">Backup Status</a></li>
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<li class="dropdown-header">Advanced Pages</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="#custom_dns">Custom DNS</a></li>
|
||||||
<li><a href="#external_dns" onclick="return show_panel(this);">External DNS</a></li>
|
<li><a href="#external_dns">External DNS</a></li>
|
||||||
<li><a href="#munin" onclick="return show_panel(this);">Munin Monitoring</a></li>
|
<li><a href="#munin">Munin Monitoring</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</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">
|
<li class="dropdown if-logged-in-admin">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Mail & Users <b class="caret"></b></a>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Mail & Users <b class="caret"></b></a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="#mail-guide" onclick="return show_panel(this);">Instructions</a></li>
|
<li><a href="#mail-guide">Instructions</a></li>
|
||||||
<li><a href="#users" onclick="return show_panel(this);">Users</a></li>
|
<li><a href="#users">Users</a></li>
|
||||||
<li><a href="#aliases" onclick="return show_panel(this);">Aliases</a></li>
|
<li><a href="#aliases">Aliases</a></li>
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<li class="dropdown-header">Your Account</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>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#sync_guide" onclick="return show_panel(this);" class="if-logged-in">Contacts/Calendar</a></li>
|
<li><a href="#sync_guide" 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="#web" class="if-logged-in-admin">Web</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<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>
|
<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) {
|
function show_panel(panelid) {
|
||||||
if (panelid.getAttribute)
|
if (panelid.getAttribute) {
|
||||||
// we might be passed an HTMLElement <a>.
|
// we might be passed an HTMLElement <a>.
|
||||||
panelid = panelid.getAttribute('href').substring(1);
|
panelid = panelid.getAttribute('href').substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
$('.admin_panel').hide();
|
$('.admin_panel').hide();
|
||||||
$('#panel_' + panelid).show();
|
$('#panel_' + panelid).show();
|
||||||
if (typeof localStorage != 'undefined')
|
|
||||||
localStorage.setItem("miab-cp-lastpanel", panelid);
|
|
||||||
if (window["show_" + panelid])
|
if (window["show_" + panelid])
|
||||||
window["show_" + panelid]();
|
window["show_" + panelid]();
|
||||||
|
|
||||||
current_panel = panelid;
|
current_panel = panelid;
|
||||||
switch_back_to_panel = null;
|
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() {
|
$(function() {
|
||||||
// Recall saved user credentials.
|
// Recall saved user credentials.
|
||||||
try {
|
try {
|
||||||
@@ -452,8 +454,9 @@ $(function() {
|
|||||||
show_hide_menus();
|
show_hide_menus();
|
||||||
|
|
||||||
// Recall what the user was last looking at.
|
// Recall what the user was last looking at.
|
||||||
if (api_credentials != null && typeof localStorage != 'undefined' && localStorage.getItem("miab-cp-lastpanel")) {
|
if (api_credentials != null && window.location.hash) {
|
||||||
show_panel(localStorage.getItem("miab-cp-lastpanel"));
|
var panelid = window.location.hash.substring(1);
|
||||||
|
show_panel(panelid);
|
||||||
} else if (api_credentials != null) {
|
} else if (api_credentials != null) {
|
||||||
show_panel('welcome');
|
show_panel('welcome');
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -168,7 +168,18 @@ function do_login() {
|
|||||||
// Open the next panel the user wants to go to. Do this after the XHR response
|
// 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,
|
// is over so that we don't start a new XHR request while this one is finishing,
|
||||||
// which confuses the loading indicator.
|
// 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,
|
undefined,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
<tr><th>Password:</th> <td>Your mail password.</td></tr>
|
<tr><th>Password:</th> <td>Your mail password.</td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p>In addition to setting up your email, you’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’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>
|
<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>
|
||||||
|
|
||||||
|
|||||||
@@ -17,14 +17,14 @@
|
|||||||
<tr><th>Calendar</td> <td><a href="https://{{hostname}}/cloud/calendar">https://{{hostname}}/cloud/calendar</a></td></tr>
|
<tr><th>Calendar</td> <td><a href="https://{{hostname}}/cloud/calendar">https://{{hostname}}/cloud/calendar</a></td></tr>
|
||||||
</table>
|
</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>
|
complete email address and your mail password.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<h4>On your mobile device</h4>
|
<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>
|
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>
|
<p>Otherwise, here are some apps that can synchronize your contacts and calendar to your Android phone.</p>
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<h2>Backup Status</h2>
|
<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 Amazon Web Services S3-compatible service, or other options.</p>
|
||||||
|
|
||||||
<h3>Configuration</h3>
|
<h3>Configuration</h3>
|
||||||
|
|
||||||
@@ -70,9 +70,12 @@
|
|||||||
<div class="small" style="margin-top: 2px">
|
<div class="small" style="margin-top: 2px">
|
||||||
Copy the Public SSH Key above, and paste it within the <tt>~/.ssh/authorized_keys</tt>
|
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
|
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>
|
</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>
|
</div>
|
||||||
<!-- S3 BACKUP -->
|
<!-- S3 BACKUP -->
|
||||||
<div class="form-group backup-target-s3">
|
<div class="form-group backup-target-s3">
|
||||||
@@ -95,13 +98,19 @@
|
|||||||
<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 Host / Endpoint</label>
|
<label for="backup-target-s3-host" class="col-sm-2 control-label">S3 Host / Endpoint</label>
|
||||||
<div class="col-sm-8">
|
<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>
|
</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-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">
|
<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 & 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>
|
</div>
|
||||||
<div class="form-group backup-target-s3">
|
<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-host").val(spec.host);
|
||||||
$("#backup-target-rsync-path").val(spec.path);
|
$("#backup-target-rsync-path").val(spec.path);
|
||||||
} else if (r.target.substring(0, 5) == "s3://") {
|
} else if (r.target.substring(0, 5) == "s3://") {
|
||||||
|
const spec = url_split(r.target);
|
||||||
$("#backup-target-type").val("s3");
|
$("#backup-target-type").val("s3");
|
||||||
var hostpath = r.target.substring(5).split('/');
|
$("#backup-target-s3-host-select").val(spec.host);
|
||||||
var host = hostpath.shift();
|
$("#backup-target-s3-host").val(spec.host);
|
||||||
$("#backup-target-s3-host-select").val(host);
|
$("#backup-target-s3-region-name").val(spec.user); // stuffing the region name in the username
|
||||||
$("#backup-target-s3-host").val(host);
|
$("#backup-target-s3-path").val(spec.path);
|
||||||
$("#backup-target-s3-path").val(hostpath.join('/'));
|
|
||||||
} else if (r.target.substring(0, 5) == "b2://") {
|
} else if (r.target.substring(0, 5) == "b2://") {
|
||||||
$("#backup-target-type").val("b2");
|
$("#backup-target-type").val("b2");
|
||||||
var targetPath = r.target.substring(5);
|
var targetPath = r.target.substring(5);
|
||||||
@@ -282,7 +291,7 @@ function show_custom_backup() {
|
|||||||
var b2_applicationkey = targetPath.split(':')[1].split('@')[0];
|
var b2_applicationkey = targetPath.split(':')[1].split('@')[0];
|
||||||
var b2_bucket = targetPath.split('@')[1];
|
var b2_bucket = targetPath.split('@')[1];
|
||||||
$("#backup-target-b2-user").val(b2_application_keyid);
|
$("#backup-target-b2-user").val(b2_application_keyid);
|
||||||
$("#backup-target-b2-pass").val(b2_applicationkey);
|
$("#backup-target-b2-pass").val(decodeURIComponent(b2_applicationkey));
|
||||||
$("#backup-target-b2-bucket").val(b2_bucket);
|
$("#backup-target-b2-bucket").val(b2_bucket);
|
||||||
}
|
}
|
||||||
toggle_form()
|
toggle_form()
|
||||||
@@ -298,13 +307,16 @@ function set_custom_backup() {
|
|||||||
if (target_type == "local" || target_type == "off")
|
if (target_type == "local" || target_type == "off")
|
||||||
target = target_type;
|
target = target_type;
|
||||||
else if (target_type == "s3")
|
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") {
|
else if (target_type == "rsync") {
|
||||||
target = "rsync://" + $("#backup-target-rsync-user").val() + "@" + $("#backup-target-rsync-host").val()
|
target = "rsync://" + $("#backup-target-rsync-user").val() + "@" + $("#backup-target-rsync-host").val()
|
||||||
+ "/" + $("#backup-target-rsync-path").val();
|
+ "/" + $("#backup-target-rsync-path").val();
|
||||||
target_user = '';
|
target_user = '';
|
||||||
} else if (target_type == "b2") {
|
} else if (target_type == "b2") {
|
||||||
target = 'b2://' + $('#backup-target-b2-user').val() + ':' + $('#backup-target-b2-pass').val()
|
target = 'b2://' + $('#backup-target-b2-user').val() + ':' + encodeURIComponent($('#backup-target-b2-pass').val())
|
||||||
+ '@' + $('#backup-target-b2-bucket').val()
|
+ '@' + $('#backup-target-b2-bucket').val()
|
||||||
target_user = '';
|
target_user = '';
|
||||||
target_pass = '';
|
target_pass = '';
|
||||||
@@ -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>
|
</script>
|
||||||
|
|||||||
@@ -10,13 +10,13 @@
|
|||||||
border-top: none;
|
border-top: none;
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
#system-checks .status-error td {
|
#system-checks .status-error td, .summary-error {
|
||||||
color: #733;
|
color: #733;
|
||||||
}
|
}
|
||||||
#system-checks .status-warning td {
|
#system-checks .status-warning td, .summary-warning {
|
||||||
color: #770;
|
color: #770;
|
||||||
}
|
}
|
||||||
#system-checks .status-ok td {
|
#system-checks .status-ok td, .summary-ok {
|
||||||
color: #040;
|
color: #040;
|
||||||
}
|
}
|
||||||
#system-checks div.extra {
|
#system-checks div.extra {
|
||||||
@@ -52,6 +52,9 @@
|
|||||||
</div> <!-- /col -->
|
</div> <!-- /col -->
|
||||||
<div class="col-md-pull-3 col-md-8">
|
<div class="col-md-pull-3 col-md-8">
|
||||||
|
|
||||||
|
<div id="system-checks-summary">
|
||||||
|
</div>
|
||||||
|
|
||||||
<table id="system-checks" class="table" style="max-width: 60em">
|
<table id="system-checks" class="table" style="max-width: 60em">
|
||||||
<thead>
|
<thead>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -64,6 +67,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
function show_system_status() {
|
function show_system_status() {
|
||||||
|
const summary = $('#system-checks-summary');
|
||||||
|
summary.html("");
|
||||||
|
|
||||||
$('#system-checks tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
|
$('#system-checks tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
|
||||||
|
|
||||||
api(
|
api(
|
||||||
@@ -93,6 +99,12 @@ function show_system_status() {
|
|||||||
{ },
|
{ },
|
||||||
function(r) {
|
function(r) {
|
||||||
$('#system-checks tbody').html("");
|
$('#system-checks tbody').html("");
|
||||||
|
const ok_symbol = "✓";
|
||||||
|
const error_symbol = "✖";
|
||||||
|
const warning_symbol = "?";
|
||||||
|
|
||||||
|
let count_by_status = { ok: 0, error: 0, warning: 0 };
|
||||||
|
|
||||||
for (var i = 0; i < r.length; i++) {
|
for (var i = 0; i < r.length; i++) {
|
||||||
var n = $("<tr><td class='status'/><td class='message'><p style='margin: 0'/><div class='extra'/><a class='showhide' href='#'/></tr>");
|
var n = $("<tr><td class='status'/><td class='message'><p style='margin: 0'/><div class='extra'/><a class='showhide' href='#'/></tr>");
|
||||||
if (i == 0) n.addClass('first')
|
if (i == 0) n.addClass('first')
|
||||||
@@ -100,9 +112,12 @@ function show_system_status() {
|
|||||||
n.addClass(r[i].type)
|
n.addClass(r[i].type)
|
||||||
else
|
else
|
||||||
n.addClass("status-" + r[i].type)
|
n.addClass("status-" + r[i].type)
|
||||||
if (r[i].type == "ok") n.find('td.status').text("✓")
|
|
||||||
if (r[i].type == "error") n.find('td.status').text("✖")
|
if (r[i].type == "ok") n.find('td.status').text(ok_symbol);
|
||||||
if (r[i].type == "warning") n.find('td.status').text("?")
|
if (r[i].type == "error") n.find('td.status').text(error_symbol);
|
||||||
|
if (r[i].type == "warning") n.find('td.status').text(warning_symbol);
|
||||||
|
count_by_status[r[i].type]++;
|
||||||
|
|
||||||
n.find('td.message p').text(r[i].text)
|
n.find('td.message p').text(r[i].text)
|
||||||
$('#system-checks tbody').append(n);
|
$('#system-checks tbody').append(n);
|
||||||
|
|
||||||
@@ -122,8 +137,17 @@ function show_system_status() {
|
|||||||
n.find('> td.message > div').append(m);
|
n.find('> td.message > div').append(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
|
// Summary counts
|
||||||
|
summary.html("Summary: ");
|
||||||
|
if (count_by_status['error'] + count_by_status['warning'] == 0) {
|
||||||
|
summary.append($('<span class="summary-ok"/>').text(`All ${count_by_status['ok']} ${ok_symbol} OK`));
|
||||||
|
} else {
|
||||||
|
summary.append($('<span class="summary-ok"/>').text(`${count_by_status['ok']} ${ok_symbol} OK, `));
|
||||||
|
summary.append($('<span class="summary-error"/>').text(`${count_by_status['error']} ${error_symbol} Error, `));
|
||||||
|
summary.append($('<span class="summary-warning"/>').text(`${count_by_status['warning']} ${warning_symbol} Warning`));
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var current_privacy_setting = null;
|
var current_privacy_setting = null;
|
||||||
|
|||||||
@@ -31,9 +31,9 @@
|
|||||||
</form>
|
</form>
|
||||||
<ul style="margin-top: 1em; padding-left: 1.5em; font-size: 90%;">
|
<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>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>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>
|
</ul>
|
||||||
|
|
||||||
<h3>Existing mail users</h3>
|
<h3>Existing mail users</h3>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<p>You can replace the default website with your own HTML pages and other static files. This control panel won’t help you design a website, but once you have <tt>.html</tt> files you can upload them following these instructions:</p>
|
<p>You can replace the default website with your own HTML pages and other static files. This control panel won’t help you design a website, but once you have <tt>.html</tt> files you can upload them following these instructions:</p>
|
||||||
|
|
||||||
<ol>
|
<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>
|
<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>
|
</tbody>
|
||||||
</table>
|
</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>
|
</ol>
|
||||||
|
|
||||||
|
|||||||
@@ -122,16 +122,13 @@ def shell(method, cmd_args, env={}, capture_stderr=False, return_bytes=False, tr
|
|||||||
if method == "check_output" and input is not None:
|
if method == "check_output" and input is not None:
|
||||||
kwargs['input'] = input
|
kwargs['input'] = input
|
||||||
|
|
||||||
|
if not trap:
|
||||||
|
ret = getattr(subprocess, method)(cmd_args, **kwargs)
|
||||||
|
else:
|
||||||
try:
|
try:
|
||||||
ret = getattr(subprocess, method)(cmd_args, **kwargs)
|
ret = getattr(subprocess, method)(cmd_args, **kwargs)
|
||||||
code = 0
|
code = 0
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
if not trap:
|
|
||||||
# Reformat exception.
|
|
||||||
msg = "Command failed with exit code {}: {}".format(e.returncode, subprocess.list2cmdline(cmd_args))
|
|
||||||
if e.output: msg += "\n\nOutput:\n" + e.output
|
|
||||||
raise Exception(msg)
|
|
||||||
else:
|
|
||||||
ret = e.output
|
ret = e.output
|
||||||
code = e.returncode
|
code = e.returncode
|
||||||
if not return_bytes and isinstance(ret, bytes): ret = ret.decode("utf8")
|
if not return_bytes and isinstance(ret, bytes): ret = ret.decode("utf8")
|
||||||
|
|||||||
@@ -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=v61
|
TAG=v67
|
||||||
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.
|
||||||
@@ -59,10 +59,14 @@ if [ ! -d $HOME/mailinabox ]; then
|
|||||||
echo
|
echo
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "$SOURCE" == "" ]; then
|
||||||
|
SOURCE=https://github.com/mail-in-a-box/mailinabox
|
||||||
|
fi
|
||||||
|
|
||||||
echo Downloading Mail-in-a-Box $TAG. . .
|
echo Downloading Mail-in-a-Box $TAG. . .
|
||||||
git clone \
|
git clone \
|
||||||
-b $TAG --depth 1 \
|
-b $TAG --depth 1 \
|
||||||
https://github.com/mail-in-a-box/mailinabox \
|
$SOURCE \
|
||||||
$HOME/mailinabox \
|
$HOME/mailinabox \
|
||||||
< /dev/null 2> /dev/null
|
< /dev/null 2> /dev/null
|
||||||
|
|
||||||
@@ -73,7 +77,7 @@ fi
|
|||||||
cd $HOME/mailinabox
|
cd $HOME/mailinabox
|
||||||
|
|
||||||
# Update it.
|
# Update it.
|
||||||
if [ "$TAG" != $(git describe) ]; then
|
if [ "$TAG" != $(git describe --always) ]; then
|
||||||
echo Updating Mail-in-a-Box to $TAG . . .
|
echo Updating Mail-in-a-Box to $TAG . . .
|
||||||
git fetch --depth 1 --force --prune origin tag $TAG
|
git fetch --depth 1 --force --prune origin tag $TAG
|
||||||
if ! git checkout -q $TAG; then
|
if ! git checkout -q $TAG; then
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ chmod go-rwx $STORAGE_ROOT/mail/dkim
|
|||||||
tools/editconf.py /etc/opendmarc.conf -s \
|
tools/editconf.py /etc/opendmarc.conf -s \
|
||||||
"Syslog=true" \
|
"Syslog=true" \
|
||||||
"Socket=inet:8893@[127.0.0.1]" \
|
"Socket=inet:8893@[127.0.0.1]" \
|
||||||
"FailureReports=true"
|
"FailureReports=false"
|
||||||
|
|
||||||
# SPFIgnoreResults causes the filter to ignore any SPF results in the header
|
# SPFIgnoreResults causes the filter to ignore any SPF results in the header
|
||||||
# of the message. This is useful if you want the filter to perfrom SPF checks
|
# of the message. This is useful if you want the filter to perfrom SPF checks
|
||||||
@@ -82,11 +82,11 @@ tools/editconf.py /etc/opendmarc.conf -s \
|
|||||||
tools/editconf.py /etc/opendmarc.conf -s \
|
tools/editconf.py /etc/opendmarc.conf -s \
|
||||||
"SPFSelfValidate=true"
|
"SPFSelfValidate=true"
|
||||||
|
|
||||||
# Enables generation of failure reports for sending domains that publish a
|
# Disables generation of failure reports for sending domains that publish a
|
||||||
# "none" policy.
|
# "none" policy.
|
||||||
|
|
||||||
tools/editconf.py /etc/opendmarc.conf -s \
|
tools/editconf.py /etc/opendmarc.conf -s \
|
||||||
"FailureReportsOnNone=true"
|
"FailureReportsOnNone=false"
|
||||||
|
|
||||||
# AlwaysAddARHeader Adds an "Authentication-Results:" header field even to
|
# AlwaysAddARHeader Adds an "Authentication-Results:" header field even to
|
||||||
# unsigned messages from domains with no "signs all" policy. The reported DKIM
|
# unsigned messages from domains with no "signs all" policy. The reported DKIM
|
||||||
|
|||||||
@@ -69,6 +69,11 @@ tools/editconf.py /etc/postfix/main.cf \
|
|||||||
maximal_queue_lifetime=2d \
|
maximal_queue_lifetime=2d \
|
||||||
bounce_queue_lifetime=1d
|
bounce_queue_lifetime=1d
|
||||||
|
|
||||||
|
# Guard against SMTP smuggling
|
||||||
|
# This short-term workaround is recommended at https://www.postfix.org/smtp-smuggling.html
|
||||||
|
tools/editconf.py /etc/postfix/main.cf \
|
||||||
|
smtpd_data_restrictions=reject_unauth_pipelining
|
||||||
|
|
||||||
# ### Outgoing Mail
|
# ### Outgoing Mail
|
||||||
|
|
||||||
# Enable the 'submission' ports 465 and 587 and tweak their settings.
|
# Enable the 'submission' ports 465 and 587 and tweak their settings.
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ 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
|
||||||
|
# A bug specific to Ubuntu 22.04 and Python 3.10 requires
|
||||||
|
# forcing a virtualenv directory layout option (see #2335
|
||||||
|
# and https://github.com/pypa/virtualenv/pull/2415). In
|
||||||
|
# our issue, reportedly installing python3-distutils didn't
|
||||||
|
# fix the problem.)
|
||||||
|
export DEB_PYTHON_INSTALL_LAYOUT='deb'
|
||||||
hide_output virtualenv -ppython3 $venv
|
hide_output virtualenv -ppython3 $venv
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ echo "Installing Nextcloud (contacts/calendar)..."
|
|||||||
# we automatically install intermediate versions as needed.
|
# we automatically install intermediate versions as needed.
|
||||||
# * The hash is the SHA1 hash of the ZIP package, which you can find by just running this script and
|
# * The hash is the SHA1 hash of the ZIP package, which you can find by just running this script and
|
||||||
# copying it from the error message when it doesn't match what is below.
|
# copying it from the error message when it doesn't match what is below.
|
||||||
nextcloud_ver=23.0.10
|
nextcloud_ver=25.0.7
|
||||||
nextcloud_hash=8831c7862e39460fbb789bacac8729fab0ba02dd
|
nextcloud_hash=a5a565c916355005c7b408dd41a1e53505e1a080
|
||||||
|
|
||||||
# Nextcloud apps
|
# Nextcloud apps
|
||||||
# --------------
|
# --------------
|
||||||
@@ -33,12 +33,16 @@ nextcloud_hash=8831c7862e39460fbb789bacac8729fab0ba02dd
|
|||||||
# https://github.com/nextcloud/user_external/blob/master/appinfo/info.xml
|
# https://github.com/nextcloud/user_external/blob/master/appinfo/info.xml
|
||||||
# * The hash is the SHA1 hash of the ZIP package, which you can find by just running this script and
|
# * The hash is the SHA1 hash of the ZIP package, which you can find by just running this script and
|
||||||
# copying it from the error message when it doesn't match what is below.
|
# copying it from the error message when it doesn't match what is below.
|
||||||
contacts_ver=4.2.2
|
contacts_ver=5.3.0
|
||||||
contacts_hash=ca13d608ed8955aa374cb4f31b6026b57ef88887
|
contacts_hash=4b0a6666374e3b55cfd2ae9b72e1d458b87d4c8c
|
||||||
calendar_ver=3.5.1
|
|
||||||
calendar_hash=c8136a3deb872a3ef73ce1155b58f3ab27ec7110
|
# Always ensure the versions are supported, see https://apps.nextcloud.com/apps/calendar
|
||||||
user_external_ver=3.0.0
|
calendar_ver=4.4.2
|
||||||
user_external_hash=0df781b261f55bbde73d8c92da3f99397000972f
|
calendar_hash=21a42e15806adc9b2618760ef94f1797ef399e2f
|
||||||
|
|
||||||
|
# And https://apps.nextcloud.com/apps/user_external
|
||||||
|
user_external_ver=3.2.0
|
||||||
|
user_external_hash=a494073dcdecbbbc79a9c77f72524ac9994d2eec
|
||||||
|
|
||||||
# Clear prior packages and install dependencies from apt.
|
# Clear prior packages and install dependencies from apt.
|
||||||
|
|
||||||
@@ -128,6 +132,7 @@ 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$PHP_VER /usr/local/lib/owncloud/occ db:add-missing-indices
|
sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ db:add-missing-indices
|
||||||
|
sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ db:add-missing-primary-keys
|
||||||
|
|
||||||
# Run conversion to BigInt identifiers, this process may take some time on large tables.
|
# Run conversion to BigInt identifiers, this process may take some time on large tables.
|
||||||
sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ db:convert-filecache-bigint --no-interaction
|
sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ db:convert-filecache-bigint --no-interaction
|
||||||
@@ -173,6 +178,12 @@ if [ ! -d /usr/local/lib/owncloud/ ] || [[ ! ${CURRENT_NEXTCLOUD_VER} =~ ^$nextc
|
|||||||
if [ ! -z ${CURRENT_NEXTCLOUD_VER} ]; 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 [ -e $STORAGE_ROOT/owncloud/config.php ]; then
|
||||||
|
# Remove the read-onlyness of the config, which is needed for migrations, especially for v24
|
||||||
|
sed -i -e '/config_is_read_only/d' $STORAGE_ROOT/owncloud/config.php
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ ${CURRENT_NEXTCLOUD_VER} =~ ^[89] ]]; 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 will continue, but skip the Nextcloud migration."
|
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 will continue, but skip the Nextcloud migration."
|
||||||
return 0
|
return 0
|
||||||
@@ -183,6 +194,7 @@ if [ ! -d /usr/local/lib/owncloud/ ] || [[ ! ${CURRENT_NEXTCLOUD_VER} =~ ^$nextc
|
|||||||
echo "Upgrades from Mail-in-a-Box prior to v60 with Nextcloud 19 or earlier are not supported. Upgrade to the latest Mail-in-a-Box version supported on your machine first. Setup will continue, but skip the Nextcloud migration."
|
echo "Upgrades from Mail-in-a-Box prior to v60 with Nextcloud 19 or earlier are not supported. Upgrade to the latest Mail-in-a-Box version supported on your machine first. Setup will continue, but skip the Nextcloud migration."
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ${CURRENT_NEXTCLOUD_VER} =~ ^20 ]]; then
|
if [[ ${CURRENT_NEXTCLOUD_VER} =~ ^20 ]]; then
|
||||||
InstallNextcloud 21.0.7 f5c7079c5b56ce1e301c6a27c0d975d608bb01c9 4.0.7 45e7cf4bfe99cd8d03625cf9e5a1bb2e90549136 3.0.4 d0284b68135777ec9ca713c307216165b294d0fe
|
InstallNextcloud 21.0.7 f5c7079c5b56ce1e301c6a27c0d975d608bb01c9 4.0.7 45e7cf4bfe99cd8d03625cf9e5a1bb2e90549136 3.0.4 d0284b68135777ec9ca713c307216165b294d0fe
|
||||||
CURRENT_NEXTCLOUD_VER="21.0.7"
|
CURRENT_NEXTCLOUD_VER="21.0.7"
|
||||||
@@ -191,6 +203,14 @@ if [ ! -d /usr/local/lib/owncloud/ ] || [[ ! ${CURRENT_NEXTCLOUD_VER} =~ ^$nextc
|
|||||||
InstallNextcloud 22.2.6 9d39741f051a8da42ff7df46ceef2653a1dc70d9 4.1.0 697f6b4a664e928d72414ea2731cb2c9d1dc3077 3.2.2 ce4030ab57f523f33d5396c6a81396d440756f5f 3.0.0 0df781b261f55bbde73d8c92da3f99397000972f
|
InstallNextcloud 22.2.6 9d39741f051a8da42ff7df46ceef2653a1dc70d9 4.1.0 697f6b4a664e928d72414ea2731cb2c9d1dc3077 3.2.2 ce4030ab57f523f33d5396c6a81396d440756f5f 3.0.0 0df781b261f55bbde73d8c92da3f99397000972f
|
||||||
CURRENT_NEXTCLOUD_VER="22.2.6"
|
CURRENT_NEXTCLOUD_VER="22.2.6"
|
||||||
fi
|
fi
|
||||||
|
if [[ ${CURRENT_NEXTCLOUD_VER} =~ ^22 ]]; then
|
||||||
|
InstallNextcloud 23.0.12 d138641b8e7aabebe69bb3ec7c79a714d122f729 4.1.0 697f6b4a664e928d72414ea2731cb2c9d1dc3077 3.2.2 ce4030ab57f523f33d5396c6a81396d440756f5f 3.0.0 0df781b261f55bbde73d8c92da3f99397000972f
|
||||||
|
CURRENT_NEXTCLOUD_VER="23.0.12"
|
||||||
|
fi
|
||||||
|
if [[ ${CURRENT_NEXTCLOUD_VER} =~ ^23 ]]; then
|
||||||
|
InstallNextcloud 24.0.12 7aa5d61632c1ccf4ca3ff00fb6b295d318c05599 4.1.0 697f6b4a664e928d72414ea2731cb2c9d1dc3077 3.2.2 ce4030ab57f523f33d5396c6a81396d440756f5f 3.0.0 0df781b261f55bbde73d8c92da3f99397000972f
|
||||||
|
CURRENT_NEXTCLOUD_VER="24.0.12"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
InstallNextcloud $nextcloud_ver $nextcloud_hash $contacts_ver $contacts_hash $calendar_ver $calendar_hash $user_external_ver $user_external_hash
|
InstallNextcloud $nextcloud_ver $nextcloud_hash $contacts_ver $contacts_hash $calendar_ver $calendar_hash $user_external_ver $user_external_hash
|
||||||
@@ -284,12 +304,12 @@ php$PHP_VER <<EOF > $CONFIG_TEMP && mv $CONFIG_TEMP $STORAGE_ROOT/owncloud/confi
|
|||||||
<?php
|
<?php
|
||||||
include("$STORAGE_ROOT/owncloud/config.php");
|
include("$STORAGE_ROOT/owncloud/config.php");
|
||||||
|
|
||||||
\$CONFIG['config_is_read_only'] = true;
|
\$CONFIG['config_is_read_only'] = false;
|
||||||
|
|
||||||
\$CONFIG['trusted_domains'] = array('$PRIMARY_HOSTNAME');
|
\$CONFIG['trusted_domains'] = array('$PRIMARY_HOSTNAME');
|
||||||
|
|
||||||
\$CONFIG['memcache.local'] = '\OC\Memcache\APCu';
|
\$CONFIG['memcache.local'] = '\OC\Memcache\APCu';
|
||||||
\$CONFIG['overwrite.cli.url'] = '/cloud';
|
\$CONFIG['overwrite.cli.url'] = 'https://${PRIMARY_HOSTNAME}/cloud';
|
||||||
\$CONFIG['mail_from_address'] = 'administrator'; # just the local part, matches our master administrator address
|
\$CONFIG['mail_from_address'] = 'administrator'; # just the local part, matches our master administrator address
|
||||||
|
|
||||||
\$CONFIG['logtimezone'] = '$TIMEZONE';
|
\$CONFIG['logtimezone'] = '$TIMEZONE';
|
||||||
@@ -353,20 +373,45 @@ tools/editconf.py /etc/php/$PHP_VER/cli/conf.d/10-opcache.ini -c ';' \
|
|||||||
opcache.save_comments=1 \
|
opcache.save_comments=1 \
|
||||||
opcache.revalidate_freq=1
|
opcache.revalidate_freq=1
|
||||||
|
|
||||||
# Migrate users_external data from <0.6.0 to version 3.0.0 (see https://github.com/nextcloud/user_external).
|
# Migrate users_external data from <0.6.0 to version 3.0.0
|
||||||
|
# (see https://github.com/nextcloud/user_external).
|
||||||
# This version was probably in use in Mail-in-a-Box v0.41 (February 26, 2019) and earlier.
|
# This version was probably in use in Mail-in-a-Box v0.41 (February 26, 2019) and earlier.
|
||||||
# We moved to v0.6.3 in 193763f8. Ignore errors - maybe there are duplicated users with the
|
# We moved to v0.6.3 in 193763f8. Ignore errors - maybe there are duplicated users with the
|
||||||
# correct backend already.
|
# correct backend already.
|
||||||
sqlite3 $STORAGE_ROOT/owncloud/owncloud.db "UPDATE oc_users_external SET backend='127.0.0.1';" || /bin/true
|
sqlite3 $STORAGE_ROOT/owncloud/owncloud.db "UPDATE oc_users_external SET backend='127.0.0.1';" || /bin/true
|
||||||
|
|
||||||
# Set up a cron job for Nextcloud.
|
# Set up a general cron job for Nextcloud.
|
||||||
|
# Also add another job for Calendar updates, per advice in the Nextcloud docs
|
||||||
|
# https://docs.nextcloud.com/server/24/admin_manual/groupware/calendar.html#background-jobs
|
||||||
cat > /etc/cron.d/mailinabox-nextcloud << EOF;
|
cat > /etc/cron.d/mailinabox-nextcloud << EOF;
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Mail-in-a-Box
|
# Mail-in-a-Box
|
||||||
*/5 * * * * root sudo -u www-data php$PHP_VER -f /usr/local/lib/owncloud/cron.php
|
*/5 * * * * root sudo -u www-data php$PHP_VER -f /usr/local/lib/owncloud/cron.php
|
||||||
|
*/5 * * * * root sudo -u www-data php$PHP_VER -f /usr/local/lib/owncloud/occ dav:send-event-reminders
|
||||||
EOF
|
EOF
|
||||||
chmod +x /etc/cron.d/mailinabox-nextcloud
|
chmod +x /etc/cron.d/mailinabox-nextcloud
|
||||||
|
|
||||||
|
# We also need to change the sending mode from background-job to occ.
|
||||||
|
# Or else the reminders will just be sent as soon as possible when the background jobs run.
|
||||||
|
hide_output sudo -u www-data php$PHP_VER -f /usr/local/lib/owncloud/occ config:app:set dav sendEventRemindersMode --value occ
|
||||||
|
|
||||||
|
# Now set the config to read-only.
|
||||||
|
# Do this only at the very bottom when no further occ commands are needed.
|
||||||
|
sed -i'' "s/'config_is_read_only'\s*=>\s*false/'config_is_read_only' => true/" $STORAGE_ROOT/owncloud/config.php
|
||||||
|
|
||||||
|
# Rotate the nextcloud.log file
|
||||||
|
cat > /etc/logrotate.d/nextcloud <<EOF
|
||||||
|
# Nextcloud logs
|
||||||
|
$STORAGE_ROOT/owncloud/nextcloud.log {
|
||||||
|
size 10M
|
||||||
|
create 640 www-data www-data
|
||||||
|
rotate 30
|
||||||
|
copytruncate
|
||||||
|
missingok
|
||||||
|
compress
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
# There's nothing much of interest that a user could do as an admin for Nextcloud,
|
# There's nothing much of interest that a user could do as an admin for Nextcloud,
|
||||||
# and there's a lot they could mess up, so we don't make any users admins of Nextcloud.
|
# and there's a lot they could mess up, so we don't make any users admins of Nextcloud.
|
||||||
# But if we wanted to, we would do this:
|
# But if we wanted to, we would do this:
|
||||||
|
|||||||
@@ -207,6 +207,6 @@ if [ "$PRIVATE_IPV6" != "$PUBLIC_IPV6" ]; then
|
|||||||
echo "Private IPv6 Address: $PRIVATE_IPV6"
|
echo "Private IPv6 Address: $PRIVATE_IPV6"
|
||||||
fi
|
fi
|
||||||
if [ -f /usr/bin/git ] && [ -d .git ]; then
|
if [ -f /usr/bin/git ] && [ -d .git ]; then
|
||||||
echo "Mail-in-a-Box Version: " $(git describe)
|
echo "Mail-in-a-Box Version: " $(git describe --always)
|
||||||
fi
|
fi
|
||||||
echo
|
echo
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ if management/status_checks.py --check-primary-hostname; then
|
|||||||
echo "If you have a DNS problem put the box's IP address in the URL"
|
echo "If you have a DNS problem put the box's IP address in the URL"
|
||||||
echo "(https://$PUBLIC_IP/admin) but then check the TLS fingerprint:"
|
echo "(https://$PUBLIC_IP/admin) but then check the TLS fingerprint:"
|
||||||
openssl x509 -in $STORAGE_ROOT/ssl/ssl_certificate.pem -noout -fingerprint -sha256\
|
openssl x509 -in $STORAGE_ROOT/ssl/ssl_certificate.pem -noout -fingerprint -sha256\
|
||||||
| sed "s/SHA256 Fingerprint=//"
|
| sed "s/SHA256 Fingerprint=//i"
|
||||||
else
|
else
|
||||||
echo https://$PUBLIC_IP/admin
|
echo https://$PUBLIC_IP/admin
|
||||||
echo
|
echo
|
||||||
@@ -175,7 +175,7 @@ else
|
|||||||
echo the certificate fingerprint matches:
|
echo the certificate fingerprint matches:
|
||||||
echo
|
echo
|
||||||
openssl x509 -in $STORAGE_ROOT/ssl/ssl_certificate.pem -noout -fingerprint -sha256\
|
openssl x509 -in $STORAGE_ROOT/ssl/ssl_certificate.pem -noout -fingerprint -sha256\
|
||||||
| sed "s/SHA256 Fingerprint=//"
|
| sed "s/SHA256 Fingerprint=//i"
|
||||||
echo
|
echo
|
||||||
echo Then you can confirm the security exception and continue.
|
echo Then you can confirm the security exception and continue.
|
||||||
echo
|
echo
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ 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 libjs-jquery libjs-jquery-mousewheel libmagic1 \
|
||||||
|
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.
|
||||||
# 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
|
||||||
@@ -35,9 +36,9 @@ 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.0
|
VERSION=1.6.5
|
||||||
HASH=fd84b4fac74419bb73e7a3bcae1978d5589c52de
|
HASH=326fcc206cddc00355e98d1e40fd0bcd9baec69f
|
||||||
PERSISTENT_LOGIN_VERSION=bde7b6840c7d91de627ea14e81cf4133cbb3c07a # version 5.2
|
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
|
||||||
CARDDAV_HASH=74f8ba7aee33e78beb9de07f7f44b81f6071b644
|
CARDDAV_HASH=74f8ba7aee33e78beb9de07f7f44b81f6071b644
|
||||||
@@ -134,7 +135,7 @@ cat > $RCM_CONFIG <<EOF;
|
|||||||
\$config['product_name'] = '$PRIMARY_HOSTNAME Webmail';
|
\$config['product_name'] = '$PRIMARY_HOSTNAME Webmail';
|
||||||
\$config['cipher_method'] = 'AES-256-CBC'; # persistent login cookie and potentially other things
|
\$config['cipher_method'] = 'AES-256-CBC'; # persistent login cookie and potentially other things
|
||||||
\$config['des_key'] = '$SECRET_KEY'; # 37 characters -> ~256 bits for AES-256, see above
|
\$config['des_key'] = '$SECRET_KEY'; # 37 characters -> ~256 bits for AES-256, see above
|
||||||
\$config['plugins'] = array('html5_notifier', 'archive', 'zipdownload', 'managesieve', 'jqueryui', 'persistent_login', 'carddav');
|
\$config['plugins'] = array('html5_notifier', 'archive', 'zipdownload', 'password', 'managesieve', 'jqueryui', 'persistent_login', 'carddav');
|
||||||
\$config['skin'] = 'elastic';
|
\$config['skin'] = 'elastic';
|
||||||
\$config['login_autocomplete'] = 2;
|
\$config['login_autocomplete'] = 2;
|
||||||
\$config['login_username_filter'] = 'email';
|
\$config['login_username_filter'] = 'email';
|
||||||
@@ -184,10 +185,9 @@ cp ${RCM_PLUGIN_DIR}/password/config.inc.php.dist \
|
|||||||
tools/editconf.py ${RCM_PLUGIN_DIR}/password/config.inc.php \
|
tools/editconf.py ${RCM_PLUGIN_DIR}/password/config.inc.php \
|
||||||
"\$config['password_minimum_length']=8;" \
|
"\$config['password_minimum_length']=8;" \
|
||||||
"\$config['password_db_dsn']='sqlite:///$STORAGE_ROOT/mail/users.sqlite';" \
|
"\$config['password_db_dsn']='sqlite:///$STORAGE_ROOT/mail/users.sqlite';" \
|
||||||
"\$config['password_query']='UPDATE users SET password=%D WHERE email=%u';" \
|
"\$config['password_query']='UPDATE users SET password=%P WHERE email=%u';" \
|
||||||
"\$config['password_dovecotpw']='/usr/bin/doveadm pw';" \
|
"\$config['password_algorithm']='sha512-crypt';" \
|
||||||
"\$config['password_dovecotpw_method']='SHA512-CRYPT';" \
|
"\$config['password_algorithm_prefix']='{SHA512-CRYPT}';"
|
||||||
"\$config['password_dovecotpw_with_method']=true;"
|
|
||||||
|
|
||||||
# so PHP can use doveadm, for the password changing plugin
|
# so PHP can use doveadm, for the password changing plugin
|
||||||
usermod -a -G dovecot www-data
|
usermod -a -G dovecot www-data
|
||||||
@@ -209,6 +209,16 @@ php$PHP_VER ${RCM_DIR}/bin/updatedb.sh --dir ${RCM_DIR}/SQL --package roundcube
|
|||||||
chown www-data:www-data $STORAGE_ROOT/mail/roundcube/roundcube.sqlite
|
chown www-data:www-data $STORAGE_ROOT/mail/roundcube/roundcube.sqlite
|
||||||
chmod 664 $STORAGE_ROOT/mail/roundcube/roundcube.sqlite
|
chmod 664 $STORAGE_ROOT/mail/roundcube/roundcube.sqlite
|
||||||
|
|
||||||
|
# Patch the Roundcube code to eliminate an issue that causes postfix to reject our sqlite
|
||||||
|
# user database (see https://github.com/mail-in-a-box/mailinabox/issues/2185)
|
||||||
|
sed -i.miabold 's/^[^#]\+.\+PRAGMA journal_mode = WAL.\+$/#&/' \
|
||||||
|
/usr/local/lib/roundcubemail/program/lib/Roundcube/db/sqlite.php
|
||||||
|
|
||||||
|
# Because Roundcube wants to set the PRAGMA we just deleted from the source, we apply it here
|
||||||
|
# to the roundcube database (see https://github.com/roundcube/roundcubemail/issues/8035)
|
||||||
|
# Database should exist, created by migration script
|
||||||
|
sqlite3 $STORAGE_ROOT/mail/roundcube/roundcube.sqlite 'PRAGMA journal_mode=WAL;'
|
||||||
|
|
||||||
# Enable PHP modules.
|
# Enable PHP modules.
|
||||||
phpenmod -v $PHP_VER imap
|
phpenmod -v $PHP_VER imap
|
||||||
restart_service php$PHP_VER-fpm
|
restart_service php$PHP_VER-fpm
|
||||||
|
|||||||
@@ -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.6.2
|
VERSION=2.7.1
|
||||||
TARGETHASH=f0e8091a8030e5b851f5ba1f9f0e1a05b8762d80
|
TARGETHASH=f15c566b1ad50de24f3f08f505f0c3d8155c2d0d
|
||||||
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
|
||||||
@@ -41,7 +41,15 @@ if [ $needs_update == 1 ]; then
|
|||||||
mv /tmp/z-push/*/src /usr/local/lib/z-push
|
mv /tmp/z-push/*/src /usr/local/lib/z-push
|
||||||
rm -rf /tmp/z-push.zip /tmp/z-push
|
rm -rf /tmp/z-push.zip /tmp/z-push
|
||||||
|
|
||||||
|
# Create admin and top scripts with PHP_VER
|
||||||
rm -f /usr/sbin/z-push-{admin,top}
|
rm -f /usr/sbin/z-push-{admin,top}
|
||||||
|
echo '#!/bin/bash' > /usr/sbin/z-push-admin
|
||||||
|
echo php$PHP_VER /usr/local/lib/z-push/z-push-admin.php '"$@"' >> /usr/sbin/z-push-admin
|
||||||
|
chmod 755 /usr/sbin/z-push-admin
|
||||||
|
echo '#!/bin/bash' > /usr/sbin/z-push-top
|
||||||
|
echo php$PHP_VER /usr/local/lib/z-push/z-push-top.php '"$@"' >> /usr/sbin/z-push-top
|
||||||
|
chmod 755 /usr/sbin/z-push-top
|
||||||
|
|
||||||
echo $VERSION > /usr/local/lib/z-push/version
|
echo $VERSION > /usr/local/lib/z-push/version
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user