diff --git a/etc/octavia.conf b/etc/octavia.conf index 24e1c01bf5..5bc6b028b8 100644 --- a/etc/octavia.conf +++ b/etc/octavia.conf @@ -7,6 +7,9 @@ # bind_port = 9876 # api_handler = queue_producer # +# How should authentication be handled (keystone, noauth) +# auth_strategy = noauth +# # Plugin options are hot_plug_plugin (Hot-pluggable controller plugin) # # octavia_plugins = hot_plug_plugin diff --git a/octavia/api/app.py b/octavia/api/app.py index 0dfd974f71..f5b14f1866 100644 --- a/octavia/api/app.py +++ b/octavia/api/app.py @@ -12,10 +12,14 @@ # License for the specific language governing permissions and limitations # under the License. +from keystonemiddleware import auth_token +from oslo_config import cfg +from oslo_middleware import cors +from oslo_middleware import request_id import pecan from octavia.api import config as app_config -from octavia.api.v1 import hooks +from octavia.common import constants from octavia.common import service as octavia_service @@ -29,15 +33,35 @@ def setup_app(pecan_config=None, debug=False, argv=None): """Creates and returns a pecan wsgi app.""" octavia_service.prepare_service(argv) - app_hooks = [hooks.ContextHook()] - if not pecan_config: pecan_config = get_pecan_config() pecan.configuration.set_config(dict(pecan_config), overwrite=True) return pecan.make_app( pecan_config.app.root, + wrap_app=_wrap_app, debug=debug, - hooks=app_hooks, + hooks=pecan_config.app.hooks, wsme=pecan_config.wsme ) + + +def _wrap_app(app): + """Wraps wsgi app with additional middlewares.""" + app = request_id.RequestId(app) + if cfg.CONF.auth_strategy == constants.KEYSTONE: + app = auth_token.AuthProtocol(app, {}) + + # This should be the last middleware in the list (which results in + # it being the first in the middleware chain). This is to ensure + # that any errors thrown by other middleware, such as an auth + # middleware - are annotated with CORS headers, and thus accessible + # by the browser. + app = cors.CORS(app, cfg.CONF) + app.set_latent( + allow_headers=['X-Auth-Token', 'X-Openstack-Request-Id'], + allow_methods=['GET', 'PUT', 'POST', 'DELETE'], + expose_headers=['X-Auth-Token', 'X-Openstack-Request-Id'] + ) + + return app diff --git a/octavia/api/config.py b/octavia/api/config.py index 7273b7366e..10814a855b 100644 --- a/octavia/api/config.py +++ b/octavia/api/config.py @@ -12,11 +12,14 @@ # License for the specific language governing permissions and limitations # under the License. +from octavia.api.v1 import hooks + # Pecan Application Configurations # See https://pecan.readthedocs.org/en/latest/configuration.html#application-configuration # noqa app = { 'root': 'octavia.api.root_controller.RootController', 'modules': ['octavia.api'], + 'hooks': [hooks.ContextHook()], 'debug': False } diff --git a/octavia/api/v1/hooks.py b/octavia/api/v1/hooks.py index 049e1e9f7a..0ee6a53117 100644 --- a/octavia/api/v1/hooks.py +++ b/octavia/api/v1/hooks.py @@ -18,6 +18,7 @@ from octavia.common import context class ContextHook(hooks.PecanHook): + """Configures a request context and attaches it to the request.""" def on_route(self, state): context_obj = context.Context.from_environ(state.request.environ) diff --git a/octavia/common/config.py b/octavia/common/config.py index f2a74b612c..96ceee9f92 100644 --- a/octavia/common/config.py +++ b/octavia/common/config.py @@ -35,15 +35,15 @@ core_opts = [ help=_("The host IP to bind to")), cfg.PortOpt('bind_port', default=9876, help=_("The port to bind to")), + cfg.StrOpt('auth_strategy', default=constants.NOAUTH, + choices=[constants.NOAUTH, constants.KEYSTONE], + help=_("The auth strategy for API requests.")), cfg.StrOpt('api_handler', default='queue_producer', help=_("The handler that the API communicates with")), cfg.StrOpt('api_paste_config', default="api-paste.ini", help=_("The API paste config file to use")), cfg.StrOpt('api_extensions_path', default="", help=_("The path for API extensions")), - cfg.StrOpt('auth_strategy', default='keystone', - choices=('noauth', 'keystone'), - help=_("The type of authentication to use")), cfg.BoolOpt('allow_bulk', default=True, help=_("Allow the usage of the bulk API")), cfg.BoolOpt('allow_pagination', default=False, diff --git a/octavia/common/constants.py b/octavia/common/constants.py index 3171f54a31..9095059464 100644 --- a/octavia/common/constants.py +++ b/octavia/common/constants.py @@ -358,3 +358,7 @@ INIT_PROC_COMM_PATH = '/proc/1/comm' KEEPALIVED_SYSTEMD = 'octavia-keepalived.service' KEEPALIVED_SYSVINIT = 'octavia-keepalived' KEEPALIVED_UPSTART = 'octavia-keepalived.conf' + +# Authentication +KEYSTONE = 'keystone' +NOAUTH = 'noauth' diff --git a/octavia/common/keystone.py b/octavia/common/keystone.py index dd6370020a..5e1358496f 100644 --- a/octavia/common/keystone.py +++ b/octavia/common/keystone.py @@ -25,6 +25,7 @@ class KeystoneSession: def __init__(self, section=constants.SERVICE_AUTH): self._session = None + self.section = section ks_loading.register_auth_conf_options(cfg.CONF, self.section) ks_loading.register_session_conf_options(cfg.CONF, self.section) diff --git a/octavia/tests/functional/api/v1/base.py b/octavia/tests/functional/api/v1/base.py index b381dfc3dc..3788b250d2 100644 --- a/octavia/tests/functional/api/v1/base.py +++ b/octavia/tests/functional/api/v1/base.py @@ -56,6 +56,7 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase): conf.config(api_handler='simulated_handler') conf.config(group="controller_worker", network_driver='network_noop_driver') + conf.config(auth_strategy='noauth') self.lb_repo = repositories.LoadBalancerRepository() self.listener_repo = repositories.ListenerRepository() self.listener_stats_repo = repositories.ListenerStatisticsRepository() @@ -74,8 +75,8 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase): self.addCleanup(reset_pecan) def _make_app(self): - return pecan.testing.load_test_app({'app': pconfig.app, - 'wsme': pconfig.wsme}) + return pecan.testing.load_test_app( + {'app': pconfig.app, 'wsme': pconfig.wsme}) def _get_full_path(self, path): return ''.join([self.BASE_PATH, path]) diff --git a/releasenotes/notes/enable-keystone-on-api-b3ebb132ad5ab308.yaml b/releasenotes/notes/enable-keystone-on-api-b3ebb132ad5ab308.yaml new file mode 100644 index 0000000000..5299785693 --- /dev/null +++ b/releasenotes/notes/enable-keystone-on-api-b3ebb132ad5ab308.yaml @@ -0,0 +1,11 @@ +--- +prelude: > + Support for Keystone token authentication on frontend Octavia API. +features: + - After setting "auth_strategy = keystone" all incoming requests to Octavia + API will be verified using Keystone are they send by authenticated person. + By default that option is disabled because Neutron LBaaS v2 is not + supporting that functionality properly. +upgrade: + - This feature add new configuration value "auth_strategy" which by default + is set for "noauth".