Pular para o conteúdo principal

Desafio 09: Proteger Recursos do Azure AI

Tempo Estimado

45-60 min | Custo: ~$1.00 (Key Vault, Private Endpoint) | Domínio: Planejar e Gerenciar Soluções de IA (20-25%)

Habilidades do exame cobertas

  • Gerenciar e proteger chaves de conta
  • Gerenciar autenticação para Azure AI Services
  • Configurar segurança de rede para recursos do Azure AI
  • Implementar identidade gerenciada para acesso seguro

Visão Geral

Proteger recursos do Azure AI envolve múltiplas camadas: proteger chaves de acesso, implementar isolamento de rede, usar identidades gerenciadas para autenticação sem chave e aplicar controle de acesso baseado em função. Um comprometimento das chaves de serviços de IA pode levar a uso não autorizado, exfiltração de dados e impacto financeiro significativo.

Neste desafio, você implementará uma postura de segurança abrangente para Azure AI Services. Você armazenará chaves no Azure Key Vault, implementará rotação de chaves sem tempo de inatividade, configurará regras de rede com private endpoints e fará a transição de autenticação baseada em chave para identidade gerenciada — a abordagem recomendada para cargas de trabalho em produção.

A abordagem de defesa em profundidade combina identidade (identidade gerenciada + RBAC), rede (private endpoints + regras de IP) e gerenciamento de segredos (Key Vault + rotação) para criar um limite de segurança robusto em torno dos seus serviços de IA.

Arquitetura

A arquitetura segura usa Key Vault para gerenciamento de segredos, identidade gerenciada para autenticação e private endpoints para isolamento de rede.

Challenge 09 topology

Pré-requisitos

  • Assinatura do Azure com função Contributor
  • Azure CLI instalado
  • Um recurso Azure AI Services (ou criará um)
  • Permissões para criar Key Vault e Private Endpoints
  • Uma rede virtual (ou criará uma)

Implementação

Tarefa 1: Armazenar Chave do AI Service no Azure Key Vault

from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
from azure.mgmt.cognitiveservices import CognitiveServicesManagementClient
import os

credential = DefaultAzureCredential()
subscription_id = "<your-subscription-id>"

# Get AI services key
cs_client = CognitiveServicesManagementClient(credential, subscription_id)
keys = cs_client.accounts.list_keys(
resource_group_name="rg-ai102-challenge09",
account_name="ai-secure-demo"
)

# Store key in Key Vault
vault_url = "https://kv-ai102-secure.vault.azure.net/"
secret_client = SecretClient(vault_url=vault_url, credential=credential)

# Store both keys for rotation purposes
secret_client.set_secret(
name="ai-services-key1",
value=keys.key1,
content_type="text/plain",
tags={"service": "cognitive-services", "key-number": "1"}
)

secret_client.set_secret(
name="ai-services-key2",
value=keys.key2,
content_type="text/plain",
tags={"service": "cognitive-services", "key-number": "2"}
)

# Store endpoint
secret_client.set_secret(
name="ai-services-endpoint",
value="https://ai-secure-demo.cognitiveservices.azure.com/",
content_type="text/plain",
tags={"service": "cognitive-services"}
)

print("Secrets stored in Key Vault:")
print(f" ai-services-key1: ****{keys.key1[-4:]}")
print(f" ai-services-key2: ****{keys.key2[-4:]}")
print(f" ai-services-endpoint: stored")

# Retrieve key from Key Vault for use
retrieved_key = secret_client.get_secret("ai-services-key1")
print(f"\nRetrieved key from vault: ****{retrieved_key.value[-4:]}")

Tarefa 2: Implementar Rotação de Chaves Sem Tempo de Inatividade

from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
from azure.mgmt.cognitiveservices import CognitiveServicesManagementClient
import time

credential = DefaultAzureCredential()
subscription_id = "<your-subscription-id>"

cs_client = CognitiveServicesManagementClient(credential, subscription_id)
secret_client = SecretClient(
vault_url="https://kv-ai102-secure.vault.azure.net/",
credential=credential
)

resource_group = "rg-ai102-challenge09"
account_name = "ai-secure-demo"

def rotate_key_zero_downtime(key_to_rotate: str = "Key1"):
"""
Zero-downtime key rotation strategy:
1. Applications use Key1 (active)
2. Regenerate Key2 (inactive) → new Key2
3. Update applications to use Key2
4. Regenerate Key1 → new Key1
5. Applications now use Key2, Key1 is fresh backup
"""
print(f"=== Starting Zero-Downtime Key Rotation ===")

# Step 1: Determine which key is currently active
active_secret = secret_client.get_secret("ai-services-active-key")
active_key_name = active_secret.value # "key1" or "key2"
inactive_key_name = "key2" if active_key_name == "key1" else "key1"
print(f"Step 1: Active key is {active_key_name}")

