diff --git a/CHANGELOG.md b/CHANGELOG.md index 1698cd03..1d0e6e0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,20 @@ ownCloud: * Updated to ownCloud to 8.2.7. +v0.19b (August 20, 2016) +------------------------ + +This update corrects a security issue introduced in v0.18. + +A remote code execution vulnerability is corrected in how the munin system monitoring graphs are generated for the control panel. The vulnerability involves an administrative user visiting a carefully crafted URL. + +v0.19a (August 18, 2016) +------------------------ + +This update corrects a security issue in v0.19. + +* fail2ban won't start if Roundcube had not yet been used - new installations probably do not have fail2ban running. + v0.19 (August 13, 2016) ----------------------- diff --git a/README.md b/README.md index 58200070..a2d5d31e 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ by me: $ curl -s https://keybase.io/joshdata/key.asc | gpg --import gpg: key C10BDD81: public key "Joshua Tauberer " imported - $ git verify-tag v0.19 + $ git verify-tag v0.19b gpg: Signature made ..... using RSA key ID C10BDD81 gpg: Good signature from "Joshua Tauberer " gpg: WARNING: This key is not certified with a trusted signature! @@ -72,7 +72,7 @@ and on my [personal homepage](https://razor.occams.info/). (Of course, if this r Checkout the tag corresponding to the most recent release: - $ git checkout v0.19 + $ git checkout v0.19b Begin the installation. diff --git a/management/daemon.py b/management/daemon.py index 9bc6429b..3c712303 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -541,10 +541,9 @@ def munin_cgi(filename): headers based on parameters in the requesting URL. All output is written to stdout which munin_cgi splits into response headers and binary response data. - munin-cgi-graph reads environment variables as well as passed input to determine + munin-cgi-graph reads environment variables to determine what it should do. It expects a path to be in the env-var PATH_INFO, and a - querystring to be in the env-var QUERY_STRING as well as passed as input to the - command. + querystring to be in the env-var QUERY_STRING. munin-cgi-graph has several failure modes. Some write HTTP Status headers and others return nonzero exit codes. Situating munin_cgi between the user-agent and munin-cgi-graph enables keeping @@ -552,7 +551,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 "%s"' + 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 @@ -564,12 +563,10 @@ def munin_cgi(filename): query_str = request.query_string.decode("utf-8", 'ignore') - env = {'PATH_INFO': '/%s/' % filename, 'QUERY_STRING': query_str} - cmd = COMMAND % query_str + env = {'PATH_INFO': '/%s/' % filename, 'REQUEST_METHOD': 'GET', 'QUERY_STRING': query_str} code, binout = utils.shell('check_output', - cmd.split(' ', 5), - # Using a maxsplit of 5 keeps the last 2 arguments together - input=query_str.encode('UTF-8'), + COMMAND.split(" ", 5), + # Using a maxsplit of 5 keeps the last arguments together env=env, return_bytes=True, trap=True) diff --git a/setup/bootstrap.sh b/setup/bootstrap.sh index 47fbb6ec..7d180bfe 100644 --- a/setup/bootstrap.sh +++ b/setup/bootstrap.sh @@ -7,7 +7,7 @@ ######################################################### if [ -z "$TAG" ]; then - TAG=v0.19 + TAG=v0.19b fi # Are we running as root? diff --git a/setup/start.sh b/setup/start.sh index 9d19a411..790afe18 100755 --- a/setup/start.sh +++ b/setup/start.sh @@ -111,15 +111,22 @@ source setup/zpush.sh source setup/management.sh source setup/munin.sh -# Ping the management daemon to write the DNS and nginx configuration files. +# Wait for the management daemon to start... until nc -z -w 4 127.0.0.1 10222 do echo Waiting for the Mail-in-a-Box management daemon to start... sleep 2 done + +# ...and then have it write the DNS and nginx configuration files and start those +# services. tools/dns_update tools/web_update +# Give fail2ban another restart. The log files may not all have been present when +# fail2ban was first configured, but they should exist now. +restart_service fail2ban + # If DNS is already working, try to provision TLS certficates from Let's Encrypt. # Suppress extra reasons why domains aren't getting a new certificate. management/ssl_certificates.py -q diff --git a/setup/system.sh b/setup/system.sh index d9f84fda..293ac68d 100755 --- a/setup/system.sh +++ b/setup/system.sh @@ -299,4 +299,9 @@ cat conf/fail2ban/jails.conf \ > /etc/fail2ban/jail.d/mailinabox.conf cp -f conf/fail2ban/filter.d/* /etc/fail2ban/filter.d/ +# On first installation, the log files that the jails look at don't all exist. +# e.g., The roundcube error log isn't normally created until someone logs into +# Roundcube for the first time. This causes fail2ban to fail to start. Later +# scripts will ensure the files exist and then fail2ban is given another +# restart at the very end of setup. restart_service fail2ban diff --git a/tests/fail2ban.py b/tests/fail2ban.py index 0f2f1e9f..fb74e706 100644 --- a/tests/fail2ban.py +++ b/tests/fail2ban.py @@ -1,20 +1,20 @@ # Test that a box's fail2ban setting are working # correctly by attempting a bunch of failed logins. -# Specify SSH login information the command line - -# we use that to reset fail2ban after each test, -# and we extract the hostname from that to open -# connections to. +# +# Specify a SSH login command (which we use to reset +# fail2ban after each test) and the hostname to +# try to log in to. ###################################################################### import sys, os, time, functools # parse command line -if len(sys.argv) < 2: - print("Usage: tests/fail2ban.py user@hostname") +if len(sys.argv) != 3: + print("Usage: tests/fail2ban.py \"ssh user@hostname\" hostname") sys.exit(1) -ssh_user, hostname = sys.argv[1].split("@", 1) +ssh_command, hostname = sys.argv[1:3] # define some test types @@ -85,7 +85,8 @@ def http_test(url, expected_status, postdata=None, qsargs=None, auth=None): auth=HTTPBasicAuth(*auth) if auth else None, data=postdata, headers={'User-Agent': 'Mail-in-a-Box fail2ban tester'}, - timeout=8) + timeout=8, + verify=False) # don't bother with HTTPS validation, it may not be configured yet except requests.exceptions.ConnectTimeout as e: raise IsBlocked() except requests.exceptions.ConnectionError as e: @@ -106,7 +107,7 @@ def restart_fail2ban_service(final=False): if not final: # Stop recidive jails during testing. command += " && sudo fail2ban-client stop recidive" - os.system("ssh %s@%s \"%s\"" % (ssh_user, hostname, command)) + os.system("%s \"%s\"" % (ssh_command, command)) def testfunc_runner(i, testfunc, *args): print(i+1, end=" ", flush=True)