From 72499d0ac0cc438d920d800ffcace5b9ac6b57d7 Mon Sep 17 00:00:00 2001 From: rghouzra Date: Mon, 6 Oct 2025 11:10:25 +0100 Subject: [PATCH] [out_oracle_log_analytics] feat: Implement IMDS authentication, chunking - Added Oracle Instance Metadata Service (IMDS) authentication, certificate, and key retrieval - Implemented log chunking for efficient data handling and transmission - Added timezone support for accurate timestamp processing - Enhanced credential parsing and metadata extraction - Improved error handling and debug logging Signed-off-by: rghouzra fixx size parameter in hash table addition Signed-off-by: reda ghouzraf <98324229+rghouzra@users.noreply.github.com> fix: improve error handling and cleanup Signed-off-by: rghouzra replace cjson with jsmn Signed-off-by: reda ghouzraf <98324229+rghouzra@users.noreply.github.com> replace cjson include with jsmn Signed-off-by: reda ghouzraf <98324229+rghouzra@users.noreply.github.com> fix centos jobs failure Signed-off-by: rghouzra update base64 encoding and improve error handling Signed-off-by: rghouzra enhance openssl compatibility Signed-off-by: rghouzra enhance openssl comptability Signed-off-by: rghouzra improve error handling in federation handling Signed-off-by: rghouzra refactor of ocid extraction logic Signed-off-by: rghouzra feat: Add tests for oracle logan output plugin Signed-off-by: rghouzra [out_oracle_log_analytics] update tenancy extraction in test mode Signed-off-by: rghouzra [out_oracle_log_analytics] enhance test mode Signed-off-by: rghouzra fix:improve error handling in test Signed-off-by: rghouzra fix: enhance resource cleanup Signed-off-by: rghouzra fix: add resource cleanup for leaf certificate Signed-off-by: rghouzra fix: improve code formatting Signed-off-by: rghouzra [out_oracle_log_analytics] refactor OCI_logan plugin code for clarity and consistency Signed-off-by: rghouzra improve code formatting and line break Signed-off-by: rghouzra --- .../out_oracle_log_analytics/CMakeLists.txt | 7 +- plugins/out_oracle_log_analytics/oci_logan.c | 1498 ++++++++++--- plugins/out_oracle_log_analytics/oci_logan.h | 121 +- .../out_oracle_log_analytics/oci_logan_conf.c | 1893 +++++++++++++++-- .../out_oracle_log_analytics/oci_logan_conf.h | 8 +- .../oci_logan_helper.c | 580 +++++ tests/runtime/CMakeLists.txt | 2 +- tests/runtime/out_oracle_log_analytics.c | 214 ++ 8 files changed, 3767 insertions(+), 556 deletions(-) create mode 100644 plugins/out_oracle_log_analytics/oci_logan_helper.c create mode 100644 tests/runtime/out_oracle_log_analytics.c diff --git a/plugins/out_oracle_log_analytics/CMakeLists.txt b/plugins/out_oracle_log_analytics/CMakeLists.txt index 81f971a9501..cb0e2f5d25b 100644 --- a/plugins/out_oracle_log_analytics/CMakeLists.txt +++ b/plugins/out_oracle_log_analytics/CMakeLists.txt @@ -1,6 +1,7 @@ set(src - oci_logan.c - oci_logan_conf.c - ) + oci_logan.c + oci_logan_conf.c + oci_logan_helper.c + ) FLB_PLUGIN(out_oracle_log_analytics "${src}" "") diff --git a/plugins/out_oracle_log_analytics/oci_logan.c b/plugins/out_oracle_log_analytics/oci_logan.c index 42ff71938aa..7377dd06625 100644 --- a/plugins/out_oracle_log_analytics/oci_logan.c +++ b/plugins/out_oracle_log_analytics/oci_logan.c @@ -17,30 +17,26 @@ * limitations under the License. */ +#include "oci_logan_conf.h" +#include "oci_logan.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include -#include +#define ASN1_STRING_get0_data(x) ASN1_STRING_data(x) -#include +static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +{ + return EVP_MD_CTX_create(); +} -#include "oci_logan_conf.h" -#include "oci_logan.h" +static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) +{ + EVP_MD_CTX_destroy(ctx); +} +#endif -static int check_config_from_record(msgpack_object key, - char *name, int len) +static int check_config_from_record(msgpack_object key, char *name, int len) { if (key.type != MSGPACK_OBJECT_STR) { return FLB_FALSE; @@ -59,28 +55,29 @@ static int check_config_from_record(msgpack_object key, * algorithm="rsa-sha256",headers="(request-target) date x-content-sha256 content-type content-length", * signature="signature" */ -static flb_sds_t create_authorization_header_content(struct flb_oci_logan *ctx, +static flb_sds_t create_authorization_header_content(struct flb_oci_logan + *ctx, flb_sds_t signature) { flb_sds_t content; content = flb_sds_create_size(512); flb_sds_cat_safe(&content, FLB_OCI_SIGN_SIGNATURE_VERSION, - sizeof(FLB_OCI_SIGN_SIGNATURE_VERSION) - 1); + sizeof(FLB_OCI_SIGN_SIGNATURE_VERSION) - 1); flb_sds_cat_safe(&content, ",", 1); flb_sds_cat_safe(&content, FLB_OCI_SIGN_KEYID, - sizeof(FLB_OCI_SIGN_KEYID) - 1); + sizeof(FLB_OCI_SIGN_KEYID) - 1); flb_sds_cat_safe(&content, "=\"", 2); flb_sds_cat_safe(&content, ctx->key_id, flb_sds_len(ctx->key_id)); flb_sds_cat_safe(&content, "\",", 2); flb_sds_cat_safe(&content, FLB_OCI_SIGN_ALGORITHM, - sizeof(FLB_OCI_SIGN_ALGORITHM) - 1); + sizeof(FLB_OCI_SIGN_ALGORITHM) - 1); flb_sds_cat_safe(&content, ",", 1); flb_sds_cat_safe(&content, FLB_OCI_SIGN_HEADERS, - sizeof(FLB_OCI_SIGN_HEADERS) - 1); + sizeof(FLB_OCI_SIGN_HEADERS) - 1); flb_sds_cat_safe(&content, ",", 1); flb_sds_cat_safe(&content, FLB_OCI_SIGN_SIGNATURE, - sizeof(FLB_OCI_SIGN_SIGNATURE) - 1); + sizeof(FLB_OCI_SIGN_SIGNATURE) - 1); flb_sds_cat_safe(&content, "=\"", 2); flb_sds_cat_safe(&content, signature, flb_sds_len(signature)); flb_sds_cat_safe(&content, "\"", 1); @@ -99,22 +96,22 @@ static flb_sds_t create_base64_sha256_signature(struct flb_oci_logan *ctx, size_t sig_len = sizeof(sig); ret = flb_hash_simple(FLB_HASH_SHA256, - (unsigned char*) signing_string, + (unsigned char *) signing_string, flb_sds_len(signing_string), sha256_buf, sizeof(sha256_buf)); - if(ret != FLB_CRYPTO_SUCCESS) { + if (ret != FLB_CRYPTO_SUCCESS) { flb_plg_error(ctx->ins, "error generating hash buffer"); return NULL; } - ret = flb_crypto_sign_simple(FLB_CRYPTO_PRIVATE_KEY, - FLB_CRYPTO_PADDING_PKCS1, - FLB_HASH_SHA256, - (unsigned char *) ctx->private_key, - flb_sds_len(ctx->private_key), - sha256_buf, sizeof(sha256_buf), - sig, &sig_len); + ret = flb_crypto_sign_simple(FLB_CRYPTO_PRIVATE_KEY, + FLB_CRYPTO_PADDING_PKCS1, + FLB_HASH_SHA256, + (unsigned char *) ctx->private_key, + flb_sds_len(ctx->private_key), + sha256_buf, sizeof(sha256_buf), + sig, &sig_len); if (ret != FLB_CRYPTO_SUCCESS) { @@ -130,8 +127,8 @@ static flb_sds_t create_base64_sha256_signature(struct flb_oci_logan *ctx, /* base 64 encode */ len = flb_sds_alloc(signature) - 1; - flb_base64_encode((unsigned char*) signature, len, &outlen, sig, - sizeof(sig)); + flb_base64_encode((unsigned char *) signature, len, &outlen, sig, + sizeof(sig)); signature[outlen] = '\0'; flb_sds_len_set(signature, outlen); @@ -171,7 +168,8 @@ static flb_sds_t get_date(void) } static flb_sds_t add_header_and_signing(struct flb_http_client *c, - flb_sds_t signing_str, const char *header, int headersize, + flb_sds_t signing_str, + const char *header, int headersize, const char *val, int val_size) { if (!signing_str) { @@ -189,7 +187,8 @@ static flb_sds_t add_header_and_signing(struct flb_http_client *c, } static int build_headers(struct flb_http_client *c, struct flb_oci_logan *ctx, - flb_sds_t json, flb_sds_t hostname, int port, flb_sds_t uri) + flb_sds_t json, flb_sds_t hostname, int port, + flb_sds_t uri) { int ret = -1; flb_sds_t tmp_sds = NULL; @@ -198,11 +197,8 @@ static int build_headers(struct flb_http_client *c, struct flb_oci_logan *ctx, flb_sds_t encoded_uri = NULL; flb_sds_t signature = NULL; flb_sds_t auth_header_str = NULL; - flb_sds_t tmp_ref = NULL; - size_t tmp_len = 0; - unsigned char sha256_buf[32] = { 0 }; tmp_sds = flb_sds_create_size(512); @@ -224,10 +220,9 @@ static int build_headers(struct flb_http_client *c, struct flb_oci_logan *ctx, goto error_label; } flb_sds_cat_safe(&signing_str, FLB_OCI_HEADER_REQUEST_TARGET, - sizeof(FLB_OCI_HEADER_REQUEST_TARGET) - 1); + sizeof(FLB_OCI_HEADER_REQUEST_TARGET) - 1); flb_sds_cat_safe(&signing_str, ": post ", sizeof(": post ") - 1); - flb_sds_cat_safe(&signing_str, encoded_uri, - flb_sds_len(encoded_uri)); + flb_sds_cat_safe(&signing_str, encoded_uri, flb_sds_len(encoded_uri)); /* Add Host to Header */ if (((c->flags & FLB_IO_TLS) && c->port == 443) @@ -260,7 +255,8 @@ static int build_headers(struct flb_http_client *c, struct flb_oci_logan *ctx, goto error_label; } signing_str = add_header_and_signing(c, signing_str, FLB_OCI_HEADER_DATE, - sizeof(FLB_OCI_HEADER_DATE) - 1, rfc1123date, + sizeof(FLB_OCI_HEADER_DATE) - 1, + rfc1123date, flb_sds_len(rfc1123date)); if (!signing_str) { flb_plg_error(ctx->ins, "cannot compose signing string"); @@ -269,12 +265,12 @@ static int build_headers(struct flb_http_client *c, struct flb_oci_logan *ctx, /* Add x-content-sha256 Header */ ret = flb_hash_simple(FLB_HASH_SHA256, - (unsigned char*) json, - flb_sds_len(json), - sha256_buf, sizeof(sha256_buf)); + (unsigned char *) json, + flb_sds_len(json), sha256_buf, sizeof(sha256_buf)); if (ret != FLB_CRYPTO_SUCCESS) { - flb_plg_error(ctx->ins, "error forming hash buffer for x-content-sha256 Header"); + flb_plg_error(ctx->ins, + "error forming hash buffer for x-content-sha256 Header"); goto error_label; } @@ -284,7 +280,7 @@ static int build_headers(struct flb_http_client *c, struct flb_oci_logan *ctx, goto error_label; } - flb_base64_encode((unsigned char*) tmp_sds, flb_sds_len(tmp_sds) - 1, + flb_base64_encode((unsigned char *) tmp_sds, flb_sds_alloc(tmp_sds) - 1, &tmp_len, sha256_buf, sizeof(sha256_buf)); tmp_sds[tmp_len] = '\0'; @@ -292,18 +288,23 @@ static int build_headers(struct flb_http_client *c, struct flb_oci_logan *ctx, signing_str = add_header_and_signing(c, signing_str, FLB_OCI_HEADER_X_CONTENT_SHA256, - sizeof(FLB_OCI_HEADER_X_CONTENT_SHA256) - 1, tmp_sds, - flb_sds_len(tmp_sds)); + sizeof + (FLB_OCI_HEADER_X_CONTENT_SHA256) - + 1, tmp_sds, flb_sds_len(tmp_sds)); if (!signing_str) { flb_plg_error(ctx->ins, "cannot compose signing string"); goto error_label; } - + flb_http_add_header(c, "opc-retry-token", 15, tmp_sds, + flb_sds_len(tmp_sds)); /* Add content-Type */ signing_str = add_header_and_signing(c, signing_str, - FLB_OCI_HEADER_CONTENT_TYPE, sizeof(FLB_OCI_HEADER_CONTENT_TYPE) - 1, - FLB_OCI_HEADER_CONTENT_TYPE_VAL, - sizeof(FLB_OCI_HEADER_CONTENT_TYPE_VAL) - 1); + FLB_OCI_HEADER_CONTENT_TYPE, + sizeof(FLB_OCI_HEADER_CONTENT_TYPE) - + 1, FLB_OCI_HEADER_CONTENT_TYPE_VAL, + sizeof + (FLB_OCI_HEADER_CONTENT_TYPE_VAL) - + 1); if (!signing_str) { flb_plg_error(ctx->ins, "cannot compose signing string"); goto error_label; @@ -314,8 +315,9 @@ static int build_headers(struct flb_http_client *c, struct flb_oci_logan *ctx, (int) flb_sds_len(json)); flb_sds_len_set(tmp_sds, tmp_len); signing_str = add_header_and_signing(c, signing_str, - FLB_OCI_HEADER_CONTENT_LENGTH, sizeof(FLB_OCI_HEADER_CONTENT_LENGTH) - 1, - tmp_sds, flb_sds_len(tmp_sds)); + FLB_OCI_HEADER_CONTENT_LENGTH, + sizeof(FLB_OCI_HEADER_CONTENT_LENGTH) + - 1, tmp_sds, flb_sds_len(tmp_sds)); if (!signing_str) { flb_plg_error(ctx->ins, "cannot compose signing string"); goto error_label; @@ -334,8 +336,9 @@ static int build_headers(struct flb_http_client *c, struct flb_oci_logan *ctx, goto error_label; } - flb_http_add_header(c, FLB_OCI_HEADER_AUTH, sizeof(FLB_OCI_HEADER_AUTH) - 1, - auth_header_str, flb_sds_len(auth_header_str)); + flb_http_add_header(c, FLB_OCI_HEADER_AUTH, + sizeof(FLB_OCI_HEADER_AUTH) - 1, auth_header_str, + flb_sds_len(auth_header_str)); /* User-Agent */ flb_http_add_header(c, FLB_OCI_HEADER_USER_AGENT, @@ -348,7 +351,7 @@ static int build_headers(struct flb_http_client *c, struct flb_oci_logan *ctx, ret = 0; - error_label: + error_label: if (tmp_sds) { flb_sds_destroy(tmp_sds); } @@ -370,8 +373,11 @@ static int build_headers(struct flb_http_client *c, struct flb_oci_logan *ctx, return ret; } -static struct flb_oci_error_response* parse_response_error(struct flb_oci_logan *ctx, - char *response, size_t response_len) +static struct flb_oci_error_response *parse_response_error(struct + flb_oci_logan *ctx, + char *response, + size_t + response_len) { int tok_size = 32, ret, i; jsmn_parser parser; @@ -444,8 +450,9 @@ static struct flb_oci_error_response* parse_response_error(struct flb_oci_logan } } else if ((key_len == sizeof(FLB_OCI_ERROR_RESPONSE_MESSAGE) - 1) - && strncasecmp(key, FLB_OCI_ERROR_RESPONSE_MESSAGE, - sizeof(FLB_OCI_ERROR_RESPONSE_MESSAGE) - 1) == 0) { + && strncasecmp(key, FLB_OCI_ERROR_RESPONSE_MESSAGE, + sizeof(FLB_OCI_ERROR_RESPONSE_MESSAGE) - 1) == + 0) { /* message */ error_response->message = flb_sds_create_len(val, val_len); @@ -468,9 +475,9 @@ static int retry_error(struct flb_http_client *c, struct flb_oci_logan *ctx) int ret = FLB_FALSE; /* possible retry error message */ - if ( !(c->resp.status == 400 || c->resp.status == 401 - || c->resp.status == 404 || c->resp.status == 409 - || c->resp.status == 429 || c->resp.status == 500)) { + if (!(c->resp.status == 400 || c->resp.status == 401 + || c->resp.status == 404 || c->resp.status == 409 + || c->resp.status == 429 || c->resp.status == 500)) { return FLB_FALSE; } @@ -478,48 +485,79 @@ static int retry_error(struct flb_http_client *c, struct flb_oci_logan *ctx) error_response = parse_response_error(ctx, c->resp.payload, c->resp.payload_size); if (!error_response) { + flb_plg_error(ctx->ins, "failed to parse error response"); return FLB_FALSE; } if (error_response->code) { tmp_len = flb_sds_len(error_response->code); if (c->resp.status == 400 - && (tmp_len == sizeof(FLB_OCI_ERROR_CODE_RELATED_RESOURCE_NOT_FOUND) - 1) - && strncasecmp(error_response->code, FLB_OCI_ERROR_CODE_RELATED_RESOURCE_NOT_FOUND, tmp_len) == 0) { + && (tmp_len == + sizeof(FLB_OCI_ERROR_CODE_RELATED_RESOURCE_NOT_FOUND) - 1) + && strncasecmp(error_response->code, + FLB_OCI_ERROR_CODE_RELATED_RESOURCE_NOT_FOUND, + tmp_len) == 0) { ret = FLB_TRUE; } - else if( c->resp.status == 401 - &&( tmp_len == sizeof(FLB_OCI_ERROR_CODE_NOT_AUTHENTICATED)-1 ) - && strncasecmp(error_response->code, FLB_OCI_ERROR_CODE_NOT_AUTHENTICATED, tmp_len) == 0) { + else if (c->resp.status == 401 + && (tmp_len == + sizeof(FLB_OCI_ERROR_CODE_NOT_AUTHENTICATED) - 1) + && strncasecmp(error_response->code, + FLB_OCI_ERROR_CODE_NOT_AUTHENTICATED, + tmp_len) == 0) { ret = FLB_TRUE; } else if (c->resp.status == 404 - && (tmp_len == sizeof(FLB_OCI_ERROR_CODE_NOT_AUTHENTICATEDORNOTFOUND) - 1) - && strncasecmp(error_response->code, FLB_OCI_ERROR_CODE_NOT_AUTHENTICATEDORNOTFOUND, tmp_len) == 0) { + && (tmp_len == + sizeof(FLB_OCI_ERROR_CODE_NOT_AUTHENTICATEDORNOTFOUND) - + 1) + && strncasecmp(error_response->code, + FLB_OCI_ERROR_CODE_NOT_AUTHENTICATEDORNOTFOUND, + tmp_len) == 0) { ret = FLB_TRUE; } else if (c->resp.status == 409 - && (tmp_len == sizeof(FLB_OCI_ERROR_CODE_INCORRECTSTATE) - 1) - && strncasecmp(error_response->code, FLB_OCI_ERROR_CODE_INCORRECTSTATE, tmp_len) == 0) { + && (tmp_len == sizeof(FLB_OCI_ERROR_CODE_INCORRECTSTATE) - 1) + && strncasecmp(error_response->code, + FLB_OCI_ERROR_CODE_INCORRECTSTATE, + tmp_len) == 0) { ret = FLB_TRUE; } else if (c->resp.status == 409 - && (tmp_len == sizeof(FLB_OCI_ERROR_CODE_NOT_AUTH_OR_RESOURCE_EXIST) - 1) - && strncasecmp(error_response->code, FLB_OCI_ERROR_CODE_NOT_AUTH_OR_RESOURCE_EXIST, tmp_len) == 0) { + && (tmp_len == + sizeof(FLB_OCI_ERROR_CODE_NOT_AUTH_OR_RESOURCE_EXIST) - + 1) + && strncasecmp(error_response->code, + FLB_OCI_ERROR_CODE_NOT_AUTH_OR_RESOURCE_EXIST, + tmp_len) == 0) { ret = FLB_TRUE; } else if (c->resp.status == 429 - && (tmp_len == sizeof(FLB_OCI_ERROR_CODE_TOO_MANY_REQUESTS) - 1) - && strncasecmp(error_response->code, FLB_OCI_ERROR_CODE_TOO_MANY_REQUESTS, tmp_len) == 0) { + && (tmp_len == + sizeof(FLB_OCI_ERROR_CODE_TOO_MANY_REQUESTS) - 1) + && strncasecmp(error_response->code, + FLB_OCI_ERROR_CODE_TOO_MANY_REQUESTS, + tmp_len) == 0) { ret = FLB_TRUE; } else if (c->resp.status == 500 - && (tmp_len == sizeof(FLB_OCI_ERROR_CODE_INTERNAL_SERVER_ERROR) - 1) - && strncasecmp(error_response->code, FLB_OCI_ERROR_CODE_INTERNAL_SERVER_ERROR, tmp_len) == 0) { + && (tmp_len == + sizeof(FLB_OCI_ERROR_CODE_INTERNAL_SERVER_ERROR) - 1) + && strncasecmp(error_response->code, + FLB_OCI_ERROR_CODE_INTERNAL_SERVER_ERROR, + tmp_len) == 0) { ret = FLB_TRUE; } } + if (ret == FLB_RETRY) { + flb_plg_error(ctx->ins, "HTTP %d: will retry", c->resp.status); + } + else { + flb_plg_error(ctx->ins, "HTTP %d: non retryable error", + c->resp.status); + } + if (error_response->code) { flb_sds_destroy(error_response->code); } @@ -532,10 +570,10 @@ static int retry_error(struct flb_http_client *c, struct flb_oci_logan *ctx) } static int cb_oci_logan_init(struct flb_output_instance *ins, - struct flb_config *config, - void *data) + struct flb_config *config, void *data) { struct flb_oci_logan *ctx; + ctx = flb_oci_logan_conf_create(ins, config); if (!ctx) { flb_plg_error(ins, "cannot initialize plugin"); @@ -568,8 +606,7 @@ static flb_sds_t compose_uri(struct flb_oci_logan *ctx, flb_sds_cat_safe(&uri_param, FLB_OCI_LOG_GROUP_ID, FLB_OCI_LOG_GROUP_ID_SIZE); flb_sds_cat_safe(&uri_param, "=", 1); - flb_sds_cat_safe(&uri_param, log_group_id, - flb_sds_len(log_group_id)); + flb_sds_cat_safe(&uri_param, log_group_id, flb_sds_len(log_group_id)); } if (!uri_param) { @@ -581,11 +618,9 @@ static flb_sds_t compose_uri(struct flb_oci_logan *ctx, if (flb_sds_len(uri_param) > 0) { flb_sds_cat_safe(&uri_param, "&", 1); } - flb_sds_cat_safe(&uri_param, FLB_OCI_LOG_SET, - FLB_OCI_LOG_SET_SIZE); + flb_sds_cat_safe(&uri_param, FLB_OCI_LOG_SET, FLB_OCI_LOG_SET_SIZE); flb_sds_cat_safe(&uri_param, "=", 1); - flb_sds_cat_safe(&uri_param, log_set, - flb_sds_len(log_set)); + flb_sds_cat_safe(&uri_param, log_set, flb_sds_len(log_set)); } if (!uri_param) { @@ -594,7 +629,7 @@ static flb_sds_t compose_uri(struct flb_oci_logan *ctx, flb_sds_cat_safe(&uri_param, "&", 1); flb_sds_cat_safe(&uri_param, FLB_OCI_PAYLOAD_TYPE, - sizeof(FLB_OCI_PAYLOAD_TYPE) - 1); + sizeof(FLB_OCI_PAYLOAD_TYPE) - 1); flb_sds_cat_safe(&uri_param, "=", 1); flb_sds_cat_safe(&uri_param, "JSON", 4); @@ -609,8 +644,9 @@ static flb_sds_t compose_uri(struct flb_oci_logan *ctx, return flb_sds_create(ctx->uri); } - full_uri = flb_sds_create_size( - flb_sds_len(ctx->uri) + 1 + flb_sds_len(uri_param)); + full_uri = + flb_sds_create_size(flb_sds_len(ctx->uri) + 1 + + flb_sds_len(uri_param)); if (!full_uri) { flb_errno(); flb_sds_destroy(uri_param); @@ -620,16 +656,379 @@ static flb_sds_t compose_uri(struct flb_oci_logan *ctx, flb_sds_cat_safe(&full_uri, ctx->uri, flb_sds_len(ctx->uri)); flb_sds_cat_safe(&full_uri, "?", 1); flb_sds_cat_safe(&full_uri, uri_param, flb_sds_len(uri_param)); - flb_sds_destroy(uri_param); return full_uri; } +/* Checks if the current security token needs refresh(expires within 5min) */ +static int token_needs_refresh(struct flb_oci_logan *ctx) +{ + time_t now; + + now = time(NULL); + return (ctx->security_token.expires_at - now) < 300; +} + +/* Refreshes the security token if it's close to expiration */ +static int refresh_security_token_if_needed(struct flb_oci_logan *ctx) +{ + flb_sds_t json_payload; + flb_sds_t response; + + if (!token_needs_refresh(ctx)) { + return 0; + } + + json_payload = create_federation_payload(ctx); + if (!json_payload) { + flb_plg_error(ctx->ins, + "failed to create federation payload for token refresh"); + return -1; + } + + response = sign_and_send_federation_request(ctx, json_payload); + flb_sds_destroy(json_payload); + + if (!response) { + flb_plg_error(ctx->ins, "token refresh failed: federation request"); + return -1; + } + + flb_sds_destroy(response); + flb_plg_info(ctx->ins, "success refresh security token"); + return 0; +} + + +static flb_sds_t sign_oci_request_with_security_token_for_logging(struct + flb_oci_logan + *ctx, + const char + *uri_path, + const char + *payload, + size_t + payload_size, + flb_sds_t + date, + const char + *host, + const char + *content_sha256) +{ + flb_sds_t string_to_sign = NULL; + flb_sds_t signature_header = NULL; + flb_sds_t lowercase_method = flb_sds_create("post"); + unsigned char *signature = NULL; + unsigned char *b64_out = NULL; + size_t sig_len = 0; + BIO *bio = NULL; + size_t b64_size, olen; + EVP_PKEY *pkey = NULL; + EVP_MD_CTX *md_ctx = NULL; + const char *headers_list; + + string_to_sign = flb_sds_create_size(2048); + if (!string_to_sign) { + return NULL; + } + flb_sds_printf(&string_to_sign, "date: %s\n", date); + flb_sds_printf(&string_to_sign, "(request-target): %s %s\n", + lowercase_method, uri_path); + flb_sds_printf(&string_to_sign, "host: %s\n", host); + flb_sds_printf(&string_to_sign, "x-content-sha256: %s\n", content_sha256); + + if (payload && payload_size > 0) { + flb_sds_printf(&string_to_sign, + "content-type: application/octet-stream\n"); + flb_sds_printf(&string_to_sign, "content-length: %zu", payload_size); + } + else { + flb_sds_printf(&string_to_sign, "content-length: 0"); + } + + flb_plg_debug(ctx->ins, "String to sign for la ->> [%s]", string_to_sign); + + bio = BIO_new_mem_buf((void *) ctx->imds.session_privkey, -1); + if (!bio) { + goto cleanup; + } + + pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + if (!pkey) { + goto cleanup; + } + + md_ctx = EVP_MD_CTX_new(); + if (!md_ctx) { + goto cleanup; + } + + if (EVP_DigestSignInit(md_ctx, NULL, EVP_sha256(), NULL, pkey) <= 0) { + goto cleanup; + } + + if (EVP_DigestSignUpdate + (md_ctx, string_to_sign, flb_sds_len(string_to_sign)) <= 0) { + goto cleanup; + } + + if (EVP_DigestSignFinal(md_ctx, NULL, &sig_len) <= 0) { + goto cleanup; + } + + signature = flb_malloc(sig_len); + if (!signature) { + goto cleanup; + } + + if (EVP_DigestSignFinal(md_ctx, signature, &sig_len) <= 0) { + goto cleanup; + } + + b64_size = ((sig_len + 2) / 3) * 4 + 1; + olen = 0; + b64_out = flb_malloc(b64_size); + if (!b64_out) { + goto cleanup; + } + + if (flb_base64_encode(b64_out, b64_size, &olen, signature, sig_len) != 0) { + goto cleanup; + } + b64_out[olen] = '\0'; + + signature_header = flb_sds_create_size(2048); + if (!signature_header) { + goto cleanup; + } + + headers_list = payload && payload_size > 0 ? + "date (request-target) host x-content-sha256 content-type content-length" + : "date (request-target) host x-content-sha256 content-length"; + + flb_sds_printf(&signature_header, + "Signature version=\"1\",keyId=\"ST$%s\",algorithm=\"rsa-sha256\"," + "signature=\"%s\",headers=\"%s\"", + ctx->security_token.token, b64_out, headers_list); + + cleanup: + if (bio) + BIO_free(bio); + if (pkey) + EVP_PKEY_free(pkey); + if (md_ctx) + EVP_MD_CTX_free(md_ctx); + if (signature) + flb_free(signature); + if (b64_out) + flb_free(b64_out); + if (string_to_sign) + flb_sds_destroy(string_to_sign); + if (lowercase_method) + flb_sds_destroy(lowercase_method); + + return signature_header; +} + +static char *calculate_content_sha256_b64(const char *content, size_t len) +{ + unsigned char hash[SHA256_DIGEST_LENGTH]; + char *b64_hash = NULL; + size_t b64_len = 4 * ((SHA256_DIGEST_LENGTH + 2) / 3) + 1; + + SHA256((unsigned char *) content, len, hash); + + b64_hash = flb_malloc(b64_len); + if (!b64_hash) { + return NULL; + } + + if (flb_base64_encode + ((unsigned char *) b64_hash, b64_len, &b64_len, hash, + SHA256_DIGEST_LENGTH) != 0) { + flb_free(b64_hash); + return NULL; + } + b64_hash[b64_len] = '\0'; + + return b64_hash; +} + + +struct flb_http_client *create_oci_signed_request_for_logging(struct + flb_oci_logan + *ctx, + struct + flb_connection + *u_conn, + const char + *uri_path, + const char + *host, int port, + const char + *payload, + size_t + payload_size, + bool + *should_retry) +{ + struct flb_http_client *client = NULL; + flb_sds_t date_header = NULL; + flb_sds_t signature_header = NULL; + char *content_sha256 = NULL; + char date_buf[128]; + time_t now; + struct tm *tm_info; + size_t date_len; + const char *user_agent; + + if (refresh_security_token_if_needed(ctx) < 0) { + flb_plg_error(ctx->ins, "refresh_security_token_if_needed failed "); + *should_retry = 1; + return NULL; + } + + if (!ctx->security_token.token + || flb_sds_len(ctx->security_token.token) == 0) { + return NULL; + } + + time(&now); + tm_info = gmtime(&now); + if (!tm_info) { + return NULL; + } + + date_len = strftime(date_buf, sizeof(date_buf) - 1, "%a, %d %b %Y %H:%M:%S GMT", + tm_info); + if (date_len == 0) { + return NULL; + } + + date_buf[date_len] = '\0'; + date_header = flb_sds_create_len(date_buf, date_len); + if (!date_header) { + return NULL; + } + + if (payload && payload_size > 0) { + content_sha256 = calculate_content_sha256_b64(payload, payload_size); + } + else { + content_sha256 = calculate_content_sha256_b64("", 0); + } + + if (!content_sha256) { + goto cleanup; + } + + client = flb_http_client(u_conn, FLB_HTTP_POST, uri_path, + payload, payload_size, host, port, NULL, 0); + if (!client) { + goto cleanup; + } + + flb_http_add_header(client, "Date", 4, date_header, + flb_sds_len(date_header)); + flb_http_add_header(client, "Accept", 6, "application/json", 16); + + if (payload && payload_size > 0) { + flb_http_add_header(client, "Content-Type", 12, + "application/octet-stream", 24); + } + + flb_http_add_header(client, "x-content-sha256", 16, content_sha256, + strlen(content_sha256)); + + flb_http_add_header(client, "opc-retry-token", 15, content_sha256, + strlen(content_sha256)); + user_agent = "fluent-bit-oci-plugin/1.0"; + flb_http_add_header(client, "User-Agent", 10, user_agent, + strlen(user_agent)); + + signature_header = sign_oci_request_with_security_token_for_logging(ctx, + uri_path, payload, + payload_size, + date_header, host, + content_sha256); + if (!signature_header) { + goto cleanup; + } + + flb_http_add_header(client, "Authorization", 13, signature_header, + flb_sds_len(signature_header)); + flb_sds_destroy(date_header); + flb_sds_destroy(signature_header); + flb_free(content_sha256); + + return client; + + cleanup: + if (client) + flb_http_client_destroy(client); + if (date_header) + flb_sds_destroy(date_header); + if (signature_header) + flb_sds_destroy(signature_header); + if (content_sha256) + flb_free(content_sha256); + + return NULL; +} + +static void dump_payload_to_file(struct flb_oci_logan *ctx, flb_sds_t payload, + flb_sds_t log_group_id) +{ + char hash_in_hex[66]; + char filename[1024]; + char *content_sha256; + int i; + size_t payload_size; + FILE *fp; + + if (!ctx->payload_files_location) { + flb_plg_error(ctx->ins, + "directory path for dumping should be specified"); + return; + } + payload_size = flb_sds_len(payload); + content_sha256 = calculate_content_sha256_b64(payload, payload_size); + if (!content_sha256) { + return; + } + + for (i = 0; i < SHA256_DIGEST_LENGTH; i++) { + sprintf(hash_in_hex + (i * 2), "%02x", content_sha256[i]); + } + + snprintf(filename, sizeof(filename), "%s/%s_%.12s.json", + ctx->payload_files_location, log_group_id, hash_in_hex); + + if (access(filename, F_OK) == 0) { + flb_plg_debug(ctx->ins, "payload s already dumped to->%s", filename); + flb_free(content_sha256); + return; + } + + fp = fopen(filename, "w"); + if (!fp) { + flb_plg_error(ctx->ins, "cant open file -> %s", filename); + flb_free(content_sha256); + return; + } + + fprintf(fp, "%s", payload); + fclose(fp); + flb_free(content_sha256); + + flb_plg_info(ctx->ins, "payload dumped to: %s", filename); +} + static int flush_to_endpoint(struct flb_oci_logan *ctx, flb_sds_t payload, - flb_sds_t log_group_id, - flb_sds_t log_set_id) + flb_sds_t log_group_id, flb_sds_t log_set_id) { int out_ret = FLB_RETRY; int http_ret; @@ -637,42 +1036,77 @@ static int flush_to_endpoint(struct flb_oci_logan *ctx, flb_sds_t full_uri; struct flb_http_client *c = NULL; struct flb_connection *u_conn; + bool should_retry; + if (!payload) { + return FLB_ERROR; + } + if (ctx->dump_payload_file) { + dump_payload_to_file(ctx, payload, log_group_id); + } full_uri = compose_uri(ctx, log_set_id, log_group_id); - if(!full_uri) { - flb_plg_error(ctx->ins, "unable to compose uri for logGroup: %s logSet: %s", + if (!full_uri) { + flb_plg_error(ctx->ins, + "unable to compose uri for logGroup: %s logSet: %s", ctx->oci_la_log_group_id, ctx->oci_la_log_set_id); + return FLB_ERROR; } flb_plg_debug(ctx->ins, "full_uri=%s", full_uri); u_conn = flb_upstream_conn_get(ctx->u); - if(!u_conn) { - goto error_label; + if (!u_conn) { + flb_sds_destroy(full_uri); + return FLB_ERROR; + } + should_retry = 0; + if (strcmp(ctx->auth_type, "instance_principal") == 0) { + c = create_oci_signed_request_for_logging(ctx, u_conn, + full_uri, + ctx->ins->host.name, + ctx->ins->host.port, + payload, + (payload ? + flb_sds_len(payload) : 0), + &should_retry); + if (!c) { + flb_plg_error(ctx->ins, + "failed to create instance principal client for logging"); + out_ret = should_retry ? FLB_RETRY : FLB_ERROR; + goto error_label; + } } - /* Create HTTP client context */ - c = flb_http_client(u_conn, FLB_HTTP_POST, full_uri, (void*) payload, - flb_sds_len(payload), ctx->ins->host.name, ctx->ins->host.port, ctx->proxy, 0); - if (!c) { - goto error_label; + else { + c = flb_http_client(u_conn, FLB_HTTP_POST, full_uri, (void *) payload, + flb_sds_len(payload), ctx->ins->host.name, + ctx->ins->host.port, ctx->proxy, 0); + if (!c) { + flb_plg_error(ctx->ins, + "failed to config file client for logging"); + goto error_label; + } + flb_http_allow_duplicated_headers(c, FLB_FALSE); + + flb_http_buffer_size(c, FLB_HTTP_DATA_SIZE_MAX); + + if (build_headers + (c, ctx, payload, ctx->ins->host.name, ctx->ins->host.port, + full_uri) < 0) { + flb_plg_error(ctx->ins, "failed to build headers"); + goto error_label; + } } - flb_http_allow_duplicated_headers(c, FLB_FALSE); - flb_plg_debug(ctx->ins, "built client"); - flb_http_buffer_size(c, FLB_HTTP_DATA_SIZE_MAX); - if (build_headers(c, ctx, payload, ctx->ins->host.name, ctx->ins->host.port, full_uri) < 0) { - flb_plg_error(ctx->ins, "failed to build headers"); + if (!c) { + flb_plg_error(ctx->ins, "failed to create HTTP client"); goto error_label; } - flb_plg_debug(ctx->ins, "built request"); out_ret = FLB_OK; http_ret = flb_http_do(c, &b_sent); - flb_plg_debug(ctx->ins, "placed request"); if (http_ret == 0) { - if (c->resp.status != 200) { flb_plg_debug(ctx->ins, "request header %s", c->header_buf); @@ -695,18 +1129,22 @@ static int flush_to_endpoint(struct flb_oci_logan *ctx, c->resp.status); } } + else { + flb_plg_debug(ctx->ins, "data -> [%s]\theaders->[%s]", + c->resp.data, c->resp.headers_end); + flb_plg_info(ctx->ins, "log sent to oci with success"); + } } else { out_ret = FLB_RETRY; - flb_plg_error(ctx->ins, "could not flush records to %s:%i (http_do=%i), retry=%s", - ctx->ins->host.name, ctx->ins->host.port, - http_ret, (out_ret == FLB_RETRY ? "true" : "false")); + flb_plg_error(ctx->ins, + "could not flush records to %s:%i (http_do=%i), retry=%s", + ctx->ins->host.name, ctx->ins->host.port, http_ret, + (out_ret == FLB_RETRY ? "true" : "false")); goto error_label; } - - - error_label: + error_label: if (full_uri) { flb_sds_destroy(full_uri); } @@ -722,28 +1160,37 @@ static int flush_to_endpoint(struct flb_oci_logan *ctx, } return out_ret; - } -static void pack_oci_fields(msgpack_packer *packer, - struct flb_oci_logan *ctx) + +static void pack_oci_fields(msgpack_packer *packer, struct flb_oci_logan *ctx, + char *tag, int tag_len) { int num_global_meta = 0; int num_event_meta = 0; int pck_sz = 2; struct mk_list *head = NULL; struct metadata_obj *f; - + char *log_path_value = NULL; + int log_path_len = 0; /* number of meta properties */ - if(ctx->oci_la_global_metadata != NULL) { + if (ctx->oci_la_global_metadata != NULL) { num_global_meta = mk_list_size(&ctx->global_metadata_fields); } - if(ctx->oci_la_metadata != NULL) { + if (ctx->oci_la_metadata != NULL) { num_event_meta = mk_list_size(&ctx->log_event_metadata_fields); } + if (ctx->oci_la_log_path && flb_sds_len(ctx->oci_la_log_path) > 0) { + log_path_value = ctx->oci_la_log_path; + log_path_len = flb_sds_len(ctx->oci_la_log_path); + } + else if (tag && tag_len > 0) { + log_path_value = tag; + log_path_len = tag_len; + } if (num_global_meta > 0) { msgpack_pack_map(packer, 2); msgpack_pack_str(packer, FLB_OCI_LOG_METADATA_SIZE); @@ -790,14 +1237,15 @@ static void pack_oci_fields(msgpack_packer *packer, ] */ msgpack_pack_str(packer, FLB_OCI_LOG_EVENTS_SIZE); - msgpack_pack_str_body(packer, FLB_OCI_LOG_EVENTS, FLB_OCI_LOG_EVENTS_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_EVENTS, + FLB_OCI_LOG_EVENTS_SIZE); msgpack_pack_array(packer, 1); if (ctx->oci_la_entity_id) { pck_sz++; } - if (ctx->oci_la_log_path) { + if (log_path_value && log_path_len > 0) { pck_sz++; } if (ctx->oci_la_entity_type) { @@ -808,13 +1256,18 @@ static void pack_oci_fields(msgpack_packer *packer, pck_sz++; } - msgpack_pack_map(packer, pck_sz); /* entityId, logSourceName, logPath, logRecords */ + if (ctx->oci_la_timezone) { + pck_sz++; + } + + msgpack_pack_map(packer, pck_sz); /* entityId, logSourceName, logPath, logRecords , timezone */ /* "entityType:"" */ if (ctx->oci_la_entity_type) { msgpack_pack_str(packer, FLB_OCI_ENTITY_TYPE_SIZE); - msgpack_pack_str_body(packer, FLB_OCI_ENTITY_TYPE, FLB_OCI_ENTITY_TYPE_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_ENTITY_TYPE, + FLB_OCI_ENTITY_TYPE_SIZE); msgpack_pack_str(packer, flb_sds_len(ctx->oci_la_entity_type)); msgpack_pack_str_body(packer, ctx->oci_la_entity_type, flb_sds_len(ctx->oci_la_entity_type)); @@ -823,7 +1276,8 @@ static void pack_oci_fields(msgpack_packer *packer, /* "entityId":"", */ if (ctx->oci_la_entity_id) { msgpack_pack_str(packer, FLB_OCI_ENTITY_ID_SIZE); - msgpack_pack_str_body(packer, FLB_OCI_ENTITY_ID, FLB_OCI_ENTITY_ID_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_ENTITY_ID, + FLB_OCI_ENTITY_ID_SIZE); msgpack_pack_str(packer, flb_sds_len(ctx->oci_la_entity_id)); msgpack_pack_str_body(packer, ctx->oci_la_entity_id, flb_sds_len(ctx->oci_la_entity_id)); @@ -840,23 +1294,40 @@ static void pack_oci_fields(msgpack_packer *packer, /* "logPath":"" */ - if (ctx->oci_la_log_path) { + if (log_path_value && log_path_len > 0) { msgpack_pack_str(packer, FLB_OCI_LOG_PATH_SIZE); - msgpack_pack_str_body(packer, FLB_OCI_LOG_PATH, FLB_OCI_LOG_PATH_SIZE); - msgpack_pack_str(packer, flb_sds_len(ctx->oci_la_log_path)); - msgpack_pack_str_body(packer, ctx->oci_la_log_path, - flb_sds_len(ctx->oci_la_log_path)); + msgpack_pack_str_body(packer, FLB_OCI_LOG_PATH, + FLB_OCI_LOG_PATH_SIZE); + msgpack_pack_str(packer, log_path_len); + msgpack_pack_str_body(packer, log_path_value, log_path_len); + } + + if (ctx->oci_la_timezone) { + msgpack_pack_str(packer, FLB_OCI_LOG_TIMEZONE_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_TIMEZONE, + FLB_OCI_LOG_TIMEZONE_SIZE); + + if (!is_valid_timezone((const char *) ctx->oci_la_timezone)) { + flb_plg_warn(ctx->ins, "invalid timezone %s, using utc", + ctx->oci_la_timezone); + msgpack_pack_str(packer, 3); + msgpack_pack_str_body(packer, "UTC", 3); + } + else { + msgpack_pack_str(packer, flb_sds_len(ctx->oci_la_timezone)); + msgpack_pack_str_body(packer, ctx->oci_la_timezone, + flb_sds_len(ctx->oci_la_timezone)); + } } - /* Add metadata */ if (num_event_meta > 0) { /* - "metadata":{ - "Error ID":"0", - "Environment":"dev", - "Client Host Region":"IST" - }, + "metadata":{ + "Error ID":"0", + "Environment":"dev", + "Client Host Region":"IST" + }, */ msgpack_pack_str(packer, FLB_OCI_LOG_METADATA_SIZE); msgpack_pack_str_body(packer, FLB_OCI_LOG_METADATA, @@ -878,52 +1349,131 @@ static void pack_oci_fields(msgpack_packer *packer, } } +static int check_metadata_dot_notation(msgpack_object key, + char **metadata_key) +{ + int meta_key_start, meta_key_len; + + if (key.type != MSGPACK_OBJECT_STR) { + return FLB_FALSE; + } + + if (key.via.str.size <= FLB_OCI_METADATA_KEY_SIZE + 1) { + return FLB_FALSE; + } + + if (memcmp + (key.via.str.ptr, FLB_OCI_METADATA_KEY, + FLB_OCI_METADATA_KEY_SIZE) != 0) { + return FLB_FALSE; + } + + if (key.via.str.ptr[FLB_OCI_METADATA_KEY_SIZE] != '.') { + return FLB_FALSE; + } + meta_key_start = FLB_OCI_METADATA_KEY_SIZE + 1; + meta_key_len = key.via.str.size - meta_key_start; + if (meta_key_len <= 0) { + return FLB_FALSE; + } + + *metadata_key = flb_malloc(meta_key_len + 1); + if (!*metadata_key) { + return FLB_FALSE; + } + + memcpy(*metadata_key, key.via.str.ptr + meta_key_start, meta_key_len); + (*metadata_key)[meta_key_len] = '\0'; + + return FLB_TRUE; +} + static int get_and_pack_oci_fields_from_record(msgpack_packer *packer, msgpack_object map, flb_sds_t *lg_id, flb_sds_t *ls_id, - struct flb_oci_logan *ctx) + struct flb_oci_logan *ctx, + char *tag, int tag_len) { int map_size = map.via.map.size; - int pck_size = 1, i; - msgpack_object *log_group_id= NULL; + int pck_size = 2, i; + msgpack_object *log_group_id = NULL; msgpack_object *log_set_id = NULL; + msgpack_object *entity_id = NULL; msgpack_object *entity_type = NULL; + msgpack_object *log_path = NULL; + msgpack_object *log_source = NULL; msgpack_object *global_metadata = NULL; msgpack_object *metadata = NULL; - for(i = 0; i < map_size; i++) { + msgpack_object *log_timezone = NULL; + + const char *log_path_value = NULL; + int log_path_len = 0; + + char tz_str[256]; + int tz_len; + struct mk_list dot_metadata_list; + struct metadata_obj *dot_meta_obj; + char *dot_meta_key; + int has_dot_metadata = 0; + int total_meta_fields; + struct mk_list *head; + struct mk_list *tmp; + int j; + + mk_list_init(&dot_metadata_list); + + for (i = 0; i < map_size; i++) { if (check_config_from_record(map.via.map.ptr[i].key, FLB_OCI_LOG_GROUP_ID_KEY, - FLB_OCI_LOG_GROUP_ID_KEY_SIZE) == FLB_TRUE) { + FLB_OCI_LOG_GROUP_ID_KEY_SIZE) == + FLB_TRUE) { if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { log_group_id = &map.via.map.ptr[i].val; } continue; } else if (check_config_from_record(map.via.map.ptr[i].key, - FLB_OCI_LOG_SET_ID_KEY, - FLB_OCI_LOG_SET_ID_KEY_SIZE) == FLB_TRUE) { + FLB_OCI_LOG_SET_ID_KEY, + FLB_OCI_LOG_SET_ID_KEY_SIZE) == + FLB_TRUE) { if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { log_set_id = &map.via.map.ptr[i].val; } continue; } + + else if (check_config_from_record(map.via.map.ptr[i].key, + FLB_OCI_LOG_TIMEZONE_KEY, + FLB_OCI_LOG_TIMEZONE_KEY_SIZE) == + FLB_TRUE) { + if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { + log_timezone = &map.via.map.ptr[i].val; + pck_size++; + } + continue; + } + else if (check_config_from_record(map.via.map.ptr[i].key, - FLB_OCI_LOG_ENTITY_ID_KEY, - FLB_OCI_LOG_ENTITY_ID_KEY_SIZE) == FLB_TRUE) { + FLB_OCI_LOG_ENTITY_ID_KEY, + FLB_OCI_LOG_ENTITY_ID_KEY_SIZE) == + FLB_TRUE) { if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { entity_id = &map.via.map.ptr[i].val; pck_size++; } continue; } + + else if (check_config_from_record(map.via.map.ptr[i].key, - FLB_OCI_LOG_ENTITY_TYPE_KEY, - FLB_OCI_LOG_ENTITY_TYPE_KEY_SIZE) == FLB_TRUE) { + FLB_OCI_LOG_ENTITY_TYPE_KEY, + FLB_OCI_LOG_ENTITY_TYPE_KEY_SIZE) == + FLB_TRUE) { if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { entity_type = &map.via.map.ptr[i].val; pck_size++; @@ -931,36 +1481,65 @@ static int get_and_pack_oci_fields_from_record(msgpack_packer *packer, continue; } else if (check_config_from_record(map.via.map.ptr[i].key, - FLB_OCI_LOG_SOURCE_NAME_KEY, - FLB_OCI_LOG_SOURCE_NAME_KEY_SIZE) == FLB_TRUE) { + FLB_OCI_LOG_SOURCE_NAME_KEY, + FLB_OCI_LOG_SOURCE_NAME_KEY_SIZE) == + FLB_TRUE) { if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { log_source = &map.via.map.ptr[i].val; - pck_size++; } continue; } else if (check_config_from_record(map.via.map.ptr[i].key, - FLB_OCI_LOG_PATH_KEY, - FLB_OCI_LOG_PATH_KEY_SIZE) == FLB_TRUE) { + FLB_OCI_LOG_PATH_KEY, + FLB_OCI_LOG_PATH_KEY_SIZE) == + FLB_TRUE) { if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { log_path = &map.via.map.ptr[i].val; pck_size++; } continue; } + else if (check_config_from_record(map.via.map.ptr[i].key, - FLB_OCI_METADATA_KEY, - FLB_OCI_METADATA_KEY_SIZE) == FLB_TRUE) { - if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { + FLB_OCI_METADATA_KEY, + FLB_OCI_METADATA_KEY_SIZE) == + FLB_TRUE) { + if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_MAP) { metadata = &map.via.map.ptr[i].val; - pck_size++; } continue; } - else if (check_config_from_record(map.via.map.ptr[i].key, - FLB_OCI_GLOBAL_METADATA_KEY, - FLB_OCI_GLOBAL_METADATA_KEY_SIZE) == FLB_TRUE) { + else if (check_metadata_dot_notation + (map.via.map.ptr[i].key, &dot_meta_key) == FLB_TRUE) { if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { + dot_meta_obj = flb_malloc(sizeof(struct metadata_obj)); + if (dot_meta_obj) { + dot_meta_obj->key = flb_sds_create(dot_meta_key); + dot_meta_obj->val = + flb_sds_create_len(map.via.map.ptr[i].val.via.str.ptr, + map.via.map.ptr[i].val.via. + str.size); + if (dot_meta_obj->key && dot_meta_obj->val) { + mk_list_add(&dot_meta_obj->_head, &dot_metadata_list); + has_dot_metadata = 1; + } + else { + if (dot_meta_obj->key) + flb_sds_destroy(dot_meta_obj->key); + if (dot_meta_obj->val) + flb_sds_destroy(dot_meta_obj->val); + flb_free(dot_meta_obj); + } + } + } + flb_free(dot_meta_key); + continue; + } + else if (check_config_from_record(map.via.map.ptr[i].key, + FLB_OCI_GLOBAL_METADATA_KEY, + FLB_OCI_GLOBAL_METADATA_KEY_SIZE) == + FLB_TRUE) { + if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_MAP) { global_metadata = &map.via.map.ptr[i].val; } continue; @@ -972,12 +1551,25 @@ static int get_and_pack_oci_fields_from_record(msgpack_packer *packer, "log source name and log group id are required"); return -1; } + + if (log_path) { + log_path_value = log_path->via.str.ptr; + log_path_len = log_path->via.str.size; + } + else if (tag && tag_len > 0) { + log_path_value = tag; + log_path_len = tag_len; + pck_size++; + } + + if (metadata || has_dot_metadata) { + pck_size++; + } if (global_metadata != NULL) { msgpack_pack_map(packer, 2); msgpack_pack_str(packer, FLB_OCI_LOG_METADATA_SIZE); msgpack_pack_str_body(packer, FLB_OCI_LOG_METADATA, FLB_OCI_LOG_METADATA_SIZE); - msgpack_pack_object(packer, *global_metadata); } else { @@ -1005,200 +1597,398 @@ static int get_and_pack_oci_fields_from_record(msgpack_packer *packer, } ] */ - msgpack_pack_str(packer, FLB_OCI_LOG_EVENTS_SIZE); - msgpack_pack_str_body(packer, FLB_OCI_LOG_EVENTS, FLB_OCI_LOG_EVENTS_SIZE); + msgpack_pack_str(packer, FLB_OCI_LOG_EVENTS_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_EVENTS, + FLB_OCI_LOG_EVENTS_SIZE); msgpack_pack_array(packer, 1); - if (metadata != NULL) { - pck_size++; - msgpack_pack_map(packer, pck_size); /* entityType, entityId, logSourceName, logPath, metadata, logRecords */ - msgpack_pack_str(packer, FLB_OCI_LOG_METADATA_SIZE); - msgpack_pack_str_body(packer, FLB_OCI_LOG_METADATA, - FLB_OCI_LOG_METADATA_SIZE); - msgpack_pack_object(packer, *global_metadata); + msgpack_pack_map(packer, pck_size); - } - else { - msgpack_pack_map(packer, pck_size); /* entityType, entityId, logSourceName, logPath, logRecords */ - } + msgpack_pack_str(packer, FLB_OCI_LOG_SOURCE_NAME_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_SOURCE_NAME, + FLB_OCI_LOG_SOURCE_NAME_SIZE); + msgpack_pack_object(packer, *log_source); - /* "entityType:"" */ if (entity_type) { msgpack_pack_str(packer, FLB_OCI_ENTITY_TYPE_SIZE); - msgpack_pack_str_body(packer, FLB_OCI_ENTITY_TYPE, FLB_OCI_ENTITY_TYPE_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_ENTITY_TYPE, + FLB_OCI_ENTITY_TYPE_SIZE); msgpack_pack_object(packer, *entity_type); } - /* "entityId":"", */ - if (entity_type) { + if (entity_id) { msgpack_pack_str(packer, FLB_OCI_ENTITY_ID_SIZE); - msgpack_pack_str_body(packer, FLB_OCI_ENTITY_ID, FLB_OCI_ENTITY_ID_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_ENTITY_ID, + FLB_OCI_ENTITY_ID_SIZE); msgpack_pack_object(packer, *entity_id); } + if (log_path_value && log_path_len > 0) { + msgpack_pack_str(packer, FLB_OCI_LOG_PATH_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_PATH, + FLB_OCI_LOG_PATH_SIZE); + msgpack_pack_str(packer, log_path_len); + msgpack_pack_str_body(packer, log_path_value, log_path_len); + } + if (log_timezone) { + tz_len = log_timezone->via.str.size; + if (tz_len >= sizeof(tz_str)) { + tz_len = sizeof(tz_str) - 1; + } + strncpy(tz_str, log_timezone->via.str.ptr, tz_len); + tz_str[tz_len] = '\0'; + + msgpack_pack_str(packer, FLB_OCI_LOG_TIMEZONE_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_TIMEZONE, + FLB_OCI_LOG_TIMEZONE_SIZE); + + if (!is_valid_timezone(tz_str)) { + flb_plg_warn(ctx->ins, + "Invalid timezone '%s' in record, using UTC", + tz_str); + msgpack_pack_str(packer, 3); + msgpack_pack_str_body(packer, "UTC", 3); + } + else { + msgpack_pack_object(packer, *log_timezone); + } + } - /* "logSourceName":"", */ - msgpack_pack_str(packer, FLB_OCI_LOG_SOURCE_NAME_SIZE); - msgpack_pack_str_body(packer, FLB_OCI_LOG_SOURCE_NAME, - FLB_OCI_LOG_SOURCE_NAME_SIZE); - msgpack_pack_object(packer, *log_source); + if (metadata || has_dot_metadata) { + msgpack_pack_str(packer, FLB_OCI_LOG_METADATA_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_METADATA, + FLB_OCI_LOG_METADATA_SIZE); + total_meta_fields = 0; + if (metadata) { + total_meta_fields += metadata->via.map.size; + } + if (has_dot_metadata) { + total_meta_fields += mk_list_size(&dot_metadata_list); + } - /* "logPath":"" */ - if (log_path) { - msgpack_pack_str(packer, FLB_OCI_LOG_PATH_SIZE); - msgpack_pack_str_body(packer, FLB_OCI_LOG_PATH, FLB_OCI_LOG_PATH_SIZE); - msgpack_pack_object(packer, *log_path); + msgpack_pack_map(packer, total_meta_fields); + + if (metadata) { + for (j = 0; j < metadata->via.map.size; j++) { + msgpack_pack_object(packer, metadata->via.map.ptr[j].key); + msgpack_pack_object(packer, metadata->via.map.ptr[j].val); + } + } + + if (has_dot_metadata) { + mk_list_foreach(head, &dot_metadata_list) { + dot_meta_obj = mk_list_entry(head, struct metadata_obj, _head); + msgpack_pack_str(packer, flb_sds_len(dot_meta_obj->key)); + msgpack_pack_str_body(packer, dot_meta_obj->key, + flb_sds_len(dot_meta_obj->key)); + msgpack_pack_str(packer, flb_sds_len(dot_meta_obj->val)); + msgpack_pack_str_body(packer, dot_meta_obj->val, + flb_sds_len(dot_meta_obj->val)); + } + } } - *lg_id = flb_sds_create_len(log_group_id->via.str.ptr, log_group_id->via.str.size); - if(!*lg_id) { + *lg_id = + flb_sds_create_len(log_group_id->via.str.ptr, + log_group_id->via.str.size); + if (!*lg_id) { return -1; } + if (log_set_id != NULL) { - *ls_id = flb_sds_create_len(log_set_id->via.str.ptr, log_set_id->via.str.size); - if(!*ls_id) { + *ls_id = + flb_sds_create_len(log_set_id->via.str.ptr, + log_set_id->via.str.size); + if (!*ls_id) { return -1; } } + if (has_dot_metadata) { + mk_list_foreach_safe(head, tmp, &dot_metadata_list) { + dot_meta_obj = mk_list_entry(head, struct metadata_obj, _head); + if (dot_meta_obj->key) + flb_sds_destroy(dot_meta_obj->key); + if (dot_meta_obj->val) + flb_sds_destroy(dot_meta_obj->val); + mk_list_del(&dot_meta_obj->_head); + flb_free(dot_meta_obj); + } + } return 0; - } -static int total_flush(struct flb_event_chunk *event_chunk, - struct flb_output_flush *out_flush, - struct flb_input_instance *ins, void *out_context, - struct flb_config *config) +static int send_batch_with_count(struct flb_oci_logan *ctx, + struct flb_event_chunk *event_chunk, + int start_record, int record_count, + flb_sds_t log_group_id, flb_sds_t log_set_id, + struct flb_config *config) { - struct flb_oci_logan *ctx = out_context; - flb_sds_t out_buf = NULL; - int ret = 0, res = FLB_OK, ret1 = 0, i; - msgpack_object map; - int map_size; msgpack_sbuffer mp_sbuf; msgpack_packer mp_pck; - int msg = -1, log = -1; struct flb_log_event_decoder log_decoder; struct flb_log_event log_event; - int num_records; - flb_sds_t log_group_id = NULL; - flb_sds_t log_set_id = NULL; - int count = 0; - - ret = flb_log_event_decoder_init(&log_decoder, (char *) event_chunk->data, event_chunk->size); + flb_sds_t out_buf = NULL; + int ret = 0, current_record = 0; + int msg = -1, log = -1, i; + msgpack_object map; + char *tag; + int tag_len; + int skip; + int packed_records; + int map_size; - if (ret != FLB_EVENT_DECODER_SUCCESS) { - flb_plg_error(ctx->ins, - "Log event decoder initialization error : %d", ret); - res = FLB_ERROR; - goto clean_up; - } + tag = event_chunk->tag; + tag_len = ((event_chunk->tag) ? flb_sds_len(event_chunk->tag) : 0); + flb_plg_debug(ctx->ins, "Processing batch: records %d-%d (%d total)", + start_record, start_record + record_count - 1, + record_count); - /* Create temporary msgpack buffer */ msgpack_sbuffer_init(&mp_sbuf); msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write); - /* pack oci fields */ - /* pack_oci_fields(&mp_pck, ctx); */ + ret = flb_log_event_decoder_init(&log_decoder, (char *) event_chunk->data, + event_chunk->size); + if (ret != FLB_EVENT_DECODER_SUCCESS) { + msgpack_sbuffer_destroy(&mp_sbuf); + return FLB_ERROR; + } - num_records = flb_mp_count(event_chunk->data, event_chunk->size); + if (ctx->oci_config_in_record == FLB_FALSE) { + pack_oci_fields(&mp_pck, ctx, tag, tag_len); + log_group_id = ctx->oci_la_log_group_id; + log_set_id = ctx->oci_la_log_set_id; + } + else { + for (skip = 0; skip < start_record; skip++) { + if (flb_log_event_decoder_next(&log_decoder, &log_event) != + FLB_EVENT_DECODER_SUCCESS) { + flb_log_event_decoder_destroy(&log_decoder); + msgpack_sbuffer_destroy(&mp_sbuf); + return FLB_ERROR; + } + } - while ((ret = flb_log_event_decoder_next( - &log_decoder, - &log_event)) == FLB_EVENT_DECODER_SUCCESS) { - map = *log_event.body; - map_size = map.via.map.size; - if (count < 1) { - if (ctx->oci_config_in_record == FLB_FALSE) { - pack_oci_fields(&mp_pck, ctx); - log_group_id = ctx->oci_la_log_group_id; - log_set_id = ctx->oci_la_log_set_id; - } else { - ret1 = get_and_pack_oci_fields_from_record(&mp_pck, map, &log_group_id, &log_set_id, ctx); - if (ret1 != 0) { - break; - } + if (flb_log_event_decoder_next(&log_decoder, &log_event) == + FLB_EVENT_DECODER_SUCCESS) { + map = *log_event.body; + ret = get_and_pack_oci_fields_from_record(&mp_pck, map, + &log_group_id, + &log_set_id, ctx, tag, + tag_len); + if (ret != 0) { + flb_log_event_decoder_destroy(&log_decoder); + msgpack_sbuffer_destroy(&mp_sbuf); + return FLB_ERROR; } - msgpack_pack_str(&mp_pck, FLB_OCI_LOG_RECORDS_SIZE); - msgpack_pack_str_body(&mp_pck, FLB_OCI_LOG_RECORDS, - FLB_OCI_LOG_RECORDS_SIZE); - msgpack_pack_array(&mp_pck, num_records); - count++; } - for(i = 0; i < map_size; i++) { - if (check_config_from_record(map.via.map.ptr[i].key, - "message", - 7) == FLB_TRUE) { + flb_log_event_decoder_destroy(&log_decoder); + ret = flb_log_event_decoder_init(&log_decoder, + (char *) event_chunk->data, + event_chunk->size); + if (ret != FLB_EVENT_DECODER_SUCCESS) { + msgpack_sbuffer_destroy(&mp_sbuf); + return FLB_ERROR; + } + } + + msgpack_pack_str(&mp_pck, FLB_OCI_LOG_RECORDS_SIZE); + msgpack_pack_str_body(&mp_pck, FLB_OCI_LOG_RECORDS, + FLB_OCI_LOG_RECORDS_SIZE); + msgpack_pack_array(&mp_pck, record_count); + + /* skip to start record */ + current_record = 0; + while (current_record < start_record && + flb_log_event_decoder_next(&log_decoder, + &log_event) == + FLB_EVENT_DECODER_SUCCESS) { + current_record++; + } + + packed_records = 0; + while (packed_records < record_count && + flb_log_event_decoder_next(&log_decoder, + &log_event) == + FLB_EVENT_DECODER_SUCCESS) { + + map = *log_event.body; + map_size = map.via.map.size; + + /* lookupfor message or log field */ + msg = -1; + log = -1; + for (i = 0; i < map_size; i++) { + if (check_config_from_record(map.via.map.ptr[i].key, "message", 7) + == FLB_TRUE) { msg = i; + break; } - if (check_config_from_record(map.via.map.ptr[i].key, - "log", - 3) == FLB_TRUE) { + if (check_config_from_record(map.via.map.ptr[i].key, "log", 3) == + FLB_TRUE) { log = i; + break; } } + + /* pack the record content */ if (log >= 0) { msgpack_pack_str(&mp_pck, map.via.map.ptr[log].val.via.str.size); - msgpack_pack_str_body(&mp_pck, map.via.map.ptr[log].val.via.str.ptr, + msgpack_pack_str_body(&mp_pck, + map.via.map.ptr[log].val.via.str.ptr, map.via.map.ptr[log].val.via.str.size); } else if (msg >= 0) { msgpack_pack_str(&mp_pck, map.via.map.ptr[msg].val.via.str.size); - msgpack_pack_str_body(&mp_pck, map.via.map.ptr[msg].val.via.str.ptr, + msgpack_pack_str_body(&mp_pck, + map.via.map.ptr[msg].val.via.str.ptr, map.via.map.ptr[msg].val.via.str.size); } - log = -1; - msg = -1; - } + else { + msgpack_pack_str(&mp_pck, 0); + msgpack_pack_str_body(&mp_pck, "", 0); + } - if (ret1 != 0) { - res = FLB_ERROR; - msgpack_sbuffer_destroy(&mp_sbuf); - flb_log_event_decoder_destroy(&log_decoder); - goto clean_up; + packed_records++; } + flb_log_event_decoder_destroy(&log_decoder); + out_buf = flb_msgpack_raw_to_json_sds(mp_sbuf.data, mp_sbuf.size, config->json_escape_unicode); + flb_plg_debug(ctx->ins, "out->buf [%s]", out_buf); msgpack_sbuffer_destroy(&mp_sbuf); - flb_log_event_decoder_destroy(&log_decoder); - flb_plg_debug(ctx->ins, "payload=%s", out_buf); - flb_plg_debug(ctx->ins, "lg_id=%s", log_group_id); + if (!out_buf) { + return FLB_ERROR; + } + + flb_plg_debug(ctx->ins, "Batch JSON size: %zu bytes", + flb_sds_len(out_buf)); ret = flush_to_endpoint(ctx, out_buf, log_group_id, log_set_id); - if(ret != FLB_OK) { - res = FLB_RETRY; - goto clean_up; + flb_sds_destroy(out_buf); + + return ret; +} + +inline static size_t estimate_record_json_size(msgpack_object_str *str) +{ + return str->size * 1.5 + 10; /* json encoding and structure */ +} + +static int total_flush(struct flb_event_chunk *event_chunk, + struct flb_output_flush *out_flush, + struct flb_input_instance *ins, void *out_context, + struct flb_config *config) +{ + struct flb_oci_logan *ctx = out_context; + struct flb_log_event_decoder log_decoder; + struct flb_log_event log_event; + int ret = 0, total_records, i; + size_t estimated_total_size = 0; + flb_sds_t log_group_id = NULL; + flb_sds_t log_set_id = NULL; + int batches_needed, records_per_batch, start_record, remaining_records, + batch_size; + msgpack_object map; + + /* intialize decoder for counting record and estimate size */ + ret = flb_log_event_decoder_init(&log_decoder, (char *) event_chunk->data, + event_chunk->size); + if (ret != FLB_EVENT_DECODER_SUCCESS) { + return FLB_ERROR; } - clean_up: - if (out_buf != NULL) { - flb_sds_destroy(out_buf); + total_records = flb_mp_count(event_chunk->data, event_chunk->size); + + while (flb_log_event_decoder_next(&log_decoder, &log_event) == + FLB_EVENT_DECODER_SUCCESS) { + map = *log_event.body; + + for (i = 0; i < map.via.map.size; i++) { + if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { + estimated_total_size += + estimate_record_json_size(&map.via.map.ptr[i].val. + via.str); + } + } } - if (log_group_id != NULL && ctx->oci_config_in_record) { - flb_sds_destroy(log_group_id); + flb_log_event_decoder_destroy(&log_decoder); + + /* add overhead for json structure */ + estimated_total_size += 5000; + + flb_plg_debug(ctx->ins, "Total records: %d, estimated size: %zu bytes", + total_records, estimated_total_size); + + /* check if chunking is needed */ + if (estimated_total_size <= MAX_PAYLOAD_SIZE_BYTES) { + flb_plg_info(ctx->ins, + "Payload fits in single request, no chunking needed"); + return send_batch_with_count(ctx, event_chunk, 0, total_records, + log_group_id, log_set_id, config); + } + + /* calculate batching parameters */ + batches_needed = (estimated_total_size + MAX_PAYLOAD_SIZE_BYTES - + 1) / MAX_PAYLOAD_SIZE_BYTES; + if (batches_needed < 1) { + batches_needed = 1; } - if (log_set_id != NULL && ctx->oci_config_in_record) { - flb_sds_destroy(log_set_id); + + records_per_batch = (total_records + batches_needed - 1) / batches_needed; + if (records_per_batch < 1) { + records_per_batch = 1; } - return res; + + flb_plg_info(ctx->ins, + "Chunking required: %d batches, ~%d records per batch", + batches_needed, records_per_batch); + + /* send data in batches */ + start_record = 0; + remaining_records = total_records; + + while (remaining_records > 0) { + batch_size = (remaining_records > + records_per_batch) ? records_per_batch : + remaining_records; + + ret = send_batch_with_count(ctx, event_chunk, start_record, batch_size, + log_group_id, log_set_id, config); + if (ret != FLB_OK) { + flb_plg_error(ctx->ins, + "Failed to send batch starting at record %d", + start_record); + return ret; + } + + start_record += batch_size; + remaining_records -= batch_size; + + flb_plg_debug(ctx->ins, "Sent batch, remaining records: %d", + remaining_records); + } + + flb_plg_info(ctx->ins, "Successfully sent all %d records in %d batches", + total_records, batches_needed); + + return FLB_OK; } static void cb_oci_logan_flush(struct flb_event_chunk *event_chunk, - struct flb_output_flush *out_flush, - struct flb_input_instance *ins, void *out_context, - struct flb_config *config) + struct flb_output_flush *out_flush, + struct flb_input_instance *ins, + void *out_context, struct flb_config *config) { struct flb_oci_logan *ctx = out_context; int ret = -1; - ret = total_flush(event_chunk, out_flush, - ins, out_context, - config); + ret = total_flush(event_chunk, out_flush, ins, out_context, config); if (ret != FLB_OK) { FLB_OUTPUT_RETURN(ret); } @@ -1219,101 +2009,107 @@ static int cb_oci_logan_exit(void *data, struct flb_config *config) /* Configuration properties map */ static struct flb_config_map config_map[] = { { - FLB_CONFIG_MAP_STR, "config_file_location", "", - 0, FLB_TRUE, offsetof(struct flb_oci_logan, config_file_location), - "Location of the oci config file for user api key signing" - }, + FLB_CONFIG_MAP_BOOL, "dump_payload_file", false, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, dump_payload_file), + "enable dumping payload to local file"}, { - FLB_CONFIG_MAP_STR, "profile_name", "DEFAULT", - 0, FLB_TRUE, offsetof(struct flb_oci_logan, profile_name), - "name of the profile in the config file from which the user configs should be loaded" - }, + FLB_CONFIG_MAP_STR, "payload_file_location", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, payload_files_location), + "location to directory where payload will be dumped"}, { - FLB_CONFIG_MAP_BOOL, "oci_config_in_record", "false", - 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_config_in_record), - "If true, oci_la_* configs will be read from the record" - }, + FLB_CONFIG_MAP_STR, "auth_type", "config_file", + 0, FLB_TRUE, offsetof(struct flb_oci_logan, auth_type), + "Authentication mode : config_file as default or instance_principal"}, { - FLB_CONFIG_MAP_STR, "uri", NULL, - 0, FLB_TRUE, offsetof(struct flb_oci_logan, uri), - "Set the uri for rest api request" - }, + FLB_CONFIG_MAP_STR, "domain_suffix", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, domain_suffix), + "domaine suffix : NULL as default or oci domain"}, { - FLB_CONFIG_MAP_STR, "oci_la_log_group_id", NULL, - 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_log_group_id), - "log group id" - }, + FLB_CONFIG_MAP_STR, "config_file_location", "", + 0, FLB_TRUE, offsetof(struct flb_oci_logan, config_file_location), + "Location of the oci config file for user api key signing"}, { - FLB_CONFIG_MAP_STR, "oci_la_log_set_id", NULL, - 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_log_set_id), - "" - }, + FLB_CONFIG_MAP_STR, "profile_name", "DEFAULT", + 0, FLB_TRUE, offsetof(struct flb_oci_logan, profile_name), + "name of the profile in the config file from which the user configs should be loaded"}, { - FLB_CONFIG_MAP_STR, "oci_la_entity_id", NULL, - 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_entity_id), - "" - }, + FLB_CONFIG_MAP_BOOL, "oci_config_in_record", "false", + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_config_in_record), + "If true, oci_la_* configs will be read from the record"}, { - FLB_CONFIG_MAP_STR, "oci_la_entity_type", NULL, - 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_entity_type), - "" - }, + FLB_CONFIG_MAP_STR, "uri", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, uri), + "Set the uri for rest api request"}, { - FLB_CONFIG_MAP_STR, "oci_la_log_source_name", NULL, - 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_log_source_name), - "" - }, + FLB_CONFIG_MAP_STR, "oci_la_log_group_id", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_log_group_id), + "log group id"}, { - FLB_CONFIG_MAP_STR, "oci_la_log_set_id", NULL, - 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_log_set_id), - "" - }, + FLB_CONFIG_MAP_STR, "oci_la_log_set_id", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_log_set_id), + ""}, { - FLB_CONFIG_MAP_STR, "oci_la_log_path", NULL, - 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_log_path), - "" - }, + FLB_CONFIG_MAP_STR, "oci_la_entity_id", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_entity_id), + ""}, { - FLB_CONFIG_MAP_SLIST_2, "oci_la_global_metadata", NULL, - FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_global_metadata), - "" - }, + FLB_CONFIG_MAP_STR, "oci_la_entity_type", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_entity_type), + ""}, { - FLB_CONFIG_MAP_SLIST_2, "oci_la_metadata", NULL, - FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_metadata), - "" - }, + FLB_CONFIG_MAP_STR, "oci_la_log_source_name", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_log_source_name), + ""}, { - FLB_CONFIG_MAP_STR, "namespace", NULL, - 0, FLB_TRUE, offsetof(struct flb_oci_logan, namespace), - "namespace in your tenancy where the log objects reside" - }, + FLB_CONFIG_MAP_STR, "oci_la_log_set_id", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_log_set_id), + ""}, { - FLB_CONFIG_MAP_STR, "proxy", NULL, - 0, FLB_TRUE, offsetof(struct flb_oci_logan, proxy), - "define proxy if required, in http://host:port format, supports only http protocol" - }, - + FLB_CONFIG_MAP_STR, "oci_la_log_path", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_log_path), + ""}, + { + FLB_CONFIG_MAP_SLIST_2, "oci_la_global_metadata", NULL, + FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct flb_oci_logan, + oci_la_global_metadata), + ""}, + { + FLB_CONFIG_MAP_SLIST_2, "oci_la_metadata", NULL, + FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct flb_oci_logan, + oci_la_metadata), + ""}, + { + FLB_CONFIG_MAP_STR, "namespace", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, namespace), + "namespace in your tenancy where the log objects reside"}, + { + FLB_CONFIG_MAP_STR, "proxy", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, proxy), + "define proxy if required, in http://host:port format, supports only http protocol"}, + { + FLB_CONFIG_MAP_STR, "oci_la_timezone", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_timezone), + "timezone for logs(UTC, GMT, +05:30)"}, {0} }; /* Plugin reference */ struct flb_output_plugin out_oracle_log_analytics_plugin = { - .name = "oracle_log_analytics", - .description = "Oracle log analytics", - .cb_init = cb_oci_logan_init, - .cb_pre_run = NULL, - .cb_flush = cb_oci_logan_flush, - .cb_exit = cb_oci_logan_exit, + .name = "oracle_log_analytics", + .description = "Oracle log analytics", + .cb_init = cb_oci_logan_init, + .cb_pre_run = NULL, + .cb_flush = cb_oci_logan_flush, + .cb_exit = cb_oci_logan_exit, /* Configuration */ - .config_map = config_map, + .config_map = config_map, /* Events supported */ - .event_type = FLB_OUTPUT_LOGS, + .event_type = FLB_OUTPUT_LOGS, /* Plugin flags */ - .flags = FLB_OUTPUT_NET | FLB_IO_OPT_TLS, + .flags = FLB_OUTPUT_NET | FLB_IO_OPT_TLS, .workers = 1, }; diff --git a/plugins/out_oracle_log_analytics/oci_logan.h b/plugins/out_oracle_log_analytics/oci_logan.h index e9edf07c62f..078242a5085 100644 --- a/plugins/out_oracle_log_analytics/oci_logan.h +++ b/plugins/out_oracle_log_analytics/oci_logan.h @@ -78,6 +78,12 @@ #define FLB_OCI_MATCH_PREFIX "oci_match_" #define FLB_OCI_MATCH_PREFIX_SIZE sizeof(FLB_OCI_MATCH_PREFIX)-1 +#define FLB_OCI_LOG_TIMEZONE_KEY "oci_la_timezone" +#define FLB_OCI_LOG_TIMEZONE_KEY_SIZE sizeof(FLB_OCI_LOG_TIMEZONE_KEY) - 1 + +#define FLB_OCI_LOG_TIMEZONE "timezone" +#define FLB_OCI_LOG_TIMEZONE_SIZE sizeof(FLB_OCI_LOG_TIMEZONE) - 1 + #ifdef FLB_HAVE_REGEX #define FLB_OCI_MATCH_REGEX_PREFIX "oci_match_regex_" #define FLB_OCI_MATCH_REGEX_PREFIX_SIZE sizeof(FLB_OCI_MATCH_REGEX_PREFIX)-1 @@ -97,7 +103,7 @@ #define FLB_OCI_PARAM_INCLUDE_COLLECT_TIME "include_collect_time" #define FLB_OCI_PARAM_INCLUDE_COLLECT_TIME_SIZE sizeof(FLB_OCI_PARAM_INCLUDE_COLLECT_TIME)-1 -#define FLB_OCI_MATCH_ID_MAX 1000 // TO avoid too large memory allocation +#define FLB_OCI_MATCH_ID_MAX 1000 // TO avoid too large memory allocation #define FLB_OCI_DEFAULT_COLLECT_TIME "oci_collect_time" #define FLB_OCI_DEFAULT_COLLECT_TIME_SIZE sizeof(FLB_OCI_DEFAULT_COLLECT_TIME)-1 @@ -150,13 +156,53 @@ #define FLB_OCI_ERROR_CODE_TOO_MANY_REQUESTS "TooManyRequests" #define FLB_OCI_ERROR_CODE_INTERNAL_SERVER_ERROR "InternalServerError" +/* for imds request*/ +#define ORACLE_IMDS_HOST "169.254.169.254" +#define ORACLE_IMDS_BASE_URL "/opc/v2" +#define ORACLE_IMDS_REGION_PATH "/instance/region" +#define ORACLE_IMDS_LEAF_CERT_PATH "/identity/cert.pem" +#define ORACLE_IMDS_LEAF_KEY_PATH "/identity/key.pem" +#define ORACLE_IMDS_INTERMEDIATE_CERT_PATH "/identity/intermediate.pem" +#define ORACLE_AUTH_HEADER "Authorization: Bearer Oracle" +#define ORACLE_IMDS_TOKEN_PATH "/opc/v2/instancePrincipal/token" + + +#define COUNT_OF_REGION (sizeof(region_mappings) / sizeof(region_mappings[0]) - 1) + +/* for chunking */ +#define MAX_PAYLOAD_SIZE_BYTES (3800000) // 3.8 mb + #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include - -struct metadata_obj { +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct metadata_obj +{ flb_sds_t key; flb_sds_t val; struct mk_list _head; @@ -165,23 +211,65 @@ struct metadata_obj { struct flb_oci_error_response { - flb_sds_t code; - flb_sds_t message; + flb_sds_t code; + flb_sds_t message; +}; + +struct flb_oracle_imds +{ + flb_sds_t region; + flb_sds_t leaf_cert; + flb_sds_t leaf_key; + flb_sds_t intermediate_cert; + flb_sds_t tenancy_ocid; + flb_sds_t fingerprint; + flb_sds_t session_pubkey; + flb_sds_t session_privkey; + struct flb_upstream *upstream; + struct flb_output_instance *ins; }; -struct flb_oci_logan { +struct oci_security_token +{ + flb_sds_t token; + time_t expires_at; + flb_sds_t session_privkey; +}; + +typedef struct +{ + const char *region; + const char *realm; +} region_realm_mapping_t; + +typedef struct +{ + const char *short_name; + const char *long_name; +} region_mapping_t; + +typedef struct +{ + const char *realm_code; + const char *domain_suffix; +} realm_mapping_t; + +struct flb_oci_logan +{ flb_sds_t namespace; flb_sds_t config_file_location; flb_sds_t profile_name; int oci_config_in_record; flb_sds_t uri; + char *domain_suffix; struct flb_upstream *u; flb_sds_t proxy; char *proxy_host; int proxy_port; // oci_la_* configs + flb_sds_t oci_la_entity_id; flb_sds_t oci_la_entity_type; @@ -194,22 +282,37 @@ struct flb_oci_logan { flb_sds_t oci_la_log_set_id; + flb_sds_t oci_la_timezone; + struct mk_list *oci_la_global_metadata; struct mk_list global_metadata_fields; struct mk_list *oci_la_metadata; struct mk_list log_event_metadata_fields; - // config_file + // config_file flb_sds_t user; flb_sds_t region; flb_sds_t tenancy; flb_sds_t key_fingerprint; flb_sds_t key_file; /* For OCI signing */ - flb_sds_t key_id; // tenancy/user/key_fingerprint + flb_sds_t key_id; // tenancy/user/key_fingerprint flb_sds_t private_key; - struct flb_output_instance *ins; + // instance prinicipal auth + struct flb_oracle_imds imds; + EVP_PKEY *session_key_pair; + struct oci_security_token security_token; + char *auth_type; + + // dump payload + char *payload_files_location; + bool dump_payload_file; }; + +int is_valid_timezone(const char *log_timezone); +const char *get_domain_suffix_for_realm(const char *realm); +const char *determine_realm_from_region(const char *region); +const char *long_region_name(char *short_region_name); #endif diff --git a/plugins/out_oracle_log_analytics/oci_logan_conf.c b/plugins/out_oracle_log_analytics/oci_logan_conf.c index 1e77271431c..8d51d1cb1c9 100644 --- a/plugins/out_oracle_log_analytics/oci_logan_conf.c +++ b/plugins/out_oracle_log_analytics/oci_logan_conf.c @@ -36,6 +36,27 @@ #include "oci_logan.h" #include "oci_logan_conf.h" +#if OPENSSL_VERSION_NUMBER < 0x10100000L + +#define ASN1_STRING_get0_data(x) ASN1_STRING_data(x) + +static inline EVP_MD_CTX *EVP_MD_CTX_new(void) +{ + return EVP_MD_CTX_create(); +} + +static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) +{ + EVP_MD_CTX_destroy(ctx); +} + +#endif + +static int is_test_mode(void); +static flb_sds_t mock_imds_request(struct flb_oci_logan *ctx, + const char *path); +static flb_sds_t mock_federation_response(struct flb_oci_logan *ctx); + static int create_pk_context(flb_sds_t filepath, const char *key_passphrase, struct flb_oci_logan *ctx) { @@ -94,28 +115,67 @@ static int create_pk_context(flb_sds_t filepath, const char *key_passphrase, return 0; } +static char *trim_whitespace(const char *str) +{ + const char *start = str; + const char *end; + char *trimmed; + size_t len; + + if (!str || *str == '\0') { + return NULL; + } + while (*start + && (*start == ' ' || *start == '\t' || *start == '\n' + || *start == '\r')) { + start++; + } + if (*start == '\0') { + return NULL; + } + end = start + strlen(start) - 1; + while (end > start + && (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r')) { + end--; + } + + len = end - start + 1; + trimmed = flb_malloc(len + 1); + if (!trimmed) { + return NULL; + } + + strncpy(trimmed, start, len); + trimmed[len] = '\0'; + + return trimmed; +} + static int load_oci_credentials(struct flb_oci_logan *ctx) { flb_sds_t content; int found_profile = 0, res = 0; char *line, *profile = NULL; int eq_pos = 0; - char* key = NULL; - char* val; + char *key = NULL; + char *val = NULL; + char *orig_key = NULL; content = flb_file_read(ctx->config_file_location); - if (content == NULL || flb_sds_len(content) == 0) - { + if (content == NULL || flb_sds_len(content) == 0) { return -1; } + flb_plg_debug(ctx->ins, "content = %s", content); - line = strtok(content, "\n"); - while(line != NULL) { + line = strtok(content, "\r\n"); + + while (line != NULL) { /* process line */ flb_plg_debug(ctx->ins, "line = %s", line); - if(!found_profile && line[0] == '[') { + + if (!found_profile && line[0] == '[') { profile = mk_string_copy_substr(line, 1, strlen(line) - 1); - if(!strcmp(profile, ctx->profile_name)) { + if (!strcmp(profile, ctx->profile_name)) { flb_plg_info(ctx->ins, "found profile"); found_profile = 1; goto iterate; @@ -123,19 +183,36 @@ static int load_oci_credentials(struct flb_oci_logan *ctx) mk_mem_free(profile); profile = NULL; } - if(found_profile) { - if(line[0] == '[') { + + if (found_profile) { + if (line[0] == '[') { break; } + eq_pos = mk_string_char_search(line, '=', strlen(line)); + if (eq_pos < 0) { + goto iterate; + } + flb_plg_debug(ctx->ins, "eq_pos %d", eq_pos); - key = mk_string_copy_substr(line, 0, eq_pos); - flb_plg_debug(ctx->ins, "key = %s", key); - val = line + eq_pos + 1; + + orig_key = mk_string_copy_substr(line, 0, eq_pos); + flb_plg_debug(ctx->ins, "key = %s", orig_key); + + key = trim_whitespace(orig_key); + mk_mem_free(orig_key); + + val = trim_whitespace(line + eq_pos + 1); + if (!key || !val) { + if (key) + flb_free(key); + if (val) + flb_free(val); res = -1; break; } + if (strcmp(key, FLB_OCI_PARAM_USER) == 0) { ctx->user = flb_sds_create(val); } @@ -151,33 +228,39 @@ static int load_oci_credentials(struct flb_oci_logan *ctx) else if (strcmp(key, FLB_OCI_PARAM_REGION) == 0) { ctx->region = flb_sds_create(val); } - else { - goto iterate; - } + + flb_free(val); + val = NULL; + flb_free(key); + key = NULL; } - iterate: + + iterate: if (profile) { mk_mem_free(profile); profile = NULL; } - if (key) { - mk_mem_free(key); - key = NULL; - } - line = strtok(NULL, "\n"); + + line = strtok(NULL, "\r\n"); } + if (!found_profile) { flb_errno(); res = -1; } flb_sds_destroy(content); + if (profile) { mk_mem_free(profile); } if (key) { - mk_mem_free(key); + flb_free(key); + } + if (val) { + flb_free(val); } + return res; } @@ -194,7 +277,8 @@ static int global_metadata_fields_create(struct flb_oci_logan *ctx) } flb_config_map_foreach(head, mv, ctx->oci_la_global_metadata) { - kname = mk_list_entry_first(mv->val.list, struct flb_slist_entry, _head); + kname = + mk_list_entry_first(mv->val.list, struct flb_slist_entry, _head); val = mk_list_entry_last(mv->val.list, struct flb_slist_entry, _head); f = flb_malloc(sizeof(struct metadata_obj)); @@ -235,7 +319,8 @@ static int log_event_metadata_create(struct flb_oci_logan *ctx) } flb_config_map_foreach(head, mv, ctx->oci_la_metadata) { - kname = mk_list_entry_first(mv->val.list, struct flb_slist_entry, _head); + kname = + mk_list_entry_first(mv->val.list, struct flb_slist_entry, _head); val = mk_list_entry_last(mv->val.list, struct flb_slist_entry, _head); f = flb_malloc(sizeof(struct metadata_obj)); @@ -262,240 +347,1668 @@ static int log_event_metadata_create(struct flb_oci_logan *ctx) return 0; } -struct flb_oci_logan *flb_oci_logan_conf_create(struct flb_output_instance *ins, - struct flb_config *config) { - struct flb_oci_logan *ctx; - struct flb_upstream *upstream; - flb_sds_t host = NULL; - int io_flags = 0, default_port; - const char *tmp; - int ret = 0; - char *protocol = NULL; - char *p_host = NULL; - char *p_port = NULL; - char *p_uri = NULL; - ctx = flb_calloc(1, sizeof(struct flb_oci_logan)); - if (!ctx) { - flb_errno(); +/* Creates and send HTTP request to oracle IMDS endpoint */ +static flb_sds_t make_imds_request(struct flb_oci_logan *ctx, + struct flb_connection *u_conn, + const char *path) +{ + struct flb_http_client *client; + flb_sds_t response = NULL; + size_t b_sent; + int ret; + + if (is_test_mode()) { + if (getenv("TEST_IMDS_SUCCESS")) { + return mock_imds_request(ctx, path); + } + else if (getenv("TEST_IMDS_FAILURE")) { + return NULL; + } + } + + flb_plg_debug(ctx->ins, "path->%s", path); + client = flb_http_client(u_conn, FLB_HTTP_GET, path, NULL, 0, + ORACLE_IMDS_HOST, 80, NULL, 0); + if (!client) { return NULL; } - mk_list_init(&ctx->global_metadata_fields); - mk_list_init(&ctx->log_event_metadata_fields); - ctx->ins = ins; + flb_http_add_header(client, "Authorization", 13, "Bearer Oracle", 13); + ret = flb_http_do(client, &b_sent); + if (ret != 0 || client->resp.status != 200) { + flb_http_client_destroy(client); + return NULL; + } - ret = flb_output_config_map_set(ins, (void *) ctx); - if (ret == -1) { - flb_plg_error(ctx->ins, "configuration error"); - flb_oci_logan_conf_destroy(ctx); + response = flb_sds_create_len(client->resp.data, client->resp.data_len); + flb_http_client_destroy(client); + return response; +} + +/* constructs the complete OCI service hostname */ +static flb_sds_t construct_oci_host(const char *service, + struct flb_oci_logan *ctx) +{ + flb_sds_t region = (ctx->imds.region ? ctx->imds.region : ctx->region); + const char *realm = NULL; + const char *domain_suffix = NULL; + flb_sds_t host; + + if (!service || !region) { return NULL; } - if (ctx->oci_config_in_record == FLB_FALSE) { - if (ctx->oci_la_log_source_name == NULL || - ctx->oci_la_log_group_id == NULL) { - flb_plg_error(ctx->ins, - "log source name and log group id are required"); - flb_oci_logan_conf_destroy(ctx); - return NULL; - } + if (ctx->domain_suffix) { + domain_suffix = ctx->domain_suffix; + goto CONSTRUCT_HOST; } - if (ctx->oci_la_global_metadata != NULL) { - ret = global_metadata_fields_create(ctx); - if (ret != 0) { - flb_errno(); - flb_oci_logan_conf_destroy(ctx); - return NULL; - } + realm = determine_realm_from_region(region); + domain_suffix = get_domain_suffix_for_realm(realm); + + CONSTRUCT_HOST:; + host = flb_sds_create_size(256); + if (!host) { + return NULL; } + flb_sds_snprintf(&host, flb_sds_alloc(host), "%s.%s.oci.%s", + service, region, domain_suffix); + return host; +} - if (ctx->oci_la_metadata != NULL) { - ret = log_event_metadata_create(ctx); - if (ret != 0) { - flb_errno(); - flb_oci_logan_conf_destroy(ctx); - return NULL; - } +/* extracts region information from IMDS HTTP response */ +flb_sds_t extract_region(const char *response) +{ + const char *body_start; + size_t len; + char *region; + const char *long_name; + const char *region_value; + flb_sds_t lregion; + + body_start = strstr(response, "\r\n\r\n"); + if (!body_start) { + return NULL; } - if (!ctx->config_file_location) { - flb_plg_error(ctx->ins, "config file location is required"); - flb_oci_logan_conf_destroy(ctx); + body_start += 4; + + while (*body_start == '\n' || *body_start == '\r' || *body_start == ' ') { + body_start++; + } + + len = strlen(body_start); + while (len > 0 + && (body_start[len - 1] == '\n' || body_start[len - 1] == '\r' + || body_start[len - 1] == ' ')) { + len--; + } + + region = malloc(len + 1); + if (!region) { return NULL; } - ret = load_oci_credentials(ctx); - if(ret != 0) { - flb_errno(); - flb_oci_logan_conf_destroy(ctx); + strncpy(region, body_start, len); + region[len] = '\0'; + long_name = long_region_name(region); + region_value = long_name ? long_name : region; + lregion = flb_sds_create(region_value); + if (!lregion) { + free(region); return NULL; } + return lregion; +} - if (ins->host.name) { - host = ins->host.name; +/* extracts PEM-formatted content from HTTP response */ +char *extract_pem_content(const char *response, const char *begin_marker, + const char *end_marker) +{ + const char *start, *end; + size_t pem_length; + char *pem_content; + + start = strstr(response, begin_marker); + if (!start) { + return NULL; } - else { - if (!ctx->region) { - flb_plg_error(ctx->ins, "Region is required"); - flb_oci_logan_conf_destroy(ctx); - return NULL; - } - host = flb_sds_create_size(512); - flb_sds_snprintf(&host, flb_sds_alloc(host), "loganalytics.%s.oci.oraclecloud.com", ctx->region); + + end = strstr(start, end_marker); + if (!end) { + return NULL; } - if (!ctx->uri) { - if (!ctx->namespace) { - flb_plg_error(ctx->ins, "Namespace is required"); - flb_oci_logan_conf_destroy(ctx); - return NULL; - } - ctx->uri = flb_sds_create_size(512); - flb_sds_snprintf(&ctx->uri, flb_sds_alloc(ctx->uri), - "/20200601/namespaces/%s/actions/uploadLogEventsFile", - ctx->namespace); + end += strlen(end_marker); + + pem_length = end - start; + pem_content = flb_calloc(pem_length + 1, 1); + if (!pem_content) { + return NULL; } + strncpy(pem_content, start, pem_length); + + return pem_content; +} +/* calculates fingerprint sha1 of X.509 certificate */ +flb_sds_t calculate_certificate_fingerprint(struct flb_oci_logan *ctx, + const char *cert_pem) +{ + unsigned char sha1_hash[SHA_DIGEST_LENGTH]; + X509 *cert = NULL; + BIO *bio = NULL; + flb_sds_t fingerprint = NULL; + unsigned char *der_cert; + int der_len, i; + char hex_fingerprint[SHA_DIGEST_LENGTH * 3 + 1]; + char *p; + + if (is_test_mode() && getenv("TEST_IMDS_SUCCESS")) { + return + flb_sds_create + ("AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD"); + } - if (create_pk_context(ctx->key_file, NULL, ctx) < 0) { - flb_plg_error(ctx->ins, "failed to create pk context"); - flb_oci_logan_conf_destroy(ctx); + bio = BIO_new_mem_buf(cert_pem, -1); + if (!bio) { return NULL; } + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (!cert) { + BIO_free(bio); + return NULL; + } - ctx->key_id = flb_sds_create_size(512); - flb_sds_snprintf(&ctx->key_id, flb_sds_alloc(ctx->key_id), - "%s/%s/%s", ctx->tenancy, ctx->user, ctx->key_fingerprint); + der_cert = NULL; + der_len = i2d_X509(cert, &der_cert); + if (der_len <= 0 || !der_cert) { + X509_free(cert); + BIO_free(bio); + return NULL; + } + SHA1(der_cert, der_len, sha1_hash); + OPENSSL_free(der_cert); - /* Check if SSL/TLS is enabled */ - io_flags = FLB_IO_TCP; - default_port = 80; + p = hex_fingerprint; -#ifdef FLB_HAVE_TLS - if (ins->use_tls == FLB_TRUE) { - io_flags = FLB_IO_TLS; - default_port = 443; + for (i = 0; i < SHA_DIGEST_LENGTH; i++) { + p += sprintf(p, "%02x:", sha1_hash[i]); } -#endif - if (ins->host.ipv6 == FLB_TRUE) { - io_flags |= FLB_IO_IPV6; + if (p > hex_fingerprint) { + *(p - 1) = '\0'; } - flb_output_net_default(host, default_port, ins); - flb_sds_destroy(host); + fingerprint = flb_sds_create(hex_fingerprint); - if (ctx->proxy) { - ret = flb_utils_url_split(tmp, &protocol, &p_host, &p_port, &p_uri); - if (ret == -1) { - flb_plg_error(ctx->ins, "could not parse proxy parameter: '%s'", tmp); - flb_oci_logan_conf_destroy(ctx); - return NULL; + for (i = 0; i < flb_sds_len(fingerprint); i++) { + if (islower(fingerprint[i])) { + fingerprint[i] = toupper(fingerprint[i]); } - ctx->proxy_host = p_host; - ctx->proxy_port = atoi(p_port); - flb_free(protocol); - flb_free(p_port); - flb_free(p_uri); - flb_free(p_host); } + X509_free(cert); + BIO_free(bio); - if (ctx->proxy) { - upstream = flb_upstream_create(config, ctx->proxy_host, ctx->proxy_port, - io_flags, ins->tls); - } - else { - /* Prepare an upstream handler */ - upstream = flb_upstream_create(config, ins->host.name, ins->host.port, - io_flags, ins->tls); - } + return fingerprint; +} - if (!upstream) { - flb_plg_error(ctx->ins, "cannot create Upstream context"); - flb_oci_logan_conf_destroy(ctx); - return NULL; +/* extracts tenancy OCID from x509 certificate subject */ +bool extract_tenancy_ocid(struct flb_oci_logan *ctx, const char *cert_pem) +{ + BIO *bio; + X509 *cert; + flb_sds_t tenancy_ocid = NULL; + X509_NAME *subject; + int entry_count, ou_len, i; + X509_NAME_ENTRY *entry; + ASN1_OBJECT *obj; + ASN1_STRING *data; + const unsigned char *ou, *colon; + const char *prefix; + size_t prefix_len, ocid_len; + + if (is_test_mode() && getenv("TEST_IMDS_SUCCESS")) { + ctx->imds.tenancy_ocid = flb_sds_create("ocid1.tenancy.oc1.phx.test"); + return 1; } - ctx->u = upstream; - /* Set instance flags into upstream */ - flb_output_upstream_set(ctx->u, ins); + bio = BIO_new_mem_buf(cert_pem, -1); + if (!bio) { + return 0; + } - return ctx; -} + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!cert) { + return 0; + } -static void metadata_fields_destroy(struct flb_oci_logan *ctx) -{ - struct mk_list *tmp; - struct mk_list *head; - struct metadata_obj *f; + subject = X509_get_subject_name(cert); + if (!subject) { + X509_free(cert); + return 0; + } - mk_list_foreach_safe(head, tmp, &ctx->global_metadata_fields) { - f = mk_list_entry(head, struct metadata_obj, _head); - if (f->key) { - flb_sds_destroy(f->key); - } - if (f->val) { - flb_sds_destroy(f->val); + entry_count = X509_NAME_entry_count(subject); + for (i = 0; i < entry_count; i++) { + entry = X509_NAME_get_entry(subject, i); + obj = X509_NAME_ENTRY_get_object(entry); + if (OBJ_obj2nid(obj) == NID_organizationalUnitName) { + data = X509_NAME_ENTRY_get_data(entry); + ou = ASN1_STRING_get0_data(data); + ou_len = ASN1_STRING_length(data); + prefix = "opc-tenant:ocid1.tenancy"; + prefix_len = strlen(prefix); + + if (ou && ou_len > (int) prefix_len + && memcmp(ou, prefix, prefix_len) == 0) { + colon = memchr(ou, ':', ou_len); + if (colon && (colon + 1) < (ou + ou_len)) { + ocid_len = (ou + ou_len) - (colon + 1); + tenancy_ocid = + flb_sds_create_len((const char *) (colon + 1), + ocid_len); + break; + } + } } - mk_list_del(&f->_head); - flb_free(f); } - mk_list_foreach_safe(head, tmp, &ctx->log_event_metadata_fields) { - f = mk_list_entry(head, struct metadata_obj, _head); - if (f->key) { - flb_sds_destroy(f->key); - } - if (f->val) { - flb_sds_destroy(f->val); - } - mk_list_del(&f->_head); - flb_free(f); + X509_free(cert); + + if (!tenancy_ocid) { + return 0; } + ctx->imds.tenancy_ocid = tenancy_ocid; + return 1; } -int flb_oci_logan_conf_destroy(struct flb_oci_logan *ctx) { - if(ctx == NULL) { - return 0; +/* retrieves region, certificates, and keys from IMDS */ +int get_keys_and_certs(struct flb_oci_logan *ctx, struct flb_config *config) +{ + flb_sds_t region_resp = NULL; + flb_sds_t clean_region_resp = NULL; + flb_sds_t cert_resp = NULL; + flb_sds_t key_resp = NULL; + flb_sds_t int_cert_resp = NULL; + struct flb_connection *u_conn = NULL; + char *clean_cert_resp, *pem_start, *pem_end; + size_t pem_len; + + if (is_test_mode() && getenv("TEST_IMDS_SUCCESS")) { + ctx->imds.region = flb_sds_create("us-phoenix-1"); + ctx->imds.leaf_cert = + flb_sds_create + ("-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----"); + ctx->imds.intermediate_cert = flb_sds_create(""); + ctx->imds.leaf_key = + flb_sds_create + ("-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----"); + ctx->imds.tenancy_ocid = flb_sds_create("ocid1.tenancy.oc1.phx.test"); + ctx->imds.fingerprint = + flb_sds_create + ("AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD"); + return 1; } - if (ctx->private_key) { - flb_sds_destroy(ctx->private_key); + if (!is_test_mode()) { + ctx->u = + flb_upstream_create(config, ORACLE_IMDS_HOST, 80, FLB_IO_TCP, + NULL); + if (!ctx->u) { + flb_plg_error(ctx->ins, + "failed to create imds upstream connection"); + return 0; + } + + u_conn = flb_upstream_conn_get(ctx->u); + if (!u_conn) { + flb_plg_error(ctx->ins, "failed to get imds upstream connection"); + return 0; + } } - if (ctx->uri) { - flb_sds_destroy(ctx->uri); + + region_resp = + make_imds_request(ctx, u_conn, + ORACLE_IMDS_BASE_URL ORACLE_IMDS_REGION_PATH); + if (!region_resp) { + flb_plg_error(ctx->ins, "failed to get region from IMDS"); + goto error; } - if (ctx->key_id) { - flb_sds_destroy(ctx->key_id); + cert_resp = + make_imds_request(ctx, u_conn, + ORACLE_IMDS_BASE_URL ORACLE_IMDS_LEAF_CERT_PATH); + if (!cert_resp) { + flb_plg_error(ctx->ins, "failed to get leaf certificate from IMDS"); + goto error; } - if (ctx->key_file) { - flb_sds_destroy(ctx->key_file); + key_resp = + make_imds_request(ctx, u_conn, + ORACLE_IMDS_BASE_URL ORACLE_IMDS_LEAF_KEY_PATH); + if (!key_resp) { + flb_plg_error(ctx->ins, "failed to get leaf key from IMDS"); + goto error; } - if(ctx->user) { - flb_sds_destroy(ctx->user); + int_cert_resp = + make_imds_request(ctx, u_conn, + ORACLE_IMDS_BASE_URL + ORACLE_IMDS_INTERMEDIATE_CERT_PATH); + if (!int_cert_resp) { + flb_plg_error(ctx->ins, + "failed to get intermediate certificate from IMDS"); + goto error; } - if(ctx->key_fingerprint) { - flb_sds_destroy(ctx->key_fingerprint); + clean_region_resp = extract_region(region_resp); + flb_sds_destroy(region_resp); + + clean_cert_resp = + extract_pem_content(cert_resp, "-----BEGIN CERTIFICATE-----", + "-----END CERTIFICATE-----"); + flb_sds_destroy(cert_resp); + + ctx->imds.region = clean_region_resp; + ctx->imds.leaf_cert = clean_cert_resp; + ctx->imds.intermediate_cert = int_cert_resp; + pem_start = strstr(key_resp, "-----BEGIN"); + pem_end = strstr(key_resp, "-----END"); + if (!pem_start || !pem_end) { + flb_plg_error(ctx->ins, "No valid PEM block found"); + goto error; } - if(ctx->tenancy) { - flb_sds_destroy(ctx->tenancy); + pem_len = + (pem_end - pem_start) + strlen("-----END RSA PRIVATE KEY-----") + 1; + ctx->imds.leaf_key = flb_sds_create_len(pem_start, pem_len); + + /* extract tenancy ocid from leaf certificate */ + if (!extract_tenancy_ocid(ctx, clean_cert_resp)) { + flb_plg_error(ctx->ins, "extract_tenancy_ocid failed"); + goto error; } - if(ctx->region) { - flb_sds_destroy(ctx->region); + + /* calculate certificate fingerprint for signing */ + ctx->imds.fingerprint = + calculate_certificate_fingerprint(ctx, clean_cert_resp); + if (!ctx->imds.fingerprint) { + flb_plg_error(ctx->ins, "calculate_certificate_fingerprint failed"); + goto error; } - if (ctx->u) { + + if (!is_test_mode()) { + flb_upstream_conn_release(u_conn); flb_upstream_destroy(ctx->u); } + ctx->u = NULL; - metadata_fields_destroy(ctx); + return 1; - flb_free(ctx); + error: + if (!is_test_mode()) { + if (region_resp) { + flb_sds_destroy(region_resp); + } + if (cert_resp) { + flb_sds_destroy(cert_resp);; + } + if (key_resp) { + flb_sds_destroy(key_resp); + } + if (int_cert_resp) { + flb_sds_destroy(int_cert_resp); + } + + ctx->imds.fingerprint = NULL; + ctx->imds.intermediate_cert = NULL; + ctx->imds.leaf_cert = NULL; + ctx->imds.leaf_key = NULL; + ctx->imds.region = NULL; + flb_upstream_conn_release(u_conn); + flb_upstream_destroy(ctx->u); + ctx->u = NULL; + } return 0; } + +/* Generates RSA key pair for session based authentication */ +static EVP_PKEY *generate_session_key_pair(struct flb_oci_logan *ctx) +{ +#if OPENSSL_VERSION_NUMBER < 0x30000000L + EVP_PKEY *pkey; + BIGNUM *bn; + RSA *rsa; + int rc; + + pkey = EVP_PKEY_new(); + bn = BN_new(); + rsa = RSA_new(); + BN_set_word(bn, RSA_F4); + rc = RSA_generate_key_ex(rsa, 2048, bn, NULL); + if (rc != 1) { + RSA_free(rsa); + BN_free(bn); + return NULL; + } + + EVP_PKEY_assign_RSA(pkey, rsa); + BN_free(bn); + return pkey; +#else + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *pkey_ctx = NULL; + + pkey_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); + if (!pkey_ctx) { + return NULL; + } + + if (EVP_PKEY_keygen_init(pkey_ctx) <= 0) { + EVP_PKEY_CTX_free(pkey_ctx); + return NULL; + } + + if (EVP_PKEY_CTX_set_rsa_keygen_bits(pkey_ctx, 2048) <= 0) { + EVP_PKEY_CTX_free(pkey_ctx); + return NULL; + } + + if (EVP_PKEY_keygen(pkey_ctx, &pkey) <= 0) { + EVP_PKEY_CTX_free(pkey_ctx); + return NULL; + } + + EVP_PKEY_CTX_free(pkey_ctx); + + return pkey; +#endif +} + + +char *extract_public_key_pem(EVP_PKEY *pkey) +{ + BIO *bio; + char *pem_data, *public_key_pem; + long pem_length; + + bio = BIO_new(BIO_s_mem()); + if (!bio) { + return NULL; + } + if (!PEM_write_bio_PUBKEY(bio, pkey)) { + BIO_free(bio); + return NULL; + } + + + pem_data = NULL; + pem_length = BIO_get_mem_data(bio, &pem_data); + + + public_key_pem = malloc(pem_length + 1); + if (!public_key_pem) { + BIO_free(bio); + return NULL; + } + + strncpy(public_key_pem, pem_data, pem_length); + public_key_pem[pem_length] = '\0'; + + BIO_free(bio); + return public_key_pem; +} + +char *extract_private_key_pem(EVP_PKEY *pkey) +{ + BIO *bio; + char *pem_data, *private_key_pem; + long pem_length; + + bio = BIO_new(BIO_s_mem()); + if (!bio) { + return NULL; + } + + if (!PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL)) { + BIO_free(bio); + return NULL; + } + + + pem_data = NULL; + pem_length = BIO_get_mem_data(bio, &pem_data); + + + private_key_pem = malloc(pem_length + 1); + if (!private_key_pem) { + BIO_free(bio); + return NULL; + } + + strncpy(private_key_pem, pem_data, pem_length); + private_key_pem[pem_length] = '\0'; + + BIO_free(bio); + return private_key_pem; +} + +static flb_sds_t sanitize_certificate(const char *cert_str) +{ + const char *start; + const char *end; + flb_sds_t clean; + size_t j; + size_t i; + + if (!cert_str) + return NULL; + + start = strstr(cert_str, "-----BEGIN"); + if (!start) + return NULL; + + start = strchr(start, '\n'); + if (!start) + return NULL; + start++; + + end = strstr(cert_str, "-----END"); + if (!end || end <= start) + return NULL; + + clean = flb_sds_create_len(start, end - start); + if (!clean) + return NULL; + + j = 0; + i = 0; + for (i = 0; i < flb_sds_len(clean); i++) { + if (!isspace(clean[i])) { + clean[j++] = clean[i]; + } + } + clean[j] = '\0'; + flb_sds_len_set(clean, j); + + return clean; +} + +/* Creates federation payload for OCI security token */ +flb_sds_t create_federation_payload(struct flb_oci_logan *ctx) +{ + flb_sds_t payload = NULL; + flb_sds_t leaf_cert; + flb_sds_t session_pubkey; + flb_sds_t intermediate_certs; + + payload = NULL; + leaf_cert = sanitize_certificate(ctx->imds.leaf_cert); + session_pubkey = sanitize_certificate(ctx->imds.session_pubkey); + intermediate_certs = NULL; + if (ctx->imds.intermediate_cert) { + intermediate_certs = + sanitize_certificate(ctx->imds.intermediate_cert); + } + + payload = flb_sds_create_size(8192); + if (!payload) { + goto cleanup; + } + + if (!leaf_cert || !session_pubkey) { + goto cleanup; + } + if (intermediate_certs && flb_sds_len(intermediate_certs) > 0) { + flb_sds_printf(&payload, + "{\"certificate\":\"%s\",\"publicKey\":\"%s\"," + "\"intermediateCertificates\":[\"%s\"]}", + leaf_cert, session_pubkey, intermediate_certs); + } + else { + flb_sds_printf(&payload, + "{\"certificate\":\"%s\",\"publicKey\":\"%s\"," + "\"intermediateCertificates\":[]}", + leaf_cert, session_pubkey); + } + + + cleanup: + flb_sds_destroy(leaf_cert); + flb_sds_destroy(session_pubkey); + flb_sds_destroy(intermediate_certs); + return payload; +} + +/* sign federation request with leaf key */ +static flb_sds_t sign_request_with_key(struct flb_oci_logan *ctx, + const char *method, + flb_sds_t url_path, + flb_sds_t payload, + flb_sds_t date, const char *host) +{ + flb_sds_t auth_header = NULL; + flb_sds_t string_to_sign = NULL; + flb_sds_t lowercase_method = NULL; + unsigned char *signature = NULL; + unsigned char *b64_out = NULL; + size_t sig_len = 0; + BIO *bio = NULL; + EVP_PKEY *pkey = NULL; + EVP_MD_CTX *md_ctx = NULL; + int i; + unsigned char hash[SHA256_DIGEST_LENGTH]; + char *b64_hash = NULL; + size_t b64_len = 0; + size_t b64_size, olen; + + string_to_sign = flb_sds_create_size(1024); + if (!string_to_sign) { + return NULL; + } + + lowercase_method = flb_sds_create(method); + if (!lowercase_method) { + flb_sds_destroy(string_to_sign); + return NULL; + } + for (i = 0; i < flb_sds_len(lowercase_method); i++) { + lowercase_method[i] = tolower(method[i]); + } + + flb_sds_printf(&string_to_sign, "date: %s\n", date); + flb_sds_printf(&string_to_sign, "(request-target): %s %s\n", + lowercase_method, url_path); + /* flb_sds_printf(&string_to_sign, "host: %s\n", host); */ + flb_sds_printf(&string_to_sign, "content-length: %zu\n", + (payload) ? strlen(payload) : 0); + flb_sds_printf(&string_to_sign, "content-type: application/json\n"); + + SHA256((unsigned char *) payload, (payload) ? flb_sds_len(payload) : 0, + hash); + + b64_len = 4 * ((SHA256_DIGEST_LENGTH + 2) / 3) + 1; + b64_hash = flb_malloc(b64_len); + if (!b64_hash) { + goto cleanup; + } + if (flb_base64_encode + ((unsigned char *) b64_hash, b64_len, &b64_len, hash, + SHA256_DIGEST_LENGTH) != 0) { + flb_free(b64_hash); + goto cleanup; + } + b64_hash[b64_len] = '\0'; + + flb_sds_printf(&string_to_sign, "x-content-sha256: %s", b64_hash); + + if (b64_hash) { + flb_free(b64_hash); + } + flb_plg_debug(ctx->ins, "string to sign: [%s]", string_to_sign); + + bio = BIO_new_mem_buf((void *) ctx->imds.leaf_key, -1); + if (!bio) { + goto cleanup; + } + + pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + if (!pkey) { + goto cleanup; + } + + md_ctx = EVP_MD_CTX_new(); + if (!md_ctx) { + goto cleanup; + } + + if (EVP_DigestSignInit(md_ctx, NULL, EVP_sha256(), NULL, pkey) <= 0) { + goto cleanup; + } + + if (EVP_DigestSignUpdate + (md_ctx, string_to_sign, flb_sds_len(string_to_sign)) <= 0) { + goto cleanup; + } + + if (EVP_DigestSignFinal(md_ctx, NULL, &sig_len) <= 0) { + goto cleanup; + } + + signature = flb_malloc(sig_len); + if (!signature) { + goto cleanup; + } + + if (EVP_DigestSignFinal(md_ctx, signature, &sig_len) <= 0) { + goto cleanup; + } + + b64_size = ((sig_len + 2) / 3) * 4 + 1; + olen = 0; + b64_out = flb_malloc(b64_size); + + if (!b64_out) { + goto cleanup; + } + + if (flb_base64_encode(b64_out, b64_size, &olen, signature, sig_len) != 0) { + goto cleanup; + } + + b64_out[olen] = '\0'; + + auth_header = flb_sds_create_size(2048); + if (!auth_header) { + goto cleanup; + } + + flb_sds_printf(&auth_header, + "Signature version=\"1\",keyId=\"%s/fed-x509/%s\",algorithm=\"rsa-sha256\"," + "signature=\"%s\",headers=\"date (request-target) content-length content-type x-content-sha256\"", + ctx->imds.tenancy_ocid, ctx->imds.fingerprint, b64_out); + + cleanup: + if (bio) { + BIO_free(bio); + } + if (pkey) { + EVP_PKEY_free(pkey); + } + if (md_ctx) { + EVP_MD_CTX_free(md_ctx); + } + if (signature) { + flb_free(signature); + } + if (b64_out) { + flb_free(b64_out); + } + if (string_to_sign) { + flb_sds_destroy(string_to_sign); + } + if (lowercase_method) { + flb_sds_destroy(lowercase_method); + } + flb_plg_debug(ctx->ins, "auth header: %s", auth_header); + + return auth_header; +} + +static flb_sds_t clean_token_string(flb_sds_t input) +{ + size_t len; + size_t read_pos; + size_t write_pos = 0; + + if (!input) + return NULL; + len = flb_sds_len(input); + for (read_pos = 0; read_pos < len; read_pos++) { + if (input[read_pos] >= 32 && input[read_pos] <= 126) { + input[write_pos++] = input[read_pos]; + } + } + + input[write_pos] = '\0'; + flb_sds_len_set(input, write_pos); + + return input; +} + +static int parse_federation_response(flb_sds_t response, + struct oci_security_token *token) +{ + jsmn_parser parser; + jsmntok_t *tokens; + int tok_size = 32; + int ret, i; + char *key; + char *val; + int key_len; + int val_len; + flb_sds_t raw_token; + + if (!response || !token) { + return -1; + } + + jsmn_init(&parser); + + tokens = flb_calloc(1, sizeof(jsmntok_t) * tok_size); + if (!tokens) { + return -1; + } + + ret = + jsmn_parse(&parser, response, flb_sds_len(response), tokens, + tok_size); + + if (ret == JSMN_ERROR_INVAL || ret == JSMN_ERROR_PART) { + flb_free(tokens); + return -1; + } + tok_size = ret; + for (i = 1; i < tok_size; i++) { + if (tokens[i].type != JSMN_STRING) { + continue; + } + + key = response + tokens[i].start; + key_len = tokens[i].end - tokens[i].start; + + if (key_len == 5 && strncmp(key, "token", 5) == 0) { + i++; + if (i >= tok_size || tokens[i].type != JSMN_STRING) { + flb_free(tokens); + return -1; + } + + val = response + tokens[i].start; + val_len = tokens[i].end - tokens[i].start; + + raw_token = flb_sds_create_len(val, val_len); + if (!raw_token) { + flb_free(tokens); + return -1; + } + + if (!clean_token_string(raw_token)) { + flb_sds_destroy(raw_token); + flb_free(tokens); + return -1; + } + + if (token->token) { + flb_sds_destroy(token->token); + } + token->token = raw_token; + + flb_free(tokens); + return 0; + } + } + + flb_free(tokens); + return -1; +} + +/* extract jwt and its expiration time */ +static int decode_jwt_and_set_expires(struct flb_oci_logan *ctx) +{ + jsmn_parser parser; + jsmntok_t *tokens; + int tok_size = 32; + int ret, i, j, padding, exp_len; + char *key, *token, *dot1, *dot2, *payload_b64url, *payload_b64, + *decoded_payload, *exp_str; + int key_len; + size_t payload_b64url_len, b64_len, decoded_len; + time_t exp_value; + char exp_buf[32]; + + if (!ctx || !ctx->security_token.token) { + flb_plg_error(ctx->ins, "Invalid context or token"); + return -1; + } + + token = ctx->security_token.token; + dot1 = strchr(token, '.'); + dot2 = dot1 ? strchr(dot1 + 1, '.') : NULL; + + if (!dot1 || !dot2) { + flb_plg_error(ctx->ins, "Invalid JWT format"); + return -1; + } + + payload_b64url_len = dot2 - (dot1 + 1); + payload_b64url = flb_malloc(payload_b64url_len + 1); + if (!payload_b64url) { + return -1; + } + + memcpy(payload_b64url, dot1 + 1, payload_b64url_len); + payload_b64url[payload_b64url_len] = '\0'; + + for (j = 0; j < payload_b64url_len; j++) { + if (payload_b64url[j] == '-') + payload_b64url[j] = '+'; + else if (payload_b64url[j] == '_') + payload_b64url[j] = '/'; + } + + + padding = (4 - (payload_b64url_len % 4)) % 4; + b64_len = payload_b64url_len + padding; + payload_b64 = flb_malloc(b64_len + 1); + if (!payload_b64) { + flb_free(payload_b64url); + return -1; + } + + strncpy(payload_b64, payload_b64url, payload_b64url_len); + memset(payload_b64 + payload_b64url_len, '=', padding); + payload_b64[b64_len] = '\0'; + + decoded_len = (b64_len * 3) / 4 + 1; + decoded_payload = flb_malloc(decoded_len); + if (!decoded_payload) { + flb_free(payload_b64url); + flb_free(payload_b64); + return -1; + } + + ret = flb_base64_decode((unsigned char *) decoded_payload, decoded_len, + &decoded_len, (unsigned char *) payload_b64, + b64_len); + if (ret != 0) { + flb_plg_error(ctx->ins, "Base64 decode failed"); + flb_free(payload_b64url); + flb_free(payload_b64); + flb_free(decoded_payload); + return -1; + } + + decoded_payload[decoded_len] = '\0'; + + jsmn_init(&parser); + + tokens = flb_calloc(1, sizeof(jsmntok_t) * tok_size); + if (!tokens) { + flb_free(payload_b64url); + flb_free(payload_b64); + flb_free(decoded_payload); + return -1; + } + + ret = jsmn_parse(&parser, decoded_payload, decoded_len, tokens, tok_size); + + if (ret == JSMN_ERROR_INVAL || ret == JSMN_ERROR_PART) { + flb_plg_error(ctx->ins, "JSON parse error"); + flb_free(tokens); + flb_free(payload_b64url); + flb_free(payload_b64); + flb_free(decoded_payload); + return -1; + } + + tok_size = ret; + + /* Find "exp" key */ + exp_value = 0; + for (i = 1; i < tok_size; i++) { + if (tokens[i].type != JSMN_STRING) { + continue; + } + + key = decoded_payload + tokens[i].start; + key_len = tokens[i].end - tokens[i].start; + + if (key_len == 3 && strncmp(key, "exp", 3) == 0) { + i++; + if (i >= tok_size || tokens[i].type != JSMN_PRIMITIVE) { + flb_plg_error(ctx->ins, "Missing or invalid 'exp' in JWT"); + flb_free(tokens); + flb_free(payload_b64url); + flb_free(payload_b64); + flb_free(decoded_payload); + return -1; + } + + /* Extract numeric value */ + exp_str = decoded_payload + tokens[i].start; + exp_len = tokens[i].end - tokens[i].start; + + if (exp_len >= sizeof(exp_buf)) { + exp_len = sizeof(exp_buf) - 1; + } + + strncpy(exp_buf, exp_str, exp_len); + exp_buf[exp_len] = '\0'; + + exp_value = (time_t) atoll(exp_buf); + break; + } + } + + if (exp_value == 0) { + flb_free(tokens); + flb_free(payload_b64url); + flb_free(payload_b64); + flb_free(decoded_payload); + return -1; + } + + ctx->security_token.expires_at = exp_value; + + flb_free(tokens); + flb_free(payload_b64url); + flb_free(payload_b64); + flb_free(decoded_payload); + + return 0; +} + +/* signs and sends federation request to OCI authentication endpoint */ +flb_sds_t sign_and_send_federation_request(struct flb_oci_logan *ctx, + flb_sds_t payload) +{ + struct flb_upstream *upstream; + struct flb_http_client *client; + size_t b_sent, b64_len; + int ret; + struct flb_connection *u_conn; + flb_sds_t resp = NULL; + int port = 443; + flb_sds_t url_path, tmp_host; + flb_sds_t auth_header = NULL; + flb_sds_t date_header = NULL; + char *host; + time_t now; + struct tm *tm_info; + char date_buf[128], user_agent[256]; + unsigned char hash[SHA256_DIGEST_LENGTH]; + char *b64_hash = NULL; + + if (is_test_mode()) { + return mock_federation_response(ctx); + } + url_path = flb_sds_create("/v1/x509"); + tmp_host = construct_oci_host("auth", ctx); + if (!tmp_host) { + flb_sds_destroy(url_path); + return NULL; + } + host = flb_calloc(flb_sds_len(tmp_host) + 1, 1); + if (!host) { + flb_sds_destroy(tmp_host); + flb_sds_destroy(url_path); + return NULL; + } + + strcpy(host, tmp_host); + flb_sds_destroy(tmp_host); + flb_plg_debug(ctx->ins, "host -> %s", host); + time(&now); + tm_info = gmtime(&now); + strftime(date_buf, sizeof(date_buf), "%a, %d %b %Y %H:%M:%S GMT", + tm_info); + date_header = flb_sds_create(date_buf); + + if (!date_header) { + flb_free(host); + flb_sds_destroy(url_path); + return NULL; + } + + upstream = flb_upstream_create(ctx->ins->config, host, port, + FLB_IO_TLS, ctx->ins->tls); + if (!upstream) { + flb_free(host); + flb_sds_destroy(url_path); + return NULL; + } + + u_conn = flb_upstream_conn_get(upstream); + if (!u_conn) { + flb_upstream_destroy(upstream); + flb_free(host); + flb_sds_destroy(url_path); + return NULL; + } + client = flb_http_client(u_conn, FLB_HTTP_POST, url_path, + payload, strlen(payload), host, port, NULL, 0); + + if (!client) { + flb_upstream_conn_release(u_conn); + flb_upstream_destroy(upstream); + flb_free(host); + flb_sds_destroy(url_path); + flb_sds_destroy(date_header); + return NULL; + } + + snprintf(user_agent, sizeof(user_agent), + "fluent-bit-oci-plugin/%s", ctx->ins->p->name); + flb_http_add_header(client, "Date", 4, date_header, + flb_sds_len(date_header)); + flb_http_add_header(client, "Content-Type", 12, "application/json", 16); + flb_http_add_header(client, "Content-Length", 14, NULL, 0); + + SHA256((unsigned char *) payload, flb_sds_len(payload), hash); + + b64_len = 4 * ((SHA256_DIGEST_LENGTH + 2) / 3) + 1; + b64_hash = flb_malloc(b64_len); + if (!b64_hash) { + goto cleanup; + } + if (flb_base64_encode + ((unsigned char *) b64_hash, b64_len, &b64_len, hash, + SHA256_DIGEST_LENGTH) != 0) { + flb_free(b64_hash); + goto cleanup; + } + b64_hash[b64_len] = '\0'; + flb_http_add_header(client, "x-content-sha256", 16, b64_hash, b64_len); + flb_http_add_header(client, "User-Agent", 10, user_agent, + strlen(user_agent)); + /* sign request using the leaf key */ + flb_plg_debug(ctx->ins, "signing with tenancy: %s, fingerprint: %s", + ctx->imds.tenancy_ocid, ctx->imds.fingerprint); + auth_header = sign_request_with_key(ctx, "POST", url_path, + payload, date_header, host); + if (!auth_header) { + flb_plg_error(ctx->ins, "failed to get authorization header"); + goto cleanup; + } + flb_http_add_header(client, "Authorization", 13, + auth_header, flb_sds_len(auth_header)); + ret = flb_http_do(client, &b_sent); + + if (ret != 0 || client->resp.status != 200) { + flb_plg_error(ctx->ins, + "federation request failed with status %d: %s", + client->resp.status, client->resp.payload); + flb_plg_error(ctx->ins, "authentication failed with status %d", + client->resp.status); + + flb_plg_debug(ctx->ins, "request headers:"); + flb_plg_debug(ctx->ins, " Authorization: %s", auth_header); + flb_plg_debug(ctx->ins, " Date: %s", date_header); + flb_plg_debug(ctx->ins, " Content-Type: application/json"); + flb_plg_debug(ctx->ins, " x-content-sha256: %s", b64_hash); + flb_plg_debug(ctx->ins, "request body: %s", payload); + goto cleanup; + } + + if (client->resp.payload && client->resp.payload_size > 0) { + resp = flb_sds_create_len(client->resp.payload, + client->resp.payload_size); + + if (parse_federation_response(resp, &ctx->security_token) < 0) { + flb_plg_error(ctx->ins, "failed to parse federation response"); + flb_sds_destroy(resp); + resp = NULL; + goto cleanup; + } + + decode_jwt_and_set_expires(ctx); + } + cleanup: + if (auth_header) { + flb_sds_destroy(auth_header); + } + flb_sds_destroy(date_header); + flb_sds_destroy(url_path); + flb_free(b64_hash); + flb_free(host); + if (client) { + flb_http_client_destroy(client); + } + flb_upstream_conn_release(u_conn); + flb_upstream_destroy(upstream); + + return resp; +} + +struct flb_oci_logan *flb_oci_logan_conf_create(struct flb_output_instance + *ins, + struct flb_config *config) +{ + struct flb_oci_logan *ctx; + struct flb_upstream *upstream; + flb_sds_t host = NULL; + int io_flags = 0, default_port; + int ret = 0; + char *protocol = NULL; + char *p_host = NULL; + char *p_port = NULL; + char *p_uri = NULL; + flb_sds_t json_payload, response; + + ctx = flb_calloc(1, sizeof(struct flb_oci_logan)); + if (!ctx) { + flb_errno(); + return NULL; + } + mk_list_init(&ctx->global_metadata_fields); + mk_list_init(&ctx->log_event_metadata_fields); + + ctx->ins = ins; + + ret = flb_output_config_map_set(ins, (void *) ctx); + if (ret == -1) { + flb_plg_error(ctx->ins, "configuration error"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + + if (strcmp(ctx->auth_type, "instance_principal") == 0) { + flb_plg_info(ctx->ins, "Using instance principal authentication"); + + if (get_keys_and_certs(ctx, config) != 1) { + flb_plg_error(ctx->ins, "failed to get keys from imds"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + + ctx->session_key_pair = generate_session_key_pair(ctx); + if (!ctx->session_key_pair) { + flb_plg_error(ctx->ins, "failed to generate session keypair"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + + ctx->imds.session_pubkey = extract_public_key_pem(ctx->session_key_pair); + ctx->imds.session_privkey = extract_private_key_pem(ctx->session_key_pair); + + if (!ctx->imds.session_pubkey || !ctx->imds.session_privkey) { + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + ctx->security_token.token = NULL; + ctx->security_token.expires_at = 0; + json_payload = create_federation_payload(ctx); + if (!json_payload) { + flb_plg_error(ctx->ins, "failed to create federation payload"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + + response = sign_and_send_federation_request(ctx, json_payload); + if (!response) { + flb_plg_error(ctx->ins, + "failed to get security token from federation endpoint"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + flb_sds_destroy(json_payload); + flb_sds_destroy(response); + + if (ctx->imds.region) { + ctx->region = flb_sds_create(ctx->imds.region); + } + } + else { + if (!ctx->config_file_location) { + flb_plg_error(ctx->ins, + "config file location i's required for config_file auth mode"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + + ret = load_oci_credentials(ctx); + if (ret != 0) { + flb_errno(); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + + if (create_pk_context(ctx->key_file, NULL, ctx) < 0) { + flb_plg_error(ctx->ins, "failed to create pk context"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + + ctx->key_id = flb_sds_create_size(512); + flb_sds_snprintf(&ctx->key_id, flb_sds_alloc(ctx->key_id), + "%s/%s/%s", ctx->tenancy, ctx->user, + ctx->key_fingerprint); + } + + if (ctx->oci_config_in_record == FLB_FALSE) { + if (ctx->oci_la_log_source_name == NULL || + ctx->oci_la_log_group_id == NULL) { + flb_plg_error(ctx->ins, + "log source name and log group id are required"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + } + + if (ctx->oci_la_global_metadata != NULL) { + ret = global_metadata_fields_create(ctx); + if (ret != 0) { + flb_errno(); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + } + + if (ctx->oci_la_metadata != NULL) { + ret = log_event_metadata_create(ctx); + if (ret != 0) { + flb_errno(); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + } + + /* Setup host and URI */ + if (ins->host.name) { + host = ins->host.name; + } + else { + if (!ctx->region) { + flb_plg_error(ctx->ins, "Region is required"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + host = construct_oci_host("loganalytics", ctx); + if (!host) { + flb_plg_error(ctx->ins, "failed to construct oci host"); + return NULL; + } + } + if (!ctx->uri) { + if (!ctx->namespace) { + flb_plg_error(ctx->ins, "Namespace is required"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + ctx->uri = flb_sds_create_size(512); + flb_sds_snprintf(&ctx->uri, flb_sds_alloc(ctx->uri), + "/20200601/namespaces/%s/actions/uploadLogEventsFile", + ctx->namespace); + } + + /* Check if SSL/TLS is enabled */ +#ifdef FLB_HAVE_TLS + if (ins->use_tls == FLB_TRUE) { + io_flags = FLB_IO_TLS; + default_port = 443; + } + else { + flb_plg_error(ctx->ins, "TLS must be enabled for OCI"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } +#else + flb_plg_error(ctx->ins, "TLS support required for OCI"); + flb_oci_logan_conf_destroy(ctx); + return NULL; +#endif + + if (ins->host.ipv6 == FLB_TRUE) { + io_flags |= FLB_IO_IPV6; + } + + flb_output_net_default(host, default_port, ins); + + if (host != ins->host.name) { + flb_sds_destroy(host); + } + + /* Setup proxy if configured */ + if (ctx->proxy) { + ret = flb_utils_url_split(ctx->proxy, &protocol, &p_host, &p_port, + &p_uri); + if (ret == -1) { + flb_plg_error(ctx->ins, "could not parse proxy parameter: '%s'", + ctx->proxy); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + + ctx->proxy_host = p_host; + ctx->proxy_port = atoi(p_port); + flb_free(protocol); + flb_free(p_port); + flb_free(p_uri); + } + + /* Create upstream connection */ + if (ctx->proxy) { + upstream = + flb_upstream_create(config, ctx->proxy_host, ctx->proxy_port, + io_flags, ins->tls); + } + else { + upstream = flb_upstream_create(config, ins->host.name, ins->host.port, + io_flags, ins->tls); + } + + if (!upstream) { + flb_plg_error(ctx->ins, "cannot create upstream context"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + ctx->u = upstream; + + flb_output_upstream_set(ctx->u, ins); + + return ctx; +} + +static void metadata_fields_destroy(struct flb_oci_logan *ctx) +{ + struct mk_list *tmp; + struct mk_list *head; + struct metadata_obj *f; + + mk_list_foreach_safe(head, tmp, &ctx->global_metadata_fields) { + f = mk_list_entry(head, struct metadata_obj, _head); + if (f->key) { + flb_sds_destroy(f->key); + } + if (f->val) { + flb_sds_destroy(f->val); + } + mk_list_del(&f->_head); + flb_free(f); + } + + mk_list_foreach_safe(head, tmp, &ctx->log_event_metadata_fields) { + f = mk_list_entry(head, struct metadata_obj, _head); + if (f->key) { + flb_sds_destroy(f->key); + } + if (f->val) { + flb_sds_destroy(f->val); + } + mk_list_del(&f->_head); + flb_free(f); + } + +} + +int flb_oci_logan_conf_destroy(struct flb_oci_logan *ctx) +{ + if (ctx == NULL) { + return 0; + } + + if (ctx->imds.region) { + flb_sds_destroy(ctx->imds.region); + } + if (ctx->imds.leaf_key) { + flb_sds_destroy(ctx->imds.leaf_key); + } + if (ctx->imds.intermediate_cert) { + flb_sds_destroy(ctx->imds.intermediate_cert); + } + if (ctx->imds.fingerprint) { + flb_sds_destroy(ctx->imds.fingerprint); + } + if (ctx->imds.tenancy_ocid) { + flb_sds_destroy(ctx->imds.tenancy_ocid); + } + if (ctx->imds.leaf_cert) { + if (is_test_mode()) { + flb_sds_destroy(ctx->imds.leaf_cert); + } + else { + flb_free(ctx->imds.leaf_cert); + } + } + if (ctx->imds.session_pubkey) { + free(ctx->imds.session_pubkey); + } + if (ctx->imds.session_privkey) { + free(ctx->imds.session_privkey); + } + if (ctx->session_key_pair) { + EVP_PKEY_free(ctx->session_key_pair); + } + + if (ctx->security_token.token) { + flb_sds_destroy(ctx->security_token.token); + } + + if (ctx->private_key) { + flb_sds_destroy(ctx->private_key); + } + if (ctx->uri) { + flb_sds_destroy(ctx->uri); + } + if (ctx->key_id) { + flb_sds_destroy(ctx->key_id); + } + if (ctx->key_file) { + flb_sds_destroy(ctx->key_file); + } + if (ctx->user) { + flb_sds_destroy(ctx->user); + } + if (ctx->key_fingerprint) { + flb_sds_destroy(ctx->key_fingerprint); + } + if (ctx->tenancy) { + flb_sds_destroy(ctx->tenancy); + } + if (ctx->region) { + flb_sds_destroy(ctx->region); + } + if (ctx->u) { + flb_upstream_destroy(ctx->u); + } + if (ctx->oci_la_timezone) { + flb_sds_destroy(ctx->oci_la_timezone); + } + + metadata_fields_destroy(ctx); + + flb_free(ctx); + return 0; +} + +static int is_test_mode(void) +{ + return getenv("FLB_OCI_PLUGIN_UNDER_TEST") != NULL; +} + +static flb_sds_t mock_imds_request(struct flb_oci_logan *ctx, + const char *path) +{ + if (strstr(path, "/instance/region")) { + return flb_sds_create("us-phoenix-1"); + } + + else if (strstr(path, "/identity/cert.pem")) { + return flb_sds_create("-----BEGIN CERTIFICATE-----\n" + "MIIC0TCCAbmgAwIBAgIJAKxHjMcXpyEUMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV\n" + "BAoMCW9yYWNsZS5jb20wHhcNMjMwMTAxMDAwMDAwWhcNMjQwMTAxMDAwMDAwWjBc\n" + "MRIwEAYDVQQKDAlvcmFjbGUuY29tMUYwRAYDVQQLDD1vcGMtdGVuYW50Om9jaWQx\n" + "LnRlbmFuY3kub2MxLnBoeC50ZXN0YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\n" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy8Dbv8prpJ/0kKhlGeJY\n" + "ozo2t60EG8L0561g13R29LvMR5hyvGZlGJpmn65+A4xHXWUId1eJsCMFtgKhXFMS\n" + "p6/8RkLcYMrFoAWKpILYdSrvJ0R66u+zR1EpqQvk8TDrNMVzfv/jDPPG2BHYkp7R\n" + "WE7pWQv8vZGnU6p3SJGvTwKdgnjGjNvCsXI8Dx7ePLxLZhX0Vg8bqXFfVVN3FlWK\n" + "VfPy4jLQfQhWVx7dL1EfJL2YiEXI1Oj2DQKLVxPHHcNRVJKXhUHJ2F6PVYqMfAJ9\n" + "bJnTHhOGZfYWO7pQQQv2eFaInp6s6LfDZ/P9l5T7PiNJvWNGnJZpVQqEXdqTxXrC\n" + "MQIDAQABoyMwITAfBgNVHSMEGDAWgBQxFw2xL6XqYqJSKhyAC/8qBkRCLTANBgkq\n" + "hkiG9w0BAQsFAAOCAQEAr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxe\n" + "nIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQ\n" + "-----END CERTIFICATE-----"); + } + + + else if (strstr(path, "/identity/key.pem")) { + return flb_sds_create("-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEpAIBAAKCAQEAr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQ\n" + "r3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3Ee\n" + "MxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQQIDAQAB\n" + "AoIBAEr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenI\n" + "RQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3\n" + "-----END RSA PRIVATE KEY-----"); + } + + else if (strstr(path, "/identity/intermediate.pem")) { + return flb_sds_create("-----BEGIN CERTIFICATE-----\n" + "MIIDHTCCAgWgAwIBAgIJAKxHjMcXpyE1MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV\n" + "BAoMCW9yYWNsZS5jb20wHhcNMjMwMTAxMDAwMDAwWhcNMjQwMTAxMDAwMDAwWjAU\n" + "MRIwEAYDVQQKDAlvcmFjbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" + "CgKCAQEAr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxe\n" + "nIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQr3EeMxxenIRQ\n" + "QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCvMR4zHF6chFCvMR4zHF6chFCvMR4z\n" + "-----END CERTIFICATE-----"); + } + return NULL; +} + +static flb_sds_t mock_federation_response(struct flb_oci_logan *ctx) +{ + const char *header; + time_t now; + time_t exp; + char payload_json[512]; + unsigned char b64_payload[1024]; + size_t b64_len; + int i; + const char *signature; + flb_sds_t jwt; + flb_sds_t response; + + header = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"; + now = time(NULL); + exp = now + 3600; + snprintf(payload_json, sizeof(payload_json), + "{\"sub\":\"ocid1.instance.oc1.phx.test\"," + "\"opc-instance\":\"ocid1.instance.oc1.phx.test\"," + "\"exp\":%ld," + "\"iat\":%ld," "\"jti\":\"test-token-id\"}", exp, now); + + + b64_len = sizeof(b64_payload); + flb_base64_encode(b64_payload, sizeof(b64_payload), &b64_len, + (unsigned char *) payload_json, strlen(payload_json)); + b64_payload[b64_len] = '\0'; + + for (i = 0; i < b64_len; i++) { + if (b64_payload[i] == '+') + b64_payload[i] = '-'; + if (b64_payload[i] == '/') + b64_payload[i] = '_'; + if (b64_payload[i] == '=') { + b64_payload[i] = '\0'; + break; + } + } + + signature = "ths_signature_is_for_test"; + + jwt = flb_sds_create_size(1024); + flb_sds_printf(&jwt, "%s.%s.%s", header, b64_payload, signature); + + response = flb_sds_create_size(2048); + flb_sds_printf(&response, "{\"token\":\"%s\"}", jwt); + + flb_sds_destroy(jwt); + + flb_plg_info(ctx->ins, "[mock]created federation response"); + + if (parse_federation_response(response, &ctx->security_token) < 0) { + flb_plg_error(ctx->ins, "failed to parse mock federation response"); + flb_sds_destroy(response); + return NULL; + } + + ctx->security_token.expires_at = exp; + + flb_plg_info(ctx->ins, "security token expir e in %ld", exp); + + return response; +} diff --git a/plugins/out_oracle_log_analytics/oci_logan_conf.h b/plugins/out_oracle_log_analytics/oci_logan_conf.h index 30a58543bce..8d74e399ec7 100644 --- a/plugins/out_oracle_log_analytics/oci_logan_conf.h +++ b/plugins/out_oracle_log_analytics/oci_logan_conf.h @@ -27,8 +27,12 @@ #include "oci_logan.h" -struct flb_oci_logan *flb_oci_logan_conf_create(struct flb_output_instance *ins, - struct flb_config *config); +struct flb_oci_logan *flb_oci_logan_conf_create(struct flb_output_instance + *ins, + struct flb_config *config); int flb_oci_logan_conf_destroy(struct flb_oci_logan *ctx); +flb_sds_t create_federation_payload(struct flb_oci_logan *ctx); +flb_sds_t sign_and_send_federation_request(struct flb_oci_logan *ctx, + flb_sds_t payload); #endif diff --git a/plugins/out_oracle_log_analytics/oci_logan_helper.c b/plugins/out_oracle_log_analytics/oci_logan_helper.c new file mode 100644 index 00000000000..82ce1dccff8 --- /dev/null +++ b/plugins/out_oracle_log_analytics/oci_logan_helper.c @@ -0,0 +1,580 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2024 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 "oci_logan.h" +#include "oci_logan_conf.h" + +static const region_mapping_t region_mappings[] = { + {"yny", "ap-chuncheon-1"}, + {"hyd", "ap-hyderabad-1"}, + {"mel", "ap-melbourne-1"}, + {"bom", "ap-mumbai-1"}, + {"kix", "ap-osaka-1"}, + {"icn", "ap-seoul-1"}, + {"syd", "ap-sydney-1"}, + {"nrt", "ap-tokyo-1"}, + {"yul", "ca-montreal-1"}, + {"yyz", "ca-toronto-1"}, + {"ams", "eu-amsterdam-1"}, + {"fra", "eu-frankfurt-1"}, + {"zrh", "eu-zurich-1"}, + {"jed", "me-jeddah-1"}, + {"dxb", "me-dubai-1"}, + {"gru", "sa-saopaulo-1"}, + {"cwl", "uk-cardiff-1"}, + {"lhr", "uk-london-1"}, + {"iad", "us-ashburn-1"}, + {"phx", "us-phoenix-1"}, + {"sjc", "us-sanjose-1"}, + {"vcp", "sa-vinhedo-1"}, + {"scl", "sa-santiago-1"}, + {"mtz", "il-jerusalem-1"}, + {"mrs", "eu-marseille-1"}, + {"sin", "ap-singapore-1"}, + {"auh", "me-abudhabi-1"}, + {"lin", "eu-milan-1"}, + {"arn", "eu-stockholm-1"}, + {"jnb", "af-johannesburg-1"}, + {"cdg", "eu-paris-1"}, + {"qro", "mx-queretaro-1"}, + {"mad", "eu-madrid-1"}, + {"ord", "us-chicago-1"}, + {"mty", "mx-monterrey-1"}, + {"aga", "us-saltlake-2"}, + {"bog", "sa-bogota-1"}, + {"vap", "sa-valparaiso-1"}, + {"xsp", "ap-singapore-2"}, + {"ruh", "me-riyadh-1"}, + {"lfi", "us-langley-1"}, + {"luf", "us-luke-1"}, + {"ric", "us-gov-ashburn-1"}, + {"pia", "us-gov-chicago-1"}, + {"tus", "us-gov-phoenix-1"}, + {"ltn", "uk-gov-london-1"}, + {"brs", "uk-gov-cardiff-1"}, + {"nja", "ap-chiyoda-1"}, + {"ukb", "ap-ibaraki-1"}, + {"mct", "me-dcc-muscat-1"}, + {"wga", "ap-dcc-canberra-1"}, + {"bgy", "eu-dcc-milan-1"}, + {"mxp", "eu-dcc-milan-2"}, + {"snn", "eu-dcc-dublin-2"}, + {"dtm", "eu-dcc-rating-2"}, + {"dus", "eu-dcc-rating-1"}, + {"ork", "eu-dcc-dublin-1"}, + {"dac", "ap-dcc-gazipur-1"}, + {"vll", "eu-madrid-2"}, + {"str", "eu-frankfurt-2"}, + {"beg", "eu-jovanovac-1"}, + {"doh", "me-dcc-doha-1"}, + {"ebb", "us-somerset-1"}, + {"ebl", "us-thames-1"}, + {"avz", "eu-dcc-zurich-1"}, + {"avf", "eu-crissier-1"}, + {"ahu", "me-abudhabi-3"}, + {"rba", "me-alain-1"}, + {"rkt", "me-abudhabi-2"}, + {"shj", "me-abudhabi-4"}, + {"dtz", "ap-seoul-2"}, + {"dln", "ap-suwon-1"}, + {"bno", "ap-chuncheon-2"}, + {NULL, NULL} +}; + +static const realm_mapping_t realm_mappings[] = { + {"oc1", "oraclecloud.com"}, + {"oc2", "oraclegovcloud.com"}, + {"oc3", "oraclegovcloud.com"}, + {"oc4", "oraclegovcloud.uk"}, + {"oc8", "oraclecloud8.com"}, + {"oc9", "oraclecloud9.com"}, + {"oc10", "oraclecloud10.com"}, + {"oc14", "oraclecloud14.com"}, + {"oc15", "oraclecloud15.com"}, + {"oc19", "oraclecloud.eu"}, + {"oc20", "oraclecloud20.com"}, + {"oc21", "oraclecloud21.com"}, + {"oc23", "oraclecloud23.com"}, + {"oc24", "oraclecloud24.com"}, + {"oc26", "oraclecloud26.com"}, + {"oc29", "oraclecloud29.com"}, + {"oc35", "oraclecloud35.com"}, + {NULL, NULL} +}; + +/* + ref--> github.com/oracle/oci-python-sdk/blob/ba91eb1a51b0c1a38603dec0373a33f9b9962f8a/src/oci/regions_definitions.py + still it have to be updated depending on new oraclecloudXX +*/ +static const region_realm_mapping_t region_realm_mappings[] = { + {"ap-chuncheon-1", "oc1"}, + {"ap-hyderabad-1", "oc1"}, + {"ap-melbourne-1", "oc1"}, + {"ap-mumbai-1", "oc1"}, + {"ap-osaka-1", "oc1"}, + {"ap-seoul-1", "oc1"}, + {"ap-sydney-1", "oc1"}, + {"ap-tokyo-1", "oc1"}, + {"ca-montreal-1", "oc1"}, + {"ca-toronto-1", "oc1"}, + {"eu-amsterdam-1", "oc1"}, + {"eu-frankfurt-1", "oc1"}, + {"eu-zurich-1", "oc1"}, + {"me-jeddah-1", "oc1"}, + {"me-dubai-1", "oc1"}, + {"sa-saopaulo-1", "oc1"}, + {"uk-cardiff-1", "oc1"}, + {"uk-london-1", "oc1"}, + {"us-ashburn-1", "oc1"}, + {"us-phoenix-1", "oc1"}, + {"us-sanjose-1", "oc1"}, + {"sa-vinhedo-1", "oc1"}, + {"sa-santiago-1", "oc1"}, + {"il-jerusalem-1", "oc1"}, + {"eu-marseille-1", "oc1"}, + {"ap-singapore-1", "oc1"}, + {"me-abudhabi-1", "oc1"}, + {"eu-milan-1", "oc1"}, + {"eu-stockholm-1", "oc1"}, + {"af-johannesburg-1", "oc1"}, + {"eu-paris-1", "oc1"}, + {"mx-queretaro-1", "oc1"}, + {"eu-madrid-1", "oc1"}, + {"us-chicago-1", "oc1"}, + {"mx-monterrey-1", "oc1"}, + {"us-saltlake-2", "oc1"}, + {"sa-bogota-1", "oc1"}, + {"sa-valparaiso-1", "oc1"}, + {"ap-singapore-2", "oc1"}, + {"me-riyadh-1", "oc1"}, + {"us-langley-1", "oc2"}, + {"us-luke-1", "oc2"}, + {"us-gov-ashburn-1", "oc3"}, + {"us-gov-chicago-1", "oc3"}, + {"us-gov-phoenix-1", "oc3"}, + {"uk-gov-london-1", "oc4"}, + {"uk-gov-cardiff-1", "oc4"}, + {"ap-chiyoda-1", "oc8"}, + {"ap-ibaraki-1", "oc8"}, + {"me-dcc-muscat-1", "oc9"}, + {"ap-dcc-canberra-1", "oc10"}, + {"eu-dcc-milan-1", "oc14"}, + {"eu-dcc-milan-2", "oc14"}, + {"eu-dcc-dublin-2", "oc14"}, + {"eu-dcc-rating-2", "oc14"}, + {"eu-dcc-rating-1", "oc14"}, + {"eu-dcc-dublin-1", "oc14"}, + {"ap-dcc-gazipur-1", "oc15"}, + {"eu-madrid-2", "oc19"}, + {"eu-frankfurt-2", "oc19"}, + {"eu-jovanovac-1", "oc20"}, + {"me-dcc-doha-1", "oc21"}, + {"us-somerset-1", "oc23"}, + {"us-thames-1", "oc23"}, + {"eu-dcc-zurich-1", "oc24"}, + {"eu-crissier-1", "oc24"}, + {"me-abudhabi-3", "oc26"}, + {"me-alain-1", "oc26"}, + {"me-abudhabi-2", "oc29"}, + {"me-abudhabi-4", "oc29"}, + {"ap-seoul-2", "oc35"}, + {"ap-suwon-1", "oc35"}, + {"ap-chuncheon-2", "oc35"}, + {NULL, NULL} +}; + + +static struct flb_hash_table *oci_timezone_hash = NULL; + +static const char *oci_supported_timezones[] = { + "africa/abidjan", "africa/accra", "africa/addis_ababa", "africa/algiers", + "africa/asmara", "africa/asmera", "africa/bamako", "africa/bangui", + "africa/banjul", "africa/bissau", "africa/blantyre", "africa/brazzaville", + "africa/bujumbura", "africa/cairo", "africa/casablanca", "africa/ceuta", + "africa/conakry", "africa/dakar", "africa/dar_es_salaam", + "africa/djibouti", + "africa/douala", "africa/el_aaiun", "africa/freetown", "africa/gaborone", + "africa/harare", "africa/johannesburg", "africa/juba", "africa/kampala", + "africa/khartoum", "africa/kigali", "africa/kinshasa", "africa/lagos", + "africa/libreville", "africa/lome", "africa/luanda", "africa/lubumbashi", + "africa/lusaka", "africa/malabo", "africa/maputo", "africa/maseru", + "africa/mbabane", "africa/mogadishu", "africa/monrovia", "africa/nairobi", + "africa/ndjamena", "africa/niamey", "africa/nouakchott", + "africa/ouagadougou", + "africa/porto-novo", "africa/sao_tome", "africa/timbuktu", + "africa/tripoli", + "africa/tunis", "africa/windhoek", "america/adak", "america/anchorage", + "america/anguilla", "america/antigua", "america/araguaina", + "america/argentina/buenos_aires", + "america/argentina/catamarca", "america/argentina/comodrivadavia", + "america/argentina/cordoba", + "america/argentina/jujuy", "america/argentina/la_rioja", + "america/argentina/mendoza", + "america/argentina/rio_gallegos", "america/argentina/salta", + "america/argentina/san_juan", + "america/argentina/san_luis", "america/argentina/tucuman", + "america/argentina/ushuaia", + "america/aruba", "america/asuncion", "america/atikokan", "america/atka", + "america/bahia", "america/bahia_banderas", "america/barbados", + "america/belem", + "america/belize", "america/blanc-sablon", "america/boa_vista", + "america/bogota", + "america/boise", "america/buenos_aires", "america/cambridge_bay", + "america/campo_grande", + "america/cancun", "america/caracas", "america/catamarca", + "america/cayenne", + "america/cayman", "america/chicago", "america/chihuahua", + "america/coral_harbour", + "america/cordoba", "america/costa_rica", "america/creston", + "america/cuiaba", + "america/curacao", "america/danmarkshavn", "america/dawson", + "america/dawson_creek", + "america/denver", "america/detroit", "america/dominica", + "america/edmonton", + "america/eirunepe", "america/el_salvador", "america/ensenada", + "america/fort_wayne", + "america/fortaleza", "america/glace_bay", "america/godthab", + "america/goose_bay", + "america/grand_turk", "america/grenada", "america/guadeloupe", + "america/guatemala", + "america/guayaquil", "america/guyana", "america/halifax", + "america/havana", + "america/hermosillo", "america/indiana/indianapolis", + "america/indiana/knox", + "america/indiana/marengo", "america/indiana/petersburg", + "america/indiana/tell_city", + "america/indiana/vevay", "america/indiana/vincennes", + "america/indiana/winamac", + "america/indianapolis", "america/inuvik", "america/iqaluit", + "america/jamaica", + "america/jujuy", "america/juneau", "america/kentucky/louisville", + "america/kentucky/monticello", + "america/knox_in", "america/kralendijk", "america/la_paz", "america/lima", + "america/los_angeles", "america/louisville", "america/lower_princes", + "america/maceio", + "america/managua", "america/manaus", "america/marigot", + "america/martinique", + "america/matamoros", "america/mazatlan", "america/mendoza", + "america/menominee", + "america/merida", "america/metlakatla", "america/mexico_city", + "america/miquelon", + "america/moncton", "america/monterrey", "america/montevideo", + "america/montreal", + "america/montserrat", "america/nassau", "america/new_york", + "america/nipigon", + "america/nome", "america/noronha", "america/north_dakota/beulah", + "america/north_dakota/center", + "america/north_dakota/new_salem", "america/ojinaga", "america/panama", + "america/pangnirtung", + "america/paramaribo", "america/phoenix", "america/port-au-prince", + "america/port_of_spain", + "america/porto_acre", "america/porto_velho", "america/puerto_rico", + "america/rainy_river", + "america/rankin_inlet", "america/recife", "america/regina", + "america/resolute", + "america/rio_branco", "america/rosario", "america/santa_isabel", + "america/santarem", + "america/santiago", "america/santo_domingo", "america/sao_paulo", + "america/scoresbysund", + "america/shiprock", "america/sitka", "america/st_barthelemy", + "america/st_johns", + "america/st_kitts", "america/st_lucia", "america/st_thomas", + "america/st_vincent", + "america/swift_current", "america/tegucigalpa", "america/thule", + "america/thunder_bay", + "america/tijuana", "america/toronto", "america/tortola", + "america/vancouver", + "america/virgin", "america/whitehorse", "america/winnipeg", + "america/yakutat", + "america/yellowknife", "antarctica/casey", "antarctica/davis", + "antarctica/dumontdurville", + "antarctica/macquarie", "antarctica/mawson", "antarctica/mcmurdo", + "antarctica/palmer", + "antarctica/rothera", "antarctica/south_pole", "antarctica/syowa", + "antarctica/troll", + "antarctica/vostok", "arctic/longyearbyen", "asia/aden", "asia/almaty", + "asia/amman", "asia/anadyr", "asia/aqtau", "asia/aqtobe", "asia/ashgabat", + "asia/ashkhabad", "asia/baghdad", "asia/bahrain", "asia/baku", + "asia/bangkok", + "asia/beirut", "asia/bishkek", "asia/brunei", "asia/calcutta", + "asia/chita", + "asia/choibalsan", "asia/chongqing", "asia/chungking", "asia/colombo", + "asia/dacca", + "asia/damascus", "asia/dhaka", "asia/dili", "asia/dubai", "asia/dushanbe", + "asia/gaza", "asia/harbin", "asia/hebron", "asia/ho_chi_minh", + "asia/hong_kong", + "asia/hovd", "asia/irkutsk", "asia/istanbul", "asia/jakarta", + "asia/jayapura", + "asia/jerusalem", "asia/kabul", "asia/kamchatka", "asia/karachi", + "asia/kashgar", + "asia/kathmandu", "asia/katmandu", "asia/khandyga", "asia/kolkata", + "asia/krasnoyarsk", + "asia/kuala_lumpur", "asia/kuching", "asia/kuwait", "asia/macao", + "asia/macau", + "asia/magadan", "asia/makassar", "asia/manila", "asia/muscat", + "asia/nicosia", + "asia/novokuznetsk", "asia/novosibirsk", "asia/omsk", "asia/oral", + "asia/phnom_penh", + "asia/pontianak", "asia/pyongyang", "asia/qatar", "asia/qyzylorda", + "asia/rangoon", + "asia/riyadh", "asia/riyadh87", "asia/riyadh88", "asia/riyadh89", + "asia/saigon", + "asia/sakhalin", "asia/samarkand", "asia/seoul", "asia/shanghai", + "asia/singapore", + "asia/srednekolymsk", "asia/taipei", "asia/tashkent", "asia/tbilisi", + "asia/tehran", + "asia/tel_aviv", "asia/thimbu", "asia/thimphu", "asia/tokyo", + "asia/ujung_pandang", + "asia/ulaanbaatar", "asia/ulan_bator", "asia/urumqi", "asia/ust-nera", + "asia/vientiane", + "asia/vladivostok", "asia/yakutsk", "asia/yekaterinburg", "asia/yerevan", + "atlantic/azores", + "atlantic/bermuda", "atlantic/canary", "atlantic/cape_verde", + "atlantic/faeroe", + "atlantic/faroe", "atlantic/jan_mayen", "atlantic/madeira", + "atlantic/reykjavik", + "atlantic/south_georgia", "atlantic/st_helena", "atlantic/stanley", + "australia/act", + "australia/adelaide", "australia/brisbane", "australia/broken_hill", + "australia/canberra", + "australia/currie", "australia/darwin", "australia/eucla", + "australia/hobart", + "australia/lhi", "australia/lindeman", "australia/lord_howe", + "australia/melbourne", + "australia/north", "australia/nsw", "australia/perth", + "australia/queensland", + "australia/south", "australia/sydney", "australia/tasmania", + "australia/victoria", + "australia/west", "australia/yancowinna", "brazil/acre", + "brazil/denoronha", + "brazil/east", "brazil/west", "canada/atlantic", "canada/central", + "canada/east-saskatchewan", + "canada/eastern", "canada/mountain", "canada/newfoundland", + "canada/pacific", + "canada/saskatchewan", "canada/yukon", "cet", "chile/continental", + "chile/easterisland", + "cst6cdt", "cuba", "eet", "egypt", "eire", "est", "est5edt", "etc/gmt", + "etc/gmt0", "etc/greenwich", "etc/uct", "etc/universal", "etc/utc", + "etc/zulu", + "europe/amsterdam", "europe/andorra", "europe/athens", "europe/belfast", + "europe/belgrade", + "europe/berlin", "europe/bratislava", "europe/brussels", + "europe/bucharest", + "europe/budapest", "europe/busingen", "europe/chisinau", + "europe/copenhagen", + "europe/dublin", "europe/gibraltar", "europe/guernsey", "europe/helsinki", + "europe/isle_of_man", "europe/istanbul", "europe/jersey", + "europe/kaliningrad", + "europe/kiev", "europe/lisbon", "europe/ljubljana", "europe/london", + "europe/luxembourg", + "europe/madrid", "europe/malta", "europe/mariehamn", "europe/minsk", + "europe/monaco", + "europe/moscow", "europe/nicosia", "europe/oslo", "europe/paris", + "europe/podgorica", + "europe/prague", "europe/riga", "europe/rome", "europe/samara", + "europe/san_marino", + "europe/sarajevo", "europe/simferopol", "europe/skopje", "europe/sofia", + "europe/stockholm", + "europe/tallinn", "europe/tirane", "europe/tiraspol", "europe/uzhgorod", + "europe/vaduz", + "europe/vatican", "europe/vienna", "europe/vilnius", "europe/volgograd", + "europe/warsaw", + "europe/zagreb", "europe/zaporozhye", "europe/zurich", "gb", "gb-eire", + "gmt", + "gmt0", "greenwich", "hongkong", "hst", "iceland", "indian/antananarivo", + "indian/chagos", "indian/christmas", "indian/cocos", "indian/comoro", + "indian/kerguelen", + "indian/mahe", "indian/maldives", "indian/mauritius", "indian/mayotte", + "indian/reunion", + "iran", "israel", "jamaica", "japan", "jst", "kwajalein", "libya", "met", + "mexico/bajanorte", "mexico/bajasur", "mexico/general", + "mideast/riyadh87", + "mideast/riyadh88", "mideast/riyadh89", "mst", "mst7mdt", "navajo", "nz", + "nz-chat", + "pacific/apia", "pacific/auckland", "pacific/bougainville", + "pacific/chatham", + "pacific/chuuk", "pacific/easter", "pacific/efate", "pacific/enderbury", + "pacific/fakaofo", + "pacific/fiji", "pacific/funafuti", "pacific/galapagos", + "pacific/gambier", + "pacific/guadalcanal", "pacific/guam", "pacific/honolulu", + "pacific/johnston", + "pacific/kiritimati", "pacific/kosrae", "pacific/kwajalein", + "pacific/majuro", + "pacific/marquesas", "pacific/midway", "pacific/nauru", "pacific/niue", + "pacific/norfolk", + "pacific/noumea", "pacific/pago_pago", "pacific/palau", + "pacific/pitcairn", + "pacific/pohnpei", "pacific/ponape", "pacific/port_moresby", + "pacific/rarotonga", + "pacific/saipan", "pacific/samoa", "pacific/tahiti", "pacific/tarawa", + "pacific/tongatapu", + "pacific/truk", "pacific/wake", "pacific/wallis", "pacific/yap", "poland", + "portugal", + "prc", "pst", "pst8pdt", "rok", "singapore", "systemv/ast4", + "systemv/ast4adt", + "systemv/cst6", "systemv/cst6cdt", "systemv/est5", "systemv/est5edt", + "systemv/hst10", + "systemv/mst7", "systemv/mst7mdt", "systemv/pst8", "systemv/pst8pdt", + "systemv/yst9", + "systemv/yst9ydt", "turkey", "uct", "universal", "us/alaska", + "us/aleutian", + "us/arizona", "us/central", "us/east-indiana", "us/eastern", "us/hawaii", + "us/indiana-starke", "us/michigan", "us/mountain", "us/pacific", + "us/pacific-new", + "us/samoa", "utc", "w-su", "wet", "zulu", + NULL +}; + +static int init_oci_timezone_hash(void) +{ + int i; + int ret; + + if (oci_timezone_hash != NULL) { + return 0; + } + + oci_timezone_hash = + flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 1024, -1); + if (!oci_timezone_hash) { + return -2; + } + + + for (i = 0; oci_supported_timezones[i] != NULL; i++) { + ret = flb_hash_table_add(oci_timezone_hash, + oci_supported_timezones[i], + strlen(oci_supported_timezones[i]), + (void *) "1", sizeof("1")); + if (ret < 0) { + flb_hash_table_destroy(oci_timezone_hash); + oci_timezone_hash = NULL; + return -3; + } + } + + return 0; +} + +static void cleanup_oci_timezone_hash(void) +{ + if (oci_timezone_hash) { + flb_hash_table_destroy(oci_timezone_hash); + oci_timezone_hash = NULL; + } +} + +static int is_oci_supported_timezone(const char *log_timezone) +{ + void *out_buf = NULL; + size_t out_size = 0; + char *lower_tz; + int i, ht_ret; + int ret_init_oci_timezone_hash; + + if (!log_timezone) { + return 0; + } + ret_init_oci_timezone_hash = init_oci_timezone_hash(); + + if (ret_init_oci_timezone_hash != 0) { + return 0; + } + lower_tz = strdup(log_timezone); + if (!lower_tz) { + return 0; + } + + for (i = 0; lower_tz[i]; i++) { + lower_tz[i] = tolower(lower_tz[i]); + } + + ht_ret = flb_hash_table_get(oci_timezone_hash, lower_tz, strlen(lower_tz), + &out_buf, &out_size); + if (ht_ret < 0) { + free(lower_tz); + return 0; + } + free(lower_tz); + return (out_buf != NULL && strcmp((char *) out_buf, "1") == 0) ? 1 : 0; +} + + +int is_valid_timezone(const char *log_timezone) +{ + + if (!log_timezone || strlen(log_timezone) == 0) { + return 0; + } + if (is_oci_supported_timezone(log_timezone)) { + return 1; + } + return 0; +} + +/* Determine the oracle cloud realm code based on region */ +const char *determine_realm_from_region(const char *region) +{ + int i; + + if (!region) { + return "oc1"; + } + for (i = 0; region_realm_mappings[i].region != NULL; i++) { + if (strcmp(region, region_realm_mappings[i].region) == 0) { + return region_realm_mappings[i].realm; + } + } + return "oc1"; +} + +/* gets the domain suffix for a specific oracle cloud realm */ +const char *get_domain_suffix_for_realm(const char *realm) +{ + int i; + + if (!realm) { + return "oraclecloud.com"; + } + for (i = 0; realm_mappings[i].realm_code != NULL; i++) { + if (strcmp(realm, realm_mappings[i].realm_code) == 0) { + return realm_mappings[i].domain_suffix; + } + } + + return "oraclecloud.com"; +} + + + +const char *long_region_name(char *short_region_name) +{ + size_t i; + + if (short_region_name == NULL) { + return NULL; + } + for (i = 0; i < COUNT_OF_REGION; i++) { + if (strcmp(short_region_name, region_mappings[i].short_name) == 0) { + return (region_mappings[i].long_name); + } + } + return NULL; +} diff --git a/tests/runtime/CMakeLists.txt b/tests/runtime/CMakeLists.txt index 01838c68d27..0ddc94822b3 100644 --- a/tests/runtime/CMakeLists.txt +++ b/tests/runtime/CMakeLists.txt @@ -222,7 +222,7 @@ if(FLB_IN_LIB) FLB_RT_TEST(FLB_OUT_STDOUT "out_stdout.c") FLB_RT_TEST(FLB_OUT_SYSLOG "out_syslog.c") FLB_RT_TEST(FLB_OUT_TCP "out_tcp.c") - + FLB_RT_TEST(FLB_OUT_ORACLE_LOG_ANALYTICS "out_oracle_log_analytics.c") if (FLB_RECORD_ACCESSOR) FLB_RT_TEST(FLB_OUT_STACKDRIVER "out_stackdriver.c") endif() diff --git a/tests/runtime/out_oracle_log_analytics.c b/tests/runtime/out_oracle_log_analytics.c new file mode 100644 index 00000000000..fb77b42bbaf --- /dev/null +++ b/tests/runtime/out_oracle_log_analytics.c @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "flb_tests_runtime.h" +#include +#include + +void test_instance_principal_auth() +{ + int ret; + flb_ctx_t *ctx; + int in_ffd; + int out_ffd; + + setenv("FLB_OCI_PLUGIN_UNDER_TEST", "1", 1); + setenv("TEST_IMDS_SUCCESS", "1", 1); + + ctx = flb_create(); + TEST_CHECK(ctx != NULL); + TEST_MSG("failed to create flb context"); + + if (!ctx) { + return; + } + + flb_service_set(ctx, "flush", "1", "grace", "1", "log_level", "debug", + NULL); + + in_ffd = flb_input(ctx, "lib", NULL); + TEST_CHECK(in_ffd >= 0); + TEST_MSG("failed to create input instance"); + + if (in_ffd < 0) { + flb_destroy(ctx); + return; + } + + flb_input_set(ctx, in_ffd, "tag", "test", NULL); + + out_ffd = flb_output(ctx, "oracle_log_analytics", NULL); + TEST_CHECK(out_ffd >= 0); + TEST_MSG("failed to create output instance"); + + if (out_ffd < 0) { + flb_destroy(ctx); + return; + } + + flb_output_set(ctx, out_ffd, + "match", "test", + "auth_type", "instance_principal", + "namespace", "test_namespace", + "oci_la_log_group_id", "test_log_group", + "oci_la_log_source_name", "test_source", + "tls", "on", "tls.verify", "off", NULL); + + ret = flb_start(ctx); + TEST_CHECK(ret == 0); + TEST_MSG("flb_start failed ret -> %d..", ret); + + if (ret == 0) { + TEST_MSG("plugin initialized successfully"); + flb_stop(ctx); + } + + flb_destroy(ctx); + + unsetenv("TEST_IMDS_SUCCESS"); +} + +void test_imds_failure() +{ + int ret; + flb_ctx_t *ctx; + int in_ffd; + int out_ffd; + + setenv("FLB_OCI_PLUGIN_UNDER_TEST", "1", 1); + setenv("TEST_IMDS_FAILURE", "1", 1); + + ctx = flb_create(); + TEST_CHECK(ctx != NULL); + + flb_service_set(ctx, "flush", "1", "grace", "1", "log_level", "debug", + NULL); + + in_ffd = flb_input(ctx, "lib", NULL); + TEST_CHECK(in_ffd >= 0); + flb_input_set(ctx, in_ffd, "tag", "test", NULL); + + out_ffd = flb_output(ctx, "oracle_log_analytics", NULL); + TEST_CHECK(out_ffd >= 0); + + flb_output_set(ctx, out_ffd, + "match", "test", + "auth_type", "instance_principal", + "namespace", "test_namespace", + "oci_la_log_group_id", "test_log_group", + "oci_la_log_source_name", "test_source", NULL); + + ret = flb_start(ctx); + + TEST_CHECK(ret != 0); + + flb_destroy(ctx); + + unsetenv("FLB_OCI_PLUGIN_UNDER_TEST"); + unsetenv("TEST_IMDS_FAILURE"); +} + +void test_config_file_auth() +{ + int ret; + flb_ctx_t *ctx; + int in_ffd; + int out_ffd; + + char config_content[] = + "[DEFAULT]\n" + "user=ocid1.user.oc1..test\n" + "fingerprint=00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff\n" + "tenancy=ocid1.tenancy.oc1..test\n" + "region=us-ashburn-1\n" "key_file=/tmp/test_key.pem\n"; + + char key_content[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEowIBAAKCAQEAy8Dbv8prpJ/0kKhlGeJYozo2t60EG8L0561g13R29LvMR5hy\n" + "vGZlGJpmn65+A4xHXWUId1eJsCMFtgKhXFMSp6/8RkLcYMrFoAWKpILYdSrvJ0R6\n" + "6u+zR1EpqQvk8TDrNMVzfv/jDPPG2BHYkp7RWE7pWQv8vZGnU6p3SJGvTwKdgnjG\n" + "jNvCsXI8Dx7ePLxLZhX0Vg8bqXFfVVN3FlWKVfPy4jLQfQhWVx7dL1EfJL2YiEXI\n" + "1Oj2DQKLVxPHHcNRVJKXhUHJ2F6PVYqMfAJ9bJnTHhOGZfYWO7pQQQv2eFaInp6s\n" + "6LfDZ/P9l5T7PiNJvWNGnJZpVQqEXdqTxXrCMQIDAQABAoIBABPHBWJ1BwpJVxjJ\n" + "DvVF6qMvHdFXdFyxJkYmGMxXl7xLdEXLzEKfvAh4Mm4XnL5tVEUWr/5uOVqPNgQm\n" + "9E3SpJdoFUk4V8hCdLr1WkdWpHLiIzH7M3LXyLzWhFrLx1nC7tFGfOZWLCmE1SLM\n" + "Yv2FvqcqAVEkpNjbYB8pLQVQZzNqvqqvFuUGW7jHLQsXnvVFLxHjD6YIqPZHsKYY\n" + "VLPSZvSx2VHWOPbBKvM6fqLwQJfJWqjJGH7sBCuQGGKzpb3Jqhqb/5WdY4LTQKCQ\n" + "bXPcGqHhGvKFCMELXCcLdqmQNflQZOTpZLVmHQMGJQYRY2gOkJCRs0UQVR0gVkDX\n" + "6l6EoAECgYEA9aAZxLlnJBjFPwQT8UkFvzAZJfPBHCkNBwLmxL/WGLQPMmLcYZkD\n" + "jGOGsLFNjNuDhAQQVjMFoME0mKNpFJGKqLcNcKYUQKdvHqTrqCOxLQpCEbvJ3qYB\n" + "dYNFLFzpfMqCiL7kQjRLzZIH3k5K9Dg8+JKNn5VX8g9LMXr7clKJgfECgYEA1Bk4\n" + "qsHjMjPMVdmVCEiCpGPBVy7j6dFWbFJT3WqdLMqz7mLvL3spLLWZKDvKFdAhFIKb\n" + "AkWVQBKpUQKxtCBfJXCX3KKqfMxXKCNm7QH7hFXpKvVKLKuXphCW/6Y+qU8JUHyO\n" + "Y5rn6eMmfzOTRJGp2C2hMKqKHw6pEJmdC5K0pLkCgYAZXPBJGhSNY3x3TIzQMvvF\n" + "jjEZRLHnmCBJqS4J1vqJLJqXqfnHSFwZBEChFLLgXLFKjJGqNGQbV3NkwZWQXkOT\n" + "Zy3FQhPLvVMKOKvWHW8bLQS7FQVDCJQ2TIJ0OPQhAjTJLQEFJ5WEK0LmJqEDTHXP\n" + "HK6LgqLEcaV2xvlYKgYbQQKBgF7eG3LWxKmfH0NbQXZ5UFVQW3VCXOQ4LBBb3y0w\n" + "VnvGqLMV5GJVP2gHxPFQM4T0eW8xLXNM5LYH3xqvLGGmvx3YGqGDh+FDHqtI9pCC\n" + "qPoLLBZ4pLfBGJfaVFHJQBLJNHNgCLz7LLQ5YYhQYjCWGKcuNvEzNiDvKMQePLjL\n" + "EoHpAoGBAK3pHYfhwCJRGNVJbKO6BjfLQr8JDXKdqzaGmKKN2eVdNGqLFjFuFl6N\n" + "fVHxq3RKFfgwGYkZ8pCNLHK7lO8Q9i2BO7qE1bDZFqRMJgC6EqhHLVEHDYVYGDrO\n" + "VDLq3cL2MQKmVPrmWCFKLJSXiKGmqYZmVXC7FqfJJrKqLdFQCZNf\n" + "-----END RSA PRIVATE KEY-----\n"; + + FILE *config_file = fopen("/tmp/test_oci_config", "w"); + TEST_CHECK(config_file != NULL); + if (config_file) { + fwrite(config_content, 1, strlen(config_content), config_file); + fclose(config_file); + } + + FILE *key_file = fopen("/tmp/test_key.pem", "w"); + TEST_CHECK(key_file != NULL); + if (key_file) { + fwrite(key_content, 1, strlen(key_content), key_file); + fclose(key_file); + } + + ctx = flb_create(); + TEST_CHECK(ctx != NULL); + + flb_service_set(ctx, "flush", "1", "grace", "1", "log_level", "debug", + NULL); + + in_ffd = flb_input(ctx, "lib", NULL); + TEST_CHECK(in_ffd >= 0); + flb_input_set(ctx, in_ffd, "tag", "test", NULL); + + out_ffd = flb_output(ctx, "oracle_log_analytics", NULL); + TEST_CHECK(out_ffd >= 0); + + flb_output_set(ctx, out_ffd, + "match", "test", + "auth_type", "config_file", + "config_file_location", "/tmp/test_oci_config", + "namespace", "test_namespace", + "oci_la_log_group_id", "test_log_group", + "oci_la_log_source_name", "test_source", + "tls", "on", "tls.verify", "off", NULL); + + ret = flb_start(ctx); + TEST_CHECK(ret == 0); + + if (ret == 0) { + flb_stop(ctx); + } + + flb_destroy(ctx); + + unlink("/tmp/test_oci_config"); + unlink("/tmp/test_key.pem"); +} + +TEST_LIST = { + {"test_instance_principal_auth", test_instance_principal_auth}, + {"test_imds_failure", test_imds_failure}, + {"test_config_file_auth", test_config_file_auth}, + {0} +};