Signal agent token is required

The agent needs to be able to understand
if the agent token is supported and is a
mandatory feature of the ironic deployment
as that can alter some of the behavior of
the agent itself.

Also adds documentation on the subject for
administrators.

Story: 2007025
Task: 37821

Change-Id: Ic635e0ed6d378d6a34a4a82e66ca647eee33bc26
This commit is contained in:
Julia Kreger 2019-12-16 14:04:31 -08:00
parent 392f2a56bf
commit deca07de3c
8 changed files with 158 additions and 4 deletions

View File

@ -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

View File

@ -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``.

View File

@ -32,6 +32,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:

View File

@ -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,
}

View File

@ -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'])

View File

@ -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

View File

@ -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

View File

@ -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.