Skip to content

Commit 66f2426

Browse files
authored
Fix FIC Auth support issues (#1547)
* Fix Arc pipeline issue for OCP * nit * download jq * Updated validation and docker * removed conftest * Fixed failed tests * fix aad token * update tests * Access token fix * revert import changes * fix test * Remove jq * Update image * fix test error * jq fix * nit * increase time window * Fix conf tests * update conf image
1 parent e56c521 commit 66f2426

File tree

9 files changed

+295
-415
lines changed

9 files changed

+295
-415
lines changed

build/common/installer/scripts/tomlparser.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@
2222
@excludePath = "*.csv2" #some invalid path
2323
@enrichContainerLogs = false
2424
@containerLogSchemaVersion = ""
25-
@collectAllKubeEvents = false
25+
@collectAllKubeEvents = ENV["AZMON_CLUSTER_COLLECT_ALL_KUBE_EVENTS"]&.downcase == "true" || false
2626
@containerLogsRoute = "v2" # default for linux
2727
@logEnableMultiline = "false"
2828
@stacktraceLanguages = "go,java,python" #supported languages for multiline logs. java is also used for dotnet stacktraces
29-
@logEnableKubernetesMetadata = false
29+
@logEnableKubernetesMetadata = ENV["AZMON_KUBERNETES_METADATA_ENABLED"]&.downcase == "true" || false
3030
@logKubernetesMetadataIncludeFields = "podlabels,podannotations,poduid,image,imageid,imagerepo,imagetag"
3131
@annotationBasedLogFiltering = false
3232
@allowed_system_namespaces = ['kube-system', 'gatekeeper-system', 'calico-system', 'azure-arc', 'kube-public', 'kube-node-lease']

test/e2e/conformance.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ sonobuoy-config:
66
plugin-name: azure-arc-monitor
77
result-format: junit
88
spec:
9-
image: mcr.microsoft.com/azuremonitor/containerinsights/cidev:ciconftest09102025
9+
image: mcr.microsoft.com/azuremonitor/containerinsights/cidev:ciconftest10282025
1010
imagePullPolicy: Always
1111
name: plugin
1212
resources: {}

test/e2e/e2e-tests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ data:
104104
- name: AZURE_CLOUD
105105
value: "AZURE_PUBLIC_CLOUD"
106106
# image tag should be updated if new tests being added after this image
107-
image: mcr.microsoft.com/azuremonitor/containerinsights/cidev:ciconftest09102025
107+
image: mcr.microsoft.com/azuremonitor/containerinsights/cidev:ciconftest10282025
108108
imagePullPolicy: IfNotPresent
109109
name: plugin
110110
resources: {}
Lines changed: 120 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,135 @@
1-
import pytest
21
import os
3-
import json
2+
import base64
43
import requests
5-
from azure.identity import ClientAssertionCredential, ClientSecretCredential, ManagedIdentityCredential, CertificateCredential
4+
import pytest
5+
from typing import Optional, Callable
6+
from azure.identity import (
7+
ClientAssertionCredential,
8+
ClientSecretCredential,
9+
ManagedIdentityCredential,
10+
CertificateCredential
11+
)
612

713
# Function that returns aad token credentials for a given spn
814
# Default behavior is to use managed identity, if use_cert_auth is set we attempt to use a certificate, if use_SPN_auth is set we fall back to SPN client secret auth
9-
def get_fed_token():
15+
def get_fed_token() -> str:
16+
"""Retrieve a federated (OIDC) token from Azure DevOps pipeline environment.
17+
18+
Expects the following environment variables to be set:
19+
- SYSTEM_ACCESSTOKEN
20+
- SERVICE_CONNECTION_ID
21+
- SYSTEM_OIDCREQUESTURI
22+
23+
Returns:
24+
JWT assertion string.
25+
26+
Raises:
27+
RuntimeError: if required env vars missing or request/response invalid.
28+
"""
1029
system_accesstoken = os.getenv('SYSTEM_ACCESSTOKEN')
1130
service_connection_id = os.getenv('SERVICE_CONNECTION_ID')
1231
system_oidc_request_uri = os.getenv('SYSTEM_OIDCREQUESTURI')
1332

14-
if system_accesstoken and service_connection_id and system_oidc_request_uri:
15-
# Construct the OIDC_REQUEST_URL
16-
oidc_request_url = f"{system_oidc_request_uri}?api-version=7.1&serviceConnectionId={service_connection_id}"
17-
# Preparing headers for ADO Pipeline OIDC authentication
18-
headers = {
19-
"Content-Length": "0",
20-
"Content-Type": "application/json",
21-
"Authorization": f"Bearer {system_accesstoken}"
22-
}
23-
24-
# Make the POST request
25-
response = requests.post(oidc_request_url, headers=headers)
26-
27-
# Check the response and extract the OIDC token
28-
if response.status_code == 200:
29-
# Assuming the response is JSON and has an 'oidcToken' field
30-
arm_oidc_token = response.json().get('oidcToken')
31-
print("Return Fed token")
32-
return arm_oidc_token
33-
else:
34-
print("Failed to retrieve FED Token:", response.status_code, response.text)
35-
36-
else:
37-
print("""
38-
One or more variables (SYSTEM_ACCESSTOKEN,
39-
SERVICE_CONNECTION_ID,
40-
SYSTEM_OIDCREQUESTURI) are either not set or empty.
41-
""")
42-
43-
def fetch_aad_token_credentials(tenant_id, client_id, client_secret, authority, use_cert_auth = False, use_SPN_auth = False, use_FIC_auth = False):
33+
missing = [name for name, val in [
34+
('SYSTEM_ACCESSTOKEN', system_accesstoken),
35+
('SERVICE_CONNECTION_ID', service_connection_id),
36+
('SYSTEM_OIDCREQUESTURI', system_oidc_request_uri)
37+
] if not val]
38+
if missing:
39+
raise RuntimeError(f"Missing required env vars for federated auth: {', '.join(missing)}")
40+
41+
oidc_request_url = f"{system_oidc_request_uri}?api-version=7.1&serviceConnectionId={service_connection_id}"
42+
headers = {
43+
"Content-Length": "0",
44+
"Content-Type": "application/json",
45+
"Authorization": f"Bearer {system_accesstoken}"
46+
}
47+
48+
try:
49+
response = requests.post(oidc_request_url, headers=headers, timeout=15)
50+
except Exception as ex:
51+
raise RuntimeError(f"HTTP request for federated token failed: {ex}") from ex
52+
53+
if response.status_code != 200:
54+
raise RuntimeError(f"Failed to retrieve federated token: status={response.status_code} body={response.text[:300]}")
55+
56+
arm_oidc_token = response.json().get('oidcToken')
57+
if not arm_oidc_token:
58+
raise RuntimeError(f"Response JSON missing 'oidcToken' field: {response.text[:300]}")
59+
return arm_oidc_token
60+
61+
62+
def build_scope(resource_endpoint: str) -> str:
63+
"""Return a resource scope suitable for Azure.Identity .get_token calls.
64+
65+
Ensures a single trailing slash before appending .default.
66+
Example: https://management.azure.com -> https://management.azure.com/.default
67+
"""
68+
resource_endpoint = resource_endpoint.rstrip('/')
69+
return f"{resource_endpoint}/.default"
70+
71+
def fetch_aad_token_credentials(
72+
tenant_id: str,
73+
client_id: Optional[str],
74+
client_secret: Optional[str],
75+
authority: str,
76+
use_cert_auth: bool = False,
77+
use_SPN_auth: bool = False,
78+
use_FIC_auth: bool = False
79+
):
80+
"""Return an Azure credential object based on selected auth mode.
81+
82+
Precedence / selection rules:
83+
- Exactly one of use_cert_auth, use_SPN_auth, use_FIC_auth may be True.
84+
- If all False, fall back to ManagedIdentityCredential.
85+
86+
Args:
87+
tenant_id: Azure AD tenant ID (guid).
88+
client_id: Application (client) ID or user-assigned managed identity client ID.
89+
client_secret: Secret or base64 cert bytes depending on mode.
90+
authority: Base authority host (e.g. https://login.microsoftonline.com).
91+
use_cert_auth: Use certificate assertion credential.
92+
use_SPN_auth: Use client secret credential.
93+
use_FIC_auth: Use federated identity credential (ClientAssertionCredential with get_fed_token).
94+
95+
Returns:
96+
Credential instance implementing get_token().
97+
"""
4498
try:
99+
modes_selected = sum(bool(x) for x in [use_cert_auth, use_SPN_auth, use_FIC_auth])
100+
if modes_selected > 1:
101+
raise ValueError("Only one auth mode may be enabled at a time.")
102+
45103
if use_FIC_auth:
46-
return ClientAssertionCredential(tenant_id=tenant_id, client_id=client_id, func=get_fed_token, authority=authority)
104+
if not client_id:
105+
raise ValueError("client_id required for federated identity auth")
106+
return ClientAssertionCredential(
107+
tenant_id=tenant_id,
108+
client_id=client_id,
109+
func=get_fed_token,
110+
authority=authority
111+
)
47112
if use_SPN_auth:
48-
return ClientSecretCredential(tenant_id=tenant_id, client_id=client_id, client_secret=client_secret, authority=authority)
113+
if not (client_id and client_secret):
114+
raise ValueError("client_id and client_secret required for SPN auth")
115+
return ClientSecretCredential(
116+
tenant_id=tenant_id,
117+
client_id=client_id,
118+
client_secret=client_secret,
119+
authority=authority
120+
)
49121
if use_cert_auth:
50-
import base64
122+
if not (client_id and client_secret):
123+
raise ValueError("client_id and client_secret (base64 cert) required for cert auth")
51124
cert_bytes = base64.b64decode(client_secret)
52-
return CertificateCredential(tenant_id=tenant_id, client_id=client_id, certificate_data=cert_bytes, send_certificate_chain=True)
53-
else:
54-
return ManagedIdentityCredential(client_id=client_id)
125+
return CertificateCredential(
126+
tenant_id=tenant_id,
127+
client_id=client_id,
128+
certificate_data=cert_bytes,
129+
send_certificate_chain=True,
130+
authority=authority
131+
)
132+
# Managed Identity path
133+
return ManagedIdentityCredential(client_id=client_id)
55134
except Exception as e:
56-
pytest.fail("Error occured while fetching credentials: " + str(e))
135+
pytest.fail("Error occurred while fetching credentials: " + str(e))

test/e2e/src/common/constants.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
AMA_LOGS_MAIN_CONTAINER_NAME = 'ama-logs'
4545

4646
# WAIT TIME BEFORE READING THE AGENT LOGS
47-
AGENT_WAIT_TIME_SECS = "180"
47+
AGENT_WAIT_TIME_SECS = "600"
4848
# Azure Monitor for Container Extension related
4949
AGENT_RESOURCES_NAMESPACE = 'kube-system'
5050
AGENT_DEPLOYMENT_NAME = 'ama-logs-rs'
@@ -74,7 +74,7 @@
7474
CONTAINER_INVENTORY_EMIT_STREAM = "containerInventoryEmitStreamSuccess"
7575

7676
# simple log analytics queries to validate for e2e workflows
77-
DEFAULT_QUERY_TIME_INTERVAL_IN_MINUTES = 10
77+
DEFAULT_QUERY_TIME_INTERVAL_IN_MINUTES = 15
7878
KUBE_POD_INVENTORY_QUERY = "KubePodInventory | where TimeGenerated > ago({0}) | count"
7979
KUBE_NODE_INVENTORY_QUERY = "KubeNodeInventory | where TimeGenerated > ago({0}) | count"
8080
KUBE_SERVICES_QUERY = "KubeServices | where TimeGenerated > ago({0}) | count"
@@ -101,7 +101,7 @@
101101
CONTAINER_PERF_RESTART_TIME_EPOCH_QUERY = "Perf | where ObjectName == 'K8SContainer' | where CounterName == 'restartTimeEpoch' | where TimeGenerated > ago({0}) | count"
102102
# container log
103103
CONTAINER_LOG_V2_QUERY = "ContainerLogV2 | where TimeGenerated > ago({0}) | count"
104-
CONTAINER_LOG_V2_K8S_METADATA_QUERY = "ContainerLogV2 | where TimeGenerated > ago({0}) | where KubernetesMetadata != "" | count"
104+
CONTAINER_LOG_V2_K8S_METADATA_QUERY = "ContainerLogV2 | where TimeGenerated > ago({0}) | where KubernetesMetadata != '' | count"
105105

106106
# insights metrics
107107
INSIGHTS_METRICS_QUERY = "InsightsMetrics | where TimeGenerated > ago({0}) | count"

test/e2e/src/core/Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
ARG PYTHON_BASE_IMAGE=python:3.6
33
FROM ${PYTHON_BASE_IMAGE}
44

5-
RUN pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org pytest pytest-xdist filelock requests kubernetes adal msrestazure
5+
RUN pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org \
6+
pytest pytest-xdist filelock requests kubernetes adal msrestazure azure-identity \
7+
azure-mgmt-hybridkubernetes azure-mgmt-kubernetesconfiguration junit-xml distro
68

79
RUN curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash \
810
&& helm version
911

1012
RUN apt-get update && apt-get -y upgrade && \
11-
apt-get -f -y install curl apt-transport-https lsb-release gnupg python3-pip && \
13+
apt-get -f -y install curl apt-transport-https lsb-release gnupg python3-pip jq && \
1214
curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /etc/apt/trusted.gpg.d/microsoft.asc.gpg && \
1315
CLI_REPO=$(lsb_release -cs) && \
1416
echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ ${CLI_REPO} main" \

test/e2e/src/core/conftest.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,6 @@ def env_dict():
6161
if not env_dict.get('CLIENT_ID'):
6262
pytest.fail('ERROR: variable CLIENT_ID is required.')
6363

64-
if not env_dict.get('CLIENT_SECRET'):
65-
pytest.fail('ERROR: variable CLIENT_SECRET is required.')
66-
6764
print("Setup Complete.")
6865
append_result_output("Setup Complete.\n", env_dict['SETUP_LOG_FILE'])
6966

test/e2e/src/core/e2e_tests.sh

Lines changed: 27 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -77,20 +77,6 @@ validateCommonParameters() {
7777
echo "ERROR: parameter TENANT_ID is required." > ${results_dir}/error
7878
python3 setup_failure_handler.py
7979
fi
80-
81-
## Look for WORKLOAD_CLIENT_ID
82-
if [[ -z "${WORKLOAD_CLIENT_ID}" ]]
83-
then
84-
if [ -z $CLIENT_ID ]; then
85-
echo "ERROR: parameter CLIENT_ID is required." > ${results_dir}/error
86-
python3 setup_failure_handler.py
87-
fi
88-
89-
if [ -z $CLIENT_SECRET ]; then
90-
echo "ERROR: parameter CLIENT_SECRET is required." > ${results_dir}/error
91-
python3 setup_failure_handler.py
92-
fi
93-
fi
9480
}
9581

9682
validateArcConfTestParameters() {
@@ -99,7 +85,7 @@ validateArcConfTestParameters() {
9985
python3 setup_failure_handler.py
10086
fi
10187

102-
if [ -z $RESOURCE_GROUP ]]; then
88+
if [ -z $RESOURCE_GROUP ]; then
10389
echo "ERROR: parameter RESOURCE_GROUP is required." > ${results_dir}/error
10490
python3 setup_failure_handler.py
10591
fi
@@ -157,47 +143,38 @@ deleteArcCIExtension() {
157143
login_to_azure() {
158144
## Federted Identity credentials authentication mechanism added.
159145
if [[ -v SYSTEM_ACCESSTOKEN && -n "$SYSTEM_ACCESSTOKEN" &&
160-
-v SERVICE_CONNECTION_ID && -n "$SERVICE_CONNECTION_ID" &&
161-
-v SYSTEM_OIDCREQUESTURI && -n "$SYSTEM_OIDCREQUESTURI" ]]; then
162-
163-
export OIDC_REQUEST_URL="${SYSTEM_OIDCREQUESTURI}?api-version=7.1&serviceConnectionId=${SERVICE_CONNECTION_ID}"
164-
echo "OIDC_REQUEST_URL= $OIDC_REQUEST_URL"
165-
166-
FED_TOKEN=$(curl -s -H "Content-Length: 0" -H "Content-Type: application/json" -H "Authorization: Bearer $SYSTEM_ACCESSTOKEN" -X POST $OIDC_REQUEST_URL | jq -r '.oidcToken')
167-
echo "FED_TOKEN= $FED_TOKEN"
168-
169-
echo "logging in using Federated Identity"
170-
az login --service-principal -u $FED_CLIENT_ID --tenant $TENANT_ID --allow-no-subscriptions --federated-token $FED_TOKEN 2> >(tee "${results_dir}/error" >&2) || python3 setup_failure_handler.py
171-
172-
elif [[ -v BASE_64_CLIENT_CERTIFICATE && -n "$BASE_64_CLIENT_CERTIFICATE" ]]; then
173-
BASE_64_CLIENT_CERTIFICATE=${BASE_64_CLIENT_CERTIFICATE#\"} # Remove leading quote
174-
BASE_64_CLIENT_CERTIFICATE=${BASE_64_CLIENT_CERTIFICATE%\"} # Remove trailing quote
175-
echo "logging in using Certificate '${BASE_64_CLIENT_CERTIFICATE}'"
176-
## create base64 cert to pem
177-
echo ${BASE_64_CLIENT_CERTIFICATE:0} | tr ' ' "\n" > base64_cert
178-
# convert base64 to PEM
179-
openssl base64 -d -in base64_cert -out clientcert.pem
180-
# minutes=60
181-
# seconds=$((minutes * 60))
182-
# sleep $seconds
183-
az login --service-principal --use-cert-sn-issuer -u $CLIENT_ID -p clientcert.pem --tenant $TENANT_ID 2> >(tee "${results_dir}/error" >&2) || python3 setup_failure_handler.py
184-
185-
elif [[ -z $WORKLOAD_CLIENT_ID ]]; then
146+
-v SERVICE_CONNECTION_ID && -n "$SERVICE_CONNECTION_ID" &&
147+
-v SYSTEM_OIDCREQUESTURI && -n "$SYSTEM_OIDCREQUESTURI" ]]; then
148+
export OIDC_REQUEST_URL="${SYSTEM_OIDCREQUESTURI}?api-version=7.1&serviceConnectionId=${SERVICE_CONNECTION_ID}"
149+
echo "OIDC_REQUEST_URL= $OIDC_REQUEST_URL"
150+
151+
FED_TOKEN=$(curl -s -H "Content-Length: 0" -H "Content-Type: application/json" -H "Authorization: Bearer $SYSTEM_ACCESSTOKEN" -X POST $OIDC_REQUEST_URL | jq -r '.oidcToken')
152+
echo "FED_TOKEN= $FED_TOKEN"
153+
154+
echo "logging in using Federated Identity"
155+
az login --service-principal -u $FED_CLIENT_ID --tenant $TENANT_ID --allow-no-subscriptions --federated-token $FED_TOKEN 2> ${results_dir}/error || python3 setup_failure_handler.py
156+
elif [[ -v BASE_64_CLIENT_CERTIFICATE && -n "$BASE_64_CLIENT_CERTIFICATE" ]]; then
157+
BASE_64_CLIENT_CERTIFICATE=${BASE_64_CLIENT_CERTIFICATE#\"} # Remove leading quote
158+
BASE_64_CLIENT_CERTIFICATE=${BASE_64_CLIENT_CERTIFICATE%\"} # Remove trailing quote
159+
echo "logging in using Certificate '${BASE_64_CLIENT_CERTIFICATE}'"
160+
## create base64 cert to pem
161+
echo ${BASE_64_CLIENT_CERTIFICATE:0} | tr ' ' "\n" > base64_cert
162+
openssl base64 -d -in base64_cert -out clientcert.pem
163+
az login --service-principal --use-cert-sn-issuer -u $CLIENT_ID -p clientcert.pem --tenant $TENANT_ID 2> ${results_dir}/error || python3 setup_failure_handler.py
164+
165+
elif [[ -v WORKLOAD_CLIENT_ID && -n "$WORKLOAD_CLIENT_ID" ]]; then
166+
echo "logging in using managed identity '${WORKLOAD_CLIENT_ID}'"
167+
az login --identity --client-id $WORKLOAD_CLIENT_ID 2> ${results_dir}/error || python3 setup_failure_handler.py
168+
else
186169
echo "logging in using service principal '${CLIENT_ID}'"
187170
az login --service-principal \
188171
-u ${CLIENT_ID} \
189172
-p ${CLIENT_SECRET} \
190173
--tenant ${TENANT_ID} 2> ${results_dir}/error || python3 setup_failure_handler.py
191-
else
192-
echo "logging in using managed identity '${WORKLOAD_CLIENT_ID}'"
193-
az login --identity \
194-
-u ${WORKLOAD_CLIENT_ID} 2> ${results_dir}/error || python3 setup_failure_handler.py
195174
fi
196-
197-
echo "setting subscription: ${SUBSCRIPTION_ID} as default subscription"
198-
##
199-
az account set \
200-
--subscription ${SUBSCRIPTION_ID} 2> >(tee "${results_dir}/error" >&2) || python3 setup_failure_handler.py
175+
176+
echo "setting subscription: ${SUBSCRIPTION_ID} as default subscription"
177+
az account set -s $SUBSCRIPTION_ID
201178
}
202179

203180

0 commit comments

Comments
 (0)