Azure IAM 标识RBAC学习 2024-11-16 08:58:02 Steven Xeldax Azure 标识、权限、IAM机制我们需要了解大概这几个方面的东西: * Identity: Azure中的标识,RBAC的资源,授权给予给权限的目标主要有用户,机器,服务主体(服务主体又包含注册应用,managed identity)。 * Role:角色,默认的角色有owner,contributor,reader三种,当然如果有专业的PIM权限管理员,我们可以自定义role权限来进行分配。 * Resouce:资源有针对Entra ID的,也有针对Azure 资源Resouce的 ## Azure Identity 在 Microsoft Entra 中,工作负载身份是应用程序、服务主体和托管身份。 应用程序是由其应用程序对象定义的抽象实体或模板。应用程序对象是应用程序的全局表示,可供所有租户使用。应用程序对象描述了如何颁发令牌、应用程序需要访问的资源以及应用程序可以执行的操作。 服务主体是特定租户中全局应用程序对象的本地表示或应用程序实例。应用程序对象用作模板,在使用该应用程序的每个租户中创建服务主体对象。服务主体对象定义应用程序在特定租户中实际上可以执行的操作、谁可以访问该应用程序以及应用程序可以访问哪些资源。 托管标识是一种特殊类型的服务主体,它消除了开发人员管理凭据的需要。  ## Azure IAM Azure IAM是一个综合性的安全系统,旨在通过身份验证、授权和审计等功能,保护Azure资源免受未经授权的访问。它允许企业根据业务需求、组织结构和安全策略,精细控制对云资源的访问权限。Azure IAM的核心组件包括Azure Active Directory(Azure AD)、角色基于访问控制(RBAC)、条件访问策略、服务主体等,这些组件协同工作,共同构建了一个多层次的防御体系。 在Azure IAM(Identity and Access Management)中,最佳实践是使用Azure Active Directory (AAD) 中的用户(user)、组(group)、服务主体(service principal)和托管标识(managed identity)来管理身份和访问权限。 用户(User):用户帐户是指具体的人员或实体,他们可以登录到 Azure 并访问资源。每个用户都有自己的用户名和密码,可以通过 Azure 门户或其他 Azure 访问方式进行身份验证和访问控制。 组(Group):组是一组用户的集合,可以将一组用户分配到组中,然后将权限分配给整个组,而不是单独分配给每个用户。这样可以简化权限管理,并确保一致性和可伸缩性。 服务主体(Service Principal):服务主体是一种安全标识,用于在非交互式场景下代表应用程序进行身份验证。服务主体通常用于自动化任务、脚本、应用程序等场景,可以为其分配特定的权限,以便它们可以安全地访问 Azure 资源。 托管标识(Managed Identity):托管标识是 Azure 资源的一种标识,用于简化应用程序与其他 Azure 资源的身份验证过程。托管标识可以与虚拟机、函数应用、逻辑应用等 Azure 资源相关联,使它们可以安全地访问其他 Azure 服务而无需硬编码凭据。 ## Azure Token 集中式标识提供程序对于拥有全球用户(这些用户不一定从企业网络登录)的应用尤其有用。 Microsoft 标识平台对用户进行身份验证并提供安全令牌,例如访问令牌、刷新令牌和 ID 令牌。 安全令牌允许客户端应用程序访问资源服务器上受保护的资源。 * 访问令牌- 访问令牌是授权服务器作为 OAuth 2.0 流程的一部分颁发的安全令牌。它包含有关用户和令牌所针对的资源的信息。该信息可用于访问 Web API 和其他受保护的资源。资源验证访问令牌以授予对客户端应用程序的访问权限。有关详细信息,请参阅Microsoft 标识平台中的访问令牌。 * 刷新令牌- 由于访问令牌仅在短时间内有效,因此授权服务器有时会在颁发访问令牌的同时颁发刷新令牌。 然后,客户端应用程序可以在需要时将此刷新令牌交换为新的访问令牌。 有关详细信息,请参阅Microsoft 标识平台中的刷新令牌。 * ID 令牌- ID 令牌作为 OpenID Connect 流的一部分发送到客户端应用程序。它们可以与访问令牌一起发送,也可以代替访问令牌发送。客户端使用 ID 令牌对用户进行身份验证。 令牌撤销如下:  ## Application Access vs Delegated access 可以看这个视频了解更多应用权限和委派权限的区别: https://youtu.be/6R3W9T01gdE Application Access(应用程序访问): 定义:应用程序访问是指应用程序本身通过认证机制获得对特定资源或服务的访问权限。 特点:应用程序访问通常是通过使用应用程序的凭据(如API密钥、证书等)来实现的,而不是通过用户的个人身份验证信息。 示例:一个服务器端应用程序可能会使用应用程序访问权限来调用其他服务的API,而无需用户的直接参与。 Delegated Access(委托访问): 定义:委托访问是指用户授予另一个实体(通常是应用程序)代表其执行特定操作或访问资源的权限。 特点:委托访问通常涉及用户对应用程序进行授权,允许应用程序代表用户执行操作。 示例:用户可以通过OAuth等授权框架授予应用程序对其存储在第三方服务中的数据的访问权限,这就是一种委托访问的形式。 ## Managed identity ### Managed identity 种类 托管标识有两种类型: 系统分配的。 某些 Azure 资源(例如虚拟机)允许你直接在资源上启用托管标识。 启用系统分配的托管标识时: 在 Microsoft Entra ID 中为该身份创建了一个特殊类型的服务主体。服务主体与该 Azure 资源的生命周期相关联。删除 Azure 资源时,Azure 会自动为您删除服务主体。 根据设计,只有该 Azure 资源可以使用此标识从 Microsoft Entra ID 请求令牌。 您授权托管标识访问一个或多个服务。 系统分配的服务主体的名称始终与为其创建的 Azure 资源的名称相同。对于部署槽,其系统分配的标识的名称为<app-name>/slots/<slot-name>。 用户分配的。 您还可以将托管标识创建为独立的 Azure 资源。 您可以创建用户分配的托管标识并将其分配给一个或多个 Azure 资源。 启用用户分配的托管标识时: 在 Microsoft Entra ID 中为身份创建特殊类型的服务主体。服务主体与使用它的资源分开管理。 用户分配的身份可被多种资源使用。 您授权托管标识访问一个或多个服务。  ### 如何使用Managed identity 获取凭证 ``` curl 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/' -H Metadata:true ``` ``` { "access_token":"eyJ0eXAiOi...", "refresh_token":"", "expires_in":"3599", "expires_on":"1504130527", "not_before":"1504126627", "resource":"https://management.azure.com", "token_type":"Bearer" } ``` 使用此访问令牌访问 Azure 资源管理器;例如,读取您之前授予此 VM 访问权限的资源组的详细信息。将<SUBSCRIPTION-ID>、<RESOURCE-GROUP>和的值替换<ACCESS-TOKEN>为您之前创建的值。 ``` curl https://management.azure.com/subscriptions/<SUBSCRIPTION-ID>/resourceGroups/<RESOURCE-GROUP>?api-version=2016-09-01 -H "Authorization: Bearer <ACCESS-TOKEN>" ``` 参考: https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/tutorial-windows-vm-access?pivots=windows-vm-access-wvm ### managed identity 代码使用 https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview-for-developers?tabs=python ## 委派代表用户同意 参考: https://learn.microsoft.com/en-us/entra/identity-platform/consent-types-developer ## Azure RBAC 自定义权限参考资料 https://learn.microsoft.com/zh-cn/azure/role-based-access-control/resource-provider-operations ## DEMO 使用system managed identity调用blob,只使用requests ``` import os import requests # 设置环境变量 STORAGE_ACCOUNT_NAME = os.getenv('STORAGE_ACCOUNT_NAME') CONTAINER_NAME = os.getenv('CONTAINER_NAME') BLOB_NAME = os.getenv('BLOB_NAME') FILE_PATH = os.getenv('FILE_PATH') # 获取托管身份的访问令牌 def get_managed_identity_token(resource): url = "http://169.254.169.254/metadata/identity/oauth2/token" headers = {"Metadata": "true"} params = { "api-version": "2018-02-01", "resource": resource } response = requests.get(url, headers=headers, params=params) response.raise_for_status() return response.json()["access_token"] # 获取访问令牌 token = get_managed_identity_token("https://storage.azure.com/") # 构建上传 URL upload_url = f"https://{STORAGE_ACCOUNT_NAME}.blob.core.windows.net/{CONTAINER_NAME}/{BLOB_NAME}" # 读取文件内容 with open(FILE_PATH, "rb") as file_data: file_content = file_data.read() # 设置请求头 headers = { "Authorization": f"Bearer {token}", "x-ms-blob-type": "BlockBlob" } # 上传文件 response = requests.put(upload_url, headers=headers, data=file_content) # 检查响应状态 if response.status_code == 201: print(f"File {FILE_PATH} uploaded to blob {BLOB_NAME} in container {CONTAINER_NAME}.") else: print(f"Failed to upload file. Status code: {response.status_code}, Response: {response.text}") ``` 使用system managed identity调用blob,使用SDK ``` import os from azure.identity import ManagedIdentityCredential from azure.storage.blob import BlobServiceClient, BlobClient, ContainerClient # 设置环境变量 STORAGE_ACCOUNT_NAME = os.getenv('STORAGE_ACCOUNT_NAME') CONTAINER_NAME = os.getenv('CONTAINER_NAME') BLOB_NAME = os.getenv('BLOB_NAME') FILE_PATH = os.getenv('FILE_PATH') # 使用托管身份获取凭据 credential = ManagedIdentityCredential() # 创建 BlobServiceClient blob_service_client = BlobServiceClient( account_url=f"https://{STORAGE_ACCOUNT_NAME}.blob.core.windows.net", credential=credential ) # 获取容器客户端 container_client = blob_service_client.get_container_client(CONTAINER_NAME) # 上传文件 with open(FILE_PATH, "rb") as data: blob_client = container_client.get_blob_client(BLOB_NAME) blob_client.upload_blob(data, overwrite=True) print(f"File {FILE_PATH} uploaded to blob {BLOB_NAME} in container {CONTAINER_NAME}.") ``` 使用应用注册client secret方式 ``` from azure.identity import ClientSecretCredential from azure.storage.blob import BlobServiceClient # 定义 Azure Blob 存储帐户的详细信息 storage_account_name = "<storage_account_name>" container_name = "<container_name>" blob_name = "<blob_name>" local_file_path = "<local_file_path>" tenant_id = "<tenant_id>" client_id = "<client_id>" client_secret = "<client_secret>" # 创建客户端密钥凭据 credential = ClientSecretCredential(tenant_id, client_id, client_secret) # 连接到 Azure Blob 存储 blob_service_client = BlobServiceClient(account_url=f"https://{storage_account_name}.blob.core.windows.net", credential=credential) # 获取或创建 Blob 容器 container_client = blob_service_client.get_container_client(container_name) container_client.create_container() # 上传文件到 Blob 存储 blob_client = container_client.get_blob_client(blob_name) with open(local_file_path, "rb") as data: blob_client.upload_blob(data) print(f"File {local_file_path} uploaded to {blob_name} in container {container_name}") ``` 只是用requests的应用注册调用blob ``` import requests import json # Azure AD应用程序的凭据 tenant_id = 'YOUR_TENANT_ID' client_id = 'YOUR_CLIENT_ID' client_secret = 'YOUR_CLIENT_SECRET' # 获取Azure AD的访问令牌 token_url = f'https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token' token_data = { 'grant_type': 'client_credentials', 'client_id': client_id, 'client_secret': client_secret, 'scope': 'https://storage.azure.com/.default' } token_r = requests.post(token_url, data=token_data) token = token_r.json().get('access_token') # 上传文件到Azure Blob存储 blob_url = 'https://YOUR_STORAGE_ACCOUNT.blob.core.windows.net/YOUR_CONTAINER/YOUR_BLOB_NAME' file_path = 'path/to/your/file' with open(file_path, 'rb') as file: headers = { 'Authorization': f'Bearer {token}', 'x-ms-blob-type': 'BlockBlob' } r = requests.put(blob_url, data=file, headers=headers) if r.status_code == 201: print('文件上传成功!') else: print('文件上传失败。') ``` ## azure OAuth OAuth 2.0 auth code grant  ``` 客户端应用程序重定向至Azure AD: GET /authorize?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&response_type=code HTTP/1.1 Host: login.microsoftonline.com 用户登录Azure AD:用户在浏览器中输入其凭据,此处没有具体的HTTP请求。 用户授权访问:用户在Azure AD登录后,决定是否授权客户端应用程序访问其Azure资源,此处没有具体的HTTP请求。 Azure AD颁发授权码:Azure AD生成授权码并重定向至客户端应用程序的重定向URI,示例中使用YOUR_REDIRECT_URI代表重定向URI。 客户端应用程序使用授权码请求访问令牌: POST /token HTTP/1.1 Host: login.microsoftonline.com Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&code=AUTHORIZATION_CODE&redirect_uri=YOUR_REDIRECT_URI Azure AD验证并颁发访问令牌: HTTP/1.1 200 OK Content-Type: application/json { "access_token": "ACCESS_TOKEN", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "REFRESH_TOKEN" } 客户端应用程序使用访问令牌访问受保护资源:客户端应用程序在每个请求的Authorization标头中包含访问令牌。 访问令牌过期和刷新:客户端应用程序可以使用刷新令牌获取新的访问令牌,示例中未包含刷新令牌的请求。 ``` OAuth 2.0 client credentials grant  ``` 客户端向Azure AD的Token Endpoint发起POST请求,包括以下参数: grant_type: client_credentials client_id: 客户端ID client_secret: 客户端密钥 scope: 请求的资源范围 HTTP请求示例: POST /{tenant}/oauth2/v2.0/token HTTP/1.1 Host: login.microsoftonline.com Content-Type: application/x-www-form-urlencoded grant_type=client_credentials&client_id={client_id}&client_secret={client_secret}&scope={scope} Azure AD验证客户端的身份和凭据,并返回包含访问令牌的响应。 HTTP响应示例: HTTP/1.1 200 OK Content-Type: application/json { "token_type": "Bearer", "expires_in": 3600, "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjEwMjM0NTY3ODkwIn0.eyJ1cG4iOiJodHRwczovL2xvZ2luLm1p..." } 步骤二:使用访问令牌访问受保护的资源 客户端使用获取到的访问令牌来访问受保护的资源。客户端在请求的Header中添加Authorization字段,值为"Bearer {access_token}"。 HTTP请求示例: GET /api/resource HTTP/1.1 Host: example.com Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjEwMjM0NTY3ODkwIn0.eyJ1cG4iOiJodHRwczovL2xvZ2luLm1p... 服务端验证访问令牌的有效性,并根据权限决定是否允许客户端访问资源。 ``` OAuth 2.0 device code flow  ``` 获取设备代码(Device Code Request): HTTP请求示例: POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/devicecode Content-Type: application/x-www-form-urlencoded client_id={client_id}&scope={scope} 返回结果包含设备代码、用户代码、以及要求用户在另一设备上登录的URL。 用户授权(User Code Authorization): 用户需要访问提供的URL,并输入用户代码进行授权。 获取访问令牌(Access Token Request): HTTP请求示例: POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:device_code &client_id={client_id} &device_code={device_code} 返回结果包含访问令牌、刷新令牌等信息。 使用访问令牌访问资源(Access Resource with Token): 使用获取到的访问令牌访问所需的资源。 ``` ## 参考资料 https://learn.microsoft.com/en-us/entra/identity-platform/ identity 资料 https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/ Managed identities for Azure resources documentation https://learn.microsoft.com/en-us/python/api/overview/azure/identity-readme?view=azure-python SDK https://learn.microsoft.com/zh-cn/azure/role-based-access-control/resource-provider-operations RBAC