mirror of https://github.com/zulip/zulip.git
CVE-2021-43799: Set a secure Erlang cookie.
The RabbitMQ docs state ([1]): RabbitMQ nodes and CLI tools (e.g. rabbitmqctl) use a cookie to determine whether they are allowed to communicate with each other. [...] The cookie is just a string of alphanumeric characters up to 255 characters in size. It is usually stored in a local file. ...and goes on to state (emphasis ours): If the file does not exist, Erlang VM will try to create one with a randomly generated value when the RabbitMQ server starts up. Using such generated cookie files are **appropriate in development environments only.** The auto-generated cookie does not use cryptographic sources of randomness, and generates 20 characters of `[A-Z]`. Because of a semi-predictable seed, the entropy of this password is thus less than the idealized 26^20 = 94 bits of entropy; in actuality, it is 36 bits of entropy, or potentially as low as 20 if the performance of the server is known. These sizes are well within the scope of remote brute-force attacks. On provision, install, and upgrade, replace the default insecure 20-character Erlang cookie with a cryptographically secure 255-character string (the max length allowed). [1] https://www.rabbitmq.com/clustering.html#erlang-cookie
This commit is contained in:
parent
93a344fc3c
commit
a5496f4098
|
@ -121,6 +121,7 @@ log][commit-log] for an up-to-date list of raw changes.
|
|||
|
||||
## Zulip 4.x series
|
||||
|
||||
- CVE-2021-43799: Remote execution of code involving RabbitMQ.
|
||||
- Closed access to RabbitMQ port 25672; initial installs tried to
|
||||
close this port, but failed to restart RabbitMQ for the
|
||||
configuration.
|
||||
|
|
|
@ -13,7 +13,7 @@ import re
|
|||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import NoReturn
|
||||
|
@ -116,15 +116,31 @@ IS_SERVER_UP = True
|
|||
|
||||
# Check if rabbitmq port 25672 is listening on anything except 127.0.0.1
|
||||
rabbitmq_dist_listen = listening_publicly(25672)
|
||||
# Check the erlang magic cookie size
|
||||
cookie_size: Optional[int] = None
|
||||
if os.path.exists("/var/lib/rabbitmq/.erlang.cookie"):
|
||||
with open("/var/lib/rabbitmq/.erlang.cookie", "r") as cookie_fh:
|
||||
cookie_size = len(cookie_fh.readline())
|
||||
else:
|
||||
logging.info("No RabbitMQ erlang cookie found, not auditing RabbitMQ security.")
|
||||
if args.skip_puppet and rabbitmq_dist_listen:
|
||||
logging.error(
|
||||
"RabbitMQ is publicly-accessible on %s; this is a security vulnerability!",
|
||||
", ".join(rabbitmq_dist_listen),
|
||||
)
|
||||
issue = "issue"
|
||||
if cookie_size is not None and cookie_size == 20:
|
||||
# See the below comment -- this is used as a lightweight
|
||||
# signal for a cookie made with Erlang's bad randomizer.
|
||||
logging.error(
|
||||
"RabbitMQ erlang cookie is insecure; this is a critical security vulnerability!"
|
||||
)
|
||||
issue = "issues"
|
||||
logging.error(
|
||||
"To fix the above security issue, re-run the upgrade without --skip-puppet "
|
||||
"To fix the above security %s, re-run the upgrade without --skip-puppet "
|
||||
"(which may be set in /etc/zulip/zulip.conf), in order to restart the "
|
||||
"necessary services. Running zulip-puppet-apply by itself is not sufficient."
|
||||
"necessary services. Running zulip-puppet-apply by itself is not sufficient.",
|
||||
issue,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
@ -304,6 +320,16 @@ if rabbitmq_dist_listen:
|
|||
logging.info("Shutting down rabbitmq to adjust its ports...")
|
||||
subprocess.check_call(["/usr/sbin/service", "rabbitmq-server", "stop"])
|
||||
|
||||
if cookie_size is not None and cookie_size == 20:
|
||||
# Checking for a 20-character cookie is used as a signal that it
|
||||
# was generated by Erlang's insecure randomizer, which only
|
||||
# provides between 20 and 36 bits of entropy; were it 20
|
||||
# characters long by a good randomizer, it would be 96 bits and
|
||||
# more than sufficient. We generate, using good randomness, a
|
||||
# 255-character cookie, the max allowed length.
|
||||
logging.info("Generating a secure erlang cookie...")
|
||||
subprocess.check_call(["./scripts/setup/generate-rabbitmq-cookie"])
|
||||
|
||||
# Adjust Puppet class names for the manifest renames in the 4.0 release
|
||||
class_renames = {
|
||||
"zulip::app_frontend": "zulip::profile::app_frontend",
|
||||
|
|
|
@ -10,6 +10,10 @@ else
|
|||
sudo=(sudo)
|
||||
fi
|
||||
|
||||
# If the RabbitMQ distribution cookie is insecure, reset it and
|
||||
# restart RabbitMQ.
|
||||
"${sudo[@]}" "$(dirname "$0")/generate-rabbitmq-cookie"
|
||||
|
||||
RABBITMQ_USERNAME=$("$(dirname "$0")/../get-django-setting" RABBITMQ_USERNAME)
|
||||
RABBITMQ_PASSWORD=$("$(dirname "$0")/../get-django-setting" RABBITMQ_PASSWORD)
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# rabbitmq sets an insecure "magic cookie" which is used for auth;
|
||||
# reset it to be longer.
|
||||
set -eu
|
||||
|
||||
cookiefile=/var/lib/rabbitmq/.erlang.cookie
|
||||
# If the RabbitMQ distribution cookie is insecure, reset it
|
||||
if [ ! -f "$cookiefile" ] || ! size="$(wc -c "$cookiefile")" || [ "${size%% *}" -le 20 ]; then
|
||||
running=0
|
||||
if service rabbitmq-server status >/dev/null; then
|
||||
running=1
|
||||
service rabbitmq-server stop
|
||||
fi
|
||||
|
||||
echo "Setting a more secure RabbitMQ distribution magic cookie"
|
||||
cookie="$(LC_ALL=C tr -dc '[:alnum:]' </dev/urandom | head -c255)"
|
||||
[ "${#cookie}" -eq 255 ] # make sure tr wasn’t OOM-killed
|
||||
tmpfile="$(mktemp "$cookiefile.XXXXXXXXXX")"
|
||||
chown rabbitmq: "$tmpfile"
|
||||
printf '%s' "$cookie" >"$tmpfile"
|
||||
mv "$tmpfile" "$cookiefile"
|
||||
|
||||
if [ "$running" == "1" ]; then
|
||||
service rabbitmq-server start
|
||||
fi
|
||||
fi
|
Loading…
Reference in New Issue