Skip to content

Commit 39f9c4e

Browse files
committed
Managed Identity support in Azure Kusto output plugin
1 parent 9de4d3a commit 39f9c4e

File tree

7 files changed

+235
-31
lines changed

7 files changed

+235
-31
lines changed

plugins/out_azure_kusto/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ set(src
22
azure_kusto.c
33
azure_kusto_conf.c
44
azure_kusto_ingest.c
5+
azure_msiauth.c
56
)
67

78
FLB_PLUGIN(out_azure_kusto "${src}" "")

plugins/out_azure_kusto/azure_kusto.c

+27-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,21 @@
2929
#include "azure_kusto.h"
3030
#include "azure_kusto_conf.h"
3131
#include "azure_kusto_ingest.h"
32+
#include "azure_msiauth.h"
33+
34+
static int azure_kusto_get_msi_token(struct flb_azure_kusto *ctx)
35+
{
36+
char *token;
37+
38+
/* Retrieve access token */
39+
token = flb_azure_msiauth_token_get(ctx->o);
40+
if (!token) {
41+
flb_plg_error(ctx->ins, "error retrieving oauth2 access token");
42+
return -1;
43+
}
44+
45+
return 0;
46+
}
3247

3348
/* Create a new oauth2 context and get a oauth2 token */
3449
static int azure_kusto_get_oauth2_token(struct flb_azure_kusto *ctx)
@@ -84,9 +99,14 @@ flb_sds_t get_azure_kusto_token(struct flb_azure_kusto *ctx)
8499
}
85100

