From 74242c277678a5d419b77666501de0be73a6c731 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Thu, 9 Oct 2014 16:45:43 +0200 Subject: [PATCH] Implement authentication via Keystone --- README.rst | 7 ++++--- example.conf | 3 +++ ironic_discoverd/__main__.py | 12 ++++++++++++ ironic_discoverd/discoverd.py | 9 ++++++++- requirements.txt | 1 + setup.py | 3 ++- 6 files changed, 30 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 4d4e2a094..33bdd2852 100644 --- a/README.rst +++ b/README.rst @@ -40,11 +40,12 @@ HTTP API consist of 2 endpoints: * ``/v1/discover`` initiate hardware discovery. Request body: JSON - list of UUID's of nodes to discover. All power management configuration for these nodes - needs to be done prior to calling the endpoint. + needs to be done prior to calling the endpoint. Requires X-Auth-Token header + with Keystone token for authentication. .. note:: - Right now this endpoint is not authenticated. It will switch to - OpenStack authentication in the near future. + Before version 0.2.0 this endpoint was not authenticated. + Now it is, but check for admin role is not implemented yet. * ``/v1/continue`` internal endpoint for the discovery ramdisk to post back discovered data. Should not be used for anything other than implementing diff --git a/example.conf b/example.conf index fbd8e8cde..ae9f4e658 100644 --- a/example.conf +++ b/example.conf @@ -17,6 +17,9 @@ ;listen_address = 0.0.0.0 ; Port to listen on. ;listen_port = 5050 +; Whether to authenticate with Keystone on discovery initialization endpoint. +; Note that discovery postback endpoint is never authenticated. +;authenticate = true ; Debug mode enabled/disabled. ;debug = false diff --git a/ironic_discoverd/__main__.py b/ironic_discoverd/__main__.py index d87f7961f..9c5d4d2d2 100644 --- a/ironic_discoverd/__main__.py +++ b/ironic_discoverd/__main__.py @@ -4,6 +4,7 @@ import sys import eventlet from flask import Flask, request +from keystoneclient import exceptions from ironic_discoverd import discoverd @@ -24,6 +25,17 @@ def post_continue(): @app.route('/v1/discover', methods=['POST']) def post_discover(): + if discoverd.CONF.getboolean('discoverd', 'authenticate'): + if not request.headers.get('X-Auth-Token'): + LOG.debug("No X-Auth-Token header, rejecting") + return 'Authentication required', 401 + try: + discoverd.get_keystone(token=request.headers['X-Auth-Token']) + except exceptions.Unauthorized: + LOG.debug("Keystone denied access, rejecting") + return 'Access denied', 403 + # TODO(dtanstur): check for admin role + data = request.get_json(force=True) LOG.debug("Got JSON %s, going into processing thread", data) eventlet.greenthread.spawn_n(discoverd.discover, data) diff --git a/ironic_discoverd/discoverd.py b/ironic_discoverd/discoverd.py index 605a87a48..7df0576da 100644 --- a/ironic_discoverd/discoverd.py +++ b/ironic_discoverd/discoverd.py @@ -4,6 +4,7 @@ import re from subprocess import call, check_call from ironicclient import client, exceptions +from keystoneclient.v2_0 import client as keystone LOG = logging.getLogger("discoverd") @@ -12,7 +13,8 @@ CONF = ConfigParser.ConfigParser( defaults={'debug': 'false', 'listen_address': '0.0.0.0', 'listen_port': '5050', - 'dnsmasq_interface': 'br-ctlplane'}) + 'dnsmasq_interface': 'br-ctlplane', + 'authenticate': 'true'}) OS_ARGS = ('os_password', 'os_username', 'os_auth_url', 'os_tenant_name') @@ -21,6 +23,11 @@ def get_client(): return client.get_client(1, **args) +def get_keystone(token): + return keystone.Client(token=token, auth_url=CONF.get('discoverd', + 'os_auth_url')) + + def is_valid_mac(address): m = "[0-9a-f]{2}(:[0-9a-f]{2}){5}$" return (isinstance(address, (str, unicode)) diff --git a/requirements.txt b/requirements.txt index 1ffa631cc..ac6bf3d72 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ Flask>=0.10,<1.0 python-ironicclient>=0.2.1 eventlet>=0.15.1 +python-keystoneclient>=0.10.0 diff --git a/setup.py b/setup.py index cd2c16d5c..23db54b84 100644 --- a/setup.py +++ b/setup.py @@ -9,6 +9,7 @@ setup( author_email = "dtansur@redhat.com", url = "https://github.com/Divius/ironic-discoverd/", packages=['ironic_discoverd'], - install_requires = ['Flask', 'python-ironicclient', 'eventlet'], + install_requires = ['Flask', 'python-ironicclient', 'eventlet', + 'python-keystoneclient'], scripts = ['bin/ironic-discoverd'], )