summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDevananda van der Veen <devananda.vdv@gmail.com>2016-06-10 17:24:53 -0700
committerDevananda van der Veen <devananda.vdv@gmail.com>2016-06-21 08:30:41 -0700
commitf5a3ff1dfcde068769f9a2a477ba6a9edaf69c77 (patch)
tree898e7f2ba8ca7ae9f995e0106c71b91c44045164
parent9369c5dcb469fea1b132e60a11bdaa1971a3edc0 (diff)
downloadironic-f5a3ff1dfcde068769f9a2a477ba6a9edaf69c77.tar.gz
Mask password on agent lookup according to policy4.2.5
We currently mask passwords in driver_info for all API responses, except for agent lookups. This is because driver_vendor_passthru just expects a dict to return as JSON in the response, and doesn't modify it at all. Change lookup to follow the defined policy when returning the node object in the response, the same way we do for other API responses. Co-authored-by: Jim Rollenhagen <jim@jimrollenhagen.com> Change-Id: Ib19d2f86d3527333e905fdbf1f09fc3dc8b8c5a7 Closes-Bug: #1572796 (cherry picked from commit 426a306fb580762e97ada04e1253dedd9b64d410)
-rw-r--r--ironic/drivers/modules/agent_base_vendor.py10
-rw-r--r--ironic/tests/db/utils.py1
-rw-r--r--ironic/tests/drivers/test_agent_base_vendor.py24
-rw-r--r--releasenotes/notes/fix-cve-2016-4985-b62abae577025365.yaml11
4 files changed, 39 insertions, 7 deletions
diff --git a/ironic/drivers/modules/agent_base_vendor.py b/ironic/drivers/modules/agent_base_vendor.py
index 12b3e7060..24973761e 100644
--- a/ironic/drivers/modules/agent_base_vendor.py
+++ b/ironic/drivers/modules/agent_base_vendor.py
@@ -16,12 +16,13 @@
# License for the specific language governing permissions and limitations
# under the License.
-
+import ast
import time
from oslo_config import cfg
from oslo_log import log
from oslo_utils import excutils
+from oslo_utils import strutils
import retrying
from ironic.common import boot_devices
@@ -436,9 +437,14 @@ class BaseAgentVendor(base.VendorInterface):
LOG.info(_LI('Initial lookup for node %s succeeded, agent is running '
'and waiting for commands'), node.uuid)
+ ndict = node.as_dict()
+ if not context.show_password:
+ ndict['driver_info'] = ast.literal_eval(
+ strutils.mask_password(ndict['driver_info'], "******"))
+
return {
'heartbeat_timeout': CONF.agent.heartbeat_timeout,
- 'node': node.as_dict()
+ 'node': ndict,
}
def _get_completed_cleaning_command(self, task):
diff --git a/ironic/tests/db/utils.py b/ironic/tests/db/utils.py
index 7ea0c5811..4bfdb5f51 100644
--- a/ironic/tests/db/utils.py
+++ b/ironic/tests/db/utils.py
@@ -152,6 +152,7 @@ def get_test_agent_driver_info():
return {
'deploy_kernel': 'glance://deploy_kernel_uuid',
'deploy_ramdisk': 'glance://deploy_ramdisk_uuid',
+ 'ipmi_password': 'foo',
}
diff --git a/ironic/tests/drivers/test_agent_base_vendor.py b/ironic/tests/drivers/test_agent_base_vendor.py
index d89dc1bfc..fdd7f341b 100644
--- a/ironic/tests/drivers/test_agent_base_vendor.py
+++ b/ironic/tests/drivers/test_agent_base_vendor.py
@@ -15,6 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import copy
import time
import types
@@ -91,7 +92,8 @@ class TestBaseAgentVendor(db_base.DbTestCase):
@mock.patch('ironic.drivers.modules.agent_base_vendor.BaseAgentVendor'
'._find_node_by_macs', autospec=True)
- def test_lookup_v2(self, find_mock):
+ def _test_lookup_v2(self, find_mock, show_password=True):
+ self.context.show_password = show_password
kwargs = {
'version': '2',
'inventory': {
@@ -108,10 +110,20 @@ class TestBaseAgentVendor(db_base.DbTestCase):
]
}
}
+ # NOTE(jroll) apparently as_dict() returns a dict full of references
+ expected = copy.deepcopy(self.node.as_dict())
+ if not show_password:
+ expected['driver_info']['ipmi_password'] = '******'
find_mock.return_value = self.node
with task_manager.acquire(self.context, self.node.uuid) as task:
node = self.passthru.lookup(task.context, **kwargs)
- self.assertEqual(self.node.as_dict(), node['node'])
+ self.assertEqual(expected, node['node'])
+
+ def test_lookup_v2_show_password(self):
+ self._test_lookup_v2(show_password=True)
+
+ def test_lookup_v2_hide_password(self):
+ self._test_lookup_v2(show_password=False)
def test_lookup_v2_missing_inventory(self):
with task_manager.acquire(self.context, self.node.uuid) as task:
@@ -136,9 +148,11 @@ class TestBaseAgentVendor(db_base.DbTestCase):
@mock.patch.object(objects.Node, 'get_by_uuid')
def test_lookup_v2_with_node_uuid(self, mock_get_node):
+ self.context.show_password = True
+ expected = copy.deepcopy(self.node.as_dict())
kwargs = {
'version': '2',
- 'node_uuid': 'fake uuid',
+ 'node_uuid': 'fake-uuid',
'inventory': {
'interfaces': [
{
@@ -156,8 +170,8 @@ class TestBaseAgentVendor(db_base.DbTestCase):
mock_get_node.return_value = self.node
with task_manager.acquire(self.context, self.node.uuid) as task:
node = self.passthru.lookup(task.context, **kwargs)
- self.assertEqual(self.node.as_dict(), node['node'])
- mock_get_node.assert_called_once_with(mock.ANY, 'fake uuid')
+ self.assertEqual(expected, node['node'])
+ mock_get_node.assert_called_once_with(mock.ANY, 'fake-uuid')
@mock.patch.object(objects.port.Port, 'get_by_address',
spec_set=types.FunctionType)
diff --git a/releasenotes/notes/fix-cve-2016-4985-b62abae577025365.yaml b/releasenotes/notes/fix-cve-2016-4985-b62abae577025365.yaml
new file mode 100644
index 000000000..6127f61e1
--- /dev/null
+++ b/releasenotes/notes/fix-cve-2016-4985-b62abae577025365.yaml
@@ -0,0 +1,11 @@
+---
+security:
+ - A critical security vulnerability (CVE-2016-4985) was fixed in this
+ release. Previously, a client with network access to the ironic-api service
+ was able to bypass Keystone authentication and retrieve all information
+ about any Node registered with Ironic, if they knew (or were able to guess)
+ the MAC address of a network card belonging to that Node, by sending a
+ crafted POST request to the /v1/drivers/$DRIVER_NAME/vendor_passthru
+ resource. Ironic's policy.json configuration is now respected when
+ responding to this request such that, if passwords should be masked for
+ other requests, they are also masked for this request.