86101
if (flb_oauth2_token_expired(ctx->o) == FLB_TRUE) {
87-
ret = azure_kusto_get_oauth2_token(ctx);
102+
if (ctx->managed_identity_client_id != NULL) {
103+
ret = azure_kusto_get_msi_token(ctx);
104+
}
105+
else {
106+
ret = azure_kusto_get_oauth2_token(ctx);
107+
}
88108
}
89-
109+
90110
/* Copy string to prevent race conditions (get_oauth2 can free the string) */
91111
if (ret == 0) {
92112
output = flb_sds_create_size(flb_sds_len(ctx->o->token_type) +
@@ -483,6 +503,11 @@ static struct flb_config_map config_map[] = {
483503
offsetof(struct flb_azure_kusto, client_secret),
484504
"Set the client secret (Application Password) of the AAD application used for "
485505
"authentication"},
506+
{FLB_CONFIG_MAP_STR, "managed_identity_client_id", (char *)NULL, 0, FLB_TRUE,
507+
offsetof(struct flb_azure_kusto, managed_identity_client_id),
508+
"A managed identity client id to authenticate with. "
509+
"Set to 'system' for system-assigned managed identity. "
510+
"Set the MI client ID (GUID) for user-assigned managed identity."},
486511
{FLB_CONFIG_MAP_STR, "ingestion_endpoint", (char *)NULL, 0, FLB_TRUE,
487512
offsetof(struct flb_azure_kusto, ingestion_endpoint),
488513
"Set the Kusto cluster's ingestion endpoint URL (e.g. "

plugins/out_azure_kusto/azure_kusto.h

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ struct flb_azure_kusto {
6868
flb_sds_t tenant_id;
6969
flb_sds_t client_id;
7070
flb_sds_t client_secret;
71+
flb_sds_t managed_identity_client_id;
7172
flb_sds_t ingestion_endpoint;
7273
flb_sds_t database_name;
7374
flb_sds_t table_name;

plugins/out_azure_kusto/azure_kusto_conf.c

+65-26
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
#include "azure_kusto.h"
3232
#include "azure_kusto_conf.h"
33+
#include "azure_msiauth.h"
3334

3435
static struct flb_upstream_node *flb_upstream_node_create_url(struct flb_azure_kusto *ctx,
3536
struct flb_config *config,
@@ -601,23 +602,8 @@ struct flb_azure_kusto *flb_azure_kusto_conf_create(struct flb_output_instance *
601602
return NULL;
602603
}
603604

604-
/* config: 'tenant_id' */
605-
if (ctx->tenant_id == NULL) {
606-
flb_plg_error(ctx->ins, "property 'tenant_id' is not defined.");
607-
flb_azure_kusto_conf_destroy(ctx);
608-
return NULL;
609-
}
610-
611-
/* config: 'client_id' */
612-
if (ctx->client_id == NULL) {
613-
flb_plg_error(ctx->ins, "property 'client_id' is not defined");
614-
flb_azure_kusto_conf_destroy(ctx);
615-
return NULL;
616-
}
617-
618-
/* config: 'client_secret' */
619-
if (ctx->client_secret == NULL) {
620-
flb_plg_error(ctx->ins, "property 'client_secret' is not defined");
605+
if (ctx->tenant_id == NULL && ctx->client_id == NULL && ctx->client_secret == NULL && ctx->managed_identity_client_id == NULL) {
606+
flb_plg_error(ctx->ins, "Service Principal or Managed Identity is not defined");
621607
flb_azure_kusto_conf_destroy(ctx);
622608
return NULL;
623609
}
@@ -643,17 +629,70 @@ struct flb_azure_kusto *flb_azure_kusto_conf_create(struct flb_output_instance *
643629
return NULL;
644630
}
645631

646-
/* Create the auth URL */
647-
ctx->oauth_url = flb_sds_create_size(sizeof(FLB_MSAL_AUTH_URL_TEMPLATE) - 1 +
648-
flb_sds_len(ctx->tenant_id));
649-
if (!ctx->oauth_url) {
650-
flb_errno();
651-
flb_azure_kusto_conf_destroy(ctx);
652-
return NULL;
632+
if (ctx->managed_identity_client_id != NULL) {
633+
/* system assigned managed identity */
634+
if (strcasecmp(ctx->managed_identity_client_id, "system") == 0) {
635+
ctx->oauth_url = flb_sds_create_size(sizeof(FLB_AZURE_MSIAUTH_URL_TEMPLATE) - 1);
636+
637+
if (!ctx->oauth_url) {
638+
flb_errno();
639+
flb_azure_kusto_conf_destroy(ctx);
640+
return NULL;
641+
}
642+
643+
flb_sds_snprintf(&ctx->oauth_url, flb_sds_alloc(ctx->oauth_url),
644+
FLB_AZURE_MSIAUTH_URL_TEMPLATE, "", "");
645+
646+
} else {
647+
/* user assigned managed identity */
648+
ctx->oauth_url = flb_sds_create_size(sizeof(FLB_AZURE_MSIAUTH_URL_TEMPLATE) - 1 +
649+
sizeof("&client_id=") - 1 +
650+
flb_sds_len(ctx->managed_identity_client_id));
651+
652+
if (!ctx->oauth_url) {
653+
flb_errno();
654+
flb_azure_kusto_conf_destroy(ctx);
655+
return NULL;
656+
}
657+
658+
flb_sds_snprintf(&ctx->oauth_url, flb_sds_alloc(ctx->oauth_url),
659+
FLB_AZURE_MSIAUTH_URL_TEMPLATE, "&client_id=", ctx->managed_identity_client_id);
660+
}
653661
}
654-
flb_sds_snprintf(&ctx->oauth_url, flb_sds_alloc(ctx->oauth_url),
655-
FLB_MSAL_AUTH_URL_TEMPLATE, ctx->tenant_id);
662+
else {
663+
/* config: 'tenant_id' */
664+
if (ctx->tenant_id == NULL) {
665+
flb_plg_error(ctx->ins, "property 'tenant_id' is not defined.");
666+
flb_azure_kusto_conf_destroy(ctx);
667+
return NULL;
668+
}
669+
670+
/* config: 'client_id' */
671+
if (ctx->client_id == NULL) {
672+
flb_plg_error(ctx->ins, "property 'client_id' is not defined");
673+
flb_azure_kusto_conf_destroy(ctx);
674+
return NULL;
675+
}
676+
677+
/* config: 'client_secret' */
678+
if (ctx->client_secret == NULL) {
679+
flb_plg_error(ctx->ins, "property 'client_secret' is not defined");
680+
flb_azure_kusto_conf_destroy(ctx);
681+
return NULL;
682+
}
656683

684+
/* Create the auth URL */
685+
ctx->oauth_url = flb_sds_create_size(sizeof(FLB_MSAL_AUTH_URL_TEMPLATE) - 1 +
686+
flb_sds_len(ctx->tenant_id));
687+
if (!ctx->oauth_url) {
688+
flb_errno();
689+
flb_azure_kusto_conf_destroy(ctx);
690+
return NULL;
691+
}
692+
flb_sds_snprintf(&ctx->oauth_url, flb_sds_alloc(ctx->oauth_url),
693+
FLB_MSAL_AUTH_URL_TEMPLATE, ctx->tenant_id);
694+
}
695+
657696
ctx->resources = flb_calloc(1, sizeof(struct flb_azure_kusto_resources));
658697
if (!ctx->resources) {
659698
flb_errno();
+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2+
3+
/* Fluent Bit
4+
* ==========
5+
* Copyright (C) 2015-2024 The Fluent Bit Authors
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
#include <fluent-bit/flb_info.h>
21+
#include <fluent-bit/flb_mem.h>
22+
#include <fluent-bit/flb_log.h>
23+
#include <fluent-bit/flb_utils.h>
24+
#include <fluent-bit/flb_oauth2.h>
25+
#include <fluent-bit/flb_upstream.h>
26+
#include <fluent-bit/flb_http_client.h>
27+
28+
#include "azure_msiauth.h"
29+
30+
char *flb_azure_msiauth_token_get(struct flb_oauth2 *ctx)
31+
{
32+
int ret;
33+
size_t b_sent;
34+
time_t now;
35+
struct flb_connection *u_conn;
36+
struct flb_http_client *c;
37+
38+
now = time(NULL);
39+
if (ctx->access_token) {
40+
/* validate unexpired token */
41+
if (ctx->expires > now && flb_sds_len(ctx->access_token) > 0) {
42+
return ctx->access_token;
43+
}
44+
}
45+
46+
/* Get Token and store it in the context */
47+
u_conn = flb_upstream_conn_get(ctx->u);
48+
if (!u_conn) {
49+
flb_error("[azure msi auth] could not get an upstream connection to %s:%i",
50+
ctx->u->tcp_host, ctx->u->tcp_port);
51+
return NULL;
52+
}
53+
54+
/* Create HTTP client context */
55+
c = flb_http_client(u_conn, FLB_HTTP_GET, ctx->uri,
56+
NULL, 0,
57+
ctx->host, atoi(ctx->port),
58+
NULL, 0);
59+
if (!c) {
60+
flb_error("[azure msi auth] error creating HTTP client context");
61+
flb_upstream_conn_release(u_conn);
62+
return NULL;
63+
}
64+
65+
/* Append HTTP Header */
66+
flb_http_add_header(c, "Metadata", 8, "true", 4);
67+
68+
/* Issue request */
69+
ret = flb_http_do(c, &b_sent);
70+
if (ret != 0) {
71+
flb_warn("[azure msi auth] cannot issue request, http_do=%i", ret);
72+
}
73+
else {
74+
flb_info("[azure msi auth] HTTP Status=%i", c->resp.status);
75+
if (c->resp.payload_size > 0) {
76+
if (c->resp.status == 200) {
77+
flb_debug("[azure msi auth] payload:\n%s", c->resp.payload);
78+
}
79+
else {
80+
flb_info("[azure msi auth] payload:\n%s", c->resp.payload);
81+
}
82+
}
83+
}
84+
85+
/* Extract token */
86+
if (c->resp.payload_size > 0 && c->resp.status == 200) {
87+
ret = flb_oauth2_parse_json_response(c->resp.payload,
88+
c->resp.payload_size, ctx);
89+
if (ret == 0) {
90+
flb_info("[azure msi auth] access token from '%s:%s' retrieved",
91+
ctx->host, ctx->port);
92+
flb_http_client_destroy(c);
93+
flb_upstream_conn_release(u_conn);
94+
ctx->issued = time(NULL);
95+
ctx->expires = ctx->issued + ctx->expires_in;
96+
return ctx->access_token;
97+
}
98+
}
99+
100+
flb_http_client_destroy(c);
101+
flb_upstream_conn_release(u_conn);
102+
103+
return NULL;
104+
}
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2+
3+
/* Fluent Bit
4+
* ==========
5+
* Copyright (C) 2015-2024 The Fluent Bit Authors
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
#include <fluent-bit/flb_info.h>
21+
22+
/* MSAL authorization URL */
23+
#define FLB_AZURE_MSIAUTH_URL_TEMPLATE \
24+
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2021-02-01%s%s&resource=https://api.kusto.windows.net"
25+
26+
char *flb_azure_msiauth_token_get(struct flb_oauth2 *ctx);
27+

src/flb_oauth2.c

+10-3
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ struct flb_oauth2 *flb_oauth2_create(struct flb_config *config,
179179
goto error;
180180
}
181181

182-
if (!prot || strcmp(prot, "https") != 0) {
182+
if (!prot || (strcmp(prot, "https") != 0 && strcmp(prot, "http") != 0)) {
183183
flb_error("[oauth2] invalid endpoint protocol: %s", auth_url);
184184
goto error;
185185
}
@@ -227,8 +227,15 @@ struct flb_oauth2 *flb_oauth2_create(struct flb_config *config,
227227
}
228228

229229
/* Create Upstream context */
230-
ctx->u = flb_upstream_create_url(config, auth_url,
231-
FLB_IO_TLS, ctx->tls);
230+
if (strcmp(prot, "https") == 0) {
231+
ctx->u = flb_upstream_create_url(config, auth_url,
232+
FLB_IO_TLS, ctx->tls);
233+
}
234+
else if (strcmp(prot, "http") == 0) {
235+
ctx->u = flb_upstream_create_url(config, auth_url,
236+
FLB_IO_TCP, NULL);
237+
}
238+
232239
if (!ctx->u) {
233240
flb_error("[oauth2] error creating upstream context");
234241
goto error;

0 commit comments

Comments
 (0)