Skip to content

Commit

Permalink
Rename ClientAssertion to WorkloadIdentity (#1646)
Browse files Browse the repository at this point in the history
  • Loading branch information
vibhansa-msft authored Mar 4, 2025
1 parent 62584ff commit e2645b8
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 43 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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**
Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
2 changes: 1 addition & 1 deletion blobfuse2-code-coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions component/azstorage/azauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
}
Expand Down Expand Up @@ -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,
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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 {
Expand All @@ -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
Expand Down
6 changes: 3 additions & 3 deletions component/azstorage/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (AuthType) AZCLI() AuthType {
return AuthType(5)
}

func (AuthType) CLIENTASSERTION() AuthType {
func (AuthType) WORKLOADIDENTITY() AuthType {
return AuthType(6)
}

Expand Down Expand Up @@ -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")
}
Expand Down

0 comments on commit e2645b8

Please sign in to comment.