Metadata service refactoring
This refeactoring is required in order to support services that provide metadata in a format which differs from the OpenStack one.
This commit is contained in:
parent
16798c76b6
commit
209592e2d6
@ -20,17 +20,18 @@ from cloudbaseinit.openstack.common import log as logging
|
||||
from cloudbaseinit.utils import classloader
|
||||
|
||||
opts = [
|
||||
cfg.ListOpt('metadata_services',
|
||||
default=[
|
||||
'cloudbaseinit.metadata.services.httpservice.HttpService',
|
||||
'cloudbaseinit.metadata.services.configdrive.configdrive.'
|
||||
'ConfigDriveService',
|
||||
'cloudbaseinit.metadata.services.ec2service.EC2Service'
|
||||
],
|
||||
help='List of enabled metadata service classes, '
|
||||
'to be tested fro availability in the provided order. '
|
||||
'The first available service will be used to retrieve '
|
||||
'metadata')
|
||||
cfg.ListOpt(
|
||||
'metadata_services',
|
||||
default=[
|
||||
'cloudbaseinit.metadata.services.httpservice.HttpService',
|
||||
'cloudbaseinit.metadata.services.configdrive.configdrive.'
|
||||
'ConfigDriveService',
|
||||
'cloudbaseinit.metadata.services.ec2service.EC2Service'
|
||||
],
|
||||
help='List of enabled metadata service classes, '
|
||||
'to be tested fro availability in the provided order. '
|
||||
'The first available service will be used to retrieve '
|
||||
'metadata')
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -15,8 +15,6 @@
|
||||
# under the License.
|
||||
|
||||
import abc
|
||||
import json
|
||||
import posixpath
|
||||
import time
|
||||
|
||||
from oslo.config import cfg
|
||||
@ -53,10 +51,6 @@ class BaseMetadataService(object):
|
||||
def load(self):
|
||||
self._cache = {}
|
||||
|
||||
@property
|
||||
def can_post_password(self):
|
||||
return False
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_data(self, path):
|
||||
pass
|
||||
@ -84,41 +78,37 @@ class BaseMetadataService(object):
|
||||
self._cache[path] = data
|
||||
return data
|
||||
|
||||
def get_content(self, data_type, name):
|
||||
path = posixpath.normpath(
|
||||
posixpath.join(data_type, 'content', name))
|
||||
return self._get_cache_data(path)
|
||||
def get_content(self, name):
|
||||
pass
|
||||
|
||||
def get_user_data(self, data_type, version='latest'):
|
||||
path = posixpath.normpath(
|
||||
posixpath.join(data_type, version, 'user_data'))
|
||||
return self._get_cache_data(path)
|
||||
def get_user_data(self):
|
||||
pass
|
||||
|
||||
def get_meta_data(self, data_type, version='latest'):
|
||||
path = posixpath.normpath(
|
||||
posixpath.join(data_type, version, 'meta_data.json'))
|
||||
data = self._get_cache_data(path)
|
||||
if type(data) is str:
|
||||
return json.loads(self._get_cache_data(path))
|
||||
else:
|
||||
return data
|
||||
def get_host_name(self):
|
||||
pass
|
||||
|
||||
def _post_data(self, path, data):
|
||||
raise NotExistingMetadataException()
|
||||
def get_public_keys(self):
|
||||
pass
|
||||
|
||||
def _get_password_path(self, version='latest'):
|
||||
return posixpath.normpath(posixpath.join('openstack',
|
||||
version,
|
||||
'password'))
|
||||
def get_network_config(self):
|
||||
pass
|
||||
|
||||
def is_password_set(self, version='latest'):
|
||||
path = self._get_password_path(version)
|
||||
return len(self._get_data(path)) > 0
|
||||
def get_admin_password(self):
|
||||
pass
|
||||
|
||||
def post_password(self, enc_password_b64, version='latest'):
|
||||
path = self._get_password_path(version)
|
||||
action = lambda: self._post_data(path, enc_password_b64)
|
||||
return self._exec_with_retry(action)
|
||||
@property
|
||||
def can_post_password(self):
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_password_set(self):
|
||||
return False
|
||||
|
||||
def post_password(self, enc_password_b64):
|
||||
pass
|
||||
|
||||
def get_client_auth_certs(self):
|
||||
pass
|
||||
|
||||
def cleanup(self):
|
||||
pass
|
||||
|
113
cloudbaseinit/metadata/services/baseopenstackservice.py
Normal file
113
cloudbaseinit/metadata/services/baseopenstackservice.py
Normal file
@ -0,0 +1,113 @@
|
||||
# Copyright 2014 Cloudbase Solutions Srl
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import posixpath
|
||||
import urllib2
|
||||
import urlparse
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from cloudbaseinit.metadata.services import base
|
||||
from cloudbaseinit.openstack.common import log as logging
|
||||
from cloudbaseinit.osutils import factory as osutils_factory
|
||||
from cloudbaseinit.plugins.windows import x509
|
||||
|
||||
opts = [
|
||||
cfg.StrOpt('metadata_base_url', default='http://169.254.169.254/',
|
||||
help='The base URL where the service looks for metadata'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(opts)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseOpenStackService(base.BaseMetadataService):
|
||||
|
||||
def get_content(self, name):
|
||||
path = posixpath.normpath(
|
||||
posixpath.join('openstack', 'content', name))
|
||||
return self._get_cache_data(path)
|
||||
|
||||
def get_user_data(self):
|
||||
path = posixpath.normpath(
|
||||
posixpath.join('openstack', 'latest', 'user_data'))
|
||||
return self._get_cache_data(path)
|
||||
|
||||
def _get_meta_data(self, version='latest'):
|
||||
path = posixpath.normpath(
|
||||
posixpath.join('openstack', version, 'meta_data.json'))
|
||||
data = self._get_cache_data(path)
|
||||
if type(data) is str:
|
||||
return json.loads(self._get_cache_data(path))
|
||||
else:
|
||||
return data
|
||||
|
||||
def get_host_name(self):
|
||||
return self._get_meta_data().get('hostname')
|
||||
|
||||
def get_public_keys(self):
|
||||
public_keys = self._get_meta_data().get('public_keys')
|
||||
if public_keys:
|
||||
return public_keys.values()
|
||||
|
||||
def get_network_config(self):
|
||||
return self._get_meta_data().get('network_config')
|
||||
|
||||
def get_admin_password(self):
|
||||
meta_data = self._get_meta_data()
|
||||
meta = meta_data.get('meta')
|
||||
|
||||
if meta and 'admin_pass' in meta:
|
||||
password = meta['admin_pass']
|
||||
elif 'admin_pass' in meta_data:
|
||||
password = meta_data['admin_pass']
|
||||
else:
|
||||
password = None
|
||||
|
||||
return password
|
||||
|
||||
def get_client_auth_certs(self):
|
||||
cert_data = None
|
||||
|
||||
meta_data = self._get_meta_data()
|
||||
meta = meta_data.get('meta')
|
||||
|
||||
if meta:
|
||||
i = 0
|
||||
while True:
|
||||
# Chunking is necessary as metadata items can be
|
||||
# max. 255 chars long
|
||||
cert_chunk = meta.get('admin_cert%d' % i)
|
||||
if not cert_chunk:
|
||||
break
|
||||
if not cert_data:
|
||||
cert_data = cert_chunk
|
||||
else:
|
||||
cert_data += cert_chunk
|
||||
i += 1
|
||||
|
||||
if not cert_data:
|
||||
# Look if the user_data contains a PEM certificate
|
||||
try:
|
||||
user_data = self.get_user_data()
|
||||
if user_data.startswith(x509.PEM_HEADER):
|
||||
cert_data = user_data
|
||||
except base.NotExistingMetadataException:
|
||||
LOG.debug("user_data metadata not present")
|
||||
|
||||
if cert_data:
|
||||
return [cert_data]
|
@ -22,6 +22,7 @@ import uuid
|
||||
from oslo.config import cfg
|
||||
|
||||
from cloudbaseinit.metadata.services import base
|
||||
from cloudbaseinit.metadata.services import baseopenstackservice
|
||||
from cloudbaseinit.openstack.common import log as logging
|
||||
from cloudbaseinit.metadata.services.configdrive import manager
|
||||
|
||||
@ -38,7 +39,7 @@ CONF.register_opts(opts)
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ConfigDriveService(base.BaseMetadataService):
|
||||
class ConfigDriveService(baseopenstackservice.BaseOpenStackService):
|
||||
def __init__(self):
|
||||
super(ConfigDriveService, self).__init__()
|
||||
self._metadata_path = None
|
||||
|
@ -21,6 +21,7 @@ import urlparse
|
||||
from oslo.config import cfg
|
||||
|
||||
from cloudbaseinit.metadata.services import base
|
||||
from cloudbaseinit.metadata.services import baseopenstackservice
|
||||
from cloudbaseinit.openstack.common import log as logging
|
||||
from cloudbaseinit.osutils import factory as osutils_factory
|
||||
|
||||
@ -35,7 +36,9 @@ CONF.register_opts(opts)
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HttpService(base.BaseMetadataService):
|
||||
class HttpService(baseopenstackservice.BaseOpenStackService):
|
||||
_POST_PASSWORD_MD_VER = '2013-04-04'
|
||||
|
||||
def __init__(self):
|
||||
super(HttpService, self).__init__()
|
||||
self._enable_retry = True
|
||||
@ -72,17 +75,13 @@ class HttpService(base.BaseMetadataService):
|
||||
self._check_metadata_ip_route()
|
||||
|
||||
try:
|
||||
self.get_meta_data('openstack')
|
||||
self._get_meta_data()
|
||||
return True
|
||||
except:
|
||||
except Exception:
|
||||
LOG.debug('Metadata not found at URL \'%s\'' %
|
||||
CONF.metadata_base_url)
|
||||
return False
|
||||
|
||||
@property
|
||||
def can_post_password(self):
|
||||
return True
|
||||
|
||||
def _get_response(self, req):
|
||||
try:
|
||||
return urllib2.urlopen(req)
|
||||
@ -106,10 +105,27 @@ class HttpService(base.BaseMetadataService):
|
||||
self._get_response(req)
|
||||
return True
|
||||
|
||||
def post_password(self, enc_password_b64, version='latest'):
|
||||
def _get_password_path(self):
|
||||
return 'openstack/%s/password' % self._POST_PASSWORD_MD_VER
|
||||
|
||||
@property
|
||||
def can_post_password(self):
|
||||
try:
|
||||
return super(HttpService, self).post_password(enc_password_b64,
|
||||
version)
|
||||
self._get_meta_data(self._POST_PASSWORD_MD_VER)
|
||||
return True
|
||||
except base.NotExistingMetadataException:
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_password_set(self):
|
||||
path = self._get_password_path()
|
||||
return len(self._get_data(path)) > 0
|
||||
|
||||
def post_password(self, enc_password_b64):
|
||||
try:
|
||||
path = self._get_password_path()
|
||||
action = lambda: self._post_data(path, enc_password_b64)
|
||||
return self._exec_with_retry(action)
|
||||
except urllib2.HTTPError as ex:
|
||||
if ex.code == 409:
|
||||
# Password already set
|
||||
|
@ -16,7 +16,8 @@
|
||||
A temporary helper which emulates cloudbaseinit.messaging.Notifier.
|
||||
|
||||
This helper method allows us to do the tedious porting to the new Notifier API
|
||||
as a standalone commit so that the commit which switches us to cloudbaseinit.messaging
|
||||
as a standalone commit so that the commit which switches us to
|
||||
cloudbaseinit.messaging
|
||||
is smaller and easier to review. This file will be removed as part of that
|
||||
commit.
|
||||
"""
|
||||
|
@ -36,17 +36,16 @@ CONF.register_opts(opts)
|
||||
|
||||
class NetworkConfigPlugin(base.BasePlugin):
|
||||
def execute(self, service, shared_data):
|
||||
meta_data = service.get_meta_data('openstack')
|
||||
if 'network_config' not in meta_data:
|
||||
network_config = service.get_network_config()
|
||||
if not network_config:
|
||||
return (base.PLUGIN_EXECUTION_DONE, False)
|
||||
|
||||
network_config = meta_data['network_config']
|
||||
if 'content_path' not in network_config:
|
||||
return (base.PLUGIN_EXECUTION_DONE, False)
|
||||
|
||||
content_path = network_config['content_path']
|
||||
content_name = content_path.rsplit('/', 1)[-1]
|
||||
debian_network_conf = service.get_content('openstack', content_name)
|
||||
debian_network_conf = service.get_content(content_name)
|
||||
|
||||
LOG.debug('network config content:\n%s' % debian_network_conf)
|
||||
|
||||
|
@ -36,14 +36,14 @@ NETBIOS_HOST_NAME_MAX_LEN = 15
|
||||
|
||||
class SetHostNamePlugin(base.BasePlugin):
|
||||
def execute(self, service, shared_data):
|
||||
meta_data = service.get_meta_data('openstack')
|
||||
if 'hostname' not in meta_data:
|
||||
osutils = osutils_factory.OSUtilsFactory().get_os_utils()
|
||||
|
||||
metadata_host_name = service.get_host_name()
|
||||
if not metadata_host_name:
|
||||
LOG.debug('Hostname not found in metadata')
|
||||
return (base.PLUGIN_EXECUTION_DONE, False)
|
||||
|
||||
osutils = osutils_factory.OSUtilsFactory().get_os_utils()
|
||||
|
||||
metadata_host_name = meta_data['hostname'].split('.', 1)[0]
|
||||
metadata_host_name = metadata_host_name.split('.', 1)[0]
|
||||
|
||||
if (len(metadata_host_name) > NETBIOS_HOST_NAME_MAX_LEN and
|
||||
CONF.netbios_host_name_compatibility):
|
||||
@ -56,6 +56,7 @@ class SetHostNamePlugin(base.BasePlugin):
|
||||
else:
|
||||
new_host_name = metadata_host_name
|
||||
|
||||
LOG.info("Setting hostname: %s" % new_host_name)
|
||||
reboot_required = osutils.set_host_name(new_host_name)
|
||||
|
||||
return (base.PLUGIN_EXECUTION_DONE, reboot_required)
|
||||
|
@ -18,7 +18,6 @@ import base64
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from cloudbaseinit.metadata.services import base as services_base
|
||||
from cloudbaseinit.openstack.common import log as logging
|
||||
from cloudbaseinit.osutils import factory as osutils_factory
|
||||
from cloudbaseinit.plugins import base
|
||||
@ -38,8 +37,6 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SetUserPasswordPlugin(base.BasePlugin):
|
||||
_post_password_md_ver = '2013-04-04'
|
||||
|
||||
def _encrypt_password(self, ssh_pub_key, password):
|
||||
cm = crypt.CryptManager()
|
||||
with cm.load_ssh_rsa_public_key(ssh_pub_key) as rsa:
|
||||
@ -47,29 +44,15 @@ class SetUserPasswordPlugin(base.BasePlugin):
|
||||
return base64.b64encode(enc_password)
|
||||
|
||||
def _get_ssh_public_key(self, service):
|
||||
meta_data = service.get_meta_data('openstack',
|
||||
self._post_password_md_ver)
|
||||
if not 'public_keys' in meta_data:
|
||||
return False
|
||||
|
||||
public_keys = meta_data['public_keys']
|
||||
ssh_pub_key = None
|
||||
for k in public_keys:
|
||||
# Get the first key
|
||||
ssh_pub_key = public_keys[k]
|
||||
break
|
||||
return ssh_pub_key
|
||||
public_keys = service.get_public_keys()
|
||||
if public_keys:
|
||||
return public_keys[0]
|
||||
|
||||
def _get_password(self, service, osutils):
|
||||
meta_data = service.get_meta_data('openstack')
|
||||
meta = meta_data.get('meta')
|
||||
password = None
|
||||
|
||||
if CONF.inject_user_password:
|
||||
if meta and 'admin_pass' in meta:
|
||||
password = meta['admin_pass']
|
||||
elif 'admin_pass' in meta_data:
|
||||
password = meta_data['admin_pass']
|
||||
password = service.get_admin_password()
|
||||
else:
|
||||
password = None
|
||||
|
||||
if password:
|
||||
LOG.warn('Using admin_pass metadata user password. Consider '
|
||||
@ -86,21 +69,13 @@ class SetUserPasswordPlugin(base.BasePlugin):
|
||||
return password
|
||||
|
||||
def _set_metadata_password(self, password, service):
|
||||
try:
|
||||
ssh_pub_key = self._get_ssh_public_key(service)
|
||||
if ssh_pub_key:
|
||||
enc_password_b64 = self._encrypt_password(ssh_pub_key,
|
||||
password)
|
||||
return service.post_password(enc_password_b64,
|
||||
self._post_password_md_ver)
|
||||
else:
|
||||
LOG.info('No SSH public key available for password encryption')
|
||||
return True
|
||||
except services_base.NotExistingMetadataException:
|
||||
# Requested version not available or password feature
|
||||
# not implemented
|
||||
LOG.info('Cannot set the password in the metadata as it is not '
|
||||
'supported by this metadata version')
|
||||
ssh_pub_key = self._get_ssh_public_key(service)
|
||||
if ssh_pub_key:
|
||||
enc_password_b64 = self._encrypt_password(ssh_pub_key,
|
||||
password)
|
||||
return service.post_password(enc_password_b64)
|
||||
else:
|
||||
LOG.info('No SSH public key available for password encryption')
|
||||
return True
|
||||
|
||||
def _set_password(self, service, osutils, user_name):
|
||||
@ -115,8 +90,7 @@ class SetUserPasswordPlugin(base.BasePlugin):
|
||||
user_name = shared_data.get(constants.SHARED_DATA_USERNAME,
|
||||
CONF.username)
|
||||
|
||||
if (service.can_post_password and
|
||||
service.is_password_set(self._post_password_md_ver)):
|
||||
if service.can_post_password and service.is_password_set:
|
||||
LOG.debug('User\'s password already set in the instance metadata')
|
||||
else:
|
||||
osutils = osutils_factory.OSUtilsFactory().get_os_utils()
|
||||
|
@ -28,8 +28,9 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
class SetUserSSHPublicKeysPlugin(base.BasePlugin):
|
||||
def execute(self, service, shared_data):
|
||||
meta_data = service.get_meta_data('openstack')
|
||||
if not 'public_keys' in meta_data:
|
||||
public_keys = service.get_public_keys()
|
||||
if not public_keys:
|
||||
LOG.debug('Public keys not found in metadata')
|
||||
return (base.PLUGIN_EXECUTION_DONE, False)
|
||||
|
||||
username = CONF.username
|
||||
@ -47,9 +48,9 @@ class SetUserSSHPublicKeysPlugin(base.BasePlugin):
|
||||
os.makedirs(user_ssh_dir)
|
||||
|
||||
authorized_keys_path = os.path.join(user_ssh_dir, "authorized_keys")
|
||||
LOG.info("Writing SSH public keys in: %s" % authorized_keys_path)
|
||||
with open(authorized_keys_path, 'w') as f:
|
||||
public_keys = meta_data['public_keys']
|
||||
for k in public_keys:
|
||||
f.write(public_keys[k])
|
||||
for public_key in public_keys:
|
||||
f.write(public_key)
|
||||
|
||||
return (base.PLUGIN_EXECUTION_DONE, False)
|
||||
|
@ -30,7 +30,7 @@ class UserDataPlugin(base.BasePlugin):
|
||||
|
||||
def execute(self, service, shared_data):
|
||||
try:
|
||||
user_data = service.get_user_data('openstack')
|
||||
user_data = service.get_user_data()
|
||||
except metadata_services_base.NotExistingMetadataException:
|
||||
return (base.PLUGIN_EXECUTION_DONE, False)
|
||||
|
||||
@ -127,7 +127,7 @@ class UserDataPlugin(base.BasePlugin):
|
||||
|
||||
def _end_part_process_event(self, handler_func):
|
||||
LOG.debug("Calling part handler \"__end__\" event")
|
||||
handler_func(None, "__end__", None, None)
|
||||
handler_func(None, "__end__", None, None)
|
||||
|
||||
def _get_plugin_return_value(self, ret_val):
|
||||
plugin_status = base.PLUGIN_EXECUTION_DONE
|
||||
|
@ -265,7 +265,7 @@ class IVdsDisk(comtypes.IUnknown):
|
||||
comtypes.COMMETHOD([], comtypes.HRESULT, 'QueryExtents',
|
||||
(['out'], ctypes.POINTER(ctypes.POINTER(
|
||||
VDS_DISK_EXTENT)),
|
||||
'ppExtentArray'),
|
||||
'ppExtentArray'),
|
||||
(['out'], ctypes.POINTER(wintypes.LONG),
|
||||
'plNumberOfExtents')),
|
||||
]
|
||||
|
@ -14,7 +14,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from cloudbaseinit.metadata.services import base as metadata_services_base
|
||||
from cloudbaseinit.openstack.common import log as logging
|
||||
from cloudbaseinit.plugins import base
|
||||
from cloudbaseinit.plugins import constants
|
||||
@ -25,37 +24,6 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ConfigWinRMCertificateAuthPlugin(base.BasePlugin):
|
||||
def _get_client_auth_cert(self, service):
|
||||
cert_data = None
|
||||
|
||||
meta_data = service.get_meta_data('openstack')
|
||||
meta = meta_data.get('meta')
|
||||
|
||||
if meta:
|
||||
i = 0
|
||||
while True:
|
||||
# Chunking is necessary as metadata items can be
|
||||
# max. 255 chars long
|
||||
cert_chunk = meta.get('admin_cert%d' % i)
|
||||
if not cert_chunk:
|
||||
break
|
||||
if not cert_data:
|
||||
cert_data = cert_chunk
|
||||
else:
|
||||
cert_data += cert_chunk
|
||||
i += 1
|
||||
|
||||
if not cert_data:
|
||||
# Look if the user_data contains a PEM certificate
|
||||
try:
|
||||
user_data = service.get_user_data('openstack')
|
||||
if user_data.startswith(x509.PEM_HEADER):
|
||||
cert_data = user_data
|
||||
except metadata_services_base.NotExistingMetadataException:
|
||||
LOG.debug("user_data metadata not present")
|
||||
|
||||
return cert_data
|
||||
|
||||
def _get_credentials(self, shared_data):
|
||||
user_name = shared_data.get(constants.SHARED_DATA_USERNAME)
|
||||
if not user_name:
|
||||
@ -76,28 +44,34 @@ class ConfigWinRMCertificateAuthPlugin(base.BasePlugin):
|
||||
def execute(self, service, shared_data):
|
||||
user_name, password = self._get_credentials(shared_data)
|
||||
|
||||
cert_data = self._get_client_auth_cert(service)
|
||||
if not cert_data:
|
||||
certs_data = service.get_client_auth_certs()
|
||||
if not certs_data:
|
||||
LOG.info("WinRM certificate authentication cannot be configured "
|
||||
"as a certificate has not been provided in the metadata")
|
||||
return (base.PLUGIN_EXECUTION_DONE, False)
|
||||
|
||||
cert_manager = x509.CryptoAPICertManager()
|
||||
cert_thumprint, cert_upn = cert_manager.import_cert(
|
||||
cert_data, store_name=x509.STORE_NAME_ROOT)
|
||||
|
||||
if not cert_upn:
|
||||
LOG.error("WinRM certificate authentication cannot be configured "
|
||||
"as the provided certificate lacks a subject alt name "
|
||||
"containing an UPN (OID 1.3.6.1.4.1.311.20.2.3)")
|
||||
return (base.PLUGIN_EXECUTION_DONE, False)
|
||||
|
||||
winrm_config = winrmconfig.WinRMConfig()
|
||||
winrm_config.set_auth_config(certificate=True)
|
||||
|
||||
if winrm_config.get_cert_mapping(cert_thumprint, cert_upn):
|
||||
winrm_config.delete_cert_mapping(cert_thumprint, cert_upn)
|
||||
winrm_config.create_cert_mapping(cert_thumprint, cert_upn,
|
||||
user_name, password)
|
||||
for cert_data in certs_data:
|
||||
cert_manager = x509.CryptoAPICertManager()
|
||||
cert_thumprint, cert_upn = cert_manager.import_cert(
|
||||
cert_data, store_name=x509.STORE_NAME_ROOT)
|
||||
|
||||
if not cert_upn:
|
||||
LOG.error("WinRM certificate authentication cannot be "
|
||||
"configured as the provided certificate lacks a "
|
||||
"subject alt name containing an UPN (OID "
|
||||
"1.3.6.1.4.1.311.20.2.3)")
|
||||
continue
|
||||
|
||||
if winrm_config.get_cert_mapping(cert_thumprint, cert_upn):
|
||||
winrm_config.delete_cert_mapping(cert_thumprint, cert_upn)
|
||||
|
||||
LOG.info("Creating WinRM certificate mapping for user "
|
||||
"%(user_name)s with UPN %(cert_upn)s",
|
||||
{'user_name': user_name, 'cert_upn': cert_upn})
|
||||
winrm_config.create_cert_mapping(cert_thumprint, cert_upn,
|
||||
user_name, password)
|
||||
|
||||
return (base.PLUGIN_EXECUTION_DONE, False)
|
||||
|
@ -37,6 +37,7 @@ STORE_NAME_TRUSTED_PEOPLE = "TrustedPeople"
|
||||
PEM_HEADER = "-----BEGIN CERTIFICATE-----"
|
||||
PEM_FOOTER = "-----END CERTIFICATE-----"
|
||||
|
||||
|
||||
class CryptoAPICertManager(object):
|
||||
def _get_cert_thumprint(self, cert_context_p):
|
||||
thumbprint = None
|
||||
|
@ -29,3 +29,7 @@ def main():
|
||||
logging.setup('cloudbaseinit')
|
||||
|
||||
init.InitManager().configure_host()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -192,7 +192,7 @@ class CryptoAPICertManagerTests(unittest.TestCase):
|
||||
mock_CertOpenStore.return_value = store_handle
|
||||
mock_CertAddCertificateContextToStore.return_value = context_to_store
|
||||
if (certstr is None or certificate is None or enhanced_key is None
|
||||
or store_handle is None or context_to_store is None):
|
||||
or store_handle is None or context_to_store is None):
|
||||
self.assertRaises(cryptoapi.CryptoAPIException,
|
||||
self._x509.create_self_signed_cert,
|
||||
'fake subject', 10, True, x509.STORE_NAME_MY)
|
||||
@ -333,7 +333,7 @@ class CryptoAPICertManagerTests(unittest.TestCase):
|
||||
0, None, mock_create_unicode_buffer(), 2)]
|
||||
|
||||
if (not crypttstr or store_handle is None or add_enc_cert is None or
|
||||
upn_len != 2):
|
||||
upn_len != 2):
|
||||
self.assertRaises(cryptoapi.CryptoAPIException,
|
||||
self._x509.import_cert, fake_cert_data, True,
|
||||
x509.STORE_NAME_MY)
|
||||
|
Loading…
Reference in New Issue
Block a user