Skip to content

Commit

Permalink
Bug 1289156 - Vendor libmysqlclient 5.7 to protect against CVE-2015-3152
Browse files Browse the repository at this point in the history


The latest versions of libmysqlclient 5.5/5.6 (used by mysqlclient) are
still vulnerable to TLS stripping, even after last year's backports of
5.7.x fixes:
  - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-3152
  - http://bugs.mysql.com/bug.php?id=82383

Ideally we'd just use the standalone Connector/C library instead of the
libmysqlclient packages, however the latest release is too old:
  - http://bugs.mysql.com/bug.php?id=82448

Heroku's cedar-14 stack comes with libmysqlclient 5.5.x, so until it is
updated to 5.7.x (see heroku/base-images#38) we
must manually vendor 5.7.x ourselves, so that connections between the
Heroku dynos and our public RDS instances are secure. We can do this and
still remain on MySQL server 5.6, since newer client releases are
backwards compatible with older server versions.

Whilst the Vagrant/Travis MySQL instances don't use TLS (and so aren't
affected), we still want them to use libmysqlclient 5.7, to be
consistent with production.

Installing the newer libmysqlclient isn't sufficient on it's own. Any
packages compiled against the older version (in our case mysqlclient)
need to be recompiled. We ensure this happens by pip uninstalling the
existing package if it was already installed.
  • Loading branch information
Ed Morley committed Aug 10, 2016
1 parent 4d1b7fe commit 3b1e45c
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
env:
global:
# Ensure the vendored libmysqlclient library can be found at run-time.
- LD_LIBRARY_PATH="$HOME/venv/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH"
- BROKER_URL='amqp://guest:guest@localhost:5672//'
- DATABASE_URL='mysql://root@localhost/test_treeherder'
- DATABASE_URL_RO='mysql://root@localhost/test_treeherder'
Expand Down Expand Up @@ -85,6 +87,7 @@ matrix:
- if [[ ! -f ~/venv/bin/activate ]]; then virtualenv -p python ~/venv; fi
- source ~/venv/bin/activate
- pip install --disable-pip-version-check --upgrade pip==8.1.1
- ./bin/vendor-libmysqlclient.sh ~/venv
# Create the test database for `manage.py check --deploy`.
- mysql -u root -e 'create database test_treeherder;'
install:
Expand Down Expand Up @@ -132,6 +135,7 @@ matrix:
- if [[ ! -f ~/venv/bin/activate ]]; then virtualenv -p python ~/venv; fi
- source ~/venv/bin/activate
- pip install --disable-pip-version-check --upgrade pip==8.1.1
- ./bin/vendor-libmysqlclient.sh ~/venv
install:
- pip install --disable-pip-version-check --require-hashes -r requirements/common.txt -r requirements/dev.txt
script:
Expand Down Expand Up @@ -169,6 +173,7 @@ matrix:
- if [[ ! -f ~/venv/bin/activate ]]; then virtualenv -p python ~/venv; fi
- source ~/venv/bin/activate
- pip install --disable-pip-version-check --upgrade pip==8.1.1
- ./bin/vendor-libmysqlclient.sh ~/venv
install:
- pip install --disable-pip-version-check --require-hashes -r requirements/common.txt -r requirements/dev.txt
script:
Expand Down
25 changes: 25 additions & 0 deletions bin/pre_compile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Tasks run by the Heroku Python buildpack prior to the compile step.

# Make non-zero exit codes & other errors fatal.
set -euo pipefail

VENDOR_DIR=".heroku/vendor"

# Hacks to work around pre_compile being run before cache is restored.
# Remove when this PR is merged:
# https://github.com/heroku/heroku-buildpack-python/pull/321
VENDOR_DIR="$CACHE_DIR/$VENDOR_DIR"
mkdir -p "$VENDOR_DIR"
export PATH="$VENDOR_DIR/bin:$PATH"

./bin/vendor-libmysqlclient.sh "$VENDOR_DIR"

# The existing value for `LD_LIBRARY_PATH` only includes `$VENDOR_DIR/lib` and not
# also the `x86_64-linux-gnu` directory that the libmysqlclient package will create
# inside it. Directories in `LD_LIBRARY_PATH` are not searched recursively and we
# cannot add additional directories, since `pre_compile` is run in a sub-shell. In
# addition, `vendor-libmysqlclient.sh` cannot flatten the directory structure,
# since `mysql_config` uses relative paths that include `x86_64-linux-gnu`.
# As such, we have to create a symlink to ensure the library is found at runtime.
ln -rsf "$VENDOR_DIR/lib/x86_64-linux-gnu/libmysqlclient.so.20" "$VENDOR_DIR/lib/libmysqlclient.so.20"
71 changes: 71 additions & 0 deletions bin/vendor-libmysqlclient.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env bash
# The latest versions of libmysqlclient 5.5/5.6 (used by mysqlclient) are still
# vulnerable to TLS stripping, even after last year's backports of 5.7.x fixes:
# - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-3152
# - https://bugzilla.mozilla.org/show_bug.cgi?id=1289156
# - http://bugs.mysql.com/bug.php?id=82383
#
# Ideally we'd just use the standalone Connector/C library instead of the
# libmysqlclient packages, however the latest release is too old:
# - http://bugs.mysql.com/bug.php?id=82448
#
# Heroku's cedar-14 stack comes with libmysqlclient 5.5.x, so until it is updated
# to 5.7.x (https://github.com/heroku/stack-images/pull/38) we must manually vendor
# 5.7.X ourselves, so that connections between the Heroku dynos and our public RDS
# instances are secure. We can do this and still remain on MySQL server 5.6, since
# newer client releases are backwards compatible with older server versions.
#
# Whilst the Vagrant/Travis MySQL instances don't use TLS (and so aren't affected),
# we still want them to use libmysqlclient 5.7, to be consistent with production.
#
# Usage:
# ./bin/vendor-libmysqlclient.sh "foo/vendor-dir"
#
# NB: The `foo/vendor-dir/bin` directory must be on the PATH ahead of `/usr/bin`
# during pip install (so mysqlclient finds `mysql_config`), and `LD_LIBRARY_PATH`
# must include `foo/vendor-dir/lib/x86_64-linux-gnu` at Python runtime.

