第4部分。
#python #azure #googlecloud #multicloud

在Multi-Cloud Identity Federation系列的前三篇文章中,我们讨论了有关访问令牌,身份令牌,如何区分它们以及如何在Google Cloud和Azure之间交换服务身份的情况,而无需公开您的密钥和秘密。如果您不知道我要指的是,请确保赶上上面的链接。

不要让您的生产应用程序的Python实施,首先是从Azure环境中使用Google Cloud Service帐户,然后从GCP进行模仿Azure应用程序注册。

1.来自Azure环境:模仿GCP服务帐户

生成Azure访问令牌

  1. 使用Azure身份库生成访问令牌

如果您的应用程序在可以访问Azure Instance Metadata Service (IMDS)的计算实例中运行,则可以使用此选项。这是推荐的方法,因为您不必在实例或环境变量中存储秘密,但并非总是根据用例可用。

注意:Azure.Identity模块是azure-identity package的一部分。

from azure.identity import DefaultAzureCredential
from azure.identity import AzureAuthorityHosts

default_credential = DefaultAzureCredential(
    authority=AzureAuthorityHosts.AZURE_PUBLIC_CLOUD
)
azure_access_token = default_credential.get_token(
    scopes=f"{os.environ['APPLICATION_ID']}.default"
)

b。与您的client_id和client_secret一起使用MSAL库

如果您无法访问IMD,则可以始终在应用程序的环境变量或最优选地存储并将其检索到密钥库中的环境变量中,将客户端_secret和client_id(应用程序ID)公开。然后,您可以使用MSAL ConfidentialClientApplication获取您的应用程序注册访问令牌。

from msal import ConfidentialClientApplication

CLIENT_SECRET = os.environ["CLIENT_SECRET"]
TENANT_ID = os.environ["TENANT_ID"]
APPLICATION_ID = os.environ["APPLICATION_ID"]

app = ConfidentialClientApplication(
    client_id=APPLICATION_ID,
    client_credential=CLIENT_SECRET,
    authority=f"{AzureAuthorityHosts.AZURE_PUBLIC_CLOUD}/{TENANT_ID}"
)
azure_access_token = app.acquire_token_for_client(
    scopes=f"{APPLICATION_ID}/.default"
)["access_token"]

使用Google的STS客户端通过工作量身份联合会获得联合令牌

令牌交换过程的第二步是向Google STS API索取短暂的令牌。确保了解article Part 2中详述的参数。

from google.oauth2.sts import Client
from google.auth.transport.requests import Request

GCP_PROJECT_NUMBER = os.environ["PROJECT_NUMER"]
GCP_PROJECT_ID = os.environ["GCP_PROJECT_ID"]
POOL_ID = os.environ["POOL_ID"]
PROVIDER_ID = os.environ["PROVIDER_ID"]

sts_client = Client(token_exchange_endpoint="https://sts.googleapis.com/v1/token")
response = sts_client.exchange_token(
    request=Request(),
    audience=f"//iam.googleapis.com/projects/{GCP_PROJECT_NUMBER}/locations/global/workloadIdentityPools/{POOL_ID}/providers/{PROVIDER_ID}",
    grant_type="urn:ietf:params:oauth:grant-type:token-exchange",
    subject_token=azure_access_token,
    scopes=["https://www.googleapis.com/auth/cloud-platform"],
    subject_token_type="urn:ietf:params:oauth:token-type:jwt",
    requested_token_type="urn:ietf:params:oauth:token-type:access_token"
)
sts_access_token = response["access_token"]

使用STS令牌模仿目标服务帐户

当您拥有STS令牌(联合令牌)时,您最终可以假冒目标服务帐户(假设您扮演了Workload Identity PrincipalSet的正确角色)

创建目标凭证对象

from google.oauth2.credentials import Credentials
from google.auth import impersonated_credentials

TARGET_SERVICE_ACCOUNT = os.environ["TARGET_SERVICE_ACCOUNT"]

sts_credentials = Credentials(token=sts_access_token)

credentials = impersonated_credentials.Credentials(
  source_credentials=sts_credentials,
  target_principal=TARGET_SERVICE_ACCOUNT,
  target_scopes = ["https://www.googleapis.com/auth/cloud-platform"],
  lifetime=500
)
credentials.refresh(Request())

从Azure环境中致电您的Google API(此处为BigQuery)

现在,令牌交换过程已经结束,您可以通过使用相应的客户端库请求目标服务帐户可以访问的任何API(这里是BigQuery)。

from google.cloud import bigquery

