[hopem, r=gnuoy] 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:
commit
48eca657cc
@ -13,6 +13,7 @@ from charmhelpers.core.hookenv import (
|
||||
relation_get,
|
||||
unit_get,
|
||||
)
|
||||
import os
|
||||
import socket
|
||||
import dns.resolver
|
||||
|
||||
@ -102,6 +103,12 @@ class MonContext(context.OSContextGenerator):
|
||||
'loglevel': config('loglevel'),
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
123
hooks/hooks.py
123
hooks/hooks.py
@ -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()
|
||||
|
||||
|
@ -31,5 +31,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 %}
|
||||
|
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user