Skip to content

Commit 4982395

Browse files
out_azure_kusto: add support for Azure sovereign and custom clouds
Add cloud_name configuration property to support multiple Azure cloud environments -- AzureCloud (default), AzureChinaCloud, and AzureUSGovernmentCloud. Additionally, add cloud_login_host, cloud_kusto_scope, and cloud_kusto_resource properties for private/sovereign clouds (e.g. USSEC, USNAT, BLEU) where the endpoints are not publicly known. When all three custom properties are provided, they override cloud_name. Each cloud resolves to the correct login host, OAuth scope, and IMDS resource URL for authentication and ingestion. Add NULL checks after each flb_sds_cat call in the workload identity token request body construction to prevent crash on OOM. Signed-off-by: Tanmaya Panda <tanmayapanda@microsoft.com>
1 parent 2d3b233 commit 4982395

File tree

5 files changed

+245
-24
lines changed

5 files changed

+245
-24
lines changed

plugins/out_azure_kusto/azure_kusto.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ static int azure_kusto_get_workload_identity_token(struct flb_azure_kusto *ctx)
5959
ret = flb_azure_workload_identity_token_get(ctx->o,
6060
ctx->workload_identity_token_file,
6161
ctx->client_id,
62-
ctx->tenant_id);
62+
ctx->tenant_id,
63+
ctx->kusto_scope);
6364
if (ret == -1) {
6465
flb_plg_error(ctx->ins, "error retrieving workload identity token");
6566
return -1;
@@ -82,7 +83,9 @@ static int azure_kusto_get_service_principal_token(struct flb_azure_kusto *ctx)
8283
return -1;
8384
}
8485

