From c4ebc36b76d32f0b3bd8973fe17b29c37866670d Mon Sep 17 00:00:00 2001 From: Rodrigo Pastrana Date: Wed, 22 Jun 2022 19:44:11 -0400 Subject: [PATCH] HPCC-27830 Azure LogAccess use Secrets - Adds esp jsecret category - Eliminates use of Env vars - Stablishes azure_logaccess secret - Updates documentation - Provides secrets template - Provides logAccess secret creator Signed-off-by: Rodrigo Pastrana --- helm/examples/azure/log-analytics/README.md | 63 ++++++++++++------ .../create-azure-logaccess-secret.sh | 66 +++++++++++++++++++ .../loganalytics-hpcc-logaccess.yaml | 19 +++--- .../loganalytics-logaccess-secrets.yaml | 8 +++ .../secrets-templates/aad-client-id | 1 + .../secrets-templates/aad-client-secret | 1 + .../secrets-templates/aad-tenant-id | 1 + .../secrets-templates/ala-workspace-id | 1 + helm/hpcc/values.schema.json | 4 ++ system/jlib/jlog.cpp | 3 +- .../AzureLogAnalyticsCurlClient.cpp | 54 +++++++++------ .../AzureLogAnalyticsCurlClient.hpp | 7 +- 12 files changed, 173 insertions(+), 55 deletions(-) create mode 100755 helm/examples/azure/log-analytics/create-azure-logaccess-secret.sh create mode 100644 helm/examples/azure/log-analytics/loganalytics-logaccess-secrets.yaml create mode 100644 helm/examples/azure/log-analytics/secrets-templates/aad-client-id create mode 100644 helm/examples/azure/log-analytics/secrets-templates/aad-client-secret create mode 100644 helm/examples/azure/log-analytics/secrets-templates/aad-tenant-id create mode 100644 helm/examples/azure/log-analytics/secrets-templates/ala-workspace-id diff --git a/helm/examples/azure/log-analytics/README.md b/helm/examples/azure/log-analytics/README.md index fc5691dcad3..97c567cf365 100644 --- a/helm/examples/azure/log-analytics/README.md +++ b/helm/examples/azure/log-analytics/README.md @@ -6,37 +6,60 @@ a Log Analytics workspace, and explicitly enabling the Log Analytics feature. A HPCC has several components which fetch HPCC component logs via the HPCC LogAccess framework, which has to be configured to access the Log Analytics workspace. The values yaml file provided here should be used as part of a Helm based HPCC deployment. -## enable-loganalytics.sh +## Quickstart +### 1 - Enabling Log Analytics +Once these two steps are completed, HPCC component logs should be routed to Azure Log Analytics and accessible via the Portal +#### a - Provide required information in env-loganalytics +The user should populate the following values in order to create a new Azure Log Analytics workspace, associate it with a target AKS cluster, and enable to processing of logs -This helper script enables the Azure Log Analytics feature on a target AKS cluster (which hosts HPCC) creates a new Azure LogAnalytics workspace (user can provide pre-existing ) -The workspace is then associated with the target AKS cluster on which HPCC is deployed. +- LOGANALYTICS_WORKSPACE_NAME (Desired name for the Azure LogAnalytics workspace to be associated with target AKS cluster) + New workspace will be created if it does not exist +- LOGANALYTICS_RESOURCE_GROUP (The Azure resource group associated with the target AKS cluster) + New workspace will be associated with this resource group +- AKS_CLUSTER_NAME (Name of the target AKS cluster to associate log analytics workspace) +- TAGS - The tags associated with the new workspace + For example: "admin=MyName email=my.email@mycompany.com environment=myenv justification=testing" +- AZURE_SUBSCRIPTION (Optional - Ensures this subscription is set before creating the new workspace) -This script is dependant on several environment variables which are defined in ./env-loganalytics +#### b - Execute enable-loganalytics.sh -## env-loganalytics -Declares several environment variables needed to create an Azure LogAnalytics workspace. -The user should populate the following values before executing the ./enable-loganalytics.sh script: +This helper script attempts to create new Azure LogAnalytics workspace (user can provide pre-existing), associates the workspace with the target AKS cluster, and enables the Azure Log Analytics feature.This script is dependant on the values provided in the previous step -LOGANALYTICS_RESOURCE_GROUP - The Azure resource group associated with the target AKS cluster - - The new workspace will be associated with this resource group +### 2 - Configure HPCC logAccess +The logAccess feature allows HPCC to query and package relevant logs for various features such as ZAP report, WorkUnit helper logs, ECLWatch log viewer, etc. -LOGANALYTICS_WORKSPACE_NAME - The desired name for the Azure LogAnalytics workspace to be created +#### a - Procure AAD registered application +Azure requires an Azure Active Directory registered application in order to broker Log Analytics API access. See official documentation: +https://docs.microsoft.com/en-us/power-apps/developer/data-platform/walkthrough-register-app-azure-active-directory -TAGS - The tags associated with the new workspace - - For example: "admin=MyName email=my.email@mycompany.com environment=myenv justification=testing" +Depending on your Azure subscription structure, it might be necessary to request this from a subscription administrator. -AKS_CLUSTER_NAME - Name of the AKS cluster to associate newly created log analytics workspace +#### b - Provide AAD registered application inforation +HPCC logAccess requires access to the AAD Tenant, client, token, and target workspace ID via secure secret object. +The secret is expected to be in the 'esp' category, and be named 'azure-logaccess'. +The following kv pairs are supported +- aad-tenant-id +- aad-client-id +- aad-client-secret +- ala-workspace-id -AZURE_SUBSCRIPTION - Optional - Ensures this subscription is set before creating the new workspace - -## loganalytics-hpcc-logaccess.yaml +The included 'create-azure-logaccess-secret.sh' helper can be used to create the necessary secret +Example manual secret creation command (assuming ./secrets-templates contains a file named exactly as the above keys): +```console + create-azure-logaccess-secret.sh .HPCC-Platform/helm/examples/azure/log-analytics/secrets-templates/ +``` -This is a values file that can be supplied to Helm when starting HPCC. -It will direct the Log Access framework to target Azure Log Analytics +Otherwise, users can create the secret manually. +Example manual secret creation command (assuming ./secrets-templates contains a file named exactly as the above keys): +```console + kubectl create secret generic azure-logaccess --from-file=HPCC-Platform/helm/examples/azure/log-analytics/secrets-templates/ +``` -This means functionality which fetches HPCC component logs will attempt to gather logs via KQL queries. +#### c - Configure HPCC logAccess +The target HPCC deployment should be directed to target the above Azure Log Analytics workspace by providing appropriate logAccess values (such as ./loganalytics-hpcc-logaccess.yaml). The previously created azure-logaccess secret must be declared and associated with the esp category, this can be accomplished via secrets value yaml (such as ./loganalytics-logaccess-secrets.yaml) Example use: ```console - helm install myhpcc hpcc/hpcc -f HPCC-Platform/helm/examples/azure/log-analytics/loganalytics-hpcc-logaccess.yaml + helm install myhpcc hpcc/hpcc -f HPCC-Platform/helm/examples/azure/log-analytics/loganalytics-hpcc-logaccess.yaml -f HPCC-Platform/helm/examples/azure/log-analytics/loganalytics-logaccess-secrets.yaml ``` + diff --git a/helm/examples/azure/log-analytics/create-azure-logaccess-secret.sh b/helm/examples/azure/log-analytics/create-azure-logaccess-secret.sh new file mode 100755 index 00000000000..1937d2dfda4 --- /dev/null +++ b/helm/examples/azure/log-analytics/create-azure-logaccess-secret.sh @@ -0,0 +1,66 @@ +#!/bin/bash +WORK_DIR=$(dirname $0) +source ${WORK_DIR}/env-loganalytics + +k8scommand="kubectl" +secretname="azure-logaccess" +secretsdir="${WORK_DIR}/secrets-templates" + +usage() +{ + echo "Creates necessary k8s secret used by HPCC's logAccess to access Azure Log Analytics" + echo "> create-azure-logaccess-secret.sh [Options]" + echo "" + echo "Options:" + echo "-d Specifies directory containing required secret values in self named files." + echo " Defaults to /<${secretssubdir}>" + echo "-h Print Usage message" + echo "" + echo "Requires directory containing secret values in dedicated files." + echo "Defaults to ${secretssubdir} if not specified via -d option." + echo "" + echo "Expected directory structure:" + echo "${secretsdir}/" + echo " aad-client-id - Should contain the ID of the AAD registered Application" + echo " aad-tenant-id - Should contain the subscription tenant of theAAD registered Application" + echo " aad-client-secret - Should contain access secret provided by AAD registered Application" + echo " ala-workspace-id - Should contain target Azure Log Analytics workspace ID. (Optional if provided in LogAccess configuration)" +} + +while [ "$#" -gt 0 ]; do + arg=$1 + case "${arg}" in + -h) + usage + exit + ;; + -d) shift + secretsdir=$1 + ;; + esac + shift +done + +echo "Creating '${secretname}' secret." + +command -v ${k8scommand} >/dev/null 2>&1 || { echo >&2 "Aborting - '${k8scommand}' not found!"; exit 1; } + +errormessage=$(${k8scommand} get secret ${secretname} 2>&1) +if [[ $? -eq 0 ]] +then + echo "WARNING: Target secret '${secretname}' already exists! Delete it and re-run if secret update desired." + echo "${errormessage}" + exit 1 +fi + +errormessage=$(${k8scommand} create secret generic ${secretname} --from-file=${secretsdir}) +if [[ $? -ne 0 ]] +then + echo "Error creating: Target secret '${secretname}'!" + echo >&2 + usage + exit 1 +else + echo "Target secret '${secretname}' successfully created!" + ${k8scommand} get secret ${secretname} +fi diff --git a/helm/examples/azure/log-analytics/loganalytics-hpcc-logaccess.yaml b/helm/examples/azure/log-analytics/loganalytics-hpcc-logaccess.yaml index 4b046706eb5..e830b050a92 100644 --- a/helm/examples/azure/log-analytics/loganalytics-hpcc-logaccess.yaml +++ b/helm/examples/azure/log-analytics/loganalytics-hpcc-logaccess.yaml @@ -3,16 +3,17 @@ global: logAccess: name: "Azure LogAnalytics LogAccess" type: "AzureLogAnalyticsCurl" - connection: - #All connection attributes are optional **IF** defined as environment variables on the HPCC container + #connection: + #All connection attributes are optional. + #It is preferable to provide connection values as secret values category 'esp', secret name 'azure_logaccess' + # NOTE: secret 'azure_logaccess' must include 'aad-client-secret' and it cannot be provided in configuration + # #workspaceID: "XYZ" #ID of the Azure LogAnalytics workspace to query logs from - # Env var equivalent: AZURE_LOGANALYTICS_WORKSPACE_ID - #tenantID: "ABC" #The Tenant ID, required for KQL API access - # Env var equivalent: AZURE_TENANT_ID + # Secret value equivalent: 'ala-workspace-id' #clientID: "DEF" #ID of Azure Active Directory registered application with api.loganalytics.io access - format: 00000000-0000-0000-0000-000000000000 - # Env var equivalent: AZURE_CLIENT_ID - #clientSecret: "XYZ123" #The secret associated with the Azure Active Directory registered application - # Env var equivalent: AZURE_CLIENT_SECRET + # Secret value equivalent: 'aad-client-id' + #tenantID: "ABC" #The Azure Active Directory Tenant ID, required for KQL API access + # Secret value equivalent: 'aad-tenant-id' logMaps: - type: "global" storeName: "ContainerLog" @@ -31,4 +32,4 @@ global: storeName: "ContainerInventory" searchColumn: "Name" - type: "host" - searchColumn: "Computer" \ No newline at end of file + searchColumn: "Computer" diff --git a/helm/examples/azure/log-analytics/loganalytics-logaccess-secrets.yaml b/helm/examples/azure/log-analytics/loganalytics-logaccess-secrets.yaml new file mode 100644 index 00000000000..43dc32fa46f --- /dev/null +++ b/helm/examples/azure/log-analytics/loganalytics-logaccess-secrets.yaml @@ -0,0 +1,8 @@ +secrets: + esp: + azure-logaccess: "azure-logaccess" +vaults: + esp: + - name: my-azure-logaccess-vault + url: http://${env.VAULT_SERVICE_HOST}:${env.VAULT_SERVICE_PORT}/v1/secret/data/esp/${secret} + kind: kv-v2 diff --git a/helm/examples/azure/log-analytics/secrets-templates/aad-client-id b/helm/examples/azure/log-analytics/secrets-templates/aad-client-id new file mode 100644 index 00000000000..23bcd38eaa9 --- /dev/null +++ b/helm/examples/azure/log-analytics/secrets-templates/aad-client-id @@ -0,0 +1 @@ +{Azure Active Directory registered Application's ID goes here} \ No newline at end of file diff --git a/helm/examples/azure/log-analytics/secrets-templates/aad-client-secret b/helm/examples/azure/log-analytics/secrets-templates/aad-client-secret new file mode 100644 index 00000000000..200382fc08a --- /dev/null +++ b/helm/examples/azure/log-analytics/secrets-templates/aad-client-secret @@ -0,0 +1 @@ +{Azure Active Directory registered Application provided secret goes here} \ No newline at end of file diff --git a/helm/examples/azure/log-analytics/secrets-templates/aad-tenant-id b/helm/examples/azure/log-analytics/secrets-templates/aad-tenant-id new file mode 100644 index 00000000000..82252531ef9 --- /dev/null +++ b/helm/examples/azure/log-analytics/secrets-templates/aad-tenant-id @@ -0,0 +1 @@ +{Azure Active Directory registered Application's Tenant (or directory) ID goes here} \ No newline at end of file diff --git a/helm/examples/azure/log-analytics/secrets-templates/ala-workspace-id b/helm/examples/azure/log-analytics/secrets-templates/ala-workspace-id new file mode 100644 index 00000000000..205401c4b3f --- /dev/null +++ b/helm/examples/azure/log-analytics/secrets-templates/ala-workspace-id @@ -0,0 +1 @@ +{Azure Log Analytics workspace ID goes here} \ No newline at end of file diff --git a/helm/hpcc/values.schema.json b/helm/hpcc/values.schema.json index 2244e2a6014..fb53bf0adab 100644 --- a/helm/hpcc/values.schema.json +++ b/helm/hpcc/values.schema.json @@ -89,6 +89,10 @@ "system": { "$ref": "#/definitions/secrets" } + , + "esp": { + "$ref": "#/definitions/secrets" + } }, "additionalProperties": false }, diff --git a/system/jlib/jlog.cpp b/system/jlib/jlog.cpp index 45ac97b67fa..38c6937940b 100644 --- a/system/jlib/jlog.cpp +++ b/system/jlib/jlog.cpp @@ -3517,12 +3517,11 @@ IRemoteLogAccess &queryRemoteLogAccessor() workspaceID: "XYZ" #ID of the Azure LogAnalytics workspace to query logs from #tenantID: "ABC" #The Tenant ID, required for KQL API access clientID: "DEF" #ID of Azure Active Directory registered application with api.loganalytics.io access - clientSecret: "XYZ123" #The secret associated with the Azure Active Directory registered application logMaps: - type: "global" storeName: "ContainerLog" searchColumn: "LogEntry" - timeStampColumn: "TimeGenerated" + timeStampColumn: "hpcc_log_timestamp" - type: "workunits" storeName: "ContainerLog" searchColumn: "hpcc_log_jobid" diff --git a/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.cpp b/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.cpp index 02e0d45ad6c..89b7cc7055b 100644 --- a/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.cpp +++ b/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.cpp @@ -253,43 +253,55 @@ static void submitKQLQuery(std::string & readBuffer, const char * token, const c } } +static constexpr const char * azureLogAccessSecretCategory = "esp"; +static constexpr const char * azureLogAccessSecretName = "azure-logaccess"; + +static constexpr const char * azureLogAccessSecretAADTenantID = "aad-tenant-id"; +static constexpr const char * azureLogAccessSecretAADClientID = "aad-client-id"; +static constexpr const char * azureLogAccessSecretAADClientSecret = "aad-client-secret"; +static constexpr const char * azureLogAccessSecretWorkspaceID = "ala-workspace-id"; + AzureLogAnalyticsCurlClient::AzureLogAnalyticsCurlClient(IPropertyTree & logAccessPluginConfig) { PROGLOG("%s: Resolving all required configuration values...", COMPONENT_NAME); - if (!getEnvVar("AZURE_TENANT_ID", m_tenantID)) - { - WARNLOG("%s: Environment variable 'AZURE_TENANT_ID' not found!", COMPONENT_NAME); - m_tenantID.set(logAccessPluginConfig.queryProp("connection/@tenantID")); - if (m_tenantID.isEmpty()) - throw makeStringException(-1, "Could not determine Azure Tenant ID, set 'AZURE_TENANT_ID' env var, or connection/@tenantID in AzureClient LogAccess configuration!"); + Owned secretTree = getSecret(azureLogAccessSecretCategory, azureLogAccessSecretName); + if (!secretTree) + throw makeStringExceptionV(-1, "%s: Could not fetch %s information!", COMPONENT_NAME, azureLogAccessSecretName); + + getSecretKeyValue(m_aadTenantID.clear(), secretTree, azureLogAccessSecretAADTenantID); + if (m_aadTenantID.isEmpty()) + { + WARNLOG("%s: Could not find '%s.%s' secret value!", COMPONENT_NAME, azureLogAccessSecretName, azureLogAccessSecretAADTenantID); + m_aadTenantID.set(logAccessPluginConfig.queryProp("connection/@tenantID")); + if (m_aadTenantID.isEmpty()) + throw makeStringExceptionV(-1, "%s: Could not find AAD Tenant ID, provide it as part of '%s.%s' secret, or connection/@tenantID in AzureClient LogAccess configuration!", COMPONENT_NAME, azureLogAccessSecretName, azureLogAccessSecretAADTenantID); } - if (!getEnvVar("AZURE_CLIENT_ID", m_clientID)) + getSecretKeyValue(m_aadClientID.clear(), secretTree, azureLogAccessSecretAADClientID); + if (m_aadClientID.isEmpty()) { - WARNLOG("%s: Environment variable 'AZURE_CLIENT_ID' not found!", COMPONENT_NAME); - m_clientID.set(logAccessPluginConfig.queryProp("connection/@clientID")); + WARNLOG("%s: Could not find '%s.%s' secret value!", COMPONENT_NAME, azureLogAccessSecretName, azureLogAccessSecretAADClientID); + m_aadClientID.set(logAccessPluginConfig.queryProp("connection/@clientID")); - if (m_clientID.isEmpty()) - throw makeStringException(-1, "Could not find Azure AD client ID, set 'AZURE_CLIENT_ID' env var, or connection/@clientID in AzureClient LogAccess configuration - format is '00000000-0000-0000-0000-000000000000'!"); + if (m_aadClientID.isEmpty()) + throw makeStringExceptionV(-1, "%s: Could not find AAD Client ID, provide it as part of %s.%s secret, or connection/@clientID in AzureClient LogAccess configuration!", COMPONENT_NAME, azureLogAccessSecretName, azureLogAccessSecretAADClientID); } - if (!getEnvVar("AZURE_CLIENT_SECRET", m_clientSecret)) + getSecretKeyValue(m_aadClientSecret.clear(),secretTree, azureLogAccessSecretAADClientSecret); + if (m_aadClientSecret.isEmpty()) { - WARNLOG("%s: Environment variable 'AZURE_CLIENT_SECRET' not found!", COMPONENT_NAME); - m_clientSecret.set(logAccessPluginConfig.queryProp("connection/@clientSecret")); - - if (m_clientSecret.isEmpty()) - throw makeStringException(-1, "Could not determine Azure AD client secret, set 'AZURE_CLIENT_SECRET' env var, or connection/@clientSecret in AzureClient LogAccess configuration!"); + throw makeStringExceptionV(-1, "%s: Required secret '%s.%s' not found!", COMPONENT_NAME, azureLogAccessSecretName, azureLogAccessSecretAADClientSecret); } - if (!getEnvVar("AZURE_LOGANALYTICS_WORKSPACE_ID", m_logAnalyticsWorkspaceID)) + getSecretKeyValue(m_logAnalyticsWorkspaceID.clear(), secretTree, azureLogAccessSecretWorkspaceID); + if (m_logAnalyticsWorkspaceID.isEmpty()) { - WARNLOG("%s: Environment variable 'AZURE_LOGANALYTICS_WORKSPACE_ID' not found!", COMPONENT_NAME); + WARNLOG("%s: Could not find '%s.%s' secret value!", COMPONENT_NAME, azureLogAccessSecretName, azureLogAccessSecretWorkspaceID); m_logAnalyticsWorkspaceID.set(logAccessPluginConfig.queryProp("connection/@workspaceID")); if (m_logAnalyticsWorkspaceID.isEmpty()) - throw makeStringException(-1, "Could not determine Azure LogAnalytics workspace ID (aka workspace customer ID), set 'AZURE_LOGANALYTICS_WORKSPACE_ID' env var, or connection/@workspaceID in AzureClient LogAccess configuration!"); + throw makeStringExceptionV(-1, "%s: Could not find ALA Workspace ID, provide it as part of %s.%s secret, or connection/@workspaceID in AzureClient LogAccess configuration!", COMPONENT_NAME, azureLogAccessSecretName, azureLogAccessSecretWorkspaceID); } m_pluginCfg.set(&logAccessPluginConfig); @@ -784,7 +796,7 @@ bool AzureLogAnalyticsCurlClient::processSearchJsonResp(LogQueryResultDetails & bool AzureLogAnalyticsCurlClient::fetchLog(LogQueryResultDetails & resultDetails, const LogAccessConditions & options, StringBuffer & returnbuf, LogAccessLogFormat format) { StringBuffer token; - requestLogAnalyticsAccessToken(token, m_clientID, m_clientSecret, m_tenantID); //throws if issues encountered + requestLogAnalyticsAccessToken(token, m_aadClientID, m_aadClientSecret, m_aadTenantID); //throws if issues encountered if (token.isEmpty()) throw makeStringExceptionV(-1, "%s Could not fetch valid Azure Log Analytics access token!", COMPONENT_NAME); diff --git a/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.hpp b/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.hpp index 8eb0832c145..90b71f9c13a 100644 --- a/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.hpp +++ b/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.hpp @@ -22,6 +22,7 @@ #include "jptree.hpp" #include "jstring.hpp" #include +#include "jsecrets.hpp" #ifndef AZURE_LOGANALYTICS_CURL_LOGACCESS_EXPORTS @@ -65,9 +66,9 @@ class AZURE_LOGANALYTICS_CURL_LOGACCESS_API AzureLogAnalyticsCurlClient : public StringBuffer m_hostIndexSearchPattern; StringBuffer m_logAnalyticsWorkspaceID; - StringBuffer m_tenantID; - StringBuffer m_clientID; - StringBuffer m_clientSecret; + StringBuffer m_aadTenantID; + StringBuffer m_aadClientID; + StringBuffer m_aadClientSecret; public: AzureLogAnalyticsCurlClient(IPropertyTree & logAccessPluginConfig);