Skip to content

Commit 51c330e

Browse files
EppOfmonbillard
authored andcommitted
Add support for Azure Workload Identity
1 parent 110245a commit 51c330e

4 files changed

Lines changed: 169 additions & 13 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pip install prometrix
2626
```
2727

2828

29-
> **⚠️ Note:** For Python **3.8 support**, you must use version **0.2.3 or below** of `prometrix`.
29+
> **⚠️ Note:** For Python **3.8 support**, you must use version **0.2.3 or below** of `prometrix`.
3030
> From `0.2.4` onward, `prometrix` requires **Python ≥ 3.9**.
3131
3232
Usage
@@ -84,6 +84,8 @@ azure_config = AzurePrometheusConfig(
8484
azure_client = get_custom_prometheus_connect(azure_config)
8585
```
8686

87+
prometrix supports Azure Managed Identity and Azure Workload Identity. See [Azure Authentication Setup](/azure-auth-setup.rst) for more details.
88+
8789
Similar configuration and creation can be done for EKS, Thanos, and Victoria Metrics Prometheus.
8890

8991
> **_NOTE:_** You need to replace the placeholder values (e.g., YOUR_CORALOGIX_PROMETHEUS_TOKEN) with your actual credentials and endpoints.

azure-auth-setup.md

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Azure managed Prometheus
2+
3+
In order to authenticate against the Azure Monitor Workspace Query endpoint, you have multiple options:
4+
5+
- Create an Azure Active Directory authentication app [Option #1](#option-1-create-an-azure-authentication-app)
6+
- Pros:
7+
- Quick setup. Just need to create an app, get the credentials and add them to the manifests
8+
- Other pods can't use the Service Principal without having the secret
9+
- Cons:
10+
- Requires a service principal (Azure AD permission)
11+
- Need the client secret in the kubernetes manifests
12+
- Client secret expires, you need to manage its rotation
13+
- Use Kubelet's Managed Identity [Option #2](#option-2-use-kubelets-managed-identity)
14+
- Pros:
15+
- Quick setup. Get the Managed Identity Client ID and add them to the manifests
16+
- No need to manage secrets. Removing the password element decreases the risk of the credentials being compromised
17+
- Cons:
18+
- Managed Identity is bound to the AKS nodepool, so any pods can use it if they know/get the client ID
19+
- Use Azure AD Workload Identity [Option #3](#option-3-use-azure-workload-identity-recommended)
20+
- Pros:
21+
- Most secure option as Managed Identity is only bound to the pod. No other pods can use it
22+
- No need to manage secrets. Removing the password element decreases the risk of the credentials being compromised
23+
- Cons:
24+
- Extra setup needed: need AKS cluster with Workload Identity add-on enabled, get the OIDC issuer URL and add it to the manifests
25+
26+
## Get the Azure prometheus query endpoint
27+
28+
1. Go to [Azure Monitor workspaces](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/microsoft.monitor%2Faccounts>) and choose your monitored workspace.
29+
2. In your monitored workspace, `overview`, find the ``Query endpoint`` and copy it.
30+
31+
## Option #1: Create an Azure authentication app
32+
33+
We will now create an Azure authentication app and get the necesssary credentials so Robusta can access Prometheus data.
34+
35+
1. Follow this Azure guide to [Register an app with Azure Active Director](https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/prometheus-self-managed-grafana-azure-active-directory#register-an-app-with-azure-active-directory)
36+
37+
```python
38+
# Create a custom Prometheus client for Azure Prometheus using Service Principal Authentication
39+
azure_config = AzurePrometheusConfig(
40+
url="https://azure-prometheus.example.com", # Replace with your Azure Monitor workspace query endpoint
41+
azure_resource="https://prometheus.monitor.azure.com", # Default resource for Azure Monitor
42+
azure_token_endpoint="https://azure-token.example.com",
43+
azure_client_id="YOUR_AZURE_CLIENT_ID",
44+
azure_tenant_id="YOUR_AZURE_TENANT_ID",
45+
azure_client_secret="YOUR_AZURE_CLIENT_SECRET",
46+
additional_labels={"job": "azure-prometheus"},
47+
)
48+
azure_client = get_custom_prometheus_connect(azure_config)
49+
```
50+
51+
3. Complete the [Allow your app access to your workspace](https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/prometheus-self-managed-grafana-azure-active-directory#allow-your-app-access-to-your-workspace>) step, so your app can query data from your Azure Monitor workspace.
52+
53+
## Option #2: Use Kubelet's Managed Identity
54+
55+
1. Get the AKS kubelet's Managed Identity Client ID:
56+
57+
```bash
58+
az aks show -g <resource-group> -n <cluster-name> --query identityProfile.kubeletidentity.clientId -o tsv
59+
```
60+
61+
2. Set the following settings based from the previous step.
62+
63+
```python
64+
# Create a custom Prometheus client for Azure Prometheus using Azure Managed Identity
65+
azure_config = AzurePrometheusConfig(
66+
url="https://azure-prometheus.example.com", # Replace with your Azure Monitor workspace query endpoint
67+
azure_use_managed_id=True,
68+
azure_resource="https://prometheus.monitor.azure.com", # Default resource for Azure Monitor
69+
azure_metadata_endpoint="http://169.254.169.254/metadata/identity/oauth2/token", # Default endpoint for Managed Identity
70+
azure_client_id="YOUR_AZURE_CLIENT_ID", # Client ID from step 1
71+
azure_tenant_id="YOUR_AZURE_TENANT_ID",
72+
additional_labels={"job": "azure-prometheus"},
73+
)
74+
azure_client = get_custom_prometheus_connect(azure_config)
75+
```
76+
77+
3. Give access to your Managed Identity on your Azure Monitor Workspace:
78+
- Open the Access Control (IAM) page for your Azure Monitor workspace in the Azure portal.
79+
- Select Add role assignment.
80+
- Select Monitoring Data Reader and select Next.
81+
- For Assign access to, select Managed identity.
82+
- Select + Select members.
83+
- Select the Managed Identity you got from step 1
84+
- Select Review + assign to save the configuration.
85+
86+
## Option #3: Use Azure Workload Identity (Recommended)
87+
88+
1. Requirements
89+
90+
AKS cluster needs to have Workload Identity add-on and OIDC issuer enabled. You can use `--enable-oidc-issuer --enable-workload-identity` with `az aks create` or `az aks update` to enable them.
91+
92+
2. Create a new Managed Identity. Change the Identity name, resource group and location to match your environment.
93+
94+
```bash
95+
export SUBSCRIPTION="$(az account show --query id --output tsv)"
96+
az identity create --name <identity-name> --resource-group <resource-group> --location "eastus" --subscription "${SUBSCRIPTION}" # keep the identity name for step 4
97+
az identity show --name <identity-name> --resource-group <resource-group> -query clientId -o tsv # keep this value for the step #3
98+
```
99+
100+
3. Set the following settings based from the previous step.
101+
102+
```python
103+
# Create a custom Prometheus client for Azure Prometheus using Azure Managed Identity
104+
azure_config = AzurePrometheusConfig(
105+
url="https://azure-prometheus.example.com", # Replace with your Azure Monitor workspace query endpoint
106+
azure_use_workload_id=True,
107+
azure_resource="https://prometheus.monitor.azure.com", # Default resource for Azure Monitor
108+
azure_token_endpoint="https://azure-token.example.com",
109+
azure_client_id="YOUR_AZURE_CLIENT_ID", # Client ID from step 2
110+
azure_tenant_id="YOUR_AZURE_TENANT_ID",
111+
additional_labels={"job": "azure-prometheus"},
112+
)
113+
azure_client = get_custom_prometheus_connect(azure_config)
114+
```
115+
116+
4. Federate the Service Account with the Managed Identity. Replace the values with the ones from the step #1.
117+
118+
```bash
119+
export AKS_OIDC_ISSUER="$(az aks show -g <resource-group> -n <cluster-name> --query "oidcIssuerProfile.issuerUrl" -otsv)" # Replace with the corresponding values of your AKS clusters.
120+
MY_NAMESPACE="mynamespace" # Replace with the namespace where your application is deployed
121+
MY_SERVICE_ACCOUNT="my-service-account" # Replace with the service account name used by your application
122+
az identity federated-credential create --name <federated-identity-name> --identity-name <identity-name> --resource-group <resource-group> --issuer ${AKS_OIDC_ISSUER} --subject system:serviceaccount:$MY_NAMESPACE:$MY_SERVICE_ACCOUNT # Use identity name from step 2
123+
```
124+
125+
5. Give access to your Managed Identity on your workspace:
126+
- Open the Access Control (IAM) page for your Azure Monitor workspace in the Azure portal.
127+
- Select Add role assignment.
128+
- Select Monitoring Data Reader and select Next.
129+
- For Assign access to, select Managed identity.
130+
- Select + Select members.
131+
- Select the Managed Identity you got from step 2
132+
- Select Review + assign to save the configuration.

prometrix/auth.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ def azure_authorization(cls, config: PrometheusConfig) -> bool:
1616
if not isinstance(config, AzurePrometheusConfig):
1717
return False
1818
return (config.azure_client_id != "" and config.azure_tenant_id != "") and (
19-
config.azure_client_secret != "" or config.azure_use_managed_id != ""
19+
config.azure_client_secret != "" or # Service Principal Auth
20+
config.azure_use_managed_id != False or # Managed Identity Auth
21+
config.azure_use_workload_id != False # Workload Identity Auth
2022
)
2123

2224
@classmethod
@@ -48,15 +50,33 @@ def _get_azure_metadata_endpoint(cls, config: PrometheusConfig):
4850
@no_type_check
4951
@classmethod
5052
def _post_azure_token_endpoint(cls, config: PrometheusConfig):
51-
return requests.post(
52-
url=config.azure_token_endpoint,
53-
headers={"Content-Type": "application/x-www-form-urlencoded"},
54-
data={
53+
# Try Azure Workload Identity
54+
with open("/var/run/secrets/azure/tokens/azure-identity-token", "r") as token_file:
55+
token = token_file.read()
56+
data = {
57+
"grant_type": "client_credentials",
58+
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
59+
"client_assertion": token,
60+
"client_id": config.azure_client_id,
61+
"scope": f"{config.azure_resource}/.default",
62+
}
63+
# Fallback to Azure Service Principal
64+
if not token:
65+
if config.azure_use_workload_id:
66+
return {
67+
"ok": False,
68+
"reason": f"Could not open token file from {token_file}",
69+
}
70+
data = {
5571
"grant_type": "client_credentials",
5672
"client_id": config.azure_client_id,
5773
"client_secret": config.azure_client_secret,
5874
"resource": config.azure_resource,
59-
},
75+
}
76+
return requests.post(
77+
url=config.azure_token_endpoint,
78+
headers={"Content-Type": "application/x-www-form-urlencoded"},
79+
data=data,
6080
)
6181

6282
@classmethod
@@ -67,7 +87,7 @@ def request_new_token(cls, config: PrometheusConfig) -> bool:
6787
try:
6888
if config.azure_use_managed_id:
6989
res = cls._get_azure_metadata_endpoint(config)
70-
else:
90+
else: # Service Principal and Workload Identity
7191
res = cls._post_azure_token_endpoint(config)
7292
except Exception:
7393
logging.exception(

prometrix/models/prometheus_config.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from enum import Enum
2+
import os
23
from typing import Dict, List, Optional
34

45
try:
@@ -69,12 +70,13 @@ class VictoriaMetricsPrometheusConfig(PrometheusConfig):
6970
# Does not support labels according to the docs, See below for apis
7071
# https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/prometheus-api-promql#supported-apis
7172
class AzurePrometheusConfig(PrometheusConfig):
72-
azure_resource: str
73-
azure_metadata_endpoint: str
74-
azure_token_endpoint: str
75-
azure_use_managed_id: Optional[str] = None
73+
azure_resource: str = "https://prometheus.monitor.azure.com"
74+
azure_metadata_endpoint: str = "http://169.254.169.254/metadata/identity/oauth2/token"
75+
azure_token_endpoint: str = f"https://login.microsoftonline.com/{os.environ.get('AZURE_TENANT_ID')}/oauth2/token"
76+
azure_use_managed_id: Optional[bool] = False
77+
azure_use_workload_id: Optional[bool] = False
7678
azure_client_id: Optional[str] = None
77-
azure_tenant_id: Optional[str] = None
79+
azure_tenant_id: Optional[str] = os.environ.get('AZURE_TENANT_ID', '')
7880
azure_client_secret: Optional[str] = None
7981
supported_apis: List[PrometheusApis] = [
8082
PrometheusApis.QUERY,

0 commit comments

Comments
 (0)