# Make non-zero exit codes & other errors fatal.
set -euo pipefail

VENDOR_DIR="$1"
VERSION="5.7.14"
PACKAGE_URLS=(
# We have to use packages from mysql.com since there is no Ubuntu distro
# release available for MySQL 5.7 on Ubuntu 14.04.
"https://cdn.mysql.com/Downloads/MySQL-5.7/libmysqlclient20_${VERSION}-1ubuntu14.04_amd64.deb"
"https://cdn.mysql.com/Downloads/MySQL-5.7/libmysqlclient-dev_${VERSION}-1ubuntu14.04_amd64.deb"
)

# Skip vendoring if libmysqlclient-dev's `mysql_config` exists and reports the correct version.
if [[ "$(mysql_config --version 2>&1)" == "$VERSION" ]]; then
exit 0
fi

echo "-----> Vendoring libmysqlclient $VERSION."

# We manually extract the packages rather than using apt-get install, since:
# - On Heroku we don't have sudo, so need to vendor the package somewhere other
# than the standard `/usr/{bin,lib,include}` locations.
# - For Vagrant/Travis we have to install mysql-server-5.6 (we have to test against
# against the same server version as used in production) and there are unresolvable
# packaging conflicts between it and libmysqlclient 5.7, if installed with apt-get.
# - The compiled binary releases of libmysqlclient aren't available as a tar archive.
for url in "${PACKAGE_URLS[@]}"; do
# Use `dpkg-deb`'s filesystem tarfile mode along with `tar`, rather than just
# `dpkg --extract`, since the latter doesn't support removing the `/./usr/`
# directory prefix itself, and so would require error-prone directory shuffling
# afterwards. The `share` directory and static library files are not used, so are
# excluded to reduce the slug/cache size (and thus transfer times) on Heroku/Travis.
curl -sS "$url" \
| dpkg-deb --fsys-tarfile /dev/stdin \
| tar -x --strip-components=2 --exclude="./usr/share" --exclude="*.a" -C "$VENDOR_DIR"
done

if [[ -n "$(pip show mysqlclient)" ]]; then
# The mysqlclient Python package won't pick up the new version of libmysqlclient
# unless it's recompiled against it. However unless we purge the old package, pip
# won't know to reinstall, since the version of mysqlclient itself hasn't changed.
echo "-----> Uninstalling mysqlclient to force recompilation during pip install."
pip uninstall -yq mysqlclient
fi
4 changes: 0 additions & 4 deletions puppet/manifests/classes/mysql.pp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
ensure => installed
}

package { 'libmysqld-dev':
ensure => installed
}

service { 'mysql':
ensure => running,
enable => true,
Expand Down
13 changes: 11 additions & 2 deletions puppet/manifests/classes/python.pp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
package{[# Python2.7 is already installed, but we need to update it to the
# latest version from the third party PPA.
"python2.7",
# Required by MySQLdb.
# Required by mysqlclient.
"python-dev",
# Required by pylibmc.
"libmemcached-dev",
Expand Down Expand Up @@ -48,8 +48,17 @@
user => "${APP_USER}",
}

exec {"vendor-libmysqlclient":
command => "${PROJ_DIR}/bin/vendor-libmysqlclient.sh ${VENV_DIR}",
require => Exec["create-virtualenv"],
user => "${APP_USER}",
}

exec{"pip-install-common":
require => Exec['create-virtualenv'],
require => [
Exec['create-virtualenv'],
Exec['vendor-libmysqlclient'],
],
user => "${APP_USER}",
cwd => '/tmp',
command => "${VENV_DIR}/bin/pip install --disable-pip-version-check --require-hashes -r ${PROJ_DIR}/requirements/common.txt",
Expand Down
5 changes: 4 additions & 1 deletion puppet/manifests/vagrant.pp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
$RABBITMQ_VHOST = '/'

Exec {
path => "/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin",
path => "${VENV_DIR}/bin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin",
}

line {"etc-hosts":
Expand All @@ -30,6 +30,9 @@

file {"/etc/profile.d/treeherder.sh":
content => "
# Ensure the vendored libmysqlclient library can be found at run-time.
export LD_LIBRARY_PATH='${VENV_DIR}/lib/x86_64-linux-gnu'
export ENABLE_LOCAL_SETTINGS_FILE='True'
export BROKER_URL='amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@localhost:5672/${RABBITMQ_VHOST}'
export DATABASE_URL='mysql://${DB_USER}:${DB_PASS}@localhost/treeherder'
Expand Down

0 comments on commit 3b1e45c

Please sign in to comment.