Drop keystone integration for PKI token format

The PKI token format is no longer supported by the keystone charm;
drop code, tests and associated template fragments which deal with
configuration of PKI revocation list processing.

Change-Id: Ie08779c2aef15589b621c324808bb13089fb4f72
Closes-Bug: 1586550
This commit is contained in:
James Page 2019-02-13 11:09:00 +00:00
parent 0f3203b18c
commit 76cddc525e
8 changed files with 0 additions and 445 deletions

View File

@ -135,10 +135,6 @@ options:
type: int
default: 500
description: Number of keystone tokens to hold in local cache.
revocation-check-interval:
type: int
default: 600
description: Interval between revocation checks to keystone.
# HA config
use-syslog:
type: boolean

View File

@ -98,7 +98,6 @@ class IdentityServiceContext(context.IdentityServiceContext):
ctxt['auth_type'] = 'keystone'
ctxt['user_roles'] = config('operator-roles')
ctxt['cache_size'] = config('cache-size')
ctxt['revocation_check_interval'] = config('revocation-check-interval')
if self.context_complete(ctxt):
return ctxt
return {}
@ -204,12 +203,6 @@ class MonContext(context.CephContext):
'unit_public_ip': unit_public_ip(),
}
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
# NOTE(dosaboy): these sections must correspond to what is supported in
# the config template.
sections = ['global', 'client.radosgw.gateway']

View File

