Skip to content

Commit f0d51b8

Browse files
committed
feat(azure_blob): add support for managed identity
fix #10777
1 parent d142e3f commit f0d51b8

9 files changed

Lines changed: 863 additions & 4 deletions

File tree

plugins/out_azure_blob/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ set(src
77
azure_blob_appendblob.c
88
azure_blob_blockblob.c
99
azure_blob_store.c
10+
azure_blob_msiauth.c
1011
)
1112

1213
FLB_PLUGIN(out_azure_blob "${src}" "")

plugins/out_azure_blob/azure_blob.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1826,7 +1826,7 @@ static struct flb_config_map config_map[] = {
18261826
{
18271827
FLB_CONFIG_MAP_STR, "auth_type", "key",
18281828
0, FLB_TRUE, offsetof(struct flb_azure_blob, auth_type),
1829-
"Set the auth type: key or sas"
1829+
"Set the auth type: key, sas, managed_identity, or workload_identity"
18301830
},
18311831

18321832
{
@@ -1835,6 +1835,32 @@ static struct flb_config_map config_map[] = {
18351835
"Azure Blob SAS token"
18361836
},
18371837

1838+
{
1839+
FLB_CONFIG_MAP_STR, "client_id", NULL,
1840+
0, FLB_TRUE, offsetof(struct flb_azure_blob, client_id),
1841+
"Azure client ID for managed identity or workload identity auth. "
1842+
"For system-assigned managed identity, set to 'system'"
1843+
},
1844+
1845+
{
1846+
FLB_CONFIG_MAP_STR, "tenant_id", NULL,
1847+
0, FLB_TRUE, offsetof(struct flb_azure_blob, tenant_id),
1848+
"Azure tenant ID (required for workload identity auth)"
1849+
},
1850+
1851+
{
1852+
FLB_CONFIG_MAP_STR, "client_secret", NULL,
1853+
0, FLB_TRUE, offsetof(struct flb_azure_blob, client_secret),
1854+
"Azure client secret (optional, for service principal auth)"
1855+
},
1856+
1857+
{
1858+
FLB_CONFIG_MAP_STR, "workload_identity_token_file", NULL,
1859+
0, FLB_TRUE, offsetof(struct flb_azure_blob, workload_identity_token_file),
1860+
"Path to the workload identity token file. "
1861+
"Default: /var/run/secrets/azure/tokens/azure-identity-token"
1862+
},
1863+
18381864
{
18391865
FLB_CONFIG_MAP_STR, "database_file", NULL,
18401866
0, FLB_TRUE, offsetof(struct flb_azure_blob, database_file),

plugins/out_azure_blob/azure_blob.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include <fluent-bit/flb_output_plugin.h>
2424
#include <fluent-bit/flb_upstream.h>
25+
#include <fluent-bit/flb_oauth2.h>
2526
#include <fluent-bit/flb_sds.h>
2627
#include <fluent-bit/flb_sqldb.h>
2728

@@ -48,8 +49,15 @@
4849
#define AZURE_BLOB_APPENDBLOB 0
4950
#define AZURE_BLOB_BLOCKBLOB 1
5051

51-
#define AZURE_BLOB_AUTH_KEY 0
52-
#define AZURE_BLOB_AUTH_SAS 1
52+
#define AZURE_BLOB_AUTH_KEY 0
53+
#define AZURE_BLOB_AUTH_SAS 1
54+
#define AZURE_BLOB_AUTH_MI_SYSTEM 2
55+
#define AZURE_BLOB_AUTH_MI_USER 3
56+
#define AZURE_BLOB_AUTH_WI 4
57+
58+
/* MSAL authorization URL template */
59+
#define FLB_AZURE_BLOB_MSAL_AUTH_URL_TEMPLATE \
60+
"https://login.microsoftonline.com/%s/oauth2/v2.0/token"
5361

5462
struct flb_azure_blob {
5563
int auto_create_container;
@@ -65,6 +73,10 @@ struct flb_azure_blob {
6573
flb_sds_t date_key;
6674
flb_sds_t auth_type;
6775
flb_sds_t sas_token;
76+
flb_sds_t client_id;
77+
flb_sds_t tenant_id;
78+
flb_sds_t client_secret;
79+
flb_sds_t workload_identity_token_file;
6880
flb_sds_t database_file;
6981
size_t part_size;
7082
time_t upload_parts_timeout;
@@ -121,6 +133,11 @@ struct flb_azure_blob {
121133
unsigned char *decoded_sk; /* decoded shared key */
122134
size_t decoded_sk_size; /* size of decoded shared key */
123135

136+
/* OAuth2 (managed identity / workload identity) */
137+
flb_sds_t oauth_url;
138+
struct flb_oauth2 *o;
139+
pthread_mutex_t token_mutex;
140+
124141
#ifdef FLB_HAVE_SQLDB
125142
/*
126143
* SQLite by default is not built with multi-threading enabled, and

plugins/out_azure_blob/azure_blob_conf.c

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "azure_blob.h"
2525
#include "azure_blob_conf.h"
2626
#include "azure_blob_db.h"
27+
#include "azure_blob_msiauth.h"
2728

2829
#include <sys/types.h>
2930
#include <sys/stat.h>
@@ -599,6 +600,29 @@ struct flb_azure_blob *flb_azure_blob_conf_create(struct flb_output_instance *in
599600
else if (strcasecmp(tmp, "sas") == 0) {
600601
ctx->atype = AZURE_BLOB_AUTH_SAS;
601602
}
603+
else if (strcasecmp(tmp, "managed_identity") == 0) {
604+
if (!ctx->client_id) {
605+
flb_plg_error(ctx->ins,
606+
"managed_identity auth requires 'client_id' "
607+
"(set to 'system' for system-assigned)");
608+
return NULL;
609+
}
610+
if (strcasecmp(ctx->client_id, "system") == 0) {
611+
ctx->atype = AZURE_BLOB_AUTH_MI_SYSTEM;
612+
}
613+
else {
614+
ctx->atype = AZURE_BLOB_AUTH_MI_USER;
615+
}
616+
}
617+
else if (strcasecmp(tmp, "workload_identity") == 0) {
618+
ctx->atype = AZURE_BLOB_AUTH_WI;
619+
if (!ctx->tenant_id || !ctx->client_id) {
620+
flb_plg_error(ctx->ins,
621+
"workload_identity auth requires "
622+
"'tenant_id' and 'client_id'");
623+
return NULL;
624+
}
625+
}
602626
else {
603627
flb_plg_error(ctx->ins, "invalid auth_type value '%s'", tmp);
604628
return NULL;
@@ -755,6 +779,59 @@ struct flb_azure_blob *flb_azure_blob_conf_create(struct flb_output_instance *in
755779
flb_sds_printf(&ctx->shared_key_prefix, "SharedKey %s:", ctx->account_name);
756780
}
757781

782+
/* Create OAuth2 context for managed identity / workload identity */
783+
if (ctx->atype == AZURE_BLOB_AUTH_MI_SYSTEM ||
784+
ctx->atype == AZURE_BLOB_AUTH_MI_USER) {
785+
/* Construct IMDS URL */
786+
if (ctx->atype == AZURE_BLOB_AUTH_MI_SYSTEM) {
787+
ctx->oauth_url = flb_sds_create_size(
788+
sizeof(FLB_AZURE_BLOB_MSIAUTH_URL_TEMPLATE) + 1);
789+
if (!ctx->oauth_url) {
790+
return NULL;
791+
}
792+
flb_sds_snprintf(&ctx->oauth_url, flb_sds_alloc(ctx->oauth_url),
793+
FLB_AZURE_BLOB_MSIAUTH_URL_TEMPLATE, "", "");
794+
}
795+
else {
796+
ctx->oauth_url = flb_sds_create_size(
797+
sizeof(FLB_AZURE_BLOB_MSIAUTH_URL_TEMPLATE) +
798+
sizeof("&client_id=") + flb_sds_len(ctx->client_id));
799+
if (!ctx->oauth_url) {
800+
return NULL;
801+
}
802+
flb_sds_snprintf(&ctx->oauth_url, flb_sds_alloc(ctx->oauth_url),
803+
FLB_AZURE_BLOB_MSIAUTH_URL_TEMPLATE,
804+
"&client_id=", ctx->client_id);
805+
}
806+
807+
ctx->o = flb_oauth2_create(config, ctx->oauth_url, 3000);
808+
if (!ctx->o) {
809+
flb_plg_error(ctx->ins, "cannot create OAuth2 context for IMDS");
810+
return NULL;
811+
}
812+
flb_stream_disable_async_mode(&ctx->o->u->base);
813+
pthread_mutex_init(&ctx->token_mutex, NULL);
814+
}
815+
else if (ctx->atype == AZURE_BLOB_AUTH_WI) {
816+
/* Construct Azure AD token endpoint URL */
817+
ctx->oauth_url = flb_sds_create_size(
818+
sizeof(FLB_AZURE_BLOB_MSAL_AUTH_URL_TEMPLATE) +
819+
flb_sds_len(ctx->tenant_id));
820+
if (!ctx->oauth_url) {
821+
return NULL;
822+
}
823+
flb_sds_snprintf(&ctx->oauth_url, flb_sds_alloc(ctx->oauth_url),
824+
FLB_AZURE_BLOB_MSAL_AUTH_URL_TEMPLATE, ctx->tenant_id);
825+
826+
ctx->o = flb_oauth2_create(config, ctx->oauth_url, 3000);
827+
if (!ctx->o) {
828+
flb_plg_error(ctx->ins, "cannot create OAuth2 context for workload identity");
829+
return NULL;
830+
}
831+
flb_stream_disable_async_mode(&ctx->o->u->base);
832+
pthread_mutex_init(&ctx->token_mutex, NULL);
833+
}
834+
758835
/* Sanitize path: remove any ending slash */
759836
if (ctx->path) {
760837
if (ctx->path[flb_sds_len(ctx->path) - 1] == '/') {
@@ -778,7 +855,11 @@ struct flb_azure_blob *flb_azure_blob_conf_create(struct flb_output_instance *in
778855
ctx->btype == AZURE_BLOB_APPENDBLOB ? "appendblob" : "blockblob",
779856
ctx->emulator_mode ? "yes" : "no",
780857
ctx->real_endpoint ? ctx->real_endpoint : "no",
781-
ctx->atype == AZURE_BLOB_AUTH_KEY ? "key" : "sas");
858+
ctx->atype == AZURE_BLOB_AUTH_KEY ? "key" :
859+
ctx->atype == AZURE_BLOB_AUTH_SAS ? "sas" :
860+
ctx->atype == AZURE_BLOB_AUTH_MI_SYSTEM ? "managed_identity (system)" :
861+
ctx->atype == AZURE_BLOB_AUTH_MI_USER ? "managed_identity (user)" :
862+
"workload_identity");
782863
return ctx;
783864
}
784865

@@ -822,6 +903,20 @@ void flb_azure_blob_conf_destroy(struct flb_azure_blob *ctx)
822903
flb_sds_destroy(ctx->shared_key_prefix);
823904
}
824905

906+
if (ctx->oauth_url) {
907+
flb_sds_destroy(ctx->oauth_url);
908+
}
909+
910+
if (ctx->o) {
911+
flb_oauth2_destroy(ctx->o);
912+
}
913+
914+
if (ctx->atype == AZURE_BLOB_AUTH_MI_SYSTEM ||
915+
ctx->atype == AZURE_BLOB_AUTH_MI_USER ||
916+
ctx->atype == AZURE_BLOB_AUTH_WI) {
917+
pthread_mutex_destroy(&ctx->token_mutex);
918+
}
919+
825920
if (ctx->u) {
826921
flb_upstream_destroy(ctx->u);
827922
}

plugins/out_azure_blob/azure_blob_http.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@
2222
#include <fluent-bit/flb_base64.h>
2323
#include <fluent-bit/flb_crypto.h>
2424
#include <fluent-bit/flb_hmac.h>
25+
#include <fluent-bit/flb_oauth2.h>
2526
#include <fluent-bit/flb_sds.h>
2627
#include <fluent-bit/flb_kv.h>
2728

2829
#include "azure_blob.h"
2930
#include "azure_blob_uri.h"
31+
#include "azure_blob_msiauth.h"
3032

3133
static int hmac_sha256_sign(unsigned char out[32],
3234
unsigned char *key, size_t key_len,
@@ -294,10 +296,12 @@ int azb_http_client_setup(struct flb_azure_blob *ctx, struct flb_http_client *c,
294296
ssize_t content_length, int blob_type,
295297
int content_type, int content_encoding)
296298
{
299+
int ret;
297300
int len;
298301
time_t now;
299302
struct tm tm;
300303
char tmp[64];
304+
char *token;
301305
flb_sds_t can_req;
302306
flb_sds_t auth;
303307

@@ -358,6 +362,53 @@ int azb_http_client_setup(struct flb_azure_blob *ctx, struct flb_http_client *c,
358362
flb_sds_destroy(can_req);
359363
flb_sds_destroy(auth);
360364
}
365+
else if (ctx->atype == AZURE_BLOB_AUTH_MI_SYSTEM ||
366+
ctx->atype == AZURE_BLOB_AUTH_MI_USER) {
367+
pthread_mutex_lock(&ctx->token_mutex);
368+
369+
if (flb_oauth2_token_expired(ctx->o) == FLB_TRUE) {
370+
token = flb_azure_blob_msiauth_token_get(ctx->o);
371+
if (!token) {
372+
flb_plg_error(ctx->ins, "error retrieving managed identity token");
373+
pthread_mutex_unlock(&ctx->token_mutex);
374+
return -1;
375+
}
376+
}
377+
378+
auth = flb_sds_create_size(flb_sds_len(ctx->o->access_token) + 8);
379+
flb_sds_cat_safe(&auth, "Bearer ", 7);
380+
flb_sds_cat_safe(&auth, ctx->o->access_token,
381+
flb_sds_len(ctx->o->access_token));
382+
flb_http_add_header(c, "Authorization", 13, auth, flb_sds_len(auth));
383+
flb_sds_destroy(auth);
384+
385+
pthread_mutex_unlock(&ctx->token_mutex);
386+
}
387+
else if (ctx->atype == AZURE_BLOB_AUTH_WI) {
388+
pthread_mutex_lock(&ctx->token_mutex);
389+
390+
if (flb_oauth2_token_expired(ctx->o) == FLB_TRUE) {
391+
ret = flb_azure_blob_workload_identity_token_get(
392+
ctx->o,
393+
ctx->workload_identity_token_file,
394+
ctx->client_id,
395+
ctx->tenant_id);
396+
if (ret == -1) {
397+
flb_plg_error(ctx->ins, "error retrieving workload identity token");
398+
pthread_mutex_unlock(&ctx->token_mutex);
399+
return -1;
400+
}
401+
}
402+
403+
auth = flb_sds_create_size(flb_sds_len(ctx->o->access_token) + 8);
404+
flb_sds_cat_safe(&auth, "Bearer ", 7);
405+
flb_sds_cat_safe(&auth, ctx->o->access_token,
406+
flb_sds_len(ctx->o->access_token));
407+
flb_http_add_header(c, "Authorization", 13, auth, flb_sds_len(auth));
408+
flb_sds_destroy(auth);
409+
410+
pthread_mutex_unlock(&ctx->token_mutex);
411+
}
361412

362413
/* Set callback context to the HTTP client context */
363414
flb_http_set_callback_context(c, ctx->ins->callback);

0 commit comments

Comments
 (0)