Entra ID Conditional Access blokuje Service Principal lub Managed Identity
Naprawa blokowania Service Principal przez Conditional Access: analiza Sign-in Logs, wykluczenie workload identities z polityk, konfiguracja Workload Identity Premium.
Ten runbook opisuje diagnozę i naprawę blokowania service principali przez Conditional Access po wdrożeniu Zero Trust. Kompleksowy przewodnik po strategii zarządzania tożsamościami znajdziesz w artykule Azure AD i zarządzanie tożsamościami - jak wdrożyć Zero Trust. Jeśli potrzebujesz pomocy z konfiguracją Conditional Access i workload identities - umów konsultacje.
Objaw
Autoryzacja service principal lub managed identity kończy się błędem AADSTS53003: Access has been blocked by Conditional Access policies. Pipeline CI/CD, Terraform plan/apply, skrypty az CLI i wywołania Microsoft Graph API przestają działać:
# Próba logowania jako service principal kończy się błędem
az login --service-principal -u <app-id> -p <secret> --tenant <tenant-id>
# ERROR: AADSTS53003: Access has been blocked by Conditional Access policies.
# Trace ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# Sprawdź Sign-in Logs dla service principali (wymaga roli Global Reader lub Security Reader)
# Logowania nieinteraktywne (service principals) - osobna sekcja w Entra ID
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/auditLogs/signIns?\$filter=signInEventTypes/any(t: t eq 'servicePrincipal') and status/errorCode eq 53003&\$top=10&\$orderby=createdDateTime desc" \
--query "value[].{app:appDisplayName, appId:appId, time:createdDateTime, policy:appliedConditionalAccessPolicies[0].displayName, result:appliedConditionalAccessPolicies[0].result}" -o table
# Alternatywnie: sprawdź logi dla konkretnego service principal (appId)
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/auditLogs/signIns?\$filter=appId eq '<app-id>' and status/errorCode eq 53003&\$top=5&\$orderby=createdDateTime desc" \
--query "value[].{time:createdDateTime, resource:resourceDisplayName, ip:ipAddress, policy:appliedConditionalAccessPolicies[].displayName, failure:status.failureReason}" -o json
W wyniku identyfikujesz nazwę polityki CA, która blokowała logowanie. Pole appliedConditionalAccessPolicies[].result wskazuje failure dla polityki wymuszającej blokadę.
# Sprawdź szczegóły konkretnej polityki Conditional Access
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" \
--query "value[?displayName=='Zero Trust - Require MFA All Apps'].{id:id, name:displayName, state:state, conditions:conditions, grantControls:grantControls}" -o json
Przyczyna
Conditional Access po wdrożeniu Zero Trust blokuje service principali z kilku powodów:
-
Polityki targetujące “All cloud apps” obejmują service principali: Nowe polityki CA z warunkiem
includeApplications: AlllubincludeUsers: Alldziałają również na logowania nieinteraktywne (service principal sign-ins). W trybie “All users” service principali NIE są domyślnie wykluczeni - Entra ID traktuje je jako podmioty podlegające polityce. -
Wymaganie MFA lub urządzenia zgodnego (Compliant Device) jest niespełnialne dla tożsamości maszynowych: Service principal nie ma fizycznego urządzenia i nie może wykonać uwierzytelniania wieloskładnikowego. Polityka wymagająca
grant: mfalubgrant: compliantDeviceautomatycznie blokuje każde logowanie nieinteraktywne. -
Nowe polityki “Workload Identities” bez odpowiednich wykluczeń: Od 2023 roku Entra ID umożliwia tworzenie polityk CA dedykowanych workload identities. Jeśli zespół bezpieczeństwa wdrożył takie polityki z warunkiem lokalizacji (Named Locations) lub ryzyka, a nie uwzględnił IP agentów CI/CD - logowania z GitHub Actions lub Azure DevOps hosted agents są blokowane.
-
Warunek Named Locations blokuje logowania z adresów IP agentów CI/CD: Agenty GitHub Actions, Azure DevOps hosted agents i inne systemy CI korzystają z dynamicznych adresów IP w chmurze. Polityka CA ograniczająca dostęp do „zaufanych lokalizacji” blokuje logowania z tych adresów, ponieważ nie znajdują się one w zdefiniowanych zakresach IP.
-
Polityka “Block legacy authentication” obejmuje przepływ OAuth2 client credentials: Niektóre polityki blokujące legacy auth (Basic, IMAP, SMTP) przez pomyłkę obejmują również nowoczesne przepływy client credentials, jeśli warunki filtrowania nie są precyzyjne.
Rozwiązanie
A) Wyklucz service principali z polityk CA targetujących użytkowników:
Service principali należy wykluczyć z polityk przeznaczonych dla użytkowników ludzkich. Użyj excludeServicePrincipals lub warunku clientAppTypes:
# Pobierz ID polityki, która blokuje
POLICY_ID=$(az rest --method GET \
--url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" \
--query "value[?displayName=='Zero Trust - Require MFA All Apps'].id" -o tsv)
# Sprawdź aktualną konfigurację wykluczeń
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/$POLICY_ID" \
--query "{users:conditions.users, apps:conditions.applications, clientApps:conditions.clientAppTypes}" -o json
# Dodaj wykluczenie service principali przez filtr clientAppTypes
# Ogranicz politykę TYLKO do logowań interaktywnych (browser + mobile apps)
az rest --method PATCH \
--url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/$POLICY_ID" \
--headers "Content-Type=application/json" \
--body '{
"conditions": {
"clientAppTypes": ["browser", "mobileAppsAndDesktopClients"]
}
}'
# Alternatywnie: wyklucz konkretne service principali z pola users.excludeServicePrincipals
# (wymaga licencji Workload Identity Premium)
az rest --method PATCH \
--url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/$POLICY_ID" \
--headers "Content-Type=application/json" \
--body '{
"conditions": {
"users": {
"excludeServicePrincipals": ["<service-principal-object-id-1>", "<service-principal-object-id-2>"]
}
}
}'
Uwaga: Pole
excludeServicePrincipalswymaga licencji Workload Identity Premium ($3/tożsamość/miesiąc). Bez tej licencji użyj warunkuclientAppTypesjako obejścia.
B) Utwórz dedykowaną politykę CA dla Workload Identities:
Zamiast wykluczać SP z polityk użytkowników - utwórz oddzielną politykę celowaną w workload identities z odpowiednimi warunkami:
# Utwórz Named Location dla IP runnerów CI/CD
az rest --method POST \
--url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/namedLocations" \
--headers "Content-Type=application/json" \
--body '{
"@odata.type": "#microsoft.graph.ipNamedLocation",
"displayName": "CI/CD Runners - GitHub Actions and Azure DevOps",
"isTrusted": true,
"ipRanges": [
{"@odata.type": "#microsoft.graph.iPv4CidrRange", "cidrAddress": "4.148.0.0/14"},
{"@odata.type": "#microsoft.graph.iPv4CidrRange", "cidrAddress": "13.64.0.0/11"},
{"@odata.type": "#microsoft.graph.iPv4CidrRange", "cidrAddress": "20.36.0.0/14"}
]
}'
# Pobierz ID nowej Named Location
LOCATION_ID=$(az rest --method GET \
--url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/namedLocations" \
--query "value[?displayName=='CI/CD Runners - GitHub Actions and Azure DevOps'].id" -o tsv)
# Utwórz politykę CA dla workload identities - blokuj logowania spoza zaufanych lokalizacji
az rest --method POST \
--url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" \
--headers "Content-Type=application/json" \
--body '{
"displayName": "Workload Identity - Block untrusted locations",
"state": "enabledForReportingButNotEnforced",
"conditions": {
"clientApplications": {
"includeServicePrincipals": ["All"]
},
"applications": {
"includeApplications": ["All"]
},
"locations": {
"includeLocations": ["All"],
"excludeLocations": ["'$LOCATION_ID'", "AllTrusted"]
}
},
"grantControls": {
"operator": "OR",
"builtInControls": ["block"]
}
}'
Ważne: Zawsze zaczynaj z
state: enabledForReportingButNotEnforced(tryb tylko raportowania). Dopiero po walidacji w Sign-in Logs przełącz naenabled.
C) Użyj Managed Identity zamiast Service Principal:
Managed Identity (system-assigned lub user-assigned) omija większość polityk CA, ponieważ logowanie odbywa się wewnętrznie w Azure bez przepływu przez Entra ID Conditional Access:
# Dla Azure DevOps - włącz Managed Identity na self-hosted agencie (VM)
az vm identity assign \
--resource-group rg-devops \
--name vm-build-agent \
--role "Contributor" \
--scope /subscriptions/<sub-id>/resourceGroups/rg-production
# Dla GitHub Actions - użyj Workload Identity Federation (OIDC)
# Utwórz federated credential dla GitHub Actions (bez secretów!)
az ad app federated-credential create \
--id <app-object-id> \
--parameters '{
"name": "github-actions-main",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:my-org/my-repo:ref:refs/heads/main",
"audiences": ["api://AzureADTokenExchange"]
}'
# W GitHub Actions workflow - logowanie przez OIDC (nie podlega CA jak client_credentials)
# .github/workflows/deploy.yml:
# - uses: azure/login@v2
# with:
# client-id: $
# tenant-id: $
# subscription-id: $
Uwaga: Managed Identity na zasobach Azure (App Service, VM, AKS) NIE podlega politykom CA w większości scenariuszy. Workload Identity Federation (OIDC) podlega politykom CA dla workload identities, ale nie wymaga secretów - co eliminuje problem rotacji i wycieku credentials.
D) Skonfiguruj Named Locations dla zakresów IP agentów CI/CD:
Jeśli musisz utrzymać politykę CA na workload identities z warunkiem lokalizacji - dodaj zakresy IP agentów jako zaufane:
# Pobierz aktualne zakresy IP GitHub Actions (zmienne - sprawdzaj regularnie)
curl -s https://api.github.com/meta | jq '.actions[]' | head -20
# Pobierz zakresy IP Azure DevOps hosted agents
curl -s https://dev.azure.com/<org>/_apis/distributedtask/serviceconnectionproxies | jq '.value[].ipAddress'
# Zaktualizuj Named Location o nowe zakresy IP
LOCATION_ID="<named-location-id>"
az rest --method PATCH \
--url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/namedLocations/$LOCATION_ID" \
--headers "Content-Type=application/json" \
--body '{
"ipRanges": [
{"@odata.type": "#microsoft.graph.iPv4CidrRange", "cidrAddress": "4.148.0.0/14"},
{"@odata.type": "#microsoft.graph.iPv4CidrRange", "cidrAddress": "13.64.0.0/11"},
{"@odata.type": "#microsoft.graph.iPv4CidrRange", "cidrAddress": "20.36.0.0/14"},
{"@odata.type": "#microsoft.graph.iPv4CidrRange", "cidrAddress": "20.40.0.0/13"},
{"@odata.type": "#microsoft.graph.iPv4CidrRange", "cidrAddress": "40.74.0.0/15"}
]
}'
Uwaga: Zakresy IP GitHub Actions zmieniają się regularnie. Zautomatyzuj aktualizację Named Locations za pomocą Azure Function lub Logic App odpytującej
https://api.github.com/meta.
Walidacja
# 1. Test logowania service principal po zmianach
az login --service-principal \
-u <app-id> \
-p <secret> \
--tenant <tenant-id>
# Expected: logowanie pomyślne, brak błędu AADSTS53003
# 2. Sprawdź Sign-in Logs - brak nowych blokad CA
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/auditLogs/signIns?\$filter=appId eq '<app-id>' and createdDateTime ge $(date -u -d '-15 minutes' +%Y-%m-%dT%H:%M:%SZ)&\$top=5" \
--query "value[].{time:createdDateTime, status:status.errorCode, policy:appliedConditionalAccessPolicies[].{name:displayName,result:result}}" -o json
# Expected: status.errorCode = 0 (success), polityki z result = "notApplied" lub "success"
# 3. Test operacji end-to-end (np. Terraform plan)
terraform plan -out=tfplan
# Expected: plan generuje się bez błędów autoryzacji
# 4. Sprawdź politykę w Report-only mode (jeśli użyto opcji B)
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/auditLogs/signIns?\$filter=appId eq '<app-id>' and appliedConditionalAccessPolicies/any(p: p/displayName eq 'Workload Identity - Block untrusted locations' and p/result eq 'reportOnlyFailure')&\$top=5" \
--query "value[].{time:createdDateTime, ip:ipAddress}" -o table
# Expected: brak wpisów (logowania z CI/CD nie są blokowane w report-only)
# 5. Skonfiguruj alert na przyszłe blokady CA dla service principali
az monitor scheduled-query create \
--name "alert-ca-blocks-service-principals" \
--resource-group rg-monitoring \
--scopes "/subscriptions/<sub-id>/resourceGroups/rg-monitoring/providers/Microsoft.OperationalInsights/workspaces/law-security" \
--condition "count 'SigninLogs | where ResultType == 53003 and ServicePrincipalId != \"\" | where TimeGenerated > ago(15m)' > 0" \
--severity 2 \
--evaluation-frequency 5m \
--window-size 15m \
--action-groups "/subscriptions/<sub-id>/resourceGroups/rg-monitoring/providers/Microsoft.Insights/actionGroups/ag-platform-team" \
--description "Conditional Access blokuje service principal - wymagana interwencja"
Po potwierdzeniu, że logowanie SP działa i pipeline przechodzą pomyślnie, przełącz politykę z trybu tylko raportowania na Enabled (jeśli stosujesz rozwiązanie B):
az rest --method PATCH \
--url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/<policy-id>" \
--headers "Content-Type=application/json" \
--body '{"state": "enabled"}'
Conditional Access blokuje Twoje automatyzacje?
Umów bezpłatną 30-minutową rozmowę. Przeanalizujemy polityki CA, skonfigurujemy wykluczenia workload identities i wdrożymy strategię Zero Trust bezpieczną dla CI/CD.