From e2645b894a4dc5e73ae73bafb4896ae036c8e3d3 Mon Sep 17 00:00:00 2001 From: Vikas Bhansali <64532198+vibhansa-msft@users.noreply.github.com> Date: Tue, 4 Mar 2025 19:00:26 +0530 Subject: [PATCH] Rename ClientAssertion to WorkloadIdentity (#1646) --- CHANGELOG.md | 2 +- README.md | 12 ++++ blobfuse2-code-coverage.yaml | 2 +- component/azstorage/azauth.go | 12 ++-- ...assertion.go => azauthWorkloadIdentity.go} | 64 +++++++++---------- component/azstorage/config.go | 6 +- 6 files changed, 55 insertions(+), 43 deletions(-) rename component/azstorage/{azauthclientassertion.go => azauthWorkloadIdentity.go} (60%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76e89ac31..88d8b2ed5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## 2.5.0 (Unreleased) **Features** -- Added `Client Assertion` based authentication for containers. Configure `tenant-id, client-id, aad-application-id and security scope` with `authMode` set to `clientassertion`. +- Added `Client Assertion` based authentication for containers. Configure `tenant-id, client-id, aad-application-id and security scope` with `authMode` set to `workloadidentity`. ## 2.5.0~preview.1 (Unreleased) **Features** diff --git a/README.md b/README.md index 9d4aaca08..dbe6804c2 100755 --- a/README.md +++ b/README.md @@ -202,6 +202,18 @@ To learn about a specific command, just include the name of the command (For exa * `AZURE_STORAGE_AAD_ENDPOINT`: Specifies a custom AAD endpoint to authenticate against * `AZURE_STORAGE_SPN_CLIENT_SECRET`: Specifies the client secret for your application registration. * `AZURE_STORAGE_AUTH_RESOURCE` : Scope to be used while requesting for token. +- Workload Identity auth: + * `AZURE_STORAGE_SPN_CLIENT_ID`: Specifies the clientid of the MI assigned to the storage account | clientid of the MI assigned as subject field on a Federated Identity Credential (FIC) on the App Registration + * `AZURE_STORAGE_SPN_TENANT_ID`: Specifies the tenant ID for your storage account + * `AZURE_STORAGE_IDENTITY_CLIENT_ID`: Specifies the application (client) ID of the App Registration or SPN + * `AZURE_STORAGE_AUTH_RESOURCE` : Scope to be used while requesting for token / MI Audience. + + Public Cloud: api://AzureADTokenExchange (Default) + + US Gov Cloud: api://AzureADTokenExchangeUSGov + + China Cloud operated by 21Vianet: api://AzureADTokenExchangeChina + - Proxy Server: * `http_proxy`: The proxy server address. Example: `10.1.22.4:8080`. * `https_proxy`: The proxy server address when https is turned off forcing http. Example: `10.1.22.4:8080`. diff --git a/blobfuse2-code-coverage.yaml b/blobfuse2-code-coverage.yaml index b32f5308d..8fe91e5c0 100644 --- a/blobfuse2-code-coverage.yaml +++ b/blobfuse2-code-coverage.yaml @@ -565,7 +565,7 @@ stages: - script: | echo 'mode: count' > ./blobfuse2_coverage_raw.rpt tail -q -n +2 ./*.cov >> ./blobfuse2_coverage_raw.rpt - cat ./blobfuse2_coverage_raw.rpt | grep -v mock_component | grep -v base_component | grep -v loopback | grep -v tools | grep -v "common/log" | grep -v "common/exectime" | grep -v "common/types.go" | grep -v "internal/stats_manager" | grep -v "main.go" | grep -v "component/azstorage/azauthmsi.go" | grep -v "component/azstorage/azauthspn.go" | grep -v "component/stream" | grep -v "component/custom" | grep -v "component/azstorage/azauthcli.go" | grep -v "exported/exported.go" | grep -v "component/block_cache/stream.go" | grep -v "component/azstorage/azauthclientassertion.go" > ./blobfuse2_coverage.rpt + cat ./blobfuse2_coverage_raw.rpt | grep -v mock_component | grep -v base_component | grep -v loopback | grep -v tools | grep -v "common/log" | grep -v "common/exectime" | grep -v "common/types.go" | grep -v "internal/stats_manager" | grep -v "main.go" | grep -v "component/azstorage/azauthmsi.go" | grep -v "component/azstorage/azauthspn.go" | grep -v "component/stream" | grep -v "component/custom" | grep -v "component/azstorage/azauthcli.go" | grep -v "exported/exported.go" | grep -v "component/block_cache/stream.go" | grep -v "component/azstorage/azAuthWorkloadIdentity.go" > ./blobfuse2_coverage.rpt go tool cover -func blobfuse2_coverage.rpt > ./blobfuse2_func_cover.rpt go tool cover -html=./blobfuse2_coverage.rpt -o ./blobfuse2_coverage.html go tool cover -html=./blobfuse2_ut.cov -o ./blobfuse2_ut.html diff --git a/component/azstorage/azauth.go b/component/azstorage/azauth.go index 0ca2398ed..80fbbd120 100644 --- a/component/azstorage/azauth.go +++ b/component/azstorage/azauth.go @@ -137,9 +137,9 @@ func getAzBlobAuth(config azAuthConfig) azAuth { azAuthBase: base, }, } - } else if config.AuthMode == EAuthType.CLIENTASSERTION() { - return &azAuthBlobClientAssertion{ - azAuthClientAssertion{ + } else if config.AuthMode == EAuthType.WORKLOADIDENTITY() { + return &azAuthBlobWorkloadIdentity{ + azAuthWorkloadIdentity{ azAuthBase: base, }, } @@ -181,9 +181,9 @@ func getAzDatalakeAuth(config azAuthConfig) azAuth { azAuthBase: base, }, } - } else if config.AuthMode == EAuthType.CLIENTASSERTION() { - return &azAuthDatalakeClientAssertion{ - azAuthClientAssertion{ + } else if config.AuthMode == EAuthType.WORKLOADIDENTITY() { + return &azAuthDatalakeWorkloadIdentity{ + azAuthWorkloadIdentity{ azAuthBase: base, }, } diff --git a/component/azstorage/azauthclientassertion.go b/component/azstorage/azauthWorkloadIdentity.go similarity index 60% rename from component/azstorage/azauthclientassertion.go rename to component/azstorage/azauthWorkloadIdentity.go index 8b11fc449..a34d29e20 100644 --- a/component/azstorage/azauthclientassertion.go +++ b/component/azstorage/azauthWorkloadIdentity.go @@ -45,31 +45,31 @@ import ( ) // Verify that the Auth implement the correct AzAuth interfaces -var _ azAuth = &azAuthBlobClientAssertion{} -var _ azAuth = &azAuthDatalakeClientAssertion{} +var _ azAuth = &azAuthBlobWorkloadIdentity{} +var _ azAuth = &azAuthDatalakeWorkloadIdentity{} -type azAuthClientAssertion struct { +type azAuthWorkloadIdentity struct { azAuthBase azOAuthBase } -func (azclientassertion *azAuthClientAssertion) getTokenCredential() (azcore.TokenCredential, error) { - opts := azclientassertion.getAzIdentityClientOptions(&azclientassertion.config) +func (azWorkloadIdentity *azAuthWorkloadIdentity) getTokenCredential() (azcore.TokenCredential, error) { + opts := azWorkloadIdentity.getAzIdentityClientOptions(&azWorkloadIdentity.config) // Create MSI cred to fetch token msiOpts := &azidentity.ManagedIdentityCredentialOptions{ ClientOptions: opts, } - msiOpts.ID = azidentity.ClientID(azclientassertion.config.ApplicationID) + msiOpts.ID = azidentity.ClientID(azWorkloadIdentity.config.ApplicationID) cred, err := azidentity.NewManagedIdentityCredential(msiOpts) if err != nil { - log.Err("azAuthClientAssertion::getTokenCredential : Failed to create managed identity credential [%s]", err.Error()) + log.Err("azAuthWorkloadIdentity::getTokenCredential : Failed to create managed identity credential [%s]", err.Error()) return nil, err } scope := "api://AzureADTokenExchange" - if azclientassertion.config.AuthResource != "" { - scope = azclientassertion.config.AuthResource + if azWorkloadIdentity.config.AuthResource != "" { + scope = azWorkloadIdentity.config.AuthResource } getClientAssertions := func(context.Context) (string, error) { @@ -78,21 +78,21 @@ func (azclientassertion *azAuthClientAssertion) getTokenCredential() (azcore.Tok }) if err != nil { - log.Err("azAuthClientAssertion::getTokenCredential : Failed to get token from managed identity credential [%s]", err.Error()) + log.Err("azAuthWorkloadIdentity::getTokenCredential : Failed to get token from managed identity credential [%s]", err.Error()) return "", err } return token.Token, nil } - if azclientassertion.config.UserAssertion == "" { + if azWorkloadIdentity.config.UserAssertion == "" { assertOpts := &azidentity.ClientAssertionCredentialOptions{ ClientOptions: opts, } return azidentity.NewClientAssertionCredential( - azclientassertion.config.TenantID, - azclientassertion.config.ClientID, + azWorkloadIdentity.config.TenantID, + azWorkloadIdentity.config.ClientID, getClientAssertions, assertOpts) } else { @@ -101,61 +101,61 @@ func (azclientassertion *azAuthClientAssertion) getTokenCredential() (azcore.Tok } return azidentity.NewOnBehalfOfCredentialWithClientAssertions( - azclientassertion.config.TenantID, - azclientassertion.config.ClientID, - azclientassertion.config.UserAssertion, + azWorkloadIdentity.config.TenantID, + azWorkloadIdentity.config.ClientID, + azWorkloadIdentity.config.UserAssertion, getClientAssertions, assertOpts) } } -type azAuthBlobClientAssertion struct { - azAuthClientAssertion +type azAuthBlobWorkloadIdentity struct { + azAuthWorkloadIdentity } // getServiceClient : returns SPN based service client for blob -func (azclientassertion *azAuthBlobClientAssertion) getServiceClient(stConfig *AzStorageConfig) (interface{}, error) { - cred, err := azclientassertion.getTokenCredential() +func (azWorkloadIdentity *azAuthBlobWorkloadIdentity) getServiceClient(stConfig *AzStorageConfig) (interface{}, error) { + cred, err := azWorkloadIdentity.getTokenCredential() if err != nil { - log.Err("azAuthBlobClientAssertion::getServiceClient : Failed to get token credential from client assertion [%s]", err.Error()) + log.Err("azAuthBlobWorkloadIdentity::getServiceClient : Failed to get token credential from client assertion [%s]", err.Error()) return nil, err } opts, err := getAzBlobServiceClientOptions(stConfig) if err != nil { - log.Err("azAuthBlobClientAssertion::getServiceClient : Failed to create client options [%s]", err.Error()) + log.Err("azAuthBlobWorkloadIdentity::getServiceClient : Failed to create client options [%s]", err.Error()) return nil, err } - svcClient, err := service.NewClient(azclientassertion.config.Endpoint, cred, opts) + svcClient, err := service.NewClient(azWorkloadIdentity.config.Endpoint, cred, opts) if err != nil { - log.Err("azAuthBlobClientAssertion::getServiceClient : Failed to create service client [%s]", err.Error()) + log.Err("azAuthBlobWorkloadIdentity::getServiceClient : Failed to create service client [%s]", err.Error()) } return svcClient, err } -type azAuthDatalakeClientAssertion struct { - azAuthClientAssertion +type azAuthDatalakeWorkloadIdentity struct { + azAuthWorkloadIdentity } // getServiceClient : returns SPN based service client for blob -func (azclientassertion *azAuthDatalakeClientAssertion) getServiceClient(stConfig *AzStorageConfig) (interface{}, error) { - cred, err := azclientassertion.getTokenCredential() +func (azWorkloadIdentity *azAuthDatalakeWorkloadIdentity) getServiceClient(stConfig *AzStorageConfig) (interface{}, error) { + cred, err := azWorkloadIdentity.getTokenCredential() if err != nil { - log.Err("azAuthDatalakeClientAssertion::getServiceClient : Failed to get token credential from client assertion [%s]", err.Error()) + log.Err("azAuthDatalakeWorkloadIdentity::getServiceClient : Failed to get token credential from client assertion [%s]", err.Error()) return nil, err } opts, err := getAzDatalakeServiceClientOptions(stConfig) if err != nil { - log.Err("azAuthDatalakeClientAssertion::getServiceClient : Failed to create client options [%s]", err.Error()) + log.Err("azAuthDatalakeWorkloadIdentity::getServiceClient : Failed to create client options [%s]", err.Error()) return nil, err } - svcClient, err := serviceBfs.NewClient(azclientassertion.config.Endpoint, cred, opts) + svcClient, err := serviceBfs.NewClient(azWorkloadIdentity.config.Endpoint, cred, opts) if err != nil { - log.Err("azAuthDatalakeClientAssertion::getServiceClient : Failed to create service client [%s]", err.Error()) + log.Err("azAuthDatalakeWorkloadIdentity::getServiceClient : Failed to create service client [%s]", err.Error()) } return svcClient, err diff --git a/component/azstorage/config.go b/component/azstorage/config.go index 8ef90ac14..fa8e9cb8e 100644 --- a/component/azstorage/config.go +++ b/component/azstorage/config.go @@ -76,7 +76,7 @@ func (AuthType) AZCLI() AuthType { return AuthType(5) } -func (AuthType) CLIENTASSERTION() AuthType { +func (AuthType) WORKLOADIDENTITY() AuthType { return AuthType(6) } @@ -474,8 +474,8 @@ func ParseAndValidateConfig(az *AzStorage, opt AzStorageOptions) error { az.stConfig.authConfig.WorkloadIdentityToken = opt.WorkloadIdentityToken case EAuthType.AZCLI(): az.stConfig.authConfig.AuthMode = EAuthType.AZCLI() - case EAuthType.CLIENTASSERTION(): - az.stConfig.authConfig.AuthMode = EAuthType.CLIENTASSERTION() + case EAuthType.WORKLOADIDENTITY(): + az.stConfig.authConfig.AuthMode = EAuthType.WORKLOADIDENTITY() if opt.ClientID == "" || opt.TenantID == "" || opt.ApplicationID == "" { return errors.New("Client ID, Tenant ID or Application ID not provided") }