allow middleware configuration from app config

From markmc's proposal:
http://lists.openstack.org/pipermail/openstack-dev/2012-July/000277.html

For backward compatiblity, configuration from paste-deploy INI is used
if it exists. If not, section [keystone_authtoken] in  global
configuration is expected, with the same parameter names.

Requires application using global cfg.CONF object (nova and glance since
folsom-2) and before there's openstack.common library, attempts to use
copy/pasted <application>.openstack.common.cfg

DocImpact

Change-Id: If6aa22280f4ce2cc698d99a130b5792dab808363
This commit is contained in:
Alan Pevec 2012-07-31 03:14:16 +02:00
parent d04e99a513
commit 174964498b
3 changed files with 125 additions and 23 deletions

View File

@ -49,7 +49,7 @@ Admin Token
For a default installation of Keystone, before you can use the REST API, you For a default installation of Keystone, before you can use the REST API, you
need to define an authorization token. This is configured in ``keystone.conf`` need to define an authorization token. This is configured in ``keystone.conf``
file under the section ``[DEFAULT]``. In the sample file provided with the file under the section ``[DEFAULT]``. In the sample file provided with the
keystone project, the line defining this token is keystone project, the line defining this token is::
[DEFAULT] [DEFAULT]
admin_token = ADMIN admin_token = ADMIN
@ -70,7 +70,7 @@ be able to use to authenticate users against keystone. The ``auth_token``
middleware supports using either the shared secret described above as middleware supports using either the shared secret described above as
`admin_token` or users for each service. `admin_token` or users for each service.
See doc:`configuration` for a walk through on how to create tenants, users, See :doc:`configuration` for a walk through on how to create tenants, users,
and roles. and roles.
Setting up services Setting up services
@ -169,7 +169,8 @@ Configuring Nova to use Keystone
When configuring Nova, it is important to create a admin service token for When configuring Nova, it is important to create a admin service token for
the service (from the Configuration step above) and include that as the key the service (from the Configuration step above) and include that as the key
'admin_token' in Nova's api-paste.ini. 'admin_token' in Nova's api-paste.ini [filter:authtoken] section or in
nova.conf [keystone_authtoken] section.
Configuring Swift to use Keystone Configuring Swift to use Keystone
--------------------------------- ---------------------------------
@ -344,3 +345,22 @@ Here is an example paste config filter that makes use of the 'admin_user' and
It should be noted that when using this option an admin tenant/role It should be noted that when using this option an admin tenant/role
relationship is required. The admin user is granted access to to the 'Admin' relationship is required. The admin user is granted access to to the 'Admin'
role to the 'admin' tenant. role to the 'admin' tenant.
The auth_token middleware can also be configured in nova.conf
[keystone_authtoken] section to keep paste config clean of site-specific
parameters::
[filter:authtoken]
paste.filter_factory = keystone.middleware.auth_token:filter_factory
and in nova.conf::
[DEFAULT]
...
auth_strategy=keystone
[keystone_authtoken]
auth_port = 35357
auth_host = 127.0.0.1
admin_user = admin
admin_password = keystone123

View File

@ -137,6 +137,32 @@ a WSGI component. Example for the auth_token middleware::
certfile = <path to middleware public cert> certfile = <path to middleware public cert>
keyfile = <path to middleware private cert> keyfile = <path to middleware private cert>
For services which have separate paste-deploy ini file, auth_token middleware
can be alternatively configured in [keystone_authtoken] section in the main
config file. For example in Nova, all middleware parameters can be removed
from api-paste.ini::
[filter:authtoken]
paste.filter_factory = keystone.middleware.auth_token:filter_factory
and set in nova.conf::
[DEFAULT]
...
auth_strategy=keystone
[keystone_authtoken]
auth_host = 127.0.0.1
auth_port = 35357
auth_protocol = http
auth_uri = http://127.0.0.1:5000/
admin_user = admin
admin_password = SuperSekretPassword
admin_tenant_name = service
Note that middleware parameters in paste config take priority, they must be
removed to use values in [keystone_authtoken] section.
Configuration Options Configuration Options
--------------------- ---------------------

View File

