diff -Nru juju-0.5+bzr531/debian/changelog juju-0.5+bzr531/debian/changelog --- juju-0.5+bzr531/debian/changelog 2012-06-18 21:01:04.000000000 +0000 +++ juju-0.5+bzr531/debian/changelog 2012-08-29 17:24:20.000000000 +0000 @@ -1,3 +1,14 @@ +juju (0.5+bzr531-0ubuntu1.3) precise-security; urgency=low + + * SECURITY UPDATE: d/p/upstream-543.patch: Disable password authentication + on LXC containers created in the local provider. (LP: #1016428) + * SECURITY UPDATE: d/p/upstream-564.patch: Fix example mysql charm to + not expose root mysql password to all users. (LP: #1040165) + * SECURITY UPDATE: d/p/upstream-565.patch: Verify charm store hostname + matches hostname on SSL certificate. (LP: #992447) + + -- Clint Byrum Thu, 23 Aug 2012 17:12:26 -0700 + juju (0.5+bzr531-0ubuntu1.2) precise-proposed; urgency=low * d/p/upstream-541-542.patch: fix shell script to make proposed work diff -Nru juju-0.5+bzr531/debian/patches/series juju-0.5+bzr531/debian/patches/series --- juju-0.5+bzr531/debian/patches/series 2012-06-18 20:59:02.000000000 +0000 +++ juju-0.5+bzr531/debian/patches/series 2012-08-29 17:24:20.000000000 +0000 @@ -5,3 +5,6 @@ upstream-537.patch upstream-538.patch upstream-541-542.patch +upstream-543.patch +upstream-564.patch +upstream-565.patch diff -Nru juju-0.5+bzr531/debian/patches/upstream-543.patch juju-0.5+bzr531/debian/patches/upstream-543.patch --- juju-0.5+bzr531/debian/patches/upstream-543.patch 1970-01-01 00:00:00.000000000 +0000 +++ juju-0.5+bzr531/debian/patches/upstream-543.patch 2012-08-29 19:07:11.000000000 +0000 @@ -0,0 +1,42 @@ +Origin: http://bazaar.launchpad.net/~juju/juju/trunk/revision/543 +Bug: http://pad.lv/1016428 +Subject: disable password auth on containers + +=== modified file 'juju/lib/lxc/data/juju-create' +--- a/juju/lib/lxc/data/juju-create 2012-06-18 19:04:58 +0000 ++++ b/juju/lib/lxc/data/juju-create 2012-06-22 17:01:27 +0000 +@@ -69,7 +69,7 @@ + + # disable root ssh logins + sed -i "s/PermitRootLogin yes/PermitRootLogin no/" /etc/ssh/sshd_config +- ++ sed -i "s/#PasswordAuthentication yes/PasswordAuthentication no/" /etc/ssh/sshd_config + } + + + +=== modified file 'juju/lib/lxc/tests/test_lxc.py' +--- a/juju/lib/lxc/tests/test_lxc.py 2012-05-10 16:20:36 +0000 ++++ b/juju/lib/lxc/tests/test_lxc.py 2012-06-22 17:01:27 +0000 +@@ -44,6 +44,9 @@ + if os.path.exists(self.config): + os.unlink(self.config) + ++ def tearDown(self): ++ self.clean_container(DEFAULT_CONTAINER) ++ + def make_config(self, network_name="virbr0"): + lxc_config = os.path.join(DATA_PATH, "lxc.conf") + template = open(lxc_config, "r").read() +@@ -255,6 +258,10 @@ + sudoers = sudo_get("etc/sudoers.d/lxc") + self.assertIn("ubuntu ALL=(ALL:ALL) NOPASSWD: ALL", sudoers) + ++ # ssh disabled password auth ++ sshd_config = sudo_get("etc/ssh/sshd_config") ++ self.assertIn("PasswordAuthentication no", sshd_config) ++ + # hostname + self.assertEqual(c.container_name, sudo_get("etc/hostname").strip()) + # the lxc-clone command provides a different ordering here + diff -Nru juju-0.5+bzr531/debian/patches/upstream-564.patch juju-0.5+bzr531/debian/patches/upstream-564.patch --- juju-0.5+bzr531/debian/patches/upstream-564.patch 1970-01-01 00:00:00.000000000 +0000 +++ juju-0.5+bzr531/debian/patches/upstream-564.patch 2012-08-29 19:10:15.000000000 +0000 @@ -0,0 +1,25 @@ +Origin: http://bazaar.launchpad.net/~juju/juju/trunk/revision/564 +Bug: http://pad.lv/1040165 +Subject: ensure saved password file is non-world readable before writing + to it + +=== modified file 'examples/precise/mysql/hooks/install' +--- a/examples/precise/mysql/hooks/install 2011-11-28 12:28:57 +0000 ++++ b/examples/precise/mysql/hooks/install 2012-08-22 23:20:44 +0000 +@@ -10,7 +10,14 @@ + # Store the password for later use by the db-relation-changed hook for + # this service unit. As a general note, for data that service units do + # not need to share, simply use the machine's local file store. +-echo $PASSWORD >> /var/lib/juju/mysql.passwd ++PASSFILE=/var/lib/juju/mysql.passwd ++if ! [ -f $PASSFILE ] ; then ++ touch $PASSFILE ++fi ++chmod 0600 $PASSFILE ++if ! [ -s $PASSFILE ] ; then ++ echo $PASSWORD >> /var/lib/juju/mysql.passwd ++fi + + echo mysql-server-5.1 mysql-server/root_password password $PASSWORD | debconf-set-selections + echo mysql-server-5.1 mysql-server/root_password_again password $PASSWORD | debconf-set-selections + diff -Nru juju-0.5+bzr531/debian/patches/upstream-565.patch juju-0.5+bzr531/debian/patches/upstream-565.patch --- juju-0.5+bzr531/debian/patches/upstream-565.patch 1970-01-01 00:00:00.000000000 +0000 +++ juju-0.5+bzr531/debian/patches/upstream-565.patch 2012-08-29 19:10:42.000000000 +0000 @@ -0,0 +1,120 @@ +Origin: http://bazaar.launchpad.net/~juju/juju/trunk/revision/565 +Bug: http://pad.lv/992447 +Subject: Verify hostname of SSL endpoint for charm store. + +=== modified file 'juju/charm/repository.py' +--- a/juju/charm/repository.py 2012-05-03 18:42:09 +0000 ++++ b/juju/charm/repository.py 2012-08-23 23:57:09 +0000 +@@ -3,11 +3,13 @@ + import os + import tempfile + import urllib ++import urlparse + import yaml + + from twisted.internet.defer import fail, inlineCallbacks, returnValue, succeed + from twisted.web.client import downloadPage, getPage + from twisted.web.error import Error ++from txaws.client.ssl import VerifyingContextFactory + + from juju.charm.provider import get_charm_from_path + from juju.charm.url import CharmURL +@@ -126,7 +128,8 @@ + url = "%s/charm-info?charms=%s" % ( + self.url_base, urllib.quote(charm_id)) + try: +- all_info = json.loads((yield getPage(url))) ++ host = urlparse.urlparse(url).hostname ++ all_info = json.loads((yield getPage(url, contextFactory=VerifyingContextFactory(host)))) + charm_info = all_info[charm_id] + for warning in charm_info.get("warnings", []): + log.warning("%s: %s", charm_id, warning) +@@ -147,8 +150,9 @@ + delete=False) + f.close() + downloading_path = f.name ++ host = urlparse.urlparse(url).hostname + try: +- yield downloadPage(url, downloading_path) ++ yield downloadPage(url, downloading_path, contextFactory=VerifyingContextFactory(host)) + except Error: + raise CharmNotFound(self.url_base, charm_url) + os.rename(downloading_path, cache_path) + +=== modified file 'juju/charm/tests/test_repository.py' +--- a/juju/charm/tests/test_repository.py 2012-07-01 22:20:22 +0000 ++++ b/juju/charm/tests/test_repository.py 2012-08-06 20:06:37 +0000 +@@ -5,6 +5,8 @@ + + from twisted.internet.defer import fail, inlineCallbacks, succeed + from twisted.web.error import Error ++from txaws.client.ssl import VerifyingContextFactory ++ + + from juju.charm.directory import CharmDirectory + from juju.charm.errors import CharmNotFound, CharmURLError, RepositoryNotFound +@@ -16,7 +18,7 @@ + from juju.lib import under + + from juju.charm import tests +-from juju.lib.mocker import ANY ++from juju.lib.mocker import ANY, MATCH + from juju.lib.testing import TestCase + + +@@ -280,15 +282,19 @@ + return json.dumps({url_str: info}) + + def mock_charm_info(self, url, result): +- self.getPage(url) ++ def match_context(value): ++ return isinstance(value, VerifyingContextFactory) ++ self.getPage(url, contextFactory=MATCH(match_context)) + self.mocker.result(result) + + def mock_download(self, url, error=None): +- self.downloadPage(url, ANY) ++ def match_context(value): ++ return isinstance(value, VerifyingContextFactory) ++ self.downloadPage(url, ANY, contextFactory=MATCH(match_context)) + if error: + return self.mocker.result(fail(error)) + +- def download(_, path): ++ def download(_, path, contextFactory): + self.assertTrue(path.startswith(self.download_path)) + with open(path, "wb") as f: + f.write(self.bundle_data) + +=== modified file 'juju/control/tests/test_upgrade_charm.py' +--- a/juju/control/tests/test_upgrade_charm.py 2012-05-04 22:43:40 +0000 ++++ b/juju/control/tests/test_upgrade_charm.py 2012-08-06 19:29:30 +0000 +@@ -12,6 +12,7 @@ + from juju.errors import FileNotFound + from juju.environment.environment import Environment + from juju.unit.workflow import UnitWorkflowState ++from juju.lib.mocker import ANY + + from .common import MachineControlToolTest + +@@ -481,7 +482,8 @@ + self.setup_exit(0) + getPage = self.mocker.replace("twisted.web.client.getPage") + getPage( +- CS_STORE_URL + "/charm-info?charms=cs%3Aseries/mysql") ++ CS_STORE_URL + "/charm-info?charms=cs%3Aseries/mysql", ++ contextFactory=ANY) + self.mocker.result(succeed(json.dumps( + {"cs:series/mysql": {"revision": 1, "sha256": "whatever"}}))) + self.mocker.replay() +@@ -502,7 +504,8 @@ + self.setup_exit(0) + + getPage = self.mocker.replace("twisted.web.client.getPage") +- getPage(CS_STORE_URL + "/charm-info?charms=cs%3Aseries/mysql") ++ getPage(CS_STORE_URL + "/charm-info?charms=cs%3Aseries/mysql", ++ contextFactory=ANY) + + self.mocker.result(succeed(json.dumps( + {"cs:series/mysql": {"revision": 1, "sha256": "whatever"}}))) +