Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HPCC-27830 Azure LogAccess use Secrets #16245

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
63 changes: 43 additions & 20 deletions helm/examples/azure/log-analytics/README.md
Expand Up @@ -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
```

66 changes: 66 additions & 0 deletions 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 <workingdir>/<${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
19 changes: 10 additions & 9 deletions helm/examples/azure/log-analytics/loganalytics-hpcc-logaccess.yaml
Expand Up @@ -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"
Expand All @@ -31,4 +32,4 @@ global:
storeName: "ContainerInventory"
searchColumn: "Name"
- type: "host"
searchColumn: "Computer"
searchColumn: "Computer"
@@ -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
@@ -0,0 +1 @@
{Azure Active Directory registered Application's ID goes here}
@@ -0,0 +1 @@
{Azure Active Directory registered Application provided secret goes here}
@@ -0,0 +1 @@
{Azure Active Directory registered Application's Tenant (or directory) ID goes here}
@@ -0,0 +1 @@
{Azure Log Analytics workspace ID goes here}
4 changes: 4 additions & 0 deletions helm/hpcc/values.schema.json
Expand Up @@ -89,6 +89,10 @@
"system": {
"$ref": "#/definitions/secrets"
}
,
"esp": {
"$ref": "#/definitions/secrets"
}
},
"additionalProperties": false
},
Expand Down
3 changes: 1 addition & 2 deletions system/jlib/jlog.cpp
Expand Up @@ -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"
Expand Down
Expand Up @@ -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<IPropertyTree> 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);
Expand Down Expand Up @@ -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);
Expand Down
Expand Up @@ -22,6 +22,7 @@
#include "jptree.hpp"
#include "jstring.hpp"
#include <ctime>
#include "jsecrets.hpp"


#ifndef AZURE_LOGANALYTICS_CURL_LOGACCESS_EXPORTS
Expand Down Expand Up @@ -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);
Expand Down