Skip to content

Commit 403c41f

Browse files
out_azure_logs_ingestion: implement Managed Identity support
This change is based on the existing approach to Managed Identity authentication used for the out_azure_kusto plugin.
1 parent 286466f commit 403c41f

File tree

8 files changed

+635
-56
lines changed

8 files changed

+635
-56
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
set(src
22
azure_logs_ingestion.c
33
azure_logs_ingestion_conf.c
4+
azure_logs_ingestion_msiauth.c
45
)
56

67
FLB_PLUGIN(out_azure_logs_ingestion "${src}" "")

plugins/out_azure_logs_ingestion/azure_logs_ingestion.c

Lines changed: 54 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
#include "azure_logs_ingestion.h"
3333
#include "azure_logs_ingestion_conf.h"
34+
#include "azure_logs_ingestion_msiauth.h"
3435

3536
static int cb_azure_logs_ingestion_init(struct flb_output_instance *ins,
3637
struct flb_config *config, void *data)
@@ -170,48 +171,63 @@ flb_sds_t get_az_li_token(struct flb_az_li *ctx)
170171
flb_plg_error(ctx->ins, "error locking mutex");
171172
return NULL;
172173
}
174+
173175
/* Retrieve access token only if expired */
174176
if (flb_oauth2_token_expired(ctx->u_auth) == FLB_TRUE) {
175177
flb_plg_debug(ctx->ins, "token expired. getting new token");
176-
/* Clear any previous oauth2 payload content */
177-
flb_oauth2_payload_clear(ctx->u_auth);
178-
179-
ret = flb_oauth2_payload_append(ctx->u_auth, "grant_type", 10,
180-
"client_credentials", 18);
181-
if (ret == -1) {
182-
flb_plg_error(ctx->ins, "error appending oauth2 params");
183-
goto token_cleanup;
178+
179+
if (ctx->auth_type == FLB_AZ_LI_AUTH_MANAGED_IDENTITY_SYSTEM ||
180+
ctx->auth_type == FLB_AZ_LI_AUTH_MANAGED_IDENTITY_USER) {
181+
/* Use MSI authentication */
182+
token = flb_azure_li_msiauth_token_get(ctx->u_auth);
183+
if (!token) {
184+
flb_plg_error(ctx->ins, "error retrieving MSI access token");
185+
goto token_cleanup;
186+
}
187+
flb_plg_debug(ctx->ins, "got azure MSI token");
184188
}
189+
else {
190+
/* Use service principal authentication */
191+
/* Clear any previous oauth2 payload content */
192+
flb_oauth2_payload_clear(ctx->u_auth);
193+
194+
ret = flb_oauth2_payload_append(ctx->u_auth, "grant_type", 10,
195+
"client_credentials", 18);
196+
if (ret == -1) {
197+
flb_plg_error(ctx->ins, "error appending oauth2 params");
198+
goto token_cleanup;
199+
}
185200

186-
ret = flb_oauth2_payload_append(ctx->u_auth, "scope", 5, FLB_AZ_LI_AUTH_SCOPE,
187-
sizeof(FLB_AZ_LI_AUTH_SCOPE) - 1);
188-
if (ret == -1) {
189-
flb_plg_error(ctx->ins, "error appending oauth2 params");
190-
goto token_cleanup;
191-
}
201+
ret = flb_oauth2_payload_append(ctx->u_auth, "scope", 5, FLB_AZ_LI_AUTH_SCOPE,
202+
sizeof(FLB_AZ_LI_AUTH_SCOPE) - 1);
203+
if (ret == -1) {
204+
flb_plg_error(ctx->ins, "error appending oauth2 params");
205+
goto token_cleanup;
206+
}
192207

193-
ret = flb_oauth2_payload_append(ctx->u_auth, "client_id", 9,
194-
ctx->client_id, -1);
195-
if (ret == -1) {
196-
flb_plg_error(ctx->ins, "error appending oauth2 params");
197-
goto token_cleanup;
198-
}
208+
ret = flb_oauth2_payload_append(ctx->u_auth, "client_id", 9,
209+
ctx->client_id, -1);
210+
if (ret == -1) {
211+
flb_plg_error(ctx->ins, "error appending oauth2 params");
212+
goto token_cleanup;
213+
}
199214

200-
ret = flb_oauth2_payload_append(ctx->u_auth, "client_secret", 13,
201-
ctx->client_secret, -1);
202-
if (ret == -1) {
203-
flb_plg_error(ctx->ins, "error appending oauth2 params");
204-
goto token_cleanup;
205-
}
215+
ret = flb_oauth2_payload_append(ctx->u_auth, "client_secret", 13,
216+
ctx->client_secret, -1);
217+
if (ret == -1) {
218+
flb_plg_error(ctx->ins, "error appending oauth2 params");
219+
goto token_cleanup;
220+
}
206221

207-
token = flb_oauth2_token_get(ctx->u_auth);
222+
token = flb_oauth2_token_get(ctx->u_auth);
208223

209-
/* Copy string to prevent race conditions */
210-
if (!token) {
211-
flb_plg_error(ctx->ins, "error retrieving oauth2 access token");
212-
goto token_cleanup;
224+
/* Copy string to prevent race conditions */
225+
if (!token) {
226+
flb_plg_error(ctx->ins, "error retrieving oauth2 access token");
227+
goto token_cleanup;
228+
}
229+
flb_plg_debug(ctx->ins, "got azure token");
213230
}
214-
flb_plg_debug(ctx->ins, "got azure token");
215231
}
216232

217233
/* Reached this code-block means, got new token or token not expired */
@@ -393,6 +409,12 @@ static struct flb_config_map config_map[] = {
393409
0, FLB_TRUE, offsetof(struct flb_az_li, client_secret),
394410
"Set the client secret of the AAD application"
395411
},
412+
{
413+
FLB_CONFIG_MAP_STR, "auth_type", "service_principal",
414+
0, FLB_TRUE, offsetof(struct flb_az_li, auth_type_str),
415+
"Set the authentication type: 'service_principal' or 'managed_identity'. "
416+
"For managed_identity, use 'system' as client_id for system-assigned identity, or specify the managed identity's client ID"
417+
},
396418
{
397419
FLB_CONFIG_MAP_STR, "dce_url", (char *)NULL,
398420
0, FLB_TRUE, offsetof(struct flb_az_li, dce_url),

plugins/out_azure_logs_ingestion/azure_logs_ingestion.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@
3434
/* refresh token every 60 minutes */
3535
#define FLB_AZ_LI_TOKEN_TIMEOUT 3600
3636

37+
/* Authentication types */
38+
typedef enum {
39+
FLB_AZ_LI_AUTH_SERVICE_PRINCIPAL = 0, /* Client ID + Client Secret */
40+
FLB_AZ_LI_AUTH_MANAGED_IDENTITY_SYSTEM, /* System-assigned managed identity */
41+
FLB_AZ_LI_AUTH_MANAGED_IDENTITY_USER /* User-assigned managed identity */
42+
} flb_az_li_auth_type;
43+
3744
#include <fluent-bit/flb_info.h>
3845
#include <fluent-bit/flb_output.h>
3946
#include <fluent-bit/flb_sds.h>
@@ -48,6 +55,10 @@ struct flb_az_li {
4855
flb_sds_t dcr_id;
4956
flb_sds_t table_name;
5057

58+
/* Authentication */
59+
int auth_type;
60+
char *auth_type_str;
61+
5162
/* time_generated: on/off */
5263
int time_generated;
5364
/* time key name */

plugins/out_azure_logs_ingestion/azure_logs_ingestion_conf.c

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include "azure_logs_ingestion.h"
2727
#include "azure_logs_ingestion_conf.h"
28+
#include "azure_logs_ingestion_msiauth.h"
2829

2930
struct flb_az_li* flb_az_li_ctx_create(struct flb_output_instance *ins,
3031
struct flb_config *config)
@@ -54,21 +55,34 @@ struct flb_az_li* flb_az_li_ctx_create(struct flb_output_instance *ins,
5455
return NULL;
5556
}
5657

57-
/* config: 'client_id' */
58-
if (!ctx->client_id) {
59-
flb_plg_error(ins, "property 'client_id' is not defined");
60-
flb_az_li_ctx_destroy(ctx);
61-
return NULL;
62-
}
63-
/* config: 'tenant_id' */
64-
if (!ctx->tenant_id) {
65-
flb_plg_error(ins, "property 'tenant_id' is not defined");
66-
flb_az_li_ctx_destroy(ctx);
67-
return NULL;
68-
}
69-
/* config: 'client_secret' */
70-
if (!ctx->client_secret) {
71-
flb_plg_error(ins, "property 'client_secret' is not defined");
58+
/* Auth method validation and setup */
59+
if (strcasecmp(ctx->auth_type_str, "service_principal") == 0) {
60+
ctx->auth_type = FLB_AZ_LI_AUTH_SERVICE_PRINCIPAL;
61+
62+
/* Verify required parameters for Service Principal auth */
63+
if (!ctx->tenant_id || !ctx->client_id || !ctx->client_secret) {
64+
flb_plg_error(ins, "When using service_principal auth, tenant_id, client_id, and client_secret are required");
65+
flb_az_li_ctx_destroy(ctx);
66+
return NULL;
67+
}
68+
}
69+
else if (strcasecmp(ctx->auth_type_str, "managed_identity") == 0) {
70+
/* Check if client_id indicates system-assigned or user-assigned managed identity */
71+
if (!ctx->client_id) {
72+
flb_plg_error(ins, "When using managed_identity auth, client_id must be set to 'system' for system-assigned or the managed identity client ID");
73+
flb_az_li_ctx_destroy(ctx);
74+
return NULL;
75+
}
76+
77+
if (strcasecmp(ctx->client_id, "system") == 0) {
78+
ctx->auth_type = FLB_AZ_LI_AUTH_MANAGED_IDENTITY_SYSTEM;
79+
} else {
80+
ctx->auth_type = FLB_AZ_LI_AUTH_MANAGED_IDENTITY_USER;
81+
}
82+
}
83+
else {
84+
flb_plg_error(ins, "Invalid auth_type '%s'. Valid options are: 'service_principal', 'managed_identity', or 'workload_identity'",
85+
ctx->auth_type_str);
7286
flb_az_li_ctx_destroy(ctx);
7387
return NULL;
7488
}
@@ -91,16 +105,43 @@ struct flb_az_li* flb_az_li_ctx_create(struct flb_output_instance *ins,
91105
return NULL;
92106
}
93107

94-
/* Allocate and set auth url */
95-
ctx->auth_url = flb_sds_create_size(sizeof(FLB_AZ_LI_AUTH_URL_TMPLT) - 1 +
96-
flb_sds_len(ctx->tenant_id));
97-
if (!ctx->auth_url) {
98-
flb_errno();
99-
flb_az_li_ctx_destroy(ctx);
100-
return NULL;
108+
/* Allocate and set auth url based on authentication method */
109+
if (ctx->auth_type == FLB_AZ_LI_AUTH_MANAGED_IDENTITY_SYSTEM) {
110+
/* System-assigned managed identity */
111+
ctx->auth_url = flb_sds_create_size(sizeof(FLB_AZ_LI_MSIAUTH_URL_TEMPLATE) - 1);
112+
if (!ctx->auth_url) {
113+
flb_errno();
114+
flb_az_li_ctx_destroy(ctx);
115+
return NULL;
116+
}
117+
flb_sds_snprintf(&ctx->auth_url, flb_sds_alloc(ctx->auth_url),
118+
FLB_AZ_LI_MSIAUTH_URL_TEMPLATE, "", "");
119+
}
120+
else if (ctx->auth_type == FLB_AZ_LI_AUTH_MANAGED_IDENTITY_USER) {
121+
/* User-assigned managed identity */
122+
ctx->auth_url = flb_sds_create_size(sizeof(FLB_AZ_LI_MSIAUTH_URL_TEMPLATE) - 1 +
123+
sizeof("&client_id=") - 1 +
124+
flb_sds_len(ctx->client_id));
125+
if (!ctx->auth_url) {
126+
flb_errno();
127+
flb_az_li_ctx_destroy(ctx);
128+
return NULL;
129+
}
130+
flb_sds_snprintf(&ctx->auth_url, flb_sds_alloc(ctx->auth_url),
131+
FLB_AZ_LI_MSIAUTH_URL_TEMPLATE, "&client_id=", ctx->client_id);
132+
}
133+
else {
134+
/* Service principal authentication */
135+
ctx->auth_url = flb_sds_create_size(sizeof(FLB_AZ_LI_AUTH_URL_TMPLT) - 1 +
136+
flb_sds_len(ctx->tenant_id));
137+
if (!ctx->auth_url) {
138+
flb_errno();
139+
flb_az_li_ctx_destroy(ctx);
140+
return NULL;
141+
}
142+
flb_sds_snprintf(&ctx->auth_url, flb_sds_alloc(ctx->auth_url),
143+
FLB_AZ_LI_AUTH_URL_TMPLT, ctx->tenant_id);
101144
}
102-
flb_sds_snprintf(&ctx->auth_url, flb_sds_alloc(ctx->auth_url),
103-
FLB_AZ_LI_AUTH_URL_TMPLT, ctx->tenant_id);
104145

105146
/* Allocate and set dce full url */
106147
ctx->dce_u_url = flb_sds_create_size(sizeof(FLB_AZ_LI_DCE_URL_TMPLT) - 1 +
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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_logs_ingestion_msiauth.h"
29+
30+
char *flb_azure_li_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 li 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 li 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 li msi auth] cannot issue request, http_do=%i", ret);
72+
}
73+
else {
74+
flb_info("[azure li msi auth] HTTP Status=%i", c->resp.status);
75+
if (c->resp.payload_size > 0 && c->resp.status != 200) {
76+
flb_info("[azure li msi auth] payload:\n%s", c->resp.payload);
77+
}
78+
}
79+
80+
/* Extract token */
81+
if (c->resp.payload_size > 0 && c->resp.status == 200) {
82+
ret = flb_oauth2_parse_json_response(c->resp.payload,
83+
c->resp.payload_size, ctx);
84+
if (ret == 0) {
85+
flb_info("[azure li msi auth] access token from '%s:%s' retrieved",
86+
ctx->host, ctx->port);
87+
flb_http_client_destroy(c);
88+
flb_upstream_conn_release(u_conn);
89+
ctx->issued = time(NULL);
90+
ctx->expires = ctx->issued + ctx->expires_in;
91+
return ctx->access_token;
92+
}
93+
}
94+
95+
flb_http_client_destroy(c);
96+
flb_upstream_conn_release(u_conn);
97+
98+
return NULL;
99+
}
Lines changed: 27 additions & 0 deletions
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+
/* MSI authorization URL template */
23+
#define FLB_AZ_LI_MSIAUTH_URL_TEMPLATE \
24+
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2021-02-01%s%s&resource=https://monitor.azure.com"
25+
26+
char *flb_azure_li_msiauth_token_get(struct flb_oauth2 *ctx);
27+
int flb_azure_kusto_conf_destroy(struct flb_az_li *ctx);

tests/runtime/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ if(FLB_IN_LIB)
204204
FLB_RT_TEST(FLB_OUT_LIB "config_map_opts.c")
205205
FLB_RT_TEST(FLB_OUT_COUNTER "out_counter.c")
206206
FLB_RT_TEST(FLB_OUT_AZURE_KUSTO "out_azure_kusto.c")
207+
FLB_RT_TEST(FLB_OUT_AZURE_LOGS_INGESTION "out_azure_logs_ingestion.c")
207208
FLB_RT_TEST(FLB_OUT_DATADOG "out_datadog.c")
208209
FLB_RT_TEST(FLB_OUT_SKYWALKING "out_skywalking.c")
209210
FLB_RT_TEST(FLB_OUT_ES "out_elasticsearch.c")

0 commit comments

Comments
 (0)