summaryrefslogtreecommitdiffstats
path: root/fluent-bit/src/aws/flb_aws_imds.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-03-09 13:19:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-03-09 13:20:02 +0000
commit58daab21cd043e1dc37024a7f99b396788372918 (patch)
tree96771e43bb69f7c1c2b0b4f7374cb74d7866d0cb /fluent-bit/src/aws/flb_aws_imds.c
parentReleasing debian version 1.43.2-1. (diff)
downloadnetdata-58daab21cd043e1dc37024a7f99b396788372918.tar.xz
netdata-58daab21cd043e1dc37024a7f99b396788372918.zip
Merging upstream version 1.44.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'fluent-bit/src/aws/flb_aws_imds.c')
-rw-r--r--fluent-bit/src/aws/flb_aws_imds.c370
1 files changed, 370 insertions, 0 deletions
diff --git a/fluent-bit/src/aws/flb_aws_imds.c b/fluent-bit/src/aws/flb_aws_imds.c
new file mode 100644
index 000000000..0e54db161
--- /dev/null
+++ b/fluent-bit/src/aws/flb_aws_imds.c
@@ -0,0 +1,370 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2022 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/aws/flb_aws_imds.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <fluent-bit/flb_aws_util.h>
+#include <fluent-bit/flb_http_client.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_jsmn.h>
+
+#define FLB_AWS_IMDS_ROOT "/"
+#define FLB_AWS_IMDS_V2_TOKEN_PATH "/latest/api/token"
+
+/* Request headers */
+static struct flb_aws_header imds_v2_token_ttl_header = {
+ .key = "X-aws-ec2-metadata-token-ttl-seconds",
+ .key_len = 36,
+ .val = "21600", /* 6 hours (ie maximum ttl) */
+ .val_len = 5,
+};
+
+/* Request header templates */
+const static struct flb_aws_header imds_v2_token_token_header_template = {
+ .key = "X-aws-ec2-metadata-token",
+ .key_len = 24,
+ .val = "", /* Replace with token value */
+ .val_len = 0, /* Replace with token length */
+};
+
+/* Declarations */
+static int get_imds_version(struct flb_aws_imds *ctx);
+static int refresh_imds_v2_token(struct flb_aws_imds *ctx);
+
+/* Default config values */
+const struct flb_aws_imds_config flb_aws_imds_config_default = {
+ FLB_AWS_IMDS_VERSION_EVALUATE};
+
+/* Create IMDS context */
+struct flb_aws_imds *flb_aws_imds_create(const struct flb_aws_imds_config *imds_config,
+ struct flb_aws_client *ec2_imds_client)
+{
+ struct flb_aws_imds *ctx = NULL;
+
+ /* Create context */
+ ctx = flb_calloc(1, sizeof(struct flb_aws_imds));
+ if (!ctx) {
+ flb_errno();
+ return NULL;
+ }
+
+ /*
+ * Set IMDS version to whatever is specified in config
+ * Version may be evaluated later if set to FLB_AWS_IMDS_VERSION_EVALUATE
+ */
+ ctx->imds_version = imds_config->use_imds_version;
+ ctx->imds_v2_token = flb_sds_create_len("INVALID_TOKEN", 13);
+ ctx->imds_v2_token_len = 13;
+
+ /* Detect IMDS support */
+ if (!ec2_imds_client->upstream) {
+ flb_debug(
+ "[imds] unable to connect to EC2 IMDS. ec2_imds_client upstream is null");
+
+ flb_aws_imds_destroy(ctx);
+ return NULL;
+ }
+ if (0 != strncmp(ec2_imds_client->upstream->tcp_host, FLB_AWS_IMDS_HOST,
+ FLB_AWS_IMDS_HOST_LEN)) {
+ flb_debug("[imds] ec2_imds_client tcp host must be set to %s", FLB_AWS_IMDS_HOST);
+ flb_aws_imds_destroy(ctx);
+ return NULL;
+ }
+ if (ec2_imds_client->upstream->tcp_port != FLB_AWS_IMDS_PORT) {
+ flb_debug("[imds] ec2_imds_client tcp port must be set to %i", FLB_AWS_IMDS_PORT);
+ flb_aws_imds_destroy(ctx);
+ return NULL;
+ }
+
+ /* Connect client */
+ ctx->ec2_imds_client = ec2_imds_client;
+ return ctx;
+}
+
+/* Destroy IMDS context */
+void flb_aws_imds_destroy(struct flb_aws_imds *ctx)
+{
+ if (ctx->imds_v2_token) {
+ flb_sds_destroy(ctx->imds_v2_token);
+ }
+
+ flb_free(ctx);
+}
+
+/* Get IMDS metadata */
+int flb_aws_imds_request(struct flb_aws_imds *ctx, const char *metadata_path,
+ flb_sds_t *metadata, size_t *metadata_len)
+{
+ return flb_aws_imds_request_by_key(ctx, metadata_path, metadata, metadata_len, NULL);
+}
+
+/* Get IMDS metadata by key */
+int flb_aws_imds_request_by_key(struct flb_aws_imds *ctx, const char *metadata_path,
+ flb_sds_t *metadata, size_t *metadata_len, char *key)
+{
+ int ret;
+ flb_sds_t tmp;
+
+ struct flb_http_client *c = NULL;
+
+ struct flb_aws_client *ec2_imds_client = ctx->ec2_imds_client;
+ struct flb_aws_header token_header = imds_v2_token_token_header_template;
+
+ /* Get IMDS version */
+ int imds_version = get_imds_version(ctx);
+
+ /* Abort on version detection failure */
+ if (imds_version == FLB_AWS_IMDS_VERSION_EVALUATE) {
+ /* Exit gracefully allowing for retrys */
+ flb_warn("[imds] unable to evaluate IMDS version");
+ return -1;
+ }
+
+ if (imds_version == FLB_AWS_IMDS_VERSION_2) {
+ token_header.val = ctx->imds_v2_token;
+ token_header.val_len = ctx->imds_v2_token_len;
+ flb_debug("[imds] using IMDSv2");
+ }
+ else {
+ flb_debug("[imds] using IMDSv1");
+ }
+
+ c = ec2_imds_client->client_vtable->request(
+ ec2_imds_client, FLB_HTTP_GET, metadata_path, NULL, 0, &token_header,
+ (imds_version == FLB_AWS_IMDS_VERSION_1) ? 0 : 1);
+ if (!c) {
+ /* Exit gracefully allowing for retrys */
+ flb_warn("[imds] failed to retrieve metadata");
+ return -1;
+ }
+
+ /* Detect invalid token */
+ if (imds_version == FLB_AWS_IMDS_VERSION_2 && c->resp.status == 401) {
+ /* Refresh token and retry request */
+ flb_http_client_destroy(c);
+ ret = refresh_imds_v2_token(ctx);
+ if (ret < 0) {
+ flb_debug("[imds] failed to refresh IMDSv2 token");
+ return -1;
+ }
+ token_header.val = ctx->imds_v2_token;
+ token_header.val_len = ctx->imds_v2_token_len;
+ flb_debug("[imds] refreshed IMDSv2 token");
+ c = ec2_imds_client->client_vtable->request(
+ ec2_imds_client, FLB_HTTP_GET, metadata_path, NULL, 0, &token_header, 1);
+ if (!c) {
+ /* Exit gracefully allowing for retries */
+ flb_warn("[imds] failed to retrieve metadata");
+ return -1;
+ }
+ }
+
+ if (c->resp.status != 200) {
+ ret = -1;
+ if (c->resp.status == 404) {
+ ret = -2;
+ }
+ if (c->resp.payload_size > 0) {
+ flb_debug("[imds] metadata request failure response\n%s", c->resp.payload);
+ }
+ flb_http_client_destroy(c);
+ return ret;
+ }
+
+ if (key != NULL) {
+ /* get the value of the key from payload json string */
+ tmp = flb_json_get_val(c->resp.payload, c->resp.payload_size, key);
+ if (!tmp) {
+ tmp = flb_sds_create_len("NULL", 4);
+ flb_error("[imds] %s is undefined in EC2 instance", key);
+ }
+ }
+ else {
+ tmp = flb_sds_create_len(c->resp.payload, c->resp.payload_size);
+ }
+
+ if (!tmp) {
+ flb_errno();
+ flb_http_client_destroy(c);
+ return -1;
+ }
+
+ *metadata = tmp;
+ *metadata_len = key == NULL ? c->resp.payload_size : strlen(tmp);
+
+ flb_http_client_destroy(c);
+ return 0;
+}
+
+/* Get VPC Id */
+flb_sds_t flb_aws_imds_get_vpc_id(struct flb_aws_imds *ctx)
+{
+ int ret;
+ flb_sds_t mac_id = NULL;
+ size_t mac_len = 0;
+ flb_sds_t vpc_id = NULL;
+ size_t vpc_id_len = 0;
+
+ /* get EC2 instance Mac id first before getting VPC id */
+ ret = flb_aws_imds_request(ctx, FLB_AWS_IMDS_MAC_PATH, &mac_id, &mac_len);
+
+ if (ret < 0) {
+ flb_sds_destroy(mac_id);
+ return NULL;
+ }
+
+ /*
+ * the VPC full path should be like:
+ * latest/meta-data/network/interfaces/macs/{mac_id}/vpc-id/"
+ */
+ flb_sds_t vpc_path = flb_sds_create_size(70);
+ vpc_path =
+ flb_sds_printf(&vpc_path, "%s/%s/%s/",
+ "/latest/meta-data/network/interfaces/macs", mac_id, "vpc-id");
+ ret = flb_aws_imds_request(ctx, vpc_path, &vpc_id, &vpc_id_len);
+
+ flb_sds_destroy(mac_id);
+ flb_sds_destroy(vpc_path);
+
+ return vpc_id;
+}
+
+/* Obtain the IMDS version */
+static int get_imds_version(struct flb_aws_imds *ctx)
+{
+ int ret;
+ struct flb_aws_client *client = ctx->ec2_imds_client;
+ struct flb_aws_header invalid_token_header;
+ struct flb_http_client *c = NULL;
+
+ if (ctx->imds_version != FLB_AWS_IMDS_VERSION_EVALUATE) {
+ return ctx->imds_version;
+ }
+
+ /*
+ * Evaluate version
+ * To evaluate wether IMDSv2 is available, send an invalid token
+ * in IMDS request. If response status is 'Unauthorized', then IMDSv2
+ * is available.
+ */
+ invalid_token_header = imds_v2_token_token_header_template;
+ invalid_token_header.val = "INVALID";
+ invalid_token_header.val_len = 7;
+ c = client->client_vtable->request(client, FLB_HTTP_GET, FLB_AWS_IMDS_ROOT, NULL, 0,
+ &invalid_token_header, 1);
+
+ if (!c) {
+ flb_debug("[imds] imds endpoint unavailable");
+ return FLB_AWS_IMDS_VERSION_EVALUATE;
+ }
+
+ /* Unauthorized response means that IMDS version 2 is in use */
+ if (c->resp.status == 401) {
+ ctx->imds_version = FLB_AWS_IMDS_VERSION_2;
+ ret = refresh_imds_v2_token(ctx);
+ if (ret == -1) {
+ /*
+ * Token cannot be refreshed, test IMDSv1
+ * If IMDSv1 cannot be used, response will be status 401
+ */
+ flb_http_client_destroy(c);
+ ctx->imds_version = FLB_AWS_IMDS_VERSION_EVALUATE;
+ c = client->client_vtable->request(client, FLB_HTTP_GET, FLB_AWS_IMDS_ROOT,
+ NULL, 0, NULL, 0);
+ if (!c) {
+ flb_debug("[imds] imds v1 attempt, endpoint unavailable");
+ return FLB_AWS_IMDS_VERSION_EVALUATE;
+ }
+
+ if (c->resp.status == 200) {
+ flb_info("[imds] to use IMDSv2, set --http-put-response-hop-limit to 2");
+ }
+ else {
+ /* IMDSv1 unavailable. IMDSv2 beyond network hop count */
+ flb_warn("[imds] failed to retrieve IMDSv2 token and IMDSv1 unavailable. "
+ "This is likely due to instance-metadata-options "
+ "--http-put-response-hop-limit being set to 1 and --http-tokens "
+ "set to required. "
+ "To use IMDSv2, please set --http-put-response-hop-limit to 2 as "
+ "described https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/"
+ "configuring-instance-metadata-options.html");
+ }
+ }
+ }
+
+ /*
+ * Success means that IMDS version 1 is in use
+ */
+ if (c->resp.status == 200) {
+ flb_warn("[imds] falling back on IMDSv1");
+ ctx->imds_version = FLB_AWS_IMDS_VERSION_1;
+ }
+
+ flb_http_client_destroy(c);
+ return ctx->imds_version;
+}
+
+/*
+ * Get an IMDSv2 token
+ * Token preserved in imds context
+ */
+static int refresh_imds_v2_token(struct flb_aws_imds *ctx)
+{
+ struct flb_http_client *c = NULL;
+ struct flb_aws_client *ec2_imds_client = ctx->ec2_imds_client;
+
+ c = ec2_imds_client->client_vtable->request(ec2_imds_client, FLB_HTTP_PUT,
+ FLB_AWS_IMDS_V2_TOKEN_PATH, NULL, 0,
+ &imds_v2_token_ttl_header, 1);
+
+ if (!c) {
+ return -1;
+ }
+
+ if (c->resp.status != 200) {
+ if (c->resp.payload_size > 0) {
+ flb_error("[imds] IMDSv2 token retrieval failure response\n%s",
+ c->resp.payload);
+ }
+
+ flb_http_client_destroy(c);
+ return -1;
+ }
+
+ /* Preserve token information in ctx */
+ if (c->resp.payload_size > 0) {
+ if (ctx->imds_v2_token) {
+ flb_sds_destroy(ctx->imds_v2_token);
+ }
+ ctx->imds_v2_token = flb_sds_create_len(c->resp.payload, c->resp.payload_size);
+ if (!ctx->imds_v2_token) {
+ flb_errno();
+ flb_http_client_destroy(c);
+ return -1;
+ }
+ ctx->imds_v2_token_len = c->resp.payload_size;
+
+ flb_http_client_destroy(c);
+ return 0;
+ }
+
+ flb_debug("[imds] IMDS metadata response was empty");
+ flb_http_client_destroy(c);
+ return -1;
+}