# Step 2: Regenerate the INACTIVE key
print(f"Step 2: Regenerating inactive {inactive_key_name}...")
regenerated = cs_client.accounts.regenerate_key(
resource_group_name=resource_group,
account_name=account_name,
parameters={"keyName": inactive_key_name.capitalize()}
)
new_key_value = regenerated.key2 if inactive_key_name == "key2" else regenerated.key1
print(f" New {inactive_key_name} generated: ****{new_key_value[-4:]}")

# Step 3: Update Key Vault with new inactive key
print(f"Step 3: Updating Key Vault with new {inactive_key_name}...")
secret_client.set_secret(
f"ai-services-{inactive_key_name}",
new_key_value
)

# Step 4: Switch active key pointer to the newly regenerated key
print(f"Step 4: Switching active key to {inactive_key_name}...")
secret_client.set_secret("ai-services-active-key", inactive_key_name)

# Step 5: Allow time for applications to pick up new key
print("Step 5: Waiting for cache expiry (applications refresh)...")
time.sleep(5) # In production: wait for app cache TTL

# Step 6: Now regenerate the old active key (it's no longer in use)
print(f"Step 6: Regenerating old active {active_key_name}...")
cs_client.accounts.regenerate_key(
resource_group_name=resource_group,
account_name=account_name,
parameters={"keyName": active_key_name.capitalize()}
)

print(f"\n✓ Rotation complete. Active key: {inactive_key_name}")

# Initialize active key tracking
secret_client.set_secret("ai-services-active-key", "key1")

# Perform rotation
rotate_key_zero_downtime()

Tarefa 3: Configurar Segurança de Rede e Private Endpoint

from azure.identity import DefaultAzureCredential
from azure.mgmt.cognitiveservices import CognitiveServicesManagementClient
from azure.mgmt.network import NetworkManagementClient

credential = DefaultAzureCredential()
subscription_id = "<your-subscription-id>"
resource_group = "rg-ai102-challenge09"

cs_client = CognitiveServicesManagementClient(credential, subscription_id)
network_client = NetworkManagementClient(credential, subscription_id)

# Step 1: Configure network rules - deny public access by default
print("Configuring network rules...")
cs_client.accounts.begin_update(
resource_group_name=resource_group,
account_name="ai-secure-demo",
account={
"properties": {
"publicNetworkAccess": "Disabled",
"networkAcls": {
"defaultAction": "Deny",
"ipRules": [
{"value": "203.0.113.0/24"} # Allow specific IP range
],
"virtualNetworkRules": []
}
}
}
).result()
print(" Public access disabled, IP whitelist configured")

# Step 2: Create VNet and Subnet for Private Endpoint
print("\nCreating VNet and subnet...")
vnet = network_client.virtual_networks.begin_create_or_update(
resource_group_name=resource_group,
virtual_network_name="vnet-ai102",
parameters={
"location": "eastus",
"properties": {
"addressSpace": {"addressPrefixes": ["10.0.0.0/16"]},
"subnets": [
{
"name": "snet-ai-services",
"properties": {
"addressPrefix": "10.0.1.0/24",
"privateEndpointNetworkPolicies": "Disabled"
}
}
]
}
}
).result()
print(f" VNet created: {vnet.name}")

# Step 3: Create Private Endpoint
print("\nCreating Private Endpoint...")
ai_resource_id = (
f"/subscriptions/{subscription_id}/resourceGroups/{resource_group}"
f"/providers/Microsoft.CognitiveServices/accounts/ai-secure-demo"
)

pe = network_client.private_endpoints.begin_create_or_update(
resource_group_name=resource_group,
private_endpoint_name="pe-ai-secure",
parameters={
"location": "eastus",
"properties": {
"subnet": {
"id": f"{vnet.id}/subnets/snet-ai-services"
},
"privateLinkServiceConnections": [
{
"name": "ai-services-connection",
"properties": {
"privateLinkServiceId": ai_resource_id,
"groupIds": ["account"]
}
}
]
}
}
).result()
print(f" Private Endpoint created: {pe.name}")
print(f" Private IP: {pe.custom_dns_configs[0].ip_addresses[0] if pe.custom_dns_configs else 'pending'}")

Tarefa 4: Atribuir Funções RBAC e Configurar Identidade Gerenciada

from azure.identity import DefaultAzureCredential, ManagedIdentityCredential
from azure.mgmt.authorization import AuthorizationManagementClient
from azure.mgmt.msi import ManagedServiceIdentityClient
from azure.ai.textanalytics import TextAnalyticsClient
import os
import uuid

credential = DefaultAzureCredential()
subscription_id = "<your-subscription-id>"
resource_group = "rg-ai102-challenge09"

auth_client = AuthorizationManagementClient(credential, subscription_id)

