summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--devstack/lib/ironic5
-rw-r--r--doc/source/admin/agent-token.rst121
-rw-r--r--doc/source/admin/index.rst1
-rw-r--r--ironic/api/controllers/v1/ramdisk.py5
-rw-r--r--ironic/tests/unit/api/controllers/v1/test_ramdisk.py7
-rw-r--r--playbooks/legacy/grenade-dsvm-ironic-multinode-multitenant/run.yaml2
-rw-r--r--playbooks/legacy/grenade-dsvm-ironic/run.yaml2
-rw-r--r--releasenotes/notes/agent-token-support-0a5b5aa1585dfbb5.yaml19
8 files changed, 158 insertions, 4 deletions
diff --git a/devstack/lib/ironic b/devstack/lib/ironic
index e0ec31701..9244ed67e 100644
--- a/devstack/lib/ironic
+++ b/devstack/lib/ironic
@@ -479,6 +479,9 @@ IRONIC_DEPLOY_LOGS_LOCAL_PATH=${IRONIC_DEPLOY_LOGS_LOCAL_PATH:-$IRONIC_VM_LOG_DI
# Fast track option
IRONIC_DEPLOY_FAST_TRACK=${IRONIC_DEPLOY_FAST_TRACK:-False}
+# Agent Token requirement
+IRONIC_REQUIRE_AGENT_TOKEN=${IRONIC_REQUIRE_AGENT_TOKEN:-True}
+
# Define baremetal min_microversion in tempest config. Default value None is picked from tempest.
TEMPEST_BAREMETAL_MIN_MICROVERSION=${TEMPEST_BAREMETAL_MIN_MICROVERSION:-}
@@ -1294,6 +1297,8 @@ function configure_ironic {
# Set fast track options
iniset $IRONIC_CONF_FILE deploy fast_track $IRONIC_DEPLOY_FAST_TRACK
+ # Set requirement for agent tokens
+ iniset $IRONIC_CONF_FILE DEFAULT require_agent_token $IRONIC_REQUIRE_AGENT_TOKEN
# No need to check if RabbitMQ is enabled, this call does it in a smart way
if [[ "$IRONIC_RPC_TRANSPORT" == "oslo" ]]; then
iniset_rpc_backend ironic $IRONIC_CONF_FILE
diff --git a/doc/source/admin/agent-token.rst b/doc/source/admin/agent-token.rst
new file mode 100644
index 000000000..90528bc38
--- /dev/null
+++ b/doc/source/admin/agent-token.rst
@@ -0,0 +1,121 @@
+.. _agent_token:
+
+===========
+Agent Token
+===========
+
+Purpose
+=======
+
+The concept of agent tokens is to provide a mechanism by which the
+relationship between an operating deployment of the Bare Metal Service
+and an instance of the ``ironic-python-agent`` is verified. In a sense,
+this token can be viewed as a session identifier or authentication token.
+
+.. warning::
+ This functionality does not remove the risk of a man-in-the-middle attack
+ that could occur from connection intercept or when TLS is not used for
+ all communication.
+
+This becomes useful in the case of deploying an "edge" node where intermediate
+networks are not trustworthy.
+
+How it works
+============
+
+These tokens are provided in one of two ways to the running agent.
+
+1. A pre-generated token which is embedded into virtual media ISOs.
+2. A one-time generated token that are provided upon the first "lookup"
+ of the node.
+
+In both cases, the tokens are a randomly generated length of 128 characters.
+
+Once the token has been provided, the token cannot be retrieved or accessed.
+It remains available to the conductors, and is stored in memory of the
+``ironic-python-agent``.
+
+.. note::
+ In the case of the token being embedded with virtual media, it is read
+ from a configuration file with-in the image. Ideally this should be paired
+ with Swift temporary URLs.
+
+With the token is available in memory in the agent, the token is embedded with
+``heartbeat`` operations to the ironic API endpoint. This enables the API to
+authenticate the heartbeat request, and refuse "heartbeat" requests from the
+``ironic-python-agent``. With the ``Ussuri`` release, the confiuration option
+``[DEFAULT]require_agent_token`` can be set ``True`` to explicitly require
+token use.
+
+.. warning::
+ If the Bare Metal Service is updated, and the version of
+ ``ironic-python-agent`` should be updated to enable this feature.
+
+In addition to heartbeats being verified, commands from the
+``ironic-conductor`` service to the ``ironic-python-agent`` also include the
+token, allowing the agent to authenticate the caller.
+
+
+With Virtual Media
+------------------
+
+.. seqdiag::
+ :scale: 80
+
+ diagram {
+ API; Conductor; Baremetal; Swift; IPA;
+ activation = none;
+ span_height = 1;
+ edge_length = 250;
+ default_note_color = white;
+ default_fontsize = 14;
+
+ Conductor -> Conductor [label = "Generates a random token"];
+ Conductor -> Conductor [label = "Generates configuration for IPA ramdisk"];
+ Conductor -> Swift [label = "IPA image, with configuration is uploaded"];
+ Conductor -> Baremetal [label = "Attach IPA virtual media in Swift as virtual CD"];
+ Conductor -> Baremetal [label = "Conductor turns power on"];
+ Baremetal -> Swift [label = "Baremetal reads virtual media"];
+ Baremetal -> Baremetal [label = "Boots IPA virtual media image"];
+ Baremetal -> Baremetal [label = "IPA is started"];
+ IPA -> Baremetal [label = "IPA loads configuration and agent token into memory"];
+ IPA -> API [label = "Lookup node"];
+ API -> IPA [label = "API responds with node UUID and token value of '******'"];
+ IPA -> API [label = "Heartbeat with agent token"];
+ }
+
+With PXE/iPXE/etc.
+------------------
+
+.. seqdiag::
+ :scale: 80
+
+ diagram {
+ API; Conductor; Baremetal; iPXE; IPA;
+ activation = none;
+ span_height = 1;
+ edge_length = 250;
+ default_note_color = white;
+ default_fontsize = 14;
+
+ Conductor -> Baremetal [label = "Conductor turns power on"];
+ Baremetal -> iPXE [label = "Baremetal reads kernel/ramdisk and starts boot"];
+ Baremetal -> Baremetal [label = "Boots IPA virtual media image"];
+ Baremetal -> Baremetal [label = "IPA is started"];
+ IPA -> Baremetal [label = "IPA loads configuration"];
+ IPA -> API [label = "Lookup node"];
+ API -> Conductor [label = "API requests conductor to generates a random token"];
+ API -> IPA [label = "API responds with node UUID and token value"];
+ IPA -> API [label = "Heartbeat with agent token"];
+ }
+
+Agent Configuration
+===================
+
+An additional setting which may be leveraged with the ``ironic-python-agent``
+is a ``agent_token_required`` setting. Under normal circumstances, this
+setting can be asserted via the configuration supplied from the Bare Metal
+service deployment upon the ``lookup`` action, but can be asserted via the
+embedded configuration for the agent in the ramdisk. This setting is also
+available via kernel command line as ``ipa-agent-token-required``.
+
diff --git a/doc/source/admin/index.rst b/doc/source/admin/index.rst
index ea20a13c3..90a36f124 100644
--- a/doc/source/admin/index.rst
+++ b/doc/source/admin/index.rst
@@ -33,6 +33,7 @@ the services.
Windows Images <building-windows-images>
Troubleshooting FAQ <troubleshooting>
Power Sync with the Compute Service <power-sync>
+ Agent Token <agent-token>
.. toctree::
:hidden:
diff --git a/ironic/api/controllers/v1/ramdisk.py b/ironic/api/controllers/v1/ramdisk.py
index 625b98ccc..0f22501b4 100644
--- a/ironic/api/controllers/v1/ramdisk.py
+++ b/ironic/api/controllers/v1/ramdisk.py
@@ -53,7 +53,10 @@ def config(token):
'statsd_port': CONF.metrics_statsd.agent_statsd_port
},
'heartbeat_timeout': CONF.api.ramdisk_heartbeat_timeout,
- 'agent_token': token
+ 'agent_token': token,
+ # Not an API version based indicator, passing as configuration
+ # as the signifigants indicates support should also be present.
+ 'agent_token_required': CONF.require_agent_token,
}
diff --git a/ironic/tests/unit/api/controllers/v1/test_ramdisk.py b/ironic/tests/unit/api/controllers/v1/test_ramdisk.py
index 7ff108443..232146697 100644
--- a/ironic/tests/unit/api/controllers/v1/test_ramdisk.py
+++ b/ironic/tests/unit/api/controllers/v1/test_ramdisk.py
@@ -62,7 +62,7 @@ class TestLookup(test_api_base.BaseApiTest):
self.mock_get_node_with_token.return_value = node
def _check_config(self, data):
- expected_metrics = {
+ expected_config = {
'metrics': {
'backend': 'statsd',
'prepend_host': CONF.metrics.agent_prepend_host,
@@ -76,9 +76,10 @@ class TestLookup(test_api_base.BaseApiTest):
'statsd_port': CONF.metrics_statsd.agent_statsd_port
},
'heartbeat_timeout': CONF.api.ramdisk_heartbeat_timeout,
- 'agent_token': mock.ANY
+ 'agent_token': mock.ANY,
+ 'agent_token_required': False,
}
- self.assertEqual(expected_metrics, data['config'])
+ self.assertEqual(expected_config, data['config'])
self.assertIsNotNone(data['config']['agent_token'])
self.assertNotEqual('******', data['config']['agent_token'])
diff --git a/playbooks/legacy/grenade-dsvm-ironic-multinode-multitenant/run.yaml b/playbooks/legacy/grenade-dsvm-ironic-multinode-multitenant/run.yaml
index e3fb7806c..faea30335 100644
--- a/playbooks/legacy/grenade-dsvm-ironic-multinode-multitenant/run.yaml
+++ b/playbooks/legacy/grenade-dsvm-ironic-multinode-multitenant/run.yaml
@@ -151,6 +151,8 @@
export DEVSTACK_LOCAL_CONFIG+=$'\n'"IRONIC_VM_COUNT=7"
+ export DEVSTACK_LOCAL_CONFIG+=$'\n'"IRONIC_REQUIRE_AGENT_TOKEN=False"
+
# Ensure the ironic-vars-EARLY file exists
touch ironic-vars-early
# Pull in the EARLY variables injected by the optional builders
diff --git a/playbooks/legacy/grenade-dsvm-ironic/run.yaml b/playbooks/legacy/grenade-dsvm-ironic/run.yaml
index 5609efed3..bf38ff9cd 100644
--- a/playbooks/legacy/grenade-dsvm-ironic/run.yaml
+++ b/playbooks/legacy/grenade-dsvm-ironic/run.yaml
@@ -97,6 +97,8 @@
export DEVSTACK_LOCAL_CONFIG+=$'\n'"IRONIC_VM_COUNT=7"
+ export DEVSTACK_LOCAL_CONFIG+=$'\n'"IRONIC_REQUIRE_AGENT_TOKEN=False"
+
# Ensure the ironic-vars-EARLY file exists
touch ironic-vars-early
# Pull in the EARLY variables injected by the optional builders
diff --git a/releasenotes/notes/agent-token-support-0a5b5aa1585dfbb5.yaml b/releasenotes/notes/agent-token-support-0a5b5aa1585dfbb5.yaml
new file mode 100644
index 000000000..f133e0365
--- /dev/null
+++ b/releasenotes/notes/agent-token-support-0a5b5aa1585dfbb5.yaml
@@ -0,0 +1,19 @@
+---
+features:
+ - |
+ Adds support of ``agent token`` which serves as a mechanism to secure
+ the normally unauthenticated API endpoints in ironic which are used in
+ the mechanics of baremetal provisioning. This feature is optional, however
+ operators may require this feature by changing the
+ ``[DEFAULT]require_agent_token`` setting to ``True``.
+upgrades:
+ - |
+ In order to use the new Agent Token support, all ramdisk settings should
+ be updated for all nodes in ironic. If token use is required by ironic's
+ configuration, and the ramdisks have not been updated, then all
+ deployment, cleaning, and rescue operations will fail until the version of
+ the ironic-python-agent ramdisk has been updated.
+issues:
+ - |
+ The ``ansible`` deployment interface does not support use of an
+ ``agent token`` at this time.