[hopem,r=]

Configure rados gateway nss with CA and signing certs
from keystone so that it can decrypt revoked token
list from keystone.

Partially-Closes-Bug: 1520339
This commit is contained in:
Edward Hope-Morley 2015-12-26 19:21:00 -05:00
parent d96c019ab2
commit 29d22731f9
4 changed files with 141 additions and 11 deletions

View File

@ -13,6 +13,7 @@ from charmhelpers.core.hookenv import (
relation_get,
unit_get,
)
import os
import socket
import dns.resolver
@ -101,6 +102,12 @@ class MonContext(context.OSContextGenerator):
'embedded_webserver': config('use-embedded-webserver'),
}
certs_path = '/var/lib/ceph/nss'
paths = [os.path.join(certs_path, 'ca.pem'),
os.path.join(certs_path, 'signing_certificate.pem')]
if all([os.path.isfile(p) for p in paths]):
ctxt['cms'] = True
if self.context_complete(ctxt):
return ctxt

View File

@ -13,14 +13,19 @@ import sys
import glob
import os
import ceph
from charmhelpers.core.hookenv import (
relation_get,
relation_ids,
related_units,
config,
unit_get,
open_port,
relation_set,
log, ERROR,
log,
DEBUG,
WARNING,
ERROR,
Hooks, UnregisteredHookError,
status_set,
)
@ -43,9 +48,11 @@ from utils import (
REQUIRED_INTERFACES,
check_optional_relations,
)
from charmhelpers.payload.execd import execd_preinstall
from charmhelpers.core.host import cmp_pkgrevno
from charmhelpers.core.host import (
cmp_pkgrevno,
mkdir,
)
from charmhelpers.contrib.network.ip import (
get_iface_for_address,
@ -89,6 +96,11 @@ PACKAGES = [
'radosgw',
'ntp',
'haproxy',
'libnss3-tools',
'python-keystoneclient',
'python-six', # Ensures correct version is installed for precise
# since python-keystoneclient does not pull in icehouse
# version
]
APACHE_PACKAGES = [
@ -155,6 +167,99 @@ def apache_ports():
shutil.copy('files/ports.conf', '/etc/apache2/ports.conf')
def setup_keystone_certs(unit=None, rid=None):
"""
Get CA and signing certs from Keystone used to decrypt revoked token list.
"""
import requests
try:
# Kilo and newer
from keystoneclient.exceptions import ConnectionRefused
except ImportError:
# Juno and older
from keystoneclient.exceptions import ConnectionError as \
ConnectionRefused
from keystoneclient.v2_0 import client
certs_path = '/var/lib/ceph/nss'
mkdir(certs_path)
rdata = relation_get(unit=unit, rid=rid)
auth_protocol = rdata.get('auth_protocol', 'http')
required_keys = ['admin_token', 'auth_host', 'auth_port']
settings = {}
for key in required_keys:
settings[key] = rdata.get(key)
if not all(settings.values()):
log("Missing relation settings (%s) - skipping cert setup" %
(', '.join([k for k in settings.keys() if not settings[k]])),
level=DEBUG)
return
auth_endpoint = "%s://%s:%s/v2.0" % (auth_protocol, settings['auth_host'],
settings['auth_port'])
keystone = client.Client(token=settings['admin_token'],
endpoint=auth_endpoint)
# CA
try:
# Kilo and newer
ca_cert = keystone.certificates.get_ca_certificate()
except AttributeError:
# Juno and older
ca_cert = requests.request('GET', auth_endpoint +
'/certificates/ca').text
except ConnectionRefused:
log("Error connecting to keystone - skipping ca/signing cert setup",
level=WARNING)
return
if ca_cert:
log("Updating ca cert from keystone", level=DEBUG)
ca = os.path.join(certs_path, 'ca.pem')
with open(ca, 'w') as fd:
fd.write(ca_cert)
out = subprocess.check_output(['openssl', 'x509', '-in', ca,
'-pubkey'])
p = subprocess.Popen(['certutil', '-d', certs_path, '-A', '-n', 'ca',
'-t', 'TCu,Cu,Tuw'], stdin=subprocess.PIPE)
p.communicate(out)
else:
log("No ca cert available from keystone", level=DEBUG)
# Signing cert
try:
# Kilo and newer
signing_cert = keystone.certificates.get_signing_certificate()
except AttributeError:
# Juno and older
signing_cert = requests.request('GET', auth_endpoint +
'/certificates/signing').text
except ConnectionRefused:
log("Error connecting to keystone - skipping ca/signing cert setup",
level=WARNING)
return
if signing_cert:
log("Updating signing cert from keystone", level=DEBUG)
signing_cert_path = os.path.join(certs_path, 'signing_certificate.pem')
with open(signing_cert_path, 'w') as fd:
fd.write(signing_cert)
out = subprocess.check_output(['openssl', 'x509', '-in',
signing_cert_path, '-pubkey'])
p = subprocess.Popen(['certutil', '-A', '-d', certs_path, '-n',
'signing_cert', '-t', 'P,P,P'],
stdin=subprocess.PIPE)
p.communicate(out)
else:
log("No signing cert available from keystone", level=DEBUG)
@hooks.hook('upgrade-charm',
'config-changed')
@restart_on_change({'/etc/ceph/ceph.conf': ['radosgw'],
@ -170,8 +275,9 @@ def config_changed():
apache_modules()
apache_ports()
apache_reload()
for r_id in relation_ids('identity-service'):
identity_joined(relid=r_id)
identity_changed(relid=r_id)
@hooks.hook('mon-relation-departed',
@ -225,10 +331,17 @@ def identity_joined(relid=None):
requested_roles=config('operator-roles'),
relation_id=relid)
if relid:
for unit in related_units(relid):
setup_keystone_certs(unit=unit, rid=relid)
else:
setup_keystone_certs()
@hooks.hook('identity-service-relation-changed')
@restart_on_change({'/etc/ceph/ceph.conf': ['radosgw']})
def identity_changed():
def identity_changed(relid=None):
identity_joined(relid)
CONFIGS.write_all()
restart()

View File

@ -30,5 +30,7 @@ rgw keystone accepted roles = {{ user_roles }}
rgw keystone token cache size = {{ cache_size }}
rgw keystone revocation interval = {{ revocation_check_interval }}
rgw s3 auth use keystone = true
#nss db path = /var/lib/ceph/nss
{% if cms -%}
nss db path = /var/lib/ceph/nss
{% endif %}
{% endif %}

View File

@ -43,6 +43,7 @@ TO_PATCH = [
'relation_ids',
'relation_set',
'relation_get',
'related_units',
'render_template',
'shutil',
'status_set',
@ -108,9 +109,8 @@ class CephRadosGWTests(CharmTestCase):
self.add_source.assert_called_with('distro', 'secretkey')
self.assertTrue(self.apt_update.called)
self.assertFalse(_install_packages.called)
self.apt_install.assert_called_with(['radosgw',
'ntp',
'haproxy'], fatal=True)
self.apt_install.assert_called_with(ceph_hooks.PACKAGES,
fatal=True)
self.apt_purge.assert_called_with(['libapache2-mod-fastcgi',
'apache2'])
@ -167,6 +167,7 @@ class CephRadosGWTests(CharmTestCase):
]
self.subprocess.call.assert_has_calls(calls)
@patch.object(ceph_hooks, 'mkdir', lambda *args: None)
def test_config_changed(self):
_install_packages = self.patch('install_packages')
_emit_apacheconf = self.patch('emit_apacheconf')
@ -221,12 +222,15 @@ class CephRadosGWTests(CharmTestCase):
cmd = ['service', 'radosgw', 'restart']
self.subprocess.call.assert_called_with(cmd)
@patch.object(ceph_hooks, 'setup_keystone_certs')
@patch('charmhelpers.contrib.openstack.ip.service_name',
lambda *args: 'ceph-radosgw')
@patch('charmhelpers.contrib.openstack.ip.config')
def test_identity_joined_early_version(self, _config):
def test_identity_joined_early_version(self, _config,
mock_setup_keystone_certs):
self.cmp_pkgrevno.return_value = -1
ceph_hooks.identity_joined()
self.assertTrue(mock_setup_keystone_certs.called)
self.sys.exit.assert_called_with(1)
@patch('charmhelpers.contrib.openstack.ip.service_name',
@ -234,6 +238,7 @@ class CephRadosGWTests(CharmTestCase):
@patch('charmhelpers.contrib.openstack.ip.resolve_address')
@patch('charmhelpers.contrib.openstack.ip.config')
def test_identity_joined(self, _config, _resolve_address):
self.related_units = ['unit/0']
self.cmp_pkgrevno.return_value = 1
_resolve_address.return_value = 'myserv'
_config.side_effect = self.test_config.get
@ -257,6 +262,7 @@ class CephRadosGWTests(CharmTestCase):
@patch('charmhelpers.contrib.openstack.ip.config')
def test_identity_joined_public_name(self, _config, _unit_get,
_is_clustered):
self.related_units = ['unit/0']
_config.side_effect = self.test_config.get
self.test_config.set('os-public-hostname', 'files.example.com')
_unit_get.return_value = 'myserv'
@ -271,11 +277,13 @@ class CephRadosGWTests(CharmTestCase):
relation_id='rid',
admin_url='http://myserv:80/swift')
def test_identity_changed(self):
@patch.object(ceph_hooks, 'identity_joined')
def test_identity_changed(self, mock_identity_joined):
_restart = self.patch('restart')
ceph_hooks.identity_changed()
self.CONFIGS.write_all.assert_called_with()
self.assertTrue(_restart.called)
self.assertTrue(mock_identity_joined.called)
@patch('charmhelpers.contrib.openstack.ip.is_clustered')
@patch('charmhelpers.contrib.openstack.ip.unit_get')