ai_resource_id = (
f"/subscriptions/{subscription_id}/resourceGroups/{resource_group}"
f"/providers/Microsoft.CognitiveServices/accounts/ai-secure-demo"
)

# Create a user-assigned managed identity
msi_client = ManagedServiceIdentityClient(credential, subscription_id)
identity = msi_client.user_assigned_identities.create_or_update(
resource_group_name=resource_group,
resource_name="id-ai-services-reader",
parameters={"location": "eastus"}
)
print(f"Managed Identity created: {identity.name}")
print(f" Client ID: {identity.client_id}")
print(f" Principal ID: {identity.principal_id}")

# Assign "Cognitive Services User" role to the managed identity
# This role allows calling AI service APIs without needing keys
COGNITIVE_SERVICES_USER_ROLE = "a97b65f3-24c7-4388-baec-2e87135dc908"

role_assignment = auth_client.role_assignments.create(
scope=ai_resource_id,
role_assignment_name=str(uuid.uuid4()),
parameters={
"properties": {
"roleDefinitionId": f"/subscriptions/{subscription_id}/providers/Microsoft.Authorization/roleDefinitions/{COGNITIVE_SERVICES_USER_ROLE}",
"principalId": identity.principal_id,
"principalType": "ServicePrincipal"
}
}
)
print(f"\nRole assigned: Cognitive Services User")
print(f" Scope: {ai_resource_id}")

# Use managed identity to authenticate (no keys needed!)
# In a VM or App Service with the identity assigned:
endpoint = os.environ["AZURE_AI_ENDPOINT"]

# With managed identity - no key required
mi_credential = ManagedIdentityCredential(client_id=identity.client_id)
client = TextAnalyticsClient(
endpoint=endpoint,
credential=mi_credential # Uses token-based auth, not API key
)

# This is the recommended production pattern
documents = ["Azure AI services support managed identity authentication."]
response = client.detect_language(documents=documents)
for doc in response:
if not doc.is_error:
print(f"\nLanguage detected: {doc.primary_language.name}")
print(" ✓ Authenticated via Managed Identity (keyless)")

Saída Esperada

Secrets stored in Key Vault:
ai-services-key1: ****a1b2
ai-services-key2: ****c3d4
ai-services-endpoint: stored

=== Starting Zero-Downtime Key Rotation ===
Step 1: Active key is key1
Step 2: Regenerating key2...
New key2 generated: ****x7y8
Step 3: Updating Key Vault with new key2...
Step 4: Switching active key to key2...
Step 5: Waiting for cache expiry...
Step 6: Regenerating old key1...
✓ Rotation complete. Active key: key2

Managed Identity created: id-ai-services-reader
Client ID: 12345678-abcd-efgh-ijkl-123456789012
Principal ID: 87654321-dcba-hgfe-lkji-210987654321
Role assigned: Cognitive Services User

Language detected: English
✓ Authenticated via Managed Identity (keyless)

Quebra & conserta

CenárioSintomaCausa RaizCorreção
Acesso negado ao Key Vault403 Forbidden ao ler segredosFunção RBAC ou política de acesso do Key Vault ausenteAtribua a função Key Vault Secrets User à identidade chamadora
Private endpoint não resolveResolução DNS retorna IP públicoZona DNS Privada ou link de VNet ausenteCrie a zona DNS privatelink.cognitiveservices.azure.com e vincule à VNet
Autenticação de identidade gerenciada falha401 "InvalidAuthenticationToken"Atribuição de função RBAC não propagada (até 5 min)Aguarde 5 minutos para propagação da atribuição de função; verifique o principalId correto
Rotação de chave causa tempo de inatividadeRequisições falham com 401 durante a rotaçãoAplicação armazena chaves em cache e não atualizaImplemente cache de chaves com TTL; rotacione a chave inativa primeiro (estratégia de duas chaves)
Regra de rede bloqueia tráfego legítimo403 de endereço IP permitidoIP está atrás de NAT/proxy com IP de saída diferenteAdicione o IP de saída real à lista de permissões; use curl ifconfig.me para encontrá-lo

Verificação de Conhecimento

1. Qual função RBAC do Azure permite que uma identidade chame APIs do Azure AI Service sem exigir uma chave de API?

2. Durante a rotação de chaves sem tempo de inatividade, qual chave você deve regenerar PRIMEIRO?

3. Qual nome de zona DNS é necessário para resolução de private endpoint do Azure Cognitive Services?

4. Qual é o método de autenticação recomendado para cargas de trabalho de produção do Azure AI executando no Azure?

5. Quando você define 'publicNetworkAccess' como 'Disabled' em um recurso do Azure AI, o que acontece com as requisições existentes baseadas em chave de API vindas da internet?

Limpeza

az group delete --name rg-ai102-challenge09 --yes --no-wait

Saiba Mais