4. Azure RBAC实战

Azure RBAC 的核心思想是"在正确的范围内,把正确的角色授予正确的主体"。上一节我们理解了角色定义、安全主体和作用域三要素,这一节把它们落到三个最典型的数据服务上:存储账户、Azure SQL 数据库和 Key Vault。这三个服务的权限模型各有特点——存储账户区分"管理平面"和"数据平面",SQL 数据库有服务器级和数据库级两层,Key Vault 则把密钥、机密、证书的权限拆得很细。如果只授予"参与者"或"所有者",等于把整个服务的读写删权限全部交出,这恰恰是我们要避免的。

先分清管理平面与数据平面

这是分配最小权限时最容易踩的坑。以存储账户为例,授予"存储账户参与者"(Storage Account Contributor)只允许你管理账户本身——查看密钥、配置网络、修改复制策略,但不能读写 Blob 里的数据。要读写数据,必须授予数据平面角色,如"存储 Blob 数据读取者"(Storage Blob Data Reader)或"存储 Blob 数据参与者"(Storage Blob Data Contributor)。很多初学者发现"我明明是参与者却读不了文件",原因就在这里。

下图展示了三个数据服务中管理平面与数据平面角色的对应关系,以及各自的最小权限落点。

三大数据服务的 RBAC 角色分层与最小权限落点 存储账户 管理平面 存储账户参与者 管账户/密钥/网络,不碰数据 数据平面 Blob 数据读取者 Blob 数据参与者 读写 Blob 内容 最小权限落点 只读→Reader+Blob读取者 作用域建议 容器级 > 账户级 按容器分别授权 Azure SQL 数据库 管理平面 SQL DB 参与者 管服务器/数据库资源 数据平面 SQL DB 读取者 SQL DB 参与者 连接并读写表数据 最小权限落点 只读→SQL DB 读取者 作用域建议 数据库级 > 服务器级 避免整服务器授权 Key Vault 管理平面 Key Vault 参与者 管保管库属性/访问策略 数据平面(细分) 机密读取者 / 密钥读取者 证书读取者 / 机密管理者 按对象类型分别授权 最小权限落点 只读机密→机密读取者 作用域建议 单个机密级 > 保管库级 RBAC 模式优于访问策略

用 Azure CLI 分配角色

假设我们有一个资源组 rg-data-prod,里面有一个存储账户 stdataops01、一个 SQL 数据库 sqldb-orders 和一个 Key Vault kv-app-prod。现在需要给一个应用团队的安全主体(这里用服务主体 sp-app-team 表示)分配最小权限:只读 Blob、只读 SQL 数据、只读机密。

第一步,拿到服务主体的对象 ID。角色分配需要的是 --assignee-object-id,而不是应用 ID:

az ad sp show --id <appId> --query id -o tsv
# 假设返回:11111111-2222-3333-4444-555555555555

第二步,为存储账户分配 Blob 数据读取者。注意作用域精确到容器,而不是整个账户:

az role assignment create \
  --assignee-object-id 11111111-2222-3333-4444-555555555555 \
  --assignee-principal-type ServicePrincipal \
  --role "Storage Blob Data Reader" \
  --scope "/subscriptions/<subId>/resourceGroups/rg-data-prod/providers/Microsoft.Storage/storageAccounts/stdataops01/blobServices/default/containers/orders"

第三步,为 SQL 数据库分配"SQL DB 读取者",作用域落在数据库级:

az role assignment create \
  --assignee-object-id 11111111-2222-3333-4444-555555555555 \
  --assignee-principal-type ServicePrincipal \
  --role "Reader" \
  --scope "/subscriptions/<subId>/resourceGroups/rg-data-prod/providers/Microsoft.Sql/servers/sqlsrv-prod/databases/sqldb-orders"

az role assignment create \
  --assignee-object-id 11111111-2222-3333-4444-555555555555 \
  --assignee-principal-type ServicePrincipal \
  --role "SQL DB Reader" \
  --scope "/subscriptions/<subId>/resourceGroups/rg-data-prod/providers/Microsoft.Sql/servers/sqlsrv-prod/databases/sqldb-orders"

第四步,为 Key Vault 分配"Key Vault 机密读取者"。前提是保管库已启用 RBAC 权限模型(enableRbacAuthorization: true),否则角色分配不会生效:

az role assignment create \
  --assignee-object-id 11111111-2222-3333-4444-555555555555 \
  --assignee-principal-type ServicePrincipal \
  --role "Key Vault Secrets Reader" \
  --scope "/subscriptions/<subId>/resourceGroups/rg-data-prod/providers/Microsoft.KeyVault/vaults/kv-app-prod"

用 PowerShell 验证权限

分配完成后必须验证,否则你无法确认权限是否真的生效、是否过大。PowerShell 的 Get-AzRoleAssignment 可以按主体列出所有分配:

Connect-AzAccount
$spId = "11111111-2222-3333-4444-555555555555"

Get-AzRoleAssignment -ObjectId $spId |
  Select-Object RoleDefinitionName, Scope |
  Format-Table -AutoSize

预期输出应只包含三条记录:Storage Blob Data Reader(容器级)、SQL DB Reader(数据库级)、Key Vault Secrets Reader(保管库级)。如果出现了"参与者"或"所有者",说明权限过大,需要删除后重新分配。

更进一步的验证是"以该主体身份实际访问"。可以用 Test-AzKeyVaultSecret 或直接尝试读取机密,确认能读到;再尝试写入机密,确认被拒绝——这才是最小权限的真正证明。对于存储账户,可以用 Get-AzStorageBlob 读取,用 Set-AzStorageBlobContent 写入,观察后者是否返回 403。

验证时建议同时检查"有效权限"而非仅看角色分配。Azure 门户的"访问控制 (IAM)"→"检查访问权限"可以模拟某个主体对某资源的最终权限,能发现继承自订阅或管理组的隐藏授权。

常见误区与排查

第一个误区是只分配管理平面角色就以为能访问数据。第二个误区是作用域给得太大,比如把 Blob 读取者授到订阅级,导致该主体能读所有存储账户的 Blob。第三个误区是 Key Vault 仍在使用访问策略模式,此时 RBAC 角色分配不生效,必须先在保管库上切换到 RBAC 模式。

排查时按这个顺序:先确认主体对象 ID 是否正确(服务主体和应用 ID 容易混淆),再确认作用域路径是否拼写正确,然后确认角色名称是否与内置角色完全一致,最后确认服务的权限模型(Key Vault 的 RBAC/访问策略、存储的平面区分)。这四步能覆盖绝大多数"分配了却没权限"的问题。

删除角色分配时使用 az role assignment delete --assignee <objectId> --role <roleName> --scope <scope>。删除前务必确认作用域,避免误删其他团队在同一资源上的授权。

把这三个服务的最小权限分配串起来看,规律是一致的:先判断需要的是管理平面还是数据平面权限,再把作用域收窄到实际需要的最小对象(容器、数据库、单个机密),最后用 PowerShell 列出分配并做一次实际访问测试。做到这三点,RBAC 才真正从"能授权"变成"授得准"。