85-
ret = flb_oauth2_payload_append(ctx->o, "scope", 5, FLB_AZURE_KUSTO_SCOPE, 39);
86+
ret = flb_oauth2_payload_append(ctx->o, "scope", 5,
87+
ctx->kusto_scope,
88+
flb_sds_len(ctx->kusto_scope));
8689
if (ret == -1) {
8790
flb_plg_error(ctx->ins, "error appending oauth2 params");
8891
return -1;
@@ -1532,6 +1535,27 @@ static struct flb_config_map config_map[] = {
15321535
offsetof(struct flb_azure_kusto, auth_type_str),
15331536
"Set the authentication type: 'service_principal', 'managed_identity', or 'workload_identity'. "
15341537
"For managed_identity, use 'system' as client_id for system-assigned identity, or specify the managed identity's client ID"},
1538+
{FLB_CONFIG_MAP_STR, "cloud_name", "AzureCloud", 0, FLB_TRUE,
1539+
offsetof(struct flb_azure_kusto, cloud_name),
1540+
"Set the Azure cloud environment. Supported values: "
1541+
"'AzureCloud' (default), 'AzureChinaCloud', 'AzureUSGovernmentCloud'. "
1542+
"For private clouds (USSEC, USNAT, BLEU, etc.), set "
1543+
"cloud_login_host, cloud_kusto_scope, and cloud_kusto_resource instead"},
1544+
{FLB_CONFIG_MAP_STR, "cloud_login_host", (char *)NULL, 0, FLB_TRUE,
1545+
offsetof(struct flb_azure_kusto, custom_login_host),
1546+
"Custom OAuth login host for private/sovereign clouds "
1547+
"(e.g. login.microsoftonline.eaglex.ic.gov). When set, cloud_kusto_scope "
1548+
"and cloud_kusto_resource must also be provided"},
1549+
{FLB_CONFIG_MAP_STR, "cloud_kusto_scope", (char *)NULL, 0, FLB_TRUE,
1550+
offsetof(struct flb_azure_kusto, custom_kusto_scope),
1551+
"Custom Kusto OAuth scope for private/sovereign clouds "
1552+
"(e.g. https://help.kusto.core.eaglex.ic.gov/.default). When set, "
1553+
"cloud_login_host and cloud_kusto_resource must also be provided"},
1554+
{FLB_CONFIG_MAP_STR, "cloud_kusto_resource", (char *)NULL, 0, FLB_TRUE,
1555+
offsetof(struct flb_azure_kusto, custom_kusto_resource),
1556+
"Custom Kusto IMDS resource URL for private/sovereign clouds "
1557+
"(e.g. https://api.kusto.core.eaglex.ic.gov/). When set, cloud_login_host "
1558+
"and cloud_kusto_scope must also be provided"},
15351559
{FLB_CONFIG_MAP_STR, "ingestion_endpoint", (char *)NULL, 0, FLB_TRUE,
15361560
offsetof(struct flb_azure_kusto, ingestion_endpoint),
15371561
"Set the Kusto cluster's ingestion endpoint URL (e.g. "

plugins/out_azure_kusto/azure_kusto.h

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,31 @@ typedef enum {
4343
FLB_AZURE_KUSTO_AUTH_WORKLOAD_IDENTITY /* Workload Identity */
4444
} flb_azure_kusto_auth_type;
4545

46-
/* Kusto streaming inserts oauth scope */
47-
#define FLB_AZURE_KUSTO_SCOPE "https://help.kusto.windows.net/.default"
46+
/* Azure cloud environment types */
47+
typedef enum {
48+
FLB_AZURE_CLOUD_PUBLIC = 0, /* AzureCloud (default) */
49+
FLB_AZURE_CLOUD_CHINA, /* AzureChinaCloud */
50+
FLB_AZURE_CLOUD_US_GOVERNMENT /* AzureUSGovernmentCloud */
51+
} flb_azure_cloud_type;
4852

49-
/* MSAL authorization URL */
53+
/* MSAL authorization URL template: %s = login host, %s = tenant_id */
5054
#define FLB_MSAL_AUTH_URL_TEMPLATE \
51-
"https://login.microsoftonline.com/%s/oauth2/v2.0/token"
55+
"https://%s/%s/oauth2/v2.0/token"
56+
57+
/* Cloud-specific login hosts */
58+
#define FLB_AZURE_LOGIN_HOST_PUBLIC "login.microsoftonline.com"
59+
#define FLB_AZURE_LOGIN_HOST_CHINA "login.chinacloudapi.cn"
60+
#define FLB_AZURE_LOGIN_HOST_US_GOVERNMENT "login.microsoftonline.us"
61+
62+
/* Cloud-specific Kusto scopes */
63+
#define FLB_AZURE_KUSTO_SCOPE_PUBLIC "https://help.kusto.windows.net/.default"
64+
#define FLB_AZURE_KUSTO_SCOPE_CHINA "https://help.kusto.chinacloudapi.cn/.default"
65+
#define FLB_AZURE_KUSTO_SCOPE_US_GOVERNMENT "https://help.kusto.usgovcloudapi.net/.default"
66+
67+
/* Cloud-specific Kusto IMDS resources */
68+
#define FLB_AZURE_KUSTO_RESOURCE_PUBLIC "https://api.kusto.windows.net/"
69+
#define FLB_AZURE_KUSTO_RESOURCE_CHINA "https://api.kusto.chinacloudapi.cn/"
70+
#define FLB_AZURE_KUSTO_RESOURCE_US_GOVERNMENT "https://api.kusto.usgovcloudapi.net/"
5271

5372
#define FLB_AZURE_KUSTO_MGMT_URI_PATH "/v1/rest/mgmt"
5473
#define FLB_AZURE_KUSTO_MGMT_BODY_TEMPLATE "{\"csl\":\"%s\", \"db\": \"NetDefaultDB\"}"
@@ -74,7 +93,6 @@ typedef enum {
7493

7594
#define FLB_AZURE_IMDS_ENDPOINT "/metadata/identity/oauth2/token"
7695
#define FLB_AZURE_IMDS_API_VERSION "2018-02-01"
77-
#define FLB_AZURE_IMDS_RESOURCE "https://api.kusto.windows.net/"
7896

7997

8098
struct flb_azure_kusto_resources {
@@ -105,6 +123,18 @@ struct flb_azure_kusto {
105123
char *auth_type_str;
106124
char *workload_identity_token_file;
107125

126+
/* Cloud environment */
127+
int cloud_type;
128+
char *cloud_name;
129+
flb_sds_t kusto_scope;
130+
flb_sds_t kusto_resource;
131+
flb_sds_t login_host;
132+
133+
/* Custom cloud overrides (for private/sovereign clouds like USSEC, USNAT, BLEU) */
134+
flb_sds_t custom_login_host;
135+
flb_sds_t custom_kusto_scope;
136+
flb_sds_t custom_kusto_resource;
137+
108138
/* compress payload */
109139
int compression_enabled;
110140

plugins/out_azure_kusto/azure_kusto_conf.c

Lines changed: 143 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,115 @@ static int flb_azure_kusto_resources_destroy(struct flb_azure_kusto_resources *r
700700
return 0;
701701
}
702702

703+
/**
704+
* Resolves cloud-specific endpoints based on the cloud_name configuration.
705+
* Sets kusto_scope, kusto_resource, and login_host in the context.
706+
*
707+
* @param ctx Pointer to the plugin's context
708+
* @return int 0 on success, -1 on failure
709+
*/
710+
static int azure_kusto_resolve_cloud_endpoints(struct flb_azure_kusto *ctx)
711+
{
712+
const char *scope = NULL;
713+
const char *resource = NULL;
714+
const char *login_host = NULL;
715+
int has_custom_login_host;
716+
int has_custom_scope;
717+
int has_custom_resource;
718+
719+
/* Check if custom overrides are provided */
720+
has_custom_login_host = (ctx->custom_login_host && flb_sds_len(ctx->custom_login_host) > 0);
721+
has_custom_scope = (ctx->custom_kusto_scope && flb_sds_len(ctx->custom_kusto_scope) > 0);
722+
has_custom_resource = (ctx->custom_kusto_resource && flb_sds_len(ctx->custom_kusto_resource) > 0);
723+
724+
/* If all three custom properties are provided, use them directly */
725+
if (has_custom_login_host && has_custom_scope && has_custom_resource) {
726+
ctx->login_host = flb_sds_create(ctx->custom_login_host);
727+
if (!ctx->login_host) {
728+
flb_errno();
729+
return -1;
730+
}
731+
732+
ctx->kusto_scope = flb_sds_create(ctx->custom_kusto_scope);
733+
if (!ctx->kusto_scope) {
734+
flb_errno();
735+
return -1;
736+
}
737+
738+
ctx->kusto_resource = flb_sds_create(ctx->custom_kusto_resource);
739+
if (!ctx->kusto_resource) {
740+
flb_errno();
741+
return -1;
742+
}
743+
744+
flb_plg_info(ctx->ins,
745+
"using custom cloud endpoints: login_host='%s', scope='%s', resource='%s'",
746+
ctx->login_host, ctx->kusto_scope, ctx->kusto_resource);
747+
return 0;
748+
}
749+
750+
/* If some but not all custom properties are set, error out */
751+
if (has_custom_login_host || has_custom_scope || has_custom_resource) {
752+
flb_plg_error(ctx->ins,
753+
"When using custom cloud endpoints, all three properties must be set: "
754+
"cloud_login_host, cloud_kusto_scope, cloud_kusto_resource");
755+
return -1;
756+
}
757+
758+
/* Resolve from well-known cloud names */
759+
if (!ctx->cloud_name || strcasecmp(ctx->cloud_name, "AzureCloud") == 0) {
760+
ctx->cloud_type = FLB_AZURE_CLOUD_PUBLIC;
761+
scope = FLB_AZURE_KUSTO_SCOPE_PUBLIC;
762+
resource = FLB_AZURE_KUSTO_RESOURCE_PUBLIC;
763+
login_host = FLB_AZURE_LOGIN_HOST_PUBLIC;
764+
}
765+
else if (strcasecmp(ctx->cloud_name, "AzureChinaCloud") == 0) {
766+
ctx->cloud_type = FLB_AZURE_CLOUD_CHINA;
767+
scope = FLB_AZURE_KUSTO_SCOPE_CHINA;
768+
resource = FLB_AZURE_KUSTO_RESOURCE_CHINA;
769+
login_host = FLB_AZURE_LOGIN_HOST_CHINA;
770+
}
771+
else if (strcasecmp(ctx->cloud_name, "AzureUSGovernmentCloud") == 0) {
772+
ctx->cloud_type = FLB_AZURE_CLOUD_US_GOVERNMENT;
773+
scope = FLB_AZURE_KUSTO_SCOPE_US_GOVERNMENT;
774+
resource = FLB_AZURE_KUSTO_RESOURCE_US_GOVERNMENT;
775+
login_host = FLB_AZURE_LOGIN_HOST_US_GOVERNMENT;
776+
}
777+
else {
778+
flb_plg_error(ctx->ins,
779+
"Unknown cloud_name '%s'. Use a well-known cloud name "
780+
"('AzureCloud', 'AzureChinaCloud', 'AzureUSGovernmentCloud') "
781+
"or specify custom endpoints via "
782+
"cloud_login_host, cloud_kusto_scope, and cloud_kusto_resource",
783+
ctx->cloud_name);
784+
return -1;
785+
}
786+
787+
ctx->kusto_scope = flb_sds_create(scope);
788+
if (!ctx->kusto_scope) {
789+
flb_errno();
790+
return -1;
791+
}
792+
793+
ctx->kusto_resource = flb_sds_create(resource);
794+
if (!ctx->kusto_resource) {
795+
flb_errno();
796+
return -1;
797+
}
798+
799+
ctx->login_host = flb_sds_create(login_host);
800+
if (!ctx->login_host) {
801+
flb_errno();
802+
return -1;
803+
}
804+
805+
flb_plg_info(ctx->ins, "cloud environment='%s', login_host='%s', scope='%s'",
806+
ctx->cloud_name ? ctx->cloud_name : "AzureCloud",
807+
ctx->login_host, ctx->kusto_scope);
808+
809+
return 0;
810+
}
811+
703812
struct flb_azure_kusto *flb_azure_kusto_conf_create(struct flb_output_instance *ins,
704813
struct flb_config *config)
705814
{
@@ -722,6 +831,14 @@ struct flb_azure_kusto *flb_azure_kusto_conf_create(struct flb_output_instance *
722831
return NULL;
723832
}
724833

834+
/* Resolve cloud-specific endpoints */
835+
ret = azure_kusto_resolve_cloud_endpoints(ctx);
836+
if (ret == -1) {
837+
flb_plg_error(ins, "failed to resolve cloud endpoints");
838+
flb_azure_kusto_conf_destroy(ctx);
839+
return NULL;
840+
}
841+
725842
/* Auth method validation and setup */
726843
if (strcasecmp(ctx->auth_type_str, "service_principal") == 0) {
727844
ctx->auth_type = FLB_AZURE_KUSTO_AUTH_SERVICE_PRINCIPAL;
@@ -802,38 +919,44 @@ struct flb_azure_kusto *flb_azure_kusto_conf_create(struct flb_output_instance *
802919
/* MSI auth */
803920
/* Construct the URL template with or without client_id for managed identity */
804921
if (ctx->auth_type == FLB_AZURE_KUSTO_AUTH_MANAGED_IDENTITY_SYSTEM) {
805-
ctx->oauth_url = flb_sds_create_size(sizeof(FLB_AZURE_MSIAUTH_URL_TEMPLATE) - 1);
922+
ctx->oauth_url = flb_sds_create_size(sizeof(FLB_AZURE_MSIAUTH_URL_TEMPLATE) - 1 +
923+
flb_sds_len(ctx->kusto_resource));
806924
if (!ctx->oauth_url) {
807925
flb_errno();
808926
flb_azure_kusto_conf_destroy(ctx);
809927
return NULL;
810928
}
811929
flb_sds_snprintf(&ctx->oauth_url, flb_sds_alloc(ctx->oauth_url),
812-
FLB_AZURE_MSIAUTH_URL_TEMPLATE, "", "");
930+
FLB_AZURE_MSIAUTH_URL_TEMPLATE, "", "",
931+
ctx->kusto_resource);
813932
} else {
814933
/* User-assigned managed identity */
815934
ctx->oauth_url = flb_sds_create_size(sizeof(FLB_AZURE_MSIAUTH_URL_TEMPLATE) - 1 +
816935
sizeof("&client_id=") - 1 +
817-
flb_sds_len(ctx->client_id));
936+
flb_sds_len(ctx->client_id) +
937+
flb_sds_len(ctx->kusto_resource));
818938
if (!ctx->oauth_url) {
819939
flb_errno();
820940
flb_azure_kusto_conf_destroy(ctx);
821941
return NULL;
822942
}
823943
flb_sds_snprintf(&ctx->oauth_url, flb_sds_alloc(ctx->oauth_url),
824-
FLB_AZURE_MSIAUTH_URL_TEMPLATE, "&client_id=", ctx->client_id);
944+
FLB_AZURE_MSIAUTH_URL_TEMPLATE, "&client_id=",
945+
ctx->client_id, ctx->kusto_resource);
825946
}
826947
} else {
827948
/* Standard OAuth2 for service principal or workload identity */
828949
ctx->oauth_url = flb_sds_create_size(sizeof(FLB_MSAL_AUTH_URL_TEMPLATE) - 1 +
950+
flb_sds_len(ctx->login_host) +
829951
flb_sds_len(ctx->tenant_id));
830952
if (!ctx->oauth_url) {
831953
flb_errno();
832954
flb_azure_kusto_conf_destroy(ctx);
833955
return NULL;
834956
}
835957
flb_sds_snprintf(&ctx->oauth_url, flb_sds_alloc(ctx->oauth_url),
836-
FLB_MSAL_AUTH_URL_TEMPLATE, ctx->tenant_id);
958+
FLB_MSAL_AUTH_URL_TEMPLATE, ctx->login_host,
959+
ctx->tenant_id);
837960
}
838961

839962
ctx->resources = flb_calloc(1, sizeof(struct flb_azure_kusto_resources));
@@ -857,6 +980,21 @@ int flb_azure_kusto_conf_destroy(struct flb_azure_kusto *ctx)
857980

858981
flb_plg_info(ctx->ins, "before exiting the plugin kusto conf destroy called");
859982

983+
if (ctx->kusto_scope) {
984+
flb_sds_destroy(ctx->kusto_scope);
985+
ctx->kusto_scope = NULL;
986+
}
987+
988+
if (ctx->kusto_resource) {
989+
flb_sds_destroy(ctx->kusto_resource);
990+
ctx->kusto_resource = NULL;
991+
}
992+
993+
if (ctx->login_host) {
994+
flb_sds_destroy(ctx->login_host);
995+
ctx->login_host = NULL;
996+
}
997+
860998
if (ctx->oauth_url) {
861999
flb_sds_destroy(ctx->oauth_url);
8621000
ctx->oauth_url = NULL;

plugins/out_azure_kusto/azure_msiauth.c

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ static flb_sds_t read_token_from_file(const char *token_file)
135135
return token;
136136
}
137137

138-
int flb_azure_workload_identity_token_get(struct flb_oauth2 *ctx, const char *token_file, const char *client_id, const char *tenant_id)
138+
int flb_azure_workload_identity_token_get(struct flb_oauth2 *ctx, const char *token_file,
139+
const char *client_id, const char *tenant_id,
140+
const char *scope)
139141
{
140142
int ret;
141143
size_t b_sent;
@@ -169,23 +171,48 @@ int flb_azure_workload_identity_token_get(struct flb_oauth2 *ctx, const char *to
169171
}
170172

171173
body = flb_sds_cat(body, "client_id=", 10);
174+
if (!body) {
175+
goto body_error;
176+
}
172177
body = flb_sds_cat(body, client_id, strlen(client_id));
173-
/* Use the correct grant_type and length for workload identity */
178+
if (!body) {
179+
goto body_error;
180+
}
174181
body = flb_sds_cat(body, "&grant_type=client_credentials", 30);
182+
if (!body) {
183+
goto body_error;
184+
}
175185
body = flb_sds_cat(body, "&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer", 77);
186+
if (!body) {
187+
goto body_error;
188+
}
176189
body = flb_sds_cat(body, "&client_assertion=", 18);
190+
if (!body) {
191+
goto body_error;
192+
}
177193
body = flb_sds_cat(body, federated_token, flb_sds_len(federated_token));
178-
/* Use the correct scope and length for Kusto */
179-
body = flb_sds_cat(body, "&scope=https://help.kusto.windows.net/.default", 46);
180-
181194
if (!body) {
182-
/* This check might be redundant if flb_sds_cat handles errors, but safe */
183-
flb_error("[azure workload identity] failed to build request body");
184-
flb_sds_destroy(federated_token);
185-
return -1;
195+
goto body_error;
196+
}
197+
/* Use the cloud-specific scope for Kusto */
198+
body = flb_sds_cat(body, "&scope=", 7);
199+
if (!body) {
200+
goto body_error;
201+
}
202+
body = flb_sds_cat(body, scope, strlen(scope));
203+
if (!body) {
204+
goto body_error;
186205
}
187206

188207
/* Get upstream connection to Azure AD token endpoint */
208+
goto body_ok;
209+
210+
body_error:
211+
flb_error("[azure workload identity] failed to build request body (OOM)");
212+
flb_sds_destroy(federated_token);
213+
return -1;
214+
215+
body_ok:
189216
u_conn = flb_upstream_conn_get(ctx->u);
190217
if (!u_conn) {
191218
flb_error("[azure workload identity] could not get an upstream connection");

plugins/out_azure_kusto/azure_msiauth.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@
1919

2020
#include <fluent-bit/flb_info.h>
2121

22-
/* MSAL authorization URL */
22+
/* MSAL authorization URL template: %s%s = optional client_id param, %s = resource URL */
2323
#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"
24+
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2021-02-01%s%s&resource=%s"
2525

2626
char *flb_azure_msiauth_token_get(struct flb_oauth2 *ctx);
27-
int flb_azure_workload_identity_token_get(struct flb_oauth2 *ctx, const char *token_file, const char *client_id, const char *tenant_id);
27+
int flb_azure_workload_identity_token_get(struct flb_oauth2 *ctx, const char *token_file,
28+
const char *client_id, const char *tenant_id,
29+
const char *scope);
2830

0 commit comments

Comments
 (0)