@ -109,8 +109,55 @@ from keystone.common import cms
from keystone.common import utils from keystone.common import utils
from keystone.openstack.common import timeutils from keystone.openstack.common import timeutils
CONF = None
try:
from openstack.common import cfg
CONF = cfg.CONF
except ImportError:
# cfg is not a library yet, try application copies
for app in 'nova', 'glance', 'quantum', 'cinder':
try:
cfg = __import__('%s.openstack.common.cfg' % app,
fromlist=['%s.openstack.common' % app])
# test which application middleware is running in
if 'config_file' in cfg.CONF:
CONF = cfg.CONF
break
except ImportError:
pass
if not CONF:
from keystone.openstack.common import cfg
CONF = cfg.CONF
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
# alternative middleware configuration in the main application's
# configuration file e.g. in nova.conf
# [keystone_authtoken]
# auth_host = 127.0.0.1
# auth_port = 35357
# auth_protocol = http
# admin_tenant_name = admin
# admin_user = admin
# admin_password = badpassword
opts = [
cfg.StrOpt('auth_admin_prefix', default=''),
cfg.StrOpt('auth_host', default='127.0.0.1'),
cfg.IntOpt('auth_port', default=35357),
cfg.StrOpt('auth_protocol', default='https'),
cfg.StrOpt('auth_uri', default=None),
cfg.BoolOpt('delay_auth_decision', default=False),
cfg.StrOpt('admin_token'),
cfg.StrOpt('admin_user'),
cfg.StrOpt('admin_password'),
cfg.StrOpt('admin_tenant_name', default='admin'),
cfg.StrOpt('certfile'),
cfg.StrOpt('keyfile'),
cfg.StrOpt('signing_dir'),
cfg.ListOpt('memcache_servers'),
cfg.IntOpt('token_cache_time', default=300),
]
CONF.register_opts(opts, group='keystone_authtoken')
class InvalidUserToken(Exception): class InvalidUserToken(Exception):
pass pass
@ -134,31 +181,33 @@ class AuthProtocol(object):
# delay_auth_decision means we still allow unauthenticated requests # delay_auth_decision means we still allow unauthenticated requests
# through and we let the downstream service make the final decision # through and we let the downstream service make the final decision
self.delay_auth_decision = (conf.get('delay_auth_decision', False) self.delay_auth_decision = (self._conf_get('delay_auth_decision') in
in ('true', 't', '1', 'on', 'yes', 'y')) (True, 'true', 't', '1', 'on', 'yes', 'y'))
# where to find the auth service (we use this to validate tokens) # where to find the auth service (we use this to validate tokens)
self.auth_host = conf.get('auth_host') self.auth_host = self._conf_get('auth_host')
self.auth_port = int(conf.get('auth_port', 35357)) self.auth_port = int(self._conf_get('auth_port'))
self.auth_protocol = conf.get('auth_protocol', 'https') self.auth_protocol = self._conf_get('auth_protocol')
if self.auth_protocol == 'http': if self.auth_protocol == 'http':
self.http_client_class = httplib.HTTPConnection self.http_client_class = httplib.HTTPConnection
else: else:
self.http_client_class = httplib.HTTPSConnection self.http_client_class = httplib.HTTPSConnection
default_auth_uri = '%s://%s:%s' % (self.auth_protocol, self.auth_admin_prefix = self._conf_get('auth_admin_prefix')
self.auth_host, self.auth_uri = self._conf_get('auth_uri')
self.auth_port) if self.auth_uri is None:
self.auth_admin_prefix = conf.get('auth_admin_prefix', '') self.auth_uri = '%s://%s:%s' % (self.auth_protocol,
self.auth_uri = conf.get('auth_uri', default_auth_uri) self.auth_host,
self.auth_port)
# SSL # SSL
self.cert_file = conf.get('certfile') self.cert_file = self._conf_get('certfile')
self.key_file = conf.get('keyfile') self.key_file = self._conf_get('keyfile')
#signing #signing
default_signing_dir = '%s/keystone-signing' % os.environ['HOME'] self.signing_dirname = self._conf_get('signing_dir')
self.signing_dirname = conf.get('signing_dir', default_signing_dir) if self.signing_dirname is None:
self.signing_dirname = '%s/keystone-signing' % os.environ['HOME']
LOG.info('Using %s as cache directory for signing certificate' % LOG.info('Using %s as cache directory for signing certificate' %
self.signing_dirname) self.signing_dirname)
if (os.path.exists(self.signing_dirname) and if (os.path.exists(self.signing_dirname) and
@ -180,17 +229,17 @@ class AuthProtocol(object):
# Credentials used to verify this component with the Auth service since # Credentials used to verify this component with the Auth service since
# validating tokens is a privileged call # validating tokens is a privileged call
self.admin_token = conf.get('admin_token') self.admin_token = self._conf_get('admin_token')
self.admin_user = conf.get('admin_user') self.admin_user = self._conf_get('admin_user')
self.admin_password = conf.get('admin_password') self.admin_password = self._conf_get('admin_password')
self.admin_tenant_name = conf.get('admin_tenant_name', 'admin') self.admin_tenant_name = self._conf_get('admin_tenant_name')
# Token caching via memcache # Token caching via memcache
self._cache = None self._cache = None
self._iso8601 = None self._iso8601 = None
memcache_servers = conf.get('memcache_servers') memcache_servers = self._conf_get('memcache_servers')
# By default the token will be cached for 5 minutes # By default the token will be cached for 5 minutes
self.token_cache_time = conf.get('token_cache_time', 300) self.token_cache_time = int(self._conf_get('token_cache_time'))
self._token_revocation_list = None self._token_revocation_list = None
self._token_revocation_list_fetched_time = None self._token_revocation_list_fetched_time = None
self.token_revocation_list_cache_timeout = \ self.token_revocation_list_cache_timeout = \
@ -205,6 +254,13 @@ class AuthProtocol(object):
except ImportError as e: except ImportError as e:
LOG.warn('disabled caching due to missing libraries %s', e) LOG.warn('disabled caching due to missing libraries %s', e)
def _conf_get(self, name):
# try config from paste-deploy first
if name in self.conf:
return self.conf[name]
else:
return CONF.keystone_authtoken[name]
def __call__(self, env, start_response): def __call__(self, env, start_response):
"""Handle incoming request. """Handle incoming request.