mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-27 09:20:32 +01:00
cloud-setup/ec2: start with requesting a IMDSv2 token
The present version of the EC2 metadata API (IMDSv2) requires a header with a token to be present in all requests. The token is essentially a cookie that's not actually a cookie that's obtained with a PUT call that doesn't put anything. Apparently it's too easy to trick someone into calling a GET method. EC2 now supports IMDSv2 everywhere with IMDSv1 being optional, so let's just use IMDSv2 unconditionally. Also, the presence of a token API can be used to detect the AWS EC2 cloud. Conflicts: - variable alignments - missing494819bbbf("cloud-setup: move common code for get_config() to base class and improve cancellation"). From it we only needed the `get_config_data->self` part, but used g_task_get_source_object instead. - missing5fb2f7e717("cloud-setup/trivial: rename "response_data" variable") https://bugzilla.redhat.com/show_bug.cgi?id=2151986 (cherry picked from commit8b7e12c2d6) (cherry picked from commit429f36cd81) (cherry picked from commite3ac982b32) (cherry picked from commitc5a3e739b1) (cherry picked from commitee157ad48b) (cherry picked from commitae3ec36462) (cherry picked from commit865fe0732e) (cherry picked from commitd75e307ebc)
This commit is contained in:
parent
0962b6e23d
commit
ef3d4758d6
1 changed files with 56 additions and 22 deletions
|
|
@ -16,6 +16,11 @@
|
|||
#define NM_EC2_METADATA_URL_BASE /* $NM_EC2_BASE/$NM_EC2_API_VERSION */ \
|
||||
"/meta-data/network/interfaces/macs/"
|
||||
|
||||
/* Token TTL of 180 seconds is chosen abitrarily, in hope that it is
|
||||
* surely more than enough to read all relevant metadata. */
|
||||
#define NM_EC2_TOKEN_TTL_HEADER "X-aws-ec2-metadata-token-ttl-seconds: 180"
|
||||
#define NM_EC2_TOKEN_HEADER "X-aws-ec2-metadata-token: "
|
||||
|
||||
static const char *
|
||||
_ec2_base(void)
|
||||
{
|
||||
|
|
@ -58,8 +63,15 @@ again:
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
enum {
|
||||
NM_EC2_HTTP_HEADER_TOKEN,
|
||||
NM_EC2_HTTP_HEADER_SENTINEL,
|
||||
_NM_EC2_HTTP_HEADER_NUM,
|
||||
};
|
||||
|
||||
struct _NMCSProviderEC2 {
|
||||
NMCSProvider parent;
|
||||
char *token;
|
||||
};
|
||||
|
||||
struct _NMCSProviderEC2Class {
|
||||
|
|
@ -70,23 +82,18 @@ G_DEFINE_TYPE(NMCSProviderEC2, nmcs_provider_ec2, NMCS_TYPE_PROVIDER);
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
_detect_get_meta_data_check_cb(long response_code,
|
||||
GBytes * response_data,
|
||||
gpointer check_user_data,
|
||||
GError **error)
|
||||
{
|
||||
return response_code == 200 && nmcs_utils_parse_get_full_line(response_data, "ami-id");
|
||||
}
|
||||
|
||||
static void
|
||||
_detect_get_meta_data_done_cb(GObject *source, GAsyncResult *result, gpointer user_data)
|
||||
_detect_get_token_done_cb(GObject *source, GAsyncResult *result, gpointer user_data)
|
||||
{
|
||||
gs_unref_object GTask *task = user_data;
|
||||
gs_free_error GError *get_error = NULL;
|
||||
gs_free_error GError *error = NULL;
|
||||
gs_unref_object GTask *task = user_data;
|
||||
NMCSProviderEC2 *self = NMCS_PROVIDER_EC2(g_task_get_source_object(task));
|
||||
gs_unref_bytes GBytes *response = NULL;
|
||||
gs_free_error GError *get_error = NULL;
|
||||
gs_free_error GError *error = NULL;
|
||||
|
||||
nm_http_client_poll_req_finish(NM_HTTP_CLIENT(source), result, NULL, NULL, &get_error);
|
||||
nm_clear_g_free(&self->token);
|
||||
|
||||
nm_http_client_poll_req_finish(NM_HTTP_CLIENT(source), result, NULL, &response, &get_error);
|
||||
|
||||
if (nm_utils_error_is_cancelled(get_error)) {
|
||||
g_task_return_error(task, g_steal_pointer(&get_error));
|
||||
|
|
@ -102,6 +109,12 @@ _detect_get_meta_data_done_cb(GObject *source, GAsyncResult *result, gpointer us
|
|||
return;
|
||||
}
|
||||
|
||||
/* We use the token as-is. Special characters can cause confusion (e.g.
|
||||
* response splitting), but we're not crossing a security boundary.
|
||||
* None of the examples in AWS documentation does any sort of
|
||||
* sanitization either. */
|
||||
self->token = g_strconcat(NM_EC2_TOKEN_HEADER, g_bytes_get_data(response, NULL), NULL);
|
||||
|
||||
g_task_return_boolean(task, TRUE);
|
||||
}
|
||||
|
||||
|
|
@ -114,17 +127,17 @@ detect(NMCSProvider *provider, GTask *task)
|
|||
http_client = nmcs_provider_get_http_client(provider);
|
||||
|
||||
nm_http_client_poll_req(http_client,
|
||||
(uri = _ec2_uri_concat("latest/meta-data/")),
|
||||
(uri = _ec2_uri_concat("latest/api/token")),
|
||||
HTTP_TIMEOUT_MS,
|
||||
256 * 1024,
|
||||
7000,
|
||||
1000,
|
||||
NULL,
|
||||
NULL,
|
||||
NM_MAKE_STRV(NM_EC2_TOKEN_TTL_HEADER),
|
||||
"PUT",
|
||||
g_task_get_cancellable(task),
|
||||
_detect_get_meta_data_check_cb,
|
||||
NULL,
|
||||
_detect_get_meta_data_done_cb,
|
||||
NULL,
|
||||
_detect_get_token_done_cb,
|
||||
task);
|
||||
}
|
||||
|
||||
|
|
@ -283,6 +296,8 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us
|
|||
GetConfigMetadataData * metadata_data = user_data;
|
||||
GetConfigIfaceData * iface_data;
|
||||
NMCSProviderGetConfigTaskData *get_config_data = metadata_data->get_config_data;
|
||||
NMCSProviderEC2 * self =
|
||||
NMCS_PROVIDER_EC2(g_task_get_source_object(get_config_data->task));
|
||||
gs_unref_hashtable GHashTable *response_parsed =
|
||||
g_steal_pointer(&metadata_data->response_parsed);
|
||||
gs_free_error GError *error = NULL;
|
||||
|
|
@ -377,7 +392,7 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us
|
|||
512 * 1024,
|
||||
10000,
|
||||
1000,
|
||||
NULL,
|
||||
NM_MAKE_STRV(self->token),
|
||||
NULL,
|
||||
iface_data->cancellable,
|
||||
NULL,
|
||||
|
|
@ -395,7 +410,7 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us
|
|||
512 * 1024,
|
||||
10000,
|
||||
1000,
|
||||
NULL,
|
||||
NM_MAKE_STRV(self->token),
|
||||
NULL,
|
||||
iface_data->cancellable,
|
||||
NULL,
|
||||
|
|
@ -477,9 +492,15 @@ _get_config_metadata_ready_check(long response_code,
|
|||
static void
|
||||
get_config(NMCSProvider *provider, NMCSProviderGetConfigTaskData *get_config_data)
|
||||
{
|
||||
NMCSProviderEC2 * self = NMCS_PROVIDER_EC2(provider);
|
||||
gs_free char * uri = NULL;
|
||||
GetConfigMetadataData *metadata_data;
|
||||
|
||||
/* This can be called only if detect() succeeded, which implies
|
||||
* there must be a token.
|
||||
*/
|
||||
nm_assert(self->token);
|
||||
|
||||
metadata_data = g_slice_new(GetConfigMetadataData);
|
||||
*metadata_data = (GetConfigMetadataData){
|
||||
.get_config_data = get_config_data,
|
||||
|
|
@ -495,7 +516,7 @@ get_config(NMCSProvider *provider, NMCSProviderGetConfigTaskData *get_config_dat
|
|||
256 * 1024,
|
||||
15000,
|
||||
1000,
|
||||
NULL,
|
||||
NM_MAKE_STRV(self->token),
|
||||
NULL,
|
||||
g_task_get_cancellable(get_config_data->task),
|
||||
_get_config_metadata_ready_check,
|
||||
|
|
@ -510,11 +531,24 @@ static void
|
|||
nmcs_provider_ec2_init(NMCSProviderEC2 *self)
|
||||
{}
|
||||
|
||||
static void
|
||||
dispose(GObject *object)
|
||||
{
|
||||
NMCSProviderEC2 *self = NMCS_PROVIDER_EC2(object);
|
||||
|
||||
nm_clear_g_free(&self->token);
|
||||
|
||||
G_OBJECT_CLASS(nmcs_provider_ec2_parent_class)->dispose(object);
|
||||
}
|
||||
|
||||
static void
|
||||
nmcs_provider_ec2_class_init(NMCSProviderEC2Class *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
||||
NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS(klass);
|
||||
|
||||
object_class->dispose = dispose;
|
||||
|
||||
provider_class->_name = "ec2";
|
||||
provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_EC2");
|
||||
provider_class->detect = detect;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue