Limit instances exposure
Prior to this commit, `sushy-emulator` served all libvirt domains and OpenStack instances to Redfish clients. This might impose security risks by exposing too much through Redfish. This change introduces a way to limit backend VMs exposure to Redfish clients by way of configuring allowed instance identities via `SUSHY_EMULATOR_ALLOWED_INSTANCES` configuration option. Story: 2004305 Task: 27865 Change-Id: Iff194ec63a27300b7687bfcdcb4fca579f111183
This commit is contained in:
parent
4b707299e0
commit
7152f1d262
@ -30,4 +30,4 @@ SUSHY_EMULATOR_BOOT_LOADER_MAP = {
|
|||||||
'x86_64': None,
|
'x86_64': None,
|
||||||
'aarch64': None
|
'aarch64': None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import sys
|
|||||||
from sushy_tools.emulator.drivers import libvirtdriver
|
from sushy_tools.emulator.drivers import libvirtdriver
|
||||||
from sushy_tools.emulator.drivers import novadriver
|
from sushy_tools.emulator.drivers import novadriver
|
||||||
from sushy_tools import error
|
from sushy_tools import error
|
||||||
|
from sushy_tools.error import FishyError
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
|
|
||||||
@ -69,6 +70,35 @@ def init_virt_driver(decorated_func):
|
|||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def instance_denied(**kwargs):
|
||||||
|
deny = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
deny = (kwargs['identity'] not in
|
||||||
|
app.config['SUSHY_EMULATOR_ALLOWED_INSTANCES'])
|
||||||
|
|
||||||
|
except KeyError:
|
||||||
|
deny = False
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if deny:
|
||||||
|
app.logger.warning('Instance %s access denied',
|
||||||
|
kwargs.get('identity'))
|
||||||
|
|
||||||
|
return deny
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_instance_access(decorated_func):
|
||||||
|
@functools.wraps(decorated_func)
|
||||||
|
def decorator(*args, **kwargs):
|
||||||
|
if instance_denied(**kwargs):
|
||||||
|
raise FishyError('Error finding instance')
|
||||||
|
|
||||||
|
return decorated_func(*args, **kwargs)
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def returns_json(decorated_func):
|
def returns_json(decorated_func):
|
||||||
@functools.wraps(decorated_func)
|
@functools.wraps(decorated_func)
|
||||||
def decorator(*args, **kwargs):
|
def decorator(*args, **kwargs):
|
||||||
@ -105,7 +135,8 @@ def root_resource():
|
|||||||
@init_virt_driver
|
@init_virt_driver
|
||||||
@returns_json
|
@returns_json
|
||||||
def system_collection_resource():
|
def system_collection_resource():
|
||||||
systems = driver.systems
|
systems = [system for system in driver.systems
|
||||||
|
if not instance_denied(identity=system)]
|
||||||
|
|
||||||
app.logger.debug('Serving systems list')
|
app.logger.debug('Serving systems list')
|
||||||
|
|
||||||
@ -115,6 +146,7 @@ def system_collection_resource():
|
|||||||
|
|
||||||
@app.route('/redfish/v1/Systems/<identity>', methods=['GET', 'PATCH'])
|
@app.route('/redfish/v1/Systems/<identity>', methods=['GET', 'PATCH'])
|
||||||
@init_virt_driver
|
@init_virt_driver
|
||||||
|
@ensure_instance_access
|
||||||
@returns_json
|
@returns_json
|
||||||
def system_resource(identity):
|
def system_resource(identity):
|
||||||
if flask.request.method == 'GET':
|
if flask.request.method == 'GET':
|
||||||
@ -168,6 +200,7 @@ def system_resource(identity):
|
|||||||
@app.route('/redfish/v1/Systems/<identity>/EthernetInterfaces',
|
@app.route('/redfish/v1/Systems/<identity>/EthernetInterfaces',
|
||||||
methods=['GET'])
|
methods=['GET'])
|
||||||
@init_virt_driver
|
@init_virt_driver
|
||||||
|
@ensure_instance_access
|
||||||
@returns_json
|
@returns_json
|
||||||
def ethernet_interfaces_collection(identity):
|
def ethernet_interfaces_collection(identity):
|
||||||
nics = driver.get_nics(identity)
|
nics = driver.get_nics(identity)
|
||||||
@ -179,6 +212,7 @@ def ethernet_interfaces_collection(identity):
|
|||||||
@app.route('/redfish/v1/Systems/<identity>/EthernetInterfaces/<nic_id>',
|
@app.route('/redfish/v1/Systems/<identity>/EthernetInterfaces/<nic_id>',
|
||||||
methods=['GET'])
|
methods=['GET'])
|
||||||
@init_virt_driver
|
@init_virt_driver
|
||||||
|
@ensure_instance_access
|
||||||
@returns_json
|
@returns_json
|
||||||
def ethernet_interface(identity, nic_id):
|
def ethernet_interface(identity, nic_id):
|
||||||
nics = driver.get_nics(identity)
|
nics = driver.get_nics(identity)
|
||||||
@ -193,6 +227,7 @@ def ethernet_interface(identity, nic_id):
|
|||||||
@app.route('/redfish/v1/Systems/<identity>/Actions/ComputerSystem.Reset',
|
@app.route('/redfish/v1/Systems/<identity>/Actions/ComputerSystem.Reset',
|
||||||
methods=['POST'])
|
methods=['POST'])
|
||||||
@init_virt_driver
|
@init_virt_driver
|
||||||
|
@ensure_instance_access
|
||||||
@returns_json
|
@returns_json
|
||||||
def system_reset_action(identity):
|
def system_reset_action(identity):
|
||||||
reset_type = flask.request.json.get('ResetType')
|
reset_type = flask.request.json.get('ResetType')
|
||||||
@ -207,6 +242,7 @@ def system_reset_action(identity):
|
|||||||
|
|
||||||
@app.route('/redfish/v1/Systems/<identity>/BIOS', methods=['GET'])
|
@app.route('/redfish/v1/Systems/<identity>/BIOS', methods=['GET'])
|
||||||
@init_virt_driver
|
@init_virt_driver
|
||||||
|
@ensure_instance_access
|
||||||
@returns_json
|
@returns_json
|
||||||
def bios(identity):
|
def bios(identity):
|
||||||
bios = driver.get_bios(identity)
|
bios = driver.get_bios(identity)
|
||||||
@ -222,6 +258,7 @@ def bios(identity):
|
|||||||
@app.route('/redfish/v1/Systems/<identity>/BIOS/Settings',
|
@app.route('/redfish/v1/Systems/<identity>/BIOS/Settings',
|
||||||
methods=['GET', 'PATCH'])
|
methods=['GET', 'PATCH'])
|
||||||
@init_virt_driver
|
@init_virt_driver
|
||||||
|
@ensure_instance_access
|
||||||
@returns_json
|
@returns_json
|
||||||
def bios_settings(identity):
|
def bios_settings(identity):
|
||||||
|
|
||||||
@ -247,6 +284,7 @@ def bios_settings(identity):
|
|||||||
@app.route('/redfish/v1/Systems/<identity>/BIOS/Actions/Bios.ResetBios',
|
@app.route('/redfish/v1/Systems/<identity>/BIOS/Actions/Bios.ResetBios',
|
||||||
methods=['POST'])
|
methods=['POST'])
|
||||||
@init_virt_driver
|
@init_virt_driver
|
||||||
|
@ensure_instance_access
|
||||||
@returns_json
|
@returns_json
|
||||||
def system_reset_bios(identity):
|
def system_reset_bios(identity):
|
||||||
|
|
||||||
|
@ -145,6 +145,30 @@ class EmulatorTestCase(base.BaseTestCase):
|
|||||||
set_power_state = driver_mock.set_power_state
|
set_power_state = driver_mock.set_power_state
|
||||||
set_power_state.assert_called_once_with('xxxx-yyyy-zzzz', 'Nmi')
|
set_power_state.assert_called_once_with('xxxx-yyyy-zzzz', 'Nmi')
|
||||||
|
|
||||||
|
@mock.patch.dict(main.app.config, {}, clear=True)
|
||||||
|
def test_instance_denied_allow_all(self, driver_mock):
|
||||||
|
self.assertFalse(main.instance_denied(identity='x'))
|
||||||
|
|
||||||
|
@mock.patch.dict(
|
||||||
|
main.app.config, {'SUSHY_EMULATOR_ALLOWED_INSTANCES': {}})
|
||||||
|
def test_instance_denied_disallow_all(self, driver_mock):
|
||||||
|
self.assertTrue(main.instance_denied(identity='a'))
|
||||||
|
|
||||||
|
def test_instance_denied_undefined_option(self, driver_mock):
|
||||||
|
with mock.patch.dict(main.app.config):
|
||||||
|
main.app.config.pop('SUSHY_EMULATOR_ALLOWED_INSTANCES', None)
|
||||||
|
self.assertFalse(main.instance_denied(identity='a'))
|
||||||
|
|
||||||
|
@mock.patch.dict(
|
||||||
|
main.app.config, {'SUSHY_EMULATOR_ALLOWED_INSTANCES': {'a'}})
|
||||||
|
def test_instance_denied_allow_some(self, driver_mock):
|
||||||
|
self.assertFalse(main.instance_denied(identity='a'))
|
||||||
|
|
||||||
|
@mock.patch.dict(
|
||||||
|
main.app.config, {'SUSHY_EMULATOR_ALLOWED_INSTANCES': {'a'}})
|
||||||
|
def test_instance_denied_disallow_some(self, driver_mock):
|
||||||
|
self.assertTrue(main.instance_denied(identity='b'))
|
||||||
|
|
||||||
def test_get_bios(self, driver_mock):
|
def test_get_bios(self, driver_mock):
|
||||||
driver_mock.get_bios.return_value = {"attribute 1": "value 1",
|
driver_mock.get_bios.return_value = {"attribute 1": "value 1",
|
||||||
"attribute 2": "value 2"}
|
"attribute 2": "value 2"}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user