@ -75,12 +75,10 @@ from charmhelpers.contrib.openstack.ha.utils import (
generate_ha_relation_data,
)
from utils import (
enable_pocket,
register_configs,
setup_ipv6,
services,
assess_status,
setup_keystone_certs,
disable_unused_apache_sites,
pause_unit_helper,
resume_unit_helper,
@ -99,17 +97,10 @@ from charmhelpers.contrib.openstack.cert_utils import (
hooks = Hooks()
CONFIGS = register_configs()
NSS_DIR = '/var/lib/ceph/nss'
PACKAGES = [
'haproxy',
'libnss3-tools',
'ntp',
'python-keystoneclient',
'python-six', # Ensures correct version is installed for precise
# since python-keystoneclient does not pull in icehouse
# version
'radosgw',
'apache2'
]
@ -166,10 +157,7 @@ def install_packages():
def install():
status_set('maintenance', 'Executing pre-install')
execd_preinstall()
enable_pocket('multiverse')
install_packages()
if not os.path.exists(NSS_DIR):
os.makedirs(NSS_DIR)
if not os.path.exists('/etc/ceph'):
os.makedirs('/etc/ceph')
@ -385,8 +373,6 @@ def configure_https():
if not is_unit_paused_set():
service_reload('apache2', restart_on_failure=True)
setup_keystone_certs(CONFIGS)
@hooks.hook('update-status')
@harden()

View File

@ -13,10 +13,8 @@
# limitations under the License.
import os
import re
import socket
import subprocess
import sys
from collections import OrderedDict
from copy import deepcopy
@ -24,19 +22,11 @@ from copy import deepcopy
import ceph_radosgw_context
from charmhelpers.core.hookenv import (
log,
DEBUG,
ERROR,
INFO,
relation_get,
relation_ids,
related_units,
application_version_set,
)
from charmhelpers.contrib.network.ip import (
format_ipv6_addr,
is_ipv6,
)
from charmhelpers.contrib.openstack import (
context,
templating,
@ -46,9 +36,6 @@ from charmhelpers.contrib.openstack.utils import (
pause_unit,
resume_unit,
)
from charmhelpers.contrib.openstack.keystone import (
format_endpoint,
)
from charmhelpers.contrib.hahelpers.cluster import (
get_hacluster_config,
https,
@ -56,7 +43,6 @@ from charmhelpers.contrib.hahelpers.cluster import (
from charmhelpers.core.host import (
cmp_pkgrevno,
lsb_release,
mkdir,
CompareHostReleases,
init_is_systemd,
)
@ -69,36 +55,6 @@ from charmhelpers.fetch import (
get_upstream_version,
)
# NOTE: some packages are installed by the charm so may not be available
# yet. Calls that depend on them should be aware of this (and use the
# defer_if_unavailable() decorator).
try:
import keystoneclient
from keystoneclient.v2_0 import client
from keystoneclient.v3 import client as client_v3
try:
# Kilo and newer
from keystoneclient.exceptions import (
ConnectionRefused,
Forbidden,
InternalServerError,
)
except ImportError:
# Juno and older
from keystoneclient.exceptions import (
ConnectionError as ConnectionRefused,
Forbidden,
InternalServerError,
)
except ImportError:
keystoneclient = None
# This is installed as a dep of python-keystoneclient
try:
import requests
except ImportError:
requests = None
# The interface is said to be satisfied if anyone of the interfaces in the
# list has a complete context.
REQUIRED_INTERFACES = {
@ -139,14 +95,6 @@ BASE_RESOURCE_MAP = OrderedDict([
])
class KSCertSetupException(BaseException):
"""Keystone SSL Certificate Setup Exception.
This exception should be raised if any part of cert setup fails.
"""
pass
def resource_map():
"""Dynamically generate a map of resources.
@ -198,18 +146,6 @@ def services():
return list(set(_services))
def enable_pocket(pocket):
apt_sources = "/etc/apt/sources.list"
with open(apt_sources, "r") as sources:
lines = sources.readlines()
with open(apt_sources, "w") as sources:
for line in lines:
if pocket in line:
sources.write(re.sub('^# deb', 'deb', line))
else:
sources.write(line)
def get_optional_interfaces():
"""Return the optional interfaces that should be checked if the relavent
relations have appeared.
@ -347,213 +283,6 @@ def get_pkg_version(name):
return version
def defer_if_unavailable(modules):
"""If a function depends on a package/module that is installed by the charm
but may not yet have been installed, it can be deferred using this
decorator.
:param modules: list of modules that must be importable.
"""
def _inner1_defer_if_unavailable(f):
def _inner2_defer_if_unavailable(*args, **kwargs):
for m in modules:
if m not in sys.modules:
log("Module '{}' does not appear to be available "
"yet - deferring call to '{}' until it "
"is.".format(m, f.__name__), level=INFO)
return
return f(*args, **kwargs)
return _inner2_defer_if_unavailable
return _inner1_defer_if_unavailable
@defer_if_unavailable(['keystoneclient'])
def get_ks_cert(ksclient, auth_endpoint, cert_type):
"""Get certificate from keystone.
:param ksclient: Keystone client
:param auth_endpoint: Keystone auth endpoint url
:param certs_path: Path to local certs store
:returns: certificate
"""
if ksclient.version == 'v3':
if cert_type == 'signing':
cert_type = 'certificates'
request = ("{}OS-SIMPLE-CERT/{}"
"".format(auth_endpoint, cert_type))
else:
request = "{}/certificates/{}".format(auth_endpoint, cert_type)
try:
try:
# Kilo and newer
if cert_type == 'ca':
cert = ksclient.certificates.get_ca_certificate()
elif cert_type in ['signing', 'certificates']:
cert = ksclient.certificates.get_signing_certificate()
else:
raise KSCertSetupException("Invalid cert type "
"'{}'".format(cert_type))
except AttributeError:
# Keystone v3 or Juno and older
response = requests.request('GET', request)
if response.status_code == requests.codes.ok:
cert = response.text
else:
raise KSCertSetupException("Unable to retrieve certificate")
except (ConnectionRefused, requests.exceptions.ConnectionError,
Forbidden, InternalServerError):
raise KSCertSetupException("Error connecting to keystone")
return cert
@defer_if_unavailable(['keystoneclient'])
def get_ks_ca_cert(ksclient, auth_endpoint, certs_path):
""""Get and store keystone CA certificate.
:param ksclient: Keystone client
:param auth_endpoint: Keystone auth endpoint url
:param certs_path: Path to local certs store
:returns: None
"""
ca_cert = get_ks_cert(ksclient, auth_endpoint, 'ca')
if ca_cert:
try:
# Cert should not contain unicode chars.
str(ca_cert)
except UnicodeEncodeError:
raise KSCertSetupException("Did not get a valid ca cert from "
"keystone - cert setup incomplete")
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:
raise KSCertSetupException("No ca cert available from keystone")
@defer_if_unavailable(['keystoneclient'])
def get_ks_signing_cert(ksclient, auth_endpoint, certs_path):
""""Get and store keystone signing certificate.
:param ksclient: Keystone client
:param auth_endpoint: Keystone auth endpoint url
:param certs_path: Path to local certs store
:returns: None
"""
signing_cert = get_ks_cert(ksclient, auth_endpoint, 'signing')
if signing_cert:
try:
# Cert should not contain unicode chars.
str(signing_cert)
except UnicodeEncodeError:
raise KSCertSetupException("Invalid signing cert from keystone")
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:
raise KSCertSetupException("No signing cert available from keystone")
@defer_if_unavailable(['keystoneclient'])
def setup_keystone_certs(CONFIGS):
"""
Get CA and signing certs from Keystone used to decrypt revoked token list.
:param unit: context unit id
:param rid: context relation id
:returns: None
"""
certs_path = '/var/lib/ceph/nss'
if not os.path.exists(certs_path):
mkdir(certs_path)
# Do not continue until identity-relation is complete
if 'identity-service' not in CONFIGS.complete_contexts():
log("Missing relation settings - deferring cert setup",
level=DEBUG)
return
ksclient = get_keystone_client_from_relation()
if not ksclient:
log("Failed to get keystoneclient", level=ERROR)
return
auth_endpoint = ksclient.auth_endpoint
try:
get_ks_ca_cert(ksclient, auth_endpoint, certs_path)
get_ks_signing_cert(ksclient, auth_endpoint, certs_path)
except KSCertSetupException as e:
log("Keystone certs setup incomplete - {}".format(e), level=INFO)
# TODO: Move to charmhelpers
# TODO: Make it session aware
def get_keystone_client_from_relation(relation_type='identity-service'):
""" Get keystone client from relation data
:param relation_type: Relation to keystone
:returns: Keystone client
"""
required = ['admin_token', 'auth_host', 'auth_port', 'api_version']
settings = {}
rdata = {}
for relid in relation_ids(relation_type):
for unit in related_units(relid):
rdata = relation_get(unit=unit, rid=relid) or {}
if set(required).issubset(set(rdata.keys())):
settings = {key: rdata.get(key) for key in required}
break
if not settings:
log("Required settings not yet provided by any identity-service "
"relation units", INFO)
return None
auth_protocol = rdata.get('auth_protocol', 'http')
if is_ipv6(settings.get('auth_host')):
settings['auth_host'] = format_ipv6_addr(settings.get('auth_host'))
api_version = rdata.get('api_version')
auth_endpoint = format_endpoint(auth_protocol,
settings['auth_host'],
settings['auth_port'],
settings['api_version'])
if api_version and '3' in api_version:
ksclient = client_v3.Client(token=settings['admin_token'],
endpoint=auth_endpoint)
else:
ksclient = client.Client(token=settings['admin_token'],
endpoint=auth_endpoint)
# Add simple way to retrieve keystone auth endpoint
ksclient.auth_endpoint = auth_endpoint
return ksclient
def disable_unused_apache_sites():
"""Ensure that unused apache configurations are disabled to prevent them
from conflicting with the charm-provided version.

View File

@ -48,11 +48,7 @@ rgw keystone admin token = {{ admin_token }}
{% endif -%}
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
{% if cms -%}
nss db path = /var/lib/ceph/nss
{% endif %}
{% else -%}
rgw swift url = http://{{ unit_public_ip }}
{% endif -%}

View File

@ -85,7 +85,6 @@ class IdentityServiceContextTest(CharmTestCase):
_format_ipv6_addr, jewel_installed=False):
self.test_config.set('operator-roles', 'Babel')
self.test_config.set('cache-size', '42')
self.test_config.set('revocation-check-interval', '7500000')
self.test_relation.set({'admin_token': 'ubuntutesting'})
self.relation_ids.return_value = ['identity-service:5']
self.related_units.return_value = ['keystone/0']
@ -122,7 +121,6 @@ class IdentityServiceContextTest(CharmTestCase):
'auth_protocol': 'http',
'auth_type': 'keystone',
'cache_size': '42',
'revocation_check_interval': '7500000',
'service_host': '127.0.0.4',
'service_port': 9876,
'service_protocol': 'http',
@ -143,7 +141,6 @@ class IdentityServiceContextTest(CharmTestCase):
jewel_installed=False):
self.test_config.set('operator-roles', 'Babel')
self.test_config.set('cache-size', '42')
self.test_config.set('revocation-check-interval', '7500000')
self.test_relation.set({'admin_token': 'ubuntutesting'})
self.relation_ids.return_value = ['identity-service:5']
self.related_units.return_value = ['keystone/0']
@ -178,7 +175,6 @@ class IdentityServiceContextTest(CharmTestCase):
'auth_protocol': 'http',
'auth_type': 'keystone',
'cache_size': '42',
'revocation_check_interval': '7500000',
'service_host': '127.0.0.4',
'service_port': 9876,
'service_protocol': 'http',
@ -199,7 +195,6 @@ class IdentityServiceContextTest(CharmTestCase):
jewel_installed=False):
self.test_config.set('operator-roles', 'Babel')
self.test_config.set('cache-size', '42')
self.test_config.set('revocation-check-interval', '7500000')
self.test_relation.set({'admin_token': 'ubuntutesting'})
self.relation_ids.return_value = ['identity-service:5']
self.related_units.return_value = ['keystone/0']
@ -239,7 +234,6 @@ class IdentityServiceContextTest(CharmTestCase):
'auth_protocol': 'http',
'auth_type': 'keystone',
'cache_size': '42',
'revocation_check_interval': '7500000',
'service_host': '127.0.0.4',
'service_port': 9876,
'service_protocol': 'http',
@ -262,7 +256,6 @@ class IdentityServiceContextTest(CharmTestCase):
_ctxt_comp, _format_ipv6_addr):
self.test_config.set('operator-roles', 'Babel')
self.test_config.set('cache-size', '42')
self.test_config.set('revocation-check-interval', '7500000')
self.test_relation.set({})
self.relation_ids.return_value = ['identity-service:5']
self.related_units.return_value = ['keystone/0']

View File

@ -13,9 +13,7 @@
# limitations under the License.
from mock import (
call,
patch,
mock_open,
MagicMock,
)
@ -26,7 +24,6 @@ from test_utils import CharmTestCase
TO_PATCH = [
'application_version_set',
'get_upstream_version',
'format_endpoint',
'https',
'relation_ids',
'relation_get',
@ -96,137 +93,6 @@ class CephRadosGWUtilTests(CharmTestCase):
# ports=None whilst port checks are disabled.
f.assert_called_once_with('assessor', services='s1', ports=None)
@patch.object(utils, 'get_keystone_client_from_relation')
@patch.object(utils, 'is_ipv6', lambda addr: False)
@patch.object(utils, 'get_ks_signing_cert')
@patch.object(utils, 'get_ks_ca_cert')
@patch.object(utils, 'mkdir')
def test_setup_keystone_certs(self, mock_mkdir,
mock_get_ks_ca_cert,
mock_get_ks_signing_cert,
mock_get_keystone_client):
auth_host = 'foo/bar'
auth_port = 80
auth_url = 'http://%s:%s/v2.0' % (auth_host, auth_port)
mock_ksclient = MagicMock()
mock_ksclient.auth_endpoint = auth_url
mock_get_keystone_client.return_value = mock_ksclient
configs = MagicMock()
configs.complete_contexts.return_value = ['identity-service']
utils.setup_keystone_certs(configs)
mock_get_ks_signing_cert.assert_has_calls([call(mock_ksclient,
auth_url,
'/var/lib/ceph/nss')])
mock_get_ks_ca_cert.assert_has_calls([call(mock_ksclient, auth_url,
'/var/lib/ceph/nss')])
@patch.object(utils, 'client_v3')
@patch.object(utils, 'client')
@patch.object(utils, 'related_units')
@patch.object(utils, 'relation_ids')
@patch.object(utils, 'is_ipv6', lambda addr: False)
@patch.object(utils, 'relation_get')
def test_get_ks_client_from_relation(self, mock_relation_get,
mock_relation_ids,
mock_related_units,
mock_client,
mock_client_v3):
auth_host = 'foo/bar'
auth_port = 80
admin_token = '666'
auth_url = 'http://%s:%s/v2.0' % (auth_host, auth_port)
self.format_endpoint.return_value = auth_url
mock_relation_ids.return_value = ['identity-service:5']
mock_related_units.return_value = ['keystone/1']
rel_data = {'auth_host': auth_host,
'auth_port': auth_port,
'admin_token': admin_token,
'api_version': '2'}
mock_relation_get.return_value = rel_data
utils.get_keystone_client_from_relation()
mock_client.Client.assert_called_with(endpoint=auth_url,
token=admin_token)
auth_url = 'http://%s:%s/v3' % (auth_host, auth_port)
self.format_endpoint.return_value = auth_url
rel_data['api_version'] = '3'
mock_relation_get.return_value = rel_data
utils.get_keystone_client_from_relation()
mock_client_v3.Client.assert_called_with(endpoint=auth_url,
token=admin_token)
@patch.object(utils, 'client_v3')
@patch.object(utils, 'client')
@patch.object(utils, 'related_units')
@patch.object(utils, 'relation_ids')
@patch.object(utils, 'is_ipv6', lambda addr: False)
@patch.object(utils, 'relation_get')
def test_get_ks_client_from_relation_not_available(self, mock_relation_get,
mock_relation_ids,
mock_related_units,
mock_client,
mock_client_v3):
mock_relation_ids.return_value = ['identity-service:5']
mock_related_units.return_value = ['keystone/1']
rel_data = {'auth_port': '5000',
'admin_token': 'foo',
'api_version': '2'}
mock_relation_get.return_value = rel_data
ksclient = utils.get_keystone_client_from_relation()
self.assertIsNone(ksclient)
@patch.object(utils, 'get_ks_cert')
@patch.object(utils.subprocess, 'Popen')
@patch.object(utils.subprocess, 'check_output')
def test_get_ks_signing_cert(self, mock_check_output, mock_Popen,
mock_get_ks_cert):
auth_host = 'foo/bar'
auth_port = 80
admin_token = '666'
auth_url = 'http://%s:%s/v2.0' % (auth_host, auth_port)
m = mock_open()
with patch.object(utils, 'open', m, create=True):
mock_get_ks_cert.return_value = 'signing_cert_data'
utils.get_ks_signing_cert(admin_token, auth_url, '/foo/bar')
mock_get_ks_cert.return_value = None
with self.assertRaises(utils.KSCertSetupException):
utils.get_ks_signing_cert(admin_token, auth_url, '/foo/bar')
c = ['openssl', 'x509', '-in',
'/foo/bar/signing_certificate.pem',
'-pubkey']
mock_check_output.assert_called_with(c)
@patch.object(utils, 'get_ks_cert')
@patch.object(utils.subprocess, 'Popen')
@patch.object(utils.subprocess, 'check_output')
def test_get_ks_ca_cert(self, mock_check_output, mock_Popen,
mock_get_ks_cert):
auth_host = 'foo/bar'
auth_port = 80
admin_token = '666'
auth_url = 'http://%s:%s/v2.0' % (auth_host, auth_port)
m = mock_open()
with patch.object(utils, 'open', m, create=True):
mock_get_ks_cert.return_value = 'ca_cert_data'
utils.get_ks_ca_cert(admin_token, auth_url, '/foo/bar')
mock_get_ks_cert.return_value = None
with self.assertRaises(utils.KSCertSetupException):
utils.get_ks_ca_cert(admin_token, auth_url, '/foo/bar')
c = ['openssl', 'x509', '-in', '/foo/bar/ca.pem',
'-pubkey']
mock_check_output.assert_called_with(c)
def _setup_relation_data(self, data):
self.relation_ids.return_value = data.keys()
self.related_units.side_effect = (

View File

@ -37,7 +37,6 @@ TO_PATCH = [
'config',
'cmp_pkgrevno',
'execd_preinstall',
'enable_pocket',
'log',
'open_port',
'os',
@ -55,7 +54,6 @@ TO_PATCH = [
'service_stop',
'service_restart',
'service',
'setup_keystone_certs',
'service_name',
'socket',
'restart_map',
@ -150,8 +148,6 @@ class CephRadosGWTests(CharmTestCase):
ceph_hooks.install()
self.assertTrue(self.execd_preinstall.called)
self.assertTrue(_install_packages.called)
self.enable_pocket.assert_called_with('multiverse')
self.os.makedirs.called_with('/var/lib/ceph/nss')
@patch.object(ceph_hooks, 'certs_joined')
@patch.object(ceph_hooks, 'update_nrpe_config')