client = bigquery.Client(credentials=credentials, project=GCP_PROJECT_ID)

# Here my TARGET_SERVICE_ACCOUNT has bigquery.jobUser role.
query = "SELECT CURRENT_DATE() as date"
query_job = client.query(query)  # Make an API request.

print("The query data:")
for row in query_job:
    print(row["date"])
# It works !

2.来自Google Cloud:模仿Azure应用程序

让我们从另一个角度查看Python实现:从GCP环境中模仿Azure应用程序。此过程在Part 3 of the series中详细介绍。确保阅读它以了解过程。

从tokencredential实施Python凭据类

大多数Microsoft客户端库可以将凭据实例作为参数。即使大多数情况下它是DefaultAzureCredentialConfidentialClientApplication,也可以通过实现TokenCredential接口来创建自己的。该类必须实现get_token方法,该方法在认证时由客户端库调用。

在这里,我们首先通过查询Google Metadata Server来执行Google ID代币生成,然后我们将ConfidentialClientApplication与ID代币一起使用为client_assertion来获取联合令牌。

from azure.core.credentials import TokenCredential, AccessToken
from msal import ConfidentialClientApplication
from google.auth.transport.requests import Request
import time

class GoogleAssertionCredential(TokenCredential):

    def __init__(self, azure_client_id, azure_tenant_id, azure_authority_host):
        # create a confidential client application
        self.app = ConfidentialClientApplication(
            azure_client_id,
            client_credential={
                'client_assertion': self._get_google_id_token()
            },
            authority=f"{azure_authority_host}{azure_tenant_id}"
        )

    def _get_google_id_token(self) -> str:
                """Request an ID token to the Metadata Server"""
        response = Request()(
            f"{GOOGLE_METADATA_API}/instance/service-accounts/default/identity",
                        f"?audience=api://AzureADTokenExchange",
            method="GET",
            headers={"Metadata-Flavor": "Google"},
          )
                return response.data.decode("utf-8")

    def get_token(
        self,
        *scopes: str,
        claims: Optional[str] = None,
        tenant_id: Optional[str] = None,
        **kwargs: Any
    ) -> AccessToken:
        # get the token using the application
        token = self.app.acquire_token_for_client(scopes)
        if 'error' in token:
            raise Exception(token['error_description'])
        expires_on = time.time() + token['expires_in']
        # return an access token with the token string and expiration time
        return AccessToken(token['access_token'], int(expires_on))

注意:使用元数据服务器的代币生成只能在GCP上部署的应用程序上使用。如果要在本地进行测试,则可以使用服务帐户文件。

credentials = IDTokenCredentials.from_service_account_file(
    GOOGLE_APPLICATION_CREDENTIALS,
    target_audience="api://AzureADTokenExchange",
)
credentials.refresh(Request())
return credentials.token

实例化googleasersertioncredention和查询最终Azure API

最后,您可以请求任何API Azure应用程序注册可以访问,以完成您的工作。只需使用您的目标Azure App client_id&tenant_id实例化GoogleasSertioncredentioncredentioncredention,然后将其传递给客户端库(在这里,假设应用程序注册在Azure存储帐户中具有贡献者的角色,则它的BlobServiceClient)

CLIENT_ID = os.environ["CLIENT_ID"]
TENANT_ID = os.environ["TENANT_ID"]

creds = GoogleAssertionCredential(
    azure_client_id=CLIENT_ID,
    azure_tenant_id=TENANT_ID,
    azure_authority_host=AzureAuthorityHosts.AZURE_PUBLIC_CLOUD
)

STORAGE_ACCOUNT = os.environ["STORAGE_ACCOUNT"]
CONTAINER = os.environ["CONTAINER"]
# Here the App registration is Contributor of the Azure storage account
blob_service_client = BlobServiceClient(f"https://{STORAGE_ACCOUNT}.blob.core.windows.net", credential=creds)
container_client = blob_service_client.get_container_client(container=CONTAINER)
for blob in container_client.list_blob_names():
    print(blob)
        # It works !

我们刚刚看到了如何与Python的制作中的Google Cloud和Azure之间具体假冒的服务身份。请注意良好的做法:

  • 没有秘密存储,如果不需要,两个云中都有元数据服务器
  • 使用正确的受众或范围仅需做的事情,因此,如果令牌泄漏,小偷只能在代币到期之前将其用于目标服务(少于1小时)

我们将在此多云系列的下一个也是最后一部分中看到如何使用Terraform来交换令牌以从Google Cloud Build中创建Azure资源。