From 575d729487aabda3366c8d50650fcf24d66bdac8 Mon Sep 17 00:00:00 2001 From: James Page Date: Wed, 25 Oct 2017 15:04:17 +0100 Subject: [PATCH] Install system certs in snap accessible location Ensure that updated system ca-certificates.crt file is copied into a location within SNAP_COMMON so that snap based services can use private CA certs. Change-Id: Ie948a239e962ce9fdfc868b1741dcccb0711afb8 Related-Bug: 1724012 --- charms_openstack/charm/classes.py | 20 ++++++++ .../charms_openstack/charm/test_classes.py | 46 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/charms_openstack/charm/classes.py b/charms_openstack/charm/classes.py index 420aff4..2fe9d1b 100644 --- a/charms_openstack/charm/classes.py +++ b/charms_openstack/charm/classes.py @@ -2,6 +2,7 @@ import base64 import contextlib import os import random +import shutil import string import subprocess @@ -29,6 +30,8 @@ VIP_KEY = "vip" CIDR_KEY = "vip_cidr" IFACE_KEY = "vip_iface" APACHE_SSL_VHOST = '/etc/apache2/sites-available/openstack_https_frontend.conf' +SYSTEM_CA_CERTS = '/etc/ssl/certs/ca-certificates.crt' +SNAP_CA_CERTS = '/var/snap/{}/common/etc/ssl/certs/ca-certificates.crt' class OpenStackCharm(BaseOpenStackCharm, @@ -630,6 +633,7 @@ class HAOpenStackCharm(OpenStackAPICharm): for path in cert_files} if checksums != new_checksums and update_certs: self.run_update_certs() + self.install_snap_certs() def configure_ca(self, ca_cert, update_certs=True): """Write Certificate Authority certificate""" @@ -650,6 +654,22 @@ class HAOpenStackCharm(OpenStackAPICharm): """ subprocess.check_call(['update-ca-certificates', '--fresh']) + def install_snap_certs(self): + """Install systems CA certificates for a snap + + Installs the aggregated host system ca-certificates.crt into + $SNAP_COMMON/etc/ssl/certs for services running within a sandboxed + snap to consume. + + Snaps should set the REQUESTS_CA_BUNDLE environment variable to + ensure requests based API calls use the updated system certs. + """ + if (os_utils.snap_install_requested() and + os.path.exists(SYSTEM_CA_CERTS)): + ca_certs = SNAP_CA_CERTS.format(self.primary_snap) + ch_host.mkdir(os.path.dirname(ca_certs)) + shutil.copyfile(SYSTEM_CA_CERTS, ca_certs) + def update_peers(self, cluster): """Update peers in the cluster about the addresses that this unit holds. diff --git a/unit_tests/charms_openstack/charm/test_classes.py b/unit_tests/charms_openstack/charm/test_classes.py index fe51adf..3e47000 100644 --- a/unit_tests/charms_openstack/charm/test_classes.py +++ b/unit_tests/charms_openstack/charm/test_classes.py @@ -747,6 +747,7 @@ class TestHAOpenStackCharm(BaseOpenStackCharmTest): def test_configure_ca(self): self.patch_target('run_update_certs') + self.patch_target('install_snap_certs') with utils.patch_open() as (mock_open, mock_file): self.target.configure_ca('myca') mock_open.assert_called_with( @@ -760,6 +761,49 @@ class TestHAOpenStackCharm(BaseOpenStackCharmTest): self.check_call.assert_called_once_with( ['update-ca-certificates', '--fresh']) + def test_install_snap_certs(self): + self.patch_object(chm.os_utils, 'snap_install_requested', + return_value=True) + self.patch_object(chm.shutil, 'copyfile') + self.patch_object(chm.ch_host, 'mkdir') + self.patch_object(chm.os.path, 'exists', return_value=True) + self.target.snaps = ['mysnap'] + + self.target.install_snap_certs() + + self.exists.assert_called_with('/etc/ssl/certs/ca-certificates.crt') + self.copyfile.assert_called_with( + '/etc/ssl/certs/ca-certificates.crt', + '/var/snap/mysnap/common/etc/ssl/certs/ca-certificates.crt', + ) + self.mkdir.assert_called_with('/var/snap/mysnap/common/etc/ssl/certs') + + self.snap_install_requested.reset_mock() + self.snap_install_requested.return_value = True + self.exists.reset_mock() + self.exists.return_value = False + self.copyfile.reset_mock() + self.mkdir.reset_mock() + + self.target.install_snap_certs() + + self.exists.assert_called_with('/etc/ssl/certs/ca-certificates.crt') + self.mkdir.assert_not_called() + self.copyfile.assert_not_called() + + self.snap_install_requested.reset_mock() + self.snap_install_requested.return_value = False + self.exists.reset_mock() + self.exists.return_value = True + self.copyfile.reset_mock() + self.mkdir.reset_mock() + + self.target.install_snap_certs() + + self.exists.assert_not_called() + self.mkdir.assert_not_called() + self.copyfile.assert_not_called() + def test_update_central_cacerts(self): self.patch_target('run_update_certs') change_hashes = ['hash1', 'hash2'] @@ -771,6 +815,8 @@ class TestHAOpenStackCharm(BaseOpenStackCharmTest): return fake_hash_inner self.patch_object(chm.ch_host, 'path_hash') self.path_hash.side_effect = fake_hash(change_hashes) + self.patch_object(chm.os_utils, 'snap_install_requested', + return_value=False) with self.target.update_central_cacerts(['file1']): pass self.run_update_certs.assert_called_with()